@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
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.10",
|
|
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": "cb64c1483123040f674d56065cb108dbb10ca985"
|
|
55
55
|
}
|
|
@@ -105,10 +105,10 @@ function Test() {
|
|
|
105
105
|
"{/* TODO: tapestry-migration (children): complex children cannot be converted to label prop"
|
|
106
106
|
)
|
|
107
107
|
expect(result).toContain(
|
|
108
|
-
"take time to find the right text for the
|
|
108
|
+
"take time to find the right text for the component"
|
|
109
109
|
)
|
|
110
110
|
expect(result).toContain(
|
|
111
|
-
"prefix and suffix to correctly display those icons"
|
|
111
|
+
"If icons are used in the component, you can use prefix and suffix to correctly display those icons"
|
|
112
112
|
)
|
|
113
113
|
expect(result).toContain('<Icon name="save" />')
|
|
114
114
|
expect(result).toContain("Save")
|
|
@@ -177,8 +177,9 @@ function Test() {
|
|
|
177
177
|
"{/* TODO: tapestry-migration (label): Button has both label prop and children"
|
|
178
178
|
)
|
|
179
179
|
expect(result).toContain(
|
|
180
|
-
"take time to find the right text for the
|
|
180
|
+
"take time to find the right text for the component."
|
|
181
181
|
)
|
|
182
|
+
expect(result).not.toContain("If icons are used")
|
|
182
183
|
expect(result).toContain('label="Existing Label"')
|
|
183
184
|
expect(result).toContain("Child Content")
|
|
184
185
|
})
|
|
@@ -356,7 +357,7 @@ function Test() {
|
|
|
356
357
|
expect(result).toMatch(/\{\s*\/\*\s*TODO: tapestry-migration/)
|
|
357
358
|
expect(result).toMatch(/\*\/\s*\}/)
|
|
358
359
|
expect(result).toContain(
|
|
359
|
-
"take time to find the right text for the
|
|
360
|
+
"take time to find the right text for the component"
|
|
360
361
|
)
|
|
361
362
|
})
|
|
362
363
|
})
|
|
@@ -1,48 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
2
|
|
|
3
3
|
import { addAttribute } from "../../shared/actions/addAttribute"
|
|
4
4
|
import { addComment } from "../../shared/actions/addComment"
|
|
5
5
|
import { removeChildren } from "../../shared/actions/removeChildren"
|
|
6
6
|
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
7
7
|
import { hasChildren } from "../../shared/conditions/hasChildren"
|
|
8
|
+
import {
|
|
9
|
+
buildComment,
|
|
10
|
+
extractTextContent,
|
|
11
|
+
} from "../../shared/helpers/childrenToLabelHelpers"
|
|
8
12
|
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
isSimpleText: boolean
|
|
12
|
-
textContent: string
|
|
13
|
-
} {
|
|
14
|
-
let textContent = ""
|
|
15
|
-
|
|
16
|
-
for (const child of children) {
|
|
17
|
-
if (child.type === "JSXText") {
|
|
18
|
-
const text = child.value.trim()
|
|
19
|
-
if (text) textContent += text
|
|
20
|
-
} else if (
|
|
21
|
-
child.type === "JSXExpressionContainer" &&
|
|
22
|
-
child.expression.type === "StringLiteral"
|
|
23
|
-
) {
|
|
24
|
-
textContent += child.expression.value
|
|
25
|
-
} else if (
|
|
26
|
-
child.type === "JSXExpressionContainer" &&
|
|
27
|
-
child.expression.type === "TemplateLiteral" &&
|
|
28
|
-
child.expression.expressions.length === 0
|
|
29
|
-
) {
|
|
30
|
-
// Simple template literal with no expressions like `hello`
|
|
31
|
-
textContent += child.expression.quasis[0].value.raw
|
|
32
|
-
} else {
|
|
33
|
-
// Complex content (JSX elements, expressions, etc.)
|
|
34
|
-
return { isSimpleText: false, textContent: "" }
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return { isSimpleText: true, textContent }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function buildComment(message: string): string {
|
|
42
|
-
return `${message} - take time to find the right text for the button. If icons are used in the Button, you can use prefix and suffix to correctly display those icons.`
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const transform = attributeTransformFactory({
|
|
14
|
+
const transform: Transform = attributeTransformFactory({
|
|
46
15
|
condition: hasChildren,
|
|
47
16
|
targetComponent: "Button",
|
|
48
17
|
targetPackage: "@planningcenter/tapestry-react",
|
|
@@ -53,7 +22,7 @@ const transform = attributeTransformFactory({
|
|
|
53
22
|
j,
|
|
54
23
|
scope: "label",
|
|
55
24
|
source,
|
|
56
|
-
text: buildComment("Button has both label prop and children"),
|
|
25
|
+
text: buildComment("Button has both label prop and children", false),
|
|
57
26
|
})
|
|
58
27
|
return true
|
|
59
28
|
}
|
|
@@ -71,7 +40,8 @@ const transform = attributeTransformFactory({
|
|
|
71
40
|
scope: "children",
|
|
72
41
|
source,
|
|
73
42
|
text: buildComment(
|
|
74
|
-
"complex children cannot be converted to label prop"
|
|
43
|
+
"complex children cannot be converted to label prop",
|
|
44
|
+
true
|
|
75
45
|
),
|
|
76
46
|
})
|
|
77
47
|
return true
|
|
@@ -1,44 +1,20 @@
|
|
|
1
1
|
import { JSXAttribute, Transform } from "jscodeshift"
|
|
2
2
|
|
|
3
|
-
import { stylePropNames } from "../../../../dist/tapestry-react-shim.cjs"
|
|
4
3
|
import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
|
|
4
|
+
import { SUPPORTED_PROPS_BASE } from "../../shared/helpers/unsupportedPropsHelpers"
|
|
5
5
|
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
...stylePropNames,
|
|
9
|
-
"children",
|
|
10
|
-
"className",
|
|
7
|
+
const BUTTON_SPECIFIC_PROPS = [
|
|
11
8
|
"disabled",
|
|
12
|
-
"kind",
|
|
13
|
-
"label",
|
|
14
|
-
"onClick",
|
|
15
|
-
"style",
|
|
16
|
-
"type",
|
|
17
|
-
"prefix",
|
|
18
|
-
"suffix",
|
|
19
|
-
"size",
|
|
20
9
|
"fullWidth",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"external",
|
|
24
|
-
"id",
|
|
25
|
-
"onMouseOver",
|
|
26
|
-
"onMouseOut",
|
|
27
|
-
"onMouseDown",
|
|
28
|
-
"onMouseUp",
|
|
29
|
-
"onKeyUp",
|
|
30
|
-
"icon",
|
|
31
|
-
"weight",
|
|
32
|
-
"tabIndex",
|
|
33
|
-
"onKeyDown",
|
|
34
|
-
"onFocus",
|
|
35
|
-
"onBlur",
|
|
36
|
-
"role",
|
|
10
|
+
"loading",
|
|
11
|
+
"loadingAriaLabel",
|
|
37
12
|
"name",
|
|
38
|
-
"
|
|
39
|
-
"spinner", // taken care of by spinnerToLoadingButton transform
|
|
13
|
+
"type",
|
|
40
14
|
]
|
|
41
15
|
|
|
16
|
+
const SUPPORTED_PROPS = [...SUPPORTED_PROPS_BASE, ...BUTTON_SPECIFIC_PROPS]
|
|
17
|
+
|
|
42
18
|
const transform: Transform = attributeTransformFactory({
|
|
43
19
|
targetComponent: "Button",
|
|
44
20
|
targetPackage: "@planningcenter/tapestry-react",
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import { Transform } from "jscodeshift"
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import childrenToLabel from "./transforms/childrenToLabel"
|
|
4
|
+
import convertStyleProps from "./transforms/convertStyleProps"
|
|
5
|
+
import inlineMemberToKind from "./transforms/inlineMemberToKind"
|
|
6
|
+
import inlinePropToKind from "./transforms/inlinePropToKind"
|
|
7
|
+
import removeAs from "./transforms/removeAs"
|
|
8
|
+
import reviewStyles from "./transforms/reviewStyles"
|
|
4
9
|
import targetBlankToExternal from "./transforms/targetBlankToExternal"
|
|
5
10
|
import toToHref from "./transforms/toToHref"
|
|
11
|
+
import unsupportedProps from "./transforms/unsupportedProps"
|
|
6
12
|
|
|
7
13
|
const transform: Transform = (fileInfo, api, options) => {
|
|
8
14
|
let currentSource = fileInfo.source
|
|
9
15
|
let hasAnyChanges = false
|
|
10
16
|
|
|
11
17
|
const transforms: Transform[] = [
|
|
12
|
-
|
|
18
|
+
inlineMemberToKind,
|
|
19
|
+
inlinePropToKind,
|
|
13
20
|
toToHref,
|
|
14
21
|
targetBlankToExternal,
|
|
22
|
+
removeAs,
|
|
23
|
+
childrenToLabel,
|
|
24
|
+
reviewStyles,
|
|
25
|
+
convertStyleProps,
|
|
26
|
+
unsupportedProps,
|
|
15
27
|
]
|
|
16
28
|
|
|
17
29
|
for (const individualTransform of transforms) {
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import jscodeshift from "jscodeshift"
|
|
2
|
+
import { describe, expect, it } from "vitest"
|
|
3
|
+
|
|
4
|
+
import transform from "./childrenToLabel"
|
|
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("childrenToLabel transform for Link", () => {
|
|
18
|
+
describe("simple text conversion", () => {
|
|
19
|
+
it("should convert simple text children to label prop", () => {
|
|
20
|
+
const input = `
|
|
21
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
22
|
+
|
|
23
|
+
function Test() {
|
|
24
|
+
return <Link href="/home">Home</Link>
|
|
25
|
+
}
|
|
26
|
+
`.trim()
|
|
27
|
+
|
|
28
|
+
const result = applyTransform(input)
|
|
29
|
+
expect(result).toContain('<Link href="/home" label="Home" />')
|
|
30
|
+
expect(result).not.toContain("Home</Link>")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("should handle text with whitespace", () => {
|
|
34
|
+
const input = `
|
|
35
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
36
|
+
|
|
37
|
+
function Test() {
|
|
38
|
+
return <Link href="/about"> About Us </Link>
|
|
39
|
+
}
|
|
40
|
+
`.trim()
|
|
41
|
+
|
|
42
|
+
const result = applyTransform(input)
|
|
43
|
+
expect(result).toContain('<Link href="/about" label="About Us" />')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it("should convert string literal expressions", () => {
|
|
47
|
+
const input = `
|
|
48
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
49
|
+
|
|
50
|
+
function Test() {
|
|
51
|
+
return <Link href="/contact">{"Contact"}</Link>
|
|
52
|
+
}
|
|
53
|
+
`.trim()
|
|
54
|
+
|
|
55
|
+
const result = applyTransform(input)
|
|
56
|
+
expect(result).toContain('<Link href="/contact" label="Contact" />')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it("should convert simple template literals without expressions", () => {
|
|
60
|
+
const input = `
|
|
61
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
62
|
+
|
|
63
|
+
function Test() {
|
|
64
|
+
return <Link href="/profile">{\`Profile\`}</Link>
|
|
65
|
+
}
|
|
66
|
+
`.trim()
|
|
67
|
+
|
|
68
|
+
const result = applyTransform(input)
|
|
69
|
+
expect(result).toContain('<Link href="/profile" label="Profile" />')
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it("should combine multiple text nodes", () => {
|
|
73
|
+
const input = `
|
|
74
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
75
|
+
|
|
76
|
+
function Test() {
|
|
77
|
+
return <Link href="/help">Get {"Help"} Now</Link>
|
|
78
|
+
}
|
|
79
|
+
`.trim()
|
|
80
|
+
|
|
81
|
+
const result = applyTransform(input)
|
|
82
|
+
expect(result).toContain('<Link href="/help" label="GetHelpNow" />')
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe("complex children scenarios", () => {
|
|
87
|
+
it("should add comment for complex JSX children", () => {
|
|
88
|
+
const input = `
|
|
89
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
90
|
+
import { Icon } from "./Icon"
|
|
91
|
+
|
|
92
|
+
function Test() {
|
|
93
|
+
return (
|
|
94
|
+
<div>
|
|
95
|
+
<Link href="/save">
|
|
96
|
+
<Icon name="save" />
|
|
97
|
+
Save
|
|
98
|
+
</Link>
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
`.trim()
|
|
103
|
+
|
|
104
|
+
const result = applyTransform(input)
|
|
105
|
+
expect(result).toContain(
|
|
106
|
+
"{/* TODO: tapestry-migration (children): complex children cannot be converted to label prop"
|
|
107
|
+
)
|
|
108
|
+
expect(result).toContain(
|
|
109
|
+
"take time to find the right text for the component"
|
|
110
|
+
)
|
|
111
|
+
expect(result).toContain(
|
|
112
|
+
"If icons are used in the component, you can use prefix and suffix to correctly display those icons"
|
|
113
|
+
)
|
|
114
|
+
expect(result).toContain('<Icon name="save" />')
|
|
115
|
+
expect(result).toContain("Save")
|
|
116
|
+
expect(result).toContain("</Link>")
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it("should add comment for expression children", () => {
|
|
120
|
+
const input = `
|
|
121
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
122
|
+
|
|
123
|
+
function Test({ linkText }) {
|
|
124
|
+
return <Link href="/home">{linkText}</Link>
|
|
125
|
+
}
|
|
126
|
+
`.trim()
|
|
127
|
+
|
|
128
|
+
const result = applyTransform(input)
|
|
129
|
+
expect(result).toContain(
|
|
130
|
+
"TODO: tapestry-migration (children): complex children cannot be converted to label prop"
|
|
131
|
+
)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it("should add comment for template literals with expressions", () => {
|
|
135
|
+
const input = `
|
|
136
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
137
|
+
|
|
138
|
+
function Test({ name }) {
|
|
139
|
+
return <Link href="/user">{\`View \${name}\`}</Link>
|
|
140
|
+
}
|
|
141
|
+
`.trim()
|
|
142
|
+
|
|
143
|
+
const result = applyTransform(input)
|
|
144
|
+
expect(result).toContain(
|
|
145
|
+
"TODO: tapestry-migration (children): complex children cannot be converted to label prop"
|
|
146
|
+
)
|
|
147
|
+
})
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
describe("existing label prop scenarios", () => {
|
|
151
|
+
it("should add comment when Link has both label and children", () => {
|
|
152
|
+
const input = `
|
|
153
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
154
|
+
|
|
155
|
+
function Test() {
|
|
156
|
+
return (
|
|
157
|
+
<div>
|
|
158
|
+
<Link href="/home" label="Existing Label">
|
|
159
|
+
Child Content
|
|
160
|
+
</Link>
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
`.trim()
|
|
165
|
+
|
|
166
|
+
const result = applyTransform(input)
|
|
167
|
+
expect(result).toContain(
|
|
168
|
+
"{/* TODO: tapestry-migration (label): Link has both label prop and children"
|
|
169
|
+
)
|
|
170
|
+
expect(result).toContain(
|
|
171
|
+
"take time to find the right text for the component."
|
|
172
|
+
)
|
|
173
|
+
expect(result).not.toContain("If icons are used")
|
|
174
|
+
expect(result).toContain('label="Existing Label"')
|
|
175
|
+
expect(result).toContain("Child Content")
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it("should handle Link with label and simple text children", () => {
|
|
179
|
+
const input = `
|
|
180
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
181
|
+
|
|
182
|
+
function Test() {
|
|
183
|
+
return <Link href="/about" label="About">About Us</Link>
|
|
184
|
+
}
|
|
185
|
+
`.trim()
|
|
186
|
+
|
|
187
|
+
const result = applyTransform(input)
|
|
188
|
+
expect(result).toContain(
|
|
189
|
+
"TODO: tapestry-migration (label): Link has both label prop and children"
|
|
190
|
+
)
|
|
191
|
+
expect(result).not.toContain("If icons are used")
|
|
192
|
+
expect(result).toContain('label="About"')
|
|
193
|
+
expect(result).toContain("About Us")
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
describe("edge cases", () => {
|
|
198
|
+
it("should skip Links with no children", () => {
|
|
199
|
+
const input = `
|
|
200
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
201
|
+
|
|
202
|
+
function Test() {
|
|
203
|
+
return <Link href="/home" label="Home" />
|
|
204
|
+
}
|
|
205
|
+
`.trim()
|
|
206
|
+
|
|
207
|
+
const result = applyTransform(input)
|
|
208
|
+
expect(result).toBeNull()
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it("should skip Links with only whitespace children", () => {
|
|
212
|
+
const input = `
|
|
213
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
214
|
+
|
|
215
|
+
function Test() {
|
|
216
|
+
return <Link href="/home"> </Link>
|
|
217
|
+
}
|
|
218
|
+
`.trim()
|
|
219
|
+
|
|
220
|
+
const result = applyTransform(input)
|
|
221
|
+
expect(result).toBeNull()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it("should handle mixed whitespace and text", () => {
|
|
225
|
+
const input = `
|
|
226
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
227
|
+
|
|
228
|
+
function Test() {
|
|
229
|
+
return <Link href="/home">
|
|
230
|
+
Home
|
|
231
|
+
</Link>
|
|
232
|
+
}
|
|
233
|
+
`.trim()
|
|
234
|
+
|
|
235
|
+
const result = applyTransform(input)
|
|
236
|
+
expect(result).toContain('<Link href="/home" label="Home" />')
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it("should not transform Link from other packages", () => {
|
|
240
|
+
const input = `
|
|
241
|
+
import { Link } from "some-other-package"
|
|
242
|
+
|
|
243
|
+
function Test() {
|
|
244
|
+
return <Link href="/home">Home</Link>
|
|
245
|
+
}
|
|
246
|
+
`.trim()
|
|
247
|
+
|
|
248
|
+
const result = applyTransform(input)
|
|
249
|
+
expect(result).toBeNull()
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it("should handle aliased imports", () => {
|
|
253
|
+
const input = `
|
|
254
|
+
import { Link as TapestryLink } from "@planningcenter/tapestry-react"
|
|
255
|
+
|
|
256
|
+
function Test() {
|
|
257
|
+
return <TapestryLink href="/home">Home</TapestryLink>
|
|
258
|
+
}
|
|
259
|
+
`.trim()
|
|
260
|
+
|
|
261
|
+
const result = applyTransform(input)
|
|
262
|
+
expect(result).toContain('<TapestryLink href="/home" label="Home" />')
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it("should return null for files without tapestry-react Link", () => {
|
|
266
|
+
const input = `
|
|
267
|
+
function Test() {
|
|
268
|
+
return <a href="/home">Home</a>
|
|
269
|
+
}
|
|
270
|
+
`.trim()
|
|
271
|
+
|
|
272
|
+
const result = applyTransform(input)
|
|
273
|
+
expect(result).toBeNull()
|
|
274
|
+
})
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
describe("multiple links", () => {
|
|
278
|
+
it("should handle multiple links with different scenarios", () => {
|
|
279
|
+
const input = `
|
|
280
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
281
|
+
|
|
282
|
+
function Test() {
|
|
283
|
+
return (
|
|
284
|
+
<div>
|
|
285
|
+
<Link href="/home">Home</Link>
|
|
286
|
+
<Link href="/about" label="About">About Content</Link>
|
|
287
|
+
<Link href="/contact">{"Contact"}</Link>
|
|
288
|
+
</div>
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
`.trim()
|
|
292
|
+
|
|
293
|
+
const result = applyTransform(input)
|
|
294
|
+
expect(result).toContain('<Link href="/home" label="Home" />')
|
|
295
|
+
expect(result).toContain(
|
|
296
|
+
"{/* TODO: tapestry-migration (label): Link has both label prop and children"
|
|
297
|
+
)
|
|
298
|
+
expect(result).toContain('<Link href="/contact" label="Contact" />')
|
|
299
|
+
})
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
describe("comment formatting", () => {
|
|
303
|
+
it("should properly format JSX comments in JSX context", () => {
|
|
304
|
+
const input = `
|
|
305
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
306
|
+
|
|
307
|
+
function Test() {
|
|
308
|
+
return (
|
|
309
|
+
<div>
|
|
310
|
+
<Link href="/complex">
|
|
311
|
+
<span>Complex</span>
|
|
312
|
+
</Link>
|
|
313
|
+
</div>
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
`.trim()
|
|
317
|
+
|
|
318
|
+
const result = applyTransform(input)
|
|
319
|
+
|
|
320
|
+
// Should have proper JSX comment syntax
|
|
321
|
+
expect(result).toMatch(/\{\s*\/\*\s*TODO: tapestry-migration/)
|
|
322
|
+
expect(result).toMatch(/\*\/\s*\}/)
|
|
323
|
+
expect(result).toContain(
|
|
324
|
+
"take time to find the right text for the component"
|
|
325
|
+
)
|
|
326
|
+
expect(result).toContain(
|
|
327
|
+
"If icons are used in the component, you can use prefix and suffix to correctly display those icons"
|
|
328
|
+
)
|
|
329
|
+
})
|
|
330
|
+
})
|
|
331
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addAttribute } from "../../shared/actions/addAttribute"
|
|
4
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
5
|
+
import { removeChildren } from "../../shared/actions/removeChildren"
|
|
6
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
7
|
+
import { hasChildren } from "../../shared/conditions/hasChildren"
|
|
8
|
+
import {
|
|
9
|
+
buildComment,
|
|
10
|
+
extractTextContent,
|
|
11
|
+
} from "../../shared/helpers/childrenToLabelHelpers"
|
|
12
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
13
|
+
|
|
14
|
+
const transform: Transform = attributeTransformFactory({
|
|
15
|
+
condition: hasChildren,
|
|
16
|
+
targetComponent: "Link",
|
|
17
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
18
|
+
transform: (element, { j, source }) => {
|
|
19
|
+
if (hasAttribute("label")(element)) {
|
|
20
|
+
addComment({
|
|
21
|
+
element,
|
|
22
|
+
j,
|
|
23
|
+
scope: "label",
|
|
24
|
+
source,
|
|
25
|
+
text: buildComment("Link has both label prop and children", false),
|
|
26
|
+
})
|
|
27
|
+
return true
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { isSimpleText, textContent } = extractTextContent(element.children!)
|
|
31
|
+
|
|
32
|
+
if (isSimpleText && textContent) {
|
|
33
|
+
addAttribute({ element, j, name: "label", value: textContent })
|
|
34
|
+
removeChildren(element)
|
|
35
|
+
return true
|
|
36
|
+
} else if (!isSimpleText) {
|
|
37
|
+
addComment({
|
|
38
|
+
element,
|
|
39
|
+
j,
|
|
40
|
+
scope: "children",
|
|
41
|
+
source,
|
|
42
|
+
text: buildComment(
|
|
43
|
+
"complex children cannot be converted to label prop",
|
|
44
|
+
true
|
|
45
|
+
),
|
|
46
|
+
})
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return false
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
export default transform
|