Skip to content

Commit 0d60348

Browse files
committed
Unify the commandline parsing worker
1 parent 37c3c5d commit 0d60348

File tree

3 files changed

+58
-43
lines changed

3 files changed

+58
-43
lines changed

src/compiler/commandLineParser.ts

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ namespace ts {
6262
/* @internal */
6363
export const libMap = createMapFromEntries(libEntries);
6464

65-
const commonOptionsWithBuild: CommandLineOption[] = [
65+
/* @internal */
66+
export const commonOptionsWithBuild: CommandLineOption[] = [
6667
{
6768
name: "help",
6869
shortName: "h",
@@ -903,17 +904,27 @@ namespace ts {
903904
}
904905
}
905906

906-
export function parseCommandLine(commandLine: ReadonlyArray<string>, readFile?: (path: string) => string | undefined): ParsedCommandLine {
907-
const options: CompilerOptions = {};
907+
/* @internal */
908+
export interface OptionsBase {
909+
[option: string]: CompilerOptionsValue | undefined;
910+
}
911+
912+
/** Tuple with error messages for 'unknown compiler option', 'option requires type' */
913+
type ParseCommandLineWorkerDiagnostics = [DiagnosticMessage, DiagnosticMessage];
914+
915+
function parseCommandLineWorker<T extends OptionsBase>(
916+
getOptionNameMap: () => OptionNameMap,
917+
[unknownOptionDiagnostic, optionTypeMismatchDiagnostic]: ParseCommandLineWorkerDiagnostics,
918+
commandLine: ReadonlyArray<string>,
919+
readFile?: (path: string) => string | undefined) {
920+
const options = {} as T;
908921
const fileNames: string[] = [];
909-
const projectReferences: ProjectReference[] | undefined = undefined;
910922
const errors: Diagnostic[] = [];
911923

912924
parseStrings(commandLine);
913925
return {
914926
options,
915927
fileNames,
916-
projectReferences,
917928
errors
918929
};
919930

@@ -926,15 +937,15 @@ namespace ts {
926937
parseResponseFile(s.slice(1));
927938
}
928939
else if (s.charCodeAt(0) === CharacterCodes.minus) {
929-
const opt = getOptionFromName(s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
940+
const opt = getOptionDeclarationFromName(getOptionNameMap, s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
930941
if (opt) {
931942
if (opt.isTSConfigOnly) {
932943
errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name));
933944
}
934945
else {
935946
// Check to see if no argument was provided (e.g. "--locale" is the last command-line argument).
936947
if (!args[i] && opt.type !== "boolean") {
937-
errors.push(createCompilerDiagnostic(Diagnostics.Compiler_option_0_expects_an_argument, opt.name));
948+
errors.push(createCompilerDiagnostic(optionTypeMismatchDiagnostic, opt.name));
938949
}
939950

940951
switch (opt.type) {
@@ -971,7 +982,7 @@ namespace ts {
971982
}
972983
}
973984
else {
974-
errors.push(createCompilerDiagnostic(Diagnostics.Unknown_compiler_option_0, s));
985+
errors.push(createCompilerDiagnostic(unknownOptionDiagnostic, s));
975986
}
976987
}
977988
else {
@@ -1014,13 +1025,19 @@ namespace ts {
10141025
}
10151026
}
10161027

1028+
export function parseCommandLine(commandLine: ReadonlyArray<string>, readFile?: (path: string) => string | undefined): ParsedCommandLine {
1029+
return parseCommandLineWorker(getOptionNameMap, [
1030+
Diagnostics.Unknown_compiler_option_0,
1031+
Diagnostics.Compiler_option_0_expects_an_argument
1032+
], commandLine, readFile);
1033+
}
1034+
10171035
/** @internal */
10181036
export function getOptionFromName(optionName: string, allowShort?: boolean): CommandLineOption | undefined {
10191037
return getOptionDeclarationFromName(getOptionNameMap, optionName, allowShort);
10201038
}
10211039

1022-
/*@internal*/
1023-
export function getOptionDeclarationFromName(getOptionNameMap: () => OptionNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
1040+
function getOptionDeclarationFromName(getOptionNameMap: () => OptionNameMap, optionName: string, allowShort = false): CommandLineOption | undefined {
10241041
optionName = optionName.toLowerCase();
10251042
const { optionNameMap, shortOptionNames } = getOptionNameMap();
10261043
// Try to translate short option names to their full equivalents.
@@ -1044,25 +1061,10 @@ namespace ts {
10441061
export function parseBuildCommand(args: string[]): ParsedBuildCommand {
10451062
let buildOptionNameMap: OptionNameMap | undefined;
10461063
const returnBuildOptionNameMap = () => (buildOptionNameMap || (buildOptionNameMap = createOptionNameMap(buildOpts)));
1047-
1048-
const buildOptions: BuildOptions = {};
1049-
const projects: string[] = [];
1050-
let errors: Diagnostic[] | undefined;
1051-
for (const arg of args) {
1052-
if (arg.charCodeAt(0) === CharacterCodes.minus) {
1053-
const opt = getOptionDeclarationFromName(returnBuildOptionNameMap, arg.slice(arg.charCodeAt(1) === CharacterCodes.minus ? 2 : 1), /*allowShort*/ true);
1054-
if (opt) {
1055-
buildOptions[opt.name as keyof BuildOptions] = true;
1056-
}
1057-
else {
1058-
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Unknown_build_option_0, arg));
1059-
}
1060-
}
1061-
else {
1062-
// Not a flag, parse as filename
1063-
projects.push(arg);
1064-
}
1065-
}
1064+
const { options: buildOptions, fileNames: projects, errors } = parseCommandLineWorker<BuildOptions>(returnBuildOptionNameMap, [
1065+
Diagnostics.Unknown_build_option_0,
1066+
Diagnostics.Build_option_0_requires_a_value_of_type_1
1067+
], args);
10661068

10671069
if (projects.length === 0) {
10681070
// tsc -b invoked with no extra arguments; act as if invoked with "tsc -b ."
@@ -1071,19 +1073,19 @@ namespace ts {
10711073

10721074
// Nonsensical combinations
10731075
if (buildOptions.clean && buildOptions.force) {
1074-
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force"));
1076+
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "force"));
10751077
}
10761078
if (buildOptions.clean && buildOptions.verbose) {
1077-
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose"));
1079+
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "verbose"));
10781080
}
10791081
if (buildOptions.clean && buildOptions.watch) {
1080-
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch"));
1082+
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "clean", "watch"));
10811083
}
10821084
if (buildOptions.watch && buildOptions.dry) {
1083-
(errors || (errors = [])).push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry"));
1085+
errors.push(createCompilerDiagnostic(Diagnostics.Options_0_and_1_cannot_be_combined, "watch", "dry"));
10841086
}
10851087

1086-
return { buildOptions, projects, errors: errors || emptyArray };
1088+
return { buildOptions, projects, errors };
10871089
}
10881090

10891091
function getDiagnosticText(_message: DiagnosticMessage, ..._args: any[]): string {

src/compiler/diagnosticMessages.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,7 +2920,10 @@
29202920
"category": "Error",
29212921
"code": 5072
29222922
},
2923-
2923+
"Build option '{0}' requires a value of type {1}.": {
2924+
"category": "Error",
2925+
"code": 5073
2926+
},
29242927

29252928
"Generates a sourcemap for each corresponding '.d.ts' file.": {
29262929
"category": "Message",
@@ -4604,7 +4607,7 @@
46044607
"category": "Message",
46054608
"code": 95062
46064609
},
4607-
4610+
46084611
"Add missing enum member '{0}'": {
46094612
"category": "Message",
46104613
"code": 95063
@@ -4613,12 +4616,12 @@
46134616
"category": "Message",
46144617
"code": 95064
46154618
},
4616-
"Convert to async function":{
4619+
"Convert to async function": {
46174620
"category": "Message",
4618-
"code": 95065
4621+
"code": 95065
46194622
},
46204623
"Convert all to async functions": {
4621-
"category": "Message",
4622-
"code": 95066
4624+
"category": "Message",
4625+
"code": 95066
46234626
}
46244627
}

src/compiler/tsbuild.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace ts {
1717
referencingProjectsMap: ConfigFileMap<ConfigFileMap<boolean>>;
1818
}
1919

20-
export interface BuildOptions {
20+
export interface BuildOptions extends OptionsBase {
2121
dry?: boolean;
2222
force?: boolean;
2323
verbose?: boolean;
@@ -370,6 +370,14 @@ namespace ts {
370370
return host;
371371
}
372372

373+
function getCompilerOptionsOfBuildOptions(buildOptions: BuildOptions): CompilerOptions {
374+
const result = {} as CompilerOptions;
375+
commonOptionsWithBuild.forEach(option => {
376+
result[option.name] = buildOptions[option.name];
377+
});
378+
return result;
379+
}
380+
373381
/**
374382
* A SolutionBuilder has an immutable set of rootNames that are the "entry point" projects, but
375383
* can dynamically add/remove other projects based on changes on the rootNames' references
@@ -384,6 +392,7 @@ namespace ts {
384392

385393
// State of the solution
386394
let options = defaultOptions;
395+
let baseCompilerOptions = getCompilerOptionsOfBuildOptions(options);
387396
type ConfigFileCacheEntry = ParsedCommandLine | Diagnostic;
388397
const configFileCache = createFileMap<ConfigFileCacheEntry>(toPath);
389398
/** Map from output file name to its pre-build timestamp */
@@ -430,6 +439,7 @@ namespace ts {
430439

431440
function resetBuildContext(opts = defaultOptions) {
432441
options = opts;
442+
baseCompilerOptions = getCompilerOptionsOfBuildOptions(options);
433443
configFileCache.clear();
434444
unchangedOutputs.clear();
435445
projectStatus.clear();
@@ -463,7 +473,7 @@ namespace ts {
463473

464474
let diagnostic: Diagnostic | undefined;
465475
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = d => diagnostic = d;
466-
const parsed = getParsedCommandLineOfConfigFile(configFilePath, {}, parseConfigFileHost);
476+
const parsed = getParsedCommandLineOfConfigFile(configFilePath, baseCompilerOptions, parseConfigFileHost);
467477
parseConfigFileHost.onUnRecoverableConfigFileDiagnostic = noop;
468478
configFileCache.setValue(configFilePath, parsed || diagnostic!);
469479
return parsed;
@@ -475,7 +485,7 @@ namespace ts {
475485

476486
function reportWatchStatus(message: DiagnosticMessage, ...args: (string | number | undefined)[]) {
477487
if (hostWithWatch.onWatchStatusChange) {
478-
hostWithWatch.onWatchStatusChange(createCompilerDiagnostic(message, ...args), host.getNewLine(), { preserveWatchOutput: options.preserveWatchOutput });
488+
hostWithWatch.onWatchStatusChange(createCompilerDiagnostic(message, ...args), host.getNewLine(), baseCompilerOptions);
479489
}
480490
}
481491

0 commit comments

Comments
 (0)