@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,72 @@
|
|
|
1
|
+
import type {AttributeMods} from '../../../types/AttributeMods'
|
|
2
|
+
|
|
3
|
+
/** @internal */
|
|
4
|
+
export const TEXT_MODS: AttributeMods = {
|
|
5
|
+
accent: {
|
|
6
|
+
type: 'rename-mapped',
|
|
7
|
+
name: 'tone',
|
|
8
|
+
mapping: {
|
|
9
|
+
true: 'suggest',
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
flex: {
|
|
13
|
+
type: 'style-mapped',
|
|
14
|
+
style: 'flex',
|
|
15
|
+
mapping: {
|
|
16
|
+
none: '0 0 auto',
|
|
17
|
+
auto: '1 1 auto',
|
|
18
|
+
initial: '0 1 auto',
|
|
19
|
+
1: '1',
|
|
20
|
+
2: '2',
|
|
21
|
+
3: '3',
|
|
22
|
+
4: '4',
|
|
23
|
+
5: '5',
|
|
24
|
+
6: '6',
|
|
25
|
+
7: '7',
|
|
26
|
+
8: '8',
|
|
27
|
+
9: '9',
|
|
28
|
+
10: '10',
|
|
29
|
+
11: '11',
|
|
30
|
+
12: '12',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
maxWidth: {
|
|
34
|
+
type: 'style-mapped',
|
|
35
|
+
style: 'maxWidth',
|
|
36
|
+
mapping: {
|
|
37
|
+
auto: 'none',
|
|
38
|
+
fill: '100%',
|
|
39
|
+
0: '20rem',
|
|
40
|
+
1: '40rem',
|
|
41
|
+
2: '60rem',
|
|
42
|
+
3: '80rem',
|
|
43
|
+
4: '100rem',
|
|
44
|
+
5: '120rem',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
textOverflow: {
|
|
48
|
+
type: 'rename-mapped',
|
|
49
|
+
name: 'truncate',
|
|
50
|
+
mapping: {
|
|
51
|
+
ellipsis: 1,
|
|
52
|
+
clip: 1,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
width: {
|
|
56
|
+
type: 'style-mapped',
|
|
57
|
+
style: 'width',
|
|
58
|
+
mapping: {
|
|
59
|
+
auto: 'auto',
|
|
60
|
+
fill: '100%',
|
|
61
|
+
stretch: 'stretch',
|
|
62
|
+
min: 'min-content',
|
|
63
|
+
max: 'max-content',
|
|
64
|
+
0: '20rem',
|
|
65
|
+
1: '40rem',
|
|
66
|
+
2: '60rem',
|
|
67
|
+
3: '80rem',
|
|
68
|
+
4: '100rem',
|
|
69
|
+
5: '120rem',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {defineInlineTest} from '../../../utils/testUtils'
|
|
2
|
+
import transform from './text'
|
|
3
|
+
|
|
4
|
+
defineInlineTest(
|
|
5
|
+
transform,
|
|
6
|
+
{fromPackage: '@legacy/ui', toPackage: '@sanity/ui'},
|
|
7
|
+
`
|
|
8
|
+
import {Text} from '@legacy/ui'
|
|
9
|
+
|
|
10
|
+
<Text />
|
|
11
|
+
`,
|
|
12
|
+
`
|
|
13
|
+
import {Text} from "@sanity/ui"
|
|
14
|
+
|
|
15
|
+
<Text as="div" trim={true} />
|
|
16
|
+
`,
|
|
17
|
+
'updates Text import path based on fromPackage and toPackage',
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
defineInlineTest(
|
|
21
|
+
transform,
|
|
22
|
+
{},
|
|
23
|
+
`
|
|
24
|
+
<Text />
|
|
25
|
+
`,
|
|
26
|
+
`
|
|
27
|
+
<Text as="div" trim={true} />
|
|
28
|
+
`,
|
|
29
|
+
'adds trim prop',
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
defineInlineTest(
|
|
33
|
+
transform,
|
|
34
|
+
{},
|
|
35
|
+
`
|
|
36
|
+
<Text />
|
|
37
|
+
`,
|
|
38
|
+
`
|
|
39
|
+
<Text as="div" trim={true} />
|
|
40
|
+
`,
|
|
41
|
+
'adds as prop',
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
defineInlineTest(
|
|
45
|
+
transform,
|
|
46
|
+
{},
|
|
47
|
+
`
|
|
48
|
+
<Text as="span" />
|
|
49
|
+
`,
|
|
50
|
+
`
|
|
51
|
+
<Text as="span" trim={true} />
|
|
52
|
+
`,
|
|
53
|
+
'does not add as prop if already set',
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
defineInlineTest(
|
|
57
|
+
transform,
|
|
58
|
+
{},
|
|
59
|
+
`
|
|
60
|
+
<Text flex="auto" />
|
|
61
|
+
`,
|
|
62
|
+
`
|
|
63
|
+
<Text
|
|
64
|
+
style={{
|
|
65
|
+
flex: "1 1 auto"
|
|
66
|
+
}}
|
|
67
|
+
as="div"
|
|
68
|
+
trim={true} />
|
|
69
|
+
`,
|
|
70
|
+
'moves flex prop to style',
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
defineInlineTest(
|
|
74
|
+
transform,
|
|
75
|
+
{},
|
|
76
|
+
`
|
|
77
|
+
<Text accent />
|
|
78
|
+
`,
|
|
79
|
+
`
|
|
80
|
+
<Text tone="suggest" as="div" trim={true} />
|
|
81
|
+
`,
|
|
82
|
+
'renames accent prop and updates mapped values',
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
defineInlineTest(
|
|
86
|
+
transform,
|
|
87
|
+
{},
|
|
88
|
+
`
|
|
89
|
+
<Text textOverflow="ellipsis" />
|
|
90
|
+
`,
|
|
91
|
+
`
|
|
92
|
+
<Text truncate={1} as="div" trim={true} />
|
|
93
|
+
`,
|
|
94
|
+
'renames textOverflow prop and updates mapped values',
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
defineInlineTest(
|
|
98
|
+
transform,
|
|
99
|
+
{},
|
|
100
|
+
`
|
|
101
|
+
<Text width={1} maxWidth="fill" />
|
|
102
|
+
`,
|
|
103
|
+
`
|
|
104
|
+
<Text
|
|
105
|
+
style={{
|
|
106
|
+
width: "40rem",
|
|
107
|
+
maxWidth: "100%"
|
|
108
|
+
}}
|
|
109
|
+
as="div"
|
|
110
|
+
trim={true} />
|
|
111
|
+
`,
|
|
112
|
+
'moves width props to style and updates mapped value',
|
|
113
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {type API, type FileInfo} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import type {BaseOptions} from '../../../types/BaseOptions'
|
|
4
|
+
import {addAttribute} from '../../../utils/addAttribute'
|
|
5
|
+
import {getComponentLocalNames} from '../../../utils/getComponentLocalNames'
|
|
6
|
+
import {shouldTransformComponent} from '../../../utils/shouldTransformComponent'
|
|
7
|
+
import {transformAttributes} from '../../../utils/transformAttributes'
|
|
8
|
+
import {transformComponent} from '../../../utils/transformComponent'
|
|
9
|
+
import {transformImport} from '../../../utils/transformImport'
|
|
10
|
+
import {TEXT_MODS} from './text.mods'
|
|
11
|
+
|
|
12
|
+
const TODO_WARNING = 'Please double check the Text migration below'
|
|
13
|
+
|
|
14
|
+
/** @internal */
|
|
15
|
+
export default function transform(
|
|
16
|
+
fileInfo: FileInfo,
|
|
17
|
+
api: API,
|
|
18
|
+
options: BaseOptions,
|
|
19
|
+
): string | undefined {
|
|
20
|
+
const {fromPackage, toPackage} = options || {}
|
|
21
|
+
|
|
22
|
+
return transformComponent(fileInfo, api, ({j, root, markChanged}) => {
|
|
23
|
+
const localNames = getComponentLocalNames(j, root, 'Text', options)
|
|
24
|
+
|
|
25
|
+
if (!shouldTransformComponent(j, root, 'Text', localNames, options)) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (transformImport(j, root, 'Text', fromPackage, toPackage)) {
|
|
30
|
+
markChanged()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
root
|
|
34
|
+
.find(j.JSXOpeningElement, {
|
|
35
|
+
name: {type: 'JSXIdentifier', name: 'Text'},
|
|
36
|
+
})
|
|
37
|
+
.forEach((path) => {
|
|
38
|
+
let changed = false
|
|
39
|
+
|
|
40
|
+
if (transformAttributes(j, path, TEXT_MODS, TODO_WARNING)) {
|
|
41
|
+
changed = true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (addAttribute(j, path.node, 'as', 'div')) {
|
|
45
|
+
changed = true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (addAttribute(j, path.node, 'trim', true)) {
|
|
49
|
+
changed = true
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (changed) {
|
|
53
|
+
markChanged()
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type AttributeMapping = Record<string | number, string | boolean | number>
|
|
2
|
+
export type CompositeAttributeMapping = Record<string, Record<string, string | number | boolean>>
|
|
3
|
+
|
|
4
|
+
export type AttributeMod =
|
|
5
|
+
| {type: 'remove'}
|
|
6
|
+
| {type: 'warn-only'; warning?: string}
|
|
7
|
+
| {type: 'warn-missing'; warning?: string}
|
|
8
|
+
| {type: 'rename-only'; name: string}
|
|
9
|
+
| {type: 'rename-mapped'; name: string; mapping: AttributeMapping}
|
|
10
|
+
| {type: 'style-only'; style: string}
|
|
11
|
+
| {type: 'style-mapped'; style: string; mapping: AttributeMapping}
|
|
12
|
+
| {type: 'mapped-only'; mapping: AttributeMapping}
|
|
13
|
+
| {type: 'shorthand-mapped'; props: {name: string; mapping: AttributeMapping}[]}
|
|
14
|
+
| {type: 'composite'; name: string; mapping: CompositeAttributeMapping}
|
|
15
|
+
|
|
16
|
+
export type AttributeMods = Record<string, AttributeMod>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {type API, type FileInfo} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import {defineInlineTest} from '../utils/testUtils'
|
|
4
|
+
import {addAttribute} from './addAttribute'
|
|
5
|
+
|
|
6
|
+
function transform(fileInfo: FileInfo, api: API): string {
|
|
7
|
+
const j = api.jscodeshift
|
|
8
|
+
const root = j(fileInfo.source)
|
|
9
|
+
|
|
10
|
+
root
|
|
11
|
+
.find(j.JSXOpeningElement, {
|
|
12
|
+
name: {type: 'JSXIdentifier', name: 'div'},
|
|
13
|
+
})
|
|
14
|
+
.forEach((path) => {
|
|
15
|
+
addAttribute(j, path.node, 'trim', true)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return root.toSource()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
defineInlineTest(
|
|
22
|
+
transform,
|
|
23
|
+
{},
|
|
24
|
+
`
|
|
25
|
+
<div />
|
|
26
|
+
`,
|
|
27
|
+
`
|
|
28
|
+
<div trim={true} />
|
|
29
|
+
`,
|
|
30
|
+
'adds prop',
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
defineInlineTest(
|
|
34
|
+
transform,
|
|
35
|
+
{},
|
|
36
|
+
`
|
|
37
|
+
<div width="100%" />
|
|
38
|
+
`,
|
|
39
|
+
`
|
|
40
|
+
<div width="100%" trim={true} />
|
|
41
|
+
`,
|
|
42
|
+
'adds prop and preserves existing props',
|
|
43
|
+
)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type {API, JSXOpeningElement} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import {getAttribute} from './getAttribute'
|
|
4
|
+
import {getAttributeValue} from './getAttributeValue'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Adds a JSX attribute to the opening element if it doesn't already exist.
|
|
8
|
+
* Returns whether the AST was updated.
|
|
9
|
+
*/
|
|
10
|
+
export function addAttribute(
|
|
11
|
+
j: API['jscodeshift'],
|
|
12
|
+
node: JSXOpeningElement,
|
|
13
|
+
name: string,
|
|
14
|
+
value: unknown,
|
|
15
|
+
): boolean {
|
|
16
|
+
if (getAttribute(node.attributes, name)) {
|
|
17
|
+
return false
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!node.attributes) {
|
|
21
|
+
node.attributes = []
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
node.attributes.push(j.jsxAttribute(j.jsxIdentifier(name), getAttributeValue(j, value)))
|
|
25
|
+
return true
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {type API, type FileInfo} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import {addImportSpecifier} from './addImportSpecifier'
|
|
4
|
+
import {defineInlineTest} from './testUtils'
|
|
5
|
+
|
|
6
|
+
function transform(fileInfo: FileInfo, api: API): string {
|
|
7
|
+
const j = api.jscodeshift
|
|
8
|
+
const root = j(fileInfo.source)
|
|
9
|
+
|
|
10
|
+
addImportSpecifier(j, root, 'Card', 'Box')
|
|
11
|
+
return root.toSource()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
defineInlineTest(
|
|
15
|
+
transform,
|
|
16
|
+
{},
|
|
17
|
+
`
|
|
18
|
+
import {Card} from '@sanity/ui'
|
|
19
|
+
`,
|
|
20
|
+
`
|
|
21
|
+
import { Card, Box } from '@sanity/ui';
|
|
22
|
+
`,
|
|
23
|
+
'adds import specifier',
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
defineInlineTest(
|
|
27
|
+
transform,
|
|
28
|
+
{},
|
|
29
|
+
`
|
|
30
|
+
import {Text, Card} from '@sanity/ui'
|
|
31
|
+
`,
|
|
32
|
+
`
|
|
33
|
+
import { Text, Card, Box } from '@sanity/ui';
|
|
34
|
+
`,
|
|
35
|
+
'adds import specifier to import with multiple',
|
|
36
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type {API} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adds an import specifier to an existing import declaration.
|
|
5
|
+
* Returns whether the AST was updated.
|
|
6
|
+
*/
|
|
7
|
+
export function addImportSpecifier(
|
|
8
|
+
j: API['jscodeshift'],
|
|
9
|
+
root: ReturnType<API['jscodeshift']>,
|
|
10
|
+
addTo: string,
|
|
11
|
+
name: string,
|
|
12
|
+
): boolean {
|
|
13
|
+
let hasChanges = false
|
|
14
|
+
|
|
15
|
+
root.find(j.ImportDeclaration).forEach((path) => {
|
|
16
|
+
const specs = path.node.specifiers
|
|
17
|
+
|
|
18
|
+
if (!specs?.length) {
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const hasFromSpecifier = specs.some(
|
|
23
|
+
(s) =>
|
|
24
|
+
s.type === 'ImportSpecifier' &&
|
|
25
|
+
s.imported.type === 'Identifier' &&
|
|
26
|
+
s.imported.name === addTo,
|
|
27
|
+
)
|
|
28
|
+
const hasToSpecifier = specs.some(
|
|
29
|
+
(s) =>
|
|
30
|
+
s.type === 'ImportSpecifier' &&
|
|
31
|
+
s.imported.type === 'Identifier' &&
|
|
32
|
+
s.imported.name === name,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
if (hasFromSpecifier && !hasToSpecifier) {
|
|
36
|
+
specs.push(j.importSpecifier(j.identifier(name), j.identifier(name)))
|
|
37
|
+
hasChanges = true
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
return hasChanges
|
|
42
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type {JSXAttribute, JSXElement} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
export function getAttribute(
|
|
4
|
+
attrs: JSXElement['openingElement']['attributes'],
|
|
5
|
+
name: string,
|
|
6
|
+
): JSXAttribute | undefined {
|
|
7
|
+
if (!attrs) {
|
|
8
|
+
return undefined
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
for (const attr of attrs) {
|
|
12
|
+
if (
|
|
13
|
+
attr.type === 'JSXAttribute' &&
|
|
14
|
+
attr.name.type === 'JSXIdentifier' &&
|
|
15
|
+
attr.name.name === name
|
|
16
|
+
) {
|
|
17
|
+
return attr
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return undefined
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {type API, type JSXAttribute} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import {type AnyExpression} from '../types/AnyExpression'
|
|
4
|
+
|
|
5
|
+
export function getAttributeExpression(
|
|
6
|
+
attr: JSXAttribute,
|
|
7
|
+
j: API['jscodeshift'],
|
|
8
|
+
): AnyExpression | null {
|
|
9
|
+
if (!attr.value) {
|
|
10
|
+
return j.booleanLiteral(true)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (attr.value.type === 'StringLiteral' || attr.value.type === 'Literal') {
|
|
14
|
+
return attr.value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (attr.value.type === 'JSXExpressionContainer') {
|
|
18
|
+
return attr.value.expression
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type {API, JSXAttribute} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
export function getAttributeValue(
|
|
4
|
+
j: API['jscodeshift'],
|
|
5
|
+
value: unknown,
|
|
6
|
+
): NonNullable<JSXAttribute['value']> {
|
|
7
|
+
if (value === true || value === false) {
|
|
8
|
+
return j.jsxExpressionContainer(j.booleanLiteral(value))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (typeof value === 'string') {
|
|
12
|
+
return j.stringLiteral(value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (typeof value === 'number') {
|
|
16
|
+
return j.jsxExpressionContainer(j.numericLiteral(value))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return j.jsxExpressionContainer(j.nullLiteral())
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type {API, ImportDefaultSpecifier, ImportSpecifier} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
export function getClonedImportSpecifiers(
|
|
4
|
+
j: API['jscodeshift'],
|
|
5
|
+
specifiers: (ImportSpecifier | ImportDefaultSpecifier)[] = [],
|
|
6
|
+
) {
|
|
7
|
+
return specifiers.map((spec) => {
|
|
8
|
+
if (spec.type === 'ImportSpecifier') {
|
|
9
|
+
if (spec.imported.type !== 'Identifier' || spec.local?.type !== 'Identifier') {
|
|
10
|
+
return spec
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const clone = j.importSpecifier(
|
|
14
|
+
j.identifier(spec.imported.name),
|
|
15
|
+
j.identifier(spec.local.name),
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
if ('importKind' in spec && spec.importKind === 'type') {
|
|
19
|
+
Object.assign(clone, {importKind: 'type'})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return clone
|
|
23
|
+
} else {
|
|
24
|
+
return j.importDefaultSpecifier(
|
|
25
|
+
j.identifier((spec as ImportDefaultSpecifier).local?.name as string),
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type {API, Collection} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import type {BaseOptions} from '../types/BaseOptions'
|
|
4
|
+
import {getComponentLocalNames} from './getComponentLocalNames'
|
|
5
|
+
import {getStyledComponentAliases} from './getStyledComponentAliases'
|
|
6
|
+
|
|
7
|
+
export function getComponentJsxNames(
|
|
8
|
+
j: API['jscodeshift'],
|
|
9
|
+
root: Collection,
|
|
10
|
+
componentName: string,
|
|
11
|
+
options?: BaseOptions,
|
|
12
|
+
): Set<string> {
|
|
13
|
+
const localNames = getComponentLocalNames(j, root, componentName, options)
|
|
14
|
+
const styledAliases = getStyledComponentAliases(j, root, localNames)
|
|
15
|
+
|
|
16
|
+
return new Set([...localNames, ...styledAliases])
|
|
17
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type {API, Collection} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import type {BaseOptions} from '../types/BaseOptions'
|
|
4
|
+
import {DEFAULT_UI_PACKAGE} from './transformImport'
|
|
5
|
+
|
|
6
|
+
export function getComponentLocalNames(
|
|
7
|
+
j: API['jscodeshift'],
|
|
8
|
+
root: Collection,
|
|
9
|
+
componentName: string,
|
|
10
|
+
options?: BaseOptions,
|
|
11
|
+
): Set<string> {
|
|
12
|
+
const importedNames = new Set<string>()
|
|
13
|
+
const packages = new Set([
|
|
14
|
+
options?.fromPackage ?? DEFAULT_UI_PACKAGE,
|
|
15
|
+
options?.toPackage ?? DEFAULT_UI_PACKAGE,
|
|
16
|
+
])
|
|
17
|
+
let hasOtherPackageImport = false
|
|
18
|
+
|
|
19
|
+
root.find(j.ImportDeclaration).forEach((path) => {
|
|
20
|
+
const node = path.node
|
|
21
|
+
|
|
22
|
+
if ('importKind' in node && node.importKind === 'type') {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const matchesPackage = packages.has(node.source.value as string)
|
|
27
|
+
|
|
28
|
+
for (const spec of node.specifiers ?? []) {
|
|
29
|
+
if (spec.type !== 'ImportSpecifier') {
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if ('importKind' in spec && spec.importKind === 'type') {
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (spec.imported.type !== 'Identifier' || spec.imported.name !== componentName) {
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const local = spec.local?.type === 'Identifier' ? spec.local.name : spec.imported.name
|
|
42
|
+
|
|
43
|
+
if (!matchesPackage) {
|
|
44
|
+
hasOtherPackageImport = true
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
importedNames.add(local)
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if (importedNames.size > 0) {
|
|
53
|
+
return importedNames
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (hasOtherPackageImport) {
|
|
57
|
+
return new Set()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const usesComponentInJsx = root.find(j.JSXOpeningElement).some(({node}) => {
|
|
61
|
+
return node.name.type === 'JSXIdentifier' && node.name.name === componentName
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
if (usesComponentInJsx) {
|
|
65
|
+
return new Set([componentName])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return new Set()
|
|
69
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type {API, JSXAttribute, JSXSpreadAttribute} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import type {CompositeAttributeMapping} from '../types/AttributeMods'
|
|
4
|
+
import {getStaticAttributeExpression} from './getStaticAttributeExpression'
|
|
5
|
+
|
|
6
|
+
export function getCompositeAttrValue(
|
|
7
|
+
j: API['jscodeshift'],
|
|
8
|
+
attrs: (JSXAttribute | JSXSpreadAttribute)[],
|
|
9
|
+
mapping: CompositeAttributeMapping,
|
|
10
|
+
) {
|
|
11
|
+
let compositeAttrValue: string | null = null
|
|
12
|
+
|
|
13
|
+
for (const compositeKey of Object.keys(mapping)) {
|
|
14
|
+
const compositeMapping = mapping[compositeKey]
|
|
15
|
+
|
|
16
|
+
if (!compositeMapping) {
|
|
17
|
+
continue
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let hasMatchingValues = true
|
|
21
|
+
|
|
22
|
+
for (const propName of Object.keys(compositeMapping)) {
|
|
23
|
+
const mappingValue = compositeMapping[propName]
|
|
24
|
+
const attrValue = getStaticAttributeExpression(j, attrs, propName)
|
|
25
|
+
|
|
26
|
+
if (mappingValue === undefined || attrValue !== mappingValue) {
|
|
27
|
+
hasMatchingValues = false
|
|
28
|
+
break
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (hasMatchingValues) {
|
|
33
|
+
compositeAttrValue = compositeKey
|
|
34
|
+
break
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return compositeAttrValue
|
|
39
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {type API, type ArrayExpression} from 'jscodeshift'
|
|
2
|
+
|
|
3
|
+
import type {AnyExpression} from '../types/AnyExpression'
|
|
4
|
+
import type {AttributeMapping} from '../types/AttributeMods'
|
|
5
|
+
import {getMappingExpression} from './getMappingExpression'
|
|
6
|
+
import {getMappingValue} from './getMappingValue'
|
|
7
|
+
|
|
8
|
+
export function getMappingArray(
|
|
9
|
+
j: API['jscodeshift'],
|
|
10
|
+
arr: AnyExpression,
|
|
11
|
+
mapping: AttributeMapping,
|
|
12
|
+
allowUndefined?: boolean,
|
|
13
|
+
): ArrayExpression | null {
|
|
14
|
+
if (arr.type !== 'ArrayExpression') {
|
|
15
|
+
return null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const elements = arr.elements as (AnyExpression | null)[]
|
|
19
|
+
const output: AnyExpression[] = []
|
|
20
|
+
|
|
21
|
+
for (const el of elements) {
|
|
22
|
+
if (el == null || el.type === 'SpreadElement') {
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const mappingValue = getMappingValue(mapping, el)
|
|
27
|
+
|
|
28
|
+
if (!mappingValue && !allowUndefined) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
output.push(getMappingExpression(j, mappingValue))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return j.arrayExpression(output as never[])
|
|
36
|
+
}
|