From 806fb972ab6059f27efc485f35ce43307eac0858 Mon Sep 17 00:00:00 2001 From: Tim Seckinger Date: Fri, 15 Jun 2018 00:15:23 +0200 Subject: [PATCH] feat(runtime): support require.resolve with options.paths --- e2e/__tests__/resolve-with-paths.test.js | 36 ++++++++++++++++++ .../__tests__/resolve-with-paths.test.js | 37 ++++++++++++++++++ e2e/resolve-with-paths/dir/mod.js | 8 ++++ e2e/resolve-with-paths/package.json | 5 +++ packages/jest-runtime/src/index.js | 38 ++++++++++++++++++- 5 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 e2e/__tests__/resolve-with-paths.test.js create mode 100644 e2e/resolve-with-paths/__tests__/resolve-with-paths.test.js create mode 100644 e2e/resolve-with-paths/dir/mod.js create mode 100644 e2e/resolve-with-paths/package.json diff --git a/e2e/__tests__/resolve-with-paths.test.js b/e2e/__tests__/resolve-with-paths.test.js new file mode 100644 index 000000000000..40f207a421e6 --- /dev/null +++ b/e2e/__tests__/resolve-with-paths.test.js @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ +'use strict'; + +const {resolve} = require('path'); + +const runJest = require('../runJest'); +const {writeFiles, cleanup} = require('../Utils'); + +const workdirNodeModules = resolve( + __dirname, + '..', + 'resolve-with-paths', + 'node_modules', +); + +beforeAll(() => { + writeFiles(resolve(workdirNodeModules, 'mod'), { + 'index.js': 'module.exports = 42;', + }); +}); + +afterAll(() => { + cleanup(workdirNodeModules); +}); + +test('require.resolve with paths', () => { + const {status} = runJest('resolve-with-paths'); + expect(status).toBe(0); +}); diff --git a/e2e/resolve-with-paths/__tests__/resolve-with-paths.test.js b/e2e/resolve-with-paths/__tests__/resolve-with-paths.test.js new file mode 100644 index 000000000000..4641965af6c3 --- /dev/null +++ b/e2e/resolve-with-paths/__tests__/resolve-with-paths.test.js @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +import {resolve} from 'path'; + +test('finds a module relative to one of the given paths', () => { + expect(require.resolve('./mod.js', {paths: ['../dir']})).toEqual( + resolve(__dirname, '..', 'dir', 'mod.js') + ); +}); + +test('finds a module without a leading "./" relative to one of the given paths', () => { + expect(require.resolve('mod.js', {paths: ['../dir']})).toEqual( + resolve(__dirname, '..', 'dir', 'mod.js') + ); +}); + +test('finds a node_module above one of the given paths', () => { + expect(require.resolve('mod', {paths: ['../dir']})).toEqual( + resolve(__dirname, '..', 'node_modules', 'mod', 'index.js') + ); +}); + +test('finds a native node module when paths are given', () => { + expect(require.resolve('fs', {paths: ['../dir']})).toEqual('fs'); +}); + +test('throws an error if the module cannot be found from given paths', () => { + expect(() => require.resolve('./mod.js', {paths: ['..']})).toThrowError( + "Cannot resolve module './mod.js' from paths ['..'] from " + ); +}); diff --git a/e2e/resolve-with-paths/dir/mod.js b/e2e/resolve-with-paths/dir/mod.js new file mode 100644 index 000000000000..5cb74bfcde64 --- /dev/null +++ b/e2e/resolve-with-paths/dir/mod.js @@ -0,0 +1,8 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +module.exports = 'mod'; diff --git a/e2e/resolve-with-paths/package.json b/e2e/resolve-with-paths/package.json new file mode 100644 index 000000000000..148788b25446 --- /dev/null +++ b/e2e/resolve-with-paths/package.json @@ -0,0 +1,5 @@ +{ + "jest": { + "testEnvironment": "node" + } +} diff --git a/packages/jest-runtime/src/index.js b/packages/jest-runtime/src/index.js index 13594738cf88..78a1b9e1fd48 100644 --- a/packages/jest-runtime/src/index.js +++ b/packages/jest-runtime/src/index.js @@ -495,6 +495,40 @@ class Runtime { return to ? this._resolver.resolveModule(from, to) : from; } + _requireResolve( + from: Path, + moduleName?: string, + {paths}: {paths?: Path[]} = {}, + ) { + if (moduleName == null) { + throw new Error( + 'The first argument to require.resolve must be a string. Received null or undefined.', + ); + } + + if (paths) { + for (const p of paths) { + const absolutePath = path.resolve(from, '..', p); + const module = this._resolver._resolveModuleFromDirIfExists( + absolutePath, + moduleName, + // required to also resolve files without leading './' directly in the path + {paths: [absolutePath]}, + ); + if (module) { + return module; + } + } + throw new Error( + `Cannot resolve module '${moduleName}' from paths ['${paths.join( + "', '", + )}'] from ${from}`, + ); + } + + return this._resolveModule(from, moduleName); + } + _execModule( localModule: Module, options: ?InternalModuleOptions, @@ -721,8 +755,8 @@ class Runtime { moduleRequire.extensions = Object.create(null); moduleRequire.requireActual = this.requireModule.bind(this, from.filename); moduleRequire.requireMock = this.requireMock.bind(this, from.filename); - moduleRequire.resolve = moduleName => - this._resolveModule(from.filename, moduleName); + moduleRequire.resolve = (moduleName, options) => + this._requireResolve(from.filename, moduleName, options); Object.defineProperty( moduleRequire, 'main',