@noctcore/lint-meta-rules 0.1.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 ADDED
@@ -0,0 +1,65 @@
1
+ # @noctcore/lint-meta-rules
2
+
3
+ Portable, parameterized **lint-meta** rules — whole-repo / cross-file invariants that ESLint's
4
+ per-file AST model cannot reach (every `package.json` name matches a convention, every imported
5
+ workspace package is a declared dependency, file-size ratchets, agent-doc presence, and more).
6
+
7
+ Each rule implements the portable [`IMetaRule`](https://www.npmjs.com/package/@noctcore/harness)
8
+ contract published by `@noctcore/harness`: a pure function of an `IMetaCtx` returning `IViolation[]`.
9
+
10
+ ## How this package is consumed
11
+
12
+ This is a **versioned source catalog**, not a runtime dependency you `require()` from a consumer's
13
+ registry. The `@noctcore/harness` `lint-meta` subcommand runs a *bounded eval* that executes only one
14
+ local `.nightcore/lint-meta/registry.js` and never resolves arbitrary imports — a deliberate security
15
+ boundary, since that file runs inside a foreign CI. So the intended integration point is nightcore's
16
+ harness **export pipeline**: it reads a rule's source here, inlines/transforms it, and emits flat
17
+ JavaScript directly into a consumer's `.nightcore/lint-meta/`. No consumer registry imports this
18
+ package at runtime.
19
+
20
+ Publishing to npm is for versioning and discoverability; it is not a "`npm install` this and
21
+ `require()` it" pitch.
22
+
23
+ ## Why factories
24
+
25
+ `IMetaRule.run(ctx)` takes no config, so every rule that hardcoded a nightcore-specific anchor (a
26
+ `@nightcore` scope, an `apps/web/src` root, a rank table) is exported as a **factory** —
27
+ `createXRule(options): IMetaRule` — with those anchors lifted to typed options carrying sensible
28
+ defaults. A programmatic caller (or the export pipeline) constructs each rule with the consumer's own
29
+ options:
30
+
31
+ ```ts
32
+ import { createPackageShapeRule, createFileSizeRatchetRule } from '@noctcore/lint-meta-rules';
33
+
34
+ const rules = [
35
+ createPackageShapeRule({ scope: '@acme' }),
36
+ createFileSizeRatchetRule({ id: 'web-file-size-ratchet', roots: ['apps/web/src'], cap: 400 }),
37
+ ];
38
+ ```
39
+
40
+ `RULE_FACTORIES` (an id → factory map), `RULE_IDS`, and `createAllRules()` are exported for iteration
41
+ over the whole catalog.
42
+
43
+ ## Rules
44
+
45
+ 13 nightcore lint-meta rules are ported as 12 factories — nightcore's `web-file-size-ratchet` and
46
+ `engine-file-size-ratchet` were byte-identical logic and collapse into a single
47
+ `createFileSizeRatchetRule` (instantiated once per capped area).
48
+
49
+ | Factory | Source rule(s) | Category | What it enforces |
50
+ | --- | --- | --- | --- |
51
+ | [`createNoWarnSeverityRule`](./docs/rules/no-warn-severity.md) | `no-warn-severity` | config | ESLint severity is `error`/`off`, never `warn`. |
52
+ | [`createPackageShapeRule`](./docs/rules/package-shape.md) | `package-shape` | config | Every workspace is named `<scope>/<dir>`; libraries expose a barrel and point build fields at `dist/`. |
53
+ | [`createWorkspaceGraphParityRule`](./docs/rules/workspace-graph-parity.md) | `workspace-graph-parity` | config | Imported `<scope>/*` specifiers are declared `workspace:*` deps and mirrored in tsconfig references. |
54
+ | [`createLayerRankRule`](./docs/rules/layer-rank.md) | `layer-rank` | source-text | A module imports only strictly-lower-ranked `<scope>` packages (no sideways/upward edges). |
55
+ | [`createFileSizeRatchetRule`](./docs/rules/file-size-ratchet.md) | `web-file-size-ratchet`, `engine-file-size-ratchet` | source-text | Source files stay under a line cap, with a one-way self-tightening baseline ratchet. |
56
+ | [`createAgentsDocPresenceRule`](./docs/rules/agents-doc-presence.md) | `agents-doc-presence` | source-text | An agent-contract doc exists at the root, every surface, and every non-opted-out package. |
57
+ | [`createTestSiblingEnforcementRule`](./docs/rules/test-sibling-enforcement.md) | `test-sibling-enforcement` | source-text | Every source file matched by `include` has a colocated sibling test. |
58
+ | [`createCanonicalHelpersSingleHomeRule`](./docs/rules/canonical-helpers-single-home.md) | `canonical-helpers-single-home` | source-text | A helper symbol is not exported from two different helper homes. |
59
+ | [`createNoClonedComponentFoldersRule`](./docs/rules/no-cloned-component-folders.md) | `no-cloned-component-folders` | source-text | A component folder name exists under only one feature (shrinking allowlist). |
60
+ | [`createUiPrimitiveShapeRule`](./docs/rules/ui-primitive-shape.md) | `ui-primitive-shape` | source-text | A folder primitive ships its proof siblings; a flat primitive carries none at the ui root. |
61
+ | [`createTestWorkspaceEnrollmentRule`](./docs/rules/test-workspace-enrollment.md) | `test-workspace-enrollment` | testing | Every tested package is enumerated in the aggregate test script. |
62
+ | [`createTestRunnerSegregationRule`](./docs/rules/test-runner-segregation.md) | `test-runner-segregation` | testing | Bun-side and foreign-side test runners are never mixed within a package. |
63
+
64
+ Every factory is callable with no arguments (all options default), so `createAllRules()` and
65
+ per-factory defaults work out of the box; supply options to retarget a rule at your own repo.