Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
7 changes: 6 additions & 1 deletion src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,12 @@ namespace ts {
},
description: Diagnostics.Specifies_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6,
error: Diagnostics.Argument_for_moduleResolution_option_must_be_node_or_classic,
}
},
{
name: "forceConsistentCasingInFileNames",
type: "boolean",
description: Diagnostics.Disallow_inconsistently_cased_references_to_the_same_file
},
];

/* @internal */
Expand Down
46 changes: 28 additions & 18 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,55 @@ namespace ts {
True = -1
}

export function createFileMap<T>(getCanonicalFileName: (fileName: string) => string): FileMap<T> {
export function createFileMap<T>(keyMapper?: (key: string) => string): FileMap<T> {
let files: Map<T> = {};
return {
get,
set,
contains,
remove,
clear,
forEachValue: forEachValueInMap
forEachValue: forEachValueInMap,
clear
};

function set(fileName: string, value: T) {
files[normalizeKey(fileName)] = value;
function forEachValueInMap(f: (key: Path, value: T) => void) {
for (let key in files) {
f(<Path>key, files[key]);
}
}

function get(fileName: string) {
return files[normalizeKey(fileName)];
// path should already be well-formed so it does not need to be normalized
function get(path: Path): T {
return files[toKey(path)];
}

function contains(fileName: string) {
return hasProperty(files, normalizeKey(fileName));
function set(path: Path, value: T) {
files[toKey(path)] = value;
}

function remove (fileName: string) {
let key = normalizeKey(fileName);
delete files[key];
function contains(path: Path) {
return hasProperty(files, toKey(path));
}

function forEachValueInMap(f: (value: T) => void) {
forEachValue(files, f);
}

function normalizeKey(key: string) {
return getCanonicalFileName(normalizeSlashes(key));
function remove(path: Path) {
const key = toKey(path);
delete files[key];
}

function clear() {
files = {};
}

function toKey(path: Path): string {
return keyMapper ? keyMapper(path) : path;
}
}

export function toPath(fileName: string, basePath: string, getCanonicalFileName: (path: string) => string): Path {
const nonCanonicalizedPath = isRootedDiskPath(fileName)
? normalizePath(fileName)
: getNormalizedAbsolutePath(fileName, basePath);
return <Path>getCanonicalFileName(nonCanonicalizedPath);
}

export const enum Comparison {
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2290,7 +2290,10 @@
"category": "Message",
"code": 6072
},

"Disallow inconsistently-cased references to the same file.": {
"category": "Message",
"code": 6073
},
"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
"code": 7005
Expand Down
119 changes: 62 additions & 57 deletions src/compiler/program.ts

Large diffs are not rendered by default.

34 changes: 19 additions & 15 deletions src/compiler/tsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,16 @@ namespace ts {
return <string>diagnostic.messageText;
}

function reportDiagnostic(diagnostic: Diagnostic) {
function reportDiagnostic(diagnostic: Diagnostic, host: CompilerHost) {
let output = "";

if (diagnostic.file) {
let loc = getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
output += `${ diagnostic.file.fileName }(${ loc.line + 1 },${ loc.character + 1 }): `;
const relativeFileName = host
? convertToRelativePath(diagnostic.file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName))
: diagnostic.file.fileName;

output += `${ relativeFileName }(${ loc.line + 1 },${ loc.character + 1 }): `;
}

let category = DiagnosticCategory[diagnostic.category].toLowerCase();
Expand All @@ -95,9 +99,9 @@ namespace ts {
sys.write(output);
}

function reportDiagnostics(diagnostics: Diagnostic[]) {
function reportDiagnostics(diagnostics: Diagnostic[], host: CompilerHost) {
for (let i = 0; i < diagnostics.length; i++) {
reportDiagnostic(diagnostics[i]);
reportDiagnostic(diagnostics[i], host);
}
}

Expand Down Expand Up @@ -161,7 +165,7 @@ namespace ts {

if (commandLine.options.locale) {
if (!isJSONSupported()) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale"));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--locale"), /* compilerHost */ undefined);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
validateLocaleAndSetLanguage(commandLine.options.locale, commandLine.errors);
Expand All @@ -170,7 +174,7 @@ namespace ts {
// If there are any errors due to command line parsing and/or
// setting up localization, report them and quit.
if (commandLine.errors.length > 0) {
reportDiagnostics(commandLine.errors);
reportDiagnostics(commandLine.errors, compilerHost);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}

Expand All @@ -180,7 +184,7 @@ namespace ts {
}

if (commandLine.options.version) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Version_0, ts.version));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Version_0, ts.version), /* compilerHost */ undefined);
return sys.exit(ExitStatus.Success);
}

Expand All @@ -192,12 +196,12 @@ namespace ts {

if (commandLine.options.project) {
if (!isJSONSupported()) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--project"));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--project"), /* compilerHost */ undefined);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
configFileName = normalizePath(combinePaths(commandLine.options.project, "tsconfig.json"));
if (commandLine.fileNames.length !== 0) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_project_cannot_be_mixed_with_source_files_on_a_command_line), /* compilerHost */ undefined);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
}
Expand All @@ -215,7 +219,7 @@ namespace ts {
// Firefox has Object.prototype.watch
if (commandLine.options.watch && commandLine.options.hasOwnProperty("watch")) {
if (!sys.watchFile) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.The_current_host_does_not_support_the_0_option, "--watch"), /* compilerHost */ undefined);
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}
if (configFileName) {
Expand Down Expand Up @@ -251,7 +255,7 @@ namespace ts {
let configObject = result.config;
let configParseResult = parseJsonConfigFileContent(configObject, sys, getDirectoryPath(configFileName));
if (configParseResult.errors.length > 0) {
reportDiagnostics(configParseResult.errors);
reportDiagnostics(configParseResult.errors, /* compilerHost */ undefined);
sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
return;
}
Expand Down Expand Up @@ -444,7 +448,7 @@ namespace ts {
}
}

reportDiagnostics(diagnostics);
reportDiagnostics(diagnostics, compilerHost);

// If the user doesn't want us to emit, then we're done at this point.
if (compilerOptions.noEmit) {
Expand All @@ -455,7 +459,7 @@ namespace ts {

// Otherwise, emit and report any errors we ran into.
let emitOutput = program.emit();
reportDiagnostics(emitOutput.diagnostics);
reportDiagnostics(emitOutput.diagnostics, compilerHost);

// If the emitter didn't emit anything, then pass that value along.
if (emitOutput.emitSkipped) {
Expand Down Expand Up @@ -568,7 +572,7 @@ namespace ts {
let currentDirectory = sys.getCurrentDirectory();
let file = combinePaths(currentDirectory, "tsconfig.json");
if (sys.fileExists(file)) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.A_tsconfig_json_file_is_already_defined_at_Colon_0, file), /* compilerHost */ undefined);
}
else {
let compilerOptions = extend(options, defaultInitCompilerOptions);
Expand All @@ -583,7 +587,7 @@ namespace ts {
}

sys.writeFile(file, JSON.stringify(configurations, undefined, 4));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file));
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Successfully_created_a_tsconfig_json_file), /* compilerHost */ undefined);
}

