@pho9ubenaa/siro 0.1.3 → 0.2.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/CHANGELOG.md +17 -0
- package/dist/cli.mjs +414 -3
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +8 -1
- package/dist/node-io-Cb8nixgE.mjs +2509 -0
- package/package.json +3 -3
- package/dist/node-io-BLwef7p6.mjs +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.2.0]
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- **Node.js `^22.18.0 || ^23.6.0 || >=24.0.0` required** (was `>=20`): `siro.config.ts` now loads via Node's native type stripping.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- **OSS benchmarks module**: coverage validation against 47 leading OSS projects across all 6 package managers. Includes sparse-checkout clone script, snapshot-based CI mode, and local clone-based checking.
|
|
12
|
+
- **jiti dependency removed**: runtime dependencies drop from 7 to 6. `.ts` configs load via native type stripping, and every `loadConfig` call now truly re-reads the config from disk (jiti's transform cache could replay a same-second rewrite).
|
|
13
|
+
- **Unminified dist**: the published bundle ships as readable JavaScript for supply-chain transparency.
|
|
14
|
+
|
|
15
|
+
### Fixes
|
|
16
|
+
|
|
17
|
+
- A `.ts` config on a runtime without type stripping fails with an actionable `ConfigError` instead of a raw `ERR_UNKNOWN_FILE_EXTENSION`.
|
|
18
|
+
- `pnpm gen:rule` works again — the scaffolder's marker had drifted from `rule-id.ts`.
|
|
19
|
+
|
|
3
20
|
## [0.1.3]
|
|
4
21
|
|
|
5
22
|
Initial public release. 27 rules across 6 package managers (npm, pnpm, yarn, bun, deno, aube).
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,416 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{C as
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { C as isPM, E as UsageError, O as SiroError, S as SEVERITIES, k as asAbsPath, m as BUILTIN_REPORTER_NAMES, n as lintCommand, p as isNodeError, t as nodeIO, v as version, w as isSeverity, x as PMS } from "./node-io-Cb8nixgE.mjs";
|
|
3
|
+
import path from "node:path";
|
|
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
|
+
/** Flags that consume the following argv token; pre-cac scanners skip that token. */
|
|
21
|
+
const VALUE_FLAGS = new Set(Object.entries({
|
|
22
|
+
json: { takesValue: false },
|
|
23
|
+
pm: { takesValue: true },
|
|
24
|
+
reporter: { takesValue: true },
|
|
25
|
+
severity: { takesValue: true }
|
|
26
|
+
}).filter(([, meta]) => meta.takesValue).map(([name]) => name));
|
|
27
|
+
const LINT_FLAGS = [
|
|
28
|
+
"pm",
|
|
29
|
+
"reporter",
|
|
30
|
+
"json",
|
|
31
|
+
"severity"
|
|
32
|
+
];
|
|
33
|
+
const FLAG_SCOPES = {
|
|
34
|
+
check: LINT_FLAGS,
|
|
35
|
+
lint: LINT_FLAGS
|
|
36
|
+
};
|
|
37
|
+
const GLOBAL_FLAGS = ["version", "help"];
|
|
38
|
+
const CAC_KEYS = [
|
|
39
|
+
"v",
|
|
40
|
+
"h",
|
|
41
|
+
"--"
|
|
42
|
+
];
|
|
43
|
+
const flagsFor = (command) => new Set([
|
|
44
|
+
...FLAG_SCOPES[command],
|
|
45
|
+
...GLOBAL_FLAGS,
|
|
46
|
+
...CAC_KEYS
|
|
47
|
+
]);
|
|
48
|
+
const collectAllFlags = () => {
|
|
49
|
+
const all = [];
|
|
50
|
+
for (const command of COMMANDS) for (const flag of flagsFor(command)) all.push(flag);
|
|
51
|
+
return new Set(all);
|
|
52
|
+
};
|
|
53
|
+
const KNOWN_FLAGS = collectAllFlags();
|
|
54
|
+
/**
|
|
55
|
+
* Whether a `--name value` flag consumes the following argv token. Only the
|
|
56
|
+
* separate-token form of a value-taking flag does (`--name=value` carries its
|
|
57
|
+
* own value), AND only when the next token isn't itself a `-`-prefixed flag —
|
|
58
|
+
* so `--reporter --help` leaves `--help` for the help/version detectors rather
|
|
59
|
+
* than eating it as the reporter value. Shared by both pre-cac scanners so the
|
|
60
|
+
* skip predicate stays in one place.
|
|
61
|
+
*/
|
|
62
|
+
const consumesNext = (token, next) => token.startsWith("--") && !token.includes("=") && VALUE_FLAGS.has(token.slice(ARGS_OFFSET$1)) && typeof next !== "undefined" && !next.startsWith("-");
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/cli/scanners.ts
|
|
66
|
+
const STEP$1 = 1;
|
|
67
|
+
const isHelpFlag = (token) => token === "--help" || token === "-h";
|
|
68
|
+
const processHelpToken = (token, next, state) => {
|
|
69
|
+
if (token === "--") return "break";
|
|
70
|
+
if (isHelpFlag(token)) state.asksHelp = true;
|
|
71
|
+
else if (consumesNext(token, next)) state.index += STEP$1;
|
|
72
|
+
else if (!token.startsWith("-") && state.first === "") state.first = token;
|
|
73
|
+
return "continue";
|
|
74
|
+
};
|
|
75
|
+
const scanHelpTokens = (argv) => {
|
|
76
|
+
const state = {
|
|
77
|
+
asksHelp: false,
|
|
78
|
+
first: "",
|
|
79
|
+
index: 0
|
|
80
|
+
};
|
|
81
|
+
for (; state.index < argv.length; state.index += STEP$1) {
|
|
82
|
+
const token = argv[state.index];
|
|
83
|
+
if (typeof token === "string" && processHelpToken(token, argv[state.index + STEP$1], state) === "break") break;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
asksHelp: state.asksHelp,
|
|
87
|
+
first: state.first || [].pop()
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
const resolveTarget = (first) => {
|
|
91
|
+
if (typeof first !== "undefined" && isCommandName(first)) return first;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Detect a --help / -h flag in argv before cac sees it (cac would otherwise
|
|
95
|
+
* write to console.log itself). Returns the target command (when the first
|
|
96
|
+
* positional names one) or 'root' for the top-level help.
|
|
97
|
+
*/
|
|
98
|
+
const detectHelpFlag = (argv) => {
|
|
99
|
+
const { asksHelp, first } = scanHelpTokens(argv);
|
|
100
|
+
if (!asksHelp) return [].pop();
|
|
101
|
+
return { target: resolveTarget(first) };
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Detect `--version` / `-v` ahead of cac for the same reason `detectHelpFlag`
|
|
105
|
+
* exists. The `--` separator stops the scan so a trailing `--version` is left
|
|
106
|
+
* for the (rejected) passthrough handler rather than flipping to the version
|
|
107
|
+
* branch — siro forwards to no wrapped tool, so non-empty passthrough is a
|
|
108
|
+
* usage error (see parseArgs).
|
|
109
|
+
*/
|
|
110
|
+
const detectVersionFlag = (argv) => {
|
|
111
|
+
for (let index = 0; index < argv.length; index += STEP$1) {
|
|
112
|
+
const token = argv[index];
|
|
113
|
+
if (typeof token === "string") {
|
|
114
|
+
if (token === "--") return false;
|
|
115
|
+
if (token === "--version" || token === "-v") return true;
|
|
116
|
+
if (consumesNext(token, argv[index + STEP$1])) index += STEP$1;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/cli/parsers.ts
|
|
124
|
+
const FIRST_INDEX$1 = 0;
|
|
125
|
+
const MIN_NODE_MAJOR = 20;
|
|
126
|
+
const rejectUnknownFlags = (flags, allowed, scope) => {
|
|
127
|
+
for (const key of Object.keys(flags)) if (!allowed.has(key)) {
|
|
128
|
+
let where = "";
|
|
129
|
+
if (scope) where = ` for '${scope}'`;
|
|
130
|
+
throw new UsageError(`Unknown flag${where}: --${key}`);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const parsePmFlag = (raw) => {
|
|
134
|
+
if (typeof raw === "undefined") return;
|
|
135
|
+
if (raw === true) throw new UsageError(`--pm requires a value (expected one of: ${PMS.join(", ")})`);
|
|
136
|
+
if (typeof raw !== "string" || !isPM(raw)) throw new UsageError(`Unknown package manager: ${raw} (expected one of: ${PMS.join(", ")})`);
|
|
137
|
+
return raw;
|
|
138
|
+
};
|
|
139
|
+
const parseSeverityFlag = (raw) => {
|
|
140
|
+
if (typeof raw === "undefined") return;
|
|
141
|
+
if (raw === true) throw new UsageError(`--severity requires a value (expected ${SEVERITIES.join("|")})`);
|
|
142
|
+
if (typeof raw !== "string" || !isSeverity(raw)) throw new UsageError(`Invalid severity: ${String(raw)} (expected ${SEVERITIES.join("|")})`);
|
|
143
|
+
return raw;
|
|
144
|
+
};
|
|
145
|
+
const resolveReporter = (flags) => {
|
|
146
|
+
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)`);
|
|
147
|
+
if (typeof flags.reporter === "string") {
|
|
148
|
+
if (flags.json === true) throw new UsageError("Use either --reporter or --json, not both.");
|
|
149
|
+
return flags.reporter;
|
|
150
|
+
}
|
|
151
|
+
if (flags.json === true) return "json";
|
|
152
|
+
return "pretty";
|
|
153
|
+
};
|
|
154
|
+
const ensureNodeVersion = (nodeVersion) => {
|
|
155
|
+
const major = Number(nodeVersion.replace(/^v/u, "").split(".")[FIRST_INDEX$1]);
|
|
156
|
+
if (Number.isFinite(major) && major < MIN_NODE_MAJOR) throw new UsageError(`Node ${MIN_NODE_MAJOR}+ required (you are on ${nodeVersion}). Upgrade your runtime to use siro.`);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/cli/cac-bridge.ts
|
|
161
|
+
const FIRST_INDEX = 0;
|
|
162
|
+
const STEP = 1;
|
|
163
|
+
const ARGS_OFFSET = 2;
|
|
164
|
+
const buildCli = () => {
|
|
165
|
+
const cli = cac("siro");
|
|
166
|
+
cli.option("--pm <name>", "Target a specific package manager").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);
|
|
167
|
+
for (const name of COMMANDS) {
|
|
168
|
+
let desc = `${name} a repository`;
|
|
169
|
+
if (name === "check") desc = "Alias of lint";
|
|
170
|
+
cli.command(`${name} [cwd]`, desc);
|
|
171
|
+
}
|
|
172
|
+
return cli;
|
|
173
|
+
};
|
|
174
|
+
const extractPositionalCwd = (matchedCommand, args) => {
|
|
175
|
+
if (matchedCommand) return args[FIRST_INDEX];
|
|
176
|
+
return args[STEP];
|
|
177
|
+
};
|
|
178
|
+
const extractCommandCandidate = (matchedCommand, args) => {
|
|
179
|
+
if (matchedCommand) return matchedCommand;
|
|
180
|
+
return args[FIRST_INDEX];
|
|
181
|
+
};
|
|
182
|
+
const extractExtraPositionals = (matchedCommand, args) => {
|
|
183
|
+
if (matchedCommand) return args.slice(STEP);
|
|
184
|
+
return args.slice(ARGS_OFFSET);
|
|
185
|
+
};
|
|
186
|
+
const parseCacOutput = (argv) => {
|
|
187
|
+
const cli = buildCli();
|
|
188
|
+
const parsed = cli.parse([
|
|
189
|
+
"node",
|
|
190
|
+
"siro",
|
|
191
|
+
...argv
|
|
192
|
+
], { run: false });
|
|
193
|
+
const flags = parsed.options;
|
|
194
|
+
const matchedCommand = cli.matchedCommandName;
|
|
195
|
+
const positionalCwd = extractPositionalCwd(matchedCommand, parsed.args);
|
|
196
|
+
return {
|
|
197
|
+
commandCandidate: extractCommandCandidate(matchedCommand, parsed.args),
|
|
198
|
+
extraPositionals: extractExtraPositionals(matchedCommand, parsed.args),
|
|
199
|
+
flags,
|
|
200
|
+
matchedCommand,
|
|
201
|
+
positionalCwd
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/cli/help.ts
|
|
207
|
+
const PMS_LIST = PMS.join("|");
|
|
208
|
+
const REPORTERS_LIST = BUILTIN_REPORTER_NAMES.join("|");
|
|
209
|
+
const SEVERITIES_LIST = SEVERITIES.join("|");
|
|
210
|
+
const FLAG_LINES = {
|
|
211
|
+
json: " --json Shortcut for --reporter json",
|
|
212
|
+
pm: ` --pm <name> Target a specific package manager (${PMS_LIST})`,
|
|
213
|
+
reporter: ` --reporter <name> Reporter (${REPORTERS_LIST}; additional reporters can be registered via siro.config.ts)`,
|
|
214
|
+
severity: ` --severity <level> Show + fail on findings at or above this level (${SEVERITIES_LIST})`
|
|
215
|
+
};
|
|
216
|
+
const HELP_ROOT = [
|
|
217
|
+
"siro — security best-practices for the npm ecosystem",
|
|
218
|
+
"",
|
|
219
|
+
"USAGE",
|
|
220
|
+
" siro <command> [path] [flags]",
|
|
221
|
+
"",
|
|
222
|
+
"COMMANDS",
|
|
223
|
+
" lint Report best-practice violations (alias: check)",
|
|
224
|
+
"",
|
|
225
|
+
"GLOBAL FLAGS",
|
|
226
|
+
FLAG_LINES.pm,
|
|
227
|
+
" --version Print the siro version",
|
|
228
|
+
" --help Show help for siro or a command",
|
|
229
|
+
"",
|
|
230
|
+
"LINT FLAGS",
|
|
231
|
+
FLAG_LINES.reporter,
|
|
232
|
+
FLAG_LINES.json,
|
|
233
|
+
FLAG_LINES.severity,
|
|
234
|
+
"",
|
|
235
|
+
"EXAMPLES",
|
|
236
|
+
" $ siro lint # report violations in cwd",
|
|
237
|
+
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
238
|
+
" $ siro lint --severity warn # also fail on warnings",
|
|
239
|
+
"",
|
|
240
|
+
"LEARN MORE",
|
|
241
|
+
" Quickstart https://github.com/pHo9UBenaA/siro/blob/main/docs/getting-started.md",
|
|
242
|
+
" Rule reference https://github.com/pHo9UBenaA/siro/blob/main/docs/rules.md",
|
|
243
|
+
" Config options https://github.com/pHo9UBenaA/siro/blob/main/docs/configuration.md"
|
|
244
|
+
].join("\n");
|
|
245
|
+
const HELP_LINT = [
|
|
246
|
+
"siro lint — report best-practice violations",
|
|
247
|
+
"",
|
|
248
|
+
"USAGE",
|
|
249
|
+
" siro lint [path] [flags]",
|
|
250
|
+
" siro check [path] [flags] (alias)",
|
|
251
|
+
"",
|
|
252
|
+
"FLAGS",
|
|
253
|
+
FLAG_LINES.pm,
|
|
254
|
+
FLAG_LINES.reporter,
|
|
255
|
+
FLAG_LINES.json,
|
|
256
|
+
FLAG_LINES.severity,
|
|
257
|
+
"",
|
|
258
|
+
"EXIT CODES",
|
|
259
|
+
" 0 No findings at or above the active threshold",
|
|
260
|
+
" 1 Findings at or above the threshold (default: error)",
|
|
261
|
+
" 2 Usage error (bad flag, broken siro.config.ts, unreadable path, …)",
|
|
262
|
+
" 70 Uncaught exception (a siro bug, or a reporter / custom rule that threw)",
|
|
263
|
+
"",
|
|
264
|
+
"EXAMPLES",
|
|
265
|
+
" $ siro lint # default: pretty reporter, fail on errors",
|
|
266
|
+
" $ siro lint --reporter github # GitHub Actions annotations",
|
|
267
|
+
" $ siro lint --severity warn # tighten the gate"
|
|
268
|
+
].join("\n");
|
|
269
|
+
const renderHelp = (target) => {
|
|
270
|
+
if (typeof target === "undefined") return HELP_ROOT;
|
|
271
|
+
if (target === "lint" || target === "check") return HELP_LINT;
|
|
272
|
+
return target;
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/cli.ts
|
|
277
|
+
const EMPTY = 0;
|
|
278
|
+
const EXIT_SUCCESS = 0;
|
|
279
|
+
const EXIT_USAGE = 2;
|
|
280
|
+
const EXIT_CRASH = 70;
|
|
281
|
+
const rejectPassthrough = (flags) => {
|
|
282
|
+
const passthrough = flags["--"];
|
|
283
|
+
if (Array.isArray(passthrough) && passthrough.length > EMPTY) throw new UsageError("siro takes no passthrough arguments after `--`.");
|
|
284
|
+
};
|
|
285
|
+
const resolveCommandCandidate = (commandCandidate) => {
|
|
286
|
+
if (typeof commandCandidate === "undefined") return { kind: "usage" };
|
|
287
|
+
if (commandCandidate === "init") return {
|
|
288
|
+
kind: "usage",
|
|
289
|
+
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."
|
|
290
|
+
};
|
|
291
|
+
if (typeof commandCandidate !== "string" || !isCommandName(commandCandidate)) return {
|
|
292
|
+
kind: "usage",
|
|
293
|
+
reason: `Unknown command: ${String(commandCandidate)}`
|
|
294
|
+
};
|
|
295
|
+
return {
|
|
296
|
+
kind: "command",
|
|
297
|
+
name: commandCandidate
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
const SINGLE = 1;
|
|
301
|
+
const rejectExtraPositionals = (extraPositionals) => {
|
|
302
|
+
if (extraPositionals.length > EMPTY) {
|
|
303
|
+
let plural = "";
|
|
304
|
+
if (extraPositionals.length > SINGLE) plural = "s";
|
|
305
|
+
throw new UsageError(`Unexpected extra argument${plural}: ${extraPositionals.join(" ")}`);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
const resolveCwd = (positionalCwd) => {
|
|
309
|
+
let raw = process.cwd();
|
|
310
|
+
if (typeof positionalCwd === "string") raw = positionalCwd;
|
|
311
|
+
return asAbsPath(path.resolve(raw));
|
|
312
|
+
};
|
|
313
|
+
const checkPreScanFlags = (argv) => {
|
|
314
|
+
const preHelp = detectHelpFlag(argv);
|
|
315
|
+
if (typeof preHelp !== "undefined") return {
|
|
316
|
+
kind: "help",
|
|
317
|
+
target: preHelp.target
|
|
318
|
+
};
|
|
319
|
+
if (detectVersionFlag(argv)) return { kind: "version" };
|
|
320
|
+
};
|
|
321
|
+
const buildLintCommand = (flags, positionalCwd, commandCandidate) => {
|
|
322
|
+
rejectUnknownFlags(flags, flagsFor(commandCandidate), commandCandidate);
|
|
323
|
+
return {
|
|
324
|
+
cwd: resolveCwd(positionalCwd),
|
|
325
|
+
kind: "lint",
|
|
326
|
+
pm: parsePmFlag(flags.pm),
|
|
327
|
+
reporter: resolveReporter(flags),
|
|
328
|
+
severity: parseSeverityFlag(flags.severity)
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
const validateFlags = (flags) => {
|
|
332
|
+
rejectUnknownFlags(flags, KNOWN_FLAGS);
|
|
333
|
+
rejectPassthrough(flags);
|
|
334
|
+
};
|
|
335
|
+
const parseArgs = (argv) => {
|
|
336
|
+
const preScan = checkPreScanFlags(argv);
|
|
337
|
+
if (typeof preScan !== "undefined") return preScan;
|
|
338
|
+
const { commandCandidate, extraPositionals, flags, positionalCwd } = parseCacOutput(argv);
|
|
339
|
+
validateFlags(flags);
|
|
340
|
+
const resolved = resolveCommandCandidate(commandCandidate);
|
|
341
|
+
if (resolved.kind === "usage") return resolved;
|
|
342
|
+
rejectExtraPositionals(extraPositionals);
|
|
343
|
+
return buildLintCommand(flags, positionalCwd, resolved.name);
|
|
344
|
+
};
|
|
345
|
+
const handleVersion = (io) => {
|
|
346
|
+
io.stdout(version);
|
|
347
|
+
return Promise.resolve(EXIT_SUCCESS);
|
|
348
|
+
};
|
|
349
|
+
const handleHelp = (target, io) => {
|
|
350
|
+
io.stdout(renderHelp(target));
|
|
351
|
+
return Promise.resolve(EXIT_SUCCESS);
|
|
352
|
+
};
|
|
353
|
+
const handleUsage = (reason, io) => {
|
|
354
|
+
if (typeof reason !== "undefined") io.stderr(`${reason}\n`);
|
|
355
|
+
io.stderr(renderHelp());
|
|
356
|
+
return Promise.resolve(EXIT_USAGE);
|
|
357
|
+
};
|
|
358
|
+
const dispatch = (cmd, io) => {
|
|
359
|
+
switch (cmd.kind) {
|
|
360
|
+
case "version": return handleVersion(io);
|
|
361
|
+
case "help": return handleHelp(cmd.target, io);
|
|
362
|
+
case "usage": return handleUsage(cmd.reason, io);
|
|
363
|
+
case "lint": return lintCommand(cmd, io);
|
|
364
|
+
default: throw new Error(`Unhandled command kind: ${String(cmd)}`);
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
const handleAsyncError = (error, io) => {
|
|
368
|
+
if (error instanceof SiroError) {
|
|
369
|
+
io.stderr(error.message);
|
|
370
|
+
return error.exitCode;
|
|
371
|
+
}
|
|
372
|
+
if (isNodeError(error) && typeof error.errno === "number") {
|
|
373
|
+
io.stderr(`File system error: ${error.message}`);
|
|
374
|
+
return EXIT_USAGE;
|
|
375
|
+
}
|
|
376
|
+
throw error;
|
|
377
|
+
};
|
|
378
|
+
const handleSyncError = (error, io) => {
|
|
379
|
+
if (error instanceof SiroError) {
|
|
380
|
+
io.stderr(error.message);
|
|
381
|
+
return Promise.resolve(error.exitCode);
|
|
382
|
+
}
|
|
383
|
+
if (isNodeError(error) && typeof error.errno === "number") {
|
|
384
|
+
io.stderr(`File system error: ${error.message}`);
|
|
385
|
+
return Promise.resolve(EXIT_USAGE);
|
|
386
|
+
}
|
|
387
|
+
return Promise.reject(error);
|
|
388
|
+
};
|
|
389
|
+
const run = (argv, io = nodeIO) => {
|
|
390
|
+
try {
|
|
391
|
+
ensureNodeVersion(process.versions.node);
|
|
392
|
+
return dispatch(parseArgs(argv), io).catch((error) => handleAsyncError(error, io));
|
|
393
|
+
} catch (error) {
|
|
394
|
+
return handleSyncError(error, io);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
const onRunFulfilled = (code) => {
|
|
398
|
+
process.exitCode = code;
|
|
399
|
+
};
|
|
400
|
+
const onRunRejected = (error) => {
|
|
401
|
+
let errStr = String(error);
|
|
402
|
+
if (error instanceof Error) errStr = error.stack ?? error.message;
|
|
403
|
+
process.stderr.write(`${errStr}\n`);
|
|
404
|
+
process.exitCode = EXIT_CRASH;
|
|
405
|
+
};
|
|
406
|
+
const ARGV_SKIP = 2;
|
|
407
|
+
const [, invokedPath] = process.argv;
|
|
408
|
+
if (invokedPath && import.meta.url === pathToFileURL(invokedPath).href) try {
|
|
409
|
+
onRunFulfilled(await run(process.argv.slice(ARGV_SKIP)));
|
|
410
|
+
} catch (error) {
|
|
411
|
+
onRunRejected(error);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
//#endregion
|
|
415
|
+
export { run };
|
|
5
416
|
//# sourceMappingURL=cli.mjs.map
|
package/dist/index.d.mts
CHANGED
|
@@ -334,7 +334,7 @@ interface LintOptions {
|
|
|
334
334
|
severity?: Severity;
|
|
335
335
|
/**
|
|
336
336
|
* Inject a non-default FS (e.g. memfs in tests). Caveat: `siro.config.{ts,mjs,js}`
|
|
337
|
-
* is
|
|
337
|
+
* is imported from the REAL disk — only the config's existence is probed
|
|
338
338
|
* through this FS. A config that lives solely in an injected FS won't load.
|
|
339
339
|
*/
|
|
340
340
|
fs?: FileSystem;
|
|
@@ -599,7 +599,7 @@ declare class UsageError extends SiroError {
|
|
|
599
599
|
declare const wrapCodecError: <TResult>(filePath: string, fn: () => TResult) => TResult;
|
|
600
600
|
//#endregion
|
|
601
601
|
//#region src/version.d.ts
|
|
602
|
-
declare const version = "0.
|
|
602
|
+
declare const version = "0.2.0";
|
|
603
603
|
//#endregion
|
|
604
604
|
export { type AbsPath, type AdvisoryRuleBinding, type AutoRuleBinding, BUILTIN_REPORTER_NAMES, type BuiltinReporterName, CONFIG_FILES, type CheckStatus, type CodecFor, type ConfigCodec, ConfigError, type ConfigFileRef, type ConfigReadValue, type ConfigScalar, type ConfigValue, type FileSystem, type Finding, type FixKind, type FixOp, type IO, type KeyAssignment, type KeyPath, type LintOptions, type LintResult, type PM, PMS, type PMSignals, PM_SIGNALS, type PackageJson, type ParsedConfig, type RelPath, type RepoContext, type Reporter, type ReporterRegistry, type RequireConfigKeyOptions, type Rule, type RuleBinding, type RuleSetting, SEVERITIES, type Severity, type SiroConfig, SiroError, UsageError, type VersionNote, asAbsPath, asRelPath, createRegistry, defineConfig, detectPMs, exitCodeForLint, filterBySeverity, getByPath, githubReporter, isPM, isReporterShape, isSeverity, jsonReporter, lintCommand, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, renderVersionNoteMessage, requireConfigKey, version, wrapCodecError };
|
|
605
605
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
import{A as
|
|
1
|
+
import { A as asRelPath, C as isPM, D as ConfigError, E as UsageError, O as SiroError, S as SEVERITIES, T as wrapCodecError, _ as jsonReporter, a as PM_SIGNALS, b as isReporterShape, c as requireConfigKey, d as exitCodeForLint, f as filterBySeverity, g as prettyReporter, h as createRegistry, i as nodeFileSystem, k as asAbsPath, l as CONFIG_FILES, m as BUILTIN_REPORTER_NAMES, n as lintCommand, o as overrideBindings, r as detectPMs, s as renderVersionNoteMessage, t as nodeIO, u as getByPath, v as version, w as isSeverity, x as PMS, y as githubReporter } from "./node-io-Cb8nixgE.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/domain/entities/siro-config.ts
|
|
4
|
+
/** Identity helper for type-checked config files. */
|
|
5
|
+
const defineConfig = (config) => config;
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
export { BUILTIN_REPORTER_NAMES, CONFIG_FILES, ConfigError, PMS, PM_SIGNALS, SEVERITIES, SiroError, UsageError, asAbsPath, asRelPath, createRegistry, defineConfig, detectPMs, exitCodeForLint, filterBySeverity, getByPath, githubReporter, isPM, isReporterShape, isSeverity, jsonReporter, lintCommand, nodeFileSystem, nodeIO, overrideBindings, prettyReporter, renderVersionNoteMessage, requireConfigKey, version, wrapCodecError };
|
|
2
9
|
//# sourceMappingURL=index.mjs.map
|