Skip to content

Updates to --build parsing on command line #59874

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 6 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 25 additions & 15 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,15 +639,6 @@ const commandOptionsWithoutBuild: CommandLineOption[] = [
paramType: Diagnostics.FILE_OR_DIRECTORY,
description: Diagnostics.Compile_the_project_given_the_path_to_its_configuration_file_or_to_a_folder_with_a_tsconfig_json,
},
{
name: "build",
type: "boolean",
shortName: "b",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date,
defaultValueDescription: false,
},
{
name: "showConfig",
type: "boolean",
Expand Down Expand Up @@ -1662,9 +1653,21 @@ function isCommandLineOptionOfCustomType(option: CommandLineOption): option is C
return !isString(option.type);
}

/** @internal */
export const tscBuildOption: CommandLineOption = {
name: "build",
type: "boolean",
shortName: "b",
showInSimplifiedHelpView: true,
category: Diagnostics.Command_line_Options,
description: Diagnostics.Build_one_or_more_projects_and_their_dependencies_if_out_of_date,
defaultValueDescription: false,
};

// Build related options
/** @internal */
export const optionsForBuild: CommandLineOption[] = [
tscBuildOption,
{
name: "verbose",
shortName: "v",
Expand Down Expand Up @@ -1849,8 +1852,16 @@ function createUnknownOptionError(
node?: PropertyName,
sourceFile?: TsConfigSourceFile,
) {
if (diagnostics.alternateMode?.getOptionsNameMap().optionsNameMap.has(unknownOption.toLowerCase())) {
return createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(sourceFile, node, diagnostics.alternateMode.diagnostic, unknownOption);
const otherOption = diagnostics.alternateMode?.getOptionsNameMap().optionsNameMap.get(unknownOption.toLowerCase());
if (otherOption) {
return createDiagnosticForNodeInSourceFileOrCompilerDiagnostic(
sourceFile,
node,
otherOption !== tscBuildOption ?
diagnostics.alternateMode!.diagnostic :
Diagnostics.Option_build_must_be_the_first_command_line_argument,
unknownOption,
);
}

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

/** @internal */
/** Parsed command line for build */
export interface ParsedBuildCommand {
buildOptions: BuildOptions;
watchOptions: WatchOptions | undefined;
Expand All @@ -2078,11 +2089,10 @@ const buildOptionsDidYouMeanDiagnostics: ParseCommandLineWorkerDiagnostics = {
optionTypeMismatchDiagnostic: Diagnostics.Build_option_0_requires_a_value_of_type_1,
};

/** @internal */
export function parseBuildCommand(args: readonly string[]): ParsedBuildCommand {
export function parseBuildCommand(commandLine: readonly string[]): ParsedBuildCommand {
const { options, watchOptions, fileNames: projects, errors } = parseCommandLineWorker(
buildOptionsDidYouMeanDiagnostics,
args,
commandLine,
);
const buildOptions = options as BuildOptions;

Expand Down
24 changes: 10 additions & 14 deletions src/compiler/executeCommandLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import {
toPath,
toSorted,
tracing,
tscBuildOption,
validateLocaleAndSetLanguage,
version,
WatchCompilerHost,
Expand Down Expand Up @@ -170,8 +171,8 @@ function shouldBePretty(sys: System, options: CompilerOptions | BuildOptions) {
function getOptionsForHelp(commandLine: ParsedCommandLine) {
// Sort our options by their names, (e.g. "--noImplicitAny" comes before "--watch")
return !!commandLine.options.all ?
toSorted(optionDeclarations, (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
filter(optionDeclarations.slice(), v => !!v.showInSimplifiedHelpView);
toSorted(optionDeclarations.concat(tscBuildOption), (a, b) => compareStringsCaseInsensitive(a.name, b.name)) :
filter(optionDeclarations.concat(tscBuildOption), v => !!v.showInSimplifiedHelpView);
}

function printVersion(sys: System) {
Expand Down Expand Up @@ -507,15 +508,15 @@ function printAllHelp(sys: System, compilerOptions: readonly CommandLineOption[]
let output: string[] = [...getHeader(sys, `${getDiagnosticText(Diagnostics.tsc_Colon_The_TypeScript_Compiler)} - ${getDiagnosticText(Diagnostics.Version_0, version)}`)];
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"))];
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))];
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"))];
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"))];
for (const line of output) {
sys.write(line);
}
}

function printBuildHelp(sys: System, buildOptions: readonly CommandLineOption[]) {
let output: string[] = [...getHeader(sys, `${getDiagnosticText(Diagnostics.tsc_Colon_The_TypeScript_Compiler)} - ${getDiagnosticText(Diagnostics.Version_0, version)}`)];
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"))];
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"))];
for (const line of output) {
sys.write(line);
}
Expand Down Expand Up @@ -559,11 +560,6 @@ function executeCommandLineWorker(
commandLine: ParsedCommandLine,
) {
let reportDiagnostic = createDiagnosticReporter(sys);
if (commandLine.options.build) {
reportDiagnostic(createCompilerDiagnostic(Diagnostics.Option_build_must_be_the_first_command_line_argument));
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
}

// Configuration file name (if any)
let configFileName: string | undefined;
if (commandLine.options.locale) {
Expand Down Expand Up @@ -732,11 +728,11 @@ function executeCommandLineWorker(
}
}

/** @internal */
export function isBuild(commandLineArgs: readonly string[]) {
/** Returns true if commandline is --build and needs to be parsed useing parseBuildCommand */
export function isBuildCommand(commandLineArgs: readonly string[]) {
if (commandLineArgs.length > 0 && commandLineArgs[0].charCodeAt(0) === CharacterCodes.minus) {
const firstOption = commandLineArgs[0].slice(commandLineArgs[0].charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase();
return firstOption === "build" || firstOption === "b";
return firstOption === tscBuildOption.name || firstOption === tscBuildOption.shortName;
}
return false;
}
Expand All @@ -749,8 +745,8 @@ export function executeCommandLine(
cb: ExecuteCommandLineCallbacks,
commandLineArgs: readonly string[],
): void | SolutionBuilder<EmitAndSemanticDiagnosticsBuilderProgram> | WatchOfConfigFile<EmitAndSemanticDiagnosticsBuilderProgram> {
if (isBuild(commandLineArgs)) {
const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs.slice(1));
if (isBuildCommand(commandLineArgs)) {
const { buildOptions, watchOptions, projects, errors } = parseBuildCommand(commandLineArgs);
if (buildOptions.generateCpuProfile && system.enableCPUProfiler) {
system.enableCPUProfiler(buildOptions.generateCpuProfile, () =>
performBuild(
Expand Down
2 changes: 1 addition & 1 deletion src/testRunner/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export * from "./unittests/tsbuildWatch/reexport.js";
export * from "./unittests/tsbuildWatch/roots.js";
export * from "./unittests/tsbuildWatch/watchEnvironment.js";
export * from "./unittests/tsc/cancellationToken.js";
export * from "./unittests/tsc/commandLine.js";
export * from "./unittests/tsc/composite.js";
export * from "./unittests/tsc/declarationEmit.js";
export * from "./unittests/tsc/extends.js";
Expand All @@ -128,7 +129,6 @@ export * from "./unittests/tsc/noEmitOnError.js";
export * from "./unittests/tsc/projectReferences.js";
export * from "./unittests/tsc/projectReferencesConfig.js";
export * from "./unittests/tsc/redirect.js";
export * from "./unittests/tsc/runWithoutArgs.js";
export * from "./unittests/tscWatch/consoleClearing.js";
export * from "./unittests/tscWatch/emit.js";
export * from "./unittests/tscWatch/emitAndErrorUpdates.js";
Expand Down
2 changes: 1 addition & 1 deletion src/testRunner/unittests/config/commandLineParsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe("unittests:: config:: commandLineParsing:: parseCommandLine", () => {

// --lib es6 0.ts
assertParseResult("Parse single option of library flag", ["--lib", "es6", "0.ts"]);
assertParseResult("Handles may only be used with --build flags", ["--clean", "--dry", "--force", "--verbose"]);
assertParseResult("Handles may only be used with --build flags", ["--build", "--clean", "--dry", "--force", "--verbose"]);
// --declarations --allowTS
assertParseResult("Handles did you mean for misspelt flags", ["--declarations", "--allowTS"]);
// --lib es5,es2015.symbol.wellknown 0.ts
Expand Down
2 changes: 1 addition & 1 deletion src/testRunner/unittests/helpers/baseline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,5 +525,5 @@ export function baselineAfterTscCompile(
}

export function tscBaselineName(scenario: string, subScenario: string, commandLineArgs: readonly string[], suffix?: string) {
return `${ts.isBuild(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch(commandLineArgs) ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}${suffix ? suffix : ""}.js`;
return `${ts.isBuildCommand(commandLineArgs) ? "tsbuild" : "tsc"}${isWatch(commandLineArgs) ? "Watch" : ""}/${scenario}/${subScenario.split(" ").join("-")}${suffix ? suffix : ""}.js`;
}
7 changes: 7 additions & 0 deletions src/testRunner/unittests/tsbuild/commandLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,4 +299,11 @@ describe("unittests:: tsbuild:: commandLine::", () => {
verifyNonIncremental({});
verifyNonIncremental({ outFile: "../outFile.js", module: ts.ModuleKind.AMD });
});

verifyTsc({
scenario: "commandLine",
subScenario: "help",
sys: () => TestServerHost.createWatchedSystem(ts.emptyArray),
commandLineArgs: ["--build", "--help"],
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { emptyArray } from "../../_namespaces/ts.js";
import { verifyTsc } from "../helpers/tsc.js";
import { TestServerHost } from "../helpers/virtualFileSystemWithWatch.js";

describe("unittests:: tsc:: runWithoutArgs::", () => {
describe("unittests:: tsc:: commandLine::", () => {
verifyTsc({
scenario: "runWithoutArgs",
scenario: "commandLine",
subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped",
sys: () =>
TestServerHost.createWatchedSystem(emptyArray, {
Expand All @@ -14,19 +14,40 @@ describe("unittests:: tsc:: runWithoutArgs::", () => {
});

verifyTsc({
scenario: "runWithoutArgs",
scenario: "commandLine",
subScenario: "show help with ExitStatus.DiagnosticsPresent_OutputsSkipped when host can't provide terminal width",
sys: () => TestServerHost.createWatchedSystem(emptyArray),
commandLineArgs: emptyArray,
});

verifyTsc({
scenario: "runWithoutArgs",
scenario: "commandLine",
subScenario: "does not add color when NO_COLOR is set",
sys: () =>
TestServerHost.createWatchedSystem(emptyArray, {
environmentVariables: new Map([["NO_COLOR", "true"]]),
}),
commandLineArgs: emptyArray,
});

verifyTsc({
scenario: "commandLine",
subScenario: "when build not first argument",
sys: () => TestServerHost.createWatchedSystem(emptyArray),
commandLineArgs: ["--verbose", "--build"],
});

verifyTsc({
scenario: "commandLine",
subScenario: "help",
sys: () => TestServerHost.createWatchedSystem(emptyArray),
commandLineArgs: ["--help"],
});

verifyTsc({
scenario: "commandLine",
subScenario: "help all",
sys: () => TestServerHost.createWatchedSystem(emptyArray),
commandLineArgs: ["--help", "--all"],
});
});
2 changes: 1 addition & 1 deletion src/testRunner/unittests/tscWatch/incremental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe("unittests:: tscWatch:: incremental:: emit file --incremental", () => {
build();
}

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

function build() {
const closer = ts.executeCommandLine(
Expand Down
10 changes: 10 additions & 0 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9123,6 +9123,7 @@ declare namespace ts {
jsDocParsingMode?: JSDocParsingMode;
}
function parseCommandLine(commandLine: readonly string[], readFile?: (path: string) => string | undefined): ParsedCommandLine;
function parseBuildCommand(commandLine: readonly string[]): ParsedBuildCommand;
/**
* Reads the config file, reports errors if any and exits if the config file cannot be found
*/
Expand Down Expand Up @@ -9177,6 +9178,13 @@ declare namespace ts {
options: TypeAcquisition;
errors: Diagnostic[];
};
/** Parsed command line for build */
interface ParsedBuildCommand {
buildOptions: BuildOptions;
watchOptions: WatchOptions | undefined;
projects: string[];
errors: Diagnostic[];
}
type DiagnosticReporter = (diagnostic: Diagnostic) => void;
/**
* Reports config file diagnostics
Expand Down Expand Up @@ -9904,6 +9912,8 @@ declare namespace ts {
emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult | undefined;
}
type InvalidatedProject<T extends BuilderProgram> = UpdateOutputFileStampsProject | BuildInvalidedProject<T>;
/** Returns true if commandline is --build and needs to be parsed useing parseBuildCommand */
function isBuildCommand(commandLineArgs: readonly string[]): boolean;
function getDefaultFormatCodeSettings(newLineCharacter?: string): FormatCodeSettings;
/**
* Represents an immutable snapshot of a script at a specified time.Once acquired, the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
--clean --dry --force --verbose
--build --clean --dry --force --verbose
CompilerOptions::
{}
WatchOptions::

FileNames::

Errors::
error TS6369: Option '--build' must be the first command line argument.
error TS5093: Compiler option '--clean' may only be used with '--build'.
error TS5093: Compiler option '--dry' may only be used with '--build'.
error TS5093: Compiler option '--force' may only be used with '--build'.
Expand Down

This file was deleted.

Loading