Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 2 additions & 1 deletion Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ var harnessSources = harnessCoreSources.concat([
"reuseProgramStructure.ts",
"cachingInServerLSHost.ts",
"moduleResolution.ts",
"tsconfigParsing.ts"
"tsconfigParsing.ts",
"expandFiles.ts"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this file to the tsconfig.json files in src\**\

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no tsconfig.json in either src/harness or tests/cases/unittests

].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
Expand Down
827 changes: 794 additions & 33 deletions src/compiler/commandLineParser.ts

Large diffs are not rendered by default.

158 changes: 157 additions & 1 deletion src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ namespace ts {
contains,
remove,
forEachValue: forEachValueInMap,
clear
reduceProperties: reducePropertiesInMap,
clear,
mergeFrom
};

function forEachValueInMap(f: (key: Path, value: T) => void) {
Expand All @@ -34,6 +36,10 @@ namespace ts {
}
}

function reducePropertiesInMap<U>(callback: (memo: U, value: T, key: Path) => U, initial: U) {
return reduceProperties(files, callback, initial);
}

// path should already be well-formed so it does not need to be normalized
function get(path: Path): T {
return files[toKey(path)];
Expand All @@ -56,6 +62,16 @@ namespace ts {
files = {};
}

function mergeFrom(other: FileMap<T>) {
other.forEachValue(mergeFromOther);
}

function mergeFromOther(key: Path, value: T) {
if (!contains(key)) {
set(key, value);
}
}

function toKey(path: Path): string {
return keyMapper ? keyMapper(path) : path;
}
Expand Down Expand Up @@ -490,6 +506,24 @@ namespace ts {
return a < b ? Comparison.LessThan : Comparison.GreaterThan;
}

export function compareStrings(a: string, b: string, ignoreCase?: boolean): Comparison {
if (a === b) return Comparison.EqualTo;
if (a === undefined) return Comparison.LessThan;
if (b === undefined) return Comparison.GreaterThan;
if (ignoreCase) {
if (String.prototype.localeCompare) {
const result = a.localeCompare(b, /*locales*/ undefined, { usage: "sort", sensitivity: "accent" });
return result < 0 ? Comparison.LessThan : result > 0 ? Comparison.GreaterThan : Comparison.EqualTo;
}

a = a.toUpperCase();
b = b.toUpperCase();
if (a === b) return Comparison.EqualTo;
}

return a < b ? Comparison.LessThan : Comparison.GreaterThan;
}

function getDiagnosticFileName(diagnostic: Diagnostic): string {
return diagnostic.file ? diagnostic.file.fileName : undefined;
}
Expand Down Expand Up @@ -756,6 +790,71 @@ namespace ts {
return path1 + directorySeparator + path2;
}

/**
* Removes a trailing directory separator from a path.
* @param path The path.
*/
export function removeTrailingDirectorySeparator(path: string) {
if (path.charAt(path.length - 1) === directorySeparator) {
return path.substr(0, path.length - 1);
}

return path;
}

/**
* Adds a trailing directory separator to a path, if it does not already have one.
* @param path The path.
*/
export function ensureTrailingDirectorySeparator(path: string) {
if (path.charAt(path.length - 1) !== directorySeparator) {
return path + directorySeparator;
}

return path;
}

export function comparePaths(a: string, b: string, currentDirectory: string, ignoreCase?: boolean) {
if (a === b) return Comparison.EqualTo;
if (a === undefined) return Comparison.LessThan;
if (b === undefined) return Comparison.GreaterThan;
a = removeTrailingDirectorySeparator(a);
b = removeTrailingDirectorySeparator(b);
const aComponents = getNormalizedPathComponents(a, currentDirectory);
const bComponents = getNormalizedPathComponents(b, currentDirectory);
const sharedLength = Math.min(aComponents.length, bComponents.length);
for (let i = 0; i < sharedLength; ++i) {
const result = compareStrings(aComponents[i], bComponents[i], ignoreCase);
if (result !== Comparison.EqualTo) {
return result;
}
}

return compareValues(aComponents.length, bComponents.length);
}

export function containsPath(parent: string, child: string, currentDirectory: string, ignoreCase?: boolean) {
if (parent === undefined || child === undefined) return false;
if (parent === child) return true;
parent = removeTrailingDirectorySeparator(parent);
child = removeTrailingDirectorySeparator(child);
if (parent === child) return true;
const parentComponents = getNormalizedPathComponents(parent, currentDirectory);
const childComponents = getNormalizedPathComponents(child, currentDirectory);
if (childComponents.length < parentComponents.length) {
return false;
}

for (let i = 0; i < parentComponents.length; ++i) {
const result = compareStrings(parentComponents[i], childComponents[i], ignoreCase);
if (result !== Comparison.EqualTo) {
return false;
}
}

return true;
}

export function fileExtensionIs(path: string, extension: string): boolean {
const pathLen = path.length;
const extLen = extension.length;
Expand Down Expand Up @@ -784,6 +883,59 @@ namespace ts {
return false;
}

/**
* Extension boundaries by priority. Lower numbers indicate higher priorities, and are
* aligned to the offset of the highest priority extension in the
* allSupportedExtensions array.
*/
export const enum ExtensionPriority {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any specific reason for these values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a formal way to specify the offsets to each extension in allSupportedExtensions, and is used to disambiguate between file.ts and file.d.ts (or also file.js with -allowJs) in the same directory. This could have just been constant values, but I felt this was more descriptive.

TypeScriptFiles = 0,
DeclarationAndJavaScriptFiles = 2,
Limit = 5,

Highest = TypeScriptFiles,
Lowest = DeclarationAndJavaScriptFiles,
}

export function getExtensionPriority(path: string, supportedExtensions: string[]): ExtensionPriority {
for (let i = supportedExtensions.length - 1; i >= 0; i--) {
if (fileExtensionIs(path, supportedExtensions[i])) {
return adjustExtensionPriority(<ExtensionPriority>i);
}
}

// If its not in the list of supported extensions, this is likely a
// TypeScript file with a non-ts extension
return ExtensionPriority.Highest;
}

/**
* Adjusts an extension priority to be the highest priority within the same range.
*/
export function adjustExtensionPriority(extensionPriority: ExtensionPriority): ExtensionPriority {
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
return ExtensionPriority.TypeScriptFiles;
}
else if (extensionPriority < ExtensionPriority.Limit) {
return ExtensionPriority.DeclarationAndJavaScriptFiles;
}
else {
return ExtensionPriority.Limit;
}
}

/**
* Gets the next lowest extension priority for a given priority.
*/
export function getNextLowestExtensionPriority(extensionPriority: ExtensionPriority): ExtensionPriority {
if (extensionPriority < ExtensionPriority.DeclarationAndJavaScriptFiles) {
return ExtensionPriority.DeclarationAndJavaScriptFiles;
}
else {
return ExtensionPriority.Limit;
}
}

const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"];
export function removeFileExtension(path: string): string {
for (const ext of extensionsToRemove) {
Expand All @@ -794,6 +946,10 @@ namespace ts {
return path;
}

export function changeExtension<T extends string | Path>(path: T, newExtension: string): T {
return <T>(removeFileExtension(path) + newExtension);
}

const backslashOrDoubleQuote = /[\"\\]/g;
const escapedCharsRegExp = /[\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const escapedCharsMap: Map<string> = {
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,14 @@
"category": "Error",
"code": 5009
},
"File specification cannot end in a recursive directory wildcard ('**'): '{0}'.": {
"category": "Error",
"code": 5010
},
"File specification cannot contain multiple recursive directory wildcards ('**'): '{0}'.": {
"category": "Error",
"code": 5011
},
"Cannot read file '{0}': {1}": {
"category": "Error",
"code": 5012
Expand Down
46 changes: 45 additions & 1 deletion src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ namespace ts {
getExecutingFilePath(): string;
getCurrentDirectory(): string;
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
readFileNames(path: string): string[];
readDirectoryNames(path: string): string[];
getMemoryUsage?(): number;
exit(exitCode?: number): void;
}
Expand Down Expand Up @@ -60,6 +62,8 @@ namespace ts {
readFile(path: string): string;
writeFile(path: string, contents: string): void;
readDirectory(path: string, extension?: string, exclude?: string[]): string[];
readDirectoryNames(path: string): string[];
readFileNames(path: string): string[];
};

export var sys: System = (function () {
Expand Down Expand Up @@ -170,6 +174,16 @@ namespace ts {
}
}

function readFileNames(path: string): string[] {
const folder = fso.GetFolder(path || ".");
return getNames(folder.files);
}

function readDirectoryNames(path: string): string[] {
const folder = fso.GetFolder(path || ".");
return getNames(folder.directories);
}

return {
args,
newLine: "\r\n",
Expand Down Expand Up @@ -200,6 +214,8 @@ namespace ts {
return new ActiveXObject("WScript.Shell").CurrentDirectory;
},
readDirectory,
readFileNames,
readDirectoryNames,
exit(exitCode?: number): void {
try {
WScript.Quit(exitCode);
Expand Down Expand Up @@ -397,6 +413,30 @@ namespace ts {
}
}

function readFileNames(path: string): string[] {
const entries = _fs.readdirSync(path || ".");
const files: string[] = [];
for (const entry of entries) {
const stat = _fs.statSync(combinePaths(path, entry));
if (stat.isFile()) {
files.push(entry);
}
}
return files.sort();
}

function readDirectoryNames(path: string): string[] {
const entries = _fs.readdirSync(path || ".");
const directories: string[] = [];
for (const entry of entries) {
const stat = _fs.statSync(combinePaths(path, entry));
if (stat.isDirectory()) {
directories.push(entry);
}
}
return directories.sort();
}

return {
args: process.argv.slice(2),
newLine: _os.EOL,
Expand Down Expand Up @@ -442,7 +482,7 @@ namespace ts {
return _path.resolve(path);
},
fileExists(path: string): boolean {
return _fs.existsSync(path);
return _fs.existsSync(path) && _fs.statSync(path).isFile();
},
directoryExists(path: string) {
return _fs.existsSync(path) && _fs.statSync(path).isDirectory();
Expand All @@ -459,6 +499,8 @@ namespace ts {
return process.cwd();
},
readDirectory,
readFileNames,
readDirectoryNames,
getMemoryUsage() {
if (global.gc) {
global.gc();
Expand Down Expand Up @@ -497,6 +539,8 @@ namespace ts {
getExecutingFilePath: () => ChakraHost.executingFile,
getCurrentDirectory: () => ChakraHost.currentDirectory,
readDirectory: ChakraHost.readDirectory,
readFileNames: ChakraHost.readFileNames,
readDirectoryNames: ChakraHost.readDirectoryNames,
exit: ChakraHost.quit,
};
}
Expand Down
39 changes: 39 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace ts {
remove(fileName: Path): void;

forEachValue(f: (key: Path, v: T) => void): void;
reduceProperties<U>(f: (memo: U, value: T, key: Path) => U, initial: U): U;
mergeFrom(other: FileMap<T>): void;
clear(): void;
}

Expand Down Expand Up @@ -1582,7 +1584,33 @@ namespace ts {
}

export interface ParseConfigHost {
useCaseSensitiveFileNames: boolean;

readDirectory(rootDir: string, extension: string, exclude: string[]): string[];

/**
* Gets a value indicating whether the specified path exists and is a file.
* @param path The path to test.
*/
fileExists(path: string): boolean;

/**
* Gets a value indicating whether the specified path exists and is a directory.
* @param path The path to test.
*/
directoryExists(path: string): boolean;

/**
* Reads the files names in the directory.
* @param rootDir The directory path.
*/
readFileNames(rootDir: string): string[];

/**
* Reads the directory names in the directory.
* @param rootDir The directory path.
*/
readDirectoryNames(rootDir: string): string[];
}

export interface WriteFileCallback {
Expand Down Expand Up @@ -2439,6 +2467,17 @@ namespace ts {
options: CompilerOptions;
fileNames: string[];
errors: Diagnostic[];
wildcardDirectories?: Map<WatchDirectoryFlags>;
}

export const enum WatchDirectoryFlags {
None = 0,
Recursive = 1 << 0,
}

export interface ExpandResult {
fileNames: string[];
wildcardDirectories: Map<WatchDirectoryFlags>;
}

/* @internal */
Expand Down
Loading