Skip to content

Commit

Permalink
feat: add support for import assertions
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Jan 24, 2023
1 parent e0b1249 commit fe477fa
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 38 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
- `[jest-mock]` fix mockReset and resetAllMocks undefined return ([#13692](https://github.com/facebook/jest/pull/13692))
- `[jest-resolve]` Add global paths to `require.resolve.paths` ([#13633](https://github.com/facebook/jest/pull/13633))
- `[jest-runtime]` Support Wasm files that import JS resources ([#13608](https://github.com/facebook/jest/pull/13608))
- `[jest-runtime]` Using the scriptTransformer cache in jest-runner ([#13735](https://github.com/facebook/jest/pull/13735))
- `[jest-runtime]` Using the scriptTransformer cache in `jest-runner` ([#13735](https://github.com/facebook/jest/pull/13735))
- `[jest-runtime]` Enforce import assertions when importing JSON in ESM ([#12755](https://github.com/facebook/jest/pull/12755))
- `[jest-snapshot]` Make sure to import `babel` outside of the sandbox ([#13694](https://github.com/facebook/jest/pull/13694))
- `[jest-transform]` Ensure the correct configuration is passed to preprocessors specified multiple times in the `transform` option ([#13770](https://github.com/facebook/jest/pull/13770))

Expand Down
117 changes: 80 additions & 37 deletions packages/jest-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,19 @@ export default class Runtime {
private async loadEsmModule(
modulePath: string,
query = '',
importAssertions: ImportAssertions = {},
): Promise<VMModule> {
if (modulePath.endsWith('.json') && importAssertions.type !== 'json') {
const error: NodeJS.ErrnoException = new Error(
`Module "${
modulePath + (query ? `?${query}` : '')
}" needs an import assertion of type "json"`,
);
error.code = 'ERR_IMPORT_ASSERTION_TYPE_MISSING';

throw error;
}

const cacheKey = modulePath + query;

if (this._fileTransformsMutex.has(cacheKey)) {
Expand Down Expand Up @@ -477,39 +489,54 @@ export default class Runtime {
});

try {
const module = new SourceTextModule(transformedCode, {
context,
identifier: modulePath,
importModuleDynamically: async (
specifier: string,
referencingModule: VMModule,
) => {
invariant(
runtimeSupportsVmModules,
'You need to run with a version of node that supports ES Modules in the VM API. See https://jestjs.io/docs/ecmascript-modules',
);
const module = await this.resolveModule(
specifier,
referencingModule.identifier,
referencingModule.context,
);
let module;
if (modulePath.endsWith('.json')) {
module = new SyntheticModule(
['default'],
function () {
const obj = JSON.parse(transformedCode);
// @ts-expect-error: TS doesn't know what `this` is
this.setExport('default', obj);
},
{context, identifier: modulePath},
);
} else {
module = new SourceTextModule(transformedCode, {
context,
identifier: modulePath,
importModuleDynamically: async (
specifier: string,
referencingModule: VMModule,
importAssertions?: ImportAssertions,
) => {
invariant(
runtimeSupportsVmModules,
'You need to run with a version of node that supports ES Modules in the VM API. See https://jestjs.io/docs/ecmascript-modules',
);
const module = await this.resolveModule(
specifier,
referencingModule.identifier,
referencingModule.context,
importAssertions,
);

return this.linkAndEvaluateModule(module);
},
initializeImportMeta: (meta: JestImportMeta) => {
meta.url = pathToFileURL(modulePath).href;
return this.linkAndEvaluateModule(module);
},
initializeImportMeta: (meta: JestImportMeta) => {
meta.url = pathToFileURL(modulePath).href;

let jest = this.jestObjectCaches.get(modulePath);
let jest = this.jestObjectCaches.get(modulePath);

if (!jest) {
jest = this._createJestObjectFor(modulePath);
if (!jest) {
jest = this._createJestObjectFor(modulePath);

this.jestObjectCaches.set(modulePath, jest);
}
this.jestObjectCaches.set(modulePath, jest);
}

meta.jest = jest;
},
});
meta.jest = jest;
},
});
}

invariant(
!this._esmoduleRegistry.has(cacheKey),
Expand Down Expand Up @@ -539,6 +566,7 @@ export default class Runtime {
specifier: string,
referencingIdentifier: string,
context: VMContext,
importAssertions: ImportAssertions = {},
): Promise<T> {
if (this.isTornDown) {
this._logFormattedReferenceError(
Expand Down Expand Up @@ -627,6 +655,7 @@ export default class Runtime {
importModuleDynamically: async (
specifier: string,
referencingModule: VMModule,
importAssertions?: ImportAssertions,
) => {
invariant(
runtimeSupportsVmModules,
Expand All @@ -636,6 +665,7 @@ export default class Runtime {
specifier,
referencingModule.identifier,
referencingModule.context,
importAssertions,
);

return this.linkAndEvaluateModule(module);
Expand Down Expand Up @@ -672,9 +702,11 @@ export default class Runtime {

if (
this._resolver.isCoreModule(resolved) ||
this.unstable_shouldLoadAsEsm(resolved)
this.unstable_shouldLoadAsEsm(resolved) ||
// json files are modules when imported in modules
resolved.endsWith('.json')
) {
return this.loadEsmModule(resolved, query);
return this.loadEsmModule(resolved, query, importAssertions);
}

return this.loadCjsAsEsm(referencingIdentifier, resolved, context);
Expand All @@ -696,12 +728,18 @@ export default class Runtime {
// this method can await it
this._esmModuleLinkingMap.set(
module,
module.link((specifier: string, referencingModule: VMModule) =>
this.resolveModule(
specifier,
referencingModule.identifier,
referencingModule.context,
),
module.link(
(
specifier: string,
referencingModule: VMModule,
importCallOptions?: ImportCallOptions,
) =>
this.resolveModule(
specifier,
referencingModule.identifier,
referencingModule.context,
importCallOptions?.assert,
),
),
);
}
Expand Down Expand Up @@ -1628,7 +1666,11 @@ export default class Runtime {
displayErrors: true,
filename: scriptFilename,
// @ts-expect-error: Experimental ESM API
importModuleDynamically: async (specifier: string) => {
importModuleDynamically: async (
specifier: string,
_script: Script,
importAssertions?: ImportAssertions,
) => {
invariant(
runtimeSupportsVmModules,
'You need to run with a version of node that supports ES Modules in the VM API. See https://jestjs.io/docs/ecmascript-modules',
Expand All @@ -1642,6 +1684,7 @@ export default class Runtime {
specifier,
scriptFilename,
context,
importAssertions,
);

return this.linkAndEvaluateModule(module);
Expand Down

0 comments on commit fe477fa

Please sign in to comment.