@@ -95,7 +95,7 @@ namespace ts {
95
95
return compilerOptions . traceResolution && host . trace !== undefined ;
96
96
}
97
97
98
- function hasZeroOrOneAsteriskCharacter ( str : string ) : boolean {
98
+ export function hasZeroOrOneAsteriskCharacter ( str : string ) : boolean {
99
99
let seenAsterisk = false ;
100
100
for ( let i = 0 ; i < str . length ; i ++ ) {
101
101
if ( str . charCodeAt ( i ) === CharacterCodes . asterisk ) {
@@ -496,48 +496,23 @@ namespace ts {
496
496
trace ( state . host , Diagnostics . baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1 , state . compilerOptions . baseUrl , moduleName ) ;
497
497
}
498
498
499
- let longestMatchPrefixLength = - 1 ;
500
- let matchedPattern : string ;
501
- let matchedStar : string ;
502
-
499
+ // string is for exact match
500
+ let matchedPattern : Pattern | string | undefined = undefined ;
503
501
if ( state . compilerOptions . paths ) {
504
502
if ( state . traceEnabled ) {
505
503
trace ( state . host , Diagnostics . paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0 , moduleName ) ;
506
504
}
507
-
508
- for ( const key in state . compilerOptions . paths ) {
509
- const pattern : string = key ;
510
- const indexOfStar = pattern . indexOf ( "*" ) ;
511
- if ( indexOfStar !== - 1 ) {
512
- const prefix = pattern . substr ( 0 , indexOfStar ) ;
513
- const suffix = pattern . substr ( indexOfStar + 1 ) ;
514
- if ( moduleName . length >= prefix . length + suffix . length &&
515
- startsWith ( moduleName , prefix ) &&
516
- endsWith ( moduleName , suffix ) ) {
517
-
518
- // use length of prefix as betterness criteria
519
- if ( prefix . length > longestMatchPrefixLength ) {
520
- longestMatchPrefixLength = prefix . length ;
521
- matchedPattern = pattern ;
522
- matchedStar = moduleName . substr ( prefix . length , moduleName . length - suffix . length ) ;
523
- }
524
- }
525
- }
526
- else if ( pattern === moduleName ) {
527
- // pattern was matched as is - no need to search further
528
- matchedPattern = pattern ;
529
- matchedStar = undefined ;
530
- break ;
531
- }
532
- }
505
+ matchedPattern = matchPatternOrExact ( Object . keys ( state . compilerOptions . paths ) , moduleName ) ;
533
506
}
534
507
535
508
if ( matchedPattern ) {
509
+ const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText ( matchedPattern , moduleName ) ;
510
+ const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText ( matchedPattern ) ;
536
511
if ( state . traceEnabled ) {
537
- trace ( state . host , Diagnostics . Module_name_0_matched_pattern_1 , moduleName , matchedPattern ) ;
512
+ trace ( state . host , Diagnostics . Module_name_0_matched_pattern_1 , moduleName , matchedPatternText ) ;
538
513
}
539
- for ( const subst of state . compilerOptions . paths [ matchedPattern ] ) {
540
- const path = matchedStar ? subst . replace ( "\ *" , matchedStar ) : subst ;
514
+ for ( const subst of state . compilerOptions . paths [ matchedPatternText ] ) {
515
+ const path = matchedStar ? subst . replace ( "*" , matchedStar ) : subst ;
541
516
const candidate = normalizePath ( combinePaths ( state . compilerOptions . baseUrl , path ) ) ;
542
517
if ( state . traceEnabled ) {
543
518
trace ( state . host , Diagnostics . Trying_substitution_0_candidate_module_location_Colon_1 , subst , path ) ;
@@ -560,6 +535,73 @@ namespace ts {
560
535
}
561
536
}
562
537
538
+ /**
539
+ * patternStrings contains both pattern strings (containing "*") and regular strings.
540
+ * Return an exact match if possible, or a pattern match, or undefined.
541
+ * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
542
+ */
543
+ function matchPatternOrExact ( patternStrings : string [ ] , candidate : string ) : string | Pattern | undefined {
544
+ const patterns : Pattern [ ] = [ ] ;
545
+ for ( const patternString of patternStrings ) {
546
+ const pattern = tryParsePattern ( patternString ) ;
547
+ if ( pattern ) {
548
+ patterns . push ( pattern ) ;
549
+ }
550
+ else if ( patternString === candidate ) {
551
+ // pattern was matched as is - no need to search further
552
+ return patternString ;
553
+ }
554
+ }
555
+
556
+ return findBestPatternMatch ( patterns , _ => _ , candidate ) ;
557
+ }
558
+
559
+ function patternText ( { prefix, suffix} : Pattern ) : string {
560
+ return `${ prefix } *${ suffix } ` ;
561
+ }
562
+
563
+ /**
564
+ * Given that candidate matches pattern, returns the text matching the '*'.
565
+ * E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar"
566
+ */
567
+ function matchedText ( pattern : Pattern , candidate : string ) : string {
568
+ Debug . assert ( isPatternMatch ( pattern , candidate ) ) ;
569
+ return candidate . substr ( pattern . prefix . length , candidate . length - pattern . suffix . length ) ;
570
+ }
571
+
572
+ /** Return the object corresponding to the best pattern to match `candidate`. */
573
+ export function findBestPatternMatch < T > ( values : T [ ] , getPattern : ( value : T ) => Pattern , candidate : string ) : T | undefined {
574
+ let matchedValue : T | undefined = undefined ;
575
+ // use length of prefix as betterness criteria
576
+ let longestMatchPrefixLength = - 1 ;
577
+
578
+ for ( const v of values ) {
579
+ const pattern = getPattern ( v ) ;
580
+ if ( isPatternMatch ( pattern , candidate ) && pattern . prefix . length > longestMatchPrefixLength ) {
581
+ longestMatchPrefixLength = pattern . prefix . length ;
582
+ matchedValue = v ;
583
+ }
584
+ }
585
+
586
+ return matchedValue ;
587
+ }
588
+
589
+ function isPatternMatch ( { prefix, suffix} : Pattern , candidate : string ) {
590
+ return candidate . length >= prefix . length + suffix . length &&
591
+ startsWith ( candidate , prefix ) &&
592
+ endsWith ( candidate , suffix ) ;
593
+ }
594
+
595
+ export function tryParsePattern ( pattern : string ) : Pattern | undefined {
596
+ // This should be verified outside of here and a proper error thrown.
597
+ Debug . assert ( hasZeroOrOneAsteriskCharacter ( pattern ) ) ;
598
+ const indexOfStar = pattern . indexOf ( "*" ) ;
599
+ return indexOfStar === - 1 ? undefined : {
600
+ prefix : pattern . substr ( 0 , indexOfStar ) ,
601
+ suffix : pattern . substr ( indexOfStar + 1 )
602
+ } ;
603
+ }
604
+
563
605
export function nodeModuleNameResolver ( moduleName : string , containingFile : string , compilerOptions : CompilerOptions , host : ModuleResolutionHost ) : ResolvedModuleWithFailedLookupLocations {
564
606
const containingDirectory = getDirectoryPath ( containingFile ) ;
565
607
const supportedExtensions = getSupportedExtensions ( compilerOptions ) ;
0 commit comments