@planningcenter/tapestry-migration-cli 2.3.0-rc.1 → 2.3.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/dist/tapestry-react-shim.cjs +5065 -0
- package/package.json +9 -5
- package/src/components/button/index.ts +48 -4
- package/src/components/button/transforms/auditSpreadProps.test.ts +352 -0
- package/src/components/button/transforms/auditSpreadProps.ts +24 -0
- package/src/components/button/transforms/childrenToLabel.test.ts +363 -0
- package/src/components/button/transforms/childrenToLabel.ts +84 -0
- package/src/components/button/transforms/commentOnVisualKindDifference.ts +24 -0
- package/src/components/button/transforms/convertStyleProps.test.ts +464 -0
- package/src/components/button/transforms/convertStyleProps.ts +16 -0
- package/src/components/button/transforms/iconToIconButton.test.ts +377 -0
- package/src/components/button/transforms/iconToIconButton.ts +53 -0
- package/src/components/button/transforms/removeAsButton.ts +15 -0
- package/src/components/button/transforms/removeDuplicateKeys.test.ts +302 -0
- package/src/components/button/transforms/removeDuplicateKeys.ts +8 -0
- package/src/components/button/transforms/reviewStyles.ts +17 -0
- package/src/components/button/transforms/spinnerToLoadingButton.test.ts +165 -0
- package/src/components/button/transforms/spinnerToLoadingButton.ts +14 -0
- package/src/components/button/transforms/themeVariantToKind.test.ts +401 -0
- package/src/components/button/transforms/themeVariantToKind.ts +90 -0
- package/src/components/button/transforms/unsupportedProps.ts +73 -0
- package/src/components/shared/actions/addAttribute.test.ts +300 -0
- package/src/components/shared/actions/addAttribute.ts +65 -0
- package/src/components/shared/actions/addComment.test.ts +1 -1
- package/src/components/shared/actions/addCommentToAttribute.test.ts +45 -0
- package/src/components/shared/actions/addCommentToAttribute.ts +28 -0
- package/src/components/shared/actions/addCommentToUnsupportedProps.ts +29 -0
- package/src/components/shared/actions/getAttributeValue.test.ts +261 -0
- package/src/components/shared/actions/getAttributeValue.ts +15 -0
- package/src/components/shared/actions/getSpreadProps.ts +7 -0
- package/src/components/shared/actions/hasSpreadProps.ts +7 -0
- package/src/components/shared/actions/removeChildren.ts +7 -0
- package/src/components/shared/actions/removeDuplicateKeys.test.ts +280 -0
- package/src/components/shared/actions/removeDuplicateKeys.ts +45 -0
- package/src/components/shared/actions/removeUnusedImport.test.ts +302 -0
- package/src/components/shared/actions/removeUnusedImport.ts +81 -0
- package/src/components/shared/actions/transformElementName.test.ts +9 -9
- package/src/components/shared/actions/transformElementName.ts +13 -16
- package/src/components/shared/conditions/hasChildren.ts +5 -0
- package/src/components/shared/getJavaScriptTheme.ts +68 -0
- package/src/components/shared/jsThemeLoader.ts +85 -0
- package/src/components/shared/transformFactories/attributeCombineFactory.test.ts +374 -0
- package/src/components/shared/transformFactories/attributeCombineFactory.ts +300 -0
- package/src/components/shared/transformFactories/attributeTransformFactory.ts +14 -6
- package/src/components/shared/transformFactories/componentTransformFactory.ts +1 -1
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +362 -0
- package/src/index.ts +4 -0
- package/src/stubs/stackViewPlugin.ts +33 -0
- package/src/stubs/tapestry-stub.ts +16 -0
- package/src/tapestry-react-shim.ts +7 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import jscodeshift, {
|
|
2
|
+
BooleanLiteral,
|
|
3
|
+
ConditionalExpression,
|
|
4
|
+
Identifier,
|
|
5
|
+
JSXAttribute,
|
|
6
|
+
JSXElement,
|
|
7
|
+
JSXExpressionContainer,
|
|
8
|
+
JSXIdentifier,
|
|
9
|
+
StringLiteral,
|
|
10
|
+
} from "jscodeshift"
|
|
11
|
+
import { describe, expect, it } from "vitest"
|
|
12
|
+
|
|
13
|
+
import { addAttribute, Conditional } from "./addAttribute"
|
|
14
|
+
|
|
15
|
+
const j = jscodeshift.withParser("tsx")
|
|
16
|
+
|
|
17
|
+
function createElementFromCode(code: string): JSXElement {
|
|
18
|
+
const source = j(`<div>${code}</div>`)
|
|
19
|
+
return source.find(j.JSXElement).at(0).get().value.children?.[0] as JSXElement
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getAttributeFromElement(
|
|
23
|
+
element: JSXElement,
|
|
24
|
+
name: string
|
|
25
|
+
): JSXAttribute | null {
|
|
26
|
+
const attributes = element.openingElement.attributes || []
|
|
27
|
+
return (
|
|
28
|
+
(attributes.find(
|
|
29
|
+
(attr) =>
|
|
30
|
+
attr.type === "JSXAttribute" &&
|
|
31
|
+
(attr.name as JSXIdentifier)?.name === name
|
|
32
|
+
) as JSXAttribute) || null
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("addAttribute", () => {
|
|
37
|
+
describe("string values", () => {
|
|
38
|
+
it("should add string attribute to element", () => {
|
|
39
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
40
|
+
|
|
41
|
+
addAttribute({ element, j, name: "kind", value: "primary" })
|
|
42
|
+
|
|
43
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
44
|
+
expect(kindAttr).not.toBeNull()
|
|
45
|
+
expect(kindAttr?.value?.type).toBe("StringLiteral")
|
|
46
|
+
expect((kindAttr?.value as StringLiteral)?.value).toBe("primary")
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("should add multiple string attributes", () => {
|
|
50
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
51
|
+
|
|
52
|
+
addAttribute({ element, j, name: "kind", value: "primary" })
|
|
53
|
+
addAttribute({ element, j, name: "size", value: "large" })
|
|
54
|
+
|
|
55
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
56
|
+
const sizeAttr = getAttributeFromElement(element, "size")
|
|
57
|
+
|
|
58
|
+
expect(kindAttr).not.toBeNull()
|
|
59
|
+
expect(sizeAttr).not.toBeNull()
|
|
60
|
+
expect((kindAttr?.value as StringLiteral)?.value).toBe("primary")
|
|
61
|
+
expect((sizeAttr?.value as StringLiteral)?.value).toBe("large")
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it("should handle empty string values", () => {
|
|
65
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
66
|
+
|
|
67
|
+
addAttribute({ element, j, name: "title", value: "" })
|
|
68
|
+
|
|
69
|
+
const titleAttr = getAttributeFromElement(element, "title")
|
|
70
|
+
expect(titleAttr).not.toBeNull()
|
|
71
|
+
expect(titleAttr?.value?.type).toBe("StringLiteral")
|
|
72
|
+
expect((titleAttr?.value as StringLiteral)?.value).toBe("")
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it("should handle special characters in string values", () => {
|
|
76
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
77
|
+
|
|
78
|
+
addAttribute({ element, j, name: "aria-label", value: "Save & Continue" })
|
|
79
|
+
|
|
80
|
+
const ariaAttr = getAttributeFromElement(element, "aria-label")
|
|
81
|
+
expect(ariaAttr).not.toBeNull()
|
|
82
|
+
expect((ariaAttr?.value as StringLiteral)?.value).toBe("Save & Continue")
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe("boolean values", () => {
|
|
87
|
+
it("should add boolean true attribute", () => {
|
|
88
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
89
|
+
|
|
90
|
+
addAttribute({ element, j, name: "disabled", value: true })
|
|
91
|
+
|
|
92
|
+
const disabledAttr = getAttributeFromElement(element, "disabled")
|
|
93
|
+
expect(disabledAttr).not.toBeNull()
|
|
94
|
+
expect(disabledAttr?.value?.type).toBe("JSXExpressionContainer")
|
|
95
|
+
|
|
96
|
+
const container = disabledAttr?.value as JSXExpressionContainer
|
|
97
|
+
expect(container.expression.type).toBe("BooleanLiteral")
|
|
98
|
+
expect((container.expression as BooleanLiteral).value).toBe(true)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it("should add boolean false attribute", () => {
|
|
102
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
103
|
+
|
|
104
|
+
addAttribute({ element, j, name: "loading", value: false })
|
|
105
|
+
|
|
106
|
+
const loadingAttr = getAttributeFromElement(element, "loading")
|
|
107
|
+
expect(loadingAttr).not.toBeNull()
|
|
108
|
+
expect(loadingAttr?.value?.type).toBe("JSXExpressionContainer")
|
|
109
|
+
|
|
110
|
+
const container = loadingAttr?.value as JSXExpressionContainer
|
|
111
|
+
expect(container.expression.type).toBe("BooleanLiteral")
|
|
112
|
+
expect((container.expression as BooleanLiteral).value).toBe(false)
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
describe("null values", () => {
|
|
117
|
+
it("should not add attribute when value is null", () => {
|
|
118
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
119
|
+
const initialAttrCount = (element.openingElement.attributes || []).length
|
|
120
|
+
|
|
121
|
+
addAttribute({ element, j, name: "kind", value: null })
|
|
122
|
+
|
|
123
|
+
const finalAttrCount = (element.openingElement.attributes || []).length
|
|
124
|
+
expect(finalAttrCount).toBe(initialAttrCount)
|
|
125
|
+
|
|
126
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
127
|
+
expect(kindAttr).toBeNull()
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe("conditional values", () => {
|
|
132
|
+
it("should add conditional attribute with test, consequent, and alternate", () => {
|
|
133
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
134
|
+
const conditionalValue: Conditional = {
|
|
135
|
+
alternate: "secondary",
|
|
136
|
+
consequent: "primary",
|
|
137
|
+
test: "isPrimary",
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
addAttribute({ element, j, name: "kind", value: conditionalValue })
|
|
141
|
+
|
|
142
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
143
|
+
expect(kindAttr).not.toBeNull()
|
|
144
|
+
expect(kindAttr?.value?.type).toBe("JSXExpressionContainer")
|
|
145
|
+
|
|
146
|
+
const container = kindAttr?.value as JSXExpressionContainer
|
|
147
|
+
expect(container.expression.type).toBe("ConditionalExpression")
|
|
148
|
+
|
|
149
|
+
const conditional = container.expression as ConditionalExpression
|
|
150
|
+
expect(conditional.test.type).toBe("Identifier")
|
|
151
|
+
expect((conditional.test as Identifier).name).toBe("isPrimary")
|
|
152
|
+
expect(conditional.consequent.type).toBe("StringLiteral")
|
|
153
|
+
expect((conditional.consequent as StringLiteral).value).toBe("primary")
|
|
154
|
+
expect(conditional.alternate.type).toBe("StringLiteral")
|
|
155
|
+
expect((conditional.alternate as StringLiteral).value).toBe("secondary")
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it("should handle conditional with empty string values", () => {
|
|
159
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
160
|
+
const conditionalValue: Conditional = {
|
|
161
|
+
alternate: "",
|
|
162
|
+
consequent: "primary",
|
|
163
|
+
test: "hasTheme",
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
addAttribute({ element, j, name: "kind", value: conditionalValue })
|
|
167
|
+
|
|
168
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
169
|
+
expect(kindAttr).not.toBeNull()
|
|
170
|
+
|
|
171
|
+
const container = kindAttr?.value as JSXExpressionContainer
|
|
172
|
+
const conditional = container.expression as ConditionalExpression
|
|
173
|
+
expect((conditional.consequent as StringLiteral).value).toBe("primary")
|
|
174
|
+
expect((conditional.alternate as StringLiteral).value).toBe("")
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it("should handle conditional with same consequent and alternate", () => {
|
|
178
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
179
|
+
const conditionalValue: Conditional = {
|
|
180
|
+
alternate: "neutral",
|
|
181
|
+
consequent: "neutral",
|
|
182
|
+
test: "condition",
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
addAttribute({ element, j, name: "kind", value: conditionalValue })
|
|
186
|
+
|
|
187
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
188
|
+
expect(kindAttr).not.toBeNull()
|
|
189
|
+
|
|
190
|
+
const container = kindAttr?.value as JSXExpressionContainer
|
|
191
|
+
const conditional = container.expression as ConditionalExpression
|
|
192
|
+
expect((conditional.consequent as StringLiteral).value).toBe("neutral")
|
|
193
|
+
expect((conditional.alternate as StringLiteral).value).toBe("neutral")
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it("should render conditional attribute correctly in source", () => {
|
|
197
|
+
const source = j("<Button>Save</Button>")
|
|
198
|
+
const element = source.find(j.JSXElement).at(0).get().value as JSXElement
|
|
199
|
+
const conditionalValue: Conditional = {
|
|
200
|
+
alternate: "secondary",
|
|
201
|
+
consequent: "primary",
|
|
202
|
+
test: "isPrimary",
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
addAttribute({ element, j, name: "kind", value: conditionalValue })
|
|
206
|
+
|
|
207
|
+
const result = source.toSource()
|
|
208
|
+
expect(result).toContain('kind={isPrimary ? "primary" : "secondary"}')
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it("should handle multiple conditional attributes", () => {
|
|
212
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
213
|
+
const kindConditional: Conditional = {
|
|
214
|
+
alternate: "secondary",
|
|
215
|
+
consequent: "primary",
|
|
216
|
+
test: "isPrimary",
|
|
217
|
+
}
|
|
218
|
+
const sizeConditional: Conditional = {
|
|
219
|
+
alternate: "small",
|
|
220
|
+
consequent: "large",
|
|
221
|
+
test: "isLarge",
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
addAttribute({ element, j, name: "kind", value: kindConditional })
|
|
225
|
+
addAttribute({ element, j, name: "size", value: sizeConditional })
|
|
226
|
+
|
|
227
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
228
|
+
const sizeAttr = getAttributeFromElement(element, "size")
|
|
229
|
+
|
|
230
|
+
expect(kindAttr).not.toBeNull()
|
|
231
|
+
expect(sizeAttr).not.toBeNull()
|
|
232
|
+
expect(element.openingElement.attributes).toHaveLength(2)
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
describe("adding to elements with existing attributes", () => {
|
|
237
|
+
it("should add attribute to element that already has attributes", () => {
|
|
238
|
+
const element = createElementFromCode(
|
|
239
|
+
'<Button onClick={handleClick} className="btn">Save</Button>'
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
addAttribute({ element, j, name: "kind", value: "primary" })
|
|
243
|
+
|
|
244
|
+
expect(element.openingElement.attributes).toHaveLength(3)
|
|
245
|
+
|
|
246
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
247
|
+
expect(kindAttr).not.toBeNull()
|
|
248
|
+
expect((kindAttr?.value as StringLiteral)?.value).toBe("primary")
|
|
249
|
+
|
|
250
|
+
const onClickAttr = getAttributeFromElement(element, "onClick")
|
|
251
|
+
const classAttr = getAttributeFromElement(element, "className")
|
|
252
|
+
expect(onClickAttr).not.toBeNull()
|
|
253
|
+
expect(classAttr).not.toBeNull()
|
|
254
|
+
})
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
describe("adding to self-closing elements", () => {
|
|
258
|
+
it("should add attribute to self-closing element", () => {
|
|
259
|
+
const element = createElementFromCode("<Button />")
|
|
260
|
+
|
|
261
|
+
addAttribute({ element, j, name: "kind", value: "primary" })
|
|
262
|
+
|
|
263
|
+
const kindAttr = getAttributeFromElement(element, "kind")
|
|
264
|
+
expect(kindAttr).not.toBeNull()
|
|
265
|
+
expect((kindAttr?.value as StringLiteral)?.value).toBe("primary")
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
describe("integration with JSCodeshift", () => {
|
|
270
|
+
it("should render correctly in transformed source", () => {
|
|
271
|
+
const source = j("<Button>Save</Button>")
|
|
272
|
+
const element = source.find(j.JSXElement).at(0).get().value as JSXElement
|
|
273
|
+
|
|
274
|
+
addAttribute({ element, j, name: "kind", value: "primary" })
|
|
275
|
+
addAttribute({ element, j, name: "disabled", value: true })
|
|
276
|
+
|
|
277
|
+
const result = source.toSource()
|
|
278
|
+
|
|
279
|
+
expect(result).toContain('kind="primary"')
|
|
280
|
+
expect(result).toContain("disabled={true}")
|
|
281
|
+
expect(result).toContain("<Button")
|
|
282
|
+
expect(result).toContain(">Save</Button>")
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it("should handle complex attribute names", () => {
|
|
286
|
+
const element = createElementFromCode("<Button>Save</Button>")
|
|
287
|
+
|
|
288
|
+
addAttribute({ element, j, name: "data-testid", value: "save-button" })
|
|
289
|
+
addAttribute({ element, j, name: "aria-describedby", value: "help-text" })
|
|
290
|
+
|
|
291
|
+
const testIdAttr = getAttributeFromElement(element, "data-testid")
|
|
292
|
+
const ariaAttr = getAttributeFromElement(element, "aria-describedby")
|
|
293
|
+
|
|
294
|
+
expect(testIdAttr).not.toBeNull()
|
|
295
|
+
expect(ariaAttr).not.toBeNull()
|
|
296
|
+
expect((testIdAttr?.value as StringLiteral)?.value).toBe("save-button")
|
|
297
|
+
expect((ariaAttr?.value as StringLiteral)?.value).toBe("help-text")
|
|
298
|
+
})
|
|
299
|
+
})
|
|
300
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { JSCodeshift, JSXElement } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
export interface Conditional {
|
|
4
|
+
alternate: string
|
|
5
|
+
consequent: string
|
|
6
|
+
test: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function formatValue(value: string | boolean, j: JSCodeshift) {
|
|
10
|
+
if (typeof value === "string") {
|
|
11
|
+
return j.stringLiteral(value)
|
|
12
|
+
} else if (typeof value === "boolean") {
|
|
13
|
+
return j.jsxExpressionContainer(j.booleanLiteral(value))
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error(`Unsupported attribute value type: ${typeof value}`)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function addAttribute({
|
|
20
|
+
element,
|
|
21
|
+
name,
|
|
22
|
+
j,
|
|
23
|
+
value,
|
|
24
|
+
}: {
|
|
25
|
+
element: JSXElement
|
|
26
|
+
j: JSCodeshift
|
|
27
|
+
name: string
|
|
28
|
+
value: string | boolean | Conditional | null
|
|
29
|
+
}) {
|
|
30
|
+
if (value === null) return
|
|
31
|
+
const attributes = element.openingElement.attributes || []
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
typeof value === "object" &&
|
|
35
|
+
"test" in value &&
|
|
36
|
+
"consequent" in value &&
|
|
37
|
+
"alternate" in value
|
|
38
|
+
) {
|
|
39
|
+
addConditionalAttribute(value, element, j, name)
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const formattedValue = formatValue(value, j)
|
|
44
|
+
attributes.push(j.jsxAttribute(j.jsxIdentifier(name), formattedValue))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function addConditionalAttribute(
|
|
48
|
+
{ test, consequent, alternate }: Conditional,
|
|
49
|
+
element: JSXElement,
|
|
50
|
+
j: JSCodeshift,
|
|
51
|
+
targetAttribute: string
|
|
52
|
+
) {
|
|
53
|
+
const conditional = j.jsxExpressionContainer(
|
|
54
|
+
j.conditionalExpression(
|
|
55
|
+
j.identifier(test),
|
|
56
|
+
j.stringLiteral(consequent),
|
|
57
|
+
j.stringLiteral(alternate)
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
const attribute = j.jsxAttribute(
|
|
61
|
+
j.jsxIdentifier(targetAttribute),
|
|
62
|
+
conditional
|
|
63
|
+
)
|
|
64
|
+
element.openingElement.attributes?.push(attribute)
|
|
65
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import jscodeshift, {
|
|
2
|
+
JSXAttribute,
|
|
3
|
+
JSXElement,
|
|
4
|
+
JSXIdentifier,
|
|
5
|
+
} from "jscodeshift"
|
|
6
|
+
import { describe, expect, it } from "vitest"
|
|
7
|
+
|
|
8
|
+
import { addCommentToAttribute } from "./addCommentToAttribute"
|
|
9
|
+
|
|
10
|
+
const j = jscodeshift.withParser("tsx")
|
|
11
|
+
|
|
12
|
+
describe("addCommentToAttribute", () => {
|
|
13
|
+
it("adds comments before attribute if provided", () => {
|
|
14
|
+
const outerCode = `
|
|
15
|
+
<div>
|
|
16
|
+
<Button
|
|
17
|
+
onClick={() => { console.log('hi')}}
|
|
18
|
+
kind='secondary'
|
|
19
|
+
fullWidth
|
|
20
|
+
{...props}>Save</Button>
|
|
21
|
+
</div>
|
|
22
|
+
`
|
|
23
|
+
const source = j(outerCode)
|
|
24
|
+
const buttonElement = source
|
|
25
|
+
.find(j.JSXElement)
|
|
26
|
+
.filter(
|
|
27
|
+
(path) =>
|
|
28
|
+
(path.value.openingElement.name as JSXIdentifier)?.name === "Button"
|
|
29
|
+
)
|
|
30
|
+
.at(0)
|
|
31
|
+
.get().value as JSXElement
|
|
32
|
+
const attribute = buttonElement.openingElement.attributes?.find(
|
|
33
|
+
(attr) => attr.type === "JSXAttribute" && attr.name.name === "kind"
|
|
34
|
+
) as JSXAttribute
|
|
35
|
+
|
|
36
|
+
const commentText = "This needs to be updated"
|
|
37
|
+
addCommentToAttribute({ attribute, j, text: commentText })
|
|
38
|
+
|
|
39
|
+
const result = source.toSource()
|
|
40
|
+
|
|
41
|
+
expect(result)
|
|
42
|
+
.toContain(`/* TODO: tapestry-migration (kind): ${commentText} */
|
|
43
|
+
kind='secondary'`)
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { JSCodeshift, JSXAttribute, JSXSpreadAttribute } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { formatComment } from "./addComment"
|
|
4
|
+
|
|
5
|
+
export function addCommentToAttribute({
|
|
6
|
+
text,
|
|
7
|
+
attribute,
|
|
8
|
+
j,
|
|
9
|
+
}: {
|
|
10
|
+
attribute: JSXAttribute | JSXSpreadAttribute
|
|
11
|
+
j: JSCodeshift
|
|
12
|
+
text: string
|
|
13
|
+
}) {
|
|
14
|
+
const attributeName =
|
|
15
|
+
((attribute.type === "JSXAttribute" && attribute.name.name) as string) ||
|
|
16
|
+
"spreadAttribute"
|
|
17
|
+
const comment = j.commentBlock(
|
|
18
|
+
formatComment(text, attributeName),
|
|
19
|
+
true,
|
|
20
|
+
false
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if (attribute.comments) {
|
|
24
|
+
attribute.comments.unshift(comment)
|
|
25
|
+
} else {
|
|
26
|
+
attribute.comments = [comment]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { JSCodeshift, JSXAttribute, JSXElement } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addCommentToAttribute } from "./addCommentToAttribute"
|
|
4
|
+
|
|
5
|
+
export function addCommentToUnsupportedProps({
|
|
6
|
+
element,
|
|
7
|
+
j,
|
|
8
|
+
props,
|
|
9
|
+
messageSuffix = () => "",
|
|
10
|
+
}: {
|
|
11
|
+
element: JSXElement
|
|
12
|
+
j: JSCodeshift
|
|
13
|
+
messageSuffix?: (prop: string) => string
|
|
14
|
+
props: string[]
|
|
15
|
+
}): boolean {
|
|
16
|
+
const unsupportedAttributes = (
|
|
17
|
+
element.openingElement.attributes || []
|
|
18
|
+
).filter(
|
|
19
|
+
(attr) =>
|
|
20
|
+
attr.type === "JSXAttribute" && props.includes(attr.name.name as string)
|
|
21
|
+
) as JSXAttribute[]
|
|
22
|
+
|
|
23
|
+
unsupportedAttributes.forEach((attribute) => {
|
|
24
|
+
const propName = attribute.name.name as string
|
|
25
|
+
const text = `'${propName}' is not supported, please migrate as needed.${messageSuffix(propName)}`
|
|
26
|
+
addCommentToAttribute({ attribute, j, text })
|
|
27
|
+
})
|
|
28
|
+
return unsupportedAttributes.length > 0
|
|
29
|
+
}
|