@planningcenter/tapestry-migration-cli 3.5.0 → 3.6.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.
Files changed (34) hide show
  1. package/README.md +37 -0
  2. package/package.json +3 -3
  3. package/src/availableComponents.ts +1 -0
  4. package/src/components/button/transforms/tooltipToWrapper.test.ts +50 -3
  5. package/src/components/button/transforms/tooltipToWrapper.ts +18 -0
  6. package/src/components/flex/index.test.ts +140 -0
  7. package/src/components/flex/index.ts +40 -0
  8. package/src/components/flex/transforms/alignmentToAlign.test.ts +185 -0
  9. package/src/components/flex/transforms/alignmentToAlign.ts +73 -0
  10. package/src/components/flex/transforms/axisToDirection.test.ts +140 -0
  11. package/src/components/flex/transforms/axisToDirection.ts +51 -0
  12. package/src/components/flex/transforms/distributionToJustify.test.ts +214 -0
  13. package/src/components/flex/transforms/distributionToJustify.ts +76 -0
  14. package/src/components/flex/transforms/innerRefToRef.test.ts +100 -0
  15. package/src/components/flex/transforms/innerRefToRef.ts +14 -0
  16. package/src/components/flex/transforms/moveFlexImport.test.ts +202 -0
  17. package/src/components/flex/transforms/moveFlexImport.ts +14 -0
  18. package/src/components/flex/transforms/setDefaultAxis.test.ts +114 -0
  19. package/src/components/flex/transforms/setDefaultAxis.ts +29 -0
  20. package/src/components/flex/transforms/spacingToGap.test.ts +176 -0
  21. package/src/components/flex/transforms/spacingToGap.ts +49 -0
  22. package/src/components/select/index.ts +2 -0
  23. package/src/components/select/transforms/onChangeSignature.test.ts +224 -0
  24. package/src/components/select/transforms/onChangeSignature.ts +172 -0
  25. package/src/components/shared/actions/resolveConstantAttributeValue.test.ts +122 -0
  26. package/src/components/shared/actions/resolveConstantAttributeValue.ts +31 -0
  27. package/src/components/toggle-switch/index.ts +2 -0
  28. package/src/components/toggle-switch/transforms/onClickToOnChange.test.ts +210 -0
  29. package/src/components/toggle-switch/transforms/onClickToOnChange.ts +71 -0
  30. package/src/index.test.ts +79 -0
  31. package/src/index.ts +29 -0
  32. package/src/migrationList.ts +22 -0
  33. package/src/utils/componentLabel.test.ts +61 -0
  34. package/src/utils/componentLabel.ts +15 -0
package/README.md CHANGED
@@ -127,11 +127,48 @@ npx @planningcenter/tapestry-migration-cli run button -p ./src/components --js-t
127
127
  npx @planningcenter/tapestry-migration-cli run button -p ./src/components --js-theme ./theme.js --report-path ./migration-report.md
