Skip to content

Commit 2de4ad0

Browse files
committed
Merge pull request #818 from Microsoft/sourceMapRelativeDirName
Fixes incorrect path resolution of sources to sourcemap when the directory paths differ in only case
2 parents 85fd141 + 4cf7874 commit 2de4ad0

25 files changed

+881
-29
lines changed

src/compiler/core.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ module ts {
476476
}
477477
}
478478

479-
export function getRelativePathToDirectoryOrUrl(directoryPathOrUrl: string, relativeOrAbsolutePath: string, currentDirectory: string, isAbsolutePathAnUrl: boolean) {
479+
export function getRelativePathToDirectoryOrUrl(directoryPathOrUrl: string, relativeOrAbsolutePath: string, currentDirectory: string, getCanonicalFileName: (fileName: string) => string, isAbsolutePathAnUrl: boolean) {
480480
var pathComponents = getNormalizedPathOrUrlComponents(relativeOrAbsolutePath, currentDirectory);
481481
var directoryComponents = getNormalizedPathOrUrlComponents(directoryPathOrUrl, currentDirectory);
482482
if (directoryComponents.length > 1 && directoryComponents[directoryComponents.length - 1] === "") {
@@ -487,7 +487,7 @@ module ts {
487487

488488
// Find the component that differs
489489
for (var joinStartIndex = 0; joinStartIndex < pathComponents.length && joinStartIndex < directoryComponents.length; joinStartIndex++) {
490-
if (directoryComponents[joinStartIndex] !== pathComponents[joinStartIndex]) {
490+
if (getCanonicalFileName(directoryComponents[joinStartIndex]) !== getCanonicalFileName(pathComponents[joinStartIndex])) {
491491
break;
492492
}
493493
}

src/compiler/emitter.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,8 @@ module ts {
527527
sourceMapData.sourceMapSources.push(getRelativePathToDirectoryOrUrl(sourcesDirectoryPath,
528528
node.filename,
529529
compilerHost.getCurrentDirectory(),
530-
/*isAbsolutePathAnUrl*/ true));
530+
compilerHost.getCanonicalFileName,
531+
/*isAbsolutePathAnUrl*/ true));
531532
sourceMapSourceIndex = sourceMapData.sourceMapSources.length - 1;
532533

533534
// The one that can be used from program to get the actual source file
@@ -669,7 +670,8 @@ module ts {
669670
getDirectoryPath(normalizePath(jsFilePath)), // get the relative sourceMapDir path based on jsFilePath
670671
combinePaths(sourceMapDir, sourceMapData.jsSourceMappingURL), // this is where user expects to see sourceMap
671672
compilerHost.getCurrentDirectory(),
672-
/*isAbsolutePathAnUrl*/ true);
673+
compilerHost.getCanonicalFileName,
674+
/*isAbsolutePathAnUrl*/ true);
673675
}
674676
else {
675677
sourceMapData.jsSourceMappingURL = combinePaths(sourceMapDir, sourceMapData.jsSourceMappingURL);
@@ -3147,7 +3149,8 @@ module ts {
31473149
getDirectoryPath(normalizeSlashes(jsFilePath)),
31483150
declFileName,
31493151
compilerHost.getCurrentDirectory(),
3150-
/*isAbsolutePathAnUrl*/ false);
3152+
compilerHost.getCanonicalFileName,
3153+
/*isAbsolutePathAnUrl*/ false);
31513154

31523155
referencePathsOutput += "/// <reference path=\"" + declFileName + "\" />" + newLine;
31533156
}

src/harness/fourslash.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2128,31 +2128,23 @@ module FourSlash {
21282128
xmlData.push(xml);
21292129
}
21302130

2131-
// Cache these between executions so we don't have to re-parse them for every test
2132-
var fourslashSourceFile: ts.SourceFile = undefined;
2133-
21342131
export function runFourSlashTestContent(content: string, fileName: string): TestXmlData {
21352132
// Parse out the files and their metadata
21362133
var testData = parseTestData(content, fileName);
21372134

21382135
currentTestState = new TestState(testData);
21392136

21402137
var result = '';
2141-
var fourslashFilename = 'fourslash.ts';
2142-
var tsFn = 'tests/cases/fourslash/' + fourslashFilename;
2143-
fourslashSourceFile = fourslashSourceFile || ts.createSourceFile(tsFn, Harness.IO.readFile(tsFn), ts.ScriptTarget.ES5, /*version*/ "0", /*isOpen*/ false);
2144-
2145-
var files: { [filename: string]: ts.SourceFile; } = {};
2146-
files[Harness.Compiler.getCanonicalFileName(fourslashFilename)] = fourslashSourceFile;
2147-
files[Harness.Compiler.getCanonicalFileName(fileName)] = ts.createSourceFile(fileName, content, ts.ScriptTarget.ES5, /*version*/ "0", /*isOpen*/ false);
2148-
files[Harness.Compiler.getCanonicalFileName(Harness.Compiler.defaultLibFileName)] = Harness.Compiler.defaultLibSourceFile;
2149-
2150-
var host = Harness.Compiler.createCompilerHost(files, (fn, contents) => result = contents);
2151-
var program = ts.createProgram([fourslashFilename, fileName], { out: "fourslashTestOutput.js" }, host);
2138+
var host = Harness.Compiler.createCompilerHost([{ unitName: Harness.Compiler.fourslashFilename, content: undefined },
2139+
{ unitName: fileName, content: content }],
2140+
(fn, contents) => result = contents,
2141+
ts.ScriptTarget.ES5,
2142+
sys.useCaseSensitiveFileNames);
2143+
var program = ts.createProgram([Harness.Compiler.fourslashFilename, fileName], { out: "fourslashTestOutput.js" }, host);
21522144
var checker = ts.createTypeChecker(program, /*fullTypeCheckMode*/ true);
21532145
checker.checkProgram();
21542146

2155-
var errs = checker.getDiagnostics(files[fileName]);
2147+
var errs = checker.getDiagnostics(program.getSourceFile(fileName));
21562148
if (errs.length > 0) {
21572149
throw new Error('Error compiling ' + fileName + ': ' + errs.map(e => e.messageText).join('\r\n'));
21582150
}

src/harness/harness.ts

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -534,18 +534,47 @@ module Harness {
534534
export var defaultLibFileName = 'lib.d.ts';
535535
export var defaultLibSourceFile = ts.createSourceFile(defaultLibFileName, IO.readFile(libFolder + 'lib.core.d.ts'), /*languageVersion*/ ts.ScriptTarget.ES5, /*version:*/ "0");
536536

537+
// Cache these between executions so we don't have to re-parse them for every test
538+
export var fourslashFilename = 'fourslash.ts';
539+
export var fourslashSourceFile: ts.SourceFile;
540+
537541
export function getCanonicalFileName(fileName: string): string {
538542
return sys.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
539543
}
540544

541-
export function createCompilerHost(filemap: { [filename: string]: ts.SourceFile; }, writeFile: (fn: string, contents: string, writeByteOrderMark:boolean) => void): ts.CompilerHost {
545+
export function createCompilerHost(inputFiles: { unitName: string; content: string; }[],
546+
writeFile: (fn: string, contents: string, writeByteOrderMark: boolean) => void,
547+
scriptTarget: ts.ScriptTarget,
548+
useCaseSensitiveFileNames: boolean): ts.CompilerHost {
549+
550+
// Local get canonical file name function, that depends on passed in parameter for useCaseSensitiveFileNames
551+
function getCanonicalFileName(fileName: string): string {
552+
return useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
553+
}
554+
555+
var filemap: { [filename: string]: ts.SourceFile; } = {};
556+
// Register input files
557+
function register(file: { unitName: string; content: string; }) {
558+
if (file.content !== undefined) {
559+
var filename = Path.switchToForwardSlashes(file.unitName);
560+
filemap[getCanonicalFileName(filename)] = ts.createSourceFile(filename, file.content, scriptTarget, /*version:*/ "0");
561+
}
562+
};
563+
inputFiles.forEach(register);
564+
542565
return {
543566
getCurrentDirectory: sys.getCurrentDirectory,
544567
getCancellationToken: (): any => undefined,
545568
getSourceFile: (fn, languageVersion) => {
546569
if (Object.prototype.hasOwnProperty.call(filemap, getCanonicalFileName(fn))) {
547570
return filemap[getCanonicalFileName(fn)];
548-
} else {
571+
}
572+
else if (fn === fourslashFilename) {
573+
var tsFn = 'tests/cases/fourslash/' + fourslashFilename;
574+
fourslashSourceFile = fourslashSourceFile || ts.createSourceFile(tsFn, Harness.IO.readFile(tsFn), scriptTarget, /*version*/ "0", /*isOpen*/ false);
575+
return fourslashSourceFile;
576+
}
577+
else {
549578
var lib = defaultLibFileName;
550579
if (fn === defaultLibFileName) {
551580
return defaultLibSourceFile;
@@ -557,7 +586,7 @@ module Harness {
557586
getDefaultLibFilename: () => defaultLibFileName,
558587
writeFile: writeFile,
559588
getCanonicalFileName: getCanonicalFileName,
560-
useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames,
589+
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
561590
getNewLine: ()=> sys.newLine
562591
};
563592
}
@@ -614,7 +643,7 @@ module Harness {
614643
}
615644

616645
public compileFiles(inputFiles: { unitName: string; content: string }[],
617-
otherFiles: { unitName: string; content?: string }[],
646+
otherFiles: { unitName: string; content: string }[],
618647
onComplete: (result: CompilerResult, checker: ts.TypeChecker) => void,
619648
settingsCallback?: (settings: ts.CompilerOptions) => void,
620649
options?: ts.CompilerOptions) {
@@ -628,9 +657,10 @@ module Harness {
628657
settingsCallback(null);
629658
}
630659

660+
var useCaseSensitiveFileNames = sys.useCaseSensitiveFileNames;
631661
this.settings.forEach(setting => {
632662
switch (setting.flag.toLowerCase()) {
633-
// "filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outDir", "noimplicitany", "noresolve"
663+
// "filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve"
634664
case "module":
635665
case "modulegentarget":
636666
if (typeof setting.value === 'string') {
@@ -706,10 +736,13 @@ module Harness {
706736
options.removeComments = setting.value === 'false';
707737
break;
708738

739+
case 'usecasesensitivefilenames':
740+
useCaseSensitiveFileNames = setting.value === 'true';
741+
break;
742+
709743
case 'mapsourcefiles':
710744
case 'maproot':
711745
case 'generatedeclarationfiles':
712-
case 'usecasesensitivefileresolution':
713746
case 'gatherDiagnostics':
714747
case 'codepage':
715748
case 'createFileLog':
@@ -748,7 +781,10 @@ module Harness {
748781
var fileOutputs: GeneratedFile[] = [];
749782

750783
var programFiles = inputFiles.map(file => file.unitName);
751-
var program = ts.createProgram(programFiles, options, createCompilerHost(filemap, (fn, contents, writeByteOrderMark) => fileOutputs.push({ fileName: fn, code: contents, writeByteOrderMark: writeByteOrderMark })));
784+
var program = ts.createProgram(programFiles, options, createCompilerHost(inputFiles.concat(otherFiles),
785+
(fn, contents, writeByteOrderMark) => fileOutputs.push({ fileName: fn, code: contents, writeByteOrderMark: writeByteOrderMark }),
786+
options.target,
787+
useCaseSensitiveFileNames));
752788

753789
var hadParseErrors = program.getDiagnostics().length > 0;
754790

@@ -1034,7 +1070,7 @@ module Harness {
10341070
var optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*(\S*)/gm; // multiple matches on multiple lines
10351071

10361072
// List of allowed metadata names
1037-
var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outDir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation"];
1073+
var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames"];
10381074

10391075
function extractCompilerSettings(content: string): CompilerSetting[] {
10401076

src/harness/projectsRunner.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ class ProjectRunner extends RunnerBase {
226226
? filename
227227
: ts.normalizeSlashes(testCase.projectRoot) + "/" + ts.normalizeSlashes(filename);
228228

229-
var diskRelativeName = ts.getRelativePathToDirectoryOrUrl(testCase.projectRoot, diskFileName, getCurrentDirectory(), false);
229+
var diskRelativeName = ts.getRelativePathToDirectoryOrUrl(testCase.projectRoot, diskFileName,
230+
getCurrentDirectory(), Harness.Compiler.getCanonicalFileName, /*isAbsolutePathAnUrl*/ false);
230231
if (ts.isRootedDiskPath(diskRelativeName) || diskRelativeName.substr(0, 3) === "../") {
231232
// If the generated output file resides in the parent folder or is rooted path,
232233
// we need to instead create files that can live in the project reference folder

tests/baselines/reference/sourceMapWithCaseSensitiveFileNames.js

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/sourceMapWithCaseSensitiveFileNames.js.map

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
===================================================================
2+
JsFile: fooResult.js
3+
mapUrl: fooResult.js.map
4+
sourceRoot:
5+
sources: ../testFiles/app.ts,../testFiles/app2.ts
6+
===================================================================
7+
-------------------------------------------------------------------
8+
emittedFile:tests/cases/compiler/testfiles/fooResult.js
9+
sourceFile:../testFiles/app.ts
10+
-------------------------------------------------------------------
11+
>>>// Note in the out result we are using same folder name only different in casing
12+
1 >
13+
2 >
14+
3 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
4 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^->
16+
1 >// Note in the out result we are using same folder name only different in casing
17+
>// Since this is case sensitive, the folders are different and hence the relative paths in sourcemap shouldn't be just app.ts or app2.ts
18+
>
19+
2 >
20+
3 >// Note in the out result we are using same folder name only different in casing
21+
1 >Emitted(1, 1) Source(3, 1) + SourceIndex(0)
22+
2 >Emitted(1, 1) Source(1, 1) + SourceIndex(0)
23+
3 >Emitted(1, 81) Source(1, 81) + SourceIndex(0)
24+
---
25+
>>>// Since this is case sensitive, the folders are different and hence the relative paths in sourcemap shouldn't be just app.ts or app2.ts
26+
1->
27+
2 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
28+
1->
29+
>
30+
2 >// Since this is case sensitive, the folders are different and hence the relative paths in sourcemap shouldn't be just app.ts or app2.ts
31+
1->Emitted(2, 1) Source(2, 1) + SourceIndex(0)
32+
2 >Emitted(2, 137) Source(2, 137) + SourceIndex(0)
33+
---
34+
>>>var c = (function () {
35+
1 >^^^^
36+
2 > ^
37+
3 > ^^^^^^^^^^^^^^->
38+
1 >
39+
>class
40+
2 > c
41+
1 >Emitted(3, 5) Source(3, 7) + SourceIndex(0)
42+
2 >Emitted(3, 6) Source(3, 8) + SourceIndex(0)
43+
---
44+
>>> function c() {
45+
1->^^^^
46+
2 > ^^^^^^^^^
47+
3 > ^
48+
1->
49+
2 > class
50+
3 > c
51+
1->Emitted(4, 5) Source(3, 1) + SourceIndex(0) name (c)
52+
2 >Emitted(4, 14) Source(3, 7) + SourceIndex(0) name (c)
53+
3 >Emitted(4, 15) Source(3, 8) + SourceIndex(0) name (c)
54+
---
55+
>>> }
56+
1 >^^^^
57+
2 > ^
58+
3 > ^^^^^^^^^->
59+
1 > {
60+
>
61+
2 > }
62+
1 >Emitted(5, 5) Source(4, 1) + SourceIndex(0) name (c.constructor)
63+
2 >Emitted(5, 6) Source(4, 2) + SourceIndex(0) name (c.constructor)
64+
---
65+
>>> return c;
66+
1->^^^^
67+
2 > ^^^^^^^^
68+
1->
69+
2 > }
70+
1->Emitted(6, 5) Source(4, 1) + SourceIndex(0) name (c)
71+
2 >Emitted(6, 13) Source(4, 2) + SourceIndex(0) name (c)
72+
---
73+
>>>})();
74+
1 >
75+
2 >^
76+
3 >
77+
4 > ^^^^
78+
5 > ^^^^^^^^^^^^^^^^^^->
79+
1 >
80+
2 >}
81+
3 >
82+
4 > class c {
83+
> }
84+
1 >Emitted(7, 1) Source(4, 1) + SourceIndex(0) name (c)
85+
2 >Emitted(7, 2) Source(4, 2) + SourceIndex(0) name (c)
86+
3 >Emitted(7, 2) Source(3, 1) + SourceIndex(0)
87+
4 >Emitted(7, 6) Source(4, 2) + SourceIndex(0)
88+
---
89+
-------------------------------------------------------------------
90+
emittedFile:tests/cases/compiler/testfiles/fooResult.js
91+
sourceFile:../testFiles/app2.ts
92+
-------------------------------------------------------------------
93+
>>>var d = (function () {
94+
1->
95+
2 >^^^^
96+
3 > ^
97+
4 > ^^^^^^^^^^^^^^->
98+
1->
99+
2 >class
100+
3 > d
101+
1->Emitted(8, 1) Source(1, 1) + SourceIndex(1)
102+
2 >Emitted(8, 5) Source(1, 7) + SourceIndex(1)
103+
3 >Emitted(8, 6) Source(1, 8) + SourceIndex(1)
104+
---
105+
>>> function d() {
106+
1->^^^^
107+
2 > ^^^^^^^^^
108+
3 > ^
109+
1->
110+
2 > class
111+
3 > d
112+
1->Emitted(9, 5) Source(1, 1) + SourceIndex(1) name (d)
113+
2 >Emitted(9, 14) Source(1, 7) + SourceIndex(1) name (d)
114+
3 >Emitted(9, 15) Source(1, 8) + SourceIndex(1) name (d)
115+
---
116+
>>> }
117+
1 >^^^^
118+
2 > ^
119+
3 > ^^^^^^^^^->
120+
1 > {
121+
>
122+
2 > }
123+
1 >Emitted(10, 5) Source(2, 1) + SourceIndex(1) name (d.constructor)
124+
2 >Emitted(10, 6) Source(2, 2) + SourceIndex(1) name (d.constructor)
125+
---
126+
>>> return d;
127+
1->^^^^
128+
2 > ^^^^^^^^
129+
1->
130+
2 > }
131+
1->Emitted(11, 5) Source(2, 1) + SourceIndex(1) name (d)
132+
2 >Emitted(11, 13) Source(2, 2) + SourceIndex(1) name (d)
133+
---
134+
>>>})();
135+
1 >
136+
2 >^
137+
3 >
138+
4 > ^^^^
139+
5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^->
140+
1 >
141+
2 >}
142+
3 >
143+
4 > class d {
144+
> }
145+
1 >Emitted(12, 1) Source(2, 1) + SourceIndex(1) name (d)
146+
2 >Emitted(12, 2) Source(2, 2) + SourceIndex(1) name (d)
147+
3 >Emitted(12, 2) Source(1, 1) + SourceIndex(1)
148+
4 >Emitted(12, 6) Source(2, 2) + SourceIndex(1)
149+
---
150+
>>>//# sourceMappingURL=fooResult.js.map

0 commit comments

Comments
 (0)