@astryxdesign/cli 0.6.3-canary.d0d49a4 → 0.6.3-canary.db4e378
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/README.md +86 -72
- package/api/build/build.doc.mjs +3 -3
- package/api/build/build.test.mjs +14 -3
- package/api/build/build.type.d.mts +35 -1
- package/api/build/build.type.mjs +24 -3
- package/api/build/help/help.d.mts +8 -5
- package/api/build/help/help.mjs +56 -6
- package/api/component/component.doc.mjs +12 -3
- package/api/component/component.mjs +31 -2
- package/api/component/component.test.mjs +38 -0
- package/api/component/component.type.d.mts +16 -5
- package/api/component/component.type.mjs +13 -5
- package/api/component/detail/blocks/blocks.d.mts +2 -1
- package/api/component/detail/blocks/blocks.mjs +4 -3
- package/api/component/list/list.d.mts +0 -5
- package/api/component/list/list.mjs +37 -9
- package/api/discover/discover.doc.mjs +1 -1
- package/api/gap-report/gap-report.doc.mjs +7 -4
- package/api/init/init.doc.mjs +4 -0
- package/api/init/init.test.mjs +41 -1
- package/api/init/remove/remove.mjs +1 -1
- package/api/init/run/run.mjs +20 -10
- package/api/integration/add-contribution.component-names.test.mjs +120 -0
- package/api/integration/add-contribution.mjs +11 -0
- package/api/integration/add-contribution.test.mjs +80 -0
- package/api/integration/integrationAdd.doc.mjs +7 -4
- package/api/integration/integrationAddComponent.doc.mjs +1 -1
- package/api/integration/validate-integration.mjs +2 -0
- package/api/integration/validate-integration.test.mjs +91 -0
- package/api/integration/validateIntegration.doc.mjs +1 -1
- package/api/json/envelope-types.test.mjs +76 -0
- package/api/json/parseResponse.doc.mjs +2 -2
- package/api/layout/expand/expand.mjs +7 -5
- package/api/layout/expand/expand.path-safety.test.mjs +53 -0
- package/api/layout/grammar/grammar.mjs +2 -1
- package/api/layout/layoutExpand.doc.mjs +1 -1
- package/api/search/search-return-type.test.mjs +54 -0
- package/api/search/search.d.mts +2 -9
- package/api/search/search.doc.mjs +5 -1
- package/api/search/search.mjs +10 -3
- package/api/search/search.test.mjs +34 -0
- package/api/swizzle/copy/copy.mjs +28 -11
- package/api/swizzle/swizzle.doc.mjs +1 -1
- package/api/template/copy/copy.mjs +17 -23
- package/api/template/copy/copy.test.mjs +17 -0
- package/api/template/template.doc.mjs +2 -1
- package/api/theme/add/add.binary.test.mjs +91 -0
- package/api/theme/add/add.mjs +20 -3
- package/api/theme/add/add.staging.test.mjs +66 -0
- package/api/theme/build/build.mjs +114 -37
- package/api/theme/build/build.public-component-vars.test.mjs +1 -1
- package/api/theme/build/build.receipt-doc.test.mjs +111 -0
- package/api/theme/build/font-warning.mjs +3 -3
- package/api/theme/build/font-warning.test.mjs +5 -2
- package/api/theme/palette/generate/generate.mjs +7 -2
- package/api/theme/palette/generate/generate.test.mjs +96 -0
- package/api/theme/palette/generate/generator.mjs +8 -1
- package/api/theme/palette/generate/generator.test.mjs +10 -0
- package/api/theme/template/template.mjs +11 -2
- package/api/theme/template/template.test.mjs +15 -0
- package/api/theme/themeBuild.doc.mjs +7 -4
- package/api/theme/themeTemplate.doc.mjs +4 -1
- package/api/upgrade/_adapter.mjs +27 -4
- package/api/upgrade/list/list.mjs +2 -1
- package/api/upgrade/list/list.test.mjs +73 -0
- package/api/upgrade/status/status.mjs +2 -2
- package/api/upgrade/upgrade.doc.mjs +2 -1
- package/api/upgrade/upgrade.type.d.mts +4 -0
- package/api/upgrade/upgrade.type.mjs +1 -0
- package/assets/codemods/term-log.mjs +32 -8
- package/assets/codemods/term-log.test.mjs +19 -1
- package/assets/codemods/transform-prop.mjs +109 -0
- package/assets/codemods/transform-prop.test.mjs +95 -0
- package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
- package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
- package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
- package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
- package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
- package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
- package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
- package/assets/templates/pages/table-tree/page.tsx +1704 -0
- package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
- package/authoring/config/config.doc.mjs +1 -1
- package/authoring/config/debug-composition.test.mjs +92 -0
- package/authoring/config/type.ts +15 -3
- package/clients/cli/command-load-failure.test.mjs +83 -0
- package/clients/cli/commands/blog.mjs +23 -8
- package/clients/cli/commands/blog.test.mjs +42 -1
- package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
- package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
- package/clients/cli/commands/build-theme.mjs +5 -5
- package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
- package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
- package/clients/cli/commands/build.doc.mjs +4 -1
- package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
- package/clients/cli/commands/build.mjs +32 -47
- package/clients/cli/commands/build.playbook.test.mjs +75 -0
- package/clients/cli/commands/build.text-fields.test.mjs +40 -0
- package/clients/cli/commands/component/index.mjs +2 -7
- package/clients/cli/commands/component-package.test.mjs +46 -0
- package/clients/cli/commands/component-resolution.test.mjs +21 -0
- package/clients/cli/commands/component.test.mjs +19 -0
- package/clients/cli/commands/detail-levels.test.mjs +2 -2
- package/clients/cli/commands/discover.broken-integration.test.mjs +48 -0
- package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
- package/clients/cli/commands/discover.doc.mjs +4 -2
- package/clients/cli/commands/discover.mjs +3 -3
- package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
- package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
- package/clients/cli/commands/doctor.mjs +6 -16
- package/clients/cli/commands/doctor.test.mjs +42 -0
- package/clients/cli/commands/gap-report.doc.mjs +16 -5
- package/clients/cli/commands/gap-report.test.mjs +72 -0
- package/clients/cli/commands/hook/index.mjs +7 -17
- package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
- package/clients/cli/commands/init.doc.mjs +19 -8
- package/clients/cli/commands/init.flag-help.test.mjs +153 -0
- package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
- package/clients/cli/commands/integration-add.doc.mjs +21 -5
- package/clients/cli/commands/interactive-guard.test.mjs +101 -24
- package/clients/cli/commands/json-contract.test.mjs +33 -0
- package/clients/cli/commands/layout-check.doc.mjs +14 -3
- package/clients/cli/commands/layout-expand.doc.mjs +21 -4
- package/clients/cli/commands/layout.doc.mjs +2 -2
- package/clients/cli/commands/layout.mjs +21 -9
- package/clients/cli/commands/layout.path-help.test.mjs +33 -0
- package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
- package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
- package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
- package/clients/cli/commands/search.doc.mjs +6 -3
- package/clients/cli/commands/search.mjs +17 -7
- package/clients/cli/commands/search.test.mjs +75 -0
- package/clients/cli/commands/swizzle.doc.mjs +2 -1
- package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
- package/clients/cli/commands/template.doc.mjs +28 -6
- package/clients/cli/commands/template.flag-help.test.mjs +117 -0
- package/clients/cli/commands/template.path-help.test.mjs +40 -0
- package/clients/cli/commands/theme-add.doc.mjs +2 -1
- package/clients/cli/commands/theme-build.doc.mjs +7 -6
- package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
- package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
- package/clients/cli/commands/theme-template.doc.mjs +1 -1
- package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
- package/clients/cli/commands/upgrade.doc.mjs +17 -6
- package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
- package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
- package/clients/cli/index.mjs +13 -30
- package/clients/cli/latest-version-env.test.mjs +50 -0
- package/clients/cli/lib/cli-error.test.mjs +7 -0
- package/clients/cli/lib/component-format.mjs +9 -9
- package/clients/cli/lib/component-format.test.mjs +1 -1
- package/clients/cli/lib/define-command.mjs +32 -6
- package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
- package/clients/cli/lib/exit-codes.test.mjs +97 -0
- package/clients/cli/lib/hook-format.mjs +5 -5
- package/clients/cli/lib/json-shim.mjs +38 -2
- package/clients/cli/lib/json-shim.test.mjs +83 -0
- package/clients/cli/lib/manifest.d.ts +2 -0
- package/clients/cli/lib/manifest.mjs +22 -0
- package/clients/cli/lib/manifest.test.mjs +17 -0
- package/foundation/agent-docs/agent-docs.d.mts +4 -0
- package/foundation/agent-docs/agent-docs.mjs +69 -3
- package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
- package/foundation/config/integration-debug.test.mjs +28 -3
- package/foundation/config/project.mjs +123 -9
- package/foundation/config/project.test.mjs +126 -0
- package/foundation/discovery/template-adapter.d.mts +14 -5
- package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
- package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
- package/foundation/discovery/template-adapter.mjs +292 -25
- package/foundation/discovery/theming-targets.test.mjs +4 -0
- package/foundation/fs/module-loader.d.mts +1 -0
- package/foundation/fs/module-loader.mjs +50 -1
- package/foundation/fs/module-loader.stdout.test.mjs +332 -0
- package/foundation/fs/path-safety.d.mts +3 -2
- package/foundation/fs/path-safety.mjs +49 -19
- package/foundation/fs/path-safety.test.mjs +50 -0
- package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
- package/foundation/integrations/autolink.mjs +6 -1
- package/foundation/response/base.d.ts +8 -4
- package/foundation/response/error-codes.doc.mjs +1 -2
- package/foundation/response/error-codes.mjs +1 -1
- package/foundation/response/error-codes.test.mjs +83 -0
- package/foundation/response/json-contract.test.mjs +11 -0
- package/foundation/response/json.d.mts +4 -2
- package/foundation/response/json.mjs +8 -10
- package/foundation/response/response-types.doc.mjs +10 -10
- package/foundation/response/response-types.doc.test.mjs +158 -0
- package/foundation/xle/expand.d.mts +2 -0
- package/foundation/xle/expand.mjs +3 -2
- package/foundation/xle/expand.test.mjs +54 -0
- package/package.json +9 -9
- package/clients/cli/lib/update-check.mjs +0 -83
- package/clients/cli/lib/update-check.test.mjs +0 -137
- package/clients/cli/update-hint-commands.test.mjs +0 -54
|
@@ -1,161 +1,129 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
-
import {describe,
|
|
3
|
+
import {describe, expect, it} from 'vitest';
|
|
4
|
+
import transform from '../rename-avatar-size-scale.mjs';
|
|
4
5
|
|
|
5
6
|
async function applyTransform(source) {
|
|
6
|
-
const {default: transform} = await import('../rename-avatar-size-scale.mjs');
|
|
7
7
|
const jscodeshift = (await import('jscodeshift')).default;
|
|
8
8
|
const j = jscodeshift.withParser('tsx');
|
|
9
9
|
const api = {jscodeshift: j, stats: () => {}, report: () => {}};
|
|
10
|
-
|
|
11
|
-
const result = transform(file, api);
|
|
12
|
-
return result ?? source;
|
|
10
|
+
return transform({source, path: 'test.tsx'}, api) ?? source;
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
describe('rename-avatar-size-scale', () => {
|
|
16
|
-
it('renames every
|
|
17
|
-
const
|
|
14
|
+
it('renames every direct Avatar size literal', async () => {
|
|
15
|
+
const output =
|
|
16
|
+
await applyTransform(`import {Avatar} from '@astryxdesign/core';
|
|
18
17
|
const a = <Avatar name="A" size="tiny" />;
|
|
19
18
|
const b = <Avatar name="B" size="xsmall" />;
|
|
20
19
|
const c = <Avatar name="C" size="small" />;
|
|
21
20
|
const d = <Avatar name="D" size="medium" />;
|
|
22
|
-
const e = <Avatar name="E" size="large"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
expect(output).toContain(`size='md'`);
|
|
27
|
-
expect(output).toContain(`size='lg'`);
|
|
28
|
-
expect(output).toContain(`size='xl'`);
|
|
29
|
-
for (const old of ['tiny', 'xsmall', 'small', 'medium', 'large']) {
|
|
30
|
-
expect(output).not.toContain(`size="${old}"`);
|
|
21
|
+
const e = <Avatar name="E" size="large" />;`);
|
|
22
|
+
|
|
23
|
+
for (const size of ['xsm', 'sm', 'md', 'lg', 'xl']) {
|
|
24
|
+
expect(output).toContain(`size='${size}'`);
|
|
31
25
|
}
|
|
32
26
|
});
|
|
33
27
|
|
|
34
|
-
it('renames
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
expect(output).toContain(`size='lg'`);
|
|
39
|
-
});
|
|
28
|
+
it('renames literals inside satisfies expressions while preserving the type', async () => {
|
|
29
|
+
const output =
|
|
30
|
+
await applyTransform(`import {Avatar} from '@astryxdesign/core';
|
|
31
|
+
const view = <Avatar size={'small' satisfies AvatarSize} />;`);
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
const input = `import {Avatar} from '@astryxdesign/core';
|
|
43
|
-
const x = <Avatar name="A" size={48} />;`;
|
|
44
|
-
const output = await applyTransform(input);
|
|
45
|
-
expect(output).toContain('size={48}');
|
|
33
|
+
expect(output).toContain("{'md' satisfies AvatarSize}");
|
|
46
34
|
});
|
|
47
35
|
|
|
48
|
-
it('
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
expect(output).toContain(`size='md'`);
|
|
53
|
-
});
|
|
36
|
+
it('renames AvatarGroup and conditional literals', async () => {
|
|
37
|
+
const output =
|
|
38
|
+
await applyTransform(`import {AvatarGroup} from '@astryxdesign/core';
|
|
39
|
+
const view = <AvatarGroup size={large ? 'large' : 'small'}>{kids}</AvatarGroup>;`);
|
|
54
40
|
|
|
55
|
-
|
|
56
|
-
const sub = `import {Avatar} from '@astryxdesign/core/Avatar';
|
|
57
|
-
const x = <Avatar name="A" size="large" />;`;
|
|
58
|
-
expect(await applyTransform(sub)).toContain(`size='xl'`);
|
|
59
|
-
const legacy = `import {Avatar} from '@xds/core';
|
|
60
|
-
const x = <Avatar name="A" size="tiny" />;`;
|
|
61
|
-
expect(await applyTransform(legacy)).toContain(`size='xsm'`);
|
|
41
|
+
expect(output).toContain("large ? 'xl' : 'md'");
|
|
62
42
|
});
|
|
63
43
|
|
|
64
|
-
it('
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
});
|
|
44
|
+
it('maps import aliases and default subpath imports', async () => {
|
|
45
|
+
const output =
|
|
46
|
+
await applyTransform(`import {Avatar as Face} from '@xds/core';
|
|
47
|
+
import Faces from '@astryxdesign/core/AvatarGroup';
|
|
48
|
+
const view = <><Face size="medium" /><Faces size="large" /></>;`);
|
|
70
49
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const meta = {
|
|
74
|
-
argTypes: {size: {control: 'select', options: ['tiny', 'xsmall']}},
|
|
75
|
-
args: {size: 'tiny'},
|
|
76
|
-
};`;
|
|
77
|
-
const output = await applyTransform(input);
|
|
78
|
-
expect(output).toContain(`['xsm', 'sm']`);
|
|
79
|
-
expect(output).toContain(`size: 'xsm'`);
|
|
50
|
+
expect(output).toContain("<Face size='lg'");
|
|
51
|
+
expect(output).toContain("<Faces size='xl'");
|
|
80
52
|
});
|
|
81
53
|
|
|
82
|
-
it('
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const meta = {
|
|
87
|
-
argTypes: {size: {control: 'select', options: ['tiny', 'xsmall', 'small', 'medium', 'large']}},
|
|
88
|
-
};`;
|
|
89
|
-
const output = await applyTransform(input);
|
|
90
|
-
expect(output).toContain(`['xsm', 'sm', 'md', 'lg', 'xl']`);
|
|
91
|
-
});
|
|
54
|
+
it('ignores declaration-level type imports paired with a local Avatar', async () => {
|
|
55
|
+
const input = `import type {Avatar} from '@astryxdesign/core';
|
|
56
|
+
const Avatar = props => <div {...props} />;
|
|
57
|
+
const view = <Avatar size="small" />;`;
|
|
92
58
|
|
|
93
|
-
|
|
94
|
-
const input = `import {AvatarGroup} from '@astryxdesign/core';
|
|
95
|
-
const sizes = (['tiny', 'xsmall', 'small', 'medium', 'large'] as const).map(s => s);`;
|
|
96
|
-
const output = await applyTransform(input);
|
|
97
|
-
expect(output).toContain(`['xsm', 'sm', 'md', 'lg', 'xl']`);
|
|
59
|
+
expect(await applyTransform(input)).toBe(input);
|
|
98
60
|
});
|
|
99
61
|
|
|
100
|
-
it('
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
const output = await applyTransform(input);
|
|
106
|
-
expect(output).toContain(`['small', 'medium', 'large']`);
|
|
107
|
-
});
|
|
62
|
+
it('ignores mixed type specifiers while migrating runtime imports', async () => {
|
|
63
|
+
const output =
|
|
64
|
+
await applyTransform(`import {type Avatar, AvatarGroup} from '@astryxdesign/core';
|
|
65
|
+
const Avatar = props => <div {...props} />;
|
|
66
|
+
const view = <><Avatar size="small" /><AvatarGroup size="large" /></>;`);
|
|
108
67
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
type Props = {size: 'tiny' | 'xsmall'};`;
|
|
112
|
-
const output = await applyTransform(input);
|
|
113
|
-
expect(output).toContain(`'xsm' | 'sm'`);
|
|
68
|
+
expect(output).toContain('<Avatar size="small"');
|
|
69
|
+
expect(output).toContain("<AvatarGroup size='xl'");
|
|
114
70
|
});
|
|
115
71
|
|
|
116
|
-
|
|
117
|
-
|
|
72
|
+
it('maps namespace imports', async () => {
|
|
73
|
+
const output =
|
|
74
|
+
await applyTransform(`import * as Core from '@astryxdesign/core';
|
|
75
|
+
const view = <Core.Avatar size="small" />;`);
|
|
118
76
|
|
|
119
|
-
|
|
120
|
-
const input = `import {Avatar} from '@astryxdesign/core';
|
|
121
|
-
type TaskPriority = 'urgent' | 'high' | 'medium' | 'low' | 'none';`;
|
|
122
|
-
const output = await applyTransform(input);
|
|
123
|
-
expect(output).toContain(`'medium'`);
|
|
124
|
-
expect(output).not.toContain(`'lg'`);
|
|
77
|
+
expect(output).toContain("size='md'");
|
|
125
78
|
});
|
|
126
79
|
|
|
127
|
-
it('
|
|
80
|
+
it('leaves numeric and dynamic sizes untouched', async () => {
|
|
128
81
|
const input = `import {Avatar} from '@astryxdesign/core';
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
expect(
|
|
82
|
+
const named = 'small';
|
|
83
|
+
const a = <Avatar size={48} />;
|
|
84
|
+
const b = <Avatar size={named} />;`;
|
|
85
|
+
expect(await applyTransform(input)).toBe(input);
|
|
133
86
|
});
|
|
134
87
|
|
|
135
|
-
it('
|
|
136
|
-
// A `size: 'small'` object entry could belong to any component; renaming it
|
|
137
|
-
// blind would be unsafe, so it is left for manual migration.
|
|
88
|
+
it('leaves objects, types, arrays, and Storybook metadata untouched', async () => {
|
|
138
89
|
const input = `import {Avatar} from '@astryxdesign/core';
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
90
|
+
type Density = 'tiny' | 'xsmall' | 'small' | 'medium' | 'large';
|
|
91
|
+
const card = {size: 'small'};
|
|
92
|
+
const sizes = ['tiny', 'xsmall', 'small', 'medium', 'large'];
|
|
93
|
+
const meta = {argTypes: {size: {options: sizes}}};
|
|
94
|
+
const view = <Avatar size={card.size} />;`;
|
|
95
|
+
|
|
96
|
+
expect(await applyTransform(input)).toBe(input);
|
|
142
97
|
});
|
|
143
98
|
|
|
144
|
-
it('does not touch files
|
|
99
|
+
it('does not touch files without an Avatar import', async () => {
|
|
145
100
|
const input = `import {Badge} from '@astryxdesign/core';
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
const output = await applyTransform(input);
|
|
149
|
-
expect(output).toContain(`size="small"`);
|
|
150
|
-
expect(output).toContain(`'tiny' | 'xsmall'`);
|
|
101
|
+
const view = <Badge size="small" />;`;
|
|
102
|
+
expect(await applyTransform(input)).toBe(input);
|
|
151
103
|
});
|
|
152
104
|
|
|
153
|
-
it('
|
|
154
|
-
const input = `import {Avatar} from '@
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
105
|
+
it('ignores same-named components imported from other packages', async () => {
|
|
106
|
+
const input = `import {Avatar} from '@other/core';
|
|
107
|
+
const view = <Avatar size="small" />;`;
|
|
108
|
+
expect(await applyTransform(input)).toBe(input);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('ignores a local component that shadows an imported alias', async () => {
|
|
112
|
+
const output =
|
|
113
|
+
await applyTransform(`import {Avatar as Face} from '@xds/core';
|
|
114
|
+
const migrated = <Face size="medium" />;
|
|
115
|
+
function Local(Face) { return <Face size="large" />; }`);
|
|
116
|
+
|
|
117
|
+
expect(output).toContain("size='lg'");
|
|
118
|
+
expect(output).toContain('size="large"');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('leaves unrelated props on Avatar untouched', async () => {
|
|
122
|
+
const output =
|
|
123
|
+
await applyTransform(`import {Avatar} from '@astryxdesign/core';
|
|
124
|
+
const view = <Avatar size="small" label="small" />;`);
|
|
125
|
+
|
|
126
|
+
expect(output).toContain("size='md'");
|
|
127
|
+
expect(output).toContain('label="small"');
|
|
160
128
|
});
|
|
161
129
|
});
|
|
@@ -4,46 +4,23 @@
|
|
|
4
4
|
* @file Codemod: Rename Avatar named sizes to Icon's abbreviated scale
|
|
5
5
|
* @see https://github.com/facebook/astryx/issues/2672
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* xsmall (24px) → sm
|
|
13
|
-
* small (36px) → md ← also the new default (was `small`)
|
|
14
|
-
* medium (48px) → lg
|
|
15
|
-
* large (128px) → xl
|
|
16
|
-
*
|
|
17
|
-
* The default size shifts from `small` to `md`, but that is the SAME 36px, so
|
|
18
|
-
* call sites that relied on the default need no change.
|
|
19
|
-
*
|
|
20
|
-
* ## Precision
|
|
21
|
-
*
|
|
22
|
-
* `small`, `medium`, and `large` are common English words that appear as
|
|
23
|
-
* unrelated string/type literals (priorities, statuses, breakpoints, …). To
|
|
24
|
-
* avoid corrupting those, the three AMBIGUOUS names are renamed ONLY in a
|
|
25
|
-
* precise context: a `size` JSX attribute on a component that resolves to an
|
|
26
|
-
* Avatar/AvatarGroup import (alias-aware).
|
|
27
|
-
*
|
|
28
|
-
* `tiny` and `xsmall` are unique to Avatar's scale, so they are additionally
|
|
29
|
-
* renamed in context-blind positions (object properties keyed `size`/`*Size`,
|
|
30
|
-
* Storybook `options` arrays, and size-typed union literals) within files that
|
|
31
|
-
* import Avatar/AvatarGroup.
|
|
7
|
+
* This transform is deliberately limited to static literals written directly
|
|
8
|
+
* in the `size` prop of an imported Avatar or AvatarGroup JSX element. It does
|
|
9
|
+
* not infer through variables, helpers, objects, types, Storybook metadata, or
|
|
10
|
+
* wrappers: missing an indirect migration is safer than rewriting another
|
|
11
|
+
* component's size vocabulary.
|
|
32
12
|
*/
|
|
33
13
|
|
|
14
|
+
import {transformProp} from '../../transform-prop.mjs';
|
|
15
|
+
|
|
34
16
|
export const meta = {
|
|
35
17
|
title: 'Rename Avatar named sizes tiny/xsmall/small/medium/large → xsm/sm/md/lg/xl',
|
|
36
18
|
description:
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'`small` to `md` (both 36px). The ambiguous names small/medium/large are ' +
|
|
40
|
-
'only renamed on Avatar/AvatarGroup `size` JSX attributes; the unique ' +
|
|
41
|
-
'names tiny/xsmall are also renamed in size-keyed props, Storybook ' +
|
|
42
|
-
'options, and union types in files importing Avatar/AvatarGroup.',
|
|
19
|
+
'Renames static sizes written directly on imported Avatar and AvatarGroup ' +
|
|
20
|
+
'JSX elements. Indirect values are left unchanged for manual migration.',
|
|
43
21
|
pr: '#2672',
|
|
44
22
|
};
|
|
45
23
|
|
|
46
|
-
/** Import sources that provide the Astryx Avatar/AvatarGroup components. */
|
|
47
24
|
const IMPORT_SOURCES = new Set([
|
|
48
25
|
'@astryxdesign/core',
|
|
49
26
|
'@astryxdesign/core/Avatar',
|
|
@@ -53,10 +30,11 @@ const IMPORT_SOURCES = new Set([
|
|
|
53
30
|
'@xds/core/AvatarGroup',
|
|
54
31
|
]);
|
|
55
32
|
|
|
56
|
-
|
|
57
|
-
|
|
33
|
+
const COMPONENT_PROPS = new Map([
|
|
34
|
+
['Avatar', new Set(['size'])],
|
|
35
|
+
['AvatarGroup', new Set(['size'])],
|
|
36
|
+
]);
|
|
58
37
|
|
|
59
|
-
/** Old → new size-name mapping. */
|
|
60
38
|
const RENAMES = new Map([
|
|
61
39
|
['tiny', 'xsm'],
|
|
62
40
|
['xsmall', 'sm'],
|
|
@@ -65,46 +43,38 @@ const RENAMES = new Map([
|
|
|
65
43
|
['large', 'xl'],
|
|
66
44
|
]);
|
|
67
45
|
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
* scale; their presence in a collection proves it is an Avatar size enum.
|
|
72
|
-
*/
|
|
73
|
-
const AMBIGUOUS = new Set(['small', 'medium', 'large']);
|
|
74
|
-
|
|
75
|
-
/** Names that appear only in Avatar's scale — safe signals of Avatar sizing. */
|
|
76
|
-
const UNIQUE = new Set(['tiny', 'xsmall']);
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Rename a string-literal node if its value is an old size name.
|
|
80
|
-
* Unwraps `'small' as const`. In non-precise contexts, ambiguous names are
|
|
81
|
-
* skipped. Returns true when a rename occurred.
|
|
82
|
-
*/
|
|
83
|
-
function renameValue(/** @type {any} */ node, {precise = false} = {}) {
|
|
84
|
-
if (!node) return false;
|
|
85
|
-
const target = node.type === 'TSAsExpression' ? node.expression : node;
|
|
86
|
-
const isString =
|
|
87
|
-
target.type === 'StringLiteral' || target.type === 'Literal';
|
|
88
|
-
if (!isString || typeof target.value !== 'string') return false;
|
|
89
|
-
const replacement = RENAMES.get(target.value);
|
|
90
|
-
if (!replacement) return false;
|
|
91
|
-
if (!precise && AMBIGUOUS.has(target.value)) return false;
|
|
92
|
-
target.value = replacement;
|
|
93
|
-
if (target.raw) target.raw = undefined;
|
|
94
|
-
return true;
|
|
46
|
+
/** @param {string} name */
|
|
47
|
+
function canonicalComponentName(name) {
|
|
48
|
+
return name.startsWith('XDS') ? name.slice(3) : name;
|
|
95
49
|
}
|
|
96
50
|
|
|
97
|
-
/**
|
|
98
|
-
function
|
|
99
|
-
|
|
51
|
+
/** @param {any} node @returns {boolean} */
|
|
52
|
+
function renameStaticValue(node) {
|
|
53
|
+
if (!node) return false;
|
|
100
54
|
if (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
55
|
+
node.type === 'TSAsExpression' ||
|
|
56
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
57
|
+
node.type === 'TypeCastExpression' ||
|
|
58
|
+
node.type === 'ParenthesizedExpression'
|
|
104
59
|
) {
|
|
105
|
-
return
|
|
60
|
+
return renameStaticValue(node.expression);
|
|
106
61
|
}
|
|
107
|
-
|
|
62
|
+
if (node.type === 'ConditionalExpression') {
|
|
63
|
+
const consequentChanged = renameStaticValue(node.consequent);
|
|
64
|
+
const alternateChanged = renameStaticValue(node.alternate);
|
|
65
|
+
return consequentChanged || alternateChanged;
|
|
66
|
+
}
|
|
67
|
+
if (
|
|
68
|
+
(node.type !== 'StringLiteral' && node.type !== 'Literal') ||
|
|
69
|
+
typeof node.value !== 'string'
|
|
70
|
+
) {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
const replacement = RENAMES.get(node.value);
|
|
74
|
+
if (!replacement) return false;
|
|
75
|
+
node.value = replacement;
|
|
76
|
+
if (node.raw) node.raw = undefined;
|
|
77
|
+
return true;
|
|
108
78
|
}
|
|
109
79
|
|
|
110
80
|
/**
|
|
@@ -117,122 +87,23 @@ export default function transformer(file, api) {
|
|
|
117
87
|
const root = j(file.source);
|
|
118
88
|
let hasChanges = false;
|
|
119
89
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
let arr = node;
|
|
138
|
-
if (arr.type === 'TSAsExpression') arr = arr.expression;
|
|
139
|
-
if (arr.type !== 'ArrayExpression') return;
|
|
140
|
-
// If the array contains a name unique to Avatar's scale (tiny/xsmall), the
|
|
141
|
-
// whole collection is unambiguously the Avatar size enum, so it is safe to
|
|
142
|
-
// rename the ambiguous members too. Otherwise stay conservative.
|
|
143
|
-
const precise = arr.elements.some((/** @type {any} */ el) => UNIQUE.has(literalString(el)));
|
|
144
|
-
for (const el of arr.elements) {
|
|
145
|
-
if (renameValue(el, {precise})) hasChanges = true;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// 1. `size` JSX attributes on target components — PRECISE (component known),
|
|
150
|
-
// so all five names, including the ambiguous ones, are renamed here.
|
|
151
|
-
root.find(j.JSXOpeningElement).forEach((/** @type {any} */ path) => {
|
|
152
|
-
const name = path.node.name;
|
|
153
|
-
const componentName = name.type === 'JSXIdentifier' ? name.name : null;
|
|
154
|
-
if (!componentName || !targetLocals.has(componentName)) return;
|
|
155
|
-
|
|
156
|
-
for (const attr of path.node.attributes) {
|
|
157
|
-
if (attr.type !== 'JSXAttribute' || attr.name?.name !== 'size') continue;
|
|
158
|
-
const value = attr.value;
|
|
159
|
-
|
|
160
|
-
// size="small"
|
|
161
|
-
if (renameValue(value, {precise: true})) {
|
|
162
|
-
hasChanges = true;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (
|
|
166
|
-
value &&
|
|
167
|
-
value.type === 'JSXExpressionContainer' &&
|
|
168
|
-
value.expression
|
|
169
|
-
) {
|
|
170
|
-
// size={'small'}
|
|
171
|
-
if (renameValue(value.expression, {precise: true})) {
|
|
172
|
-
hasChanges = true;
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
// size={cond ? 'small' : 'large'}
|
|
176
|
-
if (value.expression.type === 'ConditionalExpression') {
|
|
177
|
-
if (renameValue(value.expression.consequent, {precise: true}))
|
|
178
|
-
hasChanges = true;
|
|
179
|
-
if (renameValue(value.expression.alternate, {precise: true}))
|
|
180
|
-
hasChanges = true;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// 2. Object properties keyed `size` (or `*Size`) + Storybook `options`
|
|
187
|
-
// arrays — NON-PRECISE (the component isn't known here), so only the
|
|
188
|
-
// unique names tiny/xsmall are renamed.
|
|
189
|
-
const PropertyType = j.ObjectProperty ?? j.Property;
|
|
190
|
-
root.find(PropertyType).forEach((/** @type {any} */ path) => {
|
|
191
|
-
const key = path.node.key;
|
|
192
|
-
const keyName =
|
|
193
|
-
key.type === 'Identifier'
|
|
194
|
-
? key.name
|
|
195
|
-
: key.type === 'StringLiteral' || key.type === 'Literal'
|
|
196
|
-
? key.value
|
|
197
|
-
: null;
|
|
198
|
-
if (typeof keyName !== 'string') return;
|
|
199
|
-
|
|
200
|
-
const lower = keyName.toLowerCase();
|
|
201
|
-
if (lower !== 'size' && !lower.endsWith('size')) return;
|
|
202
|
-
|
|
203
|
-
const value = path.node.value;
|
|
204
|
-
// size: 'tiny'
|
|
205
|
-
if (renameValue(value)) {
|
|
206
|
-
hasChanges = true;
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
// size: { control: 'select', options: ['tiny', ...] }
|
|
210
|
-
if (value.type === 'ObjectExpression') {
|
|
211
|
-
const optionsProp = value.properties.find(
|
|
212
|
-
(/** @type {any} */ p) => p.key && (p.key.name === 'options' || p.key.value === 'options'),
|
|
213
|
-
);
|
|
214
|
-
if (optionsProp) renameArrayElements(optionsProp.value);
|
|
215
|
-
}
|
|
216
|
-
// size: ['tiny', 'xsmall', ...]
|
|
217
|
-
renameArrayElements(value);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// 3. Union-type literals — NON-PRECISE, so only the unique names tiny/xsmall
|
|
221
|
-
// are renamed. This avoids corrupting unrelated unions that happen to use
|
|
222
|
-
// common words like 'medium' (e.g. a priority or breakpoint type) in a
|
|
223
|
-
// file that also imports Avatar.
|
|
224
|
-
root.find(j.TSLiteralType).forEach((/** @type {any} */ path) => {
|
|
225
|
-
if (renameValue(path.node.literal)) hasChanges = true;
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// 4. Standalone array literals whose members are exactly the Avatar size
|
|
229
|
-
// scale (identified by the presence of a unique name like tiny/xsmall) —
|
|
230
|
-
// e.g. `(['tiny', 'xsmall', 'small', 'medium', 'large'] as const).map(...)`
|
|
231
|
-
// used to render every size. The unique name proves the whole array is the
|
|
232
|
-
// Avatar enum, so its ambiguous members can be renamed safely.
|
|
233
|
-
root.find(j.ArrayExpression).forEach((/** @type {any} */ path) => {
|
|
234
|
-
renameArrayElements(path.node);
|
|
235
|
-
});
|
|
90
|
+
transformProp(
|
|
91
|
+
root,
|
|
92
|
+
j,
|
|
93
|
+
{
|
|
94
|
+
matchesImport: source => IMPORT_SOURCES.has(source),
|
|
95
|
+
components: COMPONENT_PROPS,
|
|
96
|
+
normalizeComponentName: canonicalComponentName,
|
|
97
|
+
},
|
|
98
|
+
propPath => {
|
|
99
|
+
const prop = propPath.node;
|
|
100
|
+
const value =
|
|
101
|
+
prop.value?.type === 'JSXExpressionContainer'
|
|
102
|
+
? prop.value.expression
|
|
103
|
+
: prop.value;
|
|
104
|
+
if (renameStaticValue(value)) hasChanges = true;
|
|
105
|
+
},
|
|
106
|
+
);
|
|
236
107
|
|
|
237
108
|
if (!hasChanges) return undefined;
|
|
238
109
|
return root.toSource({quote: 'single'});
|
|
@@ -220,4 +220,51 @@ function wrapper(useResizable) {
|
|
|
220
220
|
const output = await apply('rename-resizable-pixel-bounds', input);
|
|
221
221
|
expect(output).toBe(input);
|
|
222
222
|
});
|
|
223
|
+
|
|
224
|
+
it('renames bounds when useResizable comes from a namespace import', async () => {
|
|
225
|
+
const input = `import * as Astryx from '@astryxdesign/core';
|
|
226
|
+
const region = Astryx.useResizable({defaultSize: 240, minSizePx: 120, maxSizePx: 480});`;
|
|
227
|
+
const output = await apply('rename-resizable-pixel-bounds', input);
|
|
228
|
+
expect(output).toContain('minSize: 120');
|
|
229
|
+
expect(output).toContain('maxSize: 480');
|
|
230
|
+
expect(output).not.toContain('minSizePx');
|
|
231
|
+
expect(output).not.toContain('maxSizePx');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('leaves a namespace that does not come from Astryx alone', async () => {
|
|
235
|
+
const input = `import * as Other from 'other-resizable';
|
|
236
|
+
const region = Other.useResizable({minSizePx: 120});`;
|
|
237
|
+
const output = await apply('rename-resizable-pixel-bounds', input);
|
|
238
|
+
expect(output).toContain('minSizePx: 120');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('does not rewrite a parameter that shadows the namespace import', async () => {
|
|
242
|
+
const input = `import * as Astryx from '@astryxdesign/core';
|
|
243
|
+
function build(Astryx) {
|
|
244
|
+
return Astryx.useResizable({minSizePx: 120});
|
|
245
|
+
}`;
|
|
246
|
+
const output = await apply('rename-resizable-pixel-bounds', input);
|
|
247
|
+
expect(output).toContain('minSizePx: 120');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it.each([
|
|
251
|
+
['as const', '{defaultSize: 240, minSizePx: 120} as const'],
|
|
252
|
+
['satisfies', '{defaultSize: 240, minSizePx: 120} satisfies object'],
|
|
253
|
+
])('renames a static configuration wrapped in %s', async (_label, config) => {
|
|
254
|
+
const input = `import {useResizable} from '@astryxdesign/core/Resizable';
|
|
255
|
+
const region = useResizable(${config});`;
|
|
256
|
+
const output = await apply('rename-resizable-pixel-bounds', input);
|
|
257
|
+
expect(output).toContain('minSize: 120');
|
|
258
|
+
expect(output).not.toContain('minSizePx');
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('renames wrapped multi-region configurations', async () => {
|
|
262
|
+
const input = `import {useResizable} from '@astryxdesign/core';
|
|
263
|
+
const regions = useResizable({regions: {
|
|
264
|
+
nav: {defaultSize: 240, minSizePx: 120} as const,
|
|
265
|
+
} as const});`;
|
|
266
|
+
const output = await apply('rename-resizable-pixel-bounds', input);
|
|
267
|
+
expect(output).toContain('minSize: 120');
|
|
268
|
+
expect(output).not.toContain('minSizePx');
|
|
269
|
+
});
|
|
223
270
|
});
|