@assure-one/design-system 1.39.0 → 1.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -28
- package/codemods/README.md +24 -7
- package/codemods/lib/registry.mjs +27 -0
- package/codemods/lib/runner.mjs +8 -3
- package/codemods/run.mjs +27 -5
- package/codemods/transforms/cm-15-dom-selectors.mjs +16 -9
- package/dist/brand-scope-BOHId6BY.d.ts +58 -0
- package/dist/{brand-theme-B6rem2II.d.ts → brand-theme-ZYM867Jz.d.ts} +1 -1
- package/dist/chunk-2MKLYYIY.js +3754 -0
- package/dist/chunk-2MKLYYIY.js.map +1 -0
- package/dist/chunk-6S2R2VWN.js +5527 -0
- package/dist/chunk-6S2R2VWN.js.map +1 -0
- package/dist/chunk-7U3ADR2Q.js +4336 -0
- package/dist/chunk-7U3ADR2Q.js.map +1 -0
- package/dist/chunk-CPOGSNXS.js +389 -0
- package/dist/chunk-CPOGSNXS.js.map +1 -0
- package/dist/chunk-EUHGZA6Q.js +313 -0
- package/dist/chunk-EUHGZA6Q.js.map +1 -0
- package/dist/chunk-KQBYTTAN.js +1829 -0
- package/dist/chunk-KQBYTTAN.js.map +1 -0
- package/dist/chunk-X76J4QSJ.js +164 -0
- package/dist/chunk-X76J4QSJ.js.map +1 -0
- package/dist/css/components.css +1 -1
- package/dist/css/legacy-aliases.css +2 -0
- package/dist/css/tailwind.css +2 -0
- package/dist/css/tokens.css +2 -0
- package/dist/data-table-view-ep8al7P9.d.ts +1132 -0
- package/dist/{design-system-provider-BM_F1C13.d.ts → design-system-provider-CfD2g2lY.d.ts} +11 -12
- package/dist/icons/index.d.ts +1 -1
- package/dist/icons/index.js +2 -1835
- package/dist/icons/index.js.map +1 -1
- package/dist/index.d.ts +373 -388
- package/dist/index.js +8688 -24271
- package/dist/index.js.map +1 -1
- package/dist/legacy/index.d.ts +1462 -7
- package/dist/legacy/index.js +5 -1
- package/dist/legacy/index.js.map +1 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/styles.css +2 -1
- package/dist/suite/index.d.ts +989 -4
- package/dist/suite/index.js +6 -1
- package/dist/suite/index.js.map +1 -1
- package/dist/testing/index.cjs +3 -2
- package/dist/testing/index.js +3 -2
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.js +3 -856
- package/dist/tokens/index.js.map +1 -1
- package/dist/{types-k_kjwipx.d.ts → types-Coqw8iaS.d.ts} +28 -1
- package/docs/components.md +54 -54
- package/docs/components.registry.json +6 -6
- package/docs/for-ai-agents.md +12 -6
- package/package.json +5 -3
- package/dist/index-BAAuIQqP.d.ts +0 -2579
- package/dist/index-DxBjmBBp.d.ts +0 -1038
package/README.md
CHANGED
|
@@ -27,13 +27,23 @@ The single source of truth for visual design across all Assure products.
|
|
|
27
27
|
pnpm add @assure-one/design-system
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
local classes and responsive variants remain authoritative even when a bundler
|
|
33
|
-
reorders CSS chunks:
|
|
30
|
+
Load the design system's CSS from your app's global stylesheet, as the split
|
|
31
|
+
entries, under one layer statement (Tailwind 4 in your build):
|
|
34
32
|
|
|
35
|
-
```
|
|
36
|
-
|
|
33
|
+
```css
|
|
34
|
+
/* src/app/globals.css — the layer statement is the first line of the file */
|
|
35
|
+
@layer theme, base, ds, components, utilities;
|
|
36
|
+
|
|
37
|
+
@import "tailwindcss";
|
|
38
|
+
|
|
39
|
+
@import "@assure-one/design-system/css/tokens.css";
|
|
40
|
+
@import "@assure-one/design-system/css/legacy-aliases.css"; /* optional */
|
|
41
|
+
@import "@assure-one/design-system/css/tailwind.css";
|
|
42
|
+
@import "@assure-one/design-system/css/shadcn.css"; /* optional */
|
|
43
|
+
@import "@assure-one/design-system/css/base.css"; /* optional */
|
|
44
|
+
@import "@assure-one/design-system/css/components.css";
|
|
45
|
+
|
|
46
|
+
@import "./ds-preset.css"; /* your app's preset, optional */
|
|
37
47
|
```
|
|
38
48
|
|
|
39
49
|
Then use components:
|
|
@@ -46,32 +56,42 @@ export default function Page() {
|
|
|
46
56
|
}
|
|
47
57
|
```
|
|
48
58
|
|
|
49
|
-
### The split CSS entries
|
|
59
|
+
### The split CSS entries
|
|
50
60
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
The split entries are the documented default CSS integration since 1.40. Every
|
|
62
|
+
design-system class is `ds:`-prefixed and compiled into `css/components.css`
|
|
63
|
+
inside `@layer ds`, so your own utilities always win over component styles and
|
|
64
|
+
your Tailwind build never has to scan our package.
|
|
55
65
|
|
|
56
|
-
| Subpath | What it is
|
|
57
|
-
| -------------------------- |
|
|
58
|
-
| `./css/tokens.css` | The `--ds-*` design tokens: `:root`, the colour scheme scope and the brand scopes. Usable without any component.
|
|
59
|
-
| `./css/legacy-aliases.css` | Read-aliases from today's token names to the namespaced ones (`--color-surface: var(--ds-color-surface)`), for as long as your own CSS reads design-system token names.
|
|
60
|
-
| `./css/tailwind.css` | A Tailwind `@theme` bridge, so `bg-surface/40`, `hover:text-fg-3` and `rounded-control` compile in _your_ build instead of silently producing nothing.
|
|
61
|
-
| `./css/shadcn.css` | The app-vocabulary bridge: `background`, `foreground`, `primary`, `muted`, `destructive`, the radius scale and the rest, each reading one `--app-*` input you can override.
|
|
62
|
-
| `./css/base.css` | Optional document defaults: body background, ink, font and `color-scheme`. Ships no preflight, no element rules and no font import.
|
|
63
|
-
| `./css/components.css` | The component utilities compiled with Tailwind's `ds` prefix (`ds:flex`, `ds:bg-surface`), inside `@layer ds` and bound to the `--ds-*` tokens.
|
|
66
|
+
| Subpath | What it is |
|
|
67
|
+
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `./css/tokens.css` | The `--ds-*` design tokens: `:root`, the colour scheme scope and the brand scopes. Usable without any component. |
|
|
69
|
+
| `./css/legacy-aliases.css` | Read-aliases from today's token names to the namespaced ones (`--color-surface: var(--ds-color-surface)`), for as long as your own CSS reads design-system token names. |
|
|
70
|
+
| `./css/tailwind.css` | A Tailwind `@theme` bridge, so `bg-surface/40`, `hover:text-fg-3` and `rounded-control` compile in _your_ build instead of silently producing nothing. |
|
|
71
|
+
| `./css/shadcn.css` | The app-vocabulary bridge: `background`, `foreground`, `primary`, `muted`, `destructive`, the radius scale and the rest, each reading one `--app-*` input you can override. |
|
|
72
|
+
| `./css/base.css` | Optional document defaults: body background, ink, font and `color-scheme`. Ships no preflight, no element rules and no font import. |
|
|
73
|
+
| `./css/components.css` | The component utilities compiled with Tailwind's `ds` prefix (`ds:flex`, `ds:bg-surface`), inside `@layer ds` and bound to the `--ds-*` tokens. |
|
|
64
74
|
|
|
65
75
|
Import order is `tokens.css` → `legacy-aliases.css` (optional) → `tailwind.css`
|
|
66
76
|
→ `shadcn.css` (optional) → `base.css` (optional) → `components.css`, after your own
|
|
67
77
|
`@import "tailwindcss"` and under the layer statement
|
|
68
78
|
`@layer theme, base, ds, components, utilities;`.
|
|
69
79
|
[**`docs/integration/css.md`**](./docs/integration/css.md) has the full
|
|
70
|
-
sequence, where the per-app preset goes,
|
|
71
|
-
|
|
80
|
+
sequence, where the per-app preset goes, why no `@source` into our package is
|
|
81
|
+
needed, and the fixture run behind each of those claims. The reasoning lives
|
|
72
82
|
in [ADR-004](./docs/adr/004-css-delivery-cascade.md) and
|
|
73
83
|
[ADR-005](./docs/adr/005-app-vocabulary-bridge.md).
|
|
74
84
|
|
|
85
|
+
### `styles.css` (deprecated)
|
|
86
|
+
|
|
87
|
+
`@assure-one/design-system/styles.css` — the all-in-one sheet with its own
|
|
88
|
+
preflight, a Google Fonts `@import` and the frozen unprefixed legacy utilities —
|
|
89
|
+
is **deprecated since 1.40** (registry `C-CSS-STYLES`). It still ships, unchanged,
|
|
90
|
+
and is removed in 3.0; the built file opens with a deprecation
|
|
91
|
+
banner. Moving to the split entries is a deliberate visual change (document font
|
|
92
|
+
size, fonts, preflight): follow [`docs/integration/css.md`](./docs/integration/css.md)
|
|
93
|
+
§6 and [`docs/migration/1.40.md`](./docs/migration/1.40.md).
|
|
94
|
+
|
|
75
95
|
### Runtime brand theming (experimental)
|
|
76
96
|
|
|
77
97
|
For a tenant colour known only at runtime, `@assure-one/design-system/tokens`
|
|
@@ -107,8 +127,9 @@ is required to get a readable lockup.
|
|
|
107
127
|
### The provider and the `/next` entry (experimental)
|
|
108
128
|
|
|
109
129
|
Nothing requires a provider: every component renders English strings, formats
|
|
110
|
-
with `en-US` and (for `LinkButton` / `Logo`)
|
|
111
|
-
|
|
130
|
+
with `en-US` and (for `LinkButton` / `Logo`) renders a plain `<a>` / `<img>`.
|
|
131
|
+
On Next.js, render `NextDesignSystemProvider` to get `next/link` routing and
|
|
132
|
+
`next/image`: the root imports no `next/*` since 2.0. `DesignSystemProvider` is the one optional, thin, client-side provider
|
|
112
133
|
for what CSS cannot do — the generic strings, the locale, the text direction,
|
|
113
134
|
the portal container, the link and image adapters, shared tooltip defaults and
|
|
114
135
|
(opt-in) the toast region. It renders no DOM wrapper ([ADR-010](./docs/adr/010-design-system-provider.md)).
|
|
@@ -143,11 +164,10 @@ export default function RootLayout({ children }) {
|
|
|
143
164
|
Pass the locale your app already knows server-side; the design system never
|
|
144
165
|
reads `navigator.language` during render, so server and client HTML agree.
|
|
145
166
|
- **`linkComponent` / `imageComponent`** — what `LinkButton` and `Logo` render
|
|
146
|
-
through. Without them the components
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
`
|
|
150
|
-
imports.
|
|
167
|
+
through. Without them the components render a plain `<a>` / `<img>` (full
|
|
168
|
+
page navigation). A non-Next app passes its router's link; a Next.js app uses
|
|
169
|
+
`NextDesignSystemProvider`. `next` is an optional peer, needed only by the
|
|
170
|
+
`/next` entry.
|
|
151
171
|
- **`portalContainer`**, **`dir`**, **`tooltip`**, **`toasts`** — documented on
|
|
152
172
|
the props. `toasts` mounts the toast region for `useToast()`; leave it off if
|
|
153
173
|
you already render `ToastProvider`.
|
package/codemods/README.md
CHANGED
|
@@ -19,6 +19,9 @@ node node_modules/@assure-one/design-system/codemods/run.mjs CM-02 src --report
|
|
|
19
19
|
|
|
20
20
|
# Everything this project is missing, in program order
|
|
21
21
|
node node_modules/@assure-one/design-system/codemods/run.mjs upgrade --dry
|
|
22
|
+
|
|
23
|
+
# Only what a project needs before 1.40 (docs/migration/1.40.md)
|
|
24
|
+
node node_modules/@assure-one/design-system/codemods/run.mjs upgrade --to 1.40 --dry
|
|
22
25
|
```
|
|
23
26
|
|
|
24
27
|
There is deliberately **no** `bin` entry and **no** `exports` entry for the
|
|
@@ -30,6 +33,7 @@ is `pnpm codemods <id> <paths>`.
|
|
|
30
33
|
| Option | Effect |
|
|
31
34
|
| ------------------------------- | ------------------------------------------------------------------------------ |
|
|
32
35
|
| `--dry` | report what would happen; write neither files nor the ledger |
|
|
36
|
+
| `--to <version>` | `upgrade` only: run the bundle that release needs (`--to 1.40`, see below) |
|
|
33
37
|
| `--root <dir>` | project root (default: the nearest `package.json` above the working directory) |
|
|
34
38
|
| `--report <prefix>` | write `<prefix>.json` and `<prefix>.md` |
|
|
35
39
|
| `--json <file>` / `--md <file>` | write one of the two |
|
|
@@ -96,6 +100,19 @@ entry. Report-only codemods and dry runs never touch it. The consumer scanner
|
|
|
96
100
|
reads the file and the migration dashboard shows the applied ids
|
|
97
101
|
(`migration-status/<app>.json` → `codemodLedger`).
|
|
98
102
|
|
|
103
|
+
## `upgrade --to 1.40`
|
|
104
|
+
|
|
105
|
+
`upgrade --to 1.40` runs, in program order, the codemods of the 1.40 bundle
|
|
106
|
+
(`UPGRADE_TARGETS` in `lib/registry.mjs`): one per registry contract that 1.40
|
|
107
|
+
removes or flips (plan §21.2) — CM-01, CM-04, CM-19, CM-12, and the finders
|
|
108
|
+
CM-15 and CM-16. A transforming codemod already in the ledger is skipped, so
|
|
109
|
+
the command is safe to repeat and safe from any 1.x; the report-only finders
|
|
110
|
+
run every time. With `--report <prefix>` each codemod writes
|
|
111
|
+
`<prefix>-<id>.json` and `.md`. The migration guide is
|
|
112
|
+
[`docs/migration/1.40.md`](../docs/migration/1.40.md). `--to 2` (also `2.0`,
|
|
113
|
+
`v2`) still works: it is the name the bundle had while Wave 9 was planned as
|
|
114
|
+
2.0 (decision D11 shipped it as 1.40).
|
|
115
|
+
|
|
99
116
|
## Reports
|
|
100
117
|
|
|
101
118
|
Every run produces the same report: a JSON document (`schema`, the codemod, the
|
|
@@ -136,8 +153,8 @@ Class R. W8-02 gave the shared Assure patterns the `/suite` entry and the
|
|
|
136
153
|
frozen and product-owned modules `/legacy`. Both are re-export barrels over the
|
|
137
154
|
same module instances the root exports, so moving an import is a pure path
|
|
138
155
|
change: no duplicated React context, no behaviour difference. The root keeps
|
|
139
|
-
every name through 1.
|
|
140
|
-
running this codemod early costs nothing and running it late is a
|
|
156
|
+
every name through 1.39 (registry `C-ROOT-SUITE`) and drops them in 1.40, so
|
|
157
|
+
running this codemod early costs nothing and running it late is a 1.40 blocker.
|
|
141
158
|
|
|
142
159
|
```tsx
|
|
143
160
|
import { Button, AgreementViewer, TimeLogger } from "@assure-one/design-system";
|
|
@@ -306,7 +323,7 @@ components with the same names, test files.
|
|
|
306
323
|
|
|
307
324
|
Class R. `ProgressBar` and `ProgressRing` colour themselves **by value** when
|
|
308
325
|
no colour is passed (`< 50%` brand, `≥ 50%` warning, `≥ 80%` success) — a
|
|
309
|
-
business mapping the design system should not own, which
|
|
326
|
+
business mapping the design system should not own, which 1.40 switches off
|
|
310
327
|
(W9-10, contract C-PROGRESS). W3-21 added `intent`; passing it disables the
|
|
311
328
|
rule. CM-19 makes every call site say what it renders today, so the flip
|
|
312
329
|
changes nothing a consumer did not write down: `variant="<legacy word>"`
|
|
@@ -338,7 +355,7 @@ gets `size="md"` — the value the component resolves today
|
|
|
338
355
|
(`defaultVariants.size`, 40px), so nothing moves on screen. It exists because
|
|
339
356
|
a size _meaning_ can only change once no call site depends on the default:
|
|
340
357
|
after CM-02, the D5 size flip (CM-03 phase 2, W9-06) touches nothing a
|
|
341
|
-
consumer did not write down, and
|
|
358
|
+
consumer did not write down, and gate G6 counts exactly this.
|
|
342
359
|
|
|
343
360
|
The attribute goes after `variant` when there is one (the order the
|
|
344
361
|
applications use) and first otherwise, on its own line when the element's
|
|
@@ -357,7 +374,7 @@ scanner that measures G6 counts production files only).
|
|
|
357
374
|
### CM-12 — Button `type="submit"` where evident
|
|
358
375
|
|
|
359
376
|
Class R. An untyped `Button` renders `<button>` without a `type`, which the
|
|
360
|
-
browser treats as `submit`;
|
|
377
|
+
browser treats as `submit`; 1.40 flips the default to `"button"` (W9-05,
|
|
361
378
|
C-BTN-TYPE, gate G4). CM-12 writes down, before the flip, which Buttons submit
|
|
362
379
|
on purpose — and only where the file itself makes that evident:
|
|
363
380
|
|
|
@@ -598,8 +615,8 @@ yet — those are the candidates for new registry entries. `confidence` is `high
|
|
|
598
615
|
for a token on the component itself, `medium` for a wrapper or a heuristic,
|
|
599
616
|
`low` for an unmapped wrapper.
|
|
600
617
|
|
|
601
|
-
CM-15 feeds
|
|
602
|
-
target 2.0). Use `--fail-on-findings` to gate on it. The committed findings for
|
|
618
|
+
CM-15 feeds gate G7 (`consumerSelectors = 0` for the freezes with registry
|
|
619
|
+
removal target 2.0, which 1.40 lifts). Use `--fail-on-findings` to gate on it. The committed findings for
|
|
603
620
|
the four consumer apps are in `docs/codemods/`.
|
|
604
621
|
|
|
605
622
|
### CM-16 — `globals.css` analyser
|
|
@@ -22,6 +22,33 @@ export const CODEMODS = [
|
|
|
22
22
|
{ id: "CM-20", module: "../transforms/cm-20-select-sentinels.mjs" },
|
|
23
23
|
];
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* `upgrade --to <version>`: the codemods a 1.x project runs before it installs
|
|
27
|
+
* that release, by the registry contracts the release removes or flips (plan
|
|
28
|
+
* §21.2). Applied in program order; transforming ones already in the ledger
|
|
29
|
+
* are skipped, report-only ones run every time.
|
|
30
|
+
*
|
|
31
|
+
* 1.40 (Wave 9, D11) — CM-01 root suite/legacy/accidental exports leave the
|
|
32
|
+
* root (C-ROOT-SUITE, C-ROOT-ACCIDENTAL) · CM-04 `variant="accent"` removed
|
|
33
|
+
* (C-BTN-ACCENT) · CM-19 Progress auto-colour off by default (C-PROGRESS) ·
|
|
34
|
+
* CM-12 Button `type="button"` default (C-BTN-TYPE) · CM-15 the 1.40 DOM
|
|
35
|
+
* freezes are lifted (C-DOM-*, C-TOAST-ARIALABEL) · CM-16 `styles.css`
|
|
36
|
+
* deprecated (C-CSS-STYLES). CM-02 is not in it: D5 keeps today's size scale.
|
|
37
|
+
*/
|
|
38
|
+
export const UPGRADE_TARGETS = {
|
|
39
|
+
"1.40": ["CM-01", "CM-04", "CM-19", "CM-12", "CM-15", "CM-16"],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Older names of a target: the 1.40 bundle was `--to 2` while Wave 9 was planned as 2.0. */
|
|
43
|
+
const UPGRADE_ALIASES = { 2: "1.40" };
|
|
44
|
+
|
|
45
|
+
/** The bundle for `--to 1.40`, `1.40.0`, `v1.40` (or the alias `--to 2`, `2.0`, `v2`…), in program order, or `null`. */
|
|
46
|
+
export function upgradeBundle(to) {
|
|
47
|
+
const [major, minor] = String(to).trim().replace(/^v/i, "").split(".");
|
|
48
|
+
const ids = UPGRADE_TARGETS[UPGRADE_ALIASES[major] ?? `${major}.${Number(minor)}`];
|
|
49
|
+
return ids ? CODEMODS.filter((c) => ids.includes(c.id)).map((c) => c.id) : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
25
52
|
export const findCodemod = (id) =>
|
|
26
53
|
CODEMODS.find((c) => c.id.toLowerCase() === String(id).toLowerCase()) ?? null;
|
|
27
54
|
|
package/codemods/lib/runner.mjs
CHANGED
|
@@ -170,14 +170,19 @@ const countBy = (items, key) => {
|
|
|
170
170
|
return Object.fromEntries([...map].sort(([a], [b]) => (a < b ? -1 : 1)));
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Codemods a project has not applied yet, in program order. With `only` (an
|
|
175
|
+
* `upgrade --to` bundle) the list is limited to those ids and keeps their
|
|
176
|
+
* report-only codemods, which never enter the ledger.
|
|
177
|
+
*/
|
|
178
|
+
export async function pendingCodemods(root, { only = null } = {}) {
|
|
175
179
|
const { CODEMODS } = await import("./registry.mjs");
|
|
176
180
|
const ids = appliedIds(readLedger(root));
|
|
177
181
|
const out = [];
|
|
178
182
|
for (const { id } of CODEMODS) {
|
|
183
|
+
if (only && !only.includes(id)) continue;
|
|
179
184
|
const mod = await loadCodemod(id);
|
|
180
185
|
out.push({ ...mod.meta, applied: ids.has(id) });
|
|
181
186
|
}
|
|
182
|
-
return out.filter((m) => m.class
|
|
187
|
+
return out.filter((m) => (m.class === "X" ? Boolean(only) : !m.applied));
|
|
183
188
|
}
|
package/codemods/run.mjs
CHANGED
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
* node node_modules/@assure-one/design-system/codemods/run.mjs list
|
|
8
8
|
* node node_modules/@assure-one/design-system/codemods/run.mjs CM-15 src --dry
|
|
9
9
|
* node node_modules/@assure-one/design-system/codemods/run.mjs upgrade --dry
|
|
10
|
+
* node node_modules/@assure-one/design-system/codemods/run.mjs upgrade --to 1.40 --dry
|
|
10
11
|
*
|
|
11
12
|
* Options
|
|
12
13
|
* --dry report only; write neither files nor the ledger
|
|
14
|
+
* --to <version> upgrade: only the codemods that release needs (`--to 1.40`;
|
|
15
|
+
* `--to 2` is an alias)
|
|
13
16
|
* --root <dir> project root (default: nearest package.json above cwd)
|
|
14
17
|
* --report <prefix> write <prefix>.json and <prefix>.md
|
|
15
18
|
* --json <file> write the JSON report
|
|
@@ -27,11 +30,12 @@ import { dirname, resolve } from "node:path";
|
|
|
27
30
|
import { findProjectRoot } from "./lib/files.mjs";
|
|
28
31
|
import { installedDsVersion } from "./lib/environment.mjs";
|
|
29
32
|
import { renderMarkdown, renderSummary } from "./lib/report.mjs";
|
|
30
|
-
import {
|
|
33
|
+
import { readLedger } from "./lib/ledger.mjs";
|
|
34
|
+
import { CODEMODS, UPGRADE_TARGETS, loadAll, upgradeBundle } from "./lib/registry.mjs";
|
|
31
35
|
import { Refused, pendingCodemods, runCodemod } from "./lib/runner.mjs";
|
|
32
36
|
|
|
33
37
|
const FLAGS = new Set(["--dry", "--print", "--quiet", "--fail-on-findings", "--help", "-h"]);
|
|
34
|
-
const VALUES = new Set(["--root", "--report", "--json", "--md"]);
|
|
38
|
+
const VALUES = new Set(["--root", "--report", "--json", "--md", "--to"]);
|
|
35
39
|
|
|
36
40
|
function parseArgs(argv) {
|
|
37
41
|
const options = {};
|
|
@@ -55,7 +59,7 @@ class UsageError extends Error {}
|
|
|
55
59
|
const usage = () => `Usage:
|
|
56
60
|
run.mjs list
|
|
57
61
|
run.mjs <CM-ID> [paths…] [--dry] [--root dir] [--report prefix] [--print] [--fail-on-findings]
|
|
58
|
-
run.mjs upgrade [paths…] [--dry]
|
|
62
|
+
run.mjs upgrade [paths…] [--to <version>] [--dry]
|
|
59
63
|
|
|
60
64
|
Codemods: ${CODEMODS.map((c) => c.id).join(", ")} (design system ${installedDsVersion()})
|
|
61
65
|
`;
|
|
@@ -112,7 +116,21 @@ async function main(argv) {
|
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
if (command === "upgrade") {
|
|
115
|
-
const
|
|
119
|
+
const only = options.to ? upgradeBundle(options.to) : null;
|
|
120
|
+
if (options.to && !only) {
|
|
121
|
+
throw new UsageError(
|
|
122
|
+
`--to ${options.to}: no upgrade bundle (known: ${Object.keys(UPGRADE_TARGETS).join(", ")}; 2 is an alias of 1.40)`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const pending = await pendingCodemods(root, { only });
|
|
126
|
+
if (only) {
|
|
127
|
+
const recorded = new Set(readLedger(root).applied.map((e) => e.id));
|
|
128
|
+
const skipped = only.filter((id) => recorded.has(id));
|
|
129
|
+
say(
|
|
130
|
+
`Upgrade to ${options.to}: ${pending.map((m) => m.id).join(", ")}` +
|
|
131
|
+
(skipped.length ? `; already in the ledger: ${skipped.join(", ")}` : ""),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
116
134
|
if (!pending.length) {
|
|
117
135
|
say(`Nothing to apply in ${root}; the ledger is up to date.`);
|
|
118
136
|
return 0;
|
|
@@ -125,8 +143,12 @@ async function main(argv) {
|
|
|
125
143
|
dry: Boolean(options.dry),
|
|
126
144
|
});
|
|
127
145
|
say(renderSummary(report));
|
|
146
|
+
for (const warning of report.warnings) say(`! ${warning}`);
|
|
128
147
|
if (options.print) process.stdout.write(renderMarkdown(report));
|
|
129
|
-
|
|
148
|
+
const prefix = options.report && pending.length > 1 ? `${options.report}-${meta.id}` : null;
|
|
149
|
+
for (const path of writeReport(report, prefix ? { report: prefix } : options))
|
|
150
|
+
say(`Report: ${path}`);
|
|
151
|
+
if (report.ledger.recorded) say(`Recorded ${meta.id} in ${root}/${report.ledger.file}`);
|
|
130
152
|
if (options["fail-on-findings"] && report.summary.registered) code = 1;
|
|
131
153
|
}
|
|
132
154
|
return code;
|
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
*
|
|
18
18
|
* Findings without a registry id are reported as `unregistered`: they are
|
|
19
19
|
* DOM coupling the registry does not track yet. Radix `data-state`-style
|
|
20
|
-
* attributes, ARIA roles
|
|
21
|
-
* (P-DATA-STATE, P-ROLES,
|
|
20
|
+
* attributes, ARIA roles, cmdk attributes and the `dl/dt/dd` of
|
|
21
|
+
* MetadataGrid/DataItem are permanent contracts (P-DATA-STATE, P-ROLES,
|
|
22
|
+
* C-CMDK-ATTR, P-NATIVE-TABLE) and are not reported.
|
|
22
23
|
*
|
|
23
24
|
* Never modifies files.
|
|
24
25
|
*/
|
|
@@ -185,16 +186,19 @@ const TAG_RULES = [
|
|
|
185
186
|
{ id: "C-DOM-12", components: /^ProgressBar$/, test: has(/role/) },
|
|
186
187
|
{ id: "C-DOM-13", components: /^(?:DataTableHeader|DataTableHead)$/, test: has(/button/) },
|
|
187
188
|
{ id: "C-DOM-15", components: /^DocumentRequestCard/, test: any },
|
|
188
|
-
{
|
|
189
|
-
id: "C-DOM-16",
|
|
190
|
-
components: /^(?:MetadataGrid|DataItem)$/,
|
|
191
|
-
test: has(/(?:^|[_>\s])(?:dt|dd|span)\b/),
|
|
192
|
-
},
|
|
189
|
+
{ id: "C-DOM-16", components: /^(?:MetadataGrid|DataItem)$/, test: has(/(?:^|[_>\s])span\b/) },
|
|
193
190
|
];
|
|
194
191
|
|
|
195
192
|
const ruleFor = (component, reach) =>
|
|
196
193
|
TAG_RULES.find((r) => r.components.test(component) && r.test(reach)) ?? null;
|
|
197
194
|
|
|
195
|
+
// `dl/dt/dd` inside MetadataGrid/DataItem are permanent (P-NATIVE-TABLE;
|
|
196
|
+
// only the `span` part of C-DOM-16 is frozen), so selectors on them are not
|
|
197
|
+
// reported.
|
|
198
|
+
const isPermanent = (component, reach) =>
|
|
199
|
+
/^(?:MetadataGrid|DataItem)$/.test(component) &&
|
|
200
|
+
/(?:^|[_>\s])(?:dl|dt|dd)\b/.test(reach.selector);
|
|
201
|
+
|
|
198
202
|
/**
|
|
199
203
|
* Whether an unmapped selector on a *wrapper* element is worth reporting: it
|
|
200
204
|
* must name an element, `*`, or a `data-`/`role` attribute. A wrapper selector
|
|
@@ -310,6 +314,7 @@ function codeFindings(file, ts) {
|
|
|
310
314
|
const reach = domReach(token);
|
|
311
315
|
if (!reach) continue;
|
|
312
316
|
const rule = ruleFor(usage.component, reach) ?? ruleFor(usage.base, reach);
|
|
317
|
+
if (!rule && isPermanent(usage.base, reach)) continue;
|
|
313
318
|
add({
|
|
314
319
|
line,
|
|
315
320
|
registryId: rule?.id ?? null,
|
|
@@ -332,6 +337,7 @@ function codeFindings(file, ts) {
|
|
|
332
337
|
.map((c) => ({ c, rule: ruleFor(c, reach) }))
|
|
333
338
|
.filter((x) => x.rule && x.rule.test !== any);
|
|
334
339
|
const hit = rules[0];
|
|
340
|
+
if (!hit && el.dsDescendants.some((c) => isPermanent(c, reach))) continue;
|
|
335
341
|
if (!hit && !namesStructure(reach.selector)) continue;
|
|
336
342
|
add({
|
|
337
343
|
line,
|
|
@@ -381,12 +387,13 @@ function codeFindings(file, ts) {
|
|
|
381
387
|
});
|
|
382
388
|
}
|
|
383
389
|
|
|
384
|
-
// 4. MetadataGrid / DataItem wrappers style
|
|
390
|
+
// 4. MetadataGrid / DataItem wrappers style their `span` from anywhere in
|
|
391
|
+
// the file (`dt/dd` are permanent, see `isPermanent`).
|
|
385
392
|
if (used.has("MetadataGrid") || used.has("DataItem")) {
|
|
386
393
|
const reported = new Set(findings.map((f) => `${f.line}${f.match}`));
|
|
387
394
|
for (const { token, line } of facts.fileTokens) {
|
|
388
395
|
const reach = domReach(token);
|
|
389
|
-
if (!reach || !/(?:^|[_>\s])
|
|
396
|
+
if (!reach || !/(?:^|[_>\s])span\b/.test(reach.selector)) continue;
|
|
390
397
|
if (reported.has(`${line}${token}`)) continue;
|
|
391
398
|
add({
|
|
392
399
|
line,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Brand scope — ambient product theming.
|
|
6
|
+
*
|
|
7
|
+
* The `brand` color family (bg-brand, hover:bg-brand-hover, bg-brand-bg,
|
|
8
|
+
* border-brand-line, text-fg-on-brand, plus the accent/focus/primary
|
|
9
|
+
* interaction tokens) follows the nearest `[data-brand]` scope and defaults to
|
|
10
|
+
* Pro. Authors type the STATE only; the product comes from the scope. Set it
|
|
11
|
+
* once high in the tree — ideally on the same root as the `.dark` class
|
|
12
|
+
* (`<html>`/`<body>`) — so Radix-portalled surfaces inherit it through the DOM.
|
|
13
|
+
*
|
|
14
|
+
* <html data-brand="tax"> // whole shell renders teal
|
|
15
|
+
* <div {...brandScope("audit")}> // a subtree renders blue
|
|
16
|
+
*
|
|
17
|
+
* For the rarer case of multiple scopes on one page WITH portalled content
|
|
18
|
+
* (a Dialog/Popover that escapes its scoped subtree to document.body), wrap the
|
|
19
|
+
* region in <BrandScope> / <BrandScopeProvider> and have the portalled
|
|
20
|
+
* `*Content` re-stamp `data-brand={useBrandScope()}` on its own element.
|
|
21
|
+
*/
|
|
22
|
+
type BrandProduct = "pro" | "audit" | "books" | "tax";
|
|
23
|
+
/**
|
|
24
|
+
* `data-brand` attribute object to spread on any element (mirrors
|
|
25
|
+
* `serviceToneStyle`). This is the primary API for opting a subtree into a
|
|
26
|
+
* product without naming the product in a utility class.
|
|
27
|
+
*/
|
|
28
|
+
declare function brandScope(product: BrandProduct): {
|
|
29
|
+
"data-brand": BrandProduct;
|
|
30
|
+
};
|
|
31
|
+
/** Every product, in suite order — for switchers and story matrices. */
|
|
32
|
+
declare const BRAND_PRODUCTS: BrandProduct[];
|
|
33
|
+
/** Human label for a product, for switchers and headings. */
|
|
34
|
+
declare const brandLabel: Record<BrandProduct, string>;
|
|
35
|
+
/**
|
|
36
|
+
* Provides the active product to descendants so portalled surfaces can
|
|
37
|
+
* re-stamp `data-brand` on themselves via {@link useBrandScope}. Purely a
|
|
38
|
+
* structural carrier — the visual theming still comes from the `[data-brand]`
|
|
39
|
+
* attribute in the DOM, not from this context.
|
|
40
|
+
*/
|
|
41
|
+
declare function BrandScopeProvider({ product, children, }: {
|
|
42
|
+
product: BrandProduct;
|
|
43
|
+
children: React.ReactNode;
|
|
44
|
+
}): react_jsx_runtime.JSX.Element;
|
|
45
|
+
/** Read the ambient product from the nearest provider (defaults to Pro). */
|
|
46
|
+
declare function useBrandScope(): BrandProduct;
|
|
47
|
+
interface BrandScopeProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
48
|
+
/** The product this subtree renders as. */
|
|
49
|
+
product: BrandProduct;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Convenience wrapper that stamps `data-brand` on a `<div>` AND provides the
|
|
53
|
+
* product via context, so a portalled descendant can re-stamp itself. Use
|
|
54
|
+
* `brandScope()` directly when you want the attribute on your own element.
|
|
55
|
+
*/
|
|
56
|
+
declare const BrandScope: React$1.ForwardRefExoticComponent<BrandScopeProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
57
|
+
|
|
58
|
+
export { BRAND_PRODUCTS as B, type BrandProduct as a, brandLabel as b, BrandScope as c, type BrandScopeProps as d, BrandScopeProvider as e, brandScope as f, useBrandScope as u };
|
|
@@ -14,7 +14,7 @@ interface BrandThemeSeed {
|
|
|
14
14
|
* `#rrggbb`. `undefined` or `null` means "the same as `brand`", which is what
|
|
15
15
|
* the built-in `[data-ds-brand]` scopes do.
|
|
16
16
|
*/
|
|
17
|
-
accent?: string | null;
|
|
17
|
+
accent?: string | null | undefined;
|
|
18
18
|
}
|
|
19
19
|
/** Every custom property `createBrandTheme()` sets, in output order. */
|
|
20
20
|
declare const BRAND_THEME_PROPERTIES: readonly ["--ds-color-action-brand-bg", "--ds-color-action-brand-bg-hover", "--ds-color-action-brand-bg-active", "--ds-color-action-brand-bg-disabled", "--ds-color-action-brand-subtle-bg", "--ds-color-action-brand-subtle-bg-hover", "--ds-color-action-brand-subtle-bg-active", "--ds-color-action-brand-border", "--ds-color-action-brand-ring", "--ds-color-action-brand-fg", "--ds-color-action-accent-bg", "--ds-color-action-accent-bg-strong", "--ds-color-action-accent-bg-hover", "--ds-color-action-accent-bg-active", "--ds-color-action-accent-subtle-bg", "--ds-color-action-accent-ring", "--ds-color-action-accent-fg", "--ds-color-border-focus", "--ds-focus-ring-color", "--ds-focus-ring-border", "--ds-gradient-brand", "--ds-compat-color-brand-soft", "--ds-compat-color-brand-soft-foreground"];
|