diff --git a/docs/Configuration.md b/docs/Configuration.md index 6755ecd34690..4fdcc8e23c3c 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -223,6 +223,23 @@ Default: `false` If enabled, the module registry for every test file will be reset before running each individual test. This is useful to isolate modules for every test so that local module state doesn't conflict between tests. This can be done programmatically using [`jest.resetModules()`](#jest-resetmodules). +### `resolver` [string] +Default: `undefined` + +This option allows the use of a custom resolver. This resolver must be a node module that exports a function expecting a string as the first argument for the path to resolve and an object with the following structure as the second argument: + +``` +{ + "basedir": string, + "browser": bool, + "extensions": [string], + "moduleDirectory": [string], + "paths": [string] +} +``` + +The function should either return a path to the module that should be resolved or throw an error if the module can't be found. + ### `rootDir` [string] Default: The root of the directory containing the `package.json` *or* the [`pwd`](http://en.wikipedia.org/wiki/Pwd) if no `package.json` is found diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index cf591f3e3349..54c3e4f20cd5 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -296,6 +296,7 @@ function normalize(config: InitialConfig, argv: Object = {}) { break; case 'setupTestFrameworkScriptFile': case 'testResultsProcessor': + case 'resolver': //$FlowFixMe value = resolve(config.rootDir, key, config[key]); break; diff --git a/packages/jest-config/src/validConfig.js b/packages/jest-config/src/validConfig.js index 27632e2c9205..8fa64c9cd13b 100644 --- a/packages/jest-config/src/validConfig.js +++ b/packages/jest-config/src/validConfig.js @@ -59,6 +59,7 @@ module.exports = ({ preset: 'react-native', resetMocks: false, resetModules: false, + resolver: '/resolver.js', rootDir: '/', roots: [''], setupFiles: ['/setup.js'], diff --git a/packages/jest-resolve/src/__mocks__/userResolver.js b/packages/jest-resolve/src/__mocks__/userResolver.js new file mode 100644 index 000000000000..de5816379e86 --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/userResolver.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2014-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'; + +module.exports = function userResolver(path, options) { + return 'module'; +}; diff --git a/packages/jest-resolve/src/__test__/resolve-test.js b/packages/jest-resolve/src/__tests__/resolve-test.js similarity index 57% rename from packages/jest-resolve/src/__test__/resolve-test.js rename to packages/jest-resolve/src/__tests__/resolve-test.js index 2ee2826fe3eb..13d261bbe6de 100644 --- a/packages/jest-resolve/src/__test__/resolve-test.js +++ b/packages/jest-resolve/src/__tests__/resolve-test.js @@ -10,6 +10,7 @@ 'use strict'; +const path = require('path'); const ModuleMap = require('jest-haste-map').ModuleMap; const Resolver = require('../'); @@ -37,3 +38,34 @@ describe('isCoreModule', () => { expect(isCore).toEqual(false); }); }); + +describe('findNodeModule', () => { + it('is possible to override the default resolver', () => { + const nodePaths = process.env.NODE_PATH + ? process.env.NODE_PATH.split(path.delimiter) + : null; + + jest.mock('../__mocks__/userResolver'); + const userResolver = require('../__mocks__/userResolver'); + userResolver.mockImplementation(() => 'module'); + + const newPath = Resolver.findNodeModule('test', { + basedir: '/', + browser: true, + extensions: ['js'], + moduleDirectory: ['node_modules'], + paths: ['/something'], + resolver: require.resolve('../__mocks__/userResolver'), + }); + + expect(newPath).toBe('module'); + expect(userResolver.mock.calls[0][0]).toBe('test'); + expect(userResolver.mock.calls[0][1]).toEqual({ + basedir: '/', + browser: true, + extensions: ['js'], + moduleDirectory: ['node_modules'], + paths: (nodePaths || []).concat(['/something']), + }); + }); +}); diff --git a/packages/jest-resolve/src/defaultResolver.js b/packages/jest-resolve/src/defaultResolver.js new file mode 100644 index 000000000000..d64e97c9aa5b --- /dev/null +++ b/packages/jest-resolve/src/defaultResolver.js @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2014-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. + * + * @flow + */ + +'use strict'; + +import type {Path} from 'types/Config'; + +const resolve = require('resolve'); +const browserResolve = require('browser-resolve'); + +type ResolverOptions = {| + basedir: Path, + browser?: boolean, + extensions?: Array, + moduleDirectory?: Array, + paths?: ?Array, +|}; + +function defaultResolver(path: Path, options: ResolverOptions) { + const resv = options.browser ? browserResolve : resolve; + + return resv.sync( + path, + { + basedir: options.basedir, + extensions: options.extensions, + moduleDirectory: options.moduleDirectory, + paths: options.paths, + } + ); +} + +module.exports = defaultResolver; diff --git a/packages/jest-resolve/src/index.js b/packages/jest-resolve/src/index.js index 730b1e087104..fe25307ec514 100644 --- a/packages/jest-resolve/src/index.js +++ b/packages/jest-resolve/src/index.js @@ -15,8 +15,6 @@ import type {ModuleMap} from 'jest-haste-map'; const nodeModulesPaths = require('resolve/lib/node-modules-paths'); const path = require('path'); -const resolve = require('resolve'); -const browserResolve = require('browser-resolve'); const isBuiltinModule = require('is-builtin-module'); type ResolverConfig = {| @@ -28,6 +26,7 @@ type ResolverConfig = {| moduleNameMapper: ?Array, modulePaths: Array, platforms?: Array, + resolver: ?Path, |}; type FindNodeModuleConfig = {| @@ -36,6 +35,7 @@ type FindNodeModuleConfig = {| extensions?: Array, moduleDirectory?: Array, paths?: Array, + resolver?: ?Path, |}; type ModuleNameMapperConfig = {| @@ -73,6 +73,7 @@ class Resolver { moduleNameMapper: options.moduleNameMapper, modulePaths: options.modulePaths, platforms: options.platforms, + resolver: options.resolver, }; this._moduleMap = moduleMap; this._moduleIDCache = Object.create(null); @@ -81,13 +82,15 @@ class Resolver { } static findNodeModule(path: Path, options: FindNodeModuleConfig): ?Path { + /* $FlowFixMe */ + const resolver = require(options.resolver || './defaultResolver.js'); const paths = options.paths; + try { - const resv = options.browser ? browserResolve : resolve; - return resv.sync( - path, + return resolver(path, { basedir: options.basedir, + browser: options.browser, extensions: options.extensions, moduleDirectory: options.moduleDirectory, paths: paths ? (nodePaths || []).concat(paths) : nodePaths, @@ -145,6 +148,7 @@ class Resolver { extensions, moduleDirectory, paths, + resolver: this._options.resolver, }); if (module) { diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index a592e31ca696..8abb9eab34f0 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -222,6 +222,7 @@ class Runtime { moduleNameMapper: getModuleNameMapper(config), modulePaths: config.modulePaths, platforms: config.haste.platforms, + resolver: config.resolver, }); } diff --git a/types/Config.js b/types/Config.js index 05805bc3488f..3c58298d6bcf 100644 --- a/types/Config.js +++ b/types/Config.js @@ -89,6 +89,7 @@ export type Config = {| preset: ?string, resetMocks: boolean, resetModules: boolean, + resolver: ?Path, rootDir: Path, roots: Array, setupFiles: Array, @@ -148,6 +149,7 @@ export type InitialConfig = {| preset?: ?string, resetMocks?: boolean, resetModules?: boolean, + resolver?: ?Path, rootDir: Path, roots?: Array, scriptPreprocessor?: string,