@hirobius/design-system 0.19.0 → 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/chunks/{form-BvNNnJ0t.js → form-COP3vK9_.js} +29 -27
- package/dist/contexts.js +157 -78
- package/dist/form.js +1 -1
- package/dist/hirobius-ui.js +1826 -5503
- package/dist/manifest.js +12 -13
- package/dist/patterns.js +820 -561
- 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 +18 -22
- package/public/hds-manifest.json +2400 -6488
- 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-CH_bwzCq.js +0 -83
- package/dist/chunks/TokenDisplayContext-evZi_214.js +0 -18
- package/dist/chunks/activity-feed-DIMeUJpW.js +0 -21950
- 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
|
@@ -3,25 +3,48 @@
|
|
|
3
3
|
* patterns-subpath codemod (hds#316, follows hds#254)
|
|
4
4
|
*
|
|
5
5
|
* Rewrites `import { Page } from '@hirobius/design-system'` to
|
|
6
|
-
* `import { Page } from '@hirobius/design-system/patterns'` for every
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* `import { Page } from '@hirobius/design-system/patterns'` for every name the
|
|
7
|
+
* root stopped exporting in 0.20.0 (hds#389 R1): the 21 pattern components and
|
|
8
|
+
* their props types, parts, hooks and `*Variants`. The list also holds the five
|
|
9
|
+
* modules that were only ever on `/patterns` (PageHeader, MetricTiles,
|
|
10
|
+
* FormActions, DestructiveSection, DataTableSection); no root import of those
|
|
11
|
+
* ever resolved, so including them changes nothing. StatusTile (with
|
|
12
|
+
* StatusTileProps and StatusTileTone) left the root for `/patterns` in the same
|
|
13
|
+
* release (hds#389 D5, hds#395), so it moves the same way. Names removed outright in
|
|
14
|
+
* 0.20.0 are in removed-0.20.json and are reported, not moved. Other named imports
|
|
15
|
+
* stay on the root. Aliases, `type` modifiers and multi-line layout
|
|
9
16
|
* are preserved; an existing `/patterns` import of the same kind is extended
|
|
10
|
-
* instead of duplicated.
|
|
17
|
+
* instead of duplicated. Imports are read with comments, strings and templates
|
|
18
|
+
* masked (codemods/unrewritable.mjs), so a statement after `;` or a comment on
|
|
19
|
+
* its line is found, and a comment in the braces moves with the specifier it
|
|
20
|
+
* sits next to (hds#434).
|
|
11
21
|
*
|
|
12
22
|
* node codemods/patterns-subpath.mjs [--root <dir>] [--check] [--dry-run]
|
|
13
23
|
*
|
|
14
24
|
* --root <dir> directory to scan (default: current directory)
|
|
15
|
-
* --check write nothing; exit 1 when a rewrite is needed or a root
|
|
16
|
-
*
|
|
25
|
+
* --check write nothing; exit 1 when a rewrite is needed, or when a root
|
|
26
|
+
* star re-export, or a namespace import or dynamic `import()`,
|
|
27
|
+
* `require()` or `vi.mock`/`jest.mock` of the root that reads a
|
|
28
|
+
* pattern name off the module, or a file that cannot be read to
|
|
29
|
+
* its end (codemods/unrewritable.mjs), needs a manual look
|
|
17
30
|
* --dry-run write nothing; print each import line before (-) and after (+), exit 0
|
|
18
31
|
*
|
|
19
|
-
* The name list is codemods/patterns-subpath.names.json
|
|
20
|
-
*
|
|
32
|
+
* The name list is codemods/patterns-subpath.names.json: what
|
|
33
|
+
* `@hirobius/design-system/patterns` exports and the root does not, generated
|
|
34
|
+
* by `pnpm codemod:names` (scripts/build-codemod-pattern-names.mjs).
|
|
35
|
+
*
|
|
36
|
+
* A name 0.20.0 removed with no replacement (codemods/removed-0.20.json, hds#394)
|
|
37
|
+
* has nowhere to move: an import of one from the root or `/patterns` is reported
|
|
38
|
+
* for a manual edit ("removed in 0.20.0, no replacement") and `--check` exits 1.
|
|
39
|
+
* A removed name that folded into a survivor (`replaced` in that file, hds#394
|
|
40
|
+
* wave 4b) is reported the same way, naming the survivor instead
|
|
41
|
+
* ("removed in 0.20.0, use Button iconOnly"). NotFoundPattern and TileGrid
|
|
42
|
+
* (hds#395) have codemods of their own: hds-not-found-pattern and hds-tile-grid.
|
|
21
43
|
*/
|
|
22
44
|
import { readFileSync, readdirSync, realpathSync, statSync, writeFileSync } from 'node:fs';
|
|
23
45
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
24
46
|
import { fileURLToPath } from 'node:url';
|
|
47
|
+
import { findUnrewritable as findHidden, maskSource } from './unrewritable.mjs';
|
|
25
48
|
|
|
26
49
|
const ROOT_PKG = '@hirobius/design-system';
|
|
27
50
|
const SUBPATH = `${ROOT_PKG}/patterns`;
|
|
@@ -43,44 +66,175 @@ export function loadPatternNames() {
|
|
|
43
66
|
return new Set(names);
|
|
44
67
|
}
|
|
45
68
|
|
|
46
|
-
|
|
47
|
-
|
|
69
|
+
/** Names 0.20.0 removed (codemods/removed-0.20.json `modules` and `replaced`): reported, never moved. */
|
|
70
|
+
export function loadRemovedNames() {
|
|
71
|
+
const { modules, replaced = {} } = JSON.parse(
|
|
72
|
+
readFileSync(join(HERE, 'removed-0.20.json'), 'utf8'),
|
|
73
|
+
);
|
|
74
|
+
return new Set([
|
|
75
|
+
...Object.values(modules).flat(),
|
|
76
|
+
...Object.values(replaced).flatMap(Object.keys),
|
|
77
|
+
]);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** What replaces a removed name that has a survivor (removed-0.20.json `replaced`), by name. */
|
|
81
|
+
export function loadReplacements() {
|
|
82
|
+
const { replaced = {} } = JSON.parse(readFileSync(join(HERE, 'removed-0.20.json'), 'utf8'));
|
|
83
|
+
return new Map(Object.values(replaced).flatMap((names) => Object.entries(names)));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const ID = '[A-Za-z_$][\\w$]*';
|
|
87
|
+
const BOM = '\uFEFF';
|
|
88
|
+
// Matched on the masked source (codemods/unrewritable.mjs), where comments are
|
|
89
|
+
// blank and the specifier is a run of spaces between its quotes. At a line start
|
|
90
|
+
// or after `;`: indent, import|export, type?, default?, { body }, quote, semicolon.
|
|
48
91
|
const namedRe = (pkg) =>
|
|
49
92
|
new RegExp(
|
|
50
|
-
|
|
93
|
+
`(?:^|(?<=;))([ \\t]*)(import|export)(\\s+type)?\\s*(?:(${ID})\\s*,\\s*)?\\{([^}]*)\\}\\s*from\\s*(['"]) {${pkg.length}}\\6(;?)`,
|
|
51
94
|
'gm',
|
|
52
95
|
);
|
|
53
|
-
// `import * as X`, `import D, * as X`, `export * from`, `export * as X from` on the root
|
|
54
|
-
const namespaceRe = new RegExp(
|
|
55
|
-
`^[ \\t]*(?:import\\s+(?:[\\w$]+\\s*,\\s*)?\\*\\s*as\\s+[\\w$]+|export\\s+\\*(?:\\s*as\\s+[\\w$]+)?)\\s*from\\s*['"]${esc(ROOT_PKG)}['"]`,
|
|
56
|
-
'gm',
|
|
57
|
-
);
|
|
58
|
-
|
|
59
96
|
const specName = (spec) =>
|
|
60
97
|
spec
|
|
61
98
|
.replace(/^type\s+/, '')
|
|
62
99
|
.split(/\s+as\s+/)[0]
|
|
63
100
|
.trim();
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The specifiers of one `{ … }` body. The masked body (comments blank) says where
|
|
104
|
+
* each specifier and comma is; the text, comments included, comes from the source.
|
|
105
|
+
* A comment rides with the specifier next to it: before it (`lead`), or after it
|
|
106
|
+
* up to the end of the comma's line (`tail`). Comments on their own lines after
|
|
107
|
+
* the last specifier are `endComment`.
|
|
108
|
+
*/
|
|
109
|
+
function parseSpecs(source, code, from, to) {
|
|
110
|
+
const specs = [];
|
|
111
|
+
let endComment = '';
|
|
112
|
+
let segStart = from;
|
|
113
|
+
for (let k = from; k <= to; k++) {
|
|
114
|
+
if (k < to && code[k] !== ',') continue;
|
|
115
|
+
const seg = code.slice(segStart, k);
|
|
116
|
+
const a = seg.search(/\S/);
|
|
117
|
+
const prev = specs[specs.length - 1];
|
|
118
|
+
let leadFrom = segStart;
|
|
119
|
+
// The rest of the previous comma's line belongs to the specifier before it.
|
|
120
|
+
const nl = seg.indexOf('\n');
|
|
121
|
+
if (prev && nl >= 0 && (a < 0 || nl < a)) {
|
|
122
|
+
const sameLine = source.slice(segStart, segStart + nl).trim();
|
|
123
|
+
if (sameLine) prev.tail = prev.tail ? `${prev.tail} ${sameLine}` : sameLine;
|
|
124
|
+
leadFrom = segStart + nl;
|
|
125
|
+
}
|
|
126
|
+
if (a < 0) endComment = source.slice(leadFrom, k).trim();
|
|
127
|
+
else {
|
|
128
|
+
const specStart = segStart + a;
|
|
129
|
+
const specEnd = segStart + seg.trimEnd().length;
|
|
130
|
+
const masked = code.slice(specStart, specEnd);
|
|
131
|
+
specs.push({
|
|
132
|
+
name: specName(masked),
|
|
133
|
+
key: masked.replace(/\s+/g, ' '),
|
|
134
|
+
lead: source.slice(leadFrom, specStart).trim(),
|
|
135
|
+
text: source.slice(specStart, specEnd),
|
|
136
|
+
tail: source.slice(specEnd, k).trim(),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
segStart = k + 1;
|
|
140
|
+
}
|
|
141
|
+
return { specs, endComment };
|
|
73
142
|
}
|
|
74
143
|
|
|
75
|
-
/**
|
|
76
|
-
function
|
|
77
|
-
const
|
|
144
|
+
/** Each named import or re-export of `pkg` in the masked `code`, in source order. */
|
|
145
|
+
function* statements(source, code, pkg) {
|
|
146
|
+
for (const m of code.matchAll(namedRe(pkg))) {
|
|
147
|
+
const to = m.index + m[0].length;
|
|
148
|
+
if (!source.startsWith(pkg, to - m[7].length - pkg.length - 1)) continue;
|
|
149
|
+
const bodyAt = m.index + m[0].indexOf('{') + 1;
|
|
150
|
+
yield {
|
|
151
|
+
from: m.index + m[1].length,
|
|
152
|
+
to,
|
|
153
|
+
kw: m[2],
|
|
154
|
+
typeKw: !!m[3],
|
|
155
|
+
def: m[4],
|
|
156
|
+
quote: m[6],
|
|
157
|
+
semi: m[7],
|
|
158
|
+
multiline: m[5].includes('\n'),
|
|
159
|
+
...parseSpecs(source, code, bodyAt, bodyAt + m[5].length),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The leading whitespace of the line `at` is on. */
|
|
165
|
+
function indentOf(source, at) {
|
|
166
|
+
const lineStart = source.lastIndexOf('\n', at - 1) + 1;
|
|
167
|
+
return /^[ \t]*/.exec(source.slice(lineStart, at).replace(BOM, ''))[0];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* What to delete for a statement that folds away entirely: its whole line when
|
|
172
|
+
* nothing else is on it, so no blank line or stray indent is left; otherwise the
|
|
173
|
+
* statement and the spaces that set it apart.
|
|
174
|
+
*/
|
|
175
|
+
function removal(source, from, to) {
|
|
176
|
+
const lineStart = source.lastIndexOf('\n', from - 1) + 1;
|
|
177
|
+
const bol = lineStart === 0 && source.startsWith(BOM) ? 1 : lineStart;
|
|
178
|
+
let end = to;
|
|
179
|
+
while (source[end] === ' ' || source[end] === '\t') end++;
|
|
180
|
+
if (/^[ \t]*$/.test(source.slice(bol, from))) {
|
|
181
|
+
const eol = source.startsWith('\r\n', end) ? 2 : source[end] === '\n' ? 1 : 0;
|
|
182
|
+
return eol > 0 || end === source.length ? [bol, end + eol] : [from, end];
|
|
183
|
+
}
|
|
184
|
+
let start = from;
|
|
185
|
+
while (start > bol && (source[start - 1] === ' ' || source[start - 1] === '\t')) start--;
|
|
186
|
+
return [start, to];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* What the codemod cannot rewrite (codemods/unrewritable.mjs): a star re-export
|
|
191
|
+
* of the root, whose importers are in other files; and a namespace import or a
|
|
192
|
+
* dynamic `import()`, `require()`, `vi.mock` or `jest.mock` of the root in a file
|
|
193
|
+
* that reads a pattern name off something (`HDS.Page`, `m['Page']`,
|
|
194
|
+
* `const { Page } = …`). A bare `Page`, such as the `/patterns` import the
|
|
195
|
+
* codemod writes or a local type, is not a read.
|
|
196
|
+
*/
|
|
197
|
+
export function findUnrewritable(source, names = loadPatternNames()) {
|
|
198
|
+
return findHidden(source, ROOT_PKG, names);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Each named import or re-export of a removed name from the root or `/patterns`, in source order. */
|
|
202
|
+
export function findRemoved(source, removed = loadRemovedNames(), use = loadReplacements()) {
|
|
203
|
+
const { code } = maskSource(source);
|
|
204
|
+
const found = [];
|
|
205
|
+
for (const pkg of [ROOT_PKG, SUBPATH])
|
|
206
|
+
for (const st of statements(source, code, pkg))
|
|
207
|
+
for (const { name } of st.specs)
|
|
208
|
+
if (removed.has(name))
|
|
209
|
+
found.push({
|
|
210
|
+
at: st.from,
|
|
211
|
+
text: `${name} from '${pkg}' (removed in 0.20.0, ${use.has(name) ? `use ${use.get(name)}` : 'no replacement'})`,
|
|
212
|
+
});
|
|
213
|
+
return found.sort((a, b) => a.at - b.at).map((f) => f.text);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Renders one import/export statement; each line after the first starts with
|
|
218
|
+
* `indent`. A list goes on one line when its source did and no comment in it
|
|
219
|
+
* needs a line break.
|
|
220
|
+
*/
|
|
221
|
+
function render({ kw, typeKw, def, quote, semi }, { specs, endComment, multiline, pkg, indent }) {
|
|
222
|
+
const head = `${kw}${typeKw ? ' type' : ''} `;
|
|
78
223
|
const tail = ` from ${quote}${pkg}${quote}${semi}`;
|
|
79
|
-
if (
|
|
224
|
+
if (specs.length === 0) return `${head}${def}${tail}`;
|
|
80
225
|
const d = def ? `${def}, ` : '';
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
226
|
+
const parts = [endComment, ...specs.flatMap((s) => [s.lead, s.text, s.tail])];
|
|
227
|
+
if (multiline || parts.some((part) => /\n|\/\//.test(part))) {
|
|
228
|
+
const pad = `${indent} `;
|
|
229
|
+
const lines = specs.flatMap((s) => [
|
|
230
|
+
...(s.lead ? [`${pad}${s.lead}`] : []),
|
|
231
|
+
`${pad}${s.text},${s.tail ? ` ${s.tail}` : ''}`,
|
|
232
|
+
]);
|
|
233
|
+
if (endComment) lines.push(`${pad}${endComment}`);
|
|
234
|
+
return `${head}${d}{\n${lines.join('\n')}\n${indent}}${tail}`;
|
|
235
|
+
}
|
|
236
|
+
const items = specs.map((s) => [s.lead, s.text, s.tail].filter(Boolean).join(' '));
|
|
237
|
+
return `${head}${d}{ ${items.join(', ')}${endComment ? ` ${endComment}` : ''} }${tail}`;
|
|
84
238
|
}
|
|
85
239
|
|
|
86
240
|
/**
|
|
@@ -89,75 +243,71 @@ function render({ indent, kw, typeKw, def, list, pkg, multiline, quote, semi })
|
|
|
89
243
|
* edits: {before: string, after: string}[] }}
|
|
90
244
|
*/
|
|
91
245
|
export function transformSource(source, names) {
|
|
246
|
+
const { code } = maskSource(source);
|
|
92
247
|
const moved = [];
|
|
93
248
|
const edits = [];
|
|
94
|
-
const isType = (m) => !!m[3];
|
|
95
|
-
const parse = (m) => ({
|
|
96
|
-
indent: m[1],
|
|
97
|
-
kw: m[2],
|
|
98
|
-
typeKw: m[3],
|
|
99
|
-
def: m[4],
|
|
100
|
-
body: m[5],
|
|
101
|
-
quote: m[6],
|
|
102
|
-
semi: m[7],
|
|
103
|
-
});
|
|
104
249
|
|
|
105
250
|
// An existing `import { … } from '/patterns'` of each kind absorbs moved names.
|
|
106
251
|
const targets = {};
|
|
107
|
-
for (const
|
|
108
|
-
if (
|
|
109
|
-
targets[
|
|
252
|
+
for (const st of statements(source, code, SUBPATH)) {
|
|
253
|
+
if (st.kw !== 'import' || st.def) continue;
|
|
254
|
+
targets[st.typeKw ? 'type' : 'value'] ??= { st, add: [], endComments: [] };
|
|
110
255
|
}
|
|
111
256
|
|
|
112
257
|
const replacements = [];
|
|
113
|
-
for (const
|
|
114
|
-
const
|
|
115
|
-
const specs = splitSpecs(p.body);
|
|
116
|
-
const go = specs.filter((s) => names.has(specName(s)));
|
|
258
|
+
for (const st of statements(source, code, ROOT_PKG)) {
|
|
259
|
+
const go = st.specs.filter((s) => names.has(s.name));
|
|
117
260
|
if (go.length === 0) continue;
|
|
118
|
-
moved.push(...go.map(
|
|
119
|
-
const keep = specs.filter((s) => !go.includes(s));
|
|
120
|
-
const
|
|
261
|
+
moved.push(...go.map((s) => s.name));
|
|
262
|
+
const keep = st.specs.filter((s) => !go.includes(s));
|
|
263
|
+
const indent = indentOf(source, st.from);
|
|
264
|
+
const target = st.kw === 'import' && targets[st.typeKw ? 'type' : 'value'];
|
|
265
|
+
// A comment before the closing brace stays with the root list while it has names.
|
|
266
|
+
const endComment = keep.length > 0 ? '' : st.endComment;
|
|
121
267
|
const lines = [];
|
|
122
|
-
if (keep.length > 0 ||
|
|
123
|
-
lines.push(render({ ...
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
268
|
+
if (keep.length > 0 || st.def)
|
|
269
|
+
lines.push(render(st, { ...st, specs: keep, pkg: ROOT_PKG, indent }));
|
|
270
|
+
if (target) {
|
|
271
|
+
target.add.push(...go);
|
|
272
|
+
if (endComment) target.endComments.push(endComment);
|
|
273
|
+
} else
|
|
274
|
+
lines.push(
|
|
275
|
+
render({ ...st, def: undefined }, { ...st, specs: go, endComment, pkg: SUBPATH, indent }),
|
|
276
|
+
);
|
|
277
|
+
replacements.push({ from: st.from, to: st.to, indent, lines });
|
|
128
278
|
}
|
|
129
279
|
|
|
130
280
|
if (replacements.length === 0) return { source, changed: false, sites: 0, moved: [], edits: [] };
|
|
131
281
|
|
|
132
282
|
const sites = replacements.length;
|
|
133
|
-
for (const {
|
|
283
|
+
for (const { st, add, endComments } of Object.values(targets)) {
|
|
134
284
|
if (add.length === 0) continue;
|
|
135
|
-
const
|
|
136
|
-
const
|
|
137
|
-
const
|
|
285
|
+
const specs = [...st.specs];
|
|
286
|
+
for (const s of add) if (!specs.some((h) => h.key === s.key)) specs.push(s);
|
|
287
|
+
const indent = indentOf(source, st.from);
|
|
288
|
+
const endComment = [st.endComment, ...endComments].filter(Boolean).join(`\n${indent} `);
|
|
138
289
|
replacements.push({
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
290
|
+
from: st.from,
|
|
291
|
+
to: st.to,
|
|
292
|
+
indent,
|
|
142
293
|
lines: [
|
|
143
|
-
render({
|
|
144
|
-
...p,
|
|
145
|
-
def: undefined,
|
|
146
|
-
list: merged,
|
|
147
|
-
pkg: SUBPATH,
|
|
148
|
-
multiline: p.body.includes('\n'),
|
|
149
|
-
}),
|
|
294
|
+
render({ ...st, def: undefined }, { ...st, specs, endComment, pkg: SUBPATH, indent }),
|
|
150
295
|
],
|
|
151
296
|
});
|
|
152
297
|
}
|
|
153
298
|
|
|
154
299
|
let out = source;
|
|
155
|
-
for (const r of replacements.sort((x, y) => y.
|
|
156
|
-
const after = r.lines.join(
|
|
300
|
+
for (const r of replacements.sort((x, y) => y.from - x.from)) {
|
|
301
|
+
const after = r.lines.join(`\n${r.indent}`);
|
|
157
302
|
// A statement folded entirely into an existing import leaves nothing behind, not a blank line.
|
|
158
|
-
const
|
|
159
|
-
out = out.slice(0,
|
|
160
|
-
|
|
303
|
+
const [from, to] = after === '' ? removal(source, r.from, r.to) : [r.from, r.to];
|
|
304
|
+
out = out.slice(0, from) + after + out.slice(to);
|
|
305
|
+
// --dry-run shows the whole line the statement starts on.
|
|
306
|
+
const prefix = source.slice(source.lastIndexOf('\n', r.from - 1) + 1, r.from).replace(BOM, '');
|
|
307
|
+
edits.unshift({
|
|
308
|
+
before: prefix + source.slice(r.from, r.to),
|
|
309
|
+
after: after === '' ? prefix.trimEnd() : prefix + after,
|
|
310
|
+
});
|
|
161
311
|
}
|
|
162
312
|
return { source: out, changed: out !== source, sites, moved, edits };
|
|
163
313
|
}
|
|
@@ -173,7 +323,13 @@ function* walk(dir) {
|
|
|
173
323
|
}
|
|
174
324
|
|
|
175
325
|
/** Scan a directory. Writes only when `write` is true. */
|
|
176
|
-
export function runCodemod({
|
|
326
|
+
export function runCodemod({
|
|
327
|
+
root,
|
|
328
|
+
write = false,
|
|
329
|
+
names = loadPatternNames(),
|
|
330
|
+
removed = loadRemovedNames(),
|
|
331
|
+
replacements = loadReplacements(),
|
|
332
|
+
}) {
|
|
177
333
|
const files = [];
|
|
178
334
|
const manual = [];
|
|
179
335
|
const moved = new Set();
|
|
@@ -181,7 +337,10 @@ export function runCodemod({ root, write = false, names = loadPatternNames() })
|
|
|
181
337
|
for (const file of walk(root)) {
|
|
182
338
|
const src = readFileSync(file, 'utf8');
|
|
183
339
|
if (!src.includes(ROOT_PKG)) continue;
|
|
184
|
-
for (const stmt of findUnrewritable(src
|
|
340
|
+
for (const stmt of findUnrewritable(src, names))
|
|
341
|
+
manual.push({ file: relative(root, file), stmt });
|
|
342
|
+
for (const stmt of findRemoved(src, removed, replacements))
|
|
343
|
+
manual.push({ file: relative(root, file), stmt, removed: true });
|
|
185
344
|
const r = transformSource(src, names);
|
|
186
345
|
if (!r.changed) continue;
|
|
187
346
|
files.push({ file: relative(root, file), sites: r.sites, moved: r.moved, edits: r.edits });
|
|
@@ -208,8 +367,10 @@ function main(argv) {
|
|
|
208
367
|
const write = !args.check && !args.dryRun;
|
|
209
368
|
const res = runCodemod({ root: args.root, write });
|
|
210
369
|
const summary = `${res.files.length} files, ${res.sites} import sites, ${res.names.length} names (${res.names.join(', ') || 'none'})`;
|
|
370
|
+
// An unreadable file names its own fix; the rest need the names moved by hand.
|
|
211
371
|
const manualLines = res.manual.map(
|
|
212
|
-
(m) =>
|
|
372
|
+
(m) =>
|
|
373
|
+
` ${m.file}: ${m.stmt}${m.removed || m.stmt.startsWith('unreadable ') ? '' : ` (move pattern names to '${SUBPATH}' by hand)`}`,
|
|
213
374
|
);
|
|
214
375
|
if (args.check) {
|
|
215
376
|
if (res.sites > 0 || res.manual.length > 0) {
|
|
@@ -1,26 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"subpath": "@hirobius/design-system/patterns",
|
|
3
3
|
"names": [
|
|
4
|
-
"ActivityFeed",
|
|
5
|
-
"AppShell",
|
|
6
4
|
"AssetImg",
|
|
7
|
-
"
|
|
8
|
-
"Carousel",
|
|
5
|
+
"AssetImgProps",
|
|
9
6
|
"CodeBlock",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
7
|
+
"CodeBlockProps",
|
|
8
|
+
"DataTableSection",
|
|
9
|
+
"DataTableSectionProps",
|
|
10
|
+
"DataTableSectionRow",
|
|
11
|
+
"DestructiveSection",
|
|
12
|
+
"DestructiveSectionProps",
|
|
12
13
|
"ErrorPattern",
|
|
13
|
-
"
|
|
14
|
+
"ErrorPatternProps",
|
|
15
|
+
"FieldWiring",
|
|
16
|
+
"FieldWiringInput",
|
|
14
17
|
"Form",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
+
"FormActions",
|
|
19
|
+
"FormActionsProps",
|
|
20
|
+
"FormField",
|
|
21
|
+
"FormFieldProps",
|
|
22
|
+
"FormFieldShell",
|
|
23
|
+
"FormFieldShellProps",
|
|
24
|
+
"FormProps",
|
|
25
|
+
"MetricTile",
|
|
26
|
+
"MetricTileProps",
|
|
27
|
+
"MetricTileTone",
|
|
28
|
+
"MetricTiles",
|
|
29
|
+
"MetricTilesProps",
|
|
18
30
|
"Page",
|
|
31
|
+
"PageHeader",
|
|
32
|
+
"PageHeaderLevel",
|
|
33
|
+
"PageHeaderProps",
|
|
34
|
+
"PageProps",
|
|
19
35
|
"Reveal",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
36
|
+
"RevealAnimation",
|
|
37
|
+
"RevealProps",
|
|
38
|
+
"StatusTile",
|
|
39
|
+
"StatusTileProps",
|
|
40
|
+
"StatusTileTone",
|
|
41
|
+
"blockCodeTextVariants",
|
|
42
|
+
"blockContainerVariants",
|
|
43
|
+
"blockHeaderVariants",
|
|
44
|
+
"chevronVariants",
|
|
45
|
+
"collapsibleToggleVariants",
|
|
46
|
+
"copyButtonVariants",
|
|
47
|
+
"inlineCodeTextVariants",
|
|
48
|
+
"inlineWrapperVariants",
|
|
49
|
+
"prePanelVariants",
|
|
50
|
+
"useFieldWiring"
|
|
25
51
|
]
|
|
26
52
|
}
|