@kaizen/components 1.80.2 → 1.80.4

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.
Files changed (74) hide show
  1. package/codemods/README.md +24 -0
  2. package/codemods/migrateV2NextToCurrent/index.ts +40 -0
  3. package/codemods/migrateV2NextToCurrent/migrateV2NextToCurrent.spec.ts +555 -0
  4. package/codemods/migrateV2NextToCurrent/migrateV2NextToCurrent.ts +104 -0
  5. package/codemods/renameV2ComponentImportsAndUsages/index.ts +30 -0
  6. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.spec.ts +390 -0
  7. package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.ts +151 -0
  8. package/codemods/utils/createModulePathTransformer.spec.ts +209 -0
  9. package/codemods/utils/createModulePathTransformer.ts +59 -0
  10. package/codemods/utils/createRenameMapFromGroups.ts +31 -0
  11. package/codemods/utils/index.ts +3 -0
  12. package/codemods/utils/updateJsxElementTagName.spec.ts +129 -0
  13. package/codemods/utils/updateJsxElementTagName.ts +56 -0
  14. package/codemods/utils/updateKaioImports.spec.ts +82 -0
  15. package/codemods/utils/updateKaioImports.ts +16 -7
  16. package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +69 -16
  17. package/dist/cjs/src/__alpha__/SingleSelect/context/SingleSelectContext.cjs +13 -0
  18. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +54 -0
  19. package/dist/cjs/src/__alpha__/SingleSelect/{SingleSelect.module.css.cjs → subcomponents/Popover/Popover.module.css.cjs} +1 -1
  20. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +94 -0
  21. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +69 -0
  22. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.cjs +12 -0
  23. package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +41 -5
  24. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +60 -10
  25. package/dist/esm/src/__alpha__/SingleSelect/context/SingleSelectContext.mjs +10 -0
  26. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +49 -0
  27. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +4 -0
  28. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +92 -0
  29. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +67 -0
  30. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.mjs +10 -0
  31. package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +43 -7
  32. package/dist/styles.css +43 -21
  33. package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +7 -9
  34. package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +12 -0
  35. package/dist/types/__alpha__/SingleSelect/context/index.d.ts +1 -0
  36. package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -1
  37. package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -1
  38. package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -1
  39. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +6 -0
  40. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/index.d.ts +1 -0
  41. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +2 -0
  42. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +4 -0
  43. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +4 -0
  44. package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.d.ts +1 -0
  45. package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +2 -1
  46. package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +1 -0
  47. package/dist/types/__alpha__/SingleSelect/types.d.ts +45 -0
  48. package/package.json +4 -4
  49. package/src/__alpha__/SingleSelect/SingleSelect.tsx +79 -14
  50. package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +5 -2
  51. package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +100 -0
  52. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +4 -4
  53. package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +21 -2
  54. package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +21 -0
  55. package/src/__alpha__/SingleSelect/context/index.ts +1 -0
  56. package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +0 -1
  57. package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +2 -1
  58. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +7 -0
  59. package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +2 -1
  60. package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +3 -1
  61. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +24 -0
  62. package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +54 -0
  63. package/src/__alpha__/SingleSelect/subcomponents/Popover/index.ts +1 -0
  64. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +2 -0
  65. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +108 -0
  66. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +75 -0
  67. package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.ts +13 -0
  68. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +1 -0
  69. package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +29 -7
  70. package/src/__alpha__/SingleSelect/subcomponents/index.ts +1 -0
  71. package/src/__alpha__/SingleSelect/types.ts +58 -0
  72. package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.module.css.mjs +0 -4
  73. package/src/__alpha__/SingleSelect/SingleSelect.module.css +0 -9
  74. package/src/__alpha__/SingleSelect/SingleSelect.spec.tsx +0 -26
