Skip to content

Commit 459a81d

Browse files
committed
WIP add async defaultResolver
1 parent d1882f2 commit 459a81d

File tree

2 files changed

+128
-12
lines changed

2 files changed

+128
-12
lines changed

packages/jest-resolve/src/defaultResolver.ts

Lines changed: 122 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@
77

88
import * as fs from 'graceful-fs';
99
import pnpResolver from 'jest-pnp-resolver';
10-
import {sync as resolveSync} from 'resolve';
10+
import {AsyncOpts, SyncOpts, sync as resolveSync} from 'resolve';
11+
import resolveAsync = require('resolve');
1112
import type {Config} from '@jest/types';
1213
import {tryRealpath} from 'jest-util';
14+
import type {PackageMeta} from './types';
1315

1416
type ResolverOptions = {
1517
basedir: Config.Path;
1618
browser?: boolean;
17-
defaultResolver: typeof defaultResolver;
19+
// QUESTION: Should it also be possible to pass a defaultResolverAsync?
20+
defaultResolver: typeof defaultResolverSync;
1821
extensions?: Array<string>;
1922
moduleDirectory?: Array<string>;
2023
paths?: Array<Config.Path>;
@@ -31,7 +34,7 @@ declare global {
3134
}
3235
}
3336

34-
export default function defaultResolver(
37+
export default function defaultResolverSync(
3538
path: Config.Path,
3639
options: ResolverOptions,
3740
): Config.Path {
@@ -41,22 +44,76 @@ export default function defaultResolver(
4144
return pnpResolver(path, options);
4245
}
4346

44-
const result = resolveSync(path, {
47+
const result = resolveSync(path, getSyncResolveOptions(options));
48+
49+
// Dereference symlinks to ensure we don't create a separate
50+
// module instance depending on how it was referenced.
51+
return realpathSync(result);
52+
}
53+
54+
export function defaultResolverAsync(
55+
path: Config.Path,
56+
options: ResolverOptions,
57+
): Promise<{path: Config.Path; meta?: PackageMeta}> {
58+
// Yarn 2 adds support to `resolve` automatically so the pnpResolver is only
59+
// needed for Yarn 1 which implements version 1 of the pnp spec
60+
if (process.versions.pnp === '1') {
61+
// QUESTION: do we need an async version of pnpResolver?
62+
return Promise.resolve({path: pnpResolver(path, options)});
63+
}
64+
65+
return new Promise((resolve, reject) => {
66+
function resolveCb(err: Error | null, result?: string, meta?: PackageMeta) {
67+
if (err) {
68+
reject(err);
69+
}
70+
if (result) {
71+
resolve({meta, path: realpathSync(result)});
72+
}
73+
}
74+
resolveAsync(path, getAsyncResolveOptions(options), resolveCb);
75+
});
76+
}
77+
78+
/**
79+
* getBaseResolveOptions returns resolution options that are shared by both the
80+
* synch and async resolution functions.
81+
*/
82+
function getBaseResolveOptions(options: ResolverOptions) {
83+
return {
4584
basedir: options.basedir,
4685
extensions: options.extensions,
47-
isDirectory,
48-
isFile,
4986
moduleDirectory: options.moduleDirectory,
5087
packageFilter: options.packageFilter,
5188
paths: options.paths,
5289
preserveSymlinks: false,
90+
};
91+
}
92+
93+
/**
94+
* getSyncResolveOptions returns resolution options that are used synchronously.
95+
*/
96+
function getSyncResolveOptions(options: ResolverOptions): SyncOpts {
97+
return {
98+
...getBaseResolveOptions(options),
99+
isDirectory: isDirectorySync,
100+
isFile: isFileSync,
53101
readPackageSync,
54102
realpathSync,
55-
});
103+
};
104+
}
56105

57-
// Dereference symlinks to ensure we don't create a separate
58-
// module instance depending on how it was referenced.
59-
return realpathSync(result);
106+
/**
107+
* getAsyncResolveOptions returns resolution options that are used asynchronously.
108+
*/
109+
function getAsyncResolveOptions(options: ResolverOptions): AsyncOpts {
110+
return {
111+
...getBaseResolveOptions(options),
112+
isDirectory: isDirectoryAsync,
113+
isFile: isFileAsync,
114+
readPackage: readPackageAsync,
115+
realpath: realpathAsync,
116+
};
60117
}
61118

62119
export function clearDefaultResolverCache(): void {
@@ -140,18 +197,71 @@ function readPackageCached(path: Config.Path): PkgJson {
140197
/*
141198
* helper functions
142199
*/
143-
function isFile(file: Config.Path): boolean {
200+
function isFileSync(file: Config.Path): boolean {
144201
return statSyncCached(file) === IPathType.FILE;
145202
}
146203

147-
function isDirectory(dir: Config.Path): boolean {
204+
function isFileAsync(
205+
file: Config.Path,
206+
cb: (err: Error | null, isFile?: boolean) => void,
207+
): void {
208+
try {
209+
// QUESTION: do we need an async version of statSyncCached?
210+
const isFile = statSyncCached(file) === IPathType.FILE;
211+
cb(null, isFile);
212+
} catch (err) {
213+
cb(err);
214+
}
215+
}
216+
217+
function isDirectorySync(dir: Config.Path): boolean {
148218
return statSyncCached(dir) === IPathType.DIRECTORY;
149219
}
150220

221+
function isDirectoryAsync(
222+
dir: Config.Path,
223+
cb: (err: Error | null, isDir?: boolean) => void,
224+
): void {
225+
try {
226+
// QUESTION: do we need an async version of statSyncCached?
227+
const isDir = statSyncCached(dir) === IPathType.DIRECTORY;
228+
cb(null, isDir);
229+
} catch (err) {
230+
cb(err);
231+
}
232+
}
233+
151234
function realpathSync(file: Config.Path): Config.Path {
152235
return realpathCached(file);
153236
}
154237

238+
function realpathAsync(
239+
file: string,
240+
cb: (err: Error | null, resolved?: string) => void,
241+
): void {
242+
try {
243+
// QUESTION: do we need an async version of realpathCached?
244+
const resolved = realpathCached(file);
245+
cb(null, resolved);
246+
} catch (err) {
247+
cb(err);
248+
}
249+
}
250+
155251
function readPackageSync(_: unknown, file: Config.Path): PkgJson {
156252
return readPackageCached(file);
157253
}
254+
255+
function readPackageAsync(
256+
_: unknown,
257+
pkgfile: string,
258+
cb: (err: Error | null, pkgJson?: Record<string, unknown>) => void,
259+
): void {
260+
try {
261+
// QUESTION: do we need an async version of readPackageCached?
262+
const pkgJson = readPackageCached(pkgfile);
263+
cb(null, pkgJson);
264+
} catch (err) {
265+
cb(err);
266+
}
267+
}

packages/jest-resolve/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@ type ModuleNameMapperConfig = {
2323
regex: RegExp;
2424
moduleName: string | Array<string>;
2525
};
26+
27+
export interface PackageMeta {
28+
name: string;
29+
version: string;
30+
[key: string]: any;
31+
}

0 commit comments

Comments
 (0)