mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Add 'project' option
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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)) {
|
||||
|
||||
@@ -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 }
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user