@sanity/ui-codemod 1.0.0-alpha.1
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/bin/run.js +5 -0
- package/dist/_chunks-es/addAttribute.js +11 -0
- package/dist/_chunks-es/addAttribute.js.map +1 -0
- package/dist/_chunks-es/box.mods.js +186 -0
- package/dist/_chunks-es/box.mods.js.map +1 -0
- package/dist/_chunks-es/flex.mods.js +74 -0
- package/dist/_chunks-es/flex.mods.js.map +1 -0
- package/dist/_chunks-es/getTransformPath.js +75 -0
- package/dist/_chunks-es/getTransformPath.js.map +1 -0
- package/dist/_chunks-es/grid.mods.js +110 -0
- package/dist/_chunks-es/grid.mods.js.map +1 -0
- package/dist/_chunks-es/layout-mods.js +424 -0
- package/dist/_chunks-es/layout-mods.js.map +1 -0
- package/dist/_chunks-es/transformComponent.js +359 -0
- package/dist/_chunks-es/transformComponent.js.map +1 -0
- package/dist/commands/latest/box.d.ts +47 -0
- package/dist/commands/latest/box.js +17 -0
- package/dist/commands/latest/box.js.map +1 -0
- package/dist/commands/latest/card.d.ts +47 -0
- package/dist/commands/latest/card.js +17 -0
- package/dist/commands/latest/card.js.map +1 -0
- package/dist/commands/latest/code.d.ts +47 -0
- package/dist/commands/latest/code.js +17 -0
- package/dist/commands/latest/code.js.map +1 -0
- package/dist/commands/latest/container.d.ts +47 -0
- package/dist/commands/latest/container.js +17 -0
- package/dist/commands/latest/container.js.map +1 -0
- package/dist/commands/latest/flex.d.ts +47 -0
- package/dist/commands/latest/flex.js +17 -0
- package/dist/commands/latest/flex.js.map +1 -0
- package/dist/commands/latest/grid.d.ts +47 -0
- package/dist/commands/latest/grid.js +17 -0
- package/dist/commands/latest/grid.js.map +1 -0
- package/dist/commands/latest/heading.d.ts +47 -0
- package/dist/commands/latest/heading.js +17 -0
- package/dist/commands/latest/heading.js.map +1 -0
- package/dist/commands/latest/text.d.ts +47 -0
- package/dist/commands/latest/text.js +17 -0
- package/dist/commands/latest/text.js.map +1 -0
- package/dist/transforms/latest/box/box.d.ts +18 -0
- package/dist/transforms/latest/box/box.js +101 -0
- package/dist/transforms/latest/box/box.js.map +1 -0
- package/dist/transforms/latest/card/card.d.ts +18 -0
- package/dist/transforms/latest/card/card.js +618 -0
- package/dist/transforms/latest/card/card.js.map +1 -0
- package/dist/transforms/latest/code/code.d.ts +18 -0
- package/dist/transforms/latest/code/code.js +73 -0
- package/dist/transforms/latest/code/code.js.map +1 -0
- package/dist/transforms/latest/container/container.d.ts +18 -0
- package/dist/transforms/latest/container/container.js +91 -0
- package/dist/transforms/latest/container/container.js.map +1 -0
- package/dist/transforms/latest/flex/flex.d.ts +18 -0
- package/dist/transforms/latest/flex/flex.js +19 -0
- package/dist/transforms/latest/flex/flex.js.map +1 -0
- package/dist/transforms/latest/grid/grid.d.ts +18 -0
- package/dist/transforms/latest/grid/grid.js +19 -0
- package/dist/transforms/latest/grid/grid.js.map +1 -0
- package/dist/transforms/latest/heading/heading.d.ts +18 -0
- package/dist/transforms/latest/heading/heading.js +85 -0
- package/dist/transforms/latest/heading/heading.js.map +1 -0
- package/dist/transforms/latest/text/text.d.ts +18 -0
- package/dist/transforms/latest/text/text.js +88 -0
- package/dist/transforms/latest/text/text.js.map +1 -0
- package/package.json +68 -0
- package/src/baseCommand.ts +74 -0
- package/src/commands/latest/box.ts +21 -0
- package/src/commands/latest/card.ts +21 -0
- package/src/commands/latest/code.ts +21 -0
- package/src/commands/latest/container.ts +21 -0
- package/src/commands/latest/flex.ts +21 -0
- package/src/commands/latest/grid.ts +21 -0
- package/src/commands/latest/heading.ts +21 -0
- package/src/commands/latest/inline.ts +21 -0
- package/src/commands/latest/label.ts +21 -0
- package/src/commands/latest/stack.ts +21 -0
- package/src/commands/latest/text.ts +21 -0
- package/src/constants/latest/layout-mods.ts +422 -0
- package/src/transforms/latest/box/box.mods.ts +88 -0
- package/src/transforms/latest/box/box.test.ts +204 -0
- package/src/transforms/latest/box/box.ts +104 -0
- package/src/transforms/latest/card/card.mods.ts +592 -0
- package/src/transforms/latest/card/card.test.ts +207 -0
- package/src/transforms/latest/card/card.ts +71 -0
- package/src/transforms/latest/code/code.mods.ts +57 -0
- package/src/transforms/latest/code/code.test.ts +61 -0
- package/src/transforms/latest/code/code.ts +53 -0
- package/src/transforms/latest/container/container.mods.ts +77 -0
- package/src/transforms/latest/container/container.test.ts +105 -0
- package/src/transforms/latest/container/container.ts +42 -0
- package/src/transforms/latest/flex/flex.mods.ts +73 -0
- package/src/transforms/latest/flex/flex.test.ts +80 -0
- package/src/transforms/latest/flex/flex.ts +42 -0
- package/src/transforms/latest/grid/grid.mods.ts +109 -0
- package/src/transforms/latest/grid/grid.test.ts +97 -0
- package/src/transforms/latest/grid/grid.ts +42 -0
- package/src/transforms/latest/heading/heading.mods.ts +70 -0
- package/src/transforms/latest/heading/heading.test.ts +87 -0
- package/src/transforms/latest/heading/heading.ts +53 -0
- package/src/transforms/latest/inline/inline.mods.ts +65 -0
- package/src/transforms/latest/inline/inline.test.ts +57 -0
- package/src/transforms/latest/inline/inline.ts +42 -0
- package/src/transforms/latest/label/label.mods.ts +72 -0
- package/src/transforms/latest/label/label.test.ts +113 -0
- package/src/transforms/latest/label/label.ts +67 -0
- package/src/transforms/latest/stack/stack.mods.ts +12 -0
- package/src/transforms/latest/stack/stack.test.ts +160 -0
- package/src/transforms/latest/stack/stack.ts +102 -0
- package/src/transforms/latest/text/text.mods.ts +72 -0
- package/src/transforms/latest/text/text.test.ts +113 -0
- package/src/transforms/latest/text/text.ts +57 -0
- package/src/types/AnyExpression.ts +9 -0
- package/src/types/AttributeMods.ts +16 -0
- package/src/types/BaseOptions.ts +6 -0
- package/src/utils/addAttribute.test.ts +43 -0
- package/src/utils/addAttribute.ts +26 -0
- package/src/utils/addImportSpecifier.test.ts +36 -0
- package/src/utils/addImportSpecifier.ts +42 -0
- package/src/utils/getAttribute.ts +22 -0
- package/src/utils/getAttributeExpression.ts +22 -0
- package/src/utils/getAttributeValue.ts +20 -0
- package/src/utils/getClonedImportSpecifiers.ts +29 -0
- package/src/utils/getComponentJsxNames.ts +17 -0
- package/src/utils/getComponentLocalNames.ts +69 -0
- package/src/utils/getCompositeAttrValue.ts +39 -0
- package/src/utils/getMappingArray.ts +36 -0
- package/src/utils/getMappingExpression.ts +22 -0
- package/src/utils/getMappingValue.ts +52 -0
- package/src/utils/getShorthandAttributes.ts +54 -0
- package/src/utils/getSplitImportSpecifiers.ts +40 -0
- package/src/utils/getStaticAttributeExpression.ts +35 -0
- package/src/utils/getStyleExpression.ts +37 -0
- package/src/utils/getStyledComponentAliases.ts +28 -0
- package/src/utils/getStyledComponentName.ts +52 -0
- package/src/utils/getTransformPath.ts +17 -0
- package/src/utils/insertTodoWarning.ts +48 -0
- package/src/utils/isImportBindingUsed.ts +53 -0
- package/src/utils/isValidStyleType.ts +21 -0
- package/src/utils/mergeStyle.ts +64 -0
- package/src/utils/removeUnusedImport.test.ts +77 -0
- package/src/utils/removeUnusedImport.ts +57 -0
- package/src/utils/replaceElement.test.ts +181 -0
- package/src/utils/replaceElement.ts +106 -0
- package/src/utils/replaceStyledComponent.test.ts +57 -0
- package/src/utils/replaceStyledComponent.ts +60 -0
- package/src/utils/shouldTransformComponent.test.ts +61 -0
- package/src/utils/shouldTransformComponent.ts +40 -0
- package/src/utils/testUtils.ts +54 -0
- package/src/utils/transformAttributes.test.ts +544 -0
- package/src/utils/transformAttributes.ts +248 -0
- package/src/utils/transformComponent.test.ts +45 -0
- package/src/utils/transformComponent.ts +31 -0
- package/src/utils/transformImport.test.ts +175 -0
- package/src/utils/transformImport.ts +55 -0
- package/src/utils/transformImportAlias.test.ts +40 -0
- package/src/utils/transformImportAlias.ts +49 -0
- package/src/utils/transformStyledComponents.test.ts +172 -0
- package/src/utils/transformStyledComponents.ts +58 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {type API, type FileInfo} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import type {AttributeMods} from '../types/AttributeMods'
|
|
4
|
+
import {defineInlineTest} from '../utils/testUtils'
|
|
5
|
+
import {getAttribute} from './getAttribute'
|
|
6
|
+
import {getComponentLocalNames} from './getComponentLocalNames'
|
|
7
|
+
import {replaceElement} from './replaceElement'
|
|
8
|
+
import {transformAttributes} from './transformAttributes'
|
|
9
|
+
|
|
10
|
+
const FROM_MOD: AttributeMods = {
|
|
11
|
+
insetTop: {
|
|
12
|
+
type: 'rename-only',
|
|
13
|
+
name: 'top',
|
|
14
|
+
},
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TO_MOD: AttributeMods = {
|
|
18
|
+
insetBottom: {
|
|
19
|
+
type: 'rename-only',
|
|
20
|
+
name: 'bottom',
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function transform(fileInfo: FileInfo, api: API): string {
|
|
25
|
+
const j = api.jscodeshift
|
|
26
|
+
const root = j(fileInfo.source)
|
|
27
|
+
|
|
28
|
+
replaceElement(
|
|
29
|
+
j,
|
|
30
|
+
root,
|
|
31
|
+
(attrs) => !getAttribute(attrs, 'margin'),
|
|
32
|
+
{
|
|
33
|
+
element: 'Card',
|
|
34
|
+
callback: (path) => transformAttributes(j, path, FROM_MOD, 'Warning'),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
element: 'Box',
|
|
38
|
+
callback: (path) => transformAttributes(j, path, TO_MOD, 'Warning'),
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return root.toSource()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
defineInlineTest(
|
|
46
|
+
transform,
|
|
47
|
+
{},
|
|
48
|
+
`
|
|
49
|
+
<Card margin={1} />
|
|
50
|
+
`,
|
|
51
|
+
`
|
|
52
|
+
<Card margin={1} />
|
|
53
|
+
`,
|
|
54
|
+
'preserves element if filter returns false',
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
defineInlineTest(
|
|
58
|
+
transform,
|
|
59
|
+
{},
|
|
60
|
+
`
|
|
61
|
+
<Card padding={1} />
|
|
62
|
+
`,
|
|
63
|
+
`
|
|
64
|
+
<Box padding={1} />
|
|
65
|
+
`,
|
|
66
|
+
'replaces element if filter returns true',
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
defineInlineTest(
|
|
70
|
+
transform,
|
|
71
|
+
{},
|
|
72
|
+
`
|
|
73
|
+
<Card padding={1}>Content</Card>
|
|
74
|
+
`,
|
|
75
|
+
`
|
|
76
|
+
<Box padding={1}>Content</Box>
|
|
77
|
+
`,
|
|
78
|
+
'replaces element and updates closing tag',
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
defineInlineTest(
|
|
82
|
+
transform,
|
|
83
|
+
{},
|
|
84
|
+
`
|
|
85
|
+
<Card margin={1} insetTop={1} />
|
|
86
|
+
`,
|
|
87
|
+
`
|
|
88
|
+
<Card margin={1} top={1} />
|
|
89
|
+
`,
|
|
90
|
+
'preserves element and runs from callback',
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
defineInlineTest(
|
|
94
|
+
transform,
|
|
95
|
+
{},
|
|
96
|
+
`
|
|
97
|
+
<Card padding={1} insetBottom={1} />
|
|
98
|
+
`,
|
|
99
|
+
`
|
|
100
|
+
<Box padding={1} bottom={1} />
|
|
101
|
+
`,
|
|
102
|
+
'preserves element and runs to callback',
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
defineInlineTest(
|
|
106
|
+
transform,
|
|
107
|
+
{},
|
|
108
|
+
`
|
|
109
|
+
import {Card} from '@sanity/ui'
|
|
110
|
+
|
|
111
|
+
<Card padding={1}>Content</Card>
|
|
112
|
+
`,
|
|
113
|
+
`
|
|
114
|
+
import {Box} from '@sanity/ui'
|
|
115
|
+
|
|
116
|
+
<Box padding={1}>Content</Box>
|
|
117
|
+
`,
|
|
118
|
+
'replaces element and import',
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
defineInlineTest(
|
|
122
|
+
transform,
|
|
123
|
+
{},
|
|
124
|
+
`
|
|
125
|
+
import {Card} from '@sanity/ui'
|
|
126
|
+
|
|
127
|
+
<>
|
|
128
|
+
<Card margin={1} />
|
|
129
|
+
<Card padding={1}>Content</Card>
|
|
130
|
+
</>
|
|
131
|
+
`,
|
|
132
|
+
`
|
|
133
|
+
import { Card, Box } from '@sanity/ui';
|
|
134
|
+
|
|
135
|
+
<>
|
|
136
|
+
<Card margin={1} />
|
|
137
|
+
<Box padding={1}>Content</Box>
|
|
138
|
+
</>
|
|
139
|
+
`,
|
|
140
|
+
'replaces element and combines imports',
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
function transformWithLocalNames(fileInfo: FileInfo, api: API): string {
|
|
144
|
+
const j = api.jscodeshift
|
|
145
|
+
const root = j(fileInfo.source)
|
|
146
|
+
const localNames = getComponentLocalNames(j, root, 'Card')
|
|
147
|
+
|
|
148
|
+
replaceElement(
|
|
149
|
+
j,
|
|
150
|
+
root,
|
|
151
|
+
(attrs) => !getAttribute(attrs, 'margin'),
|
|
152
|
+
{
|
|
153
|
+
element: 'Card',
|
|
154
|
+
localNames,
|
|
155
|
+
callback: (path) => transformAttributes(j, path, FROM_MOD, 'Warning'),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
element: 'Box',
|
|
159
|
+
callback: (path) => transformAttributes(j, path, TO_MOD, 'Warning'),
|
|
160
|
+
},
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return root.toSource()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
defineInlineTest(
|
|
167
|
+
transformWithLocalNames,
|
|
168
|
+
{},
|
|
169
|
+
`
|
|
170
|
+
import {Card as LegacyCard} from '@sanity/ui'
|
|
171
|
+
|
|
172
|
+
<LegacyCard padding={1}>Content</LegacyCard>
|
|
173
|
+
`,
|
|
174
|
+
`
|
|
175
|
+
// UI-CODEMOD TODO: Consider renaming LegacyCard to Box
|
|
176
|
+
import { Box as LegacyCard } from '@sanity/ui';
|
|
177
|
+
|
|
178
|
+
<LegacyCard padding={1}>Content</LegacyCard>
|
|
179
|
+
`,
|
|
180
|
+
'preserves aliased jsx name and rewrites import',
|
|
181
|
+
)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type {API, ASTPath, JSXAttribute, JSXOpeningElement, JSXSpreadAttribute} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import {addImportSpecifier} from './addImportSpecifier'
|
|
4
|
+
import {removeUnusedImport} from './removeUnusedImport'
|
|
5
|
+
import {transformImportAlias} from './transformImportAlias'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Replaces JSX component if `filter` passes. Runs callbacks and updates imports.
|
|
9
|
+
* Returns whether the AST was updated.
|
|
10
|
+
*/
|
|
11
|
+
export function replaceElement(
|
|
12
|
+
j: API['jscodeshift'],
|
|
13
|
+
root: ReturnType<API['jscodeshift']>,
|
|
14
|
+
filter: (attrs: (JSXAttribute | JSXSpreadAttribute)[]) => boolean,
|
|
15
|
+
from: {
|
|
16
|
+
element: string
|
|
17
|
+
localNames?: Iterable<string>
|
|
18
|
+
callback?: (path: ASTPath<JSXOpeningElement>) => boolean | void
|
|
19
|
+
},
|
|
20
|
+
to: {
|
|
21
|
+
element: string
|
|
22
|
+
callback?: (path: ASTPath<JSXOpeningElement>) => boolean | void
|
|
23
|
+
},
|
|
24
|
+
): boolean {
|
|
25
|
+
const names = new Set(from.localNames)
|
|
26
|
+
|
|
27
|
+
if (names.size === 0) {
|
|
28
|
+
names.add(from.element)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let hasChanges = false
|
|
32
|
+
let needsDefaultImportUpdate = false
|
|
33
|
+
const aliases = new Set<string>()
|
|
34
|
+
|
|
35
|
+
root.find(j.JSXOpeningElement).forEach((path) => {
|
|
36
|
+
const openingEl = path.node
|
|
37
|
+
const openingName = openingEl.name
|
|
38
|
+
|
|
39
|
+
if (openingName.type !== 'JSXIdentifier' || !names.has(openingName.name)) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const attrs = openingEl.attributes
|
|
44
|
+
|
|
45
|
+
if (!attrs || !filter(attrs)) {
|
|
46
|
+
if (from.callback?.(path)) {
|
|
47
|
+
hasChanges = true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const localTag = openingName.name
|
|
54
|
+
const preserveAlias = localTag !== from.element && localTag !== to.element
|
|
55
|
+
|
|
56
|
+
if (preserveAlias) {
|
|
57
|
+
if (!aliases.has(localTag)) {
|
|
58
|
+
if (
|
|
59
|
+
transformImportAlias(
|
|
60
|
+
j,
|
|
61
|
+
root,
|
|
62
|
+
from.element,
|
|
63
|
+
to.element,
|
|
64
|
+
localTag,
|
|
65
|
+
`Consider renaming ${localTag} to ${to.element}`,
|
|
66
|
+
)
|
|
67
|
+
) {
|
|
68
|
+
hasChanges = true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
aliases.add(localTag)
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
openingName.name = to.element
|
|
75
|
+
|
|
76
|
+
const parentNode = path.parent?.node
|
|
77
|
+
|
|
78
|
+
if (parentNode?.type === 'JSXElement') {
|
|
79
|
+
const closing = parentNode.closingElement
|
|
80
|
+
|
|
81
|
+
if (closing?.name.type === 'JSXIdentifier') {
|
|
82
|
+
closing.name.name = to.element
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
needsDefaultImportUpdate = true
|
|
87
|
+
hasChanges = true
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (to.callback?.(path)) {
|
|
91
|
+
hasChanges = true
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
if (needsDefaultImportUpdate) {
|
|
96
|
+
if (addImportSpecifier(j, root, from.element, to.element)) {
|
|
97
|
+
hasChanges = true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (removeUnusedImport(j, root, from.element)) {
|
|
101
|
+
hasChanges = true
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return hasChanges
|
|
106
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {type API, type FileInfo} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import {getComponentLocalNames} from './getComponentLocalNames'
|
|
4
|
+
import {replaceStyledComponent} from './replaceStyledComponent'
|
|
5
|
+
import {defineInlineTest} from './testUtils'
|
|
6
|
+
|
|
7
|
+
function transform(fileInfo: FileInfo, api: API): string {
|
|
8
|
+
const j = api.jscodeshift
|
|
9
|
+
const root = j(fileInfo.source)
|
|
10
|
+
|
|
11
|
+
replaceStyledComponent(j, root, {element: 'Card'}, 'Box')
|
|
12
|
+
|
|
13
|
+
return root.toSource()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function transformWithLocalNames(fileInfo: FileInfo, api: API): string {
|
|
17
|
+
const j = api.jscodeshift
|
|
18
|
+
const root = j(fileInfo.source)
|
|
19
|
+
const localNames = getComponentLocalNames(j, root, 'Card')
|
|
20
|
+
|
|
21
|
+
replaceStyledComponent(j, root, {element: 'Card', localNames}, 'Box')
|
|
22
|
+
|
|
23
|
+
return root.toSource()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
defineInlineTest(
|
|
27
|
+
transform,
|
|
28
|
+
{},
|
|
29
|
+
`
|
|
30
|
+
import {Card} from '@sanity/ui'
|
|
31
|
+
|
|
32
|
+
const RootCard = styled(Card)(({theme}) => {})
|
|
33
|
+
`,
|
|
34
|
+
`
|
|
35
|
+
import {Box} from '@sanity/ui'
|
|
36
|
+
|
|
37
|
+
const RootCard = styled(Box)(({theme}) => {})
|
|
38
|
+
`,
|
|
39
|
+
'replaces styled component',
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
defineInlineTest(
|
|
43
|
+
transformWithLocalNames,
|
|
44
|
+
{},
|
|
45
|
+
`
|
|
46
|
+
import {Card as LegacyCard} from '@sanity/ui'
|
|
47
|
+
|
|
48
|
+
const RootCard = styled(LegacyCard)(({theme}) => {})
|
|
49
|
+
`,
|
|
50
|
+
`
|
|
51
|
+
// UI-CODEMOD TODO: Consider renaming LegacyCard to Box
|
|
52
|
+
import { Box as LegacyCard } from '@sanity/ui';
|
|
53
|
+
|
|
54
|
+
const RootCard = styled(LegacyCard)(({theme}) => {})
|
|
55
|
+
`,
|
|
56
|
+
'preserves aliased styled import and rewrites import',
|
|
57
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type {API} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import {addImportSpecifier} from './addImportSpecifier'
|
|
4
|
+
import {removeUnusedImport} from './removeUnusedImport'
|
|
5
|
+
import {transformImportAlias} from './transformImportAlias'
|
|
6
|
+
|
|
7
|
+
export function replaceStyledComponent(
|
|
8
|
+
j: API['jscodeshift'],
|
|
9
|
+
root: ReturnType<API['jscodeshift']>,
|
|
10
|
+
from: {
|
|
11
|
+
element: string
|
|
12
|
+
localNames?: Iterable<string>
|
|
13
|
+
},
|
|
14
|
+
to: string,
|
|
15
|
+
): void {
|
|
16
|
+
const names = new Set(from.localNames)
|
|
17
|
+
|
|
18
|
+
if (names.size === 0) {
|
|
19
|
+
names.add(from.element)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let needsDefaultImportUpdate = false
|
|
23
|
+
const aliases = new Set<string>()
|
|
24
|
+
|
|
25
|
+
root
|
|
26
|
+
.find(j.CallExpression, {
|
|
27
|
+
callee: {type: 'Identifier', name: 'styled'},
|
|
28
|
+
})
|
|
29
|
+
.forEach((path) => {
|
|
30
|
+
const arg = path.node.arguments[0]
|
|
31
|
+
|
|
32
|
+
if (arg?.type !== 'Identifier' || !names.has(arg.name)) {
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (arg.name !== from.element && arg.name !== to) {
|
|
37
|
+
if (!aliases.has(arg.name)) {
|
|
38
|
+
transformImportAlias(
|
|
39
|
+
j,
|
|
40
|
+
root,
|
|
41
|
+
from.element,
|
|
42
|
+
to,
|
|
43
|
+
arg.name,
|
|
44
|
+
`Consider renaming ${arg.name} to ${to}`,
|
|
45
|
+
)
|
|
46
|
+
aliases.add(arg.name)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
arg.name = to
|
|
53
|
+
needsDefaultImportUpdate = true
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
if (needsDefaultImportUpdate) {
|
|
57
|
+
addImportSpecifier(j, root, from.element, to)
|
|
58
|
+
removeUnusedImport(j, root, from.element)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import jscodeshift from 'jscodeshift'
|
|
2
|
+
import {describe, expect, it} from 'vitest'
|
|
3
|
+
|
|
4
|
+
import {shouldTransformComponent} from './shouldTransformComponent'
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift
|
|
7
|
+
|
|
8
|
+
describe('shouldTransformComponent', () => {
|
|
9
|
+
it('returns true when local names are present', () => {
|
|
10
|
+
const root = j(`<Box />`)
|
|
11
|
+
|
|
12
|
+
expect(shouldTransformComponent(j, root, 'Box', new Set(['Box']))).toBe(true)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('returns false when there is no component usage or import rewrite', () => {
|
|
16
|
+
const root = j(`export const value = 1`)
|
|
17
|
+
|
|
18
|
+
expect(shouldTransformComponent(j, root, 'Box', new Set())).toBe(false)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('returns false when fromPackage and toPackage are the same', () => {
|
|
22
|
+
const root = j(`
|
|
23
|
+
import {Box} from '@legacy/ui'
|
|
24
|
+
<Box />
|
|
25
|
+
`)
|
|
26
|
+
|
|
27
|
+
expect(
|
|
28
|
+
shouldTransformComponent(j, root, 'Box', new Set(), {
|
|
29
|
+
fromPackage: '@legacy/ui',
|
|
30
|
+
toPackage: '@legacy/ui',
|
|
31
|
+
}),
|
|
32
|
+
).toBe(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('returns true when the component is imported from fromPackage', () => {
|
|
36
|
+
const root = j(`
|
|
37
|
+
import {Box} from '@legacy/ui'
|
|
38
|
+
`)
|
|
39
|
+
|
|
40
|
+
expect(
|
|
41
|
+
shouldTransformComponent(j, root, 'Box', new Set(), {
|
|
42
|
+
fromPackage: '@legacy/ui',
|
|
43
|
+
toPackage: '@sanity/ui',
|
|
44
|
+
}),
|
|
45
|
+
).toBe(true)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('returns false when the component is not imported from fromPackage', () => {
|
|
49
|
+
const root = j(`
|
|
50
|
+
import {Card} from '@legacy/ui'
|
|
51
|
+
<Card />
|
|
52
|
+
`)
|
|
53
|
+
|
|
54
|
+
expect(
|
|
55
|
+
shouldTransformComponent(j, root, 'Box', new Set(), {
|
|
56
|
+
fromPackage: '@legacy/ui',
|
|
57
|
+
toPackage: '@sanity/ui',
|
|
58
|
+
}),
|
|
59
|
+
).toBe(false)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type {API, Collection} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import type {BaseOptions} from '../types/BaseOptions'
|
|
4
|
+
import {getSplitImportSpecifiers} from './getSplitImportSpecifiers'
|
|
5
|
+
import {DEFAULT_UI_PACKAGE} from './transformImport'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns whether a component transform should run on this file, based on local
|
|
9
|
+
* usage or a pending import rewrite.
|
|
10
|
+
*/
|
|
11
|
+
export function shouldTransformComponent(
|
|
12
|
+
j: API['jscodeshift'],
|
|
13
|
+
root: Collection,
|
|
14
|
+
componentName: string,
|
|
15
|
+
localNames: Set<string>,
|
|
16
|
+
options?: BaseOptions,
|
|
17
|
+
): boolean {
|
|
18
|
+
if (localNames.size > 0) {
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const fromPackage = options?.fromPackage ?? DEFAULT_UI_PACKAGE
|
|
23
|
+
const toPackage = options?.toPackage ?? DEFAULT_UI_PACKAGE
|
|
24
|
+
|
|
25
|
+
if (fromPackage === toPackage) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return root.find(j.ImportDeclaration).some((path) => {
|
|
30
|
+
const node = path.node
|
|
31
|
+
|
|
32
|
+
if (node.source.value !== fromPackage) {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {componentSpecs} = getSplitImportSpecifiers(node.specifiers, componentName)
|
|
37
|
+
|
|
38
|
+
return componentSpecs.length > 0
|
|
39
|
+
})
|
|
40
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {type FileInfo, type Options, type Transform} from 'jscodeshift'
|
|
2
|
+
import {expect, it} from 'vitest'
|
|
3
|
+
|
|
4
|
+
const applyTransform = require('jscodeshift/dist/testUtils').applyTransform
|
|
5
|
+
|
|
6
|
+
export function defineInlineTest(
|
|
7
|
+
module: Transform,
|
|
8
|
+
options: Options,
|
|
9
|
+
input: FileInfo['source'],
|
|
10
|
+
expectedOutput: FileInfo['source'],
|
|
11
|
+
testName?: string,
|
|
12
|
+
) {
|
|
13
|
+
it(testName || 'transforms correctly', () => {
|
|
14
|
+
const testResult = runInlineTest(
|
|
15
|
+
module,
|
|
16
|
+
options,
|
|
17
|
+
{
|
|
18
|
+
source: input,
|
|
19
|
+
},
|
|
20
|
+
expectedOutput,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
return testResult instanceof Promise ? testResult : undefined
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function runInlineTest(
|
|
28
|
+
module: Transform,
|
|
29
|
+
options: Options,
|
|
30
|
+
input: {
|
|
31
|
+
source: FileInfo['source']
|
|
32
|
+
},
|
|
33
|
+
expectedOutput: FileInfo['source'],
|
|
34
|
+
testOptions?: Record<string, unknown>,
|
|
35
|
+
) {
|
|
36
|
+
const output = applyTransform(module, options, input, {
|
|
37
|
+
parser: 'tsx',
|
|
38
|
+
...testOptions,
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const expectation = (
|
|
42
|
+
o: Promise<string | null | undefined | void> | string | null | undefined | void,
|
|
43
|
+
) => expect(o).toEqual(expectedOutput.trim())
|
|
44
|
+
|
|
45
|
+
if (output instanceof Promise) {
|
|
46
|
+
return output.then((o) => {
|
|
47
|
+
expectation(o)
|
|
48
|
+
return o
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
expectation(output)
|
|
53
|
+
return output
|
|
54
|
+
}
|