Skip to content

Commit c7d496e

Browse files
committed
chore: migrate resolve and resolve.exports to unrs-resolver
close #15600
1 parent 321604a commit c7d496e

File tree

4 files changed

+103
-292
lines changed

4 files changed

+103
-292
lines changed

packages/jest-resolve/package.json

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,13 @@
2222
"chalk": "^4.0.0",
2323
"graceful-fs": "^4.2.9",
2424
"jest-haste-map": "workspace:*",
25-
"jest-pnp-resolver": "^1.2.2",
2625
"jest-util": "workspace:*",
2726
"jest-validate": "workspace:*",
28-
"resolve": "^1.20.0",
29-
"resolve.exports": "^2.0.0",
30-
"slash": "^3.0.0"
27+
"slash": "^3.0.0",
28+
"unrs-resolver": "^1.7.2"
3129
},
3230
"devDependencies": {
33-
"@types/graceful-fs": "^4.1.3",
34-
"@types/pnpapi": "^0.0.5",
35-
"@types/resolve": "^1.20.2"
31+
"@types/graceful-fs": "^4.1.3"
3632
},
3733
"engines": {
3834
"node": "^16.10.0 || ^18.12.0 || >=20.0.0"

packages/jest-resolve/src/defaultResolver.ts

Lines changed: 15 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,10 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import {dirname, isAbsolute, resolve as pathResolve} from 'path';
9-
import {fileURLToPath} from 'url';
10-
import pnpResolver from 'jest-pnp-resolver';
118
import {
12-
type SyncOpts as UpstreamResolveOptions,
13-
sync as resolveSync,
14-
} from 'resolve';
15-
import * as resolve from 'resolve.exports';
16-
import {
17-
findClosestPackageJson,
18-
isDirectory,
19-
isFile,
20-
readPackageCached,
21-
realpathSync,
22-
} from './fileWalkers';
9+
ResolverFactory,
10+
type NapiResolveOptions as UpstreamResolveOptions,
11+
} from 'unrs-resolver';
2312
import type {PackageJSON} from './types';
2413

2514
/**
@@ -52,15 +41,13 @@ export type PathFilter = (
5241
relativePath: string,
5342
) => string;
5443

55-
export type ResolverOptions = {
44+
export interface ResolverOptions extends UpstreamResolveOptions {
5645
/** Directory to begin resolving from. */
5746
basedir: string;
5847
/** List of export conditions. */
5948
conditions?: Array<string>;
6049
/** Instance of default resolver. */
6150
defaultResolver: typeof defaultResolver;
62-
/** List of file extensions to search in order. */
63-
extensions?: Array<string>;
6451
/**
6552
* List of directory names to be looked up for modules recursively.
6653
*
@@ -79,12 +66,7 @@ export type ResolverOptions = {
7966
packageFilter?: PackageFilter;
8067
/** Allows transforms a path within a package. */
8168
pathFilter?: PathFilter;
82-
/** Current root directory. */
83-
rootDir?: string;
84-
};
85-
86-
type UpstreamResolveOptionsWithConditions = UpstreamResolveOptions &
87-
ResolverOptions;
69+
}
8870

