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 Apr 27, 2022
1 parent b55add9 commit ddedd37
Showing 1 changed file with 83 additions and 42 deletions.
125 changes: 83 additions & 42 deletions packages/jest-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,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 @@ -482,39 +494,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,
);

return this.linkAndEvaluateModule(module);
},
initializeImportMeta: (meta: JestImportMeta) => {
meta.url = pathToFileURL(modulePath).href;
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;

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 @@ -544,6 +571,7 @@ export default class Runtime {
specifier: string,
referencingIdentifier: string,
context: VMContext,
importAssertions: ImportAssertions = {},
): Promise<T> {
if (this.isTornDown) {
this._logFormattedReferenceError(
Expand Down Expand Up @@ -623,6 +651,7 @@ export default class Runtime {
importModuleDynamically: async (
specifier: string,
referencingModule: VMModule,
importAssertions?: ImportAssertions,
) => {
invariant(
runtimeSupportsVmModules,
Expand All @@ -632,6 +661,7 @@ export default class Runtime {
specifier,
referencingModule.identifier,
referencingModule.context,
importAssertions,
);

return this.linkAndEvaluateModule(module);
Expand Down Expand Up @@ -670,9 +700,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 @@ -694,12 +726,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 @@ -1602,7 +1640,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 @@ -1616,6 +1658,7 @@ export default class Runtime {
specifier,
scriptFilename,
context,
importAssertions,
);

return this.linkAndEvaluateModule(module);
Expand Down Expand Up @@ -1680,10 +1723,9 @@ export default class Runtime {
: fileURLToPath(modulePath);

if (!path.isAbsolute(filename)) {
const error = new TypeError(
const error: NodeJS.ErrnoException = new TypeError(
`The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received '${filename}'`,
);
// @ts-expect-error
error.code = 'ERR_INVALID_ARG_TYPE';
throw error;
}
Expand Down Expand Up @@ -1716,14 +1758,13 @@ export default class Runtime {
filename: string | URL,
) {
if (typeof filename !== 'string') {
const error = new TypeError(
const error: NodeJS.ErrnoException = new TypeError(
`The argument 'filename' must be string. Received '${filename}'.${
filename instanceof URL
? ' Use createRequire for URL filename.'
: ''
}`,
);
// @ts-expect-error
error.code = 'ERR_INVALID_ARG_TYPE';
throw error;
}
Expand Down

0 comments on commit ddedd37

Please sign in to comment.