UNPKG

jest-runtime

Version:
1,186 lines (1,141 loc) 148 kB
/*! * /** * * 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. * * / */ /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ "./src/helpers.ts" (__unused_webpack_module, exports) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.findSiblingsWithFileExtension = exports.decodePossibleOutsideJestVmPath = exports.createOutsideJestVmPath = void 0; exports.noop = noop; function path() { const data = _interopRequireWildcard(require("node:path")); path = function () { return data; }; return data; } function _glob() { const data = require("glob"); _glob = function () { return data; }; return data; } function _slash() { const data = _interopRequireDefault(require("slash")); _slash = function () { return data; }; return data; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * 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. */ const OUTSIDE_JEST_VM_PROTOCOL = 'jest-main:'; // String manipulation is easier here, fileURLToPath is only in newer Nodes, // plus setting non-standard protocols on URL objects is difficult. const createOutsideJestVmPath = path => `${OUTSIDE_JEST_VM_PROTOCOL}//${encodeURIComponent(path)}`; exports.createOutsideJestVmPath = createOutsideJestVmPath; const decodePossibleOutsideJestVmPath = outsideJestVmPath => { if (outsideJestVmPath.startsWith(OUTSIDE_JEST_VM_PROTOCOL)) { return decodeURIComponent(outsideJestVmPath.replace(new RegExp(`^${OUTSIDE_JEST_VM_PROTOCOL}//`), '')); } return undefined; }; exports.decodePossibleOutsideJestVmPath = decodePossibleOutsideJestVmPath; const findSiblingsWithFileExtension = (moduleFileExtensions, from, moduleName) => { if (!path().isAbsolute(moduleName) && path().extname(moduleName) === '') { const dirname = path().dirname(from); const pathToModule = path().resolve(dirname, moduleName); try { const slashedDirname = (0, _slash().default)(dirname); const matches = _glob().glob.sync(`${pathToModule}.*`, { windowsPathsNoEscape: true }).map(match => { const slashedMap = (0, _slash().default)(match); const relativePath = path().posix.relative(slashedDirname, slashedMap); const slashedPath = path().posix.dirname(slashedMap) === slashedDirname ? `./${relativePath}` : relativePath; return `\t'${slashedPath}'`; }).join('\n'); if (matches) { const foundMessage = `\n\nHowever, Jest was able to find:\n${matches}`; const mappedModuleFileExtensions = moduleFileExtensions.map(ext => `'${ext}'`).join(', '); return `${foundMessage}\n\nYou might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ` + `[${mappedModuleFileExtensions}].\n\nSee https://jestjs.io/docs/configuration#modulefileextensions-arraystring`; } } catch {} } return ''; }; exports.findSiblingsWithFileExtension = findSiblingsWithFileExtension; function noop() { // empty } /***/ }, /***/ "./src/internals/CjsExportsCache.ts" (__unused_webpack_module, exports) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CjsExportsCache = void 0; function path() { const data = _interopRequireWildcard(require("node:path")); path = function () { return data; }; return data; } function _cjsModuleLexer() { const data = require("cjs-module-lexer"); _cjsModuleLexer = function () { return data; }; return data; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * 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. */ // Computes (and caches) the named exports of a CJS module by static analysis // with cjs-module-lexer, recursively walking `module.exports = require(...)` // re-exports. Native (`.node`) addons and core-module re-exports can't be // statically analysed, so they are loaded via the injected callbacks and the // real export keys are read off the resulting object. class CjsExportsCache { cache = new Map(); resolution; fileCache; transformCache; loadNativeAddon; loadCoreReexport; constructor(options) { this.resolution = options.resolution; this.fileCache = options.fileCache; this.transformCache = options.transformCache; this.loadNativeAddon = options.loadNativeAddon; this.loadCoreReexport = options.loadCoreReexport; } // `from` is the module asking for the exports - propagated to the load // callbacks so user mocks (`jest.mock('./addon.node', factory)`) dispatch // against the real importer rather than an empty placeholder. The cache is // keyed by `modulePath` only (export keys don't depend on the importer); // `from` matters only for the cache-miss load. getExportsOf(from, modulePath) { const cached = this.cache.get(modulePath); if (cached) return cached; if (path().extname(modulePath) === '.node') { const nativeModule = this.loadNativeAddon(from, modulePath); const namedExports = new Set(Object.keys(nativeModule)); this.cache.set(modulePath, namedExports); return namedExports; } const transformedCode = this.transformCache.getCachedSource(modulePath) ?? this.fileCache.readFile(modulePath); const { exports, reexports } = (0, _cjsModuleLexer().parse)(transformedCode); const namedExports = new Set(exports); for (const reexport of reexports) { if (this.resolution.isCoreModule(reexport)) { const coreExports = this.loadCoreReexport(modulePath, reexport); if (coreExports !== null && typeof coreExports === 'object') { for (const key of Object.keys(coreExports)) namedExports.add(key); } } else { const resolved = this.resolution.resolveCjs(modulePath, reexport); for (const key of this.getExportsOf(modulePath, resolved)) namedExports.add(key); } } this.cache.set(modulePath, namedExports); return namedExports; } clear() { this.cache.clear(); } } exports.CjsExportsCache = CjsExportsCache; /***/ }, /***/ "./src/internals/CjsLoader.ts" (__unused_webpack_module, exports, __webpack_require__) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.CjsLoader = void 0; function path() { const data = _interopRequireWildcard(require("node:path")); path = function () { return data; }; return data; } var _ModuleExecutor = __webpack_require__("./src/internals/ModuleExecutor.ts"); var _nodeCapabilities = __webpack_require__("./src/internals/nodeCapabilities.ts"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * 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. */ class CjsLoader { resolution; registries; mockState; transformCache; environment; coreModule; executor; requireEsm; testState; logFormattedReferenceError; constructor(options) { this.resolution = options.resolution; this.registries = options.registries; this.mockState = options.mockState; this.transformCache = options.transformCache; this.environment = options.environment; this.coreModule = options.coreModule; this.executor = options.executor; this.requireEsm = options.requireEsm; this.testState = options.testState; this.logFormattedReferenceError = options.logFormattedReferenceError; } requireModule(from, moduleName, options, isRequireActual = false) { const isInternal = options?.isInternalModule ?? false; const moduleID = this.mockState.getCjsModuleId(from, moduleName); let modulePath; // Some old tests rely on this mocking behavior. Ideally we'll change this // to be more explicit. const moduleResource = moduleName && this.resolution.getModule(moduleName); const manualMock = moduleName && this.resolution.getCjsMockModule(from, moduleName); if (!options?.isInternalModule && !isRequireActual && !moduleResource && manualMock && manualMock !== this.executor.getCurrentlyExecutingManualMock() && !this.mockState.isExplicitlyUnmocked(moduleID)) { modulePath = manualMock; } if (moduleName && this.resolution.isCoreModule(moduleName)) { return this.coreModule.require(moduleName, _nodeCapabilities.supportsNodeColonModulePrefixInRequire); } if (!modulePath) { modulePath = this.resolution.resolveCjs(from, moduleName); } if (this.resolution.shouldLoadAsEsm(modulePath)) { if (!_nodeCapabilities.supportsSyncEvaluate) { const error = new Error(`Must use import to load ES Module: ${modulePath}\n` + "Jest's require(ESM) requires Node v24.9+ for " + 'synchronous vm module APIs; the current Node version does not ' + 'expose them.'); error.code = 'ERR_REQUIRE_ESM'; throw error; } // Fast path: skip the graph walker on cache hits. const reg = this.registries.getActiveEsmRegistry(); const cached = reg.get(modulePath); if (cached && !(cached instanceof Promise)) { return cached.namespace; } return this.requireEsm(modulePath); } const moduleRegistry = isInternal ? this.registries.getInternalCjsRegistry() : this.registries.getActiveCjsRegistry(); const module = moduleRegistry.get(modulePath); if (module) { return module.exports; } // We must register the pre-allocated module object first so that any // circular dependencies that may arise while evaluating the module can // be satisfied. const localModule = { children: [], exports: {}, filename: modulePath, id: modulePath, isPreloading: false, loaded: false, path: path().dirname(modulePath) }; moduleRegistry.set(modulePath, localModule); try { this.loadModule(localModule, from, moduleName, modulePath, options, moduleRegistry); } catch (error) { moduleRegistry.delete(modulePath); // Mirror of `loadCjsAsEsm`'s SyntaxError fallback for `require()`. if (_nodeCapabilities.supportsSyncEvaluate && (0, _ModuleExecutor.isCjsParseError)(error)) { try { return this.requireEsm(modulePath); } catch (esmError) { if (esmError instanceof Error && esmError.name === 'SyntaxError') { throw error; } throw esmError; } } throw error; } return localModule.exports; } loadModule(localModule, from, moduleName, modulePath, options, moduleRegistry) { if (path().extname(modulePath) === '.json') { const transformed = this.transformCache.transformJson(modulePath, options); localModule.exports = this.environment.global.JSON.parse(transformed); } else if (path().extname(modulePath) === '.node') { localModule.exports = require(modulePath); } else { // testState gates apply only to executing JS bodies - JSON/.node go // through pure data parsing and don't run user code in the VM. if (this.testState.bailIfTornDown('You are trying to `require` a file after the Jest environment has been torn down.')) { return; } if (!_nodeCapabilities.runtimeSupportsVmModules) { this.testState.throwIfBetweenTests('You are trying to `require` a file outside of the scope of the test code.'); } const fromPath = moduleName ? from : null; const result = this.executor.exec(localModule, options, moduleRegistry, fromPath, moduleName); if (result === 'env-disposed') { this.logFormattedReferenceError('You are trying to `require` a file after the Jest environment has been torn down.'); process.exitCode = 1; return; } } localModule.loaded = true; } } exports.CjsLoader = CjsLoader; /***/ }, /***/ "./src/internals/EsmLoader.ts" (__unused_webpack_module, exports, __webpack_require__) { Object.defineProperty(exports, "__esModule", ({ value: true })); exports.EsmLoader = void 0; exports.validateImportAttributes = validateImportAttributes; function path() { const data = _interopRequireWildcard(require("node:path")); path = function () { return data; }; return data; } function _nodeUrl() { const data = require("node:url"); _nodeUrl = function () { return data; }; return data; } function _nodeVm() { const data = require("node:vm"); _nodeVm = function () { return data; }; return data; } function _jestUtil() { const data = require("jest-util"); _jestUtil = function () { return data; }; return data; } var _helpers = __webpack_require__("./src/helpers.ts"); var _ModuleExecutor = __webpack_require__("./src/internals/ModuleExecutor.ts"); var _Resolution = __webpack_require__("./src/internals/Resolution.ts"); var _nodeCapabilities = __webpack_require__("./src/internals/nodeCapabilities.ts"); var _syntheticBuilders = __webpack_require__("./src/internals/syntheticBuilders.ts"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * 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. */ // `'sync-required'` is `require(esm)` (must be loaded synchronously, throw a // typed error on edges that would normally bail). `'sync-preferred'` is the // fast path for `await import()` (try sync; fall back to the legacy async // loader on any unsupported edge). // Shape of the third arg Node passes to the `module.link` callback. TC39 final // is `{attributes}`; legacy was `{assert}`. `@types/node@18` only types the // legacy field, so we declare both ourselves. // TODO(jest next major): drop `assert` once we require Node 22+. // Source-text entries carry their dep cacheKeys (used for `linkRequests`). // Synthetic entries (mocks, core, JSON, wasm, @jest/globals) start linked // and never appear in the link-requests pass. // `SourceTextModule#hasAsyncGraph()` lets us prove a graph is sync-evaluable. // `SyntheticModule` does not expose it but is by definition sync (the user // callback is sync), so treat its absence as "not async". function moduleHasAsyncGraph(module) { return typeof module.hasAsyncGraph === 'function' ? module.hasAsyncGraph() : false; } // Mirrors Node's `require(esm)` error code so user catches work uniformly. function makeRequireAsyncError(modulePath, detail) { const error = new Error(`require() cannot be used to load ES Module ${modulePath}: ${detail}`); error.code = 'ERR_REQUIRE_ASYNC_MODULE'; return error; } // Decode a `data:` URI specifier into its mime type and decoded code/body. // `application/wasm` returns a Buffer; everything else returns a UTF-8 string. const dataURIRegex = /^data:(?<mime>text\/javascript|application\/json|application\/wasm)(?:;(?<encoding>charset=utf-8|base64))?,(?<code>.*)$/; function parseDataUri(specifier) { const match = specifier.match(dataURIRegex); if (!match || !match.groups) { throw new Error('Invalid data URI'); } const { mime, encoding, code } = match.groups; if (mime === 'application/wasm') { if (!encoding) throw new Error('Missing data URI encoding'); if (encoding !== 'base64') { throw new Error(`Invalid data URI encoding: ${encoding}`); } return { code: Buffer.from(code, 'base64'), mime }; } if (!encoding || encoding === 'charset=utf-8') { return { code: decodeURIComponent(code), mime }; } if (encoding === 'base64') { return { code: Buffer.from(code, 'base64').toString(), mime }; } throw new Error(`Invalid data URI encoding: ${encoding}`); } // Mirrors Node's `validateAttributes` in lib/internal/modules/esm/assert.js. // The only deliberate divergence: missing `type: 'json'` warns instead of // throwing — see the JSON branch below. const warnedMissingJsonAttributePairs = new Set(); // Soft cap so a long-lived process (watch mode, --runInBand) can't grow the // set without bound. When we hit it we drop everything; users see at most one // extra repeated warning per pair, which is benign. const MAX_WARNED_PAIRS = 10_000; function isJsonModule(modulePath) { return modulePath.endsWith('.json') || modulePath.startsWith('data:application/json'); } // Avoid dumping the full payload of data: URIs (or other very long specifiers) // into stderr. function describeForWarning(modulePath) { if (modulePath.startsWith('data:')) { const comma = modulePath.indexOf(','); if (comma > 0) return `${modulePath.slice(0, comma)},…`; } return modulePath; } function makeImportAttributeError(code, message) { const error = new TypeError(message); error.code = code; return error; } function validateImportAttributes(modulePath, attributes, referencingIdentifier) { for (const key of Object.keys(attributes)) { if (key !== 'type') { throw makeImportAttributeError('ERR_IMPORT_ATTRIBUTE_UNSUPPORTED', `Import attribute "${key}" with value "${attributes[key]}" is not supported (importing "${modulePath}" from ${referencingIdentifier})`); } } const declaredType = attributes.type; const isJson = isJsonModule(modulePath); if (isJson) { if (declaredType === undefined) { // TODO(jest next major): match Node and throw // ERR_IMPORT_ATTRIBUTE_MISSING here. Until then, warn so existing users // without `with { type: 'json' }` keep working. const dedupeKey = `${referencingIdentifier}::${modulePath}`; if (!warnedMissingJsonAttributePairs.has(dedupeKey)) { if (warnedMissingJsonAttributePairs.size >= MAX_WARNED_PAIRS) { warnedMissingJsonAttributePairs.clear(); } warnedMissingJsonAttributePairs.add(dedupeKey); const moduleLabel = describeForWarning(modulePath); console.warn('Jest: importing JSON without an import attribute is deprecated and will be a hard error in the next major. ' + `Update the import of "${moduleLabel}" (from ${referencingIdentifier}): ` + "use `with { type: 'json' }` for static imports, or pass " + "`{ with: { type: 'json' } }` as the second argument to dynamic `import()`."); } return; } if (declaredType !== 'json') { throw makeImportAttributeError('ERR_IMPORT_ATTRIBUTE_TYPE_INCOMPATIBLE', `Module "${modulePath}" is not of type "${declaredType}"`); } return; } // Non-JSON (implicit-type) module. Per HTML spec, the default type cannot // be re-asserted, so any explicit `type` attribute is rejected. if (declaredType !== undefined) { throw makeImportAttributeError('ERR_IMPORT_ATTRIBUTE_TYPE_INCOMPATIBLE', `Module "${modulePath}" is not of type "${declaredType}"`); } } const ESM_TRANSFORM_OPTIONS = { isInternalModule: false, supportsDynamicImport: true, supportsExportNamespaceFrom: true, supportsStaticESM: true, supportsTopLevelAwait: true }; function stripFileScheme(specifier) { return specifier.startsWith('file://') ? (0, _nodeUrl().fileURLToPath)(specifier) : specifier; } class EsmLoader { resolution; fileCache; transformCache; registries; mockState; environment; cjsExportsCache; coreModule; jestGlobals; shouldLoadAsEsm; requireModuleOrMock; testState; // Used only by the legacy async path; deletable when min-Node ≥ v24.9 // (delete the block at the bottom of this file too - eslint/tsc will // surface anything else that becomes unused). linkingMap = new WeakMap(); evaluatingMap = new WeakMap(); constructor(options) { this.resolution = options.resolution; this.fileCache = options.fileCache; this.transformCache = options.transformCache; this.registries = options.registries; this.mockState = options.mockState; this.environment = options.environment; this.cjsExportsCache = options.cjsExportsCache; this.coreModule = options.coreModule; this.jestGlobals = options.jestGlobals; this.shouldLoadAsEsm = options.shouldLoadAsEsm; this.requireModuleOrMock = options.requireModuleOrMock; this.testState = options.testState; } // A `null` here means the legacy async path is mid-flight on this same // module (registry holds a Promise from a concurrent `await import()`); // surface as ERR_REQUIRE_ESM with actionable context. // // Root-level mocks (`jest.unstable_mockModule(spec)` then `require(spec)`) // are not consulted - driving a SyntheticModule from `unlinked` to // `evaluated` needs the async link()/evaluate() pair. Transitive-dep mocks // still apply via the graph walker. requireEsmModule(modulePath) { const module = this.tryLoadGraphSync(modulePath, '', 'sync-required'); if (!module) { const error = new Error(`Cannot require() ES Module ${modulePath} synchronously: it is currently being loaded by a concurrent \`import()\`. Await that import before calling require(), or import this module instead of requiring it.`); error.code = 'ERR_REQUIRE_ESM'; throw error; } return module.namespace; } // Public for unit-test access. Production callers reach the sync graph // through `requireEsmModule` (sync require entry) or via `loadEsmModule` // (the legacy async entry, which first-tries this). tryLoadGraphSync(rootPath, rootQuery, mode) { if (this.testState.bailIfTornDown('You are trying to `import` a file after the Jest environment has been torn down.')) { return null; } const registry = this.registries.getActiveEsmRegistry(); const rootKey = rootPath + rootQuery; const cached = registry.get(rootKey); if (cached) { if (cached instanceof Promise) return null; // The legacy `loadEsmModule` source-text branch does `registry.set` // while the `SourceTextModule` is still `'unlinked'` (link runs later // in `linkAndEvaluateModule`); accessing `.namespace` on a non-evaluated // module throws `ERR_VM_MODULE_STATUS`. Surface settled entries // (`'evaluated'` / `'errored'`); bail otherwise. if (cached.status === 'evaluated') return cached; if (cached.status === 'errored') throw cached.error; return null; } const context = this.getContext(); if (this.transformCache.hasMutex(rootKey)) return null; const scratch = new Map(); const worklist = [{ cacheKey: rootKey, modulePath: rootPath }]; while (worklist.length > 0) { const { cacheKey, modulePath } = worklist.pop(); if (scratch.has(cacheKey)) continue; // Registry first, mutex second. Same settled-status gate as the root - // anything in `'unlinked'` / `'linking'` / `'linked'` / `'evaluating'` // is the legacy path mid-flight on this dep. Plugging an unlinked // module into the parent's `linkRequests` would fail Node's link // cascade; plugging a `'linked'` one would skip its body. Bail. const fromRegistry = registry.get(cacheKey); if (fromRegistry instanceof Promise) return null; if (fromRegistry) { if (fromRegistry.status === 'errored') throw fromRegistry.error; if (fromRegistry.status !== 'evaluated') return null; scratch.set(cacheKey, { cacheKey, kind: 'synthetic', module: fromRegistry }); continue; } if (this.transformCache.hasMutex(cacheKey)) return null; if (this.resolution.isCoreModule(modulePath)) { scratch.set(cacheKey, { cacheKey, kind: 'synthetic', module: (0, _syntheticBuilders.buildCoreSyntheticModule)(modulePath, context, (name, prefix) => this.coreModule.require(name, prefix)) }); continue; } if (modulePath.startsWith('data:')) { const built = this.buildSyncDataUriEntry(modulePath, cacheKey, context, scratch, registry, worklist, mode); if (built === null) return null; scratch.set(cacheKey, built); continue; } if ((0, _Resolution.isWasm)(modulePath)) { const wasmEntry = this.buildSyncWasmEntry(this.fileCache.readFileBuffer(modulePath), modulePath, cacheKey, context, scratch, registry, worklist, mode); if (wasmEntry === null) return null; scratch.set(cacheKey, wasmEntry); continue; } if (!this.transformCache.canTransformSync(modulePath)) { if (mode === 'sync-required') { throw makeRequireAsyncError(modulePath, 'a configured transformer is async-only'); } return null; } if (modulePath.endsWith('.json')) { scratch.set(cacheKey, { cacheKey, kind: 'synthetic', module: (0, _syntheticBuilders.buildJsonSyntheticModule)(this.transformCache.transform(modulePath, ESM_TRANSFORM_OPTIONS), modulePath, context) }); continue; } const transformedCode = this.transformCache.transform(modulePath, ESM_TRANSFORM_OPTIONS); const module = new (_nodeVm().SourceTextModule)(transformedCode, { context, identifier: modulePath, importModuleDynamically: this.dynamicImport, initializeImportMeta: meta => { const metaUrl = (0, _nodeUrl().pathToFileURL)(modulePath).href; meta.url = metaUrl; // @ts-expect-error Jest uses @types/node@18. meta.filename = modulePath; // @ts-expect-error Jest uses @types/node@18. meta.dirname = path().dirname(modulePath); meta.resolve = (specifier, parent = metaUrl) => { const parentPath = (0, _nodeUrl().fileURLToPath)(parent); return (0, _nodeUrl().pathToFileURL)(this.resolution.resolveEsm(parentPath, specifier)).href; }; meta.jest = this.jestGlobals.jestObjectFor(modulePath); } }); if (typeof module.hasTopLevelAwait === 'function' && module.hasTopLevelAwait()) { if (mode === 'sync-required') { throw makeRequireAsyncError(modulePath, 'top-level await'); } return null; } // If we got here without `moduleRequests`, the capability gate is lying. (0, _jestUtil().invariant)(module.moduleRequests !== undefined, `moduleRequests unavailable on ${modulePath}`); const deps = []; for (const { specifier, attributes } of module.moduleRequests) { const resolved = this.resolveSpecifierForSyncGraph(modulePath, specifier, context, scratch, registry, mode); if (resolved === null) return null; validateImportAttributes(resolved.modulePath, attributes, modulePath); deps.push(resolved.cacheKey); if (resolved.enqueue) worklist.push(resolved.enqueue); } scratch.set(cacheKey, { cacheKey, deps, kind: 'source', module }); } for (const entry of scratch.values()) { if (entry.kind !== 'source') continue; const depModules = entry.deps.map(depKey => { const depEntry = scratch.get(depKey); (0, _jestUtil().invariant)(depEntry, `Sync ESM graph missing dep ${depKey} for ${entry.cacheKey}. This is a bug in Jest, please report it!`); return depEntry.module; }); (0, _jestUtil().invariant)(typeof entry.module.linkRequests === 'function', `linkRequests unavailable on ${entry.cacheKey}`); entry.module.linkRequests(depModules); } const rootEntry = scratch.get(rootKey); (0, _jestUtil().invariant)(rootEntry, 'Sync ESM graph missing root entry'); const rootModule = rootEntry.module; if (rootEntry.kind === 'source') { (0, _jestUtil().invariant)(typeof rootModule.instantiate === 'function', 'instantiate unavailable on root'); rootModule.instantiate(); if (moduleHasAsyncGraph(rootModule)) { if (mode === 'sync-required') { let culprit = rootModule.identifier; for (const entry of scratch.values()) { if (entry.kind === 'source' && typeof entry.module.hasTopLevelAwait === 'function' && entry.module.hasTopLevelAwait()) { culprit = entry.module.identifier; break; } } throw makeRequireAsyncError(rootModule.identifier, culprit === rootModule.identifier ? 'top-level await' : `a dependency uses top-level await (${culprit})`); } return null; } } for (const entry of scratch.values()) { if (!registry.has(entry.cacheKey)) { registry.set(entry.cacheKey, entry.module); } } rootModule.evaluate().catch(_helpers.noop); if (rootModule.status === 'errored') { throw rootModule.error; } (0, _jestUtil().invariant)(rootModule.status === 'evaluated', `Expected synchronous evaluation to complete for ${rootModule.identifier}, but module status is "${rootModule.status}". This is a bug in Jest, please report it!`); return rootModule; } getContext() { (0, _jestUtil().invariant)(typeof this.environment.getVmContext === 'function', 'ES Modules are only supported if your test environment has the `getVmContext` function'); const context = this.environment.getVmContext(); (0, _jestUtil().invariant)(context, 'Test environment has been torn down'); return context; } // Commits (or reuses) a synthetic-module entry under `cacheKey` in both the // local scratch and the long-lived registry. Returns `false` when the // registry holds something the caller must bail on: a mid-flight Promise // from the legacy async path, or a non-evaluated module (legacy can stash // an `'unlinked'` SourceTextModule here while link/evaluate runs). tryCommitSynthetic(cacheKey, registry, scratch, build) { if (scratch.has(cacheKey)) return true; const fromRegistry = registry.get(cacheKey); if (fromRegistry instanceof Promise) return false; if (fromRegistry) { const cached = fromRegistry; if (cached.status === 'errored') throw cached.error; if (cached.status !== 'evaluated') return false; } const module = fromRegistry ?? build(); if (!fromRegistry) registry.set(cacheKey, module); scratch.set(cacheKey, { cacheKey, kind: 'synthetic', module }); return true; } resolveSpecifierForSyncGraph(referencingIdentifier, specifier, context, scratch, registry, mode) { if (specifier === '@jest/globals') { const cacheKey = `@jest/globals/${referencingIdentifier}`; const ok = this.tryCommitSynthetic(cacheKey, registry, scratch, () => this.jestGlobals.esmGlobalsModule(referencingIdentifier, context)); return ok ? { cacheKey, enqueue: null, modulePath: cacheKey } : null; } if (specifier.startsWith('data:')) { const cacheKey = specifier; return { cacheKey, enqueue: { cacheKey, modulePath: specifier }, modulePath: specifier }; } specifier = stripFileScheme(specifier); const [specifierPath, query = ''] = specifier.split('?'); const { shouldMock, moduleID } = this.mockState.shouldMockEsmSync(referencingIdentifier, specifierPath); if (shouldMock) { const mocked = this.importMockSync(specifierPath, moduleID, context, scratch, mode); if (mocked === null) return null; return { cacheKey: mocked.cacheKey, enqueue: null, modulePath: specifierPath }; } if (this.resolution.isCoreModule(specifierPath)) { const cacheKey = specifierPath + query; return { cacheKey, enqueue: { cacheKey, modulePath: specifierPath }, modulePath: specifierPath }; } let resolved; try { resolved = this.resolution.resolveEsm(referencingIdentifier, specifierPath); } catch (error) { if (mode === 'sync-required') throw error; return null; } const cacheKey = resolved + query; if (!resolved.endsWith('.json') && !(0, _Resolution.isWasm)(resolved) && !this.shouldLoadAsEsm(resolved)) { const ok = this.tryCommitSynthetic(cacheKey, registry, scratch, () => this.buildCjsAsEsmSyntheticModule(referencingIdentifier, resolved, context)); return ok ? { cacheKey, enqueue: null, modulePath: resolved } : null; } return { cacheKey, enqueue: { cacheKey, modulePath: resolved }, modulePath: resolved }; } importMockSync(moduleName, moduleID, context, scratch, mode) { const existing = this.registries.getModuleMock(moduleID); if (existing instanceof Promise) return null; if (existing) { if (existing.status === 'errored') throw existing.error; if (!scratch.has(moduleID)) { scratch.set(moduleID, { cacheKey: moduleID, kind: 'synthetic', module: existing }); } return { cacheKey: moduleID }; } const factory = this.mockState.getEsmFactory(moduleID); // `shouldMockEsmSync` said this spec is mocked but no factory was // registered. (0, _jestUtil().invariant)(factory !== undefined, 'Attempting to import a mock without a factory'); const result = factory(); if ((0, _jestUtil().isPromise)(result)) { if (mode === 'sync-required') { throw makeRequireAsyncError(moduleName, 'mock factory is async'); } return null; } const synth = (0, _syntheticBuilders.syntheticFromExports)(moduleName, context, result); this.registries.setModuleMock(moduleID, synth); scratch.set(moduleID, { cacheKey: moduleID, kind: 'synthetic', module: synth }); return { cacheKey: moduleID }; } // Construct a wasm SyntheticModule for the sync graph. Wasm imports are // resolved (sync) and enqueued like static-import deps. The SyntheticModule's // body closure-captures `scratch`; by evaluate-cascade time, every dep entry // is fully evaluated so `module.namespace` is safe to read. // // Uses `new WebAssembly.Module(bytes)` (sync, blocks on large modules). buildSyncWasmEntry(bytes, identifier, cacheKey, context, scratch, registry, worklist, mode) { const wasmModule = new WebAssembly.Module(bytes); const moduleSpecToCacheKey = new Map(); for (const { module: depSpec } of WebAssembly.Module.imports(wasmModule)) { if (moduleSpecToCacheKey.has(depSpec)) continue; const resolved = this.resolveSpecifierForSyncGraph(identifier, depSpec, context, scratch, registry, mode); if (resolved === null) return null; moduleSpecToCacheKey.set(depSpec, resolved.cacheKey); if (resolved.enqueue) worklist.push(resolved.enqueue); } const synthetic = (0, _syntheticBuilders.buildWasmSyntheticModule)(wasmModule, identifier, context, depSpec => { const depKey = moduleSpecToCacheKey.get(depSpec); const depEntry = scratch.get(depKey); return depEntry.module.namespace; }); return { cacheKey, kind: 'synthetic', module: synthetic }; } buildSyncDataUriEntry(specifier, cacheKey, context, scratch, registry, worklist, mode) { const esmDynamicImport = this.dynamicImport; const { mime, code } = parseDataUri(specifier); if (mime === 'application/wasm') { return this.buildSyncWasmEntry(new Uint8Array(code), specifier, cacheKey, context, scratch, registry, worklist, mode); } if (mime === 'application/json') { return { cacheKey, kind: 'synthetic', module: (0, _syntheticBuilders.buildJsonSyntheticModule)(code, specifier, context) }; } const module = new (_nodeVm().SourceTextModule)(code, { context, identifier: specifier, importModuleDynamically: esmDynamicImport, initializeImportMeta(meta) { meta.url = specifier; if (meta.url.startsWith('file://')) { // @ts-expect-error Jest uses @types/node@18. meta.filename = (0, _nodeUrl().fileURLToPath)(meta.url); // @ts-expect-error Jest uses @types/node@18. meta.dirname = path().dirname(meta.filename); } } }); if (typeof module.hasTopLevelAwait === 'function' && module.hasTopLevelAwait()) { if (mode === 'sync-required') { throw makeRequireAsyncError(specifier, 'top-level await'); } return null; } (0, _jestUtil().invariant)(module.moduleRequests !== undefined, `moduleRequests unavailable on ${specifier}`); const deps = []; for (const { specifier: depSpec, attributes } of module.moduleRequests) { const resolved = this.resolveSpecifierForSyncGraph(specifier, depSpec, context, scratch, registry, mode); if (resolved === null) return null; validateImportAttributes(resolved.modulePath, attributes, specifier); deps.push(resolved.cacheKey); if (resolved.enqueue) worklist.push(resolved.enqueue); } return { cacheKey, deps, kind: 'source', module }; } // Synthetic-module wrappers that close over the primitive deps. The // `requireModuleOrMock` callback inside `buildCjsAsEsmSyntheticModule` // is the extension-point bridge to `Runtime.requireModuleOrMock`. buildCjsAsEsmSyntheticModule(from, modulePath, context) { return (0, _syntheticBuilders.buildCjsAsEsmSyntheticModule)(from, modulePath, context, this.requireModuleOrMock, this.cjsExportsCache); } // TODO: legacy async path - everything below is deletable when min-Node // ≥ v24.9 (the sync core handles all entry shapes). Drop the `linkingMap` // / `evaluatingMap` fields with it. // Called from CJS bodies via `compileFunction`'s `importModuleDynamically`. dynamicImportFromCjs(specifier, identifier, context, importAttributes) { return this.resolveModule(specifier, identifier, context).then(m => { validateImportAttributes(m.identifier, importAttributes ?? {}, identifier); return this.linkAndEvaluateModule(m); }); } // Public entry for `Runtime.unstable_importModule`. Runtime keeps the // public method as the override seam; this is the body. async loadAndEvaluate(from, moduleName) { (0, _jestUtil().invariant)(_nodeCapabilities.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 [specifierPath, query] = (moduleName ?? '').split('?'); const modulePath = await this.resolution.resolveEsmAsync(from, specifierPath); const module = await this.loadEsmModule(modulePath, query); return this.linkAndEvaluateModule(module); } async loadEsmModule(modulePath, query = '') { // Two gates here. `supportsSyncEvaluate` is a Node-version check: the // sync core relies on `SyntheticModule` starting `'linked'` and on // `evaluate()` completing sync, both of which need v22.21+ / v24.8+. // `canResolveSync` is a configured-resolver check: with an async-only // user resolver `findNodeModule` silently falls back to the default // resolver and would silently miss user mappings. if (_nodeCapabilities.supportsSyncEvaluate && this.resolution.canResolveSync()) { const synced = this.tryLoadGraphSync(modulePath, query, 'sync-preferred'); if (synced) return synced; } const cacheKey = modulePath + query; const registry = this.registries.getActiveEsmRegistry(); if (this.transformCache.hasMutex(cacheKey)) { await this.transformCache.awaitMutex(cacheKey); } if (!registry.has(cacheKey)) { const context = this.getContext(); let transformResolve; let transformReject; this.transformCache.setMutex(cacheKey, new Promise((resolve, reject) => { transformResolve = resolve; transformReject = reject; })); (0, _jestUtil().invariant)(transformResolve && transformReject, 'Promise initialization should be sync - please report this bug to Jest!'); try { if ((0, _Resolution.isWasm)(modulePath)) { const wasm = this.importWasmModule(this.fileCache.readFileBuffer(modulePath), modulePath, context); registry.set(cacheKey, wasm); transformResolve(); return wasm; } if (this.resolution.isCoreModule(modulePath)) { const core = (0, _syntheticBuilders.evaluateSyntheticModule)((0, _syntheticBuilders.buildCoreSyntheticModule)(modulePath, context, (name, prefix) => this.coreModule.require(name, prefix))); registry.set(cacheKey, core); transformResolve(); return core; } const transformedCode = this.transformCache.canTransformSync(modulePath) ? this.transformCache.transform(modulePath, ESM_TRANSFORM_OPTIONS) : await this.transformCache.transformAsync(modulePath, ESM_TRANSFORM_OPTIONS); let module; if (modulePath.endsWith('.json')) { module = (0, _syntheticBuilders.buildJsonSyntheticModule)(transformedCode, modulePath, context); } else { module = new (_nodeVm().SourceTextModule)(transformedCode, { context, identifier: modulePath, importModuleDynamically: this.dynamicImport, initializeImportMeta: meta => { const metaUrl = (0, _nodeUrl().pathToFileURL)(modulePath).href; meta.url = metaUrl; // @ts-expect-error Jest uses @types/node@18. meta.filename = modulePath; // @ts-expect-error Jest uses @types/node@18. meta.dirname = path().dirname(modulePath); meta.resolve = (specifier, parent = metaUrl) => { const parentPath = (0, _nodeUrl().fileURLToPath)(parent); return (0, _nodeUrl().pathToFileURL)(this.resolution.resolveEsm(parentPath, specifier)).href; }; meta.jest = this.jestGlobals.jestObjectFor(modulePath); } }); } (0, _jestUtil().invariant)(!registry.has(cacheKey), `Module cache already has entry ${cacheKey}. This is a bug in Jest, please report it!`); registry.set(cacheKey, module); transformResolve(); } catch (error) { transformReject(error); throw error; } finally { this.transformCache.clearMutex(cacheKey); } } const module = registry.get(cacheKey); (0, _jestUtil().invariant)(module, 'Module cache does not contain module. This is a bug in Jest, please open up an issue'); return module; } async resolveModule(specifier, referencingIdentifier, context) { if (this.testState.bailIfTornDown('You are trying to `import` a file after the Jest environment has been torn down.')) { // @ts-expect-error -- exiting return; } const registry = this.registries.getActiveEsmRegistry(); if (specifier === '@jest/globals') { const globalsIdentifier = `@jest/globals/${referencingIdentifier}`; const fromCache = registry.get(globalsIdentifier); if (fromCache) { return fromCache; } const globals = (0, _syntheticBuilders.evaluateSyntheticModule)(this.jestGlobals.esmGlobalsModule(referencingIdentifier, context)); registry.set(globalsIdentifier, globals); return globals; } if (specifier.startsWith('data:')) { const dataDecision = await this.mockState.shouldMockEsmAsync(referencingIdentifier, specifier); if (dataDecision.shouldMock) { return this.importMock(specifier, dataDecision.moduleID, context); } const fromCache = registry.get(specifier); if (fromCache) { return fromCache; } const { mime, code } = parseDataUri(specifier); let module; if (mime === 'application/wasm') { module = await this.importWasmModule(new Uint8Array(code), specifier, context); } else if (mime === 'application/json') { module = (0, _syntheticBuilders.buildJsonSyntheticModule)(code, specifier, context); } else { module = new (_nodeVm().SourceTextModule)(code, { context, identifier: specifier, importModuleDynamically: this.dynamicImport, initializeImportMeta(meta) { meta.url = specifier; if (meta.url.startsWith('file://')) { // @ts-expect-error Jest uses @types/node@18. meta.filename = (0, _nodeUrl().fileURLToPath)(meta.url); // @ts-expect-error Jest uses @types/node@18. meta.dirname = path().dirname(meta.filename); } } }); } registry.set(specifier, module); return module; } if (specifier.startsWith('file://')) { specifier = (0, _nodeUrl().fileURLToPath)(specifier); } const [specifierPath, query] = specifier.split('?'); const decision = await this.mockState.shouldMockEsmAsync(referencingIdentifier, specifierPath); if (decision.shouldMock) { return this.importMock(specifierPath, decision.moduleID, context); } const resolved = await this.resolution.resolveEsmAsync(referencingIdentifier, specifierPath); if (resolved.endsWith('.json') || this.resolution.isCoreModule(resolved) || this.shouldLoadAsEsm(resolved)) { return this.loadEsmModule(resolved, query); } return this.loadCjsAsEsm(referencingIdentifier, resolved, context); } async linkAndEvaluateModule(module) { if (this.testState.bailIfTornDown('You are trying to `import` a file after the Jest environment has been torn down.')) { // @ts-expect-error: exiting early return; } // Already-errored module from a prior failed evaluation. if (module.status === 'errored') { throw module.error; } if (module.status === 'unlinked') { this.linkingMap.set(module, module.link(async (specifier, referencingModule, extra) => { const resolved = await this.resolveModule(specifier, referencingModule.identifier, referencingModule.context); const extraAttrs = extra; validateImportAttributes(resolved.identifier, extraAttrs?.attributes ?? extraAttrs?.assert ?? {}, referencingModule.identifier); return resolved; })); } const linkPromise = this.linkingMap.get(module); if (linkPromise != null) { await linkPromise; } else if (module.status === 'linking') { // Module entered 'linking' via Node's cascade (a parent's link() // recursed into this dep without going through our code). We have no // promise to await, so yield via setImmediate - which lets all pending // microtasks (including Node's internal linker chain) drain - until // linking finishes. const deadline = Date.now() + 5000; while (module.status === 'linking') { if (Date.now() > deadline) { throw new Error(`Jest: module ${module.identifier} is stuck in 'linking' state after 5 s - ` + 'this is likely a bug in Jest (please report it).'); } await new Promise(resolve => setImmediate(resolve)); } } if (module.status === 'linked') { if (_nodeCapabilities.supportsSyncEvaluate && !moduleHasAsyncGraph(module)) { // `evaluate()` fulfills synchronously whe