UNPKG

7.33 kBPlain TextView Raw
1import {
2 BuidlerArguments,
3 BuidlerParamDefinitions,
4 ParamDefinition,
5 ParamDefinitionsMap,
6 TaskArguments,
7 TaskDefinition,
8} from "../../types";
9import { BuidlerError } from "../core/errors";
10import { ERRORS } from "../core/errors-list";
11
12export class ArgumentsParser {
13 public static readonly PARAM_PREFIX = "--";
14
15 public static paramNameToCLA(paramName: string): string {
16 return (
17 ArgumentsParser.PARAM_PREFIX +
18 paramName
19 .split(/(?=[A-Z])/g)
20 .map((s) => s.toLowerCase())
21 .join("-")
22 );
23 }
24
25 public static cLAToParamName(cLA: string): string {
26 if (cLA.toLowerCase() !== cLA) {
27 throw new BuidlerError(ERRORS.ARGUMENTS.PARAM_NAME_INVALID_CASING, {
28 param: cLA,
29 });
30 }
31
32 const parts = cLA.slice(ArgumentsParser.PARAM_PREFIX.length).split("-");
33
34 return (
35 parts[0] +
36 parts
37 .slice(1)
38 .map((s) => s[0].toUpperCase() + s.slice(1))
39 .join("")
40 );
41 }
42
43 public parseBuidlerArguments(
44 buidlerParamDefinitions: BuidlerParamDefinitions,
45 envVariableArguments: BuidlerArguments,
46 rawCLAs: string[]
47 ): {
48 buidlerArguments: BuidlerArguments;
49 taskName?: string;
50 unparsedCLAs: string[];
51 } {
52 const buidlerArguments: Partial<BuidlerArguments> = {};
53 let taskName: string | undefined;
54 const unparsedCLAs: string[] = [];
55
56 for (let i = 0; i < rawCLAs.length; i++) {
57 const arg = rawCLAs[i];
58
59 if (taskName === undefined) {
60 if (!this._hasCLAParamNameFormat(arg)) {
61 taskName = arg;
62 continue;
63 }
64
65 if (!this._isCLAParamName(arg, buidlerParamDefinitions)) {
66 throw new BuidlerError(
67 ERRORS.ARGUMENTS.UNRECOGNIZED_COMMAND_LINE_ARG,
68 { argument: arg }
69 );
70 }
71
72 i = this._parseArgumentAt(
73 rawCLAs,
74 i,
75 buidlerParamDefinitions,
76 buidlerArguments
77 );
78 } else {
79 if (!this._isCLAParamName(arg, buidlerParamDefinitions)) {
80 unparsedCLAs.push(arg);
81 continue;
82 }
83
84 i = this._parseArgumentAt(
85 rawCLAs,
86 i,
87 buidlerParamDefinitions,
88 buidlerArguments
89 );
90 }
91 }
92
93 return {
94 buidlerArguments: this._addBuidlerDefaultArguments(
95 buidlerParamDefinitions,
96 envVariableArguments,
97 buidlerArguments
98 ),
99 taskName,
100 unparsedCLAs,
101 };
102 }
103
104 public parseTaskArguments(
105 taskDefinition: TaskDefinition,
106 rawCLAs: string[]
107 ): TaskArguments {
108 const {
109 paramArguments,
110 rawPositionalArguments,
111 } = this._parseTaskParamArguments(taskDefinition, rawCLAs);
112
113 const positionalArguments = this._parsePositionalParamArgs(
114 rawPositionalArguments,
115 taskDefinition.positionalParamDefinitions
116 );
117
118 return { ...paramArguments, ...positionalArguments };
119 }
120
121 private _parseTaskParamArguments(
122 taskDefinition: TaskDefinition,
123 rawCLAs: string[]
124 ) {
125 const paramArguments = {};
126 const rawPositionalArguments: string[] = [];
127
128 for (let i = 0; i < rawCLAs.length; i++) {
129 const arg = rawCLAs[i];
130
131 if (!this._hasCLAParamNameFormat(arg)) {
132 rawPositionalArguments.push(arg);
133 continue;
134 }
135
136 if (!this._isCLAParamName(arg, taskDefinition.paramDefinitions)) {
137 throw new BuidlerError(ERRORS.ARGUMENTS.UNRECOGNIZED_PARAM_NAME, {
138 param: arg,
139 });
140 }
141
142 i = this._parseArgumentAt(
143 rawCLAs,
144 i,
145 taskDefinition.paramDefinitions,
146 paramArguments
147 );
148 }
149
150 this._addTaskDefaultArguments(taskDefinition, paramArguments);
151
152 return { paramArguments, rawPositionalArguments };
153 }
154
155 private _addBuidlerDefaultArguments(
156 buidlerParamDefinitions: BuidlerParamDefinitions,
157 envVariableArguments: BuidlerArguments,
158 buidlerArguments: Partial<BuidlerArguments>
159 ): BuidlerArguments {
160 return {
161 ...envVariableArguments,
162 ...buidlerArguments,
163 };
164 }
165
166 private _addTaskDefaultArguments(
167 taskDefinition: TaskDefinition,
168 taskArguments: TaskArguments
169 ) {
170 for (const paramName of Object.keys(taskDefinition.paramDefinitions)) {
171 const definition = taskDefinition.paramDefinitions[paramName];
172
173 if (taskArguments[paramName] !== undefined) {
174 continue;
175 }
176 if (!definition.isOptional) {
177 throw new BuidlerError(ERRORS.ARGUMENTS.MISSING_TASK_ARGUMENT, {
178 param: ArgumentsParser.paramNameToCLA(paramName),
179 });
180 }
181
182 taskArguments[paramName] = definition.defaultValue;
183 }
184 }
185
186 private _isCLAParamName(str: string, paramDefinitions: ParamDefinitionsMap) {
187 if (!this._hasCLAParamNameFormat(str)) {
188 return false;
189 }
190
191 const name = ArgumentsParser.cLAToParamName(str);
192 return paramDefinitions[name] !== undefined;
193 }
194
195 private _hasCLAParamNameFormat(str: string) {
196 return str.startsWith(ArgumentsParser.PARAM_PREFIX);
197 }
198
199 private _parseArgumentAt(
200 rawCLAs: string[],
201 index: number,
202 paramDefinitions: ParamDefinitionsMap,
203 parsedArguments: TaskArguments
204 ) {
205 const claArg = rawCLAs[index];
206 const paramName = ArgumentsParser.cLAToParamName(claArg);
207 const definition = paramDefinitions[paramName];
208
209 if (parsedArguments[paramName] !== undefined) {
210 throw new BuidlerError(ERRORS.ARGUMENTS.REPEATED_PARAM, {
211 param: claArg,
212 });
213 }
214
215 if (definition.isFlag) {
216 parsedArguments[paramName] = true;
217 } else {
218 index++;
219 const value = rawCLAs[index];
220
221 if (value === undefined) {
222 throw new BuidlerError(ERRORS.ARGUMENTS.MISSING_TASK_ARGUMENT, {
223 param: ArgumentsParser.paramNameToCLA(paramName),
224 });
225 }
226
227 parsedArguments[paramName] = definition.type.parse(paramName, value);
228 }
229
230 return index;
231 }
232
233 private _parsePositionalParamArgs(
234 rawPositionalParamArgs: string[],
235 positionalParamDefinitions: Array<ParamDefinition<any>>
236 ): TaskArguments {
237 const args: TaskArguments = {};
238
239 for (let i = 0; i < positionalParamDefinitions.length; i++) {
240 const definition = positionalParamDefinitions[i];
241
242 const rawArg = rawPositionalParamArgs[i];
243
244 if (rawArg === undefined) {
245 if (!definition.isOptional) {
246 throw new BuidlerError(ERRORS.ARGUMENTS.MISSING_POSITIONAL_ARG, {
247 param: definition.name,
248 });
249 }
250
251 args[definition.name] = definition.defaultValue;
252 } else if (!definition.isVariadic) {
253 args[definition.name] = definition.type.parse(definition.name, rawArg);
254 } else {
255 args[definition.name] = rawPositionalParamArgs
256 .slice(i)
257 .map((raw) => definition.type.parse(definition.name, raw));
258 }
259 }
260
261 const lastDefinition =
262 positionalParamDefinitions[positionalParamDefinitions.length - 1];
263
264 const hasVariadicParam =
265 lastDefinition !== undefined && lastDefinition.isVariadic;
266
267 if (
268 !hasVariadicParam &&
269 rawPositionalParamArgs.length > positionalParamDefinitions.length
270 ) {
271 throw new BuidlerError(ERRORS.ARGUMENTS.UNRECOGNIZED_POSITIONAL_ARG, {
272 argument: rawPositionalParamArgs[positionalParamDefinitions.length],
273 });
274 }
275
276 return args;
277 }
278}