Skip to content

Commit

Permalink
Fix module identity preservation with symlinks and browser field (#9511)
Browse files Browse the repository at this point in the history
  • Loading branch information
rtsao authored Feb 4, 2020
1 parent 964dc16 commit ee9e2ea
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 2 deletions.
19 changes: 19 additions & 0 deletions e2e/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
70 changes: 70 additions & 0 deletions e2e/__tests__/resolveBrowserField.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* 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', () => {
/* eslint-disable sort-keys */
writeFiles(DIR, {
'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',
}),
'packages/has-browser-field/browser.js': `
module.exports = require("needs-preserved-id");
`,
'package.json': JSON.stringify({
jest: {
testMatch: ['<rootDir>/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);
});
`,
});
/* eslint-enable */

writeSymlinks(DIR, {
'packages/has-browser-field': 'node_modules/has-browser-field',
'packages/needs-preserved-id': 'node_modules/needs-preserved-id',
});

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);
});
11 changes: 9 additions & 2 deletions packages/jest-resolve/src/defaultResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -35,14 +36,20 @@ export default function defaultResolver(

const resolve = options.browser ? browserResolve : resolveSync;

return resolve(path, {
let result = resolve(path, {
basedir: options.basedir,
defaultResolver,
extensions: options.extensions,
moduleDirectory: options.moduleDirectory,
paths: options.paths,
rootDir: options.rootDir,
});
if (options.browser && result) {
// Dereference symlinks to ensure we don't create a separate
// module instance depending on how it was referenced.
result = realpath(result);
}
return result;
}

export const clearDefaultResolverCache = () => {
Expand Down Expand Up @@ -101,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;
}
Expand Down

0 comments on commit ee9e2ea

Please sign in to comment.