diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c0e2298881..b433bfc61233 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ### Features - `[jest-haste-map]` ignore Sapling vcs directories (`.sl/`) ([#13674](https://github.com/facebook/jest/pull/13674)) -- `[jest-resolve]` Support subpath imports ([#13705](https://github.com/facebook/jest/pull/13705)) +- `[jest-resolve]` Support subpath imports ([#13705](https://github.com/facebook/jest/pull/13705), [#13723](https://github.com/facebook/jest/pull/13723)) - `[jest-runtime]` Add `jest.isolateModulesAsync` for scoped module initialization of asynchronous functions ([#13680](https://github.com/facebook/jest/pull/13680)) - `[jest-test-result]` Added `skipped` and `focused` status to `FormattedTestResult` ([#13700](https://github.com/facebook/jest/pull/13700)) diff --git a/packages/jest-resolve/package.json b/packages/jest-resolve/package.json index d0bec260ddbb..cf786ed1a6f3 100644 --- a/packages/jest-resolve/package.json +++ b/packages/jest-resolve/package.json @@ -17,7 +17,6 @@ "./package.json": "./package.json" }, "dependencies": { - "@okikio/resolve.imports": "^1.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "jest-haste-map": "workspace:^", @@ -26,6 +25,7 @@ "jest-validate": "workspace:^", "resolve": "^1.20.0", "resolve.exports": "^1.1.0", + "resolve.imports": "^2.0.3", "slash": "^3.0.0" }, "devDependencies": { diff --git a/packages/jest-resolve/src/__mocks__/imports/array-import/browser.cjs b/packages/jest-resolve/src/__mocks__/imports/array-import/browser.cjs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/imports/array-import/chrome.cjs b/packages/jest-resolve/src/__mocks__/imports/array-import/chrome.cjs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/imports/array-import/node.mjs b/packages/jest-resolve/src/__mocks__/imports/array-import/node.mjs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/imports/array-import/package.json b/packages/jest-resolve/src/__mocks__/imports/array-import/package.json new file mode 100644 index 000000000000..15ec34ae7412 --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/imports/array-import/package.json @@ -0,0 +1,17 @@ +{ + "name": "array-import", + "imports": { + "#array-import": { + "import": [ + "./node.mjs", + "./node-2.mjs" + ], + "browser": [ + { + "chrome": "./chrome.cjs" + }, + "./browser.cjs" + ] + } + } +} diff --git a/packages/jest-resolve/src/__mocks__/imports/nested-import/browser.cjs b/packages/jest-resolve/src/__mocks__/imports/nested-import/browser.cjs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/imports/nested-import/esm.mjs b/packages/jest-resolve/src/__mocks__/imports/nested-import/esm.mjs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/imports/nested-import/node.cjs b/packages/jest-resolve/src/__mocks__/imports/nested-import/node.cjs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/jest-resolve/src/__mocks__/imports/nested-import/package.json b/packages/jest-resolve/src/__mocks__/imports/nested-import/package.json new file mode 100644 index 000000000000..abf0499a8f54 --- /dev/null +++ b/packages/jest-resolve/src/__mocks__/imports/nested-import/package.json @@ -0,0 +1,12 @@ +{ + "name": "nested-import", + "imports": { + "#nested": { + "node": { + "import": "./esm.mjs", + "require": "./node.cjs" + }, + "default": "./browser.cjs" + } + } +} diff --git a/packages/jest-resolve/src/__tests__/resolve.test.ts b/packages/jest-resolve/src/__tests__/resolve.test.ts index d9c69dbc13e1..6cecedfcb7aa 100644 --- a/packages/jest-resolve/src/__tests__/resolve.test.ts +++ b/packages/jest-resolve/src/__tests__/resolve.test.ts @@ -345,13 +345,66 @@ describe('findNodeModule', () => { ); }); + test('supports nested pattern', () => { + const result = Resolver.findNodeModule('#nested', { + basedir: path.resolve(importsRoot, './nested-import/index.cjs'), + conditions: ['node', 'require'], + }); + + expect(result).toEqual( + path.resolve(importsRoot, './nested-import/node.cjs'), + ); + }); + + describe('supports array pattern', () => { + test('resolve to first found', () => { + const result = Resolver.findNodeModule('#array-import', { + basedir: path.resolve(importsRoot, './array-import/index.cjs'), + conditions: ['import'], + }); + + expect(result).toEqual( + path.resolve(importsRoot, './array-import/node.mjs'), + ); + }); + + test('skip over not met nested condition', () => { + const result = Resolver.findNodeModule('#array-import', { + basedir: path.resolve(importsRoot, './array-import/index.cjs'), + conditions: ['browser'], + }); + + expect(result).toEqual( + path.resolve(importsRoot, './array-import/browser.cjs'), + ); + }); + + test('match nested condition', () => { + const result = Resolver.findNodeModule('#array-import', { + basedir: path.resolve(importsRoot, './array-import/index.cjs'), + conditions: ['chrome', 'browser'], + }); + + expect(result).toEqual( + path.resolve(importsRoot, './array-import/chrome.cjs'), + ); + }); + }); + test('fails for non-existent mapping', () => { expect(() => { Resolver.findNodeModule('#something-else', { basedir: path.resolve(importsRoot, './foo-import/index.js'), conditions: [], }); - }).toThrow('Missing "#something-else" import in "foo-import" package'); + }).toThrow( + expect.objectContaining({ + code: 'ERR_PACKAGE_IMPORT_NOT_DEFINED', + message: expect.stringMatching( + /^Package import specifier "#something-else" is not defined in package/, + ), + }), + ); }); }); }); diff --git a/packages/jest-resolve/src/defaultResolver.ts b/packages/jest-resolve/src/defaultResolver.ts index bffa2ad9177f..ad231802bda7 100644 --- a/packages/jest-resolve/src/defaultResolver.ts +++ b/packages/jest-resolve/src/defaultResolver.ts @@ -6,13 +6,13 @@ */ import {dirname, isAbsolute, resolve as pathResolve} from 'path'; -import {resolve as resolveImports} from '@okikio/resolve.imports'; import pnpResolver from 'jest-pnp-resolver'; import {SyncOpts as UpstreamResolveOptions, sync as resolveSync} from 'resolve'; import { Options as ResolveExportsOptions, resolve as resolveExports, } from 'resolve.exports'; +import {resolve as resolveImports} from 'resolve.imports'; import { findClosestPackageJson, isDirectory, @@ -149,9 +149,13 @@ function getPathInModule( const pkg = readPackageCached(closestPackageJson); const resolved = resolveImports( - pkg, + { + base: options.basedir, + content: pkg, + path: dirname(closestPackageJson), + }, path, - createResolveOptions(options.conditions), + createImportsResolveOptions(options.conditions), ); if (!resolved) { @@ -244,6 +248,14 @@ function createResolveOptions( {browser: false, require: true}; } +function createImportsResolveOptions(conditions: Array | undefined) { + return { + conditions: conditions + ? [...conditions, 'default'] + : ['node', 'require', 'default'], + }; +} + // if it's a relative import or an absolute path, imports/exports are ignored const shouldIgnoreRequestForExports = (path: string) => path.startsWith('.') || isAbsolute(path); diff --git a/yarn.lock b/yarn.lock index 8bfec085f959..eea8202bf9a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3721,13 +3721,6 @@ __metadata: languageName: node linkType: hard -"@okikio/resolve.imports@npm:^1.0.0": - version: 1.0.0 - resolution: "@okikio/resolve.imports@npm:1.0.0" - checksum: a06d347731b3c47e79125d346dd0172fda3cf20d138249f4ed23c5cc60b32f668f9bbab49698fc061ef5782d236622ff4cd0ce2b2520550273e863f55b687350 - languageName: node - linkType: hard - "@pkgr/utils@npm:^2.3.1": version: 2.3.1 resolution: "@pkgr/utils@npm:2.3.1" @@ -12891,7 +12884,6 @@ __metadata: version: 0.0.0-use.local resolution: "jest-resolve@workspace:packages/jest-resolve" dependencies: - "@okikio/resolve.imports": ^1.0.0 "@tsd/typescript": ^4.9.0 "@types/graceful-fs": ^4.1.3 "@types/pnpapi": ^0.0.2 @@ -12904,6 +12896,7 @@ __metadata: jest-validate: "workspace:^" resolve: ^1.20.0 resolve.exports: ^1.1.0 + resolve.imports: ^2.0.3 slash: ^3.0.0 tsd-lite: ^0.6.0 languageName: unknown @@ -16422,6 +16415,13 @@ __metadata: languageName: node linkType: hard +"pattern-key-compare@npm:^2.0.0": + version: 2.0.0 + resolution: "pattern-key-compare@npm:2.0.0" + checksum: cded15070cddbc5ef7b97c1b91371bcf59ff931f8afe383d2323f935891e6c4517bd76154f5edc2b1ab53c6fbeeb0711b9db43af592064482d0a9ccf03dd507c + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -18366,6 +18366,15 @@ __metadata: languageName: node linkType: hard +"resolve.imports@npm:^2.0.3": + version: 2.0.3 + resolution: "resolve.imports@npm:2.0.3" + dependencies: + pattern-key-compare: ^2.0.0 + checksum: 155ae4a32ccc1da7ad12b88f6c748dd1e038092838e537de1066248c32a197b42489810da6070bb2bd0dd954e8abbe841b09282c0806c5493b2e49e8f7e29632 + languageName: node + linkType: hard + "resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.3.2": version: 1.22.1 resolution: "resolve@npm:1.22.1"