77
88import * as fs from 'graceful-fs' ;
99import 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' ) ;
1112import type { Config } from '@jest/types' ;
1213import { tryRealpath } from 'jest-util' ;
14+ import type { PackageMeta } from './types' ;
1315
1416type 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
62119export 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+
151234function 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+
155251function 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+ }
0 commit comments