diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dd36349cab5..13752c82a913 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - `[jest-mock]` Add support for the Explicit Resource Management proposal to use the `using` keyword with `jest.spyOn(object, methodName)` ([#14895](https://github.com/jestjs/jest/pull/14895)) - `[jest-runtime]` Exposing new modern timers function `jest.advanceTimersToFrame()` from `@jest/fake-timers` ([#14598](https://github.com/jestjs/jest/pull/14598)) - `[jest-runtime]` Support `import.meta.filename` and `import.meta.dirname` (available from [Node 20.11](https://nodejs.org/en/blog/release/v20.11.0)) ([#14854](https://github.com/jestjs/jest/pull/14854)) +- `[jest-runtime]` Support `import.meta.resolve` ([#14930](https://github.com/jestjs/jest/pull/14930)) - `[@jest/schemas]` Upgrade `@sinclair/typebox` to v0.31 ([#14072](https://github.com/jestjs/jest/pull/14072)) - `[@jest/types]` `test.each()`: Accept a readonly (`as const`) table properly ([#14565](https://github.com/jestjs/jest/pull/14565)) - `[jest-snapshot]` [**BREAKING**] Add support for [Error causes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause) in snapshots ([#13965](https://github.com/facebook/jest/pull/13965)) diff --git a/e2e/native-esm/__tests__/native-esm.test.js b/e2e/native-esm/__tests__/native-esm.test.js index 7e0574a33963..90de1bfa7e9d 100644 --- a/e2e/native-esm/__tests__/native-esm.test.js +++ b/e2e/native-esm/__tests__/native-esm.test.js @@ -31,6 +31,7 @@ test('should have correct import.meta', () => { dirname: expect.any(String), filename: expect.any(String), jest: expect.anything(), + resolve: expect.any(Function), url: expect.any(String), }); expect(import.meta.jest).toBe(jestObject); @@ -56,6 +57,16 @@ test('should have correct import.meta', () => { true, ); } + expect( + import.meta + .resolve('colors') + .endsWith('jest/e2e/native-esm/node_modules/colors/lib/index.js'), + ).toBe(true); + expect( + import.meta + .resolve('./native-esm.test') + .endsWith('jest/e2e/native-esm/__tests__/native-esm.test.js'), + ).toBe(true); }); test('should double stuff', () => { @@ -68,6 +79,7 @@ test('should support importing node core modules', () => { expect(JSON.parse(readFileSync(packageJsonPath, 'utf8'))).toEqual({ devDependencies: { + colors: '^1.4.0', 'discord.js': '14.3.0', 'iso-constants': '^0.1.2', yargs: '^17.5.1', diff --git a/e2e/native-esm/package.json b/e2e/native-esm/package.json index c113bde36bab..021655db00d0 100644 --- a/e2e/native-esm/package.json +++ b/e2e/native-esm/package.json @@ -4,6 +4,7 @@ "isolated-vm": "^4.6.0" }, "devDependencies": { + "colors": "^1.4.0", "discord.js": "14.3.0", "iso-constants": "^0.1.2", "yargs": "^17.5.1" diff --git a/e2e/native-esm/yarn.lock b/e2e/native-esm/yarn.lock index f12796649778..4b6d633f7356 100644 --- a/e2e/native-esm/yarn.lock +++ b/e2e/native-esm/yarn.lock @@ -333,6 +333,13 @@ __metadata: languageName: node linkType: hard +"colors@npm:^1.4.0": + version: 1.4.0 + resolution: "colors@npm:1.4.0" + checksum: 98aa2c2418ad87dedf25d781be69dc5fc5908e279d9d30c34d8b702e586a0474605b3a189511482b9d5ed0d20c867515d22749537f7bc546256c6014f3ebdcec + languageName: node + linkType: hard + "cross-spawn@npm:^7.0.0": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -1087,6 +1094,7 @@ __metadata: version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." dependencies: + colors: ^1.4.0 discord.js: 14.3.0 iso-constants: ^0.1.2 isolated-vm: ^4.6.0 diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 72d247e81588..1c004f26ca40 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -518,12 +518,26 @@ export default class Runtime { return this.linkAndEvaluateModule(module); }, initializeImportMeta: (meta: JestImportMeta) => { - meta.url = pathToFileURL(modulePath).href; + const metaUrl = 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 = fileURLToPath(meta.url); + 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(meta.filename); + meta.dirname = path.dirname(modulePath); + + // @ts-expect-error It should not be async. Will be fixed when updated to @types/node@20.11.0 + meta.resolve = (specifier, parent = metaUrl) => { + const parentPath = fileURLToPath(parent); + + const resolvedPath = this._resolver.resolveModule( + parentPath, + specifier, + {conditions: this.esmConditions}, + ); + + return pathToFileURL(resolvedPath).href; + }; let jest = this.jestObjectCaches.get(modulePath); @@ -1466,28 +1480,25 @@ export default class Runtime { if (module) { return module; } - } else { - const {paths} = options; - if (paths) { - for (const p of 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 Resolver.ModuleNotFoundError( - `Cannot resolve module '${moduleName}' from paths ['${paths.join( - "', '", - )}'] from ${from}`, + } 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 Resolver.ModuleNotFoundError( + `Cannot resolve module '${moduleName}' from paths ['${options.paths.join( + "', '", + )}'] from ${from}`, + ); } try {