Skip to content

Commit

Permalink
Allow custom resolver to be used with[out] moduleNameMapper (#4174)
Browse files Browse the repository at this point in the history
* Allow the custom resolver to be used with and without the moduleNameMapper.

Also, improve error message when moduleNameMapper doesn't resolve to a module.

* Add 2nd test, to ensure custom resolver works without moduleNameMapper.

* Fix type and lint errors.

* Bug: rootDir wasn't the same value when --runInBand vs parallel

rootDir was modified after initialization to be process.cwd. In case of --runInBand,
The resolver was intialized before the rootDir value was altered. In case of parallel,
the Resolver was initialized in each spawned process using values of rootDir after
initialization.

Fix is to introduce a new config.cwd option that will be set to the parent processes
cwd. Adjust the console reporters to favor config.cwd over config.rootDir.

* React to changes upstream. Specify rootDir in defaultResolver.
  • Loading branch information
midzelis authored and cpojer committed Aug 31, 2017
1 parent 7e2c206 commit de74b38
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 29 deletions.
3 changes: 2 additions & 1 deletion docs/en/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ This option allows the use of a custom resolver. This resolver must be a node mo
"browser": bool,
"extensions": [string],
"moduleDirectory": [string],
"paths": [string]
"paths": [string],
"rootDir": [string]
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ exports[`moduleNameMapper wrong configuration 1`] = `
Configuration error:
Unknown module in configuration option moduleNameMapper
Could not locate module ./style.css (mapped as no-such-module)
Please check:
\\"moduleNameMapper\\": {
\\"/\\\\.(css|less)$/\\": \\"no-such-module\\"
}
},
\\"resolver\\": undefined
"
`;
2 changes: 1 addition & 1 deletion packages/jest-cli/src/reporters/default_reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class DefaultReporter extends BaseReporter {
TITLE_BULLET +
'Console\n\n' +
getConsoleOutput(
config.rootDir,
config.cwd,
!!this._globalConfig.verbose,
consoleBuffer,
),
Expand Down
16 changes: 11 additions & 5 deletions packages/jest-cli/src/reporters/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @flow
*/

import type {Path, ProjectConfig} from 'types/Config';
import type {Path, ProjectConfig, GlobalConfig} from 'types/Config';
import type {AggregatedResult} from 'types/TestResult';

import path from 'path';
Expand Down Expand Up @@ -37,7 +37,7 @@ const printDisplayName = (config: ProjectConfig) => {

const trimAndFormatPath = (
pad: number,
config: {rootDir: Path},
config: ProjectConfig | GlobalConfig,
testPath: Path,
columns: number,
): string => {
Expand Down Expand Up @@ -72,13 +72,19 @@ const trimAndFormatPath = (
);
};

const formatTestPath = (config: {rootDir: Path}, testPath: Path) => {
const formatTestPath = (
config: GlobalConfig | ProjectConfig,
testPath: Path,
) => {
const {dirname, basename} = relativePath(config, testPath);
return chalk.dim(dirname + path.sep) + chalk.bold(basename);
};

const relativePath = (config: {rootDir: Path}, testPath: Path) => {
testPath = path.relative(config.rootDir, testPath);
const relativePath = (config: GlobalConfig | ProjectConfig, testPath: Path) => {
// this function can be called with ProjectConfigs or GlobalConfigs. GlobalConfigs
// do not have config.cwd, only config.rootDir. Try using config.cwd, fallback
// to config.rootDir. (Also, some unit just use config.rootDir, which is ok)
testPath = path.relative(config.cwd || config.rootDir, testPath);
const dirname = path.dirname(testPath);
const basename = path.basename(testPath);
return {basename, dirname};
Expand Down
11 changes: 7 additions & 4 deletions packages/jest-cli/src/run_jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,13 @@ const runJest = async ({
}

// When using more than one context, make all printed paths relative to the
// current cwd. rootDir is only used as a token during normalization and
// has no special meaning afterwards except for printing information to the
// CLI.
setConfig(contexts, {rootDir: process.cwd()});
// current cwd. Do not modify rootDir, since will be used by custom resolvers.
// If --runInBand is true, the resolver saved a copy during initialization,
// however, if it is running on spawned processes, the initiation of the
// custom resolvers is done within each spawned process and it needs the
// original value of rootDir. Instead, use the {cwd: Path} property to resolve
// paths when printing.
setConfig(contexts, {cwd: process.cwd()});

const results = await new TestScheduler(globalConfig, {
startRun,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const getConfigs = (
cacheDirectory: options.cacheDirectory,
clearMocks: options.clearMocks,
coveragePathIgnorePatterns: options.coveragePathIgnorePatterns,
cwd: options.cwd,
displayName: options.displayName,
globals: options.globals,
haste: options.haste,
Expand Down
60 changes: 58 additions & 2 deletions packages/jest-resolve/src/__tests__/resolve.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@

'use strict';

jest.mock('../__mocks__/userResolver');

const fs = require('fs');
const path = require('path');
const ModuleMap = require('jest-haste-map').ModuleMap;
const Resolver = require('../');
const userResolver = require('../__mocks__/userResolver');

beforeEach(() => {
userResolver.mockClear();
});

describe('isCoreModule', () => {
it('returns false if `hasCoreModules` is false.', () => {
Expand Down Expand Up @@ -50,8 +57,6 @@ describe('findNodeModule', () => {
.map(p => path.resolve(resolvedCwd, p))
: null;

jest.mock('../__mocks__/userResolver');
const userResolver = require('../__mocks__/userResolver');
userResolver.mockImplementation(() => 'module');

const newPath = Resolver.findNodeModule('test', {
Expand All @@ -74,3 +79,54 @@ describe('findNodeModule', () => {
});
});
});

describe('getMockModule', () => {
it('is possible to use custom resolver to resolve deps inside mock modules with moduleNameMapper', () => {
userResolver.mockImplementation(() => 'module');

const moduleMap = new ModuleMap({
duplicates: [],
map: [],
mocks: [],
});
const resolver = new Resolver(moduleMap, {
moduleNameMapper: [
{
moduleName: '$1',
regex: /(.*)/,
},
],
resolver: require.resolve('../__mocks__/userResolver'),
});
const src = require.resolve('../');
resolver.getMockModule(src, 'dependentModule');

expect(userResolver).toHaveBeenCalled();
expect(userResolver.mock.calls[0][0]).toBe('dependentModule');
expect(userResolver.mock.calls[0][1]).toHaveProperty(
'basedir',
path.dirname(src),
);
});
it('is possible to use custom resolver to resolve deps inside mock modules without moduleNameMapper', () => {
userResolver.mockImplementation(() => 'module');

const moduleMap = new ModuleMap({
duplicates: [],
map: [],
mocks: [],
});
const resolver = new Resolver(moduleMap, {
resolver: require.resolve('../__mocks__/userResolver'),
});
const src = require.resolve('../');
resolver.getMockModule(src, 'dependentModule');

expect(userResolver).toHaveBeenCalled();
expect(userResolver.mock.calls[0][0]).toBe('dependentModule');
expect(userResolver.mock.calls[0][1]).toHaveProperty(
'basedir',
path.dirname(src),
);
});
});
2 changes: 2 additions & 0 deletions packages/jest-resolve/src/default_resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type ResolverOptions = {|
extensions?: Array<string>,
moduleDirectory?: Array<string>,
paths?: ?Array<Path>,
rootDir: ?Path,
|};

function defaultResolver(path: Path, options: ResolverOptions): Path {
Expand All @@ -28,6 +29,7 @@ function defaultResolver(path: Path, options: ResolverOptions): Path {
extensions: options.extensions,
moduleDirectory: options.moduleDirectory,
paths: options.paths,
rootDir: options.rootDir,
});
}

Expand Down
54 changes: 41 additions & 13 deletions packages/jest-resolve/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type ResolverConfig = {|
modulePaths: Array<Path>,
platforms?: Array<string>,
resolver: ?Path,
rootDir: ?Path,
|};

type FindNodeModuleConfig = {|
Expand All @@ -38,6 +39,7 @@ type FindNodeModuleConfig = {|
moduleDirectory?: Array<string>,
paths?: Array<Path>,
resolver?: ?Path,
rootDir?: ?Path,
|};

type ModuleNameMapperConfig = {|
Expand Down Expand Up @@ -79,6 +81,7 @@ class Resolver {
modulePaths: options.modulePaths,
platforms: options.platforms,
resolver: options.resolver,
rootDir: options.rootDir,
};
this._moduleMap = moduleMap;
this._moduleIDCache = Object.create(null);
Expand All @@ -100,6 +103,7 @@ class Resolver {
extensions: options.extensions,
moduleDirectory: options.moduleDirectory,
paths: paths ? (nodePaths || []).concat(paths) : nodePaths,
rootDir: options.rootDir,
});
} catch (e) {}
return null;
Expand Down Expand Up @@ -152,6 +156,7 @@ class Resolver {
moduleDirectory,
paths,
resolver: this._options.resolver,
rootDir: this._options.rootDir,
});
};

Expand Down Expand Up @@ -316,39 +321,46 @@ class Resolver {
const extensions = this._options.extensions;
const moduleDirectory = this._options.moduleDirectories;
const moduleNameMapper = this._options.moduleNameMapper;
const resolver = this._options.resolver;

if (moduleNameMapper) {
for (const {moduleName: mappedModuleName, regex} of moduleNameMapper) {
if (regex.test(moduleName)) {
// Note: once a moduleNameMapper matches the name, it must result
// in a module, or else an error is thrown.
const matches = moduleName.match(regex);
if (!matches) {
moduleName = mappedModuleName;
} else {
moduleName = mappedModuleName.replace(
/\$([0-9]+)/g,
(_, index) => matches[parseInt(index, 10)],
);
}
const updatedName = matches
? mappedModuleName.replace(
/\$([0-9]+)/g,
(_, index) => matches[parseInt(index, 10)],
)
: mappedModuleName;

const module =
this.getModule(moduleName) ||
Resolver.findNodeModule(moduleName, {
this.getModule(updatedName) ||
Resolver.findNodeModule(updatedName, {
basedir: dirname,
browser: this._options.browser,
extensions,
moduleDirectory,
paths,
resolver,
rootDir: this._options.rootDir,
});
if (!module) {
const error = new Error(
chalk.red(`${chalk.bold('Configuration error')}:
Unknown module in configuration option ${chalk.bold('moduleNameMapper')}
Could not locate module ${chalk.bold(moduleName)} (mapped as ${chalk.bold(
updatedName,
)})
Please check:
"moduleNameMapper": {
"${regex.toString()}": "${chalk.bold(moduleName)}"
}`),
"${regex.toString()}": "${chalk.bold(mappedModuleName)}"
},
"resolver": ${chalk.bold(resolver)}`),
);
error.stack = '';
throw error;
Expand All @@ -357,6 +369,22 @@ Please check:
}
}
}
if (resolver) {
// if moduleNameMapper didn't match anything, fallback to just the
// regular resolver
const module =
this.getModule(moduleName) ||
Resolver.findNodeModule(moduleName, {
basedir: dirname,
browser: this._options.browser,
extensions,
moduleDirectory,
paths,
resolver,
rootDir: this._options.rootDir,
});
return module;
}
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/jest-runner/src/run_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function runTest(
const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout;
const consoleFormatter = (type, message) =>
getConsoleOutput(
config.rootDir,
config.cwd,
!!globalConfig.verbose,
// 4 = the console call is buried 4 stack frames deep
BufferedConsole.write([], type, message, 4),
Expand Down
1 change: 1 addition & 0 deletions packages/jest-runtime/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ class Runtime {
modulePaths: config.modulePaths,
platforms: config.haste.platforms,
resolver: config.resolver,
rootDir: config.rootDir,
});
}

Expand Down
1 change: 1 addition & 0 deletions test_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = {
cacheDirectory: '/test_cache_dir/',
clearMocks: false,
coveragePathIgnorePatterns: [],
cwd: '/test_root_dir/',
displayName: undefined,
globals: {},
haste: {
Expand Down
1 change: 1 addition & 0 deletions types/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export type ProjectConfig = {|
cacheDirectory: Path,
clearMocks: boolean,
coveragePathIgnorePatterns: Array<string>,
cwd: Path,
displayName: ?string,
globals: ConfigGlobals,
haste: HasteConfig,
Expand Down

0 comments on commit de74b38

Please sign in to comment.