From 76ec2a46b1314ed1c6cd21997f96bdcf5cbdd11a Mon Sep 17 00:00:00 2001 From: Robert Kieffer Date: Wed, 15 Feb 2023 01:57:41 -0800 Subject: [PATCH] Revert import assertions (#13911) Co-authored-by: Simen Bekkhus --- CHANGELOG.md | 1 + .../__snapshots__/nativeEsm.test.ts.snap | 2 +- e2e/__tests__/nativeEsm.test.ts | 42 ++--- packages/jest-runtime/package.json | 2 - .../runtime_import_assertions.test.js | 78 -------- packages/jest-runtime/src/index.ts | 173 ++---------------- yarn.lock | 2 - 7 files changed, 26 insertions(+), 274 deletions(-) delete mode 100644 packages/jest-runtime/src/__tests__/runtime_import_assertions.test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index e769b2f18fe4..9b61f448a93a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - `[jest-core]` allow to use workerIdleMemoryLimit with only 1 worker or runInBand option ([#13846](https://github.com/facebook/jest/pull/13846)) - `[jest-message-util]` Add support for [error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) ([#13868](https://github.com/facebook/jest/pull/13868) & [#13912](https://github.com/facebook/jest/pull/13912)) +- `[jest-runtime]` Revert `import assertions` for JSON modules as it's been relegated to Stage 2 ([#13911](https://github.com/facebook/jest/pull/13911)) ### Fixes diff --git a/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap b/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap index 1e8b6bd6ed39..c1b2cee1867c 100644 --- a/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap +++ b/e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`on node <16.12.0 does not enforce import assertions 1`] = ` +exports[`does not enforce import assertions 1`] = ` "Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total diff --git a/e2e/__tests__/nativeEsm.test.ts b/e2e/__tests__/nativeEsm.test.ts index d4a1e9631a12..d31b46ac7c1c 100644 --- a/e2e/__tests__/nativeEsm.test.ts +++ b/e2e/__tests__/nativeEsm.test.ts @@ -94,24 +94,22 @@ test('runs WebAssembly (Wasm) test with native ESM', () => { expect(exitCode).toBe(0); }); -// version where `vm` API gets `import assertions` -onNodeVersions('>=16.12.0', () => { - test('enforces import assertions', () => { - const {exitCode, stderr, stdout} = runJest( - DIR, - ['native-esm-missing-import-assertions.test'], - {nodeOptions: '--experimental-vm-modules --no-warnings'}, - ); +test('does not enforce import assertions', () => { + const {exitCode, stderr, stdout} = runJest( + DIR, + ['native-esm-missing-import-assertions.test'], + {nodeOptions: '--experimental-vm-modules --no-warnings'}, + ); - const {rest} = extractSummary(stderr); + const {summary} = extractSummary(stderr); - expect(rest).toContain( - 'package.json" needs an import assertion of type "json"', - ); - expect(stdout).toBe(''); - expect(exitCode).toBe(1); - }); + expect(summary).toMatchSnapshot(); + expect(stdout).toBe(''); + expect(exitCode).toBe(0); +}); +// version where `vm` API gets `import assertions` +onNodeVersions('>=16.12.0', () => { test('supports import assertions', () => { const {exitCode, stderr, stdout} = runJest( DIR, @@ -128,20 +126,6 @@ onNodeVersions('>=16.12.0', () => { }); onNodeVersions('<16.12.0', () => { - test('does not enforce import assertions', () => { - const {exitCode, stderr, stdout} = runJest( - DIR, - ['native-esm-missing-import-assertions.test'], - {nodeOptions: '--experimental-vm-modules --no-warnings'}, - ); - - const {summary} = extractSummary(stderr); - - expect(summary).toMatchSnapshot(); - expect(stdout).toBe(''); - expect(exitCode).toBe(0); - }); - test('syntax error for import assertions', () => { const {exitCode, stderr, stdout} = runJest( DIR, diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 60a7464715b2..c4638697fa6e 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -37,7 +37,6 @@ "jest-resolve": "workspace:^", "jest-snapshot": "workspace:^", "jest-util": "workspace:^", - "semver": "^7.3.5", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -45,7 +44,6 @@ "@jest/test-utils": "workspace:^", "@types/glob": "^7.1.1", "@types/graceful-fs": "^4.1.3", - "@types/semver": "^7.1.0", "jest-environment-node": "workspace:^" }, "engines": { diff --git a/packages/jest-runtime/src/__tests__/runtime_import_assertions.test.js b/packages/jest-runtime/src/__tests__/runtime_import_assertions.test.js deleted file mode 100644 index 645f0cc31de8..000000000000 --- a/packages/jest-runtime/src/__tests__/runtime_import_assertions.test.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -import {pathToFileURL} from 'url'; -import {onNodeVersions} from '@jest/test-utils'; - -let runtime; - -// version where `vm` API gets `import assertions` -onNodeVersions('>=16.12.0', () => { - beforeAll(async () => { - const createRuntime = require('createRuntime'); - - runtime = await createRuntime(__filename); - }); - - describe('import assertions', () => { - const fileUrl = pathToFileURL(__filename).href; - const jsonFileName = `${__filename}on`; - const jsonFileUrl = pathToFileURL(jsonFileName).href; - - it('works if passed correct import assertion', () => { - expect(() => - runtime.validateImportAssertions(jsonFileName, '', {type: 'json'}), - ).not.toThrow(); - }); - - it('does nothing if no assertions passed for js file', () => { - expect(() => - runtime.validateImportAssertions(__filename, '', undefined), - ).not.toThrow(); - expect(() => - runtime.validateImportAssertions(__filename, '', {}), - ).not.toThrow(); - }); - - it('throws if invalid assertions are passed', () => { - expect(() => - runtime.validateImportAssertions(jsonFileName, '', {type: null}), - ).toThrow('Import assertion value must be a string'); - expect(() => - runtime.validateImportAssertions(jsonFileName, '', {type: 42}), - ).toThrow('Import assertion value must be a string'); - expect(() => - runtime.validateImportAssertions(jsonFileName, '', { - type: 'javascript', - }), - ).toThrow('Import assertion type "javascript" is unsupported'); - }); - - it('throws if missing json assertions', () => { - const errorMessage = `Module "${jsonFileUrl}" needs an import assertion of type "json"`; - - expect(() => - runtime.validateImportAssertions(jsonFileName, '', {}), - ).toThrow(errorMessage); - expect(() => - runtime.validateImportAssertions(jsonFileName, '', { - somethingElse: 'json', - }), - ).toThrow(errorMessage); - expect(() => runtime.validateImportAssertions(jsonFileName, '')).toThrow( - errorMessage, - ); - }); - - it('throws if json assertion passed on wrong file', () => { - expect(() => - runtime.validateImportAssertions(__filename, '', {type: 'json'}), - ).toThrow(`Module "${fileUrl}" is not of type "json"`); - }); - }); -}); diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index f33a894327a9..ce567bde1730 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -21,7 +21,6 @@ import { import {parse as parseCjs} from 'cjs-module-lexer'; import {CoverageInstrumenter, V8Coverage} from 'collect-v8-coverage'; import * as fs from 'graceful-fs'; -import {satisfies as semverSatisfies} from 'semver'; import slash = require('slash'); import stripBOM = require('strip-bom'); import type { @@ -63,11 +62,6 @@ import { const esmIsAvailable = typeof SourceTextModule === 'function'; -const runtimeSupportsImportAssertions = semverSatisfies( - process.versions.node, - '^16.12.0 || >=17.0.0', -); - const dataURIRegex = /^data:(?text\/javascript|application\/json|application\/wasm)(?:;(?charset=utf-8|base64))?,(?.*)$/; @@ -159,23 +153,6 @@ const supportsNodeColonModulePrefixInRequire = (() => { } })(); -const kImplicitAssertType = Symbol('kImplicitAssertType'); - -// copied from https://github.com/nodejs/node/blob/7dd458382580f68cf7d718d96c8f4d2d3fe8b9db/lib/internal/modules/esm/assert.js#L20-L32 -const formatTypeMap: {[type: string]: string | typeof kImplicitAssertType} = { - // @ts-expect-error - copied - __proto__: null, - builtin: kImplicitAssertType, - commonjs: kImplicitAssertType, - json: 'json', - module: kImplicitAssertType, - wasm: kImplicitAssertType, -}; - -const supportedAssertionTypes = new Set( - Object.values(formatTypeMap).filter(type => type !== kImplicitAssertType), -); - export default class Runtime { private readonly _cacheFS: Map; private readonly _cacheFSBuffer = new Map(); @@ -435,12 +412,7 @@ export default class Runtime { private async loadEsmModule( modulePath: string, query = '', - importAssertions?: ImportAssertions, ): Promise { - if (runtimeSupportsImportAssertions) { - this.validateImportAssertions(modulePath, query, importAssertions); - } - const cacheKey = modulePath + query; if (this._fileTransformsMutex.has(cacheKey)) { @@ -478,7 +450,6 @@ export default class Runtime { this.readFileBuffer(modulePath), modulePath, context, - importAssertions, ); this._esmoduleRegistry.set(cacheKey, wasm); @@ -523,7 +494,6 @@ export default class Runtime { importModuleDynamically: async ( specifier: string, referencingModule: VMModule, - importAssertions?: ImportAssertions, ) => { invariant( runtimeSupportsVmModules, @@ -533,7 +503,6 @@ export default class Runtime { specifier, referencingModule.identifier, referencingModule.context, - importAssertions, ); return this.linkAndEvaluateModule(module); @@ -578,88 +547,10 @@ export default class Runtime { return module; } - private validateImportAssertions( - modulePath: string, - query: string, - importAssertions: ImportAssertions = { - // @ts-expect-error - copy https://github.com/nodejs/node/blob/7dd458382580f68cf7d718d96c8f4d2d3fe8b9db/lib/internal/modules/esm/assert.js#LL55C50-L55C65 - __proto__: null, - }, - ) { - const format = this.getModuleFormat(modulePath); - const validType = formatTypeMap[format]; - const url = pathToFileURL(modulePath); - - if (query) { - url.search = query; - } - - const urlString = url.href; - - const assertionType = importAssertions.type; - - switch (validType) { - case undefined: - // Ignore assertions for module formats we don't recognize, to allow new - // formats in the future. - return; - - case kImplicitAssertType: - // This format doesn't allow an import assertion type, so the property - // must not be set on the import assertions object. - if (Object.prototype.hasOwnProperty.call(importAssertions, 'type')) { - handleInvalidAssertionType(urlString, assertionType); - } - return; - - case assertionType: - // The asserted type is the valid type for this format. - return; - - default: - // There is an expected type for this format, but the value of - // `importAssertions.type` might not have been it. - if (!Object.prototype.hasOwnProperty.call(importAssertions, 'type')) { - // `type` wasn't specified at all. - const error: NodeJS.ErrnoException = new Error( - `Module "${urlString}" needs an import assertion of type "json"`, - ); - error.code = 'ERR_IMPORT_ASSERTION_TYPE_MISSING'; - - throw error; - } - handleInvalidAssertionType(urlString, assertionType); - } - } - - private getModuleFormat(modulePath: string) { - if (this._resolver.isCoreModule(modulePath)) { - return 'builtin'; - } - - if (isWasm(modulePath)) { - return 'wasm'; - } - - const fileExtension = path.extname(modulePath); - - if (fileExtension === '.json') { - return 'json'; - } - - if (this.unstable_shouldLoadAsEsm(modulePath)) { - return 'module'; - } - - // any unknown format should be treated as JS - return 'commonjs'; - } - private async resolveModule( specifier: string, referencingIdentifier: string, context: VMContext, - importAssertions: ImportAssertions = {}, ): Promise { if (this.isTornDown) { this._logFormattedReferenceError( @@ -720,7 +611,6 @@ export default class Runtime { Buffer.from(match.groups.code, 'base64'), specifier, context, - importAssertions, ); } else { let code = match.groups.code; @@ -749,7 +639,6 @@ export default class Runtime { importModuleDynamically: async ( specifier: string, referencingModule: VMModule, - importAssertions?: ImportAssertions, ) => { invariant( runtimeSupportsVmModules, @@ -759,7 +648,6 @@ export default class Runtime { specifier, referencingModule.identifier, referencingModule.context, - importAssertions, ); return this.linkAndEvaluateModule(module); @@ -795,12 +683,12 @@ export default class Runtime { const resolved = await this._resolveModule(referencingIdentifier, path); if ( - this._resolver.isCoreModule(resolved) || - this.unstable_shouldLoadAsEsm(resolved) || // json files are modules when imported in modules - resolved.endsWith('.json') + resolved.endsWith('.json') || + this._resolver.isCoreModule(resolved) || + this.unstable_shouldLoadAsEsm(resolved) ) { - return this.loadEsmModule(resolved, query, importAssertions); + return this.loadEsmModule(resolved, query); } return this.loadCjsAsEsm(referencingIdentifier, resolved, context); @@ -822,18 +710,12 @@ export default class Runtime { // this method can await it this._esmModuleLinkingMap.set( module, - module.link( - ( - specifier: string, - referencingModule: VMModule, - importCallOptions?: ImportCallOptions, - ) => - this.resolveModule( - specifier, - referencingModule.identifier, - referencingModule.context, - importCallOptions?.assert, - ), + module.link((specifier: string, referencingModule: VMModule) => + this.resolveModule( + specifier, + referencingModule.identifier, + referencingModule.context, + ), ), ); } @@ -1767,11 +1649,7 @@ export default class Runtime { displayErrors: true, filename: scriptFilename, // @ts-expect-error: Experimental ESM API - importModuleDynamically: async ( - specifier: string, - _script: Script, - importAssertions?: ImportAssertions, - ) => { + importModuleDynamically: async (specifier: string) => { 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', @@ -1785,7 +1663,6 @@ export default class Runtime { specifier, scriptFilename, context, - importAssertions, ); return this.linkAndEvaluateModule(module); @@ -1837,7 +1714,6 @@ export default class Runtime { source: Buffer, identifier: string, context: VMContext, - importAssertions: ImportAssertions | undefined, ) { const wasmModule = await WebAssembly.compile(source); @@ -1851,7 +1727,6 @@ export default class Runtime { module, identifier, context, - importAssertions, ); moduleLookup[module] = await this.linkAndEvaluateModule(resolvedModule); @@ -2608,29 +2483,3 @@ async function evaluateSyntheticModule(module: SyntheticModule) { return module; } - -function handleInvalidAssertionType(url: string, type: unknown) { - if (typeof type !== 'string') { - throw new TypeError('Import assertion value must be a string'); - } - - // `type` might not have been one of the types we understand. - if (!supportedAssertionTypes.has(type)) { - const error: NodeJS.ErrnoException = new Error( - `Import assertion type "${type}" is unsupported`, - ); - - error.code = 'ERR_IMPORT_ASSERTION_TYPE_UNSUPPORTED'; - - throw error; - } - - // `type` was the wrong value for this format. - const error: NodeJS.ErrnoException = new Error( - `Module "${url}" is not of type "${type}"`, - ); - - error.code = 'ERR_IMPORT_ASSERTION_TYPE_FAILED'; - - throw error; -} diff --git a/yarn.lock b/yarn.lock index c05d756d5eb6..1a2cbefc33f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13039,7 +13039,6 @@ __metadata: "@types/glob": ^7.1.1 "@types/graceful-fs": ^4.1.3 "@types/node": "*" - "@types/semver": ^7.1.0 chalk: ^4.0.0 cjs-module-lexer: ^1.0.0 collect-v8-coverage: ^1.0.0 @@ -13053,7 +13052,6 @@ __metadata: jest-resolve: "workspace:^" jest-snapshot: "workspace:^" jest-util: "workspace:^" - semver: ^7.3.5 slash: ^3.0.0 strip-bom: ^4.0.0 languageName: unknown