128
128
  ```
129
129
 
130
+ ### Listing available migrations
131
+
132
+ Use `list --json` to discover available migrations programmatically:
133
+
134
+ ```bash
135
+ npx @planningcenter/tapestry-migration-cli list --json
136
+ ```
137
+
138
+ **Stable JSON contract (stdout, exit 0):**
139
+
140
+ ```json
141
+ {
142
+ "schemaVersion": 1,
143
+ "migrations": [
144
+ { "id": "button", "label": "Button" },
145
+ { "id": "checkbox", "label": "Checkbox" }
146
+ ]
147
+ }
148
+ ```
149
+
150
+ Contract guarantees:
151
+
152
+ - Exit code `0` and **JSON only** on stdout — no progress text or emoji.
153
+ - `schemaVersion` is the integer `1`. Future breaking changes increment this version.
154
+ - Each `id` is the exact value accepted by `run <component-name>`.
155
+ - Each `label` is nonempty display text (Spaced Title Case).
156
+ - `id`s are unique; ordering is the CLI's recommended display order.
157
+ - No `--path` required; no filesystem writes.
158
+ - Diagnostics go to **stderr** with a **nonzero** exit code.
159
+
160
+ Human-readable listing (non-contractual):
161
+
162
+ ```bash
163
+ npx @planningcenter/tapestry-migration-cli list
164
+ ```
165
+
130
166
  ## Available Components
131
167
 
132
168
  - `button` - Migrate Button components
133
169
  - `checkbox` - Migrate Checkbox components
134
170
  - `date-picker` - Migrate DatePicker components
171
+ - `dropdown` - Migrate Dropdown components
135
172
  - `input` - Migrate Input components
136
173
  - `link` - Migrate Link components
137
174
  - `radio` - Migrate Radio components
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/tapestry-migration-cli",
3
- "version": "3.5.0",
3
+ "version": "3.6.0",
4
4
  "description": "CLI tool for Tapestry migrations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@emotion/react": "^11.14.0",
33
- "@planningcenter/tapestry": "^3.5.0",
33
+ "@planningcenter/tapestry": "^3.6.0",
34
34
  "@planningcenter/tapestry-react": "^4.11.5",
35
35
  "@types/jscodeshift": "^17.3.0",
36
36
  "@types/node": "^20.0.0",
@@ -50,5 +50,5 @@
50
50
  "publishConfig": {
51
51
  "access": "public"
52
52
  },
53
- "gitHead": "958f1f269ca37d78e9a9c0ec66c208a7115331dc"
53
+ "gitHead": "0009d96d1c39de5978d2cdea8334ca5120974137"
54
54
  }
@@ -3,6 +3,7 @@ export const AVAILABLE_CLI_COMPONENTS = new Set([
3
3
  "checkbox",
4
4
  "date-picker",
5
5
  "dropdown",
6
+ "flex",
6
7
  "input",
7
8
  "link",
8
9
  "radio",
@@ -84,9 +84,9 @@ export default function Test() {
84
84
 
85
85
  expect(result).toContain('<Tooltip {...{title: "Help"}}')
86
86
  expect(result).toContain("refAsInnerRef={false}")
87
- expect(result).toContain(
88
- '<Button variant="primary" onClick={handler} disabled>Save</Button>'
89
- )
87
+ expect(result).toContain('variant="primary"')
88
+ expect(result).toContain("onClick={handler}")
89
+ expect(result).toContain("disabled>Save</Button>")
90
90
  expect(result).not.toContain("tooltip=")
91
91
  })
92
92
 
@@ -323,6 +323,53 @@ export default function Test({ items }) {
323
323
  expect(result).not.toContain("tooltip=")
324
324
  })
325
325
 
326
+ it("should add a TODO comment when the Button is disabled", () => {
327
+ const input = `
328
+ import { Button } from "@planningcenter/tapestry-react"
329
+
330
+ export default function Test() {
331
+ return <Button tooltip={{ title: "Save" }} disabled>Save</Button>
332
+ }
333
+ `.trim()
334
+
335
+ const result = applyTransform(input)
336
+
337
+ expect(result).toContain("TODO: tapestry-migration (disabled):")
338
+ expect(result).toContain("<Tooltip")
339
+ expect(result).toContain("disabled>Save</Button>")
340
+ expect(result).not.toContain("tooltip=")
341
+ })
342
+
343
+ it("should add a TODO comment when disabled has an expression value", () => {
344
+ const input = `
345
+ import { Button } from "@planningcenter/tapestry-react"
346
+
347
+ export default function Test({ isDisabled }) {
348
+ return <Button tooltip={{ title: "Save" }} disabled={isDisabled}>Save</Button>
349
+ }
350
+ `.trim()
351
+
352
+ const result = applyTransform(input)
353
+
354
+ expect(result).toContain("TODO: tapestry-migration (disabled):")
355
+ expect(result).toContain("disabled={isDisabled}")
356
+ })
357
+
358
+ it("should not add a disabled TODO comment when the Button is not disabled", () => {
359
+ const input = `
360
+ import { Button } from "@planningcenter/tapestry-react"
361
+
362
+ export default function Test() {
363
+ return <Button tooltip={{ title: "Save" }}>Save</Button>
364
+ }
365
+ `.trim()
366
+
367
+ const result = applyTransform(input)
368
+
369
+ expect(result).toContain("<Tooltip")
370
+ expect(result).not.toContain("TODO: tapestry-migration (disabled):")
371
+ })
372
+
326
373
  it("should handle empty tooltip attribute", () => {
327
374
  const input = `
