Skip to content

Commit f82910e

Browse files
committed
feat: experimental ES Modules support
1 parent 4caa7b5 commit f82910e

File tree

15 files changed

+380
-26
lines changed

15 files changed

+380
-26
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
### Features
44

5+
- `[jest-runtime, jest-jasmine2, jest-circus]` Experimental, limited ECMAScript Modules support ([#9772](https://github.com/facebook/jest/pull/9772))
6+
57
### Fixes
68

79
### Chore & Maintenance
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`on node ^12.16.0 || >=13.0.0 runs test with native ESM 1`] = `
4+
Test Suites: 1 passed, 1 total
5+
Tests: 2 passed, 2 total
6+
Snapshots: 0 total
7+
Time: <<REPLACED>>
8+
Ran all test suites.
9+
`;

e2e/__tests__/nativeEsm.test.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import {resolve} from 'path';
9+
import wrap from 'jest-snapshot-serializer-raw';
10+
import {onNodeVersions} from '@jest/test-utils';
11+
import runJest, {getConfig} from '../runJest';
12+
import {extractSummary} from '../Utils';
13+
14+
const DIR = resolve(__dirname, '../native-esm');
15+
16+
test('test config is without transform', () => {
17+
const {configs} = getConfig(DIR);
18+
19+
expect(configs).toHaveLength(1);
20+
expect(configs[0].transform).toEqual([]);
21+
});
22+
23+
// The versions vm.Module was introduced
24+
onNodeVersions('^12.16.0 || >=13.0.0', () => {
25+
test('runs test with native ESM', () => {
26+
const {exitCode, stderr, stdout} = runJest(DIR, [], {
27+
nodeOptions: '--experimental-vm-modules',
28+
});
29+
30+
const {summary} = extractSummary(stderr);
31+
32+
expect(wrap(summary)).toMatchSnapshot();
33+
expect(stdout).toBe('');
34+
expect(exitCode).toBe(0);
35+
});
36+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import {double} from '../index';
9+
10+
test('should have correct import.meta', () => {
11+
expect(typeof jest).toBe('undefined');
12+
expect(import.meta).toEqual({
13+
jest: expect.anything(),
14+
url: expect.any(String),
15+
});
16+
expect(
17+
import.meta.url.endsWith('/e2e/native-esm/__tests__/native-esm.test.js')
18+
).toBe(true);
19+
});
20+
21+
test('should double stuff', () => {
22+
expect(double(1)).toBe(2);
23+
});

e2e/native-esm/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
export function double(num) {
9+
return num * 2;
10+
}

e2e/native-esm/package.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "module",
3+
"jest": {
4+
"testEnvironment": "node",
5+
"transform": {}
6+
}
7+
}

packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,26 @@ const jestAdapter = async (
7777
}
7878
});
7979

80-
config.setupFilesAfterEnv.forEach(path => runtime.requireModule(path));
80+
await Promise.all(
81+
config.setupFilesAfterEnv.map(async path => {
82+
const esm = runtime.unstable_shouldLoadAsEsm(path);
83+
84+
if (esm) {
85+
await runtime.unstable_importModule(path);
86+
} else {
87+
runtime.requireModule(path);
88+
}
89+
}),
90+
);
91+
92+
const esm = runtime.unstable_shouldLoadAsEsm(testPath);
93+
94+
if (esm) {
95+
await runtime.unstable_importModule(testPath);
96+
} else {
97+
runtime.requireModule(testPath);
98+
}
8199

