Skip to content

Commit

Permalink
feat: add support for dynamic imports in CJS (#10620)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Oct 11, 2020
1 parent 98776b7 commit 5c221d8
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 17 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,8 @@

### Features

- `[jest-runtime]` add support for dynamic `import()` from CommonJS ([#10620](https://github.com/facebook/jest/pull/10620))

### Fixes

- `[jest-runner, jest-runtime]` fix: `require.main` undefined with `createRequire()` ([#10610](https://github.com/facebook/jest/pull/10610))
Expand Down
2 changes: 1 addition & 1 deletion e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap
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: 12 passed, 12 total
Tests: 13 passed, 13 total
Snapshots: 0 total
Time: <<REPLACED>>
Ran all test suites.
Expand Down
5 changes: 5 additions & 0 deletions e2e/native-esm/__tests__/native-esm.test.js
Expand Up @@ -62,6 +62,11 @@ test('import cjs', async () => {
expect(half(4)).toBe(2);
});

test('import esm from cjs', async () => {
const {default: halfPromise} = await import('../fromEsm.cjs');
expect(await halfPromise(1)).toBe(2);
});

test('require(cjs) and import(cjs) should share caches', async () => {
const require = createRequire(import.meta.url);

Expand Down
12 changes: 12 additions & 0 deletions e2e/native-esm/fromEsm.cjs
@@ -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.
*/

module.exports = async num => {
const {double} = await import('./dynamicImport');

return double(num);
};
52 changes: 36 additions & 16 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -378,7 +378,15 @@ class Runtime {
const module = new SourceTextModule(transformedCode, {
context,
identifier: modulePath,
importModuleDynamically: this.linkModules.bind(this),
importModuleDynamically: (
specifier: string,
referencingModule: VMModule,
) =>
this.linkModules(
specifier,
referencingModule.identifier,
referencingModule.context,
),
initializeImportMeta(meta: ImportMeta) {
meta.url = pathToFileURL(modulePath).href;
},
Expand All @@ -390,7 +398,13 @@ class Runtime {
// parallel can all await it. We then await it synchronously below, so
// we shouldn't get any unhandled rejections
module
.link(this.linkModules.bind(this))
.link((specifier: string, referencingModule: VMModule) =>
this.linkModules(
specifier,
referencingModule.identifier,
referencingModule.context,
),
)
.then(() => module.evaluate())
.then(() => module),
);
Expand All @@ -403,25 +417,26 @@ class Runtime {
return module;
}

private linkModules(specifier: string, referencingModule: VMModule) {
private linkModules(
specifier: string,
referencingIdentifier: string,
context: VMContext,
) {
if (specifier === '@jest/globals') {
const fromCache = this._esmoduleRegistry.get('@jest/globals');

if (fromCache) {
return fromCache;
}
const globals = this.getGlobalsForEsm(
referencingModule.identifier,
referencingModule.context,
);
const globals = this.getGlobalsForEsm(referencingIdentifier, context);
this._esmoduleRegistry.set('@jest/globals', globals);

return globals;
}

const [path, query] = specifier.split('?');

const resolved = this._resolveModule(referencingModule.identifier, path);
const resolved = this._resolveModule(referencingIdentifier, path);

if (
this._resolver.isCoreModule(resolved) ||
Expand All @@ -430,11 +445,7 @@ class Runtime {
return this.loadEsmModule(resolved, query);
}

return this.loadCjsAsEsm(
referencingModule.identifier,
resolved,
referencingModule.context,
);
return this.loadCjsAsEsm(referencingIdentifier, resolved, context);
}

async unstable_importModule(
Expand Down Expand Up @@ -1101,11 +1112,20 @@ class Runtime {

private createScriptFromCode(scriptSource: string, filename: string) {
try {
const scriptFilename = this._resolver.isCoreModule(filename)
? `jest-nodejs-core-${filename}`
: filename;
return new Script(this.wrapCodeInModuleWrapper(scriptSource), {
displayErrors: true,
filename: this._resolver.isCoreModule(filename)
? `jest-nodejs-core-${filename}`
: filename,
filename: scriptFilename,
// @ts-expect-error: Experimental ESM API
importModuleDynamically: (specifier: string) => {
const context = this._environment.getVmContext?.();

invariant(context);

return this.linkModules(specifier, scriptFilename, context);
},
});
} catch (e) {
throw handlePotentialSyntaxError(e);
Expand Down

0 comments on commit 5c221d8

Please sign in to comment.