Skip to content

Commit

Permalink
Add HasteResolver class with a single store to the same dependency gr…
Browse files Browse the repository at this point in the history
…aphs instances.
  • Loading branch information
cpojer committed Nov 12, 2015
1 parent 4acd628 commit 758ec87
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 85 deletions.
76 changes: 15 additions & 61 deletions src/HasteModuleLoader/HasteModuleLoader.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/

/* eslint-disable fb-www/require-args */
/* eslint-disable fb-www/require-args, fb-www/object-create-only-one-param */
'use strict';

const fs = require('graceful-fs');
const moduleMocker = require('../lib/moduleMocker');
const DependencyGraph = require('node-haste/lib/DependencyGraph');
const extractRequires = require('node-haste/lib/lib/extractRequires');
const HasteResolver = require('../resolvers/HasteResolver');
const path = require('path');
const resolve = require('resolve');
const transform = require('../lib/transform');
Expand All @@ -21,8 +20,6 @@ const NODE_PATH =
(process.env.NODE_PATH ? process.env.NODE_PATH.split(path.delimiter) : null);
const IS_PATH_BASED_MODULE_NAME = /^(?:\.\.?\/|\/)/;
const VENDOR_PATH = path.resolve(__dirname, '../../vendor');
const MOCKS_PATTERN = /(?:[\\/]|^)__mocks__[\\/]([^\/]+)\.js$/;
const REQUIRE_EXTENSIONS_PATTERN = /(\brequire\s*?\.\s*?(?:requireActual|requireMock)\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;

const mockParentModule = {
id: 'mockParent',
Expand All @@ -47,7 +44,6 @@ let _configUnmockListRegExpCache = null;

class Loader {
constructor(config, environment) {
/* eslint-disable fb-www/object-create-only-one-param */
this._config = config;
this._coverageCollectors = {};
this._currentlyExecutingModulePath = '';
Expand All @@ -60,41 +56,17 @@ class Loader {
this._configShouldMockModuleNames = {};
this._extensions = config.moduleFileExtensions.map(ext => '.' + ext);

const ignoreFilePattern = new RegExp(
[config.cacheDirectory].concat(config.modulePathIgnorePatterns).join('|')
this._resolver = HasteResolver.get(
[config.rootDir],
{
extensions: this._extensions.concat(this._config.testFileExtensions),
ignoreFilePattern: [config.cacheDirectory]
.concat(config.modulePathIgnorePatterns).join('|'),
}
);
this._depGraph = new DependencyGraph({
roots: [config.rootDir],
ignoreFilePath: path => path.match(ignoreFilePattern),
cache: {
get: (a, b, cb) => Promise.resolve(cb()),
invalidate: () => {},
},
fileWatcher: {
on: function() {
return this;
},
isWatchman: () => Promise.resolve(false),
},
extensions: this._extensions.concat(this._config.testFileExtensions),
mocksPattern: MOCKS_PATTERN,
extractRequires: code => {
const data = extractRequires(code);
data.code = data.code.replace(
REQUIRE_EXTENSIONS_PATTERN,
(match, pre, quot, dep, post) => {
data.deps.sync.push(dep);
return match;
}
);
return data;
},
});
this._resolvedModules = Object.create(null);
this._resources = Object.create(null);
this._resolveDependencyPromises = Object.create(null);
this._mocks = Object.create(null);
/* eslint-enable fb-www/object-create-only-one-param */

if (config.collectCoverage) {
this._CoverageCollector = require(config.coverageCollector);
Expand Down Expand Up @@ -131,7 +103,7 @@ class Loader {
}

getAllTestPaths() {
return this._depGraph.matchFilesByPattern(this._config.testDirectoryName);
return this._resolver.matchFilesByPattern(this._config.testDirectoryName);
}

/**
Expand Down Expand Up @@ -302,28 +274,11 @@ class Loader {
}

resolveDependencies(path) {
if (this._resolveDependencyPromises[path]) {
return this._resolveDependencyPromises[path];
}

return this._resolveDependencyPromises[path] = this._depGraph.load()
.then(() => this._depGraph.getDependencies(path))
return this._resolver.getDependencies(path)
.then(response => {
return response.finalize().then(() => {
this._mocks = response.mocks;
return Promise.all(response.dependencies.map(module => {
if (!this._resolvedModules[module.path]) {
this._resolvedModules[module.path] = {};
}
response.getResolvedDependencyPairs(module).forEach((pair) =>
this._resolvedModules[module.path][pair[0]] = pair[1]
);

return module.getName().then(
name => this._resources[name] = module
);
}));
});
this._mocks = response.mocks;
this._resolvedModules = response.resolvedModules;
this._resources = response.resources;
})
.then(() => this);
}
Expand Down Expand Up @@ -482,8 +437,7 @@ class Loader {

resolveDirectDependencies(path) {
// TODO this should only resolve direct dependencies
return this._depGraph.load()
.then(() => this._depGraph.getDependencies(path))
return this._resolver.getDependencies(path)
.then(response => response.dependencies.map(dep => dep.path));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,35 +41,28 @@ describe('nodeHasteModuleLoader', function() {
'does not cause side effects in the rest of the module system when ' +
'generating a mock',
function() {
return buildLoader()
.then(
loader => Promise.all([
loader.resolveDependencies('./root.js'),
loader.resolveDependencies('./RegularModule.js'),
]).then(() => loader)
)
.then(loader => {
const rootPath = path.join(rootDir, 'root.js');
const testRequire = loader.requireModule.bind(
loader,
rootPath
);
return buildLoader().then(loader => {
const rootPath = path.join(rootDir, 'root.js');
const testRequire = loader.requireModule.bind(
loader,
rootPath
);

const regularModule = testRequire('RegularModule');
const origModuleStateValue = regularModule.getModuleStateValue();
const regularModule = testRequire('RegularModule');
const origModuleStateValue = regularModule.getModuleStateValue();

expect(origModuleStateValue).toBe('default');
expect(origModuleStateValue).toBe('default');

// Generate a mock for a module with side effects
const mock = regularModule.jest.genMockFromModule('ModuleWithSideEffects');
// Generate a mock for a module with side effects
const mock = regularModule.jest.genMockFromModule('ModuleWithSideEffects');

// Make sure we get a mock.
expect(mock.fn()).toBe(undefined);
// Make sure we get a mock.
expect(mock.fn()).toBe(undefined);

expect(regularModule.getModuleStateValue()).toBe(
origModuleStateValue
);
});
expect(regularModule.getModuleStateValue()).toBe(
origModuleStateValue
);
});
}
);
});
Expand Down
106 changes: 106 additions & 0 deletions src/resolvers/HasteResolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Copyright (c) 2014, 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.
*/

/* eslint-disable fb-www/object-create-only-one-param */

'use strict';

const DependencyGraph = require('node-haste/lib/DependencyGraph');
const extractRequires = require('node-haste/lib/lib/extractRequires');

const MOCKS_PATTERN = /(?:[\\/]|^)__mocks__[\\/]([^\/]+)\.js$/;
const REQUIRE_EXTENSIONS_PATTERN = /(\brequire\s*?\.\s*?(?:requireActual|requireMock)\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g;

const resolvers = Object.create(null);

class HasteResolver {

constructor(roots, options) {
const ignoreFilePattern = new RegExp(options.ignoreFilePattern);
this._resolvePromises = Object.create(null);
this._depGraph = new DependencyGraph({
roots,
ignoreFilePath: path => path.match(ignoreFilePattern),
cache: {
get: (a, b, cb) => Promise.resolve(cb()),
invalidate: () => {},
},
fileWatcher: {
on: function() {
return this;
},
isWatchman: () => Promise.resolve(false),
},
extensions: options.extensions,
mocksPattern: MOCKS_PATTERN,
extractRequires: code => {
const data = extractRequires(code);
data.code = data.code.replace(
REQUIRE_EXTENSIONS_PATTERN,
(match, pre, quot, dep, post) => {
data.deps.sync.push(dep);
return match;
}
);
return data;
},
});

// warm-up
this._depGraph.load();
}

matchFilesByPattern(pattern) {
return this._depGraph.matchFilesByPattern(pattern);
}

getDependencies(path) {
if (this._resolvePromises[path]) {
return this._resolvePromises[path];
}

return this._resolvePromises[path] = this._depGraph.load().then(
() => this._depGraph.getDependencies(path).then(response =>
response.finalize().then(() => {
var deps = {
mocks: response.mocks,
resolvedModules: Object.create(null),
resources: Object.create(null),
};
return Promise.all(
response.dependencies.map(module => {
if (!deps.resolvedModules[module.path]) {
deps.resolvedModules[module.path] = {};
}
response.getResolvedDependencyPairs(module).forEach((pair) =>
deps.resolvedModules[module.path][pair[0]] = pair[1]
);
return module.getName().then(
name => deps.resources[name] = module
);
})
).then(() => deps);
})
));
}

static get(roots, options) {
const key =
roots.sort().join(':') +
'$' + options.extensions.sort().join(':') +
'$' + options.ignoreFilePattern;
if (!resolvers[key]) {
resolvers[key] = new HasteResolver(roots, options);
}

return resolvers[key];
}

}

module.exports = HasteResolver;

0 comments on commit 758ec87

Please sign in to comment.