mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 02:15:10 -05:00
Initial support for globs in tsconfig.json
This commit is contained in:
@@ -495,46 +495,51 @@ namespace ts {
|
||||
};
|
||||
|
||||
function getFileNames(): string[] {
|
||||
let fileNames: string[] = [];
|
||||
let fileNames: string[];
|
||||
if (hasProperty(json, "files")) {
|
||||
if (json["files"] instanceof Array) {
|
||||
fileNames = map(<string[]>json["files"], s => combinePaths(basePath, s));
|
||||
if (isArray(json["files"])) {
|
||||
fileNames = <string[]>json["files"];
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "files", "Array"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const filesSeen: Map<boolean> = {};
|
||||
const exclude = json["exclude"] instanceof Array ? map(<string[]>json["exclude"], normalizeSlashes) : undefined;
|
||||
const supportedExtensions = getSupportedExtensions(options);
|
||||
Debug.assert(indexOf(supportedExtensions, ".ts") < indexOf(supportedExtensions, ".d.ts"), "Changed priority of extensions to pick");
|
||||
|
||||
// Get files of supported extensions in their order of resolution
|
||||
for (const extension of supportedExtensions) {
|
||||
const filesInDirWithExtension = host.readDirectory(basePath, extension, exclude);
|
||||
for (const fileName of filesInDirWithExtension) {
|
||||
// .ts extension would read the .d.ts extension files too but since .d.ts is lower priority extension,
|
||||
// lets pick them when its turn comes up
|
||||
if (extension === ".ts" && fileExtensionIs(fileName, ".d.ts")) {
|
||||
continue;
|
||||
}
|
||||
let includeSpecs: string[];
|
||||
if (hasProperty(json, "include")) {
|
||||
if (isArray(json["include"])) {
|
||||
includeSpecs = <string[]>json["include"];
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "include", "Array"));
|
||||
}
|
||||
}
|
||||
|
||||
// If this is one of the output extension (which would be .d.ts and .js if we are allowing compilation of js files)
|
||||
// do not include this file if we included .ts or .tsx file with same base name as it could be output of the earlier compilation
|
||||
if (extension === ".d.ts" || (options.allowJs && contains(supportedJavascriptExtensions, extension))) {
|
||||
const baseName = fileName.substr(0, fileName.length - extension.length);
|
||||
if (hasProperty(filesSeen, baseName + ".ts") || hasProperty(filesSeen, baseName + ".tsx")) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let excludeSpecs: string[];
|
||||
if (hasProperty(json, "exclude")) {
|
||||
if (isArray(json["exclude"])) {
|
||||
excludeSpecs = <string[]>json["exclude"];
|
||||
}
|
||||
else {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "exclude", "Array"));
|
||||
}
|
||||
}
|
||||
|
||||
filesSeen[fileName] = true;
|
||||
fileNames.push(fileName);
|
||||
if (fileNames === undefined && includeSpecs === undefined) {
|
||||
includeSpecs = ["**/*.ts"];
|
||||
if (options.jsx) {
|
||||
includeSpecs.push("**/*.tsx");
|
||||
}
|
||||
|
||||
if (options.allowJs) {
|
||||
includeSpecs.push("**/*.js");
|
||||
if (options.jsx) {
|
||||
includeSpecs.push("**/*.jsx");
|
||||
}
|
||||
}
|
||||
}
|
||||
return fileNames;
|
||||
|
||||
return expandFiles(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,4 +589,514 @@ namespace ts {
|
||||
|
||||
return { options, errors };
|
||||
}
|
||||
|
||||
// Simplified whitelist, forces escaping of any non-word (or digit), non-whitespace character.
|
||||
const reservedCharacterPattern = /[^\w\s]/g;
|
||||
|
||||
const enum ExpandResult {
|
||||
Ok,
|
||||
Error
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands an array of file specifications.
|
||||
*
|
||||
* @param fileNames The literal file names to include.
|
||||
* @param includeSpecs The file specifications to expand.
|
||||
* @param excludeSpecs The file specifications to exclude.
|
||||
* @param basePath The base path for any relative file specifications.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
* @param errors An array for diagnostic reporting.
|
||||
*/
|
||||
export function expandFiles(fileNames: string[], includeSpecs: string[], excludeSpecs: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors?: Diagnostic[]): string[] {
|
||||
basePath = normalizePath(basePath);
|
||||
basePath = removeTrailingDirectorySeparator(basePath);
|
||||
|
||||
const excludePattern = includeSpecs ? createExcludeRegularExpression(excludeSpecs, basePath, options, host, errors) : undefined;
|
||||
const fileSet = createFileMap<Path>(host.useCaseSensitiveFileNames ? caseSensitiveKeyMapper : caseInsensitiveKeyMapper);
|
||||
|
||||
// include every literal file.
|
||||
if (fileNames) {
|
||||
for (const fileName of fileNames) {
|
||||
const path = toPath(fileName, basePath, caseSensitiveKeyMapper);
|
||||
if (!fileSet.contains(path)) {
|
||||
fileSet.set(path, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// expand and include the provided files into the file set.
|
||||
if (includeSpecs) {
|
||||
for (let includeSpec of includeSpecs) {
|
||||
includeSpec = normalizePath(includeSpec);
|
||||
includeSpec = removeTrailingDirectorySeparator(includeSpec);
|
||||
expandFileSpec(basePath, includeSpec, 0, excludePattern, options, host, errors, fileSet);
|
||||
}
|
||||
}
|
||||
|
||||
const output = fileSet.reduce(addFileToOutput, []);
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands a file specification with wildcards.
|
||||
*
|
||||
* @param basePath The directory to expand.
|
||||
* @param fileSpec The original file specification.
|
||||
* @param start The starting offset in the file specification.
|
||||
* @param excludePattern A pattern used to exclude a file specification.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
* @param errors An array for diagnostic reporting.
|
||||
* @param fileSet The set of matching files.
|
||||
* @param isExpandingRecursiveDirectory A value indicating whether the file specification includes a recursive directory wildcard prior to the start of this segment.
|
||||
*/
|
||||
function expandFileSpec(basePath: string, fileSpec: string, start: number, excludePattern: RegExp, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], fileSet: FileMap<Path>, isExpandingRecursiveDirectory?: boolean): ExpandResult {
|
||||
// Skip expansion if the base path matches an exclude pattern.
|
||||
if (isExcludedPath(excludePattern, basePath)) {
|
||||
return ExpandResult.Ok;
|
||||
}
|
||||
|
||||
// Find the offset of the next wildcard in the file specification
|
||||
let offset = indexOfWildcard(fileSpec, start);
|
||||
if (offset < 0) {
|
||||
// There were no more wildcards, so include the file.
|
||||
const path = toPath(fileSpec.substring(start), basePath, caseSensitiveKeyMapper);
|
||||
includeFile(path, excludePattern, options, host, fileSet);
|
||||
return ExpandResult.Ok;
|
||||
}
|
||||
|
||||
// Find the last directory separator before the wildcard to get the leading path.
|
||||
offset = fileSpec.lastIndexOf(directorySeparator, offset);
|
||||
if (offset > start) {
|
||||
// The wildcard occurs in a later segment, include remaining path up to
|
||||
// wildcard in prefix.
|
||||
basePath = combinePaths(basePath, fileSpec.substring(start, offset));
|
||||
|
||||
// Skip this wildcard path if the base path now matches an exclude pattern.
|
||||
if (isExcludedPath(excludePattern, basePath)) {
|
||||
return ExpandResult.Ok;
|
||||
}
|
||||
|
||||
start = offset + 1;
|
||||
}
|
||||
|
||||
// Find the offset of the next directory separator to extract the wildcard path segment.
|
||||
offset = getEndOfPathSegment(fileSpec, start);
|
||||
|
||||
// Check if the current offset is the beginning of a recursive directory pattern.
|
||||
if (isRecursiveDirectoryWildcard(fileSpec, start, offset)) {
|
||||
if (offset >= fileSpec.length) {
|
||||
// If there is no file specification following the recursive directory pattern
|
||||
// we cannot match any files, so we will ignore this pattern.
|
||||
return ExpandResult.Ok;
|
||||
}
|
||||
|
||||
// Stop expansion if a file specification contains more than one recursive directory pattern.
|
||||
if (isExpandingRecursiveDirectory) {
|
||||
if (errors) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, fileSpec));
|
||||
}
|
||||
|
||||
return ExpandResult.Error;
|
||||
}
|
||||
|
||||
// Expand the recursive directory pattern.
|
||||
return expandRecursiveDirectory(basePath, fileSpec, offset + 1, excludePattern, options, host, errors, fileSet);
|
||||
}
|
||||
|
||||
// Match the entries in the directory against the wildcard pattern.
|
||||
const pattern = createRegularExpressionFromWildcard(fileSpec, start, offset, host);
|
||||
|
||||
// If there are no more directory separators (the offset is at the end of the file specification), then
|
||||
// this must be a file.
|
||||
if (offset >= fileSpec.length) {
|
||||
const files = host.readFileNames(basePath);
|
||||
for (const extension of getSupportedExtensions(options)) {
|
||||
for (const file of files) {
|
||||
if (fileExtensionIs(file, extension)) {
|
||||
const path = toPath(file, basePath, caseSensitiveKeyMapper);
|
||||
|
||||
// .ts extension would read the .d.ts extension files too but since .d.ts is lower priority extension,
|
||||
// lets pick them when its turn comes up.
|
||||
if (extension === ".ts" && fileExtensionIs(file, ".d.ts")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is one of the output extension (which would be .d.ts and .js if we are allowing compilation of js files)
|
||||
// do not include this file if we included .ts or .tsx file with same base name as it could be output of the earlier compilation
|
||||
if (extension === ".d.ts" || (options.allowJs && contains(supportedJavascriptExtensions, extension))) {
|
||||
if (fileSet.contains(changeExtension(path, ".ts")) || fileSet.contains(changeExtension(path, ".tsx"))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// This wildcard has no further directory to process, so include the file.
|
||||
includeFile(path, excludePattern, options, host, fileSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const directories = host.readDirectoryNames(basePath);
|
||||
for (const directory of directories) {
|
||||
// If this was a directory, process the directory.
|
||||
const path = toPath(directory, basePath, caseSensitiveKeyMapper);
|
||||
if (expandFileSpec(path, fileSpec, offset + 1, excludePattern, options, host, errors, fileSet, isExpandingRecursiveDirectory) === ExpandResult.Error) {
|
||||
return ExpandResult.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ExpandResult.Ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands a `**` recursive directory wildcard.
|
||||
*
|
||||
* @param basePath The directory to recursively expand.
|
||||
* @param fileSpec The original file specification.
|
||||
* @param start The starting offset in the file specification.
|
||||
* @param excludePattern A pattern used to exclude a file specification.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
* @param errors An array for diagnostic reporting.
|
||||
* @param fileSet The set of matching files.
|
||||
*/
|
||||
function expandRecursiveDirectory(basePath: string, fileSpec: string, start: number, excludePattern: RegExp, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[], fileSet: FileMap<Path>): ExpandResult {
|
||||
// expand the non-recursive part of the file specification against the prefix path.
|
||||
if (expandFileSpec(basePath, fileSpec, start, excludePattern, options, host, errors, fileSet, /*isExpandingRecursiveDirectory*/ true) === ExpandResult.Error) {
|
||||
return ExpandResult.Error;
|
||||
}
|
||||
|
||||
// Recursively expand each subdirectory.
|
||||
const directories = host.readDirectoryNames(basePath);
|
||||
for (const entry of directories) {
|
||||
const path = combinePaths(basePath, entry);
|
||||
if (expandRecursiveDirectory(path, fileSpec, start, excludePattern, options, host, errors, fileSet) === ExpandResult.Error) {
|
||||
return ExpandResult.Error;
|
||||
}
|
||||
}
|
||||
|
||||
return ExpandResult.Ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to include a file in a file set.
|
||||
*
|
||||
* @param file The file to include.
|
||||
* @param excludePattern A pattern used to exclude a file specification.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
* @param fileSet The set of matching files.
|
||||
*/
|
||||
function includeFile(file: Path, excludePattern: RegExp, options: CompilerOptions, host: ParseConfigHost, fileSet: FileMap<Path>): void {
|
||||
// Ignore the file if it should be excluded.
|
||||
if (isExcludedPath(excludePattern, file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore the file if it doesn't exist.
|
||||
if (!host.fileExists(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore the file if it does not have a supported extension.
|
||||
if (!options.allowNonTsExtensions && !isSupportedSourceFileName(file, options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fileSet.contains(file)) {
|
||||
fileSet.set(file, file);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to an array of files.
|
||||
*
|
||||
* @param output The output array.
|
||||
* @param file The file path.
|
||||
*/
|
||||
function addFileToOutput(output: string[], file: string) {
|
||||
output.push(file);
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a path should be excluded.
|
||||
*
|
||||
* @param excludePattern A pattern used to exclude a file specification.
|
||||
* @param path The path to test for exclusion.
|
||||
*/
|
||||
function isExcludedPath(excludePattern: RegExp, path: string) {
|
||||
return excludePattern ? excludePattern.test(path) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a regular expression from a glob-style wildcard.
|
||||
*
|
||||
* @param fileSpec The file specification.
|
||||
* @param start The starting offset in the file specification.
|
||||
* @param end The end offset in the file specification.
|
||||
* @param host The host used to resolve files and directories.
|
||||
*/
|
||||
function createRegularExpressionFromWildcard(fileSpec: string, start: number, end: number, host: ParseConfigHost): RegExp {
|
||||
const pattern = createPatternFromWildcard(fileSpec, start, end);
|
||||
return new RegExp("^" + pattern + "$", host.useCaseSensitiveFileNames ? "" : "i");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pattern from a wildcard segment.
|
||||
*
|
||||
* @param fileSpec The file specification.
|
||||
* @param start The starting offset in the file specification.
|
||||
* @param end The end offset in the file specification.
|
||||
*/
|
||||
function createPatternFromWildcard(fileSpec: string, start: number, end: number): string {
|
||||
let pattern = "";
|
||||
let offset = indexOfWildcard(fileSpec, start);
|
||||
while (offset >= 0 && offset < end) {
|
||||
if (offset > start) {
|
||||
// Escape and append the non-wildcard portion to the regular expression
|
||||
pattern += escapeRegularExpressionText(fileSpec, start, offset);
|
||||
}
|
||||
|
||||
const charCode = fileSpec.charCodeAt(offset);
|
||||
if (charCode === CharacterCodes.asterisk) {
|
||||
// Append a multi-character (zero or more characters) pattern to the regular expression
|
||||
pattern += "[^/]*";
|
||||
}
|
||||
else if (charCode === CharacterCodes.question) {
|
||||
// Append a single-character (zero or one character) pattern to the regular expression
|
||||
pattern += "[^/]";
|
||||
}
|
||||
|
||||
start = offset + 1;
|
||||
offset = indexOfWildcard(fileSpec, start);
|
||||
}
|
||||
|
||||
// Escape and append any remaining non-wildcard portion.
|
||||
if (start < end) {
|
||||
pattern += escapeRegularExpressionText(fileSpec, start, end);
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a regular expression from a glob-style wildcard used to exclude a file.
|
||||
*
|
||||
* @param excludeSpecs The file specifications to exclude.
|
||||
* @param basePath The prefix path.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
* @param errors An array for diagnostic reporting.
|
||||
*/
|
||||
function createExcludeRegularExpression(excludeSpecs: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[]): RegExp {
|
||||
// Ignore an empty exclusion list
|
||||
if (!excludeSpecs || excludeSpecs.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
basePath = escapeRegularExpressionText(basePath, 0, basePath.length);
|
||||
|
||||
let pattern = "";
|
||||
for (const excludeSpec of excludeSpecs) {
|
||||
const excludePattern = createExcludePattern(excludeSpec, basePath, options, host, errors);
|
||||
if (excludePattern) {
|
||||
if (pattern.length > 0) {
|
||||
pattern += "|";
|
||||
}
|
||||
|
||||
pattern += "(" + excludePattern + ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (pattern.length > 0) {
|
||||
return new RegExp("^(" + pattern + ")($|/)", host.useCaseSensitiveFileNames ? "" : "i");
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a pattern for used to exclude a file.
|
||||
*
|
||||
* @param excludeSpec The file specification to exclude.
|
||||
* @param basePath The base path for the exclude pattern.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
* @param errors An array for diagnostic reporting.
|
||||
*/
|
||||
function createExcludePattern(excludeSpec: string, basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[]): string {
|
||||
if (!excludeSpec) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
excludeSpec = normalizePath(excludeSpec);
|
||||
excludeSpec = removeTrailingDirectorySeparator(excludeSpec);
|
||||
|
||||
let pattern = isRootedDiskPath(excludeSpec) ? "" : basePath;
|
||||
let hasRecursiveDirectoryWildcard = false;
|
||||
let segmentStart = 0;
|
||||
let segmentEnd = getEndOfPathSegment(excludeSpec, segmentStart);
|
||||
while (segmentStart < segmentEnd) {
|
||||
if (isRecursiveDirectoryWildcard(excludeSpec, segmentStart, segmentEnd)) {
|
||||
if (hasRecursiveDirectoryWildcard) {
|
||||
if (errors) {
|
||||
errors.push(createCompilerDiagnostic(Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, excludeSpec));
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// As an optimization, if the recursive directory is the last
|
||||
// wildcard, or is followed by only `*` or `*.ts`, don't add the
|
||||
// remaining pattern and exit the loop.
|
||||
if (canElideRecursiveDirectorySegment(excludeSpec, segmentEnd, options, host)) {
|
||||
break;
|
||||
}
|
||||
|
||||
hasRecursiveDirectoryWildcard = true;
|
||||
pattern += "(/.+)?";
|
||||
}
|
||||
else {
|
||||
if (pattern) {
|
||||
pattern += directorySeparator;
|
||||
}
|
||||
|
||||
pattern += createPatternFromWildcard(excludeSpec, segmentStart, segmentEnd);
|
||||
}
|
||||
|
||||
segmentStart = segmentEnd + 1;
|
||||
segmentEnd = getEndOfPathSegment(excludeSpec, segmentStart);
|
||||
}
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a recursive directory segment can be elided when
|
||||
* building a regular expression to exclude a path.
|
||||
*
|
||||
* @param excludeSpec The file specification used to exclude a path.
|
||||
* @param segmentEnd The end position of the recursive directory segment.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
*/
|
||||
function canElideRecursiveDirectorySegment(excludeSpec: string, segmentEnd: number, options: CompilerOptions, host: ParseConfigHost) {
|
||||
// If there are no segments after this segment, the pattern for this segment may be elided.
|
||||
if (segmentEnd + 1 >= excludeSpec.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the following segment is a wildcard that may be elided, the pattern for this segment may be elided.
|
||||
return canElideWildcardSegment(excludeSpec, segmentEnd + 1, options, host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a wildcard segment can be elided when building a
|
||||
* regular expression to exclude a path.
|
||||
*
|
||||
* @param excludeSpec The file specification used to exclude a path.
|
||||
* @param segmentStart The starting position of the segment.
|
||||
* @param options Compiler options.
|
||||
* @param host The host used to resolve files and directories.
|
||||
*/
|
||||
function canElideWildcardSegment(excludeSpec: string, segmentStart: number, options: CompilerOptions, host: ParseConfigHost) {
|
||||
const charCode = excludeSpec.charCodeAt(segmentStart);
|
||||
if (charCode === CharacterCodes.asterisk) {
|
||||
const end = excludeSpec.length;
|
||||
|
||||
// If the segment consists only of `*`, we may elide this segment.
|
||||
if (segmentStart + 1 === end) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the segment consists only of `*.ts`, and we do not allow
|
||||
// any other extensions for source files, we may elide this segment.
|
||||
if (!options.allowNonTsExtensions && !options.jsx && !options.allowJs && segmentStart + 4 === end) {
|
||||
const segment = excludeSpec.substr(segmentStart);
|
||||
return fileExtensionIs(host.useCaseSensitiveFileNames ? segment : segment.toLowerCase(), ".ts");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape regular expression reserved tokens.
|
||||
*
|
||||
* @param text The text to escape.
|
||||
* @param start The starting offset in the string.
|
||||
* @param end The ending offset in the string.
|
||||
*/
|
||||
function escapeRegularExpressionText(text: string, start: number, end: number) {
|
||||
return text.substring(start, end).replace(reservedCharacterPattern, "\\$&");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the wildcard at the current offset is a recursive directory wildcard.
|
||||
*
|
||||
* @param fileSpec The file specification.
|
||||
* @param segmentStart The starting offset of a segment in the file specification.
|
||||
* @param segmentEnd The ending offset of a segment in the file specification.
|
||||
*/
|
||||
function isRecursiveDirectoryWildcard(fileSpec: string, segmentStart: number, segmentEnd: number) {
|
||||
return segmentEnd - segmentStart === 2 &&
|
||||
fileSpec.charCodeAt(segmentStart) === CharacterCodes.asterisk &&
|
||||
fileSpec.charCodeAt(segmentStart + 1) === CharacterCodes.asterisk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the next wildcard character in a file specification.
|
||||
*
|
||||
* @param fileSpec The file specification.
|
||||
* @param start The starting offset in the file specification.
|
||||
*/
|
||||
function indexOfWildcard(fileSpec: string, start: number): number {
|
||||
for (let i = start; i < fileSpec.length; ++i) {
|
||||
const ch = fileSpec.charCodeAt(i);
|
||||
if (ch === CharacterCodes.asterisk || ch === CharacterCodes.question) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end position of a path segment, either the index of the next directory separator or
|
||||
* the provided end position.
|
||||
*
|
||||
* @param fileSpec The file specification.
|
||||
* @param segmentStart The start offset in the file specification.
|
||||
*/
|
||||
function getEndOfPathSegment(fileSpec: string, segmentStart: number): number {
|
||||
const end = fileSpec.length;
|
||||
if (segmentStart >= end) {
|
||||
return end;
|
||||
}
|
||||
|
||||
const offset = fileSpec.indexOf(directorySeparator, segmentStart);
|
||||
return offset < 0 ? end : offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a case sensitive key.
|
||||
*
|
||||
* @param key The original key.
|
||||
*/
|
||||
function caseSensitiveKeyMapper(key: string) {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a case insensitive key.
|
||||
*
|
||||
* @param key The original key.
|
||||
*/
|
||||
function caseInsensitiveKeyMapper(key: string) {
|
||||
return key.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user