328
375
  import { Button } from "@planningcenter/tapestry-react"
@@ -1,5 +1,7 @@
1
1
  import { addAttribute } from "../../shared/actions/addAttribute"
2
+ import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
2
3
  import { createWrapper } from "../../shared/actions/createWrapper"
4
+ import { getAttribute } from "../../shared/actions/getAttribute"
3
5
  import { getAttributeValueAsProps } from "../../shared/actions/getAttributeValueAsProps"
4
6
  import { removeAttribute } from "../../shared/actions/removeAttribute"
5
7
  import { hasAttribute } from "../../shared/conditions/hasAttribute"
@@ -19,6 +21,11 @@ const transform = attributeTransformFactory({
19
21
 
20
22
  removeAttribute("tooltip", { element, j, source })
21
23
 
24
+ // Capture the original Button's `disabled` attribute before `createWrapper`
25
+ // replaces `element` in the AST, so the comment below clearly attaches to
26
+ // the original Button attribute node.
27
+ const disabledAttribute = getAttribute({ element, name: "disabled" })
28
+
22
29
  const wrapperElement = createWrapper({
23
30
  conflictAlias: "TRTooltip",
24
31
  element,
@@ -36,6 +43,17 @@ const transform = attributeTransformFactory({
36
43
  value: false,
37
44
  })
38
45
 
46
+ if (disabledAttribute) {
47
+ addCommentToAttribute({
48
+ attribute: disabledAttribute,
49
+ j,
50
+ text:
51
+ "This Button is disabled. Disabled buttons don't emit pointer or focus events, " +
52
+ "so the wrapping Tooltip will not appear. Keep the button interactive (e.g. handle " +
53
+ "the disabled state without the native `disabled` attribute) so the Tooltip can trigger.",
54
+ })
55
+ }
56
+
39
57
  return true
40
58
  },
41
59
  })
@@ -0,0 +1,140 @@
1
+ import jscodeshift from "jscodeshift"
2
+ import { describe, expect, it } from "vitest"
3
+
4
+ import transform from "./index"
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("flex orchestrator", () => {
18
+ it("applies setDefaultAxis through the chain", () => {
19
+ const input = `
20
+ import { StackView } from "@planningcenter/tapestry-react"
21
+
22
+ export default function Test() {
23
+ return <StackView>Test</StackView>
24
+ }
25
+ `.trim()
26
+
27
+ const result = applyTransform(input)
28
+ expect(result).toContain('<Flex direction="column">Test</Flex>')
29
+ expect(result).not.toContain("axis")
30
+ })
31
+
32
+ it("applies axisToDirection through the chain", () => {
33
+ const input = `
34
+ import { StackView } from "@planningcenter/tapestry-react"
35
+
36
+ export default function Test() {
37
+ return <StackView axis="horizontal">Test</StackView>
38
+ }
39
+ `.trim()
40
+
41
+ const result = applyTransform(input)
42
+ expect(result).toContain('<Flex direction="row">Test</Flex>')
43
+ expect(result).not.toContain("axis")
44
+ })
45
+
46
+ it("applies alignmentToAlign through the chain", () => {
47
+ const input = `
48
+ import { StackView } from "@planningcenter/tapestry-react"
49
+
50
+ export default function Test() {
51
+ return <StackView axis="horizontal" alignment="center">Test</StackView>
52
+ }
53
+ `.trim()
54
+
55
+ const result = applyTransform(input)
56
+ expect(result).toContain('align="center"')
57
+ expect(result).not.toContain("alignment")
58
+ })
59
+
60
+ it("applies distributionToJustify through the chain", () => {
61
+ const input = `
62
+ import { StackView } from "@planningcenter/tapestry-react"
63
+
64
+ export default function Test() {
65
+ return <StackView axis="horizontal" distribution="center">Test</StackView>
66
+ }
67
+ `.trim()
68
+
69
+ const result = applyTransform(input)
70
+ expect(result).toContain('justify="center"')
71
+ expect(result).not.toContain("distribution")
72
+ })
73
+
74
+ it("applies spacingToGap through the chain", () => {
75
+ const input = `
76
+ import { StackView } from "@planningcenter/tapestry-react"
77
+
78
+ export default function Test() {
79
+ return <StackView axis="horizontal" spacing={2}>Test</StackView>
80
+ }
81
+ `.trim()
82
+
83
+ const result = applyTransform(input)
84
+ expect(result).toContain("gap={2}")
85
+ expect(result).not.toContain("spacing")
86
+ })
87
+
88
+ it("applies innerRefToRef through the chain", () => {
89
+ const input = `
90
+ import { StackView } from "@planningcenter/tapestry-react"
91
+
92
+ export default function Test() {
93
+ return <StackView axis="horizontal" innerRef={myRef}>Test</StackView>
94
+ }
95
+ `.trim()
96
+
97
+ const result = applyTransform(input)
98
+ expect(result).toContain("ref={myRef}")
99
+ expect(result).not.toContain("innerRef")
100
+ })
101
+
102
+ it("applies moveFlexImport through the chain", () => {
103
+ const input = `
104
+ import { StackView } from "@planningcenter/tapestry-react"
105
+
106
+ export default function Test() {
107
+ return <StackView axis="horizontal">Test</StackView>
108
+ }
109
+ `.trim()
110
+
111
+ const result = applyTransform(input)
112
+ expect(result).toContain('import { Flex } from "@planningcenter/tapestry"')
113
+ expect(result).not.toContain("StackView")
114
+ expect(result).not.toContain('from "@planningcenter/tapestry-react"')
115
+ })
116
+
117
+ it("returns null when there is nothing to transform", () => {
118
+ const input = `
119
+ import { Flex } from "@planningcenter/tapestry"
120
+
121
+ export default function Test() {
122
+ return <Flex>Test</Flex>
123
+ }
124
+ `.trim()
125
+
126
+ expect(applyTransform(input)).toBe(null)
127
+ })
128
+
129
+ it("returns null for an empty file", () => {
130
+ expect(applyTransform("")).toBe(null)
131
+ })
132
+
133
+ it("does not throw for arbitrary source", () => {
134
+ expect(() =>
135
+ applyTransform(
136
+ `import React from "react"\nexport default function Noop() { return <div /> }`
137
+ )
138
+ ).not.toThrow()
139
+ })
140
+ })
@@ -0,0 +1,40 @@
1
+ import { Transform } from "jscodeshift"
2
+
3
+ import alignmentToAlign from "./transforms/alignmentToAlign"
4
+ import axisToDirection from "./transforms/axisToDirection"
5
+ import distributionToJustify from "./transforms/distributionToJustify"
6
+ import innerRefToRef from "./transforms/innerRefToRef"
7
+ import moveFlexImport from "./transforms/moveFlexImport"
8
+ import setDefaultAxis from "./transforms/setDefaultAxis"
9
+ import spacingToGap from "./transforms/spacingToGap"
10
+
11
+ const transform: Transform = (fileInfo, api, options) => {
12
+ let currentSource = fileInfo.source
13
+ let hasAnyChanges = false
14
+
15
+ const transforms = [
16
+ setDefaultAxis,
17
+ axisToDirection,
18
+ alignmentToAlign,
19
+ distributionToJustify,
20
+ spacingToGap,
21
+ innerRefToRef,
22
+ moveFlexImport,
23
+ ]
24
+
25
+ for (const individualTransform of transforms) {
26
+ const result = individualTransform(
27
+ { ...fileInfo, source: currentSource },
28
+ api,
29
+ options
30
+ )
31
+ if (result && result !== currentSource) {
32
+ currentSource = result as string
33
+ hasAnyChanges = true
34
+ }
35
+ }
36
+
37
+ return hasAnyChanges ? currentSource : null
38
+ }
39
+
40
+ export default transform
@@ -0,0 +1,185 @@
1
+ import jscodeshift from "jscodeshift"
2
+ import { describe, expect, it } from "vitest"
3
+
4
+ import transform from "./alignmentToAlign"
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("alignmentToAlign transform", () => {
18
+ it("renames alignment to align", () => {
19
+ const input = `
20
+ import { StackView } from "@planningcenter/tapestry-react"
21
+
22
+ export default function Test() {
23
+ return <StackView alignment="center">Test</StackView>
24
+ }
25
+ `.trim()
26
+
27
+ const result = applyTransform(input)
28
+ expect(result).toContain('align="center"')
29
+ expect(result).not.toContain("alignment")
30
+ })
31
+
32
+ it("resolves StackView.CENTER constant to align", () => {
33
+ const input = `
34
+ import { StackView } from "@planningcenter/tapestry-react"
35
+
36
+ export default function Test() {
37
+ return <StackView alignment={StackView.CENTER}>Test</StackView>
38
+ }
39
+ `.trim()
40
+
41
+ const result = applyTransform(input)
42
+ expect(result).toContain('align="center"')
43
+ expect(result).not.toContain("alignment")
44
+ expect(result).not.toContain("StackView.CENTER")
45
+ })
46
+
47
+ it("resolves StackView.START constant to align", () => {
48
+ const input = `
49
+ import { StackView } from "@planningcenter/tapestry-react"
50
+
51
+ export default function Test() {
52
+ return <StackView alignment={StackView.START}>Test</StackView>
53
+ }
54
+ `.trim()
55
+
56
+ const result = applyTransform(input)
57
+ expect(result).toContain('align="start"')
58
+ expect(result).not.toContain("StackView.START")
59
+ })
60
+
61
+ it("resolves StackView.END constant to align", () => {
62
+ const input = `
63
+ import { StackView } from "@planningcenter/tapestry-react"
64
+
65
+ export default function Test() {
66
+ return <StackView alignment={StackView.END}>Test</StackView>
67
+ }
68
+ `.trim()
69
+
70
+ const result = applyTransform(input)
71
+ expect(result).toContain('align="end"')
72
+ expect(result).not.toContain("StackView.END")
73
+ })
74
+
75
+ it("resolves StackView.STRETCH constant to align", () => {
76
+ const input = `
77
+ import { StackView } from "@planningcenter/tapestry-react"
78
+
79
+ export default function Test() {
80
+ return <StackView alignment={StackView.STRETCH}>Test</StackView>
81
+ }
82
+ `.trim()
83
+
84
+ const result = applyTransform(input)
85
+ expect(result).toContain('align="stretch"')
86
+ expect(result).not.toContain("StackView.STRETCH")
87
+ })
88
+
89
+ it("resolves StackView.BASELINE constant to align", () => {
90
+ const input = `
91
+ import { StackView } from "@planningcenter/tapestry-react"
92
+
93
+ export default function Test() {
94
+ return <StackView alignment={StackView.BASELINE}>Test</StackView>
95
+ }
96
+ `.trim()
97
+
98
+ const result = applyTransform(input)
99
+ expect(result).toContain('align="baseline"')
100
+ expect(result).not.toContain("StackView.BASELINE")
101
+ })
102
+
103
+ it("flags alignment={StackView.FILL} with a TODO and leaves it in place", () => {
104
+ const input = `
105
+ import { StackView } from "@planningcenter/tapestry-react"
106
+
107
+ export default function Test() {
108
+ return <StackView alignment={StackView.FILL}>Test</StackView>
109
+ }
110
+ `.trim()
111
+
112
+ const result = applyTransform(input)
113
+ expect(result).toContain("alignment={StackView.FILL}")
114
+ expect(result).toContain("TODO: tapestry-migration (alignment)")
115
+ expect(result).not.toContain('align="')
116
+ })
117
+
118
+ it('flags alignment="fill" with a TODO and leaves it in place', () => {
119
+ const input = `
120
+ import { StackView } from "@planningcenter/tapestry-react"
121
+
122
+ export default function Test() {
123
+ return <StackView alignment="fill">Test</StackView>
124
+ }
125
+ `.trim()
126
+
127
+ const result = applyTransform(input)
128
+ expect(result).toContain('alignment="fill"')
129
+ expect(result).toContain("TODO: tapestry-migration (alignment)")
130
+ expect(result).not.toContain('align="')
131
+ })
132
+
133
+ it("flags a dynamic alignment value with a TODO", () => {
134
+ const input = `
135
+ import { StackView } from "@planningcenter/tapestry-react"
136
+
137
+ export default function Test({ alignProp }) {
138
+ return <StackView alignment={alignProp}>Test</StackView>
139
+ }
140
+ `.trim()
141
+
142
+ const result = applyTransform(input)
143
+ expect(result).toContain("alignment={alignProp}")
144
+ expect(result).toContain("TODO: tapestry-migration (alignment)")
145
+ expect(result).not.toContain('align="')
146
+ })
147
+
148
+ it("respects an aliased StackView import", () => {
149
+ const input = `
150
+ import { StackView as Stack } from "@planningcenter/tapestry-react"
151
+
152
+ export default function Test() {
153
+ return <Stack alignment="center">Test</Stack>
154
+ }
155
+ `.trim()
156
+
157
+ const result = applyTransform(input)
158
+ expect(result).toContain('align="center"')
159
+ expect(result).not.toContain("alignment")
160
+ })
161
+
162
+ it("returns null when there is no alignment to migrate", () => {
163
+ const input = `
164
+ import { StackView } from "@planningcenter/tapestry-react"
165
+
166
+ export default function Test() {
167
+ return <StackView>Test</StackView>
168
+ }
169
+ `.trim()
170
+
171
+ expect(applyTransform(input)).toBe(null)
172
+ })
173
+
174
+ it("returns null when StackView is not imported from tapestry-react", () => {
175
+ const input = `
176
+ import { StackView } from "some-other-package"
177
+
178
+ export default function Test() {
179
+ return <StackView alignment="center">Test</StackView>
180
+ }
181
+ `.trim()
182
+
183
+ expect(applyTransform(input)).toBe(null)
184
+ })
185
+ })
@@ -0,0 +1,73 @@
1
+ import { Transform } from "jscodeshift"
2
+
3
+ import { addComment } from "../../shared/actions/addComment"
4
+ import { getAttribute } from "../../shared/actions/getAttribute"
5
+ import { resolveConstantAttributeValue } from "../../shared/actions/resolveConstantAttributeValue"
6
+ import { hasAttribute } from "../../shared/conditions/hasAttribute"
7
+ import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
8
+
9
+ const VALID_ALIGN_VALUES = new Set([
10
+ "start",
11
+ "center",
12
+ "end",
13
+ "stretch",
14
+ "baseline",
15
+ ])
16
+
17
+ const CONSTANT_ALIGN_MAP: Record<string, string> = {
18
+ BASELINE: "baseline",
19
+ CENTER: "center",
20
+ END: "end",
21
+ FILL: "fill",
22
+ START: "start",
23
+ STRETCH: "stretch",
24
+ }
25
+
26
+ const transform: Transform = attributeTransformFactory({
27
+ condition: hasAttribute("alignment"),
28
+ targetComponent: "StackView",
29
+ targetPackage: "@planningcenter/tapestry-react",
30
+ transform: (element, { j, source }) => {
31
+ const alignmentAttribute = getAttribute({ element, name: "alignment" })
32
+ if (!alignmentAttribute) return false
33
+
34
+ const resolved = resolveConstantAttributeValue({
35
+ attribute: alignmentAttribute,
36
+ constantMap: CONSTANT_ALIGN_MAP,
37
+ })
38
+ const alignValue =
39
+ resolved !== null &&
40
+ (VALID_ALIGN_VALUES.has(resolved) || resolved === "fill")
41
+ ? resolved
42
+ : null
43
+
44
+ if (alignValue === null) {
45
+ addComment({
46
+ element,
47
+ j,
48
+ scope: "alignment",
49
+ source,
50
+ text: "could not migrate alignment to align automatically; convert this value to start, center, end, stretch, or baseline manually",
51
+ })
52
+ return true
53
+ }
54
+
55
+ if (alignValue === "fill") {
56
+ addComment({
57
+ element,
58
+ j,
59
+ scope: "alignment",
60
+ source,
61
+ text: 'alignment="fill" has no Flex equivalent; remove this prop and use expand on the child element instead',
62
+ })
63
+ return true
64
+ }
65
+
66
+ alignmentAttribute.name.name = "align"
67
+ alignmentAttribute.value = j.stringLiteral(alignValue)
68
+
69
+ return true
70
+ },
71
+ })
72
+
73
+ export default transform