@@ -111,7 +111,10 @@ let cachedOptions: InternalResolverOptions | undefined
111111let prevCwd : string
112112
113113let mappersCachedOptions : InternalResolverOptions
114- let mappers : Array < ( ( specifier : string ) => string [ ] ) | null > | undefined
114+ let mappers : Array < {
115+ files : Set < string >
116+ mapperFn : NonNullable < ReturnType < typeof createPathsMatcher > >
117+ } > = [ ]
115118
116119let resolverCachedOptions : InternalResolverOptions
117120let cachedResolver : Resolver | undefined
@@ -159,7 +162,7 @@ export function resolve(
159162 resolver = cachedResolver
160163 }
161164
162- log ( 'looking for: ' , source )
165+ log ( 'looking for' , source , 'in' , file )
163166
164167 source = removeQuerystring ( source )
165168
@@ -300,34 +303,35 @@ function getMappedPath(
300303 paths = [ resolved ]
301304 }
302305 } else {
303- paths = mappers !
304- . map ( mapper =>
305- mapper ?.( source ) . map ( item => [
306- ...extensions . map ( ext => `${ item } ${ ext } ` ) ,
307- ...originalExtensions . map ( ext => `${ item } /index${ ext } ` ) ,
308- ] ) ,
309- )
310- . flat ( 2 )
311- . filter ( mappedPath => {
312- if ( mappedPath === undefined ) {
313- return false
314- }
315-
316- try {
317- const stat = fs . statSync ( mappedPath , { throwIfNoEntry : false } )
318- if ( stat === undefined ) return false
319- if ( stat . isFile ( ) ) return true
320-
321- // Maybe this is a module dir?
322- if ( stat . isDirectory ( ) ) {
323- return isModule ( mappedPath )
324- }
325- } catch {
326- return false
306+ paths = [
307+ ...new Set (
308+ mappers
309+ . filter ( ( { files } ) => files . has ( file ) )
310+ . map ( ( { mapperFn } ) =>
311+ mapperFn ( source ) . map ( item => [
312+ ...extensions . map ( ext => `${ item } ${ ext } ` ) ,
313+ ...originalExtensions . map ( ext => `${ item } /index${ ext } ` ) ,
314+ ] ) ,
315+ )
316+ . flat ( 2 )
317+ . map ( toNativePathSeparator ) ,
318+ ) ,
319+ ] . filter ( mappedPath => {
320+ try {
321+ const stat = fs . statSync ( mappedPath , { throwIfNoEntry : false } )
322+ if ( stat === undefined ) return false
323+ if ( stat . isFile ( ) ) return true
324+
325+ // Maybe this is a module dir?
326+ if ( stat . isDirectory ( ) ) {
327+ return isModule ( mappedPath )
327328 }
328-
329+ } catch {
329330 return false
330- } )
331+ }
332+
333+ return false
334+ } )
331335 }
332336
333337 if ( retry && paths . length === 0 ) {
@@ -367,50 +371,114 @@ function getMappedPath(
367371 return paths [ 0 ]
368372}
369373
374+ // eslint-disable-next-line sonarjs/cognitive-complexity
370375function initMappers ( options : InternalResolverOptions ) {
371376 if (
372- mappers &&
377+ mappers . length > 0 &&
373378 mappersCachedOptions === options &&
374379 prevCwd === process . cwd ( )
375380 ) {
376381 return
377382 }
378383 prevCwd = process . cwd ( )
379384
380- const configPaths =
385+ const configPaths = (
381386 typeof options . project === 'string'
382387 ? [ options . project ]
383388 : Array . isArray ( options . project )
384389 ? options . project
385390 : [ process . cwd ( ) ]
391+ ) // 'tinyglobby' pattern must have POSIX separator
392+ . map ( config => replacePathSeparator ( config , path . sep , path . posix . sep ) )
386393
387- const ignore = [ '!**/node_modules/**' ]
394+ // https://github.com/microsoft/TypeScript/blob/df342b7206cb56b56bb3b3aecbb2ee2d2ff7b217/src/compiler/commandLineParser.ts#L3006
395+ const defaultInclude = [ '**/*' ]
396+ const defaultIgnore = [ '**/node_modules/**' ]
388397
389- // turn glob patterns into paths
398+ // Turn glob patterns into paths
390399 const projectPaths = [
391400 ...new Set ( [
392401 ...configPaths . filter ( path => ! isDynamicPattern ( path ) ) ,
393402 ...globSync (
394- [ ... configPaths . filter ( path => isDynamicPattern ( path ) ) , ... ignore ] ,
403+ configPaths . filter ( path => isDynamicPattern ( path ) ) ,
395404 {
396405 expandDirectories : false ,
406+ ignore : defaultIgnore ,
407+ absolute : true ,
397408 } ,
398409 ) ,
399410 ] ) ,
400411 ]
401412
402- mappers = projectPaths . map ( projectPath => {
403- let tsconfigResult : TsConfigResult | null
413+ mappers = projectPaths
414+ . map ( projectPath => {
415+ let tsconfigResult : TsConfigResult | null
404416
405- if ( isFile ( projectPath ) ) {
406- const { dir, base } = path . parse ( projectPath )
407- tsconfigResult = getTsconfig ( dir , base )
408- } else {
409- tsconfigResult = getTsconfig ( projectPath )
410- }
417+ if ( isFile ( projectPath ) ) {
418+ const { dir, base } = path . parse ( projectPath )
419+ tsconfigResult = getTsconfig ( dir , base )
420+ } else {
421+ tsconfigResult = getTsconfig ( projectPath )
422+ }
411423
412- return tsconfigResult && createPathsMatcher ( tsconfigResult )
413- } )
424+ if ( ! tsconfigResult ) {
425+ // eslint-disable-next-line unicorn/no-useless-undefined
426+ return undefined
427+ }
428+
429+ const mapperFn = createPathsMatcher ( tsconfigResult )
430+
431+ if ( ! mapperFn ) {
432+ // eslint-disable-next-line unicorn/no-useless-undefined
433+ return undefined
434+ }
435+
436+ const files =
437+ tsconfigResult . config . files === undefined &&
438+ tsconfigResult . config . include === undefined
439+ ? // Include everything if no files or include options
440+ globSync ( defaultInclude , {
441+ ignore : [
442+ ...( tsconfigResult . config . exclude ?? [ ] ) ,
443+ ...defaultIgnore ,
444+ ] ,
445+ absolute : true ,
446+ cwd : path . dirname ( tsconfigResult . path ) ,
447+ } )
448+ : [
449+ // https://www.typescriptlang.org/tsconfig/#files
450+ ...( tsconfigResult . config . files !== undefined &&
451+ tsconfigResult . config . files . length > 0
452+ ? tsconfigResult . config . files . map ( file =>
453+ path . normalize (
454+ path . resolve ( path . dirname ( tsconfigResult ! . path ) , file ) ,
455+ ) ,
456+ )
457+ : [ ] ) ,
458+ // https://www.typescriptlang.org/tsconfig/#include
459+ ...( tsconfigResult . config . include !== undefined &&
460+ tsconfigResult . config . include . length > 0
461+ ? globSync ( tsconfigResult . config . include , {
462+ ignore : [
463+ ...( tsconfigResult . config . exclude ?? [ ] ) ,
464+ ...defaultIgnore ,
465+ ] ,
466+ absolute : true ,
467+ } )
468+ : [ ] ) ,
469+ ]
470+
471+ if ( files . length === 0 ) {
472+ // eslint-disable-next-line unicorn/no-useless-undefined
473+ return undefined
474+ }
475+
476+ return {
477+ files : new Set ( files . map ( toNativePathSeparator ) ) ,
478+ mapperFn,
479+ }
480+ } )
481+ . filter ( isDefined )
414482
415483 mappersCachedOptions = options
416484}
@@ -427,3 +495,46 @@ function mangleScopedPackage(moduleName: string) {
427495 }
428496 return moduleName
429497}
498+
499+ /**
500+ * Replace path `p` from `from` to `to` separator.
501+ *
502+ * @param {string } p Path
503+ * @param {typeof path.sep } from From separator
504+ * @param {typeof path.sep } to To separator
505+ * @returns Path with `to` separator
506+ */
507+ function replacePathSeparator (
508+ p : string ,
509+ from : typeof path . sep ,
510+ to : typeof path . sep ,
511+ ) {
512+ return from === to ? p : p . replaceAll ( from , to )
513+ }
514+
515+ /**
516+ * Replace path `p` separator to its native separator.
517+ *
518+ * @param {string } p Path
519+ * @returns Path with native separator
520+ */
521+ function toNativePathSeparator ( p : string ) {
522+ return replacePathSeparator (
523+ p ,
524+ path [ process . platform === 'win32' ? 'posix' : 'win32' ] . sep ,
525+ path [ process . platform === 'win32' ? 'win32' : 'posix' ] . sep ,
526+ )
527+ }
528+
529+ /**
530+ * Check if value is defined.
531+ *
532+ * Helper function for TypeScript.
533+ * Should be removed when upgrading to TypeScript >= 5.5.
534+ *
535+ * @param {T | null | undefined } value Value
536+ * @returns `true` if value is defined, `false` otherwise
537+ */
538+ function isDefined < T > ( value : T | null | undefined ) : value is T {
539+ return value !== null && value !== undefined
540+ }
0 commit comments