From 62dcfada3d2e981e303c89cc4645f44f72de3612 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Sat, 31 Oct 2020 15:21:41 +0100 Subject: [PATCH] feat: tell Babel about Jest's ESM support --- packages/babel-jest/src/index.ts | 16 ++++++++++ packages/jest-runtime/package.json | 2 ++ packages/jest-runtime/src/index.ts | 11 +++++++ .../jest-transform/src/ScriptTransformer.ts | 29 +++++++++++++++++-- packages/jest-transform/src/types.ts | 6 +++- yarn.lock | 4 ++- 6 files changed, 64 insertions(+), 4 deletions(-) diff --git a/packages/babel-jest/src/index.ts b/packages/babel-jest/src/index.ts index f5fbfc1d44a6..6f7d08202c5b 100644 --- a/packages/babel-jest/src/index.ts +++ b/packages/babel-jest/src/index.ts @@ -40,6 +40,14 @@ interface BabelJestTransformOptions extends TransformOptions { sourceMaps: 'both'; } +// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/49267 +declare module '@babel/core' { + interface TransformCaller { + supportsExportNamespaceFrom?: boolean; + supportsTopLevelAwait?: boolean; + } +} + const createTransformer = ( userOptions?: TransformOptions | null, ): BabelJestTransformer => { @@ -49,7 +57,9 @@ const createTransformer = ( caller: { name: 'babel-jest', supportsDynamicImport: false, + supportsExportNamespaceFrom: false, supportsStaticESM: false, + supportsTopLevelAwait: false, ...inputOptions.caller, }, compact: false, @@ -72,9 +82,15 @@ const createTransformer = ( supportsDynamicImport: transformOptions?.supportsDynamicImport ?? options.caller.supportsDynamicImport, + supportsExportNamespaceFrom: + transformOptions?.supportsExportNamespaceFrom ?? + options.caller.supportsExportNamespaceFrom, supportsStaticESM: transformOptions?.supportsStaticESM ?? options.caller.supportsStaticESM, + supportsTopLevelAwait: + transformOptions?.supportsTopLevelAwait ?? + options.caller.supportsTopLevelAwait, }, filename, }); diff --git a/packages/jest-runtime/package.json b/packages/jest-runtime/package.json index 61f28a0e58ec..b1318797ff26 100644 --- a/packages/jest-runtime/package.json +++ b/packages/jest-runtime/package.json @@ -34,6 +34,7 @@ "jest-snapshot": "^26.6.1", "jest-util": "^26.6.1", "jest-validate": "^26.6.1", + "semver": "^7.3.2", "slash": "^3.0.0", "strip-bom": "^4.0.0", "yargs": "^15.4.1" @@ -44,6 +45,7 @@ "@types/glob": "^7.1.1", "@types/graceful-fs": "^4.1.2", "@types/node": "^14.0.27", + "@types/semver": "^7.3.4", "execa": "^4.0.0", "jest-environment-node": "^26.6.1", "jest-snapshot-serializer-raw": "^1.1.0" diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 03f5f8610ec8..2fbfb62dec67 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -18,6 +18,7 @@ import { Module as VMModule, } from 'vm'; import * as nativeModule from 'module'; +import * as semver from 'semver'; // @ts-expect-error import parseCjs = require('cjs-module-lexer'); import type {Config, Global} from '@jest/types'; @@ -78,15 +79,21 @@ type HasteMapOptions = { type InternalModuleOptions = { isInternalModule: boolean; supportsDynamicImport: boolean; + supportsExportNamespaceFrom: boolean; supportsStaticESM: boolean; + supportsTopLevelAwait: boolean; }; const defaultTransformOptions: InternalModuleOptions = { isInternalModule: false, supportsDynamicImport: esmIsAvailable, + supportsExportNamespaceFrom: false, supportsStaticESM: false, + supportsTopLevelAwait: false, }; +const nodeVersionSupportsTla = semver.satisfies(process.version, '>=14.3.0'); + type InitialModule = Omit; type ModuleRegistry = Map; @@ -378,7 +385,9 @@ class Runtime { const transformedCode = this.transformFile(modulePath, { isInternalModule: false, supportsDynamicImport: true, + supportsExportNamespaceFrom: false, supportsStaticESM: true, + supportsTopLevelAwait: nodeVersionSupportsTla, }); const module = new SourceTextModule(transformedCode, { @@ -608,7 +617,9 @@ class Runtime { return this.requireModule(from, to, { isInternalModule: true, supportsDynamicImport: esmIsAvailable, + supportsExportNamespaceFrom: false, supportsStaticESM: false, + supportsTopLevelAwait: false, }); } diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index 7fd85863146d..a3bdfaa6090b 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -62,6 +62,14 @@ async function waitForPromiseWithCleanup( } } +// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/49267 +declare module '@babel/core' { + interface TransformCaller { + supportsExportNamespaceFrom?: boolean; + supportsTopLevelAwait?: boolean; + } +} + export default class ScriptTransformer { private _cache: ProjectCache; private _config: Config.ProjectConfig; @@ -96,6 +104,7 @@ export default class ScriptTransformer { instrument: boolean, supportsDynamicImport: boolean, supportsStaticESM: boolean, + supportsTopLevelAwait: boolean, ): string { const configString = this._cache.configString; const transformer = this._getTransformer(filename); @@ -108,7 +117,9 @@ export default class ScriptTransformer { instrument, rootDir: this._config.rootDir, supportsDynamicImport, + supportsExportNamespaceFrom: false, supportsStaticESM, + supportsTopLevelAwait, }), ) .update(CACHE_VERSION) @@ -130,6 +141,7 @@ export default class ScriptTransformer { instrument: boolean, supportsDynamicImport: boolean, supportsStaticESM: boolean, + supportsTopLevelAwait: boolean, ): Config.Path { const baseCacheDir = HasteMap.getCacheFilePath( this._config.cacheDirectory, @@ -142,6 +154,7 @@ export default class ScriptTransformer { instrument, supportsDynamicImport, supportsStaticESM, + supportsTopLevelAwait, ); // Create sub folders based on the cacheKey to avoid creating one // directory with many files. @@ -216,6 +229,7 @@ export default class ScriptTransformer { supportsDynamicImport: boolean, supportsStaticESM: boolean, canMapToInput: boolean, + supportsTopLevelAwait: boolean, ): TransformedSource { const inputCode = typeof input === 'string' ? input : input.code; const inputMap = typeof input === 'string' ? null : input.map; @@ -226,7 +240,9 @@ export default class ScriptTransformer { caller: { name: '@jest/transform', supportsDynamicImport, + supportsExportNamespaceFrom: false, supportsStaticESM, + supportsTopLevelAwait, }, configFile: false, filename, @@ -267,6 +283,7 @@ export default class ScriptTransformer { instrument: boolean, supportsDynamicImport = false, supportsStaticESM = false, + supportsTopLevelAwait = false, ): TransformResult { const filename = tryRealpath(filepath); const transform = this._getTransformer(filename); @@ -276,6 +293,7 @@ export default class ScriptTransformer { instrument, supportsDynamicImport, supportsStaticESM, + supportsTopLevelAwait, ); let sourceMapPath: Config.Path | null = cacheFilePath + '.map'; // Ignore cache if `config.cache` is set (--no-cache) @@ -309,7 +327,9 @@ export default class ScriptTransformer { const processed = transform.process(content, filename, this._config, { instrument, supportsDynamicImport, + supportsExportNamespaceFrom: false, supportsStaticESM, + supportsTopLevelAwait, }); if (typeof processed === 'string') { @@ -363,6 +383,7 @@ export default class ScriptTransformer { supportsDynamicImport, supportsStaticESM, shouldEmitSourceMaps, + supportsTopLevelAwait, ); code = @@ -400,6 +421,7 @@ export default class ScriptTransformer { isInternalModule, supportsDynamicImport, supportsStaticESM, + supportsTopLevelAwait, } = options; const content = stripShebang( fileSource || fs.readFileSync(filename, 'utf8'), @@ -421,6 +443,7 @@ export default class ScriptTransformer { instrument, supportsDynamicImport, supportsStaticESM, + supportsTopLevelAwait, ); code = transformedSource.code; @@ -480,6 +503,7 @@ export default class ScriptTransformer { isInternalModule, supportsDynamicImport, supportsStaticESM, + supportsTopLevelAwait, } = options; const willTransform = !isInternalModule && !isCoreModule && this.shouldTransform(filename); @@ -491,6 +515,7 @@ export default class ScriptTransformer { false, supportsDynamicImport, supportsStaticESM, + supportsTopLevelAwait, ); return transformedJsonSource; } @@ -521,8 +546,8 @@ export default class ScriptTransformer { transforming = true; return ( // we might wanna do `supportsDynamicImport` at some point - this.transformSource(filename, code, false, false, false).code || - code + this.transformSource(filename, code, false, false, false, false) + .code || code ); } finally { transforming = false; diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index 483c8e6cc9a4..d4ef2b6ea956 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -24,7 +24,9 @@ export type Options = ShouldInstrumentOptions & isCoreModule: boolean; isInternalModule: boolean; supportsDynamicImport: boolean; + supportsExportNamespaceFrom: boolean; supportsStaticESM: boolean; + supportsTopLevelAwait: boolean; }>; // This is fixed in source-map@0.7.x, but we can't upgrade yet since it's async @@ -41,9 +43,11 @@ export type TransformResult = TransformTypes.TransformResult; export interface TransformOptions { instrument: boolean; - // names are copied from babel + // names are copied from babel: https://babeljs.io/docs/en/options#caller supportsDynamicImport?: boolean; + supportsExportNamespaceFrom?: boolean; supportsStaticESM?: boolean; + supportsTopLevelAwait?: boolean; } // TODO: For Jest 26 we should combine these into one options shape diff --git a/yarn.lock b/yarn.lock index 783f9933d4d1..6708169c21e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3747,7 +3747,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.1.0": +"@types/semver@npm:^7.1.0, @types/semver@npm:^7.3.4": version: 7.3.4 resolution: "@types/semver@npm:7.3.4" checksum: 7e8588aa55ecb344eda6954674b83a3c568d97d478e70e4617bd3ab22902590ac416ccf2cea48b58fb2f0fbd80f9ad1896332c9b3c3189ffd24e4350ff22094a @@ -11957,6 +11957,7 @@ fsevents@^1.2.7: "@types/glob": ^7.1.1 "@types/graceful-fs": ^4.1.2 "@types/node": ^14.0.27 + "@types/semver": ^7.3.4 "@types/yargs": ^15.0.0 chalk: ^4.0.0 cjs-module-lexer: ^0.4.2 @@ -11976,6 +11977,7 @@ fsevents@^1.2.7: jest-snapshot-serializer-raw: ^1.1.0 jest-util: ^26.6.1 jest-validate: ^26.6.1 + semver: ^7.3.2 slash: ^3.0.0 strip-bom: ^4.0.0 yargs: ^15.4.1