@pho9ubenaa/siro 0.3.0 → 0.4.0

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/dist/cli.mjs CHANGED
@@ -1,143 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { A as isSeverity, D as PMS, F as asAbsPath, M as UsageError, O as SEVERITIES, P as SiroError, _ as isSupportedNodeVersion, b as JSON_REPORTER_NAME, g as SUPPORTED_NODE_RANGE, h as isProjectType, k as isPM, m as PROJECT_TYPES, n as lintCommand, p as isNodeError, t as nodeIO, v as BUILTIN_REPORTER_NAMES, w as version, y as DEFAULT_REPORTER_NAME } from "./node-io-CUcMwlGH.mjs";
2
+ import { C as SUPPORTED_NODE_RANGE, D as isNodeError, M as UsageError, O as asAbsPath, S as isSeverity, T as assertDirectory, _ as PROJECT_TYPES, b as SEVERITIES, c as DEFAULT_REPORTER_NAME, f as version, j as SiroError, l as JSON_REPORTER_NAME, m as loadConfig, n as lintCommand, s as BUILTIN_REPORTER_NAMES, t as nodeIO, v as isProjectType, w as isSupportedNodeVersion, x as isPM, y as PMS } from "./node-io-BdyNbvzO.mjs";
3
3
  import path from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
- import { cac } from "cac";
6
-
7
- //#region src/cli/commands.ts
8
- /**
9
- * Canonical list of siro CLI sub-commands. Shared between `cli.ts` (the
10
- * dispatcher / arg parser) and `cli/help.ts` (the help-text renderer) so a
11
- * new command added here propagates to both via the derived `CommandName`
12
- * union — there is no second place to remember to update.
13
- */
14
- const COMMANDS = ["lint", "check"];
15
- const isCommandName = (value) => COMMANDS.some((cmd) => cmd === value);
16
-
17
- //#endregion
18
- //#region src/cli/flags.ts
19
- const ARGS_OFFSET$1 = 2;
20
- const FLAGS = {
21
- json: { takesValue: false },
22
- pm: { takesValue: true },
23
- projectType: {
24
- cliName: "project-type",
25
- takesValue: true
26
- },
27
- reporter: { takesValue: true },
28
- severity: { takesValue: true }
29
- };
30
- const flagCliName = (name, meta) => {
31
- if (typeof meta.cliName !== "undefined") return meta.cliName;
32
- return name;
33
- };
34
- /** Flags that consume the following argv token; pre-cac scanners skip that token. */
35
- const VALUE_FLAGS = new Set(Object.entries(FLAGS).filter(([, meta]) => meta.takesValue).flatMap(([name, meta]) => [name, flagCliName(name, meta)]));
36
- const LINT_FLAGS = [
37
- "pm",
38
- "projectType",
39
- "reporter",
40
- "json",
41
- "severity"
42
- ];
43
- const FLAG_SCOPES = {
44
- check: LINT_FLAGS,
45
- lint: LINT_FLAGS
46
- };
47
- const GLOBAL_FLAGS = ["version", "help"];
48
- const CAC_KEYS = [
49
- "v",
50
- "h",
51
- "--"
52
- ];
53
- const flagsFor = (command) => new Set([
54
- ...FLAG_SCOPES[command],
55
- ...GLOBAL_FLAGS,
56
- ...CAC_KEYS
57
- ]);
58
- const collectAllFlags = () => {
59
- const all = [];
60
- for (const command of COMMANDS) for (const flag of flagsFor(command)) all.push(flag);
61
- return new Set(all);
62
- };
63
- const KNOWN_FLAGS = collectAllFlags();
64
- /**
65
- * Whether a `--name value` flag consumes the following argv token. Only the
66
- * separate-token form of a value-taking flag does (`--name=value` carries its
67
- * own value), AND only when the next token isn't itself a `-`-prefixed flag —
68
- * so `--reporter --help` leaves `--help` for the help/version detectors rather
69
- * than eating it as the reporter value. Shared by both pre-cac scanners so the
70
- * skip predicate stays in one place.
71
- */
72
- const consumesNext = (token, next) => token.startsWith("--") && !token.includes("=") && VALUE_FLAGS.has(token.slice(ARGS_OFFSET$1)) && typeof next !== "undefined" && !next.startsWith("-");
73
-
74
- //#endregion
75
- //#region src/cli/scanners.ts
76
- const STEP$1 = 1;
77
- const isHelpFlag = (token) => token === "--help" || token === "-h";
78
- const processHelpToken = (token, next, state) => {
79
- if (token === "--") return "break";
80
- if (isHelpFlag(token)) state.asksHelp = true;
81
- else if (consumesNext(token, next)) state.index += STEP$1;
82
- else if (!token.startsWith("-") && state.first === "") state.first = token;
83
- return "continue";
84
- };
85
- const scanHelpTokens = (argv) => {
86
- const state = {
87
- asksHelp: false,
88
- first: "",
89
- index: 0
90
- };
91
- for (; state.index < argv.length; state.index += STEP$1) {
92
- const token = argv[state.index];
93
- if (typeof token === "string" && processHelpToken(token, argv[state.index + STEP$1], state) === "break") break;
94
- }
95
- return {
96
- asksHelp: state.asksHelp,
97
- first: state.first || void 0
98
- };
99
- };
100
- const resolveTarget = (first) => {
101
- if (typeof first !== "undefined" && isCommandName(first)) return first;
102
- };
103
- /**
104
- * Detect a --help / -h flag in argv before cac sees it (cac would otherwise
105
- * write to console.log itself). Returns the target command (when the first
106
- * positional names one) or 'root' for the top-level help.
107
- */
108
- const detectHelpFlag = (argv) => {
109
- const { asksHelp, first } = scanHelpTokens(argv);
110
- if (!asksHelp) return;
111
- return { target: resolveTarget(first) };
112
- };
113
- /**
114
- * Detect `--version` / `-v` ahead of cac for the same reason `detectHelpFlag`
115
- * exists. The `--` separator stops the scan so a trailing `--version` is left
116
- * for the (rejected) passthrough handler rather than flipping to the version
117
- * branch — siro forwards to no wrapped tool, so non-empty passthrough is a
118
- * usage error (see parseArgs).
119
- */
120
- const detectVersionFlag = (argv) => {
121
- for (let index = 0; index < argv.length; index += STEP$1) {
122
- const token = argv[index];
123
- if (typeof token === "string") {
124
- if (token === "--") return false;
125
- if (token === "--version" || token === "-v") return true;
126
- if (consumesNext(token, argv[index + STEP$1])) index += STEP$1;
127
- }
128
- }
129
- return false;
130
- };
131
-
132
- //#endregion
5
+ import { parseArgs } from "node:util";
133
6
  //#region src/cli/parsers.ts
