Add support for including dotted and .min.js files explicitly in include

Porting #9528 to release 2.0 branch
This commit is contained in:
Richard Knoll 2016-07-26 14:54:31 -07:00
parent bd6d2c0251
commit 591959507a
3 changed files with 230 additions and 11 deletions

View File

@ -689,9 +689,6 @@ namespace ts {
return output;
}
// Skip over any minified JavaScript files (ending in ".min.js")
// Skip over dotted files and folders as well
const ignoreFileNamePattern = /(\.min\.js$)|([\\/]\.[\w.])/;
/**
* Parse the contents of a config file (tsconfig.json).
* @param json The contents of the config file to parse
@ -1003,10 +1000,6 @@ namespace ts {
continue;
}
if (ignoreFileNamePattern.test(file)) {
continue;
}
// We may have included a wildcard path with a lower priority
// extension due to the user-defined order of entries in the
// "include" array. If there is a lower priority extension in the

View File

@ -922,11 +922,29 @@ namespace ts {
const reservedCharacterPattern = /[^\w\s\/]/g;
const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
/**
* 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 seperators)
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
*/
const singleAsteriskRegexFragmentFiles = "([^./]*(\\.(?!min\\.js$))?)*";
const singleAsteriskRegexFragmentOther = "[^/]*";
export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude") {
if (specs === undefined || specs.length === 0) {
return undefined;
}
const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther;
const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther;
/**
* 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
*/
const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?";
let pattern = "";
let hasWrittenSubpattern = false;
spec: for (const spec of specs) {
@ -947,13 +965,13 @@ namespace ts {
components[0] = removeTrailingDirectorySeparator(components[0]);
let optionalCount = 0;
for (const component of components) {
for (let component of components) {
if (component === "**") {
if (hasRecursiveDirectoryWildcard) {
continue spec;
}
subpattern += "(/.+?)?";
subpattern += doubleAsteriskRegexFragment;
hasRecursiveDirectoryWildcard = true;
hasWrittenComponent = true;
}
@ -967,6 +985,20 @@ namespace ts {
subpattern += directorySeparator;
}
if (usage !== "exclude") {
// 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) === CharacterCodes.asterisk) {
subpattern += "([^./]" + singleAsteriskRegexFragment + ")?";
component = component.substr(1);
}
else if (component.charCodeAt(0) === CharacterCodes.question) {
subpattern += "[^./]";
component = component.substr(1);
}
}
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
hasWrittenComponent = true;
}
@ -992,8 +1024,16 @@ namespace ts {
return "^(" + pattern + (usage === "exclude" ? ")($|/)" : ")$");
}
function replaceWildcardCharacter(match: string) {
return match === "*" ? "[^/]*" : match === "?" ? "[^/]" : "\\" + match;
function replaceWildCardCharacterFiles(match: string) {
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentFiles);
}
function replaceWildCardCharacterOther(match: string) {
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentOther);
}
function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) {
return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match;
}
export interface FileSystemEntries {

View File

@ -24,6 +24,8 @@ namespace ts {
"c:/dev/x/y/b.ts",
"c:/dev/js/a.js",
"c:/dev/js/b.js",
"c:/dev/js/d.min.js",
"c:/dev/js/ab.min.js",
"c:/ext/ext.ts",
"c:/ext/b/a..b.ts"
]);
@ -76,6 +78,17 @@ namespace ts {
"c:/dev/jspm_packages/a.ts"
]);
const caseInsensitiveDottedFoldersHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, [
"c:/dev/x/d.ts",
"c:/dev/x/y/d.ts",
"c:/dev/x/y/.e.ts",
"c:/dev/x/.y/a.ts",
"c:/dev/.z/.b.ts",
"c:/dev/.z/c.ts",
"c:/dev/w/.u/e.ts",
"c:/dev/g.min.js/.g/g.ts"
]);
describe("matchFiles", () => {
describe("with literal file list", () => {
it("without exclusions", () => {
@ -727,6 +740,33 @@ namespace ts {
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
it("include explicitly listed .min.js files when allowJs=true", () => {
const json = {
compilerOptions: {
allowJs: true
},
include: [
"js/*.min.js"
]
};
const expected: ts.ParsedCommandLine = {
options: {
allowJs: true
},
errors: [],
fileNames: [
"c:/dev/js/ab.min.js",
"c:/dev/js/d.min.js"
],
wildcardDirectories: {
"c:/dev/js": ts.WatchDirectoryFlags.None
}
};
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
assert.deepEqual(actual.fileNames, expected.fileNames);
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
it("include paths outside of the project", () => {
const json = {
include: [
@ -952,6 +992,35 @@ namespace ts {
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
it("exclude .min.js files using wildcards", () => {
const json = {
compilerOptions: {
allowJs: true
},
include: [
"js/*.min.js"
],
exclude: [
"js/a*"
]
};
const expected: ts.ParsedCommandLine = {
options: {
allowJs: true
},
errors: [],
fileNames: [
"c:/dev/js/d.min.js"
],
wildcardDirectories: {
"c:/dev/js": ts.WatchDirectoryFlags.None
}
};
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
assert.deepEqual(actual.fileNames, expected.fileNames);
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
describe("with trailing recursive directory", () => {
it("in includes", () => {
const json = {
@ -1146,5 +1215,122 @@ namespace ts {
});
});
});
describe("with files or folders that begin with a .", () => {
it("that are not explicitly included", () => {
const json = {
include: [
"x/**/*",
"w/*/*"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/x/d.ts",
"c:/dev/x/y/d.ts",
],
wildcardDirectories: {
"c:/dev/x": ts.WatchDirectoryFlags.Recursive,
"c:/dev/w": ts.WatchDirectoryFlags.Recursive
}
};
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
assert.deepEqual(actual.fileNames, expected.fileNames);
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
describe("that are explicitly included", () => {
it("without wildcards", () => {
const json = {
include: [
"x/.y/a.ts",
"c:/dev/.z/.b.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/.z/.b.ts",
"c:/dev/x/.y/a.ts"
],
wildcardDirectories: {}
};
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
assert.deepEqual(actual.fileNames, expected.fileNames);
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
it("with recursive wildcards that match directories", () => {
const json = {
include: [
"**/.*/*"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/.z/c.ts",
"c:/dev/g.min.js/.g/g.ts",
"c:/dev/w/.u/e.ts",
"c:/dev/x/.y/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
}
};
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
assert.deepEqual(actual.fileNames, expected.fileNames);
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
it("with recursive wildcards that match nothing", () => {
const json = {
include: [
"x/**/.y/*",
".z/**/.*"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/.z/.b.ts",
"c:/dev/x/.y/a.ts"
],
wildcardDirectories: {
"c:/dev/.z": ts.WatchDirectoryFlags.Recursive,
"c:/dev/x": ts.WatchDirectoryFlags.Recursive
}
};
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
assert.deepEqual(actual.fileNames, expected.fileNames);
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
it("with wildcard excludes that implicitly exclude dotted files", () => {
const json = {
include: [
"**/.*/*"
],
exclude: [
"**/*"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [],
wildcardDirectories: {}
};
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
assert.deepEqual(actual.fileNames, expected.fileNames);
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
assert.deepEqual(actual.errors, expected.errors);
});
});
});
});
}