@planningcenter/tapestry-migration-cli 2.4.0-rc.0 → 2.4.0-rc.10
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 +2 -2
- package/src/components/button/transforms/childrenToLabel.test.ts +5 -4
- package/src/components/button/transforms/childrenToLabel.ts +9 -39
- package/src/components/button/transforms/unsupportedProps.ts +7 -31
- package/src/components/link/index.ts +14 -2
- package/src/components/link/transforms/childrenToLabel.test.ts +331 -0
- package/src/components/link/transforms/childrenToLabel.ts +54 -0
- package/src/components/link/transforms/convertStyleProps.test.ts +391 -0
- package/src/components/link/transforms/convertStyleProps.ts +10 -0
- package/src/components/link/transforms/{inlineToKind.test.ts → inlineMemberToKind.test.ts} +2 -2
- package/src/components/link/transforms/{inlineToKind.ts → inlineMemberToKind.ts} +0 -2
- package/src/components/link/transforms/inlinePropToKind.test.ts +312 -0
- package/src/components/link/transforms/inlinePropToKind.ts +24 -0
- package/src/components/link/transforms/removeAs.test.ts +192 -0
- package/src/components/link/transforms/removeAs.ts +17 -0
- package/src/components/link/transforms/reviewStyles.test.ts +172 -0
- package/src/components/link/transforms/reviewStyles.ts +17 -0
- package/src/components/link/transforms/targetBlankToExternal.test.ts +14 -0
- package/src/components/link/transforms/unsupportedProps.test.ts +265 -0
- package/src/components/link/transforms/unsupportedProps.ts +58 -0
- package/src/components/shared/conditions/hasAttributeValue.test.ts +22 -1
- package/src/components/shared/conditions/hasAttributeValue.ts +24 -6
- package/src/components/shared/helpers/childrenToLabelHelpers.ts +43 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +35 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +5 -1
- package/src/stubs/textPlugin.ts +45 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./convertStyleProps"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string, options = {}) {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
return transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
options
|
|
14
|
+
) as string | null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("convertStyleProps transform", () => {
|
|
18
|
+
describe("visible prop - handled by theme system", () => {
|
|
19
|
+
it("should convert visible={false} to display: none like Button", () => {
|
|
20
|
+
const source = `
|
|
21
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
export function TestComponent() {
|
|
24
|
+
return <Link visible={false} href="/hidden">Hidden Link</Link>
|
|
25
|
+
}
|
|
26
|
+
`
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(source)
|
|
29
|
+
|
|
30
|
+
expect(result).toContain("style={{")
|
|
31
|
+
expect(result).toContain('display: "none"')
|
|
32
|
+
expect(result).not.toContain("visible={false}")
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it("should remove visible={true} with no style changes (true is default)", () => {
|
|
36
|
+
const source = `
|
|
37
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
38
|
+
|
|
39
|
+
export function TestComponent() {
|
|
40
|
+
return <Link visible={true} href="/visible">Visible Link</Link>
|
|
41
|
+
}
|
|
42
|
+
`
|
|
43
|
+
|
|
44
|
+
const result = applyTransform(source)
|
|
45
|
+
|
|
46
|
+
// visible={true} is default behavior, so it should be removed with no style added
|
|
47
|
+
expect(result).not.toContain("visible={true}")
|
|
48
|
+
expect(result).not.toContain("style={{") // No style should be added
|
|
49
|
+
expect(result).toContain('<Link href="/visible">Visible Link</Link>')
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe("supported props - preserved like Button", () => {
|
|
54
|
+
it("should preserve size prop as-is like Button does", () => {
|
|
55
|
+
const source = `
|
|
56
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
57
|
+
|
|
58
|
+
export function TestComponent() {
|
|
59
|
+
return <Link size="lg" href="/sized">Sized Link</Link>
|
|
60
|
+
}
|
|
61
|
+
`
|
|
62
|
+
|
|
63
|
+
const result = applyTransform(source)
|
|
64
|
+
|
|
65
|
+
// size should be preserved as a supported prop, not converted to CSS
|
|
66
|
+
expect(result).toBeNull() // No transformation needed
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it("should preserve all valid Link size values", () => {
|
|
70
|
+
const sizes = ["xs", "sm", "md", "lg", "xl"]
|
|
71
|
+
|
|
72
|
+
sizes.forEach((size) => {
|
|
73
|
+
const source = `
|
|
74
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
75
|
+
|
|
76
|
+
export function TestComponent() {
|
|
77
|
+
return <Link size="${size}" href="/test">Test Link</Link>
|
|
78
|
+
}
|
|
79
|
+
`
|
|
80
|
+
|
|
81
|
+
const result = applyTransform(source)
|
|
82
|
+
|
|
83
|
+
expect(result).toBeNull()
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe("textPlugin props", () => {
|
|
89
|
+
it("should convert weight to fontWeight", () => {
|
|
90
|
+
const source = `
|
|
91
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
92
|
+
|
|
93
|
+
export function TestComponent() {
|
|
94
|
+
return <Link weight="bold" href="/home">Bold Link</Link>
|
|
95
|
+
}
|
|
96
|
+
`
|
|
97
|
+
|
|
98
|
+
const result = applyTransform(source)
|
|
99
|
+
|
|
100
|
+
expect(result).toContain("style={{")
|
|
101
|
+
expect(result).toContain('fontWeight: "bold"')
|
|
102
|
+
expect(result).not.toContain("weight=")
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it("should convert align to textAlign", () => {
|
|
106
|
+
const source = `
|
|
107
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
108
|
+
|
|
109
|
+
export function TestComponent() {
|
|
110
|
+
return <Link align="center" href="/centered">Centered Link</Link>
|
|
111
|
+
}
|
|
112
|
+
`
|
|
113
|
+
|
|
114
|
+
const result = applyTransform(source)
|
|
115
|
+
|
|
116
|
+
expect(result).toContain("style={{")
|
|
117
|
+
expect(result).toContain('textAlign: "center"')
|
|
118
|
+
expect(result).not.toContain("align=")
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it("should convert italic to fontStyle", () => {
|
|
122
|
+
const source = `
|
|
123
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
124
|
+
|
|
125
|
+
export function TestComponent() {
|
|
126
|
+
return <Link italic href="/styled">Italic Link</Link>
|
|
127
|
+
}
|
|
128
|
+
`
|
|
129
|
+
|
|
130
|
+
const result = applyTransform(source)
|
|
131
|
+
|
|
132
|
+
expect(result).toContain("style={{")
|
|
133
|
+
expect(result).toContain('fontStyle: "italic"')
|
|
134
|
+
expect(result).not.toContain("italic ")
|
|
135
|
+
expect(result).not.toContain(" italic")
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it("should convert truncate to text truncation styles", () => {
|
|
139
|
+
const source = `
|
|
140
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
141
|
+
|
|
142
|
+
export function TestComponent() {
|
|
143
|
+
return <Link truncate href="/link">Very Long Link Text</Link>
|
|
144
|
+
}
|
|
145
|
+
`
|
|
146
|
+
|
|
147
|
+
const result = applyTransform(source)
|
|
148
|
+
|
|
149
|
+
expect(result).toContain("style={{")
|
|
150
|
+
expect(result).toContain('display: "block"')
|
|
151
|
+
expect(result).toContain('overflow: "hidden"')
|
|
152
|
+
expect(result).toContain('textOverflow: "ellipsis"')
|
|
153
|
+
expect(result).toContain('whiteSpace: "nowrap"')
|
|
154
|
+
expect(result).not.toContain("truncate ")
|
|
155
|
+
expect(result).not.toContain(" truncate")
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it("should convert underline to textDecoration", () => {
|
|
159
|
+
const source = `
|
|
160
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
161
|
+
|
|
162
|
+
export function TestComponent() {
|
|
163
|
+
return <Link underline="none" href="/no-underline">No Underline</Link>
|
|
164
|
+
}
|
|
165
|
+
`
|
|
166
|
+
|
|
167
|
+
const result = applyTransform(source)
|
|
168
|
+
|
|
169
|
+
expect(result).toContain("style={{")
|
|
170
|
+
expect(result).toContain('textDecoration: "none"')
|
|
171
|
+
expect(result).not.toContain("underline=")
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
it("should convert wrap={false} to whiteSpace nowrap", () => {
|
|
175
|
+
const source = `
|
|
176
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
177
|
+
|
|
178
|
+
export function TestComponent() {
|
|
179
|
+
return <Link wrap={false} href="/nowrap">No Wrap Link</Link>
|
|
180
|
+
}
|
|
181
|
+
`
|
|
182
|
+
|
|
183
|
+
const result = applyTransform(source)
|
|
184
|
+
|
|
185
|
+
expect(result).toContain("style={{")
|
|
186
|
+
expect(result).toContain('whiteSpace: "nowrap"')
|
|
187
|
+
expect(result).not.toContain("wrap=")
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it("should handle multiple text props together", () => {
|
|
191
|
+
const source = `
|
|
192
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
193
|
+
|
|
194
|
+
export function TestComponent() {
|
|
195
|
+
return (
|
|
196
|
+
<Link
|
|
197
|
+
weight="600"
|
|
198
|
+
align="right"
|
|
199
|
+
italic
|
|
200
|
+
truncate
|
|
201
|
+
href="/multi"
|
|
202
|
+
>
|
|
203
|
+
Multi-styled Link
|
|
204
|
+
</Link>
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
`
|
|
208
|
+
|
|
209
|
+
const result = applyTransform(source)
|
|
210
|
+
|
|
211
|
+
expect(result).toContain("style={{")
|
|
212
|
+
expect(result).toContain('fontWeight: "600"')
|
|
213
|
+
expect(result).toContain('textAlign: "right"')
|
|
214
|
+
expect(result).toContain('fontStyle: "italic"')
|
|
215
|
+
expect(result).toContain('overflow: "hidden"')
|
|
216
|
+
expect(result).toContain('textOverflow: "ellipsis"')
|
|
217
|
+
expect(result).not.toContain("weight=")
|
|
218
|
+
expect(result).not.toContain("align=")
|
|
219
|
+
expect(result).not.toContain("italic ")
|
|
220
|
+
expect(result).not.toContain(" italic")
|
|
221
|
+
expect(result).not.toContain("truncate ")
|
|
222
|
+
expect(result).not.toContain(" truncate")
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it("should handle weight with variable expression", () => {
|
|
226
|
+
const source = `
|
|
227
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
228
|
+
|
|
229
|
+
export function TestComponent({ linkWeight }) {
|
|
230
|
+
return <Link weight={linkWeight} href="/home">Link</Link>
|
|
231
|
+
}
|
|
232
|
+
`
|
|
233
|
+
|
|
234
|
+
const result = applyTransform(source)
|
|
235
|
+
|
|
236
|
+
expect(result).toContain("style={{")
|
|
237
|
+
// Note: Plugin props with variable expressions currently keep their original prop names
|
|
238
|
+
// This is a known limitation that also exists for Button's stackViewPlugin
|
|
239
|
+
expect(result).toContain("weight: linkWeight")
|
|
240
|
+
expect(result).not.toContain("weight={linkWeight}")
|
|
241
|
+
})
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
describe("standard style props", () => {
|
|
245
|
+
it("should convert standard style props to style object", () => {
|
|
246
|
+
const source = `
|
|
247
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
248
|
+
|
|
249
|
+
export function TestComponent() {
|
|
250
|
+
return (
|
|
251
|
+
<Link
|
|
252
|
+
marginBottom={8}
|
|
253
|
+
color="blue"
|
|
254
|
+
fontSize="14px"
|
|
255
|
+
href="/styled"
|
|
256
|
+
>
|
|
257
|
+
Styled Link
|
|
258
|
+
</Link>
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
`
|
|
262
|
+
|
|
263
|
+
const result = applyTransform(source)
|
|
264
|
+
|
|
265
|
+
expect(result).toContain("style={{")
|
|
266
|
+
expect(result).toContain('color: "blue"')
|
|
267
|
+
expect(result).toContain('fontSize: "14px"')
|
|
268
|
+
expect(result).not.toContain("marginBottom={8}")
|
|
269
|
+
expect(result).not.toContain('color="blue"')
|
|
270
|
+
expect(result).not.toContain('fontSize="14px"')
|
|
271
|
+
})
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
describe("mixed props", () => {
|
|
275
|
+
it("should handle combination of visible and standard style props", () => {
|
|
276
|
+
const source = `
|
|
277
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
278
|
+
|
|
279
|
+
export function TestComponent() {
|
|
280
|
+
return (
|
|
281
|
+
<Link
|
|
282
|
+
visible={false}
|
|
283
|
+
marginLeft={16}
|
|
284
|
+
color="red"
|
|
285
|
+
href="/mixed"
|
|
286
|
+
>
|
|
287
|
+
Mixed Props
|
|
288
|
+
</Link>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
`
|
|
292
|
+
|
|
293
|
+
const result = applyTransform(source)
|
|
294
|
+
|
|
295
|
+
expect(result).toContain("style={{")
|
|
296
|
+
expect(result).toContain('display: "none"') // from visible={false}
|
|
297
|
+
expect(result).toContain('color: "red"') // standard style prop
|
|
298
|
+
expect(result).not.toContain("visible={false}")
|
|
299
|
+
expect(result).not.toContain("marginLeft={16}")
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
it("should handle existing style prop and merge with converted props", () => {
|
|
303
|
+
const source = `
|
|
304
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
305
|
+
|
|
306
|
+
export function TestComponent() {
|
|
307
|
+
return (
|
|
308
|
+
<Link
|
|
309
|
+
style={{ padding: "10px", border: "1px solid gray" }}
|
|
310
|
+
visible={false}
|
|
311
|
+
marginTop={8}
|
|
312
|
+
href="/existing"
|
|
313
|
+
>
|
|
314
|
+
Existing Style
|
|
315
|
+
</Link>
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
`
|
|
319
|
+
|
|
320
|
+
const result = applyTransform(source)
|
|
321
|
+
|
|
322
|
+
expect(result).toContain("style={{")
|
|
323
|
+
expect(result).toContain('display: "none"') // from visible={false}
|
|
324
|
+
expect(result).not.toContain("visible={false}")
|
|
325
|
+
expect(result).not.toContain("marginTop={8}")
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
describe("no style props", () => {
|
|
330
|
+
it("should return null when no style props are present", () => {
|
|
331
|
+
const source = `
|
|
332
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
333
|
+
|
|
334
|
+
export function TestComponent() {
|
|
335
|
+
return <Link href="/plain" external>Plain Link</Link>
|
|
336
|
+
}
|
|
337
|
+
`
|
|
338
|
+
|
|
339
|
+
const result = applyTransform(source)
|
|
340
|
+
|
|
341
|
+
expect(result).toBeNull()
|
|
342
|
+
})
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
describe("variable expressions", () => {
|
|
346
|
+
it("should handle variable expressions in style props", () => {
|
|
347
|
+
const source = `
|
|
348
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
349
|
+
|
|
350
|
+
export function TestComponent() {
|
|
351
|
+
const isVisible = true
|
|
352
|
+
const linkColor = "purple"
|
|
353
|
+
return (
|
|
354
|
+
<Link
|
|
355
|
+
visible={isVisible}
|
|
356
|
+
color={linkColor}
|
|
357
|
+
href="/variables"
|
|
358
|
+
>
|
|
359
|
+
Variable Link
|
|
360
|
+
</Link>
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
`
|
|
364
|
+
|
|
365
|
+
const result = applyTransform(source)
|
|
366
|
+
|
|
367
|
+
expect(result).toContain("style={{")
|
|
368
|
+
expect(result).toContain("color: linkColor")
|
|
369
|
+
expect(result).not.toContain("visible={isVisible}")
|
|
370
|
+
expect(result).not.toContain("color={linkColor}")
|
|
371
|
+
})
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
describe("import handling", () => {
|
|
375
|
+
it("should not affect imports", () => {
|
|
376
|
+
const source = `
|
|
377
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
378
|
+
|
|
379
|
+
export function TestComponent() {
|
|
380
|
+
return <Link visible={false} href="/test">Test</Link>
|
|
381
|
+
}
|
|
382
|
+
`
|
|
383
|
+
|
|
384
|
+
const result = applyTransform(source)
|
|
385
|
+
|
|
386
|
+
expect(result).toContain(
|
|
387
|
+
'import { Link } from "@planningcenter/tapestry-react"'
|
|
388
|
+
)
|
|
389
|
+
})
|
|
390
|
+
})
|
|
391
|
+
})
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { textPlugin } from "../../../stubs/textPlugin"
|
|
2
|
+
import { stylePropTransformFactory } from "../../shared/transformFactories/stylePropTransformFactory"
|
|
3
|
+
|
|
4
|
+
export default stylePropTransformFactory({
|
|
5
|
+
plugin: textPlugin,
|
|
6
|
+
stylesToKeep: ["visible"],
|
|
7
|
+
stylesToRemove: [],
|
|
8
|
+
targetComponent: "Link",
|
|
9
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
10
|
+
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import jscodeshift from "jscodeshift"
|
|
2
2
|
import { describe, expect, it } from "vitest"
|
|
3
3
|
|
|
4
|
-
import transform from "./
|
|
4
|
+
import transform from "./inlineMemberToKind"
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
@@ -14,7 +14,7 @@ function applyTransform(source: string): string | null {
|
|
|
14
14
|
) as string | null
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
describe("
|
|
17
|
+
describe("inlineMemberToKind transform", () => {
|
|
18
18
|
describe("basic transformations", () => {
|
|
19
19
|
it("should transform Link.Inline to Link with kind='inline-text'", () => {
|
|
20
20
|
const input = `
|
|
@@ -4,7 +4,6 @@ import { addAttribute } from "../../shared/actions/addAttribute"
|
|
|
4
4
|
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
5
5
|
import { componentTransformFactory } from "../../shared/transformFactories/componentTransformFactory"
|
|
6
6
|
|
|
7
|
-
// Step 1: Add kind="inline-text" attribute to Link.Inline elements
|
|
8
7
|
const addKindAttribute = attributeTransformFactory({
|
|
9
8
|
condition: () => true, // Add to all Link.Inline elements
|
|
10
9
|
targetComponent: "Link.Inline",
|
|
@@ -20,7 +19,6 @@ const addKindAttribute = attributeTransformFactory({
|
|
|
20
19
|
},
|
|
21
20
|
})
|
|
22
21
|
|
|
23
|
-
// Step 2: Transform Link.Inline to Link (preserving original component name)
|
|
24
22
|
const transformComponent = componentTransformFactory({
|
|
25
23
|
condition: () => true, // Transform all Link.Inline elements
|
|
26
24
|
fromComponent: "Link.Inline",
|