Initial Commit
This commit is contained in:
112
packages/devtools/tooling/linter/src/error-codes/error-codes.md
Normal file
112
packages/devtools/tooling/linter/src/error-codes/error-codes.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# `error-codes`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`@suaveplan/linter/error-codes` owns the stable error-code registry and the typed
|
||||
`LinterError` class that every other module in the package throws. The registry is
|
||||
expressed through `defineCodes("LINTER", [...])` from `@suaveplan/error` so each code
|
||||
carries the `LINTER.` namespace prefix and survives across module boundaries, worker
|
||||
threads, RPC envelopes, and serialised error transports without losing its identity.
|
||||
Consumers of the linter — pre-commit hooks, CI gates, IDE integrations — discriminate
|
||||
on `error.code` to make routing decisions (retry, suppress, surface to the user) without
|
||||
parsing free-form messages, so the registry is a public contract, not an implementation
|
||||
detail.
|
||||
|
||||
## Features
|
||||
|
||||
- Closed code set declared once via `defineCodes` — typo-safe, no string literals at
|
||||
call sites.
|
||||
- `LinterError extends SuaveplanError<LinterCode>` — interoperable with every
|
||||
observability adapter shipped in `@suaveplan/error` (OTel span events, redaction,
|
||||
i18n hooks).
|
||||
- Optional `details` payload preserved on the instance and on the underlying
|
||||
`SuaveplanError.context` field so structured diagnostics travel with the error.
|
||||
- Optional `cause` forwarded to the native `Error.cause` chain — preserves the
|
||||
underlying failure (e.g. an `EACCES` from `fs.writeFile`) for debugging.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { LINTER_CODES, LinterError } from "@suaveplan/linter/error-codes";
|
||||
|
||||
throw new LinterError(LINTER_CODES.NO_TARGETS_FOUND, "no .md files matched", {
|
||||
details: { pattern: "docs/**/*.md" },
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Route errors by code in a CI gate:
|
||||
|
||||
```ts
|
||||
import { LinterError } from "@suaveplan/linter/error-codes";
|
||||
import { runUnifiedLint } from "@suaveplan/linter/unified-lint";
|
||||
|
||||
try {
|
||||
const report = await runUnifiedLint({ fix: false });
|
||||
if (!report.success) {
|
||||
process.exitCode = 1;
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof LinterError) {
|
||||
switch (err.code) {
|
||||
case "LINTER.BIOME_UNAVAILABLE":
|
||||
console.warn("biome not installed — skipping JS/TS lane");
|
||||
break;
|
||||
case "LINTER.NO_TARGETS_FOUND":
|
||||
process.exitCode = 0; // empty repo, not a failure
|
||||
break;
|
||||
default:
|
||||
console.error(err.code, err.message, err.details);
|
||||
process.exitCode = 2;
|
||||
}
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `LINTER_CODES`
|
||||
|
||||
A frozen object whose values are the registered `LINTER.<REASON>` strings. Members:
|
||||
`INVALID_OPTIONS`, `FILE_NOT_FOUND`, `NO_TARGETS_FOUND`, `READ_FAILED`,
|
||||
`WRITE_FAILED`, `MARKDOWN_PARSE_FAILED`, `BIOME_UNAVAILABLE`, `BIOME_FAILED`,
|
||||
`RUNNER_FAILED`, `UNKNOWN_FLAG`.
|
||||
|
||||
### `type LinterCode`
|
||||
|
||||
Union of every value in `LINTER_CODES`.
|
||||
|
||||
### `class LinterError extends SuaveplanError<LinterCode>`
|
||||
|
||||
Constructor: `new LinterError(code, message, options?)`.
|
||||
|
||||
- `code: LinterCode` — one of the registered codes.
|
||||
- `message: string` — human-readable summary.
|
||||
- `options.cause?: unknown` — forwarded to `Error.cause`.
|
||||
- `options.details?: Record<string, unknown>` — stored as both
|
||||
`instance.details` AND `instance.context` (via `SuaveplanError`).
|
||||
|
||||
`instance.name` is set to `"LinterError"`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The registry is enumerated by tests (`Object.values(LINTER_CODES).sort()`) to
|
||||
guarantee the public set matches the spec delta exactly. Adding a code without
|
||||
updating the test fails the gate.
|
||||
- `details` defaults to `{}` rather than `undefined` so consumers can safely spread or
|
||||
read fields without a null-check; the spec scenario "defaults details to empty record
|
||||
when omitted" pins this contract.
|
||||
- `SuaveplanError` accepts `cause` only when defined — passing `undefined` would set
|
||||
`cause: undefined` on the chain, which differs from "no cause." The constructor
|
||||
guards this with the `options?.cause === undefined ? {} : { cause: options.cause }`
|
||||
ternary.
|
||||
- The class extends `SuaveplanError<LinterCode>` rather than `SuaveplanError` so the
|
||||
`code` getter is narrowed to the linter's registered union at the type level — IDE
|
||||
autocompletion lists exactly the ten codes.
|
||||
Reference in New Issue
Block a user