@@ -9,30 +9,36 @@ import type { Importer, ImportPath } from './index.js';
99import type FileState from '../FileState.js' ;
1010import { resolveObjectPatternPropertyToValue } from '../utils/index.js' ;
1111
12+ // These extensions are sorted by priority
13+ // resolve() will check for files in the order these extensions are sorted
1214const RESOLVE_EXTENSIONS = [
1315 '.js' ,
14- '.jsx' ,
15- '.cjs' ,
16- '.mjs' ,
1716 '.ts' ,
1817 '.tsx' ,
18+ '.mjs' ,
19+ '.cjs' ,
1920 '.mts' ,
2021 '.cts' ,
22+ '.jsx' ,
2123] ;
2224
2325function 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+
2433 try {
25- return resolve . sync ( filename , {
26- basedir,
27- extensions : RESOLVE_EXTENSIONS ,
28- } ) ;
34+ return resolve . sync ( filename , resolveOptions ) ;
2935 } catch ( error ) {
3036 const ext = extname ( filename ) ;
3137 let newFilename : string ;
3238
3339 // if we try to import a JavaScript file it might be that we are actually pointing to
3440 // 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
3642 // https://www.typescriptlang.org/docs/handbook/esm-node.html#type-in-packagejson-and-new-extensions
3743 switch ( ext ) {
3844 case '.js' :
@@ -49,8 +55,9 @@ function defaultLookupModule(filename: string, basedir: string): string {
4955 }
5056
5157 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 : [ ] ,
5461 } ) ;
5562 }
5663}
@@ -62,13 +69,23 @@ interface TraverseState {
6269 resultPath ?: NodePath | null ;
6370}
6471
72+ interface FsImporterCache {
73+ parseCache : Map < string , FileState > ;
74+ resolveCache : Map < string , string | null > ;
75+ }
76+
6577// 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.
6680export default function makeFsImporter (
6781 lookupModule : (
6882 filename : string ,
6983 basedir : string ,
7084 ) => string = defaultLookupModule ,
71- cache : Map < string , FileState > = new Map ( ) ,
85+ { parseCache, resolveCache } : FsImporterCache = {
86+ parseCache : new Map ( ) ,
87+ resolveCache : new Map ( ) ,
88+ } ,
7289) : Importer {
7390 function resolveImportedValue (
7491 path : ImportPath ,
@@ -87,36 +104,48 @@ export default function makeFsImporter(
87104
88105 // Resolve the imported module using the Node resolver
89106 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 ;
91121
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 ) ;
96124
97- if ( code === 'MODULE_NOT_FOUND' || code === 'INVALID_PACKAGE_MAIN' ) {
98- return null ;
125+ return null ;
126+ }
127+
128+ throw error ;
99129 }
100130
101- throw error ;
131+ resolveCache . set ( resolveCacheKey , resolvedSource ) ;
102132 }
103-
104133 // Prevent recursive imports
105134 if ( seen . has ( resolvedSource ) ) {
106135 return null ;
107136 }
108137
109138 seen . add ( resolvedSource ) ;
110139
111- let nextFile = cache . get ( resolvedSource ) ;
140+ let nextFile = parseCache . get ( resolvedSource ) ;
112141
113142 if ( ! nextFile ) {
114143 // Read and parse the code
115144 const src = fs . readFileSync ( resolvedSource , 'utf8' ) ;
116145
117146 nextFile = file . parse ( src , resolvedSource ) ;
118147
119- cache . set ( resolvedSource , nextFile ) ;
148+ parseCache . set ( resolvedSource , nextFile ) ;
120149 }
121150
122151 return findExportedValue ( nextFile , name , seen ) ;
0 commit comments