@hirobius/design-system 0.21.0 → 0.23.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 +13 -1
- package/CHANGELOG.md +1043 -0
- package/CONSUMING.md +25 -11
- package/DESIGN.md +24 -24
- package/MIGRATIONS.md +603 -0
- package/NOTICE.md +1 -1
- package/README.md +22 -12
- package/UPGRADING.md +243 -0
- package/codemods/hds-prefix.mjs +28 -13
- package/codemods/jsx-fold.mjs +28 -13
- package/codemods/lib/atomic-write.mjs +28 -0
- package/codemods/lib/installed-version.mjs +354 -0
- package/codemods/lib/project.mjs +395 -0
- package/codemods/lib/record.mjs +110 -0
- package/codemods/lib/scan.mjs +323 -0
- package/codemods/lib/semver.mjs +57 -0
- package/codemods/not-found-pattern.mjs +3 -3
- package/codemods/patterns-subpath.mjs +28 -12
- package/codemods/registry.mjs +67 -0
- package/codemods/tile-grid.mjs +3 -3
- package/codemods/upgrade.mjs +909 -0
- package/dist/chunks/{empty-state-DNoThgfV.js → empty-state-ChC_xZfg.js} +109 -92
- package/dist/chunks/{form-By82qZcC.js → form-zCZ_B83I.js} +16 -16
- package/dist/css-contract.json +1 -0
- package/dist/fonts/ibm-plex-mono-400.woff2 +0 -0
- package/dist/fonts.css +5 -5
- package/dist/form.js +1 -1
- package/dist/hirobius-ui.js +1254 -1181
- package/dist/manifest.js +6 -5
- package/dist/patterns.js +164 -162
- package/dist/styles.css +1 -1
- package/dist/tokens.css +1 -1
- package/dist/tokens.js +14 -1
- package/dist/types/src/app/components/alert-dialog.d.ts +1 -1
- package/dist/types/src/app/components/alert.d.ts +1 -1
- package/dist/types/src/app/components/asset-img.d.ts +2 -1
- package/dist/types/src/app/components/avatar-group.d.ts +1 -1
- package/dist/types/src/app/components/avatar.d.ts +1 -1
- package/dist/types/src/app/components/badge.d.ts +1 -1
- package/dist/types/src/app/components/blockquote.d.ts +1 -1
- package/dist/types/src/app/components/breadcrumb.d.ts +1 -1
- package/dist/types/src/app/components/button.d.ts +2 -2
- package/dist/types/src/app/components/callout.d.ts +3 -2
- package/dist/types/src/app/components/card.d.ts +6 -4
- package/dist/types/src/app/components/checkbox.d.ts +1 -1
- package/dist/types/src/app/components/code-block.d.ts +2 -1
- package/dist/types/src/app/components/combobox.d.ts +2 -2
- package/dist/types/src/app/components/data-table-section.d.ts +2 -1
- package/dist/types/src/app/components/destructive-section.d.ts +2 -1
- package/dist/types/src/app/components/dialog.d.ts +1 -1
- package/dist/types/src/app/components/disclosure.d.ts +1 -1
- package/dist/types/src/app/components/divider.d.ts +2 -2
- package/dist/types/src/app/components/empty-state.d.ts +1 -1
- package/dist/types/src/app/components/field.d.ts +1 -1
- package/dist/types/src/app/components/form-actions.d.ts +2 -1
- package/dist/types/src/app/components/form.d.ts +2 -1
- package/dist/types/src/app/components/grid.d.ts +1 -1
- package/dist/types/src/app/components/hds-tooltip.d.ts +1 -1
- package/dist/types/src/app/components/icon.d.ts +2 -2
- package/dist/types/src/app/components/inline-link.d.ts +1 -1
- package/dist/types/src/app/components/input.d.ts +1 -1
- package/dist/types/src/app/components/kbd.d.ts +1 -1
- package/dist/types/src/app/components/menu.d.ts +1 -1
- package/dist/types/src/app/components/metadata-list.d.ts +1 -1
- package/dist/types/src/app/components/metric-tiles.d.ts +3 -1
- package/dist/types/src/app/components/page-header.d.ts +1 -0
- package/dist/types/src/app/components/page.d.ts +1 -0
- package/dist/types/src/app/components/pagination.d.ts +1 -1
- package/dist/types/src/app/components/popover.d.ts +1 -1
- package/dist/types/src/app/components/progress.d.ts +1 -1
- package/dist/types/src/app/components/radio.d.ts +1 -1
- package/dist/types/src/app/components/reveal.d.ts +1 -0
- package/dist/types/src/app/components/select.d.ts +1 -1
- package/dist/types/src/app/components/skeleton.d.ts +1 -1
- package/dist/types/src/app/components/slider.d.ts +1 -1
- package/dist/types/src/app/components/spinner.d.ts +1 -1
- package/dist/types/src/app/components/stack.d.ts +1 -1
- package/dist/types/src/app/components/stat.d.ts +1 -1
- package/dist/types/src/app/components/status-list-item.d.ts +1 -1
- package/dist/types/src/app/components/status-tile.d.ts +1 -1
- package/dist/types/src/app/components/table.d.ts +1 -1
- package/dist/types/src/app/components/tabs.d.ts +4 -2
- package/dist/types/src/app/components/tag.d.ts +1 -1
- package/dist/types/src/app/components/text.d.ts +1 -1
- package/dist/types/src/app/components/textarea.d.ts +1 -1
- package/dist/types/src/app/components/timestamp.d.ts +1 -1
- package/dist/types/src/app/components/toast.d.ts +1 -1
- package/dist/types/src/app/components/toggle.d.ts +1 -1
- package/dist/types/src/app/context/hds-theme.d.ts +1 -1
- package/dist/types/src/app/design-system/generated-token-refs.d.ts +13 -0
- package/dist/types/src/app/design-system/generated-token-values.d.ts +14 -1
- package/dist/types/src/app/hooks/useInteractionState.d.ts +7 -1
- package/dist/types/src/index.d.ts +126 -32
- package/dist/variables.css +33 -22
- package/docs/CONSUMING.md +2 -2
- package/hirobius.tokens.json +173 -79
- package/llms.txt +29 -7
- package/mcp/catalog.mjs +10 -7
- package/mcp/core-set.mjs +66 -0
- package/mcp/guide.mjs +9 -52
- package/package.json +28 -11
- package/public/hds-manifest.json +365 -207
- package/public/llms/components.txt +1 -1
- package/public/llms-full.txt +54 -32
- package/public/llms.txt +29 -7
- package/src/app/data/component-api.json +104 -71
- package/upgrade/index.json +215 -0
- package/upgrade/releases/0.17.0.json +962 -0
- package/upgrade/releases/0.18.0.json +8 -0
- package/upgrade/releases/0.19.0.json +233 -0
- package/upgrade/releases/0.19.1.json +8 -0
- package/upgrade/releases/0.20.0.json +5108 -0
- package/upgrade/releases/0.21.0.json +538 -0
- package/upgrade/releases/0.22.0.json +94 -0
- package/upgrade/releases/0.23.0.json +259 -0
- package/upgrade/schema.json +1102 -0
- package/dist/fonts/geist-mono-400.woff2 +0 -0
package/NOTICE.md
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
- **HDS code** (components, compiled JavaScript, types, CSS, and token files): MIT. See [LICENSE](LICENSE).
|
|
6
6
|
- **Satoshi** (weights 400, 500 and 700, by Indian Type Foundry, distributed through [Fontshare](https://www.fontshare.com/fonts/satoshi)): ITF Free Font License.
|
|
7
|
-
- **
|
|
7
|
+
- **IBM Plex Mono** (weight 400, by IBM, [source](https://github.com/IBM/plex)): SIL Open Font License 1.1.
|
|
8
8
|
|
|
9
9
|
Both fonts are embedded as base64 `@font-face` data in `dist/tokens.css` and `dist/styles.css`. The MIT license does not cover them; each font keeps its own license. `dist/variables.css` embeds no fonts.
|
package/README.md
CHANGED
|
@@ -2,24 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/hirobius/hds/actions/workflows/ci.yml) [](https://www.npmjs.com/package/@hirobius/design-system) [](https://hirobius-design-system.vercel.app)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**A governed, token-first React design system.** Components plus the system
|
|
6
|
+
around them: a DTCG token pipeline (CSS variables + TypeScript constants),
|
|
7
|
+
quality gates in git hooks and CI, one-way Figma sync, and WCAG-checked contrast
|
|
8
|
+
and focus. Live Storybook: **<https://hirobius-design-system.vercel.app>**
|
|
6
9
|
|
|
7
10
|
```bash
|
|
8
11
|
pnpm add @hirobius/design-system
|
|
9
12
|
```
|
|
10
13
|
|
|
14
|
+
Upgrading: [UPGRADING.md](UPGRADING.md) lists what each release asks of you. From 0.22.0, `npx @hirobius/design-system@latest upgrade` does it for you.
|
|
15
|
+
|
|
11
16
|
[](https://hirobius-design-system.vercel.app)
|
|
12
17
|
|
|
13
18
|
<!-- auto:start:front-door-counts -->
|
|
14
19
|
|
|
15
20
|
- **53** public component modules, exported from `src/index.ts`
|
|
16
|
-
- **
|
|
21
|
+
- **400** DTCG tokens in `hirobius.tokens.json`, compiled to CSS variables and TypeScript constants
|
|
17
22
|
- **341** Storybook stories in **71** story files
|
|
18
23
|
|
|
19
24
|
<!-- auto:end:front-door-counts -->
|
|
20
25
|
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
What the system includes:
|
|
27
|
+
|
|
28
|
+
- **Token pipeline.** DTCG tokens in `hirobius.tokens.json` compile to CSS variables and TypeScript constants through a validated alias chain, with AA-contrast gates in light and dark.
|
|
29
|
+
- **Gates with proof-of-firing.** Typecheck, zero-warning ESLint, token validity and contrast, Vitest unit and contract tests, bundle budgets, a consumer smoke build, and a Storybook build — run in git hooks and CI. Many gates carry a passing and a violating fixture that demonstrate the gate catches what it describes.
|
|
30
|
+
- **Upgrade path.** A release ledger and one consumer command: `npx @hirobius/design-system@latest upgrade`.
|
|
31
|
+
- **One-way Figma sync.** Code is the source of truth; tokens project into Figma, and `pnpm check:figma-drift` compares a committed snapshot against the model.
|
|
32
|
+
- **Theming without JavaScript.** Four root attributes — theme, density, brand, font — re-skin HDS descendants through CSS variables.
|
|
23
33
|
|
|
24
34
|
The counts are generated from source by `pnpm readme:counts`, which `pnpm tokens` also runs. `scripts/__tests__/front-door.test.mjs` fails if this README claims more than the source has.
|
|
25
35
|
|
|
@@ -118,12 +128,12 @@ HDS theming is driven by root attributes + CSS variables, so it works with **zer
|
|
|
118
128
|
JavaScript** — set them on any element (React, Astro, plain HTML) and every HDS
|
|
119
129
|
descendant re-skins:
|
|
120
130
|
|
|
121
|
-
| Dial | Attribute / var | Values | Default
|
|
122
|
-
| ------- | ---------------------------------------------- | --------------- |
|
|
123
|
-
| theme | `data-theme` | `dark` | light (unset)
|
|
124
|
-
| density | `data-density` | `compact` | comfortable (unset)
|
|
125
|
-
| brand | `data-brand` (+ `data-tenant` alias) | overlay slug | base (unset)
|
|
126
|
-
| font | `--hds-font-family` / `--hds-font-family-mono` | any font-family | Satoshi /
|
|
131
|
+
| Dial | Attribute / var | Values | Default |
|
|
132
|
+
| ------- | ---------------------------------------------- | --------------- | ----------------------- |
|
|
133
|
+
| theme | `data-theme` | `dark` | light (unset) |
|
|
134
|
+
| density | `data-density` | `compact` | comfortable (unset) |
|
|
135
|
+
| brand | `data-brand` (+ `data-tenant` alias) | overlay slug | base (unset) |
|
|
136
|
+
| font | `--hds-font-family` / `--hds-font-family-mono` | any font-family | Satoshi / IBM Plex Mono |
|
|
127
137
|
|
|
128
138
|
```html
|
|
129
139
|
<!-- zero-JS: static markup (e.g. an Astro layout) -->
|
|
@@ -215,7 +225,7 @@ Screen-reader output is checked by `tests/primitive-contracts/screen-reader.cont
|
|
|
215
225
|
|
|
216
226
|
## Visual direction: Editorial Enterprise
|
|
217
227
|
|
|
218
|
-
The governing direction is "Editorial Enterprise" — enterprise rigor with editorial pacing. It favors sharp hierarchy, open whitespace, monochrome neutrals, and a single
|
|
228
|
+
The governing direction is "Editorial Enterprise" — enterprise rigor with editorial pacing. It favors sharp hierarchy, open whitespace, monochrome neutrals, and a single tenant-set accent. In practice:
|
|
219
229
|
|
|
220
230
|
- documentation reads like a designed publication, not a component dump
|
|
221
231
|
- cards are used sparingly; whitespace, rails, dividers, and bands carry structure first
|
|
@@ -274,4 +284,4 @@ public/
|
|
|
274
284
|
|
|
275
285
|
## License
|
|
276
286
|
|
|
277
|
-
The HDS code is MIT ([LICENSE](LICENSE)). The package CSS also embeds the Satoshi and
|
|
287
|
+
The HDS code is MIT ([LICENSE](LICENSE)). The package CSS also embeds the Satoshi and IBM Plex Mono fonts, which keep their own licenses and are not covered by MIT. See [NOTICE.md](NOTICE.md).
|
package/UPGRADING.md
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Upgrading @hirobius/design-system
|
|
2
|
+
|
|
3
|
+
This file only knows the releases up to the version you have installed, 0.23.0. For newer releases, run `npx @hirobius/design-system@latest upgrade`: it always fetches the newest steps.
|
|
4
|
+
|
|
5
|
+
## How to upgrade
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx @hirobius/design-system@latest upgrade
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
It finds the version you have, moves you to the newest release, runs each release's codemods (Fixed for you) and lists what is left for you (Do by hand). It works from 0.16.0 on; from an older version, first reach 0.16.0 with the notes in CHANGELOG.md. MIGRATIONS.md has longer guides for the big releases.
|
|
12
|
+
|
|
13
|
+
## 0.23.0
|
|
14
|
+
|
|
15
|
+
Released 2026-10-10 (minor). 14 that look different.
|
|
16
|
+
|
|
17
|
+
### Looks different
|
|
18
|
+
|
|
19
|
+
- Accent-intent text and icons (such as links) now render in a dark apple green instead of a dark neutral.
|
|
20
|
+
- Hovered accent text now shifts to a darker apple green instead of a darker neutral.
|
|
21
|
+
- The accent fill on hover is now a deeper apple green instead of a dark neutral.
|
|
22
|
+
- The accent fill while pressed is now a darker apple green instead of a near-black neutral.
|
|
23
|
+
- Interactive accent fills at rest (primary buttons, selected controls) now render in a bright apple green instead of a dark neutral, paired with dark on-accent text.
|
|
24
|
+
- The low-emphasis accent tint (hover and selection backgrounds) is now a faint apple green instead of a neutral tint.
|
|
25
|
+
- Accent borders now render in apple green instead of a dark neutral.
|
|
26
|
+
- Accent-intent text and icons on page and card surfaces now render in a dark apple green instead of a neutral.
|
|
27
|
+
- Text and icons on accent fills are now a dark neutral instead of white, because the accent fill is a bright apple green that needs dark text for contrast.
|
|
28
|
+
- Accent background surfaces now render in a bright apple green instead of a dark neutral.
|
|
29
|
+
- Faint accent background surfaces are now a pale apple green instead of a neutral tint.
|
|
30
|
+
- Callout now renders as a tinted feedback fill (rounded, no left stripe) matching Alert, with the accent tone using a neutral muted fill.
|
|
31
|
+
- Code text now draws in IBM Plex Mono instead of Geist Mono, so if you preload or self-host geist-mono-400.woff2, point it at ibm-plex-mono-400.woff2 from fonts.css instead.
|
|
32
|
+
- Table data cells vertically center their content (badge/status slots no longer float above text) and the caption gains spacing above the table; loading spinners and the progress bar stop animating under prefers-reduced-motion.
|
|
33
|
+
|
|
34
|
+
## 0.22.0
|
|
35
|
+
|
|
36
|
+
Released 2026-10-08 (minor). 3 changes to make by hand (1 breaking), 1 that looks different and 2 deprecations.
|
|
37
|
+
|
|
38
|
+
### Looks different
|
|
39
|
+
|
|
40
|
+
- --component-tag-lineHeight is now 1.5 instead of an invalid value the browser ignored, so text that reads it gets that line height.
|
|
41
|
+
|
|
42
|
+
### Coming next
|
|
43
|
+
|
|
44
|
+
- Divider's strong prop, deprecated since before 0.16.0, still works; use variant="strong" instead. Removed in 1.0.0.
|
|
45
|
+
- InlineCode's compact prop, deprecated since before 0.16.0, still works; use density="compact" instead. Removed in 1.0.0.
|
|
46
|
+
|
|
47
|
+
### Do by hand
|
|
48
|
+
|
|
49
|
+
- The raw tokens (the tokens export and hirobius.tokens.json) are strict DTCG now, so read semantic.motion.\*.\$value.timingFunction instead of .easing, and the elastic spring and each semantic.elevation level from \$extensions\["com.hirobius.hds"].\$value.
|
|
50
|
+
- The figmaUrl of each component in the ./manifest export (public/hds-manifest.json) now points at file 2VgBbVpKiDnu0aftJEVyBQ, "HDS Tokens & Components"; node ids are unchanged, so a stored link needs only the new file key.
|
|
51
|
+
- The hds-mcp get_component tool now marks core: true only on the 43 ratified core components and recommended: true on the wider set AGENTS.md recommends, and list_core names its set, so read recommended where you relied on core for that wider set.
|
|
52
|
+
|
|
53
|
+
## 0.21.0
|
|
54
|
+
|
|
55
|
+
Released 2026-10-07 (minor). StatusDot is removed, fonts move to an opt-in fonts.css, the type ramp is cut to 5 roles; AGENTS.md, hds-mcp and the ESLint plugin ship.
|
|
56
|
+
|
|
57
|
+
### Looks different
|
|
58
|
+
|
|
59
|
+
- AssetImg without a src now shows a fallback that fits its container.
|
|
60
|
+
- A pressed toggle Button now fills with the accent surface, so it reads as on.
|
|
61
|
+
- Card, Surface and StatusTile no longer fill their parent's height, so give the parent a stretching layout (Grid align="stretch") where one must fill it.
|
|
62
|
+
- CodeBlock pads its code 16px instead of 80px.
|
|
63
|
+
- Dialog and AlertDialog now keep a 16px margin from the screen edge and scroll inside when taller than the screen.
|
|
64
|
+
- Disclosure variant="card" no longer clips its content or leaves empty space when closed.
|
|
65
|
+
- A vertical Divider now stretches across its row without needing a parent height.
|
|
66
|
+
- ErrorPattern now fits its container instead of filling the viewport height, so pass fullPage where it is a whole error page.
|
|
67
|
+
- A Button with no variant in FormActions primary now renders as primary, so pass variant where you want another style there.
|
|
68
|
+
- Grid now aligns its items to the start and Grid.Item no longer sets height: 100%, so pass align="stretch" to a Grid whose Cards, Surfaces or StatusTiles must share one height.
|
|
69
|
+
- A horizontal MetadataList now sizes its term column to its content.
|
|
70
|
+
- Progress now shows its neutral fill on a visible track, and its sm bar is 6px tall.
|
|
71
|
+
- SegmentedControl md segments now hug their labels in a 40px rail (sm 32px), so every segment shows instead of one filling the rail.
|
|
72
|
+
- Select now matches Input (40px tall, 44px on small screens, the same fill and an up-down chevron), and the built-in labels of Input, Textarea and Select are 14px like a FormField label.
|
|
73
|
+
- A rectangular Skeleton is now 5rem tall by default and uses the sunken surface fill, so it shows on a Card.
|
|
74
|
+
- Slider's track is now an 8px bar with a visible fill instead of a padded 48px slab.
|
|
75
|
+
- Stat now aligns its content to the start.
|
|
76
|
+
- Table no longer pads its scroll region by default, so pass flush={false} where you want the old inset back.
|
|
77
|
+
- Checkbox, Radio and Toggle rows now share one inset and height.
|
|
78
|
+
- Input, Textarea, Select, Combobox and Slider are now at most 40rem wide, so set --hds-form-control-max-width or add max-w-none to the control's className where one must be wider.
|
|
79
|
+
- On touch screens, Checkbox, Radio and Toggle rows, the Pagination page buttons and the CodeBlock copy button are now at least 44px tall.
|
|
80
|
+
- Headings (h1 to h3, heading1 to heading3) now render at 24px, display at 48px, body at 16px with 1.6 leading and mono at 13px, and eyebrow, badge and micro text is no longer uppercase.
|
|
81
|
+
|
|
82
|
+
### Coming next
|
|
83
|
+
|
|
84
|
+
- The Text variants heading1, heading2, heading3, technical, eyebrow, badge, docLede, docBody, docSmall and docCode still work but are deprecated; use title, mono, caption, body or ui instead. Removed in 1.0.0.
|
|
85
|
+
- The hds.typeStyles keys from before the ramp (h1 to h3, heading1 to heading3, eyebrow, badge, micro, technical, small, label and the rest) and hds.semantic.typography label, labelDescriptive and labelTechnical still work but are deprecated; use display, title, body, ui, caption or mono instead. Removed in 1.0.0.
|
|
86
|
+
|
|
87
|
+
### Do by hand
|
|
88
|
+
|
|
89
|
+
- StatusDot and StatusDotProps are removed, so replace each \<StatusDot tone size label> with \<Badge dot tone size label> (BadgeProps for the type), moving any style to a wrapper element or a className first.
|
|
90
|
+
- The HDS ESLint plugin's recommended config now fails raw \<button>, \<input>, \<select>, \<textarea> and \<form> (hds/no-raw-controls), so use Button, Input, Select, Textarea and Form instead or set the rule to 'off'.
|
|
91
|
+
- An info or success Alert now has role="status" instead of role="alert", so tests that find it by role must look for status.
|
|
92
|
+
- A labelled dot Badge now has role="img" instead of role="status", so tests that find it by role must look for img.
|
|
93
|
+
- A className on Button or Card can now override the colours a tone sets, so check any toned Button or Card that also passes colour classes.
|
|
94
|
+
- Combobox now returns focus to its trigger after Escape or a pick.
|
|
95
|
+
- Toggle's input now has role="switch", so tests that find it by role checkbox must look for switch.
|
|
96
|
+
- Checkbox, Radio and Toggle now take one tab stop each instead of two, so keyboard tests that press Tab twice per control must press it once.
|
|
97
|
+
- The CSS variable --semantic-typography-eyebrow-text-transform is gone, so set text-transform: uppercase yourself where a label must stay uppercase.
|
|
98
|
+
- Add import '@hirobius/design-system/fonts.css' next to your tokens.css or styles.css import, or text renders in the system font instead of Satoshi and Geist Mono.
|
|
99
|
+
- The ESLint plugin now ships in the package, so import hds from '@hirobius/design-system/eslint-plugin' and drop the separate @hirobius/eslint-plugin-hds dependency.
|
|
100
|
+
- A Badge with label but no dot now warns once in development, because label only names a dot badge, so put the text in children instead.
|
|
101
|
+
|
|
102
|
+
## 0.20.0
|
|
103
|
+
|
|
104
|
+
Released 2026-10-01 (minor). Pattern components move to /patterns; deprecated, lab and folded components, Hds\* aliases and 7 runtime deps are removed.
|
|
105
|
+
|
|
106
|
+
### Fixed for you
|
|
107
|
+
|
|
108
|
+
The upgrade command runs these codemods for you.
|
|
109
|
+
|
|
110
|
+
- Each of these is no longer exported from the package root; import it from @hirobius/design-system/patterns instead: `AssetImg`, `AssetImgProps`, `CodeBlock`, `CodeBlockProps`, `ErrorPattern`, `ErrorPatternProps`, `FieldWiring`, `FieldWiringInput`, `Form`, `FormField`, `FormFieldProps`, `FormFieldShell`, `FormFieldShellProps`, `FormProps`, `Page`, `PageProps`, `Reveal`, `RevealAnimation`, `RevealProps`, `StatusTile`, `StatusTileProps`, `StatusTileTone`, `blockCodeTextVariants`, `blockContainerVariants`, `blockHeaderVariants`, `chevronVariants`, `collapsibleToggleVariants`, `copyButtonVariants`, `inlineCodeTextVariants`, `inlineWrapperVariants`, `prePanelVariants` and `useFieldWiring`. Codemod: `hds-patterns-subpath`.
|
|
111
|
+
- HdsCheckbox is removed from the package root; use Checkbox, the same component under its bare name. Codemod: `hds-prefix`.
|
|
112
|
+
- HdsRadio is removed from the package root; use Radio, the same component under its bare name. Codemod: `hds-prefix`.
|
|
113
|
+
- HdsSelect is removed from the package root; use Select, the same component under its bare name. Codemod: `hds-prefix`.
|
|
114
|
+
- HdsSlider is removed from the package root; use Slider, the same component under its bare name. Codemod: `hds-prefix`.
|
|
115
|
+
- HdsToggle is removed from the package root; use Toggle, the same component under its bare name. Codemod: `hds-prefix`.
|
|
116
|
+
- HdsTooltip is removed from the package root; use Tooltip, the same component under its bare name. Codemod: `hds-prefix`.
|
|
117
|
+
- NotFoundPattern is removed; use ErrorPattern displayText="404" message="Page not found" from @hirobius/design-system/patterns (hds-not-found-pattern rewrites it) instead.
|
|
118
|
+
- TileGrid is removed; use Grid layout="auto-fill" minItemWidth="…" gap="medium" (hds-tile-grid rewrites it) instead.
|
|
119
|
+
|
|
120
|
+
### Looks different
|
|
121
|
+
|
|
122
|
+
- AssetImg's expand label now uses the on-accent text color, so it stays readable in dark mode.
|
|
123
|
+
- Box sx spacing given as a t-shirt name such as 'md' now applies that spacing, where before it applied none.
|
|
124
|
+
- A pressed Button now tints only its fill with a 5% overlay, white in dark mode, instead of dimming the whole control, and the press no longer animates.
|
|
125
|
+
- Input keeps its inner padding beside icons, the clear button and the spinner, so long text no longer runs under them.
|
|
126
|
+
- A string passed to Input's prefix prop now shows as text inside the field, where before it went to the input element as an attribute.
|
|
127
|
+
- A sortable Table header now fades its label to the muted color on hover.
|
|
128
|
+
- Tooltip now casts the floating shadow that popovers and menus use instead of the dialog shadow.
|
|
129
|
+
|
|
130
|
+
### Coming next
|
|
131
|
+
|
|
132
|
+
- Box sx spacing names 'tight', 'normal', 'inset' and 'spacious' still work but are deprecated, and a development build warns once for each; use 'sm' to 'xl' instead. Removed in 1.0.0.
|
|
133
|
+
- Stack gap names 'tight', 'normal', 'inset' and 'spacious' still work and do not warn yet, but are removed in 1.0.0, and Stack names no replacement until then.
|
|
134
|
+
- StatusDot still works but is removed in 0.21.0; use Badge dot with the same tone, size and label, and move any style prop to a wrapper or a className first.
|
|
135
|
+
- StatusDotProps is removed in 0.21.0; use BadgeProps instead.
|
|
136
|
+
- hds.density still works but is deprecated; use hds.semantic.space.scale one step down, so density.sm becomes scale.xs. Removed in 1.0.0.
|
|
137
|
+
- Sixteen Tailwind utilities that only HDS's deleted internal files used, such as pt-1 and max-w-2xl, still ship in styles.css and tokens.css until 1.0.0, so a page that relies on HDS's compiled CSS for them should generate them with its own Tailwind. Removed in 1.0.0.
|
|
138
|
+
|
|
139
|
+
### Do by hand
|
|
140
|
+
|
|
141
|
+
- Each of these is removed with no drop-in replacement, so rewrite or delete the code that imports it: `ActivityEvent`, `ActivityFeed`, `ActivityFeedProps`, `ActivityStatus`, `ActivityTone`, `AppShell`, `AppShellProps`, `ButtonGroup`, `ButtonGroupProps`, `Calendar`, `CalendarProps`, `Carousel`, `CarouselProps`, `CaseStudyLayout`, `CaseStudyLayoutProps`, `CinematicLink`, `CinematicLinkProps`, `CommandPalette`, `CommandPaletteProps`, `ComponentInstanceMatrix`, `ContextMenu`, `DateInput`, `DateInputProps`, `DateRangeInput`, `DateRangeInputProps`, `DateTimeInput`, `DateTimeInputProps`, `DocLinkCard`, `DocLinkCardProps`, `ErrorBoundary`, `ErrorBoundaryProps`, `FileInput`, `FileInputProps`, `FoundationSwatch`, `FoundationSwatchProps`, `HdsDocsShell`, `HdsDocsShellProps`, `HdsSystemDocLayout`, `HeadingStack`, `HeadingStackProps`, `HistoryCard`, `HistoryCardCommit`, `HistoryCardProps`, `HoverCard`, `Lightbox`, `LightboxProps`, `NavGroup`, `NavGroupProps`, `NavItem`, `NavProps`, `OverflowList`, `OverflowListProps`, `SideNav`, `SideNavLevel`, `SideNavProps`, `Sketch`, `SketchProps`, `StackedCardRail`, `StackedCardRailCard`, `StackedCardRailProps`, `Step`, `Stepper`, `StepperField`, `StepperFieldProps`, `StepperProps`, `TextLockup`, `TextLockupProps`, `Token`, `TokenProps`, `Tokenizer`, `TokenizerProps`, `Toolbar`, `ToolbarComponent`, `TopNav`, `TopNavProps`, `TreeList`, `TreeListProps`, `TreeNode`, `activityAvatarVariants`, `activityToneVariants`, `appShellVariants`, `buttonGroupVariants`, `carouselControlVariants`, `circularProgressVariants`, `cmdkDescriptionVariants`, `cmdkKindBadgeVariants`, `cmdkRowVariants`, `defaultActivityEvents`, `docLinkCardVariants`, `fileInputVariants`, `hdsTimeInputVariants`, `hdsToggleButtonVariants`, `headingStackLevelVariants`, `headingStackVariants`, `navGroupLabelVariants`, `navIndicatorVariants`, `navItemVariants`, `selectableCardVariants`, `sideNavVariants`, `stepMarkerVariants`, `tokenLabelVariants`, `tokenNodeInlineVariants` and `tokenShellVariants`.
|
|
142
|
+
- Each of these is no longer exported, so style its component through the component's props instead: `badgeVariants`, `blockquoteVariants`, `buttonVariants`, `cardVariants`, `crumbLabelVariants`, `disclosureTriggerVariants`, `fieldValueVariants`, `inlineCodeVariants`, `inputVariants`, `kbdVariants`, `metadataListVariants`, `overflowBubbleVariants`, `progressTrackVariants`, `segmentedControlDescriptionVariants`, `segmentedControlFocusRingVariants`, `segmentedControlIndicatorVariants`, `segmentedControlItemVariants`, `segmentedControlLabelVariants`, `segmentedControlRailVariants`, `segmentedControlWrapperVariants`, `skeletonVariants`, `spinnerVariants`, `statVariants`, `statusDotVariants`, `statusListItemDotVariants`, `surfaceVariants`, `tableDataCellVariants`, `tableHeaderCellVariants`, `tableSortButtonVariants`, `tagButtonVariants`, `tagPillVariants`, `textVariants`, `textareaVariants` and `toastIconVariants`.
|
|
143
|
+
- AspectRatio is removed; use Box style={{ aspectRatio: '16 / 9' }} instead.
|
|
144
|
+
- Each of these is removed; use BoxProps instead: `AspectRatioProps`, `BleedProps`, `CoverProps` and `FrameProps`.
|
|
145
|
+
- Bleed is removed; use Box style={{ marginInline: 'calc(-1 \* var(--semantic-space-scale-md))' }} instead.
|
|
146
|
+
- Center is removed; use Container maxWidth="content", with a Box style={{ paddingInline }} inside for the gutter instead.
|
|
147
|
+
- CenterProps is removed; use ContainerProps instead.
|
|
148
|
+
- CircularProgress is removed; use Progress variant="circular" instead.
|
|
149
|
+
- CircularProgressProps is removed; use ProgressProps instead.
|
|
150
|
+
- Cluster is removed; use Stack direction="row" wrap="wrap" align="center" instead.
|
|
151
|
+
- ClusterProps is removed; use StackProps instead.
|
|
152
|
+
- Cover is removed; use Box style={{ display: 'flex', flexDirection: 'column', minHeight: '100svh' }} around a Box style={{ marginBlock: 'auto' }} instead.
|
|
153
|
+
- Frame is removed; use Box style={{ aspectRatio: '16 / 9', overflow: 'hidden', borderRadius: hds.borderRadius.md }} instead.
|
|
154
|
+
- IconButton is removed; use Button iconOnly label="…" iconLeft={\<Icon icon={…} />} instead.
|
|
155
|
+
- Each of these is removed; use ButtonProps instead: `IconButtonProps` and `ToggleButtonProps`.
|
|
156
|
+
- InputGroup is removed; use Input prefix / suffix instead.
|
|
157
|
+
- Each of these is removed; use InputProps instead: `InputGroupProps` and `TimeInputProps`.
|
|
158
|
+
- MultiSelector is removed; use Combobox multiple instead.
|
|
159
|
+
- MultiSelectorOption is removed; use ComboboxOption instead.
|
|
160
|
+
- MultiSelectorProps is removed; use ComboboxMultipleProps instead.
|
|
161
|
+
- SelectableCard is removed; use Card selectable selected onSelectedChange instead.
|
|
162
|
+
- SelectableCardProps is removed; use CardProps instead.
|
|
163
|
+
- TileGridProps is removed; use GridProps (minTileWidth is minItemWidth, gap 'sm' is 'medium') instead.
|
|
164
|
+
- TimeInput is removed; use Input type="time" instead.
|
|
165
|
+
- ToggleButton is removed; use Button pressed onPressedChange (variant="ghost" is variant="tertiary") instead.
|
|
166
|
+
- HDS no longer installs each of these, so add it to your own dependencies if your code imports it: `@radix-ui/react-aspect-ratio`, `@radix-ui/react-context-menu`, `@radix-ui/react-hover-card`, `@radix-ui/react-toggle`, `@radix-ui/react-toolbar`, `date-fns` and `react-day-picker`.
|
|
167
|
+
- An icon-only Button now takes its accessible name from label, unless it has an aria-label.
|
|
168
|
+
- Card selectable, which replaces SelectableCard, toggles on click or Space only, so code or tests that press Enter to toggle it must press Space instead.
|
|
169
|
+
- Combobox options now report their real position, and a Select with showLabel={false} is named by its label and value, so tests that find them by role and name may need updating.
|
|
170
|
+
- The open Select list and the Combobox popup now carry an accessible name, so tests that find them by role and name may need the new name.
|
|
171
|
+
- The manifest no longer has a health field, so manifest.health is now undefined.
|
|
172
|
+
- The rows inside PageHeader, FormActions, DataTableSection and DestructiveSection now carry data-hds-component="Stack" instead of "Cluster", so selectors and tests that look for Cluster there must change.
|
|
173
|
+
|
|
174
|
+
## 0.19.1
|
|
175
|
+
|
|
176
|
+
Released 2026-09-30 (patch). Smaller Button-only bundles for webpack and esbuild, and Storybook accessibility fixes; nothing for a consumer to change.
|
|
177
|
+
|
|
178
|
+
Nothing in this release asks anything of you.
|
|
179
|
+
|
|
180
|
+
## 0.19.0
|
|
181
|
+
|
|
182
|
+
Released 2026-09-30 (minor). Overlays portal into the nearest data-hds scope, compact density tightens spacing, containers share one radius; adds /icons.
|
|
183
|
+
|
|
184
|
+
### Looks different
|
|
185
|
+
|
|
186
|
+
- A Table with no density prop now follows the nearest data-density attribute, so a Table inside a compact region renders compact unless you pass density.
|
|
187
|
+
- Under compact density (data-density="compact", which HdsThemeProvider density="compact" and ThemeProvider's setDensity also set) the spacing scale, surface padding and region gutter that components read now tighten, so compact screens render visibly tighter than before.
|
|
188
|
+
- Surface, StatusTile, DocLinkCard, static.css's .hds-card and the .hds-soft-nav-card and .hds-sketchbook-canvas-stage classes now round to the tenant's container radius, 12px by default, instead of a fixed 8px.
|
|
189
|
+
- The dark theme (data-theme="dark" or the dark class, which HdsThemeProvider theme="dark" and ThemeProvider set) now declares color-scheme: dark, so native scrollbars and form controls inside it render dark.
|
|
190
|
+
- The scrim behind an open Dialog, AlertDialog or CommandPalette is now the theme-aware scrim color, black in dark mode where it was a white wash.
|
|
191
|
+
|
|
192
|
+
### Do by hand
|
|
193
|
+
|
|
194
|
+
- In development, a Button with iconOnly and no iconLeft now logs one warning, so a test that fails on console warnings will flag it.
|
|
195
|
+
- Overlays mount in the nearest data-hds element, not document.body, and take its theme, so check CSS or tests that query body: Dialog, AlertDialog, Menu, ContextMenu, Popover, Select, HoverCard, Tooltip, Lightbox, AssetImg, and the overlays of Combobox, MultiSelector, CommandPalette and date inputs.
|
|
196
|
+
|
|
197
|
+
## 0.18.0
|
|
198
|
+
|
|
199
|
+
Released 2026-09-30 (minor). Adds the hds-patterns-subpath codemod, which moves root imports of the deprecated pattern components to /patterns.
|
|
200
|
+
|
|
201
|
+
Nothing in this release asks anything of you.
|
|
202
|
+
|
|
203
|
+
## 0.17.0
|
|
204
|
+
|
|
205
|
+
Released 2026-09-30 (minor). Adds /patterns and the standard type ramp; root pattern imports, Hds\* names and seven spacing tokens are deprecated.
|
|
206
|
+
|
|
207
|
+
### Looks different
|
|
208
|
+
|
|
209
|
+
- Badge text grows from 11px to 12px, CommandPalette's kind badges, shortcuts and footer from 10px to 12px, and SegmentedControl labels shrink from 15px to 14px on a 20px line, now that they use the ramp's text-xs and text-sm.
|
|
210
|
+
- Calendar's days outside the shown month are no longer drawn at half opacity, so they read darker.
|
|
211
|
+
- A disabled Tokenizer shows muted text instead of fading the whole field to 70% opacity.
|
|
212
|
+
- Second-level headings (Text and HeadingStack heading2, and any h2 inside HdsThemeProvider or another data-hds element) keep their 30px size, but their line height drops from 42px to 40px.
|
|
213
|
+
- Elements with the hds-focus class, which many HDS components use (Dialog, Select, Radio, Toggle and Slider among them), now show their focus ring on every keyboard focus, where before it showed only if the page set data-input-modality="keyboard" on html, which no HDS code did.
|
|
214
|
+
- The highlighted option in Menu, Select, Combobox and MultiSelector now also shows an inset ring in the focus color.
|
|
215
|
+
- Text follows Tailwind's default type ramp, so body text renders at 16px instead of 17px, UI and button text 1px smaller, captions and eyebrows at 12px instead of 13px, mono at 14px instead of 13px, display at 60px instead of 72px, and text-xs to text-6xl shrink to match.
|
|
216
|
+
|
|
217
|
+
### Coming next
|
|
218
|
+
|
|
219
|
+
- Importing each of these from the package root still works but is deprecated; import it from @hirobius/design-system/patterns instead: `ActivityFeed`, `AppShell`, `AssetImg`, `Calendar`, `Carousel`, `CodeBlock`, `CommandPalette`, `DocLinkCard`, `ErrorPattern`, `FileInput`, `Form`, `Lightbox`, `NavItem`, `OverflowList`, `Page`, `Reveal`, `SideNav`, `Stepper`, `Toolbar`, `TopNav` and `TreeList`. Removed early, in [0.20.0](#0200) (planned for 1.0.0).
|
|
220
|
+
- HdsCheckbox still works but is deprecated; use Checkbox, the same component under its bare name. Removed early, in [0.20.0](#0200) (planned for 1.0.0).
|
|
221
|
+
- HdsRadio still works but is deprecated; use Radio, the same component under its bare name. Removed early, in [0.20.0](#0200) (planned for 1.0.0).
|
|
222
|
+
- HdsSelect still works but is deprecated; use Select, the same component under its bare name. Removed early, in [0.20.0](#0200) (planned for 1.0.0).
|
|
223
|
+
- HdsSlider still works but is deprecated; use Slider, the same component under its bare name. Removed early, in [0.20.0](#0200) (planned for 1.0.0).
|
|
224
|
+
- HdsToggle still works but is deprecated; use Toggle, the same component under its bare name. Removed early, in [0.20.0](#0200) (planned for 1.0.0).
|
|
225
|
+
- HdsTooltip still works but is deprecated; use Tooltip, the same component under its bare name. Removed early, in [0.20.0](#0200) (planned for 1.0.0).
|
|
226
|
+
- The spacing token semantic.space.component.gap (--semantic-space-component-gap) still works but is deprecated; use semantic.space.scale.xs, the same 8px at the default density. Removed in 1.0.0.
|
|
227
|
+
- The spacing token semantic.space.component.padding (--semantic-space-component-padding) still works but is deprecated; use semantic.space.surface.padding, which it now aliases. Removed in 1.0.0.
|
|
228
|
+
- The spacing token semantic.space.layout.gutter (--semantic-space-layout-gutter) still works but is deprecated; use semantic.space.region.gutter, which it now aliases. Removed in 1.0.0.
|
|
229
|
+
- The spacing token semantic.space.layout.inset (--semantic-space-layout-inset) still works but is deprecated; use semantic.space.scale.lg, the same 32px at the default density. Removed in 1.0.0.
|
|
230
|
+
- The spacing token semantic.space.layout.normal (--semantic-space-layout-normal) still works but is deprecated; use semantic.space.scale.md, the same 24px at the default density. Removed in 1.0.0.
|
|
231
|
+
- The spacing token semantic.space.layout.spacious (--semantic-space-layout-spacious) still works but is deprecated; use semantic.space.scale.xl, the same 48px at the default density. Removed in 1.0.0.
|
|
232
|
+
- The spacing token semantic.space.layout.tight (--semantic-space-layout-tight) still works but is deprecated; use semantic.space.scale.sm, the same 16px at the default density. Removed in 1.0.0.
|
|
233
|
+
|
|
234
|
+
### Do by hand
|
|
235
|
+
|
|
236
|
+
- AssetImg with alt text is now role img named by it, and one with neither alt nor onClick is hidden from assistive technology, so tests that find it by role or name may need updating.
|
|
237
|
+
- Card.Progress now carries an aria-label, its string label or "Progress", so tests that find it by role and name may need the new name.
|
|
238
|
+
- StepperField's label is now tied to its input, so the input is announced and found by its label text.
|
|
239
|
+
- Table now renders role table, row, columnheader and cell, wrapping each row in an element with display: contents, so selectors and tests that query its cells by role or by position in the DOM may need updating.
|
|
240
|
+
- OverflowList puts its +N item in a list item and names it with hidden text instead of aria-label, and MetadataList wraps its footer in a dt and dd pair, so tests that query that markup or the "+N more" label may need updating.
|
|
241
|
+
- Carousel's slide strip and StackedCardRail's scroll areas are now named regions that take keyboard focus, so each adds a Tab stop and tests that count Tab presses past them need one more.
|
|
242
|
+
|
|
243
|
+
<!-- Generated by scripts/upgrade/compile.mjs from upgrade/releases/*.json; do not edit. Run node scripts/upgrade/compile.mjs to regenerate. -->
|
package/codemods/hds-prefix.mjs
CHANGED
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
* end (codemods/unrewritable.mjs), needs a manual look
|
|
31
31
|
* --dry-run write nothing; print each changed line before (-) and after (+), exit 0
|
|
32
32
|
*/
|
|
33
|
-
import { readFileSync, readdirSync, realpathSync
|
|
33
|
+
import { readFileSync, readdirSync, realpathSync } from 'node:fs';
|
|
34
|
+
import { writeFileAtomic } from './lib/atomic-write.mjs';
|
|
34
35
|
import { relative, resolve, join } from 'node:path';
|
|
35
36
|
import { fileURLToPath } from 'node:url';
|
|
36
37
|
import { findUnrewritable as findHidden, maskSource } from './unrewritable.mjs';
|
|
@@ -135,24 +136,38 @@ export function findUnrewritable(source, renames = RENAMES) {
|
|
|
135
136
|
return findHidden(source, ROOT_PKG, Object.keys(renames));
|
|
136
137
|
}
|
|
137
138
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
/** `skip`: absolute directories not to enter (the upgrade command's nested importers). */
|
|
140
|
+
function* walk(dir, skip = new Set()) {
|
|
141
|
+
let entries;
|
|
142
|
+
try {
|
|
143
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
144
|
+
} catch {
|
|
145
|
+
return; // an unreadable directory has nothing to rewrite
|
|
146
|
+
}
|
|
147
|
+
// Symlinks are skipped, never followed: a dangling one cannot be read and a
|
|
148
|
+
// looping one never ends.
|
|
149
|
+
for (const entry of entries) {
|
|
150
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
151
|
+
const full = join(dir, entry.name);
|
|
152
|
+
if (entry.isDirectory()) {
|
|
153
|
+
if (!skip.has(full)) yield* walk(full, skip);
|
|
154
|
+
} else if (entry.isFile() && EXTS.has(full.slice(full.lastIndexOf('.')))) yield full;
|
|
145
155
|
}
|
|
146
156
|
}
|
|
147
157
|
|
|
148
|
-
/** Scan a directory. Writes only when `write` is true. */
|
|
149
|
-
export function runCodemod({ root, write = false, renames = RENAMES }) {
|
|
158
|
+
/** Scan a directory. Writes only when `write` is true; `skip` lists directories not to enter. */
|
|
159
|
+
export function runCodemod({ root, write = false, renames = RENAMES, skip = [] }) {
|
|
150
160
|
const files = [];
|
|
151
161
|
const manual = [];
|
|
152
162
|
const renamed = new Set();
|
|
153
163
|
let sites = 0;
|
|
154
|
-
for (const file of walk(root)) {
|
|
155
|
-
|
|
164
|
+
for (const file of walk(root, new Set(skip.map((d) => resolve(d))))) {
|
|
165
|
+
let src;
|
|
166
|
+
try {
|
|
167
|
+
src = readFileSync(file, 'utf8');
|
|
168
|
+
} catch {
|
|
169
|
+
continue; // unreadable: nothing to rewrite
|
|
170
|
+
}
|
|
156
171
|
if (!src.includes(ROOT_PKG)) continue;
|
|
157
172
|
const r = transformSource(src, renames);
|
|
158
173
|
for (const stmt of findUnrewritable(r.source, renames))
|
|
@@ -161,7 +176,7 @@ export function runCodemod({ root, write = false, renames = RENAMES }) {
|
|
|
161
176
|
files.push({ file: relative(root, file), sites: r.sites, renamed: r.renamed, edits: r.edits });
|
|
162
177
|
r.renamed.forEach((n) => renamed.add(n));
|
|
163
178
|
sites += r.sites;
|
|
164
|
-
if (write)
|
|
179
|
+
if (write) writeFileAtomic(file, r.source);
|
|
165
180
|
}
|
|
166
181
|
return { files, sites, manual, names: [...renamed].sort() };
|
|
167
182
|
}
|
package/codemods/jsx-fold.mjs
CHANGED
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
* removed props type, and a namespace or dynamic import that reads the name are
|
|
23
23
|
* listed too. Running a fold twice changes nothing.
|
|
24
24
|
*/
|
|
25
|
-
import { readFileSync, readdirSync, realpathSync
|
|
25
|
+
import { readFileSync, readdirSync, realpathSync } from 'node:fs';
|
|
26
|
+
import { writeFileAtomic } from './lib/atomic-write.mjs';
|
|
26
27
|
import { join, relative, resolve } from 'node:path';
|
|
27
28
|
import { fileURLToPath } from 'node:url';
|
|
28
29
|
import { findUnrewritable, maskSource } from './unrewritable.mjs';
|
|
@@ -421,30 +422,44 @@ export function insertionPoint(source, tag, attr) {
|
|
|
421
422
|
return attr ? { at: attr.start, sep } : { at: tag.nameEnd, sep };
|
|
422
423
|
}
|
|
423
424
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
425
|
+
/** `skip`: absolute directories not to enter (the upgrade command's nested importers). */
|
|
426
|
+
function* walk(dir, skip = new Set()) {
|
|
427
|
+
let entries;
|
|
428
|
+
try {
|
|
429
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
430
|
+
} catch {
|
|
431
|
+
return; // an unreadable directory has nothing to rewrite
|
|
432
|
+
}
|
|
433
|
+
// Symlinks are skipped, never followed: a dangling one cannot be read and a
|
|
434
|
+
// looping one never ends.
|
|
435
|
+
for (const entry of entries) {
|
|
436
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
437
|
+
const full = join(dir, entry.name);
|
|
438
|
+
if (entry.isDirectory()) {
|
|
439
|
+
if (!skip.has(full)) yield* walk(full, skip);
|
|
440
|
+
} else if (entry.isFile() && EXTS.has(full.slice(full.lastIndexOf('.')))) yield full;
|
|
431
441
|
}
|
|
432
442
|
}
|
|
433
443
|
|
|
434
|
-
/** Scan a directory with one rule. Writes only when `write` is true. */
|
|
435
|
-
export function runFold({ root, write = false, rule }) {
|
|
444
|
+
/** Scan a directory with one rule. Writes only when `write` is true; `skip` lists directories not to enter. */
|
|
445
|
+
export function runFold({ root, write = false, rule, skip = [] }) {
|
|
436
446
|
const files = [];
|
|
437
447
|
const manual = [];
|
|
438
448
|
let sites = 0;
|
|
439
|
-
for (const file of walk(root)) {
|
|
440
|
-
|
|
449
|
+
for (const file of walk(root, new Set(skip.map((d) => resolve(d))))) {
|
|
450
|
+
let src;
|
|
451
|
+
try {
|
|
452
|
+
src = readFileSync(file, 'utf8');
|
|
453
|
+
} catch {
|
|
454
|
+
continue; // unreadable: nothing to rewrite
|
|
455
|
+
}
|
|
441
456
|
if (!src.includes(ROOT_PKG)) continue;
|
|
442
457
|
const r = foldComponent(src, rule);
|
|
443
458
|
for (const stmt of r.manual) manual.push({ file: relative(root, file), stmt });
|
|
444
459
|
if (!r.changed) continue;
|
|
445
460
|
files.push({ file: relative(root, file), sites: r.sites, edits: r.edits });
|
|
446
461
|
sites += r.sites;
|
|
447
|
-
if (write)
|
|
462
|
+
if (write) writeFileAtomic(file, r.source);
|
|
448
463
|
}
|
|
449
464
|
return { files, sites, manual };
|
|
450
465
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* atomic-write — replace a file without ever leaving half of it (hds#452).
|
|
3
|
+
* The text goes to a temp file next to it, which is then renamed over it, so
|
|
4
|
+
* a full disk or a kill partway leaves the old file or the new one, never a
|
|
5
|
+
* truncated one. The file keeps its mode. Node builtins only.
|
|
6
|
+
*/
|
|
7
|
+
import { chmodSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} file
|
|
11
|
+
* @param {string} text
|
|
12
|
+
*/
|
|
13
|
+
export function writeFileAtomic(file, text) {
|
|
14
|
+
let mode;
|
|
15
|
+
try {
|
|
16
|
+
mode = statSync(file).mode & 0o7777;
|
|
17
|
+
} catch {
|
|
18
|
+
mode = undefined; // a new file: the default mode
|
|
19
|
+
}
|
|
20
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
21
|
+
try {
|
|
22
|
+
writeFileSync(tmp, text);
|
|
23
|
+
if (mode !== undefined) chmodSync(tmp, mode);
|
|
24
|
+
renameSync(tmp, file);
|
|
25
|
+
} finally {
|
|
26
|
+
rmSync(tmp, { force: true });
|
|
27
|
+
}
|
|
28
|
+
}
|