return;
Expand Down
17 changes: 12 additions & 5 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ namespace ts {
[index: string]: T;
}

// branded string type used to store absolute, normalized and canonicalized paths
// arbitrary file name can be converted to Path via toPath function
export type Path = string & { __pathBrand: any };

export interface FileMap<T> {
get(fileName: string): T;
set(fileName: string, value: T): void;
contains(fileName: string): boolean;
remove(fileName: string): void;
forEachValue(f: (v: T) => void): void;
get(fileName: Path): T;
set(fileName: Path, value: T): void;
contains(fileName: Path): boolean;
remove(fileName: Path): void;

forEachValue(f: (key: Path, v: T) => void): void;
clear(): void;
}

Expand Down Expand Up @@ -1249,6 +1254,7 @@ namespace ts {
endOfFileToken: Node;

fileName: string;
/* internal */ path: Path;
text: string;

amdDependencies: {path: string; name: string}[];
Expand Down Expand Up @@ -2088,6 +2094,7 @@ namespace ts {
experimentalDecorators?: boolean;
emitDecoratorMetadata?: boolean;
moduleResolution?: ModuleResolutionKind;
forceConsistentCasingInFileNames?: boolean;
/* @internal */ stripInternal?: boolean;

// Skip checking lib.d.ts to help speed up tests.
Expand Down
7 changes: 6 additions & 1 deletion src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1349,7 +1349,6 @@ namespace ts {
export function tryResolveScriptReference(host: ScriptReferenceHost, sourceFile: SourceFile, reference: FileReference) {
if (!host.getCompilerOptions().noResolve) {
let referenceFileName = isRootedDiskPath(reference.fileName) ? reference.fileName : combinePaths(getDirectoryPath(sourceFile.fileName), reference.fileName);
referenceFileName = getNormalizedAbsolutePath(referenceFileName, host.getCurrentDirectory());
return host.getSourceFile(referenceFileName);
}
}
Expand Down Expand Up @@ -2175,6 +2174,12 @@ namespace ts {
return result;
}

export function convertToRelativePath(absoluteOrRelativePath: string, basePath: string, getCanonicalFileName: (path: string) => string): string {
return !isRootedDiskPath(absoluteOrRelativePath)
? absoluteOrRelativePath
: getRelativePathToDirectoryOrUrl(basePath, absoluteOrRelativePath, basePath, getCanonicalFileName, /* isAbsolutePathAnUrl */ false);
}

const carriageReturnLineFeed = "\r\n";
const lineFeed = "\n";
export function getNewLineCharacter(options: CompilerOptions): string {
Expand Down
4 changes: 3 additions & 1 deletion src/harness/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,9 @@ namespace Harness {
function register(file: { unitName: string; content: string; }) {
if (file.content !== undefined) {
let fileName = ts.normalizePath(file.unitName);
filemap[getCanonicalFileName(fileName)] = createSourceFileAndAssertInvariants(fileName, file.content, scriptTarget);
const sourceFile = createSourceFileAndAssertInvariants(fileName, file.content, scriptTarget);
filemap[getCanonicalFileName(fileName)] = sourceFile;
filemap[getCanonicalFileName(ts.getNormalizedAbsolutePath(fileName, getCurrentDirectory()))] = sourceFile;
}
};
inputFiles.forEach(register);
Expand Down
37 changes: 29 additions & 8 deletions src/harness/projectsRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class ProjectRunner extends RunnerBase {
}

function compileProjectFiles(moduleKind: ts.ModuleKind, getInputFiles: () => string[],
getSourceFileText: (fileName: string) => string,
getSourceFileTextImpl: (fileName: string) => string,
writeFile: (fileName: string, data: string, writeByteOrderMark: boolean) => void): CompileProjectFilesResult {

let program = ts.createProgram(getInputFiles(), createCompilerOptions(), createCompilerHost());
Expand Down Expand Up @@ -170,6 +170,11 @@ class ProjectRunner extends RunnerBase {
};
}

function getSourceFileText(fileName: string): string {
const text = getSourceFileTextImpl(fileName);
return text !== undefined ? text : getSourceFileTextImpl(ts.getNormalizedAbsolutePath(fileName, getCurrentDirectory()));
}

function getSourceFile(fileName: string, languageVersion: ts.ScriptTarget): ts.SourceFile {
let sourceFile: ts.SourceFile = undefined;
if (fileName === Harness.Compiler.defaultLibFileName) {
Expand All @@ -194,7 +199,7 @@ class ProjectRunner extends RunnerBase {
getCanonicalFileName: Harness.Compiler.getCanonicalFileName,
useCaseSensitiveFileNames: () => Harness.IO.useCaseSensitiveFileNames(),
getNewLine: () => Harness.IO.newLine(),
fileExists: fileName => getSourceFile(fileName, ts.ScriptTarget.ES5) !== undefined,
fileExists: fileName => fileName === Harness.Compiler.defaultLibFileName || getSourceFileText(fileName) !== undefined,
readFile: fileName => Harness.IO.readFile(fileName)
};
}
Expand Down Expand Up @@ -229,12 +234,15 @@ class ProjectRunner extends RunnerBase {
}

function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
// convert file name to rooted name
// if filename is not rooted - concat it with project root and then expand project root relative to current directory
let diskFileName = ts.isRootedDiskPath(fileName)
? fileName
: ts.normalizeSlashes(testCase.projectRoot) + "/" + ts.normalizeSlashes(fileName);
: Harness.IO.resolvePath(ts.normalizeSlashes(testCase.projectRoot) + "/" + ts.normalizeSlashes(fileName));

let diskRelativeName = ts.getRelativePathToDirectoryOrUrl(testCase.projectRoot, diskFileName,
getCurrentDirectory(), Harness.Compiler.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
let currentDirectory = getCurrentDirectory();
// compute file name relative to current directory (expanded project root)
let diskRelativeName = ts.getRelativePathToDirectoryOrUrl(currentDirectory, diskFileName, currentDirectory, Harness.Compiler.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
if (ts.isRootedDiskPath(diskRelativeName) || diskRelativeName.substr(0, 3) === "../") {
// If the generated output file resides in the parent folder or is rooted path,
// we need to instead create files that can live in the project reference folder
Expand Down Expand Up @@ -318,7 +326,16 @@ class ProjectRunner extends RunnerBase {
return ts.map(allInputFiles, outputFile => outputFile.emittedFileName);
}
function getSourceFileText(fileName: string): string {
return ts.forEach(allInputFiles, inputFile => inputFile.emittedFileName === fileName ? inputFile.code : undefined);
for (const inputFile of allInputFiles) {
const isMatchingFile = ts.isRootedDiskPath(fileName)
? ts.getNormalizedAbsolutePath(inputFile.emittedFileName, getCurrentDirectory()) === fileName
: inputFile.emittedFileName === fileName;

if (isMatchingFile) {
return inputFile.code;
}
}
return undefined;
}

function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
Expand Down Expand Up @@ -359,8 +376,12 @@ class ProjectRunner extends RunnerBase {
runTest: testCase.runTest,
bug: testCase.bug,
rootDir: testCase.rootDir,
resolvedInputFiles: ts.map(compilerResult.program.getSourceFiles(), inputFile => inputFile.fileName),
emittedFiles: ts.map(compilerResult.outputFiles, outputFile => outputFile.emittedFileName)
resolvedInputFiles: ts.map(compilerResult.program.getSourceFiles(), inputFile => {
return ts.convertToRelativePath(inputFile.fileName, getCurrentDirectory(), path => Harness.Compiler.getCanonicalFileName(path));
}),
emittedFiles: ts.map(compilerResult.outputFiles, outputFile => {
return ts.convertToRelativePath(outputFile.emittedFileName, getCurrentDirectory(), path => Harness.Compiler.getCanonicalFileName(path));
})
};

return resolutionInfo;
Expand Down
Loading