@lablup/ui-common-cli 0.2.0-alpha.15
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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +28 -0
- package/bin/ui-common.mjs +12 -0
- package/cli/agents.mjs +335 -0
- package/cli/diff.mjs +125 -0
- package/cli/lab-peer.mjs +226 -0
- package/cli/main.mjs +97 -0
- package/cli/passthrough.mjs +153 -0
- package/cli/paths.mjs +290 -0
- package/cli/report.mjs +157 -0
- package/cli/resolve.mjs +257 -0
- package/cli/rewrite.mjs +133 -0
- package/cli/semver.mjs +278 -0
- package/cli/shadow.mjs +161 -0
- package/cli/sync-astryx.mjs +417 -0
- package/cli/upgrade.mjs +776 -0
- package/codemods/0.2/components.mjs +903 -0
- package/codemods/0.2/elements.mjs +745 -0
- package/codemods/0.2/index.mjs +68 -0
- package/codemods/0.2/legacy-classes.json +292 -0
- package/codemods/0.2/local-modules.mjs +510 -0
- package/codemods/0.2/map.mjs +178 -0
- package/codemods/0.2/package-json.mjs +333 -0
- package/codemods/0.2/scan.mjs +397 -0
- package/codemods/0.2/stylesheets.mjs +479 -0
- package/codemods/0.2/theme.mjs +325 -0
- package/codemods/lib/jsx.mjs +352 -0
- package/codemods/lib/todo.mjs +151 -0
- package/codemods/registry.mjs +95 -0
- package/codemods/upstream.mjs +155 -0
- package/migration/0.1-to-0.2.json +629 -0
- package/package.json +50 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 0.1 -> 0.2 report-only scans. Nothing here edits a file: each finding is
|
|
3
|
+
* something the codemods cannot migrate and a person has to look at.
|
|
4
|
+
*
|
|
5
|
+
* - CSS selectors on 0.1 class names (Astryx renders none of them).
|
|
6
|
+
* - DOM hooks on those classes in scripts (querySelector, closest, classList).
|
|
7
|
+
* - Tests querying them.
|
|
8
|
+
* - `vi.mock` / `jest.mock` of @lablup/ui-common.
|
|
9
|
+
* - Custom properties the project declares that Astryx declares too.
|
|
10
|
+
* - Hard-coded 0.1 stylesheet paths the codemods did not rewrite.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { dirname, join } from "node:path";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
import postcss from "postcss";
|
|
17
|
+
import selectorParser from "postcss-selector-parser";
|
|
18
|
+
|
|
19
|
+
import { dependencyDir, targetUiCommonRoot } from "../../cli/paths.mjs";
|
|
20
|
+
import { keptClassRename } from "./map.mjs";
|
|
21
|
+
import { scanTheme } from "./theme.mjs";
|
|
22
|
+
|
|
23
|
+
const legacy = JSON.parse(
|
|
24
|
+
readFileSync(
|
|
25
|
+
join(dirname(fileURLToPath(import.meta.url)), "legacy-classes.json"),
|
|
26
|
+
"utf8",
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
/** @type {Map<string, string>} class -> 0.1 component */
|
|
31
|
+
export const LEGACY_CLASSES = new Map();
|
|
32
|
+
for (const [component, classes] of Object.entries(legacy.components)) {
|
|
33
|
+
for (const c of /** @type {string[]} */ (classes)) LEGACY_CLASSES.set(c, component);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A class 0.1 rendered: every class its stylesheets declared, plus anything
|
|
38
|
+
* under a kept component's renamed BEM block.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} name
|
|
41
|
+
*/
|
|
42
|
+
export function isLegacyClass(name) {
|
|
43
|
+
return LEGACY_CLASSES.has(name) || keptClassRename(name) !== null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Where a 0.1 class went: a kept component's class was renamed (the map's
|
|
48
|
+
* classRenames), a removed component's is gone.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} name
|
|
51
|
+
*/
|
|
52
|
+
export function describeClass(name) {
|
|
53
|
+
const owner = LEGACY_CLASSES.get(name);
|
|
54
|
+
const renamed = keptClassRename(name);
|
|
55
|
+
if (renamed?.to) return `.${name} → .${renamed.to}${owner ? ` (${owner})` : ""}`;
|
|
56
|
+
return `.${name} (${owner ?? "0.1"}): gone`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const COLLISION_PREFIXES = /^--(color|radius|spacing|font|shadow)-/;
|
|
60
|
+
|
|
61
|
+
/** @type {Set<string> | null} */
|
|
62
|
+
let astryxProperties = null;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Custom properties Astryx declares, read from the installed stylesheets so
|
|
66
|
+
* the list follows the pinned version.
|
|
67
|
+
*/
|
|
68
|
+
export function astryxCustomProperties() {
|
|
69
|
+
if (astryxProperties) return astryxProperties;
|
|
70
|
+
const names = new Set();
|
|
71
|
+
const sheets = [];
|
|
72
|
+
// The Astryx the upgrade moves to: the project may still be on 0.1.
|
|
73
|
+
const root = targetUiCommonRoot();
|
|
74
|
+
const core = dependencyDir("@astryxdesign/core", root);
|
|
75
|
+
if (core) sheets.push(join(core, "dist/astryx.css"));
|
|
76
|
+
const neutral = dependencyDir("@astryxdesign/theme-neutral", root);
|
|
77
|
+
if (neutral) sheets.push(join(neutral, "dist/theme.css"));
|
|
78
|
+
for (const sheet of sheets) {
|
|
79
|
+
let text;
|
|
80
|
+
try {
|
|
81
|
+
text = readFileSync(sheet, "utf8");
|
|
82
|
+
} catch {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
for (const match of text.matchAll(/(--[A-Za-z0-9_-]+)\s*:/g)) {
|
|
86
|
+
if (COLLISION_PREFIXES.test(match[1])) names.add(match[1]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
astryxProperties = names;
|
|
90
|
+
return names;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** @param {string} path */
|
|
94
|
+
export function isTestFile(path) {
|
|
95
|
+
return (
|
|
96
|
+
/(\.|\/)(test|spec)\.[cm]?[jt]sx?$/.test(path) ||
|
|
97
|
+
/(^|\/)(__tests__|e2e|tests?)\//.test(path)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** @param {string} text @param {number} index */
|
|
102
|
+
function lineAt(text, index) {
|
|
103
|
+
return text.slice(0, index).split("\n").length;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @param {string} selector */
|
|
107
|
+
function classesIn(selector) {
|
|
108
|
+
const found = new Set();
|
|
109
|
+
try {
|
|
110
|
+
selectorParser((s) => {
|
|
111
|
+
s.walkClasses((node) => {
|
|
112
|
+
found.add(node.value);
|
|
113
|
+
});
|
|
114
|
+
}).processSync(selector);
|
|
115
|
+
} catch {
|
|
116
|
+
for (const m of selector.matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)) found.add(m[1]);
|
|
117
|
+
}
|
|
118
|
+
return [...found];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @typedef {{category: string, file: string, line: number, text: string, detail?: string, classes?: string[]}} Finding
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {string} file relative path
|
|
127
|
+
* @param {string} source
|
|
128
|
+
* @returns {Finding[]}
|
|
129
|
+
*/
|
|
130
|
+
function scanStylesheet(file, source) {
|
|
131
|
+
/** @type {Finding[]} */
|
|
132
|
+
const findings = [];
|
|
133
|
+
const properties = astryxCustomProperties();
|
|
134
|
+
if (file.endsWith(".css")) {
|
|
135
|
+
let root;
|
|
136
|
+
try {
|
|
137
|
+
root = postcss.parse(source, { from: file });
|
|
138
|
+
} catch {
|
|
139
|
+
return findings;
|
|
140
|
+
}
|
|
141
|
+
root.walkRules((rule) => {
|
|
142
|
+
if (
|
|
143
|
+
rule.parent?.type === "atrule" &&
|
|
144
|
+
/keyframes$/.test(/** @type {any} */ (rule.parent).name)
|
|
145
|
+
)
|
|
146
|
+
return;
|
|
147
|
+
const hits = classesIn(rule.selector).filter(isLegacyClass);
|
|
148
|
+
if (hits.length > 0) {
|
|
149
|
+
findings.push({
|
|
150
|
+
category: "css-selector",
|
|
151
|
+
file,
|
|
152
|
+
line: rule.source?.start?.line ?? 0,
|
|
153
|
+
text: rule.selector.replace(/\s+/g, " "),
|
|
154
|
+
detail: hits.map(describeClass).join(", "),
|
|
155
|
+
classes: hits,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
root.walkDecls((decl) => {
|
|
160
|
+
if (properties.has(decl.prop)) {
|
|
161
|
+
const rule = /** @type {any} */ (decl.parent);
|
|
162
|
+
findings.push({
|
|
163
|
+
category: "custom-property",
|
|
164
|
+
file,
|
|
165
|
+
line: decl.source?.start?.line ?? 0,
|
|
166
|
+
text: `${rule?.selector ?? `@${rule?.name ?? ""}`} { ${decl.prop}: ${decl.value} }`,
|
|
167
|
+
detail: decl.prop,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return findings;
|
|
172
|
+
}
|
|
173
|
+
// Sass / Less: line-level.
|
|
174
|
+
source.split("\n").forEach((line, i) => {
|
|
175
|
+
const code = line.replace(/\/\/.*$/, "");
|
|
176
|
+
const declaration = /^\s*(--[A-Za-z0-9_-]+)\s*:/.exec(code);
|
|
177
|
+
if (declaration && properties.has(declaration[1])) {
|
|
178
|
+
findings.push({
|
|
179
|
+
category: "custom-property",
|
|
180
|
+
file,
|
|
181
|
+
line: i + 1,
|
|
182
|
+
text: line.trim(),
|
|
183
|
+
detail: declaration[1],
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!/[{,]\s*$/.test(code)) return;
|
|
188
|
+
const hits = [...code.matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)]
|
|
189
|
+
.map((m) => m[1])
|
|
190
|
+
.filter(isLegacyClass);
|
|
191
|
+
if (hits.length > 0) {
|
|
192
|
+
findings.push({
|
|
193
|
+
category: "css-selector",
|
|
194
|
+
file,
|
|
195
|
+
line: i + 1,
|
|
196
|
+
text: code.trim().replace(/\s*[{,]$/, ""),
|
|
197
|
+
detail: [...new Set(hits)].map(describeClass).join(", "),
|
|
198
|
+
classes: hits,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
return findings;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// DOM APIs, and the selector-taking calls of Playwright, Cypress and
|
|
206
|
+
// Testing Library's container queries.
|
|
207
|
+
const SELECTOR_CALL =
|
|
208
|
+
/(?:\b(querySelector(?:All)?|closest|matches|webkitMatchesSelector|locator|waitForSelector)|(?<![\w$])(\$\$?(?:eval)?)|\bcy\.(get|find))\(\s*(["'`])((?:(?!\4)[^\\]|\\.)*)\4/g;
|
|
209
|
+
const CLASS_CALL =
|
|
210
|
+
/\b(classList\.(?:contains|add|remove|toggle|replace)|getElementsByClassName|toHaveClass)\(\s*(["'`])((?:(?!\2)[^\\]|\\.)*)\2/g;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* @param {string} file relative path
|
|
214
|
+
* @param {string} source
|
|
215
|
+
* @returns {Finding[]}
|
|
216
|
+
*/
|
|
217
|
+
function scanScript(file, source) {
|
|
218
|
+
/** @type {Finding[]} */
|
|
219
|
+
const findings = [];
|
|
220
|
+
const test = isTestFile(file);
|
|
221
|
+
const lines = source.split("\n");
|
|
222
|
+
const record = (/** @type {RegExpMatchArray} */ m, /** @type {string[]} */ hits) => {
|
|
223
|
+
const line = lineAt(source, m.index ?? 0);
|
|
224
|
+
findings.push({
|
|
225
|
+
category: test ? "test-query" : "dom-hook",
|
|
226
|
+
file,
|
|
227
|
+
line,
|
|
228
|
+
text: (lines[line - 1] ?? "").trim(),
|
|
229
|
+
detail: [...new Set(hits)].map(describeClass).join(", "),
|
|
230
|
+
classes: hits,
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
for (const m of source.matchAll(SELECTOR_CALL)) {
|
|
234
|
+
const hits = classesIn(m[5]).filter(isLegacyClass);
|
|
235
|
+
if (hits.length > 0) record(m, hits);
|
|
236
|
+
}
|
|
237
|
+
for (const m of source.matchAll(CLASS_CALL)) {
|
|
238
|
+
const hits = m[3].split(/\s+/).filter(isLegacyClass);
|
|
239
|
+
if (hits.length > 0) record(m, hits);
|
|
240
|
+
}
|
|
241
|
+
for (const m of source.matchAll(
|
|
242
|
+
/\b(vi|jest)\.(mock|doMock|unmock)\(\s*(["'`])(@lablup\/ui-common[^"'`]*)\3/g,
|
|
243
|
+
)) {
|
|
244
|
+
const line = lineAt(source, m.index ?? 0);
|
|
245
|
+
findings.push({
|
|
246
|
+
category: "module-mock",
|
|
247
|
+
file,
|
|
248
|
+
line,
|
|
249
|
+
text: (lines[line - 1] ?? "").trim(),
|
|
250
|
+
detail: m[4],
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const properties = astryxCustomProperties();
|
|
254
|
+
for (const m of source.matchAll(/(["'`])(--[A-Za-z0-9_-]+)\1/g)) {
|
|
255
|
+
if (!properties.has(m[2])) continue;
|
|
256
|
+
const line = lineAt(source, m.index ?? 0);
|
|
257
|
+
const text = (lines[line - 1] ?? "").trim();
|
|
258
|
+
// Declarations (setProperty, style objects), not reads through var().
|
|
259
|
+
if (!/setProperty\(|["'`]\s*:/.test(text)) continue;
|
|
260
|
+
findings.push({ category: "custom-property", file, line, text, detail: m[2] });
|
|
261
|
+
}
|
|
262
|
+
return findings;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** A selector that is one class, with pseudo-classes at most: `.tabs__tab:hover`. */
|
|
266
|
+
const DEFINITION = /^\.(-?[_a-zA-Z][\w-]*)(?::{1,2}[\w-]+(?:\([^)]*\))?)*$/;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The 0.1 class names the project owns: it defines each in a stylesheet of
|
|
270
|
+
* its own as a rule by itself (`.tabs__tab { … }`) and renders it in its own
|
|
271
|
+
* markup (`className="tabs__tab"`, outside tests). Such a project most likely
|
|
272
|
+
* has its own `.tabs__tab`, so findings on it are listed apart, as lower
|
|
273
|
+
* confidence. A definition alone is not enough: an override of ui-common's
|
|
274
|
+
* class (`.drawer__content { padding: 0 }`) looks the same.
|
|
275
|
+
*
|
|
276
|
+
* @param {Array<[string, string]>} files [relative path, content]
|
|
277
|
+
* @returns {{ownClasses: Set<string>}}
|
|
278
|
+
*/
|
|
279
|
+
export function prepareScan(files) {
|
|
280
|
+
const defined = new Set();
|
|
281
|
+
/** @param {string} selectors */
|
|
282
|
+
const collect = (selectors) => {
|
|
283
|
+
for (const part of selectors.split(",")) {
|
|
284
|
+
const m = DEFINITION.exec(part.trim());
|
|
285
|
+
if (m && isLegacyClass(m[1])) defined.add(m[1]);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
for (const [file, source] of files) {
|
|
289
|
+
if (file.endsWith(".css")) {
|
|
290
|
+
try {
|
|
291
|
+
postcss
|
|
292
|
+
.parse(source, { from: file })
|
|
293
|
+
.walkRules((rule) => collect(rule.selector));
|
|
294
|
+
} catch {
|
|
295
|
+
// unparseable: nothing defined
|
|
296
|
+
}
|
|
297
|
+
} else if (/\.(scss|sass|less)$/.test(file)) {
|
|
298
|
+
for (const line of source.split("\n")) {
|
|
299
|
+
const m = /^\s*([^{}/@]+?)\s*\{\s*$/.exec(line);
|
|
300
|
+
if (m) collect(m[1]);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const own = new Set();
|
|
305
|
+
if (defined.size === 0) return { ownClasses: own };
|
|
306
|
+
const escape = (/** @type {string} */ c) => c.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
307
|
+
const pattern = new RegExp(
|
|
308
|
+
`(?:^|[\\s"'\`])(${[...defined].map(escape).join("|")})(?=[\\s"'\`]|$)`,
|
|
309
|
+
"gm",
|
|
310
|
+
);
|
|
311
|
+
for (const [file, source] of files) {
|
|
312
|
+
if (/\.(css|scss|sass|less)$/.test(file) || isTestFile(file)) continue;
|
|
313
|
+
for (const m of source.matchAll(pattern)) own.add(m[1]);
|
|
314
|
+
}
|
|
315
|
+
return { ownClasses: own };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const ORIGIN = {
|
|
319
|
+
"css-selector": "CSS selector",
|
|
320
|
+
"dom-hook": "DOM hook",
|
|
321
|
+
"test-query": "test query",
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* @param {string} file relative path
|
|
326
|
+
* @param {string} source final content
|
|
327
|
+
* @param {{ownClasses?: Set<string>}} [context] from prepareScan
|
|
328
|
+
* @returns {Finding[]}
|
|
329
|
+
*/
|
|
330
|
+
export function scanFile(file, source, context) {
|
|
331
|
+
/** @type {Finding[]} */
|
|
332
|
+
const findings = [];
|
|
333
|
+
if (/\.(css|scss|sass|less)$/.test(file))
|
|
334
|
+
findings.push(...scanStylesheet(file, source));
|
|
335
|
+
else findings.push(...scanScript(file, source));
|
|
336
|
+
|
|
337
|
+
findings.push(...scanTheme(file, source));
|
|
338
|
+
source.split("\n").forEach((line, i) => {
|
|
339
|
+
if (/@lablup\/ui-common\/styles\//.test(line)) {
|
|
340
|
+
findings.push({
|
|
341
|
+
category: "stylesheet-path",
|
|
342
|
+
file,
|
|
343
|
+
line: i + 1,
|
|
344
|
+
text: line.trim(),
|
|
345
|
+
detail: "0.1 stylesheet path: deprecated in 0.2, removed in 0.3",
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
const own = context?.ownClasses;
|
|
350
|
+
for (const f of findings) {
|
|
351
|
+
if (f.classes && own && f.classes.every((c) => own.has(c))) {
|
|
352
|
+
f.detail = `${/** @type {Record<string, string>} */ (ORIGIN)[f.category]}: ${f.detail}`;
|
|
353
|
+
f.category = "own-class";
|
|
354
|
+
}
|
|
355
|
+
delete f.classes;
|
|
356
|
+
}
|
|
357
|
+
return findings;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export const CATEGORIES = {
|
|
361
|
+
"css-selector": {
|
|
362
|
+
title: "CSS selectors on 0.1 class names",
|
|
363
|
+
help: "A removed component's classes are gone: Astryx renders its own. Restyle through the component's props, the theme, or your `components` layer. A kept component's classes were renamed to `uic-` names (shown as →), but its markup was rebuilt on Astryx, so check the selector still means what it did. Generic names (`.button`, `.select`) may be your own classes: skip those.",
|
|
364
|
+
},
|
|
365
|
+
"dom-hook": {
|
|
366
|
+
title: "DOM hooks on 0.1 class names",
|
|
367
|
+
help: "Scripts that find 0.1 markup by class stop matching. Use a ref, a data-testid, or the Astryx component's own API.",
|
|
368
|
+
},
|
|
369
|
+
"test-query": {
|
|
370
|
+
title: "Tests querying 0.1 class names",
|
|
371
|
+
help: "Query by role, label or data-testid instead.",
|
|
372
|
+
},
|
|
373
|
+
"module-mock": {
|
|
374
|
+
title: "Module mocks of @lablup/ui-common",
|
|
375
|
+
help: "A mock of the root barrel now stands in for all of Astryx too, and mocked names such as Button, Badge or Select no longer match what the code imports (Astryx's, from their own subpaths). Re-check every mock factory.",
|
|
376
|
+
},
|
|
377
|
+
"custom-property": {
|
|
378
|
+
title: "Custom properties that collide with Astryx tokens",
|
|
379
|
+
help: "Astryx declares the same name. Whichever rule wins the cascade now restyles both your CSS and Astryx's components. Rename yours, or set it through a theme (`defineTheme`) on purpose.",
|
|
380
|
+
},
|
|
381
|
+
theme: {
|
|
382
|
+
title: "0.1 theme switches and theme selectors",
|
|
383
|
+
help: '0.1 switched themes by stylesheet and `html[data-theme="orange-…"]`. In 0.2 `<Theme theme={lablupTheme} mode=…>` owns `html[data-theme]` and sets it to `light` or `dark`, so code that writes another value fights it, and selectors on another value never match.',
|
|
384
|
+
},
|
|
385
|
+
"own-class": {
|
|
386
|
+
title: "0.1 class names your own CSS also defines (lower confidence)",
|
|
387
|
+
help: "The same names as above, but your own stylesheets define each of them as a rule of its own (`.tabs__tab { … }`), so they most likely belong to markup you render, not to ui-common's. Skim them; most need nothing.",
|
|
388
|
+
},
|
|
389
|
+
"local-wrapper": {
|
|
390
|
+
title: "Local wrappers around 0.1 components",
|
|
391
|
+
help: "Your own component renders a 0.1 component and hands its props on, so it now renders the Astryx one. Its call sites pass the wrapper's props, which the upgrade does not rewrite: check the wrapper's props type and what it passes on against the Astryx component. A pure re-export (`export { Button } from …`) is not listed: its call sites were migrated.",
|
|
392
|
+
},
|
|
393
|
+
"stylesheet-path": {
|
|
394
|
+
title: "0.1 stylesheet paths left in place",
|
|
395
|
+
help: "Scripts, configs or tests that name `@lablup/ui-common/styles/*` directly. base.css and the orange themes are deprecated in 0.2 and removed in 0.3; the Lablup theme replaces them.",
|
|
396
|
+
},
|
|
397
|
+
};
|