@planningcenter/tapestry-migration-cli 3.5.0 → 3.6.1-qa-1035.0
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/README.md +37 -0
- package/dist/tapestry-react-shim.cjs +195 -11
- package/package.json +3 -3
- package/src/availableComponents.ts +1 -0
- package/src/components/button/transforms/tooltipToWrapper.test.ts +50 -3
- package/src/components/button/transforms/tooltipToWrapper.ts +18 -0
- package/src/components/flex/index.test.ts +291 -0
- package/src/components/flex/index.ts +54 -0
- package/src/components/flex/transforms/alignmentToAlign.test.ts +185 -0
- package/src/components/flex/transforms/alignmentToAlign.ts +73 -0
- package/src/components/flex/transforms/auditSpreadProps.test.ts +93 -0
- package/src/components/flex/transforms/auditSpreadProps.ts +6 -0
- package/src/components/flex/transforms/axisToDirection.test.ts +155 -0
- package/src/components/flex/transforms/axisToDirection.ts +77 -0
- package/src/components/flex/transforms/convertStyleProps.test.ts +147 -0
- package/src/components/flex/transforms/convertStyleProps.ts +23 -0
- package/src/components/flex/transforms/cssFlexAliasesToProps.test.ts +202 -0
- package/src/components/flex/transforms/cssFlexAliasesToProps.ts +110 -0
- package/src/components/flex/transforms/distributionToJustify.test.ts +214 -0
- package/src/components/flex/transforms/distributionToJustify.ts +76 -0
- package/src/components/flex/transforms/flexPropToExpand.test.ts +156 -0
- package/src/components/flex/transforms/flexPropToExpand.ts +100 -0
- package/src/components/flex/transforms/innerRefToRef.test.ts +100 -0
- package/src/components/flex/transforms/innerRefToRef.ts +14 -0
- package/src/components/flex/transforms/mediaQueriesToResponsive.test.ts +714 -0
- package/src/components/flex/transforms/mediaQueriesToResponsive.ts +523 -0
- package/src/components/flex/transforms/moveFlexImport.test.ts +202 -0
- package/src/components/flex/transforms/moveFlexImport.ts +14 -0
- package/src/components/flex/transforms/paddingPropsToFlex.test.ts +252 -0
- package/src/components/flex/transforms/paddingPropsToFlex.ts +150 -0
- package/src/components/flex/transforms/setDefaultAxis.test.ts +126 -0
- package/src/components/flex/transforms/setDefaultAxis.ts +30 -0
- package/src/components/flex/transforms/spacingToGap.test.ts +176 -0
- package/src/components/flex/transforms/spacingToGap.ts +49 -0
- package/src/components/flex/transforms/unsupportedProps.test.ts +141 -0
- package/src/components/flex/transforms/unsupportedProps.ts +17 -0
- package/src/components/select/index.ts +2 -0
- package/src/components/select/transforms/onChangeSignature.test.ts +224 -0
- package/src/components/select/transforms/onChangeSignature.ts +172 -0
- package/src/components/shared/actions/resolveConstantAttributeValue.test.ts +122 -0
- package/src/components/shared/actions/resolveConstantAttributeValue.ts +31 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +41 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.test.ts +47 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +16 -0
- package/src/components/toggle-switch/index.ts +2 -0
- package/src/components/toggle-switch/transforms/onClickToOnChange.test.ts +210 -0
- package/src/components/toggle-switch/transforms/onClickToOnChange.ts +71 -0
- package/src/index.test.ts +79 -0
- package/src/index.ts +29 -0
- package/src/migrationList.ts +22 -0
- package/src/utils/componentLabel.test.ts +61 -0
- package/src/utils/componentLabel.ts +15 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { JSXAttribute } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
|
|
4
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
5
|
+
import { transformAttributeName } from "../../shared/actions/transformAttributeName"
|
|
6
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
7
|
+
import { orConditions } from "../../shared/conditions/orConditions"
|
|
8
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
9
|
+
|
|
10
|
+
const FLEX_START_END_MAP: Record<string, string> = {
|
|
11
|
+
"flex-end": "end",
|
|
12
|
+
"flex-start": "start",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const VALID_JUSTIFY_VALUES = new Set([
|
|
16
|
+
"start",
|
|
17
|
+
"center",
|
|
18
|
+
"end",
|
|
19
|
+
"space-between",
|
|
20
|
+
"space-evenly",
|
|
21
|
+
])
|
|
22
|
+
|
|
23
|
+
const VALID_ALIGN_VALUES = new Set([
|
|
24
|
+
"start",
|
|
25
|
+
"center",
|
|
26
|
+
"end",
|
|
27
|
+
"stretch",
|
|
28
|
+
"baseline",
|
|
29
|
+
])
|
|
30
|
+
|
|
31
|
+
function getStringValue(attr: JSXAttribute): string | null {
|
|
32
|
+
const { value } = attr
|
|
33
|
+
if (value?.type === "StringLiteral") return value.value
|
|
34
|
+
if (
|
|
35
|
+
value?.type === "JSXExpressionContainer" &&
|
|
36
|
+
value.expression.type === "StringLiteral"
|
|
37
|
+
)
|
|
38
|
+
return value.expression.value
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default attributeTransformFactory({
|
|
43
|
+
condition: orConditions(
|
|
44
|
+
hasAttribute("alignItems"),
|
|
45
|
+
hasAttribute("flexBasis"),
|
|
46
|
+
hasAttribute("flexDirection"),
|
|
47
|
+
hasAttribute("flexGrow"),
|
|
48
|
+
hasAttribute("flexShrink"),
|
|
49
|
+
hasAttribute("flexWrap"),
|
|
50
|
+
hasAttribute("justifyContent")
|
|
51
|
+
),
|
|
52
|
+
targetComponent: "StackView",
|
|
53
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
54
|
+
transform: (element, { j, options }) => {
|
|
55
|
+
let changed = false
|
|
56
|
+
|
|
57
|
+
if (transformAttributeName("flexBasis", "basis", { element, j, options }))
|
|
58
|
+
changed = true
|
|
59
|
+
if (
|
|
60
|
+
transformAttributeName("flexDirection", "direction", {
|
|
61
|
+
element,
|
|
62
|
+
j,
|
|
63
|
+
options,
|
|
64
|
+
})
|
|
65
|
+
)
|
|
66
|
+
changed = true
|
|
67
|
+
if (transformAttributeName("flexGrow", "grow", { element, j, options }))
|
|
68
|
+
changed = true
|
|
69
|
+
if (transformAttributeName("flexShrink", "shrink", { element, j, options }))
|
|
70
|
+
changed = true
|
|
71
|
+
if (transformAttributeName("flexWrap", "wrap", { element, j, options }))
|
|
72
|
+
changed = true
|
|
73
|
+
|
|
74
|
+
const justifyAttr = getAttribute({ element, name: "justifyContent" })
|
|
75
|
+
if (justifyAttr) {
|
|
76
|
+
const raw = getStringValue(justifyAttr)
|
|
77
|
+
const normalized = raw !== null ? (FLEX_START_END_MAP[raw] ?? raw) : null
|
|
78
|
+
if (normalized !== null && VALID_JUSTIFY_VALUES.has(normalized)) {
|
|
79
|
+
justifyAttr.name.name = "justify"
|
|
80
|
+
justifyAttr.value = j.stringLiteral(normalized)
|
|
81
|
+
} else {
|
|
82
|
+
addCommentToAttribute({
|
|
83
|
+
attribute: justifyAttr,
|
|
84
|
+
j,
|
|
85
|
+
text: `could not migrate to justify automatically; valid values are: ${[...VALID_JUSTIFY_VALUES].join(", ")}`,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
changed = true
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const alignAttr = getAttribute({ element, name: "alignItems" })
|
|
92
|
+
if (alignAttr) {
|
|
93
|
+
const raw = getStringValue(alignAttr)
|
|
94
|
+
const normalized = raw !== null ? (FLEX_START_END_MAP[raw] ?? raw) : null
|
|
95
|
+
if (normalized !== null && VALID_ALIGN_VALUES.has(normalized)) {
|
|
96
|
+
alignAttr.name.name = "align"
|
|
97
|
+
alignAttr.value = j.stringLiteral(normalized)
|
|
98
|
+
} else {
|
|
99
|
+
addCommentToAttribute({
|
|
100
|
+
attribute: alignAttr,
|
|
101
|
+
j,
|
|
102
|
+
text: `could not migrate to align automatically; valid values are: ${[...VALID_ALIGN_VALUES].join(", ")}`,
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
changed = true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return changed
|
|
109
|
+
},
|
|
110
|
+
})
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./distributionToJustify"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string | null {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
return transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("distributionToJustify transform", () => {
|
|
18
|
+
it("renames distribution to justify", () => {
|
|
19
|
+
const input = `
|
|
20
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
21
|
+
|
|
22
|
+
export default function Test() {
|
|
23
|
+
return (
|
|
24
|
+
<StackView distribution="start">
|
|
25
|
+
<StackView distribution="center">
|
|
26
|
+
<StackView distribution="end">
|
|
27
|
+
<StackView distribution="space-between">
|
|
28
|
+
<StackView distribution="space-evenly">Test</StackView>
|
|
29
|
+
</StackView>
|
|
30
|
+
</StackView>
|
|
31
|
+
</StackView>
|
|
32
|
+
</StackView>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
`.trim()
|
|
36
|
+
|
|
37
|
+
const result = applyTransform(input)
|
|
38
|
+
expect(result).toContain('justify="start"')
|
|
39
|
+
expect(result).toContain('justify="center"')
|
|
40
|
+
expect(result).toContain('justify="end"')
|
|
41
|
+
expect(result).toContain('justify="space-between"')
|
|
42
|
+
expect(result).toContain('justify="space-evenly"')
|
|
43
|
+
expect(result).not.toContain("distribution")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("resolves StackView.CENTER constant to justify", () => {
|
|
47
|
+
const input = `
|
|
48
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
49
|
+
|
|
50
|
+
export default function Test() {
|
|
51
|
+
return <StackView distribution={StackView.CENTER}>Test</StackView>
|
|
52
|
+
}
|
|
53
|
+
`.trim()
|
|
54
|
+
|
|
55
|
+
const result = applyTransform(input)
|
|
56
|
+
expect(result).toContain('justify="center"')
|
|
57
|
+
expect(result).not.toContain("distribution")
|
|
58
|
+
expect(result).not.toContain("StackView.CENTER")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("resolves StackView.START constant to justify", () => {
|
|
62
|
+
const input = `
|
|
63
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
64
|
+
|
|
65
|
+
export default function Test() {
|
|
66
|
+
return <StackView distribution={StackView.START}>Test</StackView>
|
|
67
|
+
}
|
|
68
|
+
`.trim()
|
|
69
|
+
|
|
70
|
+
const result = applyTransform(input)
|
|
71
|
+
expect(result).toContain('justify="start"')
|
|
72
|
+
expect(result).not.toContain("StackView.START")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("resolves StackView.END constant to justify", () => {
|
|
76
|
+
const input = `
|
|
77
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
78
|
+
|
|
79
|
+
export default function Test() {
|
|
80
|
+
return <StackView distribution={StackView.END}>Test</StackView>
|
|
81
|
+
}
|
|
82
|
+
`.trim()
|
|
83
|
+
|
|
84
|
+
const result = applyTransform(input)
|
|
85
|
+
expect(result).toContain('justify="end"')
|
|
86
|
+
expect(result).not.toContain("StackView.END")
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it("resolves StackView.SPACE_BETWEEN constant to justify", () => {
|
|
90
|
+
const input = `
|
|
91
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
92
|
+
|
|
93
|
+
export default function Test() {
|
|
94
|
+
return <StackView distribution={StackView.SPACE_BETWEEN}>Test</StackView>
|
|
95
|
+
}
|
|
96
|
+
`.trim()
|
|
97
|
+
|
|
98
|
+
const result = applyTransform(input)
|
|
99
|
+
expect(result).toContain('justify="space-between"')
|
|
100
|
+
expect(result).not.toContain("StackView.SPACE_BETWEEN")
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it("resolves StackView.SPACE_EVENLY constant to justify", () => {
|
|
104
|
+
const input = `
|
|
105
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
106
|
+
|
|
107
|
+
export default function Test() {
|
|
108
|
+
return <StackView distribution={StackView.SPACE_EVENLY}>Test</StackView>
|
|
109
|
+
}
|
|
110
|
+
`.trim()
|
|
111
|
+
|
|
112
|
+
const result = applyTransform(input)
|
|
113
|
+
expect(result).toContain('justify="space-evenly"')
|
|
114
|
+
expect(result).not.toContain("StackView.SPACE_EVENLY")
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("flags distribution={StackView.FILL} with a TODO and leaves it in place", () => {
|
|
118
|
+
const input = `
|
|
119
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
120
|
+
|
|
121
|
+
export default function Test() {
|
|
122
|
+
return <StackView distribution={StackView.FILL}>Test</StackView>
|
|
123
|
+
}
|
|
124
|
+
`.trim()
|
|
125
|
+
|
|
126
|
+
const result = applyTransform(input)
|
|
127
|
+
expect(result).toContain("distribution={StackView.FILL}")
|
|
128
|
+
expect(result).toContain("TODO: tapestry-migration (distribution)")
|
|
129
|
+
expect(result).not.toContain('justify="')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('flags distribution="fill" with a TODO and leaves it in place', () => {
|
|
133
|
+
const input = `
|
|
134
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
135
|
+
|
|
136
|
+
export default function Test() {
|
|
137
|
+
return <StackView distribution="fill">Test</StackView>
|
|
138
|
+
}
|
|
139
|
+
`.trim()
|
|
140
|
+
|
|
141
|
+
const result = applyTransform(input)
|
|
142
|
+
expect(result).toContain('distribution="fill"')
|
|
143
|
+
expect(result).toContain("TODO: tapestry-migration (distribution)")
|
|
144
|
+
expect(result).not.toContain('justify="')
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it("flags an unrecognized string literal with a TODO", () => {
|
|
148
|
+
const input = `
|
|
149
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
150
|
+
|
|
151
|
+
export default function Test() {
|
|
152
|
+
return <StackView distribution="space-around">Test</StackView>
|
|
153
|
+
}
|
|
154
|
+
`.trim()
|
|
155
|
+
|
|
156
|
+
const result = applyTransform(input)
|
|
157
|
+
expect(result).toContain('distribution="space-around"')
|
|
158
|
+
expect(result).toContain("TODO: tapestry-migration (distribution)")
|
|
159
|
+
expect(result).not.toContain('justify="')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("flags a dynamic distribution value with a TODO", () => {
|
|
163
|
+
const input = `
|
|
164
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
165
|
+
|
|
166
|
+
export default function Test({ distributionProp }) {
|
|
167
|
+
return <StackView distribution={distributionProp}>Test</StackView>
|
|
168
|
+
}
|
|
169
|
+
`.trim()
|
|
170
|
+
|
|
171
|
+
const result = applyTransform(input)
|
|
172
|
+
expect(result).toContain("distribution={distributionProp}")
|
|
173
|
+
expect(result).toContain("TODO: tapestry-migration (distribution)")
|
|
174
|
+
expect(result).not.toContain('justify="')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it("respects an aliased StackView import", () => {
|
|
178
|
+
const input = `
|
|
179
|
+
import { StackView as Stack } from "@planningcenter/tapestry-react"
|
|
180
|
+
|
|
181
|
+
export default function Test() {
|
|
182
|
+
return <Stack distribution="center">Test</Stack>
|
|
183
|
+
}
|
|
184
|
+
`.trim()
|
|
185
|
+
|
|
186
|
+
const result = applyTransform(input)
|
|
187
|
+
expect(result).toContain('justify="center"')
|
|
188
|
+
expect(result).not.toContain("distribution")
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it("returns null when there is no distribution to migrate", () => {
|
|
192
|
+
const input = `
|
|
193
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
194
|
+
|
|
195
|
+
export default function Test() {
|
|
196
|
+
return <StackView>Test</StackView>
|
|
197
|
+
}
|
|
198
|
+
`.trim()
|
|
199
|
+
|
|
200
|
+
expect(applyTransform(input)).toBe(null)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it("returns null when StackView is not imported from tapestry-react", () => {
|
|
204
|
+
const input = `
|
|
205
|
+
import { StackView } from "some-other-package"
|
|
206
|
+
|
|
207
|
+
export default function Test() {
|
|
208
|
+
return <StackView distribution="center">Test</StackView>
|
|
209
|
+
}
|
|
210
|
+
`.trim()
|
|
211
|
+
|
|
212
|
+
expect(applyTransform(input)).toBe(null)
|
|
213
|
+
})
|
|
214
|
+
})
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
4
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
5
|
+
import { resolveConstantAttributeValue } from "../../shared/actions/resolveConstantAttributeValue"
|
|
6
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
7
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
8
|
+
|
|
9
|
+
const VALID_JUSTIFY_VALUES = new Set([
|
|
10
|
+
"start",
|
|
11
|
+
"center",
|
|
12
|
+
"end",
|
|
13
|
+
"space-between",
|
|
14
|
+
"space-evenly",
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
const CONSTANT_DISTRIBUTION_MAP: Record<string, string> = {
|
|
18
|
+
CENTER: "center",
|
|
19
|
+
END: "end",
|
|
20
|
+
FILL: "fill",
|
|
21
|
+
SPACE_BETWEEN: "space-between",
|
|
22
|
+
SPACE_EVENLY: "space-evenly",
|
|
23
|
+
START: "start",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const transform: Transform = attributeTransformFactory({
|
|
27
|
+
condition: hasAttribute("distribution"),
|
|
28
|
+
targetComponent: "StackView",
|
|
29
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
30
|
+
transform: (element, { j, source }) => {
|
|
31
|
+
const distributionAttribute = getAttribute({
|
|
32
|
+
element,
|
|
33
|
+
name: "distribution",
|
|
34
|
+
})
|
|
35
|
+
if (!distributionAttribute) return false
|
|
36
|
+
|
|
37
|
+
const resolved = resolveConstantAttributeValue({
|
|
38
|
+
attribute: distributionAttribute,
|
|
39
|
+
constantMap: CONSTANT_DISTRIBUTION_MAP,
|
|
40
|
+
})
|
|
41
|
+
const distributionValue =
|
|
42
|
+
resolved !== null &&
|
|
43
|
+
(VALID_JUSTIFY_VALUES.has(resolved) || resolved === "fill")
|
|
44
|
+
? resolved
|
|
45
|
+
: null
|
|
46
|
+
|
|
47
|
+
if (distributionValue === null) {
|
|
48
|
+
addComment({
|
|
49
|
+
element,
|
|
50
|
+
j,
|
|
51
|
+
scope: "distribution",
|
|
52
|
+
source,
|
|
53
|
+
text: "could not migrate distribution to justify automatically; convert this value to start, center, end, space-between, or space-evenly manually",
|
|
54
|
+
})
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (distributionValue === "fill") {
|
|
59
|
+
addComment({
|
|
60
|
+
element,
|
|
61
|
+
j,
|
|
62
|
+
scope: "distribution",
|
|
63
|
+
source,
|
|
64
|
+
text: 'distribution="fill" has no Flex equivalent; remove this prop and use expand on the child elements instead',
|
|
65
|
+
})
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
distributionAttribute.name.name = "justify"
|
|
70
|
+
distributionAttribute.value = j.stringLiteral(distributionValue)
|
|
71
|
+
|
|
72
|
+
return true
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export default transform
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./flexPropToExpand"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string | null {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
return transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("flexPropToExpand transform", () => {
|
|
18
|
+
it("converts flex={1} to expand", () => {
|
|
19
|
+
const input = `
|
|
20
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
21
|
+
export default function Test() {
|
|
22
|
+
return <StackView flex={1}>Test</StackView>
|
|
23
|
+
}
|
|
24
|
+
`.trim()
|
|
25
|
+
|
|
26
|
+
const result = applyTransform(input)
|
|
27
|
+
expect(result).toContain("expand")
|
|
28
|
+
expect(result).not.toContain("flex")
|
|
29
|
+
expect(result).not.toContain("TODO")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('converts flex="1" to expand', () => {
|
|
33
|
+
const input = `
|
|
34
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
35
|
+
export default function Test() {
|
|
36
|
+
return <StackView flex="1">Test</StackView>
|
|
37
|
+
}
|
|
38
|
+
`.trim()
|
|
39
|
+
|
|
40
|
+
const result = applyTransform(input)
|
|
41
|
+
expect(result).toContain("expand")
|
|
42
|
+
expect(result).not.toContain("flex")
|
|
43
|
+
expect(result).not.toContain("TODO")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('expands flex="1 1 0" into grow, shrink, and basis', () => {
|
|
47
|
+
const input = `
|
|
48
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
49
|
+
export default function Test() {
|
|
50
|
+
return <StackView flex="1 1 0">Test</StackView>
|
|
51
|
+
}
|
|
52
|
+
`.trim()
|
|
53
|
+
|
|
54
|
+
const result = applyTransform(input)
|
|
55
|
+
expect(result).toContain("grow={1}")
|
|
56
|
+
expect(result).toContain("shrink={1}")
|
|
57
|
+
expect(result).toContain('basis="0"')
|
|
58
|
+
expect(result).not.toContain("flex")
|
|
59
|
+
expect(result).not.toContain("TODO")
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('expands flex="1 0 0%" into grow, shrink, and basis', () => {
|
|
63
|
+
const input = `
|
|
64
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
65
|
+
export default function Test() {
|
|
66
|
+
return <StackView flex="1 0 0%">Test</StackView>
|
|
67
|
+
}
|
|
68
|
+
`.trim()
|
|
69
|
+
|
|
70
|
+
const result = applyTransform(input)
|
|
71
|
+
expect(result).toContain("grow={1}")
|
|
72
|
+
expect(result).toContain("shrink={0}")
|
|
73
|
+
expect(result).toContain('basis="0%"')
|
|
74
|
+
expect(result).not.toContain("flex")
|
|
75
|
+
expect(result).not.toContain("TODO")
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('expands flex="0 0 auto" into grow, shrink, and basis', () => {
|
|
79
|
+
const input = `
|
|
80
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
81
|
+
export default function Test() {
|
|
82
|
+
return <StackView flex="0 0 auto">Test</StackView>
|
|
83
|
+
}
|
|
84
|
+
`.trim()
|
|
85
|
+
|
|
86
|
+
const result = applyTransform(input)
|
|
87
|
+
expect(result).toContain("grow={0}")
|
|
88
|
+
expect(result).toContain("shrink={0}")
|
|
89
|
+
expect(result).toContain('basis="auto"')
|
|
90
|
+
expect(result).not.toContain("flex")
|
|
91
|
+
expect(result).not.toContain("TODO")
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it("expands flex={0} into grow={0} shrink={1} basis='0'", () => {
|
|
95
|
+
const input = `
|
|
96
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
97
|
+
export default function Test() {
|
|
98
|
+
return <StackView flex={0}>Test</StackView>
|
|
99
|
+
}
|
|
100
|
+
`.trim()
|
|
101
|
+
|
|
102
|
+
const result = applyTransform(input)
|
|
103
|
+
expect(result).toContain("grow={0}")
|
|
104
|
+
expect(result).toContain("shrink={1}")
|
|
105
|
+
expect(result).toContain('basis="0"')
|
|
106
|
+
expect(result).not.toContain("flex=")
|
|
107
|
+
expect(result).not.toContain("TODO")
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it("adds a TODO for a dynamic flex value", () => {
|
|
111
|
+
const input = `
|
|
112
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
113
|
+
export default function Test() {
|
|
114
|
+
return <StackView flex={flexValue}>Test</StackView>
|
|
115
|
+
}
|
|
116
|
+
`.trim()
|
|
117
|
+
|
|
118
|
+
const result = applyTransform(input)
|
|
119
|
+
expect(result).toContain("TODO")
|
|
120
|
+
expect(result).toContain("flex={flexValue}")
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('adds a TODO for flex="auto"', () => {
|
|
124
|
+
const input = `
|
|
125
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
126
|
+
export default function Test() {
|
|
127
|
+
return <StackView flex="auto">Test</StackView>
|
|
128
|
+
}
|
|
129
|
+
`.trim()
|
|
130
|
+
|
|
131
|
+
const result = applyTransform(input)
|
|
132
|
+
expect(result).toContain("TODO")
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it("returns null when StackView has no flex prop", () => {
|
|
136
|
+
const input = `
|
|
137
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
138
|
+
export default function Test() {
|
|
139
|
+
return <StackView gap={2}>Test</StackView>
|
|
140
|
+
}
|
|
141
|
+
`.trim()
|
|
142
|
+
|
|
143
|
+
expect(applyTransform(input)).toBe(null)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it("returns null when StackView is not imported from tapestry-react", () => {
|
|
147
|
+
const input = `
|
|
148
|
+
import { StackView } from "some-other-package"
|
|
149
|
+
export default function Test() {
|
|
150
|
+
return <StackView flex={1}>Test</StackView>
|
|
151
|
+
}
|
|
152
|
+
`.trim()
|
|
153
|
+
|
|
154
|
+
expect(applyTransform(input)).toBe(null)
|
|
155
|
+
})
|
|
156
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
|
|
2
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
3
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
4
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
5
|
+
|
|
6
|
+
function parseFlexShorthand(
|
|
7
|
+
value: string
|
|
8
|
+
): { basis: string; grow: number; shrink: number } | null {
|
|
9
|
+
const parts = value.trim().split(/\s+/)
|
|
10
|
+
if (parts.length !== 3) return null
|
|
11
|
+
|
|
12
|
+
const grow = Number(parts[0])
|
|
13
|
+
const shrink = Number(parts[1])
|
|
14
|
+
const basis = parts[2]
|
|
15
|
+
|
|
16
|
+
if (!Number.isFinite(grow) || !Number.isFinite(shrink)) return null
|
|
17
|
+
|
|
18
|
+
return { basis, grow, shrink }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const TODO_TEXT =
|
|
22
|
+
"could not migrate flex automatically; use grow, shrink, basis, or expand individually"
|
|
23
|
+
|
|
24
|
+
export default attributeTransformFactory({
|
|
25
|
+
condition: hasAttribute("flex"),
|
|
26
|
+
targetComponent: "StackView",
|
|
27
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
28
|
+
transform: (element, { j }) => {
|
|
29
|
+
const flexAttr = getAttribute({ element, name: "flex" })
|
|
30
|
+
if (!flexAttr) return false
|
|
31
|
+
|
|
32
|
+
const value = flexAttr.value
|
|
33
|
+
const attrs = element.openingElement.attributes
|
|
34
|
+
|
|
35
|
+
// flex={1} or flex="1" → expand
|
|
36
|
+
const isExpandValue =
|
|
37
|
+
(value?.type === "JSXExpressionContainer" &&
|
|
38
|
+
value.expression.type === "NumericLiteral" &&
|
|
39
|
+
(value.expression as { value: number }).value === 1) ||
|
|
40
|
+
(value?.type === "StringLiteral" &&
|
|
41
|
+
(value as { value: string }).value === "1")
|
|
42
|
+
|
|
43
|
+
if (isExpandValue) {
|
|
44
|
+
const idx = attrs.indexOf(flexAttr)
|
|
45
|
+
attrs.splice(idx, 1, j.jsxAttribute(j.jsxIdentifier("expand")))
|
|
46
|
+
return true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// flex={0} → grow={0} shrink={1} basis="0"
|
|
50
|
+
if (
|
|
51
|
+
value?.type === "JSXExpressionContainer" &&
|
|
52
|
+
value.expression.type === "NumericLiteral" &&
|
|
53
|
+
(value.expression as { value: number }).value === 0
|
|
54
|
+
) {
|
|
55
|
+
const idx = attrs.indexOf(flexAttr)
|
|
56
|
+
attrs.splice(
|
|
57
|
+
idx,
|
|
58
|
+
1,
|
|
59
|
+
j.jsxAttribute(
|
|
60
|
+
j.jsxIdentifier("grow"),
|
|
61
|
+
j.jsxExpressionContainer(j.numericLiteral(0))
|
|
62
|
+
),
|
|
63
|
+
j.jsxAttribute(
|
|
64
|
+
j.jsxIdentifier("shrink"),
|
|
65
|
+
j.jsxExpressionContainer(j.numericLiteral(1))
|
|
66
|
+
),
|
|
67
|
+
j.jsxAttribute(j.jsxIdentifier("basis"), j.stringLiteral("0"))
|
|
68
|
+
)
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// flex="N N basis" → grow={N} shrink={N} basis="X"
|
|
73
|
+
if (value?.type === "StringLiteral") {
|
|
74
|
+
const parsed = parseFlexShorthand((value as { value: string }).value)
|
|
75
|
+
if (parsed) {
|
|
76
|
+
const idx = attrs.indexOf(flexAttr)
|
|
77
|
+
attrs.splice(
|
|
78
|
+
idx,
|
|
79
|
+
1,
|
|
80
|
+
j.jsxAttribute(
|
|
81
|
+
j.jsxIdentifier("grow"),
|
|
82
|
+
j.jsxExpressionContainer(j.numericLiteral(parsed.grow))
|
|
83
|
+
),
|
|
84
|
+
j.jsxAttribute(
|
|
85
|
+
j.jsxIdentifier("shrink"),
|
|
86
|
+
j.jsxExpressionContainer(j.numericLiteral(parsed.shrink))
|
|
87
|
+
),
|
|
88
|
+
j.jsxAttribute(
|
|
89
|
+
j.jsxIdentifier("basis"),
|
|
90
|
+
j.stringLiteral(parsed.basis)
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
addCommentToAttribute({ attribute: flexAttr, j, text: TODO_TEXT })
|
|
98
|
+
return true
|
|
99
|
+
},
|
|
100
|
+
})
|