@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.
Files changed (157) hide show
  1. package/bin/run.js +5 -0
  2. package/dist/_chunks-es/addAttribute.js +11 -0
  3. package/dist/_chunks-es/addAttribute.js.map +1 -0
  4. package/dist/_chunks-es/box.mods.js +186 -0
  5. package/dist/_chunks-es/box.mods.js.map +1 -0
  6. package/dist/_chunks-es/flex.mods.js +74 -0
  7. package/dist/_chunks-es/flex.mods.js.map +1 -0
  8. package/dist/_chunks-es/getTransformPath.js +75 -0
  9. package/dist/_chunks-es/getTransformPath.js.map +1 -0
  10. package/dist/_chunks-es/grid.mods.js +110 -0
  11. package/dist/_chunks-es/grid.mods.js.map +1 -0
  12. package/dist/_chunks-es/layout-mods.js +424 -0
  13. package/dist/_chunks-es/layout-mods.js.map +1 -0
  14. package/dist/_chunks-es/transformComponent.js +359 -0
  15. package/dist/_chunks-es/transformComponent.js.map +1 -0
  16. package/dist/commands/latest/box.d.ts +47 -0
  17. package/dist/commands/latest/box.js +17 -0
  18. package/dist/commands/latest/box.js.map +1 -0
  19. package/dist/commands/latest/card.d.ts +47 -0
  20. package/dist/commands/latest/card.js +17 -0
  21. package/dist/commands/latest/card.js.map +1 -0
  22. package/dist/commands/latest/code.d.ts +47 -0
  23. package/dist/commands/latest/code.js +17 -0
  24. package/dist/commands/latest/code.js.map +1 -0
  25. package/dist/commands/latest/container.d.ts +47 -0
  26. package/dist/commands/latest/container.js +17 -0
  27. package/dist/commands/latest/container.js.map +1 -0
  28. package/dist/commands/latest/flex.d.ts +47 -0
  29. package/dist/commands/latest/flex.js +17 -0
  30. package/dist/commands/latest/flex.js.map +1 -0
  31. package/dist/commands/latest/grid.d.ts +47 -0
  32. package/dist/commands/latest/grid.js +17 -0
  33. package/dist/commands/latest/grid.js.map +1 -0
  34. package/dist/commands/latest/heading.d.ts +47 -0
  35. package/dist/commands/latest/heading.js +17 -0
  36. package/dist/commands/latest/heading.js.map +1 -0
  37. package/dist/commands/latest/text.d.ts +47 -0
  38. package/dist/commands/latest/text.js +17 -0
  39. package/dist/commands/latest/text.js.map +1 -0
  40. package/dist/transforms/latest/box/box.d.ts +18 -0
  41. package/dist/transforms/latest/box/box.js +101 -0
  42. package/dist/transforms/latest/box/box.js.map +1 -0
  43. package/dist/transforms/latest/card/card.d.ts +18 -0
  44. package/dist/transforms/latest/card/card.js +618 -0
  45. package/dist/transforms/latest/card/card.js.map +1 -0
  46. package/dist/transforms/latest/code/code.d.ts +18 -0
  47. package/dist/transforms/latest/code/code.js +73 -0
  48. package/dist/transforms/latest/code/code.js.map +1 -0
  49. package/dist/transforms/latest/container/container.d.ts +18 -0
  50. package/dist/transforms/latest/container/container.js +91 -0
  51. package/dist/transforms/latest/container/container.js.map +1 -0
  52. package/dist/transforms/latest/flex/flex.d.ts +18 -0
  53. package/dist/transforms/latest/flex/flex.js +19 -0
  54. package/dist/transforms/latest/flex/flex.js.map +1 -0
  55. package/dist/transforms/latest/grid/grid.d.ts +18 -0
  56. package/dist/transforms/latest/grid/grid.js +19 -0
  57. package/dist/transforms/latest/grid/grid.js.map +1 -0
  58. package/dist/transforms/latest/heading/heading.d.ts +18 -0
  59. package/dist/transforms/latest/heading/heading.js +85 -0
  60. package/dist/transforms/latest/heading/heading.js.map +1 -0
  61. package/dist/transforms/latest/text/text.d.ts +18 -0
  62. package/dist/transforms/latest/text/text.js +88 -0
  63. package/dist/transforms/latest/text/text.js.map +1 -0
  64. package/package.json +68 -0
  65. package/src/baseCommand.ts +74 -0
  66. package/src/commands/latest/box.ts +21 -0
  67. package/src/commands/latest/card.ts +21 -0
  68. package/src/commands/latest/code.ts +21 -0
  69. package/src/commands/latest/container.ts +21 -0
  70. package/src/commands/latest/flex.ts +21 -0
  71. package/src/commands/latest/grid.ts +21 -0
  72. package/src/commands/latest/heading.ts +21 -0
  73. package/src/commands/latest/inline.ts +21 -0
  74. package/src/commands/latest/label.ts +21 -0
  75. package/src/commands/latest/stack.ts +21 -0
  76. package/src/commands/latest/text.ts +21 -0
  77. package/src/constants/latest/layout-mods.ts +422 -0
  78. package/src/transforms/latest/box/box.mods.ts +88 -0
  79. package/src/transforms/latest/box/box.test.ts +204 -0
  80. package/src/transforms/latest/box/box.ts +104 -0
  81. package/src/transforms/latest/card/card.mods.ts +592 -0
  82. package/src/transforms/latest/card/card.test.ts +207 -0
  83. package/src/transforms/latest/card/card.ts +71 -0
  84. package/src/transforms/latest/code/code.mods.ts +57 -0
  85. package/src/transforms/latest/code/code.test.ts +61 -0
  86. package/src/transforms/latest/code/code.ts +53 -0
  87. package/src/transforms/latest/container/container.mods.ts +77 -0
  88. package/src/transforms/latest/container/container.test.ts +105 -0
  89. package/src/transforms/latest/container/container.ts +42 -0
  90. package/src/transforms/latest/flex/flex.mods.ts +73 -0
  91. package/src/transforms/latest/flex/flex.test.ts +80 -0
  92. package/src/transforms/latest/flex/flex.ts +42 -0
  93. package/src/transforms/latest/grid/grid.mods.ts +109 -0
  94. package/src/transforms/latest/grid/grid.test.ts +97 -0
  95. package/src/transforms/latest/grid/grid.ts +42 -0
  96. package/src/transforms/latest/heading/heading.mods.ts +70 -0
  97. package/src/transforms/latest/heading/heading.test.ts +87 -0
  98. package/src/transforms/latest/heading/heading.ts +53 -0
  99. package/src/transforms/latest/inline/inline.mods.ts +65 -0
  100. package/src/transforms/latest/inline/inline.test.ts +57 -0
  101. package/src/transforms/latest/inline/inline.ts +42 -0
  102. package/src/transforms/latest/label/label.mods.ts +72 -0
  103. package/src/transforms/latest/label/label.test.ts +113 -0
  104. package/src/transforms/latest/label/label.ts +67 -0
  105. package/src/transforms/latest/stack/stack.mods.ts +12 -0
  106. package/src/transforms/latest/stack/stack.test.ts +160 -0
  107. package/src/transforms/latest/stack/stack.ts +102 -0
  108. package/src/transforms/latest/text/text.mods.ts +72 -0
  109. package/src/transforms/latest/text/text.test.ts +113 -0
  110. package/src/transforms/latest/text/text.ts +57 -0
  111. package/src/types/AnyExpression.ts +9 -0
  112. package/src/types/AttributeMods.ts +16 -0
  113. package/src/types/BaseOptions.ts +6 -0
  114. package/src/utils/addAttribute.test.ts +43 -0
  115. package/src/utils/addAttribute.ts +26 -0
  116. package/src/utils/addImportSpecifier.test.ts +36 -0
  117. package/src/utils/addImportSpecifier.ts +42 -0
  118. package/src/utils/getAttribute.ts +22 -0
  119. package/src/utils/getAttributeExpression.ts +22 -0
  120. package/src/utils/getAttributeValue.ts +20 -0
  121. package/src/utils/getClonedImportSpecifiers.ts +29 -0
  122. package/src/utils/getComponentJsxNames.ts +17 -0
  123. package/src/utils/getComponentLocalNames.ts +69 -0
  124. package/src/utils/getCompositeAttrValue.ts +39 -0
  125. package/src/utils/getMappingArray.ts +36 -0
  126. package/src/utils/getMappingExpression.ts +22 -0
  127. package/src/utils/getMappingValue.ts +52 -0
  128. package/src/utils/getShorthandAttributes.ts +54 -0
  129. package/src/utils/getSplitImportSpecifiers.ts +40 -0
  130. package/src/utils/getStaticAttributeExpression.ts +35 -0
  131. package/src/utils/getStyleExpression.ts +37 -0
  132. package/src/utils/getStyledComponentAliases.ts +28 -0
  133. package/src/utils/getStyledComponentName.ts +52 -0
  134. package/src/utils/getTransformPath.ts +17 -0
  135. package/src/utils/insertTodoWarning.ts +48 -0
  136. package/src/utils/isImportBindingUsed.ts +53 -0
  137. package/src/utils/isValidStyleType.ts +21 -0
  138. package/src/utils/mergeStyle.ts +64 -0
  139. package/src/utils/removeUnusedImport.test.ts +77 -0
  140. package/src/utils/removeUnusedImport.ts +57 -0
  141. package/src/utils/replaceElement.test.ts +181 -0
  142. package/src/utils/replaceElement.ts +106 -0
  143. package/src/utils/replaceStyledComponent.test.ts +57 -0
  144. package/src/utils/replaceStyledComponent.ts +60 -0
  145. package/src/utils/shouldTransformComponent.test.ts +61 -0
  146. package/src/utils/shouldTransformComponent.ts +40 -0
  147. package/src/utils/testUtils.ts +54 -0
  148. package/src/utils/transformAttributes.test.ts +544 -0
  149. package/src/utils/transformAttributes.ts +248 -0
  150. package/src/utils/transformComponent.test.ts +45 -0
  151. package/src/utils/transformComponent.ts +31 -0
  152. package/src/utils/transformImport.test.ts +175 -0
  153. package/src/utils/transformImport.ts +55 -0
  154. package/src/utils/transformImportAlias.test.ts +40 -0
  155. package/src/utils/transformImportAlias.ts +49 -0
  156. package/src/utils/transformStyledComponents.test.ts +172 -0
  157. 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
+ }