basalt-ui 1.29.2 → 1.30.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 +9 -0
- package/MIGRATING.md +693 -368
- package/README.md +23 -2
- package/agent/rules/basalt-controls.md +34 -34
- package/agent/skills/basalt-app/SKILL.md +4 -2
- package/agent/templates/query-client.ts.tpl +18 -4
- package/configs/lefthook.yml +51 -19
- package/configs/oxlint-plugin.js +210 -84
- package/configs/oxlint-plugin.test.ts +182 -5
- package/configs/oxlint.json +4 -4
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +2 -2
- package/dist/charts/primitives/ChartFrame.d.ts +23 -3
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +4 -4
- package/dist/charts/primitives/chart-frame-layout.d.ts +55 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +16 -0
- package/dist/cli/check-theme.d.ts.map +1 -1
- package/dist/cli/check-theme.js +6 -0
- package/dist/cli/doctor.d.ts +10 -4
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +28 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +2 -1
- package/dist/cli/index.d.ts +12 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +29 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +4 -15
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +8 -14
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +12 -2
- package/dist/dashboard/section.module.css +15 -7
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +7 -2
- package/dist/dashboard/stat-group.d.ts +8 -2
- package/dist/dashboard/stat-group.d.ts.map +1 -1
- package/dist/dashboard/stat-group.js +23 -15
- package/dist/dashboard/stat-group.module.css +85 -9
- package/dist/dashboard/stat-group.module.css.d.ts +1 -0
- package/dist/dashboard/widget-grid.d.ts.map +1 -1
- package/dist/dashboard/widget-grid.js +4 -5
- package/dist/dashboard/widget-grid.module.css +64 -21
- package/dist/dashboard/widget-grid.module.css.d.ts +1 -0
- package/dist/guard/index.d.ts +19 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +27 -4
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/shell/app-brand.module.css +12 -3
- package/dist/shell/app-header.module.css +49 -9
- package/dist/shell/app-main.module.css +24 -4
- package/dist/shell/app-mobile-nav.d.ts.map +1 -1
- package/dist/shell/app-mobile-nav.js +98 -51
- package/dist/shell/app-mobile-nav.module.css +99 -11
- package/dist/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/dist/shell/app-sidebar.module.css +55 -2
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +3 -3
- package/dist/shell/mobile-nav-model.d.ts +26 -7
- package/dist/shell/mobile-nav-model.d.ts.map +1 -1
- package/dist/shell/mobile-nav-model.js +12 -3
- package/dist/shell/page-aside.module.css +8 -6
- package/dist/shell/page-bar.d.ts +13 -0
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +13 -3
- package/dist/shell/page-bar.module.css +20 -12
- package/dist/shell/page-title.module.css +3 -1
- package/dist/theme/use-basalt-spacing.d.ts.map +1 -1
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +51 -7
- package/dist/tokens/palette.d.ts +98 -17
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +110 -18
- package/dist/tokens.css +17 -5
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/charts/primitives/ChartCard.tsx +12 -7
- package/src/charts/primitives/ChartFrame.tsx +30 -15
- package/src/charts/primitives/chart-frame-layout.ts +86 -0
- package/src/cli/check-theme.ts +13 -0
- package/src/cli/doctor.ts +49 -8
- package/src/cli/help.ts +5 -3
- package/src/cli/index.ts +49 -0
- package/src/cli/init.ts +6 -27
- package/src/cli/sync.ts +11 -22
- package/src/controls/actions.tsx +51 -0
- package/src/dashboard/section.module.css +15 -7
- package/src/dashboard/stat-card.tsx +7 -2
- package/src/dashboard/stat-group.module.css +85 -9
- package/src/dashboard/stat-group.module.css.d.ts +1 -0
- package/src/dashboard/stat-group.tsx +40 -20
- package/src/dashboard/widget-grid.module.css +64 -21
- package/src/dashboard/widget-grid.module.css.d.ts +1 -0
- package/src/dashboard/widget-grid.tsx +30 -12
- package/src/guard/index.ts +79 -11
- package/src/index.ts +9 -0
- package/src/shell/app-brand.module.css +12 -3
- package/src/shell/app-header.module.css +49 -9
- package/src/shell/app-main.module.css +24 -4
- package/src/shell/app-mobile-nav.module.css +99 -11
- package/src/shell/app-mobile-nav.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.tsx +225 -83
- package/src/shell/app-sidebar.module.css +55 -2
- package/src/shell/index.tsx +26 -6
- package/src/shell/mobile-nav-model.ts +37 -9
- package/src/shell/page-aside.module.css +8 -6
- package/src/shell/page-bar.module.css +20 -12
- package/src/shell/page-bar.tsx +39 -5
- package/src/shell/page-title.module.css +3 -1
- package/src/theme/use-basalt-spacing.ts +8 -6
- package/src/tokens/index.ts +90 -6
- package/src/tokens/palette.ts +111 -18
package/README.md
CHANGED
|
@@ -241,7 +241,12 @@ exemption:
|
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
`check-theme` fails loudly (instead of a false `✓`) when the configured roots resolve to zero
|
|
244
|
-
scanned files, and `doctor`'s `guard-scan` check agrees with it
|
|
244
|
+
scanned files, and `doctor`'s `guard-scan` check agrees with it — it shares `scannableFiles` with
|
|
245
|
+
`check-theme`, so the two cannot disagree about what would be scanned. (`guard-scan` and
|
|
246
|
+
`install-parity` were absent from `doctor` for four minors up to 1.29.2, with no MIGRATING row, and
|
|
247
|
+
are back in 1.30.0. While they were gone a consumer CI step that ran `doctor` to catch an
|
|
248
|
+
install/manifest version mismatch caught nothing, zero-file roots could ship as a permanently green
|
|
249
|
+
guard, and this sentence described a check that did not exist.) Each root's **parent** also
|
|
245
250
|
contributes its `index.html` and its `public/` tree — the Vite layout `basaltViteConfig` assumes,
|
|
246
251
|
and where a raw `theme-color` or webmanifest `background_color` actually lives. `.json` is never
|
|
247
252
|
blanket-scanned; `include` names one explicitly and is the only route to it. `profile:
|
|
@@ -375,7 +380,7 @@ Apply opacity with `alpha(token, a)` (backed by `color-mix`) — never `rgba()`,
|
|
|
375
380
|
App-specific series colors live in the consumer, not the framework. The framework ships the factories:
|
|
376
381
|
|
|
377
382
|
```ts
|
|
378
|
-
// src/
|
|
383
|
+
// src/lib/series.ts — the ONE path check-theme exempts by default (see the note below)
|
|
379
384
|
import { defineSeries, groupTokens } from 'basalt-ui/tokens'
|
|
380
385
|
|
|
381
386
|
// 1. Declare the series with light/dark pairs (ColorPair shape — hex strings per scheme)
|
|
@@ -402,6 +407,22 @@ export const colors = groupTokens(GROUP, SERIES)
|
|
|
402
407
|
export const paletteGroups = { [`${GROUP}-`]: SERIES }
|
|
403
408
|
```
|
|
404
409
|
|
|
410
|
+
> **Put it at `<first basalt.root>/lib/series.ts`, or the guard reports it.** `defineSeries` takes
|
|
411
|
+
> hex `ColorPair`s _by type_, so this is the one file the doctrine tells you to write raw hex in —
|
|
412
|
+
> and the exemption is an exact PATH, not a shape: `check-theme` skips
|
|
413
|
+
> `<first basalt.root>/lib/series.ts` (the same value `DESIGN.md`'s `{{SERIES_MODULE_PATH}}`
|
|
414
|
+
> renders, so the two cannot drift), and nothing special-cases a file merely _because_ it calls
|
|
415
|
+
> `defineSeries`. A series module anywhere else — `src/theme/series.ts`, say — reports `raw-hex`
|
|
416
|
+
> twice per pair. Two ways out, and the second is not the same as the first:
|
|
417
|
+
>
|
|
418
|
+
> ```json
|
|
419
|
+
> // package.json — `seriesModulePath` MOVES the default exemption; `exempt` REPLACES it wholesale
|
|
420
|
+
> { "basalt": { "seriesModulePath": "src/theme/series.ts" } }
|
|
421
|
+
> ```
|
|
422
|
+
>
|
|
423
|
+
> Writing your own `basalt.exempt` array REPLACES the series default rather than adding to it, so
|
|
424
|
+
> list your series module in that array explicitly whenever you use the key at all.
|
|
425
|
+
|
|
405
426
|
```tsx
|
|
406
427
|
// main.tsx — wire paletteGroups into BasaltProvider
|
|
407
428
|
import { BasaltProvider, createBasaltTheme } from 'basalt-ui'
|
|
@@ -42,8 +42,7 @@ size tier and one persistence binding, and basalt owns all three. The API is in
|
|
|
42
42
|
| C15 | Every touch target inside a home clears the `touchControlHeight` floor below `sm`. The mobile `Filters (n)` sheet draws no row of its own — it renders the same `PanelRow` the aside's panel surface does. |
|
|
43
43
|
| C16 | A new guard lands `warn` with a dated `promote` version, and the build fails once the package version reaches it while the rule is still `warn`. |
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
**advisory** — the generated header above says so. A green lint run is not evidence they hold.
|
|
45
|
+
A green lint run is not evidence the advisory laws the generated header above lists hold.
|
|
47
46
|
|
|
48
47
|
## The three homes
|
|
49
48
|
|
|
@@ -52,12 +51,10 @@ C1's cross-file case, a hand-rolled section heading, C11 outside `BasaltDataTabl
|
|
|
52
51
|
| `PageBar` | `actions`, `sync`, `filters`, `filtersEnd`, `tabs` | `ctl` | the page's filters, tabs, actions, refresh |
|
|
53
52
|
| `Section` | `actions`, `tabs` | `ctl` | that section's own controls and its count |
|
|
54
53
|
| `WidgetHeader` / `ChartCard` / `StatCard` / `BasaltDataTable` / `SettingsSection` | `actions` | `ctl` | that widget's own controls and its count |
|
|
55
|
-
| `SettingsRow`
|
|
56
|
-
| `FormRow` / `FormGroup` (`basalt-ui/forms`) | children | Mantine `md` | ONE form field or a labelled cluster |
|
|
54
|
+
| `SettingsRow` · `FormRow` / `FormGroup` (`basalt-ui/forms`) | `control` · children | Mantine `md` | ONE form field, or a labelled cluster |
|
|
57
55
|
|
|
58
56
|
- **Inside `BasaltShell`** both `PageBar` rows are portals (header / the band above the scrollport)
|
|
59
57
|
— where you write `<PageBar>` never moves it. Without a shell both rows render in flow, sticky.
|
|
60
|
-
The header height is a token, never state.
|
|
61
58
|
- **The form row keeps Mantine's own `md` tier** — a raw `Select` in `SettingsRow.control` is the
|
|
62
59
|
right answer, `size` there is load-bearing. Neither `SettingsRow` nor `FormRow`/`FormGroup` is a
|
|
63
60
|
tiered slot; no filter or size rule applies to them.
|
|
@@ -76,10 +73,9 @@ C1's cross-file case, a hand-rolled section heading, C11 outside `BasaltDataTabl
|
|
|
76
73
|
| `touchControlHeight` | hit area below `sm` | every home |
|
|
77
74
|
| `controlHeight` | `size="md"` | forms — unchanged |
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
own `sm`/`xs` are NOT re-pointed elsewhere. Exception: `ChartCard.actions` is Mantine-free, so it
|
|
76
|
+
A home sizes its **slot**, never its body: a raw `Button` in `PageBar.actions` is already the right
|
|
77
|
+
height with no prop, and a `size="xs"` typed there is law C5 (`basalt/control-size-literal`).
|
|
78
|
+
Mantine's own `sm`/`xs` are NOT re-pointed. Exception: `ChartCard.actions` is Mantine-free, so it
|
|
83
79
|
carries only the tier attribute and its controls size themselves.
|
|
84
80
|
|
|
85
81
|
## Binding a control to a store
|
|
@@ -103,52 +99,56 @@ The store, its fields and their lanes are basalt-state.md. What this file adds:
|
|
|
103
99
|
is a tsc error. `ActionGroup`/`OverflowMenu`/`SyncButton` take typed action DATA, never children.
|
|
104
100
|
|
|
105
101
|
**The date picker is injected, never imported.** `basalt-ui/controls` resolves with no
|
|
106
|
-
`@mantine/dates
|
|
107
|
-
`RangeFilter.customPicker`. Never import
|
|
108
|
-
`DateInput`/`DatePickerInput` inside a home slot.
|
|
102
|
+
`@mantine/dates`; `DateRangePicker` comes from `basalt-ui/controls-dates` through
|
|
103
|
+
`RangeFilter.customPicker`. Never import it in a shared module or reach for `DatePickerInput`.
|
|
109
104
|
|
|
110
105
|
## `FilterSet` owns the responsive story
|
|
111
106
|
|
|
112
|
-
Do not build one at the call site. Above `sm
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
one JSX line, no count or reset handler to maintain.
|
|
107
|
+
Do not build one at the call site. Above `sm` it measures its own children and folds the tail into
|
|
108
|
+
a `+N` pill. Below `sm` the first `inline` children stay pills and one `Filters (n)` pill opens a
|
|
109
|
+
sheet where every child is full-width and answers one `Reset all`. **`n` and `Reset all` are
|
|
110
|
+
DERIVED** from whether each field differs from its fallback — a filter is one JSX line, no handler.
|
|
117
111
|
|
|
118
|
-
| Below `sm`
|
|
119
|
-
|
|
|
120
|
-
| `PageBar` row 1
|
|
121
|
-
| `PageBar` row 2
|
|
122
|
-
| a section header | title · count · one inline action, the rest in a kebab; tabs past three become a `Select` |
|
|
123
|
-
| a widget header | value + delta wrap under the title; the sparkline drops to bleed; one `⋯` action |
|
|
112
|
+
| Below `sm` | What basalt does |
|
|
113
|
+
| --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
114
|
+
| `PageBar` row 1 | breadcrumb · the `primary` action as an icon · ONE kebab holding every `more` action |
|
|
115
|
+
| `PageBar` row 2 | line 1: `ViewTabs` full-width (a `Select` past three options) · line 2: one inline pill · `Filters (n)` · the aside's own pill |
|
|
124
116
|
|
|
125
117
|
Every swap is CSS inside the control (one mount), never a JS media query. Two mounts under
|
|
126
118
|
`visibleFrom`/`hiddenFrom` is `basalt/responsive-twin`.
|
|
127
119
|
|
|
128
120
|
## The aside
|
|
129
121
|
|
|
130
|
-
`PageAside` is a shell REGION, not a fourth home — C1 still names three
|
|
131
|
-
|
|
132
|
-
is a ROW in the aside — never two components, the surface is read from where it's mounted.
|
|
122
|
+
`PageAside` is a shell REGION, not a fourth home — C1 still names three, and its body IS one:
|
|
123
|
+
children scope to the `panel` surface, so a `SelectFilter` renders its ROW form there, not a pill.
|
|
133
124
|
|
|
134
|
-
|
|
125
|
+
```tsx
|
|
126
|
+
<PageAside title="Composition" persistKey="composition">
|
|
127
|
+
<PanelRow label="Weight">…</PanelRow>
|
|
128
|
+
</PageAside>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
| In a `PageAside` | What it is |
|
|
135
132
|
| ---------------- | --------------------------------------------------------------------------------- |
|
|
133
|
+
| `title` | REQUIRED — the header text AND the region's accessible name; omitted, it THROWS |
|
|
134
|
+
| `persistKey` | persists the fold at `basalt:aside:<persistKey>`; omitted the fold is per-mount |
|
|
135
|
+
| `defaultFolded` | the fold on first render, honoured only while nothing is persisted |
|
|
136
|
+
| `classNames` | the three boxes it paints — `root` / `header` / `body` |
|
|
136
137
|
| an inspector row | `PanelRow` — label above, optional `hint`, mono `readout`, `end` slot |
|
|
137
138
|
| a bound slider | `SliderControl` — min/max/step come off the handle, never props |
|
|
138
139
|
| a facet list | `MultiSelectFilter` + `counts` (and `max`, past which the tail folds) |
|
|
139
140
|
| a group of rows | `Section` — flush inside the aside, which draws the rhythm itself |
|
|
140
141
|
| a choice | `PanelChoice` — a track only while ≤3 options AND every label fits, else `Select` |
|
|
141
142
|
|
|
142
|
-
- ONE
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
it renders in flow. One node either way (C9).
|
|
143
|
+
- ONE per page, written AFTER the main content — tree position is reading order, not layout.
|
|
144
|
+
- Below `sm` it projects into `PageBar` row 2 as one pill carrying its own `title`, and that row
|
|
145
|
+
exists ONLY when the bar has `tabs`, `filters` or filter-end actions — without one, or with no
|
|
146
|
+
shell, it renders in flow at the BOTTOM of the page. One node either way (C9).
|
|
146
147
|
- A bound control outside all of this is `basalt/bound-control-outside-home` — a slot prop, a
|
|
147
148
|
`FilterSet`, a `PageAside` or a `PanelRow` is a home; nothing else is.
|
|
148
149
|
|
|
149
150
|
## Sidebar blocks
|
|
150
151
|
|
|
151
152
|
A non-destination list, a progress meter or a bespoke node in the sidebar is a `SidebarBlock` —
|
|
152
|
-
declared data, three kinds (`list`/`progress`/`custom`), placed `'nav'`/`'bottom'
|
|
153
|
-
|
|
154
|
-
fold, one More-sheet row per block. A `custom` block is desktop-only, by design.
|
|
153
|
+
declared data, three kinds (`list`/`progress`/`custom`), placed `'nav'`/`'bottom'`; the rail and
|
|
154
|
+
More-sheet projections are basalt's from there (C13). A `custom` block is desktop-only, by design.
|
|
@@ -55,8 +55,10 @@ export default defineConfig({
|
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Then `BasaltShell` with a `useNav(NAV)` spread (basalt-state.md), `PageBar` per page
|
|
58
|
-
(basalt-controls.md),
|
|
59
|
-
|
|
58
|
+
(basalt-controls.md), `PageAside` on any page that needs a right-hand inspector — a region a ROUTE
|
|
59
|
+
claims rather than a shell prop, so it never appears in `BasaltShell`'s props — and
|
|
60
|
+
`<first basalt.root>/lib/series.ts` as the app's one guard-exempt series source (`/basalt-charts`).
|
|
61
|
+
There is no Tailwind.
|
|
60
62
|
|
|
61
63
|
## 3. Verify the wiring took
|
|
62
64
|
|
|
@@ -2,11 +2,25 @@
|
|
|
2
2
|
* Basalt query client — configure once, share via router context.
|
|
3
3
|
* Extend staleTime / gcTime defaults and add per-query error handling here.
|
|
4
4
|
*
|
|
5
|
+
* A FACTORY, not a module-level singleton, and that is load-bearing under SSR: a
|
|
6
|
+
* `export const queryClient = createBasaltQueryClient()` is created once per PROCESS,
|
|
7
|
+
* so every request a Node server handles shares one cache — one user's data served to
|
|
8
|
+
* the next. Call `makeQueryClient()` once per request on the server, and once at module
|
|
9
|
+
* scope in a browser-only entry:
|
|
10
|
+
*
|
|
11
|
+
* // main.tsx (CSR) — one client for the tab's lifetime
|
|
12
|
+
* const queryClient = makeQueryClient()
|
|
13
|
+
*
|
|
14
|
+
* // an SSR/prerender entry — one per request, never hoisted
|
|
15
|
+
* const queryClient = makeQueryClient()
|
|
16
|
+
*
|
|
5
17
|
* Scaffold written by `basalt-ui init`. This file is yours — `basalt-ui sync` will not overwrite it.
|
|
6
18
|
*/
|
|
7
19
|
import { createBasaltQueryClient } from 'basalt-ui'
|
|
8
20
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
export function makeQueryClient() {
|
|
22
|
+
return createBasaltQueryClient({
|
|
23
|
+
// staleTime: 60_000, // optional: global cache freshness window (ms)
|
|
24
|
+
// gcTime: 5 * 60_000, // optional: inactive-query garbage-collection window (ms)
|
|
25
|
+
})
|
|
26
|
+
}
|
package/configs/lefthook.yml
CHANGED
|
@@ -10,10 +10,16 @@
|
|
|
10
10
|
# `lefthook dump`: declare `pre-commit.commands.oxfmt.run` (or `glob:`) in your file and the
|
|
11
11
|
# preset's value is what runs — yours is discarded silently, with no warning. Only keys this preset
|
|
12
12
|
# does NOT define merge in from yours. So:
|
|
13
|
-
# • `env:`, `exclude:`, `skip:` and any command name not used here — yours, they merge.
|
|
13
|
+
# • `env:`, `root:`, `exclude:`, `skip:` and any command name not used here — yours, they merge.
|
|
14
14
|
# • `run:` and `glob:` on a command below — NOT overridable. Do not try.
|
|
15
15
|
# The sanctioned seams are therefore:
|
|
16
|
-
# • `
|
|
16
|
+
# • `root:` — the ONE seam that fixes a monorepo, and the one nobody guesses. Lefthook runs every
|
|
17
|
+
# command at the REPO ROOT; `root:` moves one command into your package and rewrites
|
|
18
|
+
# `{staged_files}` relative to it. Every command below needs it when the app is not the repo
|
|
19
|
+
# root — otherwise `oxlint`/`oxfmt` run where there is no config and `check-theme` scans
|
|
20
|
+
# nothing. See the recipe at the bottom of this file.
|
|
21
|
+
# • `BASALT_CWD` / `BASALT_BIN` (below) — set them via `env:` to point the guard at a package and
|
|
22
|
+
# at a specific CLI. An alternative to `root:` for `check-theme` only.
|
|
17
23
|
# • `exclude:` — carve files out of a glob you cannot narrow.
|
|
18
24
|
# • a differently-named command of your own, plus `skip: true` on the one here you are replacing.
|
|
19
25
|
#
|
|
@@ -62,23 +68,49 @@ pre-commit:
|
|
|
62
68
|
# Without this, a consumer accumulates violations indefinitely while `check-theme` is never
|
|
63
69
|
# invoked, and the kinds it never ran are indistinguishable from the kinds that found nothing.
|
|
64
70
|
#
|
|
65
|
-
# `$BASALT_BIN`
|
|
66
|
-
# overridable (see the note at the top).
|
|
67
|
-
# • `--no-install` because a plain `bunx basalt-ui` in a repo where basalt is not resolvable
|
|
68
|
-
# from the ROOT (bun's isolated linker puts it beside the package that depends on it)
|
|
69
|
-
# silently downloads a DIFFERENT copy from npm and gates the commit with a version nobody
|
|
70
|
-
# pinned. With the flag it fails loudly instead, which is the answer you can act on.
|
|
71
|
-
# • point it at the local bin to fix that, exactly as the seeded check.yml does:
|
|
72
|
-
# pre-commit:
|
|
73
|
-
# commands:
|
|
74
|
-
# check-theme:
|
|
75
|
-
# env: { BASALT_BIN: ./packages/web/node_modules/.bin/basalt-ui }
|
|
71
|
+
# `$BASALT_CWD` and `$BASALT_BIN` are the sanctioned override seams here, because `run:` itself
|
|
72
|
+
# is not overridable (see the note at the top).
|
|
76
73
|
#
|
|
77
74
|
# Lefthook runs commands at the REPO ROOT, while in a monorepo the `basalt` config and the
|
|
78
|
-
# manifest live in a package below it
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
75
|
+
# manifest live in a package below it. Since 1.29.0 the CLI resolves ONE way — `BASALT_CWD`,
|
|
76
|
+
# else the invocation cwd — and infers nothing from siblings or parents, so at the root it
|
|
77
|
+
# reads a directory with no basalt config and scans nothing. That used to be an unexplained
|
|
78
|
+
# `0 files scanned`; it now names this preset and the two fixes. `BASALT_CWD` is the fix that
|
|
79
|
+
# needs no other change, and this `run:` derives the bin from it so one setting covers both:
|
|
80
|
+
# pre-commit:
|
|
81
|
+
# commands:
|
|
82
|
+
# check-theme:
|
|
83
|
+
# env: { BASALT_CWD: apps/web }
|
|
84
|
+
#
|
|
85
|
+
# The default bin is `<BASALT_CWD>/node_modules/.bin/basalt-ui`, i.e. the local bin of the
|
|
86
|
+
# package the guard is being run FOR, and plain `node_modules/.bin/basalt-ui` when BASALT_CWD
|
|
87
|
+
# is unset. That replaces a `bunx --no-install basalt-ui` default which resolved to nothing at
|
|
88
|
+
# the root under bun's isolated linker — the linker puts basalt beside the package that depends
|
|
89
|
+
# on it, never at the root, so the default was broken in exactly the layout BASALT_CWD exists
|
|
90
|
+
# for. A missing bin now fails with the path it looked for instead of `bunx`'s resolver error.
|
|
91
|
+
# Override it (a hoisted install, a pinned copy) with `env: { BASALT_BIN: … }` in the same
|
|
92
|
+
# block; it wins outright.
|
|
83
93
|
check-theme:
|
|
84
|
-
run: ${BASALT_BIN
|
|
94
|
+
run: ${BASALT_BIN:-${BASALT_CWD:+$BASALT_CWD/}node_modules/.bin/basalt-ui} check-theme
|
|
95
|
+
|
|
96
|
+
# ── MONOREPO RECIPE (the app is NOT at the repo root) ────────────────────────────────────────────
|
|
97
|
+
# `extends` merges the commands above, but NOT a working directory — so at the repo root `oxlint`
|
|
98
|
+
# and `oxfmt` run where your `.oxlintrc.json` and your binaries are not, and `check-theme` scans a
|
|
99
|
+
# directory with no basalt config. Add `root:` per command in YOUR root lefthook.yml — it merges,
|
|
100
|
+
# because the preset deliberately does not define it:
|
|
101
|
+
#
|
|
102
|
+
# # <repo>/lefthook.yml
|
|
103
|
+
# extends:
|
|
104
|
+
# - apps/web/node_modules/basalt-ui/configs/lefthook.yml
|
|
105
|
+
# pre-commit:
|
|
106
|
+
# commands:
|
|
107
|
+
# oxlint: { root: 'apps/web/' }
|
|
108
|
+
# oxfmt: { root: 'apps/web/' }
|
|
109
|
+
# check-theme: { root: 'apps/web/' }
|
|
110
|
+
#
|
|
111
|
+
# One mechanism for all three, and `check-theme` needs nothing else: `root:` moves the invocation
|
|
112
|
+
# cwd, which is the only thing the CLI resolves from. `env: { BASALT_CWD: apps/web }` is the
|
|
113
|
+
# equivalent for the guard alone — reach for it when the command has to keep running at the root.
|
|
114
|
+
#
|
|
115
|
+
# `basalt-ui doctor` reads the MERGED config (`lefthook dump`) rather than this file, so it is what
|
|
116
|
+
# tells you whether the result actually gates.
|