Skip to content

Do not merge file patterns into a single regular expression #61104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ import {
getNormalizedAbsolutePath,
getOwnKeys,
getRegexFromPattern,
getRegularExpressionForWildcard,
getRegularExpressionsForWildcards,
getRelativePathFromFile,
getSpellingSuggestion,
Expand Down Expand Up @@ -2689,16 +2688,17 @@ function filterSameAsDefaultInclude(specs: readonly string[] | undefined) {
function matchesSpecs(path: string, includeSpecs: readonly string[] | undefined, excludeSpecs: readonly string[] | undefined, host: ConvertToTSConfigHost): (path: string) => boolean {
if (!includeSpecs) return returnTrue;
const patterns = getFileMatcherPatterns(path, excludeSpecs, includeSpecs, host.useCaseSensitiveFileNames, host.getCurrentDirectory());
const excludeRe = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, host.useCaseSensitiveFileNames);
const includeRe = patterns.includeFilePattern && getRegexFromPattern(patterns.includeFilePattern, host.useCaseSensitiveFileNames);
if (includeRe) {
if (excludeRe) {
return path => !(includeRe.test(path) && !excludeRe.test(path));
const excludeRegexes = patterns.excludePatterns && patterns.excludePatterns.map(pattern => getRegexFromPattern(pattern, host.useCaseSensitiveFileNames));
const includeRegexes = patterns.includeFilePatterns && patterns.includeFilePatterns.map(pattern => getRegexFromPattern(pattern, host.useCaseSensitiveFileNames));
if (includeRegexes) {
if (excludeRegexes) {
return path => !(includeRegexes.some(regex => regex.test(path)) && !excludeRegexes.some(regex => regex.test(path)));
}
return path => !includeRe.test(path);
return path => !includeRegexes.some(regex => regex.test(path));
}
if (excludeRe) {
return path => excludeRe.test(path);

if (excludeRegexes) {
return path => excludeRegexes.some(regex => regex.test(path));
}
return returnTrue;
}
Expand Down Expand Up @@ -3931,7 +3931,7 @@ export function getFileNamesFromConfigSpecs(
// Valid only if *.json specified
if (!jsonOnlyIncludeRegexes) {
const includes = validatedIncludeSpecs.filter(s => endsWith(s, Extension.Json));
const includeFilePatterns = map(getRegularExpressionsForWildcards(includes, basePath, "files"), pattern => `^${pattern}$`);
const includeFilePatterns = getRegularExpressionsForWildcards(includes, basePath, "files");
jsonOnlyIncludeRegexes = includeFilePatterns ? includeFilePatterns.map(pattern => getRegexFromPattern(pattern, host.useCaseSensitiveFileNames)) : emptyArray;
}
const includeIndex = findIndex(jsonOnlyIncludeRegexes, re => re.test(file));
Expand Down Expand Up @@ -4032,11 +4032,11 @@ export function matchesExcludeWorker(
currentDirectory: string,
basePath?: string,
): boolean {
const excludePattern = getRegularExpressionForWildcard(excludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude");
const excludeRegex = excludePattern && getRegexFromPattern(excludePattern, useCaseSensitiveFileNames);
if (!excludeRegex) return false;
if (excludeRegex.test(pathToCheck)) return true;
return !hasExtension(pathToCheck) && excludeRegex.test(ensureTrailingDirectorySeparator(pathToCheck));
const excludePatterns = getRegularExpressionsForWildcards(excludeSpecs, combinePaths(normalizePath(currentDirectory), basePath), "exclude");
const excludeRegexes = excludePatterns && excludePatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames));
if (!excludeRegexes) return false;
if (excludeRegexes.some(regex => regex.test(pathToCheck))) return true;
return !hasExtension(pathToCheck) && excludeRegexes.some(regex => regex.test(ensureTrailingDirectorySeparator(pathToCheck)));
}

function validateSpecs(specs: readonly string[], errors: Diagnostic[], disallowTrailingRecursion: boolean, jsonSourceFile: TsConfigSourceFile | undefined, specKey: string): readonly string[] {
Expand Down Expand Up @@ -4081,15 +4081,15 @@ function getWildcardDirectories({ validatedIncludeSpecs: include, validatedExclu
//
// /a/b/* - Watch /a/b directly to catch any new file
// /a/b/a?z - Watch /a/b directly to catch any new file matching a?z
const rawExcludeRegex = getRegularExpressionForWildcard(exclude, basePath, "exclude");
const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i");
const rawExcludeRegexes = getRegularExpressionsForWildcards(exclude, basePath, "exclude");
const excludeRegexes = rawExcludeRegexes && rawExcludeRegexes.map(regex => new RegExp(regex, useCaseSensitiveFileNames ? "" : "i"));
const wildcardDirectories: MapLike<WatchDirectoryFlags> = {};
const wildCardKeyToPath = new Map<CanonicalKey, string>();
if (include !== undefined) {
const recursiveKeys: CanonicalKey[] = [];
for (const file of include) {
const spec = normalizePath(combinePaths(basePath, file));
if (excludeRegex && excludeRegex.test(spec)) {
if (some(excludeRegexes, excludeRegex => excludeRegex.test(spec))) {
continue;
}

Expand Down
42 changes: 13 additions & 29 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ import {
LogicalOperator,
LogicalOrCoalescingAssignmentOperator,
mangleScopedPackageName,
map,
mapDefined,
MapLike,
MemberName,
Expand Down Expand Up @@ -9552,26 +9551,15 @@ const wildcardMatchers = {
exclude: excludeMatcher,
};

/** @internal */
export function getRegularExpressionForWildcard(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
const patterns = getRegularExpressionsForWildcards(specs, basePath, usage);
if (!patterns || !patterns.length) {
return undefined;
}

const pattern = patterns.map(pattern => `(${pattern})`).join("|");
// If excluding, match "foo/bar/baz...", but if including, only allow "foo".
const terminator = usage === "exclude" ? "($|/)" : "$";
return `^(${pattern})${terminator}`;
}

/** @internal */
export function getRegularExpressionsForWildcards(specs: readonly string[] | undefined, basePath: string, usage: "files" | "directories" | "exclude"): readonly string[] | undefined {
if (specs === undefined || specs.length === 0) {
return undefined;
}

return flatMap(specs, spec => spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]));
// If excluding, match "foo/bar/baz...", but if including, only allow "foo".
const terminator = usage === "exclude" ? "($|/)" : "$";
return flatMap(specs, spec => spec && `^${getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage])}${terminator}`);
}

/**
Expand Down Expand Up @@ -9684,12 +9672,9 @@ export interface FileSystemEntries {

/** @internal */
export interface FileMatcherPatterns {
/** One pattern for each "include" spec. */
includeFilePatterns: readonly string[] | undefined;
/** One pattern matching one of any of the "include" specs. */
includeFilePattern: string | undefined;
includeDirectoryPattern: string | undefined;
excludePattern: string | undefined;
includeDirectoryPatterns: readonly string[] | undefined;
excludePatterns: readonly string[] | undefined;
basePaths: readonly string[];
}

Expand All @@ -9704,10 +9689,9 @@ export function getFileMatcherPatterns(path: string, excludes: readonly string[]
const absolutePath = combinePaths(currentDirectory, path);

return {
includeFilePatterns: map(getRegularExpressionsForWildcards(includes, absolutePath, "files"), pattern => `^${pattern}$`),
includeFilePattern: getRegularExpressionForWildcard(includes, absolutePath, "files"),
includeDirectoryPattern: getRegularExpressionForWildcard(includes, absolutePath, "directories"),
excludePattern: getRegularExpressionForWildcard(excludes, absolutePath, "exclude"),
includeFilePatterns: getRegularExpressionsForWildcards(includes, absolutePath, "files"),
includeDirectoryPatterns: getRegularExpressionsForWildcards(includes, absolutePath, "directories"),
excludePatterns: getRegularExpressionsForWildcards(excludes, absolutePath, "exclude"),
basePaths: getBasePaths(path, includes, useCaseSensitiveFileNames),
};
}
Expand All @@ -9729,8 +9713,8 @@ export function matchFiles(path: string, extensions: readonly string[] | undefin
const patterns = getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory);

const includeFileRegexes = patterns.includeFilePatterns && patterns.includeFilePatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames));
const includeDirectoryRegex = patterns.includeDirectoryPattern && getRegexFromPattern(patterns.includeDirectoryPattern, useCaseSensitiveFileNames);
const excludeRegex = patterns.excludePattern && getRegexFromPattern(patterns.excludePattern, useCaseSensitiveFileNames);
const includeDirectoryRegexes = patterns.includeDirectoryPatterns && patterns.includeDirectoryPatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames));
const excludeRegexes = patterns.excludePatterns && patterns.excludePatterns.map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames));

// Associate an array of results with each include regex. This keeps results in order of the "include" order.
// If there are no "includes", then just put everything in results[0].
Expand All @@ -9753,7 +9737,7 @@ export function matchFiles(path: string, extensions: readonly string[] | undefin
const name = combinePaths(path, current);
const absoluteName = combinePaths(absolutePath, current);
if (extensions && !fileExtensionIsOneOf(name, extensions)) continue;
if (excludeRegex && excludeRegex.test(absoluteName)) continue;
if (excludeRegexes && excludeRegexes.some(regex => regex.test(absoluteName))) continue;
if (!includeFileRegexes) {
results[0].push(name);
}
Expand All @@ -9776,8 +9760,8 @@ export function matchFiles(path: string, extensions: readonly string[] | undefin
const name = combinePaths(path, current);
const absoluteName = combinePaths(absolutePath, current);
if (
(!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) &&
(!excludeRegex || !excludeRegex.test(absoluteName))
(!includeDirectoryRegexes || includeDirectoryRegexes.some(regex => regex.test(absoluteName))) &&
(!excludeRegexes || !excludeRegexes.some(regex => regex.test(absoluteName)))
) {
visitDirectory(name, absoluteName, depth);
}
Expand Down
5 changes: 3 additions & 2 deletions src/services/getEditsForFileRename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,11 @@ function updateTsconfigFiles(program: Program, changeTracker: textChanges.Change
const includes = mapDefined(property.initializer.elements, e => isStringLiteral(e) ? e.text : undefined);
if (includes.length === 0) return;
const matchers = getFileMatcherPatterns(configDir, /*excludes*/ [], includes, useCaseSensitiveFileNames, currentDirectory);
const includeRegexes = Debug.checkDefined(matchers.includeFilePatterns).map(pattern => getRegexFromPattern(pattern, useCaseSensitiveFileNames));
// If there isn't some include for this, add a new one.
if (
getRegexFromPattern(Debug.checkDefined(matchers.includeFilePattern), useCaseSensitiveFileNames).test(oldFileOrDirPath) &&
!getRegexFromPattern(Debug.checkDefined(matchers.includeFilePattern), useCaseSensitiveFileNames).test(newFileOrDirPath)
includeRegexes.some(regex => regex.test(oldFileOrDirPath)) &&
!includeRegexes.some(regex => regex.test(newFileOrDirPath))
) {
changeTracker.insertNodeAfter(configFile, last(property.initializer.elements), factory.createStringLiteral(relativePath(newFileOrDirPath)));
}
Expand Down