8971
export type SyncResolver = (path: string, options: ResolverOptions) => string;
9072
export type AsyncResolver = (
@@ -95,159 +77,21 @@ export type AsyncResolver = (
9577
export type Resolver = SyncResolver | AsyncResolver;
9678

9779
const defaultResolver: SyncResolver = (path, options) => {
98-
// Yarn 2 adds support to `resolve` automatically so the pnpResolver is only
99-
// needed for Yarn 1 which implements version 1 of the pnp spec
100-
if (process.versions.pnp === '1') {
101-
return pnpResolver(path, options);
102-
}
103-
104-
const resolveOptions: UpstreamResolveOptionsWithConditions = {
80+
const resolveOptions: UpstreamResolveOptions = {
10581
...options,
106-
isDirectory,
107-
isFile,
108-
preserveSymlinks: false,
109-
readPackageSync,
110-
realpathSync,
82+
conditionNames: options.conditionNames || options.conditions,
83+
modules: options.modules || options.moduleDirectory,
11184
};
11285

113-
const pathToResolve = getPathInModule(path, resolveOptions);
114-
115-
// resolveSync dereferences symlinks to ensure we don't create a separate
116-
// module instance depending on how it was referenced.
117-
const result = resolveSync(pathToResolve, resolveOptions);
118-
119-
return result;
120-
};
121-
122-
export default defaultResolver;
123-
124-
/*
125-
* helper functions
126-
*/
127-
128-
function readPackageSync(_: unknown, file: string): PackageJSON {
129-
return readPackageCached(file);
130-
}
86+
const unrsResolver = new ResolverFactory(resolveOptions);
13187

132-
function getPathInModule(
133-
path: string,
134-
options: UpstreamResolveOptionsWithConditions,
135-
): string {
136-
if (path.startsWith('file://')) {
137-
path = fileURLToPath(path);
138-
}
88+
const result = unrsResolver.sync(options.basedir, path);
13989

140-
if (shouldIgnoreRequestForExports(path)) {
141-
return path;
90+
if (result.error) {
91+
throw new Error(result.error);
14292
}
14393

144-
if (path.startsWith('#')) {
145-
const closestPackageJson = findClosestPackageJson(options.basedir);
146-
147-
if (!closestPackageJson) {
148-
throw new Error(
149-
`Jest: unable to locate closest package.json from ${options.basedir} when resolving import "${path}"`,
150-
);
151-
}
152-
153-
const pkg = readPackageCached(closestPackageJson);
154-
155-
const resolved = resolve.imports(
156-
pkg,
157-
path as resolve.Imports.Entry,
158-
createResolveOptions(options.conditions),
159-
);
160-
161-
if (resolved) {
162-
const target = resolved[0];
163-
return target.startsWith('.')
164-
? // internal relative filepath
165-
pathResolve(dirname(closestPackageJson), target)
166-
: // this is an external module, re-resolve it
167-
defaultResolver(target, options);
168-
}
169-
170-
if (pkg.imports) {
171-
throw new Error(
172-
'`imports` exists, but no results - this is a bug in Jest. Please report an issue',
173-
);
174-
}
175-
}
176-
177-
const segments = path.split('/');
178-
179-
let moduleName = segments.shift();
180-
181-
if (moduleName) {
182-
if (moduleName.startsWith('@')) {
183-
moduleName = `${moduleName}/${segments.shift()}`;
184-
}
185-
186-
// self-reference
187-
const closestPackageJson = findClosestPackageJson(options.basedir);
188-
if (closestPackageJson) {
189-
const pkg = readPackageCached(closestPackageJson);
190-
191-
if (pkg.name === moduleName) {
192-
const resolved = resolve.exports(
193-
pkg,
194-
(segments.join('/') || '.') as resolve.Exports.Entry,
195-
createResolveOptions(options.conditions),
196-
);
197-
198-
if (resolved) {
199-
return pathResolve(dirname(closestPackageJson), resolved[0]);
200-
}
201-
202-
if (pkg.exports) {
203-
throw new Error(
204-
'`exports` exists, but no results - this is a bug in Jest. Please report an issue',
205-
);
206-
}
207-
}
208-
}
209-
210-
let packageJsonPath = '';
211-
212-
try {
213-
packageJsonPath = resolveSync(`${moduleName}/package.json`, options);
214-
} catch {
215-
// ignore if package.json cannot be found
216-
}
217-
218-
if (packageJsonPath && isFile(packageJsonPath)) {
219-
const pkg = readPackageCached(packageJsonPath);
220-
221-
const resolved = resolve.exports(
222-
pkg,
223-
(segments.join('/') || '.') as resolve.Exports.Entry,
224-
createResolveOptions(options.conditions),
225-
);
226-
227-
if (resolved) {
228-
return pathResolve(dirname(packageJsonPath), resolved[0]);
229-
}
230-
231-
if (pkg.exports) {
232-
throw new Error(
233-
'`exports` exists, but no results - this is a bug in Jest. Please report an issue',
234-
);
235-
}
236-
}
237-
}
238-
239-
return path;
240-
}
241-
242-
function createResolveOptions(
243-
conditions: Array<string> | undefined,
244-
): resolve.Options {
245-
return conditions
246-
? {conditions, unsafe: true}
247-
: // no conditions were passed - let's assume this is Jest internal and it should be `require`
248-
{browser: false, require: true};
249-
}
94+
return result.path!;
95+
};
25096

251-
// if it's a relative import or an absolute path, imports/exports are ignored
252-
const shouldIgnoreRequestForExports = (path: string) =>
253-
path.startsWith('.') || isAbsolute(path);
97+
export default defaultResolver;

packages/jest-resolve/src/resolver.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ export default class Resolver {
126126
extensions: options.extensions,
127127
moduleDirectory: options.moduleDirectory,
128128
paths: paths ? [...(nodePaths || []), ...paths] : nodePaths,
129-
rootDir: options.rootDir,
130129
});
131130
} catch (error) {
132131
// we always wanna throw if it's an internal import
@@ -169,7 +168,6 @@ export default class Resolver {
169168
extensions: options.extensions,
170169
moduleDirectory: options.moduleDirectory,
171170
paths: paths ? [...(nodePaths || []), ...paths] : nodePaths,
172-
rootDir: options.rootDir,
173171
});
174172
return result;
175173
} catch (error: unknown) {

0 commit comments

Comments
 (0)