Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1141bd4

Browse files
authored
Better ESM-CJS compatibility fix (#16050)
1 parent dcd3c79 commit 1141bd4

28 files changed

Lines changed: 550 additions & 13 deletions

.github/workflows/nodejs.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,35 @@ jobs:
149149
- name: run tests with leak detection
150150
run: yarn test-leak
151151

152+
test-runtime-vm-modules:
153+
name: Node LTS on Ubuntu with --experimental-vm-modules (jest-runtime)
154+
runs-on: ubuntu-latest
155+
needs: prepare-yarn-cache-ubuntu
156+
157+
steps:
158+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
159+
with:
160+
persist-credentials: false
161+
- name: Use Node.js LTS
162+
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
163+
with:
164+
node-version: lts/*
165+
cache: yarn
166+
- name: install
167+
run: yarn --immutable
168+
- name: build
169+
run: yarn build:js
170+
- name: Get number of CPU cores
171+
id: cpu-cores
172+
uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2.0.0
173+
- name: run jest-runtime tests with --experimental-vm-modules
174+
uses: nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08 # v3.0.2
175+
with:
176+
timeout_minutes: 10
177+
max_attempts: 3
178+
retry_on: error
179+
command: yarn jest-runtime-vm-modules-ci --max-workers ${{ steps.cpu-cores.outputs.count }}
180+
152181
test-coverage:
153182
name: Node LTS on Ubuntu with coverage (${{ matrix.shard }})
154183
strategy:

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
- `[jest-resolve]` Add `canResolveSync()` on `Resolver` so callers can detect when a user-configured resolver only exports an `async` hook ([#16064](https://github.com/jestjs/jest/pull/16064))
88
- `[jest-runtime]` Use synchronous `evaluate()` for ES modules without top-level `await` on Node versions that support it (v24.9+), and prefer the synchronous transform path when a sync transformer is configured ([#16062](https://github.com/jestjs/jest/pull/16062))
99

10+
### Fixes
11+
12+
- `[jest-runtime]` Improve CJS-from-ESM interop: `__esModule`/Babel default unwrap, broader named-export coverage, and shared CJS singleton across importers ([#16050](https://github.com/jestjs/jest/pull/16050))
13+
- `[jest-runtime]` Load `.js` files with ESM syntax but no `"type":"module"` marker as native ESM ([#16050](https://github.com/jestjs/jest/pull/16050))
14+
- `[jest-runtime]` Fix deadlocks and double-evaluation in concurrent ESM and wasm imports ([#16050](https://github.com/jestjs/jest/pull/16050))
15+
1016
## 30.3.0
1117

1218
### Features
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2+
3+
exports[`runs ESM/CJS interop fixture (__esModule unwrapping, named exports, singleton caching) 1`] = `
4+
"Test Suites: 1 passed, 1 total
5+
Tests: 6 passed, 6 total
6+
Snapshots: 0 total
7+
Time: <<REPLACED>>
8+
Ran all test suites matching cjs-esm-interop."
9+
`;
10+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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 {extractSummary} from '../Utils';
10+
import runJest from '../runJest';
11+
12+
const DIR = resolve(__dirname, '../native-esm-cjs-require');
13+
14+
test('runs ESM/CJS interop fixture (__esModule unwrapping, named exports, singleton caching)', () => {
15+
const {exitCode, stderr} = runJest(
16+
DIR,
17+
['--testPathPatterns', 'cjs-esm-interop'],
18+
{nodeOptions: '--experimental-vm-modules --no-warnings'},
19+
);
20+
21+
const {summary} = extractSummary(stderr);
22+
23+
expect(summary).toMatchSnapshot();
24+
expect(exitCode).toBe(0);
25+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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 greet, {helper} from '../babel-style-default.cjs';
9+
import plainDefault, {multiply, value} from '../plain-cjs.cjs';
10+
import {getCount as countA, increment as incA} from '../importer-a.mjs';
11+
import {getCount as countB, increment as incB} from '../importer-b.mjs';
12+
13+
// ── __esModule interop ────────────────────────────────────────────────────────
14+
15+
test('default import of __esModule CJS unwraps .default, not the whole exports', () => {
16+
// greet should be the function, not {__esModule: true, default: fn, helper: fn}
17+
expect(typeof greet).toBe('function');
18+
expect(greet('World')).toBe('Hello, World!');
19+
});
20+
21+
test('named imports of __esModule CJS work alongside default', () => {
22+
expect(helper(7)).toBe(14);
23+
});
24+
25+
test('__esModule key is not exposed as a named export', async () => {
26+
const ns = await import('../babel-style-default.cjs');
27+
expect(Object.keys(ns)).not.toContain('__esModule');
28+
});
29+
30+
// ── plain CJS (no __esModule flag) ───────────────────────────────────────────
31+
32+
test('default import of plain CJS is the whole module.exports object', () => {
33+
expect(plainDefault).toEqual({multiply: expect.any(Function), value: 99});
34+
});
35+
36+
test('named imports from plain CJS work', () => {
37+
expect(value).toBe(99);
38+
expect(multiply(6, 7)).toBe(42);
39+
});
40+
41+
// ── CJS-as-ESM caching (singleton) ───────────────────────────────────────────
42+
43+
test('two ESM importers of the same CJS module share one singleton instance', () => {
44+
// importer-a and importer-b both re-export singleton-state.cjs.
45+
// If the CJS module is cached, mutations are visible across importers.
46+
incA();
47+
incA();
48+
incB(); // operates on the same underlying CJS export object
49+
expect(countA()).toBe(3);
50+
expect(countB()).toBe(3);
51+
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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+
// Simulates a CJS file emitted by Babel/Webpack from an ESM source.
9+
// The __esModule flag signals that `.default` is the real default export.
10+
'use strict';
11+
Object.defineProperty(exports, '__esModule', {value: true});
12+
exports.default = function greet(name) {
13+
return `Hello, ${name}!`;
14+
};
15+
exports.helper = function helper(x) {
16+
return x * 2;
17+
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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 {increment, getCount} from './singleton-state.cjs';
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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 {increment, getCount} from './singleton-state.cjs';
Lines changed: 7 additions & 0 deletions
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+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
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+
// Plain CJS without __esModule — default export should be the whole module.exports.
9+
'use strict';
10+
11+
module.exports = {
12+
multiply(a, b) {
13+
return a * b;
14+
},
15+
value: 99,
16+
};

0 commit comments

Comments
 (0)