Skip to content

Commit

Permalink
fix: add global path in require.resolve.paths (#13633)
Browse files Browse the repository at this point in the history
* fix: add global path in require.resolve.paths

* test: update snapshot

* test: update snapshot

* test: add builtin module test case

* refactor: grap global paths only once

* test: update cases and snapshot

* chore: add path join and update test case

* test: update test cases

* clarify and add changelog

Co-authored-by: Tim Seckinger <seckinger.tim@gmail.com>
  • Loading branch information
lvqq and jeysal committed Dec 2, 2022
1 parent ef91356 commit 2ec6d24
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 9 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,8 @@

### Fixes

- `[jest-resolve]` add global paths to `require.resolve.paths` ([#13633](https://github.com/facebook/jest/pull/13633))

### Chore & Maintenance

- `[@jest/fake-timers]` Update `@sinonjs/fake-timers` ([#13612](https://github.com/facebook/jest/pull/13612))
Expand Down
4 changes: 2 additions & 2 deletions e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap
Expand Up @@ -41,7 +41,7 @@ exports[`moduleNameMapper wrong array configuration 1`] = `
12 | module.exports = () => 'test';
13 |
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:752:17)
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:758:17)
at Object.require (index.js:10:1)
at Object.require (__tests__/index.js:10:20)"
`;
Expand Down Expand Up @@ -71,7 +71,7 @@ exports[`moduleNameMapper wrong configuration 1`] = `
12 | module.exports = () => 'test';
13 |
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:752:17)
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/resolver.js:758:17)
at Object.require (index.js:10:1)
at Object.require (__tests__/index.js:10:20)"
`;
47 changes: 47 additions & 0 deletions packages/jest-resolve/src/__tests__/resolve.test.ts
Expand Up @@ -707,3 +707,50 @@ describe('Resolver.getModulePaths() -> nodeModulesPaths()', () => {
expect(dirs_actual).toEqual(expect.arrayContaining(dirs_expected));
});
});

describe('Resolver.getGlobalPaths()', () => {
const _path = path;
let moduleMap: IModuleMap;
beforeEach(() => {
moduleMap = ModuleMap.create('/');
});

it('return global paths with npm package', () => {
jest.doMock('path', () => _path.posix);
const resolver = new Resolver(moduleMap, {} as ResolverConfig);
const globalPaths = resolver.getGlobalPaths('jest');
globalPaths.forEach(globalPath =>
expect(require.resolve.paths('jest')).toContain(globalPath),
);
});

it('return empty array with builtin module', () => {
jest.doMock('path', () => _path.posix);
const resolver = new Resolver(moduleMap, {} as ResolverConfig);
const globalPaths = resolver.getGlobalPaths('fs');
expect(globalPaths).toStrictEqual([]);
});

it('return global paths with absolute path', () => {
jest.doMock('path', () => _path.posix);
const resolver = new Resolver(moduleMap, {} as ResolverConfig);
const globalPaths = resolver.getGlobalPaths('/');
globalPaths.forEach(globalPath =>
expect(require.resolve.paths('/')).toContain(globalPath),
);
});

it('return empty array with relative path', () => {
jest.doMock('path', () => _path.posix);
const resolver = new Resolver(moduleMap, {} as ResolverConfig);
const globalPaths = resolver.getGlobalPaths('./');
expect(globalPaths).toStrictEqual([]);
});

it('return empty array without module name', () => {
jest.doMock('path', () => _path.posix);
const resolver = new Resolver(moduleMap, {} as ResolverConfig);
const globalPaths = resolver.getGlobalPaths();
expect(globalPaths).toStrictEqual([]);
});
});
14 changes: 14 additions & 0 deletions packages/jest-resolve/src/nodeModulesPaths.ts
Expand Up @@ -70,3 +70,17 @@ export default function nodeModulesPaths(

return options.paths ? dirs.concat(options.paths) : dirs;
}

function findGlobalPaths(): Array<string> {
const {root} = path.parse(process.cwd());
const globalPath = path.join(root, 'node_modules');
const resolvePaths = require.resolve.paths('/');

if (resolvePaths) {
// the global paths start one after the root node_modules
const rootIndex = resolvePaths.indexOf(globalPath);
return rootIndex > -1 ? resolvePaths.slice(rootIndex + 1) : [];
}
return [];
}
export const GlobalPaths = findGlobalPaths();
10 changes: 9 additions & 1 deletion packages/jest-resolve/src/resolver.ts
Expand Up @@ -20,7 +20,7 @@ import defaultResolver, {
} from './defaultResolver';
import {clearFsCache} from './fileWalkers';
import isBuiltinModule from './isBuiltinModule';
import nodeModulesPaths from './nodeModulesPaths';
import nodeModulesPaths, {GlobalPaths} from './nodeModulesPaths';
import shouldLoadAsEsm, {clearCachedLookups} from './shouldLoadAsEsm';
import type {ResolverConfig} from './types';

Expand Down Expand Up @@ -526,6 +526,14 @@ export default class Resolver {
return paths;
}

getGlobalPaths(moduleName?: string): Array<string> {
if (!moduleName || moduleName[0] === '.' || this.isCoreModule(moduleName)) {
return [];
}

return GlobalPaths;
}

getModuleID(
virtualMocks: Map<string, boolean>,
from: string,
Expand Down
Expand Up @@ -132,8 +132,13 @@ describe('Runtime requireModule', () => {
'RegularModule',
);
expect(exports.paths.length).toBeGreaterThan(0);
exports.paths.forEach(path => {
expect(moduleDirectories.some(dir => path.endsWith(dir))).toBe(true);
const root = path.parse(process.cwd()).root;
const globalPath = path.join(root, 'node_modules');
const rootIndex = exports.paths.findIndex(path => path === globalPath);
exports.paths.forEach((path, index) => {
if (index <= rootIndex) {
expect(moduleDirectories.some(dir => path.endsWith(dir))).toBe(true);
}
});
});

Expand Down
20 changes: 16 additions & 4 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -1066,7 +1066,13 @@ export default class Runtime {
} else {
// Only include the fromPath if a moduleName is given. Else treat as root.
const fromPath = moduleName ? from : null;
this._execModule(localModule, options, moduleRegistry, fromPath);
this._execModule(
localModule,
options,
moduleRegistry,
fromPath,
moduleName,
);
}
localModule.loaded = true;
}
Expand Down Expand Up @@ -1398,6 +1404,7 @@ export default class Runtime {
}

private _requireResolvePaths(from: string, moduleName?: string) {
const fromDir = path.resolve(from, '..');
if (moduleName == null) {
throw new Error(
'The first argument to require.resolve.paths must be a string. Received null or undefined.',
Expand All @@ -1410,19 +1417,22 @@ export default class Runtime {
}

if (moduleName[0] === '.') {
return [path.resolve(from, '..')];
return [fromDir];
}
if (this._resolver.isCoreModule(moduleName)) {
return null;
}
return this._resolver.getModulePaths(path.resolve(from, '..'));
const modulePaths = this._resolver.getModulePaths(fromDir);
const globalPaths = this._resolver.getGlobalPaths(moduleName);
return [...modulePaths, ...globalPaths];
}

private _execModule(
localModule: InitialModule,
options: InternalModuleOptions | undefined,
moduleRegistry: ModuleRegistry,
from: string | null,
moduleName?: string,
) {
if (this.isTornDown) {
this._logFormattedReferenceError(
Expand Down Expand Up @@ -1454,8 +1464,10 @@ export default class Runtime {
return moduleRegistry.get(key) || null;
},
});
const modulePaths = this._resolver.getModulePaths(module.path);
const globalPaths = this._resolver.getGlobalPaths(moduleName);
module.paths = [...modulePaths, ...globalPaths];

module.paths = this._resolver.getModulePaths(module.path);
Object.defineProperty(module, 'require', {
value: this._createRequireImplementation(module, options),
});
Expand Down

0 comments on commit 2ec6d24

Please sign in to comment.