@astryxdesign/cli 0.1.4-canary.cdee37b → 0.1.4-canary.cfe8ece
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/docs/getting-started.doc.mjs +0 -4
- package/docs/migration.doc.mjs +0 -134
- package/docs/styling.doc.mjs +0 -53
- package/docs/working-with-ai.doc.mjs +1 -1
- package/package.json +10 -10
- package/src/codemods/__tests__/registry.test.mjs +0 -1
- package/src/codemods/registry.mjs +0 -1
- package/src/commands/agent-docs.mjs +5 -7
- package/src/commands/agent-docs.test.mjs +0 -33
- package/src/commands/build-theme.mjs +1 -2
- package/src/commands/build-theme.variants.test.mjs +18 -7
- package/src/commands/init.mjs +1 -1
- package/src/commands/swizzle.mjs +0 -34
- package/src/commands/swizzle.routing.test.mjs +0 -67
- package/templates/blocks/components/AspectRatio/AspectRatioCircleImage.tsx +6 -1
- package/templates/blocks/components/AspectRatio/AspectRatioImageGallery.tsx +7 -2
- package/templates/blocks/components/AspectRatio/AspectRatioShowcase.tsx +5 -2
- package/templates/blocks/components/AspectRatio/AspectRatioSquareImage.tsx +6 -1
- package/templates/blocks/components/AspectRatio/AspectRatioWidescreen.tsx +6 -1
- package/templates/blocks/components/Collapsible/CollapsibleDividedAccordion.doc.mjs +1 -1
- package/templates/blocks/components/Collapsible/CollapsibleDividedAccordion.tsx +1 -1
- package/templates/blocks/components/Collapsible/CollapsibleWithoutCard.tsx +23 -25
- package/templates/pages/classic-gallery/page.tsx +1 -1
- package/templates/pages/dashboard/page.tsx +1 -1
- package/templates/pages/documentation/page.tsx +1 -1
- package/templates/pages/kanban-board/page.tsx +13 -37
- package/templates/pages/mixed-gallery/page.tsx +1 -1
- package/templates/pages/payment-form/page.tsx +6 -2
- package/templates/pages/product-detail/page.tsx +11 -3
- package/templates/pages/product-gallery/page.tsx +1 -1
- package/templates/pages/settings/page.tsx +1 -1
- package/templates/pages/side-gallery/page.tsx +1 -1
- package/templates/pages/table-page/page.tsx +1 -1
- package/templates/pages/table-page-chart/page.tsx +1 -1
- package/templates/pages/table-page-heatmap-status/page.tsx +1 -1
- package/templates/pages/table-page-shoe-store-heatmap/page.tsx +1 -1
- package/src/codemods/transforms/v0.1.5/__tests__/rename-switch-label-spacing-default-to-hug.test.mjs +0 -93
- package/src/codemods/transforms/v0.1.5/index.mjs +0 -19
- package/src/codemods/transforms/v0.1.5/rename-switch-label-spacing-default-to-hug.mjs +0 -140
- package/src/commands/build-theme.color-scheme.test.mjs +0 -153
- package/templates/pages/incident-console/page.tsx +0 -580
- package/templates/pages/incident-console/template.doc.mjs +0 -12
- package/templates/pages/messaging-shell/page.tsx +0 -676
- package/templates/pages/messaging-shell/template.doc.mjs +0 -12
|
@@ -8,10 +8,15 @@ import {Center} from '@astryxdesign/core/Center';
|
|
|
8
8
|
export default function AspectRatioSquareImage() {
|
|
9
9
|
return (
|
|
10
10
|
<Center width={300}>
|
|
11
|
-
<AspectRatio ratio={1}
|
|
11
|
+
<AspectRatio ratio={1}>
|
|
12
12
|
<img
|
|
13
13
|
src="https://lookaside.facebook.com/assets/astryx/light-home-square-1.png"
|
|
14
14
|
alt="1:1 square"
|
|
15
|
+
style={{
|
|
16
|
+
width: '100%',
|
|
17
|
+
height: '100%',
|
|
18
|
+
objectFit: 'cover',
|
|
19
|
+
}}
|
|
15
20
|
/>
|
|
16
21
|
</AspectRatio>
|
|
17
22
|
</Center>
|
|
@@ -8,10 +8,15 @@ import {Center} from '@astryxdesign/core/Center';
|
|
|
8
8
|
export default function AspectRatioWidescreen() {
|
|
9
9
|
return (
|
|
10
10
|
<Center width={600}>
|
|
11
|
-
<AspectRatio ratio={16 / 9}
|
|
11
|
+
<AspectRatio ratio={16 / 9}>
|
|
12
12
|
<img
|
|
13
13
|
src="https://lookaside.facebook.com/assets/astryx/light-scene-horizontal-1.png"
|
|
14
14
|
alt="16:9 widescreen"
|
|
15
|
+
style={{
|
|
16
|
+
width: '100%',
|
|
17
|
+
height: '100%',
|
|
18
|
+
objectFit: 'cover',
|
|
19
|
+
}}
|
|
15
20
|
/>
|
|
16
21
|
</AspectRatio>
|
|
17
22
|
</Center>
|
|
@@ -6,7 +6,7 @@ export const doc = {
|
|
|
6
6
|
exampleFor: 'Collapsible',
|
|
7
7
|
name: 'Collapsible — Divided Accordion',
|
|
8
8
|
displayName: 'Collapsible — Divided Accordion',
|
|
9
|
-
description: 'FAQ-style accordion using the
|
|
9
|
+
description: 'FAQ-style accordion using the dividers prop on CollapsibleGroup: built-in row hairlines and density padding with zero custom CSS. Use for FAQs, settings lists, and nav sections.',
|
|
10
10
|
isReady: true,
|
|
11
11
|
aspectRatio: 4 / 3,
|
|
12
12
|
componentsUsed: ['Collapsible', 'CollapsibleGroup', 'Text'],
|
|
@@ -8,7 +8,7 @@ import {Text} from '@astryxdesign/core/Text';
|
|
|
8
8
|
export default function CollapsibleDividedAccordion() {
|
|
9
9
|
return (
|
|
10
10
|
<div style={{width: '100%', maxWidth: 400}}>
|
|
11
|
-
<CollapsibleGroup type="single"
|
|
11
|
+
<CollapsibleGroup type="single" dividers="between" defaultValue="reset">
|
|
12
12
|
<Collapsible trigger="How do I reset my password?" value="reset">
|
|
13
13
|
<Text type="body">
|
|
14
14
|
Go to Settings → Security → Change Password. You'll receive a
|
|
@@ -2,36 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
|
-
import {Collapsible
|
|
5
|
+
import {Collapsible} from '@astryxdesign/core/Collapsible';
|
|
6
6
|
import {Divider} from '@astryxdesign/core/Divider';
|
|
7
7
|
import {Text} from '@astryxdesign/core/Text';
|
|
8
8
|
import {VStack} from '@astryxdesign/core/Layout';
|
|
9
9
|
|
|
10
10
|
export default function CollapsibleWithoutCard() {
|
|
11
11
|
return (
|
|
12
|
-
<
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
</VStack>
|
|
35
|
-
</CollapsibleGroup>
|
|
12
|
+
<VStack gap={3} style={{width: '100%', maxWidth: 400}}>
|
|
13
|
+
<Collapsible trigger="Deployment Details">
|
|
14
|
+
<Text type="body">
|
|
15
|
+
Last deployed on April 18, 2026 at 3:42 PM by Sarah Chen. Build
|
|
16
|
+
duration was 2m 14s with zero warnings.
|
|
17
|
+
</Text>
|
|
18
|
+
</Collapsible>
|
|
19
|
+
<Divider />
|
|
20
|
+
<Collapsible trigger="Environment Variables" defaultIsOpen={false}>
|
|
21
|
+
<Text type="body">
|
|
22
|
+
12 variables configured. Last updated March 30, 2026. All secrets are
|
|
23
|
+
encrypted at rest with AES-256.
|
|
24
|
+
</Text>
|
|
25
|
+
</Collapsible>
|
|
26
|
+
<Divider />
|
|
27
|
+
<Collapsible trigger="Build Logs" defaultIsOpen={false}>
|
|
28
|
+
<Text type="body">
|
|
29
|
+
Build completed successfully. 847 modules compiled, 0 errors, 0
|
|
30
|
+
warnings. Bundle size: 142 KB gzipped.
|
|
31
|
+
</Text>
|
|
32
|
+
</Collapsible>
|
|
33
|
+
</VStack>
|
|
36
34
|
);
|
|
37
35
|
}
|
|
@@ -439,11 +439,8 @@ export default function KanbanBoardTemplate() {
|
|
|
439
439
|
let cb = columnRefCbs.current.get(id);
|
|
440
440
|
if (!cb) {
|
|
441
441
|
cb = el => {
|
|
442
|
-
if (el) {
|
|
443
|
-
|
|
444
|
-
} else {
|
|
445
|
-
columnEls.current.delete(id);
|
|
446
|
-
}
|
|
442
|
+
if (el) {columnEls.current.set(id, el);}
|
|
443
|
+
else {columnEls.current.delete(id);}
|
|
447
444
|
};
|
|
448
445
|
columnRefCbs.current.set(id, cb);
|
|
449
446
|
}
|
|
@@ -454,11 +451,8 @@ export default function KanbanBoardTemplate() {
|
|
|
454
451
|
let cb = cardRefCbs.current.get(id);
|
|
455
452
|
if (!cb) {
|
|
456
453
|
cb = el => {
|
|
457
|
-
if (el) {
|
|
458
|
-
|
|
459
|
-
} else {
|
|
460
|
-
cardEls.current.delete(id);
|
|
461
|
-
}
|
|
454
|
+
if (el) {cardEls.current.set(id, el);}
|
|
455
|
+
else {cardEls.current.delete(id);}
|
|
462
456
|
};
|
|
463
457
|
cardRefCbs.current.set(id, cb);
|
|
464
458
|
}
|
|
@@ -493,9 +487,7 @@ export default function KanbanBoardTemplate() {
|
|
|
493
487
|
): DropTarget | null => {
|
|
494
488
|
for (const [colId, el] of Array.from(columnEls.current.entries())) {
|
|
495
489
|
const r = el.getBoundingClientRect();
|
|
496
|
-
if (px < r.left || px > r.right || py < r.top || py > r.bottom) {
|
|
497
|
-
continue;
|
|
498
|
-
}
|
|
490
|
+
if (px < r.left || px > r.right || py < r.top || py > r.bottom) {continue;}
|
|
499
491
|
|
|
500
492
|
const ids = itemsByColumn[colId]
|
|
501
493
|
.filter(it => it.id !== draggedId)
|
|
@@ -504,9 +496,7 @@ export default function KanbanBoardTemplate() {
|
|
|
504
496
|
let index = ids.length;
|
|
505
497
|
for (let i = 0; i < ids.length; i++) {
|
|
506
498
|
const cardEl = cardEls.current.get(ids[i]);
|
|
507
|
-
if (!cardEl) {
|
|
508
|
-
continue;
|
|
509
|
-
}
|
|
499
|
+
if (!cardEl) {continue;}
|
|
510
500
|
const cr = cardEl.getBoundingClientRect();
|
|
511
501
|
if (py < cr.top + cr.height / 2) {
|
|
512
502
|
index = i;
|
|
@@ -523,27 +513,21 @@ export default function KanbanBoardTemplate() {
|
|
|
523
513
|
const commitDrag = (id: string, target: DropTarget) => {
|
|
524
514
|
setItems(prev => {
|
|
525
515
|
const moved = prev.find(it => it.id === id);
|
|
526
|
-
if (!moved) {
|
|
527
|
-
return prev;
|
|
528
|
-
}
|
|
516
|
+
if (!moved) {return prev;}
|
|
529
517
|
|
|
530
518
|
const rest = prev.filter(it => it.id !== id);
|
|
531
519
|
const updated: WorkItem = {...moved, column: target.column};
|
|
532
520
|
const colItems = rest.filter(it => it.column === target.column);
|
|
533
521
|
const anchor = colItems[target.index];
|
|
534
522
|
|
|
535
|
-
if (!anchor) {
|
|
536
|
-
return [...rest, updated];
|
|
537
|
-
}
|
|
523
|
+
if (!anchor) {return [...rest, updated];}
|
|
538
524
|
const at = rest.indexOf(anchor);
|
|
539
525
|
return [...rest.slice(0, at), updated, ...rest.slice(at)];
|
|
540
526
|
});
|
|
541
527
|
};
|
|
542
528
|
|
|
543
529
|
const onCardPointerDown = (e: ReactPointerEvent, id: string) => {
|
|
544
|
-
if (e.button !== 0) {
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
530
|
+
if (e.button !== 0) {return;}
|
|
547
531
|
// Let the card's own controls (the actions menu) handle the press.
|
|
548
532
|
if (
|
|
549
533
|
(e.target as HTMLElement).closest(
|
|
@@ -554,9 +538,7 @@ export default function KanbanBoardTemplate() {
|
|
|
554
538
|
}
|
|
555
539
|
|
|
556
540
|
const el = cardEls.current.get(id);
|
|
557
|
-
if (!el) {
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
541
|
+
if (!el) {return;}
|
|
560
542
|
|
|
561
543
|
const rect = el.getBoundingClientRect();
|
|
562
544
|
const startX = e.clientX;
|
|
@@ -592,9 +574,7 @@ export default function KanbanBoardTemplate() {
|
|
|
592
574
|
|
|
593
575
|
const onUp = () => {
|
|
594
576
|
teardownRef.current?.();
|
|
595
|
-
if (started && target) {
|
|
596
|
-
commitDrag(id, target);
|
|
597
|
-
}
|
|
577
|
+
if (started && target) {commitDrag(id, target);}
|
|
598
578
|
setDrag(null);
|
|
599
579
|
};
|
|
600
580
|
|
|
@@ -614,9 +594,7 @@ export default function KanbanBoardTemplate() {
|
|
|
614
594
|
|
|
615
595
|
// Suppress selection while dragging and detach listeners on unmount.
|
|
616
596
|
useEffect(() => {
|
|
617
|
-
if (!isDragging) {
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
597
|
+
if (!isDragging) {return;}
|
|
620
598
|
const previous = document.body.style.userSelect;
|
|
621
599
|
document.body.style.userSelect = 'none';
|
|
622
600
|
return () => {
|
|
@@ -634,9 +612,7 @@ export default function KanbanBoardTemplate() {
|
|
|
634
612
|
const ghostTarget =
|
|
635
613
|
drag && drag.target && drag.target.column === colId ? drag : null;
|
|
636
614
|
|
|
637
|
-
if (visible.length === 0 && !ghostTarget) {
|
|
638
|
-
return null;
|
|
639
|
-
}
|
|
615
|
+
if (visible.length === 0 && !ghostTarget) {return null;}
|
|
640
616
|
|
|
641
617
|
const nodes: ReactNode[] = visible.map(it => (
|
|
642
618
|
<BoardCard
|
|
@@ -153,6 +153,10 @@ const fmt = (n: number) => `$${n.toFixed(2)}`;
|
|
|
153
153
|
// :root by `@astryxdesign/core/astryx.css`). No StyleX compiler required.
|
|
154
154
|
|
|
155
155
|
const fullWidth: CSSProperties = {width: '100%'};
|
|
156
|
+
// LayoutContent clips overflow by default, which traps position:sticky
|
|
157
|
+
// children (the sticky order summary). With height="auto" the page scrolls
|
|
158
|
+
// at the window, so let overflow be visible here so sticky can pin.
|
|
159
|
+
const visibleOverflow: CSSProperties = {overflow: 'visible'};
|
|
156
160
|
// Form column flex-basis so the two checkout columns share width evenly.
|
|
157
161
|
const formColBasis: CSSProperties = {flexBasis: 0};
|
|
158
162
|
// Space the Order Summary content below its collapsible trigger title.
|
|
@@ -262,9 +266,9 @@ export default function PaymentFormPage() {
|
|
|
262
266
|
|
|
263
267
|
return (
|
|
264
268
|
<Layout
|
|
265
|
-
height="
|
|
269
|
+
height="auto"
|
|
266
270
|
content={
|
|
267
|
-
<LayoutContent padding={0}>
|
|
271
|
+
<LayoutContent padding={0} style={visibleOverflow}>
|
|
268
272
|
<Center axis="horizontal">
|
|
269
273
|
<Section
|
|
270
274
|
variant="transparent"
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
'use client';
|
|
4
4
|
|
|
5
5
|
import {useState} from 'react';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
VStack,
|
|
8
|
+
HStack,
|
|
9
|
+
Layout,
|
|
10
|
+
LayoutContent,
|
|
11
|
+
} from '@astryxdesign/core/Layout';
|
|
7
12
|
import {Center} from '@astryxdesign/core/Center';
|
|
8
13
|
import {Grid} from '@astryxdesign/core/Grid';
|
|
9
14
|
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
@@ -198,7 +203,10 @@ function ProductInfo() {
|
|
|
198
203
|
<VStack gap={2}>
|
|
199
204
|
<Text type="label">Finish</Text>
|
|
200
205
|
<VStack hAlign="start">
|
|
201
|
-
<SegmentedControl
|
|
206
|
+
<SegmentedControl
|
|
207
|
+
value={finish}
|
|
208
|
+
onChange={setFinish}
|
|
209
|
+
label="Finish">
|
|
202
210
|
{FINISHES.map(f => (
|
|
203
211
|
<SegmentedControlItem
|
|
204
212
|
key={f.value}
|
|
@@ -278,7 +286,7 @@ export default function ProductDetailTemplate() {
|
|
|
278
286
|
|
|
279
287
|
return (
|
|
280
288
|
<Layout
|
|
281
|
-
height="
|
|
289
|
+
height="auto"
|
|
282
290
|
contentWidth={1200}
|
|
283
291
|
content={
|
|
284
292
|
<LayoutContent padding={6}>
|
package/src/codemods/transforms/v0.1.5/__tests__/rename-switch-label-spacing-default-to-hug.test.mjs
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
import {describe, it, expect} from 'vitest';
|
|
4
|
-
|
|
5
|
-
async function applyTransform(source) {
|
|
6
|
-
const {default: transform} =
|
|
7
|
-
await import('../rename-switch-label-spacing-default-to-hug.mjs');
|
|
8
|
-
const jscodeshift = (await import('jscodeshift')).default;
|
|
9
|
-
const j = jscodeshift.withParser('tsx');
|
|
10
|
-
const api = {jscodeshift: j, stats: () => {}, report: () => {}};
|
|
11
|
-
const file = {source, path: 'test.tsx'};
|
|
12
|
-
const result = transform(file, api);
|
|
13
|
-
return result ?? source;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
describe('rename-switch-label-spacing-default-to-hug', () => {
|
|
17
|
-
it('renames Switch labelSpacing="default" to labelSpacing="hug"', async () => {
|
|
18
|
-
const input = `<Switch label="Notify" value={on} labelSpacing="default" />`;
|
|
19
|
-
const output = await applyTransform(input);
|
|
20
|
-
expect(output).toContain("'hug'");
|
|
21
|
-
expect(output).not.toContain('default');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it("renames expression-container labelSpacing={'default'}", async () => {
|
|
25
|
-
const input = `<Switch label="Notify" value={on} labelSpacing={'default'} />`;
|
|
26
|
-
const output = await applyTransform(input);
|
|
27
|
-
expect(output).toContain("'hug'");
|
|
28
|
-
expect(output).not.toContain("'default'");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('renames default inside a ternary labelSpacing expression', async () => {
|
|
32
|
-
const input = `<Switch label="Notify" value={on} labelSpacing={isRow ? 'spread' : 'default'} />`;
|
|
33
|
-
const output = await applyTransform(input);
|
|
34
|
-
expect(output).toContain("'hug'");
|
|
35
|
-
expect(output).toContain("'spread'");
|
|
36
|
-
expect(output).not.toContain("'default'");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('does not change labelSpacing="spread"', async () => {
|
|
40
|
-
const input = `<Switch label="Notify" value={on} labelSpacing="spread" />`;
|
|
41
|
-
const output = await applyTransform(input);
|
|
42
|
-
expect(output).toBe(input);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('does not touch labelSpacing="default" on non-Switch components', async () => {
|
|
46
|
-
const input = `<LegacyToggle labelSpacing="default" />`;
|
|
47
|
-
const output = await applyTransform(input);
|
|
48
|
-
expect(output).toBe(input);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('does not touch other props with a "default" value on Switch', async () => {
|
|
52
|
-
const input = `<Switch label="Notify" value={on} data-variant="default" />`;
|
|
53
|
-
const output = await applyTransform(input);
|
|
54
|
-
expect(output).toBe(input);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('does not touch unrelated "default" string literals', async () => {
|
|
58
|
-
const input = `const theme = {mode: 'default', label: 'Default Mode'};`;
|
|
59
|
-
const output = await applyTransform(input);
|
|
60
|
-
expect(output).toBe(input);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('renames object labelSpacing prop in files importing Switch', async () => {
|
|
64
|
-
const input = `import {Switch} from '@astryxdesign/core/Switch';
|
|
65
|
-
const cfg = {labelSpacing: 'default'};`;
|
|
66
|
-
const output = await applyTransform(input);
|
|
67
|
-
expect(output).toContain("labelSpacing: 'hug'");
|
|
68
|
-
expect(output).not.toContain("'default'");
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("renames labelSpacing: 'default' as const in files importing Switch", async () => {
|
|
72
|
-
const input = `import {Switch} from '@astryxdesign/core/Switch';
|
|
73
|
-
const ROWS = [{labelSpacing: 'default' as const, label: 'Adjacent'}];`;
|
|
74
|
-
const output = await applyTransform(input);
|
|
75
|
-
expect(output).toContain("'hug' as const");
|
|
76
|
-
expect(output).not.toContain("'default'");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('does not rename object labelSpacing prop when Switch is not imported', async () => {
|
|
80
|
-
const input = `const cfg = {labelSpacing: 'default'};`;
|
|
81
|
-
const output = await applyTransform(input);
|
|
82
|
-
expect(output).toBe(input);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('renames Storybook argTypes options in files importing Switch', async () => {
|
|
86
|
-
const input = `import {Switch} from '@astryxdesign/core/Switch';
|
|
87
|
-
const meta = {argTypes: {labelSpacing: {control: 'select', options: ['default', 'spread']}}};`;
|
|
88
|
-
const output = await applyTransform(input);
|
|
89
|
-
expect(output).toContain("'hug'");
|
|
90
|
-
expect(output).toContain("'spread'");
|
|
91
|
-
expect(output).not.toContain("'default'");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file v0.1.5 transform manifest
|
|
5
|
-
*
|
|
6
|
-
* Lists all codemods for the v0.1.5 release in the order they should run.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import renameSwitchLabelSpacingDefaultToHug, {
|
|
10
|
-
meta as renameSwitchLabelSpacingDefaultToHugMeta,
|
|
11
|
-
} from './rename-switch-label-spacing-default-to-hug.mjs';
|
|
12
|
-
|
|
13
|
-
export default [
|
|
14
|
-
{
|
|
15
|
-
name: 'rename-switch-label-spacing-default-to-hug',
|
|
16
|
-
transform: renameSwitchLabelSpacingDefaultToHug,
|
|
17
|
-
meta: renameSwitchLabelSpacingDefaultToHugMeta,
|
|
18
|
-
},
|
|
19
|
-
];
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @file Codemod: Rename Switch labelSpacing "default" to "hug"
|
|
5
|
-
* @see https://github.com/facebook/astryx/issues/2889
|
|
6
|
-
*
|
|
7
|
-
* The `labelSpacing="default"` value is renamed to `labelSpacing="hug"` so
|
|
8
|
-
* the name describes the behavior (label and switch hug each other), matching
|
|
9
|
-
* the behavioral register of the sibling `spread` value and the existing
|
|
10
|
-
* `hug`/`fill` layout vocabulary on TabList and SegmentedControl. The old
|
|
11
|
-
* `default` value keeps working as a deprecated alias, so this codemod is a
|
|
12
|
-
* cleanup, not a build fix.
|
|
13
|
-
*
|
|
14
|
-
* Transforms (scoped to Switch):
|
|
15
|
-
* - <Switch labelSpacing="default" /> → <Switch labelSpacing="hug" />
|
|
16
|
-
* - <Switch labelSpacing={'default'} /> → <Switch labelSpacing={'hug'} />
|
|
17
|
-
* - <Switch labelSpacing={cond ? 'default' : x}> → <Switch labelSpacing={cond ? 'hug' : x}>
|
|
18
|
-
* - Storybook argTypes: labelSpacing: { options: [..., 'default', ...] }
|
|
19
|
-
* → replaces 'default' with 'hug' (files importing Switch)
|
|
20
|
-
* - Object properties: { labelSpacing: 'default' } / { labelSpacing: 'default' as const }
|
|
21
|
-
* → { labelSpacing: 'hug' } (files importing Switch)
|
|
22
|
-
*
|
|
23
|
-
* The transform deliberately does NOT touch bare `'default'` strings that are
|
|
24
|
-
* not a `labelSpacing` value (an extremely common string otherwise), so it is
|
|
25
|
-
* safe to run across an entire codebase.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
export const meta = {
|
|
29
|
-
title: 'Rename Switch labelSpacing "default" to "hug"',
|
|
30
|
-
description:
|
|
31
|
-
'Renames the `labelSpacing="default"` value to `labelSpacing="hug"` on ' +
|
|
32
|
-
'Switch. The old value keeps working as a deprecated alias. Also updates ' +
|
|
33
|
-
'Storybook labelSpacing argTypes options and object-literal `labelSpacing` ' +
|
|
34
|
-
'props in files that import Switch.',
|
|
35
|
-
pr: '#2889',
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const OLD_VALUE = 'default';
|
|
39
|
-
const NEW_VALUE = 'hug';
|
|
40
|
-
|
|
41
|
-
/** Prop whose value is being renamed. */
|
|
42
|
-
const TARGET_PROP = 'labelSpacing';
|
|
43
|
-
|
|
44
|
-
/** Components whose `labelSpacing` prop accepts a SwitchLabelSpacing value. */
|
|
45
|
-
const TARGET_COMPONENTS = new Set(['Switch']);
|
|
46
|
-
|
|
47
|
-
export default function transformer(file, api) {
|
|
48
|
-
const j = api.jscodeshift;
|
|
49
|
-
const root = j(file.source);
|
|
50
|
-
let hasChanges = false;
|
|
51
|
-
|
|
52
|
-
/** Rewrite a string-literal node when it equals the old value. */
|
|
53
|
-
function renameStringLiteral(node) {
|
|
54
|
-
if (!node) return false;
|
|
55
|
-
if (
|
|
56
|
-
(node.type === 'StringLiteral' || node.type === 'Literal') &&
|
|
57
|
-
node.value === OLD_VALUE
|
|
58
|
-
) {
|
|
59
|
-
node.value = NEW_VALUE;
|
|
60
|
-
if (node.raw) node.raw = `'${NEW_VALUE}'`;
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Recursively rewrite the old value inside ternary/logical expressions. */
|
|
67
|
-
function renameInExpression(node) {
|
|
68
|
-
if (!node) return false;
|
|
69
|
-
let changed = false;
|
|
70
|
-
if (node.type === 'StringLiteral' || node.type === 'Literal') {
|
|
71
|
-
changed = renameStringLiteral(node) || changed;
|
|
72
|
-
} else if (node.type === 'ConditionalExpression') {
|
|
73
|
-
changed = renameInExpression(node.consequent) || changed;
|
|
74
|
-
changed = renameInExpression(node.alternate) || changed;
|
|
75
|
-
} else if (node.type === 'LogicalExpression') {
|
|
76
|
-
changed = renameInExpression(node.left) || changed;
|
|
77
|
-
changed = renameInExpression(node.right) || changed;
|
|
78
|
-
} else if (node.type === 'TSAsExpression') {
|
|
79
|
-
// `'default' as const` → rewrite the inner expression
|
|
80
|
-
changed = renameInExpression(node.expression) || changed;
|
|
81
|
-
}
|
|
82
|
-
return changed;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// --- 1. JSX attribute: labelSpacing="default" / labelSpacing={'default'} on Switch ---
|
|
86
|
-
root.find(j.JSXOpeningElement).forEach(path => {
|
|
87
|
-
const name = path.node.name;
|
|
88
|
-
const componentName = name.type === 'JSXIdentifier' ? name.name : null;
|
|
89
|
-
if (!componentName || !TARGET_COMPONENTS.has(componentName)) return;
|
|
90
|
-
|
|
91
|
-
path.node.attributes.forEach(attr => {
|
|
92
|
-
if (attr.type !== 'JSXAttribute') return;
|
|
93
|
-
if (!attr.name || attr.name.name !== TARGET_PROP) return;
|
|
94
|
-
|
|
95
|
-
const value = attr.value;
|
|
96
|
-
if (!value) return;
|
|
97
|
-
|
|
98
|
-
if (value.type === 'StringLiteral' || value.type === 'Literal') {
|
|
99
|
-
if (renameStringLiteral(value)) hasChanges = true;
|
|
100
|
-
} else if (value.type === 'JSXExpressionContainer') {
|
|
101
|
-
if (renameInExpression(value.expression)) hasChanges = true;
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// Object-property / argTypes transforms only run in files that use a target
|
|
107
|
-
// component — keeps unrelated `{ labelSpacing: 'default' }` strings safe.
|
|
108
|
-
const importsTarget =
|
|
109
|
-
root
|
|
110
|
-
.find(j.ImportSpecifier)
|
|
111
|
-
.filter(p => TARGET_COMPONENTS.has(p.node.imported?.name))
|
|
112
|
-
.size() > 0;
|
|
113
|
-
|
|
114
|
-
if (importsTarget) {
|
|
115
|
-
const PropertyType = j.ObjectProperty ?? j.Property;
|
|
116
|
-
|
|
117
|
-
// --- 2. Object property: { labelSpacing: 'default' } / 'default' as const ---
|
|
118
|
-
root.find(PropertyType, {key: {name: TARGET_PROP}}).forEach(path => {
|
|
119
|
-
if (renameInExpression(path.node.value)) hasChanges = true;
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// --- 3. Storybook argTypes: labelSpacing: { options: [..., 'default', ...] } ---
|
|
123
|
-
root.find(PropertyType, {key: {name: TARGET_PROP}}).forEach(path => {
|
|
124
|
-
const value = path.node.value;
|
|
125
|
-
if (!value || value.type !== 'ObjectExpression') return;
|
|
126
|
-
|
|
127
|
-
const optionsProp = value.properties.find(
|
|
128
|
-
p => p.key && (p.key.name === 'options' || p.key.value === 'options'),
|
|
129
|
-
);
|
|
130
|
-
if (optionsProp && optionsProp.value.type === 'ArrayExpression') {
|
|
131
|
-
optionsProp.value.elements.forEach(el => {
|
|
132
|
-
if (renameStringLiteral(el)) hasChanges = true;
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!hasChanges) return undefined;
|
|
139
|
-
return root.toSource({quote: 'single'});
|
|
140
|
-
}
|