@planningcenter/tapestry-migration-cli 2.4.0-rc.10 → 2.4.0-rc.11
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/tapestry-migration-cli",
|
|
3
|
-
"version": "2.4.0-rc.
|
|
3
|
+
"version": "2.4.0-rc.11",
|
|
4
4
|
"description": "CLI tool for Tapestry migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "284e43162c5ec8403699fbfec99ae235a380a7dc"
|
|
55
55
|
}
|
|
@@ -4,6 +4,7 @@ import childrenToLabel from "./transforms/childrenToLabel"
|
|
|
4
4
|
import convertStyleProps from "./transforms/convertStyleProps"
|
|
5
5
|
import inlineMemberToKind from "./transforms/inlineMemberToKind"
|
|
6
6
|
import inlinePropToKind from "./transforms/inlinePropToKind"
|
|
7
|
+
import moveLinkImport from "./transforms/moveLinkImport"
|
|
7
8
|
import removeAs from "./transforms/removeAs"
|
|
8
9
|
import reviewStyles from "./transforms/reviewStyles"
|
|
9
10
|
import targetBlankToExternal from "./transforms/targetBlankToExternal"
|
|
@@ -24,6 +25,7 @@ const transform: Transform = (fileInfo, api, options) => {
|
|
|
24
25
|
reviewStyles,
|
|
25
26
|
convertStyleProps,
|
|
26
27
|
unsupportedProps,
|
|
28
|
+
moveLinkImport,
|
|
27
29
|
]
|
|
28
30
|
|
|
29
31
|
for (const individualTransform of transforms) {
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./moveLinkImport"
|
|
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("moveLinkImport transform", () => {
|
|
18
|
+
describe("basic transformations", () => {
|
|
19
|
+
it("should move Link import from tapestry-react to tapestry", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
export default function Test() {
|
|
24
|
+
return <Link href="/test">Test</Link>
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const expected = `
|
|
29
|
+
import { Link } from "@planningcenter/tapestry";
|
|
30
|
+
|
|
31
|
+
export default function Test() {
|
|
32
|
+
return <Link href="/test">Test</Link>
|
|
33
|
+
}
|
|
34
|
+
`.trim()
|
|
35
|
+
|
|
36
|
+
const result = applyTransform(input)
|
|
37
|
+
expect(result?.trim()).toBe(expected)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it("should handle multiple Link components", () => {
|
|
41
|
+
const input = `
|
|
42
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
43
|
+
|
|
44
|
+
export default function Test() {
|
|
45
|
+
return (
|
|
46
|
+
<div>
|
|
47
|
+
<Link href="/home">Home</Link>
|
|
48
|
+
<Link href="/about">About</Link>
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
`.trim()
|
|
53
|
+
|
|
54
|
+
const expected = `
|
|
55
|
+
import { Link } from "@planningcenter/tapestry";
|
|
56
|
+
|
|
57
|
+
export default function Test() {
|
|
58
|
+
return (
|
|
59
|
+
<div>
|
|
60
|
+
<Link href="/home">Home</Link>
|
|
61
|
+
<Link href="/about">About</Link>
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
`.trim()
|
|
66
|
+
|
|
67
|
+
const result = applyTransform(input)
|
|
68
|
+
expect(result?.trim()).toBe(expected)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it("should preserve other imports from tapestry-react", () => {
|
|
72
|
+
const input = `
|
|
73
|
+
import { Link, Button, Input } from "@planningcenter/tapestry-react"
|
|
74
|
+
|
|
75
|
+
export default function Test() {
|
|
76
|
+
return <Link href="/test">Test</Link>
|
|
77
|
+
}
|
|
78
|
+
`.trim()
|
|
79
|
+
|
|
80
|
+
const result = applyTransform(input)
|
|
81
|
+
expect(result).toContain(
|
|
82
|
+
'import { Button, Input } from "@planningcenter/tapestry-react"'
|
|
83
|
+
)
|
|
84
|
+
expect(result).toContain(
|
|
85
|
+
'import { Link } from "@planningcenter/tapestry"'
|
|
86
|
+
)
|
|
87
|
+
expect(result).toContain('<Link href="/test">Test</Link>')
|
|
88
|
+
expect(result).not.toContain(
|
|
89
|
+
'Link, Button } from "@planningcenter/tapestry-react"'
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe("import conflict handling", () => {
|
|
95
|
+
it("should use alias when Link already imported from tapestry", () => {
|
|
96
|
+
const input = `
|
|
97
|
+
import { Link } from "@planningcenter/tapestry"
|
|
98
|
+
import { Link as ReactLink } from "@planningcenter/tapestry-react"
|
|
99
|
+
|
|
100
|
+
export default function Test() {
|
|
101
|
+
return <ReactLink href="/test">Test</ReactLink>
|
|
102
|
+
}
|
|
103
|
+
`.trim()
|
|
104
|
+
|
|
105
|
+
const result = applyTransform(input)
|
|
106
|
+
expect(result).toContain(
|
|
107
|
+
'import { Link } from "@planningcenter/tapestry"'
|
|
108
|
+
)
|
|
109
|
+
expect(result).toContain('<Link href="/test">Test</Link>')
|
|
110
|
+
expect(result).not.toContain('from "@planningcenter/tapestry-react"')
|
|
111
|
+
expect(result).not.toContain("ReactLink")
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("should handle existing tapestry import with other components", () => {
|
|
115
|
+
const input = `
|
|
116
|
+
import { Button, Input } from "@planningcenter/tapestry"
|
|
117
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
118
|
+
|
|
119
|
+
export default function Test() {
|
|
120
|
+
return <Link href="/test">Test</Link>
|
|
121
|
+
}
|
|
122
|
+
`.trim()
|
|
123
|
+
|
|
124
|
+
const result = applyTransform(input)
|
|
125
|
+
expect(result).toContain(
|
|
126
|
+
'import { Button, Input, Link } from "@planningcenter/tapestry"'
|
|
127
|
+
)
|
|
128
|
+
expect(result).toContain('<Link href="/test">Test</Link>')
|
|
129
|
+
expect(result).not.toContain('from "@planningcenter/tapestry-react"')
|
|
130
|
+
})
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
describe("edge cases", () => {
|
|
134
|
+
it("should not transform if Link is not imported from tapestry-react", () => {
|
|
135
|
+
const input = `
|
|
136
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
137
|
+
|
|
138
|
+
export default function Test() {
|
|
139
|
+
return <Button>Test</Button>
|
|
140
|
+
}
|
|
141
|
+
`.trim()
|
|
142
|
+
|
|
143
|
+
const result = applyTransform(input)
|
|
144
|
+
expect(result).toBe(null)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it("should handle Link with aliased import", () => {
|
|
148
|
+
const input = `
|
|
149
|
+
import { Link as ReactLink } from "@planningcenter/tapestry-react"
|
|
150
|
+
|
|
151
|
+
export default function Test() {
|
|
152
|
+
return <ReactLink href="/test">Test</ReactLink>
|
|
153
|
+
}
|
|
154
|
+
`.trim()
|
|
155
|
+
|
|
156
|
+
const result = applyTransform(input)
|
|
157
|
+
expect(result).toContain(
|
|
158
|
+
'import { Link } from "@planningcenter/tapestry"'
|
|
159
|
+
)
|
|
160
|
+
expect(result).toContain('<Link href="/test">Test</Link>')
|
|
161
|
+
expect(result).not.toContain("ReactLink")
|
|
162
|
+
expect(result).not.toContain('from "@planningcenter/tapestry-react"')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it("should handle self-closing Link components", () => {
|
|
166
|
+
const input = `
|
|
167
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
168
|
+
|
|
169
|
+
export default function Test() {
|
|
170
|
+
return <Link href="/test" />
|
|
171
|
+
}
|
|
172
|
+
`.trim()
|
|
173
|
+
|
|
174
|
+
const expected = `
|
|
175
|
+
import { Link } from "@planningcenter/tapestry";
|
|
176
|
+
|
|
177
|
+
export default function Test() {
|
|
178
|
+
return <Link href="/test" />
|
|
179
|
+
}
|
|
180
|
+
`.trim()
|
|
181
|
+
|
|
182
|
+
const result = applyTransform(input)
|
|
183
|
+
expect(result?.trim()).toBe(expected)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it("should handle Link with props", () => {
|
|
187
|
+
const input = `
|
|
188
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
189
|
+
|
|
190
|
+
export default function Test() {
|
|
191
|
+
return <Link href="/test" kind="primary" onClick={handleClick}>Test</Link>
|
|
192
|
+
}
|
|
193
|
+
`.trim()
|
|
194
|
+
|
|
195
|
+
const result = applyTransform(input)
|
|
196
|
+
expect(result).toContain(
|
|
197
|
+
'import { Link } from "@planningcenter/tapestry"'
|
|
198
|
+
)
|
|
199
|
+
expect(result).toContain('kind="primary"')
|
|
200
|
+
expect(result).toContain("onClick={handleClick}")
|
|
201
|
+
expect(result).not.toContain('from "@planningcenter/tapestry-react"')
|
|
202
|
+
})
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
describe("no changes scenarios", () => {
|
|
206
|
+
it("should return null when no Link import exists", () => {
|
|
207
|
+
const input = `
|
|
208
|
+
import React from "react"
|
|
209
|
+
|
|
210
|
+
export default function Test() {
|
|
211
|
+
return <div>Hello</div>
|
|
212
|
+
}
|
|
213
|
+
`.trim()
|
|
214
|
+
|
|
215
|
+
const result = applyTransform(input)
|
|
216
|
+
expect(result).toBe(null)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it("should return null when Link already imported from tapestry", () => {
|
|
220
|
+
const input = `
|
|
221
|
+
import { Link } from "@planningcenter/tapestry"
|
|
222
|
+
|
|
223
|
+
export default function Test() {
|
|
224
|
+
return <Link href="/test">Test</Link>
|
|
225
|
+
}
|
|
226
|
+
`.trim()
|
|
227
|
+
|
|
228
|
+
const result = applyTransform(input)
|
|
229
|
+
expect(result).toBe(null)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it("should return null for empty file", () => {
|
|
233
|
+
const result = applyTransform("")
|
|
234
|
+
expect(result).toBe(null)
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
describe("complex scenarios", () => {
|
|
239
|
+
it("should handle mixed imports and multiple components", () => {
|
|
240
|
+
const input = `
|
|
241
|
+
import React from "react"
|
|
242
|
+
import { Link, Button } from "@planningcenter/tapestry-react"
|
|
243
|
+
import { Input } from "@planningcenter/tapestry"
|
|
244
|
+
|
|
245
|
+
export default function Test() {
|
|
246
|
+
return (
|
|
247
|
+
<form>
|
|
248
|
+
<Input name="email" />
|
|
249
|
+
<Button type="submit">Submit</Button>
|
|
250
|
+
<Link href="/cancel">Cancel</Link>
|
|
251
|
+
</form>
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
`.trim()
|
|
255
|
+
|
|
256
|
+
const result = applyTransform(input)
|
|
257
|
+
expect(result).toContain(
|
|
258
|
+
'import { Button } from "@planningcenter/tapestry-react"'
|
|
259
|
+
)
|
|
260
|
+
expect(result).toContain(
|
|
261
|
+
'import { Input, Link } from "@planningcenter/tapestry"'
|
|
262
|
+
)
|
|
263
|
+
expect(result).toContain('<Button type="submit">Submit</Button>')
|
|
264
|
+
expect(result).toContain('<Link href="/cancel">Cancel</Link>')
|
|
265
|
+
expect(result).not.toContain(
|
|
266
|
+
'Link, Button } from "@planningcenter/tapestry-react"'
|
|
267
|
+
)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
it("should handle Link usage in JSX expressions", () => {
|
|
271
|
+
const input = `
|
|
272
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
273
|
+
|
|
274
|
+
export default function Test({ showLink }) {
|
|
275
|
+
return (
|
|
276
|
+
<div>
|
|
277
|
+
{showLink && <Link href="/conditional">Conditional</Link>}
|
|
278
|
+
{items.map(item => <Link key={item.id} href={item.url}>{item.name}</Link>)}
|
|
279
|
+
</div>
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
`.trim()
|
|
283
|
+
|
|
284
|
+
const result = applyTransform(input)
|
|
285
|
+
expect(result).toContain(
|
|
286
|
+
'import { Link } from "@planningcenter/tapestry"'
|
|
287
|
+
)
|
|
288
|
+
expect(result).toContain('<Link href="/conditional">Conditional</Link>')
|
|
289
|
+
expect(result).toContain(
|
|
290
|
+
"<Link key={item.id} href={item.url}>{item.name}</Link>"
|
|
291
|
+
)
|
|
292
|
+
expect(result).not.toContain('from "@planningcenter/tapestry-react"')
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { componentTransformFactory } from "../../shared/transformFactories/componentTransformFactory"
|
|
4
|
+
|
|
5
|
+
const transform: Transform = componentTransformFactory({
|
|
6
|
+
condition: () => true,
|
|
7
|
+
conflictAlias: "TLink",
|
|
8
|
+
fromComponent: "Link",
|
|
9
|
+
fromPackage: "@planningcenter/tapestry-react",
|
|
10
|
+
toComponent: "Link",
|
|
11
|
+
toPackage: "@planningcenter/tapestry",
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default transform
|