Skip to content

Add new compiler option --rootDir #2772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 20, 2015
7 changes: 7 additions & 0 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,13 @@ module ts {
type: "boolean",
description: Diagnostics.Do_not_emit_comments_to_output,
},
{
name: "rootDir",
type: "string",
isFilePath: true,
description: Diagnostics.Specifies_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir,
paramType: Diagnostics.LOCATION,
},
{
name: "separateCompilation",
type: "boolean",
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ module ts {
Suppress_noImplicitAny_errors_for_indexing_objects_lacking_index_signatures: { code: 6055, category: DiagnosticCategory.Message, key: "Suppress noImplicitAny errors for indexing objects lacking index signatures." },
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." },
Preserve_new_lines_when_emitting_code: { code: 6057, category: DiagnosticCategory.Message, key: "Preserve new-lines when emitting code." },
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." },
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." },
Variable_0_implicitly_has_an_1_type: { code: 7005, category: DiagnosticCategory.Error, key: "Variable '{0}' implicitly has an '{1}' type." },
Parameter_0_implicitly_has_an_1_type: { code: 7006, category: DiagnosticCategory.Error, key: "Parameter '{0}' implicitly has an '{1}' type." },
Member_0_implicitly_has_an_1_type: { code: 7008, category: DiagnosticCategory.Error, key: "Member '{0}' implicitly has an '{1}' type." },
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,15 @@
"category": "Message",
"code": 6057
},
"Specifies the root directory of input files. Use to control the output directory structure with --outDir.": {
"category": "Message",
"code": 6058
},
"File '{0}' is not under 'rootDir' '{1}'. 'rootDir' is expected to contain all source files.": {
"category": "Error",
"code": 6059
},


"Variable '{0}' implicitly has an '{1}' type.": {
"category": "Error",
Expand Down
103 changes: 69 additions & 34 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,66 @@ module ts {
}
}

function computeCommonSourceDirectory(sourceFiles: SourceFile[]): string {
let commonPathComponents: string[];
let currentDirectory = host.getCurrentDirectory();
forEach(files, sourceFile => {
Copy link
Member

Choose a reason for hiding this comment

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

I think you meant sourceFiles here not files.

Copy link
Member

Choose a reason for hiding this comment

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

Also use a for..of unless you're really hesitant to use a label.

// Each file contributes into common source file path
if (isDeclarationFile(sourceFile)) {
return;
}

let sourcePathComponents = getNormalizedPathComponents(sourceFile.fileName, currentDirectory);
sourcePathComponents.pop(); // The base file name is not part of the common directory path

if (!commonPathComponents) {
// first file
commonPathComponents = sourcePathComponents;
return;
}

for (let i = 0, n = Math.min(commonPathComponents.length, sourcePathComponents.length); i < n; i++) {
if (commonPathComponents[i] !== sourcePathComponents[i]) {
if (i === 0) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
return;
}

// New common path found that is 0 -> i-1
commonPathComponents.length = i;
break;
}
}

// If the sourcePathComponents was shorter than the commonPathComponents, truncate to the sourcePathComponents
if (sourcePathComponents.length < commonPathComponents.length) {
commonPathComponents.length = sourcePathComponents.length;
}
});

return getNormalizedPathFromPathComponents(commonPathComponents);
}

function checkSourceFilesBelongToPath(sourceFiles: SourceFile[], rootDirectory: string): boolean {
let allFilesBelongToPath = true;
Copy link
Member

Choose a reason for hiding this comment

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

                           ~~~~~~~~~~~~~~~~~~~~~
Warning (508, 28) DR1000: Variable 'allFilesBelongToPath' is declared but never read from.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

 return allFilesBelongToPath;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

just to report all violations instead of just the first one.

if (sourceFiles) {
let currentDirectory = host.getCurrentDirectory();
let absoluteRootDirectoryPath = host.getCanonicalFileName(getNormalizedAbsolutePath(rootDirectory, currentDirectory));

for (var sourceFile of sourceFiles) {
if (!isDeclarationFile(sourceFile)) {
let absoluteSourceFilePath = host.getCanonicalFileName(getNormalizedAbsolutePath(sourceFile.fileName, currentDirectory));
if (absoluteSourceFilePath.indexOf(absoluteRootDirectoryPath) !== 0) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_under_rootDir_1_rootDir_is_expected_to_contain_all_source_files, sourceFile.fileName, options.rootDir));
allFilesBelongToPath = false;
}
}
}
}

