mirror of https://github.com/status-im/metro.git
Updates from Wed 24 Jun
This commit is contained in:
commit
e316a17b14
|
@ -11,7 +11,6 @@
|
||||||
// Don't forget to everything listed here to `testConfig.json`
|
// Don't forget to everything listed here to `testConfig.json`
|
||||||
// modulePathIgnorePatterns.
|
// modulePathIgnorePatterns.
|
||||||
var sharedBlacklist = [
|
var sharedBlacklist = [
|
||||||
__dirname,
|
|
||||||
'website',
|
'website',
|
||||||
'node_modules/react-tools/src/utils/ImmutableObject.js',
|
'node_modules/react-tools/src/utils/ImmutableObject.js',
|
||||||
'node_modules/react-tools/src/core/ReactInstanceHandles.js',
|
'node_modules/react-tools/src/core/ReactInstanceHandles.js',
|
||||||
|
|
|
@ -48,10 +48,11 @@ var messageHandlers = {
|
||||||
loadScript(message.url, sendReply.bind(null, null));
|
loadScript(message.url, sendReply.bind(null, null));
|
||||||
},
|
},
|
||||||
'executeJSCall': function(message, sendReply) {
|
'executeJSCall': function(message, sendReply) {
|
||||||
var returnValue = [[], [], [], [], []];
|
var returnValue = null;
|
||||||
try {
|
try {
|
||||||
if (window && window.require) {
|
if (window && window.require) {
|
||||||
returnValue = window.require(message.moduleName)[message.moduleMethod].apply(null, message.arguments);
|
var module = window.require(message.moduleName);
|
||||||
|
returnValue = module[message.moduleMethod].apply(module, message.arguments);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
sendReply(JSON.stringify(returnValue));
|
sendReply(JSON.stringify(returnValue));
|
||||||
|
|
|
@ -11,7 +11,6 @@ on run argv
|
||||||
set theURL to item 1 of argv
|
set theURL to item 1 of argv
|
||||||
|
|
||||||
tell application "Chrome"
|
tell application "Chrome"
|
||||||
activate
|
|
||||||
|
|
||||||
if (count every window) = 0 then
|
if (count every window) = 0 then
|
||||||
make new window
|
make new window
|
||||||
|
@ -40,6 +39,7 @@ on run argv
|
||||||
set theWindow's active tab index to theTabIndex
|
set theWindow's active tab index to theTabIndex
|
||||||
else
|
else
|
||||||
tell window 1
|
tell window 1
|
||||||
|
activate
|
||||||
make new tab with properties {URL:theURL}
|
make new tab with properties {URL:theURL}
|
||||||
end tell
|
end tell
|
||||||
end if
|
end if
|
||||||
|
|
|
@ -66,6 +66,9 @@ if (options.projectRoots) {
|
||||||
if (__dirname.match(/node_modules\/react-native\/packager$/)) {
|
if (__dirname.match(/node_modules\/react-native\/packager$/)) {
|
||||||
// packager is running from node_modules of another project
|
// packager is running from node_modules of another project
|
||||||
options.projectRoots = [path.resolve(__dirname, '../../..')];
|
options.projectRoots = [path.resolve(__dirname, '../../..')];
|
||||||
|
} else if (__dirname.match(/Pods\/React\/packager$/)) {
|
||||||
|
// packager is running from node_modules of another project
|
||||||
|
options.projectRoots = [path.resolve(__dirname, '../../..')];
|
||||||
} else {
|
} else {
|
||||||
options.projectRoots = [path.resolve(__dirname, '..')];
|
options.projectRoots = [path.resolve(__dirname, '..')];
|
||||||
}
|
}
|
||||||
|
@ -83,11 +86,15 @@ if (options.root) {
|
||||||
|
|
||||||
if (options.assetRoots) {
|
if (options.assetRoots) {
|
||||||
if (!Array.isArray(options.assetRoots)) {
|
if (!Array.isArray(options.assetRoots)) {
|
||||||
options.assetRoots = options.assetRoots.split(',');
|
options.assetRoots = options.assetRoots.split(',').map(function (dir) {
|
||||||
|
return path.resolve(process.cwd(), dir);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (__dirname.match(/node_modules\/react-native\/packager$/)) {
|
if (__dirname.match(/node_modules\/react-native\/packager$/)) {
|
||||||
options.assetRoots = [path.resolve(__dirname, '../../..')];
|
options.assetRoots = [path.resolve(__dirname, '../../..')];
|
||||||
|
} else if (__dirname.match(/Pods\/React\/packager$/)) {
|
||||||
|
options.assetRoots = [path.resolve(__dirname, '../../..')];
|
||||||
} else {
|
} else {
|
||||||
options.assetRoots = [path.resolve(__dirname, '..')];
|
options.assetRoots = [path.resolve(__dirname, '..')];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Keep in sync with packager/transformer.js
|
||||||
|
{
|
||||||
|
"retainLines": true,
|
||||||
|
"compact": true,
|
||||||
|
"comments": false,
|
||||||
|
"whitelist": [
|
||||||
|
"es6.arrowFunctions",
|
||||||
|
"es6.blockScoping",
|
||||||
|
// This is the only place where we differ from transformer.js
|
||||||
|
"es6.constants",
|
||||||
|
"es6.classes",
|
||||||
|
"es6.destructuring",
|
||||||
|
"es6.parameters.rest",
|
||||||
|
"es6.properties.computed",
|
||||||
|
"es6.properties.shorthand",
|
||||||
|
"es6.spread",
|
||||||
|
"es6.templateLiterals",
|
||||||
|
"es7.trailingFunctionCommas",
|
||||||
|
"es7.objectRestSpread",
|
||||||
|
"flow",
|
||||||
|
"react"
|
||||||
|
],
|
||||||
|
"sourceMaps": false
|
||||||
|
}
|
|
@ -1,5 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
jest.autoMockOff();
|
|
||||||
module.exports = require.requireActual('bluebird');
|
|
||||||
jest.autoMockOn();
|
|
|
@ -8,6 +8,10 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
require('babel/register')({
|
||||||
|
only: /react-packager\/src/
|
||||||
|
});
|
||||||
|
|
||||||
useGracefulFs();
|
useGracefulFs();
|
||||||
|
|
||||||
var Activity = require('./src/Activity');
|
var Activity = require('./src/Activity');
|
||||||
|
|
|
@ -8,7 +8,7 @@ jest
|
||||||
.mock('crypto')
|
.mock('crypto')
|
||||||
.mock('fs');
|
.mock('fs');
|
||||||
|
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
|
|
||||||
describe('AssetServer', function() {
|
describe('AssetServer', function() {
|
||||||
var AssetServer;
|
var AssetServer;
|
||||||
|
|
|
@ -11,13 +11,13 @@
|
||||||
var declareOpts = require('../lib/declareOpts');
|
var declareOpts = require('../lib/declareOpts');
|
||||||
var getAssetDataFromName = require('../lib/getAssetDataFromName');
|
var getAssetDataFromName = require('../lib/getAssetDataFromName');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
|
|
||||||
var lstat = Promise.promisify(fs.lstat);
|
var stat = Promise.denodeify(fs.stat);
|
||||||
var readDir = Promise.promisify(fs.readdir);
|
var readDir = Promise.denodeify(fs.readdir);
|
||||||
var readFile = Promise.promisify(fs.readFile);
|
var readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
module.exports = AssetServer;
|
module.exports = AssetServer;
|
||||||
|
|
||||||
|
@ -56,12 +56,15 @@ AssetServer.prototype._getAssetRecord = function(assetPath) {
|
||||||
this._roots,
|
this._roots,
|
||||||
path.dirname(assetPath)
|
path.dirname(assetPath)
|
||||||
).then(function(dir) {
|
).then(function(dir) {
|
||||||
return [
|
return Promise.all([
|
||||||
dir,
|
dir,
|
||||||
readDir(dir),
|
readDir(dir),
|
||||||
];
|
]);
|
||||||
}).spread(function(dir, files) {
|
}).then(function(res) {
|
||||||
|
var dir = res[0];
|
||||||
|
var files = res[1];
|
||||||
var assetData = getAssetDataFromName(filename);
|
var assetData = getAssetDataFromName(filename);
|
||||||
|
|
||||||
var map = buildAssetMap(dir, files);
|
var map = buildAssetMap(dir, files);
|
||||||
var record = map[assetData.assetName];
|
var record = map[assetData.assetName];
|
||||||
|
|
||||||
|
@ -98,14 +101,14 @@ AssetServer.prototype.getAssetData = function(assetPath) {
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
record.files.map(function(file) {
|
record.files.map(function(file) {
|
||||||
return lstat(file);
|
return stat(file);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}).then(function(stats) {
|
}).then(function(stats) {
|
||||||
var hash = crypto.createHash('md5');
|
var hash = crypto.createHash('md5');
|
||||||
|
|
||||||
stats.forEach(function(stat) {
|
stats.forEach(function(fstat) {
|
||||||
hash.update(stat.mtime.getTime().toString());
|
hash.update(fstat.mtime.getTime().toString());
|
||||||
});
|
});
|
||||||
|
|
||||||
data.hash = hash.digest('hex');
|
data.hash = hash.digest('hex');
|
||||||
|
@ -114,21 +117,23 @@ AssetServer.prototype.getAssetData = function(assetPath) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function findRoot(roots, dir) {
|
function findRoot(roots, dir) {
|
||||||
return Promise.some(
|
return Promise.all(
|
||||||
roots.map(function(root) {
|
roots.map(function(root) {
|
||||||
var absPath = path.join(root, dir);
|
var absPath = path.join(root, dir);
|
||||||
return lstat(absPath).then(function(stat) {
|
return stat(absPath).then(function(fstat) {
|
||||||
if (!stat.isDirectory()) {
|
return {path: absPath, isDirectory: fstat.isDirectory()};
|
||||||
throw new Error('Looking for dirs');
|
}, function (err) {
|
||||||
}
|
return {path: absPath, isDirectory: false};
|
||||||
stat._path = absPath;
|
|
||||||
return stat;
|
|
||||||
});
|
});
|
||||||
}),
|
})
|
||||||
1
|
).then(
|
||||||
).spread(
|
function(stats) {
|
||||||
function(stat) {
|
for (var i = 0; i < stats.length; i++) {
|
||||||
return stat._path;
|
if (stats[i].isDirectory) {
|
||||||
|
return stats[i].path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('Could not find any directories');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Module = require('./Module');
|
||||||
|
const Promise = require('promise');
|
||||||
|
const getAssetDataFromName = require('../lib/getAssetDataFromName');
|
||||||
|
|
||||||
|
class AssetModule extends Module {
|
||||||
|
|
||||||
|
isHaste() {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencies() {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_read() {
|
||||||
|
return Promise.resolve({});
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return super.getName().then(id => {
|
||||||
|
const {name, type} = getAssetDataFromName(this.path);
|
||||||
|
return id.replace(/\/[^\/]+$/, `/${name}.${type}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlainObject() {
|
||||||
|
return this.getName().then(name => this.addReference({
|
||||||
|
path: this.path,
|
||||||
|
isJSON: false,
|
||||||
|
isAsset: true,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
resolution: getAssetDataFromName(this.path).resolution,
|
||||||
|
id: name,
|
||||||
|
dependencies: [],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
hash() {
|
||||||
|
return `AssetModule : ${this.path}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AssetModule;
|
|
@ -0,0 +1,40 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Module = require('./Module');
|
||||||
|
const Promise = require('promise');
|
||||||
|
const getAssetDataFromName = require('../lib/getAssetDataFromName');
|
||||||
|
|
||||||
|
class AssetModule_DEPRECATED extends Module {
|
||||||
|
isHaste() {
|
||||||
|
return Promise.resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return Promise.resolve(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencies() {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlainObject() {
|
||||||
|
const {name, resolution} = getAssetDataFromName(this.path);
|
||||||
|
|
||||||
|
return Promise.resolve(this.addReference({
|
||||||
|
path: this.path,
|
||||||
|
id: `image!${name}`,
|
||||||
|
resolution,
|
||||||
|
isAsset_DEPRECATED: true,
|
||||||
|
dependencies: [],
|
||||||
|
isJSON: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
isAsset: false,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
hash() {
|
||||||
|
return `AssetModule_DEPRECATED : ${this.path}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = AssetModule_DEPRECATED;
|
3241
react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js
vendored
Normal file
3241
react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,556 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
const Fastfs = require('../fastfs');
|
||||||
|
const ModuleCache = require('../ModuleCache');
|
||||||
|
const AssetModule_DEPRECATED = require('../AssetModule_DEPRECATED');
|
||||||
|
const declareOpts = require('../../lib/declareOpts');
|
||||||
|
const isAbsolutePath = require('absolute-path');
|
||||||
|
const debug = require('debug')('DependencyGraph');
|
||||||
|
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
|
||||||
|
const util = require('util');
|
||||||
|
const Promise = require('promise');
|
||||||
|
const _ = require('underscore');
|
||||||
|
|
||||||
|
const validateOpts = declareOpts({
|
||||||
|
roots: {
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
ignoreFilePath: {
|
||||||
|
type: 'function',
|
||||||
|
default: function(){}
|
||||||
|
},
|
||||||
|
fileWatcher: {
|
||||||
|
type: 'object',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
assetRoots_DEPRECATED: {
|
||||||
|
type: 'array',
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
assetExts: {
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
providesModuleNodeModules: {
|
||||||
|
type: 'array',
|
||||||
|
default: [
|
||||||
|
'react-tools',
|
||||||
|
'react-native',
|
||||||
|
// Parse requires AsyncStorage. They will
|
||||||
|
// change that to require('react-native') which
|
||||||
|
// should work after this release and we can
|
||||||
|
// remove it from here.
|
||||||
|
'parse',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
class DependencyGraph {
|
||||||
|
constructor(options) {
|
||||||
|
this._opts = validateOpts(options);
|
||||||
|
this._hasteMap = Object.create(null);
|
||||||
|
this._immediateResolutionCache = Object.create(null);
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
if (this._loading) {
|
||||||
|
return this._loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulePattern = new RegExp(
|
||||||
|
'\.(' + ['js', 'json'].concat(this._assetExts).join('|') + ')$'
|
||||||
|
);
|
||||||
|
|
||||||
|
this._fastfs = new Fastfs(this._opts.roots,this._opts.fileWatcher, {
|
||||||
|
pattern: modulePattern,
|
||||||
|
ignore: this._opts.ignoreFilePath,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._fastfs.on('change', this._processFileChange.bind(this));
|
||||||
|
|
||||||
|
this._moduleCache = new ModuleCache(this._fastfs);
|
||||||
|
|
||||||
|
this._loading = Promise.all([
|
||||||
|
this._fastfs.build().then(() => this._buildHasteMap()),
|
||||||
|
this._buildAssetMap_DEPRECATED(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return this._loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveDependency(fromModule, toModuleName) {
|
||||||
|
if (fromModule._ref) {
|
||||||
|
fromModule = fromModule._ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resHash = resolutionHash(fromModule.path, toModuleName);
|
||||||
|
|
||||||
|
if (this._immediateResolutionCache[resHash]) {
|
||||||
|
return Promise.resolve(this._immediateResolutionCache[resHash]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const asset_DEPRECATED = this._resolveAsset_DEPRECATED(
|
||||||
|
fromModule,
|
||||||
|
toModuleName
|
||||||
|
);
|
||||||
|
if (asset_DEPRECATED) {
|
||||||
|
return Promise.resolve(asset_DEPRECATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheResult = (result) => {
|
||||||
|
this._immediateResolutionCache[resHash] = result;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const forgive = () => {
|
||||||
|
console.warn(
|
||||||
|
'Unable to resolve module %s from %s',
|
||||||
|
toModuleName,
|
||||||
|
fromModule.path
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this._isNodeModulesDir(fromModule.path)
|
||||||
|
&& toModuleName[0] !== '.' &&
|
||||||
|
toModuleName[0] !== '/') {
|
||||||
|
return this._resolveHasteDependency(fromModule, toModuleName).catch(
|
||||||
|
() => this._resolveNodeDependency(fromModule, toModuleName)
|
||||||
|
).then(
|
||||||
|
cacheResult,
|
||||||
|
forgive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._resolveNodeDependency(fromModule, toModuleName)
|
||||||
|
.then(
|
||||||
|
cacheResult,
|
||||||
|
forgive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrderedDependencies(entryPath) {
|
||||||
|
return this.load().then(() => {
|
||||||
|
const absolutePath = path.resolve(this._getAbsolutePath(entryPath));
|
||||||
|
|
||||||
|
if (absolutePath == null) {
|
||||||
|
throw new NotFoundError(
|
||||||
|
'Cannot find entry file %s in any of the roots: %j',
|
||||||
|
entryPath,
|
||||||
|
this._opts.roots
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = this._moduleCache.getModule(absolutePath);
|
||||||
|
const deps = [];
|
||||||
|
const visited = Object.create(null);
|
||||||
|
visited[entry.hash()] = true;
|
||||||
|
|
||||||
|
const collect = (mod) => {
|
||||||
|
deps.push(mod);
|
||||||
|
return mod.getDependencies().then(
|
||||||
|
depNames => Promise.all(
|
||||||
|
depNames.map(name => this.resolveDependency(mod, name))
|
||||||
|
).then((dependencies) => [depNames, dependencies])
|
||||||
|
).then(([depNames, dependencies]) => {
|
||||||
|
let p = Promise.resolve();
|
||||||
|
dependencies.forEach((modDep, i) => {
|
||||||
|
if (modDep == null) {
|
||||||
|
debug(
|
||||||
|
'WARNING: Cannot find required module `%s` from module `%s`',
|
||||||
|
depNames[i],
|
||||||
|
mod.path
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = p.then(() => {
|
||||||
|
if (!visited[modDep.hash()]) {
|
||||||
|
visited[modDep.hash()] = true;
|
||||||
|
return collect(modDep);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return collect(entry)
|
||||||
|
.then(() => Promise.all(deps.map(dep => dep.getPlainObject())));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAbsolutePath(filePath) {
|
||||||
|
if (isAbsolutePath(filePath)) {
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this._opts.roots.length; i++) {
|
||||||
|
const root = this._opts.roots[i];
|
||||||
|
const absPath = path.join(root, filePath);
|
||||||
|
if (this._fastfs.fileExists(absPath)) {
|
||||||
|
return absPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_resolveHasteDependency(fromModule, toModuleName) {
|
||||||
|
toModuleName = normalizePath(toModuleName);
|
||||||
|
|
||||||
|
let p = fromModule.getPackage();
|
||||||
|
if (p) {
|
||||||
|
p = p.redirectRequire(toModuleName);
|
||||||
|
} else {
|
||||||
|
p = Promise.resolve(toModuleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.then((realModuleName) => {
|
||||||
|
let dep = this._hasteMap[realModuleName];
|
||||||
|
|
||||||
|
if (dep && dep.type === 'Module') {
|
||||||
|
return dep;
|
||||||
|
}
|
||||||
|
|
||||||
|
let packageName = realModuleName;
|
||||||
|
|
||||||
|
while (packageName && packageName !== '.') {
|
||||||
|
dep = this._hasteMap[packageName];
|
||||||
|
if (dep && dep.type === 'Package') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
packageName = path.dirname(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dep && dep.type === 'Package') {
|
||||||
|
const potentialModulePath = path.join(
|
||||||
|
dep.root,
|
||||||
|
path.relative(packageName, realModuleName)
|
||||||
|
);
|
||||||
|
return this._loadAsFile(potentialModulePath)
|
||||||
|
.catch(() => this._loadAsDir(potentialModulePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Unable to resolve dependency');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_redirectRequire(fromModule, modulePath) {
|
||||||
|
return Promise.resolve(fromModule.getPackage()).then(p => {
|
||||||
|
if (p) {
|
||||||
|
return p.redirectRequire(modulePath);
|
||||||
|
}
|
||||||
|
return modulePath;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_resolveNodeDependency(fromModule, toModuleName) {
|
||||||
|
if (toModuleName[0] === '.' || toModuleName[1] === '/') {
|
||||||
|
const potentialModulePath = isAbsolutePath(toModuleName) ?
|
||||||
|
toModuleName :
|
||||||
|
path.join(path.dirname(fromModule.path), toModuleName);
|
||||||
|
return this._redirectRequire(fromModule, potentialModulePath).then(
|
||||||
|
realModuleName => this._loadAsFile(realModuleName)
|
||||||
|
.catch(() => this._loadAsDir(realModuleName))
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this._redirectRequire(fromModule, toModuleName).then(
|
||||||
|
realModuleName => {
|
||||||
|
const searchQueue = [];
|
||||||
|
for (let currDir = path.dirname(fromModule.path);
|
||||||
|
currDir !== '/';
|
||||||
|
currDir = path.dirname(currDir)) {
|
||||||
|
searchQueue.push(
|
||||||
|
path.join(currDir, 'node_modules', realModuleName)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let p = Promise.reject(new Error('Node module not found'));
|
||||||
|
searchQueue.forEach(potentialModulePath => {
|
||||||
|
p = p.catch(
|
||||||
|
() => this._loadAsFile(potentialModulePath)
|
||||||
|
).catch(
|
||||||
|
() => this._loadAsDir(potentialModulePath)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return p;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_resolveAsset_DEPRECATED(fromModule, toModuleName) {
|
||||||
|
if (this._assetMap_DEPRECATED != null) {
|
||||||
|
const assetMatch = toModuleName.match(/^image!(.+)/);
|
||||||
|
// Process DEPRECATED global asset requires.
|
||||||
|
if (assetMatch && assetMatch[1]) {
|
||||||
|
if (!this._assetMap_DEPRECATED[assetMatch[1]]) {
|
||||||
|
debug('WARINING: Cannot find asset:', assetMatch[1]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this._assetMap_DEPRECATED[assetMatch[1]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isAssetFile(file) {
|
||||||
|
return this._opts.assetExts.indexOf(extname(file)) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadAsFile(potentialModulePath) {
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
if (this._isAssetFile(potentialModulePath)) {
|
||||||
|
const {name, type} = getAssetDataFromName(potentialModulePath);
|
||||||
|
const pattern = new RegExp('^' + name + '(@[\\d\\.]+x)?\\.' + type);
|
||||||
|
// We arbitrarly grab the first one, because scale selection
|
||||||
|
// will happen somewhere
|
||||||
|
const [assetFile] = this._fastfs.matches(
|
||||||
|
path.dirname(potentialModulePath),
|
||||||
|
pattern
|
||||||
|
);
|
||||||
|
|
||||||
|
if (assetFile) {
|
||||||
|
return this._moduleCache.getAssetModule(assetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let file;
|
||||||
|
if (this._fastfs.fileExists(potentialModulePath)) {
|
||||||
|
file = potentialModulePath;
|
||||||
|
} else if (this._fastfs.fileExists(potentialModulePath + '.js')) {
|
||||||
|
file = potentialModulePath + '.js';
|
||||||
|
} else if (this._fastfs.fileExists(potentialModulePath + '.json')) {
|
||||||
|
file = potentialModulePath + '.json';
|
||||||
|
} else {
|
||||||
|
throw new Error(`File ${potentialModulePath} doesnt exist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._moduleCache.getModule(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadAsDir(potentialDirPath) {
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
if (!this._fastfs.dirExists(potentialDirPath)) {
|
||||||
|
throw new Error(`Invalid directory ${potentialDirPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const packageJsonPath = path.join(potentialDirPath, 'package.json');
|
||||||
|
if (this._fastfs.fileExists(packageJsonPath)) {
|
||||||
|
return this._moduleCache.getPackage(packageJsonPath)
|
||||||
|
.getMain().then(
|
||||||
|
(main) => this._loadAsFile(main).catch(
|
||||||
|
() => this._loadAsDir(main)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._loadAsFile(path.join(potentialDirPath, 'index'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildHasteMap() {
|
||||||
|
let promises = this._fastfs.findFilesByExt('js', {
|
||||||
|
ignore: (file) => this._isNodeModulesDir(file)
|
||||||
|
}).map(file => this._processHasteModule(file));
|
||||||
|
|
||||||
|
promises = promises.concat(
|
||||||
|
this._fastfs.findFilesByName('package.json', {
|
||||||
|
ignore: (file) => this._isNodeModulesDir(file)
|
||||||
|
}).map(file => this._processHastePackage(file))
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
_processHasteModule(file) {
|
||||||
|
const module = this._moduleCache.getModule(file);
|
||||||
|
return module.isHaste().then(
|
||||||
|
isHaste => isHaste && module.getName()
|
||||||
|
.then(name => this._updateHasteMap(name, module))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_processHastePackage(file) {
|
||||||
|
file = path.resolve(file);
|
||||||
|
const p = this._moduleCache.getPackage(file, this._fastfs);
|
||||||
|
return p.isHaste()
|
||||||
|
.then(isHaste => isHaste && p.getName()
|
||||||
|
.then(name => this._updateHasteMap(name, p)))
|
||||||
|
.catch(e => {
|
||||||
|
if (e instanceof SyntaxError) {
|
||||||
|
// Malformed package.json.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateHasteMap(name, mod) {
|
||||||
|
if (this._hasteMap[name]) {
|
||||||
|
debug('WARNING: conflicting haste modules: ' + name);
|
||||||
|
if (mod.type === 'Package' &&
|
||||||
|
this._hasteMap[name].type === 'Module') {
|
||||||
|
// Modules takes precendence over packages.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._hasteMap[name] = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isNodeModulesDir(file) {
|
||||||
|
const inNodeModules = file.indexOf('/node_modules/') !== -1;
|
||||||
|
|
||||||
|
if (!inNodeModules) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirs = this._opts.providesModuleNodeModules;
|
||||||
|
|
||||||
|
for (let i = 0; i < dirs.length; i++) {
|
||||||
|
const index = file.indexOf(dirs[i]);
|
||||||
|
if (index !== -1) {
|
||||||
|
return file.slice(index).indexOf('/node_modules/') !== -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_processAsset_DEPRECATED(file) {
|
||||||
|
let ext = extname(file);
|
||||||
|
if (this._opts.assetExts.indexOf(ext) !== -1) {
|
||||||
|
let name = assetName(file, ext);
|
||||||
|
if (this._assetMap_DEPRECATED[name] != null) {
|
||||||
|
debug('Conflcting assets', name);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._assetMap_DEPRECATED[name] = new AssetModule_DEPRECATED(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildAssetMap_DEPRECATED() {
|
||||||
|
if (this._opts.assetRoots_DEPRECATED == null ||
|
||||||
|
this._opts.assetRoots_DEPRECATED.length === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._assetMap_DEPRECATED = Object.create(null);
|
||||||
|
|
||||||
|
const pattern = new RegExp(
|
||||||
|
'\.(' + this._opts.assetExts.join('|') + ')$'
|
||||||
|
);
|
||||||
|
|
||||||
|
const fastfs = new Fastfs(
|
||||||
|
this._opts.assetRoots_DEPRECATED,
|
||||||
|
this._opts.fileWatcher,
|
||||||
|
{ pattern, ignore: this._opts.ignoreFilePath }
|
||||||
|
);
|
||||||
|
|
||||||
|
fastfs.on('change', this._processAssetChange_DEPRECATED.bind(this));
|
||||||
|
|
||||||
|
return fastfs.build().then(
|
||||||
|
() => fastfs.findFilesByExts(this._opts.assetExts).map(
|
||||||
|
file => this._processAsset_DEPRECATED(file)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_processAssetChange_DEPRECATED(type, filePath, root, fstat) {
|
||||||
|
const name = assetName(filePath);
|
||||||
|
if (type === 'change' || type === 'delete') {
|
||||||
|
delete this._assetMap_DEPRECATED[name];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'change' || type === 'add') {
|
||||||
|
this._loading = this._loading.then(
|
||||||
|
() => this._processAsset_DEPRECATED(path.join(root, filePath))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_processFileChange(type, filePath, root, fstat) {
|
||||||
|
// It's really hard to invalidate the right module resolution cache
|
||||||
|
// so we just blow it up with every file change.
|
||||||
|
this._immediateResolutionCache = Object.create(null);
|
||||||
|
|
||||||
|
const absPath = path.join(root, filePath);
|
||||||
|
if ((fstat && fstat.isDirectory()) ||
|
||||||
|
this._opts.ignoreFilePath(absPath) ||
|
||||||
|
this._isNodeModulesDir(absPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'delete' || type === 'change') {
|
||||||
|
_.each(this._hasteMap, (mod, name) => {
|
||||||
|
if (mod.path === absPath) {
|
||||||
|
delete this._hasteMap[name];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (type === 'delete') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extname(absPath) === 'js' || extname(absPath) === 'json') {
|
||||||
|
this._loading = this._loading.then(() => {
|
||||||
|
if (path.basename(filePath) === 'package.json') {
|
||||||
|
return this._processHastePackage(absPath);
|
||||||
|
} else {
|
||||||
|
return this._processHasteModule(absPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function assetName(file, ext) {
|
||||||
|
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function extname(name) {
|
||||||
|
return path.extname(name).replace(/^\./, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolutionHash(modulePath, depName) {
|
||||||
|
return `${path.resolve(modulePath)}:${depName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function NotFoundError() {
|
||||||
|
Error.call(this);
|
||||||
|
Error.captureStackTrace(this, this.constructor);
|
||||||
|
var msg = util.format.apply(util, arguments);
|
||||||
|
this.message = msg;
|
||||||
|
this.type = this.name = 'NotFoundError';
|
||||||
|
this.status = 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePath(modulePath) {
|
||||||
|
if (path.sep === '/') {
|
||||||
|
modulePath = path.normalize(modulePath);
|
||||||
|
} else if (path.posix) {
|
||||||
|
modulePath = path.posix.normalize(modulePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return modulePath.replace(/\/$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(NotFoundError, Error);
|
||||||
|
|
||||||
|
module.exports = DependencyGraph;
|
|
@ -0,0 +1,133 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Promise = require('promise');
|
||||||
|
const docblock = require('./DependencyGraph/docblock');
|
||||||
|
const isAbsolutePath = require('absolute-path');
|
||||||
|
const path = require('path');
|
||||||
|
const replacePatterns = require('./replacePatterns');
|
||||||
|
|
||||||
|
class Module {
|
||||||
|
|
||||||
|
constructor(file, fastfs, moduleCache) {
|
||||||
|
if (!isAbsolutePath(file)) {
|
||||||
|
throw new Error('Expected file to be absolute path but got ' + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.path = path.resolve(file);
|
||||||
|
this.type = 'Module';
|
||||||
|
|
||||||
|
this._fastfs = fastfs;
|
||||||
|
this._moduleCache = moduleCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
isHaste() {
|
||||||
|
return this._read().then(data => !!data.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this._read().then(data => {
|
||||||
|
if (data.id) {
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = this.getPackage();
|
||||||
|
|
||||||
|
if (!p) {
|
||||||
|
// Name is full path
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.getName()
|
||||||
|
.then(name => {
|
||||||
|
if (!name) {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.join(name, path.relative(p.root, this.path));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getPackage() {
|
||||||
|
return this._moduleCache.getPackageForModule(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencies() {
|
||||||
|
return this._read().then(data => data.dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
_read() {
|
||||||
|
if (!this._reading) {
|
||||||
|
this._reading = this._fastfs.readFile(this.path).then(content => {
|
||||||
|
const data = {};
|
||||||
|
const moduleDocBlock = docblock.parseAsObject(content);
|
||||||
|
if (moduleDocBlock.providesModule || moduleDocBlock.provides) {
|
||||||
|
data.id = /^(\S*)/.exec(
|
||||||
|
moduleDocBlock.providesModule || moduleDocBlock.provides
|
||||||
|
)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore requires in generated code. An example of this is prebuilt
|
||||||
|
// files like the SourceMap library.
|
||||||
|
if ('extern' in moduleDocBlock) {
|
||||||
|
data.dependencies = [];
|
||||||
|
} else {
|
||||||
|
data.dependencies = extractRequires(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._reading;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlainObject() {
|
||||||
|
return Promise.all([
|
||||||
|
this.getName(),
|
||||||
|
this.getDependencies(),
|
||||||
|
]).then(([name, dependencies]) => this.addReference({
|
||||||
|
path: this.path,
|
||||||
|
isJSON: path.extname(this.path) === '.json',
|
||||||
|
isAsset: false,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
resolution: undefined,
|
||||||
|
id: name,
|
||||||
|
dependencies
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
hash() {
|
||||||
|
return `Module : ${this.path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
addReference(obj) {
|
||||||
|
Object.defineProperty(obj, '_ref', { value: this });
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract all required modules from a `code` string.
|
||||||
|
*/
|
||||||
|
var blockCommentRe = /\/\*(.|\n)*?\*\//g;
|
||||||
|
var lineCommentRe = /\/\/.+(\n|$)/g;
|
||||||
|
function extractRequires(code /*: string*/) /*: Array<string>*/ {
|
||||||
|
var deps = [];
|
||||||
|
|
||||||
|
code
|
||||||
|
.replace(blockCommentRe, '')
|
||||||
|
.replace(lineCommentRe, '')
|
||||||
|
.replace(replacePatterns.IMPORT_RE, (match, pre, quot, dep, post) => {
|
||||||
|
deps.push(dep);
|
||||||
|
return match;
|
||||||
|
})
|
||||||
|
.replace(replacePatterns.REQUIRE_RE, function(match, pre, quot, dep, post) {
|
||||||
|
deps.push(dep);
|
||||||
|
});
|
||||||
|
|
||||||
|
return deps;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Module;
|
|
@ -0,0 +1,72 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const AssetModule = require('./AssetModule');
|
||||||
|
const Package = require('./Package');
|
||||||
|
const Module = require('./Module');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
class ModuleCache {
|
||||||
|
|
||||||
|
constructor(fastfs) {
|
||||||
|
this._moduleCache = Object.create(null);
|
||||||
|
this._packageCache = Object.create(null);
|
||||||
|
this._fastfs = fastfs;
|
||||||
|
fastfs.on('change', this._processFileChange.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
getModule(filePath) {
|
||||||
|
filePath = path.resolve(filePath);
|
||||||
|
if (!this._moduleCache[filePath]) {
|
||||||
|
this._moduleCache[filePath] = new Module(filePath, this._fastfs, this);
|
||||||
|
}
|
||||||
|
return this._moduleCache[filePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
getAssetModule(filePath) {
|
||||||
|
filePath = path.resolve(filePath);
|
||||||
|
if (!this._moduleCache[filePath]) {
|
||||||
|
this._moduleCache[filePath] = new AssetModule(
|
||||||
|
filePath,
|
||||||
|
this._fastfs,
|
||||||
|
this
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this._moduleCache[filePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
getPackage(filePath) {
|
||||||
|
filePath = path.resolve(filePath);
|
||||||
|
if (!this._packageCache[filePath]){
|
||||||
|
this._packageCache[filePath] = new Package(filePath, this._fastfs);
|
||||||
|
}
|
||||||
|
return this._packageCache[filePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
getPackageForModule(module) {
|
||||||
|
// TODO(amasad): use ES6 Map.
|
||||||
|
if (module.__package) {
|
||||||
|
if (this._packageCache[module.__package]) {
|
||||||
|
return this._packageCache[module.__package];
|
||||||
|
} else {
|
||||||
|
delete module.__package;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const packagePath = this._fastfs.closest(module.path, 'package.json');
|
||||||
|
|
||||||
|
if (!packagePath) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.__package = packagePath;
|
||||||
|
return this.getPackage(packagePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
_processFileChange(type, filePath, root) {
|
||||||
|
const absPath = path.join(root, filePath);
|
||||||
|
delete this._moduleCache[absPath];
|
||||||
|
delete this._packageCache[absPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = ModuleCache;
|
|
@ -1,61 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function ModuleDescriptor(fields) {
|
|
||||||
if (!fields.id) {
|
|
||||||
throw new Error('Missing required fields id');
|
|
||||||
}
|
|
||||||
this.id = fields.id;
|
|
||||||
|
|
||||||
if (!fields.path) {
|
|
||||||
throw new Error('Missing required fields path');
|
|
||||||
}
|
|
||||||
this.path = fields.path;
|
|
||||||
|
|
||||||
if (!fields.dependencies) {
|
|
||||||
throw new Error('Missing required fields dependencies');
|
|
||||||
}
|
|
||||||
this.dependencies = fields.dependencies;
|
|
||||||
|
|
||||||
this.resolveDependency = fields.resolveDependency;
|
|
||||||
|
|
||||||
this.entry = fields.entry || false;
|
|
||||||
|
|
||||||
this.isPolyfill = fields.isPolyfill || false;
|
|
||||||
|
|
||||||
this.isAsset_DEPRECATED = fields.isAsset_DEPRECATED || false;
|
|
||||||
this.isAsset = fields.isAsset || false;
|
|
||||||
|
|
||||||
if (this.isAsset_DEPRECATED && this.isAsset) {
|
|
||||||
throw new Error('Cannot be an asset and a deprecated asset');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.resolution = fields.resolution;
|
|
||||||
|
|
||||||
if (this.isAsset && isNaN(this.resolution)) {
|
|
||||||
throw new Error('Expected resolution to be a number for asset modules');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.altId = fields.altId;
|
|
||||||
|
|
||||||
this.isJSON = fields.isJSON;
|
|
||||||
|
|
||||||
this._fields = fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
ModuleDescriptor.prototype.toJSON = function() {
|
|
||||||
return {
|
|
||||||
id: this.id,
|
|
||||||
path: this.path,
|
|
||||||
dependencies: this.dependencies
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = ModuleDescriptor;
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const isAbsolutePath = require('absolute-path');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
class Package {
|
||||||
|
|
||||||
|
constructor(file, fastfs) {
|
||||||
|
this.path = path.resolve(file);
|
||||||
|
this.root = path.dirname(this.path);
|
||||||
|
this._fastfs = fastfs;
|
||||||
|
this.type = 'Package';
|
||||||
|
}
|
||||||
|
|
||||||
|
getMain() {
|
||||||
|
return this._read().then(json => {
|
||||||
|
if (typeof json.browser === 'string') {
|
||||||
|
return path.join(this.root, json.browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
let main = json.main || 'index';
|
||||||
|
|
||||||
|
if (json.browser && typeof json.browser === 'object') {
|
||||||
|
main = json.browser[main] ||
|
||||||
|
json.browser[main + '.js'] ||
|
||||||
|
json.browser[main + '.json'] ||
|
||||||
|
json.browser[main.replace(/(\.js|\.json)$/, '')] ||
|
||||||
|
main;
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.join(this.root, main);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isHaste() {
|
||||||
|
return this._read().then(json => !!json.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
return this._read().then(json => json.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
redirectRequire(name) {
|
||||||
|
return this._read().then(json => {
|
||||||
|
const {browser} = json;
|
||||||
|
|
||||||
|
if (!browser || typeof browser !== 'object') {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name[0] !== '/') {
|
||||||
|
return browser[name] || name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAbsolutePath(name)) {
|
||||||
|
throw new Error(`Expected ${name} to be absolute path`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const relPath = './' + path.relative(this.root, name);
|
||||||
|
const redirect = browser[relPath] ||
|
||||||
|
browser[relPath + '.js'] ||
|
||||||
|
browser[relPath + '.json'];
|
||||||
|
if (redirect) {
|
||||||
|
return path.join(
|
||||||
|
this.root,
|
||||||
|
redirect
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_read() {
|
||||||
|
if (!this._reading) {
|
||||||
|
this._reading = this._fastfs.readFile(this.path)
|
||||||
|
.then(jsonStr => JSON.parse(jsonStr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._reading;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Package;
|
|
@ -10,16 +10,20 @@
|
||||||
|
|
||||||
jest.dontMock('../')
|
jest.dontMock('../')
|
||||||
.dontMock('q')
|
.dontMock('q')
|
||||||
.dontMock('../replacePatterns')
|
.dontMock('../replacePatterns');
|
||||||
.setMock('../../ModuleDescriptor', function(data) {return data;});
|
|
||||||
|
|
||||||
jest.mock('path');
|
jest.mock('path');
|
||||||
|
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
|
|
||||||
describe('HasteDependencyResolver', function() {
|
describe('HasteDependencyResolver', function() {
|
||||||
var HasteDependencyResolver;
|
var HasteDependencyResolver;
|
||||||
|
|
||||||
|
function createModule(o) {
|
||||||
|
o.getPlainObject = () => Promise.resolve(o);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
// For the polyfillDeps
|
// For the polyfillDeps
|
||||||
require('path').join.mockImpl(function(a, b) {
|
require('path').join.mockImpl(function(a, b) {
|
||||||
|
@ -30,7 +34,10 @@ describe('HasteDependencyResolver', function() {
|
||||||
|
|
||||||
describe('getDependencies', function() {
|
describe('getDependencies', function() {
|
||||||
pit('should get dependencies with polyfills', function() {
|
pit('should get dependencies with polyfills', function() {
|
||||||
var module = {id: 'index', path: '/root/index.js', dependencies: ['a']};
|
var module = createModule({
|
||||||
|
id: 'index',
|
||||||
|
path: '/root/index.js', dependencies: ['a']
|
||||||
|
});
|
||||||
var deps = [module];
|
var deps = [module];
|
||||||
|
|
||||||
var depResolver = new HasteDependencyResolver({
|
var depResolver = new HasteDependencyResolver({
|
||||||
|
@ -40,7 +47,7 @@ describe('HasteDependencyResolver', function() {
|
||||||
// Is there a better way? How can I mock the prototype instead?
|
// Is there a better way? How can I mock the prototype instead?
|
||||||
var depGraph = depResolver._depGraph;
|
var depGraph = depResolver._depGraph;
|
||||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||||
return deps;
|
return Promise.resolve(deps);
|
||||||
});
|
});
|
||||||
depGraph.load.mockImpl(function() {
|
depGraph.load.mockImpl(function() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
@ -113,7 +120,12 @@ describe('HasteDependencyResolver', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should get dependencies with polyfills', function() {
|
pit('should get dependencies with polyfills', function() {
|
||||||
var module = {id: 'index', path: '/root/index.js', dependencies: ['a']};
|
var module = createModule({
|
||||||
|
id: 'index',
|
||||||
|
path: '/root/index.js',
|
||||||
|
dependencies: ['a'],
|
||||||
|
});
|
||||||
|
|
||||||
var deps = [module];
|
var deps = [module];
|
||||||
|
|
||||||
var depResolver = new HasteDependencyResolver({
|
var depResolver = new HasteDependencyResolver({
|
||||||
|
@ -123,7 +135,7 @@ describe('HasteDependencyResolver', function() {
|
||||||
// Is there a better way? How can I mock the prototype instead?
|
// Is there a better way? How can I mock the prototype instead?
|
||||||
var depGraph = depResolver._depGraph;
|
var depGraph = depResolver._depGraph;
|
||||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||||
return deps;
|
return Promise.resolve(deps);
|
||||||
});
|
});
|
||||||
depGraph.load.mockImpl(function() {
|
depGraph.load.mockImpl(function() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
@ -196,7 +208,11 @@ describe('HasteDependencyResolver', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
pit('should pass in more polyfills', function() {
|
pit('should pass in more polyfills', function() {
|
||||||
var module = {id: 'index', path: '/root/index.js', dependencies: ['a']};
|
var module = createModule({
|
||||||
|
id: 'index',
|
||||||
|
path: '/root/index.js',
|
||||||
|
dependencies: ['a']
|
||||||
|
});
|
||||||
var deps = [module];
|
var deps = [module];
|
||||||
|
|
||||||
var depResolver = new HasteDependencyResolver({
|
var depResolver = new HasteDependencyResolver({
|
||||||
|
@ -207,7 +223,7 @@ describe('HasteDependencyResolver', function() {
|
||||||
// Is there a better way? How can I mock the prototype instead?
|
// Is there a better way? How can I mock the prototype instead?
|
||||||
var depGraph = depResolver._depGraph;
|
var depGraph = depResolver._depGraph;
|
||||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||||
return deps;
|
return Promise.resolve(deps);
|
||||||
});
|
});
|
||||||
depGraph.load.mockImpl(function() {
|
depGraph.load.mockImpl(function() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
@ -294,7 +310,7 @@ describe('HasteDependencyResolver', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('wrapModule', function() {
|
describe('wrapModule', function() {
|
||||||
it('should resolve modules', function() {
|
pit('should resolve modules', function() {
|
||||||
var depResolver = new HasteDependencyResolver({
|
var depResolver = new HasteDependencyResolver({
|
||||||
projectRoot: '/root',
|
projectRoot: '/root',
|
||||||
});
|
});
|
||||||
|
@ -446,20 +462,21 @@ describe('HasteDependencyResolver', function() {
|
||||||
|
|
||||||
depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) {
|
depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) {
|
||||||
if (toModuleName === 'x') {
|
if (toModuleName === 'x') {
|
||||||
return {
|
return Promise.resolve(createModule({
|
||||||
id: 'changed'
|
id: 'changed'
|
||||||
};
|
}));
|
||||||
} else if (toModuleName === 'y') {
|
} else if (toModuleName === 'y') {
|
||||||
return { id: 'Y' };
|
return Promise.resolve(createModule({ id: 'Y' }));
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return Promise.resolve(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
var processedCode = depResolver.wrapModule({
|
return depResolver.wrapModule({
|
||||||
id: 'test module',
|
id: 'test module',
|
||||||
path: '/root/test.js',
|
path: '/root/test.js',
|
||||||
dependencies: dependencies
|
dependencies: dependencies
|
||||||
}, code);
|
}, code).then(processedCode => {
|
||||||
|
|
||||||
expect(processedCode).toEqual([
|
expect(processedCode).toEqual([
|
||||||
'__d(\'test module\',["changed","Y"],function(global,' +
|
'__d(\'test module\',["changed","Y"],function(global,' +
|
||||||
|
@ -600,8 +617,10 @@ describe('HasteDependencyResolver', function() {
|
||||||
'require("Y")',
|
'require("Y")',
|
||||||
'require( \'z\' )',
|
'require( \'z\' )',
|
||||||
'require( "a")',
|
'require( "a")',
|
||||||
'require("b" )});',
|
'require("b" )',
|
||||||
|
'});',
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
|
@ -0,0 +1,310 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const Promise = require('promise');
|
||||||
|
const {EventEmitter} = require('events');
|
||||||
|
|
||||||
|
const _ = require('underscore');
|
||||||
|
const debug = require('debug')('DependencyGraph');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const readDir = Promise.denodeify(fs.readdir);
|
||||||
|
const readFile = Promise.denodeify(fs.readFile);
|
||||||
|
const stat = Promise.denodeify(fs.stat);
|
||||||
|
const hasOwn = Object.prototype.hasOwnProperty;
|
||||||
|
|
||||||
|
class Fastfs extends EventEmitter {
|
||||||
|
constructor(roots, fileWatcher, {ignore, pattern}) {
|
||||||
|
super();
|
||||||
|
this._fileWatcher = fileWatcher;
|
||||||
|
this._ignore = ignore;
|
||||||
|
this._pattern = pattern;
|
||||||
|
this._roots = roots.map(root => new File(root, { isDir: true }));
|
||||||
|
this._fastPaths = Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
const queue = this._roots.slice();
|
||||||
|
return this._search(queue).then(() => {
|
||||||
|
this._fileWatcher.on('all', this._processFileChange.bind(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stat(filePath) {
|
||||||
|
return Promise.resolve().then(() => {
|
||||||
|
const file = this._getFile(filePath);
|
||||||
|
return file.stat();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllFiles() {
|
||||||
|
return _.chain(this._roots)
|
||||||
|
.map(root => root.getFiles())
|
||||||
|
.flatten()
|
||||||
|
.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
findFilesByExt(ext, { ignore }) {
|
||||||
|
return this.getAllFiles()
|
||||||
|
.filter(
|
||||||
|
file => file.ext() === ext && (!ignore || !ignore(file.path))
|
||||||
|
)
|
||||||
|
.map(file => file.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
findFilesByExts(exts) {
|
||||||
|
return this.getAllFiles()
|
||||||
|
.filter(file => exts.indexOf(file.ext()) !== -1)
|
||||||
|
.map(file => file.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
findFilesByName(name, { ignore }) {
|
||||||
|
return this.getAllFiles()
|
||||||
|
.filter(
|
||||||
|
file => path.basename(file.path) === name &&
|
||||||
|
(!ignore || !ignore(file.path))
|
||||||
|
)
|
||||||
|
.map(file => file.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
readFile(filePath) {
|
||||||
|
return this._getFile(filePath).read();
|
||||||
|
}
|
||||||
|
|
||||||
|
closest(filePath, name) {
|
||||||
|
for (let file = this._getFile(filePath).parent;
|
||||||
|
file;
|
||||||
|
file = file.parent) {
|
||||||
|
if (file.children[name]) {
|
||||||
|
return file.children[name].path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileExists(filePath) {
|
||||||
|
const file = this._getFile(filePath);
|
||||||
|
return file && !file.isDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
dirExists(filePath) {
|
||||||
|
const file = this._getFile(filePath);
|
||||||
|
return file && file.isDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
matches(dir, pattern) {
|
||||||
|
let dirFile = this._getFile(dir);
|
||||||
|
if (!dirFile.isDir) {
|
||||||
|
throw new Error(`Expected file ${dirFile.path} to be a directory`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(dirFile.children)
|
||||||
|
.filter(name => name.match(pattern))
|
||||||
|
.map(name => path.join(dirFile.path, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
_getRoot(filePath) {
|
||||||
|
for (let i = 0; i < this._roots.length; i++) {
|
||||||
|
let possibleRoot = this._roots[i];
|
||||||
|
if (isDescendant(possibleRoot.path, filePath)) {
|
||||||
|
return possibleRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAndAssertRoot(filePath) {
|
||||||
|
const root = this._getRoot(filePath);
|
||||||
|
if (!root) {
|
||||||
|
throw new Error(`File ${filePath} not found in any of the roots`);
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getFile(filePath) {
|
||||||
|
filePath = path.normalize(filePath);
|
||||||
|
if (!hasOwn.call(this._fastPaths, filePath)) {
|
||||||
|
this._fastPaths[filePath] = this._getAndAssertRoot(filePath).getFileFromPath(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._fastPaths[filePath];
|
||||||
|
}
|
||||||
|
|
||||||
|
_add(file) {
|
||||||
|
this._getAndAssertRoot(file.path).addChild(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
_search(queue) {
|
||||||
|
const dir = queue.shift();
|
||||||
|
if (!dir) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return readAndStatDir(dir.path).then(([filePaths, stats]) => {
|
||||||
|
filePaths.forEach((filePath, i) => {
|
||||||
|
if (this._ignore(filePath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats[i].isDirectory()) {
|
||||||
|
queue.push(
|
||||||
|
new File(filePath, { isDir: true, fstat: stats[i] })
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filePath.match(this._pattern)) {
|
||||||
|
this._add(new File(filePath, { fstat: stats[i] }));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this._search(queue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_processFileChange(type, filePath, root, fstat) {
|
||||||
|
const absPath = path.join(root, filePath);
|
||||||
|
if (this._ignore(absPath) || (fstat && fstat.isDirectory())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure this event belongs to one of our roots.
|
||||||
|
if (!this._getRoot(absPath)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'delete' || type === 'change') {
|
||||||
|
const file = this._getFile(absPath);
|
||||||
|
if (file) {
|
||||||
|
file.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this._fastPaths[path.normalize(absPath)];
|
||||||
|
|
||||||
|
if (type !== 'delete') {
|
||||||
|
this._add(new File(absPath, {
|
||||||
|
isDir: false,
|
||||||
|
fstat
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('change', type, filePath, root, fstat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class File {
|
||||||
|
constructor(filePath, {isDir, fstat}) {
|
||||||
|
this.path = filePath;
|
||||||
|
this.isDir = Boolean(isDir);
|
||||||
|
if (this.isDir) {
|
||||||
|
this.children = Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fstat) {
|
||||||
|
this._stat = Promise.resolve(fstat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
read() {
|
||||||
|
if (!this._read) {
|
||||||
|
this._read = readFile(this.path, 'utf8');
|
||||||
|
}
|
||||||
|
return this._read;
|
||||||
|
}
|
||||||
|
|
||||||
|
stat() {
|
||||||
|
if (!this._stat) {
|
||||||
|
this._stat = stat(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
addChild(file) {
|
||||||
|
const parts = path.relative(this.path, file.path).split(path.sep);
|
||||||
|
|
||||||
|
if (parts.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts.length === 1) {
|
||||||
|
this.children[parts[0]] = file;
|
||||||
|
file.parent = this;
|
||||||
|
} else if (this.children[parts[0]]) {
|
||||||
|
this.children[parts[0]].addChild(file);
|
||||||
|
} else {
|
||||||
|
const dir = new File(path.join(this.path, parts[0]), { isDir: true });
|
||||||
|
dir.parent = this;
|
||||||
|
this.children[parts[0]] = dir;
|
||||||
|
dir.addChild(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFileFromPath(filePath) {
|
||||||
|
const parts = path.relative(this.path, filePath)
|
||||||
|
.split(path.sep);
|
||||||
|
|
||||||
|
/*eslint consistent-this:0*/
|
||||||
|
let file = this;
|
||||||
|
for (let i = 0; i < parts.length; i++) {
|
||||||
|
let fileName = parts[i];
|
||||||
|
if (!fileName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file || !file.isDir) {
|
||||||
|
// File not found.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = file.children[fileName];
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFiles() {
|
||||||
|
return _.flatten(_.values(this.children).map(file => {
|
||||||
|
if (file.isDir) {
|
||||||
|
return file.getFiles();
|
||||||
|
} else {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
ext() {
|
||||||
|
return path.extname(this.path).replace(/^\./, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
if (!this.parent) {
|
||||||
|
throw new Error(`No parent to delete ${this.path} from`);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete this.parent.children[path.basename(this.path)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDescendant(root, child) {
|
||||||
|
return path.relative(root, child).indexOf('..') !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readAndStatDir(dir) {
|
||||||
|
return readDir(dir)
|
||||||
|
.then(files => Promise.all(files.map(f => path.join(dir, f))))
|
||||||
|
.then(files => Promise.all(
|
||||||
|
files.map(f => stat(f).catch(handleBrokenLink))
|
||||||
|
).then(stats => [
|
||||||
|
// Remove broken links.
|
||||||
|
files.filter((file, i ) => !!stats[i]),
|
||||||
|
stats.filter(Boolean),
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBrokenLink(e) {
|
||||||
|
debug('WARNING: error stating, possibly broken symlink', e.message);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Fastfs;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,798 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var ModuleDescriptor = require('../../ModuleDescriptor');
|
|
||||||
var Promise = require('bluebird');
|
|
||||||
var fs = require('fs');
|
|
||||||
var docblock = require('./docblock');
|
|
||||||
var replacePatterns = require('../replacePatterns');
|
|
||||||
var path = require('path');
|
|
||||||
var isAbsolutePath = require('absolute-path');
|
|
||||||
var debug = require('debug')('DependecyGraph');
|
|
||||||
var util = require('util');
|
|
||||||
var declareOpts = require('../../../lib/declareOpts');
|
|
||||||
var getAssetDataFromName = require('../../../lib/getAssetDataFromName');
|
|
||||||
|
|
||||||
var readFile = Promise.promisify(fs.readFile);
|
|
||||||
var readDir = Promise.promisify(fs.readdir);
|
|
||||||
var lstat = Promise.promisify(fs.lstat);
|
|
||||||
var realpath = Promise.promisify(fs.realpath);
|
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
|
||||||
roots: {
|
|
||||||
type: 'array',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
ignoreFilePath: {
|
|
||||||
type: 'function',
|
|
||||||
default: function(){}
|
|
||||||
},
|
|
||||||
fileWatcher: {
|
|
||||||
type: 'object',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
assetRoots_DEPRECATED: {
|
|
||||||
type: 'array',
|
|
||||||
default: [],
|
|
||||||
},
|
|
||||||
assetExts: {
|
|
||||||
type: 'array',
|
|
||||||
required: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function DependecyGraph(options) {
|
|
||||||
var opts = validateOpts(options);
|
|
||||||
|
|
||||||
this._roots = opts.roots;
|
|
||||||
this._assetRoots_DEPRECATED = opts.assetRoots_DEPRECATED;
|
|
||||||
this._assetExts = opts.assetExts;
|
|
||||||
this._ignoreFilePath = opts.ignoreFilePath;
|
|
||||||
this._fileWatcher = options.fileWatcher;
|
|
||||||
|
|
||||||
this._loaded = false;
|
|
||||||
this._queue = this._roots.slice();
|
|
||||||
this._graph = Object.create(null);
|
|
||||||
this._packageByRoot = Object.create(null);
|
|
||||||
this._packagesById = Object.create(null);
|
|
||||||
this._moduleById = Object.create(null);
|
|
||||||
this._debugUpdateEvents = [];
|
|
||||||
|
|
||||||
this._moduleExtPattern = new RegExp(
|
|
||||||
'\.(' + ['js', 'json'].concat(this._assetExts).join('|') + ')$'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Kick off the search process to precompute the dependency graph.
|
|
||||||
this._init();
|
|
||||||
}
|
|
||||||
|
|
||||||
DependecyGraph.prototype.load = function() {
|
|
||||||
if (this._loading != null) {
|
|
||||||
return this._loading;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._loading = Promise.all([
|
|
||||||
this._search(),
|
|
||||||
this._buildAssetMap_DEPRECATED(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return this._loading;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an entry file return an array of all the dependent module descriptors.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype.getOrderedDependencies = function(entryPath) {
|
|
||||||
var absolutePath = this._getAbsolutePath(entryPath);
|
|
||||||
if (absolutePath == null) {
|
|
||||||
throw new NotFoundError(
|
|
||||||
'Cannot find entry file %s in any of the roots: %j',
|
|
||||||
entryPath,
|
|
||||||
this._roots
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var module = this._graph[absolutePath];
|
|
||||||
if (module == null) {
|
|
||||||
throw new Error('Module with path "' + entryPath + '" is not in graph');
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var deps = [];
|
|
||||||
var visited = Object.create(null);
|
|
||||||
|
|
||||||
// Node haste sucks. Id's aren't unique. So to make sure our entry point
|
|
||||||
// is the thing that ends up in our dependency list.
|
|
||||||
var graphMap = Object.create(this._moduleById);
|
|
||||||
graphMap[module.id] = module;
|
|
||||||
|
|
||||||
// Recursively collect the dependency list.
|
|
||||||
function collect(module) {
|
|
||||||
deps.push(module);
|
|
||||||
|
|
||||||
module.dependencies.forEach(function(name) {
|
|
||||||
var id = sansExtJs(name);
|
|
||||||
var dep = self.resolveDependency(module, id);
|
|
||||||
|
|
||||||
if (dep == null) {
|
|
||||||
debug(
|
|
||||||
'WARNING: Cannot find required module `%s` from module `%s`.',
|
|
||||||
name,
|
|
||||||
module.id
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!visited[dep.id]) {
|
|
||||||
visited[dep.id] = true;
|
|
||||||
collect(dep);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
visited[module.id] = true;
|
|
||||||
collect(module);
|
|
||||||
|
|
||||||
return deps;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a module descriptor `fromModule` return the module descriptor for
|
|
||||||
* the required module `depModuleId`. It could be top-level or relative,
|
|
||||||
* or both.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype.resolveDependency = function(
|
|
||||||
fromModule,
|
|
||||||
depModuleId
|
|
||||||
) {
|
|
||||||
if (this._assetMap_DEPRECATED != null) {
|
|
||||||
var assetMatch = depModuleId.match(/^image!(.+)/);
|
|
||||||
// Process DEPRECATED global asset requires.
|
|
||||||
if (assetMatch && assetMatch[1]) {
|
|
||||||
if (!this._assetMap_DEPRECATED[assetMatch[1]]) {
|
|
||||||
debug('WARINING: Cannot find asset:', assetMatch[1]);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this._assetMap_DEPRECATED[assetMatch[1]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var packageJson, modulePath, dep;
|
|
||||||
|
|
||||||
// Package relative modules starts with '.' or '..'.
|
|
||||||
if (depModuleId[0] !== '.') {
|
|
||||||
|
|
||||||
// Check if we need to map the dependency to something else via the
|
|
||||||
// `browser` field in package.json
|
|
||||||
var fromPackageJson = this._lookupPackage(fromModule.path);
|
|
||||||
if (fromPackageJson && fromPackageJson.browser &&
|
|
||||||
fromPackageJson.browser[depModuleId]) {
|
|
||||||
depModuleId = fromPackageJson.browser[depModuleId];
|
|
||||||
}
|
|
||||||
|
|
||||||
// `depModuleId` is simply a top-level `providesModule`.
|
|
||||||
// `depModuleId` is a package module but given the full path from the
|
|
||||||
// package, i.e. package_name/module_name
|
|
||||||
if (this._moduleById[sansExtJs(depModuleId)]) {
|
|
||||||
return this._moduleById[sansExtJs(depModuleId)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// `depModuleId` is a package and it's depending on the "main" resolution.
|
|
||||||
packageJson = this._packagesById[depModuleId];
|
|
||||||
|
|
||||||
// We are being forgiving here and raising an error because we could be
|
|
||||||
// processing a file that uses it's own require system.
|
|
||||||
if (packageJson == null) {
|
|
||||||
debug(
|
|
||||||
'WARNING: Cannot find required module `%s` from module `%s`.',
|
|
||||||
depModuleId,
|
|
||||||
fromModule.id
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var main;
|
|
||||||
|
|
||||||
// We prioritize the `browser` field if it's a module path.
|
|
||||||
if (typeof packageJson.browser === 'string') {
|
|
||||||
main = packageJson.browser;
|
|
||||||
} else {
|
|
||||||
main = packageJson.main || 'index';
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is a mapping for main in the `browser` field.
|
|
||||||
if (packageJson.browser && typeof packageJson.browser === 'object') {
|
|
||||||
var tmpMain = packageJson.browser[main] ||
|
|
||||||
packageJson.browser[withExtJs(main)] ||
|
|
||||||
packageJson.browser[sansExtJs(main)];
|
|
||||||
if (tmpMain) {
|
|
||||||
main = tmpMain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modulePath = withExtJs(path.join(packageJson._root, main));
|
|
||||||
dep = this._graph[modulePath];
|
|
||||||
|
|
||||||
// Some packages use just a dir and rely on an index.js inside that dir.
|
|
||||||
if (dep == null) {
|
|
||||||
dep = this._graph[path.join(packageJson._root, main, 'index.js')];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dep == null) {
|
|
||||||
throw new Error(
|
|
||||||
'Cannot find package main file for package: ' + packageJson._root
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return dep;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// `depModuleId` is a module defined in a package relative to `fromModule`.
|
|
||||||
packageJson = this._lookupPackage(fromModule.path);
|
|
||||||
|
|
||||||
if (packageJson == null) {
|
|
||||||
throw new Error(
|
|
||||||
'Expected relative module lookup from ' + fromModule.id + ' to ' +
|
|
||||||
depModuleId + ' to be within a package but no package.json found.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Example: depModuleId: ../a/b
|
|
||||||
// fromModule.path: /x/y/z
|
|
||||||
// modulePath: /x/y/a/b
|
|
||||||
var dir = path.dirname(fromModule.path);
|
|
||||||
modulePath = path.join(dir, depModuleId);
|
|
||||||
|
|
||||||
if (packageJson.browser && typeof packageJson.browser === 'object') {
|
|
||||||
var relPath = './' + path.relative(packageJson._root, modulePath);
|
|
||||||
var tmpModulePath = packageJson.browser[withExtJs(relPath)] ||
|
|
||||||
packageJson.browser[sansExtJs(relPath)];
|
|
||||||
if (tmpModulePath) {
|
|
||||||
modulePath = path.join(packageJson._root, tmpModulePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// JS modules can be required without extensios.
|
|
||||||
if (!this._isFileAsset(modulePath) && !modulePath.match(/\.json$/)) {
|
|
||||||
modulePath = withExtJs(modulePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
dep = this._graph[modulePath];
|
|
||||||
|
|
||||||
// Maybe the dependency is a directory and there is an index.js inside it.
|
|
||||||
if (dep == null) {
|
|
||||||
dep = this._graph[path.join(dir, depModuleId, 'index.js')];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maybe it's an asset with @n.nx resolution and the path doesn't map
|
|
||||||
// to the id
|
|
||||||
if (dep == null && this._isFileAsset(modulePath)) {
|
|
||||||
dep = this._moduleById[this._lookupName(modulePath)];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dep == null) {
|
|
||||||
debug(
|
|
||||||
'WARNING: Cannot find required module `%s` from module `%s`.' +
|
|
||||||
' Inferred required module path is %s',
|
|
||||||
depModuleId,
|
|
||||||
fromModule.id,
|
|
||||||
modulePath
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dep;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Intiates the filewatcher and kicks off the search process.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._init = function() {
|
|
||||||
var processChange = this._processFileChange.bind(this);
|
|
||||||
var watcher = this._fileWatcher;
|
|
||||||
|
|
||||||
this._loading = this.load().then(function() {
|
|
||||||
watcher.on('all', processChange);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements a DFS over the file system looking for modules and packages.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._search = function() {
|
|
||||||
var self = this;
|
|
||||||
var dir = this._queue.shift();
|
|
||||||
|
|
||||||
if (dir == null) {
|
|
||||||
return Promise.resolve(this._graph);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Steps:
|
|
||||||
// 1. Read a dir and stat all the entries.
|
|
||||||
// 2. Filter the files and queue up the directories.
|
|
||||||
// 3. Process any package.json in the files
|
|
||||||
// 4. recur.
|
|
||||||
return readAndStatDir(dir)
|
|
||||||
.spread(function(files, stats) {
|
|
||||||
var modulePaths = files.filter(function(filePath, i) {
|
|
||||||
if (self._ignoreFilePath(filePath)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stats[i].isDirectory()) {
|
|
||||||
self._queue.push(filePath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stats[i].isSymbolicLink()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return filePath.match(self._moduleExtPattern);
|
|
||||||
});
|
|
||||||
|
|
||||||
var processing = self._findAndProcessPackage(files, dir)
|
|
||||||
.then(function() {
|
|
||||||
return Promise.all(modulePaths.map(self._processModule.bind(self)));
|
|
||||||
});
|
|
||||||
|
|
||||||
return Promise.all([
|
|
||||||
processing,
|
|
||||||
self._search()
|
|
||||||
]);
|
|
||||||
})
|
|
||||||
.then(function() {
|
|
||||||
return self;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a list of files find a `package.json` file, and if found parse it
|
|
||||||
* and update indices.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._findAndProcessPackage = function(files, root) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var packagePath;
|
|
||||||
for (var i = 0; i < files.length ; i++) {
|
|
||||||
var file = files[i];
|
|
||||||
if (path.basename(file) === 'package.json') {
|
|
||||||
packagePath = file;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packagePath != null) {
|
|
||||||
return this._processPackage(packagePath);
|
|
||||||
} else {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._processPackage = function(packagePath) {
|
|
||||||
var packageRoot = path.dirname(packagePath);
|
|
||||||
var self = this;
|
|
||||||
return readFile(packagePath, 'utf8')
|
|
||||||
.then(function(content) {
|
|
||||||
var packageJson;
|
|
||||||
try {
|
|
||||||
packageJson = JSON.parse(content);
|
|
||||||
} catch (e) {
|
|
||||||
debug('WARNING: malformed package.json: ', packagePath);
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageJson.name == null) {
|
|
||||||
debug(
|
|
||||||
'WARNING: package.json `%s` is missing a name field',
|
|
||||||
packagePath
|
|
||||||
);
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
packageJson._root = packageRoot;
|
|
||||||
self._addPackageToIndices(packageJson);
|
|
||||||
|
|
||||||
return packageJson;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._addPackageToIndices = function(packageJson) {
|
|
||||||
this._packageByRoot[packageJson._root] = packageJson;
|
|
||||||
this._packagesById[packageJson.name] = packageJson;
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._removePackageFromIndices = function(packageJson) {
|
|
||||||
delete this._packageByRoot[packageJson._root];
|
|
||||||
delete this._packagesById[packageJson.name];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse a module and update indices.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._processModule = function(modulePath) {
|
|
||||||
var moduleData = { path: path.resolve(modulePath) };
|
|
||||||
var module;
|
|
||||||
|
|
||||||
if (this._assetExts.indexOf(extname(modulePath)) > -1) {
|
|
||||||
var assetData = getAssetDataFromName(this._lookupName(modulePath));
|
|
||||||
moduleData.id = assetData.assetName;
|
|
||||||
moduleData.resolution = assetData.resolution;
|
|
||||||
moduleData.isAsset = true;
|
|
||||||
moduleData.dependencies = [];
|
|
||||||
module = new ModuleDescriptor(moduleData);
|
|
||||||
this._updateGraphWithModule(module);
|
|
||||||
return Promise.resolve(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extname(modulePath) === 'json') {
|
|
||||||
moduleData.id = this._lookupName(modulePath);
|
|
||||||
moduleData.isJSON = true;
|
|
||||||
moduleData.dependencies = [];
|
|
||||||
module = new ModuleDescriptor(moduleData);
|
|
||||||
this._updateGraphWithModule(module);
|
|
||||||
return Promise.resolve(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
return readFile(modulePath, 'utf8')
|
|
||||||
.then(function(content) {
|
|
||||||
var moduleDocBlock = docblock.parseAsObject(content);
|
|
||||||
if (moduleDocBlock.providesModule || moduleDocBlock.provides) {
|
|
||||||
moduleData.id = /^(\S*)/.exec(
|
|
||||||
moduleDocBlock.providesModule || moduleDocBlock.provides
|
|
||||||
)[1];
|
|
||||||
|
|
||||||
// Incase someone wants to require this module via
|
|
||||||
// packageName/path/to/module
|
|
||||||
moduleData.altId = self._lookupName(modulePath);
|
|
||||||
} else {
|
|
||||||
moduleData.id = self._lookupName(modulePath);
|
|
||||||
}
|
|
||||||
moduleData.dependencies = extractRequires(content);
|
|
||||||
|
|
||||||
module = new ModuleDescriptor(moduleData);
|
|
||||||
self._updateGraphWithModule(module);
|
|
||||||
return module;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the name of module relative to a package it may belong to.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._lookupName = function(modulePath) {
|
|
||||||
var packageJson = this._lookupPackage(modulePath);
|
|
||||||
if (packageJson == null) {
|
|
||||||
return path.resolve(modulePath);
|
|
||||||
} else {
|
|
||||||
var relativePath =
|
|
||||||
sansExtJs(path.relative(packageJson._root, modulePath));
|
|
||||||
return path.join(packageJson.name, relativePath);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._deleteModule = function(module) {
|
|
||||||
delete this._graph[module.path];
|
|
||||||
|
|
||||||
// Others may keep a reference so we mark it as deleted.
|
|
||||||
module.deleted = true;
|
|
||||||
|
|
||||||
// Haste allows different module to have the same id.
|
|
||||||
if (this._moduleById[module.id] === module) {
|
|
||||||
delete this._moduleById[module.id];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (module.altId && this._moduleById[module.altId] === module) {
|
|
||||||
delete this._moduleById[module.altId];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the graph and indices with the module.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._updateGraphWithModule = function(module) {
|
|
||||||
if (this._graph[module.path]) {
|
|
||||||
this._deleteModule(this._graph[module.path]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._graph[module.path] = module;
|
|
||||||
|
|
||||||
if (this._moduleById[module.id]) {
|
|
||||||
debug(
|
|
||||||
'WARNING: Top-level module name conflict `%s`.\n' +
|
|
||||||
'module with path `%s` will replace `%s`',
|
|
||||||
module.id,
|
|
||||||
module.path,
|
|
||||||
this._moduleById[module.id].path
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._moduleById[module.id] = module;
|
|
||||||
|
|
||||||
// Some module maybe refrenced by both @providesModule and
|
|
||||||
// require(package/moduleName).
|
|
||||||
if (module.altId != null && this._moduleById[module.altId] == null) {
|
|
||||||
this._moduleById[module.altId] = module;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the nearest package to a module.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._lookupPackage = function(modulePath) {
|
|
||||||
var packageByRoot = this._packageByRoot;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Auxiliary function to recursively lookup a package.
|
|
||||||
*/
|
|
||||||
function lookupPackage(currDir) {
|
|
||||||
// ideally we stop once we're outside root and this can be a simple child
|
|
||||||
// dir check. However, we have to support modules that was symlinked inside
|
|
||||||
// our project root.
|
|
||||||
if (currDir === '/') {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
var packageJson = packageByRoot[currDir];
|
|
||||||
if (packageJson) {
|
|
||||||
return packageJson;
|
|
||||||
} else {
|
|
||||||
return lookupPackage(path.dirname(currDir));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lookupPackage(path.dirname(modulePath));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a filewatcher change event.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._processFileChange = function(
|
|
||||||
eventType,
|
|
||||||
filePath,
|
|
||||||
root,
|
|
||||||
stat
|
|
||||||
) {
|
|
||||||
var absPath = path.join(root, filePath);
|
|
||||||
if (this._ignoreFilePath(absPath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._debugUpdateEvents.push({event: eventType, path: filePath});
|
|
||||||
|
|
||||||
if (this._assetExts.indexOf(extname(filePath)) > -1) {
|
|
||||||
this._processAssetChange_DEPRECATED(eventType, absPath);
|
|
||||||
// Fall through because new-style assets are actually modules.
|
|
||||||
}
|
|
||||||
|
|
||||||
var isPackage = path.basename(filePath) === 'package.json';
|
|
||||||
if (eventType === 'delete') {
|
|
||||||
if (isPackage) {
|
|
||||||
var packageJson = this._packageByRoot[path.dirname(absPath)];
|
|
||||||
if (packageJson) {
|
|
||||||
this._removePackageFromIndices(packageJson);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var module = this._graph[absPath];
|
|
||||||
if (module == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._deleteModule(module);
|
|
||||||
}
|
|
||||||
} else if (!(stat && stat.isDirectory())) {
|
|
||||||
var self = this;
|
|
||||||
this._loading = this._loading.then(function() {
|
|
||||||
if (isPackage) {
|
|
||||||
return self._processPackage(absPath);
|
|
||||||
}
|
|
||||||
return self._processModule(absPath);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype.getDebugInfo = function() {
|
|
||||||
return '<h1>FileWatcher Update Events</h1>' +
|
|
||||||
'<pre>' + util.inspect(this._debugUpdateEvents) + '</pre>' +
|
|
||||||
'<h1> Graph dump </h1>' +
|
|
||||||
'<pre>' + util.inspect(this._graph) + '</pre>';
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches all roots for the file and returns the first one that has file of
|
|
||||||
* the same path.
|
|
||||||
*/
|
|
||||||
DependecyGraph.prototype._getAbsolutePath = function(filePath) {
|
|
||||||
if (isAbsolutePath(filePath)) {
|
|
||||||
return filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < this._roots.length; i++) {
|
|
||||||
var root = this._roots[i];
|
|
||||||
var absPath = path.join(root, filePath);
|
|
||||||
if (this._graph[absPath]) {
|
|
||||||
return absPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._buildAssetMap_DEPRECATED = function() {
|
|
||||||
if (this._assetRoots_DEPRECATED == null ||
|
|
||||||
this._assetRoots_DEPRECATED.length === 0) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._assetMap_DEPRECATED = Object.create(null);
|
|
||||||
return buildAssetMap_DEPRECATED(
|
|
||||||
this._assetRoots_DEPRECATED,
|
|
||||||
this._processAsset_DEPRECATED.bind(this)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._processAsset_DEPRECATED = function(file) {
|
|
||||||
var ext = extname(file);
|
|
||||||
if (this._assetExts.indexOf(ext) !== -1) {
|
|
||||||
var name = assetName(file, ext);
|
|
||||||
if (this._assetMap_DEPRECATED[name] != null) {
|
|
||||||
debug('Conflcting assets', name);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._assetMap_DEPRECATED[name] = new ModuleDescriptor({
|
|
||||||
id: 'image!' + name,
|
|
||||||
path: path.resolve(file),
|
|
||||||
isAsset_DEPRECATED: true,
|
|
||||||
dependencies: [],
|
|
||||||
resolution: getAssetDataFromName(file).resolution,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._processAssetChange_DEPRECATED = function(eventType, file) {
|
|
||||||
if (this._assetMap_DEPRECATED == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = assetName(file, extname(file));
|
|
||||||
if (eventType === 'change' || eventType === 'delete') {
|
|
||||||
delete this._assetMap_DEPRECATED[name];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === 'change' || eventType === 'add') {
|
|
||||||
this._processAsset_DEPRECATED(file);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
DependecyGraph.prototype._isFileAsset = function(file) {
|
|
||||||
return this._assetExts.indexOf(extname(file)) !== -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract all required modules from a `code` string.
|
|
||||||
*/
|
|
||||||
var blockCommentRe = /\/\*(.|\n)*?\*\//g;
|
|
||||||
var lineCommentRe = /\/\/.+(\n|$)/g;
|
|
||||||
function extractRequires(code) {
|
|
||||||
var deps = [];
|
|
||||||
|
|
||||||
code
|
|
||||||
.replace(blockCommentRe, '')
|
|
||||||
.replace(lineCommentRe, '')
|
|
||||||
.replace(replacePatterns.IMPORT_RE, function(match, pre, quot, dep, post) {
|
|
||||||
deps.push(dep);
|
|
||||||
return match;
|
|
||||||
})
|
|
||||||
.replace(replacePatterns.REQUIRE_RE, function(match, pre, quot, dep, post) {
|
|
||||||
deps.push(dep);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deps;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `file` without the .js extension.
|
|
||||||
*/
|
|
||||||
function sansExtJs(file) {
|
|
||||||
if (file.match(/\.js$/)) {
|
|
||||||
return file.slice(0, -3);
|
|
||||||
} else {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `file` with the .js extension.
|
|
||||||
*/
|
|
||||||
function withExtJs(file) {
|
|
||||||
if (file.match(/\.js$/)) {
|
|
||||||
return file;
|
|
||||||
} else {
|
|
||||||
return file + '.js';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleBrokenLink(e) {
|
|
||||||
debug('WARNING: error stating, possibly broken symlink', e.message);
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
function readAndStatDir(dir) {
|
|
||||||
return readDir(dir)
|
|
||||||
.then(function(files){
|
|
||||||
return Promise.all(files.map(function(filePath) {
|
|
||||||
return realpath(path.join(dir, filePath)).catch(handleBrokenLink);
|
|
||||||
}));
|
|
||||||
}).then(function(files) {
|
|
||||||
files = files.filter(function(f) {
|
|
||||||
return !!f;
|
|
||||||
});
|
|
||||||
|
|
||||||
var stats = files.map(function(filePath) {
|
|
||||||
return lstat(filePath).catch(handleBrokenLink);
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
files,
|
|
||||||
Promise.all(stats),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a list of roots and list of extensions find all the files in
|
|
||||||
* the directory with that extension and build a map of those assets.
|
|
||||||
*/
|
|
||||||
function buildAssetMap_DEPRECATED(roots, processAsset) {
|
|
||||||
var queue = roots.slice(0);
|
|
||||||
|
|
||||||
function search() {
|
|
||||||
var root = queue.shift();
|
|
||||||
|
|
||||||
if (root == null) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return readAndStatDir(root).spread(function(files, stats) {
|
|
||||||
files.forEach(function(file, i) {
|
|
||||||
if (stats[i].isDirectory()) {
|
|
||||||
queue.push(file);
|
|
||||||
} else {
|
|
||||||
processAsset(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return search();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return search();
|
|
||||||
}
|
|
||||||
|
|
||||||
function assetName(file, ext) {
|
|
||||||
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function extname(name) {
|
|
||||||
return path.extname(name).replace(/^\./, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function NotFoundError() {
|
|
||||||
Error.call(this);
|
|
||||||
Error.captureStackTrace(this, this.constructor);
|
|
||||||
var msg = util.format.apply(util, arguments);
|
|
||||||
this.message = msg;
|
|
||||||
this.type = this.name = 'NotFoundError';
|
|
||||||
this.status = 404;
|
|
||||||
}
|
|
||||||
|
|
||||||
util.inherits(NotFoundError, Error);
|
|
||||||
|
|
||||||
module.exports = DependecyGraph;
|
|
|
@ -1,177 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var path = require('path');
|
|
||||||
var DependencyGraph = require('./DependencyGraph');
|
|
||||||
var replacePatterns = require('./replacePatterns');
|
|
||||||
var ModuleDescriptor = require('../ModuleDescriptor');
|
|
||||||
var declareOpts = require('../../lib/declareOpts');
|
|
||||||
|
|
||||||
var DEFINE_MODULE_CODE = [
|
|
||||||
'__d(',
|
|
||||||
'\'_moduleName_\',',
|
|
||||||
'_deps_,',
|
|
||||||
'function(global, require, requireDynamic, requireLazy, module, exports) {',
|
|
||||||
' _code_',
|
|
||||||
'}',
|
|
||||||
');',
|
|
||||||
].join('');
|
|
||||||
|
|
||||||
var DEFINE_MODULE_REPLACE_RE = /_moduleName_|_code_|_deps_/g;
|
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
|
||||||
projectRoots: {
|
|
||||||
type: 'array',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
blacklistRE: {
|
|
||||||
type: 'object', // typeof regex is object
|
|
||||||
},
|
|
||||||
polyfillModuleNames: {
|
|
||||||
type: 'array',
|
|
||||||
default: [],
|
|
||||||
},
|
|
||||||
nonPersistent: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
moduleFormat: {
|
|
||||||
type: 'string',
|
|
||||||
default: 'haste',
|
|
||||||
},
|
|
||||||
assetRoots: {
|
|
||||||
type: 'array',
|
|
||||||
default: [],
|
|
||||||
},
|
|
||||||
fileWatcher: {
|
|
||||||
type: 'object',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
assetExts: {
|
|
||||||
type: 'array',
|
|
||||||
required: true,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function HasteDependencyResolver(options) {
|
|
||||||
var opts = validateOpts(options);
|
|
||||||
|
|
||||||
this._depGraph = new DependencyGraph({
|
|
||||||
roots: opts.projectRoots,
|
|
||||||
assetRoots_DEPRECATED: opts.assetRoots,
|
|
||||||
assetExts: opts.assetExts,
|
|
||||||
ignoreFilePath: function(filepath) {
|
|
||||||
return filepath.indexOf('__tests__') !== -1 ||
|
|
||||||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
|
|
||||||
},
|
|
||||||
fileWatcher: opts.fileWatcher,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
this._polyfillModuleNames = opts.polyfillModuleNames || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
var getDependenciesValidateOpts = declareOpts({
|
|
||||||
dev: {
|
|
||||||
type: 'boolean',
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
|
|
||||||
var opts = getDependenciesValidateOpts(options);
|
|
||||||
|
|
||||||
var depGraph = this._depGraph;
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
return depGraph.load()
|
|
||||||
.then(function() {
|
|
||||||
var dependencies = depGraph.getOrderedDependencies(main);
|
|
||||||
var mainModuleId = dependencies[0].id;
|
|
||||||
|
|
||||||
self._prependPolyfillDependencies(dependencies, opts.dev);
|
|
||||||
|
|
||||||
return {
|
|
||||||
mainModuleId: mainModuleId,
|
|
||||||
dependencies: dependencies
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
|
||||||
dependencies,
|
|
||||||
isDev
|
|
||||||
) {
|
|
||||||
var polyfillModuleNames = [
|
|
||||||
isDev
|
|
||||||
? path.join(__dirname, 'polyfills/prelude_dev.js')
|
|
||||||
: path.join(__dirname, 'polyfills/prelude.js'),
|
|
||||||
path.join(__dirname, 'polyfills/require.js'),
|
|
||||||
path.join(__dirname, 'polyfills/polyfills.js'),
|
|
||||||
path.join(__dirname, 'polyfills/console.js'),
|
|
||||||
path.join(__dirname, 'polyfills/error-guard.js'),
|
|
||||||
path.join(__dirname, 'polyfills/String.prototype.es6.js'),
|
|
||||||
path.join(__dirname, 'polyfills/Array.prototype.es6.js'),
|
|
||||||
].concat(this._polyfillModuleNames);
|
|
||||||
|
|
||||||
var polyfillModules = polyfillModuleNames.map(
|
|
||||||
function(polyfillModuleName, idx) {
|
|
||||||
return new ModuleDescriptor({
|
|
||||||
path: polyfillModuleName,
|
|
||||||
id: polyfillModuleName,
|
|
||||||
dependencies: polyfillModuleNames.slice(0, idx),
|
|
||||||
isPolyfill: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
dependencies.unshift.apply(dependencies, polyfillModules);
|
|
||||||
};
|
|
||||||
|
|
||||||
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
|
||||||
if (module.isPolyfill) {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resolvedDeps = Object.create(null);
|
|
||||||
var resolvedDepsArr = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < module.dependencies.length; i++) {
|
|
||||||
var depName = module.dependencies[i];
|
|
||||||
var dep = this._depGraph.resolveDependency(module, depName);
|
|
||||||
if (dep) {
|
|
||||||
resolvedDeps[depName] = dep.id;
|
|
||||||
resolvedDepsArr.push(dep.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var relativizeCode = function(codeMatch, pre, quot, depName, post) {
|
|
||||||
var depId = resolvedDeps[depName];
|
|
||||||
if (depId) {
|
|
||||||
return pre + quot + depId + post;
|
|
||||||
} else {
|
|
||||||
return codeMatch;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return DEFINE_MODULE_CODE.replace(DEFINE_MODULE_REPLACE_RE, function(key) {
|
|
||||||
return {
|
|
||||||
'_moduleName_': module.id,
|
|
||||||
'_code_': code.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
|
||||||
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
|
|
||||||
'_deps_': JSON.stringify(resolvedDepsArr),
|
|
||||||
}[key];
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
HasteDependencyResolver.prototype.getDebugInfo = function() {
|
|
||||||
return this._depGraph.getDebugInfo();
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = HasteDependencyResolver;
|
|
|
@ -8,15 +8,174 @@
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var HasteDependencyResolver = require('./haste');
|
var path = require('path');
|
||||||
var NodeDependencyResolver = require('./node');
|
var DependencyGraph = require('./DependencyGraph');
|
||||||
|
var replacePatterns = require('./replacePatterns');
|
||||||
|
var declareOpts = require('../lib/declareOpts');
|
||||||
|
var Promise = require('promise');
|
||||||
|
|
||||||
module.exports = function createDependencyResolver(options) {
|
var validateOpts = declareOpts({
|
||||||
if (options.moduleFormat === 'haste') {
|
projectRoots: {
|
||||||
return new HasteDependencyResolver(options);
|
type: 'array',
|
||||||
} else if (options.moduleFormat === 'node') {
|
required: true,
|
||||||
return new NodeDependencyResolver(options);
|
},
|
||||||
|
blacklistRE: {
|
||||||
|
type: 'object', // typeof regex is object
|
||||||
|
},
|
||||||
|
polyfillModuleNames: {
|
||||||
|
type: 'array',
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
nonPersistent: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
moduleFormat: {
|
||||||
|
type: 'string',
|
||||||
|
default: 'haste',
|
||||||
|
},
|
||||||
|
assetRoots: {
|
||||||
|
type: 'array',
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
fileWatcher: {
|
||||||
|
type: 'object',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
assetExts: {
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function HasteDependencyResolver(options) {
|
||||||
|
var opts = validateOpts(options);
|
||||||
|
|
||||||
|
this._depGraph = new DependencyGraph({
|
||||||
|
roots: opts.projectRoots,
|
||||||
|
assetRoots_DEPRECATED: opts.assetRoots,
|
||||||
|
assetExts: opts.assetExts,
|
||||||
|
ignoreFilePath: function(filepath) {
|
||||||
|
return filepath.indexOf('__tests__') !== -1 ||
|
||||||
|
(opts.blacklistRE && opts.blacklistRE.test(filepath));
|
||||||
|
},
|
||||||
|
fileWatcher: opts.fileWatcher,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this._polyfillModuleNames = opts.polyfillModuleNames || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var getDependenciesValidateOpts = declareOpts({
|
||||||
|
dev: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
|
||||||
|
var opts = getDependenciesValidateOpts(options);
|
||||||
|
|
||||||
|
var depGraph = this._depGraph;
|
||||||
|
var self = this;
|
||||||
|
return depGraph.load().then(
|
||||||
|
() => depGraph.getOrderedDependencies(main).then(
|
||||||
|
dependencies => {
|
||||||
|
const mainModuleId = dependencies[0].id;
|
||||||
|
self._prependPolyfillDependencies(
|
||||||
|
dependencies,
|
||||||
|
opts.dev
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mainModuleId: mainModuleId,
|
||||||
|
dependencies: dependencies
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
||||||
|
dependencies,
|
||||||
|
isDev
|
||||||
|
) {
|
||||||
|
var polyfillModuleNames = [
|
||||||
|
isDev
|
||||||
|
? path.join(__dirname, 'polyfills/prelude_dev.js')
|
||||||
|
: path.join(__dirname, 'polyfills/prelude.js'),
|
||||||
|
path.join(__dirname, 'polyfills/require.js'),
|
||||||
|
path.join(__dirname, 'polyfills/polyfills.js'),
|
||||||
|
path.join(__dirname, 'polyfills/console.js'),
|
||||||
|
path.join(__dirname, 'polyfills/error-guard.js'),
|
||||||
|
path.join(__dirname, 'polyfills/String.prototype.es6.js'),
|
||||||
|
path.join(__dirname, 'polyfills/Array.prototype.es6.js'),
|
||||||
|
].concat(this._polyfillModuleNames);
|
||||||
|
|
||||||
|
var polyfillModules = polyfillModuleNames.map(
|
||||||
|
(polyfillModuleName, idx) => ({
|
||||||
|
path: polyfillModuleName,
|
||||||
|
id: polyfillModuleName,
|
||||||
|
dependencies: polyfillModuleNames.slice(0, idx),
|
||||||
|
isPolyfill: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
dependencies.unshift.apply(dependencies, polyfillModules);
|
||||||
|
};
|
||||||
|
|
||||||
|
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||||
|
if (module.isPolyfill) {
|
||||||
|
return Promise.resolve(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedDeps = Object.create(null);
|
||||||
|
const resolvedDepsArr = [];
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
module.dependencies.map(depName => {
|
||||||
|
return this._depGraph.resolveDependency(module, depName)
|
||||||
|
.then((dep) => dep && dep.getPlainObject().then(mod => {
|
||||||
|
if (mod) {
|
||||||
|
resolvedDeps[depName] = mod.id;
|
||||||
|
resolvedDepsArr.push(mod.id);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
).then(() => {
|
||||||
|
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
|
||||||
|
const depId = resolvedDeps[depName];
|
||||||
|
if (depId) {
|
||||||
|
return pre + quot + depId + post;
|
||||||
} else {
|
} else {
|
||||||
throw new Error('unsupported');
|
return codeMatch;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return defineModuleCode({
|
||||||
|
code: code
|
||||||
|
.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
||||||
|
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
|
||||||
|
deps: JSON.stringify(resolvedDepsArr),
|
||||||
|
moduleName: module.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
HasteDependencyResolver.prototype.getDebugInfo = function() {
|
||||||
|
return this._depGraph.getDebugInfo();
|
||||||
|
};
|
||||||
|
|
||||||
|
function defineModuleCode({moduleName, code, deps}) {
|
||||||
|
return [
|
||||||
|
`__d(`,
|
||||||
|
`'${moduleName}',`,
|
||||||
|
`${deps},`,
|
||||||
|
'function(global, require, ',
|
||||||
|
'requireDynamic, requireLazy, module, exports) {',
|
||||||
|
` ${code}`,
|
||||||
|
'\n});',
|
||||||
|
].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = HasteDependencyResolver;
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/**
|
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the BSD-style license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree. An additional grant
|
|
||||||
* of patent rights can be found in the PATENTS file in the same directory.
|
|
||||||
*/
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var Promise = require('bluebird');
|
|
||||||
var ModuleDescriptor = require('../ModuleDescriptor');
|
|
||||||
|
|
||||||
var mdeps = require('module-deps');
|
|
||||||
var path = require('path');
|
|
||||||
|
|
||||||
exports.getRuntimeCode = function() {};
|
|
||||||
|
|
||||||
exports.wrapModule = function(id, source) {
|
|
||||||
return Promise.resolve(
|
|
||||||
'define(' + JSON.stringify(id) + ',' + ' function(exports, module) {\n'
|
|
||||||
+ source + '\n});'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getDependencies = function(root, fileEntryPath) {
|
|
||||||
return new Promise(function(resolve) {
|
|
||||||
fileEntryPath = path.join(process.cwd(), root, fileEntryPath);
|
|
||||||
|
|
||||||
var md = mdeps();
|
|
||||||
|
|
||||||
md.end({file: fileEntryPath});
|
|
||||||
|
|
||||||
var deps = [];
|
|
||||||
|
|
||||||
md.on('data', function(data) {
|
|
||||||
deps.push(
|
|
||||||
new ModuleDescriptor({
|
|
||||||
id: data.id,
|
|
||||||
deps: data.deps,
|
|
||||||
path: data.file,
|
|
||||||
entry: data.entry
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
md.on('end', function() {
|
|
||||||
resolve(deps);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var sane = require('sane');
|
var sane = require('sane');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var exec = require('child_process').exec;
|
var exec = require('child_process').exec;
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ util.inherits(FileWatcher, EventEmitter);
|
||||||
FileWatcher.prototype.end = function() {
|
FileWatcher.prototype.end = function() {
|
||||||
return this._loading.then(function(watchers) {
|
return this._loading.then(function(watchers) {
|
||||||
watchers.forEach(function(watcher) {
|
watchers.forEach(function(watcher) {
|
||||||
return Promise.promisify(watcher.close, watcher)();
|
return Promise.denodeify(watcher.close).call(watcher);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ var declareOpts = require('../lib/declareOpts');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var isAbsolutePath = require('absolute-path');
|
var isAbsolutePath = require('absolute-path');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
var tmpdir = require('os').tmpDir();
|
var tmpdir = require('os').tmpDir();
|
||||||
var version = require('../../../../package.json').version;
|
var version = require('../../../../package.json').version;
|
||||||
|
|
||||||
|
@ -74,11 +74,13 @@ Cache.prototype.get = function(filepath, loaderCb) {
|
||||||
|
|
||||||
Cache.prototype._set = function(filepath, loaderPromise) {
|
Cache.prototype._set = function(filepath, loaderPromise) {
|
||||||
this._data[filepath] = loaderPromise.then(function(data) {
|
this._data[filepath] = loaderPromise.then(function(data) {
|
||||||
return [
|
return Promise.all([
|
||||||
data,
|
data,
|
||||||
Promise.promisify(fs.stat)(filepath)
|
Promise.denodeify(fs.stat)(filepath)
|
||||||
];
|
]);
|
||||||
}).spread(function(data, stat) {
|
}).then(function(ref) {
|
||||||
|
var data = ref[0];
|
||||||
|
var stat = ref[1];
|
||||||
this._persistEventually();
|
this._persistEventually();
|
||||||
return {
|
return {
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -113,7 +115,7 @@ Cache.prototype._persistCache = function() {
|
||||||
Object.keys(data).forEach(function(key, i) {
|
Object.keys(data).forEach(function(key, i) {
|
||||||
json[key] = values[i];
|
json[key] = values[i];
|
||||||
});
|
});
|
||||||
return Promise.promisify(fs.writeFile)(cacheFilepath, JSON.stringify(json));
|
return Promise.denodeify(fs.writeFile)(cacheFilepath, JSON.stringify(json));
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
this._persisting = null;
|
this._persisting = null;
|
||||||
|
|
|
@ -17,7 +17,7 @@ jest
|
||||||
.mock('os')
|
.mock('os')
|
||||||
.mock('fs');
|
.mock('fs');
|
||||||
|
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
|
|
||||||
describe('JSTransformer Cache', function() {
|
describe('JSTransformer Cache', function() {
|
||||||
var Cache;
|
var Cache;
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
var Cache = require('./Cache');
|
var Cache = require('./Cache');
|
||||||
var workerFarm = require('worker-farm');
|
var workerFarm = require('worker-farm');
|
||||||
var declareOpts = require('../lib/declareOpts');
|
var declareOpts = require('../lib/declareOpts');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var ModuleTransport = require('../lib/ModuleTransport');
|
var ModuleTransport = require('../lib/ModuleTransport');
|
||||||
|
|
||||||
var readFile = Promise.promisify(fs.readFile);
|
var readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
module.exports = Transformer;
|
module.exports = Transformer;
|
||||||
Transformer.TransformError = TransformError;
|
Transformer.TransformError = TransformError;
|
||||||
|
@ -69,7 +69,7 @@ function Transformer(options) {
|
||||||
options.transformModulePath
|
options.transformModulePath
|
||||||
);
|
);
|
||||||
|
|
||||||
this._transform = Promise.promisify(this._workers);
|
this._transform = Promise.denodeify(this._workers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,15 @@ jest
|
||||||
|
|
||||||
jest.mock('fs');
|
jest.mock('fs');
|
||||||
|
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
|
|
||||||
describe('Packager', function() {
|
describe('Packager', function() {
|
||||||
var getDependencies;
|
var getDependencies;
|
||||||
var wrapModule;
|
var wrapModule;
|
||||||
var Packager;
|
var Packager;
|
||||||
|
var packager;
|
||||||
|
var assetServer;
|
||||||
|
var modules;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
getDependencies = jest.genMockFn();
|
getDependencies = jest.genMockFn();
|
||||||
|
@ -35,30 +38,27 @@ describe('Packager', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
Packager = require('../');
|
Packager = require('../');
|
||||||
});
|
|
||||||
|
|
||||||
pit('create a package', function() {
|
|
||||||
require('fs').statSync.mockImpl(function() {
|
require('fs').statSync.mockImpl(function() {
|
||||||
return {
|
return {
|
||||||
isDirectory: function() {return true;}
|
isDirectory: () => true
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
require('fs').readFile.mockImpl(function(file, callback) {
|
require('fs').readFile.mockImpl(function(file, callback) {
|
||||||
callback(null, '{"json":true}');
|
callback(null, '{"json":true}');
|
||||||
});
|
});
|
||||||
|
|
||||||
var assetServer = {
|
assetServer = {
|
||||||
getAssetData: jest.genMockFn(),
|
getAssetData: jest.genMockFn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var packager = new Packager({
|
packager = new Packager({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
assetServer: assetServer,
|
assetServer: assetServer,
|
||||||
});
|
});
|
||||||
|
|
||||||
var modules = [
|
modules = [
|
||||||
{id: 'foo', path: '/root/foo.js', dependencies: []},
|
{id: 'foo', path: '/root/foo.js', dependencies: []},
|
||||||
{id: 'bar', path: '/root/bar.js', dependencies: []},
|
{id: 'bar', path: '/root/bar.js', dependencies: []},
|
||||||
{
|
{
|
||||||
|
@ -101,7 +101,7 @@ describe('Packager', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapModule.mockImpl(function(module, code) {
|
wrapModule.mockImpl(function(module, code) {
|
||||||
return 'lol ' + code + ' lol';
|
return Promise.resolve('lol ' + code + ' lol');
|
||||||
});
|
});
|
||||||
|
|
||||||
require('image-size').mockImpl(function(path, cb) {
|
require('image-size').mockImpl(function(path, cb) {
|
||||||
|
@ -116,7 +116,9 @@ describe('Packager', function() {
|
||||||
type: 'png',
|
type: 'png',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('create a package', function() {
|
||||||
return packager.package('/root/foo.js', true, 'source_map_url')
|
return packager.package('/root/foo.js', true, 'source_map_url')
|
||||||
.then(function(p) {
|
.then(function(p) {
|
||||||
expect(p.addModule.mock.calls[0][0]).toEqual({
|
expect(p.addModule.mock.calls[0][0]).toEqual({
|
||||||
|
@ -200,4 +202,42 @@ describe('Packager', function() {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pit('gets the list of dependencies', function() {
|
||||||
|
return packager.getDependencies('/root/foo.js', true)
|
||||||
|
.then(({dependencies}) => {
|
||||||
|
expect(dependencies).toEqual([
|
||||||
|
{
|
||||||
|
dependencies: [],
|
||||||
|
id: 'foo',
|
||||||
|
path: '/root/foo.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependencies: [],
|
||||||
|
id: 'bar',
|
||||||
|
path: '/root/bar.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependencies: [],
|
||||||
|
id: 'image!img',
|
||||||
|
isAsset_DEPRECATED: true,
|
||||||
|
path: '/root/img/img.png',
|
||||||
|
resolution: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependencies: [],
|
||||||
|
id: 'new_image.png',
|
||||||
|
isAsset: true,
|
||||||
|
path: '/root/img/new_image.png',
|
||||||
|
resolution: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependencies: [],
|
||||||
|
id: 'package/file.json',
|
||||||
|
isJSON: true,
|
||||||
|
path: '/root/file.json',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
var Transformer = require('../JSTransformer');
|
var Transformer = require('../JSTransformer');
|
||||||
var DependencyResolver = require('../DependencyResolver');
|
var DependencyResolver = require('../DependencyResolver');
|
||||||
var Package = require('./Package');
|
var Package = require('./Package');
|
||||||
|
@ -20,8 +20,8 @@ var ModuleTransport = require('../lib/ModuleTransport');
|
||||||
var declareOpts = require('../lib/declareOpts');
|
var declareOpts = require('../lib/declareOpts');
|
||||||
var imageSize = require('image-size');
|
var imageSize = require('image-size');
|
||||||
|
|
||||||
var sizeOf = Promise.promisify(imageSize);
|
var sizeOf = Promise.denodeify(imageSize);
|
||||||
var readFile = Promise.promisify(fs.readFile);
|
var readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
var validateOpts = declareOpts({
|
var validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
|
@ -159,16 +159,17 @@ Packager.prototype._transformModule = function(ppackage, module) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolver = this._resolver;
|
var resolver = this._resolver;
|
||||||
return transform.then(function(transformed) {
|
return transform.then(
|
||||||
var code = resolver.wrapModule(module, transformed.code);
|
transformed => resolver.wrapModule(module, transformed.code).then(
|
||||||
return new ModuleTransport({
|
code => new ModuleTransport({
|
||||||
code: code,
|
code: code,
|
||||||
map: transformed.map,
|
map: transformed.map,
|
||||||
sourceCode: transformed.sourceCode,
|
sourceCode: transformed.sourceCode,
|
||||||
sourcePath: transformed.sourcePath,
|
sourcePath: transformed.sourcePath,
|
||||||
virtual: transformed.virtual,
|
virtual: transformed.virtual,
|
||||||
});
|
})
|
||||||
});
|
)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Packager.prototype.getGraphDebugInfo = function() {
|
Packager.prototype.getGraphDebugInfo = function() {
|
||||||
|
@ -206,7 +207,9 @@ Packager.prototype.generateAssetModule = function(ppackage, module) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
sizeOf(module.path),
|
sizeOf(module.path),
|
||||||
this._assetServer.getAssetData(relPath),
|
this._assetServer.getAssetData(relPath),
|
||||||
]).spread(function(dimensions, assetData) {
|
]).then(function(res) {
|
||||||
|
var dimensions = res[0];
|
||||||
|
var assetData = res[1];
|
||||||
var img = {
|
var img = {
|
||||||
__packager_asset: true,
|
__packager_asset: true,
|
||||||
fileSystemLocation: path.dirname(module.path),
|
fileSystemLocation: path.dirname(module.path),
|
||||||
|
|
|
@ -20,7 +20,7 @@ jest.setMock('worker-farm', function() { return function() {}; })
|
||||||
.setMock('uglify-js')
|
.setMock('uglify-js')
|
||||||
.dontMock('../');
|
.dontMock('../');
|
||||||
|
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
|
|
||||||
describe('processRequest', function() {
|
describe('processRequest', function() {
|
||||||
var server;
|
var server;
|
||||||
|
|
|
@ -15,7 +15,7 @@ var FileWatcher = require('../FileWatcher');
|
||||||
var Packager = require('../Packager');
|
var Packager = require('../Packager');
|
||||||
var Activity = require('../Activity');
|
var Activity = require('../Activity');
|
||||||
var AssetServer = require('../AssetServer');
|
var AssetServer = require('../AssetServer');
|
||||||
var Promise = require('bluebird');
|
var Promise = require('promise');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var exec = require('child_process').exec;
|
var exec = require('child_process').exec;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
@ -131,13 +131,14 @@ Server.prototype._onFileChange = function(type, filepath, root) {
|
||||||
Server.prototype._rebuildPackages = function() {
|
Server.prototype._rebuildPackages = function() {
|
||||||
var buildPackage = this.buildPackage.bind(this);
|
var buildPackage = this.buildPackage.bind(this);
|
||||||
var packages = this._packages;
|
var packages = this._packages;
|
||||||
Object.keys(packages).forEach(function(key) {
|
|
||||||
var options = getOptionsFromUrl(key);
|
Object.keys(packages).forEach(function(optionsJson) {
|
||||||
|
var options = JSON.parse(optionsJson);
|
||||||
// Wait for a previous build (if exists) to finish.
|
// Wait for a previous build (if exists) to finish.
|
||||||
packages[key] = (packages[key] || Promise.resolve()).finally(function() {
|
packages[optionsJson] = (packages[optionsJson] || Promise.resolve()).finally(function() {
|
||||||
// With finally promise callback we can't change the state of the promise
|
// With finally promise callback we can't change the state of the promise
|
||||||
// so we need to reassign the promise.
|
// so we need to reassign the promise.
|
||||||
packages[key] = buildPackage(options).then(function(p) {
|
packages[optionsJson] = buildPackage(options).then(function(p) {
|
||||||
// Make a throwaway call to getSource to cache the source string.
|
// Make a throwaway call to getSource to cache the source string.
|
||||||
p.getSource({
|
p.getSource({
|
||||||
inlineSourceMap: options.inlineSourceMap,
|
inlineSourceMap: options.inlineSourceMap,
|
||||||
|
@ -146,7 +147,7 @@ Server.prototype._rebuildPackages = function() {
|
||||||
return p;
|
return p;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return packages[key];
|
return packages[optionsJson];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -228,15 +229,15 @@ Server.prototype._processDebugRequest = function(reqUrl, res) {
|
||||||
res.end(ret);
|
res.end(ret);
|
||||||
} else if (parts[1] === 'packages') {
|
} else if (parts[1] === 'packages') {
|
||||||
ret += '<h1> Cached Packages </h1>';
|
ret += '<h1> Cached Packages </h1>';
|
||||||
Promise.all(Object.keys(this._packages).map(function(url) {
|
Promise.all(Object.keys(this._packages).map(function(optionsJson) {
|
||||||
return this._packages[url].then(function(p) {
|
return this._packages[optionsJson].then(function(p) {
|
||||||
ret += '<div><h2>' + url + '</h2>';
|
ret += '<div><h2>' + optionsJson + '</h2>';
|
||||||
ret += p.getDebugInfo();
|
ret += p.getDebugInfo();
|
||||||
});
|
});
|
||||||
}, this)).then(
|
}, this)).then(
|
||||||
function() { res.end(ret); },
|
function() { res.end(ret); },
|
||||||
function(e) {
|
function(e) {
|
||||||
res.wrteHead(500);
|
res.writeHead(500);
|
||||||
res.end('Internal Error');
|
res.end('Internal Error');
|
||||||
console.log(e.stack);
|
console.log(e.stack);
|
||||||
}
|
}
|
||||||
|
@ -350,9 +351,10 @@ Server.prototype.processRequest = function(req, res, next) {
|
||||||
|
|
||||||
var startReqEventId = Activity.startEvent('request:' + req.url);
|
var startReqEventId = Activity.startEvent('request:' + req.url);
|
||||||
var options = getOptionsFromUrl(req.url);
|
var options = getOptionsFromUrl(req.url);
|
||||||
var building = this._packages[req.url] || this.buildPackage(options);
|
var optionsJson = JSON.stringify(options);
|
||||||
|
var building = this._packages[optionsJson] || this.buildPackage(options);
|
||||||
|
|
||||||
this._packages[req.url] = building;
|
this._packages[optionsJson] = building;
|
||||||
building.then(
|
building.then(
|
||||||
function(p) {
|
function(p) {
|
||||||
if (requestType === 'bundle') {
|
if (requestType === 'bundle') {
|
||||||
|
|
|
@ -51,7 +51,7 @@ fs.readFile.mockImpl(function(filepath, encoding, callback) {
|
||||||
var node = getToNode(filepath);
|
var node = getToNode(filepath);
|
||||||
// dir check
|
// dir check
|
||||||
if (node && typeof node === 'object' && node.SYMLINK == null) {
|
if (node && typeof node === 'object' && node.SYMLINK == null) {
|
||||||
callback(new Error('Trying to read a dir, ESIDR, or whatever'));
|
callback(new Error('Error readFile a dir: ' + filepath));
|
||||||
}
|
}
|
||||||
return callback(null, node);
|
return callback(null, node);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -59,12 +59,13 @@ fs.readFile.mockImpl(function(filepath, encoding, callback) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.lstat.mockImpl(function(filepath, callback) {
|
fs.stat.mockImpl(function(filepath, callback) {
|
||||||
var node;
|
var node;
|
||||||
try {
|
try {
|
||||||
node = getToNode(filepath);
|
node = getToNode(filepath);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return callback(e);
|
callback(e);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mtime = {
|
var mtime = {
|
||||||
|
@ -73,7 +74,12 @@ fs.lstat.mockImpl(function(filepath, callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (node && typeof node === 'object' && node.SYMLINK == null) {
|
if (node.SYMLINK) {
|
||||||
|
fs.stat(node.SYMLINK, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node && typeof node === 'object') {
|
||||||
callback(null, {
|
callback(null, {
|
||||||
isDirectory: function() {
|
isDirectory: function() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -89,9 +95,6 @@ fs.lstat.mockImpl(function(filepath, callback) {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
isSymbolicLink: function() {
|
isSymbolicLink: function() {
|
||||||
if (typeof node === 'object' && node.SYMLINK) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
mtime: mtime,
|
mtime: mtime,
|
||||||
|
@ -113,6 +116,9 @@ function getToNode(filepath) {
|
||||||
}
|
}
|
||||||
var node = filesystem;
|
var node = filesystem;
|
||||||
parts.slice(1).forEach(function(part) {
|
parts.slice(1).forEach(function(part) {
|
||||||
|
if (node && node.SYMLINK) {
|
||||||
|
node = getToNode(node.SYMLINK);
|
||||||
|
}
|
||||||
node = node[part];
|
node = node[part];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue