@@ -9,30 +9,36 @@ import type { Importer, ImportPath } from './index.js';
9
9
import type FileState from '../FileState.js' ;
10
10
import { resolveObjectPatternPropertyToValue } from '../utils/index.js' ;
11
11
12
+ // These extensions are sorted by priority
13
+ // resolve() will check for files in the order these extensions are sorted
12
14
const RESOLVE_EXTENSIONS = [
13
15
'.js' ,
14
- '.jsx' ,
15
- '.cjs' ,
16
- '.mjs' ,
17
16
'.ts' ,
18
17
'.tsx' ,
18
+ '.mjs' ,
19
+ '.cjs' ,
19
20
'.mts' ,
20
21
'.cts' ,
22
+ '.jsx' ,
21
23
] ;
22
24
23
25
function defaultLookupModule ( filename : string , basedir : string ) : string {
26
+ const resolveOptions = {
27
+ basedir,
28
+ extensions : RESOLVE_EXTENSIONS ,
29
+ // we do not need to check core modules as we cannot import them anyway
30
+ includeCoreModules : false ,
31
+ } ;
32
+
24
33
try {
25
- return resolve . sync ( filename , {
26
- basedir,
27
- extensions : RESOLVE_EXTENSIONS ,
28
- } ) ;
34
+ return resolve . sync ( filename , resolveOptions ) ;
29
35
} catch ( error ) {
30
36
const ext = extname ( filename ) ;
31
37
let newFilename : string ;
32
38
33
39
// if we try to import a JavaScript file it might be that we are actually pointing to
34
40
// a TypeScript file. This can happen in ES modules as TypeScript requires to import other
35
- // TypeScript files with JavaScript extensions
41
+ // TypeScript files with .js extensions
36
42
// https://www.typescriptlang.org/docs/handbook/esm-node.html#type-in-packagejson-and-new-extensions
37
43
switch ( ext ) {
38
44
case '.js' :
@@ -49,8 +55,9 @@ function defaultLookupModule(filename: string, basedir: string): string {
49
55
}
50
56
51
57
return resolve . sync ( newFilename , {
52
- basedir,
53
- extensions : RESOLVE_EXTENSIONS ,
58
+ ...resolveOptions ,
59
+ // we already know that there is an extension at this point, so no need to check other extensions
60
+ extensions : [ ] ,
54
61
} ) ;
55
62
}
56
63
}
@@ -62,13 +69,23 @@ interface TraverseState {
62
69
resultPath ?: NodePath | null ;
63
70
}
64
71
72
+ interface FsImporterCache {
73
+ parseCache : Map < string , FileState > ;
74
+ resolveCache : Map < string , string | null > ;
75
+ }
76
+
65
77
// Factory for the resolveImports importer
78
+ // If this resolver is used in an environment where the source files change (e.g. watch)
79
+ // then the cache needs to be cleared on file changes.
66
80
export default function makeFsImporter (
67
81
lookupModule : (
68
82
filename : string ,
69
83
basedir : string ,
70
84
) => string = defaultLookupModule ,
71
- cache : Map < string , FileState > = new Map ( ) ,
85
+ { parseCache, resolveCache } : FsImporterCache = {
86
+ parseCache : new Map ( ) ,
87
+ resolveCache : new Map ( ) ,
88
+ } ,
72
89
) : Importer {
73
90
function resolveImportedValue (
74
91
path : ImportPath ,
@@ -87,36 +104,48 @@ export default function makeFsImporter(
87
104
88
105
// Resolve the imported module using the Node resolver
89
106
const basedir = dirname ( filename ) ;
90
- let resolvedSource : string | undefined ;
107
+ const resolveCacheKey = `${ basedir } |${ source } ` ;
108
+ let resolvedSource = resolveCache . get ( resolveCacheKey ) ;
109
+
110
+ // We haven't found it before, so no need to look again
111
+ if ( resolvedSource === null ) {
112
+ return null ;
113
+ }
114
+
115
+ // First time we try to resolve this file
116
+ if ( resolvedSource === undefined ) {
117
+ try {
118
+ resolvedSource = lookupModule ( source , basedir ) ;
119
+ } catch ( error ) {
120
+ const { code } = error as NodeJS . ErrnoException ;
91
121
92
- try {
93
- resolvedSource = lookupModule ( source , basedir ) ;
94
- } catch ( error ) {
95
- const { code } = error as NodeJS . ErrnoException ;
122
+ if ( code === 'MODULE_NOT_FOUND' || code === 'INVALID_PACKAGE_MAIN' ) {
123
+ resolveCache . set ( resolveCacheKey , null ) ;
96
124
97
- if ( code === 'MODULE_NOT_FOUND' || code === 'INVALID_PACKAGE_MAIN' ) {
98
- return null ;
125
+ return null ;
126
+ }
127
+
128
+ throw error ;
99
129
}
100
130
101
- throw error ;
131
+ resolveCache . set ( resolveCacheKey , resolvedSource ) ;
102
132
}
103
-
104
133
// Prevent recursive imports
105
134
if ( seen . has ( resolvedSource ) ) {
106
135
return null ;
107
136
}
108
137
109
138
seen . add ( resolvedSource ) ;
110
139
111
- let nextFile = cache . get ( resolvedSource ) ;
140
+ let nextFile = parseCache . get ( resolvedSource ) ;
112
141
113
142
if ( ! nextFile ) {
114
143
// Read and parse the code
115
144
const src = fs . readFileSync ( resolvedSource , 'utf8' ) ;
116
145
117
146
nextFile = file . parse ( src , resolvedSource ) ;
118
147
119
- cache . set ( resolvedSource , nextFile ) ;
148
+ parseCache . set ( resolvedSource , nextFile ) ;
120
149
}
121
150
122
151
return findExportedValue ( nextFile , name , seen ) ;
0 commit comments