134
- const rejectUnknownFlags = (flags, allowed, scope) => {
135
- for (const key of Object.keys(flags)) if (!allowed.has(key)) {
136
- let where = "";
137
- if (scope) where = ` for '${scope}'`;
138
- throw new UsageError(`Unknown flag${where}: --${key}`);
139
- }
140
- };
141
7
  const parsePmFlag = (raw) => {
142
8
  if (typeof raw === "undefined") return;
143
9
  if (raw === true) throw new UsageError(`--pm requires a value (expected one of: ${PMS.join(", ")})`);
@@ -156,65 +22,103 @@ const parseSeverityFlag = (raw) => {
156
22
  if (typeof raw !== "string" || !isSeverity(raw)) throw new UsageError(`Invalid severity: ${String(raw)} (expected ${SEVERITIES.join("|")})`);
157
23
  return raw;
158
24
  };
159
- const resolveReporter = (flags) => {
160
- if (flags.reporter === true) throw new UsageError(`--reporter requires a value (expected one of: ${BUILTIN_REPORTER_NAMES.join(", ")}, or a reporter registered via siro.config.ts)`);
161
- if (Array.isArray(flags.reporter) || Array.isArray(flags.json) || typeof flags.reporter === "string" && flags.json === true) throw new UsageError("Invalid reporter selection: use either --reporter or --json, and specify it only once.");
162
- if (typeof flags.reporter === "string") return flags.reporter;
163
- if (flags.json === true) return JSON_REPORTER_NAME;
164
- return DEFAULT_REPORTER_NAME;
165
- };
166
25
  const ensureNodeVersion = (nodeVersion) => {
167
26
  if (!isSupportedNodeVersion(nodeVersion)) throw new UsageError(`Node ${SUPPORTED_NODE_RANGE} required (you are on ${nodeVersion}). Upgrade your runtime to use siro.`);
168
27
  };
169
-
170
28
  //#endregion
171
- //#region src/cli/cac-bridge.ts
172
- const STEP = 1;
173
- const ARGS_OFFSET = 2;
174
- const buildCli = () => {
175
- const cli = cac("siro");
176
- cli.option("--pm <name>", "Target a specific package manager").option("--project-type <type>", "Project type (application|package)").option("--reporter <name>", `Reporter (${BUILTIN_REPORTER_NAMES.join("|")})`).option("--json", "Shortcut for --reporter json").option("--severity <level>", `Display/fail threshold (${SEVERITIES.join("|")})`).help().version(version);
177
- for (const name of COMMANDS) {
178
- let desc = `${name} a repository`;
179
- if (name === "check") desc = "Alias of lint";
180
- cli.command(`${name} [cwd]`, desc);
181
- }
182
- return cli;
183
- };
184
- const extractPositionalCwd = (matchedCommand, args) => {
185
- if (matchedCommand) {
186
- const [cwd] = args;
187
- return cwd;
29
+ //#region src/cli/commands.ts
30
+ const COMMANDS = ["lint", "check"];
31
+ const isCommandName = (value) => COMMANDS.some((cmd) => cmd === value);
32
+ //#endregion
33
+ //#region src/cli/parse-args.ts
34
+ const VALUE_FLAGS = new Set([
35
+ "pm",
36
+ "project-type",
37
+ "reporter",
38
+ "severity"
39
+ ]);
40
+ const BOOLEAN_FLAGS = new Set([
41
+ "help",
42
+ "version",
43
+ "json"
44
+ ]);
45
+ const parseCommand = (argv) => {
46
+ const { tokens } = parseArgs({
47
+ args: [...argv],
48
+ options: {
49
+ help: {
50
+ type: "boolean",
51
+ short: "h"
52
+ },
53
+ version: {
54
+ type: "boolean",
55
+ short: "v"
56
+ }
57
+ },
58
+ strict: false,
59
+ tokens: true
60
+ });
61
+ const flags = /* @__PURE__ */ new Map();
62
+ const positionals = [];
63
+ let error;
64
+ for (let index = 0; index < tokens.length; index += 1) {
65
+ const token = tokens[index];
66
+ if (!token) break;
67
+ if (token.kind === "option-terminator") {
68
+ if (index + 1 < tokens.length) error ??= "siro takes no passthrough arguments after `--`.";
69
+ break;
70
+ }
71
+ if (token.kind === "positional") {
72
+ positionals.push(token.value);
73
+ continue;
74
+ }
75
+ if (BOOLEAN_FLAGS.has(token.name)) if (token.value !== void 0) error ??= `Flag ${token.rawName} does not accept a value.`;
76
+ else {
77
+ if (token.name === "json" && flags.has("json")) error ??= "--json must be specified only once.";
78
+ flags.set(token.name, true);
79
+ }
80
+ else if (VALUE_FLAGS.has(token.name)) {
81
+ let value = token.value;
82
+ const next = tokens[index + 1];
83
+ if (value === void 0 && next?.kind === "positional" && next.index === token.index + 1) {
84
+ value = next.value;
85
+ index += 1;
86
+ }
87
+ if (value === void 0 || value === "") error ??= `${token.rawName} requires a value.`;
88
+ else {
89
+ if (flags.has(token.name)) error ??= `${token.rawName} must be specified only once.`;
90
+ flags.set(token.name, value);
91
+ }
92
+ } else error ??= `Unknown flag: ${token.rawName}`;
188
93
  }
189
- return args[STEP];
190
- };
191
- const extractCommandCandidate = (matchedCommand, args) => {
192
- if (matchedCommand) return matchedCommand;
193
- const [candidate] = args;
194
- return candidate;
195
- };
196
- const extractExtraPositionals = (matchedCommand, args) => {
197
- if (matchedCommand) return args.slice(STEP);
198
- return args.slice(ARGS_OFFSET);
199
- };
200
- const parseCacOutput = (argv) => {
201
- const cli = buildCli();
202
- const parsed = cli.parse([
203
- "node",
204
- "siro",
205
- ...argv
206
- ], { run: false });
207
- const flags = parsed.options;
208
- const matchedCommand = cli.matchedCommandName;
209
- const positionalCwd = extractPositionalCwd(matchedCommand, parsed.args);
94
+ const [command, cwd, ...extra] = positionals;
95
+ if (flags.has("help")) return {
96
+ kind: "help",
97
+ target: command && isCommandName(command) ? command : void 0
98
+ };
99
+ if (flags.has("version")) return { kind: "version" };
100
+ if (error) throw new UsageError(error);
101
+ if (command === void 0) return { kind: "usage" };
102
+ if (command === "init") return {
103
+ kind: "usage",
104
+ reason: "The 'init' command was removed: siro is lint-only. Run `siro lint --reporter json` and review each finding's remediation."
105
+ };
106
+ if (!isCommandName(command)) return {
107
+ kind: "usage",
108
+ reason: `Unknown command: ${command}`
109
+ };
110
+ if (extra.length > 0) throw new UsageError(`Unexpected extra argument: ${extra.join(" ")}`);
111
+ if (flags.has("reporter") && flags.has("json")) throw new UsageError("Invalid reporter selection: use either --reporter or --json.");
112
+ const reporter = flags.get("reporter");
210
113
  return {
211
- commandCandidate: extractCommandCandidate(matchedCommand, parsed.args),
212
- extraPositionals: extractExtraPositionals(matchedCommand, parsed.args),
213
- flags,
214
- positionalCwd
114
+ kind: "lint",
115
+ cwd: asAbsPath(path.resolve(cwd ?? process.cwd())),
116
+ pm: parsePmFlag(flags.get("pm")),
117
+ projectType: parseProjectTypeFlag(flags.get("project-type")),
118
+ severity: parseSeverityFlag(flags.get("severity")),
119
+ reporter: typeof reporter === "string" ? reporter : flags.has("json") ? JSON_REPORTER_NAME : DEFAULT_REPORTER_NAME
215
120
  };
216
121
  };
217
-
218
122
  //#endregion
219
123
  //#region src/cli/help.ts
220
124
  const PMS_LIST = PMS.join("|");
@@ -288,143 +192,63 @@ const renderHelp = (target) => {
288
192
  if (target === "lint" || target === "check") return HELP_LINT;
289
193
  return target;
290
194
  };
291
-
292
195
  //#endregion
293
196
  //#region src/cli.ts
294
197
  const EXIT_SUCCESS = 0;
295
198
  const EXIT_USAGE = 2;
296
199
  const EXIT_CRASH = 70;
297
- const rejectPassthrough = (flags) => {
298
- const passthrough = flags["--"];
299
- if (Array.isArray(passthrough) && passthrough.length > 0) throw new UsageError("siro takes no passthrough arguments after `--`.");
300
- };
301
- const resolveCommandCandidate = (commandCandidate) => {
302
- if (typeof commandCandidate === "undefined") return { kind: "usage" };
303
- if (commandCandidate === "init") return {
304
- kind: "usage",
305
- reason: "The 'init' command was removed: siro is lint-only. Run `siro lint --reporter json` and apply each finding's `fix` operations with your editor or an agent skill."
306
- };
307
- if (typeof commandCandidate !== "string" || !isCommandName(commandCandidate)) return {
308
- kind: "usage",
309
- reason: `Unknown command: ${String(commandCandidate)}`
310
- };
311
- return {
312
- kind: "command",
313
- name: commandCandidate
314
- };
315
- };
316
- const SINGLE = 1;
317
- const rejectExtraPositionals = (extraPositionals) => {
318
- if (extraPositionals.length > 0) {
319
- let plural = "";
320
- if (extraPositionals.length > SINGLE) plural = "s";
321
- throw new UsageError(`Unexpected extra argument${plural}: ${extraPositionals.join(" ")}`);
322
- }
323
- };
324
- const resolveCwd = (positionalCwd) => {
325
- let raw = process.cwd();
326
- if (typeof positionalCwd === "string") raw = positionalCwd;
327
- return asAbsPath(path.resolve(raw));
328
- };
329
- const checkPreScanFlags = (argv) => {
330
- const preHelp = detectHelpFlag(argv);
331
- if (typeof preHelp !== "undefined") return {
332
- kind: "help",
333
- target: preHelp.target
334
- };
335
- if (detectVersionFlag(argv)) return { kind: "version" };
336
- };
337
- const buildLintCommand = (flags, positionalCwd, commandCandidate) => {
338
- rejectUnknownFlags(flags, flagsFor(commandCandidate), commandCandidate);
339
- return {
340
- cwd: resolveCwd(positionalCwd),
341
- kind: "lint",
342
- pm: parsePmFlag(flags.pm),
343
- projectType: parseProjectTypeFlag(flags.projectType),
344
- reporter: resolveReporter(flags),
345
- severity: parseSeverityFlag(flags.severity)
346
- };
347
- };
348
- const validateFlags = (flags) => {
349
- rejectUnknownFlags(flags, KNOWN_FLAGS);
350
- rejectPassthrough(flags);
351
- };
352
- const parseArgs = (argv) => {
353
- const preScan = checkPreScanFlags(argv);
354
- if (typeof preScan !== "undefined") return preScan;
355
- const { commandCandidate, extraPositionals, flags, positionalCwd } = parseCacOutput(argv);
356
- validateFlags(flags);
357
- const resolved = resolveCommandCandidate(commandCandidate);
358
- if (resolved.kind === "usage") return resolved;
359
- rejectExtraPositionals(extraPositionals);
360
- return buildLintCommand(flags, positionalCwd, resolved.name);
361
- };
362
- const handleVersion = (io) => {
363
- io.stdout(version);
364
- return Promise.resolve(EXIT_SUCCESS);
365
- };
366
- const handleHelp = (target, io) => {
367
- io.stdout(renderHelp(target));
368
- return Promise.resolve(EXIT_SUCCESS);
369
- };
370
- const handleUsage = (reason, io) => {
371
- if (typeof reason !== "undefined") io.stderr(`${reason}\n`);
372
- io.stderr(renderHelp());
373
- return Promise.resolve(EXIT_USAGE);
374
- };
375
200
  const dispatch = (cmd, io) => {
376
201
  switch (cmd.kind) {
377
- case "version": return handleVersion(io);
378
- case "help": return handleHelp(cmd.target, io);
379
- case "usage": return handleUsage(cmd.reason, io);
380
- case "lint": return lintCommand(cmd, io);
202
+ case "version":
203
+ io.stdout(version);
204
+ return EXIT_SUCCESS;
205
+ case "help":
206
+ io.stdout(renderHelp(cmd.target));
207
+ return EXIT_SUCCESS;
208
+ case "usage":
209
+ if (cmd.reason) io.stderr(`${cmd.reason}\n`);
210
+ io.stderr(renderHelp());
211
+ return EXIT_USAGE;
212
+ case "lint":
213
+ assertDirectory(cmd.cwd);
214
+ return loadConfig(cmd.cwd).then((config) => lintCommand({
215
+ ...cmd,
216
+ config
217
+ }, io));
381
218
  default: throw new Error(`Unhandled command kind: ${String(cmd)}`);
382
219
  }
383
220
  };
384
- const handleAsyncError = (error, io) => {
221
+ const handleError = (error, io) => {
385
222
  if (error instanceof SiroError) {
386
223
  io.stderr(error.message);
387
224
  return error.exitCode;
388
225
  }
389
- if (isNodeError(error) && typeof error.errno === "number") {
226
+ if (isNodeError(error) && "errno" in error && typeof error.errno === "number") {
390
227
  io.stderr(`File system error: ${error.message}`);
391
228
  return EXIT_USAGE;
392
229
  }
393
230
  throw error;
394
231
  };
395
- const handleSyncError = (error, io) => {
396
- if (error instanceof SiroError) {
397
- io.stderr(error.message);
398
- return Promise.resolve(error.exitCode);
399
- }
400
- if (isNodeError(error) && typeof error.errno === "number") {
401
- io.stderr(`File system error: ${error.message}`);
402
- return Promise.resolve(EXIT_USAGE);
403
- }
404
- return Promise.reject(error);
405
- };
406
- const run = (argv, io = nodeIO) => {
232
+ const run = async (argv, io = nodeIO) => {
407
233
  try {
408
234
  ensureNodeVersion(process.versions.node);
409
- return dispatch(parseArgs(argv), io).catch((error) => handleAsyncError(error, io));
235
+ return await dispatch(parseCommand(argv), io);
410
236
  } catch (error) {
411
- return handleSyncError(error, io);
237
+ return handleError(error, io);
412
238
  }
413
239
  };
414
- const onRunFulfilled = (code) => {
415
- process.exitCode = code;
416
- };
417
- const onRunRejected = (error) => {
418
- let errStr = String(error);
419
- if (error instanceof Error) errStr = error.stack ?? error.message;
420
- process.stderr.write(`${errStr}\n`);
421
- process.exitCode = EXIT_CRASH;
240
+ const runMain = async (argv) => {
241
+ try {
242
+ process.exitCode = await run(argv);
243
+ } catch (error) {
244
+ const errStr = error instanceof Error ? error.stack ?? error.message : String(error);
245
+ process.stderr.write(`${errStr}\n`);
246
+ process.exitCode = EXIT_CRASH;
247
+ }
422
248
  };
423
- const runMain = (argv) => run(argv).then(onRunFulfilled, onRunRejected);
424
- const ARGV_SKIP = 2;
425
249
  const [, invokedPath] = process.argv;
426
- if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) await runMain(process.argv.slice(ARGV_SKIP));
427
-
250
+ if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) await runMain(process.argv.slice(2));
428
251
  //#endregion
429
252
  export { run, runMain };
253
+
430
254
  //# sourceMappingURL=cli.mjs.map