@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.
- package/codemods/README.md +24 -0
- package/codemods/migrateV2NextToCurrent/index.ts +40 -0
- package/codemods/migrateV2NextToCurrent/migrateV2NextToCurrent.spec.ts +555 -0
- package/codemods/migrateV2NextToCurrent/migrateV2NextToCurrent.ts +104 -0
- package/codemods/renameV2ComponentImportsAndUsages/index.ts +30 -0
- package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.spec.ts +390 -0
- package/codemods/renameV2ComponentImportsAndUsages/renameV2ComponentImportsAndUsages.ts +151 -0
- package/codemods/utils/createModulePathTransformer.spec.ts +209 -0
- package/codemods/utils/createModulePathTransformer.ts +59 -0
- package/codemods/utils/createRenameMapFromGroups.ts +31 -0
- package/codemods/utils/index.ts +3 -0
- package/codemods/utils/updateJsxElementTagName.spec.ts +129 -0
- package/codemods/utils/updateJsxElementTagName.ts +56 -0
- package/codemods/utils/updateKaioImports.spec.ts +82 -0
- package/codemods/utils/updateKaioImports.ts +16 -7
- package/dist/cjs/src/__alpha__/SingleSelect/SingleSelect.cjs +69 -16
- package/dist/cjs/src/__alpha__/SingleSelect/context/SingleSelectContext.cjs +13 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.cjs +54 -0
- package/dist/cjs/src/__alpha__/SingleSelect/{SingleSelect.module.css.cjs → subcomponents/Popover/Popover.module.css.cjs} +1 -1
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.cjs +94 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.cjs +69 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.cjs +12 -0
- package/dist/cjs/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.cjs +41 -5
- package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.mjs +60 -10
- package/dist/esm/src/__alpha__/SingleSelect/context/SingleSelectContext.mjs +10 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.mjs +49 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css.mjs +4 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.mjs +92 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.mjs +67 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.mjs +10 -0
- package/dist/esm/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.mjs +43 -7
- package/dist/styles.css +43 -21
- package/dist/types/__alpha__/SingleSelect/SingleSelect.d.ts +7 -9
- package/dist/types/__alpha__/SingleSelect/context/SingleSelectContext.d.ts +12 -0
- package/dist/types/__alpha__/SingleSelect/context/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/List/List.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/Popover.d.ts +6 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/index.d.ts +2 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.d.ts +4 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.d.ts +4 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.d.ts +2 -1
- package/dist/types/__alpha__/SingleSelect/subcomponents/index.d.ts +1 -0
- package/dist/types/__alpha__/SingleSelect/types.d.ts +45 -0
- package/package.json +4 -4
- package/src/__alpha__/SingleSelect/SingleSelect.tsx +79 -14
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.mdx +5 -2
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.spec.stories.tsx +100 -0
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stickersheet.stories.tsx +4 -4
- package/src/__alpha__/SingleSelect/_docs/SingleSelect.stories.tsx +21 -2
- package/src/__alpha__/SingleSelect/context/SingleSelectContext.tsx +21 -0
- package/src/__alpha__/SingleSelect/context/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/List/List.module.css +0 -1
- package/src/__alpha__/SingleSelect/subcomponents/List/List.tsx +2 -1
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.module.css +7 -0
- package/src/__alpha__/SingleSelect/subcomponents/ListItem/ListItem.tsx +2 -1
- package/src/__alpha__/SingleSelect/subcomponents/ListSection/ListSection.tsx +3 -1
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.module.css +24 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/Popover.tsx +54 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/index.ts +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/index.ts +2 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePopoverPositioning.ts +108 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/usePositioningStyles.ts +75 -0
- package/src/__alpha__/SingleSelect/subcomponents/Popover/utils/useSupportsAnchorPositioning.ts +13 -0
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.module.css +1 -0
- package/src/__alpha__/SingleSelect/subcomponents/Trigger/Trigger.tsx +29 -7
- package/src/__alpha__/SingleSelect/subcomponents/index.ts +1 -0
- package/src/__alpha__/SingleSelect/types.ts +58 -0
- package/dist/esm/src/__alpha__/SingleSelect/SingleSelect.module.css.mjs +0 -4
- package/src/__alpha__/SingleSelect/SingleSelect.module.css +0 -9
- package/src/__alpha__/SingleSelect/SingleSelect.spec.tsx +0 -26
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import type ts from 'typescript'
|
|
2
|
+
import { parseJsx } from '../__tests__/utils'
|
|
3
|
+
import { processImportDeclaration, type ProcessImportOptions } from './createModulePathTransformer'
|
|
4
|
+
|
|
5
|
+
const createTestOptions = (): ProcessImportOptions => ({
|
|
6
|
+
importsToRemove: new Map(),
|
|
7
|
+
importsToAdd: new Map(),
|
|
8
|
+
renameMap: new Map([
|
|
9
|
+
[
|
|
10
|
+
'OldComponent',
|
|
11
|
+
{
|
|
12
|
+
newName: 'NewComponent',
|
|
13
|
+
fromModules: ['@kaizen/components/v3/actions'],
|
|
14
|
+
toModule: '@kaizen/components',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
[
|
|
18
|
+
'Select',
|
|
19
|
+
{
|
|
20
|
+
newName: 'SingleSelect',
|
|
21
|
+
fromModules: ['@kaizen/components/next', '@kaizen/components/future'],
|
|
22
|
+
toModule: '@kaizen/components',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
]),
|
|
26
|
+
validRenames: new Set(),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const getImportDeclaration = (code: string): ts.ImportDeclaration => {
|
|
30
|
+
const sourceFile = parseJsx(code)
|
|
31
|
+
return sourceFile.statements[0] as ts.ImportDeclaration
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('processImportDeclaration', () => {
|
|
35
|
+
it('should transform basic import', () => {
|
|
36
|
+
const options = createTestOptions()
|
|
37
|
+
const importDeclaration = getImportDeclaration(
|
|
38
|
+
`import { OldComponent } from "@kaizen/components/v3/actions"`,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
processImportDeclaration(importDeclaration, options)
|
|
42
|
+
|
|
43
|
+
expect(options.importsToRemove.get('@kaizen/components/v3/actions')).toContain('OldComponent')
|
|
44
|
+
expect(options.importsToAdd.get('@kaizen/components')?.get('NewComponent')).toEqual({
|
|
45
|
+
componentName: 'NewComponent',
|
|
46
|
+
alias: undefined,
|
|
47
|
+
isTypeOnly: false,
|
|
48
|
+
})
|
|
49
|
+
expect(options.validRenames?.has('OldComponent')).toBe(true)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should transform aliased import', () => {
|
|
53
|
+
const options = createTestOptions()
|
|
54
|
+
const importDeclaration = getImportDeclaration(
|
|
55
|
+
`import { OldComponent as MyComponent } from "@kaizen/components/v3/actions"`,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
processImportDeclaration(importDeclaration, options)
|
|
59
|
+
|
|
60
|
+
expect(options.importsToRemove.get('@kaizen/components/v3/actions')).toContain('OldComponent')
|
|
61
|
+
expect(options.importsToAdd.get('@kaizen/components')?.get('NewComponent')).toEqual({
|
|
62
|
+
componentName: 'NewComponent',
|
|
63
|
+
alias: 'MyComponent',
|
|
64
|
+
isTypeOnly: false,
|
|
65
|
+
})
|
|
66
|
+
expect(options.validRenames?.has('OldComponent')).toBe(true)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('should transform type-only import at import clause level', () => {
|
|
70
|
+
const options = createTestOptions()
|
|
71
|
+
const importDeclaration = getImportDeclaration(
|
|
72
|
+
`import type { OldComponent } from "@kaizen/components/v3/actions"`,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
processImportDeclaration(importDeclaration, options)
|
|
76
|
+
|
|
77
|
+
expect(options.importsToRemove.get('@kaizen/components/v3/actions')).toContain('OldComponent')
|
|
78
|
+
expect(options.importsToAdd.get('@kaizen/components')?.get('NewComponent')).toEqual({
|
|
79
|
+
componentName: 'NewComponent',
|
|
80
|
+
alias: undefined,
|
|
81
|
+
isTypeOnly: true,
|
|
82
|
+
})
|
|
83
|
+
expect(options.validRenames?.has('OldComponent')).toBe(true)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('should transform inline type-only import', () => {
|
|
87
|
+
const options = createTestOptions()
|
|
88
|
+
const importDeclaration = getImportDeclaration(
|
|
89
|
+
`import { type OldComponent } from "@kaizen/components/v3/actions"`,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
processImportDeclaration(importDeclaration, options)
|
|
93
|
+
|
|
94
|
+
expect(options.importsToRemove.get('@kaizen/components/v3/actions')).toContain('OldComponent')
|
|
95
|
+
expect(options.importsToAdd.get('@kaizen/components')?.get('NewComponent')).toEqual({
|
|
96
|
+
componentName: 'NewComponent',
|
|
97
|
+
alias: undefined,
|
|
98
|
+
isTypeOnly: true,
|
|
99
|
+
})
|
|
100
|
+
expect(options.validRenames?.has('OldComponent')).toBe(true)
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('should handle mixed imports in single declaration', () => {
|
|
104
|
+
const options = createTestOptions()
|
|
105
|
+
const importDeclaration = getImportDeclaration(
|
|
106
|
+
`import { OldComponent, AnotherComponent } from "@kaizen/components/v3/actions"`,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
processImportDeclaration(importDeclaration, options)
|
|
110
|
+
|
|
111
|
+
expect(options.importsToRemove.get('@kaizen/components/v3/actions')).toContain('OldComponent')
|
|
112
|
+
expect(options.importsToRemove.get('@kaizen/components/v3/actions')).not.toContain(
|
|
113
|
+
'AnotherComponent',
|
|
114
|
+
)
|
|
115
|
+
expect(options.importsToAdd.get('@kaizen/components')?.get('NewComponent')).toEqual({
|
|
116
|
+
componentName: 'NewComponent',
|
|
117
|
+
alias: undefined,
|
|
118
|
+
isTypeOnly: false,
|
|
119
|
+
})
|
|
120
|
+
expect(options.validRenames?.has('OldComponent')).toBe(true)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('should ignore imports from non-targeted modules', () => {
|
|
124
|
+
const options = createTestOptions()
|
|
125
|
+
const importDeclaration = getImportDeclaration(
|
|
126
|
+
`import { OldComponent } from "some-other-library"`,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
processImportDeclaration(importDeclaration, options)
|
|
130
|
+
|
|
131
|
+
expect(options.importsToRemove.size).toBe(0)
|
|
132
|
+
expect(options.importsToAdd.size).toBe(0)
|
|
133
|
+
expect(options.validRenames?.size).toBe(0)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should ignore imports not in rename map', () => {
|
|
137
|
+
const options = createTestOptions()
|
|
138
|
+
const importDeclaration = getImportDeclaration(
|
|
139
|
+
`import { SomeOtherComponent } from "@kaizen/components/v3/actions"`,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
processImportDeclaration(importDeclaration, options)
|
|
143
|
+
|
|
144
|
+
expect(options.importsToRemove.size).toBe(0)
|
|
145
|
+
expect(options.importsToAdd.size).toBe(0)
|
|
146
|
+
expect(options.validRenames?.size).toBe(0)
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should handle multiple fromModules for same component', () => {
|
|
150
|
+
const options = createTestOptions()
|
|
151
|
+
const importDeclaration1 = getImportDeclaration(
|
|
152
|
+
`import { Select } from "@kaizen/components/next"`,
|
|
153
|
+
)
|
|
154
|
+
const importDeclaration2 = getImportDeclaration(
|
|
155
|
+
`import { Select } from "@kaizen/components/future"`,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
processImportDeclaration(importDeclaration1, options)
|
|
159
|
+
|
|
160
|
+
expect(options.importsToRemove.get('@kaizen/components/next')).toContain('Select')
|
|
161
|
+
expect(options.importsToAdd.get('@kaizen/components')?.get('SingleSelect')).toEqual({
|
|
162
|
+
componentName: 'SingleSelect',
|
|
163
|
+
alias: undefined,
|
|
164
|
+
isTypeOnly: false,
|
|
165
|
+
})
|
|
166
|
+
expect(options.validRenames?.has('Select')).toBe(true)
|
|
167
|
+
|
|
168
|
+
// Reset for second test
|
|
169
|
+
options.importsToRemove.clear()
|
|
170
|
+
options.importsToAdd.clear()
|
|
171
|
+
options.validRenames?.clear()
|
|
172
|
+
|
|
173
|
+
processImportDeclaration(importDeclaration2, options)
|
|
174
|
+
|
|
175
|
+
expect(options.importsToRemove.get('@kaizen/components/future')).toContain('Select')
|
|
176
|
+
expect(options.importsToAdd.get('@kaizen/components')?.get('SingleSelect')).toEqual({
|
|
177
|
+
componentName: 'SingleSelect',
|
|
178
|
+
alias: undefined,
|
|
179
|
+
isTypeOnly: false,
|
|
180
|
+
})
|
|
181
|
+
expect(options.validRenames?.has('Select')).toBe(true)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should handle default imports gracefully', () => {
|
|
185
|
+
const options = createTestOptions()
|
|
186
|
+
const importDeclaration = getImportDeclaration(
|
|
187
|
+
`import OldComponent from "@kaizen/components/v3/actions"`,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
processImportDeclaration(importDeclaration, options)
|
|
191
|
+
|
|
192
|
+
expect(options.importsToRemove.size).toBe(0)
|
|
193
|
+
expect(options.importsToAdd.size).toBe(0)
|
|
194
|
+
expect(options.validRenames?.size).toBe(0)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('should handle namespace imports gracefully', () => {
|
|
198
|
+
const options = createTestOptions()
|
|
199
|
+
const importDeclaration = getImportDeclaration(
|
|
200
|
+
`import * as Components from "@kaizen/components/v3/actions"`,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
processImportDeclaration(importDeclaration, options)
|
|
204
|
+
|
|
205
|
+
expect(options.importsToRemove.size).toBe(0)
|
|
206
|
+
expect(options.importsToAdd.size).toBe(0)
|
|
207
|
+
expect(options.validRenames?.size).toBe(0)
|
|
208
|
+
})
|
|
209
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
import { setImportToAdd, setImportToRemove, type UpdateKaioImportsArgs } from './updateKaioImports'
|
|
3
|
+
|
|
4
|
+
export type ModuleRenameConfig = {
|
|
5
|
+
newName: string
|
|
6
|
+
fromModules: string[]
|
|
7
|
+
toModule: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type ProcessImportOptions = {
|
|
11
|
+
importsToRemove: NonNullable<UpdateKaioImportsArgs['importsToRemove']>
|
|
12
|
+
importsToAdd: NonNullable<UpdateKaioImportsArgs['importsToAdd']>
|
|
13
|
+
renameMap: Map<string, ModuleRenameConfig>
|
|
14
|
+
validRenames?: Set<string>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Takes an import declaration and transforms it as specified in the options.renameMap
|
|
19
|
+
*
|
|
20
|
+
* @param node - The import declaration to process
|
|
21
|
+
* @param options - Configuration for import transformations
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // For an import like: import { OldButton } from '@kaizen/legacy'
|
|
26
|
+
* // With renameMap: { 'OldButton': { newName: 'Button', fromModules: ['@kaizen/legacy'], toModule: '@kaizen/button' } }
|
|
27
|
+
* // Results in: import { Button } from '@kaizen/button'
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export const processImportDeclaration = (
|
|
31
|
+
node: ts.ImportDeclaration,
|
|
32
|
+
options: ProcessImportOptions,
|
|
33
|
+
): void => {
|
|
34
|
+
const { importsToRemove, importsToAdd, renameMap, validRenames } = options
|
|
35
|
+
const moduleSpecifier = node.moduleSpecifier.getText().slice(1, -1)
|
|
36
|
+
|
|
37
|
+
const importClause = node.importClause
|
|
38
|
+
if (importClause?.namedBindings && ts.isNamedImports(importClause.namedBindings)) {
|
|
39
|
+
importClause.namedBindings.elements.forEach((importSpecifier) => {
|
|
40
|
+
const importName = importSpecifier.propertyName?.getText() ?? importSpecifier.name?.getText()
|
|
41
|
+
|
|
42
|
+
const renameConfig = renameMap.get(importName)
|
|
43
|
+
if (renameConfig?.fromModules.includes(moduleSpecifier)) {
|
|
44
|
+
validRenames?.add(importName)
|
|
45
|
+
setImportToRemove(importsToRemove, moduleSpecifier, importName)
|
|
46
|
+
|
|
47
|
+
const alias = importSpecifier.propertyName?.getText()
|
|
48
|
+
? importSpecifier.name?.getText()
|
|
49
|
+
: undefined
|
|
50
|
+
|
|
51
|
+
setImportToAdd(importsToAdd, renameConfig.toModule, {
|
|
52
|
+
componentName: renameConfig.newName,
|
|
53
|
+
alias,
|
|
54
|
+
isTypeOnly: importSpecifier.isTypeOnly || importClause.isTypeOnly,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ModuleRenameConfig } from './createModulePathTransformer'
|
|
2
|
+
|
|
3
|
+
export type ComponentEntry = string | [string, string] // [oldName, newName]
|
|
4
|
+
|
|
5
|
+
export type ComponentGroup = {
|
|
6
|
+
components: ComponentEntry[]
|
|
7
|
+
fromModules: string[]
|
|
8
|
+
toModule: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const createRenameMapFromGroups = (
|
|
12
|
+
groups: ComponentGroup[],
|
|
13
|
+
): Map<string, ModuleRenameConfig> => {
|
|
14
|
+
const renameMap = new Map<string, ModuleRenameConfig>()
|
|
15
|
+
|
|
16
|
+
groups.forEach(({ components, fromModules, toModule }) => {
|
|
17
|
+
components.forEach((componentEntry) => {
|
|
18
|
+
const [oldName, newName] = Array.isArray(componentEntry)
|
|
19
|
+
? componentEntry
|
|
20
|
+
: [componentEntry, componentEntry]
|
|
21
|
+
|
|
22
|
+
renameMap.set(oldName, {
|
|
23
|
+
newName,
|
|
24
|
+
fromModules,
|
|
25
|
+
toModule,
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
return renameMap
|
|
31
|
+
}
|
package/codemods/utils/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './createJsxElementWithChildren'
|
|
2
2
|
export * from './createProp'
|
|
3
|
+
export * from './createRenameMapFromGroups'
|
|
3
4
|
export * from './getPropValueText'
|
|
4
5
|
export * from './getKaioTagName'
|
|
5
6
|
export * from './migrateStringProp'
|
|
@@ -9,3 +10,5 @@ export * from './transformSource'
|
|
|
9
10
|
export * from './updateKaioImports'
|
|
10
11
|
export * from './updateJsxElementWithNewProps'
|
|
11
12
|
export * from './transformV1ButtonPropsToButtonOrLinkButton'
|
|
13
|
+
export * from './updateJsxElementTagName'
|
|
14
|
+
export * from './createModulePathTransformer'
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
import { parseJsx } from '../__tests__/utils'
|
|
3
|
+
import { updateJsxElementTagName, type ComponentRenameConfig } from './updateJsxElementTagName'
|
|
4
|
+
|
|
5
|
+
const componentRenameMap = new Map<string, ComponentRenameConfig>([
|
|
6
|
+
[
|
|
7
|
+
'Pancakes',
|
|
8
|
+
{
|
|
9
|
+
newName: 'Beer',
|
|
10
|
+
fromModule: '@kaizen/components/next',
|
|
11
|
+
toModule: '@kaizen/components',
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
['Bart', { newName: 'Bender', fromModule: '@kaizen/components', toModule: '@kaizen/components' }],
|
|
15
|
+
[
|
|
16
|
+
'EatMyShorts',
|
|
17
|
+
{ newName: 'MeatBags', fromModule: '@kaizen/components', toModule: '@kaizen/components' },
|
|
18
|
+
],
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
describe('updateJsxElementTagName()', () => {
|
|
22
|
+
const factory = ts.factory
|
|
23
|
+
|
|
24
|
+
it('should ignore nodes not in componentMap', () => {
|
|
25
|
+
const elem = factory.createJsxSelfClosingElement(
|
|
26
|
+
factory.createIdentifier('Cowabunga'),
|
|
27
|
+
undefined,
|
|
28
|
+
factory.createJsxAttributes([]),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
const result = updateJsxElementTagName(factory, elem, 'Cowabunga', componentRenameMap)
|
|
32
|
+
|
|
33
|
+
expect(ts.isJsxSelfClosingElement(result)).toBe(true)
|
|
34
|
+
expect((result.tagName as ts.Identifier).text).toBe('Cowabunga')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should update self closing tag', () => {
|
|
38
|
+
const elem = factory.createJsxSelfClosingElement(
|
|
39
|
+
factory.createIdentifier('Pancakes'),
|
|
40
|
+
undefined,
|
|
41
|
+
factory.createJsxAttributes([]),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const result = updateJsxElementTagName(factory, elem, 'Beer', componentRenameMap)
|
|
45
|
+
|
|
46
|
+
expect(ts.isJsxSelfClosingElement(result)).toBe(true)
|
|
47
|
+
expect((result.tagName as ts.Identifier).text).toBe('Beer')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should handle Component.SubComponent when in Component is found in componentMap', () => {
|
|
51
|
+
const source = `<Bart.Shorts />`
|
|
52
|
+
const sourceFile = parseJsx(source)
|
|
53
|
+
const statement = sourceFile.statements[0] as ts.ExpressionStatement
|
|
54
|
+
const elem = statement.expression as ts.JsxSelfClosingElement
|
|
55
|
+
|
|
56
|
+
const result = updateJsxElementTagName(factory, elem, 'ignored', componentRenameMap)
|
|
57
|
+
|
|
58
|
+
expect(ts.isJsxSelfClosingElement(result)).toBe(true)
|
|
59
|
+
expect(ts.isPropertyAccessExpression(result.tagName)).toBe(true)
|
|
60
|
+
|
|
61
|
+
const tagName = result.tagName as ts.PropertyAccessExpression
|
|
62
|
+
expect((tagName.expression as ts.Identifier).text).toBe('Bender')
|
|
63
|
+
expect(tagName.name.text).toBe('Shorts')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should ignore Component.SubComponent when in Component is not in componentMap', () => {
|
|
67
|
+
const source = `<Not.Here />`
|
|
68
|
+
const sourceFile = parseJsx(source)
|
|
69
|
+
const statement = sourceFile.statements[0] as ts.ExpressionStatement
|
|
70
|
+
const elem = statement.expression as ts.JsxSelfClosingElement
|
|
71
|
+
|
|
72
|
+
const result = updateJsxElementTagName(factory, elem, 'ignored', componentRenameMap)
|
|
73
|
+
|
|
74
|
+
expect(ts.isJsxSelfClosingElement(result)).toBe(true)
|
|
75
|
+
expect(ts.isPropertyAccessExpression(result.tagName)).toBe(true)
|
|
76
|
+
|
|
77
|
+
const tagName = result.tagName as ts.PropertyAccessExpression
|
|
78
|
+
expect((tagName.expression as ts.Identifier).text).toBe('Not')
|
|
79
|
+
expect(tagName.name.text).toBe('Here')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('should update JSX opening element tag name', () => {
|
|
83
|
+
const openingElement = factory.createJsxOpeningElement(
|
|
84
|
+
factory.createIdentifier('Pancakes'),
|
|
85
|
+
undefined,
|
|
86
|
+
factory.createJsxAttributes([]),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const result = updateJsxElementTagName(factory, openingElement, 'Pizza', componentRenameMap)
|
|
90
|
+
|
|
91
|
+
expect(ts.isJsxOpeningElement(result)).toBe(true)
|
|
92
|
+
expect((result.tagName as ts.Identifier).text).toBe('Pizza')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should update JSX closing element tag name', () => {
|
|
96
|
+
const closingElement = factory.createJsxClosingElement(factory.createIdentifier('EatMyShorts'))
|
|
97
|
+
|
|
98
|
+
const result = updateJsxElementTagName(factory, closingElement, 'MeatBags', componentRenameMap)
|
|
99
|
+
|
|
100
|
+
expect(ts.isJsxClosingElement(result)).toBe(true)
|
|
101
|
+
expect((result.tagName as ts.Identifier).text).toBe('MeatBags')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('should preserve attributes when updating JSX elements', () => {
|
|
105
|
+
const attributes = factory.createJsxAttributes([
|
|
106
|
+
factory.createJsxAttribute(
|
|
107
|
+
factory.createIdentifier('className'),
|
|
108
|
+
factory.createStringLiteral('test-class'),
|
|
109
|
+
),
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
const element = factory.createJsxSelfClosingElement(
|
|
113
|
+
factory.createIdentifier('Pancakes'),
|
|
114
|
+
undefined,
|
|
115
|
+
attributes,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
const result = updateJsxElementTagName(factory, element, 'Pizza', componentRenameMap)
|
|
119
|
+
|
|
120
|
+
expect(ts.isJsxSelfClosingElement(result)).toBe(true)
|
|
121
|
+
expect((result.tagName as ts.Identifier).text).toBe('Pizza')
|
|
122
|
+
expect((result as ts.JsxSelfClosingElement).attributes.properties.length).toBe(1)
|
|
123
|
+
|
|
124
|
+
const attribute = (result as ts.JsxSelfClosingElement).attributes
|
|
125
|
+
.properties[0] as ts.JsxAttribute
|
|
126
|
+
expect((attribute.name as ts.Identifier).text).toBe('className')
|
|
127
|
+
expect((attribute.initializer as ts.StringLiteral).text).toBe('test-class')
|
|
128
|
+
})
|
|
129
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import ts from 'typescript'
|
|
2
|
+
|
|
3
|
+
export type ComponentRenameConfig = {
|
|
4
|
+
newName: string
|
|
5
|
+
fromModule: string
|
|
6
|
+
toModule: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const updateJsxElementTagName = (
|
|
10
|
+
factory: ts.NodeFactory,
|
|
11
|
+
node: ts.JsxOpeningElement | ts.JsxClosingElement | ts.JsxSelfClosingElement,
|
|
12
|
+
newTagName: string,
|
|
13
|
+
componentRenameMap: Map<string, ComponentRenameConfig>,
|
|
14
|
+
): ts.JsxOpeningElement | ts.JsxClosingElement | ts.JsxSelfClosingElement => {
|
|
15
|
+
let newTagNameExpr: ts.JsxTagNameExpression
|
|
16
|
+
|
|
17
|
+
if (ts.isPropertyAccessExpression(node.tagName)) {
|
|
18
|
+
const baseComponentName = node.tagName.expression.getText()
|
|
19
|
+
const rename = componentRenameMap.get(baseComponentName)
|
|
20
|
+
|
|
21
|
+
if (rename) {
|
|
22
|
+
newTagNameExpr = factory.createPropertyAccessExpression(
|
|
23
|
+
factory.createIdentifier(rename.newName),
|
|
24
|
+
node.tagName.name,
|
|
25
|
+
) as ts.JsxTagNameExpression
|
|
26
|
+
} else {
|
|
27
|
+
newTagNameExpr = node.tagName
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
newTagNameExpr = factory.createIdentifier(newTagName)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (ts.isJsxSelfClosingElement(node)) {
|
|
34
|
+
return factory.updateJsxSelfClosingElement(
|
|
35
|
+
node,
|
|
36
|
+
newTagNameExpr,
|
|
37
|
+
node.typeArguments,
|
|
38
|
+
node.attributes,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (ts.isJsxOpeningElement(node)) {
|
|
43
|
+
return factory.updateJsxOpeningElement(
|
|
44
|
+
node,
|
|
45
|
+
newTagNameExpr,
|
|
46
|
+
node.typeArguments,
|
|
47
|
+
node.attributes,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (ts.isJsxClosingElement(node)) {
|
|
52
|
+
return factory.updateJsxClosingElement(node, newTagNameExpr)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return node
|
|
56
|
+
}
|
|
@@ -200,6 +200,88 @@ describe('updateKaioImports()', () => {
|
|
|
200
200
|
}),
|
|
201
201
|
).toEqual(printAst(outputAst))
|
|
202
202
|
})
|
|
203
|
+
|
|
204
|
+
describe('type-only imports', () => {
|
|
205
|
+
it('creates a new type-only import declaration', () => {
|
|
206
|
+
const inputAst = parseJsx(`
|
|
207
|
+
import { Well } from "@kaizen/components"
|
|
208
|
+
`)
|
|
209
|
+
const outputAst = parseJsx(`
|
|
210
|
+
import { Well } from "@kaizen/components"
|
|
211
|
+
import type { Card } from "@kaizen/components/next"
|
|
212
|
+
`)
|
|
213
|
+
expect(
|
|
214
|
+
transformInput(inputAst)({
|
|
215
|
+
importsToAdd: new Map([
|
|
216
|
+
[
|
|
217
|
+
'@kaizen/components/next',
|
|
218
|
+
new Map([['Card', { componentName: 'Card', isTypeOnly: true }]]),
|
|
219
|
+
],
|
|
220
|
+
]),
|
|
221
|
+
}),
|
|
222
|
+
).toEqual(printAst(outputAst))
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('adds type-only import to existing regular imports', () => {
|
|
226
|
+
const inputAst = parseJsx(`
|
|
227
|
+
import { Select } from "@kaizen/components/next"
|
|
228
|
+
`)
|
|
229
|
+
const outputAst = parseJsx(`
|
|
230
|
+
import { Select, type Card } from "@kaizen/components/next"
|
|
231
|
+
`)
|
|
232
|
+
expect(
|
|
233
|
+
transformInput(inputAst)({
|
|
234
|
+
importsToAdd: new Map([
|
|
235
|
+
[
|
|
236
|
+
'@kaizen/components/next',
|
|
237
|
+
new Map([['Card', { componentName: 'Card', isTypeOnly: true }]]),
|
|
238
|
+
],
|
|
239
|
+
]),
|
|
240
|
+
}),
|
|
241
|
+
).toEqual(printAst(outputAst))
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('adds type-only import to existing type-only imports', () => {
|
|
245
|
+
const inputAst = parseJsx(`
|
|
246
|
+
import type { CardProps } from "@kaizen/components/next"
|
|
247
|
+
`)
|
|
248
|
+
const outputAst = parseJsx(`
|
|
249
|
+
import type { CardProps, type ButtonProps } from "@kaizen/components/next"
|
|
250
|
+
`)
|
|
251
|
+
expect(
|
|
252
|
+
transformInput(inputAst)({
|
|
253
|
+
importsToAdd: new Map([
|
|
254
|
+
[
|
|
255
|
+
'@kaizen/components/next',
|
|
256
|
+
new Map([['ButtonProps', { componentName: 'ButtonProps', isTypeOnly: true }]]),
|
|
257
|
+
],
|
|
258
|
+
]),
|
|
259
|
+
}),
|
|
260
|
+
).toEqual(printAst(outputAst))
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
it('adds mix of type-only and regular imports', () => {
|
|
264
|
+
const inputAst = parseJsx(`
|
|
265
|
+
import { Select } from "@kaizen/components/next"
|
|
266
|
+
`)
|
|
267
|
+
const outputAst = parseJsx(`
|
|
268
|
+
import { Select, type CardProps, Button } from "@kaizen/components/next"
|
|
269
|
+
`)
|
|
270
|
+
expect(
|
|
271
|
+
transformInput(inputAst)({
|
|
272
|
+
importsToAdd: new Map([
|
|
273
|
+
[
|
|
274
|
+
'@kaizen/components/next',
|
|
275
|
+
new Map([
|
|
276
|
+
['CardProps', { componentName: 'CardProps', isTypeOnly: true }],
|
|
277
|
+
['Button', { componentName: 'Button', isTypeOnly: false }],
|
|
278
|
+
]),
|
|
279
|
+
],
|
|
280
|
+
]),
|
|
281
|
+
}),
|
|
282
|
+
).toEqual(printAst(outputAst))
|
|
283
|
+
})
|
|
284
|
+
})
|
|
203
285
|
})
|
|
204
286
|
})
|
|
205
287
|
})
|
|
@@ -34,7 +34,7 @@ const removeNamedImports = (
|
|
|
34
34
|
return node
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
type NewImportAttributes = { componentName: string; alias?: string }
|
|
37
|
+
type NewImportAttributes = { componentName: string; alias?: string; isTypeOnly?: boolean }
|
|
38
38
|
type ImportsToAdd = Map<string, NewImportAttributes>
|
|
39
39
|
|
|
40
40
|
const createImportDeclaration = (
|
|
@@ -42,9 +42,12 @@ const createImportDeclaration = (
|
|
|
42
42
|
importsToAdd: ImportsToAdd,
|
|
43
43
|
moduleSpecifier: string,
|
|
44
44
|
): ts.ImportDeclaration => {
|
|
45
|
-
const
|
|
45
|
+
const imports = Array.from(importsToAdd.values())
|
|
46
|
+
const allTypeOnly = imports.every(({ isTypeOnly }) => isTypeOnly)
|
|
47
|
+
|
|
48
|
+
const namedImports = imports.map(({ componentName, alias, isTypeOnly }) =>
|
|
46
49
|
factory.createImportSpecifier(
|
|
47
|
-
false,
|
|
50
|
+
allTypeOnly ? false : (isTypeOnly ?? false),
|
|
48
51
|
alias ? factory.createIdentifier(componentName) : undefined,
|
|
49
52
|
factory.createIdentifier(alias ?? componentName),
|
|
50
53
|
),
|
|
@@ -52,7 +55,7 @@ const createImportDeclaration = (
|
|
|
52
55
|
|
|
53
56
|
return factory.createImportDeclaration(
|
|
54
57
|
undefined,
|
|
55
|
-
factory.createImportClause(
|
|
58
|
+
factory.createImportClause(allTypeOnly, undefined, factory.createNamedImports(namedImports)),
|
|
56
59
|
factory.createStringLiteral(moduleSpecifier),
|
|
57
60
|
)
|
|
58
61
|
}
|
|
@@ -75,9 +78,13 @@ const updateNamedImports = (
|
|
|
75
78
|
})
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
Array.from(importsToAdd.values())
|
|
81
|
+
const newImports = Array.from(importsToAdd.values())
|
|
82
|
+
const allNewTypeOnly = newImports.every(({ isTypeOnly }) => isTypeOnly)
|
|
83
|
+
const hasExistingImports = importSpecifiers.length > 0
|
|
84
|
+
|
|
85
|
+
newImports.forEach(({ alias, componentName, isTypeOnly }) => {
|
|
79
86
|
const newImport = factory.createImportSpecifier(
|
|
80
|
-
false,
|
|
87
|
+
!allNewTypeOnly || hasExistingImports ? (isTypeOnly ?? false) : false,
|
|
81
88
|
alias ? factory.createIdentifier(componentName) : undefined,
|
|
82
89
|
factory.createIdentifier(alias ?? componentName),
|
|
83
90
|
)
|
|
@@ -87,12 +94,14 @@ const updateNamedImports = (
|
|
|
87
94
|
}
|
|
88
95
|
})
|
|
89
96
|
|
|
97
|
+
const isModuleLevelTypeOnly = allNewTypeOnly && !hasExistingImports
|
|
98
|
+
|
|
90
99
|
return factory.updateImportDeclaration(
|
|
91
100
|
node,
|
|
92
101
|
node.modifiers,
|
|
93
102
|
factory.updateImportClause(
|
|
94
103
|
node.importClause,
|
|
95
|
-
node.importClause.isTypeOnly,
|
|
104
|
+
isModuleLevelTypeOnly || node.importClause.isTypeOnly,
|
|
96
105
|
node.importClause.name,
|
|
97
106
|
factory.createNamedImports(importSpecifiers),
|
|
98
107
|
),
|