hook-o-gnese 0.0.8 → 0.0.9
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 +32 -9
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -57,7 +57,19 @@ function applyCliRuleOverrides(cfg, overrides) {
|
|
|
57
57
|
}
|
|
58
58
|
//#endregion
|
|
59
59
|
//#region src/formatters/stylish.ts
|
|
60
|
-
const
|
|
60
|
+
const wrap = (open, close) => (s) => `\x1b[${open}m${s}\x1b[${close}m`;
|
|
61
|
+
const red = wrap(31, 39);
|
|
62
|
+
const yellow = wrap(33, 39);
|
|
63
|
+
const bold = wrap(1, 22);
|
|
64
|
+
const dim = wrap(2, 22);
|
|
65
|
+
const underline = wrap(4, 24);
|
|
66
|
+
const identity = (s) => s;
|
|
67
|
+
const stylish = ({ diagnostics, filesScanned, durationMs, color }) => {
|
|
68
|
+
const paintRed = color ? red : identity;
|
|
69
|
+
const paintYellow = color ? yellow : identity;
|
|
70
|
+
const paintBold = color ? bold : identity;
|
|
71
|
+
const paintDim = color ? dim : identity;
|
|
72
|
+
const paintUnderline = color ? underline : identity;
|
|
61
73
|
if (diagnostics.length === 0) return `✓ no problems found (${filesScanned} files, ${durationMs}ms)\n`;
|
|
62
74
|
const byFile = /* @__PURE__ */ new Map();
|
|
63
75
|
for (const d of diagnostics) {
|
|
@@ -66,21 +78,29 @@ const stylish = ({ diagnostics, filesScanned, durationMs }) => {
|
|
|
66
78
|
}
|
|
67
79
|
const lines = [];
|
|
68
80
|
for (const [file, ds] of byFile) {
|
|
69
|
-
lines.push(`\n${file}`);
|
|
81
|
+
lines.push(`\n${paintUnderline(file)}`);
|
|
70
82
|
for (const d of ds) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
83
|
+
const isError = d.severity === "error";
|
|
84
|
+
const sevLabel = isError ? "error" : "warn ";
|
|
85
|
+
const sev = paintBold(isError ? paintRed(sevLabel) : paintYellow(sevLabel));
|
|
86
|
+
const loc = paintDim(`${d.line}:${d.column}`.padEnd(7));
|
|
87
|
+
lines.push(` ${loc} ${sev} ${d.message} ${paintDim(d.rule)}`);
|
|
74
88
|
}
|
|
75
89
|
}
|
|
76
90
|
const errors = diagnostics.filter((d) => d.severity === "error").length;
|
|
77
91
|
const warnings = diagnostics.filter((d) => d.severity === "warn").length;
|
|
78
|
-
|
|
92
|
+
const summary = `${diagnostics.length} problems (${errors} error${errors === 1 ? "" : "s"}, ${warnings} warning${warnings === 1 ? "" : "s"}) in ${filesScanned} files, ${durationMs}ms`;
|
|
93
|
+
const paintSummary = errors > 0 ? paintRed : warnings > 0 ? paintYellow : identity;
|
|
94
|
+
lines.push(`\n${paintBold(paintSummary(summary))}`);
|
|
79
95
|
return lines.join("\n") + "\n";
|
|
80
96
|
};
|
|
81
97
|
//#endregion
|
|
82
98
|
//#region src/formatters/json.ts
|
|
83
|
-
const json = (
|
|
99
|
+
const json = ({ diagnostics, filesScanned, durationMs }) => JSON.stringify({
|
|
100
|
+
diagnostics,
|
|
101
|
+
filesScanned,
|
|
102
|
+
durationMs
|
|
103
|
+
}, null, 2);
|
|
84
104
|
//#endregion
|
|
85
105
|
//#region src/formatters/sarif.ts
|
|
86
106
|
const sarif = ({ diagnostics }) => {
|
|
@@ -182,7 +202,8 @@ async function runCli(opts, io) {
|
|
|
182
202
|
io.writeStdout(formatter({
|
|
183
203
|
diagnostics,
|
|
184
204
|
filesScanned: files.length,
|
|
185
|
-
durationMs
|
|
205
|
+
durationMs,
|
|
206
|
+
color: !!opts.color
|
|
186
207
|
}));
|
|
187
208
|
if (diagnostics.some((d) => d.severity === "error")) return 1;
|
|
188
209
|
return 0;
|
|
@@ -220,13 +241,15 @@ const overrides = (values.rule ?? []).map((spec) => {
|
|
|
220
241
|
severity: sev
|
|
221
242
|
};
|
|
222
243
|
});
|
|
244
|
+
const color = !!process.stdout.isTTY && !process.env.NO_COLOR && values.format === "stylish";
|
|
223
245
|
const code = await runCli({
|
|
224
246
|
paths: positionals,
|
|
225
247
|
format: values.format,
|
|
226
248
|
config: values.config,
|
|
227
249
|
typeAware: !!values["type-aware"],
|
|
228
250
|
ruleOverrides: overrides,
|
|
229
|
-
cwd: process.cwd()
|
|
251
|
+
cwd: process.cwd(),
|
|
252
|
+
color
|
|
230
253
|
}, {
|
|
231
254
|
readTextFile: (p) => readFile(p, "utf-8"),
|
|
232
255
|
writeStdout: (s) => {
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["jsonFmt"],"sources":["../src/config.ts","../src/formatters/stylish.ts","../src/formatters/json.ts","../src/formatters/sarif.ts","../src/formatters/github.ts","../src/cli-core.ts","../src/cli.node.ts"],"sourcesContent":["import type { EngineConfig, Severity } from \"./engine.ts\";\n\nconst DEFAULT_RULES: Record<string, { severity: Severity; options?: unknown }> =\n {\n \"hook-o-gnese/no-fat-effects\": { severity: \"warn\" },\n \"hook-o-gnese/state-scatter\": { severity: \"warn\" },\n \"hook-o-gnese/hook-coupling\": { severity: \"error\" },\n \"hook-o-gnese/custom-hook-depth\": {\n severity: \"warn\",\n options: { maxDepth: 3 },\n },\n };\n\nexport const DEFAULT_IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.next/**\",\n \"**/.cache/**\",\n];\n\ninterface FileConfig {\n rules?: Record<string, Severity | [Severity, unknown]>;\n ignore?: string[];\n typeAware?: boolean;\n}\n\nexport type ReadTextFile = (path: string) => Promise<string>;\n\nexport async function loadConfig(\n cwd: string,\n configPath: string | undefined,\n readTextFile: ReadTextFile,\n): Promise<{ engine: EngineConfig; ignore: string[] }> {\n const candidates = configPath\n ? [configPath]\n : [`${cwd.replace(/\\/$/, \"\")}/.hookogneserc.json`];\n\n let fileCfg: FileConfig = {};\n for (const c of candidates) {\n try {\n const text = await readTextFile(c);\n fileCfg = JSON.parse(text);\n break;\n } catch {\n // not found — fine, use defaults\n }\n }\n\n const rules: EngineConfig[\"rules\"] = { ...DEFAULT_RULES };\n if (fileCfg.rules) {\n for (const [id, spec] of Object.entries(fileCfg.rules)) {\n if (Array.isArray(spec)) {\n rules[id] = { severity: spec[0], options: spec[1] };\n } else {\n rules[id] = { severity: spec };\n }\n }\n }\n\n return {\n engine: {\n rules,\n cwd,\n typeAware: fileCfg.typeAware ?? false,\n },\n ignore: fileCfg.ignore ?? DEFAULT_IGNORE,\n };\n}\n\nexport function applyCliRuleOverrides(\n cfg: EngineConfig,\n overrides: Array<{ id: string; severity: Severity }>,\n): EngineConfig {\n const rules = { ...cfg.rules };\n for (const o of overrides) {\n rules[o.id] = {\n ...(rules[o.id] ?? { severity: \"off\" }),\n severity: o.severity,\n };\n }\n return { ...cfg, rules };\n}\n","import type { Formatter } from \"./types.ts\";\n\nexport const stylish: Formatter = (\n { diagnostics, filesScanned, durationMs },\n) => {\n if (diagnostics.length === 0) {\n return `✓ no problems found (${filesScanned} files, ${durationMs}ms)\\n`;\n }\n const byFile = new Map<string, typeof diagnostics>();\n for (const d of diagnostics) {\n if (!byFile.has(d.file)) byFile.set(d.file, []);\n byFile.get(d.file)!.push(d);\n }\n const lines: string[] = [];\n for (const [file, ds] of byFile) {\n lines.push(`\\n${file}`);\n for (const d of ds) {\n const sev = d.severity === \"error\" ? \"error\" : \"warn \";\n const loc = `${d.line}:${d.column}`.padEnd(7);\n lines.push(` ${loc} ${sev} ${d.message} ${d.rule}`);\n }\n }\n const errors = diagnostics.filter((d) => d.severity === \"error\").length;\n const warnings = diagnostics.filter((d) => d.severity === \"warn\").length;\n lines.push(\n `\\n${diagnostics.length} problems (${errors} error${\n errors === 1 ? \"\" : \"s\"\n }, ${warnings} warning${\n warnings === 1 ? \"\" : \"s\"\n }) in ${filesScanned} files, ${durationMs}ms`,\n );\n return lines.join(\"\\n\") + \"\\n\";\n};\n","import type { Formatter } from \"./types.ts\";\n\nexport const json: Formatter = (ctx) => JSON.stringify(ctx, null, 2);\n","import type { Formatter } from \"./types.ts\";\n\nexport const sarif: Formatter = ({ diagnostics }) => {\n const ruleIds = [...new Set(diagnostics.map((d) => d.rule))];\n return JSON.stringify(\n {\n version: \"2.1.0\",\n $schema:\n \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json\",\n runs: [{\n tool: {\n driver: {\n name: \"hook-o-gnese\",\n informationUri: \"https://github.com/rehoutm/spaghetti-hook-o-gnese\",\n rules: ruleIds.map((id) => ({ id })),\n },\n },\n results: diagnostics.map((d) => ({\n ruleId: d.rule,\n level: d.severity === \"error\" ? \"error\" : \"warning\",\n message: { text: d.message },\n locations: [{\n physicalLocation: {\n artifactLocation: { uri: d.file },\n region: {\n startLine: d.line,\n startColumn: d.column,\n endLine: d.endLine,\n endColumn: d.endColumn,\n },\n },\n }],\n })),\n }],\n },\n null,\n 2,\n );\n};\n","import type { Formatter } from \"./types.ts\";\n\nexport const github: Formatter = ({ diagnostics }) =>\n diagnostics.map((d) => {\n const cmd = d.severity === \"error\" ? \"::error\" : \"::warning\";\n const safe = d.message.replace(/\\r?\\n/g, \" \").replace(/::/g, \":\");\n return `${cmd} file=${d.file},line=${d.line},col=${d.column},title=${d.rule}::${safe}`;\n }).join(\"\\n\") + \"\\n\";\n","import { globby } from \"globby\";\nimport { lintFiles } from \"./engine.ts\";\nimport type { Severity } from \"./engine.ts\";\nimport { applyCliRuleOverrides, DEFAULT_IGNORE, loadConfig } from \"./config.ts\";\nimport { isTypescriptAvailable } from \"./ts-program.ts\";\nimport { stylish } from \"./formatters/stylish.ts\";\nimport { json as jsonFmt } from \"./formatters/json.ts\";\nimport { sarif } from \"./formatters/sarif.ts\";\nimport { github } from \"./formatters/github.ts\";\nimport type { Formatter } from \"./formatters/types.ts\";\n\nconst FORMATTERS: Record<string, Formatter> = {\n stylish,\n json: jsonFmt,\n sarif,\n github,\n};\n\nexport const HELP = `\nhook-o-gnese — score React hook complexity\n\nUsage:\n hook-o-gnese [options] <paths...>\n\nOptions:\n --format=<fmt> stylish (default) | json | sarif | github\n --config=<path> path to .hookogneserc.json\n --type-aware enable custom-hook-depth (slower, uses TS Compiler API).\n Requires 'typescript' installed in your project.\n --rule=<id>=<sev> override rule severity (off|warn|error). Repeatable.\n --help, -h show this message\n\nExamples:\n hook-o-gnese ./src\n hook-o-gnese ./src --format=sarif > report.sarif\n hook-o-gnese ./src --type-aware --rule=hook-o-gnese/state-scatter=error\n`.trim();\n\nexport interface CliOptions {\n paths: string[];\n format: string;\n config?: string;\n typeAware: boolean;\n ruleOverrides: Array<{ id: string; severity: Severity }>;\n cwd: string;\n}\n\nexport interface RuntimeIO {\n readTextFile(path: string): Promise<string>;\n writeStdout(s: string): void;\n writeStderr(s: string): void;\n}\n\nexport async function runCli(opts: CliOptions, io: RuntimeIO): Promise<number> {\n if (opts.paths.length === 0) {\n io.writeStderr(\"Error: no paths provided. Use --help for usage.\\n\");\n return 2;\n }\n\n const formatter = FORMATTERS[opts.format];\n if (!formatter) {\n io.writeStderr(`Error: unknown format '${opts.format}'\\n`);\n return 2;\n }\n\n const { engine, ignore } = await loadConfig(\n opts.cwd,\n opts.config,\n io.readTextFile,\n );\n // CLI flag --type-aware force-enables; config-file `typeAware: true` is\n // honored as-is. Either way, downgrade to false if `typescript` cannot be\n // loaded, so the rule never explodes at create() time.\n if (opts.typeAware) engine.typeAware = true;\n if (engine.typeAware && !isTypescriptAvailable(opts.cwd)) {\n io.writeStderr(\n \"warning: type-aware rules require the 'typescript' package to be \" +\n \"installed in your project. Skipping type-aware rules. \" +\n \"Install with: npm i -D typescript@>=6\\n\",\n );\n engine.typeAware = false;\n }\n const finalEngine = applyCliRuleOverrides(engine, opts.ruleOverrides);\n\n const files = await globby(opts.paths, {\n ignore: [...DEFAULT_IGNORE, ...ignore],\n expandDirectories: { extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"] },\n absolute: false,\n });\n\n if (files.length === 0) {\n io.writeStderr(\"Error: no matching files found\\n\");\n return 2;\n }\n\n const start = performance.now();\n const diagnostics = await lintFiles(files, finalEngine, io.readTextFile);\n const durationMs = Math.round(performance.now() - start);\n\n io.writeStdout(formatter({\n diagnostics,\n filesScanned: files.length,\n durationMs,\n }));\n\n if (diagnostics.some((d) => d.severity === \"error\")) return 1;\n return 0;\n}\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\";\nimport { readFile } from \"node:fs/promises\";\nimport { HELP, runCli } from \"./cli-core.ts\";\nimport type { Severity } from \"./engine.ts\";\n\nconst { values, positionals } = parseArgs({\n args: process.argv.slice(2),\n options: {\n help: { type: \"boolean\", short: \"h\" },\n \"type-aware\": { type: \"boolean\" },\n format: { type: \"string\", default: \"stylish\" },\n config: { type: \"string\" },\n rule: { type: \"string\", multiple: true },\n },\n allowPositionals: true,\n});\n\nif (values.help) {\n console.log(HELP);\n process.exit(0);\n}\n\nconst overrides = (values.rule ?? []).map((spec) => {\n const [id, sev] = spec.split(\"=\");\n return { id, severity: sev as Severity };\n});\n\nconst code = await runCli(\n {\n paths: positionals,\n format: values.format as string,\n config: values.config,\n typeAware: !!values[\"type-aware\"],\n ruleOverrides: overrides,\n cwd: process.cwd(),\n },\n {\n readTextFile: (p) => readFile(p, \"utf-8\"),\n writeStdout: (s) => {\n process.stdout.write(s);\n },\n writeStderr: (s) => {\n process.stderr.write(s);\n },\n },\n);\nprocess.exit(code);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["jsonFmt"],"sources":["../src/config.ts","../src/formatters/stylish.ts","../src/formatters/json.ts","../src/formatters/sarif.ts","../src/formatters/github.ts","../src/cli-core.ts","../src/cli.node.ts"],"sourcesContent":["import type { EngineConfig, Severity } from \"./engine.ts\";\n\nconst DEFAULT_RULES: Record<string, { severity: Severity; options?: unknown }> =\n {\n \"hook-o-gnese/no-fat-effects\": { severity: \"warn\" },\n \"hook-o-gnese/state-scatter\": { severity: \"warn\" },\n \"hook-o-gnese/hook-coupling\": { severity: \"error\" },\n \"hook-o-gnese/custom-hook-depth\": {\n severity: \"warn\",\n options: { maxDepth: 3 },\n },\n };\n\nexport const DEFAULT_IGNORE = [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.next/**\",\n \"**/.cache/**\",\n];\n\ninterface FileConfig {\n rules?: Record<string, Severity | [Severity, unknown]>;\n ignore?: string[];\n typeAware?: boolean;\n}\n\nexport type ReadTextFile = (path: string) => Promise<string>;\n\nexport async function loadConfig(\n cwd: string,\n configPath: string | undefined,\n readTextFile: ReadTextFile,\n): Promise<{ engine: EngineConfig; ignore: string[] }> {\n const candidates = configPath\n ? [configPath]\n : [`${cwd.replace(/\\/$/, \"\")}/.hookogneserc.json`];\n\n let fileCfg: FileConfig = {};\n for (const c of candidates) {\n try {\n const text = await readTextFile(c);\n fileCfg = JSON.parse(text);\n break;\n } catch {\n // not found — fine, use defaults\n }\n }\n\n const rules: EngineConfig[\"rules\"] = { ...DEFAULT_RULES };\n if (fileCfg.rules) {\n for (const [id, spec] of Object.entries(fileCfg.rules)) {\n if (Array.isArray(spec)) {\n rules[id] = { severity: spec[0], options: spec[1] };\n } else {\n rules[id] = { severity: spec };\n }\n }\n }\n\n return {\n engine: {\n rules,\n cwd,\n typeAware: fileCfg.typeAware ?? false,\n },\n ignore: fileCfg.ignore ?? DEFAULT_IGNORE,\n };\n}\n\nexport function applyCliRuleOverrides(\n cfg: EngineConfig,\n overrides: Array<{ id: string; severity: Severity }>,\n): EngineConfig {\n const rules = { ...cfg.rules };\n for (const o of overrides) {\n rules[o.id] = {\n ...(rules[o.id] ?? { severity: \"off\" }),\n severity: o.severity,\n };\n }\n return { ...cfg, rules };\n}\n","import type { Formatter } from \"./types.ts\";\n\nconst wrap = (open: number, close: number) => (s: string) =>\n `\\x1b[${open}m${s}\\x1b[${close}m`;\n\nconst red = wrap(31, 39);\nconst yellow = wrap(33, 39);\nconst bold = wrap(1, 22);\nconst dim = wrap(2, 22);\nconst underline = wrap(4, 24);\nconst identity = (s: string) => s;\n\nexport const stylish: Formatter = (\n { diagnostics, filesScanned, durationMs, color },\n) => {\n const paintRed = color ? red : identity;\n const paintYellow = color ? yellow : identity;\n const paintBold = color ? bold : identity;\n const paintDim = color ? dim : identity;\n const paintUnderline = color ? underline : identity;\n\n if (diagnostics.length === 0) {\n return `✓ no problems found (${filesScanned} files, ${durationMs}ms)\\n`;\n }\n const byFile = new Map<string, typeof diagnostics>();\n for (const d of diagnostics) {\n if (!byFile.has(d.file)) byFile.set(d.file, []);\n byFile.get(d.file)!.push(d);\n }\n const lines: string[] = [];\n for (const [file, ds] of byFile) {\n lines.push(`\\n${paintUnderline(file)}`);\n for (const d of ds) {\n const isError = d.severity === \"error\";\n const sevLabel = isError ? \"error\" : \"warn \";\n const sev = paintBold(\n isError ? paintRed(sevLabel) : paintYellow(sevLabel),\n );\n const loc = paintDim(`${d.line}:${d.column}`.padEnd(7));\n lines.push(` ${loc} ${sev} ${d.message} ${paintDim(d.rule)}`);\n }\n }\n const errors = diagnostics.filter((d) => d.severity === \"error\").length;\n const warnings = diagnostics.filter((d) => d.severity === \"warn\").length;\n const summary = `${diagnostics.length} problems (${errors} error${\n errors === 1 ? \"\" : \"s\"\n }, ${warnings} warning${\n warnings === 1 ? \"\" : \"s\"\n }) in ${filesScanned} files, ${durationMs}ms`;\n const paintSummary = errors > 0\n ? paintRed\n : warnings > 0\n ? paintYellow\n : identity;\n lines.push(`\\n${paintBold(paintSummary(summary))}`);\n return lines.join(\"\\n\") + \"\\n\";\n};\n","import type { Formatter } from \"./types.ts\";\n\nexport const json: Formatter = ({ diagnostics, filesScanned, durationMs }) =>\n JSON.stringify({ diagnostics, filesScanned, durationMs }, null, 2);\n","import type { Formatter } from \"./types.ts\";\n\nexport const sarif: Formatter = ({ diagnostics }) => {\n const ruleIds = [...new Set(diagnostics.map((d) => d.rule))];\n return JSON.stringify(\n {\n version: \"2.1.0\",\n $schema:\n \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json\",\n runs: [{\n tool: {\n driver: {\n name: \"hook-o-gnese\",\n informationUri: \"https://github.com/rehoutm/spaghetti-hook-o-gnese\",\n rules: ruleIds.map((id) => ({ id })),\n },\n },\n results: diagnostics.map((d) => ({\n ruleId: d.rule,\n level: d.severity === \"error\" ? \"error\" : \"warning\",\n message: { text: d.message },\n locations: [{\n physicalLocation: {\n artifactLocation: { uri: d.file },\n region: {\n startLine: d.line,\n startColumn: d.column,\n endLine: d.endLine,\n endColumn: d.endColumn,\n },\n },\n }],\n })),\n }],\n },\n null,\n 2,\n );\n};\n","import type { Formatter } from \"./types.ts\";\n\nexport const github: Formatter = ({ diagnostics }) =>\n diagnostics.map((d) => {\n const cmd = d.severity === \"error\" ? \"::error\" : \"::warning\";\n const safe = d.message.replace(/\\r?\\n/g, \" \").replace(/::/g, \":\");\n return `${cmd} file=${d.file},line=${d.line},col=${d.column},title=${d.rule}::${safe}`;\n }).join(\"\\n\") + \"\\n\";\n","import { globby } from \"globby\";\nimport { lintFiles } from \"./engine.ts\";\nimport type { Severity } from \"./engine.ts\";\nimport { applyCliRuleOverrides, DEFAULT_IGNORE, loadConfig } from \"./config.ts\";\nimport { isTypescriptAvailable } from \"./ts-program.ts\";\nimport { stylish } from \"./formatters/stylish.ts\";\nimport { json as jsonFmt } from \"./formatters/json.ts\";\nimport { sarif } from \"./formatters/sarif.ts\";\nimport { github } from \"./formatters/github.ts\";\nimport type { Formatter } from \"./formatters/types.ts\";\n\nconst FORMATTERS: Record<string, Formatter> = {\n stylish,\n json: jsonFmt,\n sarif,\n github,\n};\n\nexport const HELP = `\nhook-o-gnese — score React hook complexity\n\nUsage:\n hook-o-gnese [options] <paths...>\n\nOptions:\n --format=<fmt> stylish (default) | json | sarif | github\n --config=<path> path to .hookogneserc.json\n --type-aware enable custom-hook-depth (slower, uses TS Compiler API).\n Requires 'typescript' installed in your project.\n --rule=<id>=<sev> override rule severity (off|warn|error). Repeatable.\n --help, -h show this message\n\nExamples:\n hook-o-gnese ./src\n hook-o-gnese ./src --format=sarif > report.sarif\n hook-o-gnese ./src --type-aware --rule=hook-o-gnese/state-scatter=error\n`.trim();\n\nexport interface CliOptions {\n paths: string[];\n format: string;\n config?: string;\n typeAware: boolean;\n ruleOverrides: Array<{ id: string; severity: Severity }>;\n cwd: string;\n color?: boolean;\n}\n\nexport interface RuntimeIO {\n readTextFile(path: string): Promise<string>;\n writeStdout(s: string): void;\n writeStderr(s: string): void;\n}\n\nexport async function runCli(opts: CliOptions, io: RuntimeIO): Promise<number> {\n if (opts.paths.length === 0) {\n io.writeStderr(\"Error: no paths provided. Use --help for usage.\\n\");\n return 2;\n }\n\n const formatter = FORMATTERS[opts.format];\n if (!formatter) {\n io.writeStderr(`Error: unknown format '${opts.format}'\\n`);\n return 2;\n }\n\n const { engine, ignore } = await loadConfig(\n opts.cwd,\n opts.config,\n io.readTextFile,\n );\n // CLI flag --type-aware force-enables; config-file `typeAware: true` is\n // honored as-is. Either way, downgrade to false if `typescript` cannot be\n // loaded, so the rule never explodes at create() time.\n if (opts.typeAware) engine.typeAware = true;\n if (engine.typeAware && !isTypescriptAvailable(opts.cwd)) {\n io.writeStderr(\n \"warning: type-aware rules require the 'typescript' package to be \" +\n \"installed in your project. Skipping type-aware rules. \" +\n \"Install with: npm i -D typescript@>=6\\n\",\n );\n engine.typeAware = false;\n }\n const finalEngine = applyCliRuleOverrides(engine, opts.ruleOverrides);\n\n const files = await globby(opts.paths, {\n ignore: [...DEFAULT_IGNORE, ...ignore],\n expandDirectories: { extensions: [\"ts\", \"tsx\", \"js\", \"jsx\"] },\n absolute: false,\n });\n\n if (files.length === 0) {\n io.writeStderr(\"Error: no matching files found\\n\");\n return 2;\n }\n\n const start = performance.now();\n const diagnostics = await lintFiles(files, finalEngine, io.readTextFile);\n const durationMs = Math.round(performance.now() - start);\n\n io.writeStdout(formatter({\n diagnostics,\n filesScanned: files.length,\n durationMs,\n color: !!opts.color,\n }));\n\n if (diagnostics.some((d) => d.severity === \"error\")) return 1;\n return 0;\n}\n","#!/usr/bin/env node\nimport { parseArgs } from \"node:util\";\nimport { readFile } from \"node:fs/promises\";\nimport { HELP, runCli } from \"./cli-core.ts\";\nimport type { Severity } from \"./engine.ts\";\n\nconst { values, positionals } = parseArgs({\n args: process.argv.slice(2),\n options: {\n help: { type: \"boolean\", short: \"h\" },\n \"type-aware\": { type: \"boolean\" },\n format: { type: \"string\", default: \"stylish\" },\n config: { type: \"string\" },\n rule: { type: \"string\", multiple: true },\n },\n allowPositionals: true,\n});\n\nif (values.help) {\n console.log(HELP);\n process.exit(0);\n}\n\nconst overrides = (values.rule ?? []).map((spec) => {\n const [id, sev] = spec.split(\"=\");\n return { id, severity: sev as Severity };\n});\n\nconst color = !!process.stdout.isTTY && !process.env.NO_COLOR &&\n values.format === \"stylish\";\nconst code = await runCli(\n {\n paths: positionals,\n format: values.format as string,\n config: values.config,\n typeAware: !!values[\"type-aware\"],\n ruleOverrides: overrides,\n cwd: process.cwd(),\n color,\n },\n {\n readTextFile: (p) => readFile(p, \"utf-8\"),\n writeStdout: (s) => {\n process.stdout.write(s);\n },\n writeStderr: (s) => {\n process.stderr.write(s);\n },\n },\n);\nprocess.exit(code);\n"],"mappings":"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"}
|