Skip to content
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

Allow --composite false or --composite null on the command line #36997

Merged
merged 10 commits into from
Feb 26, 2020
87 changes: 57 additions & 30 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ namespace ts {
type: "boolean",
affectsEmit: true,
isTSConfigOnly: true,
isTsConfigOnlyButAllowsCommandLineFalsy: true,
sheetalkamat marked this conversation as resolved.
Show resolved Hide resolved
category: Diagnostics.Basic_Options,
description: Diagnostics.Enable_project_compilation,
transpileOptionValue: undefined
Expand Down Expand Up @@ -1279,44 +1280,70 @@ namespace ts {
errors: Diagnostic[]
) {
if (opt.isTSConfigOnly) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
if (opt.isTsConfigOnlyButAllowsCommandLineFalsy) {
Debug.assert(opt.type === "boolean", "Currently tsconfig only option with only boolean types are allowed to specify falsy value on command line");
const optValue = args[i];
switch (optValue) {
case "false":
options[opt.name] = false;
i++;
break;
case "undefined":
sheetalkamat marked this conversation as resolved.
Show resolved Hide resolved
case "null":
options[opt.name] = undefined;
i++;
break;
default:
if (optValue === "true") i++;
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line, opt.name));
}
}
else {
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
}
}
else {
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
if (!args[i] && opt.type !== "boolean") {
errors.push(createCompilerDiagnostic(diagnostics.optionTypeMismatchDiagnostic, opt.name, getCompilerOptionValueTypeString(opt)));
}

switch (opt.type) {
case "number":
options[opt.name] = parseInt(args[i]);
i++;
break;
case "boolean":
// boolean flag has optional value true, false, others
const optValue = args[i];
options[opt.name] = optValue !== "false";
// consume next argument as boolean flag value
if (optValue === "false" || optValue === "true") {
if (args[i] !== "undefined" && args[i] !== "null") {
switch (opt.type) {
case "number":
options[opt.name] = parseInt(args[i]);
i++;
}
break;
case "string":
options[opt.name] = args[i] || "";
i++;
break;
case "list":
const result = parseListTypeOption(opt, args[i], errors);
options[opt.name] = result || [];
if (result) {
break;
case "boolean":
// boolean flag has optional value true, false, others
const optValue = args[i];
options[opt.name] = optValue !== "false";
// consume next argument as boolean flag value
if (optValue === "false" || optValue === "true") {
i++;
}
break;
case "string":
options[opt.name] = args[i] || "";
i++;
}
break;
// If not a primitive, the possible types are specified in what is effectively a map of options.
default:
options[opt.name] = parseCustomTypeOption(<CommandLineOptionOfCustomType>opt, args[i], errors);
i++;
break;
break;
case "list":
const result = parseListTypeOption(opt, args[i], errors);
options[opt.name] = result || [];
if (result) {
i++;
}
break;
// If not a primitive, the possible types are specified in what is effectively a map of options.
default:
options[opt.name] = parseCustomTypeOption(<CommandLineOptionOfCustomType>opt, args[i], errors);
i++;
break;
}
}
else {
options[opt.name] = undefined;
i++;
}
}
return i;
Expand Down Expand Up @@ -2170,7 +2197,7 @@ namespace ts {
}

function convertToOptionValueWithAbsolutePaths(option: CommandLineOption | undefined, value: CompilerOptionsValue, toAbsolutePath: (path: string) => string) {
if (option) {
if (option && !isNullOrUndefined(value)) {
if (option.type === "list") {
const values = value as readonly (string | number)[];
if (option.element.isFilePath && values.length) {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4292,6 +4292,10 @@
"category": "Message",
"code": 6228
},
"Option '{0}' can only be specified in 'tsconfig.json' file or set to 'false' or 'undefined' on command line.": {
"category": "Error",
"code": 6229
},

"Projects to reference": {
"category": "Message",
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5314,6 +5314,7 @@ namespace ts {
description?: DiagnosticMessage; // The message describing what the command line switch does
paramType?: DiagnosticMessage; // The name to be used for a non-boolean option's parameter
isTSConfigOnly?: boolean; // True if option can only be specified via tsconfig.json file
isTsConfigOnlyButAllowsCommandLineFalsy?: boolean; // True if option can only be specified via tsconfig.json file but on command line can set false values
isCommandLineOnly?: boolean;
showInSimplifiedHelpView?: boolean;
category?: DiagnosticMessage;
Expand Down
1 change: 1 addition & 0 deletions src/testRunner/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"unittests/tsbuild/transitiveReferences.ts",
"unittests/tsbuild/watchEnvironment.ts",
"unittests/tsbuild/watchMode.ts",
"unittests/tsc/composite.ts",
"unittests/tsc/declarationEmit.ts",
"unittests/tsc/incremental.ts",
"unittests/tsc/listFilesOnly.ts",
Expand Down
86 changes: 86 additions & 0 deletions src/testRunner/unittests/config/commandLineParsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,92 @@ namespace ts {
});
});

describe("parse --composite", () => {
it("allows setting it to false", () => {
assertParseResult(["--composite", "false", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: { composite: false }
});
});

it("allows setting it to undefined and tsbuild info to undefined", () => {
assertParseResult(["--composite", "undefined", "-tsBuildInfoFile", "undefined", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: { composite: undefined, tsBuildInfoFile: undefined }
});
});

it("allows setting it to null and tsbuild info to null", () => {
assertParseResult(["--composite", "null", "-tsBuildInfoFile", "null", "0.ts"],
{
errors: [],
fileNames: ["0.ts"],
options: { composite: undefined, tsBuildInfoFile: undefined }
});
});

it("Errors if set to true", () => {
// --lib es6 0.ts
assertParseResult(["--composite", "true", "0.ts"],
{
errors: [
{
messageText: formatStringFromArgs(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.message, ["composite"]),
category: Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.category,
code: Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: {}
});
});

it("Errors if its last parameter", () => {
// --lib es6 0.ts
assertParseResult(["0.ts", "--composite"],
{
errors: [
{
messageText: formatStringFromArgs(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.message, ["composite"]),
category: Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.category,
code: Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: {}
});
});

it("Errors if its followed by other option", () => {
// --lib es6 0.ts
assertParseResult(["0.ts", "--composite", "--strictNullChecks"],
{
errors: [
{
messageText: formatStringFromArgs(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.message, ["composite"]),
category: Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.category,
code: Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file_or_set_to_false_or_undefined_on_command_line.code,
file: undefined,
start: undefined,
length: undefined
}
],
fileNames: ["0.ts"],
options: { strictNullChecks: true }
});
});
});

describe("Watch options", () => {
it("parse --watchFile", () => {
assertParseResult(["--watchFile", "UseFsEvents", "0.ts"],
Expand Down
101 changes: 101 additions & 0 deletions src/testRunner/unittests/tsc/composite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
namespace ts {
describe("unittests:: tsc:: composite::", () => {
verifyTsc({
scenario: "composite",
subScenario: "when setting composite false on command line",
fs: () => loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
sheetalkamat marked this conversation as resolved.
Show resolved Hide resolved
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "false", "--p", "src/project"],
});

verifyTsc({
scenario: "composite",
subScenario: "when setting composite undefined on command line",
fs: () => loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "undefined", "--p", "src/project"],
});

verifyTsc({
scenario: "composite",
subScenario: "when setting composite false on command line but has tsbuild info in config",
fs: () => loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"tsBuildInfoFile": "tsconfig.json.tsbuildinfo"
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "false", "--p", "src/project"],
});

verifyTsc({
scenario: "composite",
subScenario: "when setting composite false and tsbuildinfo as undefined on command line but has tsbuild info in config",
fs: () => loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"tsBuildInfoFile": "tsconfig.json.tsbuildinfo"
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "false", "--p", "src/project", "--tsBuildInfoFile", "undefined"],
});

verifyTsc({
scenario: "composite",
subScenario: "when setting composite false and tsbuildinfo as null on command line but has tsbuild info in config",
fs: () => loadProjectFromFiles({
"/src/project/src/main.ts": "export const x = 10;",
"/src/project/tsconfig.json": Utils.dedent`
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"tsBuildInfoFile": "tsconfig.json.tsbuildinfo"
},
"include": [
"src/**/*.ts"
]
}`,
}),
commandLineArgs: ["--composite", "false", "--p", "src/project", "--tsBuildInfoFile", "null"],
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//// [/lib/initial-buildOutput.txt]
/lib/tsc --composite false --p src/project --tsBuildInfoFile null
exitCode:: ExitStatus.Success


//// [/src/project/src/main.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.x = 10;


Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//// [/lib/initial-buildOutput.txt]
/lib/tsc --composite false --p src/project --tsBuildInfoFile undefined
exitCode:: ExitStatus.Success


//// [/src/project/src/main.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.x = 10;


Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//// [/lib/initial-buildOutput.txt]
/lib/tsc --composite false --p src/project
src/project/tsconfig.json(5,9): error TS5069: Option 'tsBuildInfoFile' cannot be specified without specifying option 'incremental' or option 'composite'.
exitCode:: ExitStatus.DiagnosticsPresent_OutputsGenerated


//// [/src/project/src/main.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.x = 10;


Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//// [/lib/initial-buildOutput.txt]
/lib/tsc --composite false --p src/project
exitCode:: ExitStatus.Success


//// [/src/project/src/main.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.x = 10;


Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//// [/lib/initial-buildOutput.txt]
/lib/tsc --composite undefined --p src/project
exitCode:: ExitStatus.Success


//// [/src/project/src/main.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.x = 10;