@homepages/template-kit 0.1.0 → 0.3.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 +220 -0
- package/README.md +70 -16
- package/dist/base.css +36 -48
- package/dist/cli/check/config.js +37 -0
- package/dist/cli/check/css.js +117 -0
- package/dist/cli/check/diagnostics.js +32 -0
- package/dist/cli/check/index.js +87 -0
- package/dist/cli/check/loader.js +142 -0
- package/dist/cli/check/paths.js +15 -0
- package/dist/cli/check/relativize.js +18 -0
- package/dist/cli/check/render-invariants.js +24 -0
- package/dist/cli/check/resolve-tool.js +38 -0
- package/dist/cli/check/stages/deps.js +337 -0
- package/dist/cli/check/stages/lint.js +46 -0
- package/dist/cli/check/stages/render.js +101 -0
- package/dist/cli/check/stages/size.js +158 -0
- package/dist/cli/check/stages/tree.js +207 -0
- package/dist/cli/check/stages/typecheck.js +46 -0
- package/dist/cli/check/stages/validate/catalog-exhaustiveness.js +18 -0
- package/dist/cli/check/stages/validate/facts.js +18 -0
- package/dist/cli/check/stages/validate/formatter-contract.js +14 -0
- package/dist/cli/check/stages/validate/group-contract.js +21 -0
- package/dist/cli/check/stages/validate/index.js +66 -0
- package/dist/cli/check/stages/validate/list-scalar-contract.js +22 -0
- package/dist/cli/check/stages/validate/nav-contract.js +93 -0
- package/dist/cli/check/stages/validate/nested-slot-contract.js +12 -0
- package/dist/cli/check/stages/validate/object-list-contract.js +31 -0
- package/dist/cli/check/stages/validate/orchestrator.js +241 -0
- package/dist/cli/check/stages/validate/produced-by-contract.js +18 -0
- package/dist/cli/check/stages/validate/row-contract.js +31 -0
- package/dist/cli/check/stages/validate/select-contract.js +13 -0
- package/dist/cli/check/stages/validate/source-contract.js +21 -0
- package/dist/cli/check/stages/validate/variant-contract.js +15 -0
- package/dist/cli/check/workspace.js +86 -0
- package/dist/cli/theme/generate.js +43 -0
- package/dist/cli/theme/index.js +33 -0
- package/dist/cli/theme/load-theme.js +69 -0
- package/dist/cli.js +22 -4
- package/dist/contracts/fill-treatments.js +47 -0
- package/dist/design-system/theme.d.ts +30 -299
- package/dist/design-system/theme.js +112 -83
- package/dist/eslint/rules/image-bare-needs-reason.js +6 -1
- package/dist/eslint/rules/no-client-directive-in-contract.js +6 -1
- package/dist/eslint/rules/no-client-runtime-in-server.js +5 -1
- package/dist/eslint/rules/no-css-import-from-render-path.js +8 -2
- package/dist/eslint/rules/no-hex.js +84 -7
- package/dist/eslint/rules/no-inline-style.js +5 -1
- package/dist/eslint/rules/no-nondeterminism.js +5 -1
- package/dist/eslint/rules/no-raw-element.js +6 -1
- package/dist/eslint/rules/props-from-schema.js +6 -1
- package/dist/eslint/rules/serializable-island-props.js +5 -1
- package/dist/eslint/rules/slot-marker-literal.js +6 -1
- package/dist/eslint.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/package.js +1 -1
- package/dist/primitives/Image.js +1 -1
- package/dist/rules/registry.js +16 -0
- package/dist/schema/fill-spec.d.ts +80 -590
- package/dist/schema/fixture-schema.d.ts +5 -81
- package/dist/schema/manifest.d.ts +39 -475
- package/dist/schema/resolve-section-ref.js +10 -0
- package/dist/schema/rows.js +10 -0
- package/dist/schema/section-nav.js +9 -0
- package/dist/schema/section-schema.d.ts +51 -437
- package/dist/schema/slot-types.d.ts +12 -16
- package/dist/styles.css +31 -88
- package/docs/INDEX.md +5 -3
- package/docs/check.md +121 -0
- package/docs/eslint.md +25 -50
- package/docs/llms.txt +26 -11
- package/docs/primitives.md +5 -0
- package/docs/rules/INDEX.md +65 -0
- package/docs/rules/audit-severity.md +91 -0
- package/docs/rules/bundle-binary-asset.md +102 -0
- package/docs/rules/bundle-incomplete.md +117 -0
- package/docs/rules/css-reason.md +94 -0
- package/docs/rules/determinism-drift.md +112 -0
- package/docs/rules/fixtures-invalid.md +96 -0
- package/docs/rules/image-bare-needs-reason.md +117 -0
- package/docs/rules/license-denied.md +114 -0
- package/docs/rules/lockfile-missing.md +68 -0
- package/docs/rules/lockfile-stale.md +88 -0
- package/docs/rules/manifest-invalid.md +99 -0
- package/docs/rules/missing-slot-marker.md +140 -0
- package/docs/rules/no-bare-css-import.md +128 -0
- package/docs/rules/no-client-directive-in-contract.md +111 -0
- package/docs/rules/no-client-runtime-in-server.md +143 -0
- package/docs/rules/no-css-import-from-render-path.md +118 -0
- package/docs/rules/no-hex.md +163 -0
- package/docs/rules/no-inline-style.md +121 -0
- package/docs/rules/no-nondeterminism.md +100 -0
- package/docs/rules/no-raw-element.md +100 -0
- package/docs/rules/no-templates.md +87 -0
- package/docs/rules/parse-error.md +76 -0
- package/docs/rules/props-from-schema.md +119 -0
- package/docs/rules/render-invariant.md +165 -0
- package/docs/rules/schema-invalid.md +96 -0
- package/docs/rules/serializable-island-props.md +146 -0
- package/docs/rules/server-vs-client.md +105 -0
- package/docs/rules/sidebar-order.md +109 -0
- package/docs/rules/single-react.md +97 -0
- package/docs/rules/size-renderer-bundle.md +109 -0
- package/docs/rules/size-section-css.md +95 -0
- package/docs/rules/slot-group-marker.md +127 -0
- package/docs/rules/slot-marker-literal.md +148 -0
- package/docs/rules/typecheck.md +103 -0
- package/docs/rules/unlayered-fence.md +113 -0
- package/docs/theme-and-css.md +193 -82
- package/package.json +4 -9
- package/tsconfig.json +1 -1
package/dist/styles.css
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* @homepages/template-kit —
|
|
2
|
+
* @homepages/template-kit — the global Tailwind preamble.
|
|
3
3
|
*
|
|
4
|
-
* Import this AFTER `@import "tailwindcss";` in your own CSS entry
|
|
4
|
+
* Import this AFTER `@import "tailwindcss";` in your own CSS entry, and import your
|
|
5
|
+
* template's compiled theme after it:
|
|
5
6
|
*
|
|
6
7
|
* @import "tailwindcss";
|
|
7
8
|
* @import "@homepages/template-kit/styles.css";
|
|
9
|
+
* @import "./theme.css"; // compileThemeToCss(yourTheme) output
|
|
8
10
|
*
|
|
9
11
|
* This file must NOT import Tailwind itself. Exactly one `@import "tailwindcss"` may
|
|
10
12
|
* exist per build graph — your entry owns it. Two double-emit Tailwind's preflight
|
|
11
13
|
* reset (box-sizing declared twice, ~4 KB of duplicate base layer).
|
|
12
14
|
*
|
|
15
|
+
* WHAT IS HERE: only what is true of EVERY template — the breakpoint ladder, the
|
|
16
|
+
* spacing base unit, and the motion defaults.
|
|
17
|
+
*
|
|
18
|
+
* WHAT IS NOT: your design tokens. Colors, fonts, type, radii and shadows are yours.
|
|
19
|
+
* Declare them in `theme.ts` under names you choose, and `compileThemeToCss` generates
|
|
20
|
+
* the `@theme inline` alias layer that turns each one into a utility. Nothing about
|
|
21
|
+
* your visual system is shared, and there is no global file for you to edit to make a
|
|
22
|
+
* token of yours usable.
|
|
23
|
+
*
|
|
13
24
|
* The @source below resolves relative to THIS FILE'S SHIPPED LOCATION (dist/styles.css),
|
|
14
25
|
* registering the kit's compiled JS for class scanning — that is how the utility classes
|
|
15
26
|
* baked into the kit's primitives reach your compiled CSS. It also overrides Tailwind's
|
|
@@ -23,107 +34,39 @@
|
|
|
23
34
|
* utilities and 19 stray @property registrations, ~2.5 KB. If a primitive ever moves out
|
|
24
35
|
* of primitives/, widen this — an unstyled <Image> in every template is the failure mode,
|
|
25
36
|
* and scripts/verify-consumer.mjs is what catches it.
|
|
26
|
-
*
|
|
27
|
-
* The @theme block aliases Tailwind's utility namespace onto the `--tr-*` custom
|
|
28
|
-
* properties your template's theme.ts supplies (`text-ink` → `var(--tr-color-ink)`).
|
|
29
|
-
* The aliases are the cross-template contract; the values behind them are yours.
|
|
30
37
|
*/
|
|
31
38
|
@source "./primitives/**/*.js";
|
|
32
39
|
|
|
40
|
+
/* `@theme inline` so a spacing/breakpoint value is substituted straight into the
|
|
41
|
+
* utilities Tailwind generates (`.gap-2 { gap: .5rem }`), rather than emitted as a
|
|
42
|
+
* `:root` custom property the utilities then read through `var()`. These are fixed
|
|
43
|
+
* global constants, not per-page-overridable tokens, so inlining is both correct and
|
|
44
|
+
* what keeps the compiled output stable. Nothing here may reference a token a theme.ts
|
|
45
|
+
* defines. */
|
|
33
46
|
@theme inline {
|
|
34
|
-
/* ----------
|
|
35
|
-
--color-ink: var(--tr-color-ink);
|
|
36
|
-
--color-ink-soft: var(--tr-color-ink-soft);
|
|
37
|
-
--color-ink-mute: var(--tr-color-ink-mute);
|
|
38
|
-
--color-background: var(--tr-color-background);
|
|
39
|
-
--color-surface: var(--tr-color-surface);
|
|
40
|
-
--color-surface-alt: var(--tr-color-surface-alt);
|
|
41
|
-
--color-border: var(--tr-color-border);
|
|
42
|
-
--color-border-strong: var(--tr-color-border-strong);
|
|
43
|
-
--color-accent: var(--tr-color-accent);
|
|
44
|
-
--color-accent-strong: var(--tr-color-accent-strong);
|
|
45
|
-
--color-accent-contrast: var(--tr-color-accent-contrast);
|
|
46
|
-
--color-primary: var(--tr-color-primary);
|
|
47
|
-
--color-primary-fg: var(--tr-color-primary-fg);
|
|
48
|
-
--color-primary-darker: var(--tr-color-primary-darker);
|
|
49
|
-
--color-overlay: var(--tr-color-overlay);
|
|
50
|
-
--color-overlay-strong: var(--tr-color-overlay-strong);
|
|
51
|
-
--color-overlay-light: var(--tr-color-overlay-light);
|
|
52
|
-
--color-footer-bg: var(--tr-color-footer-bg);
|
|
53
|
-
--color-footer-fg: var(--tr-color-footer-fg);
|
|
54
|
-
--color-footer-fg-mute: var(--tr-color-footer-fg-mute);
|
|
55
|
-
--color-footer-divider: var(--tr-color-footer-divider);
|
|
56
|
-
|
|
57
|
-
/* Decorative brand swatches — the open `palette` escape hatch in TokenTheme.
|
|
58
|
-
* A theme that omits one leaves its utility resolving to an unset custom
|
|
59
|
-
* property; only reach for these when a semantic role genuinely doesn't fit. */
|
|
60
|
-
--color-yellow-deep: var(--tr-color-yellow-deep);
|
|
61
|
-
--color-yellow-200: var(--tr-color-yellow-200);
|
|
62
|
-
--color-yellow-100: var(--tr-color-yellow-100);
|
|
63
|
-
--color-brown: var(--tr-color-brown);
|
|
64
|
-
--color-green: var(--tr-color-green);
|
|
65
|
-
--color-blue: var(--tr-color-blue);
|
|
66
|
-
--color-rule: var(--tr-color-rule);
|
|
67
|
-
--color-gray-light: var(--tr-color-gray-light);
|
|
68
|
-
--color-team-gold: var(--tr-color-team-gold);
|
|
69
|
-
--color-mute: var(--tr-color-mute);
|
|
70
|
-
--color-scrim: var(--tr-color-scrim);
|
|
71
|
-
--color-footer-line: var(--tr-color-footer-line);
|
|
72
|
-
--color-footer-mute: var(--tr-color-footer-mute);
|
|
73
|
-
--color-divider-40: var(--tr-color-divider-40);
|
|
74
|
-
--color-contact-deep: var(--tr-color-contact-deep);
|
|
75
|
-
--color-header-deep: var(--tr-color-header-deep);
|
|
76
|
-
|
|
77
|
-
/* ---------- Fonts ---------- */
|
|
78
|
-
--font-sans: var(--tr-font-sans);
|
|
79
|
-
--font-display: var(--tr-font-sans);
|
|
80
|
-
--font-serif: var(--tr-font-serif);
|
|
81
|
-
--font-num: var(--tr-font-num);
|
|
82
|
-
--font-label: var(--tr-font-label);
|
|
83
|
-
|
|
84
|
-
/* ---------- Type scale (mobile baseline; layer up with md:/lg:) ---------- */
|
|
85
|
-
--text-xs: var(--tr-text-xs);
|
|
86
|
-
--text-sm: var(--tr-text-sm);
|
|
87
|
-
--text-base: var(--tr-text-base);
|
|
88
|
-
--text-lg: var(--tr-text-lg);
|
|
89
|
-
--text-xl: var(--tr-text-xl);
|
|
90
|
-
--text-2xl: var(--tr-text-2xl);
|
|
91
|
-
--text-3xl: var(--tr-text-3xl);
|
|
92
|
-
--text-4xl: var(--tr-text-4xl);
|
|
93
|
-
--text-5xl: var(--tr-text-5xl);
|
|
94
|
-
--text-display: var(--tr-text-display);
|
|
95
|
-
|
|
96
|
-
/* ---------- Spacing scale (alias to --tr-space-*) ---------- */
|
|
47
|
+
/* ---------- Spacing ---------- */
|
|
97
48
|
--spacing: 0.25rem; /* v4 base unit; Tailwind multiplies (e.g. p-4 = 1rem) */
|
|
98
49
|
|
|
99
|
-
/* ----------
|
|
100
|
-
|
|
101
|
-
--radius-md: var(--tr-radius-md);
|
|
102
|
-
--radius-lg: var(--tr-radius-lg);
|
|
103
|
-
--radius-full: var(--tr-radius-full);
|
|
104
|
-
|
|
105
|
-
/* ---------- Shadows ---------- */
|
|
106
|
-
--shadow-sm: var(--tr-shadow-sm);
|
|
107
|
-
--shadow-md: var(--tr-shadow-md);
|
|
108
|
-
--shadow-lg: var(--tr-shadow-lg);
|
|
109
|
-
|
|
110
|
-
/* ---------- Motion ---------- */
|
|
50
|
+
/* ---------- Motion ----------
|
|
51
|
+
* The --tr-* values these read are defined in base.css, which every page loads. */
|
|
111
52
|
--default-transition-duration: var(--tr-duration-fast);
|
|
112
53
|
--default-transition-timing-function: var(--tr-ease);
|
|
113
54
|
|
|
114
55
|
/* ---------- Canonical breakpoint ladder ----------
|
|
115
|
-
* Eight canonical widths — the same set the preview grid and the screenshot
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
56
|
+
* Eight canonical widths — the same set the preview grid and the screenshot harness
|
|
57
|
+
* verify a section against. `VERIFICATION_WIDTHS` (design-system/breakpoints.ts) is
|
|
58
|
+
* the matching TS constant; CSS cannot import it, so `src/css/styles.test.ts` fails
|
|
59
|
+
* the build if the two ever drift.
|
|
60
|
+
*
|
|
61
|
+
* 350 is the mobile-first BASE (no prefix); the other seven are named breakpoints:
|
|
119
62
|
*
|
|
120
63
|
* bp390 (390) · bp425 (425) · bp512 (512) · md (768) · lg (1024)
|
|
121
64
|
* · bp1440 (1440) · bp1920 (1920)
|
|
122
65
|
*
|
|
123
66
|
* `md` and `lg` keep Tailwind's own names and values. The off-ladder defaults
|
|
124
|
-
* (sm/xl/2xl) are reset to `initial` so a section cannot come to depend on a
|
|
125
|
-
*
|
|
126
|
-
*
|
|
67
|
+
* (sm/xl/2xl) are reset to `initial` so a section cannot come to depend on a width
|
|
68
|
+
* nobody screenshots. A swap-heavy section may still reach for a raw `@media`
|
|
69
|
+
* micro-breakpoint at an off-ladder width (e.g. 640 / 769).
|
|
127
70
|
*/
|
|
128
71
|
--breakpoint-sm: initial;
|
|
129
72
|
--breakpoint-bp390: 390px;
|
package/docs/INDEX.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
purpose: Router for the docs corpus that ships inside the @homepages/template-kit npm package.
|
|
3
3
|
status: living
|
|
4
4
|
related: []
|
|
5
|
-
updated: 2026-07-
|
|
5
|
+
updated: 2026-07-15
|
|
6
6
|
---
|
|
7
7
|
# template-kit docs — router
|
|
8
8
|
|
|
@@ -16,6 +16,8 @@ detail.
|
|
|
16
16
|
| `llms.txt` | Machine-readable index of this corpus for an agent working inside an installed kit | orienting an agent in a consuming workspace |
|
|
17
17
|
| `schema-system.md` | The section authoring contract: `schema.ts` / `fill-spec.ts` / `fixtures.ts` / `Renderer.tsx`, `SectionProps` inference, the closed source/transform vocabulary, and the golden property fixtures | authoring or changing any section |
|
|
18
18
|
| `primitives.md` | The five contract primitives (`Section`, `Slot`, `SlotItem`, `SlotGroup`, `Image`) and the marker attributes they emit | writing a section's markup |
|
|
19
|
-
| `theme-and-css.md` | The two CSS entries, the one-Tailwind-import rule, `TokenTheme`, the breakpoint ladder, and the opt-in `content-visibility` recipe for long, image-heavy pages | a template's theme, its CSS entry, or a long page with a live map or full-screen overlay |
|
|
19
|
+
| `theme-and-css.md` | The two CSS entries, the one-Tailwind-import rule, `TokenTheme` (your own token names → your own Tailwind utilities), the `template-kit theme` command that generates `theme.css` from `theme.ts`, the breakpoint ladder, and the opt-in `content-visibility` recipe for long, image-heavy pages | a template's theme, its CSS entry, or a long page with a live map or full-screen overlay |
|
|
20
20
|
| `islands.md` | Interactivity: `"use client"` components, the serializable-props rule, editor options, the loader | anything interactive in a section |
|
|
21
|
-
| `eslint.md` | The `@homepages/template-kit/eslint` preset:
|
|
21
|
+
| `eslint.md` | The `@homepages/template-kit/eslint` preset: the two-line config, the ESLint + parser install it expects, and its glob scope | wiring up lint in a template workspace |
|
|
22
|
+
| `check.md` | The `template-kit check` CLI command: the toolchain it resolves from your workspace, its stages, `--all`/`--json`, output shape, and exit code | running `check` locally or wiring it into CI |
|
|
23
|
+
| `rules/` | One page per rule-id (`template-kit/*`), across both venues — the id printed by a lint or `check` failure resolves here. `rules/INDEX.md` routes them; `rules/server-vs-client.md` is the concept the server-rendering rules assume | any lint or `check` failure |
|
package/docs/check.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
purpose: The `template-kit check` command — what it runs, its flags, its output shape, and its exit code.
|
|
3
|
+
status: living
|
|
4
|
+
related: [INDEX.md, rules/INDEX.md, eslint.md, theme-and-css.md]
|
|
5
|
+
updated: 2026-07-15
|
|
6
|
+
---
|
|
7
|
+
# template-kit check
|
|
8
|
+
|
|
9
|
+
`template-kit check` is the one command that answers "will the platform accept this
|
|
10
|
+
template?" — the same gate you run locally and the one the registry re-runs when you
|
|
11
|
+
submit. Every failure prints a stable `template-kit/<id>`, a `file:line` where one
|
|
12
|
+
applies, and a one-line fix hint. The id resolves to its own page under
|
|
13
|
+
[`rules/`](rules/INDEX.md).
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
template-kit check # infer the template from cwd, or the workspace's one template
|
|
19
|
+
template-kit check <template> # check one named template by its key
|
|
20
|
+
template-kit check --all # check every template in the workspace
|
|
21
|
+
template-kit check --json # machine-readable report (combine with any of the above)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run it from the workspace root, or from inside a template's own folder — a bare `check`
|
|
25
|
+
infers which template you mean. A workspace with more than one template must either name
|
|
26
|
+
one or pass `--all`.
|
|
27
|
+
|
|
28
|
+
### What it needs installed
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm i -D typescript eslint @typescript-eslint/parser esbuild
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`check` runs **your** toolchain, not a copy of its own: it resolves TypeScript, ESLint
|
|
35
|
+
and esbuild from your workspace, so the compiler that judges your types is the one you
|
|
36
|
+
pinned, and a linter and a bundler never end up in the production install of a package
|
|
37
|
+
whose job is to render sections. A missing one is reported by name, with the install
|
|
38
|
+
line to fix it.
|
|
39
|
+
|
|
40
|
+
## What it runs
|
|
41
|
+
|
|
42
|
+
Two granularities, because a workspace has one `package.json` and one lockfile, but can
|
|
43
|
+
hold many templates.
|
|
44
|
+
|
|
45
|
+
**Six authoring stages**, run in order, once per selected template. None of them abort
|
|
46
|
+
the run — a template with several unrelated problems reports all of them in one pass, not
|
|
47
|
+
one class of fix at a time:
|
|
48
|
+
|
|
49
|
+
1. **typecheck** — the workspace's own TypeScript compiles the template's files, judged
|
|
50
|
+
against the workspace's own `tsconfig.json`.
|
|
51
|
+
2. **lint** — the workspace's own ESLint, running the kit's shipped preset over the
|
|
52
|
+
template's files. See [the ESLint preset](eslint.md) for the rules this stage shares
|
|
53
|
+
with your editor.
|
|
54
|
+
3. **validate** — the schema, fill-spec, fixtures, and manifest contracts: every
|
|
55
|
+
cross-reference between a section's contract files, and a template's `manifest.json`
|
|
56
|
+
against the sections it composes.
|
|
57
|
+
4. **tree** — the rules a one-file-at-a-time linter cannot express: a missing contract
|
|
58
|
+
file, a slot declared in `schema.ts` with no marker anywhere in the section's JSX, a
|
|
59
|
+
hand-written CSS file's own rules.
|
|
60
|
+
5. **render** — a determinism probe (every fixture is rendered twice; the two outputs
|
|
61
|
+
must be byte-identical) and a scan of the rendered HTML for objective defects — a
|
|
62
|
+
stray `[object Object]`, a bare `null`/`undefined`/`NaN` leaf, a `src`-less `<img>` —
|
|
63
|
+
plus, for the `typical` fixture, that the editor's sidebar order matches the page's
|
|
64
|
+
real render order.
|
|
65
|
+
6. **size** — every section's renderer bundle and compiled stylesheet, gzipped, against a
|
|
66
|
+
fixed byte budget.
|
|
67
|
+
|
|
68
|
+
**The dependency gates** run once per invocation, never once per template — they are
|
|
69
|
+
properties of the workspace's one lockfile and installed tree, not of any one template:
|
|
70
|
+
a lockfile present and satisfying `package.json`, exactly one copy of React installed,
|
|
71
|
+
no `npm audit` finding at high or critical severity, and every shipped dependency's
|
|
72
|
+
license on an allowlist.
|
|
73
|
+
|
|
74
|
+
A run that selects zero templates to check is itself a failure —
|
|
75
|
+
[`no-templates`](rules/no-templates.md) — never a silent pass.
|
|
76
|
+
|
|
77
|
+
## Output
|
|
78
|
+
|
|
79
|
+
Human-readable output prints one line per template (`✓ <key>`, or `✗ <key> — N
|
|
80
|
+
problem(s)` followed by each finding), then any workspace-level findings from the
|
|
81
|
+
dependency gates, then `check passed` if everything passed.
|
|
82
|
+
|
|
83
|
+
`--json` prints one report object instead, keyed per template so a single-template
|
|
84
|
+
consumer (the registry, ingesting one submission) and an all-templates consumer (your
|
|
85
|
+
own CI) parse the same shape:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
{
|
|
89
|
+
ok: boolean,
|
|
90
|
+
kitVersion: string,
|
|
91
|
+
templates: {
|
|
92
|
+
[templateKey: string]: { ok: boolean, diagnostics: Diagnostic[] },
|
|
93
|
+
},
|
|
94
|
+
// Dependency-gate findings — evaluated once, not per template.
|
|
95
|
+
workspace: { ok: boolean, diagnostics: Diagnostic[] },
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Each `Diagnostic` carries a `ruleId` (`template-kit/<id>`), the `template` it belongs to,
|
|
100
|
+
an optional `section` and `file`/`line`, a `message`, and a `fix`.
|
|
101
|
+
|
|
102
|
+
## Exit code
|
|
103
|
+
|
|
104
|
+
`0` iff every selected template and the workspace pass. `1` otherwise — including when
|
|
105
|
+
the run itself can't proceed at all (an unknown template name, no `templates/` folder
|
|
106
|
+
in the workspace).
|
|
107
|
+
|
|
108
|
+
## Thresholds are not configurable
|
|
109
|
+
|
|
110
|
+
Every byte budget, severity floor, and license allowlist `check` enforces is a platform
|
|
111
|
+
acceptance criterion, not a per-workspace preference — there is no config file or flag
|
|
112
|
+
that raises them. If a template has a legitimate reason to need an exception, ask the
|
|
113
|
+
platform team; a workaround that quietly disables a gate will simply fail again at
|
|
114
|
+
submission.
|
|
115
|
+
|
|
116
|
+
## See also
|
|
117
|
+
|
|
118
|
+
- [Rules](rules/INDEX.md) — every id `check` (and the lint preset) can print, one page
|
|
119
|
+
each, Rule / Reason / Fix.
|
|
120
|
+
- [ESLint preset](eslint.md) — the one-file-at-a-time half of the same rule set, run
|
|
121
|
+
directly by your editor, not only through `check`.
|
package/docs/eslint.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
purpose: The authoring-lint preset shipped at `@homepages/template-kit/eslint` — setup
|
|
2
|
+
purpose: The authoring-lint preset shipped at `@homepages/template-kit/eslint` — setup and scope. Per-rule reference lives in `rules/`.
|
|
3
3
|
status: living
|
|
4
|
-
related: [INDEX.md, islands.md, theme-and-css.md, primitives.md]
|
|
4
|
+
related: [INDEX.md, rules/INDEX.md, check.md, islands.md, theme-and-css.md, primitives.md]
|
|
5
5
|
updated: 2026-07-14
|
|
6
6
|
---
|
|
7
7
|
# ESLint preset
|
|
@@ -12,6 +12,10 @@ rule namespace.
|
|
|
12
12
|
|
|
13
13
|
## Setup
|
|
14
14
|
|
|
15
|
+
```bash
|
|
16
|
+
npm i -D eslint @typescript-eslint/parser
|
|
17
|
+
```
|
|
18
|
+
|
|
15
19
|
```js
|
|
16
20
|
// eslint.config.mjs
|
|
17
21
|
import kit from "@homepages/template-kit/eslint";
|
|
@@ -19,9 +23,15 @@ export default kit;
|
|
|
19
23
|
```
|
|
20
24
|
|
|
21
25
|
That is the whole setup — no plugin registration, no parser wiring, no tsconfig.
|
|
22
|
-
The preset
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
The preset registers its rules and sets `languageOptions` (parser included) itself,
|
|
27
|
+
so those two lines are a complete config.
|
|
28
|
+
|
|
29
|
+
You install ESLint and the parser yourself because the kit deliberately does **not**
|
|
30
|
+
depend on them: a package whose main entry is a set of React primitives must not drag
|
|
31
|
+
a linter into the production install of every app that renders a template. The preset
|
|
32
|
+
resolves the parser from your workspace when you import it. If you see
|
|
33
|
+
`Cannot find package '@typescript-eslint/parser'`, that is the install line above
|
|
34
|
+
missing.
|
|
25
35
|
|
|
26
36
|
The preset is **syntactic only** — it never sets `parserOptions.project` or
|
|
27
37
|
`projectService`. Nothing it enforces requires type information, which is what
|
|
@@ -36,55 +46,20 @@ three config entries, matched by glob rather than by directory depth:
|
|
|
36
46
|
|
|
37
47
|
| Entry | Files | Rules |
|
|
38
48
|
|---|---|---|
|
|
39
|
-
| `template-kit/authoring` | `**/*.ts`, `**/*.tsx` | the
|
|
49
|
+
| `template-kit/authoring` | `**/*.ts`, `**/*.tsx` | the eleven file-scoped rules — see [`rules/`](rules/INDEX.md) |
|
|
40
50
|
| `template-kit/contract-files` | `**/Renderer.tsx`, `**/schema.ts`, `**/fill-spec.ts`, `**/fixtures.ts` | `no-client-directive-in-contract` |
|
|
41
51
|
| `template-kit/renderer` | `**/Renderer.tsx` | `props-from-schema` |
|
|
42
52
|
|
|
43
53
|
Server-vs-client scoping is decided **per file**, by the `"use client"` directive
|
|
44
|
-
prologue —
|
|
45
|
-
|
|
46
|
-
handlers, and inline `style` are legal there and the corresponding rules exempt
|
|
47
|
-
it. A rule that does not exempt islands says so explicitly below.
|
|
54
|
+
prologue — see [server vs client](rules/server-vs-client.md) for what that means
|
|
55
|
+
and exactly which rules stop at that boundary.
|
|
48
56
|
|
|
49
57
|
## The rules
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
| Rule | What it bans |
|
|
60
|
-
|---|---|
|
|
61
|
-
| `no-nondeterminism` | Clock/randomness reads (`Date.now()`, `new Date()` with no args, `Math.random()`, `crypto.randomUUID()`/`getRandomValues()`, `performance.now()`) — the publisher and the editor must render byte-identical HTML. Exempt inside an island. |
|
|
62
|
-
| `no-client-runtime-in-server` | Effects, state hooks, event handlers, and browser globals (`fetch`, `window`, `document`, …) — a Renderer must be a pure function of its props. Exempt inside an island. |
|
|
63
|
-
| `no-inline-style` | The `style=` JSX prop — it bypasses the cascade and Tailwind's responsive prefixes. Exempt inside an island. |
|
|
64
|
-
| `no-raw-element` | Raw `<section>`/`<img>` — each has a contract-bearing primitive (`Section`/`Image`) that must be used instead. |
|
|
65
|
-
| `image-bare-needs-reason` | `<Image bare>` with no `// bare: <reason>` comment justifying the skipped frame. |
|
|
66
|
-
| `no-css-import-from-render-path` | A `.css` import anywhere in a section's render path — the SSR graph drops it silently. Import CSS from the section's own `styles.css` instead. |
|
|
67
|
-
|
|
68
|
-
### Islands
|
|
69
|
-
|
|
70
|
-
| Rule | What it checks |
|
|
71
|
-
|---|---|
|
|
72
|
-
| `serializable-island-props` | A statically-visible prop passed to an island (a literal function, `new Date()`/`Map`/`Set`/etc., a bigint literal, `NaN`/`Infinity`) that cannot cross the server→browser JSON boundary. It is the static twin of the runtime check — see [islands.md](islands.md#props-must-be-json-serializable). |
|
|
73
|
-
|
|
74
|
-
### Universal — no island exemption
|
|
75
|
-
|
|
76
|
-
| Rule | What it bans |
|
|
77
|
-
|---|---|
|
|
78
|
-
| `no-hex` | Hex color literals and CSS color functions (`oklch`/`oklab`/`rgb`/`rgba`/`hsl`/`hsla`/`color-mix`) in a color context (a Tailwind color-utility bracket, an arbitrary-property color declaration, or an inline `style` value). Token discipline is universal — see [theme-and-css.md](theme-and-css.md#color-roles). |
|
|
79
|
-
|
|
80
|
-
Deliberate asymmetry: `no-inline-style` exempts islands, `no-hex` does not — the
|
|
81
|
-
hydration boundary is not a palette boundary. A section carries no palette of its
|
|
82
|
-
own in either render path.
|
|
83
|
-
|
|
84
|
-
### Markers and schema
|
|
85
|
-
|
|
86
|
-
| Rule | What it checks |
|
|
87
|
-
|---|---|
|
|
88
|
-
| `slot-marker-literal` | A slot-key-bearing marker (`<Slot id>`, `<SlotGroup id>`, `slotId`, `data-slot-id`, `data-slot-group`) must carry a string-literal key, not an expression — the slot-marker completeness check (`template-kit check`) reads these keys statically. `SlotItem` is exempt: its prop is `index: number`, an array position, so there is no schema key to cross-check. |
|
|
89
|
-
| `props-from-schema` | A `Renderer.tsx`'s exported `Props` type must come from `SectionProps<typeof schema>`. A hand-written `Props` shadows that inference and drifts. |
|
|
90
|
-
| `no-client-directive-in-contract` | `"use client"` in a section's four contract files (`Renderer.tsx`, `schema.ts`, `fill-spec.ts`, `fixtures.ts`) — the platform reads them outside a browser; marking one an island takes the section's server-rendered HTML with it. |
|
|
59
|
+
Every rule the preset enforces — what it bans, why, and a before/after fix — is
|
|
60
|
+
documented once, per id, in [`rules/`](rules/INDEX.md). Rules scoped to a single
|
|
61
|
+
file are enforced here, by this preset; rules scoped to the **tree** — a missing
|
|
62
|
+
contract file, a slot declared in `schema.ts` but unmarked in `Renderer.tsx`, a CSS
|
|
63
|
+
budget — cannot be expressed one-file-at-a-time and belong to
|
|
64
|
+
[`template-kit check`](check.md) under the same `template-kit/<id>` namespace. One
|
|
65
|
+
namespace, two venues: the id printed by either failure resolves to exactly one page.
|
package/docs/llms.txt
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
> inside the npm package so an agent can read it locally, versioned in lockstep
|
|
5
5
|
> with the code it describes.
|
|
6
6
|
|
|
7
|
-
Status: 0.0.x. The schema system, the contract primitives, and the theme
|
|
7
|
+
Status: 0.0.x. The schema system, the contract primitives, and the theme system are
|
|
8
8
|
complete — a section is authorable end to end against the kit. The `template-kit`
|
|
9
|
-
CLI's commands
|
|
9
|
+
CLI's `check` and `theme` commands are implemented — see [Check](check.md) and
|
|
10
|
+
[Theme and CSS](theme-and-css.md#generating-themecss); its other commands arrive with
|
|
11
|
+
the surfaces they drive.
|
|
10
12
|
|
|
11
13
|
Everything an author writes a section against is imported from the package root —
|
|
12
14
|
`@homepages/template-kit`, no subpath imports. The two CSS entries
|
|
@@ -27,14 +29,27 @@ Everything an author writes a section against is imported from the package root
|
|
|
27
29
|
and `Image` (responsive `<picture>`, empty-state box). Plus the marker-attribute
|
|
28
30
|
contract (`ATTR_SLOT_ID`, `ATTR_SLOT_ITEM`, …). Presentation is the template's own JSX.
|
|
29
31
|
- [Theme and CSS](theme-and-css.md): the two CSS entries and how they load; import
|
|
30
|
-
Tailwind exactly once; `
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
the
|
|
32
|
+
Tailwind exactly once; `base.css` ships no reset of its own — it requires the
|
|
33
|
+
preflight your Tailwind entry brings; `TokenTheme` — a template declares its own
|
|
34
|
+
tokens under names it chooses (`colors` `fonts` `type` `radii` `shadows` `layout`,
|
|
35
|
+
kebab-case, emitted verbatim) and each becomes a Tailwind utility; the `template-kit
|
|
36
|
+
theme` CLI command that compiles `theme.ts` to the generated `theme.css` the
|
|
37
|
+
Tailwind entry `@import`s after the kit's `styles.css`; the required `document`
|
|
38
|
+
block; the breakpoint ladder (base 350, `bp390` `bp425` `bp512` `md` `lg` `bp1440`
|
|
39
|
+
`bp1920`); the opt-in `content-visibility` recipe for long, image-heavy pages, and
|
|
40
|
+
why it requires a full-screen overlay to be appended to `document.body` rather than
|
|
41
|
+
rendered inside the section.
|
|
35
42
|
- [README](../README.md): install, exports, and the release flow.
|
|
36
43
|
- [Islands](islands.md): interactivity — client components, the serializable-props contract, editor options, and the browser loader.
|
|
37
|
-
- [ESLint preset](eslint.md): `@homepages/template-kit/eslint`, the
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
- [ESLint preset](eslint.md): `@homepages/template-kit/eslint`, the flat-config preset —
|
|
45
|
+
a two-line config with no parser wiring, over an ESLint + `@typescript-eslint/parser`
|
|
46
|
+
install of your own (the kit depends on neither — both resolve from your workspace).
|
|
47
|
+
Its three-entry glob scope. Per-rule reference lives in `rules/`.
|
|
48
|
+
- [Check](check.md): the `template-kit check` CLI command — the toolchain it runs (your
|
|
49
|
+
TypeScript, ESLint and esbuild, resolved from your workspace), its six per-template
|
|
50
|
+
stages, the once-per-invocation dependency gates, `--all`/`--json`, the report shape,
|
|
51
|
+
and the exit code. Thresholds are fixed platform acceptance criteria, not configurable.
|
|
52
|
+
- [Rules](rules/INDEX.md): every rule-id a lint or `check` failure can print, under the
|
|
53
|
+
`template-kit/<id>` namespace, one page each at `docs/rules/<id>.md` — Rule / Reason /
|
|
54
|
+
Fix / Before→After. **The loop:** see an id in a failure → read `docs/rules/<id>.md` →
|
|
55
|
+
fix. Nothing else to search for; the id is the filename.
|
package/docs/primitives.md
CHANGED
|
@@ -176,6 +176,11 @@ When `src` is empty the wrapper renders alone as a neutral `role="img"` rect, ke
|
|
|
176
176
|
layout box and the slot selectable. **That deterministic empty state is the reason the
|
|
177
177
|
frame exists**, so pass `aspectRatio` rather than sizing the image from its own content.
|
|
178
178
|
|
|
179
|
+
The frame's placeholder fill rides on `.tr-image-frame` (from
|
|
180
|
+
[`base.css`](theme-and-css.md#basecss-requires-a-tailwind-entry)), not on a theme utility:
|
|
181
|
+
you name your own tokens, so no primitive may assume a colour of yours exists. Retint it
|
|
182
|
+
per template by setting `--tr-image-frame-bg`; it defaults to a neutral grey.
|
|
183
|
+
|
|
179
184
|
**Responsive.** Given a `responsive` descriptor the `<img>` is wrapped in `<picture>` with
|
|
180
185
|
per-format AVIF/WebP `<source srcset>` (the base raster stays on the `<img>`), plus a
|
|
181
186
|
media-gated mobile crop when `responsiveMobile` + `mobileBreakpoint` are set, and carries
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
purpose: Router for the rule corpus — every template-kit/<id> an author can hit, and the page that explains it.
|
|
3
|
+
status: living
|
|
4
|
+
related: [server-vs-client.md, ../eslint.md, ../check.md, ../INDEX.md]
|
|
5
|
+
updated: 2026-07-14
|
|
6
|
+
---
|
|
7
|
+
# Rules
|
|
8
|
+
|
|
9
|
+
Every authoring rule the kit enforces is printed under one namespace, `template-kit/<id>`,
|
|
10
|
+
and resolves to exactly one page here: `docs/rules/<id>.md`.
|
|
11
|
+
|
|
12
|
+
**One namespace, two venues.** Some rules are properties of **one file** (a `Date.now()`
|
|
13
|
+
in a Renderer) and are enforced by the ESLint preset. Others are properties of the
|
|
14
|
+
**tree** (a slot declared in `schema.ts` but never marked in the JSX, a stylesheet, a
|
|
15
|
+
dependency, a byte budget) and cannot be expressed one file at a time; those are enforced
|
|
16
|
+
by [`template-kit check`](../check.md). You never need to know which engine produced a
|
|
17
|
+
violation — only the id, which names its page either way.
|
|
18
|
+
|
|
19
|
+
Read [server vs client](server-vs-client.md) first if you are here for any rule that
|
|
20
|
+
mentions "server-rendered code" or an island. It is the one concept those rules assume,
|
|
21
|
+
and it is explained there rather than on each of them.
|
|
22
|
+
|
|
23
|
+
| Rule | Venue | What it enforces |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| [`no-nondeterminism`](no-nondeterminism.md) | eslint | No clock or randomness reads in server-rendered code. |
|
|
26
|
+
| [`no-client-runtime-in-server`](no-client-runtime-in-server.md) | eslint | No effects, state, event handlers, network, or browser globals in server-rendered code. |
|
|
27
|
+
| [`serializable-island-props`](serializable-island-props.md) | eslint | Props handed to an island must be JSON-serializable. |
|
|
28
|
+
| [`no-client-directive-in-contract`](no-client-directive-in-contract.md) | eslint | No "use client" in a section's four contract files. |
|
|
29
|
+
| [`props-from-schema`](props-from-schema.md) | eslint | A Renderer's Props type must be derived from schema.ts, not hand-written. |
|
|
30
|
+
| [`no-inline-style`](no-inline-style.md) | eslint | No inline style= in server-rendered JSX. Islands are exempt. |
|
|
31
|
+
| [`no-raw-element`](no-raw-element.md) | eslint | No raw `<section>` or `<img>` — use `<Section>` and `<Image>`. |
|
|
32
|
+
| [`image-bare-needs-reason`](image-bare-needs-reason.md) | eslint | `<Image bare>` needs a `// bare:` comment saying why it skips the frame. |
|
|
33
|
+
| [`slot-marker-literal`](slot-marker-literal.md) | eslint | A slot-marker key must be a string literal, not an expression. |
|
|
34
|
+
| [`no-hex`](no-hex.md) | eslint | No hard-coded colours — the one legal colour function is a `color-mix()` over theme-derived arguments. Islands are NOT exempt. |
|
|
35
|
+
| [`no-css-import-from-render-path`](no-css-import-from-render-path.md) | eslint | No relative/absolute .css import from a section's render path — a package's is the sanctioned channel. |
|
|
36
|
+
| [`bundle-incomplete`](bundle-incomplete.md) | check | A section folder is missing one of its four required files. |
|
|
37
|
+
| [`bundle-binary-asset`](bundle-binary-asset.md) | check | A binary asset file sits in a section folder — not supported yet. |
|
|
38
|
+
| [`schema-invalid`](schema-invalid.md) | check | A section's schema.ts or fill-spec.ts fails a contract assertion, or cannot be loaded. |
|
|
39
|
+
| [`fixtures-invalid`](fixtures-invalid.md) | check | A section's fixtures.ts does not satisfy the fixture-module shape. |
|
|
40
|
+
| [`manifest-invalid`](manifest-invalid.md) | check | A template's manifest.json is missing, unparseable, or violates the manifest contract. |
|
|
41
|
+
| [`missing-slot-marker`](missing-slot-marker.md) | check | An editable slot in schema.ts has no marker in the section's JSX. |
|
|
42
|
+
| [`slot-group-marker`](slot-group-marker.md) | check | A group with two or more members has no `<SlotGroup>` wrapper. |
|
|
43
|
+
| [`css-reason`](css-reason.md) | check | A hand-written CSS file must open with a `css-reason:` comment. |
|
|
44
|
+
| [`no-bare-css-import`](no-bare-css-import.md) | check | No @import in hand-written CSS — the browser drops it silently. |
|
|
45
|
+
| [`unlayered-fence`](unlayered-fence.md) | check | An `/* unlayered: */` escape hatch must be balanced and give a reason. |
|
|
46
|
+
| [`sidebar-order`](sidebar-order.md) | check | Schema slot order must match the order the page renders them in. |
|
|
47
|
+
| [`typecheck`](typecheck.md) | check | The template must compile under TypeScript. |
|
|
48
|
+
| [`parse-error`](parse-error.md) | check | A file could not be parsed at all — a syntax error, not a rule violation. |
|
|
49
|
+
| [`determinism-drift`](determinism-drift.md) | check | Rendering the same fixture twice must produce byte-identical HTML. |
|
|
50
|
+
| [`render-invariant`](render-invariant.md) | check | Rendered output must carry no [object Object], bare null/undefined/NaN leaf, or src-less `<img>`. |
|
|
51
|
+
| [`lockfile-missing`](lockfile-missing.md) | check | The workspace has no package-lock.json. |
|
|
52
|
+
| [`lockfile-stale`](lockfile-stale.md) | check | The lockfile does not satisfy package.json — an `npm ci` would fail. |
|
|
53
|
+
| [`single-react`](single-react.md) | check | The installed tree carries more than one copy of react or react-dom. |
|
|
54
|
+
| [`audit-severity`](audit-severity.md) | check | A dependency carries a high or critical security advisory. |
|
|
55
|
+
| [`license-denied`](license-denied.md) | check | A production dependency's license is not on the allowlist. |
|
|
56
|
+
| [`size-renderer-bundle`](size-renderer-bundle.md) | check | A section's renderer bundle exceeds its byte budget. |
|
|
57
|
+
| [`size-section-css`](size-section-css.md) | check | A section's compiled CSS exceeds its byte budget. |
|
|
58
|
+
| [`no-templates`](no-templates.md) | check | The workspace has no template to check at all. |
|
|
59
|
+
|
|
60
|
+
## See also
|
|
61
|
+
|
|
62
|
+
- [Server vs client](server-vs-client.md) — how a file is scoped, and which rules stop at
|
|
63
|
+
the hydration boundary.
|
|
64
|
+
- [ESLint preset](../eslint.md) — setting the preset up in a template workspace.
|
|
65
|
+
- [Check](../check.md) — running `template-kit check`, its flags, output shape, and exit code.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
purpose: The template-kit/audit-severity authoring rule — what it enforces, why, and how to fix it.
|
|
3
|
+
status: living
|
|
4
|
+
related: [INDEX.md, license-denied.md]
|
|
5
|
+
updated: 2026-07-14
|
|
6
|
+
---
|
|
7
|
+
# template-kit/audit-severity
|
|
8
|
+
|
|
9
|
+
> A dependency with a **high** or **critical** advisory fails. Low and moderate pass, on
|
|
10
|
+
> purpose. There is no way to suppress it.
|
|
11
|
+
|
|
12
|
+
## Rule
|
|
13
|
+
|
|
14
|
+
`npm audit` runs over your workspace, and a violation is reported for each advisory at
|
|
15
|
+
severity **high** or **critical**.
|
|
16
|
+
|
|
17
|
+
**Low and moderate advisories are a deliberate pass.** They are not "not yet enforced" —
|
|
18
|
+
they are not enforced. Nobody will ask you to clear them.
|
|
19
|
+
|
|
20
|
+
**There is no suppression mechanism.** No inline waiver, no ignore file, no severity
|
|
21
|
+
override. If you are looking for the flag, there isn't one, and that is the design.
|
|
22
|
+
|
|
23
|
+
## Reason
|
|
24
|
+
|
|
25
|
+
The kit lets you reach for third-party packages — a carousel, a map SDK, a date formatter —
|
|
26
|
+
because a section that has to hand-write everything is a section nobody finishes. The
|
|
27
|
+
consequence is that your dependency tree ships to a published page we host for a customer,
|
|
28
|
+
so the supply chain is part of the submission.
|
|
29
|
+
|
|
30
|
+
This gate is the **floor**, not a review. Cutting at high/critical is what keeps it a floor:
|
|
31
|
+
a rule that fired on every transitive moderate would be a rule everyone learned to route
|
|
32
|
+
around, and the exception would swallow the check. Two severities, no exceptions, and a
|
|
33
|
+
clear answer to "can I ship this."
|
|
34
|
+
|
|
35
|
+
## Fix
|
|
36
|
+
|
|
37
|
+
In order of preference:
|
|
38
|
+
|
|
39
|
+
1. **`npm audit fix`** — resolves most advisories by moving a transitive dependency inside
|
|
40
|
+
its existing range.
|
|
41
|
+
2. **Upgrade the offending package.** If the fix is in a new major, `npm audit fix --force`
|
|
42
|
+
will take it, but read what it changes first.
|
|
43
|
+
3. **Drop the package.** An advisory with no fix available is a package with no fix
|
|
44
|
+
available. Replace it, or hand-write the part of it you actually use.
|
|
45
|
+
|
|
46
|
+
Then re-run `npm audit` and commit the lockfile.
|
|
47
|
+
|
|
48
|
+
### Before
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
$ npm audit
|
|
52
|
+
# npm audit report
|
|
53
|
+
|
|
54
|
+
marked <4.0.10
|
|
55
|
+
Severity: high
|
|
56
|
+
Inefficient Regular Expression Complexity in marked
|
|
57
|
+
fix available via `npm audit fix --force`
|
|
58
|
+
|
|
59
|
+
1 high severity vulnerability
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```jsonc
|
|
63
|
+
// package.json
|
|
64
|
+
{
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"marked": "^3.0.8"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### After
|
|
72
|
+
|
|
73
|
+
```jsonc
|
|
74
|
+
// package.json
|
|
75
|
+
{
|
|
76
|
+
"dependencies": {
|
|
77
|
+
"marked": "^14.1.2"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
$ npm audit
|
|
84
|
+
found 0 vulnerabilities
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## See also
|
|
88
|
+
|
|
89
|
+
- [`license-denied`](license-denied.md) — the other supply-chain gate on your production
|
|
90
|
+
dependencies.
|
|
91
|
+
- [`lockfile-stale`](lockfile-stale.md) — commit the lockfile the fix regenerated.
|