Skip to content

Commit

Permalink
Add support for NODE_PRESERVE_SYMLINKS and --preserve-symlinks behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
Giancarlo Anemone committed May 5, 2020
1 parent 1add160 commit 185664c
Show file tree
Hide file tree
Showing 21 changed files with 239 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
/examples/*/node_modules/
/examples/mongodb/globalConfig.json

/e2e/preserve-symlinks/*
/e2e/*/node_modules
/e2e/*/.pnp
/e2e/*/.pnp.js
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Features

- `[*]` Add support for NODE_PRESERVE_SYMLINKS and --preserve-symlinks behavior

### Fixes

### Chore & Maintenance
Expand Down
95 changes: 95 additions & 0 deletions e2e/__tests__/preserveSymlinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* 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 {join, resolve} from 'path';
import {
existsSync,
mkdirSync,
rmdirSync,
symlinkSync,
unlinkSync,
} from 'graceful-fs';

import runJest from '../runJest';
import {extractSummary} from '../Utils';

const destRoot = resolve(__dirname, '../preserve-symlinks');
const srcRoot = resolve(__dirname, '../symlinked-source-dir');

const files = [
'package.json',
'a.js',
'b.js',
'ab.js',
'__tests__/a.test.js',
'__tests__/b.test.js',
'__tests__/ab.test.js',
];

function cleanup() {
files
.map(f => join(destRoot, f))
.filter(f => existsSync(f))
.forEach(f => {
unlinkSync(f);
});
if (existsSync(join(destRoot, '__tests__'))) {
rmdirSync(join(destRoot, '__tests__'));
}
if (existsSync(destRoot)) {
rmdirSync(destRoot);
}
}

beforeAll(() => {
cleanup();
mkdirSync(destRoot);
mkdirSync(join(destRoot, '__tests__'));
files.forEach(f => {
symlinkSync(join(srcRoot, f), join(destRoot, f));
});
});

afterAll(() => {
cleanup();
});

test('preserving symlinks with environment variable', () => {
const {stderr, exitCode} = runJest('preserve-symlinks', ['--no-watchman'], {
preserveSymlinks: '1',
});
const {summary, rest} = extractSummary(stderr);
expect(exitCode).toEqual(0);
expect(rest.split('\n').length).toEqual(3);
expect(rest).toMatch('PASS __tests__/ab.test.js');
expect(rest).toMatch('PASS __tests__/a.test.js');
expect(rest).toMatch('PASS __tests__/b.test.js');
expect(summary).toMatch('Test Suites: 3 passed, 3 total');
expect(summary).toMatch('Tests: 3 passed, 3 total');
expect(summary).toMatch('Snapshots: 0 total');
});

test('preserving symlinks with --preserve-symlinks node flag', () => {
const {stderr, exitCode} = runJest('preserve-symlinks', ['--no-watchman'], {
nodeFlags: ['--preserve-symlinks'],
});
const {summary, rest} = extractSummary(stderr);
expect(exitCode).toEqual(0);
expect(rest.split('\n').length).toEqual(3);
expect(rest).toMatch('PASS __tests__/ab.test.js');
expect(rest).toMatch('PASS __tests__/a.test.js');
expect(rest).toMatch('PASS __tests__/b.test.js');
expect(summary).toMatch('Test Suites: 3 passed, 3 total');
expect(summary).toMatch('Tests: 3 passed, 3 total');
expect(summary).toMatch('Snapshots: 0 total');
});

test('no preserve symlinks configuration', () => {
const {exitCode, stdout} = runJest('preserve-symlinks', ['--no-watchman']);
expect(exitCode).toEqual(1);
expect(stdout).toMatch('No tests found, exiting with code 1');
});
7 changes: 7 additions & 0 deletions e2e/runJest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {normalizeIcons} from './Utils';
const JEST_PATH = path.resolve(__dirname, '../packages/jest-cli/bin/jest.js');

