Skip to content

Commit 31afb98

Browse files
authored
Updates to --build parsing on command line (#59874)
1 parent ea69909 commit 31afb98

File tree

18 files changed

+1112
-50
lines changed

18 files changed

+1112
-50
lines changed

src/compiler/commandLineParser.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -639,15 +639,6 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
639639
paramType: Diagnostics.FILE_OR_DIRECTORY,
640640
description: Diagnostics.Compile_the_project_given_the_path_to_its_configuration_file_or_to_a_folder_with_a_tsconfig_json,
641641
},
642-
{
643-
name: "build",
644-
type: "boolean",
645-
shortName: "b",
646-
showInSimplifiedHelpView: true,
647-
category: Diagnostics.Command_line_Options,
648-
description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date,
649-
defaultValueDescription: false,
650-
},
651642
{
652643
name: "showConfig",
653644
type: "boolean",
@@ -1662,9 +1653,21 @@ function isCommandLineOptionOfCustomType(option: CommandLineOption): option is C
16621653
return !isString(option.type);
16631654
}
16641655

1656+
/** @internal */
1657+
export const tscBuildOption: CommandLineOption = {
1658+
name: "build",
1659+
type: "boolean",
1660+
shortName: "b",
1661+
showInSimplifiedHelpView: true,
1662+
category: Diagnostics.Command_line_Options,
1663+
description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date,
1664+
defaultValueDescription: false,
1665+
};
1666+
16651667
// Build related options
16661668
/** @internal */
16671669
export const optionsForBuild: CommandLineOption[] = [
1670+
tscBuildOption,
16681671
{
16691672
name: "verbose",
16701673
shortName: "v",
@@ -1849,8 +1852,16 @@ function createUnknownOptionError(
18491852
node?: PropertyName,
18501853
sourceFile?: TsConfigSourceFile,
18511854
) {
1852-
if (diagnostics.alternateMode?.getOptionsNameMap().optionsNameMap.has(unknownOption.toLowerCase())) {
1853-
return createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics.alternateMode.diagnostic, unknownOption);
1855+
const otherOption = diagnostics.alternateMode?.getOptionsNameMap().optionsNameMap.get(unknownOption.toLowerCase());
1856+
if (otherOption) {
1857+
return createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(
1858+
sourceFile,
1859+
node,
1860+
otherOption !== tscBuildOption ?
1861+
diagnostics.alternateMode!.diagnostic :
1862+
Diagnostics.Option_build_must_be_the_first_command_line_argument,
1863+
unknownOption,
1864+
);
18541865
}
18551866

18561867
const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName);
@@ -2051,7 +2062,7 @@ function getOptionDeclarationFromName(getOptionNameMap: () => OptionsNameMap, op
20512062
return optionsNameMap.get(optionName);
20522063
}
20532064

2054-
/** @internal */
2065+
/** Parsed command line for build */
20552066
export interface ParsedBuildCommand {
20562067
buildOptions: BuildOptions;
20572068
watchOptions: WatchOptions | undefined;
@@ -2078,11 +2089,10 @@ const buildOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = {
20782089
optionTypeMismatchDiagnostic: Diagnostics.Build_option_0_requires_a_value_of_type_1,
20792090
};
20802091

2081-
/** @internal */
2082-
export function parseBuildCommand(args: readonly string[]): ParsedBuildCommand {
2092+
export function parseBuildCommand(commandLine: readonly string[]): ParsedBuildCommand {
20832093
const { options, watchOptions, fileNames: projects, errors } = parseCommandLineWorker(
20842094
buildOptionsDidYouMeanDiagnostics,
2085-
args,
2095+
commandLine,
20862096
);
20872097
const buildOptions = options as BuildOptions;
20882098

src/compiler/executeCommandLine.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ import {
8181
toPath,
8282
toSorted,
8383
tracing,
84+
tscBuildOption,
8485
validateLocaleAndSetLanguage,
8586
version,
8687
WatchCompilerHost,
@@ -170,8 +171,8 @@ function shouldBePretty(sys: System, options: CompilerOptions | BuildOptions) {
170171
function getOptionsForHelp(commandLine: ParsedCommandLine) {
171172
// Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch")
172173
return !!commandLine.options.all ?
173-
toSorted(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
174-
filter(optionDeclarations.slice(), v => !!v.showInSimplifiedHelpView);
174+
toSorted(optionDeclarations.concat(tscBuildOption), (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
175+
filter(optionDeclarations.concat(tscBuildOption), v => !!v.showInSimplifiedHelpView);
175176
}
176177

177178
function printVersion(sys: System) {
@@ -507,15 +508,15 @@ function printAllHelp(sys: System, compilerOptions: readonly CommandLineOption[]
507508
let output: string[] = [...getHeader(sys, `${getDiagnosticText(Diagnostics.tsc_Colon_The_TypeScript_Compiler)} - ${getDiagnosticText(Diagnostics.Version_0, version)}`)];
508509
output = [...output, ...generateSectionOptionsOutput(sys, getDiagnosticText(Diagnostics.ALL_COMPILER_OPTIONS), compilerOptions, /*subCategory*/ true, /*beforeOptionsDescription*/ undefined, formatMessage(Diagnostics.You_can_learn_about_all_of_the_compiler_options_at_0, "https://aka.ms/tsc"))];
509510
output = [...output, ...generateSectionOptionsOutput(sys, getDiagnosticText(Diagnostics.WATCH_OPTIONS), watchOptions, /*subCategory*/ false, getDiagnosticText(Diagnostics.Including_watch_w_will_start_watching_the_current_project_for_the_file_changes_Once_set_you_can_config_watch_mode_with_Colon))];
510-
output = [...output, ...generateSectionOptionsOutput(sys, getDiagnosticText(Diagnostics.BUILD_OPTIONS), buildOptions, /*subCategory*/ false, formatMessage(Diagnostics.Using_build_b_will_make_tsc_behave_more_like_a_build_orchestrator_than_a_compiler_This_is_used_to_trigger_building_composite_projects_which_you_can_learn_more_about_at_0, "https://aka.ms/tsc-composite-builds"))];
511+
output = [...output, ...generateSectionOptionsOutput(sys, getDiagnosticText(Diagnostics.BUILD_OPTIONS), filter(buildOptions, option => option !== tscBuildOption), /*subCategory*/ false, formatMessage(Diagnostics.Using_build_b_will_make_tsc_behave_more_like_a_build_orchestrator_than_a_compiler_This_is_used_to_trigger_building_composite_projects_which_you_can_learn_more_about_at_0, "https://aka.ms/tsc-composite-builds"))];
511512
for (const line of output) {
512513
sys.write(line);
513514
}
514515
}
515516

516517
function printBuildHelp(sys: System, buildOptions: readonly CommandLineOption[]) {
517518
let output: string[] = [...getHeader(sys, `${getDiagnosticText(Diagnostics.tsc_Colon_The_TypeScript_Compiler)} - ${getDiagnosticText(Diagnostics.Version_0, version)}`)];
518-
output = [...output, ...generateSectionOptionsOutput(sys, getDiagnosticText(Diagnostics.BUILD_OPTIONS), buildOptions, /*subCategory*/ false, formatMessage(Diagnostics.Using_build_b_will_make_tsc_behave_more_like_a_build_orchestrator_than_a_compiler_This_is_used_to_trigger_building_composite_projects_which_you_can_learn_more_about_at_0, "https://aka.ms/tsc-composite-builds"))];
519+
output = [...output, ...generateSectionOptionsOutput(sys, getDiagnosticText(Diagnostics.BUILD_OPTIONS), filter(buildOptions, option => option !== tscBuildOption), /*subCategory*/ false, formatMessage(Diagnostics.Using_build_b_will_make_tsc_behave_more_like_a_build_orchestrator_than_a_compiler_This_is_used_to_trigger_building_composite_projects_which_you_can_learn_more_about_at_0, "https://aka.ms/tsc-composite-builds"))];
519520
for (const line of output) {
520521
sys.write(line);
521522
}
@@ -559,11 +560,6 @@ function executeCommandLineWorker(
559560
commandLine: ParsedCommandLine,
560561
) {
561562
let reportDiagnostic = createDiagnosticReporter(sys);
562-
if (commandLine.options.build) {
563-
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument));
564-
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
565-
}
566-
567563
// Configuration file name (if any)
568564
let configFileName: string | undefined;
569565
if (commandLine.options.locale) {
@@ -732,11 +728,11 @@ function executeCommandLineWorker(
732728
}
733729
}
734730

735-
/** @internal */
736-
export function isBuild(commandLineArgs: readonly string[]) {
731+
/** Returns true if commandline is --build and needs to be parsed useing parseBuildCommand */
732+
export function isBuildCommand(commandLineArgs: readonly string[]) {
737733
if (commandLineArgs.length > 0 && commandLineArgs[0].charCodeAt(0) === CharacterCodes.minus) {
738734
const firstOption = commandLineArgs[0].slice(commandLineArgs[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
739-
return firstOption === "build" || firstOption === "b";
735+
return firstOption === tscBuildOption.name || firstOption === tscBuildOption.shortName;
740736
}
741737
return false;
742738
}
@@ -749,8 +745,8 @@ export function executeCommandLine(
749745
cb: ExecuteCommandLineCallbacks,
750746
commandLineArgs: readonly string[],
751747
): void | SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram> | WatchOfConfigFile<EmitAndSemanticDiagnosticsBuilderProgram> {
752-
if (isBuild(commandLineArgs)) {
753-
const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs.slice(1));
748+
if (isBuildCommand(commandLineArgs)) {
749+
const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs);
754750
if (buildOptions.generateCpuProfile && system.enableCPUProfiler) {
755751
system.enableCPUProfiler(buildOptions.generateCpuProfile, () =>
756752
performBuild(

src/testRunner/tests.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ export * from "./unittests/tsbuildWatch/reexport.js";
114114
export * from "./unittests/tsbuildWatch/roots.js";
115115
export * from "./unittests/tsbuildWatch/watchEnvironment.js";
116116
export * from "./unittests/tsc/cancellationToken.js";
117+
export * from "./unittests/tsc/commandLine.js";
117118
export * from "./unittests/tsc/composite.js";
118119
export * from "./unittests/tsc/declarationEmit.js";
119120
export * from "./unittests/tsc/extends.js";
@@ -128,7 +129,6 @@ export * from "./unittests/tsc/noEmitOnError.js";
128129
export * from "./unittests/tsc/projectReferences.js";
129130
export * from "./unittests/tsc/projectReferencesConfig.js";
130131
export * from "./unittests/tsc/redirect.js";
131-
export * from "./unittests/tsc/runWithoutArgs.js";
132132
export * from "./unittests/tscWatch/consoleClearing.js";
133133
export * from "./unittests/tscWatch/emit.js";
134134
export * from "./unittests/tscWatch/emitAndErrorUpdates.js";

src/testRunner/unittests/config/commandLineParsing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe("unittests:: config:: commandLineParsing:: parseCommandLine", () => {
2626

2727
// --lib es6 0.ts
2828
assertParseResult("Parse single option of library flag", ["--lib", "es6", "0.ts"]);
29-
assertParseResult("Handles may only be used with --build flags", ["--clean", "--dry", "--force", "--verbose"]);
29+
assertParseResult("Handles may only be used with --build flags", ["--build", "--clean", "--dry", "--force", "--verbose"]);
3030
// --declarations --allowTS
3131
assertParseResult("Handles did you mean for misspelt flags", ["--declarations", "--allowTS"]);
3232
// --lib es5,es2015.symbol.wellknown 0.ts

src/testRunner/unittests/helpers/baseline.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,5 +525,5 @@ export function baselineAfterTscCompile(
525525
}
526526

527527
export function tscBaselineName(scenario: string, subScenario: string, commandLineArgs: readonly string[], suffix?: string) {
528-
return `${ts.isBuild(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch(commandLineArgs) ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}${suffix ? suffix : ""}.js`;
528+
return `${ts.isBuildCommand(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch(commandLineArgs) ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}${suffix ? suffix : ""}.js`;
529529
}

src/testRunner/unittests/tsbuild/commandLine.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,4 +299,11 @@ describe("unittests:: tsbuild:: commandLine::", () => {
299299
verifyNonIncremental({});
300300
verifyNonIncremental({ outFile: "../outFile.js", module: ts.ModuleKind.AMD });
301301
});
302+
303+
verifyTsc({
304+
scenario: "commandLine",
305+
subScenario: "help",
306+
sys: () => TestServerHost.createWatchedSystem(ts.emptyArray),
307+
commandLineArgs: ["--build", "--help"],
308+
});
302309
});

src/testRunner/unittests/tsc/runWithoutArgs.ts renamed to src/testRunner/unittests/tsc/commandLine.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { emptyArray } from "../../_namespaces/ts.js";
22
import { verifyTsc } from "../helpers/tsc.js";
33
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";
44

5-
describe("unittests:: tsc:: runWithoutArgs::", () => {
5+
describe("unittests:: tsc:: commandLine::", () => {
66
verifyTsc({
7-
scenario: "runWithoutArgs",
7+
scenario: "commandLine",
88
subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped",
99
sys: () =>
1010
TestServerHost.createWatchedSystem(emptyArray, {
@@ -14,19 +14,40 @@ describe("unittests:: tsc:: runWithoutArgs::", () => {
1414
});
1515

1616
verifyTsc({
17-
scenario: "runWithoutArgs",
17+
scenario: "commandLine",
1818
subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped when host can't provide terminal width",
1919
sys: () => TestServerHost.createWatchedSystem(emptyArray),
2020
commandLineArgs: emptyArray,
2121
});
2222

2323
verifyTsc({
24-
scenario: "runWithoutArgs",
24+
scenario: "commandLine",
2525
subScenario: "does not add color when NO_COLOR is set",
2626
sys: () =>
2727
TestServerHost.createWatchedSystem(emptyArray, {
2828
environmentVariables: new Map([["NO_COLOR", "true"]]),
2929
}),
3030
commandLineArgs: emptyArray,
3131
});
32+
33+
verifyTsc({
34+
scenario: "commandLine",
35+
subScenario: "when build not first argument",
36+
sys: () => TestServerHost.createWatchedSystem(emptyArray),
37+
commandLineArgs: ["--verbose", "--build"],
38+
});
39+
40+
verifyTsc({
41+
scenario: "commandLine",
42+
subScenario: "help",
43+
sys: () => TestServerHost.createWatchedSystem(emptyArray),
44+
commandLineArgs: ["--help"],
45+
});
46+
47+
verifyTsc({
48+
scenario: "commandLine",
49+
subScenario: "help all",
50+
sys: () => TestServerHost.createWatchedSystem(emptyArray),
51+
commandLineArgs: ["--help", "--all"],
52+
});
3253
});

src/testRunner/unittests/tscWatch/incremental.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ describe("unittests:: tscWatch:: incremental:: emit file --incremental", () => {
6060
build();
6161
}
6262

63-
Baseline.runBaseline(`${ts.isBuild(argsToPass) ? "tsbuild/watchMode" : "tscWatch"}/incremental/${subScenario.split(" ").join("-")}-${incremental ? "incremental" : "watch"}.js`, baseline.join("\r\n"));
63+
Baseline.runBaseline(`${ts.isBuildCommand(argsToPass) ? "tsbuild/watchMode" : "tscWatch"}/incremental/${subScenario.split(" ").join("-")}-${incremental ? "incremental" : "watch"}.js`, baseline.join("\r\n"));
6464

6565
function build() {
6666
const closer = ts.executeCommandLine(

tests/baselines/reference/api/typescript.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9123,6 +9123,7 @@ declare namespace ts {
91239123
jsDocParsingMode?: JSDocParsingMode;
91249124
}
91259125
function parseCommandLine(commandLine: readonly string[], readFile?: (path: string) => string | undefined): ParsedCommandLine;
9126+
function parseBuildCommand(commandLine: readonly string[]): ParsedBuildCommand;
91269127
/**
91279128
* Reads the config file, reports errors if any and exits if the config file cannot be found
91289129
*/
@@ -9177,6 +9178,13 @@ declare namespace ts {
91779178
options: TypeAcquisition;
91789179
errors: Diagnostic[];
91799180
};
9181+
/** Parsed command line for build */
9182+
interface ParsedBuildCommand {
9183+
buildOptions: BuildOptions;
9184+
watchOptions: WatchOptions | undefined;
9185+
projects: string[];
9186+
errors: Diagnostic[];
9187+
}
91809188
type DiagnosticReporter = (diagnostic: Diagnostic) => void;
91819189
/**
91829190
* Reports config file diagnostics
@@ -9904,6 +9912,8 @@ declare namespace ts {
99049912
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult | undefined;
99059913
}
99069914
type InvalidatedProject<T extends BuilderProgram> = UpdateOutputFileStampsProject | BuildInvalidedProject<T>;
9915+
/** Returns true if commandline is --build and needs to be parsed useing parseBuildCommand */
9916+
function isBuildCommand(commandLineArgs: readonly string[]): boolean;
99079917
function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings;
99089918
/**
99099919
* Represents an immutable snapshot of a script at a specified time.Once acquired, the

tests/baselines/reference/config/commandLineParsing/parseCommandLine/Handles may only be used with --build flags.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
--clean --dry --force --verbose
1+
--build --clean --dry --force --verbose
22
CompilerOptions::
33
{}
44
WatchOptions::
55

66
FileNames::
77

88
Errors::
9+
error TS6369: Option '--build' must be the first command line argument.
910
error TS5093: Compiler option '--clean' may only be used with '--build'.
1011
error TS5093: Compiler option '--dry' may only be used with '--build'.
1112
error TS5093: Compiler option '--force' may only be used with '--build'.

tests/baselines/reference/config/showConfig/Shows tsconfig for single option/build/tsconfig.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)