basalt-ui 1.19.1 → 1.20.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/AGENTS.md +15 -1
- package/CHANGELOG.md +20 -0
- package/MIGRATING.md +228 -0
- package/README.md +53 -9
- package/agent/rules/basalt-app.md +43 -0
- package/agent/rules/basalt-charts.md +26 -14
- package/agent/rules/basalt-mantine.md +25 -10
- package/agent/rules/basalt-router.md +22 -10
- package/agent/rules/basalt-tokens.md +75 -6
- package/agent/skills/basalt-app/SKILL.md +42 -6
- package/configs/check.yml +11 -4
- package/configs/lefthook.yml +21 -2
- package/configs/oxlint-plugin.js +515 -25
- package/configs/oxlint-plugin.test.ts +383 -10
- package/configs/oxlint.json +10 -1
- package/dist/agent/stick-to-bottom.js +1 -1
- package/dist/agent-chat/thread-detail-panel.js +1 -1
- package/dist/agent-chat/thread-message.js +2 -2
- package/dist/agent-chat/tool-chip.js +1 -1
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +1 -1
- package/dist/charts/primitives/ChartLegend.js +2 -2
- package/dist/charts/primitives/ChartTooltip.js +1 -1
- package/dist/cli/index.d.ts +107 -15
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +610 -72
- package/dist/cli/toolchain-wiring.test.d.ts +2 -0
- package/dist/cli/toolchain-wiring.test.d.ts.map +1 -0
- package/dist/dashboard/delta-badge.js +2 -2
- package/dist/dashboard/stat-card.js +1 -1
- package/dist/data/virtual-list.js +5 -5
- package/dist/guard/index.d.ts +56 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +326 -59
- package/dist/guard/types.d.ts +25 -5
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/register.d.ts +25 -0
- package/dist/register.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.module.css +11 -3
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.js +1 -1
- package/dist/tokens/index.d.ts +3 -3
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +2 -0
- package/dist/tokens/palette.d.ts +35 -27
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +24 -18
- package/dist/tokens.css +19 -17
- package/dist/vite-color-mix.js +2 -2
- package/llms.txt +3 -3
- package/package.json +4 -3
- package/src/agent/stick-to-bottom.tsx +1 -1
- package/src/agent-chat/thread-detail-panel.tsx +1 -1
- package/src/agent-chat/thread-message.tsx +3 -3
- package/src/agent-chat/tool-chip.tsx +1 -1
- package/src/charts/kinds/Donut.tsx +2 -1
- package/src/charts/primitives/ChartLegend.tsx +2 -2
- package/src/charts/primitives/ChartTooltip.tsx +1 -1
- package/src/cli/index.ts +1070 -99
- package/src/cli/init-sync.test.ts +19 -3
- package/src/cli/tokens-css.test.ts +27 -14
- package/src/cli/toolchain-wiring.test.ts +694 -0
- package/src/dashboard/delta-badge.tsx +3 -3
- package/src/dashboard/stat-card.tsx +1 -1
- package/src/data/virtual-list.tsx +8 -8
- package/src/guard/check-source.test.ts +663 -11
- package/src/guard/index.ts +727 -94
- package/src/guard/types.ts +29 -4
- package/src/register.ts +32 -0
- package/src/shell/app-mobile-nav.module.css +11 -3
- package/src/shell/app-mobile-nav.tsx +1 -1
- package/src/surfaces.ts +1 -1
- package/src/theme/density-relations.test.ts +8 -5
- package/src/theme/spacing.test.ts +2 -0
- package/src/theme-toggle/index.tsx +2 -2
- package/src/tokens/index.ts +5 -3
- package/src/tokens/palette.ts +24 -18
- package/src/vite-color-mix.ts +2 -2
|
@@ -104,10 +104,16 @@ Install `@tanstack/react-router` as a peer, then import from `basalt-ui/router-t
|
|
|
104
104
|
|
|
105
105
|
**Prerequisites:**
|
|
106
106
|
|
|
107
|
-
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
- **The only hard prerequisite is the `Register` module augmentation** (see "The prerequisite that
|
|
108
|
+
makes all of this real" below). `defineNav` / `navGroup` / `navTarget` / `flattenNav` / `useNav` /
|
|
109
|
+
`createSearchParamStore` all work against a hand-written `createRouter({ routeTree })` with
|
|
110
|
+
code-defined routes, no Vite plugin and no generated tree — verified end-to-end in a consumer,
|
|
111
|
+
including that a bad `to` still prints the real route-path union.
|
|
112
|
+
- **`@tanstack/router-plugin/vite` + `tsr generate` are needed only for `staticData`** — that is,
|
|
113
|
+
for `useRouterBreadcrumbs` and any nav metadata read off the route tree. The
|
|
114
|
+
`StaticDataRouteOption` augmentation takes effect once `src/routeTree.gen.ts` exists. In CI and
|
|
115
|
+
after adding routes, run `tsr generate` (or `bun run typecheck`, which triggers it) before relying
|
|
116
|
+
on breadcrumb data.
|
|
111
117
|
- The loader's `context.queryClient` requires `createRootRouteWithContext<{ queryClient: QueryClient }>()`.
|
|
112
118
|
Run `basalt-ui init` to scaffold a `query-client.ts` + `__root.tsx` seed that wires the TanStack
|
|
113
119
|
Router + Query context correctly — the generated `__root.tsx` uses
|
|
@@ -255,12 +261,18 @@ With that missing, every `to` widens to `string`, a nonexistent route path compi
|
|
|
255
261
|
required `search` compiles, and the API looks like it is working while catching nothing. This is
|
|
256
262
|
the single highest-value thing to verify when adopting the config: change one `to` to a garbage
|
|
257
263
|
path and confirm you get a compile error listing the real route paths. If you don't, the
|
|
258
|
-
augmentation is missing or `tsr generate` has not run.
|
|
259
|
-
|
|
260
|
-
Second thing to know before you migrate:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
augmentation is missing (or, on a file-route setup, `tsr generate` has not run).
|
|
265
|
+
|
|
266
|
+
Second thing to know before you migrate: whether a missing `search` is caught depends on the
|
|
267
|
+
schema's OUTPUT type, not on the `validateSearch` signature. If every key of `T` is required —
|
|
268
|
+
which is what a Zod object with defaults produces, since defaults apply on parse and the output has
|
|
269
|
+
no optionals — TanStack raises `MakeRequiredSearchParams` and the compiler catches it. Two routes in
|
|
270
|
+
one consumer had been shipping nav links with no search params behind a `to={target.to as never}`
|
|
271
|
+
cast; removing the callback surfaced both immediately.
|
|
272
|
+
|
|
273
|
+
**The caveat holds only when `T` has no required keys** (every key optional, or the validator
|
|
274
|
+
returns a partial). There, a forgotten `search` compiles — diff the old target table against the new
|
|
275
|
+
definition by hand, key by key. Everywhere else, trust the error.
|
|
264
276
|
|
|
265
277
|
Error messages here are the router's own and they are large: one wrong `to` prints the whole
|
|
266
278
|
route-path union plus a `RouterCore<…>` blob, because TanStack's `Constrain` falls back to the
|
|
@@ -24,12 +24,76 @@ status pops — never floods. (Blueprint/Basalt zinc-charcoal are the historical
|
|
|
24
24
|
ancestors; `docs/DESIGN-SPEC.md` in the basalt-ui repo supersedes both — see its "Doctrine
|
|
25
25
|
inversions" section.) This
|
|
26
26
|
rule is the operational checklist; it is enforced mechanically by **`basalt-ui check-theme`**
|
|
27
|
-
(wire it into `lint`: `oxlint . && basalt-ui check-theme`
|
|
28
|
-
`theme-allow
|
|
27
|
+
(wire it into `lint`: `oxlint . && basalt-ui check-theme`; `init` seeds that as `lint:basalt`). A
|
|
28
|
+
violation fails the build. Escape hatch: a scoped `theme-allow <rule-id> — <reason>` comment
|
|
29
|
+
(diff-visible, deliberate) — see the contract below.
|
|
29
30
|
|
|
30
31
|
`check-theme` reads its config from your `package.json` `"basalt"` key
|
|
31
|
-
(`{ roots?, exempt?, spacingSteps?, forbiddenAccents? }`); set `roots`
|
|
32
|
-
to the files that legitimately define palette values.
|
|
32
|
+
(`{ roots?, exempt?, include?, profile?, severity?, spacingSteps?, forbiddenAccents? }`); set `roots`
|
|
33
|
+
to your source dirs and `exempt` to the files that legitimately define palette values.
|
|
34
|
+
|
|
35
|
+
**`roots` is not optional in practice.** Omitted it falls back to `src` relative to the cwd — fine
|
|
36
|
+
for a single-app repo, zero files for anything under `apps/*/src`, and `check-theme` then exits 1
|
|
37
|
+
with `0 files scanned`. Since 1.20.0 `init` writes a real `roots` from the detected layout and
|
|
38
|
+
`doctor`'s `guard-scan` check fails when the configured roots resolve to nothing; before that a repo
|
|
39
|
+
could look fully wired while the gate scanned nothing.
|
|
40
|
+
|
|
41
|
+
The scan reaches slightly beyond `roots`: each root's PARENT contributes its `index.html` and its
|
|
42
|
+
`public/` tree (the Vite layout basalt's own preset assumes), because that is where a `theme-color`
|
|
43
|
+
meta and a webmanifest's `background_color` actually live. `.json` is never blanket-scanned — name a
|
|
44
|
+
design-surface JSON in `basalt.include`, the only route to one.
|
|
45
|
+
|
|
46
|
+
## `theme-allow` — the 1.20.0 contract
|
|
47
|
+
|
|
48
|
+
Scope it. `theme-allow <rule-id> — <reason>` waives that ONE kind; the id is a guard kind
|
|
49
|
+
(`raw-surface`, `inline-spacing`, …) or an oxlint plugin rule (`hand-rolled-plot`,
|
|
50
|
+
`raw-scroll-container`, …), with or without the `basalt/` prefix.
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// theme-allow raw-surface — third-party widget needs a literal corner
|
|
54
|
+
<Widget style={{ borderRadius: 3 }} />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- **A bare `theme-allow` still waives everything**, so no upgrade takes a build down over comment
|
|
58
|
+
placement — but it now reports `theme-allow-unscoped` (`warn` for one minor, then `error`).
|
|
59
|
+
Rescope the ones you have; that is the whole migration.
|
|
60
|
+
- **Spell the id right — a typo waives nothing.** A word in the id slot that names no rule is
|
|
61
|
+
recorded as unknown and suppresses nothing; the annotation covers exactly the ids it got right.
|
|
62
|
+
Only a genuinely bare `theme-allow` is the blanket form. Because the id slot is read strictly, a
|
|
63
|
+
prose reason has to be introduced by a separator: `theme-allow: <why>` or
|
|
64
|
+
`theme-allow <id> — <why>`, never `theme-allow <why>`.
|
|
65
|
+
- **Three placements work and both engines agree on all three**: the reported line, a comment-ONLY
|
|
66
|
+
line directly above it (the only form JSX can express — the reported line is usually a multi-line
|
|
67
|
+
opening tag or a `{expr}` child), and in CSS a trailing annotation reaching back over the
|
|
68
|
+
declaration it terminates. The third is what survives the shipped `oxfmt` reflowing a long
|
|
69
|
+
`background-color` so the hex lands ABOVE the comment.
|
|
70
|
+
- **`basalt/hand-rolled-plot` no longer grants whole-file immunity** off whichever comment happened
|
|
71
|
+
to sit on the first assembly node. Every node is waived on its own; a file-scoped exception needs
|
|
72
|
+
a written declaration that both names the rule and gives a reason, anywhere in the file.
|
|
73
|
+
|
|
74
|
+
### New in 1.20.0 — five guard kinds, all `warn` for one minor
|
|
75
|
+
|
|
76
|
+
| Kind | Fires on |
|
|
77
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
78
|
+
| `theme-allow-unscoped` | a `theme-allow` with no rule id, no reason, or an id that names no rule |
|
|
79
|
+
| `surface-shadow-override` | a `boxShadow` built FROM tokens that REPLACES `--vx-shadow-card` instead of composing with it |
|
|
80
|
+
| `css-raw-surface` | the kebab dialect of the surface kinds in CSS (`border-radius: 6px`); sub-scale corners and circle/pill values pass |
|
|
81
|
+
| `inline-font-size` | `style={{ fontSize: 11 }}` — the `check-theme` half of `basalt/no-raw-font-size`, for a CI that runs only the guard |
|
|
82
|
+
| `hidden-inline-style` | a multi-line opening tag, or a style object hoisted to a const — `raw-html-layout`'s formatting-independent half |
|
|
83
|
+
|
|
84
|
+
Two new oxlint rules ship at `warn` alongside them: **`basalt/shadow-basalt-export`** (below) and
|
|
85
|
+
**`basalt/hand-rolled-shell`** (`AppShell` parts or `Burger` in a file that does not render
|
|
86
|
+
`BasaltShell`). **`basalt/raw-size-literal` is now `error`** — CSS-length strings on
|
|
87
|
+
`size`/`fz`/`fontSize`. Every kind keeps its own boolean, `basalt.severity` and `exemptRules`.
|
|
88
|
+
|
|
89
|
+
## The guard sees palette, not vocabulary
|
|
90
|
+
|
|
91
|
+
Round 4 swept seven consumer repos: every gate green, and ~15 independently re-rolled copies of
|
|
92
|
+
components basalt already ships (`StatCard` in 4 of 4 apps, `EmptyState`, a 266-line hand-rolled
|
|
93
|
+
`AppShell`). A fork written by a token-fluent author uses exactly the right tokens, so no palette
|
|
94
|
+
guard can ever see it. **`basalt/shadow-basalt-export` is the cheap detector** — it warns when a
|
|
95
|
+
local component's name collides with a live basalt export, read from the real shipped barrel. The
|
|
96
|
+
habit it stands in for: check whether basalt already ships the thing before building it.
|
|
33
97
|
|
|
34
98
|
## Filled surfaces — the fill band
|
|
35
99
|
|
|
@@ -158,5 +222,10 @@ export const paletteGroups = { [`${GROUP}-`]: SERIES } // -> { 'activity-': SERI
|
|
|
158
222
|
## When the guard fires
|
|
159
223
|
|
|
160
224
|
Fix the source, don't silence it — reach for the right token first. Only add `theme-allow` for a
|
|
161
|
-
genuine, documented exception (
|
|
162
|
-
|
|
225
|
+
genuine, documented exception (a third-party widget needing a literal), scoped to the rule id, with
|
|
226
|
+
a reason: a bare `// theme-allow` passes the check, tells the next reader nothing, and now reports
|
|
227
|
+
`theme-allow-unscoped` besides. The palette-definition files are listed in your `exempt` set so they
|
|
228
|
+
don't self-trip. A stylesheet emitted by `basalt-ui tokens:css` needs no entry — in a `.css` file
|
|
229
|
+
carrying that exact two-line `@generated basalt-ui` header, the guard skips the LINES that are
|
|
230
|
+
basalt custom properties, selectors, `}` or self-closing comments. Pasting the marker anywhere else
|
|
231
|
+
suppresses nothing, and an ordinary declaration added to such a file is still reported.
|
|
@@ -53,6 +53,28 @@ own the files.
|
|
|
53
53
|
- `.basalt/manifest.json` — a sha256 per managed file + the basalt-ui version that wrote it, so
|
|
54
54
|
`sync` can three-way diff and `doctor` can spot a stale install.
|
|
55
55
|
|
|
56
|
+
It also patches `package.json`: **`basalt.roots`** inferred from the real layout (workspace packages
|
|
57
|
+
depending on basalt or Mantine, else every workspace `src/`, else `src`) and a **`lint:basalt`**
|
|
58
|
+
script (`oxlint . && basalt-ui check-theme`). Everything derives from `roots` — the guard's scan, the
|
|
59
|
+
seeded CI `oxfmt` globs, the default exemption — so a workspace repo without it scaffolds a guard
|
|
60
|
+
that scans zero files while every gate reports green. Correct it if your sources live elsewhere. A
|
|
61
|
+
Mantine-free consumer adds `"basalt": { "profile": "tokens-only" }` by hand; `check-theme` never
|
|
62
|
+
infers that, because inferring it from a missing `@mantine/core` would silence 17 kinds on a repo
|
|
63
|
+
that simply keeps Mantine in another workspace package.
|
|
64
|
+
|
|
65
|
+
**`init` on an EXISTING app is a lint-debt event, not a no-op.** Adopting the shipped preset turns
|
|
66
|
+
on whole oxlint plugins the repo was never linted against, so previously-clean code lands with real
|
|
67
|
+
findings on the first run. `init` names the plugins and every file it kept — plus what keeping it
|
|
68
|
+
costs (an existing `.oxlintrc.json` that does not `extends` the preset means the entire basalt lint
|
|
69
|
+
half is off, and nothing else says so; `--merge-lint` splices it in). Run `oxlint .` and triage
|
|
70
|
+
before the next commit; turn a rule off with a written reason rather than disabling a plugin.
|
|
71
|
+
|
|
72
|
+
Then `basalt-ui doctor`. `SKIPPED` is a third outcome beside pass/warn/fail and exits non-zero on
|
|
73
|
+
its own — a check that could not run is not a check that passed — and the hard checks now include
|
|
74
|
+
`basalt-resolves`, `guard-scan` (would `check-theme` cover more than zero files?) and
|
|
75
|
+
`oxlint-preset` (does `.oxlintrc.json` really extend the preset? JSONC is parsed, not rejected).
|
|
76
|
+
Expect doctor to go red where it was green on an older basalt; that is the point.
|
|
77
|
+
|
|
56
78
|
After `init`, do the runtime wiring (the CLI scaffolds files, not your app's composition):
|
|
57
79
|
|
|
58
80
|
```tsx
|
|
@@ -94,10 +116,15 @@ ordering (e.g. alongside `@content-collections/vite`) and the full head/PWA/mani
|
|
|
94
116
|
import 'basalt-ui/styles.css' // @layer basalt base styles, iOS input safety net, font stack
|
|
95
117
|
```
|
|
96
118
|
|
|
97
|
-
Lint wires as `oxlint . && basalt-ui check-theme`
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
119
|
+
Lint wires as `oxlint . && basalt-ui check-theme` (seeded as the `lint:basalt` script) — the theme
|
|
120
|
+
guard is the teeth behind the token doctrine. Before writing any composite, check whether basalt
|
|
121
|
+
already ships it: round 4 found ~15 forked copies of shipped components across seven repos, all
|
|
122
|
+
passing every gate, because a fork by a token-fluent author uses exactly the right tokens.
|
|
123
|
+
`basalt/shadow-basalt-export` warns on the name collision; nothing catches the rest.
|
|
124
|
+
|
|
125
|
+
The consumer series file (`<first basalt.root>/lib/series.ts` by default — so `src/lib/series.ts` on
|
|
126
|
+
a plain app, `apps/web/src/lib/series.ts` on a monorepo; override via `basalt.seriesModulePath`) is
|
|
127
|
+
the single guard-exempt palette source — see `/basalt-charts`.
|
|
101
128
|
|
|
102
129
|
## Refresh: `bunx basalt-ui sync`
|
|
103
130
|
|
|
@@ -140,13 +167,22 @@ both.
|
|
|
140
167
|
- A thin `DESIGN.md` (deltas) + the thirteen `basalt-*` rules + the managed CLAUDE block are present.
|
|
141
168
|
- `<first basalt.root>/lib/series.ts` exists as the one guard-exempt series source (for any app metric colors).
|
|
142
169
|
- `basalt-ui sync --check` wired in CI (recommended) to catch drift on future upgrades.
|
|
170
|
+
- `basalt-ui doctor` run after `init` and read to the last line — a `SKIPPED` check exits non-zero
|
|
171
|
+
and is not a pass.
|
|
143
172
|
|
|
144
173
|
## Notes
|
|
145
174
|
|
|
146
175
|
- `init`, `sync`, and `check-theme` are all **real**. `sync` reconciles via a sha256 three-way diff
|
|
147
176
|
(`--check` is a CI drift gate, `--force` overwrites local edits); `check-theme` is the palette
|
|
148
|
-
guard; `doctor`
|
|
149
|
-
`bunx basalt-ui …`.
|
|
177
|
+
guard; `doctor` checks resolution, version, guard scan, oxlint preset, spacing-scale drift and
|
|
178
|
+
cross-package `ai` major parity. All via `bunx basalt-ui …`.
|
|
179
|
+
- **Not a React app?** `bunx basalt-ui tokens:css --out src/tokens.css` emits the `--vx-*` layer with
|
|
180
|
+
no package in your dependency tree, `--selector-class dark` for the Tailwind `<html class="dark">`
|
|
181
|
+
convention, and `fonts:css` emits the shipped `--basalt-font-*` stacks. Both carry the two-line
|
|
182
|
+
`@generated basalt-ui` header the guard skips on — that header verbatim, and then only the LINES
|
|
183
|
+
that are basalt custom properties, selectors, `}` or self-closing comments, so the marker pasted
|
|
184
|
+
into a `.tsx` suppresses nothing and a declaration added to such a file is still reported — and
|
|
185
|
+
both take `--check` as a CI drift gate. See `docs/FRAMEWORK-FREE.md` in the basalt-ui repo.
|
|
150
186
|
- The framework ships **no** icon or notification dep — pass icons as `ReactNode` and wire toasts
|
|
151
187
|
yourself (e.g. `ThemeLabControls`' `copyIcon` / `onCopy`).
|
|
152
188
|
- The framework DOES ship `motion` (motion.dev, formerly framer-motion) as a bundled dependency —
|
package/configs/check.yml
CHANGED
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
# color literals / off-identity accents that bypass the --vx-* token system).
|
|
6
6
|
# `basalt-ui sync --check` fails when the basalt-managed files (rules, skills, the
|
|
7
7
|
# CLAUDE block) have drifted from the installed basalt-ui version.
|
|
8
|
-
# The oxfmt globs below are rendered from `basalt.roots` at init time —
|
|
9
|
-
#
|
|
8
|
+
# The oxfmt globs below are rendered from `basalt.roots` at init time — `init` writes that
|
|
9
|
+
# key from the repo's real layout (workspace packages included), so a monorepo does not
|
|
10
|
+
# seed a `src/**` that matches nothing.
|
|
11
|
+
# The basalt-ui command below is the LOCAL bin, rendered from where basalt actually
|
|
12
|
+
# installed. `bunx basalt-ui` is deliberately not used: when the package is not resolvable
|
|
13
|
+
# from the working directory (bun's isolated linker puts it beside the package that depends
|
|
14
|
+
# on it, not at the root) bunx silently downloads a DIFFERENT copy from npm and the gate
|
|
15
|
+
# runs a version nobody pinned.
|
|
10
16
|
name: check
|
|
11
17
|
on:
|
|
12
18
|
pull_request:
|
|
@@ -23,8 +29,9 @@ jobs:
|
|
|
23
29
|
bun-version: latest
|
|
24
30
|
- run: bun install --frozen-lockfile
|
|
25
31
|
- run: bunx oxlint .
|
|
26
|
-
- run:
|
|
27
|
-
- run:
|
|
32
|
+
- run: '{{BASALT_BIN}} doctor'
|
|
33
|
+
- run: '{{BASALT_BIN}} check-theme'
|
|
34
|
+
- run: '{{BASALT_BIN}} sync --check'
|
|
28
35
|
- run: bunx oxfmt {{ROOTS_GLOBS}} '!**/dist/**' --check
|
|
29
36
|
# Enable once your app defines these package.json scripts:
|
|
30
37
|
# - run: bun run typecheck
|
package/configs/lefthook.yml
CHANGED
|
@@ -4,8 +4,17 @@
|
|
|
4
4
|
# - node_modules/basalt-ui/configs/lefthook.yml
|
|
5
5
|
# lefthook merges these commands with the consumer's own, so this preset auto-updates
|
|
6
6
|
# with the package. Runs oxlint + oxfmt on staged files and the theme guard on the whole
|
|
7
|
-
# tree, pre-commit. Assumes oxlint/oxfmt are installed
|
|
8
|
-
#
|
|
7
|
+
# tree, pre-commit. Assumes oxlint/oxfmt are installed.
|
|
8
|
+
#
|
|
9
|
+
# It does NOT assume your `.oxlintrc.json` extends the shipped preset — that assumption was
|
|
10
|
+
# false for exactly the repos this seed exists for, since `init` KEEPS an existing oxlint
|
|
11
|
+
# config. `basalt-ui doctor` now checks the extends and hard-fails when it is missing; run it
|
|
12
|
+
# in CI (the seeded check.yml does) rather than trusting a comment nobody reads.
|
|
13
|
+
#
|
|
14
|
+
# `init` seeds the path RESOLVED from where basalt actually installed. Under bun's isolated
|
|
15
|
+
# linker basalt lives beside the package that depends on it, not at the repo root, and a
|
|
16
|
+
# root-relative `extends` there resolves to nothing — silently, because lefthook merges an
|
|
17
|
+
# empty file without complaint.
|
|
9
18
|
pre-commit:
|
|
10
19
|
parallel: true
|
|
11
20
|
commands:
|
|
@@ -28,5 +37,15 @@ pre-commit:
|
|
|
28
37
|
# was written against: a guard that only runs when someone remembers to run it is not a guard.
|
|
29
38
|
# Without this, a consumer accumulates violations indefinitely while `check-theme` is never
|
|
30
39
|
# invoked, and the kinds it never ran are indistinguishable from the kinds that found nothing.
|
|
40
|
+
#
|
|
41
|
+
# Lefthook runs commands at the REPO ROOT, while in a monorepo the `basalt` config and the
|
|
42
|
+
# manifest live in a package below it — verified to produce `0 files scanned`, i.e. following
|
|
43
|
+
# this preset installed a gate that never gated. `check-theme` now relocates to the single
|
|
44
|
+
# workspace package carrying a basalt config and says so; with more than one, set BASALT_CWD
|
|
45
|
+
# in your own lefthook.yml to name the package:
|
|
46
|
+
# pre-commit:
|
|
47
|
+
# commands:
|
|
48
|
+
# check-theme:
|
|
49
|
+
# env: { BASALT_CWD: apps/web }
|
|
31
50
|
check-theme:
|
|
32
51
|
run: bunx basalt-ui check-theme
|