@planningcenter/tapestry-migration-cli 3.4.1 → 3.6.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 +59 -0
- package/dist/tapestry-react-shim.cjs +5 -1
- package/package.json +3 -3
- package/src/availableComponents.ts +14 -0
- package/src/components/button/transforms/tooltipToWrapper.test.ts +50 -3
- package/src/components/button/transforms/tooltipToWrapper.ts +18 -0
- package/src/components/dropdown/index.test.ts +67 -0
- package/src/components/dropdown/index.ts +4 -0
- package/src/components/dropdown/transforms/placementIdToMenu.test.ts +6 -1
- package/src/components/dropdown/transforms/placementIdToMenu.ts +1 -1
- package/src/components/dropdown/transforms/variantToKind.test.ts +292 -0
- package/src/components/dropdown/transforms/variantToKind.ts +138 -0
- package/src/components/dropdown/transforms/wrapIconTrigger.test.ts +336 -0
- package/src/components/dropdown/transforms/wrapIconTrigger.ts +233 -0
- package/src/components/flex/index.test.ts +140 -0
- package/src/components/flex/index.ts +40 -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/axisToDirection.test.ts +140 -0
- package/src/components/flex/transforms/axisToDirection.ts +51 -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/innerRefToRef.test.ts +100 -0
- package/src/components/flex/transforms/innerRefToRef.ts +14 -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/setDefaultAxis.test.ts +114 -0
- package/src/components/flex/transforms/setDefaultAxis.ts +29 -0
- package/src/components/flex/transforms/spacingToGap.test.ts +176 -0
- package/src/components/flex/transforms/spacingToGap.ts +49 -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/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 +34 -18
- package/src/migrationList.ts +22 -0
- package/src/utils/componentLabel.test.ts +61 -0
- package/src/utils/componentLabel.ts +15 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./index"
|
|
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("flex orchestrator", () => {
|
|
18
|
+
it("applies setDefaultAxis through the chain", () => {
|
|
19
|
+
const input = `
|
|
20
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
21
|
+
|
|
22
|
+
export default function Test() {
|
|
23
|
+
return <StackView>Test</StackView>
|
|
24
|
+
}
|
|
25
|
+
`.trim()
|
|
26
|
+
|
|
27
|
+
const result = applyTransform(input)
|
|
28
|
+
expect(result).toContain('<Flex direction="column">Test</Flex>')
|
|
29
|
+
expect(result).not.toContain("axis")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("applies axisToDirection through the chain", () => {
|
|
33
|
+
const input = `
|
|
34
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
35
|
+
|
|
36
|
+
export default function Test() {
|
|
37
|
+
return <StackView axis="horizontal">Test</StackView>
|
|
38
|
+
}
|
|
39
|
+
`.trim()
|
|
40
|
+
|
|
41
|
+
const result = applyTransform(input)
|
|
42
|
+
expect(result).toContain('<Flex direction="row">Test</Flex>')
|
|
43
|
+
expect(result).not.toContain("axis")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("applies alignmentToAlign through the chain", () => {
|
|
47
|
+
const input = `
|
|
48
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
49
|
+
|
|
50
|
+
export default function Test() {
|
|
51
|
+
return <StackView axis="horizontal" alignment="center">Test</StackView>
|
|
52
|
+
}
|
|
53
|
+
`.trim()
|
|
54
|
+
|
|
55
|
+
const result = applyTransform(input)
|
|
56
|
+
expect(result).toContain('align="center"')
|
|
57
|
+
expect(result).not.toContain("alignment")
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it("applies distributionToJustify through the chain", () => {
|
|
61
|
+
const input = `
|
|
62
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
63
|
+
|
|
64
|
+
export default function Test() {
|
|
65
|
+
return <StackView axis="horizontal" distribution="center">Test</StackView>
|
|
66
|
+
}
|
|
67
|
+
`.trim()
|
|
68
|
+
|
|
69
|
+
const result = applyTransform(input)
|
|
70
|
+
expect(result).toContain('justify="center"')
|
|
71
|
+
expect(result).not.toContain("distribution")
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it("applies spacingToGap through the chain", () => {
|
|
75
|
+
const input = `
|
|
76
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
77
|
+
|
|
78
|
+
export default function Test() {
|
|
79
|
+
return <StackView axis="horizontal" spacing={2}>Test</StackView>
|
|
80
|
+
}
|
|
81
|
+
`.trim()
|
|
82
|
+
|
|
83
|
+
const result = applyTransform(input)
|
|
84
|
+
expect(result).toContain("gap={2}")
|
|
85
|
+
expect(result).not.toContain("spacing")
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it("applies innerRefToRef through the chain", () => {
|
|
89
|
+
const input = `
|
|
90
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
91
|
+
|
|
92
|
+
export default function Test() {
|
|
93
|
+
return <StackView axis="horizontal" innerRef={myRef}>Test</StackView>
|
|
94
|
+
}
|
|
95
|
+
`.trim()
|
|
96
|
+
|
|
97
|
+
const result = applyTransform(input)
|
|
98
|
+
expect(result).toContain("ref={myRef}")
|
|
99
|
+
expect(result).not.toContain("innerRef")
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it("applies moveFlexImport through the chain", () => {
|
|
103
|
+
const input = `
|
|
104
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
105
|
+
|
|
106
|
+
export default function Test() {
|
|
107
|
+
return <StackView axis="horizontal">Test</StackView>
|
|
108
|
+
}
|
|
109
|
+
`.trim()
|
|
110
|
+
|
|
111
|
+
const result = applyTransform(input)
|
|
112
|
+
expect(result).toContain('import { Flex } from "@planningcenter/tapestry"')
|
|
113
|
+
expect(result).not.toContain("StackView")
|
|
114
|
+
expect(result).not.toContain('from "@planningcenter/tapestry-react"')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("returns null when there is nothing to transform", () => {
|
|
118
|
+
const input = `
|
|
119
|
+
import { Flex } from "@planningcenter/tapestry"
|
|
120
|
+
|
|
121
|
+
export default function Test() {
|
|
122
|
+
return <Flex>Test</Flex>
|
|
123
|
+
}
|
|
124
|
+
`.trim()
|
|
125
|
+
|
|
126
|
+
expect(applyTransform(input)).toBe(null)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("returns null for an empty file", () => {
|
|
130
|
+
expect(applyTransform("")).toBe(null)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("does not throw for arbitrary source", () => {
|
|
134
|
+
expect(() =>
|
|
135
|
+
applyTransform(
|
|
136
|
+
`import React from "react"\nexport default function Noop() { return <div /> }`
|
|
137
|
+
)
|
|
138
|
+
).not.toThrow()
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import alignmentToAlign from "./transforms/alignmentToAlign"
|
|
4
|
+
import axisToDirection from "./transforms/axisToDirection"
|
|
5
|
+
import distributionToJustify from "./transforms/distributionToJustify"
|
|
6
|
+
import innerRefToRef from "./transforms/innerRefToRef"
|
|
7
|
+
import moveFlexImport from "./transforms/moveFlexImport"
|
|
8
|
+
import setDefaultAxis from "./transforms/setDefaultAxis"
|
|
9
|
+
import spacingToGap from "./transforms/spacingToGap"
|
|
10
|
+
|
|
11
|
+
const transform: Transform = (fileInfo, api, options) => {
|
|
12
|
+
let currentSource = fileInfo.source
|
|
13
|
+
let hasAnyChanges = false
|
|
14
|
+
|
|
15
|
+
const transforms = [
|
|
16
|
+
setDefaultAxis,
|
|
17
|
+
axisToDirection,
|
|
18
|
+
alignmentToAlign,
|
|
19
|
+
distributionToJustify,
|
|
20
|
+
spacingToGap,
|
|
21
|
+
innerRefToRef,
|
|
22
|
+
moveFlexImport,
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
for (const individualTransform of transforms) {
|
|
26
|
+
const result = individualTransform(
|
|
27
|
+
{ ...fileInfo, source: currentSource },
|
|
28
|
+
api,
|
|
29
|
+
options
|
|
30
|
+
)
|
|
31
|
+
if (result && result !== currentSource) {
|
|
32
|
+
currentSource = result as string
|
|
33
|
+
hasAnyChanges = true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return hasAnyChanges ? currentSource : null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default transform
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./alignmentToAlign"
|
|
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("alignmentToAlign transform", () => {
|
|
18
|
+
it("renames alignment to align", () => {
|
|
19
|
+
const input = `
|
|
20
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
21
|
+
|
|
22
|
+
export default function Test() {
|
|
23
|
+
return <StackView alignment="center">Test</StackView>
|
|
24
|
+
}
|
|
25
|
+
`.trim()
|
|
26
|
+
|
|
27
|
+
const result = applyTransform(input)
|
|
28
|
+
expect(result).toContain('align="center"')
|
|
29
|
+
expect(result).not.toContain("alignment")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("resolves StackView.CENTER constant to align", () => {
|
|
33
|
+
const input = `
|
|
34
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
35
|
+
|
|
36
|
+
export default function Test() {
|
|
37
|
+
return <StackView alignment={StackView.CENTER}>Test</StackView>
|
|
38
|
+
}
|
|
39
|
+
`.trim()
|
|
40
|
+
|
|
41
|
+
const result = applyTransform(input)
|
|
42
|
+
expect(result).toContain('align="center"')
|
|
43
|
+
expect(result).not.toContain("alignment")
|
|
44
|
+
expect(result).not.toContain("StackView.CENTER")
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("resolves StackView.START constant to align", () => {
|
|
48
|
+
const input = `
|
|
49
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
50
|
+
|
|
51
|
+
export default function Test() {
|
|
52
|
+
return <StackView alignment={StackView.START}>Test</StackView>
|
|
53
|
+
}
|
|
54
|
+
`.trim()
|
|
55
|
+
|
|
56
|
+
const result = applyTransform(input)
|
|
57
|
+
expect(result).toContain('align="start"')
|
|
58
|
+
expect(result).not.toContain("StackView.START")
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it("resolves StackView.END constant to align", () => {
|
|
62
|
+
const input = `
|
|
63
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
64
|
+
|
|
65
|
+
export default function Test() {
|
|
66
|
+
return <StackView alignment={StackView.END}>Test</StackView>
|
|
67
|
+
}
|
|
68
|
+
`.trim()
|
|
69
|
+
|
|
70
|
+
const result = applyTransform(input)
|
|
71
|
+
expect(result).toContain('align="end"')
|
|
72
|
+
expect(result).not.toContain("StackView.END")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("resolves StackView.STRETCH constant to align", () => {
|
|
76
|
+
const input = `
|
|
77
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
78
|
+
|
|
79
|
+
export default function Test() {
|
|
80
|
+
return <StackView alignment={StackView.STRETCH}>Test</StackView>
|
|
81
|
+
}
|
|
82
|
+
`.trim()
|
|
83
|
+
|
|
84
|
+
const result = applyTransform(input)
|
|
85
|
+
expect(result).toContain('align="stretch"')
|
|
86
|
+
expect(result).not.toContain("StackView.STRETCH")
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it("resolves StackView.BASELINE constant to align", () => {
|
|
90
|
+
const input = `
|
|
91
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
92
|
+
|
|
93
|
+
export default function Test() {
|
|
94
|
+
return <StackView alignment={StackView.BASELINE}>Test</StackView>
|
|
95
|
+
}
|
|
96
|
+
`.trim()
|
|
97
|
+
|
|
98
|
+
const result = applyTransform(input)
|
|
99
|
+
expect(result).toContain('align="baseline"')
|
|
100
|
+
expect(result).not.toContain("StackView.BASELINE")
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it("flags alignment={StackView.FILL} with a TODO and leaves it in place", () => {
|
|
104
|
+
const input = `
|
|
105
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
106
|
+
|
|
107
|
+
export default function Test() {
|
|
108
|
+
return <StackView alignment={StackView.FILL}>Test</StackView>
|
|
109
|
+
}
|
|
110
|
+
`.trim()
|
|
111
|
+
|
|
112
|
+
const result = applyTransform(input)
|
|
113
|
+
expect(result).toContain("alignment={StackView.FILL}")
|
|
114
|
+
expect(result).toContain("TODO: tapestry-migration (alignment)")
|
|
115
|
+
expect(result).not.toContain('align="')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('flags alignment="fill" with a TODO and leaves it in place', () => {
|
|
119
|
+
const input = `
|
|
120
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
121
|
+
|
|
122
|
+
export default function Test() {
|
|
123
|
+
return <StackView alignment="fill">Test</StackView>
|
|
124
|
+
}
|
|
125
|
+
`.trim()
|
|
126
|
+
|
|
127
|
+
const result = applyTransform(input)
|
|
128
|
+
expect(result).toContain('alignment="fill"')
|
|
129
|
+
expect(result).toContain("TODO: tapestry-migration (alignment)")
|
|
130
|
+
expect(result).not.toContain('align="')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("flags a dynamic alignment value with a TODO", () => {
|
|
134
|
+
const input = `
|
|
135
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
136
|
+
|
|
137
|
+
export default function Test({ alignProp }) {
|
|
138
|
+
return <StackView alignment={alignProp}>Test</StackView>
|
|
139
|
+
}
|
|
140
|
+
`.trim()
|
|
141
|
+
|
|
142
|
+
const result = applyTransform(input)
|
|
143
|
+
expect(result).toContain("alignment={alignProp}")
|
|
144
|
+
expect(result).toContain("TODO: tapestry-migration (alignment)")
|
|
145
|
+
expect(result).not.toContain('align="')
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
it("respects an aliased StackView import", () => {
|
|
149
|
+
const input = `
|
|
150
|
+
import { StackView as Stack } from "@planningcenter/tapestry-react"
|
|
151
|
+
|
|
152
|
+
export default function Test() {
|
|
153
|
+
return <Stack alignment="center">Test</Stack>
|
|
154
|
+
}
|
|
155
|
+
`.trim()
|
|
156
|
+
|
|
157
|
+
const result = applyTransform(input)
|
|
158
|
+
expect(result).toContain('align="center"')
|
|
159
|
+
expect(result).not.toContain("alignment")
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it("returns null when there is no alignment to migrate", () => {
|
|
163
|
+
const input = `
|
|
164
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
165
|
+
|
|
166
|
+
export default function Test() {
|
|
167
|
+
return <StackView>Test</StackView>
|
|
168
|
+
}
|
|
169
|
+
`.trim()
|
|
170
|
+
|
|
171
|
+
expect(applyTransform(input)).toBe(null)
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it("returns null when StackView is not imported from tapestry-react", () => {
|
|
175
|
+
const input = `
|
|
176
|
+
import { StackView } from "some-other-package"
|
|
177
|
+
|
|
178
|
+
export default function Test() {
|
|
179
|
+
return <StackView alignment="center">Test</StackView>
|
|
180
|
+
}
|
|
181
|
+
`.trim()
|
|
182
|
+
|
|
183
|
+
expect(applyTransform(input)).toBe(null)
|
|
184
|
+
})
|
|
185
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
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_ALIGN_VALUES = new Set([
|
|
10
|
+
"start",
|
|
11
|
+
"center",
|
|
12
|
+
"end",
|
|
13
|
+
"stretch",
|
|
14
|
+
"baseline",
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
const CONSTANT_ALIGN_MAP: Record<string, string> = {
|
|
18
|
+
BASELINE: "baseline",
|
|
19
|
+
CENTER: "center",
|
|
20
|
+
END: "end",
|
|
21
|
+
FILL: "fill",
|
|
22
|
+
START: "start",
|
|
23
|
+
STRETCH: "stretch",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const transform: Transform = attributeTransformFactory({
|
|
27
|
+
condition: hasAttribute("alignment"),
|
|
28
|
+
targetComponent: "StackView",
|
|
29
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
30
|
+
transform: (element, { j, source }) => {
|
|
31
|
+
const alignmentAttribute = getAttribute({ element, name: "alignment" })
|
|
32
|
+
if (!alignmentAttribute) return false
|
|
33
|
+
|
|
34
|
+
const resolved = resolveConstantAttributeValue({
|
|
35
|
+
attribute: alignmentAttribute,
|
|
36
|
+
constantMap: CONSTANT_ALIGN_MAP,
|
|
37
|
+
})
|
|
38
|
+
const alignValue =
|
|
39
|
+
resolved !== null &&
|
|
40
|
+
(VALID_ALIGN_VALUES.has(resolved) || resolved === "fill")
|
|
41
|
+
? resolved
|
|
42
|
+
: null
|
|
43
|
+
|
|
44
|
+
if (alignValue === null) {
|
|
45
|
+
addComment({
|
|
46
|
+
element,
|
|
47
|
+
j,
|
|
48
|
+
scope: "alignment",
|
|
49
|
+
source,
|
|
50
|
+
text: "could not migrate alignment to align automatically; convert this value to start, center, end, stretch, or baseline manually",
|
|
51
|
+
})
|
|
52
|
+
return true
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (alignValue === "fill") {
|
|
56
|
+
addComment({
|
|
57
|
+
element,
|
|
58
|
+
j,
|
|
59
|
+
scope: "alignment",
|
|
60
|
+
source,
|
|
61
|
+
text: 'alignment="fill" has no Flex equivalent; remove this prop and use expand on the child element instead',
|
|
62
|
+
})
|
|
63
|
+
return true
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
alignmentAttribute.name.name = "align"
|
|
67
|
+
alignmentAttribute.value = j.stringLiteral(alignValue)
|
|
68
|
+
|
|
69
|
+
return true
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
export default transform
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./axisToDirection"
|
|
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("axisToDirection transform", () => {
|
|
18
|
+
it('maps axis="horizontal" to direction="row"', () => {
|
|
19
|
+
const input = `
|
|
20
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
21
|
+
|
|
22
|
+
export default function Test() {
|
|
23
|
+
return <StackView axis="horizontal">Test</StackView>
|
|
24
|
+
}
|
|
25
|
+
`.trim()
|
|
26
|
+
|
|
27
|
+
const result = applyTransform(input)
|
|
28
|
+
expect(result).toContain('direction="row"')
|
|
29
|
+
expect(result).not.toContain("axis")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('maps axis="vertical" to direction="column"', () => {
|
|
33
|
+
const input = `
|
|
34
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
35
|
+
|
|
36
|
+
export default function Test() {
|
|
37
|
+
return <StackView axis="vertical">Test</StackView>
|
|
38
|
+
}
|
|
39
|
+
`.trim()
|
|
40
|
+
|
|
41
|
+
const result = applyTransform(input)
|
|
42
|
+
expect(result).toContain('direction="column"')
|
|
43
|
+
expect(result).not.toContain("axis")
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("maps an expression-wrapped string literal", () => {
|
|
47
|
+
const input = `
|
|
48
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
49
|
+
|
|
50
|
+
export default function Test() {
|
|
51
|
+
return <StackView axis={"horizontal"}>Test</StackView>
|
|
52
|
+
}
|
|
53
|
+
`.trim()
|
|
54
|
+
|
|
55
|
+
const result = applyTransform(input)
|
|
56
|
+
expect(result).toContain('direction="row"')
|
|
57
|
+
expect(result).not.toContain("axis")
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it("maps the StackView.HORIZONTAL constant", () => {
|
|
61
|
+
const input = `
|
|
62
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
63
|
+
|
|
64
|
+
export default function Test() {
|
|
65
|
+
return <StackView axis={StackView.HORIZONTAL}>Test</StackView>
|
|
66
|
+
}
|
|
67
|
+
`.trim()
|
|
68
|
+
|
|
69
|
+
const result = applyTransform(input)
|
|
70
|
+
expect(result).toContain('direction="row"')
|
|
71
|
+
expect(result).not.toContain("axis")
|
|
72
|
+
expect(result).not.toContain("StackView.HORIZONTAL")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("maps the StackView.VERTICAL constant", () => {
|
|
76
|
+
const input = `
|
|
77
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
78
|
+
|
|
79
|
+
export default function Test() {
|
|
80
|
+
return <StackView axis={StackView.VERTICAL}>Test</StackView>
|
|
81
|
+
}
|
|
82
|
+
`.trim()
|
|
83
|
+
|
|
84
|
+
const result = applyTransform(input)
|
|
85
|
+
expect(result).toContain('direction="column"')
|
|
86
|
+
expect(result).not.toContain("StackView.VERTICAL")
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it("flags a dynamic axis value and leaves it in place", () => {
|
|
90
|
+
const input = `
|
|
91
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
92
|
+
|
|
93
|
+
export default function Test({ orientation }) {
|
|
94
|
+
return <StackView axis={orientation}>Test</StackView>
|
|
95
|
+
}
|
|
96
|
+
`.trim()
|
|
97
|
+
|
|
98
|
+
const result = applyTransform(input)
|
|
99
|
+
expect(result).toContain("axis={orientation}")
|
|
100
|
+
expect(result).toContain("TODO: tapestry-migration (axis)")
|
|
101
|
+
expect(result).not.toContain('direction="')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it("maps an aliased StackView import", () => {
|
|
105
|
+
const input = `
|
|
106
|
+
import { StackView as Stack } from "@planningcenter/tapestry-react"
|
|
107
|
+
|
|
108
|
+
export default function Test() {
|
|
109
|
+
return <Stack axis="horizontal">Test</Stack>
|
|
110
|
+
}
|
|
111
|
+
`.trim()
|
|
112
|
+
|
|
113
|
+
const result = applyTransform(input)
|
|
114
|
+
expect(result).toContain('direction="row"')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("returns null when there is no axis to map", () => {
|
|
118
|
+
const input = `
|
|
119
|
+
import { StackView } from "@planningcenter/tapestry-react"
|
|
120
|
+
|
|
121
|
+
export default function Test() {
|
|
122
|
+
return <StackView>Test</StackView>
|
|
123
|
+
}
|
|
124
|
+
`.trim()
|
|
125
|
+
|
|
126
|
+
expect(applyTransform(input)).toBe(null)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it("returns null when StackView is not imported from tapestry-react", () => {
|
|
130
|
+
const input = `
|
|
131
|
+
import { StackView } from "some-other-package"
|
|
132
|
+
|
|
133
|
+
export default function Test() {
|
|
134
|
+
return <StackView axis="horizontal">Test</StackView>
|
|
135
|
+
}
|
|
136
|
+
`.trim()
|
|
137
|
+
|
|
138
|
+
expect(applyTransform(input)).toBe(null)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
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 CONSTANT_AXIS_MAP: Record<string, string> = {
|
|
10
|
+
HORIZONTAL: "horizontal",
|
|
11
|
+
VERTICAL: "vertical",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const AXIS_DIRECTION_MAP: Record<string, string> = {
|
|
15
|
+
horizontal: "row",
|
|
16
|
+
vertical: "column",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const transform: Transform = attributeTransformFactory({
|
|
20
|
+
condition: hasAttribute("axis"),
|
|
21
|
+
targetComponent: "StackView",
|
|
22
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
23
|
+
transform: (element, { j, source }) => {
|
|
24
|
+
const axisAttribute = getAttribute({ element, name: "axis" })
|
|
25
|
+
if (!axisAttribute) return false
|
|
26
|
+
|
|
27
|
+
const resolved = resolveConstantAttributeValue({
|
|
28
|
+
attribute: axisAttribute,
|
|
29
|
+
constantMap: CONSTANT_AXIS_MAP,
|
|
30
|
+
})
|
|
31
|
+
const direction = resolved ? AXIS_DIRECTION_MAP[resolved] : undefined
|
|
32
|
+
|
|
33
|
+
if (!direction) {
|
|
34
|
+
addComment({
|
|
35
|
+
element,
|
|
36
|
+
j,
|
|
37
|
+
scope: "axis",
|
|
38
|
+
source,
|
|
39
|
+
text: "could not migrate axis to direction automatically; convert this value to row or column manually",
|
|
40
|
+
})
|
|
41
|
+
return true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
axisAttribute.name.name = "direction"
|
|
45
|
+
axisAttribute.value = j.stringLiteral(direction)
|
|
46
|
+
|
|
47
|
+
return true
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
export default transform
|