mirror of https://github.com/status-im/metro.git
Refactor DependencyResolver into request/response
Reviewed By: @martinbigio Differential Revision: D2425842
This commit is contained in:
parent
87d4d3c55c
commit
86a099d00a
|
@ -125,7 +125,7 @@ describe('Bundler', function() {
|
|||
});
|
||||
});
|
||||
|
||||
wrapModule.mockImpl(function(module, code) {
|
||||
wrapModule.mockImpl(function(response, module, code) {
|
||||
return Promise.resolve('lol ' + code + ' lol');
|
||||
});
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ class Bundler {
|
|||
const findEventId = Activity.startEvent('find dependencies');
|
||||
let transformEventId;
|
||||
|
||||
return this.getDependencies(main, isDev, platform).then((result) => {
|
||||
return this.getDependencies(main, isDev, platform).then((response) => {
|
||||
Activity.endEvent(findEventId);
|
||||
transformEventId = Activity.startEvent('transform');
|
||||
|
||||
|
@ -147,14 +147,19 @@ class Bundler {
|
|||
complete: '=',
|
||||
incomplete: ' ',
|
||||
width: 40,
|
||||
total: result.dependencies.length,
|
||||
total: response.dependencies.length,
|
||||
});
|
||||
}
|
||||
|
||||
bundle.setMainModuleId(result.mainModuleId);
|
||||
bundle.setMainModuleId(response.mainModuleId);
|
||||
return Promise.all(
|
||||
result.dependencies.map(
|
||||
module => this._transformModule(bundle, module, platform).then(transformed => {
|
||||
response.dependencies.map(
|
||||
module => this._transformModule(
|
||||
bundle,
|
||||
response,
|
||||
module,
|
||||
platform
|
||||
).then(transformed => {
|
||||
if (bar) {
|
||||
bar.tick();
|
||||
}
|
||||
|
@ -182,7 +187,7 @@ class Bundler {
|
|||
return this._resolver.getDependencies(main, { dev: isDev, platform });
|
||||
}
|
||||
|
||||
_transformModule(bundle, module, platform = null) {
|
||||
_transformModule(bundle, response, module, platform = null) {
|
||||
let transform;
|
||||
|
||||
if (module.isAsset_DEPRECATED()) {
|
||||
|
@ -199,7 +204,11 @@ class Bundler {
|
|||
|
||||
const resolver = this._resolver;
|
||||
return transform.then(
|
||||
transformed => resolver.wrapModule(module, transformed.code).then(
|
||||
transformed => resolver.wrapModule(
|
||||
response,
|
||||
module,
|
||||
transformed.code
|
||||
).then(
|
||||
code => new ModuleTransport({
|
||||
code: code,
|
||||
map: transformed.map,
|
||||
|
|
|
@ -9,25 +9,8 @@
|
|||
'use strict';
|
||||
|
||||
jest
|
||||
.dontMock('absolute-path')
|
||||
.dontMock('crypto')
|
||||
.dontMock('underscore')
|
||||
.dontMock('path')
|
||||
.dontMock('../index')
|
||||
.dontMock('../../lib/getAssetDataFromName')
|
||||
.dontMock('../../DependencyResolver/crawlers')
|
||||
.dontMock('../../DependencyResolver/crawlers/node')
|
||||
.dontMock('../../DependencyResolver/DependencyGraph/docblock')
|
||||
.dontMock('../../DependencyResolver/fastfs')
|
||||
.dontMock('../../DependencyResolver/replacePatterns')
|
||||
.dontMock('../../DependencyResolver')
|
||||
.dontMock('../../DependencyResolver/DependencyGraph')
|
||||
.dontMock('../../DependencyResolver/AssetModule_DEPRECATED')
|
||||
.dontMock('../../DependencyResolver/AssetModule')
|
||||
.dontMock('../../DependencyResolver/Module')
|
||||
.dontMock('../../DependencyResolver/Package')
|
||||
.dontMock('../../DependencyResolver/Polyfill')
|
||||
.dontMock('../../DependencyResolver/ModuleCache');
|
||||
.autoMockOff()
|
||||
.mock('../../Cache');
|
||||
|
||||
const Promise = require('promise');
|
||||
const path = require('path');
|
||||
|
|
100
react-packager/src/DependencyResolver/DependencyGraph/DeprecatedAssetMap.js
vendored
Normal file
100
react-packager/src/DependencyResolver/DependencyGraph/DeprecatedAssetMap.js
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* 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 Activity = require('../../Activity');
|
||||
const AssetModule_DEPRECATED = require('../AssetModule_DEPRECATED');
|
||||
const Fastfs = require('../fastfs');
|
||||
const debug = require('debug')('ReactPackager:DependencyGraph');
|
||||
const path = require('path');
|
||||
|
||||
class DeprecatedAssetMap {
|
||||
constructor({ fsCrawl, roots, assetExts, fileWatcher, ignoreFilePath, helpers }) {
|
||||
if (roots == null || roots.length === 0) {
|
||||
this._disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this._helpers = helpers;
|
||||
this._map = Object.create(null);
|
||||
this._assetExts = assetExts;
|
||||
this._fastfs = new Fastfs(
|
||||
'Assets',
|
||||
roots,
|
||||
fileWatcher,
|
||||
{ ignore: ignoreFilePath, crawling: fsCrawl }
|
||||
);
|
||||
|
||||
this._fastfs.on('change', this._processFileChange.bind(this));
|
||||
}
|
||||
|
||||
build() {
|
||||
if (this._disabled) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this._fastfs.build().then(
|
||||
() => {
|
||||
const processAsset_DEPRECATEDActivity = Activity.startEvent(
|
||||
'Building (deprecated) Asset Map',
|
||||
);
|
||||
|
||||
this._fastfs.findFilesByExts(this._assetExts).forEach(
|
||||
file => this._processAsset(file)
|
||||
);
|
||||
|
||||
Activity.endEvent(processAsset_DEPRECATEDActivity);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
resolve(fromModule, toModuleName) {
|
||||
if (this._disabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assetMatch = toModuleName.match(/^image!(.+)/);
|
||||
if (assetMatch && assetMatch[1]) {
|
||||
if (!this._map[assetMatch[1]]) {
|
||||
debug('WARINING: Cannot find asset:', assetMatch[1]);
|
||||
return null;
|
||||
}
|
||||
return this._map[assetMatch[1]];
|
||||
}
|
||||
}
|
||||
|
||||
_processAsset(file) {
|
||||
let ext = this._helpers.extname(file);
|
||||
if (this._assetExts.indexOf(ext) !== -1) {
|
||||
let name = assetName(file, ext);
|
||||
if (this._map[name] != null) {
|
||||
debug('Conflcting assets', name);
|
||||
}
|
||||
|
||||
this._map[name] = new AssetModule_DEPRECATED(file);
|
||||
}
|
||||
}
|
||||
|
||||
_processFileChange(type, filePath, root, fstat) {
|
||||
const name = assetName(filePath);
|
||||
if (type === 'change' || type === 'delete') {
|
||||
delete this._map[name];
|
||||
}
|
||||
|
||||
if (type === 'change' || type === 'add') {
|
||||
this._processAsset(path.join(root, filePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assetName(file, ext) {
|
||||
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
|
||||
}
|
||||
|
||||
module.exports = DeprecatedAssetMap;
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* 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 getPontentialPlatformExt = require('../../lib/getPlatformExtension');
|
||||
|
||||
class HasteMap {
|
||||
constructor({ fastfs, moduleCache, helpers }) {
|
||||
this._fastfs = fastfs;
|
||||
this._moduleCache = moduleCache;
|
||||
this._helpers = helpers;
|
||||
this._map = Object.create(null);
|
||||
}
|
||||
|
||||
build() {
|
||||
let promises = this._fastfs.findFilesByExt('js', {
|
||||
ignore: (file) => this._helpers.isNodeModulesDir(file)
|
||||
}).map(file => this._processHasteModule(file));
|
||||
|
||||
promises = promises.concat(
|
||||
this._fastfs.findFilesByName('package.json', {
|
||||
ignore: (file) => this._helpers.isNodeModulesDir(file)
|
||||
}).map(file => this._processHastePackage(file))
|
||||
);
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
processFileChange(type, absPath) {
|
||||
return Promise.resolve().then(() => {
|
||||
/*eslint no-labels: 0 */
|
||||
if (type === 'delete' || type === 'change') {
|
||||
loop: for (let name in this._map) {
|
||||
let modules = this._map[name];
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
if (modules[i].path === absPath) {
|
||||
modules.splice(i, 1);
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'delete') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._helpers.extname(absPath) === 'js' ||
|
||||
this._helpers.extname(absPath) === 'json') {
|
||||
if (path.basename(absPath) === 'package.json') {
|
||||
return this._processHastePackage(absPath);
|
||||
} else {
|
||||
return this._processHasteModule(absPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getModule(name, platform = null) {
|
||||
if (this._map[name]) {
|
||||
const modules = this._map[name];
|
||||
if (platform != null) {
|
||||
for (let i = 0; i < modules.length; i++) {
|
||||
if (getPontentialPlatformExt(modules[i].path) === platform) {
|
||||
return modules[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_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._map[name] == null) {
|
||||
this._map[name] = [];
|
||||
}
|
||||
|
||||
if (mod.type === 'Module') {
|
||||
// Modules takes precendence over packages.
|
||||
this._map[name].unshift(mod);
|
||||
} else {
|
||||
this._map[name].push(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HasteMap;
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* 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');
|
||||
|
||||
class Helpers {
|
||||
constructor({ providesModuleNodeModules, assetExts }) {
|
||||
this._providesModuleNodeModules = providesModuleNodeModules;
|
||||
this._assetExts = assetExts;
|
||||
}
|
||||
|
||||
isNodeModulesDir(file) {
|
||||
let parts = path.normalize(file).split(path.sep);
|
||||
const indexOfNodeModules = parts.lastIndexOf('node_modules');
|
||||
|
||||
if (indexOfNodeModules === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parts = parts.slice(indexOfNodeModules + 1);
|
||||
|
||||
const dirs = this._providesModuleNodeModules;
|
||||
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
if (parts.indexOf(dirs[i]) > -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isAssetFile(file) {
|
||||
return this._assetExts.indexOf(this.extname(file)) !== -1;
|
||||
}
|
||||
|
||||
extname(name) {
|
||||
return path.extname(name).replace(/^\./, '');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Helpers;
|
|
@ -0,0 +1,347 @@
|
|||
/**
|
||||
* 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 debug = require('debug')('ReactPackager:DependencyGraph');
|
||||
const util = require('util');
|
||||
const path = require('path');
|
||||
const isAbsolutePath = require('absolute-path');
|
||||
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
|
||||
|
||||
class ResolutionRequest {
|
||||
constructor({
|
||||
platform,
|
||||
entryPath,
|
||||
hasteMap,
|
||||
deprecatedAssetMap,
|
||||
helpers,
|
||||
moduleCache,
|
||||
fastfs,
|
||||
}) {
|
||||
this._platform = platform;
|
||||
this._entryPath = entryPath;
|
||||
this._hasteMap = hasteMap;
|
||||
this._deprecatedAssetMap = deprecatedAssetMap;
|
||||
this._helpers = helpers;
|
||||
this._moduleCache = moduleCache;
|
||||
this._fastfs = fastfs;
|
||||
this._resetResolutionCache();
|
||||
}
|
||||
|
||||
_tryResolve(action, secondaryAction) {
|
||||
return action().catch((error) => {
|
||||
if (error.type !== 'UnableToResolveError') {
|
||||
throw error;
|
||||
}
|
||||
return secondaryAction();
|
||||
});
|
||||
}
|
||||
|
||||
resolveDependency(fromModule, toModuleName) {
|
||||
const resHash = resolutionHash(fromModule.path, toModuleName);
|
||||
|
||||
if (this._immediateResolutionCache[resHash]) {
|
||||
return Promise.resolve(this._immediateResolutionCache[resHash]);
|
||||
}
|
||||
|
||||
const asset_DEPRECATED = this._deprecatedAssetMap.resolve(
|
||||
fromModule,
|
||||
toModuleName
|
||||
);
|
||||
if (asset_DEPRECATED) {
|
||||
return Promise.resolve(asset_DEPRECATED);
|
||||
}
|
||||
|
||||
const cacheResult = (result) => {
|
||||
this._immediateResolutionCache[resHash] = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
const forgive = (error) => {
|
||||
if (error.type !== 'UnableToResolveError') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'Unable to resolve module %s from %s',
|
||||
toModuleName,
|
||||
fromModule.path
|
||||
);
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!this._helpers.isNodeModulesDir(fromModule.path)
|
||||
&& toModuleName[0] !== '.' &&
|
||||
toModuleName[0] !== '/') {
|
||||
return this._tryResolve(
|
||||
() => this._resolveHasteDependency(fromModule, toModuleName),
|
||||
() => this._resolveNodeDependency(fromModule, toModuleName)
|
||||
).then(
|
||||
cacheResult,
|
||||
forgive,
|
||||
);
|
||||
}
|
||||
|
||||
return this._resolveNodeDependency(fromModule, toModuleName)
|
||||
.then(
|
||||
cacheResult,
|
||||
forgive
|
||||
);
|
||||
}
|
||||
|
||||
getOrderedDependencies(response) {
|
||||
return Promise.resolve().then(() => {
|
||||
const entry = this._moduleCache.getModule(this._entryPath);
|
||||
const visited = Object.create(null);
|
||||
visited[entry.hash()] = true;
|
||||
|
||||
const collect = (mod) => {
|
||||
response.pushDependency(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();
|
||||
|
||||
const filteredPairs = [];
|
||||
|
||||
dependencies.forEach((modDep, i) => {
|
||||
if (modDep == null) {
|
||||
debug(
|
||||
'WARNING: Cannot find required module `%s` from module `%s`',
|
||||
depNames[i],
|
||||
mod.path
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return filteredPairs.push([depNames[i], modDep]);
|
||||
});
|
||||
|
||||
response.setResolvedDependencyPairs(mod, filteredPairs);
|
||||
|
||||
filteredPairs.forEach(([depName, modDep]) => {
|
||||
p = p.then(() => {
|
||||
if (!visited[modDep.hash()]) {
|
||||
visited[modDep.hash()] = true;
|
||||
return collect(modDep);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
return p;
|
||||
});
|
||||
};
|
||||
|
||||
return collect(entry);
|
||||
});
|
||||
}
|
||||
|
||||
getAsyncDependencies(response) {
|
||||
return Promise.resolve().then(() => {
|
||||
const mod = this._moduleCache.getModule(this._entryPath);
|
||||
return mod.getAsyncDependencies().then(bundles =>
|
||||
Promise
|
||||
.all(bundles.map(bundle =>
|
||||
Promise.all(bundle.map(
|
||||
dep => this.resolveDependency(mod, dep)
|
||||
))
|
||||
))
|
||||
.then(bs => bs.map(bundle => bundle.map(dep => dep.path)))
|
||||
);
|
||||
}).then(asyncDependencies => asyncDependencies.forEach(
|
||||
(dependency) => response.pushAsyncDependency(dependency)
|
||||
));
|
||||
}
|
||||
|
||||
_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.getModule(realModuleName, this._platform);
|
||||
if (dep && dep.type === 'Module') {
|
||||
return dep;
|
||||
}
|
||||
|
||||
let packageName = realModuleName;
|
||||
while (packageName && packageName !== '.') {
|
||||
dep = this._hasteMap.getModule(packageName, this._platform);
|
||||
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._tryResolve(
|
||||
() => this._loadAsFile(potentialModulePath),
|
||||
() => this._loadAsDir(potentialModulePath),
|
||||
);
|
||||
}
|
||||
|
||||
throw new UnableToResolveError('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._tryResolve(
|
||||
() => this._loadAsFile(realModuleName),
|
||||
() => 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 UnableToResolveError('Node module not found'));
|
||||
searchQueue.forEach(potentialModulePath => {
|
||||
p = this._tryResolve(
|
||||
() => this._tryResolve(
|
||||
() => p,
|
||||
() => this._loadAsFile(potentialModulePath),
|
||||
),
|
||||
() => this._loadAsDir(potentialModulePath)
|
||||
);
|
||||
});
|
||||
|
||||
return p;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_loadAsFile(potentialModulePath) {
|
||||
return Promise.resolve().then(() => {
|
||||
if (this._helpers.isAssetFile(potentialModulePath)) {
|
||||
const {name, type} = getAssetDataFromName(potentialModulePath);
|
||||
|
||||
let pattern = '^' + name + '(@[\\d\\.]+x)?';
|
||||
if (this._platform != null) {
|
||||
pattern += '(\\.' + this._platform + ')?';
|
||||
}
|
||||
pattern += '\\.' + type;
|
||||
|
||||
// We arbitrarly grab the first one, because scale selection
|
||||
// will happen somewhere
|
||||
const [assetFile] = this._fastfs.matches(
|
||||
path.dirname(potentialModulePath),
|
||||
new RegExp(pattern)
|
||||
);
|
||||
|
||||
if (assetFile) {
|
||||
return this._moduleCache.getAssetModule(assetFile);
|
||||
}
|
||||
}
|
||||
|
||||
let file;
|
||||
if (this._fastfs.fileExists(potentialModulePath)) {
|
||||
file = potentialModulePath;
|
||||
} else if (this._platform != null &&
|
||||
this._fastfs.fileExists(potentialModulePath + '.' + this._platform + '.js')) {
|
||||
file = potentialModulePath + '.' + this._platform + '.js';
|
||||
} else if (this._fastfs.fileExists(potentialModulePath + '.js')) {
|
||||
file = potentialModulePath + '.js';
|
||||
} else if (this._fastfs.fileExists(potentialModulePath + '.json')) {
|
||||
file = potentialModulePath + '.json';
|
||||
} else {
|
||||
throw new UnableToResolveError(`File ${potentialModulePath} doesnt exist`);
|
||||
}
|
||||
|
||||
return this._moduleCache.getModule(file);
|
||||
});
|
||||
}
|
||||
|
||||
_loadAsDir(potentialDirPath) {
|
||||
return Promise.resolve().then(() => {
|
||||
if (!this._fastfs.dirExists(potentialDirPath)) {
|
||||
throw new UnableToResolveError(`Invalid directory ${potentialDirPath}`);
|
||||
}
|
||||
|
||||
const packageJsonPath = path.join(potentialDirPath, 'package.json');
|
||||
if (this._fastfs.fileExists(packageJsonPath)) {
|
||||
return this._moduleCache.getPackage(packageJsonPath)
|
||||
.getMain().then(
|
||||
(main) => this._tryResolve(
|
||||
() => this._loadAsFile(main),
|
||||
() => this._loadAsDir(main)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return this._loadAsFile(path.join(potentialDirPath, 'index'));
|
||||
});
|
||||
}
|
||||
|
||||
_resetResolutionCache() {
|
||||
this._immediateResolutionCache = Object.create(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function resolutionHash(modulePath, depName) {
|
||||
return `${path.resolve(modulePath)}:${depName}`;
|
||||
}
|
||||
|
||||
|
||||
function UnableToResolveError() {
|
||||
Error.call(this);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
var msg = util.format.apply(util, arguments);
|
||||
this.message = msg;
|
||||
this.type = this.name = 'UnableToResolveError';
|
||||
}
|
||||
|
||||
util.inherits(UnableToResolveError, Error);
|
||||
|
||||
|
||||
function normalizePath(modulePath) {
|
||||
if (path.sep === '/') {
|
||||
modulePath = path.normalize(modulePath);
|
||||
} else if (path.posix) {
|
||||
modulePath = path.posix.normalize(modulePath);
|
||||
}
|
||||
|
||||
return modulePath.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
|
||||
module.exports = ResolutionRequest;
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* 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';
|
||||
|
||||
class ResolutionResponse {
|
||||
constructor() {
|
||||
this.dependencies = [];
|
||||
this.asyncDependencies = [];
|
||||
this.mainModuleId = null;
|
||||
this._mappings = Object.create(null);
|
||||
this._finalized = false;
|
||||
}
|
||||
|
||||
_assertNotFinalized() {
|
||||
if (this._finalized) {
|
||||
throw new Error('Attempted to mutate finalized response.');
|
||||
}
|
||||
}
|
||||
|
||||
_assertFinalized() {
|
||||
if (!this._finalized) {
|
||||
throw new Error('Attempted to access unfinalized response.');
|
||||
}
|
||||
}
|
||||
|
||||
finalize() {
|
||||
return this._mainModule.getName().then(id => {
|
||||
this.mainModuleId = id;
|
||||
this._finalized = true;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
pushDependency(module) {
|
||||
this._assertNotFinalized();
|
||||
if (this.dependencies.length === 0) {
|
||||
this._mainModule = module;
|
||||
}
|
||||
|
||||
this.dependencies.push(module);
|
||||
}
|
||||
|
||||
prependDependency(module) {
|
||||
this._assertNotFinalized();
|
||||
this.dependencies.unshift(module);
|
||||
}
|
||||
|
||||
pushAsyncDependency(dependency){
|
||||
this._assertNotFinalized();
|
||||
this.asyncDependencies.push(dependency);
|
||||
}
|
||||
|
||||
setResolvedDependencyPairs(module, pairs) {
|
||||
this._assertNotFinalized();
|
||||
const hash = module.hash();
|
||||
if (this._mappings[hash] == null) {
|
||||
this._mappings[hash] = pairs;
|
||||
}
|
||||
}
|
||||
|
||||
getResolvedDependencyPairs(module) {
|
||||
this._assertFinalized();
|
||||
return this._mappings[module.hash()];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResolutionResponse;
|
|
@ -8,26 +8,13 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
jest
|
||||
.dontMock('../index')
|
||||
.dontMock('crypto')
|
||||
.dontMock('absolute-path')
|
||||
.dontMock('../docblock')
|
||||
.dontMock('../../crawlers')
|
||||
.dontMock('../../crawlers/node')
|
||||
.dontMock('../../replacePatterns')
|
||||
.dontMock('../../../lib/getPlatformExtension')
|
||||
.dontMock('../../../lib/getAssetDataFromName')
|
||||
.dontMock('../../fastfs')
|
||||
.dontMock('../../AssetModule_DEPRECATED')
|
||||
.dontMock('../../AssetModule')
|
||||
.dontMock('../../Module')
|
||||
.dontMock('../../Package')
|
||||
.dontMock('../../ModuleCache');
|
||||
jest.autoMockOff();
|
||||
|
||||
const Promise = require('promise');
|
||||
|
||||
jest.mock('fs');
|
||||
jest
|
||||
.mock('fs')
|
||||
.mock('../../../Cache');
|
||||
|
||||
describe('DependencyGraph', function() {
|
||||
var cache;
|
||||
|
@ -36,12 +23,13 @@ describe('DependencyGraph', function() {
|
|||
var fileWatcher;
|
||||
var fs;
|
||||
|
||||
function getOrderedDependenciesAsJSON(dgraph, entry) {
|
||||
return dgraph.getOrderedDependencies(entry).then(
|
||||
deps => Promise.all(deps.map(dep => Promise.all([
|
||||
function getOrderedDependenciesAsJSON(dgraph, entry, platform) {
|
||||
return dgraph.getDependencies(entry, platform)
|
||||
.then(response => response.finalize())
|
||||
.then(({ dependencies }) => Promise.all(dependencies.map(dep => Promise.all([
|
||||
dep.getName(),
|
||||
dep.getDependencies(),
|
||||
]).then(([name, dependencies]) => ({
|
||||
]).then(([name, moduleDependencies]) => ({
|
||||
path: dep.path,
|
||||
isJSON: dep.isJSON(),
|
||||
isAsset: dep.isAsset(),
|
||||
|
@ -49,7 +37,7 @@ describe('DependencyGraph', function() {
|
|||
isPolyfill: dep.isPolyfill(),
|
||||
resolution: dep.resolution,
|
||||
id: name,
|
||||
dependencies
|
||||
dependencies: moduleDependencies,
|
||||
})))
|
||||
));
|
||||
}
|
||||
|
@ -66,10 +54,10 @@ describe('DependencyGraph', function() {
|
|||
isWatchman: () => Promise.resolve(false)
|
||||
};
|
||||
|
||||
cache = new Cache({});
|
||||
cache = new Cache();
|
||||
});
|
||||
|
||||
describe('getOrderedDependencies', function() {
|
||||
describe('get sync dependencies', function() {
|
||||
pit('should get dependencies', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
|
@ -455,9 +443,7 @@ describe('DependencyGraph', function() {
|
|||
cache: cache,
|
||||
});
|
||||
|
||||
dgraph.setup({ platform: 'ios' });
|
||||
|
||||
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js', 'ios').then(function(deps) {
|
||||
expect(deps)
|
||||
.toEqual([
|
||||
{
|
||||
|
@ -3699,4 +3685,40 @@ describe('DependencyGraph', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAsyncDependencies', () => {
|
||||
pit('should get dependencies', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'System.import("a")'
|
||||
].join('\n'),
|
||||
'a.js': [
|
||||
'/**',
|
||||
' * @providesModule a',
|
||||
' */',
|
||||
].join('\n'),
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
|
||||
return dgraph.getDependencies('/root/index.js')
|
||||
.then(response => response.finalize())
|
||||
.then(({ asyncDependencies }) => {
|
||||
expect(asyncDependencies).toEqual([
|
||||
['/root/a.js']
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,18 +9,20 @@
|
|||
'use strict';
|
||||
|
||||
const Activity = require('../../Activity');
|
||||
const AssetModule_DEPRECATED = require('../AssetModule_DEPRECATED');
|
||||
const Fastfs = require('../fastfs');
|
||||
const ModuleCache = require('../ModuleCache');
|
||||
const Promise = require('promise');
|
||||
const crawl = require('../crawlers');
|
||||
const debug = require('debug')('DependencyGraph');
|
||||
const declareOpts = require('../../lib/declareOpts');
|
||||
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
|
||||
const getPontentialPlatformExt = require('../../lib/getPlatformExtension');
|
||||
const isAbsolutePath = require('absolute-path');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
const Helpers = require('./Helpers');
|
||||
const ResolutionRequest = require('./ResolutionRequest');
|
||||
const ResolutionResponse = require('./ResolutionResponse');
|
||||
const HasteMap = require('./HasteMap');
|
||||
const DeprecatedAssetMap = require('./DeprecatedAssetMap');
|
||||
|
||||
const validateOpts = declareOpts({
|
||||
roots: {
|
||||
|
@ -69,9 +71,8 @@ const validateOpts = declareOpts({
|
|||
class DependencyGraph {
|
||||
constructor(options) {
|
||||
this._opts = validateOpts(options);
|
||||
this._hasteMap = Object.create(null);
|
||||
this._resetResolutionCache();
|
||||
this._cache = this._opts.cache;
|
||||
this._helpers = new Helpers(this._opts);
|
||||
this.load();
|
||||
}
|
||||
|
||||
|
@ -104,13 +105,29 @@ class DependencyGraph {
|
|||
|
||||
this._moduleCache = new ModuleCache(this._fastfs, this._cache);
|
||||
|
||||
this._hasteMap = new HasteMap({
|
||||
fastfs: this._fastfs,
|
||||
moduleCache: this._moduleCache,
|
||||
assetExts: this._opts.exts,
|
||||
helpers: this._helpers,
|
||||
});
|
||||
|
||||
this._deprecatedAssetMap = new DeprecatedAssetMap({
|
||||
fsCrawl: this._crawling,
|
||||
roots: this._opts.assetRoots_DEPRECATED,
|
||||
helpers: this._helpers,
|
||||
fileWatcher: this._opts.fileWatcher,
|
||||
ignoreFilePath: this._opts.ignoreFilePath,
|
||||
assetExts: this._opts.assetExts,
|
||||
});
|
||||
|
||||
this._loading = Promise.all([
|
||||
this._fastfs.build()
|
||||
.then(() => {
|
||||
const hasteActivity = Activity.startEvent('Building Haste Map');
|
||||
return this._buildHasteMap().then(() => Activity.endEvent(hasteActivity));
|
||||
return this._hasteMap.build().then(() => Activity.endEvent(hasteActivity));
|
||||
}),
|
||||
this._buildAssetMap_DEPRECATED(),
|
||||
this._deprecatedAssetMap.build(),
|
||||
]).then(() =>
|
||||
Activity.endEvent(depGraphActivity)
|
||||
);
|
||||
|
@ -118,537 +135,74 @@ class DependencyGraph {
|
|||
return this._loading;
|
||||
}
|
||||
|
||||
setup({ platform }) {
|
||||
if (platform && this._opts.platforms.indexOf(platform) === -1) {
|
||||
getDependencies(entryPath, platform) {
|
||||
return this.load().then(() => {
|
||||
platform = this._getRequestPlatform(entryPath, platform);
|
||||
const absPath = this._getAbsolutePath(entryPath);
|
||||
const req = new ResolutionRequest({
|
||||
platform,
|
||||
entryPath: absPath,
|
||||
deprecatedAssetMap: this._deprecatedAssetMap,
|
||||
hasteMap: this._hasteMap,
|
||||
helpers: this._helpers,
|
||||
moduleCache: this._moduleCache,
|
||||
fastfs: this._fastfs,
|
||||
});
|
||||
|
||||
const response = new ResolutionResponse();
|
||||
|
||||
return Promise.all([
|
||||
req.getOrderedDependencies(response),
|
||||
req.getAsyncDependencies(response),
|
||||
]).then(() => response);
|
||||
});
|
||||
}
|
||||
|
||||
_getRequestPlatform(entryPath, platform) {
|
||||
if (platform == null) {
|
||||
platform = getPontentialPlatformExt(entryPath);
|
||||
if (platform == null || this._opts.platforms.indexOf(platform) === -1) {
|
||||
platform = null;
|
||||
}
|
||||
} else if (this._opts.platforms.indexOf(platform) === -1) {
|
||||
throw new Error('Unrecognized platform: ' + platform);
|
||||
}
|
||||
|
||||
// TODO(amasad): This is a potential race condition. Mutliple requests could
|
||||
// interfere with each other. This needs a refactor to fix -- which will
|
||||
// follow this diff.
|
||||
if (this._platformExt !== platform) {
|
||||
this._resetResolutionCache();
|
||||
}
|
||||
this._platformExt = platform;
|
||||
}
|
||||
|
||||
resolveDependency(fromModule, toModuleName) {
|
||||
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 entry = this._getModuleForEntryPath(entryPath);
|
||||
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(() => deps);
|
||||
});
|
||||
}
|
||||
|
||||
getAsyncDependencies(entryPath) {
|
||||
return this.load().then(() => {
|
||||
const mod = this._getModuleForEntryPath(entryPath);
|
||||
return mod.getAsyncDependencies().then(bundles =>
|
||||
Promise
|
||||
.all(bundles.map(bundle =>
|
||||
Promise.all(bundle.map(
|
||||
dep => this.resolveDependency(mod, dep)
|
||||
))
|
||||
))
|
||||
.then(bs => bs.map(bundle => bundle.map(dep => dep.path)))
|
||||
);
|
||||
});
|
||||
return platform;
|
||||
}
|
||||
|
||||
_getAbsolutePath(filePath) {
|
||||
if (isAbsolutePath(filePath)) {
|
||||
return filePath;
|
||||
return path.resolve(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;
|
||||
const potentialAbsPath = path.join(root, filePath);
|
||||
if (this._fastfs.fileExists(potentialAbsPath)) {
|
||||
return path.resolve(potentialAbsPath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
_getModuleForEntryPath(entryPath) {
|
||||
const absPath = this._getAbsolutePath(entryPath);
|
||||
|
||||
if (absPath == null) {
|
||||
throw new NotFoundError(
|
||||
'Could not find source file at %s',
|
||||
entryPath
|
||||
);
|
||||
}
|
||||
|
||||
const absolutePath = path.resolve(absPath);
|
||||
|
||||
if (absolutePath == null) {
|
||||
throw new NotFoundError(
|
||||
'Cannot find entry file %s in any of the roots: %j',
|
||||
entryPath,
|
||||
filePath,
|
||||
this._opts.roots
|
||||
);
|
||||
}
|
||||
|
||||
// `platformExt` could be set in the `setup` method.
|
||||
if (!this._platformExt) {
|
||||
const platformExt = getPontentialPlatformExt(entryPath);
|
||||
if (platformExt && this._opts.platforms.indexOf(platformExt) > -1) {
|
||||
this._platformExt = platformExt;
|
||||
} else {
|
||||
this._platformExt = null;
|
||||
}
|
||||
}
|
||||
|
||||
return this._moduleCache.getModule(absolutePath);
|
||||
}
|
||||
|
||||
_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._getHasteModule(realModuleName);
|
||||
if (dep && dep.type === 'Module') {
|
||||
return dep;
|
||||
}
|
||||
|
||||
let packageName = realModuleName;
|
||||
while (packageName && packageName !== '.') {
|
||||
dep = this._getHasteModule(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);
|
||||
|
||||
let pattern = '^' + name + '(@[\\d\\.]+x)?';
|
||||
if (this._platformExt != null) {
|
||||
pattern += '(\\.' + this._platformExt + ')?';
|
||||
}
|
||||
pattern += '\\.' + type;
|
||||
|
||||
// We arbitrarly grab the first one, because scale selection
|
||||
// will happen somewhere
|
||||
const [assetFile] = this._fastfs.matches(
|
||||
path.dirname(potentialModulePath),
|
||||
new RegExp(pattern)
|
||||
);
|
||||
|
||||
if (assetFile) {
|
||||
return this._moduleCache.getAssetModule(assetFile);
|
||||
}
|
||||
}
|
||||
|
||||
let file;
|
||||
if (this._fastfs.fileExists(potentialModulePath)) {
|
||||
file = potentialModulePath;
|
||||
} else if (this._platformExt != null &&
|
||||
this._fastfs.fileExists(potentialModulePath + '.' + this._platformExt + '.js')) {
|
||||
file = potentialModulePath + '.' + this._platformExt + '.js';
|
||||
} 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] == null) {
|
||||
this._hasteMap[name] = [];
|
||||
}
|
||||
|
||||
if (mod.type === 'Module') {
|
||||
// Modules takes precendence over packages.
|
||||
this._hasteMap[name].unshift(mod);
|
||||
} else {
|
||||
this._hasteMap[name].push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
_getHasteModule(name) {
|
||||
if (this._hasteMap[name]) {
|
||||
const modules = this._hasteMap[name];
|
||||
if (this._platformExt != null) {
|
||||
for (let i = 0; i < modules.length; i++) {
|
||||
if (getPontentialPlatformExt(modules[i].path) === this._platformExt) {
|
||||
return modules[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_isNodeModulesDir(file) {
|
||||
let parts = path.normalize(file).split(path.sep);
|
||||
const indexOfNodeModules = parts.lastIndexOf('node_modules');
|
||||
|
||||
if (indexOfNodeModules === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parts = parts.slice(indexOfNodeModules + 1);
|
||||
|
||||
const dirs = this._opts.providesModuleNodeModules;
|
||||
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
if (parts.indexOf(dirs[i]) > -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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 fastfs = new Fastfs(
|
||||
'Assets',
|
||||
this._opts.assetRoots_DEPRECATED,
|
||||
this._opts.fileWatcher,
|
||||
{ ignore: this._opts.ignoreFilePath, crawling: this._crawling }
|
||||
);
|
||||
|
||||
fastfs.on('change', this._processAssetChange_DEPRECATED.bind(this));
|
||||
|
||||
return fastfs.build().then(
|
||||
() => {
|
||||
const processAsset_DEPRECATEDActivity = Activity.startEvent(
|
||||
'Building (deprecated) Asset Map',
|
||||
);
|
||||
|
||||
const assets = fastfs.findFilesByExts(this._opts.assetExts).map(
|
||||
file => this._processAsset_DEPRECATED(file)
|
||||
);
|
||||
|
||||
Activity.endEvent(processAsset_DEPRECATEDActivity);
|
||||
return assets;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_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._resetResolutionCache();
|
||||
|
||||
const absPath = path.join(root, filePath);
|
||||
if ((fstat && fstat.isDirectory()) ||
|
||||
if (fstat && fstat.isDirectory() ||
|
||||
this._opts.ignoreFilePath(absPath) ||
|
||||
this._isNodeModulesDir(absPath)) {
|
||||
this._helpers.isNodeModulesDir(absPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*eslint no-labels: 0 */
|
||||
if (type === 'delete' || type === 'change') {
|
||||
loop: for (let name in this._hasteMap) {
|
||||
let modules = this._hasteMap[name];
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
if (modules[i].path === absPath) {
|
||||
modules.splice(i, 1);
|
||||
break loop;
|
||||
this._loading = this._loading.then(
|
||||
() => this._hasteMap.processFileChange(type, absPath)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_resetResolutionCache() {
|
||||
this._immediateResolutionCache = Object.create(null);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -658,17 +212,6 @@ function NotFoundError() {
|
|||
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;
|
||||
|
|
|
@ -34,6 +34,22 @@ describe('HasteDependencyResolver', function() {
|
|||
HasteDependencyResolver = require('../');
|
||||
});
|
||||
|
||||
class ResolutionResponseMock {
|
||||
constructor({dependencies, mainModuleId, asyncDependencies}) {
|
||||
this.dependencies = dependencies;
|
||||
this.mainModuleId = mainModuleId;
|
||||
this.asyncDependencies = asyncDependencies;
|
||||
}
|
||||
|
||||
prependDependency(dependency) {
|
||||
this.dependencies.unshift(dependency);
|
||||
}
|
||||
|
||||
finalize() {
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
function createModule(id, dependencies) {
|
||||
var module = new Module();
|
||||
module.getName.mockImpl(() => Promise.resolve(id));
|
||||
|
@ -52,11 +68,12 @@ describe('HasteDependencyResolver', function() {
|
|||
|
||||
// Is there a better way? How can I mock the prototype instead?
|
||||
var depGraph = depResolver._depGraph;
|
||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||
return Promise.resolve(deps);
|
||||
});
|
||||
depGraph.load.mockImpl(function() {
|
||||
return Promise.resolve();
|
||||
depGraph.getDependencies.mockImpl(function() {
|
||||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
return depResolver.getDependencies('/root/index.js', { dev: false })
|
||||
|
@ -133,19 +150,19 @@ describe('HasteDependencyResolver', function() {
|
|||
projectRoot: '/root',
|
||||
});
|
||||
|
||||
// Is there a better way? How can I mock the prototype instead?
|
||||
var depGraph = depResolver._depGraph;
|
||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||
return Promise.resolve(deps);
|
||||
});
|
||||
depGraph.load.mockImpl(function() {
|
||||
return Promise.resolve();
|
||||
depGraph.getDependencies.mockImpl(function() {
|
||||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
return depResolver.getDependencies('/root/index.js', { dev: true })
|
||||
.then(function(result) {
|
||||
expect(result.mainModuleId).toEqual('index');
|
||||
expect(depGraph.getOrderedDependencies).toBeCalledWith('/root/index.js');
|
||||
expect(depGraph.getDependencies).toBeCalledWith('/root/index.js', undefined);
|
||||
expect(result.dependencies[0]).toBe(Polyfill.mock.instances[0]);
|
||||
expect(result.dependencies[result.dependencies.length - 1])
|
||||
.toBe(module);
|
||||
|
@ -161,13 +178,13 @@ describe('HasteDependencyResolver', function() {
|
|||
polyfillModuleNames: ['some module'],
|
||||
});
|
||||
|
||||
// Is there a better way? How can I mock the prototype instead?
|
||||
var depGraph = depResolver._depGraph;
|
||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||
return Promise.resolve(deps);
|
||||
});
|
||||
depGraph.load.mockImpl(function() {
|
||||
return Promise.resolve();
|
||||
depGraph.getDependencies.mockImpl(function() {
|
||||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
return depResolver.getDependencies('/root/index.js', { dev: false })
|
||||
|
@ -343,17 +360,23 @@ describe('HasteDependencyResolver', function() {
|
|||
].join('\n');
|
||||
/*eslint-disable */
|
||||
|
||||
depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) {
|
||||
if (toModuleName === 'x') {
|
||||
return Promise.resolve(createModule('changed'));
|
||||
} else if (toModuleName === 'y') {
|
||||
return Promise.resolve(createModule('Y'));
|
||||
}
|
||||
const module = createModule('test module', ['x', 'y']);
|
||||
|
||||
return Promise.resolve(null);
|
||||
const resolutionResponse = new ResolutionResponseMock({
|
||||
dependencies: [module],
|
||||
mainModuleId: 'test module',
|
||||
asyncDependencies: [],
|
||||
});
|
||||
|
||||
resolutionResponse.getResolvedDependencyPairs = (module) => {
|
||||
return [
|
||||
['x', createModule('changed')],
|
||||
['y', createModule('Y')],
|
||||
];
|
||||
}
|
||||
|
||||
return depResolver.wrapModule(
|
||||
resolutionResponse,
|
||||
createModule('test module', ['x', 'y']),
|
||||
code
|
||||
).then(processedCode => {
|
||||
|
|
|
@ -13,6 +13,8 @@ const stat = Promise.denodeify(fs.stat);
|
|||
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
const NOT_FOUND_IN_ROOTS = 'NotFoundInRootsError';
|
||||
|
||||
class Fastfs extends EventEmitter {
|
||||
constructor(name, roots, fileWatcher, {ignore, crawling}) {
|
||||
super();
|
||||
|
@ -90,7 +92,11 @@ class Fastfs extends EventEmitter {
|
|||
}
|
||||
|
||||
readFile(filePath) {
|
||||
return this._getFile(filePath).read();
|
||||
const file = this._getFile(filePath);
|
||||
if (!file) {
|
||||
throw new Error(`Unable to find file with path: ${file}`);
|
||||
}
|
||||
return file.read();
|
||||
}
|
||||
|
||||
closest(filePath, name) {
|
||||
|
@ -105,12 +111,30 @@ class Fastfs extends EventEmitter {
|
|||
}
|
||||
|
||||
fileExists(filePath) {
|
||||
const file = this._getFile(filePath);
|
||||
let file;
|
||||
try {
|
||||
file = this._getFile(filePath);
|
||||
} catch (e) {
|
||||
if (e.type === NOT_FOUND_IN_ROOTS) {
|
||||
return false;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return file && !file.isDir;
|
||||
}
|
||||
|
||||
dirExists(filePath) {
|
||||
const file = this._getFile(filePath);
|
||||
let file;
|
||||
try {
|
||||
file = this._getFile(filePath);
|
||||
} catch (e) {
|
||||
if (e.type === NOT_FOUND_IN_ROOTS) {
|
||||
return false;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return file && file.isDir;
|
||||
}
|
||||
|
||||
|
@ -138,7 +162,9 @@ class Fastfs extends EventEmitter {
|
|||
_getAndAssertRoot(filePath) {
|
||||
const root = this._getRoot(filePath);
|
||||
if (!root) {
|
||||
throw new Error(`File ${filePath} not found in any of the roots`);
|
||||
const error = new Error(`File ${filePath} not found in any of the roots`);
|
||||
error.type = NOT_FOUND_IN_ROOTS;
|
||||
throw error;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
|
|
@ -82,36 +82,18 @@ var getDependenciesValidateOpts = declareOpts({
|
|||
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
|
||||
var opts = getDependenciesValidateOpts(options);
|
||||
|
||||
var depGraph = this._depGraph;
|
||||
var self = this;
|
||||
|
||||
depGraph.setup({ platform: opts.platform });
|
||||
|
||||
return Promise.all([
|
||||
depGraph.getOrderedDependencies(main),
|
||||
depGraph.getAsyncDependencies(main),
|
||||
]).then(
|
||||
([dependencies, asyncDependencies]) => dependencies[0].getName().then(
|
||||
mainModuleId => {
|
||||
self._prependPolyfillDependencies(
|
||||
dependencies,
|
||||
opts.dev,
|
||||
return this._depGraph.getDependencies(main, opts.platform).then(
|
||||
resolutionResponse => {
|
||||
this._getPolyfillDependencies(opts.dev).reverse().forEach(
|
||||
polyfill => resolutionResponse.prependDependency(polyfill)
|
||||
);
|
||||
|
||||
return {
|
||||
mainModuleId,
|
||||
dependencies,
|
||||
asyncDependencies,
|
||||
};
|
||||
return resolutionResponse.finalize();
|
||||
}
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
||||
dependencies,
|
||||
isDev
|
||||
) {
|
||||
HasteDependencyResolver.prototype._getPolyfillDependencies = function(isDev) {
|
||||
var polyfillModuleNames = [
|
||||
isDev
|
||||
? path.join(__dirname, 'polyfills/prelude_dev.js')
|
||||
|
@ -124,7 +106,7 @@ HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
|||
path.join(__dirname, 'polyfills/Array.prototype.es6.js'),
|
||||
].concat(this._polyfillModuleNames);
|
||||
|
||||
var polyfillModules = polyfillModuleNames.map(
|
||||
return polyfillModuleNames.map(
|
||||
(polyfillModuleName, idx) => new Polyfill({
|
||||
path: polyfillModuleName,
|
||||
id: polyfillModuleName,
|
||||
|
@ -132,11 +114,10 @@ HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
|||
isPolyfill: true,
|
||||
})
|
||||
);
|
||||
|
||||
dependencies.unshift.apply(dependencies, polyfillModules);
|
||||
};
|
||||
|
||||
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||
HasteDependencyResolver.prototype.wrapModule = function(resolutionResponse, module, code) {
|
||||
return Promise.resolve().then(() => {
|
||||
if (module.isPolyfill()) {
|
||||
return Promise.resolve(code);
|
||||
}
|
||||
|
@ -144,18 +125,16 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
|||
const resolvedDeps = Object.create(null);
|
||||
const resolvedDepsArr = [];
|
||||
|
||||
return module.getDependencies().then(
|
||||
dependencies => Promise.all(dependencies.map(
|
||||
depName => this._depGraph.resolveDependency(module, depName)
|
||||
.then(depModule => {
|
||||
return Promise.all(
|
||||
resolutionResponse.getResolvedDependencyPairs(module).map(
|
||||
([depName, depModule]) => {
|
||||
if (depModule) {
|
||||
return depModule.getName().then(name => {
|
||||
resolvedDeps[depName] = name;
|
||||
resolvedDepsArr.push(name);
|
||||
});
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
).then(() => {
|
||||
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
|
||||
|
@ -169,14 +148,14 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
|||
|
||||
return module.getName().then(
|
||||
name => defineModuleCode({
|
||||
code: code
|
||||
.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
||||
code: code.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
||||
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
|
||||
deps: JSON.stringify(resolvedDepsArr),
|
||||
moduleName: name,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
HasteDependencyResolver.prototype.getDebugInfo = function() {
|
||||
|
|
Loading…
Reference in New Issue