diff --git a/CHANGELOG.md b/CHANGELOG.md index 3203b1e853e0..5c46758f80dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## master +### Fixes + +* `[jest-resolve]` Update node module resolution algorithm to correctly handle + symlinked paths ([#5085](https://github.com/facebook/jest/pull/5085)) + ## 22.4.2 ### Fixes diff --git a/appveyor.yml b/appveyor.yml index 0ce7d699fc34..f48ec1c833ea 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,6 +6,7 @@ init: # debugging Appveyor build. More info: # https://www.appveyor.com/docs/how-to/rdp-to-build-worker/ - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) + - git config --global core.symlinks true install: - ps: Install-Product node $env:nodejs_version x64 diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index 6e52ab37dc7e..c6259e8b9cd7 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -9,7 +9,8 @@ "main": "build/index.js", "dependencies": { "browser-resolve": "^1.11.2", - "chalk": "^2.0.1" + "chalk": "^2.0.1", + "realpath-native": "^1.0.0" }, "devDependencies": { "jest-haste-map": "^22.4.2" diff --git a/packages/jest-resolve/src/__mocks__/bar/node_modules/bar/index.js b/packages/jest-resolve/src/__mocks__/bar/node_modules/bar/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/bar/node_modules/foo b/packages/jest-resolve/src/__mocks__/bar/node_modules/foo new file mode 120000 index 000000000000..99d688a4b56a --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/bar/node_modules/foo @@ -0,0 +1 @@ +../../foo/node_modules/foo \ No newline at end of file diff --git a/packages/jest-resolve/src/__mocks__/foo/node_modules/dep/index.js b/packages/jest-resolve/src/__mocks__/foo/node_modules/dep/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/foo/node_modules/foo/index.js b/packages/jest-resolve/src/__mocks__/foo/node_modules/foo/index.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__tests__/resolve.test.js b/packages/jest-resolve/src/__tests__/resolve.test.js index 7806495d8ee9..91986a73ac5f 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.js +++ b/packages/jest-resolve/src/__tests__/resolve.test.js @@ -129,6 +129,19 @@ describe('resolveModule', () => { require.resolve('../__mocks__/mockJsxDependency.native.jsx'), ); }); + + it('is possible to resolve node modules by resolving their realpath', () => { + const resolver = new Resolver(moduleMap, { + extensions: ['.js'], + }); + const src = require.resolve( + '../../src/__mocks__/bar/node_modules/foo/index.js', + ); + const resolved = resolver.resolveModule(src, 'dep'); + expect(resolved).toBe( + require.resolve('../../src/__mocks__/foo/node_modules/dep/index.js'), + ); + }); }); describe('getMockModule', () => { @@ -203,6 +216,16 @@ describe('Resolver.getModulePaths() -> nodeModulesPaths()', () => { map: [], mocks: [], }); + + // Mocking realpath to function the old way, where it just looks at + // pathstrings instead of actually trying to access the physical directory. + // This test suite won't work otherwise, since we cannot make assumptions + // about the test environment when it comes to absolute paths. + jest.doMock('realpath-native', () => { + return { + sync: dirInput => dirInput, + }; + }); }); afterAll(() => { diff --git a/packages/jest-resolve/src/node_modules_paths.js b/packages/jest-resolve/src/node_modules_paths.js index 21383dca2d52..52da44372758 100644 --- a/packages/jest-resolve/src/node_modules_paths.js +++ b/packages/jest-resolve/src/node_modules_paths.js @@ -11,6 +11,7 @@ import type {Path} from 'types/Config'; import path from 'path'; +import {sync as realpath} from 'realpath-native'; type NodeModulesPathsOptions = {| moduleDirectory?: Array, @@ -37,11 +38,17 @@ export default function nodeModulesPaths( prefix = '\\\\'; } - const paths = [basedirAbs]; - let parsed = path.parse(basedirAbs); + // The node resolution algorithm (as implemented by NodeJS + // and TypeScript) traverses parents of the physical path, + // not the symlinked path + const physicalBasedir = realpath(basedirAbs); + + const paths = [physicalBasedir]; + let parsed = path.parse(physicalBasedir); while (parsed.dir !== paths[paths.length - 1]) { - paths.push(parsed.dir); - parsed = path.parse(parsed.dir); + const realParsedDir = realpath(parsed.dir); + paths.push(realParsedDir); + parsed = path.parse(realParsedDir); } const dirs = paths diff --git a/yarn.lock b/yarn.lock index 4fb55bb21993..1e32c1df1b78 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4534,6 +4534,10 @@ jest-docblock@^21, jest-docblock@^21.0.0, jest-docblock@^21.2.0: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" +jest-get-type@^21.2.0: + version "21.2.0" + resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" + jest-haste-map@^21: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-21.2.0.tgz#1363f0a8bb4338f24f001806571eff7a4b2ff3d8" @@ -4545,6 +4549,39 @@ jest-haste-map@^21: sane "^2.0.0" worker-farm "^1.3.1" +jest-message-util@^21.2.1: + version "21.2.1" + resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-message-util/-/jest-message-util-21.2.1.tgz#bfe5d4692c84c827d1dcf41823795558f0a1acbe" + dependencies: + chalk "^2.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + +jest-mock@^21.2.0: + version "21.2.0" + resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-mock/-/jest-mock-21.2.0.tgz#7eb0770e7317968165f61ea2a7281131534b3c0f" + +jest-util@^21.2.1: + version "21.2.1" + resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-util/-/jest-util-21.2.1.tgz#a274b2f726b0897494d694a6c3d6a61ab819bb78" + dependencies: + callsites "^2.0.0" + chalk "^2.0.1" + graceful-fs "^4.1.11" + jest-message-util "^21.2.1" + jest-mock "^21.2.0" + jest-validate "^21.2.1" + mkdirp "^0.5.1" + +jest-validate@^21.2.1: + version "21.2.1" + resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" + dependencies: + chalk "^2.0.1" + jest-get-type "^21.2.0" + leven "^2.1.0" + pretty-format "^21.2.1" + jquery@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787" @@ -6200,6 +6237,13 @@ prettier@^1.10.1: version "1.10.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.1.tgz#01423fea6957ea23618d37d339ef0e7f7c967fc6" +pretty-format@^21.2.1: + version "21.2.1" + resolved "https://onedrive.pkgs.visualstudio.com/_packaging/odsp-npm/npm/registry/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36" + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + pretty-format@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-4.3.1.tgz#530be5c42b3c05b36414a7a2a4337aa80acd0e8d"