From c47797f81c5405b288427ca7cbd8f0d2ead1f5eb Mon Sep 17 00:00:00 2001 From: Ryan Tsao Date: Mon, 3 Feb 2020 13:49:23 -0800 Subject: [PATCH 1/6] Fix module id preservation with symlinks and browser field --- packages/jest-resolve/src/defaultResolver.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 3c88694e5982..9259c432d109 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -35,7 +35,7 @@ export default function defaultResolver( const resolve = options.browser ? browserResolve : resolveSync; - return resolve(path, { + let result = resolve(path, { basedir: options.basedir, defaultResolver, extensions: options.extensions, @@ -43,6 +43,12 @@ export default function defaultResolver( paths: options.paths, rootDir: options.rootDir, }); + if (result) { + // Dereference symlinks to ensure we don't create a separate + // module instance depending on how it was referenced. + result = fs.realpathSync(result); + } + return result; } export const clearDefaultResolverCache = () => { @@ -98,11 +104,6 @@ function resolveSync( if (isDirectory(dir)) { result = resolveAsFile(name) || resolveAsDirectory(name); } - if (result) { - // Dereference symlinks to ensure we don't create a separate - // module instance depending on how it was referenced. - result = fs.realpathSync(result); - } return result; } From 0fb3651125413b0e4594d841f7eedbd914bb3392 Mon Sep 17 00:00:00 2001 From: Ryan Tsao Date: Mon, 3 Feb 2020 14:45:20 -0800 Subject: [PATCH 2/6] Update defaultResolver.ts --- packages/jest-resolve/src/defaultResolver.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 9259c432d109..56a65fb81b1d 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -43,7 +43,7 @@ export default function defaultResolver( paths: options.paths, rootDir: options.rootDir, }); - if (result) { + if (options.browser && result) { // Dereference symlinks to ensure we don't create a separate // module instance depending on how it was referenced. result = fs.realpathSync(result); @@ -104,6 +104,11 @@ function resolveSync( if (isDirectory(dir)) { result = resolveAsFile(name) || resolveAsDirectory(name); } + if (result) { + // Dereference symlinks to ensure we don't create a separate + // module instance depending on how it was referenced. + result = fs.realpathSync(result); + } return result; } From 5608e999150b51b78b0d7425f7f24339625fdb7f Mon Sep 17 00:00:00 2001 From: Ryan Tsao Date: Tue, 4 Feb 2020 11:31:41 -0800 Subject: [PATCH 3/6] Add e2e test --- e2e/Utils.ts | 19 +++++++ e2e/__tests__/resolveBrowserField.test.ts | 68 +++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 e2e/__tests__/resolveBrowserField.test.ts diff --git a/e2e/Utils.ts b/e2e/Utils.ts index a98e2e6bca39..f294fbb09138 100644 --- a/e2e/Utils.ts +++ b/e2e/Utils.ts @@ -90,6 +90,25 @@ export const writeFiles = ( }); }; +export const writeSymlinks = ( + directory: string, + symlinks: {[existingFile: string]: string}, +) => { + createDirectory(directory); + Object.keys(symlinks).forEach(fileOrPath => { + const symLinkPath = symlinks[fileOrPath]; + const dirname = path.dirname(symLinkPath); + + if (dirname !== '/') { + createDirectory(path.join(directory, dirname)); + } + fs.symlinkSync( + path.resolve(directory, ...fileOrPath.split('/')), + path.resolve(directory, ...symLinkPath.split('/')), + ); + }); +}; + const NUMBER_OF_TESTS_TO_FORCE_USING_WORKERS = 25; /** * Forces Jest to use workers by generating many test files to run. diff --git a/e2e/__tests__/resolveBrowserField.test.ts b/e2e/__tests__/resolveBrowserField.test.ts new file mode 100644 index 000000000000..52483767ed8d --- /dev/null +++ b/e2e/__tests__/resolveBrowserField.test.ts @@ -0,0 +1,68 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +import {tmpdir} from 'os'; +import * as path from 'path'; +import {cleanup, writeFiles, writeSymlinks} from '../Utils'; +import runJest from '../runJest'; + +const DIR = path.resolve(tmpdir(), 'resolve-browser-field-test'); + +beforeEach(() => cleanup(DIR)); +afterAll(() => cleanup(DIR)); + +test('preserves module identity for symlinks when using browser field resolution', () => { + writeFiles(DIR, { + 'packages/needs-preserved-id/package.json': JSON.stringify({ + name: 'needs-preserved-id', + }), + 'packages/needs-preserved-id/index.js': ` + console.log("needs-preserved-id executed"); + module.exports = {}; + `, + 'packages/has-browser-field/package.json': JSON.stringify({ + name: 'has-browser-field', + browser: 'browser.js', + }), + 'packages/has-browser-field/browser.js': ` + module.exports = require("needs-preserved-id"); + `, + 'package.json': JSON.stringify({ + jest: { + testMatch: ['/test-files/test.js'], + browser: true, + }, + }), + 'test-files/test.js': ` + const id1 = require("needs-preserved-id"); + const id2 = require("has-browser-field"); + + test("module should have reference equality", () => { + expect(id1).toBe(id2); + }); + `, + }); + + writeSymlinks(DIR, { + 'packages/needs-preserved-id': 'node_modules/needs-preserved-id', + 'packages/has-browser-field': 'node_modules/has-browser-field', + }); + + writeSymlinks(DIR, { + 'packages/needs-preserved-id': + 'packages/has-browser-field/node_modules/needs-preserved-id', + }); + + const {stdout, stderr, exitCode} = runJest(DIR, ['--no-watchman']); + expect(stderr).toContain('Test Suites: 1 passed, 1 total'); + expect(stdout).toMatchInlineSnapshot(` + " console.log packages/needs-preserved-id/index.js:2 + needs-preserved-id executed + " + `); + expect(exitCode).toEqual(0); +}); From 20e0cdc1a8b60ce5db24de01541bda3453a14d23 Mon Sep 17 00:00:00 2001 From: Ryan Tsao Date: Tue, 4 Feb 2020 11:34:32 -0800 Subject: [PATCH 4/6] Replace fs.realpathSync with realpath-native --- packages/jest-resolve/src/defaultResolver.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index 56a65fb81b1d..7dde191c71b5 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -8,6 +8,7 @@ import * as fs from 'fs'; import * as path from 'path'; import {sync as browserResolve} from 'browser-resolve'; +import {sync as realpath} from 'realpath-native'; import pnpResolver from 'jest-pnp-resolver'; import {Config} from '@jest/types'; import isBuiltinModule from './isBuiltinModule'; @@ -46,7 +47,7 @@ export default function defaultResolver( if (options.browser && result) { // Dereference symlinks to ensure we don't create a separate // module instance depending on how it was referenced. - result = fs.realpathSync(result); + result = realpath(result); } return result; } @@ -107,7 +108,7 @@ function resolveSync( if (result) { // Dereference symlinks to ensure we don't create a separate // module instance depending on how it was referenced. - result = fs.realpathSync(result); + result = realpath(result); } return result; } From 560cadf9ad0af679890eb51739a050ffbeab20cb Mon Sep 17 00:00:00 2001 From: Ryan Tsao Date: Tue, 4 Feb 2020 11:47:18 -0800 Subject: [PATCH 5/6] Satisfy linter --- e2e/__tests__/resolveBrowserField.test.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/e2e/__tests__/resolveBrowserField.test.ts b/e2e/__tests__/resolveBrowserField.test.ts index 52483767ed8d..898e888bbe58 100644 --- a/e2e/__tests__/resolveBrowserField.test.ts +++ b/e2e/__tests__/resolveBrowserField.test.ts @@ -16,14 +16,15 @@ beforeEach(() => cleanup(DIR)); afterAll(() => cleanup(DIR)); test('preserves module identity for symlinks when using browser field resolution', () => { + /* eslint-disable sort-keys */ writeFiles(DIR, { - 'packages/needs-preserved-id/package.json': JSON.stringify({ - name: 'needs-preserved-id', - }), 'packages/needs-preserved-id/index.js': ` console.log("needs-preserved-id executed"); module.exports = {}; `, + 'packages/needs-preserved-id/package.json': JSON.stringify({ + name: 'needs-preserved-id', + }), 'packages/has-browser-field/package.json': JSON.stringify({ name: 'has-browser-field', browser: 'browser.js', @@ -46,10 +47,11 @@ test('preserves module identity for symlinks when using browser field resolution }); `, }); + /* eslint-enable sort-keys */ writeSymlinks(DIR, { - 'packages/needs-preserved-id': 'node_modules/needs-preserved-id', 'packages/has-browser-field': 'node_modules/has-browser-field', + 'packages/needs-preserved-id': 'node_modules/needs-preserved-id', }); writeSymlinks(DIR, { From 2fdb2fdc13a7b7dd0cd0cfd12f656c32c2ee591d Mon Sep 17 00:00:00 2001 From: Ryan Tsao Date: Tue, 4 Feb 2020 12:38:28 -0800 Subject: [PATCH 6/6] Update e2e/__tests__/resolveBrowserField.test.ts Co-Authored-By: Simen Bekkhus --- e2e/__tests__/resolveBrowserField.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/__tests__/resolveBrowserField.test.ts b/e2e/__tests__/resolveBrowserField.test.ts index 898e888bbe58..761b25abbff3 100644 --- a/e2e/__tests__/resolveBrowserField.test.ts +++ b/e2e/__tests__/resolveBrowserField.test.ts @@ -47,7 +47,7 @@ test('preserves module identity for symlinks when using browser field resolution }); `, }); - /* eslint-enable sort-keys */ + /* eslint-enable */ writeSymlinks(DIR, { 'packages/has-browser-field': 'node_modules/has-browser-field',