@planningcenter/tapestry-migration-cli 3.1.0-rc.1 → 3.1.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 +3 -3
- package/src/components/button/transforms/convertStyleProps.test.ts +97 -0
- package/src/components/button/transforms/removeTypeButton.test.ts +0 -1
- package/src/components/checkbox/transforms/moveCheckboxImport.test.ts +3 -0
- package/src/components/input/index.ts +66 -0
- package/src/components/input/transformableInput.ts +8 -0
- package/src/components/input/transforms/auditSpreadProps.test.ts +192 -0
- package/src/components/input/transforms/auditSpreadProps.ts +26 -0
- package/src/components/input/transforms/autoWidthTransform.test.ts +172 -0
- package/src/components/input/transforms/autoWidthTransform.ts +41 -0
- package/src/components/input/transforms/convertStyleProps.test.ts +128 -0
- package/src/components/input/transforms/convertStyleProps.ts +12 -0
- package/src/components/input/transforms/highlightOnInteractionToSelectTextOnFocus.test.ts +186 -0
- package/src/components/input/transforms/highlightOnInteractionToSelectTextOnFocus.ts +27 -0
- package/src/components/input/transforms/inputLabelToLabelProp.test.ts +319 -0
- package/src/components/input/transforms/inputLabelToLabelProp.ts +203 -0
- package/src/components/input/transforms/mergeFieldIntoInput.test.ts +391 -0
- package/src/components/input/transforms/mergeFieldIntoInput.ts +213 -0
- package/src/components/input/transforms/mergeInputLabel.test.ts +458 -0
- package/src/components/input/transforms/mergeInputLabel.ts +204 -0
- package/src/components/input/transforms/moveInputImport.test.ts +166 -0
- package/src/components/input/transforms/moveInputImport.ts +14 -0
- package/src/components/input/transforms/numberFieldAddTypeNumber.test.ts +92 -0
- package/src/components/input/transforms/numberFieldAddTypeNumber.ts +14 -0
- package/src/components/input/transforms/numberFieldRenameToInput.test.ts +126 -0
- package/src/components/input/transforms/numberFieldRenameToInput.ts +9 -0
- package/src/components/input/transforms/removeAsInput.test.ts +139 -0
- package/src/components/input/transforms/removeAsInput.ts +20 -0
- package/src/components/input/transforms/removeDuplicateKeys.test.ts +302 -0
- package/src/components/input/transforms/removeDuplicateKeys.ts +10 -0
- package/src/components/input/transforms/removeInputBox.test.ts +352 -0
- package/src/components/input/transforms/removeInputBox.ts +109 -0
- package/src/components/input/transforms/removeRedundantAriaLabel.test.ts +128 -0
- package/src/components/input/transforms/removeRedundantAriaLabel.ts +21 -0
- package/src/components/input/transforms/removeTypeInput.test.ts +212 -0
- package/src/components/input/transforms/removeTypeInput.ts +22 -0
- package/src/components/input/transforms/removeTypeText.test.ts +160 -0
- package/src/components/input/transforms/removeTypeText.ts +18 -0
- package/src/components/input/transforms/sizeMapping.test.ts +198 -0
- package/src/components/input/transforms/sizeMapping.ts +17 -0
- package/src/components/input/transforms/skipRenderSideProps.test.ts +236 -0
- package/src/components/input/transforms/skipRenderSideProps.ts +27 -0
- package/src/components/input/transforms/stateToInvalid.test.ts +208 -0
- package/src/components/input/transforms/stateToInvalid.ts +59 -0
- package/src/components/input/transforms/stateToInvalidTernary.test.ts +159 -0
- package/src/components/input/transforms/stateToInvalidTernary.ts +13 -0
- package/src/components/input/transforms/unsupportedProps.test.ts +566 -0
- package/src/components/input/transforms/unsupportedProps.ts +84 -0
- package/src/components/link/transforms/reviewStyles.test.ts +0 -1
- package/src/components/select/index.ts +54 -0
- package/src/components/select/transformableSelect.ts +7 -0
- package/src/components/select/transforms/auditSpreadProps.test.ts +103 -0
- package/src/components/select/transforms/auditSpreadProps.ts +26 -0
- package/src/components/select/transforms/childrenToOptions.test.ts +329 -0
- package/src/components/select/transforms/childrenToOptions.ts +282 -0
- package/src/components/select/transforms/convertLegacyOptions.test.ts +150 -0
- package/src/components/select/transforms/convertLegacyOptions.ts +105 -0
- package/src/components/select/transforms/convertStyleProps.test.ts +73 -0
- package/src/components/select/transforms/convertStyleProps.ts +12 -0
- package/src/components/select/transforms/emptyValueToPlaceholder.test.ts +122 -0
- package/src/components/select/transforms/emptyValueToPlaceholder.ts +22 -0
- package/src/components/select/transforms/innerRefToRef.test.ts +89 -0
- package/src/components/select/transforms/innerRefToRef.ts +18 -0
- package/src/components/select/transforms/mapChildrenToOptions.test.ts +498 -0
- package/src/components/select/transforms/mapChildrenToOptions.ts +324 -0
- package/src/components/select/transforms/mergeFieldIntoSelect.test.ts +430 -0
- package/src/components/select/transforms/mergeFieldIntoSelect.ts +233 -0
- package/src/components/select/transforms/mergeSelectLabel.test.ts +458 -0
- package/src/components/select/transforms/mergeSelectLabel.ts +225 -0
- package/src/components/select/transforms/moveSelectImport.test.ts +148 -0
- package/src/components/select/transforms/moveSelectImport.ts +14 -0
- package/src/components/select/transforms/removeDefaultProps.test.ts +249 -0
- package/src/components/select/transforms/removeDefaultProps.ts +112 -0
- package/src/components/select/transforms/sizeMapping.test.ts +188 -0
- package/src/components/select/transforms/sizeMapping.ts +17 -0
- package/src/components/select/transforms/skipMultipleSelect.test.ts +148 -0
- package/src/components/select/transforms/skipMultipleSelect.ts +23 -0
- package/src/components/select/transforms/unsupportedProps.test.ts +252 -0
- package/src/components/select/transforms/unsupportedProps.ts +44 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +102 -0
- package/src/components/shared/transformFactories/helpers/manageImports.ts +14 -12
- package/src/components/shared/transformFactories/sizeMappingFactory.ts +9 -2
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +56 -17
- package/src/components/shared/transformFactories/ternaryConditionalToPropFactory.ts +65 -0
- package/src/components/text-area/index.ts +48 -0
- package/src/components/text-area/transforms/auditSpreadProps.test.ts +139 -0
- package/src/components/text-area/transforms/auditSpreadProps.ts +10 -0
- package/src/components/text-area/transforms/convertStyleProps.test.ts +158 -0
- package/src/components/text-area/transforms/convertStyleProps.ts +10 -0
- package/src/components/text-area/transforms/innerRefToRef.test.ts +206 -0
- package/src/components/text-area/transforms/innerRefToRef.ts +14 -0
- package/src/components/text-area/transforms/mergeFieldIntoTextArea.test.ts +477 -0
- package/src/components/text-area/transforms/mergeFieldIntoTextArea.ts +227 -0
- package/src/components/text-area/transforms/moveTextAreaImport.test.ts +168 -0
- package/src/components/text-area/transforms/moveTextAreaImport.ts +13 -0
- package/src/components/text-area/transforms/removeDuplicateKeys.test.ts +129 -0
- package/src/components/text-area/transforms/removeDuplicateKeys.ts +8 -0
- package/src/components/text-area/transforms/removeRedundantAriaLabel.test.ts +183 -0
- package/src/components/text-area/transforms/removeRedundantAriaLabel.ts +59 -0
- package/src/components/text-area/transforms/sizeMapping.test.ts +199 -0
- package/src/components/text-area/transforms/sizeMapping.ts +15 -0
- package/src/components/text-area/transforms/stateToInvalid.test.ts +204 -0
- package/src/components/text-area/transforms/stateToInvalid.ts +57 -0
- package/src/components/text-area/transforms/stateToInvalidTernary.test.ts +133 -0
- package/src/components/text-area/transforms/stateToInvalidTernary.ts +11 -0
- package/src/components/text-area/transforms/unsupportedProps.test.ts +275 -0
- package/src/components/text-area/transforms/unsupportedProps.ts +35 -0
- package/src/index.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/tapestry-migration-cli",
|
|
3
|
-
"version": "3.1.0-rc.
|
|
3
|
+
"version": "3.1.0-rc.10",
|
|
4
4
|
"description": "CLI tool for Tapestry migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@emotion/react": "^11.14.0",
|
|
35
|
-
"@planningcenter/tapestry": "^3.1.0-rc.
|
|
35
|
+
"@planningcenter/tapestry": "^3.1.0-rc.10",
|
|
36
36
|
"@planningcenter/tapestry-react": "^4.11.5",
|
|
37
37
|
"@types/jscodeshift": "^17.3.0",
|
|
38
38
|
"@types/node": "^20.0.0",
|
|
@@ -52,5 +52,5 @@
|
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
|
54
54
|
},
|
|
55
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "f2b292d99911669942785d4c0d49327caf0fa663"
|
|
56
56
|
}
|
|
@@ -252,6 +252,103 @@ describe("convertStyleProps transform", () => {
|
|
|
252
252
|
expect(result).toContain("paddingTop:")
|
|
253
253
|
expect(result).not.toContain("marginLeft={16}")
|
|
254
254
|
expect(result).not.toContain('paddingTop="8px"')
|
|
255
|
+
expect(result).toContain('color: "red"')
|
|
256
|
+
expect(result).toContain("fontSize: 14")
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
it("should preserve non-conflicting existing props when merging", () => {
|
|
260
|
+
const source = `
|
|
261
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
262
|
+
|
|
263
|
+
export function TestComponent() {
|
|
264
|
+
return (
|
|
265
|
+
<Button
|
|
266
|
+
style={{ color: "red" }}
|
|
267
|
+
marginLeft={16}
|
|
268
|
+
>
|
|
269
|
+
Test
|
|
270
|
+
</Button>
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
`
|
|
274
|
+
|
|
275
|
+
const result = applyTransform(source)
|
|
276
|
+
|
|
277
|
+
expect(result).not.toBeNull()
|
|
278
|
+
expect(result).toContain('color: "red"')
|
|
279
|
+
expect(result).toContain("marginLeft:")
|
|
280
|
+
expect(result).not.toContain("marginLeft={16}")
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
it("should let new props win over conflicting existing props", () => {
|
|
284
|
+
const source = `
|
|
285
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
286
|
+
|
|
287
|
+
export function TestComponent() {
|
|
288
|
+
return (
|
|
289
|
+
<Button
|
|
290
|
+
style={{ marginLeft: "999px" }}
|
|
291
|
+
marginLeft={16}
|
|
292
|
+
>
|
|
293
|
+
Test
|
|
294
|
+
</Button>
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
`
|
|
298
|
+
|
|
299
|
+
const result = applyTransform(source)
|
|
300
|
+
|
|
301
|
+
expect(result).not.toBeNull()
|
|
302
|
+
expect(result).not.toContain('"999px"')
|
|
303
|
+
expect(result).toContain("marginLeft:")
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
it("should preserve SpreadElement when merging", () => {
|
|
307
|
+
const source = `
|
|
308
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
309
|
+
|
|
310
|
+
export function TestComponent() {
|
|
311
|
+
return (
|
|
312
|
+
<Button
|
|
313
|
+
style={{ ...base, color: "red" }}
|
|
314
|
+
paddingTop="8px"
|
|
315
|
+
>
|
|
316
|
+
Test
|
|
317
|
+
</Button>
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
`
|
|
321
|
+
|
|
322
|
+
const result = applyTransform(source)
|
|
323
|
+
|
|
324
|
+
expect(result).not.toBeNull()
|
|
325
|
+
expect(result).toContain("...base")
|
|
326
|
+
expect(result).toContain('color: "red"')
|
|
327
|
+
expect(result).toContain("paddingTop:")
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it("should merge computed styles into existing dynamic style prop via spread", () => {
|
|
331
|
+
const source = `
|
|
332
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
333
|
+
|
|
334
|
+
export function TestComponent() {
|
|
335
|
+
return (
|
|
336
|
+
<Button
|
|
337
|
+
style={getStyles()}
|
|
338
|
+
marginLeft={16}
|
|
339
|
+
>
|
|
340
|
+
Test
|
|
341
|
+
</Button>
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
`
|
|
345
|
+
|
|
346
|
+
const result = applyTransform(source)
|
|
347
|
+
|
|
348
|
+
expect(result).not.toBeNull()
|
|
349
|
+
expect(result).toContain("...getStyles()")
|
|
350
|
+
expect(result).toContain("marginLeft:")
|
|
351
|
+
expect(result).not.toContain("Could not merge")
|
|
255
352
|
})
|
|
256
353
|
})
|
|
257
354
|
|
|
@@ -91,6 +91,9 @@ function Test() {
|
|
|
91
91
|
'import { Button } from "@planningcenter/tapestry-react"'
|
|
92
92
|
)
|
|
93
93
|
expect(result).toContain(
|
|
94
|
+
'import { Checkbox } from "@planningcenter/tapestry"'
|
|
95
|
+
)
|
|
96
|
+
expect(result).not.toContain(
|
|
94
97
|
'import { Checkbox } from "@planningcenter/tapestry-react"'
|
|
95
98
|
)
|
|
96
99
|
})
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import auditSpreadProps from "./transforms/auditSpreadProps"
|
|
4
|
+
import autoWidthTransform from "./transforms/autoWidthTransform"
|
|
5
|
+
import convertStyleProps from "./transforms/convertStyleProps"
|
|
6
|
+
import highlightOnInteractionToSelectTextOnFocus from "./transforms/highlightOnInteractionToSelectTextOnFocus"
|
|
7
|
+
import inputLabelToLabelProp from "./transforms/inputLabelToLabelProp"
|
|
8
|
+
import mergeFieldIntoInput from "./transforms/mergeFieldIntoInput"
|
|
9
|
+
import mergeInputLabel from "./transforms/mergeInputLabel"
|
|
10
|
+
import moveInputImport from "./transforms/moveInputImport"
|
|
11
|
+
import numberFieldAddTypeNumber from "./transforms/numberFieldAddTypeNumber"
|
|
12
|
+
import numberFieldRenameToInput from "./transforms/numberFieldRenameToInput"
|
|
13
|
+
import removeAsInput from "./transforms/removeAsInput"
|
|
14
|
+
import removeDuplicateKeys from "./transforms/removeDuplicateKeys"
|
|
15
|
+
import removeInputBox from "./transforms/removeInputBox"
|
|
16
|
+
import removeRedundantAriaLabel from "./transforms/removeRedundantAriaLabel"
|
|
17
|
+
import removeTypeText from "./transforms/removeTypeText"
|
|
18
|
+
import sizeMapping from "./transforms/sizeMapping"
|
|
19
|
+
import skipRenderSideProps from "./transforms/skipRenderSideProps"
|
|
20
|
+
import stateToInvalid from "./transforms/stateToInvalid"
|
|
21
|
+
import stateToInvalidTernary from "./transforms/stateToInvalidTernary"
|
|
22
|
+
import unsupportedProps from "./transforms/unsupportedProps"
|
|
23
|
+
|
|
24
|
+
const transform: Transform = (fileInfo, api, options) => {
|
|
25
|
+
let currentSource = fileInfo.source
|
|
26
|
+
let hasAnyChanges = false
|
|
27
|
+
|
|
28
|
+
const transforms = [
|
|
29
|
+
numberFieldAddTypeNumber,
|
|
30
|
+
numberFieldRenameToInput,
|
|
31
|
+
removeInputBox,
|
|
32
|
+
skipRenderSideProps,
|
|
33
|
+
mergeInputLabel,
|
|
34
|
+
mergeFieldIntoInput,
|
|
35
|
+
auditSpreadProps,
|
|
36
|
+
highlightOnInteractionToSelectTextOnFocus,
|
|
37
|
+
sizeMapping,
|
|
38
|
+
autoWidthTransform,
|
|
39
|
+
stateToInvalidTernary,
|
|
40
|
+
stateToInvalid,
|
|
41
|
+
convertStyleProps,
|
|
42
|
+
inputLabelToLabelProp,
|
|
43
|
+
removeDuplicateKeys,
|
|
44
|
+
removeRedundantAriaLabel,
|
|
45
|
+
removeTypeText,
|
|
46
|
+
removeAsInput,
|
|
47
|
+
unsupportedProps,
|
|
48
|
+
moveInputImport,
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
for (const individualTransform of transforms) {
|
|
52
|
+
const result = individualTransform(
|
|
53
|
+
{ ...fileInfo, source: currentSource },
|
|
54
|
+
api,
|
|
55
|
+
options
|
|
56
|
+
)
|
|
57
|
+
if (result && result !== currentSource) {
|
|
58
|
+
currentSource = result as string
|
|
59
|
+
hasAnyChanges = true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return hasAnyChanges ? currentSource : null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default transform
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { hasAttribute } from "../shared/conditions/hasAttribute"
|
|
2
|
+
import { notCondition } from "../shared/conditions/notCondition"
|
|
3
|
+
import { orConditions } from "../shared/conditions/orConditions"
|
|
4
|
+
import { TransformCondition } from "../shared/types"
|
|
5
|
+
|
|
6
|
+
export const transformableInput: TransformCondition = notCondition(
|
|
7
|
+
orConditions(hasAttribute("renderLeft"), hasAttribute("renderRight"))
|
|
8
|
+
)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./auditSpreadProps"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
const AUDIT_COMMENT =
|
|
9
|
+
"TODO: tapestry-migration (spreadAttribute): Spread props can contain unsupported props, please explore usages and migrate as needed."
|
|
10
|
+
|
|
11
|
+
function applyTransform(source: string) {
|
|
12
|
+
const fileInfo = { path: "test.tsx", source }
|
|
13
|
+
return transform(
|
|
14
|
+
fileInfo,
|
|
15
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
16
|
+
{}
|
|
17
|
+
) as string | null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("auditSpreadProps transform", () => {
|
|
21
|
+
describe("basic transformations", () => {
|
|
22
|
+
it("should add comment to Input with single spread prop", () => {
|
|
23
|
+
const input = `
|
|
24
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
25
|
+
|
|
26
|
+
export default function Test() {
|
|
27
|
+
const props = { onChange: handleChange }
|
|
28
|
+
return <Input {...props} label="Name" />
|
|
29
|
+
}
|
|
30
|
+
`.trim()
|
|
31
|
+
|
|
32
|
+
const result = applyTransform(input)
|
|
33
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
34
|
+
expect(result).toContain("{...props}")
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("should add comment to Input with multiple spread props", () => {
|
|
38
|
+
const input = `
|
|
39
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
40
|
+
|
|
41
|
+
export default function Test() {
|
|
42
|
+
const baseProps = { onChange: handleChange }
|
|
43
|
+
const styleProps = { className: "input" }
|
|
44
|
+
return <Input {...baseProps} {...styleProps} label="Name" />
|
|
45
|
+
}
|
|
46
|
+
`.trim()
|
|
47
|
+
|
|
48
|
+
const result = applyTransform(input)
|
|
49
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
50
|
+
expect(result).toContain("{...baseProps}")
|
|
51
|
+
expect(result).toContain("{...styleProps}")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it("should handle Input with spread props and regular attributes", () => {
|
|
55
|
+
const input = `
|
|
56
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
57
|
+
|
|
58
|
+
export default function Test() {
|
|
59
|
+
const props = { onChange: handleChange }
|
|
60
|
+
return <Input label="Name" {...props} disabled />
|
|
61
|
+
}
|
|
62
|
+
`.trim()
|
|
63
|
+
|
|
64
|
+
const result = applyTransform(input)
|
|
65
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
66
|
+
expect(result).toContain('label="Name"')
|
|
67
|
+
expect(result).toContain("{...props}")
|
|
68
|
+
expect(result).toContain("disabled")
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it("should handle multiple Input components with spread props", () => {
|
|
72
|
+
const input = `
|
|
73
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
74
|
+
|
|
75
|
+
export default function Test() {
|
|
76
|
+
const props1 = { onChange: handleChange1 }
|
|
77
|
+
const props2 = { onChange: handleChange2 }
|
|
78
|
+
return (
|
|
79
|
+
<div>
|
|
80
|
+
<Input {...props1} label="First" />
|
|
81
|
+
<Input {...props2} label="Second" />
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
`.trim()
|
|
86
|
+
|
|
87
|
+
const result = applyTransform(input)
|
|
88
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
89
|
+
expect(result).toContain("{...props1}")
|
|
90
|
+
expect(result).toContain("{...props2}")
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe("edge cases", () => {
|
|
95
|
+
it("should not transform Input without spread props", () => {
|
|
96
|
+
const input = `
|
|
97
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
98
|
+
|
|
99
|
+
export default function Test() {
|
|
100
|
+
return <Input onChange={handleChange} label="Name" />
|
|
101
|
+
}
|
|
102
|
+
`.trim()
|
|
103
|
+
|
|
104
|
+
const result = applyTransform(input)
|
|
105
|
+
expect(result).toBe(null)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it("should not transform if Input is not imported from @planningcenter/tapestry-react", () => {
|
|
109
|
+
const input = `
|
|
110
|
+
import { Input } from "other-library"
|
|
111
|
+
|
|
112
|
+
export default function Test() {
|
|
113
|
+
const props = { onChange: handleChange }
|
|
114
|
+
return <Input {...props} label="Name" />
|
|
115
|
+
}
|
|
116
|
+
`.trim()
|
|
117
|
+
|
|
118
|
+
const result = applyTransform(input)
|
|
119
|
+
expect(result).toBe(null)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it("should handle Input with alias import", () => {
|
|
123
|
+
const input = `
|
|
124
|
+
import { Input as TapestryInput } from "@planningcenter/tapestry-react"
|
|
125
|
+
|
|
126
|
+
export default function Test() {
|
|
127
|
+
const props = { onChange: handleChange }
|
|
128
|
+
return <TapestryInput {...props} label="Name" />
|
|
129
|
+
}
|
|
130
|
+
`.trim()
|
|
131
|
+
|
|
132
|
+
const result = applyTransform(input)
|
|
133
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
134
|
+
expect(result).toContain("{...props}")
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it("should handle mixed Input components (with and without spread props)", () => {
|
|
138
|
+
const input = `
|
|
139
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
140
|
+
|
|
141
|
+
export default function Test() {
|
|
142
|
+
const props = { onChange: handleSave }
|
|
143
|
+
return (
|
|
144
|
+
<div>
|
|
145
|
+
<Input {...props} label="First" />
|
|
146
|
+
<Input label="Second" />
|
|
147
|
+
<Input onChange={handleChange} label="Third" />
|
|
148
|
+
</div>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
`.trim()
|
|
152
|
+
|
|
153
|
+
const result = applyTransform(input)
|
|
154
|
+
expect(result).toContain(AUDIT_COMMENT)
|
|
155
|
+
expect(result).toContain("{...props}")
|
|
156
|
+
expect(result).toContain('<Input label="Second" />')
|
|
157
|
+
expect(result).toContain("onChange={handleChange}")
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it("should return null when no Input components have spread props", () => {
|
|
161
|
+
const input = `
|
|
162
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
163
|
+
|
|
164
|
+
export default function Test() {
|
|
165
|
+
return (
|
|
166
|
+
<div>
|
|
167
|
+
<Input label="First" />
|
|
168
|
+
<Input label="Second" />
|
|
169
|
+
</div>
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
`.trim()
|
|
173
|
+
|
|
174
|
+
const result = applyTransform(input)
|
|
175
|
+
expect(result).toBe(null)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it("should return null when no Input imports exist", () => {
|
|
179
|
+
const input = `
|
|
180
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
181
|
+
|
|
182
|
+
export default function Test() {
|
|
183
|
+
const props = { onClick: handleClick }
|
|
184
|
+
return <Button {...props}>Save</Button>
|
|
185
|
+
}
|
|
186
|
+
`.trim()
|
|
187
|
+
|
|
188
|
+
const result = applyTransform(input)
|
|
189
|
+
expect(result).toBe(null)
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
|
|
4
|
+
import { getSpreadProps } from "../../shared/actions/getSpreadProps"
|
|
5
|
+
import { hasSpreadProps } from "../../shared/actions/hasSpreadProps"
|
|
6
|
+
import { andConditions } from "../../shared/conditions/andConditions"
|
|
7
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
8
|
+
import { transformableInput } from "../transformableInput"
|
|
9
|
+
|
|
10
|
+
const COMMENT =
|
|
11
|
+
"Spread props can contain unsupported props, please explore usages and migrate as needed."
|
|
12
|
+
|
|
13
|
+
const transform: Transform = attributeTransformFactory({
|
|
14
|
+
condition: andConditions(hasSpreadProps, transformableInput),
|
|
15
|
+
targetComponent: "Input",
|
|
16
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
17
|
+
transform: (element, { j }) => {
|
|
18
|
+
const spreadProps = getSpreadProps(element)
|
|
19
|
+
spreadProps.forEach((prop) =>
|
|
20
|
+
addCommentToAttribute({ attribute: prop, j, text: COMMENT })
|
|
21
|
+
)
|
|
22
|
+
return spreadProps.length > 0
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export default transform
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./autoWidthTransform"
|
|
5
|
+
|
|
6
|
+
const j = jscodeshift.withParser("tsx")
|
|
7
|
+
|
|
8
|
+
function applyTransform(source: string): string {
|
|
9
|
+
const fileInfo = { path: "test.tsx", source }
|
|
10
|
+
const result = transform(
|
|
11
|
+
fileInfo,
|
|
12
|
+
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
+
{}
|
|
14
|
+
) as string | null
|
|
15
|
+
return result || source
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("autoWidthTransform transform", () => {
|
|
19
|
+
describe("boolean values — pass through unchanged", () => {
|
|
20
|
+
it("should not add comment for shorthand autoWidth", () => {
|
|
21
|
+
const input = `
|
|
22
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
23
|
+
|
|
24
|
+
function Test() {
|
|
25
|
+
return <Input autoWidth label="Name" />
|
|
26
|
+
}
|
|
27
|
+
`.trim()
|
|
28
|
+
|
|
29
|
+
const result = applyTransform(input)
|
|
30
|
+
expect(result).not.toContain("TODO: tapestry-migration")
|
|
31
|
+
expect(result).toBe(input)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it("should not add comment for autoWidth={true}", () => {
|
|
35
|
+
const input = `
|
|
36
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
37
|
+
|
|
38
|
+
function Test() {
|
|
39
|
+
return <Input autoWidth={true} label="Name" />
|
|
40
|
+
}
|
|
41
|
+
`.trim()
|
|
42
|
+
|
|
43
|
+
const result = applyTransform(input)
|
|
44
|
+
expect(result).not.toContain("TODO: tapestry-migration")
|
|
45
|
+
expect(result).toBe(input)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it("should not add comment for autoWidth={false}", () => {
|
|
49
|
+
const input = `
|
|
50
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
51
|
+
|
|
52
|
+
function Test() {
|
|
53
|
+
return <Input autoWidth={false} label="Name" />
|
|
54
|
+
}
|
|
55
|
+
`.trim()
|
|
56
|
+
|
|
57
|
+
const result = applyTransform(input)
|
|
58
|
+
expect(result).not.toContain("TODO: tapestry-migration")
|
|
59
|
+
expect(result).toBe(input)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe("non-boolean values — add comment", () => {
|
|
64
|
+
it("should add comment for numeric autoWidth", () => {
|
|
65
|
+
const input = `
|
|
66
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
67
|
+
|
|
68
|
+
function Test() {
|
|
69
|
+
return <Input autoWidth={120} label="Name" />
|
|
70
|
+
}
|
|
71
|
+
`.trim()
|
|
72
|
+
|
|
73
|
+
const result = applyTransform(input)
|
|
74
|
+
expect(result).toContain("TODO: tapestry-migration (autoWidth)")
|
|
75
|
+
expect(result).toContain(
|
|
76
|
+
"'autoWidth' no longer accepts measurement values"
|
|
77
|
+
)
|
|
78
|
+
expect(result).toContain("autoWidth={120}")
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it("should add comment for string autoWidth", () => {
|
|
82
|
+
const input = `
|
|
83
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
84
|
+
|
|
85
|
+
function Test() {
|
|
86
|
+
return <Input autoWidth="fit-content" label="Name" />
|
|
87
|
+
}
|
|
88
|
+
`.trim()
|
|
89
|
+
|
|
90
|
+
const result = applyTransform(input)
|
|
91
|
+
expect(result).toContain("TODO: tapestry-migration (autoWidth)")
|
|
92
|
+
expect(result).toContain(
|
|
93
|
+
"'autoWidth' no longer accepts measurement values"
|
|
94
|
+
)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it("should add comment for variable autoWidth", () => {
|
|
98
|
+
const input = `
|
|
99
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
100
|
+
|
|
101
|
+
function Test() {
|
|
102
|
+
const width = 120
|
|
103
|
+
return <Input autoWidth={width} label="Name" />
|
|
104
|
+
}
|
|
105
|
+
`.trim()
|
|
106
|
+
|
|
107
|
+
const result = applyTransform(input)
|
|
108
|
+
expect(result).toContain("TODO: tapestry-migration (autoWidth)")
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it("should add comment for expression autoWidth", () => {
|
|
112
|
+
const input = `
|
|
113
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
114
|
+
|
|
115
|
+
function Test() {
|
|
116
|
+
return <Input autoWidth={isWide ? 200 : 100} label="Name" />
|
|
117
|
+
}
|
|
118
|
+
`.trim()
|
|
119
|
+
|
|
120
|
+
const result = applyTransform(input)
|
|
121
|
+
expect(result).toContain("TODO: tapestry-migration (autoWidth)")
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe("edge cases", () => {
|
|
126
|
+
it("should not affect Input without autoWidth prop", () => {
|
|
127
|
+
const input = `
|
|
128
|
+
import { Input } from "@planningcenter/tapestry-react"
|
|
129
|
+
|
|
130
|
+
function Test() {
|
|
131
|
+
return <Input label="Name" />
|
|
132
|
+
}
|
|
133
|
+
`.trim()
|
|
134
|
+
|
|
135
|
+
const result = applyTransform(input)
|
|
136
|
+
expect(result).toBe(input)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("should not affect other components", () => {
|
|
140
|
+
const input = `
|
|
141
|
+
import { Input, Button } from "@planningcenter/tapestry-react"
|
|
142
|
+
|
|
143
|
+
function Test() {
|
|
144
|
+
return (
|
|
145
|
+
<div>
|
|
146
|
+
<Button autoWidth={120}>Click</Button>
|
|
147
|
+
<Input autoWidth={120} label="Name" />
|
|
148
|
+
</div>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
`.trim()
|
|
152
|
+
|
|
153
|
+
const result = applyTransform(input)
|
|
154
|
+
expect(result).toContain("TODO: tapestry-migration (autoWidth)")
|
|
155
|
+
// Verify the Button is unchanged
|
|
156
|
+
expect(result).toContain("<Button autoWidth={120}>Click</Button>")
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it("should not transform if not imported from @planningcenter/tapestry-react", () => {
|
|
160
|
+
const input = `
|
|
161
|
+
import { Input } from "other-library"
|
|
162
|
+
|
|
163
|
+
function Test() {
|
|
164
|
+
return <Input autoWidth={120} label="Name" />
|
|
165
|
+
}
|
|
166
|
+
`.trim()
|
|
167
|
+
|
|
168
|
+
const result = applyTransform(input)
|
|
169
|
+
expect(result).toBe(input)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
4
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
5
|
+
import { andConditions } from "../../shared/conditions/andConditions"
|
|
6
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
7
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
8
|
+
import { transformableInput } from "../transformableInput"
|
|
9
|
+
|
|
10
|
+
const transform: Transform = attributeTransformFactory({
|
|
11
|
+
condition: andConditions(hasAttribute("autoWidth"), transformableInput),
|
|
12
|
+
targetComponent: "Input",
|
|
13
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
14
|
+
transform: (element, { j, source }) => {
|
|
15
|
+
const attr = getAttribute({ element, name: "autoWidth" })
|
|
16
|
+
if (!attr) return false
|
|
17
|
+
|
|
18
|
+
// Shorthand boolean (<Input autoWidth />) — pass through
|
|
19
|
+
if (attr.value === null) return false
|
|
20
|
+
|
|
21
|
+
// Explicit boolean ({true} or {false}) — pass through
|
|
22
|
+
if (
|
|
23
|
+
attr.value.type === "JSXExpressionContainer" &&
|
|
24
|
+
attr.value.expression.type === "BooleanLiteral"
|
|
25
|
+
) {
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Non-boolean value — add comment
|
|
30
|
+
addComment({
|
|
31
|
+
element,
|
|
32
|
+
j,
|
|
33
|
+
scope: "autoWidth",
|
|
34
|
+
source,
|
|
35
|
+
text: "'autoWidth' no longer accepts measurement values. Use autoWidth={true} to grow to content width, or remove the prop.",
|
|
36
|
+
})
|
|
37
|
+
return true
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export default transform
|