mirror of https://github.com/status-im/metro.git
Sync new haste features from upstream
Reviewed By: amasad Differential Revision: D2644383 fb-gh-sync-id: 5225e6afb8e5b835865473b2880a77235e6bd6eb
This commit is contained in:
parent
ccea74fc87
commit
a8ded758d0
|
@ -15,7 +15,8 @@ const Promise = require('promise');
|
||||||
const GENERIC_PLATFORM = 'generic';
|
const GENERIC_PLATFORM = 'generic';
|
||||||
|
|
||||||
class HasteMap {
|
class HasteMap {
|
||||||
constructor({ fastfs, moduleCache, helpers }) {
|
constructor({ extensions, fastfs, moduleCache, helpers }) {
|
||||||
|
this._extensions = extensions;
|
||||||
this._fastfs = fastfs;
|
this._fastfs = fastfs;
|
||||||
this._moduleCache = moduleCache;
|
this._moduleCache = moduleCache;
|
||||||
this._helpers = helpers;
|
this._helpers = helpers;
|
||||||
|
@ -24,7 +25,7 @@ class HasteMap {
|
||||||
build() {
|
build() {
|
||||||
this._map = Object.create(null);
|
this._map = Object.create(null);
|
||||||
|
|
||||||
let promises = this._fastfs.findFilesByExt('js', {
|
let promises = this._fastfs.findFilesByExts(this._extensions, {
|
||||||
ignore: (file) => this._helpers.isNodeModulesDir(file),
|
ignore: (file) => this._helpers.isNodeModulesDir(file),
|
||||||
}).map(file => this._processHasteModule(file));
|
}).map(file => this._processHasteModule(file));
|
||||||
|
|
||||||
|
@ -57,8 +58,7 @@ class HasteMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._helpers.extname(absPath) === 'js' ||
|
if (this._extensions.indexOf(this._helpers.extname(absPath)) !== -1) {
|
||||||
this._helpers.extname(absPath) === 'json') {
|
|
||||||
if (path.basename(absPath) === 'package.json') {
|
if (path.basename(absPath) === 'package.json') {
|
||||||
return this._processHastePackage(absPath);
|
return this._processHastePackage(absPath);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -97,8 +97,10 @@ class ResolutionRequest {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrderedDependencies(response) {
|
getOrderedDependencies(response, mocksPattern) {
|
||||||
return Promise.resolve().then(() => {
|
return this._getAllMocks(mocksPattern).then(mocks => {
|
||||||
|
response.setMocks(mocks);
|
||||||
|
|
||||||
const entry = this._moduleCache.getModule(this._entryPath);
|
const entry = this._moduleCache.getModule(this._entryPath);
|
||||||
const visited = Object.create(null);
|
const visited = Object.create(null);
|
||||||
visited[entry.hash()] = true;
|
visited[entry.hash()] = true;
|
||||||
|
@ -110,8 +112,20 @@ class ResolutionRequest {
|
||||||
depNames.map(name => this.resolveDependency(mod, name))
|
depNames.map(name => this.resolveDependency(mod, name))
|
||||||
).then((dependencies) => [depNames, dependencies])
|
).then((dependencies) => [depNames, dependencies])
|
||||||
).then(([depNames, dependencies]) => {
|
).then(([depNames, dependencies]) => {
|
||||||
|
if (mocks) {
|
||||||
|
return mod.getName().then(name => {
|
||||||
|
if (mocks[name]) {
|
||||||
|
const mockModule =
|
||||||
|
this._moduleCache.getModule(mocks[name]);
|
||||||
|
depNames.push(name);
|
||||||
|
dependencies.push(mockModule);
|
||||||
|
}
|
||||||
|
return [depNames, dependencies];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve([depNames, dependencies]);
|
||||||
|
}).then(([depNames, dependencies]) => {
|
||||||
let p = Promise.resolve();
|
let p = Promise.resolve();
|
||||||
|
|
||||||
const filteredPairs = [];
|
const filteredPairs = [];
|
||||||
|
|
||||||
dependencies.forEach((modDep, i) => {
|
dependencies.forEach((modDep, i) => {
|
||||||
|
@ -163,6 +177,20 @@ class ResolutionRequest {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getAllMocks(pattern) {
|
||||||
|
// Take all mocks in all the roots into account. This is necessary
|
||||||
|
// because currently mocks are global: any module can be mocked by
|
||||||
|
// any mock in the system.
|
||||||
|
let mocks = null;
|
||||||
|
if (pattern) {
|
||||||
|
mocks = Object.create(null);
|
||||||
|
this._fastfs.matchFilesByPattern(pattern).forEach(file =>
|
||||||
|
mocks[path.basename(file, path.extname(file))] = file
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Promise.resolve(mocks);
|
||||||
|
}
|
||||||
|
|
||||||
_resolveHasteDependency(fromModule, toModuleName) {
|
_resolveHasteDependency(fromModule, toModuleName) {
|
||||||
toModuleName = normalizePath(toModuleName);
|
toModuleName = normalizePath(toModuleName);
|
||||||
|
|
||||||
|
@ -349,6 +377,7 @@ class ResolutionRequest {
|
||||||
_resetResolutionCache() {
|
_resetResolutionCache() {
|
||||||
this._immediateResolutionCache = Object.create(null);
|
this._immediateResolutionCache = Object.create(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ class ResolutionResponse {
|
||||||
this.dependencies = [];
|
this.dependencies = [];
|
||||||
this.asyncDependencies = [];
|
this.asyncDependencies = [];
|
||||||
this.mainModuleId = null;
|
this.mainModuleId = null;
|
||||||
|
this.mocks = null;
|
||||||
this._mappings = Object.create(null);
|
this._mappings = Object.create(null);
|
||||||
this._finalized = false;
|
this._finalized = false;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +65,10 @@ class ResolutionResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setMocks(mocks) {
|
||||||
|
this.mocks = mocks;
|
||||||
|
}
|
||||||
|
|
||||||
getResolvedDependencyPairs(module) {
|
getResolvedDependencyPairs(module) {
|
||||||
this._assertFinalized();
|
this._assertFinalized();
|
||||||
return this._mappings[module.hash()];
|
return this._mappings[module.hash()];
|
||||||
|
|
|
@ -3676,4 +3676,184 @@ describe('DependencyGraph', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Extensions', () => {
|
||||||
|
pit('supports custom file extensions', () => {
|
||||||
|
var root = '/root';
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.jsx': [
|
||||||
|
'/**',
|
||||||
|
' * @providesModule index',
|
||||||
|
' */',
|
||||||
|
'require("a")',
|
||||||
|
].join('\n'),
|
||||||
|
'a.coffee': [
|
||||||
|
'/**',
|
||||||
|
' * @providesModule a',
|
||||||
|
' */',
|
||||||
|
].join('\n'),
|
||||||
|
'X.js': '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var dgraph = new DependencyGraph({
|
||||||
|
...defaults,
|
||||||
|
roots: [root],
|
||||||
|
extensions: ['jsx', 'coffee'],
|
||||||
|
});
|
||||||
|
|
||||||
|
return dgraph.matchFilesByPattern('.*')
|
||||||
|
.then(files => {
|
||||||
|
expect(files).toEqual([
|
||||||
|
'/root/index.jsx', '/root/a.coffee',
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
.then(() => getOrderedDependenciesAsJSON(dgraph, '/root/index.jsx'))
|
||||||
|
.then(deps => {
|
||||||
|
expect(deps).toEqual([
|
||||||
|
{
|
||||||
|
dependencies: ['a'],
|
||||||
|
id: 'index',
|
||||||
|
isAsset: false,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isJSON: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
path: '/root/index.jsx',
|
||||||
|
resolution: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dependencies: [],
|
||||||
|
id: 'a',
|
||||||
|
isAsset: false,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isJSON: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
path: '/root/a.coffee',
|
||||||
|
resolution: undefined,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Mocks', () => {
|
||||||
|
pit('resolves to null if mocksPattern is not specified', () => {
|
||||||
|
var root = '/root';
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'__mocks': {
|
||||||
|
'A.js': '',
|
||||||
|
},
|
||||||
|
'index.js': '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var dgraph = new DependencyGraph({
|
||||||
|
...defaults,
|
||||||
|
roots: [root],
|
||||||
|
});
|
||||||
|
|
||||||
|
return dgraph.getDependencies('/root/index.js')
|
||||||
|
.then(response => response.finalize())
|
||||||
|
.then(response => {
|
||||||
|
expect(response.mocks).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('retrieves a list of all mocks in the system', () => {
|
||||||
|
var root = '/root';
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'__mocks__': {
|
||||||
|
'A.js': '',
|
||||||
|
'b.js': '',
|
||||||
|
},
|
||||||
|
'b.js': [
|
||||||
|
'/**',
|
||||||
|
' * @providesModule b',
|
||||||
|
' */',
|
||||||
|
].join('\n'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var dgraph = new DependencyGraph({
|
||||||
|
...defaults,
|
||||||
|
roots: [root],
|
||||||
|
mocksPattern: /(?:[\\/]|^)__mocks__[\\/]([^\/]+)\.js$/,
|
||||||
|
});
|
||||||
|
|
||||||
|
return dgraph.getDependencies('/root/b.js')
|
||||||
|
.then(response => response.finalize())
|
||||||
|
.then(response => {
|
||||||
|
expect(response.mocks).toEqual({
|
||||||
|
A: '/root/__mocks__/A.js',
|
||||||
|
b: '/root/__mocks__/b.js',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
pit('adds mocks as a dependency of their actual module', () => {
|
||||||
|
var root = '/root';
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'__mocks__': {
|
||||||
|
'A.js': [
|
||||||
|
'require("b");',
|
||||||
|
].join('\n'),
|
||||||
|
'b.js': '',
|
||||||
|
},
|
||||||
|
'A.js': [
|
||||||
|
'/**',
|
||||||
|
' * @providesModule A',
|
||||||
|
' */',
|
||||||
|
'require("foo");',
|
||||||
|
].join('\n'),
|
||||||
|
'foo.js': [
|
||||||
|
'/**',
|
||||||
|
' * @providesModule foo',
|
||||||
|
' */',
|
||||||
|
].join('\n'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var dgraph = new DependencyGraph({
|
||||||
|
...defaults,
|
||||||
|
roots: [root],
|
||||||
|
mocksPattern: /(?:[\\/]|^)__mocks__[\\/]([^\/]+)\.js$/,
|
||||||
|
});
|
||||||
|
|
||||||
|
return getOrderedDependenciesAsJSON(dgraph, '/root/A.js')
|
||||||
|
.then(deps => {
|
||||||
|
expect(deps).toEqual([
|
||||||
|
{
|
||||||
|
path: '/root/A.js',
|
||||||
|
isJSON: false,
|
||||||
|
isAsset: false,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
id: 'A',
|
||||||
|
dependencies: ['foo', 'A'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/root/foo.js',
|
||||||
|
isJSON: false,
|
||||||
|
isAsset: false,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
id: 'foo',
|
||||||
|
dependencies: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/root/__mocks__/A.js',
|
||||||
|
isJSON: false,
|
||||||
|
isAsset: false,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
id: '/root/__mocks__/A.js',
|
||||||
|
dependencies: ['b'],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,6 +38,9 @@ class DependencyGraph {
|
||||||
providesModuleNodeModules,
|
providesModuleNodeModules,
|
||||||
platforms,
|
platforms,
|
||||||
cache,
|
cache,
|
||||||
|
extensions,
|
||||||
|
mocksPattern,
|
||||||
|
extractRequires,
|
||||||
}) {
|
}) {
|
||||||
this._opts = {
|
this._opts = {
|
||||||
activity: activity || defaultActivity,
|
activity: activity || defaultActivity,
|
||||||
|
@ -49,6 +52,9 @@ class DependencyGraph {
|
||||||
providesModuleNodeModules,
|
providesModuleNodeModules,
|
||||||
platforms: platforms || [],
|
platforms: platforms || [],
|
||||||
cache,
|
cache,
|
||||||
|
extensions: extensions || ['js', 'json'],
|
||||||
|
mocksPattern,
|
||||||
|
extractRequires,
|
||||||
};
|
};
|
||||||
this._cache = this._opts.cache;
|
this._cache = this._opts.cache;
|
||||||
this._helpers = new Helpers(this._opts);
|
this._helpers = new Helpers(this._opts);
|
||||||
|
@ -70,7 +76,7 @@ class DependencyGraph {
|
||||||
const allRoots = this._opts.roots.concat(this._opts.assetRoots_DEPRECATED);
|
const allRoots = this._opts.roots.concat(this._opts.assetRoots_DEPRECATED);
|
||||||
this._crawling = crawl(allRoots, {
|
this._crawling = crawl(allRoots, {
|
||||||
ignore: this._opts.ignoreFilePath,
|
ignore: this._opts.ignoreFilePath,
|
||||||
exts: ['js', 'json'].concat(this._opts.assetExts),
|
exts: this._opts.extensions.concat(this._opts.assetExts),
|
||||||
fileWatcher: this._opts.fileWatcher,
|
fileWatcher: this._opts.fileWatcher,
|
||||||
});
|
});
|
||||||
this._crawling.then((files) => activity.endEvent(crawlActivity));
|
this._crawling.then((files) => activity.endEvent(crawlActivity));
|
||||||
|
@ -88,10 +94,15 @@ class DependencyGraph {
|
||||||
|
|
||||||
this._fastfs.on('change', this._processFileChange.bind(this));
|
this._fastfs.on('change', this._processFileChange.bind(this));
|
||||||
|
|
||||||
this._moduleCache = new ModuleCache(this._fastfs, this._cache);
|
this._moduleCache = new ModuleCache(
|
||||||
|
this._fastfs,
|
||||||
|
this._cache,
|
||||||
|
this._opts.extractRequires
|
||||||
|
);
|
||||||
|
|
||||||
this._hasteMap = new HasteMap({
|
this._hasteMap = new HasteMap({
|
||||||
fastfs: this._fastfs,
|
fastfs: this._fastfs,
|
||||||
|
extensions: this._opts.extensions,
|
||||||
moduleCache: this._moduleCache,
|
moduleCache: this._moduleCache,
|
||||||
assetExts: this._opts.exts,
|
assetExts: this._opts.exts,
|
||||||
helpers: this._helpers,
|
helpers: this._helpers,
|
||||||
|
@ -138,12 +149,16 @@ class DependencyGraph {
|
||||||
const response = new ResolutionResponse();
|
const response = new ResolutionResponse();
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
req.getOrderedDependencies(response),
|
req.getOrderedDependencies(response, this._opts.mocksPattern),
|
||||||
req.getAsyncDependencies(response),
|
req.getAsyncDependencies(response),
|
||||||
]).then(() => response);
|
]).then(() => response);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matchFilesByPattern(pattern) {
|
||||||
|
return this.load().then(() => this._fastfs.matchFilesByPattern(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
_getRequestPlatform(entryPath, platform) {
|
_getRequestPlatform(entryPath, platform) {
|
||||||
if (platform == null) {
|
if (platform == null) {
|
||||||
platform = getPlatformExtension(entryPath);
|
platform = getPlatformExtension(entryPath);
|
||||||
|
@ -208,6 +223,7 @@ class DependencyGraph {
|
||||||
return this._loading;
|
return this._loading;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function NotFoundError() {
|
function NotFoundError() {
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
const docblock = require('./DependencyGraph/docblock');
|
const docblock = require('./DependencyGraph/docblock');
|
||||||
const isAbsolutePath = require('absolute-path');
|
const isAbsolutePath = require('absolute-path');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const replacePatterns = require('./replacePatterns');
|
const extractRequires = require('./lib/extractRequires');
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
|
|
||||||
constructor(file, fastfs, moduleCache, cache) {
|
constructor(file, fastfs, moduleCache, cache, extractor) {
|
||||||
if (!isAbsolutePath(file)) {
|
if (!isAbsolutePath(file)) {
|
||||||
throw new Error('Expected file to be absolute path but got ' + file);
|
throw new Error('Expected file to be absolute path but got ' + file);
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,11 @@ class Module {
|
||||||
this._fastfs = fastfs;
|
this._fastfs = fastfs;
|
||||||
this._moduleCache = moduleCache;
|
this._moduleCache = moduleCache;
|
||||||
this._cache = cache;
|
this._cache = cache;
|
||||||
|
this._extractor = extractor;
|
||||||
}
|
}
|
||||||
|
|
||||||
isHaste() {
|
isHaste() {
|
||||||
return this._cache.get(this.path, 'haste', () =>
|
return this._read().then(data => !!data.id);
|
||||||
this._read().then(data => !!data.id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getName() {
|
getName() {
|
||||||
|
@ -67,19 +66,17 @@ class Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDependencies() {
|
getDependencies() {
|
||||||
return this._cache.get(this.path, 'dependencies', () =>
|
return this._read().then(data => data.dependencies);
|
||||||
this._read().then(data => data.dependencies)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidate() {
|
|
||||||
this._cache.invalidate(this.path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAsyncDependencies() {
|
getAsyncDependencies() {
|
||||||
return this._read().then(data => data.asyncDependencies);
|
return this._read().then(data => data.asyncDependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidate() {
|
||||||
|
this._cache.invalidate(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
_read() {
|
_read() {
|
||||||
if (!this._reading) {
|
if (!this._reading) {
|
||||||
this._reading = this._fastfs.readFile(this.path).then(content => {
|
this._reading = this._fastfs.readFile(this.path).then(content => {
|
||||||
|
@ -96,7 +93,7 @@ class Module {
|
||||||
if ('extern' in moduleDocBlock) {
|
if ('extern' in moduleDocBlock) {
|
||||||
data.dependencies = [];
|
data.dependencies = [];
|
||||||
} else {
|
} else {
|
||||||
var dependencies = extractRequires(content);
|
var dependencies = (this._extractor || extractRequires)(content).deps;
|
||||||
data.dependencies = dependencies.sync;
|
data.dependencies = dependencies.sync;
|
||||||
data.asyncDependencies = dependencies.async;
|
data.asyncDependencies = dependencies.async;
|
||||||
}
|
}
|
||||||
|
@ -140,48 +137,4 @@ class Module {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract all required modules from a `code` string.
|
|
||||||
*/
|
|
||||||
const blockCommentRe = /\/\*(.|\n)*?\*\//g;
|
|
||||||
const lineCommentRe = /\/\/.+(\n|$)/g;
|
|
||||||
function extractRequires(code) {
|
|
||||||
var deps = {
|
|
||||||
sync: [],
|
|
||||||
async: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
code
|
|
||||||
.replace(blockCommentRe, '')
|
|
||||||
.replace(lineCommentRe, '')
|
|
||||||
// Parse sync dependencies. See comment below for further detils.
|
|
||||||
.replace(replacePatterns.IMPORT_RE, (match, pre, quot, dep, post) => {
|
|
||||||
deps.sync.push(dep);
|
|
||||||
return match;
|
|
||||||
})
|
|
||||||
.replace(replacePatterns.EXPORT_RE, (match, pre, quot, dep, post) => {
|
|
||||||
deps.sync.push(dep);
|
|
||||||
return match;
|
|
||||||
})
|
|
||||||
// Parse the sync dependencies this module has. When the module is
|
|
||||||
// required, all it's sync dependencies will be loaded into memory.
|
|
||||||
// Sync dependencies can be defined either using `require` or the ES6
|
|
||||||
// `import` or `export` syntaxes:
|
|
||||||
// var dep1 = require('dep1');
|
|
||||||
.replace(replacePatterns.REQUIRE_RE, (match, pre, quot, dep, post) => {
|
|
||||||
deps.sync.push(dep);
|
|
||||||
})
|
|
||||||
// Parse async dependencies this module has. As opposed to what happens
|
|
||||||
// with sync dependencies, when the module is required, it's async
|
|
||||||
// dependencies won't be loaded into memory. This is deferred till the
|
|
||||||
// code path gets to the import statement:
|
|
||||||
// System.import('dep1')
|
|
||||||
.replace(replacePatterns.SYSTEM_IMPORT_RE, (match, pre, quot, dep, post) => {
|
|
||||||
deps.async.push([dep]);
|
|
||||||
return match;
|
|
||||||
});
|
|
||||||
|
|
||||||
return deps;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Module;
|
module.exports = Module;
|
||||||
|
|
|
@ -7,11 +7,12 @@ const path = require('path');
|
||||||
|
|
||||||
class ModuleCache {
|
class ModuleCache {
|
||||||
|
|
||||||
constructor(fastfs, cache) {
|
constructor(fastfs, cache, extractRequires) {
|
||||||
this._moduleCache = Object.create(null);
|
this._moduleCache = Object.create(null);
|
||||||
this._packageCache = Object.create(null);
|
this._packageCache = Object.create(null);
|
||||||
this._fastfs = fastfs;
|
this._fastfs = fastfs;
|
||||||
this._cache = cache;
|
this._cache = cache;
|
||||||
|
this._extractRequires = extractRequires;
|
||||||
fastfs.on('change', this._processFileChange.bind(this));
|
fastfs.on('change', this._processFileChange.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ class ModuleCache {
|
||||||
this._fastfs,
|
this._fastfs,
|
||||||
this,
|
this,
|
||||||
this._cache,
|
this._cache,
|
||||||
|
this._extractRequires
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return this._moduleCache[filePath];
|
return this._moduleCache[filePath];
|
||||||
|
|
|
@ -11,18 +11,19 @@
|
||||||
jest
|
jest
|
||||||
.dontMock('absolute-path')
|
.dontMock('absolute-path')
|
||||||
.dontMock('../fastfs')
|
.dontMock('../fastfs')
|
||||||
.dontMock('../replacePatterns')
|
.dontMock('../lib/extractRequires')
|
||||||
|
.dontMock('../lib/replacePatterns')
|
||||||
.dontMock('../DependencyGraph/docblock')
|
.dontMock('../DependencyGraph/docblock')
|
||||||
.dontMock('../Module');
|
.dontMock('../Module');
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.mock('fs');
|
.mock('fs');
|
||||||
|
|
||||||
var Fastfs = require('../fastfs');
|
const Fastfs = require('../fastfs');
|
||||||
var Module = require('../Module');
|
const Module = require('../Module');
|
||||||
var ModuleCache = require('../ModuleCache');
|
const ModuleCache = require('../ModuleCache');
|
||||||
var Promise = require('promise');
|
const Promise = require('promise');
|
||||||
var fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
describe('Module', () => {
|
describe('Module', () => {
|
||||||
const fileWatcher = {
|
const fileWatcher = {
|
||||||
|
@ -30,17 +31,31 @@ describe('Module', () => {
|
||||||
isWatchman: () => Promise.resolve(false),
|
isWatchman: () => Promise.resolve(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const Cache = jest.genMockFn();
|
||||||
|
Cache.prototype.get = jest.genMockFn().mockImplementation(
|
||||||
|
(filepath, field, cb) => cb(filepath)
|
||||||
|
);
|
||||||
|
Cache.prototype.invalidate = jest.genMockFn();
|
||||||
|
Cache.prototype.end = jest.genMockFn();
|
||||||
|
|
||||||
|
|
||||||
describe('Async Dependencies', () => {
|
describe('Async Dependencies', () => {
|
||||||
function expectAsyncDependenciesToEqual(expected) {
|
function expectAsyncDependenciesToEqual(expected) {
|
||||||
var fastfs = new Fastfs(
|
const fastfs = new Fastfs(
|
||||||
'test',
|
'test',
|
||||||
['/root'],
|
['/root'],
|
||||||
fileWatcher,
|
fileWatcher,
|
||||||
{crawling: Promise.resolve(['/root/index.js']), ignore: []},
|
{crawling: Promise.resolve(['/root/index.js']), ignore: []},
|
||||||
);
|
);
|
||||||
|
const cache = new Cache();
|
||||||
|
|
||||||
return fastfs.build().then(() => {
|
return fastfs.build().then(() => {
|
||||||
var module = new Module('/root/index.js', fastfs, new ModuleCache(fastfs));
|
const module = new Module(
|
||||||
|
'/root/index.js',
|
||||||
|
fastfs,
|
||||||
|
new ModuleCache(fastfs, cache),
|
||||||
|
cache
|
||||||
|
);
|
||||||
|
|
||||||
return module.getAsyncDependencies().then(actual =>
|
return module.getAsyncDependencies().then(actual =>
|
||||||
expect(actual).toEqual(expected)
|
expect(actual).toEqual(expected)
|
||||||
|
@ -94,4 +109,43 @@ describe('Module', () => {
|
||||||
return expectAsyncDependenciesToEqual([['dep1']]);
|
return expectAsyncDependenciesToEqual([['dep1']]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Extrators', () => {
|
||||||
|
|
||||||
|
function createModuleWithExtractor(extractor) {
|
||||||
|
const fastfs = new Fastfs(
|
||||||
|
'test',
|
||||||
|
['/root'],
|
||||||
|
fileWatcher,
|
||||||
|
{crawling: Promise.resolve(['/root/index.js']), ignore: []},
|
||||||
|
);
|
||||||
|
const cache = new Cache();
|
||||||
|
|
||||||
|
return fastfs.build().then(() => {
|
||||||
|
return new Module(
|
||||||
|
'/root/index.js',
|
||||||
|
fastfs,
|
||||||
|
new ModuleCache(fastfs, cache),
|
||||||
|
cache,
|
||||||
|
extractor
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pit('uses custom require extractors if specified', () => {
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return createModuleWithExtractor(
|
||||||
|
code => ({deps: {sync: ['foo', 'bar']}})
|
||||||
|
).then(module =>
|
||||||
|
module.getDependencies().then(actual =>
|
||||||
|
expect(actual).toEqual(['foo', 'bar'])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -79,21 +79,19 @@ class Fastfs extends EventEmitter {
|
||||||
return [].concat(...this._roots.map(root => root.getFiles()));
|
return [].concat(...this._roots.map(root => root.getFiles()));
|
||||||
}
|
}
|
||||||
|
|
||||||
findFilesByExt(ext, { ignore }) {
|
findFilesByExt(ext, { ignore } = {}) {
|
||||||
|
return this.findFilesByExts([ext], {ignore});
|
||||||
|
}
|
||||||
|
|
||||||
|
findFilesByExts(exts, { ignore } = {}) {
|
||||||
return this.getAllFiles()
|
return this.getAllFiles()
|
||||||
.filter(
|
.filter(file => (
|
||||||
file => file.ext() === ext && (!ignore || !ignore(file.path))
|
exts.indexOf(file.ext()) !== -1 && (!ignore || !ignore(file.path))
|
||||||
)
|
))
|
||||||
.map(file => file.path);
|
.map(file => file.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
findFilesByExts(exts) {
|
findFilesByName(name, { ignore } = {}) {
|
||||||
return this.getAllFiles()
|
|
||||||
.filter(file => exts.indexOf(file.ext()) !== -1)
|
|
||||||
.map(file => file.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
findFilesByName(name, { ignore }) {
|
|
||||||
return this.getAllFiles()
|
return this.getAllFiles()
|
||||||
.filter(
|
.filter(
|
||||||
file => path.basename(file.path) === name &&
|
file => path.basename(file.path) === name &&
|
||||||
|
@ -102,6 +100,12 @@ class Fastfs extends EventEmitter {
|
||||||
.map(file => file.path);
|
.map(file => file.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
matchFilesByPattern(pattern) {
|
||||||
|
return this.getAllFiles()
|
||||||
|
.filter(file => file.path.match(pattern))
|
||||||
|
.map(file => file.path);
|
||||||
|
}
|
||||||
|
|
||||||
readFile(filePath) {
|
readFile(filePath) {
|
||||||
const file = this._getFile(filePath);
|
const file = this._getFile(filePath);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* 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 replacePatterns = require('./replacePatterns');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract all required modules from a `code` string.
|
||||||
|
*/
|
||||||
|
const blockCommentRe = /\/\*(.|\n)*?\*\//g;
|
||||||
|
const lineCommentRe = /\/\/.+(\n|$)/g;
|
||||||
|
function extractRequires(code) {
|
||||||
|
var deps = {
|
||||||
|
sync: [],
|
||||||
|
async: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
code = code
|
||||||
|
.replace(blockCommentRe, '')
|
||||||
|
.replace(lineCommentRe, '')
|
||||||
|
// Parse the sync dependencies this module has. When the module is
|
||||||
|
// required, all it's sync dependencies will be loaded into memory.
|
||||||
|
// Sync dependencies can be defined either using `require` or the ES6
|
||||||
|
// `import` or `export` syntaxes:
|
||||||
|
// var dep1 = require('dep1');
|
||||||
|
.replace(replacePatterns.IMPORT_RE, (match, pre, quot, dep, post) => {
|
||||||
|
deps.sync.push(dep);
|
||||||
|
return match;
|
||||||
|
})
|
||||||
|
.replace(replacePatterns.EXPORT_RE, (match, pre, quot, dep, post) => {
|
||||||
|
deps.sync.push(dep);
|
||||||
|
return match;
|
||||||
|
})
|
||||||
|
.replace(replacePatterns.REQUIRE_RE, (match, pre, quot, dep, post) => {
|
||||||
|
deps.sync.push(dep);
|
||||||
|
return match;
|
||||||
|
})
|
||||||
|
// Parse async dependencies this module has. As opposed to what happens
|
||||||
|
// with sync dependencies, when the module is required, it's async
|
||||||
|
// dependencies won't be loaded into memory. This is deferred till the
|
||||||
|
// code path gets to the import statement:
|
||||||
|
// System.import('dep1')
|
||||||
|
.replace(replacePatterns.SYSTEM_IMPORT_RE, (match, pre, quot, dep, post) => {
|
||||||
|
deps.async.push([dep]);
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {code, deps};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = extractRequires;
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
exports.IMPORT_RE = /(\bimport\s+(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g;
|
||||||
|
exports.EXPORT_RE = /(\bexport\s+(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g;
|
||||||
|
exports.REQUIRE_RE = /(\brequire\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;
|
||||||
|
exports.SYSTEM_IMPORT_RE = /(\bSystem\.import\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;
|
|
@ -10,7 +10,8 @@
|
||||||
|
|
||||||
jest.dontMock('../')
|
jest.dontMock('../')
|
||||||
.dontMock('underscore')
|
.dontMock('underscore')
|
||||||
.dontMock('../../DependencyResolver/replacePatterns');
|
.dontMock('../../DependencyResolver/lib/extractRequires')
|
||||||
|
.dontMock('../../DependencyResolver/lib/replacePatterns');
|
||||||
|
|
||||||
jest.mock('path');
|
jest.mock('path');
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const Activity = require('../Activity');
|
const Activity = require('../Activity');
|
||||||
const DependencyGraph = require('../DependencyResolver/DependencyGraph');
|
const DependencyGraph = require('../DependencyResolver/DependencyGraph');
|
||||||
const replacePatterns = require('../DependencyResolver/replacePatterns');
|
const replacePatterns = require('../DependencyResolver/lib/replacePatterns');
|
||||||
const Polyfill = require('../DependencyResolver/Polyfill');
|
const Polyfill = require('../DependencyResolver/Polyfill');
|
||||||
const declareOpts = require('../lib/declareOpts');
|
const declareOpts = require('../lib/declareOpts');
|
||||||
const Promise = require('promise');
|
const Promise = require('promise');
|
||||||
|
|
Loading…
Reference in New Issue