Skip to content

Commit 3ffa245

Browse files
authored
Cache parsed path mapping patterns (#44078)
* Cache parsed path mapping patterns If a project has many of them (e.g. 1800), parsing the patterns repeatedly can take up a lot of time. * Move cache to ConfigFileSpecs * Inline constants * Simplify cache access
1 parent ddd3cf9 commit 3ffa245

File tree

6 files changed

+43
-32
lines changed

6 files changed

+43
-32
lines changed

src/compiler/binder.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1977,19 +1977,17 @@ namespace ts {
19771977
declareModuleSymbol(node);
19781978
}
19791979
else {
1980-
let pattern: Pattern | undefined;
1980+
let pattern: string | Pattern | undefined;
19811981
if (node.name.kind === SyntaxKind.StringLiteral) {
19821982
const { text } = node.name;
1983-
if (hasZeroOrOneAsteriskCharacter(text)) {
1984-
pattern = tryParsePattern(text);
1985-
}
1986-
else {
1983+
pattern = tryParsePattern(text);
1984+
if (pattern === undefined) {
19871985
errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text);
19881986
}
19891987
}
19901988

19911989
const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes)!;
1992-
file.patternAmbientModules = append<PatternAmbientModule>(file.patternAmbientModules, pattern && { pattern, symbol });
1990+
file.patternAmbientModules = append<PatternAmbientModule>(file.patternAmbientModules, pattern && !isString(pattern) ? { pattern, symbol } : undefined);
19931991
}
19941992
}
19951993
else {

src/compiler/commandLineParser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,7 @@ namespace ts {
25332533
validatedFilesSpec: filter(filesSpecs, isString),
25342534
validatedIncludeSpecs,
25352535
validatedExcludeSpecs,
2536+
pathPatterns: undefined, // Initialized on first use
25362537
};
25372538
}
25382539

src/compiler/moduleNameResolver.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -952,7 +952,7 @@ namespace ts {
952952
}
953953

954954
function tryLoadModuleUsingPathsIfEligible(extensions: Extensions, moduleName: string, loader: ResolutionKindSpecificLoader, state: ModuleResolutionState) {
955-
const { baseUrl, paths } = state.compilerOptions;
955+
const { baseUrl, paths, configFile } = state.compilerOptions;
956956
if (paths && !pathIsRelative(moduleName)) {
957957
if (state.traceEnabled) {
958958
if (baseUrl) {
@@ -961,7 +961,8 @@ namespace ts {
961961
trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName);
962962
}
963963
const baseDirectory = getPathsBasePath(state.compilerOptions, state.host)!; // Always defined when 'paths' is defined
964-
return tryLoadModuleUsingPaths(extensions, moduleName, baseDirectory, paths, loader, /*onlyRecordFailures*/ false, state);
964+
const pathPatterns = configFile?.configFileSpecs ? configFile.configFileSpecs.pathPatterns ||= tryParsePatterns(paths) : undefined;
965+
return tryLoadModuleUsingPaths(extensions, moduleName, baseDirectory, paths, pathPatterns, loader, /*onlyRecordFailures*/ false, state);
965966
}
966967
}
967968

@@ -1400,7 +1401,7 @@ namespace ts {
14001401
if (state.traceEnabled) {
14011402
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, versionPaths.version, version, moduleName);
14021403
}
1403-
const result = tryLoadModuleUsingPaths(extensions, moduleName, candidate, versionPaths.paths, loader, onlyRecordFailuresForPackageFile || onlyRecordFailuresForIndex, state);
1404+
const result = tryLoadModuleUsingPaths(extensions, moduleName, candidate, versionPaths.paths, /*pathPatterns*/ undefined, loader, onlyRecordFailuresForPackageFile || onlyRecordFailuresForIndex, state);
14041405
if (result) {
14051406
return removeIgnoredPackageId(result.value);
14061407
}
@@ -1536,7 +1537,7 @@ namespace ts {
15361537
trace(state.host, Diagnostics.package_json_has_a_typesVersions_entry_0_that_matches_compiler_version_1_looking_for_a_pattern_to_match_module_name_2, packageInfo.versionPaths.version, version, rest);
15371538
}
15381539
const packageDirectoryExists = nodeModulesDirectoryExists && directoryProbablyExists(packageDirectory, state.host);
1539-
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, loader, !packageDirectoryExists, state);
1540+
const fromPaths = tryLoadModuleUsingPaths(extensions, rest, packageDirectory, packageInfo.versionPaths.paths, /*pathPatterns*/ undefined, loader, !packageDirectoryExists, state);
15401541
if (fromPaths) {
15411542
return fromPaths.value;
15421543
}
@@ -1546,8 +1547,9 @@ namespace ts {
15461547
return loader(extensions, candidate, !nodeModulesDirectoryExists, state);
15471548
}
15481549