82-
runtime.requireModule(testPath);
83100
const results = await runAndTransformResultsToJestFormat({
84101
config,
85102
globalConfig,

packages/jest-jasmine2/src/index.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,17 @@ async function jasmine2(
155155
testPath,
156156
});
157157

158-
config.setupFilesAfterEnv.forEach(path => runtime.requireModule(path));
158+
await Promise.all(
159+
config.setupFilesAfterEnv.map(async path => {
160+
const esm = runtime.unstable_shouldLoadAsEsm(path);
161+
162+
if (esm) {
163+
await runtime.unstable_importModule(path);
164+
} else {
165+
runtime.requireModule(path);
166+
}
167+
}),
168+
);
159169

160170
if (globalConfig.enabledTestsMap) {
161171
env.specFilter = (spec: Spec) => {
@@ -169,7 +179,14 @@ async function jasmine2(
169179
env.specFilter = (spec: Spec) => testNameRegex.test(spec.getFullName());
170180
}
171181

172-
runtime.requireModule(testPath);
182+
const esm = runtime.unstable_shouldLoadAsEsm(testPath);
183+
184+
if (esm) {
185+
await runtime.unstable_importModule(testPath);
186+
} else {
187+
runtime.requireModule(testPath);
188+
}
189+
173190
await env.execute();
174191

175192
const results = await reporter.getResults();

packages/jest-runner/src/runTest.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ function freezeConsole(
6767
};
6868
}
6969

70+
function invariant(condition: unknown, message?: string): asserts condition {
71+
if (!condition) {
72+
throw new Error(message);
73+
}
74+
}
75+
7076
// Keeping the core of "runTest" as a separate function (as "runTestInternal")
7177
// is key to be able to detect memory leaks. Since all variables are local to
7278
// the function, when "runTestInternal" finishes its execution, they can all be
@@ -163,7 +169,19 @@ async function runTestInternal(
163169

164170
const start = Date.now();
165171

166-
config.setupFiles.forEach(path => runtime!.requireModule(path));
172+
await Promise.all(
173+
config.setupFiles.map(async path => {
174+
invariant(runtime, 'TS is being difficult');
175+
176+
const esm = runtime.unstable_shouldLoadAsEsm(path);
177+
178+
if (esm) {
179+
await runtime.unstable_importModule(path);
180+
} else {
181+
runtime.requireModule(path);
182+
}
183+
}),
184+
);
167185

168186
const sourcemapOptions: sourcemapSupport.Options = {
169187
environment: 'node',

packages/jest-runtime/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"jest-snapshot": "^25.3.0",
3939
"jest-util": "^25.3.0",
4040
"jest-validate": "^25.3.0",
41+
"read-pkg-up": "^7.0.1",
4142
"realpath-native": "^2.0.0",
4243
"slash": "^3.0.0",
4344
"strip-bom": "^4.0.0",

packages/jest-runtime/src/__mocks__/createRuntime.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,17 @@ module.exports = async function createRuntime(filename, config) {
4949
Runtime.createResolver(config, hasteMap.moduleMap),
5050
);
5151

52-
config.setupFiles.forEach(path => runtime.requireModule(path));
52+
await Promise.all(
53+
config.setupFiles.map(async path => {
54+
const esm = runtime.unstable_shouldLoadAsEsm(path);
55+
56+
if (esm) {
57+
await runtime.unstable_importModule(path);
58+
} else {
59+
runtime.requireModule(path);
60+
}
61+
}),
62+
);
5363

5464
runtime.__mockRootPath = path.join(config.rootDir, 'root.js');
5565
runtime.__mockSubdirPath = path.join(

packages/jest-runtime/src/cli/index.ts

+17-2
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,24 @@ export async function run(
9393

9494
const runtime = new Runtime(config, environment, hasteMap.resolver);
9595

96-
config.setupFiles.forEach(path => runtime.requireModule(path));
96+
await Promise.all(
97+
config.setupFiles.map(async path => {
98+
const esm = runtime.unstable_shouldLoadAsEsm(path);
9799

98-
runtime.requireModule(filePath);
100+
if (esm) {
101+
await runtime.unstable_importModule(path);
102+
} else {
103+
runtime.requireModule(path);
104+
}
105+
}),
106+
);
107+
const esm = runtime.unstable_shouldLoadAsEsm(filePath);
108+
109+
if (esm) {
110+
await runtime.unstable_importModule(filePath);
111+
} else {
112+
runtime.requireModule(filePath);
113+
}
99114
} catch (e) {
100115
console.error(chalk.red(e.stack || e));
101116
process.on('exit', () => (process.exitCode = 1));

0 commit comments

Comments
 (0)