return allFilesBelongToPath;
}

function verifyCompilerOptions() {
if (options.separateCompilation) {
if (options.sourceMap) {
Expand Down Expand Up @@ -517,41 +577,16 @@ module ts {
(options.mapRoot && // there is --mapRoot specified and there would be multiple js files generated
(!options.out || firstExternalModuleSourceFile !== undefined))) {

let commonPathComponents: string[];
forEach(files, sourceFile => {
// Each file contributes into common source file path
if (!(sourceFile.flags & NodeFlags.DeclarationFile)
&& !fileExtensionIs(sourceFile.fileName, ".js")) {
let sourcePathComponents = getNormalizedPathComponents(sourceFile.fileName, host.getCurrentDirectory());
sourcePathComponents.pop(); // FileName is not part of directory
if (commonPathComponents) {
for (let i = 0; i < Math.min(commonPathComponents.length, sourcePathComponents.length); i++) {
if (commonPathComponents[i] !== sourcePathComponents[i]) {
if (i === 0) {
diagnostics.add(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
return;
}

// New common path found that is 0 -> i-1
commonPathComponents.length = i;
break;
}
}

// If the fileComponent path completely matched and less than already found update the length
if (sourcePathComponents.length < commonPathComponents.length) {
commonPathComponents.length = sourcePathComponents.length;
}
}
else {
// first file
commonPathComponents = sourcePathComponents;
}
}
});
if (options.rootDir && checkSourceFilesBelongToPath(files, options.rootDir)) {
// If a rootDir is specified and is valid use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, host.getCurrentDirectory());
}
else {
// Compute the commonSourceDirectory from the input files
commonSourceDirectory = computeCommonSourceDirectory(files);
}

commonSourceDirectory = getNormalizedPathFromPathComponents(commonPathComponents);
if (commonSourceDirectory) {
if (commonSourceDirectory && commonSourceDirectory[commonSourceDirectory.length - 1] !== directorySeparator) {
// Make sure directory path ends with directory separator so this string can directly
// used to replace with "" to get the relative path of the source file and the relative path doesn't
// start with / making it rooted path
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,7 @@ module ts {
preserveConstEnums?: boolean;
project?: string;
removeComments?: boolean;
rootDir?: string;
sourceMap?: boolean;
sourceRoot?: string;
suppressImplicitAnyIndexErrors?: boolean;
Expand Down
5 changes: 4 additions & 1 deletion src/harness/projectsRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface ProjectRunnerTestCase {
runTest?: boolean; // Run the resulting test
bug?: string; // If there is any bug associated with this test case
noResolve?: boolean;
rootDir?: string; // --rootDir
}

interface ProjectRunnerTestCaseResolutionInfo extends ProjectRunnerTestCase {
Expand Down Expand Up @@ -160,7 +161,8 @@ class ProjectRunner extends RunnerBase {
mapRoot: testCase.resolveMapRoot && testCase.mapRoot ? ts.sys.resolvePath(testCase.mapRoot) : testCase.mapRoot,
sourceRoot: testCase.resolveSourceRoot && testCase.sourceRoot ? ts.sys.resolvePath(testCase.sourceRoot) : testCase.sourceRoot,
module: moduleKind,
noResolve: testCase.noResolve
noResolve: testCase.noResolve,
rootDir: testCase.rootDir
};
}

Expand Down Expand Up @@ -344,6 +346,7 @@ class ProjectRunner extends RunnerBase {
baselineCheck: testCase.baselineCheck,
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)
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare class C {
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path="FolderC/fileC.d.ts" />
declare class B {
c: C;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"scenario": "rootDirectory: specify rootDirectory",
"projectRoot": "tests/cases/projects/rootDirectory",
"inputFiles": [
"FolderA/FolderB/fileB.ts"
],
"outDir": "outdir/simple",
"sourceMap": true,
"declaration": true,
"baselineCheck": true,
"rootDir": "FolderA",
"resolvedInputFiles": [
"lib.d.ts",
"FolderA/FolderB/FolderC/fileC.ts",
"FolderA/FolderB/fileB.ts"
],
"emittedFiles": [
"outdir/simple/FolderB/FolderC/fileC.js.map",
"outdir/simple/FolderB/FolderC/fileC.js",
"outdir/simple/FolderB/FolderC/fileC.d.ts",
"outdir/simple/FolderB/fileB.js.map",
"outdir/simple/FolderB/fileB.js",
"outdir/simple/FolderB/fileB.d.ts"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
===================================================================
JsFile: fileC.js
mapUrl: fileC.js.map
sourceRoot:
sources: ../../../../FolderA/FolderB/FolderC/fileC.ts
===================================================================
-------------------------------------------------------------------
emittedFile:outdir/simple/FolderB/FolderC/fileC.js
sourceFile:../../../../FolderA/FolderB/FolderC/fileC.ts
-------------------------------------------------------------------
>>>var C = (function () {
1 >
2 >^^^^^^^^^^^^^^^^^^^->
1 >
1 >Emitted(1, 1) Source(1, 1) + SourceIndex(0)
---
>>> function C() {
1->^^^^
2 > ^^->
1->
1->Emitted(2, 5) Source(1, 1) + SourceIndex(0) name (C)
---
>>> }
1->^^^^
2 > ^
3 > ^^^^^^^^^->
1->class C {
>
2 > }
1->Emitted(3, 5) Source(2, 1) + SourceIndex(0) name (C.constructor)
2 >Emitted(3, 6) Source(2, 2) + SourceIndex(0) name (C.constructor)
---
>>> return C;
1->^^^^
2 > ^^^^^^^^
1->
2 > }
1->Emitted(4, 5) Source(2, 1) + SourceIndex(0) name (C)
2 >Emitted(4, 13) Source(2, 2) + SourceIndex(0) name (C)
---
>>>})();
1 >
2 >^
3 >
4 > ^^^^
5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^->
1 >
2 >}
3 >
4 > class C {
> }
1 >Emitted(5, 1) Source(2, 1) + SourceIndex(0) name (C)
2 >Emitted(5, 2) Source(2, 2) + SourceIndex(0) name (C)
3 >Emitted(5, 2) Source(1, 1) + SourceIndex(0)
4 >Emitted(5, 6) Source(2, 2) + SourceIndex(0)
---
>>>//# sourceMappingURL=fileC.js.map===================================================================
JsFile: fileB.js
mapUrl: fileB.js.map
sourceRoot:
sources: ../../../FolderA/FolderB/fileB.ts
===================================================================
-------------------------------------------------------------------
emittedFile:outdir/simple/FolderB/fileB.js
sourceFile:../../../FolderA/FolderB/fileB.ts
-------------------------------------------------------------------
>>>/// <reference path='FolderC/fileC.ts'/>
1 >
2 >
3 >^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1 >/// <reference path='FolderC/fileC.ts'/>
>
2 >
3 >/// <reference path='FolderC/fileC.ts'/>
1 >Emitted(1, 1) Source(2, 1) + SourceIndex(0)
2 >Emitted(1, 1) Source(1, 1) + SourceIndex(0)
3 >Emitted(1, 41) Source(1, 41) + SourceIndex(0)
---
>>>var B = (function () {
>>> function B() {
1 >^^^^
2 > ^^->
1 >
>
1 >Emitted(3, 5) Source(2, 1) + SourceIndex(0) name (B)
---
>>> }
1->^^^^
2 > ^
3 > ^^^^^^^^^->
1->class B {
> public c: C;
>
2 > }
1->Emitted(4, 5) Source(4, 1) + SourceIndex(0) name (B.constructor)
2 >Emitted(4, 6) Source(4, 2) + SourceIndex(0) name (B.constructor)
---
>>> return B;
1->^^^^
2 > ^^^^^^^^
1->
2 > }
1->Emitted(5, 5) Source(4, 1) + SourceIndex(0) name (B)
2 >Emitted(5, 13) Source(4, 2) + SourceIndex(0) name (B)
---
>>>})();
1 >
2 >^
3 >
4 > ^^^^
5 > ^^^^^^^^^^^^^^^^^^^^^^^^^^^->
1 >
2 >}
3 >
4 > class B {
> public c: C;
> }
1 >Emitted(6, 1) Source(4, 1) + SourceIndex(0) name (B)
2 >Emitted(6, 2) Source(4, 2) + SourceIndex(0) name (B)
3 >Emitted(6, 2) Source(2, 1) + SourceIndex(0)
4 >Emitted(6, 6) Source(4, 2) + SourceIndex(0)
---
>>>//# sourceMappingURL=fileB.js.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare class C {
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path="FolderC/fileC.d.ts" />
declare class B {
c: C;
}
Loading