@@ -0,0 +1,104 @@
1
+ import ts from 'typescript'
2
+ import {
3
+ createRenameMapFromGroups,
4
+ processImportDeclaration,
5
+ setImportToAdd,
6
+ setImportToRemove,
7
+ updateKaioImports,
8
+ type ComponentGroup,
9
+ type TagImportAttributesMap,
10
+ type UpdateKaioImportsArgs,
11
+ } from '../utils'
12
+
13
+ const componentGroups: ComponentGroup[] = [
14
+ {
15
+ components: ['Menu', 'MenuHeader', 'MenuItem', 'MenuPopover', 'MenuSection', 'MenuTrigger'],
16
+ fromModules: ['@kaizen/components/next', '@kaizen/components/v3/actions'],
17
+ toModule: '@kaizen/components',
18
+ },
19
+ {
20
+ components: ['Tabs', 'Tab', 'TabList', 'TabPanel', 'Icon', 'Focusable', 'Key'],
21
+ fromModules: ['@kaizen/components/next', '@kaizen/components/future'],
22
+ toModule: '@kaizen/components',
23
+ },
24
+ {
25
+ components: ['Tooltip', 'TooltipTrigger'],
26
+ fromModules: [
27
+ '@kaizen/components/next',
28
+ '@kaizen/components/future',
29
+ '@kaizen/components/v3/overlays',
30
+ ],
31
+ toModule: '@kaizen/components',
32
+ },
33
+ {
34
+ components: ['ReversedColors'],
35
+ fromModules: ['@kaizen/components/v3/utilities'],
36
+ toModule: '@kaizen/components',
37
+ },
38
+ {
39
+ components: ['Button', 'ButtonProps', 'ButtonsSizes', 'ButtonVariants'],
40
+ fromModules: [
41
+ '@kaizen/components/next',
42
+ '@kaizen/components/future',
43
+ '@kaizen/components/v3/actions',
44
+ ],
45
+ toModule: '@kaizen/components',
46
+ },
47
+ ]
48
+
49
+ const renameMap = createRenameMapFromGroups(componentGroups)
50
+
51
+ const modulePathMap = new Map([
52
+ ['@kaizen/components/v3/react-aria', '@kaizen/components/react-aria'],
53
+ ['@kaizen/components/v3/react-aria-components', '@kaizen/components/react-aria-components'],
54
+ ])
55
+
56
+ export const migrateV2NextToCurrent =
57
+ (_tagsMap: TagImportAttributesMap | undefined): ts.TransformerFactory<ts.SourceFile> =>
58
+ (context) =>
59
+ (rootNode) => {
60
+ const importsToRemove: UpdateKaioImportsArgs['importsToRemove'] = new Map()
61
+ const importsToAdd: UpdateKaioImportsArgs['importsToAdd'] = new Map()
62
+
63
+ const visit = (node: ts.Node): ts.Node => {
64
+ if (ts.isImportDeclaration(node)) {
65
+ const moduleSpecifier = node.moduleSpecifier
66
+ if (ts.isStringLiteral(moduleSpecifier)) {
67
+ const moduleName = moduleSpecifier.text
68
+
69
+ if (modulePathMap.has(moduleName)) {
70
+ const newModuleName = modulePathMap.get(moduleName)!
71
+ const namedBindings = node.importClause?.namedBindings
72
+
73
+ if (namedBindings && ts.isNamedImports(namedBindings)) {
74
+ namedBindings.elements.forEach((element) => {
75
+ const importName = element.name.text
76
+ const aliasName = element.propertyName?.text
77
+
78
+ setImportToRemove(importsToRemove, moduleName, aliasName ?? importName)
79
+
80
+ setImportToAdd(importsToAdd, newModuleName, {
81
+ componentName: aliasName ?? importName,
82
+ alias: aliasName ? importName : undefined,
83
+ })
84
+ })
85
+ }
86
+ } else {
87
+ processImportDeclaration(node, {
88
+ importsToRemove,
89
+ importsToAdd,
90
+ renameMap,
91
+ })
92
+ }
93
+ }
94
+ }
95
+
96
+ return ts.visitEachChild(node, visit, context)
97
+ }
98
+
99
+ const transformedNode = ts.visitNode(rootNode, visit)
100
+
101
+ return updateKaioImports({ importsToRemove, importsToAdd })(context)(
102
+ transformedNode as ts.SourceFile,
103
+ )
104
+ }
@@ -0,0 +1,30 @@
1
+ import { transformComponentsInDir } from '../utils'
2
+ import { renameV2ComponentImportsAndUsages } from './renameV2ComponentImportsAndUsages'
3
+
4
+ const run = (): void => {
5
+ console.log('~(-_- ~) Running V2 component renaming (~ -_-)~')
6
+
7
+ const targetDir = process.argv[2]
8
+ if (!targetDir) {
9
+ process.exit(1)
10
+ }
11
+
12
+ transformComponentsInDir(
13
+ targetDir,
14
+ [
15
+ 'Select',
16
+ 'SelectProps',
17
+ 'SelectOption',
18
+ 'SelectOptionGroup',
19
+ 'SelectItem',
20
+ 'SelectOptionNode',
21
+ 'SelectOptionGroupNode',
22
+ 'SelectItemNode',
23
+ 'LikertScaleLegacy',
24
+ 'TitleBlockZen',
25
+ ],
26
+ (tagNames) => [renameV2ComponentImportsAndUsages(tagNames)],
27
+ )
28
+ }
29
+
30
+ run()
@@ -0,0 +1,390 @@
1
+ import { parseJsx } from '../__tests__/utils'
2
+ import {
3
+ getKaioTagNamesMapByComponentName,
4
+ printAst,
5
+ transformSource,
6
+ type TransformSourceArgs,
7
+ } from '../utils'
8
+ import { renameV2ComponentImportsAndUsages } from './renameV2ComponentImportsAndUsages'
9
+
10
+ const transformComponents = (sourceFile: TransformSourceArgs['sourceFile']): string => {
11
+ const kaioTagNamesMap = getKaioTagNamesMapByComponentName(sourceFile, [
12
+ 'Select',
13
+ 'LikertScaleLegacy',
14
+ 'TitleBlockZen',
15
+ ])
16
+ return transformSource({
17
+ sourceFile,
18
+ transformers: [renameV2ComponentImportsAndUsages(kaioTagNamesMap!)],
19
+ })
20
+ }
21
+
22
+ describe('renameV2ComponentImportsAndUsages', () => {
23
+ describe('Select -> SigleSelect', () => {
24
+ it('should rename Select import from next to SingleSelect', () => {
25
+ const inputAst = parseJsx(`import { Select } from "@kaizen/components/next"`)
26
+ const expectedAst = parseJsx(`import { SingleSelect } from "@kaizen/components"`)
27
+
28
+ const result = transformComponents(inputAst)
29
+ expect(result).toBe(printAst(expectedAst))
30
+ })
31
+
32
+ it('should rename Select import from future to SingleSelect', () => {
33
+ const inputAst = parseJsx(`import { Select } from "@kaizen/components/future"`)
34
+ const expectedAst = parseJsx(`import { SingleSelect } from "@kaizen/components"`)
35
+
36
+ const result = transformComponents(inputAst)
37
+ expect(result).toBe(printAst(expectedAst))
38
+ })
39
+
40
+ it('should rename Select JSX element to SingleSelect', () => {
41
+ const inputAst = parseJsx(`
42
+ import { Select } from "@kaizen/components/next"
43
+
44
+ const MyComponent = () => <Select />
45
+ `)
46
+ const expectedAst = parseJsx(`
47
+ import { SingleSelect } from "@kaizen/components"
48
+
49
+ const MyComponent = () => <SingleSelect />
50
+ `)
51
+
52
+ const result = transformComponents(inputAst)
53
+ expect(result).toBe(printAst(expectedAst))
54
+ })
55
+
56
+ it('should rename Select[Type] to SingleSelect[Type] from future module', () => {
57
+ const inputAst = parseJsx(`
58
+ import type { SelectProps, SelectOption, SelectOptionGroup, SelectItem, SelectOptionNode, SelectOptionGroupNode, SelectItemNode } from "@kaizen/components/future"
59
+ `)
60
+ const expectedAst = parseJsx(`
61
+ import type { SingleSelectProps, SingleSelectOption, SingleSelectOptionGroup, SingleSelectItem, SingleSelectOptionNode, SingleSelectOptionGroupNode, SingleSelectItemNode } from "@kaizen/components"
62
+ `)
63
+
64
+ const result = transformComponents(inputAst)
65
+ expect(result).toBe(printAst(expectedAst))
66
+ })
67
+
68
+ it('should rename Select[Type] to SingleSelect[Type] from next module', () => {
69
+ const inputAst = parseJsx(`
70
+ import type { SelectProps, SelectOption, SelectOptionGroup, SelectItem, SelectOptionNode, SelectOptionGroupNode, SelectItemNode } from "@kaizen/components/next"
71
+ `)
72
+ const expectedAst = parseJsx(`
73
+ import type { SingleSelectProps, SingleSelectOption, SingleSelectOptionGroup, SingleSelectItem, SingleSelectOptionNode, SingleSelectOptionGroupNode, SingleSelectItemNode } from "@kaizen/components"
74
+ `)
75
+
76
+ const result = transformComponents(inputAst)
77
+ expect(result).toBe(printAst(expectedAst))
78
+ })
79
+
80
+ it('should preserve type-only import declaration style', () => {
81
+ const inputAst = parseJsx(`
82
+ import type { SelectOption } from "@kaizen/components/next"
83
+
84
+ const option: SelectOption = { label: 'Test', value: 'test' }
85
+ `)
86
+ const expectedAst = parseJsx(`
87
+ import type { SingleSelectOption } from "@kaizen/components"
88
+
89
+ const option: SingleSelectOption = { label: 'Test', value: 'test' }
90
+ `)
91
+
92
+ const result = transformComponents(inputAst)
93
+ expect(result).toBe(printAst(expectedAst))
94
+ })
95
+
96
+ it('should preserve inline type import style', () => {
97
+ const inputAst = parseJsx(`
98
+ import { type SelectItem, Button } from "@kaizen/components/next"
99
+
100
+ const item: SelectItem = { label: 'Test', value: 'test' }
101
+ `)
102
+ const expectedAst = parseJsx(`
103
+ import { Button } from "@kaizen/components/next"
104
+ import type { SingleSelectItem } from "@kaizen/components"
105
+
106
+ const item: SingleSelectItem = { label: 'Test', value: 'test' }
107
+ `)
108
+
109
+ const result = transformComponents(inputAst)
110
+ expect(result).toBe(printAst(expectedAst))
111
+ })
112
+
113
+ it('should handle mixed imports with Select types and other components', () => {
114
+ const inputAst = parseJsx(`
115
+ import { Select, Button } from "@kaizen/components/next"
116
+ import type { SelectProps, SelectOption } from "@kaizen/components/future"
117
+
118
+ const MyComponent = (props: SelectProps) => (
119
+ <>
120
+ <Select />
121
+ <Button>Click me</Button>
122
+ </>
123
+ )
124
+
125
+ const option: SelectOption = { label: 'Test', value: 'test' }
126
+ `)
127
+ const expectedAst = parseJsx(`
128
+ import { Button } from "@kaizen/components/next"
129
+ import { SingleSelect, type SingleSelectProps, type SingleSelectOption } from "@kaizen/components"
130
+
131
+ const MyComponent = (props: SingleSelectProps) => (
132
+ <>
133
+ <SingleSelect />
134
+ <Button>Click me</Button>
135
+ </>
136
+ )
137
+
138
+ const option: SingleSelectOption = { label: 'Test', value: 'test' }
139
+ `)
140
+
141
+ const result = transformComponents(inputAst)
142
+ expect(result).toBe(printAst(expectedAst))
143
+ })
144
+
145
+ it('should handle Select with opening and closing tags', () => {
146
+ const inputAst = parseJsx(`
147
+ import { Select } from "@kaizen/components/next"
148
+
149
+ const MyComponent = () => (
150
+ <Select>
151
+ <option value="1">Option 1</option>
152
+ </Select>
153
+ )
154
+ `)
155
+ const expectedAst = parseJsx(`
156
+ import { SingleSelect } from "@kaizen/components"
157
+
158
+ const MyComponent = () => (
159
+ <SingleSelect>
160
+ <option value="1">Option 1</option>
161
+ </SingleSelect>
162
+ )
163
+ `)
164
+
165
+ const result = transformComponents(inputAst)
166
+ expect(result).toBe(printAst(expectedAst))
167
+ })
168
+
169
+ it('should handle aliased imports', () => {
170
+ const inputAst = parseJsx(`
171
+ import { Select as MySelect } from "@kaizen/components/next"
172
+
173
+ const MyComponent = () => <MySelect />
174
+ `)
175
+ const expectedAst = parseJsx(`
176
+ import { SingleSelect as MySelect } from "@kaizen/components"
177
+
178
+ const MyComponent = () => <MySelect />
179
+ `)
180
+
181
+ const result = transformComponents(inputAst)
182
+ expect(result).toBe(printAst(expectedAst))
183
+ })
184
+
185
+ it('should handle Component.SubComponent', () => {
186
+ const inputAst = parseJsx(`
187
+ import { Select } from "@kaizen/components/next"
188
+
189
+ const MyComponent = () => (
190
+ <Select>
191
+ {({ items }) =>
192
+ items.map((item) => {
193
+ return <Select.Option />;
194
+ })
195
+ }
196
+ </Select>
197
+ );
198
+ `)
199
+ const expectedAst = parseJsx(`
200
+ import { SingleSelect } from "@kaizen/components"
201
+
202
+ const MyComponent = () => (
203
+ <SingleSelect>
204
+ {({ items }) =>
205
+ items.map((item) => {
206
+ return <SingleSelect.Option />;
207
+ })
208
+ }
209
+ </SingleSelect>
210
+ );
211
+ `)
212
+
213
+ const result = transformComponents(inputAst)
214
+ expect(result).toBe(printAst(expectedAst))
215
+ })
216
+
217
+ it('should not override local types of same name', () => {
218
+ const inputAst = parseJsx(`
219
+ type SelectProps = {
220
+ thing: string
221
+ }
222
+
223
+ const MyComponent = (props: SelectProps) => {
224
+ return <div>{props.customField}</div>
225
+ }
226
+ `)
227
+
228
+ const result = transformComponents(inputAst)
229
+ expect(result).toBe(printAst(inputAst))
230
+ })
231
+ })
232
+
233
+ describe('LikertScaleLegacy -> LikertScale', () => {
234
+ it('should rename LikertScaleLegacy import to LikertScale', () => {
235
+ const inputAst = parseJsx(`import { LikertScaleLegacy } from "@kaizen/components"`)
236
+ const expectedAst = parseJsx(`import { LikertScale } from "@kaizen/components"`)
237
+
238
+ const result = transformComponents(inputAst)
239
+ expect(result).toBe(printAst(expectedAst))
240
+ })
241
+
242
+ it('should rename LikertScaleLegacy JSX element to LikertScale', () => {
243
+ const inputAst = parseJsx(`
244
+ import { LikertScaleLegacy } from "@kaizen/components"
245
+
246
+ const MyComponent = () => <LikertScaleLegacy />
247
+ `)
248
+ const expectedAst = parseJsx(`
249
+ import { LikertScale } from "@kaizen/components"
250
+
251
+ const MyComponent = () => <LikertScale />
252
+ `)
253
+
254
+ const result = transformComponents(inputAst)
255
+ expect(result).toBe(printAst(expectedAst))
256
+ })
257
+
258
+ it('should leave LikertScaleProps type as is', () => {
259
+ const inputAst = parseJsx(`
260
+ import type { LikertScaleProps } from "@kaizen/components"
261
+
262
+ const MyComponent = (props: LikertScaleProps) => null
263
+ `)
264
+ const result = transformComponents(inputAst)
265
+ expect(result).toBe(printAst(inputAst))
266
+ })
267
+ })
268
+
269
+ describe('TitleBlockZen -> TitleBlock', () => {
270
+ it('should rename TitleBlockZen import to TitleBlock', () => {
271
+ const inputAst = parseJsx(`import { TitleBlockZen } from "@kaizen/components"`)
272
+ const expectedAst = parseJsx(`import { TitleBlock } from "@kaizen/components"`)
273
+
274
+ const result = transformComponents(inputAst)
275
+ expect(result).toBe(printAst(expectedAst))
276
+ })
277
+
278
+ it('should rename TitleBlockZen JSX element to TitleBlock', () => {
279
+ const inputAst = parseJsx(`
280
+ import { TitleBlockZen } from "@kaizen/components"
281
+
282
+ const MyComponent = () => <TitleBlockZen />
283
+ `)
284
+ const expectedAst = parseJsx(`
285
+ import { TitleBlock } from "@kaizen/components"
286
+
287
+ const MyComponent = () => <TitleBlock />
288
+ `)
289
+
290
+ const result = transformComponents(inputAst)
291
+ expect(result).toBe(printAst(expectedAst))
292
+ })
293
+
294
+ it('should leave TitleBlockProps type as is', () => {
295
+ const inputAst = parseJsx(`
296
+ import type { TitleBlockProps } from "@kaizen/components/next"
297
+
298
+ const MyComponent = (props: TitleBlockProps) => null
299
+ `)
300
+ const result = transformComponents(inputAst)
301
+ expect(result).toBe(printAst(inputAst))
302
+ })
303
+
304
+ it('should rename TitleBlockZen when it appears within a component prop', () => {
305
+ const inputAst = parseJsx(`
306
+ import { TitleBlockZen } from "@kaizen/components"
307
+
308
+ const MyComponent = () => {
309
+ return (
310
+ <Card
311
+ CardContent={
312
+ <Header>
313
+ <TitleBlockZen
314
+ navigationTabs={props.map(prop => <NavigationTab {...prop} />)}
315
+ />
316
+ </Header>
317
+ }
318
+ />
319
+ )
320
+ }
321
+ `)
322
+ const expectedAst = parseJsx(`
323
+ import { TitleBlock } from "@kaizen/components"
324
+
325
+ const MyComponent = () => {
326
+ return (
327
+ <Card
328
+ CardContent={
329
+ <Header>
330
+ <TitleBlock
331
+ navigationTabs={props.map(prop => <NavigationTab {...prop} />)}
332
+ />
333
+ </Header>
334
+ }
335
+ />
336
+ )
337
+ }
338
+ `)
339
+
340
+ const result = transformComponents(inputAst)
341
+ expect(result).toBe(printAst(expectedAst))
342
+ })
343
+ })
344
+
345
+ describe('Mixed scenarios', () => {
346
+ it('should handle multiple component imports in same file', () => {
347
+ const inputAst = parseJsx(`
348
+ import { Select } from "@kaizen/components/next"
349
+ import { LikertScaleLegacy, TitleBlockZen } from "@kaizen/components"
350
+
351
+ const MyComponent = () => (
352
+ <div>
353
+ <Select />
354
+ <LikertScaleLegacy />
355
+ <TitleBlockZen />
356
+ </div>
357
+ )
358
+ `)
359
+ const expectedAst = parseJsx(`
360
+ import { SingleSelect, LikertScale, TitleBlock } from "@kaizen/components"
361
+
362
+ const MyComponent = () => (
363
+ <div>
364
+ <SingleSelect />
365
+ <LikertScale />
366
+ <TitleBlock />
367
+ </div>
368
+ )
369
+ `)
370
+
371
+ const result = transformComponents(inputAst)
372
+ expect(result).toBe(printAst(expectedAst))
373
+ })
374
+
375
+ it('should not transform components not in the rename map', () => {
376
+ const inputAst = parseJsx(`
377
+ import { Button, Card } from "@kaizen/components"
378
+
379
+ const MyComponent = () => (
380
+ <Card>
381
+ <Button>Click me</Button>
382
+ </Card>
383
+ )
384
+ `)
385
+
386
+ const result = transformComponents(inputAst)
387
+ expect(result).toBe(printAst(inputAst))
388
+ })
389
+ })
390
+ })
@@ -0,0 +1,151 @@
1
+ import ts from 'typescript'
2
+ import {
3
+ createRenameMapFromGroups,
4
+ processImportDeclaration,
5
+ setImportToAdd,
6
+ setImportToRemove,
7
+ updateJsxElementTagName,
8
+ updateKaioImports,
9
+ type ComponentGroup,
10
+ type TagImportAttributesMap,
11
+ type UpdateKaioImportsArgs,
12
+ } from '../utils'
13
+
14
+ const componentGroups: ComponentGroup[] = [
15
+ {
16
+ components: [
17
+ ['Select', 'SingleSelect'],
18
+ ['SelectProps', 'SingleSelectProps'],
19
+ ['SelectOption', 'SingleSelectOption'],
20
+ ['SelectOptionGroup', 'SingleSelectOptionGroup'],
21
+ ['SelectItem', 'SingleSelectItem'],
22
+ ['SelectOptionNode', 'SingleSelectOptionNode'],
23
+ ['SelectOptionGroupNode', 'SingleSelectOptionGroupNode'],
24
+ ['SelectItemNode', 'SingleSelectItemNode'],
25
+ ['LikertScaleLegacy', 'LikertScale'],
26
+ ['TitleBlockZen', 'TitleBlock'],
27
+ ],
28
+ fromModules: ['@kaizen/components/next', '@kaizen/components/future', '@kaizen/components'],
29
+ toModule: '@kaizen/components',
30
+ },
31
+ ]
32
+
33
+ const renameMap = createRenameMapFromGroups(componentGroups)
34
+
35
+ export const renameV2ComponentImportsAndUsages =
36
+ (tagsMap: TagImportAttributesMap | undefined): ts.TransformerFactory<ts.SourceFile> =>
37
+ (context) =>
38
+ (rootNode) => {
39
+ const importsToRemove: UpdateKaioImportsArgs['importsToRemove'] = new Map()
40
+ const importsToAdd: UpdateKaioImportsArgs['importsToAdd'] = new Map()
41
+ const validRenames = new Set<string>()
42
+
43
+ const visit = (node: ts.Node): ts.Node => {
44
+ if (ts.isImportDeclaration(node)) {
45
+ processImportDeclaration(node, {
46
+ importsToRemove,
47
+ importsToAdd,
48
+ renameMap,
49
+ validRenames,
50
+ })
51
+ }
52
+
53
+ if (
54
+ ts.isJsxOpeningElement(node) ||
55
+ ts.isJsxSelfClosingElement(node) ||
56
+ ts.isJsxClosingElement(node)
57
+ ) {
58
+ if (ts.isPropertyAccessExpression(node.tagName)) {
59
+ const left = node.tagName.expression.getText()
60
+ const renameConfig = renameMap.get(left)
61
+
62
+ if (renameConfig) {
63
+ setImportToRemove(importsToRemove, renameConfig.fromModules[0], left)
64
+ setImportToAdd(importsToAdd, renameConfig.toModule, {
65
+ componentName: renameConfig.newName,
66
+ })
67
+
68
+ // Create temporary map for updateJsxElementTagName
69
+ const tempComponentMap = new Map([
70
+ [
71
+ left,
72
+ {
73
+ newName: renameConfig.newName,
74
+ fromModule: renameConfig.fromModules[0],
75
+ toModule: renameConfig.toModule,
76
+ },
77
+ ],
78
+ ])
79
+
80
+ return updateJsxElementTagName(
81
+ context.factory,
82
+ node,
83
+ renameConfig.newName,
84
+ tempComponentMap,
85
+ )
86
+ }
87
+
88
+ return ts.visitEachChild(node, visit, context)
89
+ }
90
+
91
+ const tagName = node.tagName.getText()
92
+ const tagImportAttributes = tagsMap?.get(tagName)
93
+ if (!tagImportAttributes) return ts.visitEachChild(node, visit, context)
94
+ const kaioComponentName = tagImportAttributes.originalName
95
+ const oldImportSource = tagImportAttributes.importModuleName
96
+ const renameConfig = renameMap.get(kaioComponentName)
97
+ if (!renameConfig) return ts.visitEachChild(node, visit, context)
98
+ if (!renameConfig.fromModules.includes(oldImportSource)) {
99
+ console.warn(
100
+ `Expected ${kaioComponentName} to be imported from one of [${renameConfig.fromModules.join(', ')}], but found ${oldImportSource}`,
101
+ )
102
+ return ts.visitEachChild(node, visit, context)
103
+ }
104
+ setImportToRemove(importsToRemove, oldImportSource, kaioComponentName)
105
+
106
+ const alias =
107
+ tagImportAttributes?.tagName !== tagImportAttributes?.originalName
108
+ ? tagImportAttributes?.tagName
109
+ : undefined
110
+ setImportToAdd(importsToAdd, renameConfig.toModule, {
111
+ componentName: renameConfig.newName,
112
+ alias,
113
+ })
114
+ const jsxElementName = alias ?? renameConfig.newName
115
+ const tempComponentMap = new Map([
116
+ [
117
+ kaioComponentName,
118
+ {
119
+ newName: renameConfig.newName,
120
+ fromModule: oldImportSource,
121
+ toModule: renameConfig.toModule,
122
+ },
123
+ ],
124
+ ])
125
+ return updateJsxElementTagName(context.factory, node, jsxElementName, tempComponentMap)
126
+ }
127
+
128
+ if (ts.isTypeReferenceNode(node)) {
129
+ const typeName = node.typeName.getText()
130
+
131
+ if (validRenames.has(typeName)) {
132
+ const renameConfig = renameMap.get(typeName)
133
+ if (renameConfig) {
134
+ return context.factory.updateTypeReferenceNode(
135
+ node,
136
+ context.factory.createIdentifier(renameConfig.newName),
137
+ node.typeArguments,
138
+ )
139
+ }
140
+ }
141
+ }
142
+
143
+ return ts.visitEachChild(node, visit, context)
144
+ }
145
+
146
+ const transformedNode = ts.visitNode(rootNode, visit)
147
+
148
+ return updateKaioImports({ importsToRemove, importsToAdd })(context)(
149
+ transformedNode as ts.SourceFile,
150
+ )
151
+ }