1549-
function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, baseDirectory: string, paths: MapLike<string[]>, loader: ResolutionKindSpecificLoader, onlyRecordFailures: boolean, state: ModuleResolutionState): SearchResult<Resolved> {
1550-
const matchedPattern = matchPatternOrExact(getOwnKeys(paths), moduleName);
1550+
function tryLoadModuleUsingPaths(extensions: Extensions, moduleName: string, baseDirectory: string, paths: MapLike<string[]>, pathPatterns: readonly (string | Pattern)[] | undefined, loader: ResolutionKindSpecificLoader, onlyRecordFailures: boolean, state: ModuleResolutionState): SearchResult<Resolved> {
1551+
pathPatterns ||= tryParsePatterns(paths);
1552+
const matchedPattern = matchPatternOrExact(pathPatterns, moduleName);
15511553
if (matchedPattern) {
15521554
const matchedStar = isString(matchedPattern) ? undefined : matchedText(matchedPattern, moduleName);
15531555
const matchedPatternText = isString(matchedPattern) ? matchedPattern : patternText(matchedPattern);

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6195,6 +6195,7 @@ namespace ts {
61956195
validatedFilesSpec: readonly string[] | undefined;
61966196
validatedIncludeSpecs: readonly string[] | undefined;
61976197
validatedExcludeSpecs: readonly string[] | undefined;
6198+
pathPatterns: readonly (string | Pattern)[] | undefined;
61986199
}
61996200

62006201
/* @internal */

src/compiler/utilities.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6769,14 +6769,25 @@ namespace ts {
67696769
return changeAnyExtension(path, newExtension, extensionsToRemove, /*ignoreCase*/ false) as T;
67706770
}
67716771

6772-
export function tryParsePattern(pattern: string): Pattern | undefined {
6773-
// This should be verified outside of here and a proper error thrown.
6774-
Debug.assert(hasZeroOrOneAsteriskCharacter(pattern));
6772+
/**
6773+
* Returns the input if there are no stars, a pattern if there is exactly one,
6774+
* and undefined if there are more.
6775+
*/
6776+
export function tryParsePattern(pattern: string): string | Pattern | undefined {
67756777
const indexOfStar = pattern.indexOf("*");
6776-
return indexOfStar === -1 ? undefined : {
6777-
prefix: pattern.substr(0, indexOfStar),
6778-
suffix: pattern.substr(indexOfStar + 1)
6779-
};
6778+
if (indexOfStar === -1) {
6779+
return pattern;
6780+
}
6781+
return pattern.indexOf("*", indexOfStar + 1) !== -1
6782+
? undefined
6783+
: {
6784+
prefix: pattern.substr(0, indexOfStar),
6785+
suffix: pattern.substr(indexOfStar + 1)
6786+
};
6787+
}
6788+
6789+
export function tryParsePatterns(paths: MapLike<string[]>): (string | Pattern)[] {
6790+
return mapDefined(getOwnKeys(paths), path => tryParsePattern(path));
67806791
}
67816792

67826793
export function positionIsSynthesized(pos: number): boolean {
@@ -6822,21 +6833,19 @@ namespace ts {
68226833

68236834

68246835
/**
6825-
* patternStrings contains both pattern strings (containing "*") and regular strings.
6836+
* patternOrStrings contains both patterns (containing "*") and regular strings.
68266837
* Return an exact match if possible, or a pattern match, or undefined.
68276838
* (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.)
68286839
*/
6829-
export function matchPatternOrExact(patternStrings: readonly string[], candidate: string): string | Pattern | undefined {
6840+
export function matchPatternOrExact(patternOrStrings: readonly (string | Pattern)[], candidate: string): string | Pattern | undefined {
68306841
const patterns: Pattern[] = [];
6831-
for (const patternString of patternStrings) {
6832-
if (!hasZeroOrOneAsteriskCharacter(patternString)) continue;
6833-
const pattern = tryParsePattern(patternString);
6834-
if (pattern) {
6835-
patterns.push(pattern);
6836-
}
6837-
else if (patternString === candidate) {
6838-
// pattern was matched as is - no need to search further
6839-
return patternString;
6842+
for (const patternOrString of patternOrStrings) {
6843+
if (patternOrString === candidate) {
6844+
return candidate;
6845+
}
6846+
6847+
if (!isString(patternOrString)) {
6848+
patterns.push(patternOrString);
68406849
}
68416850
}
68426851

src/services/stringCompletions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,8 @@ namespace ts.Completions.StringCompletions {
553553
return undefined;
554554
}
555555

556-
const parsed = hasZeroOrOneAsteriskCharacter(pattern) ? tryParsePattern(pattern) : undefined;
557-
if (!parsed) {
556+
const parsed = tryParsePattern(pattern);
557+
if (parsed === undefined || isString(parsed)) {
558558
return undefined;
559559
}
560560

0 commit comments

Comments
 (0)