@wirecat/cli-base 0.21.0 → 0.23.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/README.md +16 -2
- package/dist/commands/document.d.ts +72 -0
- package/dist/commands/document.d.ts.map +1 -0
- package/dist/commands/document.js +71 -0
- package/dist/commands/document.js.map +1 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +23 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/deadline.js +4 -4
- package/dist/deadline.js.map +1 -1
- package/dist/dev/bin.js +0 -0
- package/dist/dev/lint.d.ts +33 -0
- package/dist/dev/lint.d.ts.map +1 -0
- package/dist/dev/lint.js +257 -0
- package/dist/dev/lint.js.map +1 -0
- package/dist/dev/main.d.ts.map +1 -1
- package/dist/dev/main.js +132 -2
- package/dist/dev/main.js.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -1
- package/dist/errors.js.map +1 -1
- package/dist/failure.d.ts +76 -0
- package/dist/failure.d.ts.map +1 -0
- package/dist/failure.js +150 -0
- package/dist/failure.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/logging.d.ts +17 -15
- package/dist/logging.d.ts.map +1 -1
- package/dist/logging.js +49 -28
- package/dist/logging.js.map +1 -1
- package/dist/output-buffer.js +4 -4
- package/dist/output-buffer.js.map +1 -1
- package/dist/standards/errors.d.ts +51 -0
- package/dist/standards/errors.d.ts.map +1 -0
- package/dist/standards/errors.js +82 -0
- package/dist/standards/errors.js.map +1 -0
- package/dist/standards/index.d.ts +8 -0
- package/dist/standards/index.d.ts.map +1 -0
- package/dist/standards/index.js +8 -0
- package/dist/standards/index.js.map +1 -0
- package/dist/standards/lint.d.ts +29 -0
- package/dist/standards/lint.d.ts.map +1 -0
- package/dist/standards/lint.js +62 -0
- package/dist/standards/lint.js.map +1 -0
- package/dist/standards/log.d.ts +21 -0
- package/dist/standards/log.d.ts.map +1 -0
- package/dist/standards/log.js +18 -0
- package/dist/standards/log.js.map +1 -0
- package/dist/standards/output.d.ts +126 -0
- package/dist/standards/output.d.ts.map +1 -0
- package/dist/standards/output.js +54 -0
- package/dist/standards/output.js.map +1 -0
- package/dist/standards/registry.d.ts +18 -0
- package/dist/standards/registry.d.ts.map +1 -0
- package/dist/standards/registry.js +41 -0
- package/dist/standards/registry.js.map +1 -0
- package/dist/standards/steps.d.ts +28 -0
- package/dist/standards/steps.d.ts.map +1 -0
- package/dist/standards/steps.js +56 -0
- package/dist/standards/steps.js.map +1 -0
- package/dist/standards/vocabulary.d.ts +40 -0
- package/dist/standards/vocabulary.d.ts.map +1 -0
- package/dist/standards/vocabulary.js +255 -0
- package/dist/standards/vocabulary.js.map +1 -0
- package/package.json +27 -20
package/README.md
CHANGED
|
@@ -46,16 +46,17 @@ streams.stderr // []
|
|
|
46
46
|
| `paths` | config, state and cache in the XDG layout on Linux and macOS (`~/.config`, `~/.local/share`, `~/.cache`) and the `AppData` one on Windows, each overridable by environment variable; `migrateLegacyMacPaths` moves an older release's `~/Library` folders once |
|
|
47
47
|
| `config` | JSON config loading that names the bad field, and an atomic write that locks the directory down |
|
|
48
48
|
| `credentials` | environment → keyring → file, warning once and falling through when the keyring refuses |
|
|
49
|
-
| `logging` |
|
|
49
|
+
| `logging` | `createRecordLog`: one `LogRecord` per JSON line (OpenTelemetry field names, flat), secrets redacted by field name at any depth; a file that cannot be written is reported to `onError`, never thrown |
|
|
50
50
|
| `retry` | full-jitter backoff, and the distinction between "no answer came" and "safe to repeat" |
|
|
51
51
|
| `/testing` | `captureStreams`, `memoryKeyring`, `brokenKeyring`, `fakeClock` |
|
|
52
|
-
| `/commands` | the command registry: `describeProgram`, `annotate`, `flatten` — see below |
|
|
52
|
+
| `/commands` | the command registry: `describeProgram`, `annotate`, `flatten`, `refuseJoinedPath`, and `commandsDocument` — the one `commands --json` shape, with its schema — see below |
|
|
53
53
|
| `/completion` | shell completion over the registry: `suggest`, `formatSuggestions` — see below |
|
|
54
54
|
| `/update` | keeping an install current: `installerOf`, `updateCommand`, `latestVersion`, `mayNotify`, `updateNotice`, `runUpdate` — see below |
|
|
55
55
|
| `/codegen` | build time: an official API description → types, Valibot schemas, an operation manifest and a coverage page — see below |
|
|
56
56
|
| `/release` | release time: `releaseCheck` and the checks it runs — changelog shape, links, package contents, version in step — see below |
|
|
57
57
|
| `/skill` | the CLI's SKILL.md for coding agents: `skillCommand` (`show`, `install`), `skillHint`, `skillResource` — see below |
|
|
58
58
|
| `/mcp` | local stdio MCP setup for Codex and Claude Code, plus a read-only handshake and tool-list probe |
|
|
59
|
+
| `/standards` | the registries the standard is made of — vocabulary, output envelope, error object and reasons, next steps, log record, lint rules — as Valibot schemas that also emit JSON Schema 2020-12 |
|
|
59
60
|
|
|
60
61
|
**Nothing in the root export is HTTP.** Status classification, `Retry-After` parsing and the fetch
|
|
61
62
|
seam live in `@wirecat/cli-base/http`, so a CLI that speaks a socket never depends on a stack it
|
|
@@ -112,6 +113,11 @@ Each option says whether it `takesValue` and, separately, whether it is `mandato
|
|
|
112
113
|
Commander's `required`, which means "takes a value when given" — plus its choices, default,
|
|
113
114
|
environment variable, the options it `conflicts` with and the values it `implies`.
|
|
114
115
|
|
|
116
|
+
`refuseJoinedPath(program, args)` throws a `validation_error` when a command path arrived as one
|
|
117
|
+
argument (`"projects add" --help`, what a zsh loop over `$c` passes), naming the words to type.
|
|
118
|
+
Commander would print the root help instead; call it on the words before `parseAsync`, after a
|
|
119
|
+
leading profile word if the CLI takes one.
|
|
120
|
+
|
|
115
121
|
**Shell completion is `@wirecat/cli-base/completion`**, built on the registry. `suggest` takes the
|
|
116
122
|
words typed so far and answers what may come next: a command, an action, an option, one of its
|
|
117
123
|
values, or an argument's values from a source the CLI hands in — a local cache, never the network,
|
|
@@ -304,6 +310,14 @@ extends:
|
|
|
304
310
|
| `scripts/docs-check.ts` | `cli-dev docs-check --rules scripts/release/checks.ts`, or `--ids CLI,MAX` with the English defaults |
|
|
305
311
|
| `scripts/test-matrix.ts` | `cli-dev test-matrix --program dist/program.js --untested scripts/test-matrix-untested.ts --name max [--check]` |
|
|
306
312
|
|
|
313
|
+
| — | `cli-dev lint --cli dist/bin.js` (or `--commands <file>`): the command tree against the standard |
|
|
314
|
+
|
|
315
|
+
`cli-dev lint` reads `<cli> commands --json`, refuses a document not in `commandsDocument`'s shape, and
|
|
316
|
+
judges it by `LINT_RULES` and `VOCABULARY` from `/standards`. Known violations live in
|
|
317
|
+
`lint/baseline/<rule-id>.json`, one file per rule, and may only shrink: an entry with no violation left
|
|
318
|
+
fails the run. `--write-baseline` rewrites those files to today's violations. Permanent exceptions go in
|
|
319
|
+
`lint/overrides.json` — `[{ "rule", "target", "reason" }]` — and more than `--budget` (10) of them fails.
|
|
320
|
+
|
|
307
321
|
`--rules` names a module exporting `CHANGELOG` and `docsRules(root)`; `--program` one exporting
|
|
308
322
|
`createProgram`; `--untested` one exporting `UNTESTED`. A TypeScript module loads on Node 24 as it is.
|
|
309
323
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import * as v from "valibot";
|
|
3
|
+
import { type CommandInfo, type OptionInfo } from "./index.js";
|
|
4
|
+
/**
|
|
5
|
+
* What `<cli> commands --json` prints, in every CLI on cli-base. `scope` and `inheritedOptions` are
|
|
6
|
+
* there only when one command path was asked for. Nothing else at the top: `cli-dev lint` refuses it.
|
|
7
|
+
*/
|
|
8
|
+
export declare const commandsDocumentSchema: v.StrictObjectSchema<{
|
|
9
|
+
readonly cli: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
|
|
10
|
+
readonly version: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.NonEmptyAction<string, undefined>]>;
|
|
11
|
+
/** The major version of the JSON the CLI's commands print. */
|
|
12
|
+
readonly contract: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
13
|
+
readonly description: v.StringSchema<undefined>;
|
|
14
|
+
readonly globalOptions: v.ArraySchema<v.ObjectSchema<{
|
|
15
|
+
readonly flags: v.StringSchema<undefined>;
|
|
16
|
+
readonly description: v.StringSchema<undefined>;
|
|
17
|
+
readonly takesValue: v.BooleanSchema<undefined>;
|
|
18
|
+
readonly mandatory: v.BooleanSchema<undefined>;
|
|
19
|
+
readonly choices: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
20
|
+
readonly default: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
21
|
+
readonly env: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
22
|
+
readonly conflicts: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
23
|
+
readonly implies: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
|
|
24
|
+
}, undefined>, undefined>;
|
|
25
|
+
readonly scope: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
26
|
+
readonly inheritedOptions: v.OptionalSchema<v.ArraySchema<v.ObjectSchema<{
|
|
27
|
+
readonly path: v.ArraySchema<v.StringSchema<undefined>, undefined>;
|
|
28
|
+
readonly options: v.ArraySchema<v.ObjectSchema<{
|
|
29
|
+
readonly flags: v.StringSchema<undefined>;
|
|
30
|
+
readonly description: v.StringSchema<undefined>;
|
|
31
|
+
readonly takesValue: v.BooleanSchema<undefined>;
|
|
32
|
+
readonly mandatory: v.BooleanSchema<undefined>;
|
|
33
|
+
readonly choices: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
34
|
+
readonly default: v.OptionalSchema<v.UnknownSchema, undefined>;
|
|
35
|
+
readonly env: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
36
|
+
readonly conflicts: v.OptionalSchema<v.ArraySchema<v.StringSchema<undefined>, undefined>, undefined>;
|
|
37
|
+
readonly implies: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnknownSchema, undefined>, undefined>;
|
|
38
|
+
}, undefined>, undefined>;
|
|
39
|
+
}, undefined>, undefined>, undefined>;
|
|
40
|
+
readonly commands: v.ArraySchema<v.GenericSchema<CommandInfo>, undefined>;
|
|
41
|
+
readonly exitCodes: v.RecordSchema<v.StringSchema<undefined>, v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>]>, undefined>;
|
|
42
|
+
}, undefined>;
|
|
43
|
+
export interface CommandsDocument {
|
|
44
|
+
cli: string;
|
|
45
|
+
version: string;
|
|
46
|
+
contract: number;
|
|
47
|
+
description: string;
|
|
48
|
+
globalOptions: OptionInfo[];
|
|
49
|
+
scope?: string[];
|
|
50
|
+
inheritedOptions?: {
|
|
51
|
+
path: string[];
|
|
52
|
+
options: OptionInfo[];
|
|
53
|
+
}[];
|
|
54
|
+
commands: CommandInfo[];
|
|
55
|
+
exitCodes: Record<string, number>;
|
|
56
|
+
}
|
|
57
|
+
export declare const commandsDocumentJsonSchema: () => import("@valibot/to-json-schema").JsonSchema;
|
|
58
|
+
export declare const parseCommandsDocument: (value: unknown) => {
|
|
59
|
+
document: CommandsDocument;
|
|
60
|
+
problems?: undefined;
|
|
61
|
+
} | {
|
|
62
|
+
document?: undefined;
|
|
63
|
+
problems: string[];
|
|
64
|
+
};
|
|
65
|
+
export interface CommandsDocumentAbout {
|
|
66
|
+
cli: string;
|
|
67
|
+
version: string;
|
|
68
|
+
contract: number;
|
|
69
|
+
}
|
|
70
|
+
/** The whole tree; a CLI that answers `commands <path>` adds `scope` and `inheritedOptions` itself. */
|
|
71
|
+
export declare const commandsDocument: (root: Command, about: CommandsDocumentAbout) => CommandsDocument;
|
|
72
|
+
//# sourceMappingURL=document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/commands/document.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAG5B,OAAO,EAAE,KAAK,WAAW,EAAoC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AAwChG;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;IAGjC,8DAA8D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAQ9D,CAAA;AAEF,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,UAAU,EAAE,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,gBAAgB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,UAAU,EAAE,CAAA;KAAE,EAAE,CAAA;IAC9D,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC;AAED,eAAO,MAAM,0BAA0B,oDAA6E,CAAA;AAEpH,eAAO,MAAM,qBAAqB,UACzB,OAAO,KACb;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAGnG,CAAA;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,uGAAuG;AACvG,eAAO,MAAM,gBAAgB,SAAU,OAAO,SAAS,qBAAqB,KAAG,gBAQ7E,CAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
import { EXIT_CODES, GENERIC_FAILURE } from "../exit-codes.js";
|
|
3
|
+
import { issuesOf, jsonSchemaOf } from "../standards/registry.js";
|
|
4
|
+
import { describeOptions, describeProgram } from "./index.js";
|
|
5
|
+
const argumentInfoSchema = v.object({
|
|
6
|
+
name: v.string(),
|
|
7
|
+
required: v.boolean(),
|
|
8
|
+
variadic: v.boolean(),
|
|
9
|
+
description: v.string(),
|
|
10
|
+
choices: v.optional(v.array(v.string())),
|
|
11
|
+
default: v.optional(v.unknown()),
|
|
12
|
+
});
|
|
13
|
+
const optionInfoSchema = v.object({
|
|
14
|
+
flags: v.string(),
|
|
15
|
+
description: v.string(),
|
|
16
|
+
takesValue: v.boolean(),
|
|
17
|
+
mandatory: v.boolean(),
|
|
18
|
+
choices: v.optional(v.array(v.string())),
|
|
19
|
+
default: v.optional(v.unknown()),
|
|
20
|
+
env: v.optional(v.string()),
|
|
21
|
+
conflicts: v.optional(v.array(v.string())),
|
|
22
|
+
implies: v.optional(v.record(v.string(), v.unknown())),
|
|
23
|
+
});
|
|
24
|
+
const commandInfoSchema = v.object({
|
|
25
|
+
path: v.array(v.string()),
|
|
26
|
+
name: v.string(),
|
|
27
|
+
summary: v.optional(v.string()),
|
|
28
|
+
description: v.string(),
|
|
29
|
+
usage: v.string(),
|
|
30
|
+
origin: v.picklist(["handwritten", "generated"]),
|
|
31
|
+
operationId: v.optional(v.string()),
|
|
32
|
+
mutates: v.optional(v.boolean()),
|
|
33
|
+
local: v.optional(v.boolean()),
|
|
34
|
+
state: v.optional(v.picklist(["beta", "deprecated"])),
|
|
35
|
+
examples: v.optional(v.array(v.string())),
|
|
36
|
+
arguments: v.array(argumentInfoSchema),
|
|
37
|
+
options: v.array(optionInfoSchema),
|
|
38
|
+
commands: v.array(v.lazy(() => commandInfoSchema)),
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* What `<cli> commands --json` prints, in every CLI on cli-base. `scope` and `inheritedOptions` are
|
|
42
|
+
* there only when one command path was asked for. Nothing else at the top: `cli-dev lint` refuses it.
|
|
43
|
+
*/
|
|
44
|
+
export const commandsDocumentSchema = v.strictObject({
|
|
45
|
+
cli: v.pipe(v.string(), v.nonEmpty()),
|
|
46
|
+
version: v.pipe(v.string(), v.nonEmpty()),
|
|
47
|
+
/** The major version of the JSON the CLI's commands print. */
|
|
48
|
+
contract: v.pipe(v.number(), v.integer(), v.minValue(0)),
|
|
49
|
+
description: v.string(),
|
|
50
|
+
globalOptions: v.array(optionInfoSchema),
|
|
51
|
+
scope: v.optional(v.array(v.string())),
|
|
52
|
+
inheritedOptions: v.optional(v.array(v.object({ path: v.array(v.string()), options: v.array(optionInfoSchema) }))),
|
|
53
|
+
commands: v.array(commandInfoSchema),
|
|
54
|
+
exitCodes: v.record(v.string(), v.pipe(v.number(), v.integer())),
|
|
55
|
+
});
|
|
56
|
+
export const commandsDocumentJsonSchema = () => jsonSchemaOf(commandsDocumentSchema, { command: commandInfoSchema });
|
|
57
|
+
export const parseCommandsDocument = (value) => {
|
|
58
|
+
const parsed = v.safeParse(commandsDocumentSchema, value);
|
|
59
|
+
return parsed.success ? { document: parsed.output } : { problems: issuesOf(parsed.issues) };
|
|
60
|
+
};
|
|
61
|
+
/** The whole tree; a CLI that answers `commands <path>` adds `scope` and `inheritedOptions` itself. */
|
|
62
|
+
export const commandsDocument = (root, about) => ({
|
|
63
|
+
cli: about.cli,
|
|
64
|
+
version: about.version,
|
|
65
|
+
contract: about.contract,
|
|
66
|
+
description: root.description(),
|
|
67
|
+
globalOptions: describeOptions(root),
|
|
68
|
+
commands: describeProgram(root),
|
|
69
|
+
exitCodes: { ok: 0, generic_failure: GENERIC_FAILURE, ...EXIT_CODES },
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/commands/document.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -72,5 +72,12 @@ export declare const metaOf: (command: Command) => CommandMeta;
|
|
|
72
72
|
export declare const describeProgram: (root: Command) => CommandInfo[];
|
|
73
73
|
export declare const describeOptions: (command: Command) => OptionInfo[];
|
|
74
74
|
export declare const flatten: (commands: readonly CommandInfo[]) => CommandInfo[];
|
|
75
|
+
export { type CommandsDocument, type CommandsDocumentAbout, commandsDocument, commandsDocumentJsonSchema, commandsDocumentSchema, parseCommandsDocument, } from "./document.js";
|
|
75
76
|
export { COMMANDS_PAGE_LABELS, type CommandsPage, type CommandsPageLabels, type CommandsPageText, commandsPage, } from "./page.js";
|
|
77
|
+
/**
|
|
78
|
+
* Refuses a command path that arrived as one argument — `"projects add" --help`, what a zsh loop over
|
|
79
|
+
* `$c` passes. Commander settles `--help` for an unknown word before any hook runs and prints the root
|
|
80
|
+
* help, so the words are checked before parsing. Pass the words after a leading profile, if any.
|
|
81
|
+
*/
|
|
82
|
+
export declare const refuseJoinedPath: (root: Command, args: readonly string[]) => void;
|
|
76
83
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAY,OAAO,EAAU,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CliError } from "../errors.js";
|
|
1
2
|
const labels = new WeakMap();
|
|
2
3
|
/** Attaches what the tree cannot say. Repeated calls merge; the command is returned for chaining. */
|
|
3
4
|
export const annotate = (command, meta) => {
|
|
@@ -58,5 +59,27 @@ const describeOption = (option) => {
|
|
|
58
59
|
...(implied ? { implies: implied } : {}),
|
|
59
60
|
};
|
|
60
61
|
};
|
|
62
|
+
export { commandsDocument, commandsDocumentJsonSchema, commandsDocumentSchema, parseCommandsDocument, } from "./document.js";
|
|
61
63
|
export { COMMANDS_PAGE_LABELS, commandsPage, } from "./page.js";
|
|
64
|
+
/**
|
|
65
|
+
* Refuses a command path that arrived as one argument — `"projects add" --help`, what a zsh loop over
|
|
66
|
+
* `$c` passes. Commander settles `--help` for an unknown word before any hook runs and prints the root
|
|
67
|
+
* help, so the words are checked before parsing. Pass the words after a leading profile, if any.
|
|
68
|
+
*/
|
|
69
|
+
export const refuseJoinedPath = (root, args) => {
|
|
70
|
+
let command = root;
|
|
71
|
+
const path = [];
|
|
72
|
+
for (const arg of args) {
|
|
73
|
+
if (arg.startsWith("-"))
|
|
74
|
+
return;
|
|
75
|
+
const [first = "", ...rest] = arg.trim().split(/\s+/);
|
|
76
|
+
const next = command.commands.find((child) => child.name() === first || child.aliases().includes(first));
|
|
77
|
+
if (next === undefined)
|
|
78
|
+
return;
|
|
79
|
+
if (rest.length > 0)
|
|
80
|
+
throw new CliError("validation_error", `"${arg}" was given as one argument; run: ${[root.name(), ...path, first, ...rest].join(" ")}`);
|
|
81
|
+
path.push(first);
|
|
82
|
+
command = next;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
62
85
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"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"}
|
package/dist/deadline.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CliError } from "./errors.js";
|
|
1
|
+
import { CliError, isCliError } from "./errors.js";
|
|
2
2
|
import { realSleep } from "./time.js";
|
|
3
3
|
export const createDeadline = ({ timeoutMs, signal, sleep = realSleep } = {}) => {
|
|
4
4
|
if (timeoutMs !== undefined && (!Number.isSafeInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > 2147483647))
|
|
@@ -13,13 +13,13 @@ export const createDeadline = ({ timeoutMs, signal, sleep = realSleep } = {}) =>
|
|
|
13
13
|
timer.abort();
|
|
14
14
|
signal?.removeEventListener("abort", onAbort);
|
|
15
15
|
};
|
|
16
|
-
const onAbort = () => abort(signal?.reason
|
|
16
|
+
const onAbort = () => abort(isCliError(signal?.reason) ? signal.reason : new CliError("cancelled", "command cancelled"));
|
|
17
17
|
if (signal?.aborted)
|
|
18
18
|
onAbort();
|
|
19
19
|
else
|
|
20
20
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
21
21
|
const expire = () => abort(new CliError("timeout", "the command did not finish before its deadline", {
|
|
22
|
-
reason: "
|
|
22
|
+
reason: "command.timed-out",
|
|
23
23
|
timeoutMs,
|
|
24
24
|
retryable: false,
|
|
25
25
|
}));
|
|
@@ -68,7 +68,7 @@ export const createDeadline = ({ timeoutMs, signal, sleep = realSleep } = {}) =>
|
|
|
68
68
|
await running.catch((reason) => {
|
|
69
69
|
settled = reason;
|
|
70
70
|
});
|
|
71
|
-
if (settled
|
|
71
|
+
if (isCliError(settled) && settled.code === "outcome_unknown")
|
|
72
72
|
throw settled;
|
|
73
73
|
throw controller.signal.reason;
|
|
74
74
|
}
|
package/dist/deadline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deadline.js","sourceRoot":"","sources":["../src/deadline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"deadline.js","sourceRoot":"","sources":["../src/deadline.ts"],"names":[],"mappings":"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"}
|
package/dist/dev/bin.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CommandsDocument } from "../commands/document.js";
|
|
2
|
+
import { type LintRule, type Override } from "../standards/lint.js";
|
|
3
|
+
import type { Vocabulary } from "../standards/vocabulary.js";
|
|
4
|
+
export interface Violation {
|
|
5
|
+
rule: string;
|
|
6
|
+
/** What the baseline and overrides name: `chats list`, `chats show <chat>`, `chats list --limit`, `--until`. */
|
|
7
|
+
target: string;
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
/** Splits an example the way a shell would for the quoting examples use: '…', "…" and a backslash. */
|
|
11
|
+
export declare const exampleWords: (line: string) => string[];
|
|
12
|
+
export declare const lintRuleIds: () => string[];
|
|
13
|
+
/** Every violation, one per rule and target, sorted. Generated commands keep the names of the API they mirror. */
|
|
14
|
+
export declare const lintCommands: (document: CommandsDocument, vocabulary: Vocabulary, rules?: readonly LintRule[]) => Violation[];
|
|
15
|
+
export interface Judgement {
|
|
16
|
+
/** Not in the baseline and not overridden: these fail the run. */
|
|
17
|
+
fresh: Violation[];
|
|
18
|
+
baselined: number;
|
|
19
|
+
overridden: number;
|
|
20
|
+
/** Baseline or override entries with no violation left: the list only shrinks, so these fail too. */
|
|
21
|
+
stale: {
|
|
22
|
+
rule: string;
|
|
23
|
+
target: string;
|
|
24
|
+
in: "baseline" | "overrides";
|
|
25
|
+
}[];
|
|
26
|
+
/** Rule ids named by a baseline file or an override that no rule has. */
|
|
27
|
+
unknownRules: string[];
|
|
28
|
+
overBudget: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare const judge: (violations: readonly Violation[], baseline: Readonly<Record<string, readonly string[]>>, overrides: readonly Override[], budget: number, rules?: readonly LintRule[]) => Judgement;
|
|
31
|
+
/** What `--write-baseline` writes: per rule, the targets no override covers. A rule with none gets no file. */
|
|
32
|
+
export declare const baselineOf: (violations: readonly Violation[], overrides: readonly Override[], rules?: readonly LintRule[]) => Record<string, string[]>;
|
|
33
|
+
//# sourceMappingURL=lint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/dev/lint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAA;AAE5D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,gHAAgH;IAChH,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB;AA+BD,sGAAsG;AACtG,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,MAAM,EAM/C,CAAA;AAmMH,eAAO,MAAM,WAAW,QAAO,MAAM,EAAyB,CAAA;AAE9D,kHAAkH;AAClH,eAAO,MAAM,YAAY,aACb,gBAAgB,cACd,UAAU,UACf,SAAS,QAAQ,EAAE,KACzB,SAAS,EAUX,CAAA;AAED,MAAM,WAAW,SAAS;IACxB,kEAAkE;IAClE,KAAK,EAAE,SAAS,EAAE,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,qGAAqG;IACrG,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,UAAU,GAAG,WAAW,CAAA;KAAE,EAAE,CAAA;IACvE,yEAAyE;IACzE,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,eAAO,MAAM,KAAK,eACJ,SAAS,SAAS,EAAE,YACtB,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,aAC1C,SAAS,QAAQ,EAAE,UACtB,MAAM,UACP,SAAS,QAAQ,EAAE,KACzB,SA4BF,CAAA;AAED,+GAA+G;AAC/G,eAAO,MAAM,UAAU,eACT,SAAS,SAAS,EAAE,aACrB,SAAS,QAAQ,EAAE,UACvB,SAAS,QAAQ,EAAE,KACzB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAWzB,CAAA"}
|
package/dist/dev/lint.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { LINT_RULES } from "../standards/lint.js";
|
|
2
|
+
const SUMMARY_LIMIT = 80;
|
|
3
|
+
const EXAMPLES_MIN = 1;
|
|
4
|
+
const EXAMPLES_MAX = 3;
|
|
5
|
+
/** STANDARD's search hierarchy: `search <resource>`, and `search all` for every resource. */
|
|
6
|
+
const SEARCH_ROOT = "search";
|
|
7
|
+
const SEARCH_EVERYTHING = "all";
|
|
8
|
+
const byText = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
9
|
+
const walk = (commands) => commands.flatMap((one) => [one, ...walk(one.commands)]);
|
|
10
|
+
const longName = (flags) => flags.split(/[ ,|]+/).find((part) => part.startsWith("--")) ?? flags;
|
|
11
|
+
/** A flag's kind of value: its value name, or `switch`. Two kinds under one name are two meanings. */
|
|
12
|
+
const kindOf = (option) => {
|
|
13
|
+
if (!option.takesValue)
|
|
14
|
+
return "switch";
|
|
15
|
+
return option.flags.match(/[<[]([^>\]]+)[>\]]/)?.[1]?.toLowerCase() ?? "value";
|
|
16
|
+
};
|
|
17
|
+
const summaryOf = (command) => (command.summary ?? command.description.split("\n")[0] ?? "").trim();
|
|
18
|
+
const label = (command) => command.path.join(" ");
|
|
19
|
+
const handwrittenLeaves = (document) => walk(document.commands).filter((command) => command.commands.length === 0 && command.origin === "handwritten");
|
|
20
|
+
/** Splits an example the way a shell would for the quoting examples use: '…', "…" and a backslash. */
|
|
21
|
+
export const exampleWords = (line) => [...line.matchAll(/(?:'[^']*'|"(?:\\.|[^"\\])*"|\\.|[^\s'"\\])+/g)].map(([word]) => word.replace(/'([^']*)'|"((?:\\.|[^"\\])*)"|\\(.)/g, (_, single, double, escaped) => single ?? escaped ?? double.replace(/\\(.)/g, "$1")));
|
|
22
|
+
const BUILT_IN = ["-h", "--help"];
|
|
23
|
+
const optionNames = (option) => option.flags.split(/[ ,|]+/).filter((part) => part.startsWith("-"));
|
|
24
|
+
/** Why an example would not parse against the command it is listed under, or undefined when it would. */
|
|
25
|
+
const faultOf = (example, command, ancestors, document) => {
|
|
26
|
+
const words = exampleWords(example);
|
|
27
|
+
if (words[0] !== document.cli)
|
|
28
|
+
return `does not start with "${document.cli}"`;
|
|
29
|
+
const known = [document.globalOptions, ...ancestors.map((one) => one.options), command.options].flat();
|
|
30
|
+
const find = (word) => known.find((option) => optionNames(option).includes(word));
|
|
31
|
+
const option = (at) => {
|
|
32
|
+
const word = words[at] ?? "";
|
|
33
|
+
const [name = word, inline] = word.startsWith("--") ? word.split(/=(.*)/s) : [word];
|
|
34
|
+
if (BUILT_IN.includes(name))
|
|
35
|
+
return at + 1;
|
|
36
|
+
const found = find(name);
|
|
37
|
+
if (!found)
|
|
38
|
+
return `${command.path.join(" ")} has no option ${name}`;
|
|
39
|
+
if (!found.takesValue || inline !== undefined)
|
|
40
|
+
return at + 1;
|
|
41
|
+
const optional = /\[[^\]]*\]/.test(found.flags);
|
|
42
|
+
if (/\.\.\.[>\]]/.test(found.flags)) {
|
|
43
|
+
let next = at + 1;
|
|
44
|
+
while (next < words.length && !words[next]?.startsWith("-"))
|
|
45
|
+
next += 1;
|
|
46
|
+
return next;
|
|
47
|
+
}
|
|
48
|
+
if (optional)
|
|
49
|
+
return words[at + 1] === undefined || words[at + 1]?.startsWith("-") ? at + 1 : at + 2;
|
|
50
|
+
return words[at + 1] === undefined ? `${name} needs a value` : at + 2;
|
|
51
|
+
};
|
|
52
|
+
let at = 1;
|
|
53
|
+
let profile = false;
|
|
54
|
+
while (words[at] !== command.path[0]) {
|
|
55
|
+
if (at >= words.length)
|
|
56
|
+
return `does not run ${command.path.join(" ")}`;
|
|
57
|
+
if (words[at]?.startsWith("-")) {
|
|
58
|
+
const next = option(at);
|
|
59
|
+
if (typeof next === "string")
|
|
60
|
+
return next;
|
|
61
|
+
at = next;
|
|
62
|
+
}
|
|
63
|
+
else if (!profile) {
|
|
64
|
+
profile = true;
|
|
65
|
+
at += 1;
|
|
66
|
+
}
|
|
67
|
+
else
|
|
68
|
+
return `does not run ${command.path.join(" ")}`;
|
|
69
|
+
}
|
|
70
|
+
for (const segment of command.path) {
|
|
71
|
+
if (words[at] !== segment)
|
|
72
|
+
return `does not run ${command.path.join(" ")}`;
|
|
73
|
+
at += 1;
|
|
74
|
+
}
|
|
75
|
+
let positional = 0;
|
|
76
|
+
let rest = false;
|
|
77
|
+
while (at < words.length) {
|
|
78
|
+
const word = words[at] ?? "";
|
|
79
|
+
if (!rest && word === "--") {
|
|
80
|
+
rest = true;
|
|
81
|
+
at += 1;
|
|
82
|
+
}
|
|
83
|
+
else if (!rest && word.startsWith("-") && word.length > 1) {
|
|
84
|
+
const next = option(at);
|
|
85
|
+
if (typeof next === "string")
|
|
86
|
+
return next;
|
|
87
|
+
at = next;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
positional += 1;
|
|
91
|
+
at += 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const required = command.arguments.filter((argument) => argument.required).length;
|
|
95
|
+
const variadic = command.arguments.some((argument) => argument.variadic);
|
|
96
|
+
if (positional < required)
|
|
97
|
+
return `${command.path.join(" ")} needs ${required} argument(s); the example gives ${positional}`;
|
|
98
|
+
if (!variadic && positional > command.arguments.length)
|
|
99
|
+
return `${command.path.join(" ")} takes ${command.arguments.length} argument(s); the example gives ${positional}`;
|
|
100
|
+
return undefined;
|
|
101
|
+
};
|
|
102
|
+
const withAncestors = (commands, above = []) => commands.flatMap((one) => [
|
|
103
|
+
[one, above],
|
|
104
|
+
...withAncestors(one.commands, [...above, one]),
|
|
105
|
+
]);
|
|
106
|
+
const CHECKS = {
|
|
107
|
+
"unknown-verb": (document, vocabulary) => {
|
|
108
|
+
const allowed = new Set([
|
|
109
|
+
...vocabulary.verbs.map((entry) => entry.word),
|
|
110
|
+
...vocabulary.views.map((entry) => entry.word),
|
|
111
|
+
]);
|
|
112
|
+
const roots = new Set(vocabulary.roots.map((entry) => entry.word));
|
|
113
|
+
const searchable = new Set([...vocabulary.nouns.map((entry) => entry.word), SEARCH_EVERYTHING]);
|
|
114
|
+
const searchLeaf = (command) => command.path.at(-2) === SEARCH_ROOT && searchable.has(command.name);
|
|
115
|
+
return handwrittenLeaves(document)
|
|
116
|
+
.filter((command) => !allowed.has(command.name) && !(command.path.length === 1 && roots.has(command.name)))
|
|
117
|
+
.filter((command) => !searchLeaf(command))
|
|
118
|
+
.map((command) => ({
|
|
119
|
+
target: label(command),
|
|
120
|
+
message: `"${command.name}" is not a verb or view noun in the vocabulary`,
|
|
121
|
+
}));
|
|
122
|
+
},
|
|
123
|
+
"unknown-resource": (document, vocabulary) => {
|
|
124
|
+
const nouns = new Set(vocabulary.nouns.map((entry) => entry.word));
|
|
125
|
+
const roots = new Set(vocabulary.roots.map((entry) => entry.word));
|
|
126
|
+
const verbs = new Set(vocabulary.verbs.map((entry) => entry.word));
|
|
127
|
+
return walk(document.commands)
|
|
128
|
+
.filter((command) => command.commands.length > 0 && command.origin === "handwritten")
|
|
129
|
+
.filter((command) => !nouns.has(command.name) && !roots.has(command.name))
|
|
130
|
+
.map((command) => ({
|
|
131
|
+
target: label(command),
|
|
132
|
+
message: verbs.has(command.name)
|
|
133
|
+
? `the group "${command.name}" is named with a verb; a group is a resource`
|
|
134
|
+
: `"${command.name}" is not a resource in the vocabulary: a plural, a registered singular, or a root word`,
|
|
135
|
+
}));
|
|
136
|
+
},
|
|
137
|
+
"flag-two-meanings": (document, vocabulary) => {
|
|
138
|
+
const registered = new Map(vocabulary.flags.map((flag) => [flag.name, flag.value?.slice(1, -1).toLowerCase() ?? "switch"]));
|
|
139
|
+
const uses = new Map();
|
|
140
|
+
const note = (option, where) => {
|
|
141
|
+
const name = longName(option.flags);
|
|
142
|
+
const kinds = uses.get(name) ?? new Map();
|
|
143
|
+
kinds.set(kindOf(option), [...(kinds.get(kindOf(option)) ?? []), where]);
|
|
144
|
+
uses.set(name, kinds);
|
|
145
|
+
};
|
|
146
|
+
for (const option of document.globalOptions)
|
|
147
|
+
note(option, "every command");
|
|
148
|
+
for (const command of walk(document.commands))
|
|
149
|
+
if (command.origin === "handwritten")
|
|
150
|
+
for (const option of command.options)
|
|
151
|
+
note(option, label(command));
|
|
152
|
+
const found = [];
|
|
153
|
+
for (const [name, kinds] of [...uses].sort(([a], [b]) => byText(a, b))) {
|
|
154
|
+
const expected = registered.get(name);
|
|
155
|
+
const off = expected === undefined ? [] : [...kinds.keys()].filter((kind) => kind !== expected);
|
|
156
|
+
if (kinds.size < 2 && off.length === 0)
|
|
157
|
+
continue;
|
|
158
|
+
const described = [...kinds]
|
|
159
|
+
.map(([kind, where]) => `${kind === "switch" ? "a switch" : `<${kind}>`} in ${where.slice(0, 3).join(", ")}`)
|
|
160
|
+
.join("; ");
|
|
161
|
+
found.push({
|
|
162
|
+
target: name,
|
|
163
|
+
message: expected === undefined
|
|
164
|
+
? `${name} has ${kinds.size} meanings: ${described}`
|
|
165
|
+
: `${name} is ${expected === "switch" ? "a switch" : `<${expected}>`} in the vocabulary, but: ${described}`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return found;
|
|
169
|
+
},
|
|
170
|
+
"missing-command-help": (document) => walk(document.commands)
|
|
171
|
+
.filter((command) => summaryOf(command) === "")
|
|
172
|
+
.map((command) => ({ target: label(command), message: "no summary or description" })),
|
|
173
|
+
"command-help-too-long": (document) => walk(document.commands)
|
|
174
|
+
.filter((command) => command.origin === "handwritten" && summaryOf(command).length > SUMMARY_LIMIT)
|
|
175
|
+
.map((command) => ({
|
|
176
|
+
target: label(command),
|
|
177
|
+
message: `summary is ${summaryOf(command).length} characters; at most ${SUMMARY_LIMIT}`,
|
|
178
|
+
})),
|
|
179
|
+
"command-example-count": (document) => handwrittenLeaves(document)
|
|
180
|
+
.filter((command) => {
|
|
181
|
+
const count = command.examples?.length ?? 0;
|
|
182
|
+
return count < EXAMPLES_MIN || count > EXAMPLES_MAX;
|
|
183
|
+
})
|
|
184
|
+
.map((command) => ({
|
|
185
|
+
target: label(command),
|
|
186
|
+
message: `${command.examples?.length ?? 0} examples; give ${EXAMPLES_MIN} to ${EXAMPLES_MAX}`,
|
|
187
|
+
})),
|
|
188
|
+
"faulty-help-example-parameters": (document) => withAncestors(document.commands)
|
|
189
|
+
.filter(([command]) => command.commands.length === 0 && command.origin === "handwritten")
|
|
190
|
+
.flatMap(([command, ancestors]) => (command.examples ?? []).flatMap((example) => {
|
|
191
|
+
const fault = faultOf(example, command, ancestors, document);
|
|
192
|
+
return fault === undefined ? [] : [{ target: `${label(command)}: ${example}`, message: fault }];
|
|
193
|
+
})),
|
|
194
|
+
"missing-argument-help": (document) => walk(document.commands).flatMap((command) => command.arguments
|
|
195
|
+
.filter((argument) => argument.description.trim() === "")
|
|
196
|
+
.map((argument) => ({ target: `${label(command)} <${argument.name}>`, message: "argument not described" }))),
|
|
197
|
+
"missing-option-help": (document) => [
|
|
198
|
+
...document.globalOptions
|
|
199
|
+
.filter((option) => option.description.trim() === "")
|
|
200
|
+
.map((option) => ({ target: longName(option.flags), message: "global option not described" })),
|
|
201
|
+
...walk(document.commands).flatMap((command) => command.options
|
|
202
|
+
.filter((option) => option.description.trim() === "")
|
|
203
|
+
.map((option) => ({ target: `${label(command)} ${longName(option.flags)}`, message: "option not described" }))),
|
|
204
|
+
],
|
|
205
|
+
};
|
|
206
|
+
export const lintRuleIds = () => Object.keys(CHECKS);
|
|
207
|
+
/** Every violation, one per rule and target, sorted. Generated commands keep the names of the API they mirror. */
|
|
208
|
+
export const lintCommands = (document, vocabulary, rules = LINT_RULES) => {
|
|
209
|
+
const seen = new Set();
|
|
210
|
+
return rules.flatMap((rule) => {
|
|
211
|
+
const check = CHECKS[rule.id];
|
|
212
|
+
if (!check)
|
|
213
|
+
throw new Error(`lint rule "${rule.id}" has no check`);
|
|
214
|
+
return check(document, vocabulary)
|
|
215
|
+
.filter(({ target }) => !seen.has(`${rule.id} ${target}`) && seen.add(`${rule.id} ${target}`))
|
|
216
|
+
.map((found) => ({ rule: rule.id, ...found }))
|
|
217
|
+
.sort((a, b) => byText(a.target, b.target));
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
export const judge = (violations, baseline, overrides, budget, rules = LINT_RULES) => {
|
|
221
|
+
const ids = new Set(rules.map((rule) => rule.id));
|
|
222
|
+
const key = (rule, target) => `${rule} ${target}`;
|
|
223
|
+
const overridden = new Set(overrides.map((entry) => key(entry.rule, entry.target)));
|
|
224
|
+
const baselined = new Set(Object.entries(baseline).flatMap(([rule, targets]) => targets.map((t) => key(rule, t))));
|
|
225
|
+
const found = new Set(violations.map((violation) => key(violation.rule, violation.target)));
|
|
226
|
+
const open = violations.filter((violation) => !overridden.has(key(violation.rule, violation.target)));
|
|
227
|
+
const fresh = open.filter((violation) => !baselined.has(key(violation.rule, violation.target)));
|
|
228
|
+
return {
|
|
229
|
+
fresh,
|
|
230
|
+
baselined: open.length - fresh.length,
|
|
231
|
+
overridden: violations.length - open.length,
|
|
232
|
+
stale: [
|
|
233
|
+
...Object.entries(baseline).flatMap(([rule, targets]) => targets
|
|
234
|
+
.filter((target) => !found.has(key(rule, target)) || overridden.has(key(rule, target)))
|
|
235
|
+
.map((target) => ({ rule, target, in: "baseline" }))),
|
|
236
|
+
...overrides
|
|
237
|
+
.filter((entry) => !found.has(key(entry.rule, entry.target)))
|
|
238
|
+
.map((entry) => ({ rule: entry.rule, target: entry.target, in: "overrides" })),
|
|
239
|
+
],
|
|
240
|
+
unknownRules: [
|
|
241
|
+
...new Set([...Object.keys(baseline), ...overrides.map((entry) => entry.rule)].filter((id) => !ids.has(id))),
|
|
242
|
+
].sort(),
|
|
243
|
+
overBudget: overrides.length > budget,
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
/** What `--write-baseline` writes: per rule, the targets no override covers. A rule with none gets no file. */
|
|
247
|
+
export const baselineOf = (violations, overrides, rules = LINT_RULES) => {
|
|
248
|
+
const overridden = new Set(overrides.map((entry) => `${entry.rule} ${entry.target}`));
|
|
249
|
+
return Object.fromEntries(rules.map((rule) => [
|
|
250
|
+
rule.id,
|
|
251
|
+
violations
|
|
252
|
+
.filter((violation) => violation.rule === rule.id && !overridden.has(`${violation.rule} ${violation.target}`))
|
|
253
|
+
.map((violation) => violation.target)
|
|
254
|
+
.sort(),
|
|
255
|
+
]));
|
|
256
|
+
};
|
|
257
|
+
//# sourceMappingURL=lint.js.map
|