type RunJestOptions = {
preserveSymlinks?: string;
nodeFlags?: Array<string>;
nodeOptions?: string;
nodePath?: string;
skipPkgJsonCheck?: boolean; // don't complain if can't find package.json
Expand Down Expand Up @@ -77,8 +79,13 @@ function spawnJest(

if (options.nodeOptions) env['NODE_OPTIONS'] = options.nodeOptions;
if (options.nodePath) env['NODE_PATH'] = options.nodePath;
if (options.preserveSymlinks)
env['NODE_PRESERVE_SYMLINKS'] = options.preserveSymlinks;

const spawnArgs = [JEST_PATH, ...args];
if (options.nodeFlags) {
spawnArgs.unshift(...options.nodeFlags);
}
const spawnOptions = {
cwd: dir,
env,
Expand Down
12 changes: 12 additions & 0 deletions e2e/symlinked-source-dir/__tests__/a.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* 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.
*
*/
const a = require('../a');

test('a', () => {
expect(a()).toEqual('a');
});
12 changes: 12 additions & 0 deletions e2e/symlinked-source-dir/__tests__/ab.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* 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.
*
*/
const ab = require('../ab');

test('ab', () => {
expect(ab()).toEqual('ab');
});
12 changes: 12 additions & 0 deletions e2e/symlinked-source-dir/__tests__/b.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* 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.
*
*/
const b = require('../b');

test('b', () => {
expect(b()).toEqual('b');
});
10 changes: 10 additions & 0 deletions e2e/symlinked-source-dir/a.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* 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.
*
*/
module.exports = function a() {
return 'a';
};
13 changes: 13 additions & 0 deletions e2e/symlinked-source-dir/ab.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* 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.
*
*/
const a = require('./a');
const b = require('./b');

module.exports = function ab() {
return a() + b();
};
10 changes: 10 additions & 0 deletions e2e/symlinked-source-dir/b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* 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.
*
*/
module.exports = function b() {
return 'b';
};
5 changes: 5 additions & 0 deletions e2e/symlinked-source-dir/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"jest": {
"testEnvironment": "node"
}
}
3 changes: 2 additions & 1 deletion packages/jest-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"jest-util": "^26.0.1",
"jest-validate": "^26.0.1",
"micromatch": "^4.0.2",
"pretty-format": "^26.0.1"
"pretty-format": "^26.0.1",
"should-preserve-links": "^1.0.4"
},
"devDependencies": {
"@types/babel__core": "^7.0.4",
Expand Down
10 changes: 10 additions & 0 deletions packages/jest-config/should-preserve-links.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* 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.
*/

declare module 'should-preserve-links' {
export default function shouldPreserveLinks(): boolean;
}
3 changes: 2 additions & 1 deletion packages/jest-config/src/Defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import type {Config} from '@jest/types';
import {replacePathSepForRegex} from 'jest-regex-util';
import shouldPreserveSymlinks from 'should-preserve-links';
import {NODE_MODULES} from './constants';
import getCacheDirectory from './getCacheDirectory';

Expand Down Expand Up @@ -66,7 +67,7 @@ const defaultOptions: Config.DefaultOptions = {
useStderr: false,
watch: false,
watchPathIgnorePatterns: [],
watchman: true,
watchman: !shouldPreserveSymlinks(),
};

export default defaultOptions;
3 changes: 2 additions & 1 deletion packages/jest-haste-map/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"micromatch": "^4.0.2",
"sane": "^4.0.3",
"walker": "^1.0.7",
"which": "^2.0.2"
"which": "^2.0.2",
"should-preserve-links": "^1.0.4"
},
"devDependencies": {
"@jest/test-utils": "^26.0.0",
Expand Down
10 changes: 10 additions & 0 deletions packages/jest-haste-map/should-preserve-links.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* 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.
*/

declare module 'should-preserve-links' {
export default function shouldPreserveLinks(): boolean;
}
15 changes: 12 additions & 3 deletions packages/jest-haste-map/src/crawlers/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as path from 'path';
import {spawn} from 'child_process';
import * as fs from 'graceful-fs';
import which = require('which');
import shouldPreserveSymlinks from 'should-preserve-links';
import H from '../constants';
import * as fastPath from '../lib/fast_path';
import type {
Expand All @@ -18,6 +19,8 @@ import type {
InternalHasteMap,
} from '../types';

const preserveSymlinks = shouldPreserveSymlinks();

type Result = Array<[/* id */ string, /* mtime */ number, /* size */ number]>;

type Callback = (result: Result) => void;
Expand Down Expand Up @@ -67,7 +70,7 @@ function find(
}

if (typeof entry !== 'string') {
if (entry.isSymbolicLink()) {
if (!preserveSymlinks && entry.isSymbolicLink()) {
return;
}

Expand All @@ -84,7 +87,7 @@ function find(

// This logic is unnecessary for node > v10.10, but leaving it in
// since we need it for backwards-compatibility still.
if (!err && stat && !stat.isSymbolicLink()) {
if (!err && stat && (preserveSymlinks || !stat.isSymbolicLink())) {
if (stat.isDirectory()) {
search(file);
} else {
Expand Down Expand Up @@ -121,7 +124,13 @@ function findNative(
callback: Callback,
): void {
const args = Array.from(roots);
args.push('-type', 'f');
if (preserveSymlinks) {
// follow symlinks to determine file type
args.unshift('-L');
args.push('( -not -type d )');
} else {
args.push('-type', 'f');
}
if (extensions.length) {
args.push('(');
}
Expand Down
1 change: 1 addition & 0 deletions packages/jest-util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"types": "build/index.d.ts",
"dependencies": {
"@jest/types": "^26.0.1",
"should-preserve-links": "^1.0.4",
"chalk": "^4.0.0",
"graceful-fs": "^4.2.4",
"is-ci": "^2.0.0",
Expand Down
10 changes: 10 additions & 0 deletions packages/jest-util/should-preserve-links.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* 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.
*/

declare module 'should-preserve-links' {
export default function shouldPreserveLinks(): boolean;
}
6 changes: 6 additions & 0 deletions packages/jest-util/src/tryRealpath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@

import {realpathSync} from 'graceful-fs';
import type {Config} from '@jest/types';
import shouldPreserveSymlinks from 'should-preserve-links';

const preserveSymlinks = shouldPreserveSymlinks();

export default function tryRealpath(path: Config.Path): Config.Path {
if (preserveSymlinks) {
return path;
}
try {
path = realpathSync.native(path);
} catch (error) {
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12159,6 +12159,11 @@ shellwords@^0.1.1:
resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==

should-preserve-links@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/should-preserve-links/-/should-preserve-links-1.0.4.tgz#7b2a0b44efb9f0edd94332a06af92d84cc988876"
integrity sha512-73GxeFbAj4PAj/q4lHvui1hd1X6TWRgG41znftUvwQa+lNRz+X73od8Z5N4CyD2xCFE2PDMUvIfkYSyhwJaI4A==

side-channel@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"
Expand Down

0 comments on commit 185664c

Please sign in to comment.