Merge branch 'master' into loadJsFromModules

This commit is contained in:
Bill Ticehurst
2016-06-21 09:03:38 -07:00
1001 changed files with 301419 additions and 277561 deletions
+366 -61
View File
@@ -6,7 +6,7 @@
namespace ts {
/* @internal */
export let optionDeclarations: CommandLineOption[] = [
export const optionDeclarations: CommandLineOption[] = [
{
name: "charset",
type: "string",
@@ -37,6 +37,11 @@ namespace ts {
type: "boolean",
description: Diagnostics.Print_this_message,
},
{
name: "help",
shortName: "?",
type: "boolean"
},
{
name: "init",
type: "boolean",
@@ -332,8 +337,13 @@ namespace ts {
}
},
{
name: "typesRoot",
type: "string"
name: "typeRoots",
type: "list",
element: {
name: "typeRoots",
type: "string",
isFilePath: true
}
},
{
name: "types",
@@ -401,11 +411,16 @@ namespace ts {
"es2015.symbol": "lib.es2015.symbol.d.ts",
"es2015.symbol.wellknown": "lib.es2015.symbol.wellknown.d.ts",
"es2016.array.include": "lib.es2016.array.include.d.ts",
"es2017.object": "lib.es2017.object.d.ts"
"es2017.object": "lib.es2017.object.d.ts",
"es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts"
},
},
description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation_Colon
},
{
name: "disableProjectSizeLimit",
type: "boolean"
},
{
name: "strictNullChecks",
type: "boolean",
@@ -487,13 +502,20 @@ namespace ts {
}
/* @internal */
export function parseListTypeOption(opt: CommandLineOptionOfListType, value: string, errors: Diagnostic[]): (string | number)[] {
const values = trimString((value || "")).split(",");
export function parseListTypeOption(opt: CommandLineOptionOfListType, value = "", errors: Diagnostic[]): (string | number)[] | undefined {
value = trimString(value);
if (startsWith(value, "-")) {
return undefined;
}
if (value === "") {
return [];
}
const values = value.split(",");
switch (opt.element.type) {
case "number":
return ts.map(values, parseInt);
return map(values, parseInt);
case "string":
return ts.map(values, v => v || "");
return map(values, v => v || "");
default:
return filter(map(values, v => parseCustomTypeOption(<CommandLineOptionOfCustomType>opt.element, v, errors)), v => !!v);
}
@@ -554,8 +576,11 @@ namespace ts {
i++;
break;
case "list":
options[opt.name] = parseListTypeOption(<CommandLineOptionOfListType>opt, args[i], errors);
i++;
const result = parseListTypeOption(<CommandLineOptionOfListType>opt, args[i], errors);
options[opt.name] = result || [];
if (result) {
i++;
}
break;
// If not a primitive, the possible types are specified in what is effectively a map of options.
default:
@@ -666,7 +691,7 @@ namespace ts {
// Skip over any minified JavaScript files (ending in ".min.js")
// Skip over dotted files and folders as well
const IgnoreFileNamePattern = /(\.min\.js$)|([\\/]\.[\w.])/;
const ignoreFileNamePattern = /(\.min\.js$)|([\\/]\.[\w.])/;
/**
* Parse the contents of a config file (tsconfig.json).
* @param json The contents of the config file to parse
@@ -682,78 +707,66 @@ namespace ts {
options.configFilePath = configFileName;
const fileNames = getFileNames(errors);
const { fileNames, wildcardDirectories } = getFileNames(errors);
return {
options,
fileNames,
typingOptions,
raw: json,
errors
errors,
wildcardDirectories
};
function getFileNames(errors: Diagnostic[]): string[] {
let fileNames: string[] = [];
function getFileNames(errors: Diagnostic[]): ExpandResult {
let fileNames: string[];
if (hasProperty(json, "files")) {
if (isArray(json["files"])) {
fileNames = map(<string[]>json["files"], s => combinePaths(basePath, s));
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> = {};
let exclude: string[] = [];
if (isArray(json["exclude"])) {
exclude = json["exclude"];
let includeSpecs: string[];
if (hasProperty(json, "include")) {
if (isArray(json["include"])) {
includeSpecs = <string[]>json["include"];
}
else {
// by default exclude node_modules, and any specificied output directory
exclude = ["node_modules", "bower_components", "jspm_packages"];
}
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
if (outDir) {
exclude.push(outDir);
}
exclude = map(exclude, normalizeSlashes);
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;
}
if (IgnoreFileNamePattern.test(fileName)) {
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))) {
const baseName = fileName.substr(0, fileName.length - extension.length);
if (hasProperty(filesSeen, baseName + ".ts") || hasProperty(filesSeen, baseName + ".tsx")) {
continue;
}
}
filesSeen[fileName] = true;
fileNames.push(fileName);
}
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_requires_a_value_of_type_1, "include", "Array"));
}
}
if (hasProperty(json, "excludes") && !hasProperty(json, "exclude")) {
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"));
}
}
else if (hasProperty(json, "excludes")) {
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_option_excludes_Did_you_mean_exclude));
}
return fileNames;
else {
// By default, exclude common package folders
excludeSpecs = ["node_modules", "bower_components", "jspm_packages"];
}
// Always exclude the output directory unless explicitly included
const outDir = json["compilerOptions"] && json["compilerOptions"]["outDir"];
if (outDir) {
excludeSpecs.push(outDir);
}
if (fileNames === undefined && includeSpecs === undefined) {
includeSpecs = ["**/*"];
}
return matchFileNames(fileNames, includeSpecs, excludeSpecs, basePath, options, host, errors);
}
}
@@ -849,4 +862,296 @@ namespace ts {
function trimString(s: string) {
return typeof s.trim === "function" ? s.trim() : s.replace(/^[\s]+|[\s]+$/g, "");
}
/**
* Tests for a path that ends in a recursive directory wildcard.
* Matches **, \**, **\, and \**\, but not a**b.
*
* NOTE: used \ in place of / above to avoid issues with multiline comments.
*
* Breakdown:
* (^|\/) # matches either the beginning of the string or a directory separator.
* \*\* # matches the recursive directory wildcard "**".
* \/?$ # matches an optional trailing directory separator at the end of the string.
*/
const invalidTrailingRecursionPattern = /(^|\/)\*\*\/?$/;
/**
* Tests for a path with multiple recursive directory wildcards.
* Matches **\** and **\a\**, but not **\a**b.
*
* NOTE: used \ in place of / above to avoid issues with multiline comments.
*
* Breakdown:
* (^|\/) # matches either the beginning of the string or a directory separator.
* \*\*\/ # matches a recursive directory wildcard "**" followed by a directory separator.
* (.*\/)? # optionally matches any number of characters followed by a directory separator.
* \*\* # matches a recursive directory wildcard "**"
* ($|\/) # matches either the end of the string or a directory separator.
*/
const invalidMultipleRecursionPatterns = /(^|\/)\*\*\/(.*\/)?\*\*($|\/)/;
/**
* Tests for a path containing a wildcard character in a directory component of the path.
* Matches \*\, \?\, and \a*b\, but not \a\ or \a\*.
*
* NOTE: used \ in place of / above to avoid issues with multiline comments.
*
* Breakdown:
* \/ # matches a directory separator.
* [^/]*? # matches any number of characters excluding directory separators (non-greedy).
* [*?] # matches either a wildcard character (* or ?)
* [^/]* # matches any number of characters excluding directory separators (greedy).
* \/ # matches a directory separator.
*/
const watchRecursivePattern = /\/[^/]*?[*?][^/]*\//;
/**
* Matches the portion of a wildcard path that does not contain wildcards.
* Matches \a of \a\*, or \a\b\c of \a\b\c\?\d.
*
* NOTE: used \ in place of / above to avoid issues with multiline comments.
*
* Breakdown:
* ^ # matches the beginning of the string
* [^*?]* # matches any number of non-wildcard characters
* (?=\/[^/]*[*?]) # lookahead that matches a directory separator followed by
* # a path component that contains at least one wildcard character (* or ?).
*/
const wildcardDirectoryPattern = /^[^*?]*(?=\/[^/]*[*?])/;
/**
* Expands an array of file specifications.
*
* @param fileNames The literal file names to include.
* @param include The wildcard file specifications to include.
* @param exclude The wildcard 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.
*/
function matchFileNames(fileNames: string[], include: string[], exclude: string[], basePath: string, options: CompilerOptions, host: ParseConfigHost, errors: Diagnostic[]): ExpandResult {
basePath = normalizePath(basePath);
// The exclude spec list is converted into a regular expression, which allows us to quickly
// test whether a file or directory should be excluded before recursively traversing the
// file system.
const keyMapper = host.useCaseSensitiveFileNames ? caseSensitiveKeyMapper : caseInsensitiveKeyMapper;
// Literal file names (provided via the "files" array in tsconfig.json) are stored in a
// file map with a possibly case insensitive key. We use this map later when when including
// wildcard paths.
const literalFileMap: Map<string> = {};
// Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a
// file map with a possibly case insensitive key. We use this map to store paths matched
// via wildcard, and to handle extension priority.
const wildcardFileMap: Map<string> = {};
if (include) {
include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false);
}
if (exclude) {
exclude = validateSpecs(exclude, errors, /*allowTrailingRecursion*/ true);
}
// Wildcard directories (provided as part of a wildcard path) are stored in a
// file map that marks whether it was a regular wildcard match (with a `*` or `?` token),
// or a recursive directory. This information is used by filesystem watchers to monitor for
// new entries in these paths.
const wildcardDirectories: Map<WatchDirectoryFlags> = getWildcardDirectories(include, exclude, basePath, host.useCaseSensitiveFileNames);
// Rather than requery this for each file and filespec, we query the supported extensions
// once and store it on the expansion context.
const supportedExtensions = getSupportedExtensions(options);
// Literal files are always included verbatim. An "include" or "exclude" specification cannot
// remove a literal file.
if (fileNames) {
for (const fileName of fileNames) {
const file = combinePaths(basePath, fileName);
literalFileMap[keyMapper(file)] = file;
}
}
if (include && include.length > 0) {
for (const file of host.readDirectory(basePath, supportedExtensions, exclude, include)) {
// If we have already included a literal or wildcard path with a
// higher priority extension, we should skip this file.
//
// This handles cases where we may encounter both <file>.ts and
// <file>.d.ts (or <file>.js if "allowJs" is enabled) in the same
// directory when they are compilation outputs.
if (hasFileWithHigherPriorityExtension(file, literalFileMap, wildcardFileMap, supportedExtensions, keyMapper)) {
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
// same directory, we should remove it.
removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper);
const key = keyMapper(file);
if (!hasProperty(literalFileMap, key) && !hasProperty(wildcardFileMap, key)) {
wildcardFileMap[key] = file;
}
}
}
const literalFiles = reduceProperties(literalFileMap, addFileToOutput, []);
const wildcardFiles = reduceProperties(wildcardFileMap, addFileToOutput, []);
wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive);
return {
fileNames: literalFiles.concat(wildcardFiles),
wildcardDirectories
};
}
function validateSpecs(specs: string[], errors: Diagnostic[], allowTrailingRecursion: boolean) {
const validSpecs: string[] = [];
for (const spec of specs) {
if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) {
errors.push(createCompilerDiagnostic(Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec));
}
else if (invalidMultipleRecursionPatterns.test(spec)) {
errors.push(createCompilerDiagnostic(Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, spec));
}
else {
validSpecs.push(spec);
}
}
return validSpecs;
}
/**
* Gets directories in a set of include patterns that should be watched for changes.
*/
function getWildcardDirectories(include: string[], exclude: string[], path: string, useCaseSensitiveFileNames: boolean) {
// We watch a directory recursively if it contains a wildcard anywhere in a directory segment
// of the pattern:
//
// /a/b/**/d - Watch /a/b recursively to catch changes to any d in any subfolder recursively
// /a/b/*/d - Watch /a/b recursively to catch any d in any immediate subfolder, even if a new subfolder is added
//
// We watch a directory without recursion if it contains a wildcard in the file segment of
// the pattern:
//
// /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, path, "exclude");
const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i");
const wildcardDirectories: Map<WatchDirectoryFlags> = {};
if (include !== undefined) {
const recursiveKeys: string[] = [];
for (const file of include) {
const name = combinePaths(path, file);
if (excludeRegex && excludeRegex.test(name)) {
continue;
}
const match = wildcardDirectoryPattern.exec(name);
if (match) {
const key = useCaseSensitiveFileNames ? match[0] : match[0].toLowerCase();
const flags = watchRecursivePattern.test(name) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None;
const existingFlags = getProperty(wildcardDirectories, key);
if (existingFlags === undefined || existingFlags < flags) {
wildcardDirectories[key] = flags;
if (flags === WatchDirectoryFlags.Recursive) {
recursiveKeys.push(key);
}
}
}
}
// Remove any subpaths under an existing recursively watched directory.
for (const key in wildcardDirectories) {
if (hasProperty(wildcardDirectories, key)) {
for (const recursiveKey of recursiveKeys) {
if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) {
delete wildcardDirectories[key];
}
}
}
}
}
return wildcardDirectories;
}
/**
* Determines whether a literal or wildcard file has already been included that has a higher
* extension priority.
*
* @param file The path to the file.
* @param extensionPriority The priority of the extension.
* @param context The expansion context.
*/
function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map<string>, wildcardFiles: Map<string>, extensions: string[], keyMapper: (value: string) => string) {
const extensionPriority = getExtensionPriority(file, extensions);
const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority);
for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) {
const higherPriorityExtension = extensions[i];
const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension));
if (hasProperty(literalFiles, higherPriorityPath) || hasProperty(wildcardFiles, higherPriorityPath)) {
return true;
}
}
return false;
}
/**
* Removes files included via wildcard expansion with a lower extension priority that have
* already been included.
*
* @param file The path to the file.
* @param extensionPriority The priority of the extension.
* @param context The expansion context.
*/
function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map<string>, extensions: string[], keyMapper: (value: string) => string) {
const extensionPriority = getExtensionPriority(file, extensions);
const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority);
for (let i = nextExtensionPriority; i < extensions.length; i++) {
const lowerPriorityExtension = extensions[i];
const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension));
delete wildcardFiles[lowerPriorityPath];
}
}
/**
* 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;
}
/**
* 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();
}
}