Add 'project' option

This commit is contained in:
Ryan Cavanaugh
2017-12-19 16:19:13 -08:00
parent cdde79b9bb
commit a348e246e7
5 changed files with 95 additions and 51 deletions
+6
View File
@@ -217,6 +217,12 @@ namespace ts {
category: Diagnostics.Basic_Options,
description: Diagnostics.Specify_the_root_directory_of_input_files_Use_to_control_the_output_directory_structure_with_outDir,
},
{
name: "project",
type: "boolean",
category: Diagnostics.Basic_Options,
description: Diagnostics.Enable_project_compilation,
},
{
name: "removeComments",
type: "boolean",
+7
View File
@@ -2019,6 +2019,13 @@ namespace ts {
: moduleKind === ModuleKind.System;
}
export function getEmitDeclarations(compilerOptions: CompilerOptions) {
if (compilerOptions.project) {
return true;
}
return compilerOptions.declaration;
}
export type StrictOptionName = "noImplicitAny" | "noImplicitThis" | "strictNullChecks" | "strictFunctionTypes" | "strictPropertyInitialization" | "alwaysStrict";
export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean {
+17 -9
View File
@@ -3413,25 +3413,33 @@
"code": 6186
},
"Project references may not form a circular graph. Cycle detected: {0}": {
"category": "Error",
"code": 6300
},
"Projects to reference": {
"category": "Message",
"code": 6301
},
"Referenced project '{0}' must have 'declaration': true": {
"category": "Error",
"Enable project compilation": {
"category": "Message",
"code": 6302
},
"Referenced project '{0}' must have an explicit 'rootDir' setting": {
"Project references may not form a circular graph. Cycle detected: {0}": {
"category": "Error",
"code": 6303
},
"Output file '{0}' has not been built from source file '{1}'": {
"Projects may not disable declaration emit.": {
"category": "Error",
"code": 6404
"code": 6304
},
"Output file '{0}' has not been built from source file '{1}'.": {
"category": "Error",
"code": 6305
},
"Referenced project '{0}' must have setting \"project\": true.": {
"category": "Error",
"code": 6306
},
"File '{0}' is not in project file list. Projects must list all files or use an 'include' pattern.": {
"category": "Error",
"code": 6307
},
"Enable strict checking of property initialization in classes.": {
+29 -14
View File
@@ -612,6 +612,17 @@ namespace ts {
Debug.assert(!!missingFilePaths);
// List of collected files is complete; validate exhautiveness if this is a project with a file list
if (options.project && rootNames.length < files.length) {
const normalizedRootNames = rootNames.map(r => normalizePath(r));
const sourceFiles = files.filter(f => !f.isDeclarationFile).map(f => normalizePath(f.path));
for (const file of sourceFiles) {
if (normalizedRootNames.every(r => r !== file)) {
programDiagnostics.add(createCompilerDiagnostic(Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern, file));
}
}
}
// unconditionally set moduleResolutionCache to undefined to avoid unnecessary leaks
moduleResolutionCache = undefined;
@@ -674,7 +685,11 @@ namespace ts {
function getCommonSourceDirectory() {
if (commonSourceDirectory === undefined) {
const emittedFiles = filter(files, file => sourceFileMayBeEmitted(file, options, isSourceFileFromExternalLibrary));
if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
if (options.project) {
// Project compilations never infer their root from the input source paths
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir || ".", currentDirectory);
}
else if (options.rootDir && checkSourceFilesBelongToPath(emittedFiles, options.rootDir)) {
// If a rootDir is specified and is valid use it as the commonSourceDirectory
commonSourceDirectory = getNormalizedAbsolutePath(options.rootDir, currentDirectory);
}
@@ -1101,7 +1116,7 @@ namespace ts {
// otherwise, using options specified in '--lib' instead of '--target' default library file
const equalityComparer = host.useCaseSensitiveFileNames() ? equateStringsCaseSensitive : equateStringsCaseInsensitive;
if (!options.lib) {
return equalityComparer(file.fileName, getDefaultLibraryFileName());
return equalityComparer(file.fileName, getDefaultLibraryFileName());
}
else {
return forEach(options.lib, libFileName => equalityComparer(file.fileName, combinePaths(defaultLibraryPath, libFileName)));
@@ -2065,10 +2080,9 @@ namespace ts {
const result = createMap<string>();
walkProjectReferenceGraph(host, rootOptions, createMapping);
function createMapping(_resolvedFile: string, referencedProject: CompilerOptions) {
// No rootDir in target set; this will be an error later on in the process
if (referencedProject.rootDir === undefined) return;
result.set(referencedProject.rootDir, referencedProject.outDir);
function createMapping(resolvedFile: string, referencedProject: CompilerOptions) {
const rootDir = normalizePath(referencedProject.rootDir || getDirectoryPath(resolvedFile));
result.set(rootDir, referencedProject.outDir);
// If this project uses outFile, add the outFile to our compilation
if (referencedProject.outFile) {
const outFile = combinePaths(referencedProject.outDir, referencedProject.outFile);
@@ -2080,10 +2094,8 @@ namespace ts {
function checkProjectReferenceGraph() {
// Checks the following conditions:
// * Any referenced project has declaration: true
// * Any referenced project has an explicit rootDir
// * Any referenced project has project: true
// * No circularities exist
// * TODO No project root is a subfolder of any other project root
const illegalRefs = createMap<true>();
const cycleName: string[] = [options.configFilePath || host.getCurrentDirectory()];
@@ -2100,11 +2112,8 @@ namespace ts {
Debug.fail("Options cannot be undefined");
return;
}
if (!opts.declaration) {
createDiagnosticForOptionName(Diagnostics.Referenced_project_0_must_have_declaration_Colon_true, fileName);
}
if (!opts.rootDir) {
createDiagnosticForOptionName(Diagnostics.Referenced_project_0_must_have_an_explicit_rootDir_setting, fileName);
if (!opts.project) {
createDiagnosticForOptionName(Diagnostics.Referenced_project_0_must_have_setting_project_Colon_true, fileName);
}
illegalRefs.set(normalizedPath, true);
cycleName.push(normalizedPath);
@@ -2146,6 +2155,12 @@ namespace ts {
createDiagnosticForOptionName(Diagnostics.Option_paths_cannot_be_used_without_specifying_baseUrl_option, "paths");
}
if (options.project) {
if (options.declaration === false) {
createDiagnosticForOptionName(Diagnostics.Projects_may_not_disable_declaration_emit, "declaration");
}
}
if (options.paths) {
for (const key in options.paths) {
if (!hasProperty(options.paths, key)) {
+36 -28
View File
@@ -4,9 +4,10 @@
namespace ts {
interface TestProjectSpecification {
configFileName?: string;
references: string[];
references?: string[];
files: { [fileName: string]: string };
outputFiles?: { [fileName: string]: string };
config?: object;
options?: Partial<CompilerOptions>;
}
interface TestSpecification {
@@ -52,11 +53,11 @@ namespace ts {
const options = {
compilerOptions: {
references: sp.references.map(r => ({ path: r })),
declaration: true,
rootDir: ".",
project: true,
outDir: "bin",
...sp.options
}
},
...sp.config
};
const configContent = JSON.stringify(options);
const outDir = options.compilerOptions.outDir;
@@ -123,35 +124,42 @@ namespace ts {
* Validate that we enforce the basic settings constraints for referenced projects
*/
describe("project-references constraint checking for settings", () => {
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": emptyModule },
references: ["../secondary"]
},
"/secondary": {
files: { "/secondary/b.ts": moduleImporting("../primary/a") },
references: [],
options: {
declaration: false
}
}
};
it("errors when declaration = false", () => {
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Referenced_project_0_must_have_declaration_Colon_true);
});
});
const spec: TestSpecification = {
"/primary": {
files: { "/primary/a.ts": emptyModule },
references: [],
options: {
declaration: false
}
}
};
it("errors when rootDir is not set", () => {
spec["/secondary"].options.declaration = true;
spec["/secondary"].options.rootDir = undefined;
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Referenced_project_0_must_have_an_explicit_rootDir_setting);
assertHasError("Reports an error about the wrong decl setting", errs, Diagnostics.Projects_may_not_disable_declaration_emit);
});
});
// * TODO Projects must list all files or none
it("errors when the file list is not exhaustive", () => {
const spec: TestSpecification = {
"/primary": {
files: {
"/primary/a.ts": "import * as b from './b'",
"/primary/b.ts": "export {}"
},
references: [],
config: {
files: ["a.ts"]
}
}
};
testProjectReferences(spec, "/primary/tsconfig.json", program => {
const errs = program.getOptionsDiagnostics();
assertHasError("Reports an error about b.ts not being in the list", errs, Diagnostics.File_0_is_not_in_project_file_list_Projects_must_list_all_files_or_use_an_include_pattern);
});
});
// * TODO No project root is a subfolder of any other project root
});
/**
@@ -190,7 +198,7 @@ namespace ts {
it("redirects to the output .d.ts file", () => {
const spec: TestSpecification = {
"/alpha": {
files: { "/alpha/a.ts": "export const m: number;" },
files: { "/alpha/a.ts": "export const m: number = 3;" },
references: [],
outputFiles: { "a.d.ts": emptyModule }
},