@planningcenter/tapestry-migration-cli 3.5.0 → 3.6.1-qa-1035.0
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/README.md +37 -0
- package/dist/tapestry-react-shim.cjs +195 -11
- package/package.json +3 -3
- package/src/availableComponents.ts +1 -0
- package/src/components/button/transforms/tooltipToWrapper.test.ts +50 -3
- package/src/components/button/transforms/tooltipToWrapper.ts +18 -0
- package/src/components/flex/index.test.ts +291 -0
- package/src/components/flex/index.ts +54 -0
- package/src/components/flex/transforms/alignmentToAlign.test.ts +185 -0
- package/src/components/flex/transforms/alignmentToAlign.ts +73 -0
- package/src/components/flex/transforms/auditSpreadProps.test.ts +93 -0
- package/src/components/flex/transforms/auditSpreadProps.ts +6 -0
- package/src/components/flex/transforms/axisToDirection.test.ts +155 -0
- package/src/components/flex/transforms/axisToDirection.ts +77 -0
- package/src/components/flex/transforms/convertStyleProps.test.ts +147 -0
- package/src/components/flex/transforms/convertStyleProps.ts +23 -0
- package/src/components/flex/transforms/cssFlexAliasesToProps.test.ts +202 -0
- package/src/components/flex/transforms/cssFlexAliasesToProps.ts +110 -0
- package/src/components/flex/transforms/distributionToJustify.test.ts +214 -0
- package/src/components/flex/transforms/distributionToJustify.ts +76 -0
- package/src/components/flex/transforms/flexPropToExpand.test.ts +156 -0
- package/src/components/flex/transforms/flexPropToExpand.ts +100 -0
- package/src/components/flex/transforms/innerRefToRef.test.ts +100 -0
- package/src/components/flex/transforms/innerRefToRef.ts +14 -0
- package/src/components/flex/transforms/mediaQueriesToResponsive.test.ts +714 -0
- package/src/components/flex/transforms/mediaQueriesToResponsive.ts +523 -0
- package/src/components/flex/transforms/moveFlexImport.test.ts +202 -0
- package/src/components/flex/transforms/moveFlexImport.ts +14 -0
- package/src/components/flex/transforms/paddingPropsToFlex.test.ts +252 -0
- package/src/components/flex/transforms/paddingPropsToFlex.ts +150 -0
- package/src/components/flex/transforms/setDefaultAxis.test.ts +126 -0
- package/src/components/flex/transforms/setDefaultAxis.ts +30 -0
- package/src/components/flex/transforms/spacingToGap.test.ts +176 -0
- package/src/components/flex/transforms/spacingToGap.ts +49 -0
- package/src/components/flex/transforms/unsupportedProps.test.ts +141 -0
- package/src/components/flex/transforms/unsupportedProps.ts +17 -0
- package/src/components/select/index.ts +2 -0
- package/src/components/select/transforms/onChangeSignature.test.ts +224 -0
- package/src/components/select/transforms/onChangeSignature.ts +172 -0
- package/src/components/shared/actions/resolveConstantAttributeValue.test.ts +122 -0
- package/src/components/shared/actions/resolveConstantAttributeValue.ts +31 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +41 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.test.ts +47 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +16 -0
- package/src/components/toggle-switch/index.ts +2 -0
- package/src/components/toggle-switch/transforms/onClickToOnChange.test.ts +210 -0
- package/src/components/toggle-switch/transforms/onClickToOnChange.ts +71 -0
- package/src/index.test.ts +79 -0
- package/src/index.ts +29 -0
- package/src/migrationList.ts +22 -0
- package/src/utils/componentLabel.test.ts +61 -0
- package/src/utils/componentLabel.ts +15 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
import { JSCodeshift, JSXAttribute, Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { formatComment } from "../../shared/actions/addComment"
|
|
4
|
+
import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
|
|
5
|
+
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
6
|
+
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
7
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
8
|
+
|
|
9
|
+
function commentOnAttr(
|
|
10
|
+
attr: JSXAttribute,
|
|
11
|
+
scope: string,
|
|
12
|
+
text: string,
|
|
13
|
+
j: JSCodeshift
|
|
14
|
+
) {
|
|
15
|
+
const comment = j.commentBlock(formatComment(text, scope), true, false)
|
|
16
|
+
attr.comments = attr.comments ? [...attr.comments, comment] : [comment]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const VALID_FLEX_BREAKPOINTS = new Set(["xs", "sm", "md", "lg", "xl"])
|
|
20
|
+
|
|
21
|
+
const CONSTANT_AXIS_MAP: Record<string, string> = {
|
|
22
|
+
HORIZONTAL: "horizontal",
|
|
23
|
+
VERTICAL: "vertical",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const AXIS_DIRECTION_MAP: Record<string, string> = {
|
|
27
|
+
horizontal: "row",
|
|
28
|
+
vertical: "column",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const VALID_ALIGN_VALUES = new Set([
|
|
32
|
+
"start",
|
|
33
|
+
"center",
|
|
34
|
+
"end",
|
|
35
|
+
"stretch",
|
|
36
|
+
"baseline",
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
const CONSTANT_ALIGN_MAP: Record<string, string> = {
|
|
40
|
+
BASELINE: "baseline",
|
|
41
|
+
CENTER: "center",
|
|
42
|
+
END: "end",
|
|
43
|
+
FILL: "fill",
|
|
44
|
+
START: "start",
|
|
45
|
+
STRETCH: "stretch",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const VALID_JUSTIFY_VALUES = new Set([
|
|
49
|
+
"start",
|
|
50
|
+
"center",
|
|
51
|
+
"end",
|
|
52
|
+
"space-between",
|
|
53
|
+
"space-evenly",
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
const CONSTANT_DISTRIBUTION_MAP: Record<string, string> = {
|
|
57
|
+
CENTER: "center",
|
|
58
|
+
END: "end",
|
|
59
|
+
FILL: "fill",
|
|
60
|
+
SPACE_BETWEEN: "space-between",
|
|
61
|
+
SPACE_EVENLY: "space-evenly",
|
|
62
|
+
START: "start",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const VALID_GAP_VALUES = new Set([0, 0.25, 0.5, 1, 1.5, 2, 3, 4, 5, 6, 7])
|
|
66
|
+
|
|
67
|
+
const PADDING_SCALE = new Set([0, 0.25, 0.5, 1, 1.5, 2, 3, 4, 5, 6, 7])
|
|
68
|
+
|
|
69
|
+
const PIXEL_TO_TOKEN: Record<string, number> = {
|
|
70
|
+
"0": 0,
|
|
71
|
+
"0px": 0,
|
|
72
|
+
"2px": 0.25,
|
|
73
|
+
"4px": 0.5,
|
|
74
|
+
"8px": 1,
|
|
75
|
+
"12px": 1.5,
|
|
76
|
+
"16px": 2,
|
|
77
|
+
"24px": 3,
|
|
78
|
+
"32px": 4,
|
|
79
|
+
"40px": 5,
|
|
80
|
+
"48px": 6,
|
|
81
|
+
"56px": 7,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const PADDING_SCALE_LIST = [...PADDING_SCALE].join(", ")
|
|
85
|
+
|
|
86
|
+
const PADDING_TODO =
|
|
87
|
+
"could not migrate padding automatically; " +
|
|
88
|
+
`use a Tapestry spacing scale number (${PADDING_SCALE_LIST}) ` +
|
|
89
|
+
"for Flex padding, or use style={{ padding: ... }} for arbitrary CSS values"
|
|
90
|
+
|
|
91
|
+
const FLEX_START_END_MAP: Record<string, string> = {
|
|
92
|
+
"flex-end": "end",
|
|
93
|
+
"flex-start": "start",
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const FLEX_RESPONSIVE_PASSTHROUGH = new Set([
|
|
97
|
+
"align",
|
|
98
|
+
"alignSelf",
|
|
99
|
+
"basis",
|
|
100
|
+
"columnGap",
|
|
101
|
+
"direction",
|
|
102
|
+
"gap",
|
|
103
|
+
"grow",
|
|
104
|
+
"justify",
|
|
105
|
+
"paddingBlock",
|
|
106
|
+
"paddingInline",
|
|
107
|
+
"rowGap",
|
|
108
|
+
"shrink",
|
|
109
|
+
"wrap",
|
|
110
|
+
])
|
|
111
|
+
|
|
112
|
+
type InnerPropEntry = {
|
|
113
|
+
newKey: string
|
|
114
|
+
newValue:
|
|
115
|
+
| ReturnType<JSCodeshift["stringLiteral"]>
|
|
116
|
+
| ReturnType<JSCodeshift["numericLiteral"]>
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
type InnerPropPlan = InnerPropEntry | { newEntries: InnerPropEntry[] }
|
|
120
|
+
|
|
121
|
+
type InnerPropResult = InnerPropPlan | { todo: string } | null
|
|
122
|
+
|
|
123
|
+
function planInnerProp(
|
|
124
|
+
propName: string,
|
|
125
|
+
propValue: unknown,
|
|
126
|
+
j: JSCodeshift
|
|
127
|
+
): InnerPropResult {
|
|
128
|
+
if (typeof propValue !== "object" || propValue === null)
|
|
129
|
+
return { todo: `could not resolve value; review manually` }
|
|
130
|
+
const node = propValue as {
|
|
131
|
+
property?: unknown
|
|
132
|
+
type: string
|
|
133
|
+
value?: unknown
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (propName === "axis") {
|
|
137
|
+
let axisValue: string | null = null
|
|
138
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
139
|
+
axisValue = node.value
|
|
140
|
+
} else if (node.type === "MemberExpression") {
|
|
141
|
+
const prop = node.property as { name?: string; type?: string } | undefined
|
|
142
|
+
if (prop?.type === "Identifier" && typeof prop.name === "string") {
|
|
143
|
+
axisValue = CONSTANT_AXIS_MAP[prop.name] ?? null
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const direction = axisValue !== null ? AXIS_DIRECTION_MAP[axisValue] : null
|
|
147
|
+
if (!direction)
|
|
148
|
+
return {
|
|
149
|
+
todo: `could not migrate to direction automatically; specify "horizontal" or "vertical"`,
|
|
150
|
+
}
|
|
151
|
+
return { newKey: "direction", newValue: j.stringLiteral(direction) }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (propName === "alignment") {
|
|
155
|
+
let alignValue: string | null = null
|
|
156
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
157
|
+
alignValue = FLEX_START_END_MAP[node.value] ?? node.value
|
|
158
|
+
} else if (node.type === "MemberExpression") {
|
|
159
|
+
const prop = node.property as { name?: string; type?: string } | undefined
|
|
160
|
+
if (prop?.type === "Identifier" && typeof prop.name === "string") {
|
|
161
|
+
alignValue = CONSTANT_ALIGN_MAP[prop.name] ?? null
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (alignValue === "fill")
|
|
165
|
+
return {
|
|
166
|
+
todo: '"fill" has no Flex equivalent; use expand on the child element instead',
|
|
167
|
+
}
|
|
168
|
+
if (alignValue === null || !VALID_ALIGN_VALUES.has(alignValue))
|
|
169
|
+
return {
|
|
170
|
+
todo: `could not migrate to align automatically; valid values are: ${[...VALID_ALIGN_VALUES].join(", ")}`,
|
|
171
|
+
}
|
|
172
|
+
return { newKey: "align", newValue: j.stringLiteral(alignValue) }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (propName === "distribution") {
|
|
176
|
+
let distValue: string | null = null
|
|
177
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
178
|
+
distValue = node.value
|
|
179
|
+
} else if (node.type === "MemberExpression") {
|
|
180
|
+
const prop = node.property as { name?: string; type?: string } | undefined
|
|
181
|
+
if (prop?.type === "Identifier" && typeof prop.name === "string") {
|
|
182
|
+
distValue = CONSTANT_DISTRIBUTION_MAP[prop.name] ?? null
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (distValue === "fill")
|
|
186
|
+
return {
|
|
187
|
+
todo: '"fill" has no Flex equivalent; use expand on child elements instead',
|
|
188
|
+
}
|
|
189
|
+
if (distValue === null || !VALID_JUSTIFY_VALUES.has(distValue))
|
|
190
|
+
return {
|
|
191
|
+
todo: `could not migrate to justify automatically; valid values are: ${[...VALID_JUSTIFY_VALUES].join(", ")}`,
|
|
192
|
+
}
|
|
193
|
+
return { newKey: "justify", newValue: j.stringLiteral(distValue) }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (propName === "spacing") {
|
|
197
|
+
if (
|
|
198
|
+
node.type === "NumericLiteral" &&
|
|
199
|
+
typeof node.value === "number" &&
|
|
200
|
+
VALID_GAP_VALUES.has(node.value)
|
|
201
|
+
) {
|
|
202
|
+
return { newKey: "gap", newValue: j.numericLiteral(node.value) }
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
todo: `could not migrate to gap automatically; use a Tapestry spacing scale number (${[...VALID_GAP_VALUES].join(", ")})`,
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (propName === "justifyContent") {
|
|
210
|
+
let value: string | null = null
|
|
211
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
212
|
+
const normalized = FLEX_START_END_MAP[node.value] ?? node.value
|
|
213
|
+
value = VALID_JUSTIFY_VALUES.has(normalized) ? normalized : null
|
|
214
|
+
}
|
|
215
|
+
if (value !== null)
|
|
216
|
+
return { newKey: "justify", newValue: j.stringLiteral(value) }
|
|
217
|
+
return {
|
|
218
|
+
todo: `could not migrate to justify automatically; valid values are: ${[...VALID_JUSTIFY_VALUES].join(", ")}`,
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (propName === "alignItems") {
|
|
223
|
+
let value: string | null = null
|
|
224
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
225
|
+
const normalized = FLEX_START_END_MAP[node.value] ?? node.value
|
|
226
|
+
value = VALID_ALIGN_VALUES.has(normalized) ? normalized : null
|
|
227
|
+
}
|
|
228
|
+
if (value !== null)
|
|
229
|
+
return { newKey: "align", newValue: j.stringLiteral(value) }
|
|
230
|
+
return {
|
|
231
|
+
todo: `could not migrate to align automatically; valid values are: ${[...VALID_ALIGN_VALUES].join(", ")}`,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (propName === "flexDirection") {
|
|
236
|
+
const VALID_DIRECTION_VALUES = new Set([
|
|
237
|
+
"row",
|
|
238
|
+
"column",
|
|
239
|
+
"row-reverse",
|
|
240
|
+
"column-reverse",
|
|
241
|
+
])
|
|
242
|
+
let value: string | null = null
|
|
243
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
244
|
+
value = VALID_DIRECTION_VALUES.has(node.value) ? node.value : null
|
|
245
|
+
}
|
|
246
|
+
if (value !== null)
|
|
247
|
+
return { newKey: "direction", newValue: j.stringLiteral(value) }
|
|
248
|
+
return {
|
|
249
|
+
todo: `could not migrate to direction automatically; valid values are: row, column, row-reverse, column-reverse`,
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (propName === "flexGrow" || propName === "flexShrink") {
|
|
254
|
+
const newKey = propName === "flexGrow" ? "grow" : "shrink"
|
|
255
|
+
if (node.type === "NumericLiteral" && typeof node.value === "number") {
|
|
256
|
+
return { newKey, newValue: j.numericLiteral(node.value) }
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
todo: `could not migrate to ${newKey} automatically; use a numeric value`,
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (propName === "flexBasis") {
|
|
264
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
265
|
+
return { newKey: "basis", newValue: j.stringLiteral(node.value) }
|
|
266
|
+
}
|
|
267
|
+
if (node.type === "NumericLiteral" && typeof node.value === "number") {
|
|
268
|
+
return { newKey: "basis", newValue: j.numericLiteral(node.value) }
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
todo: "could not migrate to basis automatically; use a string or numeric value",
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (propName === "flexWrap") {
|
|
276
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
277
|
+
return { newKey: "wrap", newValue: j.stringLiteral(node.value) }
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
todo: "could not migrate to wrap automatically; use a string value",
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (propName === "padding") {
|
|
285
|
+
if (
|
|
286
|
+
node.type === "NumericLiteral" &&
|
|
287
|
+
typeof node.value === "number" &&
|
|
288
|
+
PADDING_SCALE.has(node.value)
|
|
289
|
+
) {
|
|
290
|
+
return null
|
|
291
|
+
}
|
|
292
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
293
|
+
const mapped = PIXEL_TO_TOKEN[node.value]
|
|
294
|
+
if (mapped !== undefined)
|
|
295
|
+
return { newKey: "padding", newValue: j.numericLiteral(mapped) }
|
|
296
|
+
}
|
|
297
|
+
return { todo: PADDING_TODO }
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (propName === "paddingHorizontal") {
|
|
301
|
+
if (
|
|
302
|
+
node.type === "NumericLiteral" &&
|
|
303
|
+
typeof node.value === "number" &&
|
|
304
|
+
PADDING_SCALE.has(node.value)
|
|
305
|
+
) {
|
|
306
|
+
return { newKey: "paddingInline", newValue: j.numericLiteral(node.value) }
|
|
307
|
+
}
|
|
308
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
309
|
+
const mapped = PIXEL_TO_TOKEN[node.value]
|
|
310
|
+
if (mapped !== undefined)
|
|
311
|
+
return { newKey: "paddingInline", newValue: j.numericLiteral(mapped) }
|
|
312
|
+
}
|
|
313
|
+
return { todo: PADDING_TODO }
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (propName === "paddingVertical") {
|
|
317
|
+
if (
|
|
318
|
+
node.type === "NumericLiteral" &&
|
|
319
|
+
typeof node.value === "number" &&
|
|
320
|
+
PADDING_SCALE.has(node.value)
|
|
321
|
+
) {
|
|
322
|
+
return { newKey: "paddingBlock", newValue: j.numericLiteral(node.value) }
|
|
323
|
+
}
|
|
324
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
325
|
+
const mapped = PIXEL_TO_TOKEN[node.value]
|
|
326
|
+
if (mapped !== undefined)
|
|
327
|
+
return { newKey: "paddingBlock", newValue: j.numericLiteral(mapped) }
|
|
328
|
+
}
|
|
329
|
+
return { todo: PADDING_TODO }
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (propName === "flex") {
|
|
333
|
+
if (node.type === "StringLiteral" && typeof node.value === "string") {
|
|
334
|
+
const parts = (node.value as string).trim().split(/\s+/)
|
|
335
|
+
if (parts.length === 3) {
|
|
336
|
+
const grow = Number(parts[0])
|
|
337
|
+
const shrink = Number(parts[1])
|
|
338
|
+
const basis = parts[2]
|
|
339
|
+
if (Number.isFinite(grow) && Number.isFinite(shrink)) {
|
|
340
|
+
return {
|
|
341
|
+
newEntries: [
|
|
342
|
+
{ newKey: "grow", newValue: j.numericLiteral(grow) },
|
|
343
|
+
{ newKey: "shrink", newValue: j.numericLiteral(shrink) },
|
|
344
|
+
{ newKey: "basis", newValue: j.stringLiteral(basis) },
|
|
345
|
+
],
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
todo: "could not migrate flex automatically; use grow, shrink, and basis individually",
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (
|
|
354
|
+
node.type === "NumericLiteral" &&
|
|
355
|
+
typeof node.value === "number" &&
|
|
356
|
+
node.value === 0
|
|
357
|
+
) {
|
|
358
|
+
return {
|
|
359
|
+
newEntries: [
|
|
360
|
+
{ newKey: "grow", newValue: j.numericLiteral(0) },
|
|
361
|
+
{ newKey: "shrink", newValue: j.numericLiteral(1) },
|
|
362
|
+
{ newKey: "basis", newValue: j.stringLiteral("0") },
|
|
363
|
+
],
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
todo: "expand is not supported in responsive; use grow, shrink, and basis individually",
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (FLEX_RESPONSIVE_PASSTHROUGH.has(propName)) return null
|
|
372
|
+
|
|
373
|
+
return {
|
|
374
|
+
todo: `not a valid Flex responsive prop; remove or replace manually`,
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const TODO_TEXT =
|
|
379
|
+
"could not migrate mediaQueries to responsive automatically; " +
|
|
380
|
+
"ensure breakpoints are xs/sm/md/lg/xl with static object values"
|
|
381
|
+
|
|
382
|
+
const transform: Transform = attributeTransformFactory({
|
|
383
|
+
condition: hasAttribute("mediaQueries"),
|
|
384
|
+
targetComponent: "StackView",
|
|
385
|
+
targetPackage: "@planningcenter/tapestry-react",
|
|
386
|
+
transform: (element, { j }) => {
|
|
387
|
+
const mediaQueriesAttr = getAttribute({ element, name: "mediaQueries" })
|
|
388
|
+
if (!mediaQueriesAttr) return false
|
|
389
|
+
|
|
390
|
+
const attrValue = mediaQueriesAttr.value
|
|
391
|
+
|
|
392
|
+
if (attrValue?.type !== "JSXExpressionContainer") {
|
|
393
|
+
addCommentToAttribute({ attribute: mediaQueriesAttr, j, text: TODO_TEXT })
|
|
394
|
+
return true
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (attrValue.expression.type !== "ObjectExpression") {
|
|
398
|
+
addCommentToAttribute({ attribute: mediaQueriesAttr, j, text: TODO_TEXT })
|
|
399
|
+
return true
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const outerObj = attrValue.expression
|
|
403
|
+
const mutations: Array<() => void> = []
|
|
404
|
+
const innerTodos: Array<{ key: string; message: string }> = []
|
|
405
|
+
|
|
406
|
+
for (const outerProp of outerObj.properties) {
|
|
407
|
+
if (outerProp.type !== "ObjectProperty") {
|
|
408
|
+
addCommentToAttribute({
|
|
409
|
+
attribute: mediaQueriesAttr,
|
|
410
|
+
j,
|
|
411
|
+
text: TODO_TEXT,
|
|
412
|
+
})
|
|
413
|
+
return true
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const outerKey = outerProp.key
|
|
417
|
+
if (outerProp.computed || outerKey.type !== "Identifier") {
|
|
418
|
+
addCommentToAttribute({
|
|
419
|
+
attribute: mediaQueriesAttr,
|
|
420
|
+
j,
|
|
421
|
+
text: TODO_TEXT,
|
|
422
|
+
})
|
|
423
|
+
return true
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (!VALID_FLEX_BREAKPOINTS.has(outerKey.name)) {
|
|
427
|
+
addCommentToAttribute({
|
|
428
|
+
attribute: mediaQueriesAttr,
|
|
429
|
+
j,
|
|
430
|
+
text: TODO_TEXT,
|
|
431
|
+
})
|
|
432
|
+
return true
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const outerVal = outerProp.value
|
|
436
|
+
if (outerVal.type !== "ObjectExpression") {
|
|
437
|
+
addCommentToAttribute({
|
|
438
|
+
attribute: mediaQueriesAttr,
|
|
439
|
+
j,
|
|
440
|
+
text: TODO_TEXT,
|
|
441
|
+
})
|
|
442
|
+
return true
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
for (const innerProp of outerVal.properties) {
|
|
446
|
+
if (innerProp.type !== "ObjectProperty") {
|
|
447
|
+
innerTodos.push({
|
|
448
|
+
key: "[spread]",
|
|
449
|
+
message:
|
|
450
|
+
"spread elements inside a breakpoint object cannot be migrated automatically; review manually",
|
|
451
|
+
})
|
|
452
|
+
continue
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const innerKey = innerProp.key
|
|
456
|
+
if (innerProp.computed || innerKey.type !== "Identifier") {
|
|
457
|
+
innerTodos.push({
|
|
458
|
+
key: "[computed key]",
|
|
459
|
+
message:
|
|
460
|
+
"computed property keys inside a breakpoint object cannot be migrated automatically; review manually",
|
|
461
|
+
})
|
|
462
|
+
continue
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const result = planInnerProp(innerKey.name, innerProp.value, j)
|
|
466
|
+
if (result === null) continue
|
|
467
|
+
if ("todo" in result) {
|
|
468
|
+
innerTodos.push({ key: innerKey.name, message: result.todo })
|
|
469
|
+
continue
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if ("newEntries" in result) {
|
|
473
|
+
const entries = result.newEntries
|
|
474
|
+
mutations.push(() => {
|
|
475
|
+
const idx = outerVal.properties.indexOf(innerProp)
|
|
476
|
+
outerVal.properties.splice(
|
|
477
|
+
idx,
|
|
478
|
+
1,
|
|
479
|
+
...entries.map(({ newKey, newValue }) =>
|
|
480
|
+
j.objectProperty(j.identifier(newKey), newValue)
|
|
481
|
+
)
|
|
482
|
+
)
|
|
483
|
+
})
|
|
484
|
+
} else {
|
|
485
|
+
mutations.push(() => {
|
|
486
|
+
innerKey.name = result.newKey
|
|
487
|
+
innerProp.value = result.newValue
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
mediaQueriesAttr.name.name = "responsive"
|
|
494
|
+
for (const mutation of mutations) {
|
|
495
|
+
mutation()
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (innerTodos.length > 0) {
|
|
499
|
+
const messageToKeys = new Map<string, string[]>()
|
|
500
|
+
for (const { key, message } of innerTodos) {
|
|
501
|
+
const existing = messageToKeys.get(message)
|
|
502
|
+
if (existing) {
|
|
503
|
+
if (!existing.includes(key)) existing.push(key)
|
|
504
|
+
} else {
|
|
505
|
+
messageToKeys.set(message, [key])
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
const parts = [...messageToKeys].map(
|
|
509
|
+
([message, keys]) => `${keys.join(", ")}: ${message}`
|
|
510
|
+
)
|
|
511
|
+
commentOnAttr(
|
|
512
|
+
mediaQueriesAttr,
|
|
513
|
+
"responsive",
|
|
514
|
+
`could not automatically migrate all breakpoint props — ${parts.join("; ")}`,
|
|
515
|
+
j
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return true
|
|
520
|
+
},
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
export default transform
|