@@ -83,6 +83,7 @@ import {
83
83
mapDefined ,
84
84
MapLike ,
85
85
matchPatternOrExact ,
86
+ memoizeOne ,
86
87
min ,
87
88
ModuleDeclaration ,
88
89
ModuleKind ,
@@ -127,6 +128,34 @@ import {
127
128
UserPreferences ,
128
129
} from "./_namespaces/ts.js" ;
129
130
131
+ const stringToRegex = memoizeOne ( ( pattern : string ) => {
132
+ try {
133
+ let slash = pattern . indexOf ( "/" ) ;
134
+ if ( slash !== 0 ) {
135
+ // No leading slash, treat as a pattern
136
+ return new RegExp ( pattern ) ;
137
+ }
138
+ const lastSlash = pattern . lastIndexOf ( "/" ) ;
139
+ if ( slash === lastSlash ) {
140
+ // Only one slash, treat as a pattern
141
+ return new RegExp ( pattern ) ;
142
+ }
143
+ while ( ( slash = pattern . indexOf ( "/" , slash + 1 ) ) !== lastSlash ) {
144
+ if ( pattern [ slash - 1 ] !== "\\" ) {
145
+ // Unescaped middle slash, treat as a pattern
146
+ return new RegExp ( pattern ) ;
147
+ }
148
+ }
149
+ // Only case-insensitive and unicode flags make sense
150
+ const flags = pattern . substring ( lastSlash + 1 ) . replace ( / [ ^ i u ] / g, "" ) ;
151
+ pattern = pattern . substring ( 1 , lastSlash ) ;
152
+ return new RegExp ( pattern , flags ) ;
153
+ }
154
+ catch {
155
+ return undefined ;
156
+ }
157
+ } ) ;
158
+
130
159
// Used by importFixes, getEditsForFileRename, and declaration emit to synthesize import module specifiers.
131
160
132
161
/** @internal */
@@ -144,18 +173,20 @@ export interface ModuleSpecifierPreferences {
144
173
* @param syntaxImpliedNodeFormat Used when the import syntax implies ESM or CJS irrespective of the mode of the file.
145
174
*/
146
175
getAllowedEndingsInPreferredOrder ( syntaxImpliedNodeFormat ?: ResolutionMode ) : ModuleSpecifierEnding [ ] ;
176
+ readonly excludeRegexes ?: readonly string [ ] ;
147
177
}
148
178
149
179
/** @internal */
150
180
export function getModuleSpecifierPreferences (
151
- { importModuleSpecifierPreference, importModuleSpecifierEnding } : UserPreferences ,
181
+ { importModuleSpecifierPreference, importModuleSpecifierEnding, autoImportSpecifierExcludeRegexes } : UserPreferences ,
152
182
host : Pick < ModuleSpecifierResolutionHost , "getDefaultResolutionModeForFile" > ,
153
183
compilerOptions : CompilerOptions ,
154
184
importingSourceFile : Pick < SourceFile , "fileName" | "impliedNodeFormat" > ,
155
185
oldImportSpecifier ?: string ,
156
186
) : ModuleSpecifierPreferences {
157
187
const filePreferredEnding = getPreferredEnding ( ) ;
158
188
return {
189
+ excludeRegexes : autoImportSpecifierExcludeRegexes ,
159
190
relativePreference : oldImportSpecifier !== undefined ? ( isExternalModuleNameRelative ( oldImportSpecifier ) ?
160
191
RelativePreference . Relative :
161
192
RelativePreference . NonRelative ) :
@@ -362,7 +393,13 @@ export function getModuleSpecifiersWithCacheInfo(
362
393
) : ModuleSpecifierResult {
363
394
let computedWithoutCache = false ;
364
395
const ambient = tryGetModuleNameFromAmbientModule ( moduleSymbol , checker ) ;
365
- if ( ambient ) return { kind : "ambient" , moduleSpecifiers : [ ambient ] , computedWithoutCache } ;
396
+ if ( ambient ) {
397
+ return {
398
+ kind : "ambient" ,
399
+ moduleSpecifiers : ! ( forAutoImport && isExcludedByRegex ( ambient , userPreferences . autoImportSpecifierExcludeRegexes ) ) ? [ ambient ] : emptyArray ,
400
+ computedWithoutCache,
401
+ } ;
402
+ }
366
403
367
404
// eslint-disable-next-line prefer-const
368
405
let [ kind , specifiers , moduleSourceFile , modulePaths , cache ] = tryGetModuleSpecifiersFromCacheWorker (
@@ -459,11 +496,13 @@ function computeModuleSpecifiers(
459
496
const specifier = modulePath . isInNodeModules
460
497
? tryGetModuleNameAsNodeModule ( modulePath , info , importingSourceFile , host , compilerOptions , userPreferences , /*packageNameOnly*/ undefined , options . overrideImportMode )
461
498
: undefined ;
462
- nodeModulesSpecifiers = append ( nodeModulesSpecifiers , specifier ) ;
463
- if ( specifier && modulePath . isRedirect ) {
464
- // If we got a specifier for a redirect, it was a bare package specifier (e.g. "@foo/bar",
465
- // not "@foo/bar/path/to/file"). No other specifier will be this good, so stop looking.
466
- return { kind : "node_modules" , moduleSpecifiers : nodeModulesSpecifiers ! , computedWithoutCache : true } ;
499
+ if ( specifier && ! ( forAutoImport && isExcludedByRegex ( specifier , preferences . excludeRegexes ) ) ) {
500
+ nodeModulesSpecifiers = append ( nodeModulesSpecifiers , specifier ) ;
501
+ if ( modulePath . isRedirect ) {
502
+ // If we got a specifier for a redirect, it was a bare package specifier (e.g. "@foo/bar",
503
+ // not "@foo/bar/path/to/file"). No other specifier will be this good, so stop looking.
504
+ return { kind : "node_modules" , moduleSpecifiers : nodeModulesSpecifiers , computedWithoutCache : true } ;
505
+ }
467
506
}
468
507
469
508
if ( ! specifier ) {
@@ -476,7 +515,7 @@ function computeModuleSpecifiers(
476
515
preferences ,
477
516
/*pathsOnly*/ modulePath . isRedirect ,
478
517
) ;
479
- if ( ! local ) {
518
+ if ( ! local || forAutoImport && isExcludedByRegex ( local , preferences . excludeRegexes ) ) {
480
519
continue ;
481
520
}
482
521
if ( modulePath . isRedirect ) {
@@ -512,7 +551,11 @@ function computeModuleSpecifiers(
512
551
return pathsSpecifiers ?. length ? { kind : "paths" , moduleSpecifiers : pathsSpecifiers , computedWithoutCache : true } :
513
552
redirectPathsSpecifiers ?. length ? { kind : "redirect" , moduleSpecifiers : redirectPathsSpecifiers , computedWithoutCache : true } :
514
553
nodeModulesSpecifiers ?. length ? { kind : "node_modules" , moduleSpecifiers : nodeModulesSpecifiers , computedWithoutCache : true } :
515
- { kind : "relative" , moduleSpecifiers : Debug . checkDefined ( relativeSpecifiers ) , computedWithoutCache : true } ;
554
+ { kind : "relative" , moduleSpecifiers : relativeSpecifiers ?? emptyArray , computedWithoutCache : true } ;
555
+ }
556
+
557
+ function isExcludedByRegex ( moduleSpecifier : string , excludeRegexes : readonly string [ ] | undefined ) : boolean {
558
+ return some ( excludeRegexes , pattern => ! ! stringToRegex ( pattern ) ?. test ( moduleSpecifier ) ) ;
516
559
}
517
560
518
561
interface Info {
@@ -536,7 +579,7 @@ function getInfo(importingSourceFileName: string, host: ModuleSpecifierResolutio
536
579
537
580
function getLocalModuleSpecifier ( moduleFileName : string , info : Info , compilerOptions : CompilerOptions , host : ModuleSpecifierResolutionHost , importMode : ResolutionMode , preferences : ModuleSpecifierPreferences ) : string ;
538
581
function getLocalModuleSpecifier ( moduleFileName : string , info : Info , compilerOptions : CompilerOptions , host : ModuleSpecifierResolutionHost , importMode : ResolutionMode , preferences : ModuleSpecifierPreferences , pathsOnly ?: boolean ) : string | undefined ;
539
- function getLocalModuleSpecifier ( moduleFileName : string , info : Info , compilerOptions : CompilerOptions , host : ModuleSpecifierResolutionHost , importMode : ResolutionMode , { getAllowedEndingsInPreferredOrder : getAllowedEndingsInPrefererredOrder , relativePreference } : ModuleSpecifierPreferences , pathsOnly ?: boolean ) : string | undefined {
582
+ function getLocalModuleSpecifier ( moduleFileName : string , info : Info , compilerOptions : CompilerOptions , host : ModuleSpecifierResolutionHost , importMode : ResolutionMode , { getAllowedEndingsInPreferredOrder : getAllowedEndingsInPrefererredOrder , relativePreference, excludeRegexes } : ModuleSpecifierPreferences , pathsOnly ?: boolean ) : string | undefined {
540
583
const { baseUrl, paths, rootDirs } = compilerOptions ;
541
584
if ( pathsOnly && ! paths ) {
542
585
return undefined ;
@@ -568,6 +611,15 @@ function getLocalModuleSpecifier(moduleFileName: string, info: Info, compilerOpt
568
611
return relativePath ;
569
612
}
570
613
614
+ const relativeIsExcluded = isExcludedByRegex ( relativePath , excludeRegexes ) ;
615
+ const nonRelativeIsExcluded = isExcludedByRegex ( maybeNonRelative , excludeRegexes ) ;
616
+ if ( ! relativeIsExcluded && nonRelativeIsExcluded ) {
617
+ return relativePath ;
618
+ }
619
+ if ( relativeIsExcluded && ! nonRelativeIsExcluded ) {
620
+ return maybeNonRelative ;
621
+ }
622
+
571
623
if ( relativePreference === RelativePreference . NonRelative && ! pathIsRelative ( maybeNonRelative ) ) {
572
624
return maybeNonRelative ;
573
625
}
0 commit comments