-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy pathcli.options.ts
More file actions
126 lines (114 loc) · 4.05 KB
/
cli.options.ts
File metadata and controls
126 lines (114 loc) · 4.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { exceptionToString } from "@glideapps/ts-necessities";
import {
// biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
hasOwnProperty,
} from "collection-utils";
import commandLineArgs from "command-line-args";
import _ from "lodash";
import {
type OptionDefinition,
type RendererOptions,
type TargetLanguage,
assert,
defaultTargetLanguages,
getTargetLanguage,
isLanguageName,
messageError,
} from "quicktype-core";
import { inferCLIOptions } from "./inference";
import { makeOptionDefinitions } from "./optionDefinitions";
import type { CLIOptions } from "./CLIOptions.types";
// Parse the options in argv and split them into global options and renderer options,
// according to each option definition's `renderer` field. If `partial` is false this
// will throw if it encounters an unknown option.
function parseOptions(
definitions: OptionDefinition[],
argv: string[],
partial: boolean,
): Partial<CLIOptions> {
let opts: commandLineArgs.CommandLineOptions;
try {
opts = commandLineArgs(
definitions.map((def) => ({
...def,
type: def.optionType === "boolean" ? Boolean : String,
})),
{ argv, partial },
);
} catch (e) {
assert(!partial, "Partial option parsing should not have failed");
return messageError("DriverCLIOptionParsingFailed", {
message: exceptionToString(e),
});
}
for (const k of Object.keys(opts)) {
if (opts[k] === null) {
return messageError("DriverCLIOptionParsingFailed", {
message: `Missing value for command line option "${k}"`,
});
}
}
const options: {
[key: string]: unknown;
rendererOptions: RendererOptions;
} = { rendererOptions: {} };
for (const optionDefinition of definitions) {
if (!hasOwnProperty(opts, optionDefinition.name)) {
continue;
}
const optionValue = opts[optionDefinition.name] as string;
if (optionDefinition.kind !== "cli") {
(
options.rendererOptions as Record<
typeof optionDefinition.name,
unknown
>
)[optionDefinition.name] = optionValue;
} else {
const k = _.lowerFirst(
optionDefinition.name.split("-").map(_.upperFirst).join(""),
);
options[k] = optionValue;
}
}
return options;
}
export function parseCLIOptions(
argv: string[],
inputTargetLanguage?: TargetLanguage,
): CLIOptions {
if (argv.length === 0) {
return inferCLIOptions({ help: true }, inputTargetLanguage);
}
const targetLanguages = inputTargetLanguage
? [inputTargetLanguage]
: defaultTargetLanguages;
const optionDefinitions = makeOptionDefinitions(targetLanguages);
// We can only fully parse the options once we know which renderer is selected,
// because there are renderer-specific options. But we only know which renderer
// is selected after we've parsed the options. Hence, we parse the options
// twice. This is the first parse to get the renderer:
const incompleteOptions = inferCLIOptions(
parseOptions(optionDefinitions, argv, true),
inputTargetLanguage,
);
let targetLanguage = inputTargetLanguage as TargetLanguage;
if (inputTargetLanguage === undefined) {
const languageName = isLanguageName(incompleteOptions.lang)
? incompleteOptions.lang
: "typescript";
targetLanguage = getTargetLanguage(languageName);
}
const rendererOptionDefinitions =
targetLanguage.cliOptionDefinitions.actual;
// Use the global options as well as the renderer options from now on:
const allOptionDefinitions = _.concat(
optionDefinitions,
rendererOptionDefinitions,
);
// This is the parse that counts:
return inferCLIOptions(
parseOptions(allOptionDefinitions, argv, false),
targetLanguage,
);
}