@hirobius/design-system 0.19.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONSUMING.md +9 -6
- package/DESIGN.md +7 -5
- package/README.md +14 -11
- package/codemods/hds-prefix.mjs +231 -0
- package/codemods/jsx-fold.mjs +515 -0
- package/codemods/not-found-pattern.mjs +71 -0
- package/codemods/patterns-subpath.mjs +242 -81
- package/codemods/patterns-subpath.names.json +41 -15
- package/codemods/removed-0.20.json +194 -0
- package/codemods/tile-grid.mjs +283 -0
- package/codemods/unrewritable.mjs +291 -0
- package/dist/chunks/DemoStateContext-Cz5TmrYu.js +32 -0
- package/dist/chunks/empty-state-BBqhCx0S.js +1217 -0
- package/dist/contexts.js +157 -78
- package/dist/hirobius-ui.js +1517 -5270
- package/dist/manifest.js +12 -13
- package/dist/patterns.js +780 -513
- package/dist/styles.css +1 -1
- package/dist/tokens.css +1 -1
- package/dist/tokens.js +29 -8
- package/dist/types/src/app/components/avatar-group.d.ts +1 -2
- package/dist/types/src/app/components/badge.d.ts +8 -3
- package/dist/types/src/app/components/blockquote.d.ts +1 -2
- package/dist/types/src/app/components/box-sx.d.ts +110 -13
- package/dist/types/src/app/components/box.d.ts +6 -3
- package/dist/types/src/app/components/breadcrumb.d.ts +0 -5
- package/dist/types/src/app/components/button.d.ts +33 -4
- package/dist/types/src/app/components/card.d.ts +7 -8
- package/dist/types/src/app/components/combobox.d.ts +47 -11
- package/dist/types/src/app/components/container.d.ts +2 -2
- package/dist/types/src/app/components/data-table-section.d.ts +1 -1
- package/dist/types/src/app/components/dialog.d.ts +7 -1
- package/dist/types/src/app/components/disclosure.d.ts +1 -5
- package/dist/types/src/app/components/field.d.ts +1 -2
- package/dist/types/src/app/components/grid.d.ts +10 -6
- package/dist/types/src/app/components/hds-tooltip.d.ts +12 -2
- package/dist/types/src/app/components/inline-code.d.ts +1 -2
- package/dist/types/src/app/components/input.d.ts +8 -5
- package/dist/types/src/app/components/kbd.d.ts +1 -2
- package/dist/types/src/app/components/metadata-list.d.ts +1 -2
- package/dist/types/src/app/components/page-header.d.ts +0 -2
- package/dist/types/src/app/components/popover.d.ts +8 -0
- package/dist/types/src/app/components/progress.d.ts +33 -6
- package/dist/types/src/app/components/segmented-control.d.ts +2 -26
- package/dist/types/src/app/components/select.d.ts +10 -1
- package/dist/types/src/app/components/skeleton.d.ts +2 -3
- package/dist/types/src/app/components/spinner.d.ts +1 -2
- package/dist/types/src/app/components/stack.d.ts +4 -1
- package/dist/types/src/app/components/stat.d.ts +1 -2
- package/dist/types/src/app/components/status-dot.d.ts +18 -4
- package/dist/types/src/app/components/status-list-item.d.ts +1 -2
- package/dist/types/src/app/components/surface.d.ts +2 -3
- package/dist/types/src/app/components/switcher.d.ts +1 -1
- package/dist/types/src/app/components/table.d.ts +1 -15
- package/dist/types/src/app/components/tag.d.ts +0 -6
- package/dist/types/src/app/components/text.d.ts +1 -2
- package/dist/types/src/app/components/textarea.d.ts +1 -2
- package/dist/types/src/app/components/toast.d.ts +1 -2
- package/dist/types/src/app/components/tooltip.d.ts +1 -1
- package/dist/types/src/app/context/DemoStateContext.d.ts +1 -1
- package/dist/types/src/app/context/RouterContext.d.ts +5 -5
- package/dist/types/src/app/data/manifest-types.d.ts +6 -5
- package/dist/types/src/app/design-system/generated-token-refs.d.ts +8 -0
- package/dist/types/src/app/design-system/generated-token-values.d.ts +1 -0
- package/dist/types/src/app/design-system/tokens.d.ts +22 -3
- package/dist/types/src/icons.d.ts +1 -1
- package/dist/types/src/index.d.ts +33 -124
- package/dist/types/src/patterns.d.ts +20 -25
- package/dist/variables.css +6 -0
- package/docs/CONSUMING.md +21 -13
- package/hirobius.tokens.json +48 -15
- package/llms.txt +19 -20
- package/package.json +17 -22
- package/public/hds-manifest.json +2405 -6537
- package/public/llms/components.txt +41 -423
- package/public/llms/layout.txt +4 -4
- package/public/llms-full.txt +67 -448
- package/public/llms.txt +19 -20
- package/src/app/data/component-api.json +1379 -3271
- package/dist/chunks/LanguageContext-BxdCwiFg.js +0 -95
- package/dist/chunks/TokenDisplayContext-DKYJMVWk.js +0 -26
- package/dist/chunks/activity-feed-BGQ6v3wc.js +0 -22040
- package/dist/types/src/app/components/activity-feed.d.ts +0 -73
- package/dist/types/src/app/components/app-shell.d.ts +0 -40
- package/dist/types/src/app/components/aspect-ratio.d.ts +0 -25
- package/dist/types/src/app/components/bleed.d.ts +0 -39
- package/dist/types/src/app/components/button-group.d.ts +0 -22
- package/dist/types/src/app/components/calendar.d.ts +0 -22
- package/dist/types/src/app/components/carousel.d.ts +0 -26
- package/dist/types/src/app/components/center.d.ts +0 -36
- package/dist/types/src/app/components/cinematic-link.d.ts +0 -18
- package/dist/types/src/app/components/circular-progress.d.ts +0 -28
- package/dist/types/src/app/components/cluster.d.ts +0 -39
- package/dist/types/src/app/components/command-palette.d.ts +0 -46
- package/dist/types/src/app/components/component-instance-matrix.d.ts +0 -27
- package/dist/types/src/app/components/context-menu.d.ts +0 -78
- package/dist/types/src/app/components/cover.d.ts +0 -43
- package/dist/types/src/app/components/date-input.d.ts +0 -31
- package/dist/types/src/app/components/date-range-input.d.ts +0 -34
- package/dist/types/src/app/components/date-time-input.d.ts +0 -30
- package/dist/types/src/app/components/doc-link-card.d.ts +0 -32
- package/dist/types/src/app/components/error-boundary.d.ts +0 -25
- package/dist/types/src/app/components/file-input.d.ts +0 -30
- package/dist/types/src/app/components/foundation-swatch.d.ts +0 -45
- package/dist/types/src/app/components/frame.d.ts +0 -38
- package/dist/types/src/app/components/heading-stack.d.ts +0 -52
- package/dist/types/src/app/components/history-card.d.ts +0 -12
- package/dist/types/src/app/components/hover-card.d.ts +0 -46
- package/dist/types/src/app/components/icon-button.d.ts +0 -22
- package/dist/types/src/app/components/image-lightbox.d.ts +0 -14
- package/dist/types/src/app/components/input-group.d.ts +0 -23
- package/dist/types/src/app/components/multi-selector.d.ts +0 -40
- package/dist/types/src/app/components/nav-group.d.ts +0 -29
- package/dist/types/src/app/components/nav-item.d.ts +0 -33
- package/dist/types/src/app/components/not-found-pattern.d.ts +0 -1
- package/dist/types/src/app/components/overflow-list.d.ts +0 -21
- package/dist/types/src/app/components/selectable-card.d.ts +0 -29
- package/dist/types/src/app/components/side-nav.d.ts +0 -23
- package/dist/types/src/app/components/sketch.d.ts +0 -19
- package/dist/types/src/app/components/stacked-card-rail.d.ts +0 -32
- package/dist/types/src/app/components/stepper-field.d.ts +0 -12
- package/dist/types/src/app/components/stepper.d.ts +0 -33
- package/dist/types/src/app/components/text-lockup.d.ts +0 -38
- package/dist/types/src/app/components/tile-grid.d.ts +0 -17
- package/dist/types/src/app/components/time-input.d.ts +0 -24
- package/dist/types/src/app/components/toggle-button.d.ts +0 -32
- package/dist/types/src/app/components/token.d.ts +0 -90
- package/dist/types/src/app/components/tokenUtils.d.ts +0 -38
- package/dist/types/src/app/components/tokenizer.d.ts +0 -24
- package/dist/types/src/app/components/toolbar.d.ts +0 -36
- package/dist/types/src/app/components/top-nav.d.ts +0 -23
- package/dist/types/src/app/components/tree-list.d.ts +0 -30
- package/dist/types/src/app/data/nav-model.d.ts +0 -39
- package/dist/types/src/app/hooks/useFocusVisible.d.ts +0 -9
- package/dist/types/src/app/layouts/CaseStudyLayout.d.ts +0 -20
- package/dist/types/src/app/layouts/DocLayout.d.ts +0 -14
- package/dist/types/src/app/layouts/EmbeddedDocLayoutContext.d.ts +0 -6
- package/dist/types/src/app/layouts/HdsDocsShell.d.ts +0 -72
- package/dist/types/src/app/layouts/HdsSystemDocLayout.d.ts +0 -14
- package/dist/types/src/app/lib/hds-search.d.ts +0 -45
- package/dist/types/src/app/lib/nav-active.d.ts +0 -16
- package/dist/types/src/app/lib/navLevels.d.ts +0 -4
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Every export 0.20.0 removed from the package root or @hirobius/design-system/patterns with no replacement (hds#389 R1 docs/lab components, hds#394 wave 4a and the root *Variants helpers), keyed by the source module that exported it. Hand-kept because the modules are gone; scripts/__tests__/patterns-subpath-removed.test.mjs fails if any listed name is exported from the root or /patterns again. codemods/patterns-subpath.mjs reports an import of a listed name for a manual edit instead of moving it. Names that moved (/patterns) or were renamed (Hds* aliases) are not here; MIGRATIONS.md '0.20.0 removals' has every row. `replaced` holds the names removed with a survivor (hds#394 wave 4b, and hds#395 B5 where a codemod of its own rewrites ops' sites), by module, each with the survivor the codemod's report names (\"use <survivor>\") instead of \"no replacement\".",
|
|
3
|
+
"release": "0.20.0",
|
|
4
|
+
"modules": {
|
|
5
|
+
"src/app/components/activity-feed.tsx": [
|
|
6
|
+
"activityAvatarVariants",
|
|
7
|
+
"ActivityEvent",
|
|
8
|
+
"ActivityFeed",
|
|
9
|
+
"ActivityFeedProps",
|
|
10
|
+
"ActivityStatus",
|
|
11
|
+
"ActivityTone",
|
|
12
|
+
"activityToneVariants",
|
|
13
|
+
"defaultActivityEvents"
|
|
14
|
+
],
|
|
15
|
+
"src/app/components/app-shell.tsx": ["AppShell", "AppShellProps", "appShellVariants"],
|
|
16
|
+
"src/app/components/avatar-group.tsx": ["overflowBubbleVariants"],
|
|
17
|
+
"src/app/components/badge.tsx": ["badgeVariants"],
|
|
18
|
+
"src/app/components/blockquote.tsx": ["blockquoteVariants"],
|
|
19
|
+
"src/app/components/breadcrumb.tsx": ["crumbLabelVariants"],
|
|
20
|
+
"src/app/components/button-group.tsx": [
|
|
21
|
+
"ButtonGroup",
|
|
22
|
+
"ButtonGroupProps",
|
|
23
|
+
"buttonGroupVariants"
|
|
24
|
+
],
|
|
25
|
+
"src/app/components/button.tsx": ["buttonVariants"],
|
|
26
|
+
"src/app/components/calendar.tsx": ["Calendar", "CalendarProps"],
|
|
27
|
+
"src/app/components/card.tsx": ["cardVariants"],
|
|
28
|
+
"src/app/components/carousel.tsx": ["Carousel", "carouselControlVariants", "CarouselProps"],
|
|
29
|
+
"src/app/components/cinematic-link.tsx": ["CinematicLink", "CinematicLinkProps"],
|
|
30
|
+
"src/app/components/circular-progress.tsx": ["circularProgressVariants"],
|
|
31
|
+
"src/app/components/command-palette.tsx": [
|
|
32
|
+
"cmdkDescriptionVariants",
|
|
33
|
+
"cmdkKindBadgeVariants",
|
|
34
|
+
"cmdkRowVariants",
|
|
35
|
+
"CommandPalette",
|
|
36
|
+
"CommandPaletteProps"
|
|
37
|
+
],
|
|
38
|
+
"src/app/components/component-instance-matrix.tsx": ["ComponentInstanceMatrix"],
|
|
39
|
+
"src/app/components/context-menu.tsx": ["ContextMenu"],
|
|
40
|
+
"src/app/components/date-input.tsx": ["DateInput", "DateInputProps"],
|
|
41
|
+
"src/app/components/date-range-input.tsx": ["DateRangeInput", "DateRangeInputProps"],
|
|
42
|
+
"src/app/components/date-time-input.tsx": ["DateTimeInput", "DateTimeInputProps"],
|
|
43
|
+
"src/app/components/disclosure.tsx": ["disclosureTriggerVariants"],
|
|
44
|
+
"src/app/components/doc-link-card.tsx": [
|
|
45
|
+
"DocLinkCard",
|
|
46
|
+
"DocLinkCardProps",
|
|
47
|
+
"docLinkCardVariants"
|
|
48
|
+
],
|
|
49
|
+
"src/app/components/error-boundary.tsx": ["ErrorBoundary", "ErrorBoundaryProps"],
|
|
50
|
+
"src/app/components/field.tsx": ["fieldValueVariants"],
|
|
51
|
+
"src/app/components/file-input.tsx": ["FileInput", "FileInputProps", "fileInputVariants"],
|
|
52
|
+
"src/app/components/foundation-swatch.tsx": ["FoundationSwatch", "FoundationSwatchProps"],
|
|
53
|
+
"src/app/components/heading-stack.tsx": [
|
|
54
|
+
"HeadingStack",
|
|
55
|
+
"headingStackLevelVariants",
|
|
56
|
+
"HeadingStackProps",
|
|
57
|
+
"headingStackVariants"
|
|
58
|
+
],
|
|
59
|
+
"src/app/components/history-card.tsx": ["HistoryCard", "HistoryCardCommit", "HistoryCardProps"],
|
|
60
|
+
"src/app/components/hover-card.tsx": ["HoverCard"],
|
|
61
|
+
"src/app/components/image-lightbox.tsx": ["Lightbox", "LightboxProps"],
|
|
62
|
+
"src/app/components/inline-code.tsx": ["inlineCodeVariants"],
|
|
63
|
+
"src/app/components/input.tsx": ["inputVariants"],
|
|
64
|
+
"src/app/components/kbd.tsx": ["kbdVariants"],
|
|
65
|
+
"src/app/components/metadata-list.tsx": ["metadataListVariants"],
|
|
66
|
+
"src/app/components/nav-group.tsx": ["NavGroup", "navGroupLabelVariants", "NavGroupProps"],
|
|
67
|
+
"src/app/components/nav-item.tsx": [
|
|
68
|
+
"navIndicatorVariants",
|
|
69
|
+
"NavItem",
|
|
70
|
+
"navItemVariants",
|
|
71
|
+
"NavProps"
|
|
72
|
+
],
|
|
73
|
+
"src/app/components/overflow-list.tsx": ["OverflowList", "OverflowListProps"],
|
|
74
|
+
"src/app/components/progress.tsx": ["progressTrackVariants"],
|
|
75
|
+
"src/app/components/segmented-control.tsx": [
|
|
76
|
+
"segmentedControlDescriptionVariants",
|
|
77
|
+
"segmentedControlFocusRingVariants",
|
|
78
|
+
"segmentedControlIndicatorVariants",
|
|
79
|
+
"segmentedControlItemVariants",
|
|
80
|
+
"segmentedControlLabelVariants",
|
|
81
|
+
"segmentedControlRailVariants",
|
|
82
|
+
"segmentedControlWrapperVariants"
|
|
83
|
+
],
|
|
84
|
+
"src/app/components/selectable-card.tsx": ["selectableCardVariants"],
|
|
85
|
+
"src/app/components/side-nav.tsx": [
|
|
86
|
+
"SideNav",
|
|
87
|
+
"SideNavLevel",
|
|
88
|
+
"SideNavProps",
|
|
89
|
+
"sideNavVariants"
|
|
90
|
+
],
|
|
91
|
+
"src/app/components/skeleton.tsx": ["skeletonVariants"],
|
|
92
|
+
"src/app/components/sketch.tsx": ["Sketch", "SketchProps"],
|
|
93
|
+
"src/app/components/spinner.tsx": ["spinnerVariants"],
|
|
94
|
+
"src/app/components/stacked-card-rail.tsx": [
|
|
95
|
+
"StackedCardRail",
|
|
96
|
+
"StackedCardRailCard",
|
|
97
|
+
"StackedCardRailProps"
|
|
98
|
+
],
|
|
99
|
+
"src/app/components/stat.tsx": ["statVariants"],
|
|
100
|
+
"src/app/components/status-dot.tsx": ["statusDotVariants"],
|
|
101
|
+
"src/app/components/status-list-item.tsx": ["statusListItemDotVariants"],
|
|
102
|
+
"src/app/components/stepper-field.tsx": ["StepperField", "StepperFieldProps"],
|
|
103
|
+
"src/app/components/stepper.tsx": ["Step", "stepMarkerVariants", "Stepper", "StepperProps"],
|
|
104
|
+
"src/app/components/surface.tsx": ["surfaceVariants"],
|
|
105
|
+
"src/app/components/table.tsx": [
|
|
106
|
+
"tableDataCellVariants",
|
|
107
|
+
"tableHeaderCellVariants",
|
|
108
|
+
"tableSortButtonVariants"
|
|
109
|
+
],
|
|
110
|
+
"src/app/components/tag.tsx": ["tagButtonVariants", "tagPillVariants"],
|
|
111
|
+
"src/app/components/text-lockup.tsx": ["TextLockup", "TextLockupProps"],
|
|
112
|
+
"src/app/components/text.tsx": ["textVariants"],
|
|
113
|
+
"src/app/components/textarea.tsx": ["textareaVariants"],
|
|
114
|
+
"src/app/components/time-input.tsx": ["hdsTimeInputVariants"],
|
|
115
|
+
"src/app/components/toast.tsx": ["toastIconVariants"],
|
|
116
|
+
"src/app/components/toggle-button.tsx": ["hdsToggleButtonVariants"],
|
|
117
|
+
"src/app/components/token.tsx": [
|
|
118
|
+
"Token",
|
|
119
|
+
"tokenLabelVariants",
|
|
120
|
+
"tokenNodeInlineVariants",
|
|
121
|
+
"TokenProps",
|
|
122
|
+
"tokenShellVariants"
|
|
123
|
+
],
|
|
124
|
+
"src/app/components/tokenizer.tsx": ["Tokenizer", "TokenizerProps"],
|
|
125
|
+
"src/app/components/toolbar.tsx": ["Toolbar", "ToolbarComponent"],
|
|
126
|
+
"src/app/components/top-nav.tsx": ["TopNav", "TopNavProps"],
|
|
127
|
+
"src/app/components/tree-list.tsx": ["TreeList", "TreeListProps", "TreeNode"],
|
|
128
|
+
"src/app/layouts/CaseStudyLayout.tsx": ["CaseStudyLayout", "CaseStudyLayoutProps"],
|
|
129
|
+
"src/app/layouts/HdsDocsShell.tsx": ["HdsDocsShell", "HdsDocsShellProps"],
|
|
130
|
+
"src/app/layouts/HdsSystemDocLayout.tsx": ["HdsSystemDocLayout"]
|
|
131
|
+
},
|
|
132
|
+
"replaced": {
|
|
133
|
+
"src/app/components/aspect-ratio.tsx": {
|
|
134
|
+
"AspectRatio": "Box style={{ aspectRatio: '16 / 9' }}",
|
|
135
|
+
"AspectRatioProps": "BoxProps"
|
|
136
|
+
},
|
|
137
|
+
"src/app/components/bleed.tsx": {
|
|
138
|
+
"Bleed": "Box style={{ marginInline: 'calc(-1 * var(--semantic-space-scale-md))' }}",
|
|
139
|
+
"BleedProps": "BoxProps"
|
|
140
|
+
},
|
|
141
|
+
"src/app/components/center.tsx": {
|
|
142
|
+
"Center": "Container maxWidth=\"content\", with a Box style={{ paddingInline }} inside for the gutter",
|
|
143
|
+
"CenterProps": "ContainerProps"
|
|
144
|
+
},
|
|
145
|
+
"src/app/components/circular-progress.tsx": {
|
|
146
|
+
"CircularProgress": "Progress variant=\"circular\"",
|
|
147
|
+
"CircularProgressProps": "ProgressProps"
|
|
148
|
+
},
|
|
149
|
+
"src/app/components/cluster.tsx": {
|
|
150
|
+
"Cluster": "Stack direction=\"row\" wrap=\"wrap\" align=\"center\"",
|
|
151
|
+
"ClusterProps": "StackProps"
|
|
152
|
+
},
|
|
153
|
+
"src/app/components/cover.tsx": {
|
|
154
|
+
"Cover": "Box style={{ display: 'flex', flexDirection: 'column', minHeight: '100svh' }} around a Box style={{ marginBlock: 'auto' }}",
|
|
155
|
+
"CoverProps": "BoxProps"
|
|
156
|
+
},
|
|
157
|
+
"src/app/components/frame.tsx": {
|
|
158
|
+
"Frame": "Box style={{ aspectRatio: '16 / 9', overflow: 'hidden', borderRadius: hds.borderRadius.md }}",
|
|
159
|
+
"FrameProps": "BoxProps"
|
|
160
|
+
},
|
|
161
|
+
"src/app/components/icon-button.tsx": {
|
|
162
|
+
"IconButton": "Button iconOnly label=\"…\" iconLeft={<Icon icon={…} />}",
|
|
163
|
+
"IconButtonProps": "ButtonProps"
|
|
164
|
+
},
|
|
165
|
+
"src/app/components/input-group.tsx": {
|
|
166
|
+
"InputGroup": "Input prefix / suffix",
|
|
167
|
+
"InputGroupProps": "InputProps"
|
|
168
|
+
},
|
|
169
|
+
"src/app/components/multi-selector.tsx": {
|
|
170
|
+
"MultiSelector": "Combobox multiple",
|
|
171
|
+
"MultiSelectorOption": "ComboboxOption",
|
|
172
|
+
"MultiSelectorProps": "ComboboxMultipleProps"
|
|
173
|
+
},
|
|
174
|
+
"src/app/components/not-found-pattern.tsx": {
|
|
175
|
+
"NotFoundPattern": "ErrorPattern displayText=\"404\" message=\"Page not found\" from @hirobius/design-system/patterns (hds-not-found-pattern rewrites it)"
|
|
176
|
+
},
|
|
177
|
+
"src/app/components/selectable-card.tsx": {
|
|
178
|
+
"SelectableCard": "Card selectable selected onSelectedChange",
|
|
179
|
+
"SelectableCardProps": "CardProps"
|
|
180
|
+
},
|
|
181
|
+
"src/app/components/tile-grid.tsx": {
|
|
182
|
+
"TileGrid": "Grid layout=\"auto-fill\" minItemWidth=\"…\" gap=\"medium\" (hds-tile-grid rewrites it)",
|
|
183
|
+
"TileGridProps": "GridProps (minTileWidth is minItemWidth, gap 'sm' is 'medium')"
|
|
184
|
+
},
|
|
185
|
+
"src/app/components/time-input.tsx": {
|
|
186
|
+
"TimeInput": "Input type=\"time\"",
|
|
187
|
+
"TimeInputProps": "InputProps"
|
|
188
|
+
},
|
|
189
|
+
"src/app/components/toggle-button.tsx": {
|
|
190
|
+
"ToggleButton": "Button pressed onPressedChange (variant=\"ghost\" is variant=\"tertiary\")",
|
|
191
|
+
"ToggleButtonProps": "ButtonProps"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* tile-grid codemod (hds#395, hds#389's 2026-10-01 decision update)
|
|
4
|
+
*
|
|
5
|
+
* 0.20.0 removed TileGrid from the package root. Grid renders its recipe since
|
|
6
|
+
* hds#393: `layout="auto-fill"` with `minItemWidth`, and the fixed 12px
|
|
7
|
+
* `medium` gap step. TileGrid defaulted to 260px tiles on a 12px gap; Grid
|
|
8
|
+
* defaults to 280px and 32px, so the rewrite always writes both:
|
|
9
|
+
*
|
|
10
|
+
* <TileGrid minTileWidth="280px"> <Grid layout="auto-fill" minItemWidth="280px" gap="medium">
|
|
11
|
+
* <TileGrid minTileWidth="260px" gap="sm"> <Grid layout="auto-fill" minItemWidth="260px" gap="medium">
|
|
12
|
+
* <TileGrid> <Grid layout="auto-fill" minItemWidth="260px" gap="medium">
|
|
13
|
+
*
|
|
14
|
+
* The rendered tracks and gap are the same under every tenant and density
|
|
15
|
+
* (scripts/__tests__/grid-tile-grid-parity.test.mjs). An expression width keeps
|
|
16
|
+
* TileGrid's fallback where it can be undefined: `minTileWidth={w}` becomes
|
|
17
|
+
* `minItemWidth={w ?? '260px'}`. One that never is (a string or template
|
|
18
|
+
* literal, a conditional whose branches all are) is written through as it is,
|
|
19
|
+
* since TypeScript rejects `??` on it (TS2869). The codemod reads that the way
|
|
20
|
+
* TypeScript does, from the syntax.
|
|
21
|
+
* Every other attribute, comment and line break stays, and the import names Grid
|
|
22
|
+
* (an alias stays the file's name; a file that binds Grid to something else keeps
|
|
23
|
+
* `Grid as TileGrid`; a file that already imports Grid reuses it).
|
|
24
|
+
*
|
|
25
|
+
* Left for a manual edit (codemods/jsx-fold.mjs): `gap="xs"` and `gap="md"`,
|
|
26
|
+
* which were fixed 8px and 16px where Grid's nearest steps (`tight`, `normal`)
|
|
27
|
+
* tighten under compact density; a gap or a spread the codemod cannot read; a
|
|
28
|
+
* width that is always undefined, or one it does not read (an operator other
|
|
29
|
+
* than a conditional or a logical one, a cast, an arrow); a self-closing tag
|
|
30
|
+
* (Grid needs children); any use that is not a JSX tag; and `TileGridProps`.
|
|
31
|
+
* Running it twice changes nothing.
|
|
32
|
+
*
|
|
33
|
+
* node codemods/tile-grid.mjs [--root <dir>] [--check] [--dry-run]
|
|
34
|
+
*
|
|
35
|
+
* --root <dir> directory to scan (default: current directory)
|
|
36
|
+
* --check write nothing; exit 1 while a rewrite is needed or a site needs
|
|
37
|
+
* a manual edit (a namespace or dynamic import that reads the
|
|
38
|
+
* name, or a file that cannot be read to its end, included)
|
|
39
|
+
* --dry-run write nothing; print each changed line before (-) and after (+), exit 0
|
|
40
|
+
*/
|
|
41
|
+
import { ROOT_PKG, foldComponent, insertionPoint, isEntry, main, runFold } from './jsx-fold.mjs';
|
|
42
|
+
import { maskSource } from './unrewritable.mjs';
|
|
43
|
+
|
|
44
|
+
/** TileGrid's default width, which Grid does not share. */
|
|
45
|
+
const TILE_WIDTH = '260px';
|
|
46
|
+
|
|
47
|
+
/** TileGrid's fixed gaps Grid has no fixed step for, and Grid's nearest density-aware one. */
|
|
48
|
+
const NO_STEP = {
|
|
49
|
+
xs: '8px, and gap="tight" is 8px but 6px under compact density',
|
|
50
|
+
md: '16px, and gap="normal" is 16px but 12px under compact density',
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// TypeScript's syntactic nullishness of an expression (checker.ts
|
|
54
|
+
// getSyntacticNullishnessSemantics), which decides TS2869 and TS2871 on `x ?? y`.
|
|
55
|
+
const ALWAYS = 1;
|
|
56
|
+
const NEVER = 2;
|
|
57
|
+
const SOMETIMES = ALWAYS | NEVER;
|
|
58
|
+
|
|
59
|
+
const OPEN = '([{';
|
|
60
|
+
const CLOSE = ')]}';
|
|
61
|
+
|
|
62
|
+
/** The bracket that closes the one at `at` in masked code, or -1. */
|
|
63
|
+
function closing(m, at) {
|
|
64
|
+
let depth = 0;
|
|
65
|
+
for (let i = at; i < m.length; i++) {
|
|
66
|
+
if (OPEN.includes(m[i])) depth++;
|
|
67
|
+
else if (CLOSE.includes(m[i]) && --depth === 0) return i;
|
|
68
|
+
}
|
|
69
|
+
return -1;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The backtick that closes the template whose opening backtick is at `at`, or -1. */
|
|
73
|
+
function templateEnd(m, at) {
|
|
74
|
+
let depth = 0;
|
|
75
|
+
for (let i = at + 1; i < m.length; i++) {
|
|
76
|
+
if (m[i] === '{') depth++;
|
|
77
|
+
else if (m[i] === '}') depth--;
|
|
78
|
+
else if (m[i] === '`' && depth === 0) return i;
|
|
79
|
+
}
|
|
80
|
+
return -1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The operators of masked code outside brackets that decide its shape: `,`,
|
|
85
|
+
* `=>`, an assignment, the conditional's `?` and `:`, `??`, `||`, `&&`, and the
|
|
86
|
+
* `as` and `satisfies` keywords.
|
|
87
|
+
*/
|
|
88
|
+
function topLevel(m) {
|
|
89
|
+
const ops = [];
|
|
90
|
+
let depth = 0;
|
|
91
|
+
for (let i = 0; i < m.length; i++) {
|
|
92
|
+
const ch = m[i];
|
|
93
|
+
if (OPEN.includes(ch)) depth++;
|
|
94
|
+
else if (CLOSE.includes(ch)) depth--;
|
|
95
|
+
else if (depth > 0) continue;
|
|
96
|
+
else if (ch === ',') ops.push({ at: i, op: ',' });
|
|
97
|
+
else if (ch === '=') {
|
|
98
|
+
if (m[i + 1] === '>') ops.push({ at: i++, op: '=>' });
|
|
99
|
+
else if (m[i + 1] === '=') while (m[i + 1] === '=') i++;
|
|
100
|
+
else if (
|
|
101
|
+
m[i - 1] === '!' ||
|
|
102
|
+
((m[i - 1] === '<' || m[i - 1] === '>') && m[i - 2] !== m[i - 1])
|
|
103
|
+
)
|
|
104
|
+
continue;
|
|
105
|
+
else ops.push({ at: i, op: '=' });
|
|
106
|
+
} else if (ch === '?') {
|
|
107
|
+
if (m[i + 1] === '?') {
|
|
108
|
+
ops.push({ at: i, op: m[i + 2] === '=' ? '=' : '??' });
|
|
109
|
+
i += m[i + 2] === '=' ? 2 : 1;
|
|
110
|
+
} else if (m[i + 1] === '.' && !/\d/.test(m[i + 2] ?? '')) i++;
|
|
111
|
+
else ops.push({ at: i, op: '?' });
|
|
112
|
+
} else if (ch === ':') ops.push({ at: i, op: ':' });
|
|
113
|
+
else if ((ch === '|' || ch === '&') && m[i + 1] === ch) {
|
|
114
|
+
ops.push({ at: i, op: m[i + 2] === '=' ? '=' : '||' });
|
|
115
|
+
i += m[i + 2] === '=' ? 2 : 1;
|
|
116
|
+
} else if (/[\w$]/.test(ch) && !/[\w$.]/.test(m[i - 1] ?? '')) {
|
|
117
|
+
const word = /^[\w$]+/.exec(m.slice(i))[0];
|
|
118
|
+
if (word === 'as' || word === 'satisfies') ops.push({ at: i, op: 'as' });
|
|
119
|
+
i += word.length - 1;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return ops;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* TypeScript's nullishness of a name, a member access, a call, an element
|
|
127
|
+
* access or a tagged template (each optional), with any `!`; null for anything
|
|
128
|
+
* else. `chain`: it binds tighter than `??`, so it takes no parentheses.
|
|
129
|
+
*/
|
|
130
|
+
function chainNullishness(m) {
|
|
131
|
+
const id = /^[A-Za-z_$][\w$]*/.exec(m);
|
|
132
|
+
const group = m[0] === '(' ? closing(m, 0) : -1;
|
|
133
|
+
if (!id && group < 0) return null;
|
|
134
|
+
const base = id ? id[0] : m.slice(1, group);
|
|
135
|
+
let i = id ? base.length : group + 1;
|
|
136
|
+
let access = false;
|
|
137
|
+
while (i < m.length) {
|
|
138
|
+
const [step, optional] = /^\s*(\?\.)?\s*/.exec(m.slice(i));
|
|
139
|
+
const at = i + step.length;
|
|
140
|
+
const rest = m.slice(at);
|
|
141
|
+
const prop = (optional ? /^[A-Za-z_$][\w$]*/ : /^\.\s*[A-Za-z_$][\w$]*/).exec(rest);
|
|
142
|
+
if (prop) i = at + prop[0].length;
|
|
143
|
+
else if (rest[0] === '[' || rest[0] === '(') i = closing(m, at) + 1;
|
|
144
|
+
else if (rest[0] === '`' && !optional) i = templateEnd(m, at) + 1;
|
|
145
|
+
else if (rest[0] === '!' && rest[1] !== '=' && !optional) {
|
|
146
|
+
i = at + 1;
|
|
147
|
+
continue;
|
|
148
|
+
} else return null;
|
|
149
|
+
if (i === 0) return null; // closing() or templateEnd() found no end
|
|
150
|
+
access = true;
|
|
151
|
+
}
|
|
152
|
+
if (access) return { nullish: SOMETIMES, chain: true };
|
|
153
|
+
if (!id) {
|
|
154
|
+
const inner = nullishness(base);
|
|
155
|
+
return inner && { nullish: inner.nullish, chain: true };
|
|
156
|
+
}
|
|
157
|
+
if (base === 'undefined' || base === 'null') return { nullish: ALWAYS, chain: true };
|
|
158
|
+
if (base === 'true' || base === 'false') return { nullish: NEVER, chain: true };
|
|
159
|
+
if (/^(?:typeof|void|delete|await|new|yield|function|class)$/.test(base)) return null;
|
|
160
|
+
return { nullish: SOMETIMES, chain: true };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Whether an expression (masked code: strings, templates and comments blanked)
|
|
165
|
+
* can be null or undefined, the way TypeScript reads it from the syntax:
|
|
166
|
+
* ALWAYS, NEVER or SOMETIMES, or null where the codemod does not read it (an
|
|
167
|
+
* assignment, an arrow, a comma, a cast, an operator other than a conditional
|
|
168
|
+
* or a logical one). `chain`: it needs no parentheses in front of `??`.
|
|
169
|
+
* @param {string} code
|
|
170
|
+
* @returns {{ nullish: number, chain: boolean } | null}
|
|
171
|
+
*/
|
|
172
|
+
function nullishness(code) {
|
|
173
|
+
let m = code.trim();
|
|
174
|
+
while (m[0] === '(' && closing(m, 0) === m.length - 1) m = m.slice(1, -1).trim();
|
|
175
|
+
if (m === '') return null;
|
|
176
|
+
const ops = topLevel(m);
|
|
177
|
+
if (ops.some((o) => o.op === ',' || o.op === '=>' || o.op === '=')) return null;
|
|
178
|
+
const q = ops.findIndex((o) => o.op === '?');
|
|
179
|
+
if (q >= 0) {
|
|
180
|
+
// The conditional's own `:`, past any nested conditional in its true branch.
|
|
181
|
+
let colon = -1;
|
|
182
|
+
for (let k = q + 1, open = 0; k < ops.length && colon < 0; k++)
|
|
183
|
+
if (ops[k].op === '?') open++;
|
|
184
|
+
else if (ops[k].op === ':' && open-- === 0) colon = ops[k].at;
|
|
185
|
+
if (colon < 0 || ops[q].at === 0) return null;
|
|
186
|
+
const yes = nullishness(m.slice(ops[q].at + 1, colon));
|
|
187
|
+
const no = nullishness(m.slice(colon + 1));
|
|
188
|
+
return yes && no && { nullish: yes.nullish | no.nullish, chain: false };
|
|
189
|
+
}
|
|
190
|
+
if (ops.some((o) => o.op === 'as' || o.op === ':')) return null;
|
|
191
|
+
const coalesce = ops.filter((o) => o.op === '??').pop();
|
|
192
|
+
if (coalesce) {
|
|
193
|
+
const right = nullishness(m.slice(coalesce.at + 2));
|
|
194
|
+
return right && { nullish: right.nullish, chain: false };
|
|
195
|
+
}
|
|
196
|
+
if (ops.some((o) => o.op === '||')) return { nullish: SOMETIMES, chain: false };
|
|
197
|
+
if (/^(['"])\s*\1$/.test(m)) return { nullish: NEVER, chain: true };
|
|
198
|
+
if (m[0] === '`' && templateEnd(m, 0) === m.length - 1) return { nullish: NEVER, chain: true };
|
|
199
|
+
return chainNullishness(m);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** @type {import('./jsx-fold.mjs').FoldRule} */
|
|
203
|
+
export const RULE = Object.freeze({
|
|
204
|
+
name: 'TileGrid',
|
|
205
|
+
survivor: 'Grid',
|
|
206
|
+
survivorFrom: ROOT_PKG,
|
|
207
|
+
types: { TileGridProps: "GridProps: minTileWidth is minItemWidth, gap 'sm' is 'medium'" },
|
|
208
|
+
rewrite(tag, source) {
|
|
209
|
+
if (tag.selfClosing && !tag.attrs.some((a) => a.name === 'children'))
|
|
210
|
+
return { manual: 'Grid needs children; rewrite it by hand' };
|
|
211
|
+
if (tag.attrs.some((a) => a.kind === 'spread'))
|
|
212
|
+
return { manual: 'a spread can carry minTileWidth or gap; rewrite it by hand' };
|
|
213
|
+
const gap = tag.attrs.find((a) => a.name === 'gap');
|
|
214
|
+
if (gap && !(gap.kind === 'string' && gap.value === 'sm')) {
|
|
215
|
+
const text = source.slice(gap.start, gap.end);
|
|
216
|
+
const why = gap.kind === 'string' && NO_STEP[gap.value];
|
|
217
|
+
return {
|
|
218
|
+
manual: why
|
|
219
|
+
? `${text}: TileGrid's gap was a fixed ${why}; rewrite it by hand`
|
|
220
|
+
: `${text}: the codemod cannot read the gap; rewrite it by hand`,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const width = tag.attrs.find((a) => a.name === 'minTileWidth');
|
|
224
|
+
if (width && width.kind === 'bare')
|
|
225
|
+
return { manual: 'minTileWidth has no value; rewrite it by hand' };
|
|
226
|
+
const edits = [];
|
|
227
|
+
if (gap) edits.push({ start: gap.valueStart + 1, end: gap.valueEnd - 1, text: 'medium' });
|
|
228
|
+
if (width) {
|
|
229
|
+
const { sep } = insertionPoint(source, tag, width);
|
|
230
|
+
edits.push(
|
|
231
|
+
{ start: width.start, end: width.start, text: `layout="auto-fill"${sep}` },
|
|
232
|
+
{ start: width.start, end: width.start + 'minTileWidth'.length, text: 'minItemWidth' },
|
|
233
|
+
);
|
|
234
|
+
if (width.kind === 'expr') {
|
|
235
|
+
const { code, unterminated } = maskSource(width.value);
|
|
236
|
+
const kind = unterminated ? null : nullishness(code);
|
|
237
|
+
const text = source.slice(width.start, width.end);
|
|
238
|
+
if (!kind)
|
|
239
|
+
return {
|
|
240
|
+
manual: `the codemod cannot tell whether ${text} can be undefined, where TileGrid drew ${TILE_WIDTH} tiles and Grid draws 280px; rewrite it by hand`,
|
|
241
|
+
};
|
|
242
|
+
if (kind.nullish === ALWAYS)
|
|
243
|
+
return {
|
|
244
|
+
manual: `${text} is always null or undefined, which drew TileGrid's ${TILE_WIDTH} tiles; write minItemWidth="${TILE_WIDTH}" by hand`,
|
|
245
|
+
};
|
|
246
|
+
if (kind.nullish === SOMETIMES) {
|
|
247
|
+
// In masked code a comment is blank, so the expression's own text starts
|
|
248
|
+
// and ends at its first and last non-blank character.
|
|
249
|
+
const first = width.valueStart + 1 + code.search(/\S/);
|
|
250
|
+
const last = width.valueStart + 1 + code.trimEnd().length;
|
|
251
|
+
const fallback = ` ?? '${TILE_WIDTH}'`;
|
|
252
|
+
if (kind.chain) edits.push({ start: last, end: last, text: fallback });
|
|
253
|
+
else
|
|
254
|
+
edits.push(
|
|
255
|
+
{ start: first, end: first, text: '(' },
|
|
256
|
+
{ start: last, end: last, text: `)${fallback}` },
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (!gap) edits.push({ start: width.end, end: width.end, text: `${sep}gap="medium"` });
|
|
261
|
+
} else {
|
|
262
|
+
const { at, sep } = insertionPoint(source, tag, null);
|
|
263
|
+
const attrs = ['layout="auto-fill"', `minItemWidth="${TILE_WIDTH}"`];
|
|
264
|
+
if (!gap) attrs.push('gap="medium"');
|
|
265
|
+
edits.push({ start: at, end: at, text: attrs.map((a) => `${sep}${a}`).join('') });
|
|
266
|
+
}
|
|
267
|
+
return { edits };
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
/** Pure transform of one file's source (codemods/jsx-fold.mjs `foldComponent`). */
|
|
272
|
+
export function transformSource(source) {
|
|
273
|
+
return foldComponent(source, RULE);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Scan a directory. Writes only when `write` is true. */
|
|
277
|
+
export function runCodemod({ root, write = false }) {
|
|
278
|
+
return runFold({ root, write, rule: RULE });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (isEntry(import.meta.url)) {
|
|
282
|
+
process.exit(main(process.argv.slice(2), { bin: 'hds-tile-grid', rule: RULE }));
|
|
283
|
+
}
|