UNPKG

jest-runtime

Version:
1,251 lines (1,234 loc) 78 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; function path() { const data = _interopRequireWildcard(require("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 => (0, _slash().default)(match)).map(match => { const relativePath = path().posix.relative(slashedDirname, match); return path().posix.dirname(match) === slashedDirname ? `./${relativePath}` : relativePath; }).map(match => `\t'${match}'`).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; /***/ }) /******/ }); /************************************************************************/ /******/ // The module cache /******/ var __webpack_module_cache__ = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ var cachedModule = __webpack_module_cache__[moduleId]; /******/ if (cachedModule !== undefined) { /******/ return cachedModule.exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = __webpack_module_cache__[moduleId] = { /******/ // no module.id needed /******/ // no module.loaded needed /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /************************************************************************/ var __webpack_exports__ = {}; // This entry needs to be wrapped in an IIFE because it uses a non-standard name for the exports (exports). (() => { var exports = __webpack_exports__; Object.defineProperty(exports, "__esModule", ({ value: true })); exports["default"] = void 0; function _module() { const data = _interopRequireDefault(require("module")); _module = function () { return data; }; return data; } function path() { const data = _interopRequireWildcard(require("path")); path = function () { return data; }; return data; } function _url() { const data = require("url"); _url = function () { return data; }; return data; } function _vm() { const data = require("vm"); _vm = function () { return data; }; return data; } function _cjsModuleLexer() { const data = require("cjs-module-lexer"); _cjsModuleLexer = function () { return data; }; return data; } function _collectV8Coverage() { const data = require("collect-v8-coverage"); _collectV8Coverage = function () { return data; }; return data; } function fs() { const data = _interopRequireWildcard(require("graceful-fs")); fs = function () { return data; }; return data; } function _slash() { const data = _interopRequireDefault(require("slash")); _slash = function () { return data; }; return data; } function _stripBom() { const data = _interopRequireDefault(require("strip-bom")); _stripBom = function () { return data; }; return data; } function _transform() { const data = require("@jest/transform"); _transform = function () { return data; }; return data; } function _jestHasteMap() { const data = _interopRequireDefault(require("jest-haste-map")); _jestHasteMap = function () { return data; }; return data; } function _jestMessageUtil() { const data = require("jest-message-util"); _jestMessageUtil = function () { return data; }; return data; } function _jestRegexUtil() { const data = require("jest-regex-util"); _jestRegexUtil = function () { return data; }; return data; } function _jestResolve() { const data = _interopRequireDefault(require("jest-resolve")); _jestResolve = function () { return data; }; return data; } function _jestSnapshot() { const data = require("jest-snapshot"); _jestSnapshot = 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"); 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); } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } /** * 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 esmIsAvailable = typeof _vm().SourceTextModule === 'function'; const dataURIRegex = /^data:(?<mime>text\/javascript|application\/json|application\/wasm)(?:;(?<encoding>charset=utf-8|base64))?,(?<code>.*)$/; const defaultTransformOptions = { isInternalModule: false, supportsDynamicImport: esmIsAvailable, supportsExportNamespaceFrom: false, supportsStaticESM: false, supportsTopLevelAwait: false }; // These are modules that we know // * are safe to require from the outside (not stateful, not prone to errors passing in instances from different realms), and // * take sufficiently long to require to warrant an optimization. // When required from the outside, they use the worker's require cache and are thus // only loaded once per worker, not once per test file. // Use /benchmarks/test-file-overhead to measure the impact. // Note that this only applies when they are required in an internal context; // users who require one of these modules in their tests will still get the module from inside the VM. // Prefer listing a module here only if it is impractical to use the jest-resolve-outside-vm-option where it is required, // e.g. because there are many require sites spread across the dependency graph. const INTERNAL_MODULE_REQUIRE_OUTSIDE_OPTIMIZED_MODULES = new Set(['chalk']); const JEST_RESOLVE_OUTSIDE_VM_OPTION = Symbol.for('jest-resolve-outside-vm-option'); const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); const retryTimesSymbol = Symbol.for('RETRY_TIMES'); const waitBeforeRetrySymbol = Symbol.for('WAIT_BEFORE_RETRY'); const retryImmediatelySybmbol = Symbol.for('RETRY_IMMEDIATELY'); const logErrorsBeforeRetrySymbol = Symbol.for('LOG_ERRORS_BEFORE_RETRY'); const NODE_MODULES = `${path().sep}node_modules${path().sep}`; const getModuleNameMapper = config => { if (Array.isArray(config.moduleNameMapper) && config.moduleNameMapper.length > 0) { return config.moduleNameMapper.map(([regex, moduleName]) => ({ moduleName, regex: new RegExp(regex) })); } return null; }; const isWasm = modulePath => modulePath.endsWith('.wasm'); const unmockRegExpCache = new WeakMap(); const EVAL_RESULT_VARIABLE = 'Object.<anonymous>'; const runtimeSupportsVmModules = typeof _vm().SyntheticModule === 'function'; const supportsNodeColonModulePrefixInRequire = (() => { try { require('node:fs'); return true; } catch { return false; } })(); class Runtime { _cacheFS; _cacheFSBuffer = new Map(); _config; _globalConfig; _coverageOptions; _currentlyExecutingModulePath; _environment; _explicitShouldMock; _explicitShouldMockModule; _onGenerateMock; _fakeTimersImplementation; _internalModuleRegistry; _isCurrentlyExecutingManualMock; _mainModule; _mockFactories; _mockMetaDataCache; _mockRegistry; _isolatedMockRegistry; _moduleMockRegistry; _moduleMockFactories; _moduleMocker; _isolatedModuleRegistry; _moduleRegistry; _esmoduleRegistry; _cjsNamedExports; _esmModuleLinkingMap; _testPath; _resolver; _shouldAutoMock; _shouldMockModuleCache; _shouldUnmockTransitiveDependenciesCache; _sourceMapRegistry; _scriptTransformer; _fileTransforms; _fileTransformsMutex; _v8CoverageInstrumenter; _v8CoverageResult; _v8CoverageSources; _transitiveShouldMock; _unmockList; _virtualMocks; _virtualModuleMocks; _moduleImplementation; jestObjectCaches; jestGlobals; esmConditions; cjsConditions; isTornDown = false; isInsideTestCode; constructor(config, environment, resolver, transformer, cacheFS, coverageOptions, testPath, globalConfig) { this._cacheFS = cacheFS; this._config = config; this._coverageOptions = coverageOptions; this._currentlyExecutingModulePath = ''; this._environment = environment; this._globalConfig = globalConfig; this._explicitShouldMock = new Map(); this._explicitShouldMockModule = new Map(); this._onGenerateMock = new Set(); this._internalModuleRegistry = new Map(); this._isCurrentlyExecutingManualMock = null; this._mainModule = null; this._mockFactories = new Map(); this._mockRegistry = new Map(); this._moduleMockRegistry = new Map(); this._moduleMockFactories = new Map(); (0, _jestUtil().invariant)(this._environment.moduleMocker, '`moduleMocker` must be set on an environment when created'); this._moduleMocker = this._environment.moduleMocker; this._isolatedModuleRegistry = null; this._isolatedMockRegistry = null; this._moduleRegistry = new Map(); this._esmoduleRegistry = new Map(); this._cjsNamedExports = new Map(); this._esmModuleLinkingMap = new WeakMap(); this._testPath = testPath; this._resolver = resolver; this._scriptTransformer = transformer; this._shouldAutoMock = config.automock; this._sourceMapRegistry = new Map(); this._fileTransforms = new Map(); this._fileTransformsMutex = new Map(); this._virtualMocks = new Map(); this._virtualModuleMocks = new Map(); this.jestObjectCaches = new Map(); this._mockMetaDataCache = new Map(); this._shouldMockModuleCache = new Map(); this._shouldUnmockTransitiveDependenciesCache = new Map(); this._transitiveShouldMock = new Map(); this._fakeTimersImplementation = config.fakeTimers.legacyFakeTimers ? this._environment.fakeTimers : this._environment.fakeTimersModern; this._unmockList = unmockRegExpCache.get(config); if (!this._unmockList && config.unmockedModulePathPatterns) { this._unmockList = new RegExp(config.unmockedModulePathPatterns.join('|')); unmockRegExpCache.set(config, this._unmockList); } const envExportConditions = this._environment.exportConditions?.() ?? []; this.esmConditions = [...new Set(['import', 'default', ...envExportConditions])]; this.cjsConditions = [...new Set(['require', 'default', ...envExportConditions])]; if (config.automock) { for (const filePath of config.setupFiles) { if (filePath.includes(NODE_MODULES)) { const moduleID = this._resolver.getModuleID(this._virtualMocks, filePath, undefined, // shouldn't really matter, but in theory this will make sure the caching is correct { conditions: this.unstable_shouldLoadAsEsm(filePath) ? this.esmConditions : this.cjsConditions }); this._transitiveShouldMock.set(moduleID, false); } } } this.resetModules(); } static shouldInstrument = _transform().shouldInstrument; static async createContext(config, options) { (0, _jestUtil().createDirectory)(config.cacheDirectory); const instance = await Runtime.createHasteMap(config, { console: options.console, maxWorkers: options.maxWorkers, resetCache: !config.cache, watch: options.watch, watchman: options.watchman }); const hasteMap = await instance.build(); return { config, hasteFS: hasteMap.hasteFS, moduleMap: hasteMap.moduleMap, resolver: Runtime.createResolver(config, hasteMap.moduleMap) }; } static createHasteMap(config, options) { const ignorePatternParts = [...config.modulePathIgnorePatterns, ...(options && options.watch ? config.watchPathIgnorePatterns : []), config.cacheDirectory.startsWith(config.rootDir + path().sep) && config.cacheDirectory].filter(Boolean); const ignorePattern = ignorePatternParts.length > 0 ? new RegExp(ignorePatternParts.join('|')) : undefined; return _jestHasteMap().default.create({ cacheDirectory: config.cacheDirectory, computeSha1: config.haste.computeSha1, console: options?.console, dependencyExtractor: config.dependencyExtractor, enableSymlinks: config.haste.enableSymlinks, extensions: [_jestSnapshot().EXTENSION, ...config.moduleFileExtensions], forceNodeFilesystemAPI: config.haste.forceNodeFilesystemAPI, hasteImplModulePath: config.haste.hasteImplModulePath, hasteMapModulePath: config.haste.hasteMapModulePath, id: config.id, ignorePattern, maxWorkers: options?.maxWorkers || 1, mocksPattern: (0, _jestRegexUtil().escapePathForRegex)(`${path().sep}__mocks__${path().sep}`), platforms: config.haste.platforms || ['ios', 'android'], resetCache: options?.resetCache, retainAllFiles: config.haste.retainAllFiles || false, rootDir: config.rootDir, roots: config.roots, throwOnModuleCollision: config.haste.throwOnModuleCollision, useWatchman: options?.watchman, watch: options?.watch, workerThreads: options?.workerThreads }); } static createResolver(config, moduleMap) { return new (_jestResolve().default)(moduleMap, { defaultPlatform: config.haste.defaultPlatform, extensions: config.moduleFileExtensions.map(extension => `.${extension}`), hasCoreModules: true, moduleDirectories: config.moduleDirectories, moduleNameMapper: getModuleNameMapper(config), modulePaths: config.modulePaths, platforms: config.haste.platforms, resolver: config.resolver, rootDir: config.rootDir }); } static async runCLI() { throw new Error('The jest-runtime CLI has been moved into jest-repl'); } static getCLIOptions() { throw new Error('The jest-runtime CLI has been moved into jest-repl'); } // unstable as it should be replaced by https://github.com/nodejs/modules/issues/393, and we don't want people to use it unstable_shouldLoadAsEsm(modulePath) { return isWasm(modulePath) || _jestResolve().default.unstable_shouldLoadAsEsm(modulePath, this._config.extensionsToTreatAsEsm); } async loadEsmModule(modulePath, query = '') { const cacheKey = modulePath + query; const registry = this._isolatedModuleRegistry ?? this._esmoduleRegistry; if (this._fileTransformsMutex.has(cacheKey)) { await this._fileTransformsMutex.get(cacheKey); } if (!registry.has(cacheKey)) { (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'); let transformResolve; let transformReject; this._fileTransformsMutex.set(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!'); if (isWasm(modulePath)) { const wasm = this._importWasmModule(this.readFileBuffer(modulePath), modulePath, context); registry.set(cacheKey, wasm); transformResolve(); return wasm; } if (this._resolver.isCoreModule(modulePath)) { const core = this._importCoreModule(modulePath, context); registry.set(cacheKey, core); transformResolve(); return core; } const transformedCode = await this.transformFileAsync(modulePath, { isInternalModule: false, supportsDynamicImport: true, supportsExportNamespaceFrom: true, supportsStaticESM: true, supportsTopLevelAwait: true }); try { let module; if (modulePath.endsWith('.json')) { module = new (_vm().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 (_vm().SourceTextModule)(transformedCode, { context, identifier: modulePath, importModuleDynamically: async (specifier, referencingModule) => { (0, _jestUtil().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 => { const metaUrl = (0, _url().pathToFileURL)(modulePath).href; meta.url = metaUrl; // @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/node@20.11.0 meta.filename = modulePath; // @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/node@20.11.0 meta.dirname = path().dirname(modulePath); // @ts-expect-error: todo fixme meta.resolve = (specifier, parent = metaUrl) => { const parentPath = (0, _url().fileURLToPath)(parent); const resolvedPath = this._resolver.resolveModule(parentPath, specifier, { conditions: this.esmConditions }); return (0, _url().pathToFileURL)(resolvedPath).href; }; let jest = this.jestObjectCaches.get(modulePath); if (!jest) { jest = this._createJestObjectFor(modulePath); this.jestObjectCaches.set(modulePath, jest); } meta.jest = jest; } }); } (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; } } 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.isTornDown) { this._logFormattedReferenceError('You are trying to `import` a file after the Jest environment has been torn down.'); process.exitCode = 1; // @ts-expect-error -- exiting return; } if (this.isInsideTestCode === false) { throw new ReferenceError('You are trying to `import` a file outside of the scope of the test code.'); } const registry = this._isolatedModuleRegistry ?? this._esmoduleRegistry; if (specifier === '@jest/globals') { const fromCache = registry.get('@jest/globals'); if (fromCache) { return fromCache; } const globals = this.getGlobalsForEsm(referencingIdentifier, context); registry.set('@jest/globals', globals); return globals; } if (specifier.startsWith('data:')) { if (await this._shouldMockModule(referencingIdentifier, specifier, this._explicitShouldMockModule)) { return this.importMock(referencingIdentifier, specifier, context); } const fromCache = registry.get(specifier); if (fromCache) { return fromCache; } const match = specifier.match(dataURIRegex); if (!match || !match.groups) { throw new Error('Invalid data URI'); } const mime = match.groups.mime; const encoding = match.groups.encoding; let module; if (mime === 'application/wasm') { if (!encoding) { throw new Error('Missing data URI encoding'); } if (encoding !== 'base64') { throw new Error(`Invalid data URI encoding: ${encoding}`); } module = await this._importWasmModule(Buffer.from(match.groups.code, 'base64'), specifier, context); } else { let code = match.groups.code; if (!encoding || encoding === 'charset=utf-8') { code = decodeURIComponent(code); } else if (encoding === 'base64') { code = Buffer.from(code, 'base64').toString(); } else { throw new Error(`Invalid data URI encoding: ${encoding}`); } if (mime === 'application/json') { module = new (_vm().SyntheticModule)(['default'], function () { const obj = JSON.parse(code); // @ts-expect-error: TS doesn't know what `this` is this.setExport('default', obj); }, { context, identifier: specifier }); } else { module = new (_vm().SourceTextModule)(code, { context, identifier: specifier, importModuleDynamically: async (specifier, referencingModule) => { (0, _jestUtil().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) { // no `jest` here as it's not loaded in a file meta.url = specifier; if (meta.url.startsWith('file://')) { // @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/node@20.11.0 meta.filename = (0, _url().fileURLToPath)(meta.url); // @ts-expect-error Jest uses @types/node@16. Will be fixed when updated to @types/node@20.11.0 meta.dirname = path().dirname(meta.filename); } } }); } } registry.set(specifier, module); return module; } if (specifier.startsWith('file://')) { specifier = (0, _url().fileURLToPath)(specifier); } const [specifierPath, query] = specifier.split('?'); if (await this._shouldMockModule(referencingIdentifier, specifierPath, this._explicitShouldMockModule)) { return this.importMock(referencingIdentifier, specifierPath, context); } const resolved = await this._resolveModule(referencingIdentifier, specifierPath); if ( // json files are modules when imported in modules resolved.endsWith('.json') || this._resolver.isCoreModule(resolved) || this.unstable_shouldLoadAsEsm(resolved)) { return this.loadEsmModule(resolved, query); } return this.loadCjsAsEsm(referencingIdentifier, resolved, context); } async linkAndEvaluateModule(module) { if (this.isTornDown) { this._logFormattedReferenceError('You are trying to `import` a file after the Jest environment has been torn down.'); process.exitCode = 1; return; } if (this.isInsideTestCode === false) { throw new ReferenceError('You are trying to `import` a file outside of the scope of the test code.'); } if (module.status === 'unlinked') { // since we might attempt to link the same module in parallel, stick the promise in a weak map so every call to // this method can await it this._esmModuleLinkingMap.set(module, module.link((specifier, referencingModule) => this.resolveModule(specifier, referencingModule.identifier, referencingModule.context))); } await this._esmModuleLinkingMap.get(module); if (module.status === 'linked') { await module.evaluate(); } return module; } async unstable_importModule(from, moduleName) { (0, _jestUtil().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 [path, query] = (moduleName ?? '').split('?'); const modulePath = await this._resolveModule(from, path); const module = await this.loadEsmModule(modulePath, query); return this.linkAndEvaluateModule(module); } loadCjsAsEsm(from, modulePath, context) { // CJS loaded via `import` should share cache with other CJS: https://github.com/nodejs/modules/issues/503 const cjs = this.requireModuleOrMock(from, modulePath); const parsedExports = this.getExportsOfCjs(modulePath); const cjsExports = [...parsedExports].filter(exportName => { // we don't wanna respect any exports _named_ default as a named export if (exportName === 'default') { return false; } return Object.hasOwnProperty.call(cjs, exportName); }); const module = new (_vm().SyntheticModule)([...cjsExports, 'default'], function () { for (const exportName of cjsExports) { // @ts-expect-error: TS doesn't know what `this` is this.setExport(exportName, cjs[exportName]); } // @ts-expect-error: TS doesn't know what `this` is this.setExport('default', cjs); }, { context, identifier: modulePath }); return evaluateSyntheticModule(module); } async importMock(from, moduleName, context) { const moduleID = await this._resolver.getModuleIDAsync(this._virtualModuleMocks, from, moduleName, { conditions: this.esmConditions }); if (this._moduleMockRegistry.has(moduleID)) { return this._moduleMockRegistry.get(moduleID); } if (this._moduleMockFactories.has(moduleID)) { const invokedFactory = await this._moduleMockFactories.get(moduleID // has check above makes this ok )(); const module = new (_vm().SyntheticModule)(Object.keys(invokedFactory), function () { for (const [key, value] of Object.entries(invokedFactory)) { // @ts-expect-error: TS doesn't know what `this` is this.setExport(key, value); } }, { context, identifier: moduleName }); this._moduleMockRegistry.set(moduleID, module); return evaluateSyntheticModule(module); } throw new Error('Attempting to import a mock without a factory'); } getExportsOfCjs(modulePath) { const cachedNamedExports = this._cjsNamedExports.get(modulePath); if (cachedNamedExports) { return cachedNamedExports; } if (path().extname(modulePath) === '.node') { const nativeModule = this.requireModuleOrMock('', modulePath); const namedExports = new Set(Object.keys(nativeModule)); this._cjsNamedExports.set(modulePath, namedExports); return namedExports; } const transformedCode = this._fileTransforms.get(modulePath)?.code ?? this.readFile(modulePath); const { exports, reexports } = (0, _cjsModuleLexer().parse)(transformedCode); const namedExports = new Set(exports); for (const reexport of reexports) { if (this._resolver.isCoreModule(reexport)) { const exports = this.requireModule(modulePath, reexport); if (exports !== null && typeof exports === 'object') { for (const e of Object.keys(exports)) namedExports.add(e); } } else { const resolved = this._resolveCjsModule(modulePath, reexport); const exports = this.getExportsOfCjs(resolved); for (const e of exports) namedExports.add(e); } } this._cjsNamedExports.set(modulePath, namedExports); return namedExports; } requireModule(from, moduleName, options, isRequireActual = false) { const isInternal = options?.isInternalModule ?? false; const resolveModuleOptions = { conditions: this.cjsConditions }; const moduleID = this._resolver.getModuleID(this._virtualMocks, from, moduleName, resolveModuleOptions); let modulePath; // Some old tests rely on this mocking behavior. Ideally we'll change this // to be more explicit. const moduleResource = moduleName && this._resolver.getModule(moduleName); const manualMock = moduleName && this._resolver.getMockModule(from, moduleName, resolveModuleOptions); if (!options?.isInternalModule && !isRequireActual && !moduleResource && manualMock && manualMock !== this._isCurrentlyExecutingManualMock && this._explicitShouldMock.get(moduleID) !== false) { modulePath = manualMock; } if (moduleName && this._resolver.isCoreModule(moduleName)) { return this._requireCoreModule(moduleName, supportsNodeColonModulePrefixInRequire); } if (!modulePath) { modulePath = this._resolveCjsModule(from, moduleName); } if (this.unstable_shouldLoadAsEsm(modulePath)) { // Node includes more info in the message const error = new Error(`Must use import to load ES Module: ${modulePath}`); error.code = 'ERR_REQUIRE_ESM'; throw error; } let moduleRegistry; if (isInternal) { moduleRegistry = this._internalModuleRegistry; } else if (this._isolatedModuleRegistry) { moduleRegistry = this._isolatedModuleRegistry; } else { moduleRegistry = this._moduleRegistry; } 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); throw error; } return localModule.exports; } requireInternalModule(from, to) { if (to) { const require = _module().default.createRequire(from); if (INTERNAL_MODULE_REQUIRE_OUTSIDE_OPTIMIZED_MODULES.has(to)) { return require(to); } const outsideJestVmPath = (0, _helpers.decodePossibleOutsideJestVmPath)(to); if (outsideJestVmPath) { return require(outsideJestVmPath); } } return this.requireModule(from, to, { isInternalModule: true, supportsDynamicImport: esmIsAvailable, supportsExportNamespaceFrom: false, supportsStaticESM: false, supportsTopLevelAwait: false }); } requireActual(from, moduleName) { return this.requireModule(from, moduleName, undefined, true); } requireMock(from, moduleName) { const options = { conditions: this.cjsConditions }; const moduleID = this._resolver.getModuleID(this._virtualMocks, from, moduleName, options); if (this._isolatedMockRegistry?.has(moduleID)) { return this._isolatedMockRegistry.get(moduleID); } else if (this._mockRegistry.has(moduleID)) { return this._mockRegistry.get(moduleID); } const mockRegistry = this._isolatedMockRegistry || this._mockRegistry; if (this._mockFactories.has(moduleID)) { // has check above makes this ok const module = this._mockFactories.get(moduleID)(); mockRegistry.set(moduleID, module); return module; } const manualMockOrStub = this._resolver.getMockModule(from, moduleName, options); let modulePath = this._resolver.getMockModule(from, moduleName, options) || this._resolveCjsModule(from, moduleName); let isManualMock = manualMockOrStub && !this._resolver.resolveStubModuleName(from, moduleName, options); if (!isManualMock) { // If the actual module file has a __mocks__ dir sitting immediately next // to it, look to see if there is a manual mock for this file. // // subDir1/my_module.js // subDir1/__mocks__/my_module.js // subDir2/my_module.js // subDir2/__mocks__/my_module.js // // Where some other module does a relative require into each of the // respective subDir{1,2} directories and expects a manual mock // corresponding to that particular my_module.js file. const moduleDir = path().dirname(modulePath); const moduleFileName = path().basename(modulePath); const potentialManualMock = path().join(moduleDir, '__mocks__', moduleFileName); if (fs().existsSync(potentialManualMock)) { isManualMock = true; modulePath = potentialManualMock; } } if (isManualMock) { const localModule = { children: [], exports: {}, filename: modulePath, id: modulePath, isPreloading: false, loaded: false, path: path().dirname(modulePath) }; this._loadModule(localModule, from, moduleName, modulePath, undefined, mockRegistry); mockRegistry.set(moduleID, localModule.exports); } else { // Look for a real module to generate an automock from mockRegistry.set(moduleID, this._generateMock(from, moduleName)); } return mockRegistry.get(moduleID); } _loadModule(localModule, from, moduleName, modulePath, options, moduleRegistry) { if (path().extname(modulePath) === '.json') { const text = (0, _stripBom().default)(this.readFile(modulePath)); const transformedFile = this._scriptTransformer.transformJson(modulePath, this._getFullTransformationOptions(options), text); localModule.exports = this._environment.global.JSON.parse(transformedFile); } else if (path().extname(modulePath) === '.node') { localModule.exports = require(modulePath); } else { // Only include the fromPath if a moduleName is given. Else treat as root. const fromPath = moduleName ? from : null; this._execModule(localModule, options, moduleRegistry, fromPath, moduleName); } localModule.loaded = true; } _getFullTransformationOptions(options = defaultTransformOptions) { return { ...options, ...this._coverageOptions }; } requireModuleOrMock(from, moduleName) { // this module is unmockable if (moduleName === '@jest/globals') { // @ts-expect-error: we don't care that it's not assignable to T return this.getGlobalsForCjs(from); } try { if (this._shouldMockCjs(from, moduleName, this._explicitShouldMock)) { return this.requireMock(from, moduleName); } else { return this.requireModule(from, moduleName); } } catch (error) { const moduleNotFound = _jestResolve().default.tryCastModuleNotFoundError(error); if (moduleNotFound) { if (moduleNotFound.siblingWithSimilarExtensionFound === null || moduleNotFound.siblingWithSimilarExtensionFound === undefined) { moduleNotFound.hint = (0, _helpers.findSiblingsWithFileExtension)(this._config.moduleFileExtensions, from, moduleNotFound.moduleName || moduleName); moduleNotFound.siblingWithSimilarExtensionFound = Boolean(moduleNotFound.hint); } moduleNotFound.buildMessage(this._config.rootDir); throw moduleNotFound; } throw error; } } isolateModules(fn) { if (this._isolatedModuleRegistry || this._isolatedMockRegistry) { throw new Error('isolateModules cannot be nested inside another isolateModules or isolateModulesAsync.'); } this._isolatedModuleRegistry = new Map(); this._isolatedMockRegistry = new Map(); try { fn(); } finally { // might be cleared within the callback this._isolatedModuleRegistry?.clear(); this._isolatedMockRegistry?.clear(); this._isolatedModuleRegistry = null; this._isolatedMockRegistry = null; } } async isolateModulesAsync(fn) { if (this._isolatedModuleRegistry || this._isolatedMockRegistry) { throw new Error('isolateModulesAsync cannot be nested inside another isolateModulesAsync or isolateModules.'); } this._isolatedModuleRegistry = new Map(); this._isolatedMockRegistry = new Map(); try { await fn(); } finally { // might be cleared within the callback this._isolatedModuleRegistry?.clear(); this._isolatedMockRegistry?.clear(); this._isolatedModuleRegistry = null; this._isolatedMockRegistry = null; } } resetModules() { this._isolatedModuleRegistry?.clear(); this._isolatedMockRegistry?.clear(); this._isolatedModuleRegistry = null; this._isolatedMockRegistry = null; this._mockRegistry.clear(); this._moduleRegistry.clear(); this._esmoduleRegistry.clear(); this._fileTransformsMutex.clear(); this._cjsNamedExports.clear(); this._moduleMockRegistry.clear(); this._cacheFS.clear(); this._cacheFSBuffer.clear(); if (this._coverageOptions.collectCoverage && this._coverageOptions.coverageProvider === 'v8' && this._v8CoverageSources) { this._v8CoverageSources = new Map([...this._v8CoverageSources, ...this._fileTransforms]); } this._fileTransforms.clear(); if (this._environment) { if (this._environment.global) { const envGlobal = this._environment.global; for (const key of Object.keys(envGlobal)) { const globalMock = envGlobal[key]; if ((typeof globalMock === 'object' && globalMock !== null || typeof globalMock === 'function') && '_isMockFunction' in globalMock && globalMock._isMockFunction === true) { globalMock.mockClear(); } } } if (this._environment.fakeTimers) { this._environment.fakeTimers.clearAllTimers(); } } } async collectV8Coverage() { this._v8CoverageInstrumenter = new (_collectV8Coverage().CoverageInstrumenter)(); this._v8CoverageSources = new Map(); await this._v8CoverageInstrumenter.startInstrumenting(); } async stopCollectingV8Coverage() { if (!this._v8CoverageInstrumenter || !this._v8CoverageSources) { throw new Error('You need to call `collectV8Coverage` first.'); } this._v8CoverageResult = await this._v8CoverageInstrumenter.stopInstrumenting(); this._v8CoverageSources = new Map([...this._v8CoverageSources, ...this._fileTransforms]); } getAllCoverageInfoCopy() { return (0, _jestUtil().deepCyclicCopy)(this._environment.global.__coverage__); } getAllV8CoverageInfoCopy() { if (!this._v8CoverageResult || !this._v8CoverageSources) { throw new Error('You need to call `stopCollectingV8Coverage` first.'); } return this._v8CoverageResult.filter(res => res.url.startsWith('file://')).map(res => ({ ...res, url: (0, _url().fileURLToPath)(res.url) })).filter(res => // TODO: will this work on windows? It might be better if `shouldInstrument` deals with it anyways res.url.startsWith(this._config.rootDir) && (0, _transform().shouldInstrument)(res.url, this._coverageOptions, this._config, /* loadedFilenames */[...this._v8CoverageSources.keys()])).map(result => { const transformedFile = this._v8CoverageSources.get(result.url); return { codeTransformResult: transformedFile, result }; }); } getSourceMaps() { return this._sourceMapRegistry; } setMock(from, moduleName, mockFactory, options) { if (options?.virtual) { const mockPath = this._resolver.getModulePath(from, moduleName); this._virtualMocks.set(mockPath, true); } const moduleID = this._resolver.getModuleID(this._virtualMocks, from, moduleName, { conditions: this.cjsConditions }); this._explicitShouldMock.set(moduleID, true); this._mockFactories.set(moduleID, mockFactory); } setModuleMock(from, moduleName, mockFactory, options) { if (options?.virtual) { const mockPath = this._resolver.getModulePath(from, moduleName); this._virtualModuleMocks.set(mockPath, true); } const moduleID = this._resolver.getModuleID(this._virtualModuleMocks, from, moduleName, { conditions: this.esmConditions }); this._explicitShouldMockModule.set(moduleID, true); this._moduleMockFactories.set(moduleID, mockFactory); } restoreAllMocks() { this._moduleMocker.restoreAllMocks(); } resetAllMocks() { this._moduleMocker.resetAllMocks(); } clearAllMocks() { this._moduleMocker.clearAllMocks(); } enterTestCode() { this.isInsideTestCode = true; } leaveTestCode() { this.isInsideTestCode = false; } teardown() { this.restoreAllMocks(); this.resetModules(); this._internalModuleRegistry.clear(); this._mainModule = null; this._mockFactories.clear(); this._moduleMockFactories.clear(); this._mockMetaDataCache.clear(); this._shouldMockModuleCache.clear(); this._shouldUnmockTransitiveDependenciesCache.clear(); this._explicitShouldMock.clear(); this._explicitShouldMockModule.clear(); this._transitiveShouldMock.clear(); this._virtualMocks.clear(); this._virtualModuleMocks.clear(); this._cacheFS.clear(); this._unmockList = undefined; this._sourceMapRegistry.clear(); this._fileTransforms.clear(); this.jestObjectCaches.clear(); this._v8CoverageSources?.clear(); this._v8CoverageResult = []; this._v8CoverageInstrumenter = undefined; this._moduleImplementation = undefined; this.isTornDown = true; } _resolveCjsModule(from, to) { return to ? this._resolver.resolveModule(from, to, { conditions: this.cjsConditions }) : from; } _resolveModule(from, to) { return to ? this._resolver.resolveModuleAsync(from, to, { conditions: this.esmConditions }) : from; } _requireResolve(from, moduleName, options = {}) { if (moduleName == null) { throw new Error('The first argument to require.resolve must be a string. Received null or undefined.'); } if (path().isAbsolute(moduleName)) { const module = this._resolver.resolveModuleFromDirIfExists(moduleName, moduleName, { conditions: this.cjsConditions, paths: [] }); if (module) { return module; } } else if (options.paths) { for (const p of options.paths) { const absolutePath = path().resolve(from, '..', p); const module = this._resolver.resolveModuleFromDirIfExists(absolutePath, moduleName, // required to also resolve files without leading './' directly in the path { conditions: this.cjsConditions, paths: [absolutePath] }); if (module) { return module; } } throw new (_jestResolve().default.ModuleNotFoundError)(`Cannot resolve module '${moduleName}' from paths ['${options.paths.join("', '")}'] from ${from}`); } try { return this._resolveCjsModule(from, moduleName); } catch (error) { const module = this._resolver.getMockModule(from, moduleName, { conditions: this.cjsConditions }); if (module) { return module; } else { throw error; } } } _requireResolvePaths(from, moduleName) { const fromDir = path().resolve(from, '..'); if (moduleName == null) { throw new Error('The first argument to require.resolve.paths must be a string. Received null or undefined.'); } if (moduleName.length === 0) { throw new Error('The first argument to require.resolve.paths must not be the empty string.'); } if (moduleName[0] === '.') { return [fromDir]; } if (this._resolver.isCoreModule(moduleName)) { return null; } const modulePaths = this._resolver.getModulePaths(fromDir); const globalPaths = this._resolver.getGlobalPaths(moduleName); return [...modulePaths, ...globalPaths]; } _execModule(localModule, options, moduleRegistry, from, moduleName) { if (this.isTornDown) { this._logFormattedReferenceError('You are trying to `import` a file after the Jest environment has been torn down.'); process.exitCode = 1; return; } if (this.isInsideTestCode === false) { throw new ReferenceError('You are trying to `import` a file outside of the scope of the test code.'); } // If the environment was disposed, prevent this module from being executed. if (!this._environment.global) { return; } const module = localModule; const filename = module.filename; const lastExecutingModulePath = this._currentlyExecutingModulePath; this._currentlyExecutingModulePath = filename; const origCurrExecutingManualMock = this._isCurrentlyExecutingManualMock; this._isCurrentlyExecutingManualMock = filename; module.children = []; Object.defineProperty(module, 'parent', { enumerable: true, get() { const key = from || ''; return moduleRegistry.get(key) || null; } }); const modulePaths = this._resolver.getModulePaths(module.path); const globalPaths = this._resolver.getGlobalPaths(moduleName); module.paths = [...modulePaths, ...globalPaths]; Object.defineProperty(module, 'require', { value: this._createRequireImplementation(module, options) }); const transformedCode = this.transformFile(filename, options); let compiledFunction = null; const script = this.createScriptFromCode(transformedCode, filename); let runScript = null; const vmContext = this._environment.getVmContext(); if (vmContext) { runScript = script.runInContext(vmContext, { filename }); } if (runScript !== null) { compiledFunction = runScript[EVAL_RESULT_VARIABLE]; } if (compiledFunction === null) { this._logFormattedReferenceError('You are trying to `import` a file after the Jest environment has been torn down.'); process.exitCode = 1; return; } const jestObject = this._createJestObjectFor(filename); this.jestObjectCaches.set(filename, jestObject