Skip to content

Commit

Permalink
feat: support named exports from CJS as named ESM imports (#10673)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB authored Oct 23, 2020
1 parent 1db1b6e commit 374fe52
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- `[jest-runtime]` Support named exports from CommonJS as named ES Module imports ([#10673](https://github.com/facebook/jest/pull/10673))
- `[jest-validate]` Add support for `recursiveDenylist` option as an alternative to `recursiveBlacklist` ([#10236](https://github.com/facebook/jest/pull/10236))

### Fixes
Expand Down
2 changes: 1 addition & 1 deletion e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

exports[`on node ^12.16.0 || >=13.7.0 runs test with native ESM 1`] = `
Test Suites: 1 passed, 1 total
Tests: 14 passed, 14 total
Tests: 15 passed, 15 total
Snapshots: 0 total
Time: <<REPLACED>>
Ran all test suites.
Expand Down
8 changes: 8 additions & 0 deletions e2e/native-esm/__tests__/native-esm.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import staticImportedStatefulWithQuery from '../stateful.mjs?query=1';
import staticImportedStatefulWithAnotherQuery from '../stateful.mjs?query=2';
/* eslint-enable */
import {double} from '../index';
import defaultFromCjs, {namedFunction} from '../namedExport.cjs';

test('should have correct import.meta', () => {
expect(typeof require).toBe('undefined');
Expand Down Expand Up @@ -137,3 +138,10 @@ test('varies module cache by query', () => {
expect(staticImportedStatefulWithAnotherQuery()).toBe(2);
expect(staticImportedStatefulWithAnotherQuery()).toBe(3);
});

test('supports named imports from CJS', () => {
expect(namedFunction()).toBe('hello from a named CJS function!');
expect(defaultFromCjs.default()).toBe('"default" export');

expect(Object.keys(defaultFromCjs)).toEqual(['namedFunction', 'default']);
});
9 changes: 9 additions & 0 deletions e2e/native-esm/namedExport.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* 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.namedFunction = () => 'hello from a named CJS function!';
module.exports.default = () => '"default" export';
1 change: 1 addition & 0 deletions packages/jest-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@jest/types": "^26.6.0",
"@types/yargs": "^15.0.0",
"chalk": "^4.0.0",
"cjs-module-lexer": "^0.4.2",
"collect-v8-coverage": "^1.0.0",
"exit": "^0.1.2",
"glob": "^7.1.3",
Expand Down
25 changes: 24 additions & 1 deletion packages/jest-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
Module as VMModule,
} from 'vm';
import * as nativeModule from 'module';
// @ts-expect-error
import parseCjs = require('cjs-module-lexer');
import type {Config, Global} from '@jest/types';
import type {
Jest,
Expand Down Expand Up @@ -474,9 +476,30 @@ class Runtime {
// CJS loaded via `import` should share cache with other CJS: https://github.com/nodejs/modules/issues/503
const cjs = this.requireModuleOrMock(from, modulePath);

const transformedCode = this._fileTransforms.get(modulePath);

let cjsExports: ReadonlyArray<string> = [];

if (transformedCode) {
const {exports} = parseCjs(transformedCode.code);

// @ts-expect-error
cjsExports = exports.filter(exportName => {
// we don't wanna respect any exports _names_ default as a named export
if (exportName === 'default') {
return false;
}
return Object.hasOwnProperty.call(cjs, exportName);
});
}

const module = new SyntheticModule(
['default'],
[...cjsExports, 'default'],
function () {
cjsExports.forEach(exportName => {
// @ts-expect-error
this.setExport(exportName, cjs[exportName]);
});
// @ts-expect-error: TS doesn't know what `this` is
this.setExport('default', cjs);
},
Expand Down
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5770,6 +5770,13 @@ __metadata:
languageName: node
linkType: hard

"cjs-module-lexer@npm:^0.4.2":
version: 0.4.2
resolution: "cjs-module-lexer@npm:0.4.2"
checksum: 2b06d73648a4c1e468f235457205984d0a7f62daaf750fa163c4d6d0965e50998d609bf2b9693aad8b55498aad460a9c2ec758f73eb60b7f988faf0eb54f53b8
languageName: node
linkType: hard

"class-utils@npm:^0.3.5":
version: 0.3.6
resolution: "class-utils@npm:0.3.6"
Expand Down Expand Up @@ -11850,6 +11857,7 @@ fsevents@^1.2.7:
"@types/node": ^14.0.27
"@types/yargs": ^15.0.0
chalk: ^4.0.0
cjs-module-lexer: ^0.4.2
collect-v8-coverage: ^1.0.0
execa: ^4.0.0
exit: ^0.1.2
Expand Down

0 comments on commit 374fe52

Please sign in to comment.