250 lines
13 KiB
Plaintext
250 lines
13 KiB
Plaintext
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.matchFiles = void 0;
|
|
const core_1 = require("./core");
|
|
const path_1 = require("./path");
|
|
// KLUDGE: Don't assume one 'node_modules' links to another. More likely a single directory inside the node_modules is the symlink.
|
|
// ALso, don't assume that an `@foo` directory is linked. More likely the contents of that are linked.
|
|
// Reserved characters, forces escaping of any non-word (or digit), non-whitespace character.
|
|
// It may be inefficient (we could just match (/[-[\]{}()*+?.,\\^$|#\s]/g), but this is future
|
|
// proof.
|
|
const reservedCharacterPattern = /[^\w\s\/]/g;
|
|
const wildcardCharCodes = [42 /* CharacterCodes.asterisk */, 63 /* CharacterCodes.question */];
|
|
const commonPackageFolders = ["node_modules", "bower_components", "jspm_packages"];
|
|
const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
|
|
const filesMatcher = {
|
|
/**
|
|
* Matches any single directory segment unless it is the last segment and a .min.js file
|
|
* Breakdown:
|
|
* [^./] # matches everything up to the first . character (excluding directory separators)
|
|
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
|
|
*/
|
|
singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*",
|
|
/**
|
|
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
|
|
* files or directories, does not match subdirectories that start with a . character
|
|
*/
|
|
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
|
|
replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment)
|
|
};
|
|
const directoriesMatcher = {
|
|
singleAsteriskRegexFragment: "[^/]*",
|
|
/**
|
|
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
|
|
* files or directories, does not match subdirectories that start with a . character
|
|
*/
|
|
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
|
|
replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment)
|
|
};
|
|
const excludeMatcher = {
|
|
singleAsteriskRegexFragment: "[^/]*",
|
|
doubleAsteriskRegexFragment: "(/.+?)?",
|
|
replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment)
|
|
};
|
|
const wildcardMatchers = {
|
|
files: filesMatcher,
|
|
directories: directoriesMatcher,
|
|
exclude: excludeMatcher
|
|
};
|
|
function getRegularExpressionForWildcard(specs, basePath, usage) {
|
|
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}`;
|
|
}
|
|
function getRegularExpressionsForWildcards(specs, basePath, usage) {
|
|
if (specs === undefined || specs.length === 0) {
|
|
return undefined;
|
|
}
|
|
return (0, core_1.flatMap)(specs, spec => spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]));
|
|
}
|
|
/**
|
|
* An "includes" path "foo" is implicitly a glob "foo/** /*" (without the space) if its last component has no extension,
|
|
* and does not contain any glob characters itself.
|
|
*/
|
|
function isImplicitGlob(lastPathComponent) {
|
|
return !/[.*?]/.test(lastPathComponent);
|
|
}
|
|
function getSubPatternFromSpec(spec, basePath, usage, { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }) {
|
|
let subpattern = "";
|
|
let hasWrittenComponent = false;
|
|
const components = (0, path_1.getNormalizedPathComponents)(spec, basePath);
|
|
const lastComponent = (0, core_1.last)(components);
|
|
if (usage !== "exclude" && lastComponent === "**") {
|
|
return undefined;
|
|
}
|
|
// getNormalizedPathComponents includes the separator for the root component.
|
|
// We need to remove to create our regex correctly.
|
|
components[0] = (0, path_1.removeTrailingDirectorySeparator)(components[0]);
|
|
if (isImplicitGlob(lastComponent)) {
|
|
components.push("**", "*");
|
|
}
|
|
let optionalCount = 0;
|
|
for (let component of components) {
|
|
if (component === "**") {
|
|
subpattern += doubleAsteriskRegexFragment;
|
|
}
|
|
else {
|
|
if (usage === "directories") {
|
|
subpattern += "(";
|
|
optionalCount++;
|
|
}
|
|
if (hasWrittenComponent) {
|
|
subpattern += path_1.directorySeparator;
|
|
}
|
|
if (usage !== "exclude") {
|
|
let componentPattern = "";
|
|
// The * and ? wildcards should not match directories or files that start with . if they
|
|
// appear first in a component. Dotted directories and files can be included explicitly
|
|
// like so: **/.*/.*
|
|
if (component.charCodeAt(0) === 42 /* CharacterCodes.asterisk */) {
|
|
componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?";
|
|
component = component.substr(1);
|
|
}
|
|
else if (component.charCodeAt(0) === 63 /* CharacterCodes.question */) {
|
|
componentPattern += "[^./]";
|
|
component = component.substr(1);
|
|
}
|
|
componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
|
|
// Patterns should not include subfolders like node_modules unless they are
|
|
// explicitly included as part of the path.
|
|
//
|
|
// As an optimization, if the component pattern is the same as the component,
|
|
// then there definitely were no wildcard characters and we do not need to
|
|
// add the exclusion pattern.
|
|
if (componentPattern !== component) {
|
|
subpattern += implicitExcludePathRegexPattern;
|
|
}
|
|
subpattern += componentPattern;
|
|
}
|
|
else {
|
|
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
|
|
}
|
|
}
|
|
hasWrittenComponent = true;
|
|
}
|
|
while (optionalCount > 0) {
|
|
subpattern += ")?";
|
|
optionalCount--;
|
|
}
|
|
return subpattern;
|
|
}
|
|
function replaceWildcardCharacter(match, singleAsteriskRegexFragment) {
|
|
return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match;
|
|
}
|
|
/** @param path directory of the tsconfig.json */
|
|
function getFileMatcherPatterns(path, excludes, includes, useCaseSensitiveFileNames, currentDirectory) {
|
|
path = (0, path_1.normalizePath)(path);
|
|
currentDirectory = (0, path_1.normalizePath)(currentDirectory);
|
|
const absolutePath = (0, path_1.combinePaths)(currentDirectory, path);
|
|
return {
|
|
includeFilePatterns: (0, core_1.map)(getRegularExpressionsForWildcards(includes, absolutePath, "files"), pattern => `^${pattern}$`),
|
|
includeFilePattern: getRegularExpressionForWildcard(includes, absolutePath, "files"),
|
|
includeDirectoryPattern: getRegularExpressionForWildcard(includes, absolutePath, "directories"),
|
|
excludePattern: getRegularExpressionForWildcard(excludes, absolutePath, "exclude"),
|
|
basePaths: getBasePaths(path, includes, useCaseSensitiveFileNames)
|
|
};
|
|
}
|
|
function getRegexFromPattern(pattern, useCaseSensitiveFileNames) {
|
|
return new RegExp(pattern, useCaseSensitiveFileNames ? "" : "i");
|
|
}
|
|
/** @param path directory of the tsconfig.json */
|
|
function matchFiles(path, extensions, excludes, includes, useCaseSensitiveFileNames, currentDirectory, depth, getFileSystemEntries, realpath) {
|
|
path = (0, path_1.normalizePath)(path);
|
|
currentDirectory = (0, path_1.normalizePath)(currentDirectory);
|
|
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);
|
|
// 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].
|
|
const results = includeFileRegexes ? includeFileRegexes.map(() => []) : [[]];
|
|
const visited = new Map();
|
|
const toCanonical = (0, core_1.createGetCanonicalFileName)(useCaseSensitiveFileNames);
|
|
for (const basePath of patterns.basePaths) {
|
|
visitDirectory(basePath, (0, path_1.combinePaths)(currentDirectory, basePath), depth);
|
|
}
|
|
return (0, core_1.flatten)(results);
|
|
function visitDirectory(path, absolutePath, depth) {
|
|
const canonicalPath = toCanonical(realpath(absolutePath));
|
|
if (visited.has(canonicalPath))
|
|
return;
|
|
visited.set(canonicalPath, true);
|
|
const { files, directories } = getFileSystemEntries(path);
|
|
for (const current of (0, core_1.sort)(files, core_1.compareStringsCaseSensitive)) {
|
|
const name = (0, path_1.combinePaths)(path, current);
|
|
const absoluteName = (0, path_1.combinePaths)(absolutePath, current);
|
|
if (extensions && !(0, path_1.fileExtensionIsOneOf)(name, extensions))
|
|
continue;
|
|
if (excludeRegex && excludeRegex.test(absoluteName))
|
|
continue;
|
|
if (!includeFileRegexes) {
|
|
results[0].push(name);
|
|
}
|
|
else {
|
|
const includeIndex = (0, core_1.findIndex)(includeFileRegexes, re => re.test(absoluteName));
|
|
if (includeIndex !== -1) {
|
|
results[includeIndex].push(name);
|
|
}
|
|
}
|
|
}
|
|
if (depth !== undefined) {
|
|
depth--;
|
|
if (depth === 0) {
|
|
return;
|
|
}
|
|
}
|
|
for (const current of (0, core_1.sort)(directories, core_1.compareStringsCaseSensitive)) {
|
|
const name = (0, path_1.combinePaths)(path, current);
|
|
const absoluteName = (0, path_1.combinePaths)(absolutePath, current);
|
|
if ((!includeDirectoryRegex || includeDirectoryRegex.test(absoluteName)) &&
|
|
(!excludeRegex || !excludeRegex.test(absoluteName))) {
|
|
visitDirectory(name, absoluteName, depth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.matchFiles = matchFiles;
|
|
/**
|
|
* Computes the unique non-wildcard base paths amongst the provided include patterns.
|
|
*/
|
|
function getBasePaths(path, includes, useCaseSensitiveFileNames) {
|
|
// Storage for our results in the form of literal paths (e.g. the paths as written by the user).
|
|
const basePaths = [path];
|
|
if (includes) {
|
|
// Storage for literal base paths amongst the include patterns.
|
|
const includeBasePaths = [];
|
|
for (const include of includes) {
|
|
// We also need to check the relative paths by converting them to absolute and normalizing
|
|
// in case they escape the base path (e.g "..\somedirectory")
|
|
const absolute = (0, path_1.isRootedDiskPath)(include) ? include : (0, path_1.normalizePath)((0, path_1.combinePaths)(path, include));
|
|
// Append the literal and canonical candidate base paths.
|
|
includeBasePaths.push(getIncludeBasePath(absolute));
|
|
}
|
|
// Sort the offsets array using either the literal or canonical path representations.
|
|
includeBasePaths.sort((0, core_1.getStringComparer)(!useCaseSensitiveFileNames));
|
|
// Iterate over each include base path and include unique base paths that are not a
|
|
// subpath of an existing base path
|
|
for (const includeBasePath of includeBasePaths) {
|
|
if ((0, core_1.every)(basePaths, basePath => !(0, path_1.containsPath)(basePath, includeBasePath, path, !useCaseSensitiveFileNames))) {
|
|
basePaths.push(includeBasePath);
|
|
}
|
|
}
|
|
}
|
|
return basePaths;
|
|
}
|
|
function getIncludeBasePath(absolute) {
|
|
const wildcardOffset = (0, core_1.indexOfAnyCharCode)(absolute, wildcardCharCodes);
|
|
if (wildcardOffset < 0) {
|
|
// No "*" or "?" in the path
|
|
return !(0, path_1.hasExtension)(absolute)
|
|
? absolute
|
|
: (0, path_1.removeTrailingDirectorySeparator)((0, path_1.getDirectoryPath)(absolute));
|
|
}
|
|
return absolute.substring(0, absolute.lastIndexOf(path_1.directorySeparator, wildcardOffset));
|
|
}
|
|
//# sourceMappingURL=utilities.js.map |