Skip to content

Commit 7f8e21b

Browse files
committed
Merge pull request #2772 from Microsoft/rootDir
Add new compiler option --rootDir
2 parents 02a480d + ed2e105 commit 7f8e21b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+899
-35
lines changed

src/compiler/commandLineParser.ts

+7
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ module ts {
110110
type: "boolean",
111111
description: Diagnostics.Do_not_emit_comments_to_output,
112112
},
113+
{
114+
name: "rootDir",
115+
type: "string",
116+
isFilePath: true,
117+
description: Diagnostics.Specifies_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir,
118+
paramType: Diagnostics.LOCATION,
119+
},
113120
{
114121
name: "separateCompilation",
115122
type: "boolean",

src/compiler/diagnosticInformationMap.generated.ts

+2
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,8 @@ module ts {
496496
Suppress_noImplicitAny_errors_for_indexing_objects_lacking_index_signatures: { code: 6055, category: DiagnosticCategory.Message, key: "Suppress noImplicitAny errors for indexing objects lacking index signatures." },
497497
Do_not_emit_declarations_for_code_that_has_an_internal_annotation: { code: 6056, category: DiagnosticCategory.Message, key: "Do not emit declarations for code that has an '@internal' annotation." },
498498
Preserve_new_lines_when_emitting_code: { code: 6057, category: DiagnosticCategory.Message, key: "Preserve new-lines when emitting code." },
499+
Specifies_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir: { code: 6058, category: DiagnosticCategory.Message, key: "Specifies the root directory of input files. Use to control the output directory structure with --outDir." },
500+
File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files: { code: 6059, category: DiagnosticCategory.Error, key: "File '{0}' is not under 'rootDir' '{1}'. 'rootDir' is expected to contain all source files." },
499501
Variable_0_implicitly_has_an_1_type: { code: 7005, category: DiagnosticCategory.Error, key: "Variable '{0}' implicitly has an '{1}' type." },
500502
Parameter_0_implicitly_has_an_1_type: { code: 7006, category: DiagnosticCategory.Error, key: "Parameter '{0}' implicitly has an '{1}' type." },
501503
Member_0_implicitly_has_an_1_type: { code: 7008, category: DiagnosticCategory.Error, key: "Member '{0}' implicitly has an '{1}' type." },

src/compiler/diagnosticMessages.json

+9
Original file line numberDiff line numberDiff line change
@@ -1972,6 +1972,15 @@
19721972
"category": "Message",
19731973
"code": 6057
19741974
},
1975+
"Specifies the root directory of input files. Use to control the output directory structure with --outDir.": {
1976+
"category": "Message",
1977+
"code": 6058
1978+
},
1979+
"File '{0}' is not under 'rootDir' '{1}'. 'rootDir' is expected to contain all source files.": {
1980+
"category": "Error",
1981+
"code": 6059
1982+
},
1983+
19751984

19761985
"Variable '{0}' implicitly has an '{1}' type.": {
19771986
"category": "Error",

src/compiler/program.ts

+69-34
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,66 @@ module ts {
455455
}
456456
}
457457

458+
function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
459+
let commonPathComponents: string[];
460+
let currentDirectory = host.getCurrentDirectory();
461+
forEach(files, sourceFile => {
462+
// Each file contributes into common source file path
463+
if (isDeclarationFile(sourceFile)) {
464+
return;
465+
}
466+
467+
let sourcePathComponents = getNormalizedPathComponents(sourceFile.fileName, currentDirectory);
468+
sourcePathComponents.pop(); // The base file name is not part of the common directory path
469+
470+
if (!commonPathComponents) {
471+
// first file
472+
commonPathComponents = sourcePathComponents;
473+
return;
474+
}
475+
476+
for (let i = 0, n = Math.min(commonPathComponents.length, sourcePathComponents.length); i < n; i++) {
477+
if (commonPathComponents[i] !== sourcePathComponents[i]) {
478+
if (i === 0) {
479+
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
480+
return;
481+
}
482+
483+
// New common path found that is 0 -> i-1
484+
commonPathComponents.length = i;
485+
break;
486+
}
487+
}
488+
489+
// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
490+
if (sourcePathComponents.length < commonPathComponents.length) {
491+
commonPathComponents.length = sourcePathComponents.length;
492+
}
493+
});
494+
495+
return getNormalizedPathFromPathComponents(commonPathComponents);
496+
}
497+
498+
function checkSourceFilesBelongToPath(sourceFiles: SourceFile[], rootDirectory: string): boolean {
499+
let allFilesBelongToPath = true;
500+
if (sourceFiles) {
501+
let currentDirectory = host.getCurrentDirectory();
502+
let absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));
503+
504+
for (var sourceFile of sourceFiles) {
505+
if (!isDeclarationFile(sourceFile)) {
506+
let absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
507+
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
508+
diagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, options.rootDir));
509+
allFilesBelongToPath = false;
510+
}
511+
}
512+
}
513+
}
514+
515+
return allFilesBelongToPath;
516+
}
517+
458518
function verifyCompilerOptions() {
459519
if (options.separateCompilation) {
460520
if (options.sourceMap) {
@@ -517,41 +577,16 @@ module ts {
517577
(options.mapRoot && // there is --mapRoot specified and there would be multiple js files generated
518578
(!options.out || firstExternalModuleSourceFile !== undefined))) {
519579

520-
let commonPathComponents: string[];
521-
forEach(files, sourceFile => {
522-
// Each file contributes into common source file path
523-
if (!(sourceFile.flags & NodeFlags.DeclarationFile)
524-
&& !fileExtensionIs(sourceFile.fileName, ".js")) {
525-
let sourcePathComponents = getNormalizedPathComponents(sourceFile.fileName, host.getCurrentDirectory());
526-
sourcePathComponents.pop(); // FileName is not part of directory
527-
if (commonPathComponents) {
528-
for (let i = 0; i < Math.min(commonPathComponents.length, sourcePathComponents.length); i++) {
529-
if (commonPathComponents[i] !== sourcePathComponents[i]) {
530-
if (i === 0) {
531-
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
532-
return;
533-
}
534-
535-
// New common path found that is 0 -> i-1
536-
commonPathComponents.length = i;
537-
break;
538-
}
539-
}
540-
541-
// If the fileComponent path completely matched and less than already found update the length
542-
if (sourcePathComponents.length < commonPathComponents.length) {
543-
commonPathComponents.length = sourcePathComponents.length;
544-
}
545-
}
546-
else {
547-
// first file
548-
commonPathComponents = sourcePathComponents;
549-
}
550-
}
551-
});
580+
if (options.rootDir && checkSourceFilesBelongToPath(files, options.rootDir)) {
581+
// If a rootDir is specified and is valid use it as the commonSourceDirectory
582+
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, host.getCurrentDirectory());
583+
}
584+
else {
585+
// Compute the commonSourceDirectory from the input files
586+
commonSourceDirectory = computeCommonSourceDirectory(files);
587+
}
552588

553-
commonSourceDirectory = getNormalizedPathFromPathComponents(commonPathComponents);
554-
if (commonSourceDirectory) {
589+
if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
555590
// Make sure directory path ends with directory separator so this string can directly
556591
// used to replace with "" to get the relative path of the source file and the relative path doesn't
557592
// start with / making it rooted path

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,7 @@ module ts {
16541654
preserveConstEnums?: boolean;
16551655
project?: string;
16561656
removeComments?: boolean;
1657+
rootDir?: string;
16571658
sourceMap?: boolean;
16581659
sourceRoot?: string;
16591660
suppressImplicitAnyIndexErrors?: boolean;

src/harness/projectsRunner.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ interface ProjectRunnerTestCase {
1818
runTest?: boolean; // Run the resulting test
1919
bug?: string; // If there is any bug associated with this test case
2020
noResolve?: boolean;
21+
rootDir?: string; // --rootDir
2122
}
2223

2324
interface ProjectRunnerTestCaseResolutionInfo extends ProjectRunnerTestCase {
@@ -160,7 +161,8 @@ class ProjectRunner extends RunnerBase {
160161
mapRoot: testCase.resolveMapRoot && testCase.mapRoot ? ts.sys.resolvePath(testCase.mapRoot) : testCase.mapRoot,
161162
sourceRoot: testCase.resolveSourceRoot && testCase.sourceRoot ? ts.sys.resolvePath(testCase.sourceRoot) : testCase.sourceRoot,
162163
module: moduleKind,
163-
noResolve: testCase.noResolve
164+
noResolve: testCase.noResolve,
165+
rootDir: testCase.rootDir
164166
};
165167
}
166168

@@ -344,6 +346,7 @@ class ProjectRunner extends RunnerBase {
344346
baselineCheck: testCase.baselineCheck,
345347
runTest: testCase.runTest,
346348
bug: testCase.bug,
349+
rootDir: testCase.rootDir,
347350
resolvedInputFiles: ts.map(compilerResult.program.getSourceFiles(), inputFile => inputFile.fileName),
348351
emittedFiles: ts.map(compilerResult.outputFiles, outputFile => outputFile.emittedFileName)
349352
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare class C {
2+
}

tests/baselines/reference/project/rootDirectory/amd/outdir/simple/FolderB/FolderC/fileC.js

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/project/rootDirectory/amd/outdir/simple/FolderB/FolderC/fileC.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path="FolderC/fileC.d.ts" />
2+
declare class B {
3+
c: C;
4+
}

tests/baselines/reference/project/rootDirectory/amd/outdir/simple/FolderB/fileB.js

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/project/rootDirectory/amd/outdir/simple/FolderB/fileB.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"scenario": "rootDirectory: specify rootDirectory",
3+
"projectRoot": "tests/cases/projects/rootDirectory",
4+
"inputFiles": [
5+
"FolderA/FolderB/fileB.ts"
6+
],
7+
"outDir": "outdir/simple",
8+
"sourceMap": true,
9+
"declaration": true,
10+
"baselineCheck": true,
11+
"rootDir": "FolderA",
12+
"resolvedInputFiles": [
13+
"lib.d.ts",
14+
"FolderA/FolderB/FolderC/fileC.ts",
15+
"FolderA/FolderB/fileB.ts"
16+
],
17+
"emittedFiles": [
18+
"outdir/simple/FolderB/FolderC/fileC.js.map",
19+
"outdir/simple/FolderB/FolderC/fileC.js",
20+
"outdir/simple/FolderB/FolderC/fileC.d.ts",
21+
"outdir/simple/FolderB/fileB.js.map",
22+
"outdir/simple/FolderB/fileB.js",
23+
"outdir/simple/FolderB/fileB.d.ts"
24+
]
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
===================================================================
2+
JsFile: fileC.js
3+
mapUrl: fileC.js.map
4+
sourceRoot:
5+
sources: ../../../../FolderA/FolderB/FolderC/fileC.ts
6+
===================================================================
7+
-------------------------------------------------------------------
8+
emittedFile:outdir/simple/FolderB/FolderC/fileC.js
9+
sourceFile:../../../../FolderA/FolderB/FolderC/fileC.ts
10+
-------------------------------------------------------------------
11+
>>>var C = (function () {
12+
1 >
13+
2 >^^^^^^^^^^^^^^^^^^^->
14+
1 >
15+
1 >Emitted(1, 1) Source(1, 1) + SourceIndex(0)
16+
---
17+
>>> function C() {
18+
1->^^^^
19+
2 > ^^->
20+
1->
21+
1->Emitted(2, 5) Source(1, 1) + SourceIndex(0) name (C)
22+
---
23+
>>> }
24+
1->^^^^
25+
2 > ^
26+
3 > ^^^^^^^^^->
27+
1->class C {
28+
>
29+
2 > }
30+
1->Emitted(3, 5) Source(2, 1) + SourceIndex(0) name (C.constructor)
31+
2 >Emitted(3, 6) Source(2, 2) + SourceIndex(0) name (C.constructor)
32+
---
33+
>>> return C;
34+
1->^^^^
35+
2 > ^^^^^^^^
36+
1->
37+
2 > }
38+
1->Emitted(4, 5) Source(2, 1) + SourceIndex(0) name (C)
39+
2 >Emitted(4, 13) Source(2, 2) + SourceIndex(0) name (C)
40+
---
41+
>>>})();
42+
1 >
43+
2 >^
44+
3 >
45+
4 > ^^^^
46+
5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^->
47+
1 >
48+
2 >}
49+
3 >
50+
4 > class C {
51+
> }
52+
1 >Emitted(5, 1) Source(2, 1) + SourceIndex(0) name (C)
53+
2 >Emitted(5, 2) Source(2, 2) + SourceIndex(0) name (C)
54+
3 >Emitted(5, 2) Source(1, 1) + SourceIndex(0)
55+
4 >Emitted(5, 6) Source(2, 2) + SourceIndex(0)
56+
---
57+
>>>//# sourceMappingURL=fileC.js.map===================================================================
58+
JsFile: fileB.js
59+
mapUrl: fileB.js.map
60+
sourceRoot:
61+
sources: ../../../FolderA/FolderB/fileB.ts
62+
===================================================================
63+
-------------------------------------------------------------------
64+
emittedFile:outdir/simple/FolderB/fileB.js
65+
sourceFile:../../../FolderA/FolderB/fileB.ts
66+
-------------------------------------------------------------------
67+
>>>/// <reference path='FolderC/fileC.ts'/>
68+
1 >
69+
2 >
70+
3 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
71+
1 >/// <reference path='FolderC/fileC.ts'/>
72+
>
73+
2 >
74+
3 >/// <reference path='FolderC/fileC.ts'/>
75+
1 >Emitted(1, 1) Source(2, 1) + SourceIndex(0)
76+
2 >Emitted(1, 1) Source(1, 1) + SourceIndex(0)
77+
3 >Emitted(1, 41) Source(1, 41) + SourceIndex(0)
78+
---
79+
>>>var B = (function () {
80+
>>> function B() {
81+
1 >^^^^
82+
2 > ^^->
83+
1 >
84+
>
85+
1 >Emitted(3, 5) Source(2, 1) + SourceIndex(0) name (B)
86+
---
87+
>>> }
88+
1->^^^^
89+
2 > ^
90+
3 > ^^^^^^^^^->
91+
1->class B {
92+
> public c: C;
93+
>
94+
2 > }
95+
1->Emitted(4, 5) Source(4, 1) + SourceIndex(0) name (B.constructor)
96+
2 >Emitted(4, 6) Source(4, 2) + SourceIndex(0) name (B.constructor)
97+
---
98+
>>> return B;
99+
1->^^^^
100+
2 > ^^^^^^^^
101+
1->
102+
2 > }
103+
1->Emitted(5, 5) Source(4, 1) + SourceIndex(0) name (B)
104+
2 >Emitted(5, 13) Source(4, 2) + SourceIndex(0) name (B)
105+
---
106+
>>>})();
107+
1 >
108+
2 >^
109+
3 >
110+
4 > ^^^^
111+
5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^->
112+
1 >
113+
2 >}
114+
3 >
115+
4 > class B {
116+
> public c: C;
117+
> }
118+
1 >Emitted(6, 1) Source(4, 1) + SourceIndex(0) name (B)
119+
2 >Emitted(6, 2) Source(4, 2) + SourceIndex(0) name (B)
120+
3 >Emitted(6, 2) Source(2, 1) + SourceIndex(0)
121+
4 >Emitted(6, 6) Source(4, 2) + SourceIndex(0)
122+
---
123+
>>>//# sourceMappingURL=fileB.js.map
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare class C {
2+
}

tests/baselines/reference/project/rootDirectory/node/outdir/simple/FolderB/FolderC/fileC.js

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/project/rootDirectory/node/outdir/simple/FolderB/FolderC/fileC.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path="FolderC/fileC.d.ts" />
2+
declare class B {
3+
c: C;
4+
}

0 commit comments

Comments
 (0)