@zgeoff/checks 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +66 -2
- package/dist/cli.js +2285 -0
- package/package.json +42 -4
package/dist/cli.js
ADDED
|
@@ -0,0 +1,2285 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// @bun
|
|
3
|
+
|
|
4
|
+
// src/cli.ts
|
|
5
|
+
import { resolve as resolve2 } from "path";
|
|
6
|
+
|
|
7
|
+
// src/missing-program-error.ts
|
|
8
|
+
class MissingProgramError extends Error {
|
|
9
|
+
program;
|
|
10
|
+
constructor(check, program) {
|
|
11
|
+
super(`${check}: ${program} is not on the PATH, and the ${check} check needs it. Install ${program}, then run zgeoff-check again.`);
|
|
12
|
+
this.name = "MissingProgramError";
|
|
13
|
+
this.program = program;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/run-program.ts
|
|
18
|
+
function runProgram(check, context, argv) {
|
|
19
|
+
const [program] = argv;
|
|
20
|
+
if (Bun.which(program, { PATH: context.env["PATH"] ?? "" }) === null) {
|
|
21
|
+
throw new MissingProgramError(check, program);
|
|
22
|
+
}
|
|
23
|
+
const result = Bun.spawnSync([...argv], {
|
|
24
|
+
cwd: context.cwd,
|
|
25
|
+
env: { ...context.env },
|
|
26
|
+
stdin: "ignore",
|
|
27
|
+
stdout: "pipe",
|
|
28
|
+
stderr: "pipe"
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
exitCode: result.exitCode,
|
|
32
|
+
stdout: result.stdout.toString(),
|
|
33
|
+
stderr: result.stderr.toString()
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/checks/parse-shellcheck-output.ts
|
|
38
|
+
function parseShellcheckOutput(result) {
|
|
39
|
+
const problemLine = /^(?<path>.+?):(?<line>\d+):\d+: (?<level>\w+): (?<message>.*)$/u;
|
|
40
|
+
if (result.exitCode > 1) {
|
|
41
|
+
throw new Error(`shell: shellcheck failed: ${result.stderr.trim()}`);
|
|
42
|
+
}
|
|
43
|
+
const findings = result.stdout.split(`
|
|
44
|
+
`).flatMap((line) => {
|
|
45
|
+
const groups = problemLine.exec(line)?.groups;
|
|
46
|
+
if (groups === undefined) {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
return [
|
|
50
|
+
{
|
|
51
|
+
path: groups["path"] ?? "",
|
|
52
|
+
line: Number(groups["line"]),
|
|
53
|
+
message: `shellcheck ${groups["level"]}: ${groups["message"]}`
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
});
|
|
57
|
+
if (result.exitCode === 1 && findings.length === 0) {
|
|
58
|
+
throw new Error(`shell: shellcheck failed: ${result.stderr.trim()}`);
|
|
59
|
+
}
|
|
60
|
+
return findings;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/checks/parse-shfmt-output.ts
|
|
64
|
+
function parseShfmtOutput(result) {
|
|
65
|
+
const parseErrorLine = /^(?<path>.+?):(?<line>\d+):\d+: (?<message>.*)$/u;
|
|
66
|
+
const stderrLines = result.stderr.split(`
|
|
67
|
+
`).filter((line) => line.trim() !== "");
|
|
68
|
+
const failures = stderrLines.filter((line) => !parseErrorLine.test(line));
|
|
69
|
+
if (result.exitCode > 1 || failures.length > 0) {
|
|
70
|
+
throw new Error(`shell: shfmt failed: ${result.stderr.trim()}`);
|
|
71
|
+
}
|
|
72
|
+
const errors = stderrLines.flatMap((line) => {
|
|
73
|
+
const groups = parseErrorLine.exec(line)?.groups;
|
|
74
|
+
if (groups === undefined) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
return [
|
|
78
|
+
{
|
|
79
|
+
path: groups["path"] ?? "",
|
|
80
|
+
line: Number(groups["line"]),
|
|
81
|
+
message: `shfmt cannot parse the file: ${groups["message"]}`
|
|
82
|
+
}
|
|
83
|
+
];
|
|
84
|
+
});
|
|
85
|
+
const diffs = collectDiffFindings(result.stdout);
|
|
86
|
+
if (result.exitCode !== 0 && errors.length === 0 && diffs.length === 0) {
|
|
87
|
+
throw new Error(`shell: shfmt failed: ${result.stderr.trim()}`);
|
|
88
|
+
}
|
|
89
|
+
return [...errors, ...diffs];
|
|
90
|
+
}
|
|
91
|
+
function collectDiffFindings(diff) {
|
|
92
|
+
const diffTargetLine = /^\+\+\+ (?<path>.+)$/u;
|
|
93
|
+
const hunkHeader = /^@@ -(?<start>\d+)(?:,\d+)? \+\d+(?:,\d+)? @@/u;
|
|
94
|
+
const findings = [];
|
|
95
|
+
let path = "";
|
|
96
|
+
let line = 0;
|
|
97
|
+
let reported = true;
|
|
98
|
+
for (const text of diff.split(`
|
|
99
|
+
`)) {
|
|
100
|
+
const target = diffTargetLine.exec(text)?.groups;
|
|
101
|
+
if (target !== undefined) {
|
|
102
|
+
path = target["path"] ?? "";
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const hunk = hunkHeader.exec(text)?.groups;
|
|
106
|
+
if (hunk !== undefined) {
|
|
107
|
+
line = Number(hunk["start"]);
|
|
108
|
+
reported = false;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (reported || text.startsWith("---")) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (text.startsWith("-") || text.startsWith("+")) {
|
|
115
|
+
findings.push({
|
|
116
|
+
path,
|
|
117
|
+
line,
|
|
118
|
+
message: `shfmt would reformat this hunk; run shfmt -w ${path} to fix it`
|
|
119
|
+
});
|
|
120
|
+
reported = true;
|
|
121
|
+
} else if (text.startsWith(" ")) {
|
|
122
|
+
line += 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return findings;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/checks/shell-check.ts
|
|
129
|
+
var shellCheck = {
|
|
130
|
+
name: "shell",
|
|
131
|
+
glob: "**/*.sh",
|
|
132
|
+
check: checkShellFiles
|
|
133
|
+
};
|
|
134
|
+
var BATCH_SIZE = 200;
|
|
135
|
+
function checkShellFiles(context) {
|
|
136
|
+
const plain = {
|
|
137
|
+
...context,
|
|
138
|
+
env: Object.fromEntries(Object.entries(context.env).filter(([name]) => name !== "FORCE_COLOR"))
|
|
139
|
+
};
|
|
140
|
+
const findings = [];
|
|
141
|
+
for (let start = 0;start < plain.files.length; start += BATCH_SIZE) {
|
|
142
|
+
const batch = plain.files.slice(start, start + BATCH_SIZE);
|
|
143
|
+
findings.push(...runShellcheck(plain, batch), ...runShfmt(plain, batch));
|
|
144
|
+
}
|
|
145
|
+
return findings;
|
|
146
|
+
}
|
|
147
|
+
function runShellcheck(context, files) {
|
|
148
|
+
const result = runProgram("shell", context, [
|
|
149
|
+
"shellcheck",
|
|
150
|
+
"--format=gcc",
|
|
151
|
+
"--external-sources",
|
|
152
|
+
"--source-path=SCRIPTDIR",
|
|
153
|
+
`--source-path=${context.cwd}`,
|
|
154
|
+
"--",
|
|
155
|
+
...files
|
|
156
|
+
]);
|
|
157
|
+
return parseShellcheckOutput(result);
|
|
158
|
+
}
|
|
159
|
+
function runShfmt(context, files) {
|
|
160
|
+
const result = runProgram("shell", context, ["shfmt", "-d", "--", ...files]);
|
|
161
|
+
return parseShfmtOutput(result);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// src/all-checks.ts
|
|
165
|
+
var allChecks = [shellCheck];
|
|
166
|
+
|
|
167
|
+
// src/load-config.ts
|
|
168
|
+
import { readFileSync } from "fs";
|
|
169
|
+
import { join } from "path";
|
|
170
|
+
|
|
171
|
+
// ../std/src/is-record.ts
|
|
172
|
+
function isRecord(value) {
|
|
173
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
174
|
+
}
|
|
175
|
+
// src/config-error.ts
|
|
176
|
+
class ConfigError extends Error {
|
|
177
|
+
constructor(message) {
|
|
178
|
+
super(message);
|
|
179
|
+
this.name = "ConfigError";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/load-config.ts
|
|
184
|
+
var CONFIG_FILE = "zgeoff-check.json";
|
|
185
|
+
function loadConfig(cwd, checkNames) {
|
|
186
|
+
let text;
|
|
187
|
+
try {
|
|
188
|
+
text = readFileSync(join(cwd, CONFIG_FILE), "utf8");
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
191
|
+
return { off: new Map };
|
|
192
|
+
}
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
let value;
|
|
196
|
+
try {
|
|
197
|
+
value = JSON.parse(text);
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw new ConfigError(`${CONFIG_FILE}: ${error instanceof Error ? error.message : "not JSON"}`);
|
|
200
|
+
}
|
|
201
|
+
return parseConfig(value, checkNames);
|
|
202
|
+
}
|
|
203
|
+
function parseConfig(value, checkNames) {
|
|
204
|
+
if (!isRecord(value)) {
|
|
205
|
+
throw new ConfigError(`${CONFIG_FILE}: the file must hold an object`);
|
|
206
|
+
}
|
|
207
|
+
const unknownKeys = Object.keys(value).filter((key) => key !== "off");
|
|
208
|
+
if (unknownKeys.length > 0) {
|
|
209
|
+
throw new ConfigError(`${CONFIG_FILE}: unknown key ${unknownKeys.join(", ")}`);
|
|
210
|
+
}
|
|
211
|
+
const entries = value["off"] ?? [];
|
|
212
|
+
if (!Array.isArray(entries)) {
|
|
213
|
+
throw new ConfigError(`${CONFIG_FILE}: off must be a list of { "check", "reason" } entries`);
|
|
214
|
+
}
|
|
215
|
+
const off = new Map;
|
|
216
|
+
for (const [index, entry] of entries.entries()) {
|
|
217
|
+
const at = `${CONFIG_FILE}: off[${index}]`;
|
|
218
|
+
if (!isRecord(entry)) {
|
|
219
|
+
throw new ConfigError(`${at} must be an object with a check and a reason`);
|
|
220
|
+
}
|
|
221
|
+
const check = entry["check"];
|
|
222
|
+
const reason = entry["reason"];
|
|
223
|
+
const extraKeys = Object.keys(entry).filter((key) => key !== "check" && key !== "reason");
|
|
224
|
+
if (extraKeys.length > 0) {
|
|
225
|
+
throw new ConfigError(`${at} has an unknown key ${extraKeys.join(", ")}`);
|
|
226
|
+
}
|
|
227
|
+
if (typeof check !== "string" || !checkNames.includes(check)) {
|
|
228
|
+
throw new ConfigError(`${at} names no known check; the checks are ${checkNames.join(", ")}`);
|
|
229
|
+
}
|
|
230
|
+
if (typeof reason !== "string" || reason.trim() === "") {
|
|
231
|
+
throw new ConfigError(`${at} turns off ${check} with no reason; give one in reason`);
|
|
232
|
+
}
|
|
233
|
+
if (off.has(check)) {
|
|
234
|
+
throw new ConfigError(`${at} turns off ${check} a second time`);
|
|
235
|
+
}
|
|
236
|
+
off.set(check, reason);
|
|
237
|
+
}
|
|
238
|
+
return { off };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/utils-D-MmF6sM.js
|
|
242
|
+
function kebabnize(str) {
|
|
243
|
+
return str.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase());
|
|
244
|
+
}
|
|
245
|
+
function formatChoices(choices) {
|
|
246
|
+
return choices.map((value) => JSON.stringify(value)).join(", ");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/utils.js
|
|
250
|
+
function isLazyCommand(cmd) {
|
|
251
|
+
return typeof cmd === "function" && "commandName" in cmd;
|
|
252
|
+
}
|
|
253
|
+
async function resolveLazyCommand(cmd, name, needRunResolving = false) {
|
|
254
|
+
let command;
|
|
255
|
+
if (isLazyCommand(cmd)) {
|
|
256
|
+
const baseCommand = {
|
|
257
|
+
name: cmd.commandName,
|
|
258
|
+
description: cmd.description,
|
|
259
|
+
args: cmd.args,
|
|
260
|
+
examples: cmd.examples,
|
|
261
|
+
internal: cmd.internal,
|
|
262
|
+
entry: cmd.entry,
|
|
263
|
+
toKebab: cmd.toKebab,
|
|
264
|
+
rendering: cmd.rendering,
|
|
265
|
+
subCommands: cmd.subCommands
|
|
266
|
+
};
|
|
267
|
+
if ("resource" in cmd && cmd.resource)
|
|
268
|
+
baseCommand.resource = cmd.resource;
|
|
269
|
+
command = Object.assign(create(), baseCommand);
|
|
270
|
+
if (needRunResolving) {
|
|
271
|
+
const loaded = await cmd();
|
|
272
|
+
if (typeof loaded === "function")
|
|
273
|
+
command.run = loaded;
|
|
274
|
+
else if (typeof loaded === "object") {
|
|
275
|
+
if (loaded.run == null)
|
|
276
|
+
throw new TypeError(`'run' is required in command: ${cmd.name || name}`);
|
|
277
|
+
command.run = loaded.run;
|
|
278
|
+
command.name = loaded.name ?? cmd.commandName;
|
|
279
|
+
command.description = loaded.description ?? cmd.description;
|
|
280
|
+
command.args = loaded.args ?? cmd.args;
|
|
281
|
+
command.examples = loaded.examples ?? cmd.examples;
|
|
282
|
+
command.internal = loaded.internal ?? cmd.internal;
|
|
283
|
+
command.entry = loaded.entry ?? cmd.entry;
|
|
284
|
+
command.toKebab = loaded.toKebab ?? cmd.toKebab;
|
|
285
|
+
command.rendering = loaded.rendering ?? cmd.rendering;
|
|
286
|
+
command.subCommands = loaded.subCommands || cmd.subCommands;
|
|
287
|
+
if ("resource" in loaded && loaded.resource)
|
|
288
|
+
command.resource = loaded.resource;
|
|
289
|
+
} else
|
|
290
|
+
throw new TypeError(`Cannot resolve command: ${cmd.name || name}`);
|
|
291
|
+
}
|
|
292
|
+
} else
|
|
293
|
+
command = Object.assign(create(), cmd);
|
|
294
|
+
if (command.name == null && name)
|
|
295
|
+
command.name = name;
|
|
296
|
+
return deepFreeze(command);
|
|
297
|
+
}
|
|
298
|
+
function create(obj = null) {
|
|
299
|
+
return Object.create(obj);
|
|
300
|
+
}
|
|
301
|
+
function log(...args) {
|
|
302
|
+
console.log(...args);
|
|
303
|
+
}
|
|
304
|
+
function getCommandSubCommands(cmd) {
|
|
305
|
+
const subCommands = isLazyCommand(cmd) ? cmd.subCommands : typeof cmd === "object" ? cmd.subCommands : undefined;
|
|
306
|
+
if (!subCommands)
|
|
307
|
+
return;
|
|
308
|
+
if (subCommands instanceof Map)
|
|
309
|
+
return subCommands.size > 0 ? subCommands : undefined;
|
|
310
|
+
if (typeof subCommands === "object") {
|
|
311
|
+
const entries = Object.entries(subCommands);
|
|
312
|
+
if (entries.length === 0)
|
|
313
|
+
return;
|
|
314
|
+
const map = /* @__PURE__ */ new Map;
|
|
315
|
+
for (const [name, cmd] of entries)
|
|
316
|
+
map.set(name, cmd);
|
|
317
|
+
return map;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function deepFreeze(obj, ignores = []) {
|
|
321
|
+
if (obj === null || typeof obj !== "object")
|
|
322
|
+
return obj;
|
|
323
|
+
for (const key of Object.keys(obj)) {
|
|
324
|
+
const value = obj[key];
|
|
325
|
+
if (ignores.includes(key))
|
|
326
|
+
continue;
|
|
327
|
+
if (typeof value === "object" && value !== null)
|
|
328
|
+
deepFreeze(value, ignores);
|
|
329
|
+
}
|
|
330
|
+
return Object.freeze(obj);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/context-CSecf6R5.js
|
|
334
|
+
var ANONYMOUS_COMMAND_NAME = "(anonymous)";
|
|
335
|
+
var NOOP = () => {};
|
|
336
|
+
var CLI_OPTIONS_DEFAULT = {
|
|
337
|
+
name: undefined,
|
|
338
|
+
description: undefined,
|
|
339
|
+
version: undefined,
|
|
340
|
+
cwd: undefined,
|
|
341
|
+
usageSilent: false,
|
|
342
|
+
subCommands: undefined,
|
|
343
|
+
leftMargin: 2,
|
|
344
|
+
middleMargin: 10,
|
|
345
|
+
usageOptionType: false,
|
|
346
|
+
usageOptionValue: true,
|
|
347
|
+
strict: false,
|
|
348
|
+
renderHeader: undefined,
|
|
349
|
+
renderUsage: undefined,
|
|
350
|
+
renderValidationErrors: undefined,
|
|
351
|
+
plugins: undefined,
|
|
352
|
+
fallbackToEntry: false
|
|
353
|
+
};
|
|
354
|
+
async function createCommandContext({ args = {}, explicit = {}, values = {}, positionals = [], rest = [], argv = [], tokens = [], command = {}, extensions = {}, cliOptions = {}, callMode = "entry", commandPath = [], omitted = false, validationError }) {
|
|
355
|
+
const _args = Object.entries(args).reduce((acc, [key, value]) => {
|
|
356
|
+
acc[key] = Object.assign(create(), value);
|
|
357
|
+
return acc;
|
|
358
|
+
}, create());
|
|
359
|
+
const env = Object.assign(create(), CLI_OPTIONS_DEFAULT, cliOptions);
|
|
360
|
+
if (command.rendering) {
|
|
361
|
+
const { header, usage, validationErrors } = command.rendering;
|
|
362
|
+
if (header !== undefined)
|
|
363
|
+
env.renderHeader = header;
|
|
364
|
+
if (usage !== undefined)
|
|
365
|
+
env.renderUsage = usage;
|
|
366
|
+
if (validationErrors !== undefined)
|
|
367
|
+
env.renderValidationErrors = validationErrors;
|
|
368
|
+
}
|
|
369
|
+
const core = Object.assign(create(), {
|
|
370
|
+
name: getCommandName(command),
|
|
371
|
+
description: command.description,
|
|
372
|
+
omitted,
|
|
373
|
+
callMode,
|
|
374
|
+
commandPath,
|
|
375
|
+
env,
|
|
376
|
+
args: _args,
|
|
377
|
+
explicit,
|
|
378
|
+
values,
|
|
379
|
+
positionals,
|
|
380
|
+
rest,
|
|
381
|
+
_: argv,
|
|
382
|
+
tokens,
|
|
383
|
+
toKebab: command.toKebab,
|
|
384
|
+
log: cliOptions.usageSilent ? NOOP : log,
|
|
385
|
+
validationError
|
|
386
|
+
});
|
|
387
|
+
if (Object.keys(extensions).length > 0) {
|
|
388
|
+
const ext = create(null);
|
|
389
|
+
Object.defineProperty(core, "extensions", {
|
|
390
|
+
value: ext,
|
|
391
|
+
writable: false,
|
|
392
|
+
enumerable: true,
|
|
393
|
+
configurable: true
|
|
394
|
+
});
|
|
395
|
+
for (const [key, extension] of Object.entries(extensions)) {
|
|
396
|
+
ext[key] = await extension.factory(core, command);
|
|
397
|
+
if (extension.onFactory)
|
|
398
|
+
await extension.onFactory(core, command);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return deepFreeze(core, ["extensions", "entryCommand"]);
|
|
402
|
+
}
|
|
403
|
+
function getCommandName(cmd) {
|
|
404
|
+
if (isLazyCommand(cmd))
|
|
405
|
+
return cmd.commandName || cmd.name || "(anonymous)";
|
|
406
|
+
else if (typeof cmd === "object")
|
|
407
|
+
return cmd.name || "(anonymous)";
|
|
408
|
+
else
|
|
409
|
+
return ANONYMOUS_COMMAND_NAME;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/resolver-CAlgxe_R-3pz9PVFu.js
|
|
413
|
+
var HYPHEN_CHAR = "-";
|
|
414
|
+
var HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
|
|
415
|
+
var EQUAL_CHAR = "=";
|
|
416
|
+
var EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
|
|
417
|
+
var TERMINATOR = "--";
|
|
418
|
+
var SHORT_OPTION_PREFIX = HYPHEN_CHAR;
|
|
419
|
+
var LONG_OPTION_PREFIX = "--";
|
|
420
|
+
function parseArgs(args, options = {}) {
|
|
421
|
+
const { allowCompatible = false } = options;
|
|
422
|
+
const tokens = [];
|
|
423
|
+
const remainings = [...args];
|
|
424
|
+
let index = -1;
|
|
425
|
+
let groupCount = 0;
|
|
426
|
+
let hasShortGroupValue = false;
|
|
427
|
+
let shortGroupValueInline = true;
|
|
428
|
+
while (remainings.length > 0) {
|
|
429
|
+
const arg = remainings.shift();
|
|
430
|
+
if (arg == undefined)
|
|
431
|
+
break;
|
|
432
|
+
if (groupCount > 0)
|
|
433
|
+
groupCount--;
|
|
434
|
+
else
|
|
435
|
+
index++;
|
|
436
|
+
if (arg === TERMINATOR) {
|
|
437
|
+
tokens.push({
|
|
438
|
+
kind: "option-terminator",
|
|
439
|
+
index
|
|
440
|
+
});
|
|
441
|
+
const mapped = remainings.map((arg) => {
|
|
442
|
+
return {
|
|
443
|
+
kind: "positional",
|
|
444
|
+
index: ++index,
|
|
445
|
+
value: arg
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
tokens.push(...mapped);
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
if (isShortOption(arg)) {
|
|
452
|
+
const shortOption = arg.charAt(1);
|
|
453
|
+
let value;
|
|
454
|
+
let inlineValue;
|
|
455
|
+
if (groupCount) {
|
|
456
|
+
tokens.push({
|
|
457
|
+
kind: "option",
|
|
458
|
+
name: shortOption,
|
|
459
|
+
rawName: arg,
|
|
460
|
+
index,
|
|
461
|
+
value,
|
|
462
|
+
inlineValue
|
|
463
|
+
});
|
|
464
|
+
if (groupCount === 1 && hasShortGroupValue) {
|
|
465
|
+
value = remainings.shift();
|
|
466
|
+
inlineValue = shortGroupValueInline;
|
|
467
|
+
hasShortGroupValue = false;
|
|
468
|
+
tokens.push({
|
|
469
|
+
kind: "option",
|
|
470
|
+
index,
|
|
471
|
+
value,
|
|
472
|
+
inlineValue
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
} else
|
|
476
|
+
tokens.push({
|
|
477
|
+
kind: "option",
|
|
478
|
+
name: shortOption,
|
|
479
|
+
rawName: arg,
|
|
480
|
+
index,
|
|
481
|
+
value,
|
|
482
|
+
inlineValue
|
|
483
|
+
});
|
|
484
|
+
if (value != null)
|
|
485
|
+
++index;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (isShortOptionGroup(arg)) {
|
|
489
|
+
const expanded = [];
|
|
490
|
+
let separated = false;
|
|
491
|
+
let dashed = false;
|
|
492
|
+
let shortValue = "";
|
|
493
|
+
for (let i = 1;i < arg.length; i++) {
|
|
494
|
+
const shortableOption = arg.charAt(i);
|
|
495
|
+
if (separated)
|
|
496
|
+
shortValue += shortableOption;
|
|
497
|
+
else if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE && expanded.length > 0)
|
|
498
|
+
separated = true;
|
|
499
|
+
else if (!allowCompatible && shortableOption.codePointAt(0) === HYPHEN_CODE) {
|
|
500
|
+
separated = true;
|
|
501
|
+
dashed = true;
|
|
502
|
+
shortValue += shortableOption;
|
|
503
|
+
} else
|
|
504
|
+
expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
|
|
505
|
+
}
|
|
506
|
+
hasShortGroupValue = separated;
|
|
507
|
+
shortGroupValueInline = !dashed;
|
|
508
|
+
if (separated)
|
|
509
|
+
expanded.push(shortValue);
|
|
510
|
+
remainings.unshift(...expanded);
|
|
511
|
+
groupCount = expanded.length;
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
if (isLongOption(arg)) {
|
|
515
|
+
const longOption = arg.slice(2);
|
|
516
|
+
tokens.push({
|
|
517
|
+
kind: "option",
|
|
518
|
+
name: longOption,
|
|
519
|
+
rawName: arg,
|
|
520
|
+
index,
|
|
521
|
+
value: undefined,
|
|
522
|
+
inlineValue: undefined
|
|
523
|
+
});
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
if (isLongOptionAndValue(arg)) {
|
|
527
|
+
const equalIndex = arg.indexOf(EQUAL_CHAR);
|
|
528
|
+
const longOption = arg.slice(2, equalIndex);
|
|
529
|
+
const value = arg.slice(equalIndex + 1);
|
|
530
|
+
tokens.push({
|
|
531
|
+
kind: "option",
|
|
532
|
+
name: longOption,
|
|
533
|
+
rawName: `${LONG_OPTION_PREFIX}${longOption}`,
|
|
534
|
+
index,
|
|
535
|
+
value,
|
|
536
|
+
inlineValue: true
|
|
537
|
+
});
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
tokens.push({
|
|
541
|
+
kind: "positional",
|
|
542
|
+
index,
|
|
543
|
+
value: arg
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
return tokens;
|
|
547
|
+
}
|
|
548
|
+
function isShortOption(arg) {
|
|
549
|
+
return arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE;
|
|
550
|
+
}
|
|
551
|
+
function isShortOptionGroup(arg) {
|
|
552
|
+
if (arg.length <= 2)
|
|
553
|
+
return false;
|
|
554
|
+
if (arg.codePointAt(0) !== HYPHEN_CODE)
|
|
555
|
+
return false;
|
|
556
|
+
if (arg.codePointAt(1) === HYPHEN_CODE)
|
|
557
|
+
return false;
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
function isLongOption(arg) {
|
|
561
|
+
return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3);
|
|
562
|
+
}
|
|
563
|
+
function isLongOptionAndValue(arg) {
|
|
564
|
+
return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3);
|
|
565
|
+
}
|
|
566
|
+
function hasLongOptionPrefix(arg) {
|
|
567
|
+
return arg.startsWith(LONG_OPTION_PREFIX) && arg.length > 2;
|
|
568
|
+
}
|
|
569
|
+
var PURE_PARSE = Symbol.for("args-tokens.pureParse");
|
|
570
|
+
var ArgsValidationErrorKeys = {
|
|
571
|
+
requiredOption: "err:arg:required-option",
|
|
572
|
+
requiredPositional: "err:arg:required-positional",
|
|
573
|
+
invalidType: "err:arg:invalid-type",
|
|
574
|
+
invalidChoice: "err:arg:invalid-choice",
|
|
575
|
+
customParse: "err:arg:custom-parse",
|
|
576
|
+
unknownOption: "err:arg:unknown-option",
|
|
577
|
+
unexpectedValue: "err:arg:unexpected-value",
|
|
578
|
+
missingValue: "err:arg:missing-value",
|
|
579
|
+
conflict: "err:arg:conflict",
|
|
580
|
+
invalidDefault: "err:arg:invalid-default"
|
|
581
|
+
};
|
|
582
|
+
var ARGS_VALIDATION_ERROR_BRAND = Symbol.for("args-tokens.ArgsValidationError");
|
|
583
|
+
var ArgsValidationError = class extends Error {
|
|
584
|
+
code;
|
|
585
|
+
values;
|
|
586
|
+
constructor(message, options = {}) {
|
|
587
|
+
super(message, { cause: options.cause });
|
|
588
|
+
this.name = "ArgsValidationError";
|
|
589
|
+
this.code = options.code;
|
|
590
|
+
this.values = options.values ?? {};
|
|
591
|
+
Object.defineProperty(this, ARGS_VALIDATION_ERROR_BRAND, {
|
|
592
|
+
value: true,
|
|
593
|
+
enumerable: false,
|
|
594
|
+
writable: false,
|
|
595
|
+
configurable: false
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
function isArgsValidationError(error) {
|
|
600
|
+
if (error instanceof ArgsValidationError)
|
|
601
|
+
return true;
|
|
602
|
+
if (typeof error !== "object" || error === null)
|
|
603
|
+
return false;
|
|
604
|
+
if (!Object.hasOwn(error, ARGS_VALIDATION_ERROR_BRAND) || error[ARGS_VALIDATION_ERROR_BRAND] !== true)
|
|
605
|
+
return false;
|
|
606
|
+
const values = error.values;
|
|
607
|
+
return typeof values === "object" && values !== null;
|
|
608
|
+
}
|
|
609
|
+
var SKIP_POSITIONAL_DEFAULT = -1;
|
|
610
|
+
var ARG_TYPES = /* @__PURE__ */ new Set([
|
|
611
|
+
"string",
|
|
612
|
+
"boolean",
|
|
613
|
+
"number",
|
|
614
|
+
"enum",
|
|
615
|
+
"positional",
|
|
616
|
+
"custom"
|
|
617
|
+
]);
|
|
618
|
+
function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKIP_POSITIONAL_DEFAULT, toKebab = false } = {}) {
|
|
619
|
+
const skipPositionalIndex = typeof skipPositional === "number" ? Math.max(skipPositional, SKIP_POSITIONAL_DEFAULT) : SKIP_POSITIONAL_DEFAULT;
|
|
620
|
+
const rest = [];
|
|
621
|
+
const optionTokens = [];
|
|
622
|
+
const positionalTokens = [];
|
|
623
|
+
const argEntries = Object.entries(args);
|
|
624
|
+
let currentLongOption;
|
|
625
|
+
let currentShortOption;
|
|
626
|
+
const expandableShortOptions = [];
|
|
627
|
+
function getOptionName(rawArg, schema) {
|
|
628
|
+
return toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
|
|
629
|
+
}
|
|
630
|
+
function checkLongTokenName(option, schema, token) {
|
|
631
|
+
if (token.rawName == undefined || !hasLongOptionPrefix(token.rawName))
|
|
632
|
+
return false;
|
|
633
|
+
return token.name === option || isNegatedToken(token, option, schema);
|
|
634
|
+
}
|
|
635
|
+
function toShortValue() {
|
|
636
|
+
if (expandableShortOptions.length === 0)
|
|
637
|
+
return;
|
|
638
|
+
else {
|
|
639
|
+
let value = "";
|
|
640
|
+
for (let i = 0;i < expandableShortOptions.length; i++)
|
|
641
|
+
value += expandableShortOptions[i].name ?? "";
|
|
642
|
+
expandableShortOptions.length = 0;
|
|
643
|
+
return value;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function applyLongOptionValue(value) {
|
|
647
|
+
if (currentLongOption) {
|
|
648
|
+
currentLongOption.value = value;
|
|
649
|
+
optionTokens.push({ ...currentLongOption });
|
|
650
|
+
currentLongOption = undefined;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function applyShortOptionValue(value) {
|
|
654
|
+
if (currentShortOption) {
|
|
655
|
+
currentShortOption.value = value ?? toShortValue();
|
|
656
|
+
optionTokens.push({ ...currentShortOption });
|
|
657
|
+
currentShortOption = undefined;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
const schemas = Object.values(args);
|
|
661
|
+
const booleanLongOptionNames = /* @__PURE__ */ new Set;
|
|
662
|
+
for (let i = 0;i < argEntries.length; i++) {
|
|
663
|
+
const entry = argEntries[i];
|
|
664
|
+
const rawArg = entry[0];
|
|
665
|
+
const schema = entry[1];
|
|
666
|
+
if (typeof schema.parse !== "function") {
|
|
667
|
+
if (schema.type === "custom")
|
|
668
|
+
throw new TypeError(`argument '${getOptionName(rawArg, schema)}' should have a 'parse' function`);
|
|
669
|
+
if (schema.type === "enum" && !Array.isArray(schema.choices))
|
|
670
|
+
throw new TypeError(`argument '${getOptionName(rawArg, schema)}' should have a 'choices' array`);
|
|
671
|
+
if (!ARG_TYPES.has(schema.type))
|
|
672
|
+
throw new Error(`Unsupported argument type '${schema.type}' for option '${getOptionName(rawArg, schema)}'`);
|
|
673
|
+
}
|
|
674
|
+
if (schema.type !== "boolean")
|
|
675
|
+
continue;
|
|
676
|
+
const arg = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
|
|
677
|
+
booleanLongOptionNames.add(arg);
|
|
678
|
+
if (schema.negatable === true)
|
|
679
|
+
booleanLongOptionNames.add(`no-${arg}`);
|
|
680
|
+
}
|
|
681
|
+
let terminated = false;
|
|
682
|
+
for (let i = 0;i < tokens.length; i++) {
|
|
683
|
+
const token = tokens[i];
|
|
684
|
+
if (token.kind === "positional") {
|
|
685
|
+
if (terminated) {
|
|
686
|
+
rest.push(token.value);
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
if (currentShortOption) {
|
|
690
|
+
if (schemas.find((schema) => schema.short === currentShortOption.name && schema.type === "boolean") || expandableShortOptions.length > 0) {
|
|
691
|
+
positionalTokens.push({ ...token });
|
|
692
|
+
applyShortOptionValue();
|
|
693
|
+
} else
|
|
694
|
+
applyShortOptionValue(token.value);
|
|
695
|
+
} else if (currentLongOption) {
|
|
696
|
+
if (booleanLongOptionNames.has(currentLongOption.name)) {
|
|
697
|
+
positionalTokens.push({ ...token });
|
|
698
|
+
applyLongOptionValue();
|
|
699
|
+
} else
|
|
700
|
+
applyLongOptionValue(token.value);
|
|
701
|
+
} else
|
|
702
|
+
positionalTokens.push({ ...token });
|
|
703
|
+
} else if (token.kind === "option") {
|
|
704
|
+
if (token.rawName) {
|
|
705
|
+
if (token.rawName.startsWith("--")) {
|
|
706
|
+
applyLongOptionValue();
|
|
707
|
+
applyShortOptionValue();
|
|
708
|
+
if (token.inlineValue)
|
|
709
|
+
optionTokens.push({ ...token });
|
|
710
|
+
else
|
|
711
|
+
currentLongOption = { ...token };
|
|
712
|
+
} else if (isShortOption(token.rawName)) {
|
|
713
|
+
if (currentShortOption) {
|
|
714
|
+
if (currentShortOption.index === token.index) {
|
|
715
|
+
if (shortGrouping) {
|
|
716
|
+
currentShortOption.value = token.value;
|
|
717
|
+
optionTokens.push({ ...currentShortOption });
|
|
718
|
+
currentShortOption = { ...token };
|
|
719
|
+
} else
|
|
720
|
+
expandableShortOptions.push({ ...token });
|
|
721
|
+
} else {
|
|
722
|
+
currentShortOption.value = toShortValue();
|
|
723
|
+
optionTokens.push({ ...currentShortOption });
|
|
724
|
+
currentShortOption = { ...token };
|
|
725
|
+
}
|
|
726
|
+
applyLongOptionValue();
|
|
727
|
+
} else {
|
|
728
|
+
currentShortOption = { ...token };
|
|
729
|
+
applyLongOptionValue();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
if (currentShortOption && currentShortOption.index == token.index) {
|
|
734
|
+
const letters = toShortValue();
|
|
735
|
+
currentShortOption.value = letters === undefined ? token.value : `${letters}${token.inlineValue ? "=" : ""}${token.value ?? ""}`;
|
|
736
|
+
currentShortOption.inlineValue = token.inlineValue;
|
|
737
|
+
optionTokens.push({ ...currentShortOption });
|
|
738
|
+
currentShortOption = undefined;
|
|
739
|
+
}
|
|
740
|
+
applyLongOptionValue();
|
|
741
|
+
}
|
|
742
|
+
} else {
|
|
743
|
+
if (token.kind === "option-terminator")
|
|
744
|
+
terminated = true;
|
|
745
|
+
applyLongOptionValue();
|
|
746
|
+
applyShortOptionValue();
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
applyLongOptionValue();
|
|
750
|
+
applyShortOptionValue();
|
|
751
|
+
const values = Object.create(null);
|
|
752
|
+
const errors = [];
|
|
753
|
+
const explicit = Object.create(null);
|
|
754
|
+
const actualInputNames = /* @__PURE__ */ new Map;
|
|
755
|
+
let requiredPositionalsAfter;
|
|
756
|
+
const positionalItemCount = tokens.filter((token) => token.kind === "positional").length;
|
|
757
|
+
function getPositionalSkipIndex() {
|
|
758
|
+
return Math.min(skipPositionalIndex, positionalItemCount);
|
|
759
|
+
}
|
|
760
|
+
function getRequiredPositionalsAfter(rawArg) {
|
|
761
|
+
requiredPositionalsAfter ??= createRequiredPositionalsAfter(argEntries);
|
|
762
|
+
return requiredPositionalsAfter[rawArg] ?? 0;
|
|
763
|
+
}
|
|
764
|
+
let positionalsCount = 0;
|
|
765
|
+
for (let argIndex = 0;argIndex < argEntries.length; argIndex++) {
|
|
766
|
+
const entry = argEntries[argIndex];
|
|
767
|
+
const rawArg = entry[0];
|
|
768
|
+
const schema = entry[1];
|
|
769
|
+
const arg = getOptionName(rawArg, schema);
|
|
770
|
+
explicit[rawArg] = false;
|
|
771
|
+
if (schema.type === "positional") {
|
|
772
|
+
if (skipPositionalIndex > SKIP_POSITIONAL_DEFAULT)
|
|
773
|
+
while (positionalsCount <= getPositionalSkipIndex())
|
|
774
|
+
positionalsCount++;
|
|
775
|
+
if (schema.multiple) {
|
|
776
|
+
const availablePositionals = Math.max(positionalTokens.length - positionalsCount, 0);
|
|
777
|
+
const positionalsToConsume = availablePositionals > 0 ? Math.max(availablePositionals - getRequiredPositionalsAfter(rawArg), 0) : 0;
|
|
778
|
+
if (positionalsToConsume > 0) {
|
|
779
|
+
const endPositionals = positionalsCount + positionalsToConsume;
|
|
780
|
+
if (typeof schema.parse === "function") {
|
|
781
|
+
const parsed = [];
|
|
782
|
+
for (let i = positionalsCount;i < endPositionals; i++) {
|
|
783
|
+
const p = positionalTokens[i];
|
|
784
|
+
const [parsedValue, error] = parseSchemaValue(p.value, rawArg, arg, schema);
|
|
785
|
+
if (error)
|
|
786
|
+
errors.push(error);
|
|
787
|
+
else
|
|
788
|
+
parsed.push(parsedValue);
|
|
789
|
+
}
|
|
790
|
+
values[rawArg] = parsed;
|
|
791
|
+
} else {
|
|
792
|
+
const valuesArray = [];
|
|
793
|
+
for (let i = positionalsCount;i < endPositionals; i++)
|
|
794
|
+
valuesArray.push(positionalTokens[i].value);
|
|
795
|
+
values[rawArg] = valuesArray;
|
|
796
|
+
}
|
|
797
|
+
positionalsCount = endPositionals;
|
|
798
|
+
explicit[rawArg] = true;
|
|
799
|
+
} else if (schema.required)
|
|
800
|
+
errors.push(createRequireError(rawArg, arg, schema));
|
|
801
|
+
else if (hasDefault(schema))
|
|
802
|
+
values[rawArg] = getDefaultValue(schema);
|
|
803
|
+
} else {
|
|
804
|
+
const positional = positionalTokens[positionalsCount];
|
|
805
|
+
if (shouldRequireMissingSinglePositional(schema)) {
|
|
806
|
+
if (positional != null) {
|
|
807
|
+
resolveSinglePositionalValue(values, errors, rawArg, arg, schema, positional);
|
|
808
|
+
explicit[rawArg] = true;
|
|
809
|
+
positionalsCount++;
|
|
810
|
+
} else
|
|
811
|
+
errors.push(createRequireError(rawArg, arg, schema));
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
if (positional != null) {
|
|
815
|
+
const requiredPositionals = getRequiredPositionalsAfter(rawArg);
|
|
816
|
+
if (Math.max(positionalTokens.length - positionalsCount, 0) > requiredPositionals) {
|
|
817
|
+
resolveSinglePositionalValue(values, errors, rawArg, arg, schema, positional);
|
|
818
|
+
explicit[rawArg] = true;
|
|
819
|
+
positionalsCount++;
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
if (hasDefault(schema))
|
|
824
|
+
values[rawArg] = schema.default;
|
|
825
|
+
}
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
if (schema.required) {
|
|
829
|
+
if (!optionTokens.find((token) => {
|
|
830
|
+
return schema.short && token.name === schema.short && token.rawName != null && isShortOption(token.rawName) || checkLongTokenName(arg, schema, token);
|
|
831
|
+
})) {
|
|
832
|
+
errors.push(createRequireError(rawArg, arg, schema));
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
let resolved = false;
|
|
837
|
+
for (let i = 0;i < optionTokens.length; i++) {
|
|
838
|
+
const token = optionTokens[i];
|
|
839
|
+
if (checkLongTokenName(arg, schema, token) || schema.short === token.name && token.rawName != null && isShortOption(token.rawName)) {
|
|
840
|
+
explicit[rawArg] = true;
|
|
841
|
+
const rawName = token.rawName;
|
|
842
|
+
const actualInputName = isShortOption(rawName) ? `-${token.name}` : rawName;
|
|
843
|
+
actualInputNames.set(rawArg, actualInputName);
|
|
844
|
+
const missing = schema.type !== "boolean" && token.value === undefined;
|
|
845
|
+
if (!missing) {
|
|
846
|
+
const invalid = validateRequire(token, rawArg, arg, schema);
|
|
847
|
+
if (invalid) {
|
|
848
|
+
errors.push(invalid);
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
const [parsedValue, error] = missing ? [undefined, createMissingValueError(rawArg, arg, schema, findOptionLikeNextArgument(tokens, optionTokens, i, argEntries, toKebab))] : parse(token, rawArg, arg, schema);
|
|
853
|
+
if (error)
|
|
854
|
+
errors.push(error);
|
|
855
|
+
else {
|
|
856
|
+
resolved = true;
|
|
857
|
+
if (schema.multiple) {
|
|
858
|
+
values[rawArg] ||= [];
|
|
859
|
+
values[rawArg].push(parsedValue);
|
|
860
|
+
} else
|
|
861
|
+
values[rawArg] = parsedValue;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (!resolved && schema.default != null) {
|
|
866
|
+
if (hasDefaultOutsideChoices(schema))
|
|
867
|
+
errors.push(createDefaultChoiceError(rawArg, arg, schema));
|
|
868
|
+
else
|
|
869
|
+
values[rawArg] = getDefaultValue(schema);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
const conflictErrors = checkConflicts(args, explicit, toKebab, actualInputNames);
|
|
873
|
+
errors.push(...conflictErrors);
|
|
874
|
+
return {
|
|
875
|
+
values,
|
|
876
|
+
positionals: positionalTokens.map((token) => token.value),
|
|
877
|
+
rest,
|
|
878
|
+
error: errors.length > 0 ? new AggregateError(errors) : undefined,
|
|
879
|
+
explicit
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
function isNegatedToken(token, option, schema) {
|
|
883
|
+
return schema.type === "boolean" && schema.negatable === true && token.name === `no-${option}`;
|
|
884
|
+
}
|
|
885
|
+
function parse(token, rawArg, option, schema) {
|
|
886
|
+
if (schema.type === "enum" && schema.choices && !schema.choices.includes(token.value))
|
|
887
|
+
return [undefined, createChoiceError(rawArg, option, schema, token.value)];
|
|
888
|
+
if (typeof schema.parse === "function") {
|
|
889
|
+
if (schema.type === "boolean") {
|
|
890
|
+
const [boolValue, error] = resolveBooleanValue(token, rawArg, option, schema);
|
|
891
|
+
if (error)
|
|
892
|
+
return [undefined, error];
|
|
893
|
+
return parseSchemaValue(String(boolValue), rawArg, option, schema);
|
|
894
|
+
}
|
|
895
|
+
return parseSchemaValue(token.value, rawArg, option, schema);
|
|
896
|
+
}
|
|
897
|
+
switch (schema.type) {
|
|
898
|
+
case "string":
|
|
899
|
+
return typeof token.value === "string" ? [token.value, undefined] : [undefined, createTypeError(rawArg, option, schema, token.value)];
|
|
900
|
+
case "boolean":
|
|
901
|
+
return resolveBooleanValue(token, rawArg, option, schema);
|
|
902
|
+
case "number":
|
|
903
|
+
if (typeof token.value !== "string" || !isNumeric(token.value))
|
|
904
|
+
return [undefined, createTypeError(rawArg, option, schema, token.value)];
|
|
905
|
+
return [+token.value, undefined];
|
|
906
|
+
case "enum":
|
|
907
|
+
return [token.value, undefined];
|
|
908
|
+
case "custom":
|
|
909
|
+
throw new TypeError(`argument '${option}' should have a 'parse' function`);
|
|
910
|
+
default:
|
|
911
|
+
throw new Error(`Unsupported argument type '${schema.type}' for option '${option}'`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
function resolveBooleanValue(token, rawArg, option, schema) {
|
|
915
|
+
const negated = isNegatedToken(token, option, schema);
|
|
916
|
+
if (!token.inlineValue)
|
|
917
|
+
return [!negated, undefined];
|
|
918
|
+
if (negated)
|
|
919
|
+
return [undefined, createUnexpectedValueError(rawArg, option, schema, token)];
|
|
920
|
+
if (token.value !== "true" && token.value !== "false")
|
|
921
|
+
return [undefined, createTypeError(rawArg, option, schema, token.value)];
|
|
922
|
+
return [token.value === "true", undefined];
|
|
923
|
+
}
|
|
924
|
+
function parseSchemaValue(value, rawArg, option, schema) {
|
|
925
|
+
try {
|
|
926
|
+
return [schema.parse(value), undefined];
|
|
927
|
+
} catch (error) {
|
|
928
|
+
return [undefined, createCustomParseError(error, rawArg, option, schema, value)];
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
function createRequireError(rawArg, option, schema) {
|
|
932
|
+
return new ArgResolveError(schema.type === "positional" ? `Positional argument '${option}' is required` : `Optional argument ${createOptionDisplayName(option, schema)} is required`, option, "required", schema, {
|
|
933
|
+
code: schema.type === "positional" ? ArgsValidationErrorKeys.requiredPositional : ArgsValidationErrorKeys.requiredOption,
|
|
934
|
+
values: schema.type === "positional" ? {
|
|
935
|
+
displayName: createArgumentDisplayName(option, schema),
|
|
936
|
+
name: rawArg
|
|
937
|
+
} : {
|
|
938
|
+
displayName: createOptionDisplayName(option, schema),
|
|
939
|
+
name: rawArg
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
function resolveSinglePositionalValue(values, errors, rawArg, option, schema, positional) {
|
|
944
|
+
if (typeof schema.parse === "function") {
|
|
945
|
+
const [parsedValue, error] = parseSchemaValue(positional.value, rawArg, option, schema);
|
|
946
|
+
if (error)
|
|
947
|
+
errors.push(error);
|
|
948
|
+
else
|
|
949
|
+
values[rawArg] = parsedValue;
|
|
950
|
+
} else
|
|
951
|
+
values[rawArg] = positional.value;
|
|
952
|
+
}
|
|
953
|
+
function hasDefault(schema) {
|
|
954
|
+
return schema.default != null;
|
|
955
|
+
}
|
|
956
|
+
function getDefaultValue(schema) {
|
|
957
|
+
const value = schema.default;
|
|
958
|
+
return schema.multiple && !Array.isArray(value) ? [value] : value;
|
|
959
|
+
}
|
|
960
|
+
function hasDefaultOutsideChoices(schema) {
|
|
961
|
+
const defaultType = typeof schema.default;
|
|
962
|
+
if (schema.type !== "enum" || !Array.isArray(schema.choices) || defaultType !== "string" && defaultType !== "number" && defaultType !== "boolean" || schema.choices.includes(schema.default))
|
|
963
|
+
return false;
|
|
964
|
+
const { parse } = schema;
|
|
965
|
+
if (typeof parse !== "function")
|
|
966
|
+
return true;
|
|
967
|
+
try {
|
|
968
|
+
return hasPureParseBrand(parse);
|
|
969
|
+
} catch {
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function shouldRequireMissingSinglePositional(schema) {
|
|
974
|
+
if (schema.required === true)
|
|
975
|
+
return true;
|
|
976
|
+
if (schema.required === false)
|
|
977
|
+
return false;
|
|
978
|
+
return !hasDefault(schema);
|
|
979
|
+
}
|
|
980
|
+
function getRequiredPositionalInputCount(schema) {
|
|
981
|
+
if (schema.type !== "positional")
|
|
982
|
+
return 0;
|
|
983
|
+
if (schema.multiple)
|
|
984
|
+
return schema.required === true ? 1 : 0;
|
|
985
|
+
return shouldRequireMissingSinglePositional(schema) ? 1 : 0;
|
|
986
|
+
}
|
|
987
|
+
function createRequiredPositionalsAfter(argEntries) {
|
|
988
|
+
const requiredPositionalsAfter = Object.create(null);
|
|
989
|
+
let minimumRequiredPositionals = 0;
|
|
990
|
+
for (let i = argEntries.length - 1;i >= 0; i--) {
|
|
991
|
+
const [rawArg, schema] = argEntries[i];
|
|
992
|
+
if (schema.type !== "positional")
|
|
993
|
+
continue;
|
|
994
|
+
requiredPositionalsAfter[rawArg] = minimumRequiredPositionals;
|
|
995
|
+
minimumRequiredPositionals += getRequiredPositionalInputCount(schema);
|
|
996
|
+
}
|
|
997
|
+
return requiredPositionalsAfter;
|
|
998
|
+
}
|
|
999
|
+
var ArgResolveError = class extends ArgsValidationError {
|
|
1000
|
+
name;
|
|
1001
|
+
schema;
|
|
1002
|
+
type;
|
|
1003
|
+
constructor(message, name, type, schema, options = {}) {
|
|
1004
|
+
super(message, options);
|
|
1005
|
+
this.name = name;
|
|
1006
|
+
this.type = type;
|
|
1007
|
+
this.schema = schema;
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
function validateRequire(token, rawArg, option, schema) {
|
|
1011
|
+
if (schema.required && schema.type !== "boolean" && !token.value)
|
|
1012
|
+
return createRequireError(rawArg, option, schema);
|
|
1013
|
+
}
|
|
1014
|
+
function isNumeric(str) {
|
|
1015
|
+
return str.trim() !== "" && !isNaN(str);
|
|
1016
|
+
}
|
|
1017
|
+
function createTypeError(rawArg, option, schema, actual, expected = schema.type) {
|
|
1018
|
+
return new ArgResolveError(`Optional argument ${createOptionDisplayName(option, schema)} should be '${expected}'`, option, "type", schema, {
|
|
1019
|
+
code: ArgsValidationErrorKeys.invalidType,
|
|
1020
|
+
values: {
|
|
1021
|
+
displayName: createOptionDisplayName(option, schema),
|
|
1022
|
+
name: rawArg,
|
|
1023
|
+
expected,
|
|
1024
|
+
...actual != null ? { actual } : {}
|
|
1025
|
+
}
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
function createMissingValueError(rawArg, option, schema, next) {
|
|
1029
|
+
const displayName = createOptionDisplayName(option, schema);
|
|
1030
|
+
const suggestion = next === undefined || !acceptsSuggestedValue(schema, next) ? undefined : `--${option}=${next}`;
|
|
1031
|
+
const hint = suggestion === undefined ? "" : ` (to pass '${next}' as its value, write '${suggestion}')`;
|
|
1032
|
+
const choices = schema.choices ?? [];
|
|
1033
|
+
return new ArgResolveError(`Optional argument ${displayName} requires a value${hint}`, option, "type", schema, {
|
|
1034
|
+
code: ArgsValidationErrorKeys.missingValue,
|
|
1035
|
+
values: {
|
|
1036
|
+
displayName,
|
|
1037
|
+
name: rawArg,
|
|
1038
|
+
expected: schema.type === "custom" ? schema.metavar ?? schema.type : schema.type,
|
|
1039
|
+
...schema.type === "enum" ? {
|
|
1040
|
+
choices: formatChoices(choices),
|
|
1041
|
+
choiceValues: [...choices]
|
|
1042
|
+
} : {},
|
|
1043
|
+
...suggestion === undefined ? {} : {
|
|
1044
|
+
next,
|
|
1045
|
+
suggestion
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
function findOptionLikeNextArgument(tokens, optionTokens, position, argEntries, toKebab) {
|
|
1051
|
+
const token = optionTokens[position];
|
|
1052
|
+
for (let i = position + 1;i < optionTokens.length; i++)
|
|
1053
|
+
if (optionTokens[i].index === token.index)
|
|
1054
|
+
return;
|
|
1055
|
+
const nextArg = tokens.filter((t) => t.index === token.index + 1);
|
|
1056
|
+
if (nextArg.length === 0 || nextArg.some((t) => t.kind !== "option" || t.rawName == null))
|
|
1057
|
+
return;
|
|
1058
|
+
let text;
|
|
1059
|
+
let known;
|
|
1060
|
+
if (nextArg.length === 1 && nextArg[0].rawName.startsWith("--")) {
|
|
1061
|
+
const [option] = nextArg;
|
|
1062
|
+
text = option.inlineValue ? `${option.rawName}=${option.value}` : option.rawName;
|
|
1063
|
+
known = createKnownOptionNames(argEntries, toKebab).long.has(option.name);
|
|
1064
|
+
} else if (nextArg.every((t) => isShortOption(t.rawName) && t.value === undefined)) {
|
|
1065
|
+
text = `-${nextArg.map((t) => t.name).join("")}`;
|
|
1066
|
+
const { short } = createKnownOptionNames(argEntries, toKebab);
|
|
1067
|
+
known = optionTokens.every((t) => t.index !== token.index + 1 || short.has(t.name));
|
|
1068
|
+
} else
|
|
1069
|
+
return;
|
|
1070
|
+
return known ? undefined : text;
|
|
1071
|
+
}
|
|
1072
|
+
function createKnownOptionNames(argEntries, toKebab) {
|
|
1073
|
+
const long = /* @__PURE__ */ new Set;
|
|
1074
|
+
const short = /* @__PURE__ */ new Set;
|
|
1075
|
+
for (const [rawArg, schema] of argEntries) {
|
|
1076
|
+
if (schema.type === "positional")
|
|
1077
|
+
continue;
|
|
1078
|
+
const name = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
|
|
1079
|
+
long.add(name);
|
|
1080
|
+
if (schema.type === "boolean" && schema.negatable === true)
|
|
1081
|
+
long.add(`no-${name}`);
|
|
1082
|
+
if (schema.short)
|
|
1083
|
+
short.add(schema.short);
|
|
1084
|
+
}
|
|
1085
|
+
return {
|
|
1086
|
+
long,
|
|
1087
|
+
short
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function acceptsSuggestedValue(schema, value) {
|
|
1091
|
+
if (schema.type === "number" && !isNumeric(value))
|
|
1092
|
+
return false;
|
|
1093
|
+
if (schema.type === "enum" && schema.choices && !schema.choices.includes(value))
|
|
1094
|
+
return false;
|
|
1095
|
+
const { parse } = schema;
|
|
1096
|
+
if (typeof parse !== "function")
|
|
1097
|
+
return true;
|
|
1098
|
+
try {
|
|
1099
|
+
if (!hasPureParseBrand(parse))
|
|
1100
|
+
return schema.type === "number" || schema.type === "enum" && schema.choices !== undefined;
|
|
1101
|
+
parse(value);
|
|
1102
|
+
return true;
|
|
1103
|
+
} catch {
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
function hasPureParseBrand(parse) {
|
|
1108
|
+
return Object.hasOwn(parse, PURE_PARSE) && parse[PURE_PARSE] === true;
|
|
1109
|
+
}
|
|
1110
|
+
function createUnexpectedValueError(rawArg, option, schema, token) {
|
|
1111
|
+
const rawName = token.rawName;
|
|
1112
|
+
const displayName = `'${rawName}'`;
|
|
1113
|
+
return new ArgResolveError(`Optional argument ${displayName} does not take a value`, option, "type", schema, {
|
|
1114
|
+
code: ArgsValidationErrorKeys.unexpectedValue,
|
|
1115
|
+
values: {
|
|
1116
|
+
displayName,
|
|
1117
|
+
name: rawArg,
|
|
1118
|
+
rawName,
|
|
1119
|
+
...token.value != null ? { actual: token.value } : {}
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
function createChoiceError(rawArg, option, schema, actual) {
|
|
1124
|
+
const choices = schema.choices ?? [];
|
|
1125
|
+
return new ArgResolveError(`Optional argument ${createOptionDisplayName(option, schema)} should be chosen from '${schema.type}' [${formatChoices(choices)}] values`, option, "type", schema, {
|
|
1126
|
+
code: ArgsValidationErrorKeys.invalidChoice,
|
|
1127
|
+
values: {
|
|
1128
|
+
displayName: createOptionDisplayName(option, schema),
|
|
1129
|
+
name: rawArg,
|
|
1130
|
+
expected: schema.type,
|
|
1131
|
+
choices: formatChoices(choices),
|
|
1132
|
+
choiceValues: [...choices],
|
|
1133
|
+
...actual != null ? { actual } : {}
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
function createDefaultChoiceError(rawArg, option, schema) {
|
|
1138
|
+
const choices = schema.choices ?? [];
|
|
1139
|
+
const displayName = createOptionDisplayName(option, schema);
|
|
1140
|
+
return new ArgResolveError(`Optional argument ${displayName} has the default ${typeof schema.default === "string" ? JSON.stringify(schema.default) : String(schema.default)}, which is not one of '${schema.type}' [${formatChoices(choices)}] values`, option, "type", schema, {
|
|
1141
|
+
code: ArgsValidationErrorKeys.invalidDefault,
|
|
1142
|
+
values: {
|
|
1143
|
+
displayName,
|
|
1144
|
+
name: rawArg,
|
|
1145
|
+
expected: schema.type,
|
|
1146
|
+
choices: formatChoices(choices),
|
|
1147
|
+
choiceValues: [...choices],
|
|
1148
|
+
actual: schema.default
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
function createCustomParseError(error, rawArg, option, schema, value) {
|
|
1153
|
+
const reused = reuseValidationError(error, rawArg, option, schema, value);
|
|
1154
|
+
if (reused)
|
|
1155
|
+
return reused;
|
|
1156
|
+
const reason = getErrorReason(error);
|
|
1157
|
+
return new ArgsValidationError(reason, {
|
|
1158
|
+
code: ArgsValidationErrorKeys.customParse,
|
|
1159
|
+
values: {
|
|
1160
|
+
displayName: createArgumentDisplayName(option, schema),
|
|
1161
|
+
name: rawArg,
|
|
1162
|
+
reason
|
|
1163
|
+
},
|
|
1164
|
+
cause: error
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
function reuseValidationError(error, rawArg, option, schema, value) {
|
|
1168
|
+
try {
|
|
1169
|
+
if (isArgsValidationError(error)) {
|
|
1170
|
+
augmentValidationError(error, rawArg, option, schema, value);
|
|
1171
|
+
return error;
|
|
1172
|
+
}
|
|
1173
|
+
} catch {}
|
|
1174
|
+
}
|
|
1175
|
+
function augmentValidationError(error, rawArg, option, schema, value) {
|
|
1176
|
+
const values = error.values;
|
|
1177
|
+
values.name ??= rawArg;
|
|
1178
|
+
values.displayName ??= createArgumentDisplayName(option, schema);
|
|
1179
|
+
if ((error.code === ArgsValidationErrorKeys.invalidType || error.code === ArgsValidationErrorKeys.invalidChoice) && values.actual == null)
|
|
1180
|
+
values.actual = value;
|
|
1181
|
+
}
|
|
1182
|
+
function getErrorReason(error) {
|
|
1183
|
+
return error instanceof Error ? error.message : String(error);
|
|
1184
|
+
}
|
|
1185
|
+
function createArgumentDisplayName(option, schema) {
|
|
1186
|
+
return schema.type === "positional" ? `'${option}'` : createOptionDisplayName(option, schema);
|
|
1187
|
+
}
|
|
1188
|
+
function createOptionDisplayName(option, schema) {
|
|
1189
|
+
return `'--${option}'${schema.short ? ` or '-${schema.short}'` : ""}`;
|
|
1190
|
+
}
|
|
1191
|
+
function createConflictError(rawArg, option, schema, actualName, conflictingArg, conflictingActualName) {
|
|
1192
|
+
const displayName = `'${actualName}'`;
|
|
1193
|
+
const conflictDisplayName = `'${conflictingActualName}'`;
|
|
1194
|
+
return new ArgResolveError(`${schema.type === "positional" ? "Positional" : "Optional"} argument ${displayName} conflicts with ${conflictDisplayName}`, option, "conflict", schema, {
|
|
1195
|
+
code: ArgsValidationErrorKeys.conflict,
|
|
1196
|
+
values: {
|
|
1197
|
+
displayName,
|
|
1198
|
+
name: rawArg,
|
|
1199
|
+
conflictDisplayName,
|
|
1200
|
+
conflictName: conflictingArg
|
|
1201
|
+
}
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
function checkConflicts(args, explicit, toKebab, actualInputNames) {
|
|
1205
|
+
for (const rawArg in args) {
|
|
1206
|
+
const schema = args[rawArg];
|
|
1207
|
+
if (!explicit[rawArg])
|
|
1208
|
+
continue;
|
|
1209
|
+
if (!schema.conflicts)
|
|
1210
|
+
continue;
|
|
1211
|
+
const conflicts = Array.isArray(schema.conflicts) ? schema.conflicts : [schema.conflicts];
|
|
1212
|
+
for (let i = 0;i < conflicts.length; i++) {
|
|
1213
|
+
const conflictingArg = conflicts[i];
|
|
1214
|
+
if (!explicit[conflictingArg])
|
|
1215
|
+
continue;
|
|
1216
|
+
const arg = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
|
|
1217
|
+
const conflictingArgKebab = toKebab || args[conflictingArg]?.toKebab ? kebabnize(conflictingArg) : conflictingArg;
|
|
1218
|
+
return [createConflictError(rawArg, arg, schema, schema.type === "positional" ? arg : actualInputNames.get(rawArg) || `--${arg}`, conflictingArg, args[conflictingArg]?.type === "positional" ? conflictingArgKebab : actualInputNames.get(conflictingArg) || `--${conflictingArgKebab}`)];
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
return [];
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/error-pbHb3QAL.js
|
|
1225
|
+
var CommandNotFoundErrorKeys = { notFound: "err:cmd:not-found" };
|
|
1226
|
+
var CommandResolutionErrorKeys = {
|
|
1227
|
+
ambiguous: "err:cmd:ambiguous",
|
|
1228
|
+
inconsistentOptions: "err:cmd:inconsistent-options",
|
|
1229
|
+
lazySchemaMismatch: "err:cmd:lazy-schema-mismatch"
|
|
1230
|
+
};
|
|
1231
|
+
var COMMAND_NOT_FOUND_ERROR_BRAND = Symbol.for("gunshi.CommandNotFoundError");
|
|
1232
|
+
var COMMAND_RESOLUTION_ERROR_BRAND = Symbol.for("gunshi.CommandResolutionError");
|
|
1233
|
+
var CommandNotFoundError = class extends Error {
|
|
1234
|
+
code;
|
|
1235
|
+
values;
|
|
1236
|
+
commandName;
|
|
1237
|
+
candidates;
|
|
1238
|
+
commandPath;
|
|
1239
|
+
constructor(message, options) {
|
|
1240
|
+
super(message, { cause: options.cause });
|
|
1241
|
+
this.name = "CommandNotFoundError";
|
|
1242
|
+
this.code = options.code;
|
|
1243
|
+
this.values = options.values || {};
|
|
1244
|
+
this.commandName = options.commandName;
|
|
1245
|
+
this.candidates = options.candidates || [];
|
|
1246
|
+
this.commandPath = options.commandPath || [];
|
|
1247
|
+
Object.defineProperty(this, COMMAND_NOT_FOUND_ERROR_BRAND, {
|
|
1248
|
+
value: true,
|
|
1249
|
+
enumerable: false,
|
|
1250
|
+
writable: false,
|
|
1251
|
+
configurable: false
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
var CommandResolutionError = class extends Error {
|
|
1256
|
+
code;
|
|
1257
|
+
values;
|
|
1258
|
+
commandPath;
|
|
1259
|
+
candidatePaths;
|
|
1260
|
+
constructor(message, options = {}) {
|
|
1261
|
+
super(message, { cause: options.cause });
|
|
1262
|
+
this.name = "CommandResolutionError";
|
|
1263
|
+
this.code = options.code;
|
|
1264
|
+
this.values = options.values || {};
|
|
1265
|
+
this.commandPath = options.commandPath || [];
|
|
1266
|
+
this.candidatePaths = options.candidatePaths || [];
|
|
1267
|
+
Object.defineProperty(this, COMMAND_RESOLUTION_ERROR_BRAND, {
|
|
1268
|
+
value: true,
|
|
1269
|
+
enumerable: false,
|
|
1270
|
+
writable: false,
|
|
1271
|
+
configurable: false
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
function isCommandNotFoundError(error) {
|
|
1276
|
+
if (error instanceof CommandNotFoundError)
|
|
1277
|
+
return true;
|
|
1278
|
+
if (!isRecord2(error) || !hasCommandNotFoundErrorShape(error))
|
|
1279
|
+
return false;
|
|
1280
|
+
if (Object.hasOwn(error, COMMAND_NOT_FOUND_ERROR_BRAND) && error[COMMAND_NOT_FOUND_ERROR_BRAND] === true)
|
|
1281
|
+
return true;
|
|
1282
|
+
return error instanceof Error && error.name === "CommandNotFoundError";
|
|
1283
|
+
}
|
|
1284
|
+
function isCommandResolutionError(error) {
|
|
1285
|
+
if (error instanceof CommandResolutionError)
|
|
1286
|
+
return true;
|
|
1287
|
+
if (!isRecord2(error) || !hasCommandResolutionErrorShape(error))
|
|
1288
|
+
return false;
|
|
1289
|
+
return Object.hasOwn(error, COMMAND_RESOLUTION_ERROR_BRAND) && error[COMMAND_RESOLUTION_ERROR_BRAND] === true;
|
|
1290
|
+
}
|
|
1291
|
+
function isArgsValidationError2(error) {
|
|
1292
|
+
if (error instanceof ArgsValidationError)
|
|
1293
|
+
return true;
|
|
1294
|
+
if (!isRecord2(error) || !hasArgsValidationErrorShape(error))
|
|
1295
|
+
return false;
|
|
1296
|
+
if (isArgsValidationError(error))
|
|
1297
|
+
return true;
|
|
1298
|
+
return error instanceof Error && error.name === "ArgsValidationError";
|
|
1299
|
+
}
|
|
1300
|
+
function isRecord2(value) {
|
|
1301
|
+
return typeof value === "object" && value !== null;
|
|
1302
|
+
}
|
|
1303
|
+
function isStringArray(value) {
|
|
1304
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1305
|
+
}
|
|
1306
|
+
function hasOptionalStringCode(error) {
|
|
1307
|
+
return "code" in error && (error.code === undefined || typeof error.code === "string");
|
|
1308
|
+
}
|
|
1309
|
+
function hasCommandNotFoundErrorShape(error) {
|
|
1310
|
+
return hasOptionalStringCode(error) && isRecord2(error.values) && typeof error.commandName === "string" && isStringArray(error.candidates) && isStringArray(error.commandPath);
|
|
1311
|
+
}
|
|
1312
|
+
function hasCommandResolutionErrorShape(error) {
|
|
1313
|
+
return hasOptionalStringCode(error) && isRecord2(error.values) && isStringArray(error.commandPath) && Array.isArray(error.candidatePaths) && error.candidatePaths.every((path) => isStringArray(path));
|
|
1314
|
+
}
|
|
1315
|
+
function hasArgsValidationErrorShape(error) {
|
|
1316
|
+
return hasOptionalStringCode(error) && isRecord2(error.values);
|
|
1317
|
+
}
|
|
1318
|
+
function hasPriorityValidationError(error) {
|
|
1319
|
+
return error?.errors.some((error) => isCommandNotFoundError(error) || isCommandResolutionError(error) || isArgsValidationError2(error) && error.code === ArgsValidationErrorKeys.unknownOption) ?? false;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/core-EDPnjwxe.js
|
|
1323
|
+
var EMPTY_RENDERER = () => Promise.resolve("");
|
|
1324
|
+
function createDecorators() {
|
|
1325
|
+
const headerDecorators = [];
|
|
1326
|
+
const usageDecorators = [];
|
|
1327
|
+
const validationDecorators = [];
|
|
1328
|
+
const commandDecorators = [];
|
|
1329
|
+
function buildRenderer(decorators, defaultRenderer) {
|
|
1330
|
+
if (decorators.length === 0)
|
|
1331
|
+
return defaultRenderer;
|
|
1332
|
+
let renderer = defaultRenderer;
|
|
1333
|
+
for (const decorator of decorators) {
|
|
1334
|
+
const previousRenderer = renderer;
|
|
1335
|
+
renderer = (ctx) => decorator(previousRenderer, ctx);
|
|
1336
|
+
}
|
|
1337
|
+
return renderer;
|
|
1338
|
+
}
|
|
1339
|
+
return Object.freeze({
|
|
1340
|
+
addHeaderDecorator(decorator) {
|
|
1341
|
+
headerDecorators.push(decorator);
|
|
1342
|
+
},
|
|
1343
|
+
addUsageDecorator(decorator) {
|
|
1344
|
+
usageDecorators.push(decorator);
|
|
1345
|
+
},
|
|
1346
|
+
addValidationErrorsDecorator(decorator) {
|
|
1347
|
+
validationDecorators.push(decorator);
|
|
1348
|
+
},
|
|
1349
|
+
addCommandDecorator(decorator) {
|
|
1350
|
+
commandDecorators.push(decorator);
|
|
1351
|
+
},
|
|
1352
|
+
get commandDecorators() {
|
|
1353
|
+
return [...commandDecorators];
|
|
1354
|
+
},
|
|
1355
|
+
getHeaderRenderer() {
|
|
1356
|
+
return buildRenderer(headerDecorators, EMPTY_RENDERER);
|
|
1357
|
+
},
|
|
1358
|
+
getUsageRenderer() {
|
|
1359
|
+
return buildRenderer(usageDecorators, EMPTY_RENDERER);
|
|
1360
|
+
},
|
|
1361
|
+
getValidationErrorsRenderer() {
|
|
1362
|
+
if (validationDecorators.length === 0)
|
|
1363
|
+
return EMPTY_RENDERER;
|
|
1364
|
+
let renderer = EMPTY_RENDERER;
|
|
1365
|
+
for (const decorator of validationDecorators) {
|
|
1366
|
+
const previousRenderer = renderer;
|
|
1367
|
+
renderer = (ctx, error) => decorator(previousRenderer, ctx, error);
|
|
1368
|
+
}
|
|
1369
|
+
return renderer;
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1373
|
+
function createPluginContext(decorators, initialSubCommands) {
|
|
1374
|
+
const globalOptions = /* @__PURE__ */ new Map;
|
|
1375
|
+
const subCommands = new Map(initialSubCommands || []);
|
|
1376
|
+
return Object.freeze({
|
|
1377
|
+
get globalOptions() {
|
|
1378
|
+
return new Map(globalOptions);
|
|
1379
|
+
},
|
|
1380
|
+
addGlobalOption(name, schema) {
|
|
1381
|
+
if (!name)
|
|
1382
|
+
throw new Error("Option name must be a non-empty string");
|
|
1383
|
+
if (globalOptions.has(name))
|
|
1384
|
+
throw new Error(`Global option '${name}' is already registered`);
|
|
1385
|
+
const taken = schema.short && findGlobalOptionByShort(globalOptions, schema.short);
|
|
1386
|
+
if (taken) {
|
|
1387
|
+
console.warn(`Short name '-${schema.short}' of the global option '${name}' is already used by '${taken}'. Global option '${name}' is registered without it.`);
|
|
1388
|
+
globalOptions.set(name, {
|
|
1389
|
+
...schema,
|
|
1390
|
+
short: undefined
|
|
1391
|
+
});
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
globalOptions.set(name, schema);
|
|
1395
|
+
},
|
|
1396
|
+
get subCommands() {
|
|
1397
|
+
return new Map(subCommands);
|
|
1398
|
+
},
|
|
1399
|
+
addCommand(name, command) {
|
|
1400
|
+
if (!name)
|
|
1401
|
+
throw new Error("Command name must be a non-empty string");
|
|
1402
|
+
if (subCommands.has(name))
|
|
1403
|
+
throw new Error(`Command '${name}' is already registered`);
|
|
1404
|
+
subCommands.set(name, command);
|
|
1405
|
+
},
|
|
1406
|
+
hasCommand(name) {
|
|
1407
|
+
return subCommands.has(name);
|
|
1408
|
+
},
|
|
1409
|
+
decorateHeaderRenderer(decorator) {
|
|
1410
|
+
decorators.addHeaderDecorator(decorator);
|
|
1411
|
+
},
|
|
1412
|
+
decorateUsageRenderer(decorator) {
|
|
1413
|
+
decorators.addUsageDecorator(decorator);
|
|
1414
|
+
},
|
|
1415
|
+
decorateValidationErrorsRenderer(decorator) {
|
|
1416
|
+
decorators.addValidationErrorsDecorator(decorator);
|
|
1417
|
+
},
|
|
1418
|
+
decorateCommand(decorator) {
|
|
1419
|
+
decorators.addCommandDecorator(decorator);
|
|
1420
|
+
}
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
function findGlobalOptionByShort(globalOptions, short) {
|
|
1424
|
+
for (const [name, schema] of globalOptions)
|
|
1425
|
+
if (schema.short === short)
|
|
1426
|
+
return name;
|
|
1427
|
+
}
|
|
1428
|
+
function resolveDependencies(plugins) {
|
|
1429
|
+
const sorted = [];
|
|
1430
|
+
const visited = /* @__PURE__ */ new Set;
|
|
1431
|
+
const visiting = /* @__PURE__ */ new Set;
|
|
1432
|
+
const pluginMap = /* @__PURE__ */ new Map;
|
|
1433
|
+
for (const plugin of plugins) {
|
|
1434
|
+
if (typeof plugin.id !== "string" || plugin.id.length === 0)
|
|
1435
|
+
throw new Error("Plugin id must be a non-empty string");
|
|
1436
|
+
if (pluginMap.has(plugin.id))
|
|
1437
|
+
console.warn(`Duplicate plugin id detected: \`${plugin.id}\``);
|
|
1438
|
+
pluginMap.set(plugin.id, plugin);
|
|
1439
|
+
}
|
|
1440
|
+
function visit(plugin) {
|
|
1441
|
+
if (typeof plugin.id !== "string" || plugin.id.length === 0)
|
|
1442
|
+
throw new Error("Plugin id must be a non-empty string");
|
|
1443
|
+
if (visited.has(plugin.id))
|
|
1444
|
+
return;
|
|
1445
|
+
if (visiting.has(plugin.id))
|
|
1446
|
+
throw new Error(`Circular dependency detected: \`${[...visiting].join(` -> `) + " -> " + plugin.id}\``);
|
|
1447
|
+
visiting.add(plugin.id);
|
|
1448
|
+
const deps = plugin.dependencies || [];
|
|
1449
|
+
for (const dep of deps) {
|
|
1450
|
+
const depId = typeof dep === "string" ? dep : dep.id;
|
|
1451
|
+
const isOptional = typeof dep === "string" ? false : dep.optional || false;
|
|
1452
|
+
const depPlugin = pluginMap.get(depId);
|
|
1453
|
+
if (!depPlugin && !isOptional)
|
|
1454
|
+
throw new Error(`Missing required dependency: \`${depId}\` on \`${plugin.id}\``);
|
|
1455
|
+
if (depPlugin)
|
|
1456
|
+
visit(depPlugin);
|
|
1457
|
+
}
|
|
1458
|
+
visiting.delete(plugin.id);
|
|
1459
|
+
visited.add(plugin.id);
|
|
1460
|
+
sorted.push(plugin);
|
|
1461
|
+
}
|
|
1462
|
+
for (const plugin of plugins)
|
|
1463
|
+
visit(plugin);
|
|
1464
|
+
return sorted;
|
|
1465
|
+
}
|
|
1466
|
+
function resolveCommandArgs(globalOptions, args) {
|
|
1467
|
+
return Object.assign(create(), resolveGlobalOptions(globalOptions, args), args);
|
|
1468
|
+
}
|
|
1469
|
+
function resolveGlobalOptions(globalOptions, args) {
|
|
1470
|
+
if (!globalOptions)
|
|
1471
|
+
return;
|
|
1472
|
+
const shortNames = /* @__PURE__ */ new Set;
|
|
1473
|
+
for (const schema of Object.values(args || {}))
|
|
1474
|
+
if (schema.type !== "positional" && schema.short)
|
|
1475
|
+
shortNames.add(schema.short);
|
|
1476
|
+
const resolved = create();
|
|
1477
|
+
for (const [name, schema] of globalOptions)
|
|
1478
|
+
resolved[name] = schema.short && shortNames.has(schema.short) ? {
|
|
1479
|
+
...schema,
|
|
1480
|
+
short: undefined
|
|
1481
|
+
} : schema;
|
|
1482
|
+
return resolved;
|
|
1483
|
+
}
|
|
1484
|
+
async function cliCore(argv, entry, options, plugins, usageOnly = false) {
|
|
1485
|
+
const decorators = createDecorators();
|
|
1486
|
+
if (usageOnly)
|
|
1487
|
+
decorators.addCommandDecorator(usageOnlyDecorator());
|
|
1488
|
+
const entryCommand = createEntryCommand(entry);
|
|
1489
|
+
const initialSubCommands = createInitialSubCommands(options, entryCommand);
|
|
1490
|
+
const pluginContext = createPluginContext(decorators, initialSubCommands);
|
|
1491
|
+
const resolvedPlugins = await applyPlugins(pluginContext, [...plugins, ...options.plugins || []]);
|
|
1492
|
+
const cliOptions = normalizeCliOptions(options, decorators, pluginContext, entryCommand);
|
|
1493
|
+
if (initialSubCommands.size === 0 && cliOptions.subCommands.size > 0 && options.fallbackToEntry === undefined)
|
|
1494
|
+
cliOptions.fallbackToEntry = true;
|
|
1495
|
+
const tokens = parseArgs(argv);
|
|
1496
|
+
const selection = resolveCommandSelection(tokens, entry, cliOptions, pluginContext.globalOptions);
|
|
1497
|
+
const resolved = selection.context;
|
|
1498
|
+
const { commandName: name, command, callMode, commandPath, depth, levelSubCommands } = resolved;
|
|
1499
|
+
let targetCommand = command;
|
|
1500
|
+
let targetCommandName = name;
|
|
1501
|
+
let targetCallMode = callMode;
|
|
1502
|
+
let targetCommandPath = commandPath;
|
|
1503
|
+
let targetDepth = depth;
|
|
1504
|
+
let targetOmitted = resolved.omitted;
|
|
1505
|
+
let targetLevelSubCommands = levelSubCommands;
|
|
1506
|
+
const additionalValidationErrors = selection.error ? [selection.error] : [];
|
|
1507
|
+
if (!targetCommand) {
|
|
1508
|
+
if (!resolved.parentCommand || !resolved.unresolvedCommandName)
|
|
1509
|
+
throw new Error(`Command not found: ${name || ""}`);
|
|
1510
|
+
targetCommand = resolved.parentCommand;
|
|
1511
|
+
targetCommandName = resolved.parentCommandName;
|
|
1512
|
+
targetCommandPath = resolved.parentCommandPath || [];
|
|
1513
|
+
targetDepth = targetCommandPath.length;
|
|
1514
|
+
targetCallMode = targetDepth > 0 ? "subCommand" : "entry";
|
|
1515
|
+
targetOmitted = false;
|
|
1516
|
+
targetLevelSubCommands = resolved.parentSubCommands;
|
|
1517
|
+
additionalValidationErrors.push(createCommandNotFoundError(resolved));
|
|
1518
|
+
}
|
|
1519
|
+
const routingOptions = Object.assign(create(), cliOptions, { subCommands: new Map(cliOptions.subCommands) });
|
|
1520
|
+
if (targetLevelSubCommands)
|
|
1521
|
+
cliOptions.subCommands = targetLevelSubCommands;
|
|
1522
|
+
const resolvedCommand = isLazyCommand(targetCommand) ? await resolveLazyCommand(targetCommand, targetCommandName, true) : targetCommand;
|
|
1523
|
+
if (selection.error == null && isLazyCommand(targetCommand)) {
|
|
1524
|
+
const loaded = resolveCommandTreeFromPositionals(getRoutingPositionals(tokens, pluginContext.globalOptions, resolvedCommand), entry, routingOptions);
|
|
1525
|
+
if (!sameCommandPath(loaded.commandPath, targetCommandPath))
|
|
1526
|
+
additionalValidationErrors.push(createLazySchemaMismatchError(targetCommandPath, loaded.commandPath));
|
|
1527
|
+
}
|
|
1528
|
+
const commandArgs = getCommandArgs(resolvedCommand);
|
|
1529
|
+
const args = resolveCommandArgs(pluginContext.globalOptions, commandArgs);
|
|
1530
|
+
cliOptions.globalOptions = resolveEffectiveGlobalOptions(pluginContext.globalOptions, commandArgs, args);
|
|
1531
|
+
const skipPositional = resolveSkipPositional(tokens, pluginContext.globalOptions, resolvedCommand, targetDepth);
|
|
1532
|
+
const diagnostic = selection.diagnostic === true;
|
|
1533
|
+
const { explicit, values, positionals, rest, error } = diagnostic ? {
|
|
1534
|
+
explicit: create(),
|
|
1535
|
+
values: create(),
|
|
1536
|
+
positionals: [],
|
|
1537
|
+
rest: [],
|
|
1538
|
+
error: undefined
|
|
1539
|
+
} : resolveArgs(args, tokens, {
|
|
1540
|
+
shortGrouping: true,
|
|
1541
|
+
toKebab: resolvedCommand.toKebab,
|
|
1542
|
+
skipPositional
|
|
1543
|
+
});
|
|
1544
|
+
const validationError = mergeValidationErrors(error, [...additionalValidationErrors, ...cliOptions.strict && !diagnostic ? createUnknownOptionErrors(findUnknownOptions(args, tokens, { toKebab: resolvedCommand.toKebab })) : []]);
|
|
1545
|
+
return await executeCommand(resolvedCommand, await createCommandContext({
|
|
1546
|
+
args,
|
|
1547
|
+
explicit,
|
|
1548
|
+
values,
|
|
1549
|
+
positionals,
|
|
1550
|
+
rest,
|
|
1551
|
+
argv,
|
|
1552
|
+
tokens,
|
|
1553
|
+
omitted: targetOmitted,
|
|
1554
|
+
callMode: targetCallMode,
|
|
1555
|
+
commandPath: targetCommandPath,
|
|
1556
|
+
command: resolvedCommand,
|
|
1557
|
+
extensions: getPluginExtensions(resolvedPlugins),
|
|
1558
|
+
validationError,
|
|
1559
|
+
cliOptions
|
|
1560
|
+
}), decorators.commandDecorators);
|
|
1561
|
+
}
|
|
1562
|
+
function usageOnlyDecorator() {
|
|
1563
|
+
return () => async (ctx) => {
|
|
1564
|
+
if (hasPriorityValidationError(ctx.validationError))
|
|
1565
|
+
throw ctx.validationError;
|
|
1566
|
+
const buf = [];
|
|
1567
|
+
if (ctx.env.renderHeader != null) {
|
|
1568
|
+
const header = await ctx.env.renderHeader(ctx);
|
|
1569
|
+
if (header)
|
|
1570
|
+
buf.push(header);
|
|
1571
|
+
}
|
|
1572
|
+
if (ctx.env.renderUsage == null)
|
|
1573
|
+
return;
|
|
1574
|
+
const usage = await ctx.env.renderUsage(ctx);
|
|
1575
|
+
if (!usage)
|
|
1576
|
+
return;
|
|
1577
|
+
buf.push(usage);
|
|
1578
|
+
return buf.join(`
|
|
1579
|
+
`);
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
async function applyPlugins(pluginContext, plugins) {
|
|
1583
|
+
const sortedPlugins = resolveDependencies(plugins);
|
|
1584
|
+
for (const plugin of sortedPlugins)
|
|
1585
|
+
try {
|
|
1586
|
+
await plugin(pluginContext);
|
|
1587
|
+
} catch (error) {
|
|
1588
|
+
throw new Error(`Failed to install the plugin \`${plugin.id}\``, { cause: error });
|
|
1589
|
+
}
|
|
1590
|
+
return sortedPlugins;
|
|
1591
|
+
}
|
|
1592
|
+
function resolveEffectiveGlobalOptions(globalOptions, commandArgs, args) {
|
|
1593
|
+
const effective = /* @__PURE__ */ new Map;
|
|
1594
|
+
for (const name of globalOptions.keys())
|
|
1595
|
+
if (!Object.hasOwn(commandArgs, name))
|
|
1596
|
+
effective.set(name, args[name]);
|
|
1597
|
+
return effective;
|
|
1598
|
+
}
|
|
1599
|
+
function getCommandArgs(cmd) {
|
|
1600
|
+
if (isLazyCommand(cmd))
|
|
1601
|
+
return cmd.args || create();
|
|
1602
|
+
else if (typeof cmd === "object")
|
|
1603
|
+
return cmd.args || create();
|
|
1604
|
+
else
|
|
1605
|
+
return create();
|
|
1606
|
+
}
|
|
1607
|
+
var NEGATABLE_OPTION_PREFIX = "no-";
|
|
1608
|
+
function findUnknownOptions(args, tokens, options) {
|
|
1609
|
+
const knownLongOptions = /* @__PURE__ */ new Set;
|
|
1610
|
+
const knownShortOptions = /* @__PURE__ */ new Set;
|
|
1611
|
+
const knownNegatableOptions = /* @__PURE__ */ new Set;
|
|
1612
|
+
const knownLongOptionCandidates = /* @__PURE__ */ new Set;
|
|
1613
|
+
const longOptionCandidates = [];
|
|
1614
|
+
function addLongOptionCandidate(name) {
|
|
1615
|
+
const candidate = `--${name}`;
|
|
1616
|
+
if (knownLongOptionCandidates.has(candidate))
|
|
1617
|
+
return;
|
|
1618
|
+
knownLongOptionCandidates.add(candidate);
|
|
1619
|
+
longOptionCandidates.push(candidate);
|
|
1620
|
+
}
|
|
1621
|
+
for (const [name, schema] of Object.entries(args)) {
|
|
1622
|
+
if (schema.type === "positional")
|
|
1623
|
+
continue;
|
|
1624
|
+
const optionName = resolveOptionName(name, schema, options);
|
|
1625
|
+
knownLongOptions.add(optionName);
|
|
1626
|
+
if (!schema.hidden)
|
|
1627
|
+
addLongOptionCandidate(optionName);
|
|
1628
|
+
if (schema.short)
|
|
1629
|
+
knownShortOptions.add(schema.short);
|
|
1630
|
+
if (schema.type === "boolean" && schema.negatable) {
|
|
1631
|
+
const negatableOptionName = `${NEGATABLE_OPTION_PREFIX}${optionName}`;
|
|
1632
|
+
knownNegatableOptions.add(negatableOptionName);
|
|
1633
|
+
if (!schema.hidden)
|
|
1634
|
+
addLongOptionCandidate(negatableOptionName);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
const unknownOptions = [];
|
|
1638
|
+
for (const token of tokens) {
|
|
1639
|
+
if (token.kind === "option-terminator")
|
|
1640
|
+
break;
|
|
1641
|
+
if (token.kind !== "option" || !token.name || !token.rawName)
|
|
1642
|
+
continue;
|
|
1643
|
+
const isLongOption = token.rawName.startsWith("--");
|
|
1644
|
+
if (isLongOption) {
|
|
1645
|
+
if (knownLongOptions.has(token.name) || knownNegatableOptions.has(token.name))
|
|
1646
|
+
continue;
|
|
1647
|
+
} else if (knownShortOptions.has(token.name))
|
|
1648
|
+
continue;
|
|
1649
|
+
unknownOptions.push({
|
|
1650
|
+
rawName: token.rawName,
|
|
1651
|
+
name: token.name,
|
|
1652
|
+
candidates: isLongOption ? longOptionCandidates : []
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
return unknownOptions;
|
|
1656
|
+
}
|
|
1657
|
+
function resolveOptionName(name, schema, options) {
|
|
1658
|
+
return options.toKebab || schema.toKebab ? kebabnize(name) : name;
|
|
1659
|
+
}
|
|
1660
|
+
function createUnknownOptionErrors(unknownOptions) {
|
|
1661
|
+
return unknownOptions.map(({ rawName, name, candidates }) => {
|
|
1662
|
+
return new ArgsValidationError(`Unknown option: ${rawName}`, {
|
|
1663
|
+
code: ArgsValidationErrorKeys.unknownOption,
|
|
1664
|
+
values: {
|
|
1665
|
+
rawName,
|
|
1666
|
+
name,
|
|
1667
|
+
candidates
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
function mergeValidationErrors(error, additionalErrors) {
|
|
1673
|
+
if (additionalErrors.length === 0)
|
|
1674
|
+
return error;
|
|
1675
|
+
return new AggregateError(error ? [...error.errors, ...additionalErrors] : additionalErrors, error?.message || additionalErrors[0]?.message);
|
|
1676
|
+
}
|
|
1677
|
+
var isObject = (val) => val !== null && typeof val === "object";
|
|
1678
|
+
function createEntryCommand(entryCmd) {
|
|
1679
|
+
if (isLazyCommand(entryCmd))
|
|
1680
|
+
return Object.assign((...args) => entryCmd(...args), entryCmd, { entry: true });
|
|
1681
|
+
else if (typeof entryCmd === "object")
|
|
1682
|
+
return Object.assign(create(), entryCmd, { entry: true });
|
|
1683
|
+
else if (typeof entryCmd === "function")
|
|
1684
|
+
return {
|
|
1685
|
+
run: entryCmd,
|
|
1686
|
+
name: entryCmd.name || "(anonymous)",
|
|
1687
|
+
entry: true
|
|
1688
|
+
};
|
|
1689
|
+
}
|
|
1690
|
+
function createInitialSubCommands(options, entryCommand) {
|
|
1691
|
+
const hasSubCommands = options.subCommands ? options.subCommands instanceof Map ? options.subCommands.size > 0 : isObject(options.subCommands) && Object.keys(options.subCommands).length > 0 : false;
|
|
1692
|
+
const subCommands = new Map(options.subCommands instanceof Map ? options.subCommands : []);
|
|
1693
|
+
if (!(options.subCommands instanceof Map) && isObject(options.subCommands))
|
|
1694
|
+
for (const [name, cmd] of Object.entries(options.subCommands))
|
|
1695
|
+
subCommands.set(name, cmd);
|
|
1696
|
+
if (hasSubCommands && entryCommand)
|
|
1697
|
+
subCommands.set(resolveEntryName(entryCommand), entryCommand);
|
|
1698
|
+
return subCommands;
|
|
1699
|
+
}
|
|
1700
|
+
function normalizeCliOptions(options, decorators, pluginContext, entryCommand) {
|
|
1701
|
+
const subCommands = new Map(pluginContext.subCommands);
|
|
1702
|
+
const resolvedOptions = Object.assign(create(), CLI_OPTIONS_DEFAULT, options, {
|
|
1703
|
+
subCommands,
|
|
1704
|
+
entryCommand
|
|
1705
|
+
});
|
|
1706
|
+
if (resolvedOptions.renderHeader === undefined)
|
|
1707
|
+
resolvedOptions.renderHeader = decorators.getHeaderRenderer();
|
|
1708
|
+
if (resolvedOptions.renderUsage === undefined)
|
|
1709
|
+
resolvedOptions.renderUsage = decorators.getUsageRenderer();
|
|
1710
|
+
if (resolvedOptions.renderValidationErrors === undefined)
|
|
1711
|
+
resolvedOptions.renderValidationErrors = decorators.getValidationErrorsRenderer();
|
|
1712
|
+
return resolvedOptions;
|
|
1713
|
+
}
|
|
1714
|
+
function getPositionalTokens(tokens) {
|
|
1715
|
+
const positionals = [];
|
|
1716
|
+
for (const token of tokens) {
|
|
1717
|
+
if (token.kind === "option-terminator")
|
|
1718
|
+
break;
|
|
1719
|
+
if (token.kind === "positional" && token.value)
|
|
1720
|
+
positionals.push(token.value);
|
|
1721
|
+
}
|
|
1722
|
+
return positionals;
|
|
1723
|
+
}
|
|
1724
|
+
function resolveCommandTree(tokens, entry, options) {
|
|
1725
|
+
return resolveCommandTreeFromPositionals(getPositionalTokens(tokens), entry, options);
|
|
1726
|
+
}
|
|
1727
|
+
function resolveCommandTreeFromPositionals(positionals, entry, options) {
|
|
1728
|
+
function resolveAsEntry() {
|
|
1729
|
+
if (typeof entry === "function") {
|
|
1730
|
+
if (isLazyCommand(entry))
|
|
1731
|
+
return {
|
|
1732
|
+
commandName: resolveEntryName(entry),
|
|
1733
|
+
command: entry,
|
|
1734
|
+
callMode: "entry",
|
|
1735
|
+
commandPath: [],
|
|
1736
|
+
depth: 0,
|
|
1737
|
+
omitted: options.subCommands.size > 0 && !positionals[0],
|
|
1738
|
+
levelSubCommands: options.subCommands.size > 0 ? options.subCommands : undefined
|
|
1739
|
+
};
|
|
1740
|
+
else
|
|
1741
|
+
return {
|
|
1742
|
+
command: {
|
|
1743
|
+
run: entry,
|
|
1744
|
+
entry: true
|
|
1745
|
+
},
|
|
1746
|
+
callMode: "entry",
|
|
1747
|
+
commandPath: [],
|
|
1748
|
+
depth: 0,
|
|
1749
|
+
omitted: options.subCommands.size > 0 && !positionals[0],
|
|
1750
|
+
levelSubCommands: options.subCommands.size > 0 ? options.subCommands : undefined
|
|
1751
|
+
};
|
|
1752
|
+
} else if (typeof entry === "object")
|
|
1753
|
+
return {
|
|
1754
|
+
commandName: resolveEntryName(entry),
|
|
1755
|
+
command: entry,
|
|
1756
|
+
callMode: "entry",
|
|
1757
|
+
commandPath: [],
|
|
1758
|
+
depth: 0,
|
|
1759
|
+
omitted: options.subCommands.size > 0 && !positionals[0],
|
|
1760
|
+
levelSubCommands: options.subCommands.size > 0 ? options.subCommands : undefined
|
|
1761
|
+
};
|
|
1762
|
+
else
|
|
1763
|
+
return {
|
|
1764
|
+
callMode: "unexpected",
|
|
1765
|
+
commandPath: [],
|
|
1766
|
+
depth: 0,
|
|
1767
|
+
omitted: false,
|
|
1768
|
+
levelSubCommands: undefined
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
if (positionals.length === 0 || options.subCommands.size === 0)
|
|
1772
|
+
return resolveAsEntry();
|
|
1773
|
+
let currentSubCommands = options.subCommands;
|
|
1774
|
+
let resolvedCommand;
|
|
1775
|
+
let resolvedName;
|
|
1776
|
+
const commandPath = [];
|
|
1777
|
+
let depth = 0;
|
|
1778
|
+
for (let i = 0;i < positionals.length; i++) {
|
|
1779
|
+
const token = positionals[i];
|
|
1780
|
+
const cmd = currentSubCommands.get(token);
|
|
1781
|
+
if (cmd == null) {
|
|
1782
|
+
if (depth === 0) {
|
|
1783
|
+
if (options.fallbackToEntry)
|
|
1784
|
+
return resolveAsEntry();
|
|
1785
|
+
const parent = resolveAsEntry();
|
|
1786
|
+
return {
|
|
1787
|
+
commandName: token,
|
|
1788
|
+
callMode: "unexpected",
|
|
1789
|
+
commandPath: [],
|
|
1790
|
+
depth: 0,
|
|
1791
|
+
omitted: false,
|
|
1792
|
+
levelSubCommands: undefined,
|
|
1793
|
+
unresolvedCommandName: token,
|
|
1794
|
+
parentCommand: parent.command,
|
|
1795
|
+
parentCommandName: parent.commandName,
|
|
1796
|
+
parentCommandPath: [],
|
|
1797
|
+
parentSubCommands: options.subCommands
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
return {
|
|
1801
|
+
commandName: token,
|
|
1802
|
+
callMode: "unexpected",
|
|
1803
|
+
commandPath: [...commandPath],
|
|
1804
|
+
depth,
|
|
1805
|
+
omitted: false,
|
|
1806
|
+
levelSubCommands: undefined,
|
|
1807
|
+
unresolvedCommandName: token,
|
|
1808
|
+
parentCommand: resolvedCommand,
|
|
1809
|
+
parentCommandName: resolvedName,
|
|
1810
|
+
parentCommandPath: [...commandPath],
|
|
1811
|
+
parentSubCommands: currentSubCommands
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
let resolved = cmd;
|
|
1815
|
+
if (typeof cmd === "function" && cmd.commandName == null)
|
|
1816
|
+
resolved = Object.assign((...args) => cmd(...args), cmd, { commandName: token });
|
|
1817
|
+
else if (typeof cmd === "object" && cmd.name == null)
|
|
1818
|
+
resolved = Object.assign(create(), cmd, { name: token });
|
|
1819
|
+
resolvedCommand = resolved;
|
|
1820
|
+
resolvedName = token;
|
|
1821
|
+
commandPath.push(token);
|
|
1822
|
+
depth++;
|
|
1823
|
+
const nestedSubCommands = getCommandSubCommands(cmd);
|
|
1824
|
+
if (nestedSubCommands && nestedSubCommands.size > 0)
|
|
1825
|
+
currentSubCommands = nestedSubCommands;
|
|
1826
|
+
else
|
|
1827
|
+
break;
|
|
1828
|
+
}
|
|
1829
|
+
if (!resolvedCommand)
|
|
1830
|
+
return resolveAsEntry();
|
|
1831
|
+
const resolvedSubCommands = getCommandSubCommands(resolvedCommand);
|
|
1832
|
+
const omitted = resolvedSubCommands != null && resolvedSubCommands.size > 0;
|
|
1833
|
+
let levelSubCommands;
|
|
1834
|
+
if (omitted && resolvedSubCommands) {
|
|
1835
|
+
levelSubCommands = new Map(resolvedSubCommands);
|
|
1836
|
+
let entryCopy;
|
|
1837
|
+
if (typeof resolvedCommand === "function")
|
|
1838
|
+
entryCopy = Object.assign((...args) => resolvedCommand(...args), resolvedCommand, { entry: true });
|
|
1839
|
+
else
|
|
1840
|
+
entryCopy = Object.assign(create(), resolvedCommand, { entry: true });
|
|
1841
|
+
levelSubCommands.set(resolvedName || resolveEntryName(entryCopy), entryCopy);
|
|
1842
|
+
}
|
|
1843
|
+
return {
|
|
1844
|
+
commandName: resolvedName,
|
|
1845
|
+
command: resolvedCommand,
|
|
1846
|
+
callMode: depth > 0 ? "subCommand" : "entry",
|
|
1847
|
+
commandPath,
|
|
1848
|
+
depth,
|
|
1849
|
+
omitted,
|
|
1850
|
+
levelSubCommands
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
function resolveCommandSelection(tokens, entry, options, globalOptions) {
|
|
1854
|
+
const rawPositionals = getPositionalTokens(tokens);
|
|
1855
|
+
const legacy = resolveCommandTree(tokens, entry, options);
|
|
1856
|
+
if (!tokens.some((token) => token.kind === "option"))
|
|
1857
|
+
return { context: legacy };
|
|
1858
|
+
const candidates = collectRoutingCandidates(rawPositionals, options.subCommands);
|
|
1859
|
+
const evaluations = candidates.map((candidate) => evaluateRoutingCandidate(candidate));
|
|
1860
|
+
const validExplicit = evaluations.filter((evaluation) => evaluation.result.command != null && evaluation.result.unresolvedCommandName == null && sameCommandPath(evaluation.result.commandPath, evaluation.path) && evaluation.path.length > 0);
|
|
1861
|
+
if (legacy.command != null && legacy.unresolvedCommandName == null && legacy.commandPath.length > 0) {
|
|
1862
|
+
const legacyMatch = validExplicit.find((evaluation) => sameCommandPath(evaluation.path, legacy.commandPath));
|
|
1863
|
+
if (legacyMatch)
|
|
1864
|
+
return { context: legacyMatch.result };
|
|
1865
|
+
}
|
|
1866
|
+
if (validExplicit.length === 1)
|
|
1867
|
+
return { context: validExplicit[0].result };
|
|
1868
|
+
if (validExplicit.length > 1)
|
|
1869
|
+
return createCommandResolutionResult(options, entry, CommandResolutionErrorKeys.ambiguous, validExplicit.map((evaluation) => evaluation.path));
|
|
1870
|
+
const entryEvaluation = evaluateRoutingCandidate({
|
|
1871
|
+
path: [],
|
|
1872
|
+
command: getEntryRoutingCommand(entry, options)
|
|
1873
|
+
});
|
|
1874
|
+
if (entryEvaluation.result.command != null && entryEvaluation.result.unresolvedCommandName == null && entryEvaluation.result.commandPath.length === 0)
|
|
1875
|
+
return { context: entryEvaluation.result };
|
|
1876
|
+
const failures = evaluations.filter((evaluation) => {
|
|
1877
|
+
const { result } = evaluation;
|
|
1878
|
+
if (!result.unresolvedCommandName)
|
|
1879
|
+
return false;
|
|
1880
|
+
const parentPath = result.parentCommandPath || [];
|
|
1881
|
+
return isPathPrefix(evaluation.path, parentPath);
|
|
1882
|
+
}).map((evaluation) => evaluation.result);
|
|
1883
|
+
if (entryEvaluation.result.unresolvedCommandName)
|
|
1884
|
+
failures.push(entryEvaluation.result);
|
|
1885
|
+
const deepestFailures = selectDeepestFailures(failures);
|
|
1886
|
+
if (deepestFailures.length === 1)
|
|
1887
|
+
return { context: deepestFailures[0] };
|
|
1888
|
+
if (deepestFailures.length > 1) {
|
|
1889
|
+
const paths = deepestFailures.map((result) => result.parentCommandPath || []);
|
|
1890
|
+
return createCommandResolutionResult(options, entry, CommandResolutionErrorKeys.ambiguous, paths);
|
|
1891
|
+
}
|
|
1892
|
+
return createCommandResolutionResult(options, entry, CommandResolutionErrorKeys.inconsistentOptions, candidates.map((candidate) => candidate.path));
|
|
1893
|
+
function evaluateRoutingCandidate(candidate) {
|
|
1894
|
+
const positionals = getRoutingPositionals(tokens, globalOptions, candidate.command);
|
|
1895
|
+
return {
|
|
1896
|
+
...candidate,
|
|
1897
|
+
positionals,
|
|
1898
|
+
result: resolveCommandTreeFromPositionals(positionals, entry, options)
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
function collectRoutingCandidates(rawPositionals, root) {
|
|
1903
|
+
const candidates = [];
|
|
1904
|
+
const seen = /* @__PURE__ */ new Set;
|
|
1905
|
+
function collect(commands, offset, parentPath) {
|
|
1906
|
+
for (let index = offset;index < rawPositionals.length; index++) {
|
|
1907
|
+
const name = rawPositionals[index];
|
|
1908
|
+
const command = commands.get(name);
|
|
1909
|
+
if (command == null)
|
|
1910
|
+
continue;
|
|
1911
|
+
const path = [...parentPath, name];
|
|
1912
|
+
const key = JSON.stringify(path);
|
|
1913
|
+
const normalized = normalizeCommandReference(command, name);
|
|
1914
|
+
if (!seen.has(key)) {
|
|
1915
|
+
seen.add(key);
|
|
1916
|
+
candidates.push({
|
|
1917
|
+
path,
|
|
1918
|
+
command: normalized
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
const nested = getCommandSubCommands(normalized);
|
|
1922
|
+
if (nested && nested.size > 0)
|
|
1923
|
+
collect(nested, index + 1, path);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
collect(root, 0, []);
|
|
1927
|
+
return candidates;
|
|
1928
|
+
}
|
|
1929
|
+
function normalizeCommandReference(command, name) {
|
|
1930
|
+
if (typeof command === "function" && command.commandName == null)
|
|
1931
|
+
return Object.assign((...args) => command(...args), command, { commandName: name });
|
|
1932
|
+
if (typeof command === "object" && command.name == null)
|
|
1933
|
+
return Object.assign(create(), command, { name });
|
|
1934
|
+
return command;
|
|
1935
|
+
}
|
|
1936
|
+
function getEntryRoutingCommand(entry, options) {
|
|
1937
|
+
if (options.entryCommand)
|
|
1938
|
+
return options.entryCommand;
|
|
1939
|
+
if (typeof entry === "function" && !isLazyCommand(entry))
|
|
1940
|
+
return {
|
|
1941
|
+
run: entry,
|
|
1942
|
+
entry: true
|
|
1943
|
+
};
|
|
1944
|
+
return entry;
|
|
1945
|
+
}
|
|
1946
|
+
function getRoutingPositionals(tokens, globalOptions, command) {
|
|
1947
|
+
return readPositionals(tokens, globalOptions, command).filter((value) => value !== "");
|
|
1948
|
+
}
|
|
1949
|
+
function readPositionals(tokens, globalOptions, command) {
|
|
1950
|
+
const effective = resolveCommandArgs(globalOptions, getCommandArgs(command));
|
|
1951
|
+
const routingArgs = create();
|
|
1952
|
+
for (const [name, schema] of Object.entries(effective)) {
|
|
1953
|
+
if (schema.type === "positional")
|
|
1954
|
+
continue;
|
|
1955
|
+
routingArgs[name] = {
|
|
1956
|
+
type: schema.type === "boolean" ? "boolean" : "string",
|
|
1957
|
+
short: schema.short,
|
|
1958
|
+
negatable: schema.negatable,
|
|
1959
|
+
toKebab: schema.toKebab
|
|
1960
|
+
};
|
|
1961
|
+
}
|
|
1962
|
+
return resolveArgs(routingArgs, tokens, {
|
|
1963
|
+
shortGrouping: true,
|
|
1964
|
+
toKebab: command.toKebab
|
|
1965
|
+
}).positionals;
|
|
1966
|
+
}
|
|
1967
|
+
function resolveSkipPositional(tokens, globalOptions, command, depth) {
|
|
1968
|
+
if (depth === 0)
|
|
1969
|
+
return -1;
|
|
1970
|
+
if (!tokens.some((token) => token.kind === "positional" && token.value === ""))
|
|
1971
|
+
return depth - 1;
|
|
1972
|
+
let names = 0;
|
|
1973
|
+
for (const [index, value] of readPositionals(tokens, globalOptions, command).entries()) {
|
|
1974
|
+
if (value === "")
|
|
1975
|
+
continue;
|
|
1976
|
+
names++;
|
|
1977
|
+
if (names === depth)
|
|
1978
|
+
return index;
|
|
1979
|
+
}
|
|
1980
|
+
return depth - 1;
|
|
1981
|
+
}
|
|
1982
|
+
function sameCommandPath(left, right) {
|
|
1983
|
+
return left.length === right.length && left.every((name, index) => name === right[index]);
|
|
1984
|
+
}
|
|
1985
|
+
function isPathPrefix(prefix, path) {
|
|
1986
|
+
return prefix.length <= path.length && prefix.every((name, index) => name === path[index]);
|
|
1987
|
+
}
|
|
1988
|
+
function selectDeepestFailures(failures) {
|
|
1989
|
+
const unique = /* @__PURE__ */ new Map;
|
|
1990
|
+
for (const failure of failures) {
|
|
1991
|
+
const path = failure.parentCommandPath || [];
|
|
1992
|
+
const key = JSON.stringify(path);
|
|
1993
|
+
unique.set(key, failure);
|
|
1994
|
+
}
|
|
1995
|
+
const deepest = Math.max(-1, ...[...unique.values()].map((failure) => (failure.parentCommandPath || []).length));
|
|
1996
|
+
return [...unique.values()].filter((failure) => (failure.parentCommandPath || []).length === deepest);
|
|
1997
|
+
}
|
|
1998
|
+
function createCommandResolutionResult(options, entry, code, candidatePaths) {
|
|
1999
|
+
const normalizedPaths = candidatePaths.map((path) => [...path]);
|
|
2000
|
+
const commandPath = commonCommandPath(normalizedPaths);
|
|
2001
|
+
const parent = resolveCommandTreeFromPositionals(commandPath, entry, options);
|
|
2002
|
+
const command = createDiagnosticCommand(parent.command, commandPath.at(-1));
|
|
2003
|
+
const context = {
|
|
2004
|
+
...parent,
|
|
2005
|
+
command,
|
|
2006
|
+
commandPath,
|
|
2007
|
+
depth: commandPath.length,
|
|
2008
|
+
commandName: parent.commandName || commandPath.at(-1),
|
|
2009
|
+
callMode: commandPath.length > 0 ? "subCommand" : "entry",
|
|
2010
|
+
omitted: false
|
|
2011
|
+
};
|
|
2012
|
+
const formattedPaths = normalizedPaths.map((path) => path.join(" ")).join(", ");
|
|
2013
|
+
const formattedPath = commandPath.join(" ");
|
|
2014
|
+
const values = {
|
|
2015
|
+
commandPath: formattedPath,
|
|
2016
|
+
candidatePaths: formattedPaths,
|
|
2017
|
+
candidates: formattedPaths
|
|
2018
|
+
};
|
|
2019
|
+
const message = createCommandResolutionMessage(code, formattedPath, formattedPaths);
|
|
2020
|
+
return {
|
|
2021
|
+
context,
|
|
2022
|
+
error: new CommandResolutionError(message, {
|
|
2023
|
+
code,
|
|
2024
|
+
values,
|
|
2025
|
+
commandPath,
|
|
2026
|
+
candidatePaths: normalizedPaths
|
|
2027
|
+
}),
|
|
2028
|
+
diagnostic: true
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
function commonCommandPath(paths) {
|
|
2032
|
+
if (paths.length === 0)
|
|
2033
|
+
return [];
|
|
2034
|
+
const common = [...paths[0]];
|
|
2035
|
+
for (const path of paths.slice(1)) {
|
|
2036
|
+
let length = 0;
|
|
2037
|
+
while (length < common.length && length < path.length && common[length] === path[length])
|
|
2038
|
+
length++;
|
|
2039
|
+
common.length = length;
|
|
2040
|
+
}
|
|
2041
|
+
return common;
|
|
2042
|
+
}
|
|
2043
|
+
function createDiagnosticCommand(command, name) {
|
|
2044
|
+
if (typeof command === "function")
|
|
2045
|
+
return Object.assign(create(), command, {
|
|
2046
|
+
name: command.commandName || name,
|
|
2047
|
+
run: undefined
|
|
2048
|
+
});
|
|
2049
|
+
return Object.assign(create(), command || {}, {
|
|
2050
|
+
name: command?.name || name,
|
|
2051
|
+
run: undefined
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
function createCommandResolutionMessage(code, commandPath, candidatePaths) {
|
|
2055
|
+
switch (code) {
|
|
2056
|
+
case CommandResolutionErrorKeys.ambiguous:
|
|
2057
|
+
return `Ambiguous command resolution. Move options after the command name: ${candidatePaths}`;
|
|
2058
|
+
case CommandResolutionErrorKeys.lazySchemaMismatch:
|
|
2059
|
+
return `Lazy command schema mismatch at ${commandPath}. Declare leading options in the lazy definition or move options after the command name.`;
|
|
2060
|
+
default:
|
|
2061
|
+
return "The options do not identify a consistent command. Move options after the command name.";
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
function createLazySchemaMismatchError(commandPath, loadedPath) {
|
|
2065
|
+
const expected = [...commandPath];
|
|
2066
|
+
const actual = [...loadedPath];
|
|
2067
|
+
return new CommandResolutionError(`Lazy command schema mismatch at ${expected.join(" ")}. Declare leading options in the lazy definition or move options after the command name.`, {
|
|
2068
|
+
code: CommandResolutionErrorKeys.lazySchemaMismatch,
|
|
2069
|
+
values: {
|
|
2070
|
+
commandPath: expected.join(" "),
|
|
2071
|
+
loadedCommandPath: actual.join(" ")
|
|
2072
|
+
},
|
|
2073
|
+
commandPath: expected,
|
|
2074
|
+
candidatePaths: [expected, actual]
|
|
2075
|
+
});
|
|
2076
|
+
}
|
|
2077
|
+
function createCommandNotFoundError(resolved) {
|
|
2078
|
+
const commandName = resolved.unresolvedCommandName || resolved.commandName || "";
|
|
2079
|
+
return new CommandNotFoundError(`Command not found: ${commandName}`, {
|
|
2080
|
+
code: CommandNotFoundErrorKeys.notFound,
|
|
2081
|
+
values: { commandName },
|
|
2082
|
+
commandName,
|
|
2083
|
+
candidates: [...resolved.parentSubCommands?.keys() || []],
|
|
2084
|
+
commandPath: resolved.parentCommandPath || []
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
function resolveEntryName(entry) {
|
|
2088
|
+
return isLazyCommand(entry) ? entry.commandName || "(anonymous)" : entry.name || "(anonymous)";
|
|
2089
|
+
}
|
|
2090
|
+
function getPluginExtensions(plugins) {
|
|
2091
|
+
const extensions = create();
|
|
2092
|
+
for (const plugin of plugins)
|
|
2093
|
+
if (plugin.extension) {
|
|
2094
|
+
const key = plugin.id;
|
|
2095
|
+
if (extensions[key])
|
|
2096
|
+
console.warn(`Plugin "${key}" is already installed. Ignore it for command context extending.`);
|
|
2097
|
+
else
|
|
2098
|
+
extensions[key] = plugin.extension;
|
|
2099
|
+
}
|
|
2100
|
+
return extensions;
|
|
2101
|
+
}
|
|
2102
|
+
async function executeCommand(cmd, ctx, decorators) {
|
|
2103
|
+
const commandRunner = cmd.run || NOOP;
|
|
2104
|
+
const baseRunner = (ctx) => {
|
|
2105
|
+
if (hasPriorityValidationError(ctx.validationError))
|
|
2106
|
+
throw ctx.validationError;
|
|
2107
|
+
return commandRunner(ctx);
|
|
2108
|
+
};
|
|
2109
|
+
const decoratedRunner = decorators.reduceRight((runner, decorator) => decorator(runner), baseRunner);
|
|
2110
|
+
try {
|
|
2111
|
+
if (ctx.env.onBeforeCommand)
|
|
2112
|
+
await ctx.env.onBeforeCommand(ctx);
|
|
2113
|
+
const result = await decoratedRunner(ctx);
|
|
2114
|
+
if (ctx.env.onAfterCommand)
|
|
2115
|
+
await ctx.env.onAfterCommand(ctx, result);
|
|
2116
|
+
return typeof result === "string" ? result : undefined;
|
|
2117
|
+
} catch (error) {
|
|
2118
|
+
if (ctx.env.onErrorCommand)
|
|
2119
|
+
try {
|
|
2120
|
+
await ctx.env.onErrorCommand(ctx, error);
|
|
2121
|
+
} catch (hookError) {
|
|
2122
|
+
console.error("Error in onErrorCommand hook:", hookError);
|
|
2123
|
+
}
|
|
2124
|
+
throw error;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/bone.js
|
|
2129
|
+
async function cli(args, entry, options = {}) {
|
|
2130
|
+
return cliCore(args, entry, options, []);
|
|
2131
|
+
}
|
|
2132
|
+
|
|
2133
|
+
// ../../node_modules/.bun/gunshi@1.0.0-rc.1/node_modules/gunshi/lib/definition.js
|
|
2134
|
+
function define(definition) {
|
|
2135
|
+
return definition;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// src/parse-cli-args.ts
|
|
2139
|
+
async function parseCLIArgs(argv) {
|
|
2140
|
+
let parsed = null;
|
|
2141
|
+
const command = define({
|
|
2142
|
+
name: "zgeoff-check",
|
|
2143
|
+
args: {
|
|
2144
|
+
help: { type: "boolean", short: "h", default: false }
|
|
2145
|
+
},
|
|
2146
|
+
run: (ctx) => {
|
|
2147
|
+
if (ctx.validationError !== undefined) {
|
|
2148
|
+
throw ctx.validationError;
|
|
2149
|
+
}
|
|
2150
|
+
parsed = { help: ctx.values.help, files: [...ctx.positionals, ...ctx.rest] };
|
|
2151
|
+
}
|
|
2152
|
+
});
|
|
2153
|
+
try {
|
|
2154
|
+
await cli([...argv], command, { strict: true });
|
|
2155
|
+
} catch (error) {
|
|
2156
|
+
return formatParseError(error);
|
|
2157
|
+
}
|
|
2158
|
+
if (parsed === null) {
|
|
2159
|
+
return "the command line could not be parsed";
|
|
2160
|
+
}
|
|
2161
|
+
const args = parsed;
|
|
2162
|
+
if (args.help && args.files.length > 0) {
|
|
2163
|
+
return "--help takes no files";
|
|
2164
|
+
}
|
|
2165
|
+
return args;
|
|
2166
|
+
}
|
|
2167
|
+
function formatParseError(error) {
|
|
2168
|
+
if (error instanceof AggregateError) {
|
|
2169
|
+
return error.errors.map((reason) => formatParseError(reason)).join(`
|
|
2170
|
+
`);
|
|
2171
|
+
}
|
|
2172
|
+
return error instanceof Error ? error.message : String(error);
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
// src/run-checks.ts
|
|
2176
|
+
import { statSync } from "fs";
|
|
2177
|
+
import { isAbsolute, relative, resolve } from "path";
|
|
2178
|
+
async function runChecks(options) {
|
|
2179
|
+
const files = (options.files ?? runGitLsFiles(options)).map((file) => toRepoPath(options.cwd, file)).filter((file) => checkPathExists(options.cwd, file));
|
|
2180
|
+
const findings = [];
|
|
2181
|
+
for (const check of options.checks) {
|
|
2182
|
+
if (options.config.off.has(check.name)) {
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
const glob = new Bun.Glob(check.glob);
|
|
2186
|
+
const matched = files.filter((file) => glob.match(file));
|
|
2187
|
+
if (matched.length === 0) {
|
|
2188
|
+
continue;
|
|
2189
|
+
}
|
|
2190
|
+
const checkFindings = await check.check({ cwd: options.cwd, files: matched, env: options.env });
|
|
2191
|
+
findings.push(...checkFindings.toSorted((a, b) => a.path.localeCompare(b.path) || a.line - b.line).map((finding) => ({
|
|
2192
|
+
check: check.name,
|
|
2193
|
+
path: finding.path,
|
|
2194
|
+
line: finding.line,
|
|
2195
|
+
message: finding.message
|
|
2196
|
+
})));
|
|
2197
|
+
}
|
|
2198
|
+
return findings;
|
|
2199
|
+
}
|
|
2200
|
+
function runGitLsFiles(options) {
|
|
2201
|
+
const result = Bun.spawnSync(["git", "ls-files", "-z"], {
|
|
2202
|
+
cwd: options.cwd,
|
|
2203
|
+
env: { ...options.env },
|
|
2204
|
+
stdin: "ignore",
|
|
2205
|
+
stdout: "pipe",
|
|
2206
|
+
stderr: "pipe"
|
|
2207
|
+
});
|
|
2208
|
+
if (result.exitCode !== 0) {
|
|
2209
|
+
throw new Error(`git ls-files failed: ${result.stderr.toString().trim()}`);
|
|
2210
|
+
}
|
|
2211
|
+
return result.stdout.toString().split("\x00").filter((file) => file !== "");
|
|
2212
|
+
}
|
|
2213
|
+
function checkPathExists(cwd, file) {
|
|
2214
|
+
try {
|
|
2215
|
+
statSync(resolve(cwd, file));
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
|
|
2218
|
+
return false;
|
|
2219
|
+
}
|
|
2220
|
+
throw error;
|
|
2221
|
+
}
|
|
2222
|
+
return true;
|
|
2223
|
+
}
|
|
2224
|
+
function toRepoPath(cwd, file) {
|
|
2225
|
+
const path = relative(cwd, resolve(cwd, file));
|
|
2226
|
+
return isAbsolute(path) ? file : path;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
// src/run-git-show-toplevel.ts
|
|
2230
|
+
function runGitShowToplevel(cwd, env) {
|
|
2231
|
+
if (Bun.which("git", { PATH: env["PATH"] ?? "" }) === null) {
|
|
2232
|
+
return cwd;
|
|
2233
|
+
}
|
|
2234
|
+
const result = Bun.spawnSync(["git", "rev-parse", "--show-toplevel"], {
|
|
2235
|
+
cwd,
|
|
2236
|
+
env: { ...env },
|
|
2237
|
+
stdin: "ignore",
|
|
2238
|
+
stdout: "pipe",
|
|
2239
|
+
stderr: "pipe"
|
|
2240
|
+
});
|
|
2241
|
+
return result.exitCode === 0 ? result.stdout.toString().trim() : cwd;
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// src/cli.ts
|
|
2245
|
+
var USAGE = `usage: zgeoff-check [--] [file ...]
|
|
2246
|
+
|
|
2247
|
+
Runs every check over the files that git tracks, or over the files given.
|
|
2248
|
+
Each finding prints as path:line: <check>: <message>, with the path from the repo root.
|
|
2249
|
+
Exits 0 with no finding, 1 with any finding, and 2 when the run could not finish.
|
|
2250
|
+
Checks: ${allChecks.map((check) => check.name).join(", ")}`;
|
|
2251
|
+
var parsed = await parseCLIArgs(process.argv.slice(2));
|
|
2252
|
+
if (typeof parsed === "string") {
|
|
2253
|
+
process.stderr.write(`zgeoff-check: ${parsed}
|
|
2254
|
+
${USAGE}
|
|
2255
|
+
`);
|
|
2256
|
+
process.exit(2);
|
|
2257
|
+
}
|
|
2258
|
+
if (parsed.help) {
|
|
2259
|
+
process.stdout.write(`${USAGE}
|
|
2260
|
+
`);
|
|
2261
|
+
process.exit(0);
|
|
2262
|
+
}
|
|
2263
|
+
var files = parsed.files;
|
|
2264
|
+
try {
|
|
2265
|
+
const root = runGitShowToplevel(process.cwd(), process.env);
|
|
2266
|
+
const config = loadConfig(root, allChecks.map((check) => check.name));
|
|
2267
|
+
const findings = await runChecks({
|
|
2268
|
+
cwd: root,
|
|
2269
|
+
checks: allChecks,
|
|
2270
|
+
config,
|
|
2271
|
+
env: process.env,
|
|
2272
|
+
...files.length === 0 ? {} : { files: files.map((file) => resolve2(file)) }
|
|
2273
|
+
});
|
|
2274
|
+
for (const finding of findings) {
|
|
2275
|
+
process.stdout.write(`${finding.path}:${finding.line}: ${finding.check}: ${finding.message}
|
|
2276
|
+
`);
|
|
2277
|
+
}
|
|
2278
|
+
const exitCode = findings.length === 0 ? 0 : 1;
|
|
2279
|
+
process.exit(exitCode);
|
|
2280
|
+
} catch (error) {
|
|
2281
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2282
|
+
process.stderr.write(`zgeoff-check: ${message}
|
|
2283
|
+
`);
|
|
2284
|
+
process.exit(2);
|
|
2285
|
+
}
|