@planningcenter/tapestry-migration-cli 3.2.3-rc.5 → 3.2.3-rc.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/tapestry-migration-cli",
3
- "version": "3.2.3-rc.5",
3
+ "version": "3.2.3-rc.6",
4
4
  "description": "CLI tool for Tapestry migrations",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@emotion/react": "^11.14.0",
35
- "@planningcenter/tapestry": "^3.2.3-rc.5",
35
+ "@planningcenter/tapestry": "^3.2.3-rc.6",
36
36
  "@planningcenter/tapestry-react": "^4.11.5",
37
37
  "@types/jscodeshift": "^17.3.0",
38
38
  "@types/node": "^20.0.0",
@@ -52,5 +52,5 @@
52
52
  "publishConfig": {
53
53
  "access": "public"
54
54
  },
55
- "gitHead": "709a2ca95766701dd9466ee22c1e8a96fa3da9df"
55
+ "gitHead": "66ef71c22b569382d2047f8cce603051322a451d"
56
56
  }
@@ -1,8 +1,5 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { BUTTON_LINK_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
4
  const BUTTON_SPECIFIC_PROPS = [
8
5
  "disabled",
@@ -19,35 +16,10 @@ const SUPPORTED_PROPS = [
19
16
  ...BUTTON_SPECIFIC_PROPS,
20
17
  ]
21
18
 
22
- const transform: Transform = attributeTransformFactory({
19
+ const transform = unsupportedPropsFactory({
20
+ supportedProps: SUPPORTED_PROPS,
23
21
  targetComponent: "Button",
24
22
  targetPackage: "@planningcenter/tapestry-react",
25
- transform: (element, { j }) => {
26
- const UNSUPPORTED_PROPS = (element.openingElement.attributes || [])
27
- .filter(
28
- (attr) =>
29
- attr.type === "JSXAttribute" &&
30
- !SUPPORTED_PROPS.includes(attr.name.name as string) &&
31
- !(attr.name.name as string).startsWith("aria-") &&
32
- !(attr.name.name as string).startsWith("data-")
33
- )
34
- .map((attr) => (attr as JSXAttribute).name.name as string)
35
-
36
- return addCommentToUnsupportedProps({
37
- element,
38
- j,
39
- messageSuffix: (prop) => {
40
- if (prop === "mediaQueries") {
41
- return "\n * It is recommended to use CSS media queries in a class that you apply to the component.\n"
42
- }
43
- if (prop === "hover" || prop === "focus" || prop === "active") {
44
- return "\n * State-based styles (hover, focus, active) should be handled with CSS class selectors.\n"
45
- }
46
- return ""
47
- },
48
- props: UNSUPPORTED_PROPS,
49
- })
50
- },
51
23
  })
52
24
 
53
25
  export default transform
@@ -1,8 +1,5 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { CHECKBOX_RADIO_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
4
  const CHECKBOX_SPECIFIC_PROPS = ["indeterminate"]
8
5
 
@@ -11,32 +8,10 @@ const SUPPORTED_PROPS = [
11
8
  ...CHECKBOX_SPECIFIC_PROPS,
12
9
  ]
13
10
 
14
- const transform: Transform = attributeTransformFactory({
11
+ const transform = unsupportedPropsFactory({
12
+ supportedProps: SUPPORTED_PROPS,
15
13
  targetComponent: "Checkbox",
16
14
  targetPackage: "@planningcenter/tapestry-react",
17
- transform: (element, { j }) => {
18
- const UNSUPPORTED_PROPS = (element.openingElement.attributes || [])
19
- .filter(
20
- (attr) =>
21
- attr.type === "JSXAttribute" &&
22
- !SUPPORTED_PROPS.includes(attr.name.name as string) &&
23
- !(attr.name.name as string).startsWith("aria-") &&
24
- !(attr.name.name as string).startsWith("data-")
25
- )
26
- .map((attr) => (attr as JSXAttribute).name.name as string)
27
-
28
- return addCommentToUnsupportedProps({
29
- element,
30
- j,
31
- messageSuffix: (prop) => {
32
- if (prop === "css") {
33
- return "\n * CSS prop is not supported. Use className or style prop instead.\n"
34
- }
35
- return ""
36
- },
37
- props: UNSUPPORTED_PROPS,
38
- })
39
- },
40
15
  })
41
16
 
42
17
  export default transform
@@ -1,37 +1,10 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { DATE_PICKER_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
- const transform: Transform = attributeTransformFactory({
4
+ const transform = unsupportedPropsFactory({
5
+ supportedProps: DATE_PICKER_SUPPORTED_PROPS,
8
6
  targetComponent: "DateField",
9
7
  targetPackage: "@planningcenter/tapestry-react",
10
- transform: (element, { j }) => {
11
- const attrs = element.openingElement.attributes || []
12
-
13
- const UNSUPPORTED_PROPS = attrs
14
- .filter(
15
- (attr) =>
16
- attr.type === "JSXAttribute" &&
17
- !DATE_PICKER_SUPPORTED_PROPS.includes(attr.name.name as string) &&
18
- !(attr.name.name as string).startsWith("aria-") &&
19
- !(attr.name.name as string).startsWith("data-")
20
- )
21
- .map((attr) => (attr as JSXAttribute).name.name as string)
22
-
23
- return addCommentToUnsupportedProps({
24
- element,
25
- j,
26
- messageSuffix: (prop) => {
27
- if (prop === "css") {
28
- return "\n * CSS prop is not supported. Use className or style prop instead.\n"
29
- }
30
- return ""
31
- },
32
- props: UNSUPPORTED_PROPS,
33
- })
34
- },
35
8
  })
36
9
 
37
10
  export default transform
@@ -6,6 +6,7 @@ import { getAttributeValue } from "../../shared/actions/getAttributeValue"
6
6
  import {
7
7
  ACCEPTED_INPUT_TYPES,
8
8
  INPUT_SUPPORTED_PROPS,
9
+ isAriaOrDataAttribute,
9
10
  TYPE_SPECIFIC_PROPS,
10
11
  } from "../../shared/helpers/unsupportedPropsHelpers"
11
12
  import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
@@ -55,8 +56,7 @@ const transform: Transform = attributeTransformFactory({
55
56
  attr.type === "JSXAttribute" &&
56
57
  !INPUT_SUPPORTED_PROPS.includes(attr.name.name as string) &&
57
58
  !typeAllowedProps.includes(attr.name.name as string) &&
58
- !(attr.name.name as string).startsWith("aria-") &&
59
- !(attr.name.name as string).startsWith("data-")
59
+ !isAriaOrDataAttribute(attr.name.name as string)
60
60
  )
61
61
  .map((attr) => (attr as JSXAttribute).name.name as string)
62
62
 
@@ -28,7 +28,9 @@ export default function Test() {
28
28
  const result = applyTransform(input)
29
29
  expect(result).toContain("TODO: tapestry-migration (css)")
30
30
  expect(result).toContain("'css' is not supported")
31
- expect(result).toContain("Use 'className' prop with CSS classes")
31
+ expect(result).toContain(
32
+ "CSS prop is not supported. Use className or style prop instead."
33
+ )
32
34
  expect(result).toContain('css={{ color: "red" }}')
33
35
  })
34
36
 
@@ -1,8 +1,5 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { BUTTON_LINK_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
4
  // Note: 'target' and 'rel' are NOT included because they are handled by targetBlankToExternal transform
8
5
  const LINK_SPECIFIC_PROPS = [
@@ -18,41 +15,16 @@ const LINK_SPECIFIC_PROPS = [
18
15
 
19
16
  const SUPPORTED_PROPS = [...BUTTON_LINK_SUPPORTED_PROPS, ...LINK_SPECIFIC_PROPS]
20
17
 
21
- const transform: Transform = attributeTransformFactory({
18
+ const transform = unsupportedPropsFactory({
19
+ commentNotes: (prop) => {
20
+ if (prop === "disabled") {
21
+ return "\n * Links do not support the disabled prop. Consider using a button, hiding the link, or using CSS to style it as disabled.\n"
22
+ }
23
+ return ""
24
+ },
25
+ supportedProps: SUPPORTED_PROPS,
22
26
  targetComponent: "Link",
23
27
  targetPackage: "@planningcenter/tapestry-react",
24
- transform: (element, { j }) => {
25
- const UNSUPPORTED_PROPS = (element.openingElement.attributes || [])
26
- .filter(
27
- (attr) =>
28
- attr.type === "JSXAttribute" &&
29
- !SUPPORTED_PROPS.includes(attr.name.name as string) &&
30
- !(attr.name.name as string).startsWith("aria-") &&
31
- !(attr.name.name as string).startsWith("data-")
32
- )
33
- .map((attr) => (attr as JSXAttribute).name.name as string)
34
-
35
- return addCommentToUnsupportedProps({
36
- element,
37
- j,
38
- messageSuffix: (prop) => {
39
- if (prop === "css") {
40
- return "\n * Use 'className' prop with CSS classes instead of the css prop.\n"
41
- }
42
- if (prop === "disabled") {
43
- return "\n * Links do not support the disabled prop. Consider using a button, hiding the link, or using CSS to style it as disabled.\n"
44
- }
45
- if (prop === "mediaQueries") {
46
- return "\n * It is recommended to use CSS media queries in a class that you apply to the component.\n"
47
- }
48
- if (prop === "hover" || prop === "focus" || prop === "active") {
49
- return "\n * State-based styles (hover, focus, active) should be handled with CSS class selectors.\n"
50
- }
51
- return ""
52
- },
53
- props: UNSUPPORTED_PROPS,
54
- })
55
- },
56
28
  })
57
29
 
58
30
  export default transform
@@ -1,35 +1,10 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { CHECKBOX_RADIO_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
- const transform: Transform = attributeTransformFactory({
4
+ const transform = unsupportedPropsFactory({
5
+ supportedProps: CHECKBOX_RADIO_SUPPORTED_PROPS,
8
6
  targetComponent: "Radio",
9
7
  targetPackage: "@planningcenter/tapestry-react",
10
- transform: (element, { j }) => {
11
- const UNSUPPORTED_PROPS = (element.openingElement.attributes || [])
12
- .filter(
13
- (attr) =>
14
- attr.type === "JSXAttribute" &&
15
- !CHECKBOX_RADIO_SUPPORTED_PROPS.includes(attr.name.name as string) &&
16
- !(attr.name.name as string).startsWith("aria-") &&
17
- !(attr.name.name as string).startsWith("data-")
18
- )
19
- .map((attr) => (attr as JSXAttribute).name.name as string)
20
-
21
- return addCommentToUnsupportedProps({
22
- element,
23
- j,
24
- messageSuffix: (prop) => {
25
- if (prop === "css") {
26
- return "\n * CSS prop is not supported. Use className or style prop instead.\n"
27
- }
28
- return ""
29
- },
30
- props: UNSUPPORTED_PROPS,
31
- })
32
- },
33
8
  })
34
9
 
35
10
  export default transform
@@ -1,44 +1,18 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { SELECT_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
  import { transformableSelect } from "../transformableSelect"
7
4
 
8
- const transform: Transform = attributeTransformFactory({
5
+ const transform = unsupportedPropsFactory({
6
+ commentNotes: (prop) => {
7
+ if (prop === "renderValue") {
8
+ return "\n * renderValue is not supported. The select displays the selected option label.\n"
9
+ }
10
+ return ""
11
+ },
9
12
  condition: transformableSelect,
13
+ supportedProps: SELECT_SUPPORTED_PROPS,
10
14
  targetComponent: "Select",
11
15
  targetPackage: "@planningcenter/tapestry-react",
12
- transform: (element, { j }) => {
13
- const UNSUPPORTED_PROPS = (element.openingElement.attributes || [])
14
- .filter(
15
- (attr) =>
16
- attr.type === "JSXAttribute" &&
17
- attr.name.type === "JSXIdentifier" &&
18
- !SELECT_SUPPORTED_PROPS.includes(attr.name.name as string) &&
19
- !(attr.name.name as string).startsWith("aria-") &&
20
- !(attr.name.name as string).startsWith("data-")
21
- )
22
- .map((attr) => (attr as JSXAttribute).name.name as string)
23
-
24
- return addCommentToUnsupportedProps({
25
- element,
26
- j,
27
- messageSuffix: (prop) => {
28
- if (prop === "css") {
29
- return "\n * CSS prop is not supported. Use className or style prop instead.\n"
30
- }
31
- if (prop === "tooltip") {
32
- return "\n * Wrapping a select in a tooltip is an unsupported anti-pattern.\n"
33
- }
34
- if (prop === "renderValue") {
35
- return "\n * renderValue is not supported. The select displays the selected option label.\n"
36
- }
37
- return ""
38
- },
39
- props: UNSUPPORTED_PROPS,
40
- })
41
- },
42
16
  })
43
17
 
44
18
  export default transform
@@ -4,6 +4,10 @@ export const STYLE_PROP_NAMES_WITHOUT_CSS = stylePropNames.filter(
4
4
  (prop: string) => prop !== "css"
5
5
  )
6
6
 
7
+ export const isAriaOrDataAttribute = (name: unknown) =>
8
+ typeof name === "string" &&
9
+ (name.startsWith("aria-") || name.startsWith("data-"))
10
+
7
11
  export const COMMON_PROPS = [
8
12
  "className",
9
13
  "id",
@@ -0,0 +1,162 @@
1
+ import jscodeshift from "jscodeshift"
2
+ import { describe, expect, it } from "vitest"
3
+
4
+ import { hasAttribute } from "../conditions/hasAttribute"
5
+ import { unsupportedPropsFactory } from "./unsupportedPropsFactory"
6
+
7
+ const j = jscodeshift.withParser("tsx")
8
+
9
+ function applyTransform(transform: ReturnType<typeof unsupportedPropsFactory>) {
10
+ return (source: string): string | null => {
11
+ const fileInfo = { path: "test.tsx", source }
12
+ const api = { j, jscodeshift: j, report: () => {}, stats: () => {} }
13
+ return transform(fileInfo, api, {}) as string | null
14
+ }
15
+ }
16
+
17
+ describe("unsupportedPropsFactory", () => {
18
+ it("adds a TODO comment for unsupported props", () => {
19
+ const transform = unsupportedPropsFactory({
20
+ supportedProps: ["label"],
21
+ targetComponent: "Button",
22
+ targetPackage: "@planningcenter/tapestry-react",
23
+ })
24
+ const source = `import { Button } from "@planningcenter/tapestry-react"
25
+ <Button customProp="value" label="Save" />`
26
+
27
+ const result = applyTransform(transform)(source)
28
+
29
+ expect(result).toContain(
30
+ "/* TODO: tapestry-migration (customProp): 'customProp' is not supported, please migrate as needed."
31
+ )
32
+ expect(result).toContain('customProp="value"')
33
+ })
34
+
35
+ it("does not flag supported props", () => {
36
+ const transform = unsupportedPropsFactory({
37
+ supportedProps: ["label", "kind"],
38
+ targetComponent: "Button",
39
+ targetPackage: "@planningcenter/tapestry-react",
40
+ })
41
+ const source = `import { Button } from "@planningcenter/tapestry-react"
42
+ <Button label="Save" kind="primary" />`
43
+
44
+ const result = applyTransform(transform)(source)
45
+
46
+ expect(result).toBeNull()
47
+ })
48
+
49
+ it("always allows aria-* and data-* attributes", () => {
50
+ const transform = unsupportedPropsFactory({
51
+ supportedProps: ["label"],
52
+ targetComponent: "Button",
53
+ targetPackage: "@planningcenter/tapestry-react",
54
+ })
55
+ const source = `import { Button } from "@planningcenter/tapestry-react"
56
+ <Button aria-label="Save" data-testid="btn" label="Save" />`
57
+
58
+ const result = applyTransform(transform)(source)
59
+
60
+ expect(result).toBeNull()
61
+ })
62
+
63
+ it("includes a default note for the css prop without explicit commentNotes", () => {
64
+ const transform = unsupportedPropsFactory({
65
+ supportedProps: ["label"],
66
+ targetComponent: "Button",
67
+ targetPackage: "@planningcenter/tapestry-react",
68
+ })
69
+ const source = `import { Button } from "@planningcenter/tapestry-react"
70
+ <Button css={{ color: 'red' }} label="Save" />`
71
+
72
+ const result = applyTransform(transform)(source)
73
+
74
+ expect(result).toContain("/* TODO: tapestry-migration (css):")
75
+ expect(result).toContain(
76
+ "CSS prop is not supported. Use className or style prop instead."
77
+ )
78
+ })
79
+
80
+ it("falls back to the default css note when commentNotes returns empty", () => {
81
+ const transform = unsupportedPropsFactory({
82
+ commentNotes: (prop) =>
83
+ prop === "tooltip" ? "\n * Tooltip note.\n" : "",
84
+ supportedProps: ["label"],
85
+ targetComponent: "Button",
86
+ targetPackage: "@planningcenter/tapestry-react",
87
+ })
88
+ const source = `import { Button } from "@planningcenter/tapestry-react"
89
+ <Button css={{ color: 'red' }} label="Save" />`
90
+
91
+ const result = applyTransform(transform)(source)
92
+
93
+ expect(result).toContain(
94
+ "CSS prop is not supported. Use className or style prop instead."
95
+ )
96
+ })
97
+
98
+ it("lets commentNotes override the default css note", () => {
99
+ const transform = unsupportedPropsFactory({
100
+ commentNotes: (prop) => (prop === "css" ? "\n * Custom css note.\n" : ""),
101
+ supportedProps: ["label"],
102
+ targetComponent: "Button",
103
+ targetPackage: "@planningcenter/tapestry-react",
104
+ })
105
+ const source = `import { Button } from "@planningcenter/tapestry-react"
106
+ <Button css={{ color: 'red' }} label="Save" />`
107
+
108
+ const result = applyTransform(transform)(source)
109
+
110
+ expect(result).toContain("Custom css note.")
111
+ expect(result).not.toContain(
112
+ "CSS prop is not supported. Use className or style prop instead."
113
+ )
114
+ })
115
+
116
+ it("appends commentNotes output to the comment text", () => {
117
+ const transform = unsupportedPropsFactory({
118
+ commentNotes: (prop) =>
119
+ prop === "css" ? "\n * Use className instead.\n" : "",
120
+ supportedProps: ["label"],
121
+ targetComponent: "Button",
122
+ targetPackage: "@planningcenter/tapestry-react",
123
+ })
124
+ const source = `import { Button } from "@planningcenter/tapestry-react"
125
+ <Button css={{ color: 'red' }} label="Save" />`
126
+
127
+ const result = applyTransform(transform)(source)
128
+
129
+ expect(result).toContain("/* TODO: tapestry-migration (css):")
130
+ expect(result).toContain("Use className instead.")
131
+ })
132
+
133
+ it("respects an optional condition", () => {
134
+ const transform = unsupportedPropsFactory({
135
+ condition: hasAttribute("required"),
136
+ supportedProps: ["label"],
137
+ targetComponent: "Button",
138
+ targetPackage: "@planningcenter/tapestry-react",
139
+ })
140
+ const sourceWithoutRequired = `import { Button } from "@planningcenter/tapestry-react"
141
+ <Button customProp="value" label="Save" />`
142
+ const sourceWithRequired = `import { Button } from "@planningcenter/tapestry-react"
143
+ <Button customProp="value" required label="Save" />`
144
+
145
+ expect(applyTransform(transform)(sourceWithoutRequired)).toBeNull()
146
+ expect(applyTransform(transform)(sourceWithRequired)).toContain(
147
+ "/* TODO: tapestry-migration (customProp):"
148
+ )
149
+ })
150
+
151
+ it("returns null when target component is not imported", () => {
152
+ const transform = unsupportedPropsFactory({
153
+ supportedProps: ["label"],
154
+ targetComponent: "Button",
155
+ targetPackage: "@planningcenter/tapestry-react",
156
+ })
157
+ const source = `import { Link } from "@planningcenter/tapestry-react"
158
+ <Link customProp="value">Click</Link>`
159
+
160
+ expect(applyTransform(transform)(source)).toBeNull()
161
+ })
162
+ })
@@ -0,0 +1,60 @@
1
+ import { JSXAttribute, Transform } from "jscodeshift"
2
+
3
+ import { addCommentToUnsupportedProps } from "../actions/addCommentToUnsupportedProps"
4
+ import { isAriaOrDataAttribute } from "../helpers/unsupportedPropsHelpers"
5
+ import { TransformCondition } from "../types"
6
+ import { attributeTransformFactory } from "./attributeTransformFactory"
7
+
8
+ const defaultCommentNotes = (prop: string): string => {
9
+ if (prop === "css") {
10
+ return "\n * CSS prop is not supported. Use className or style prop instead.\n"
11
+ }
12
+ if (prop === "mediaQueries") {
13
+ return "\n * It is recommended to use CSS media queries in a class that you apply to the component.\n"
14
+ }
15
+ if (prop === "hover" || prop === "focus" || prop === "active") {
16
+ return "\n * State-based styles (hover, focus, active) should be handled with CSS class selectors.\n"
17
+ }
18
+ if (prop === "tooltip") {
19
+ return "\n * Wrapping a component in a tooltip is an unsupported anti-pattern.\n"
20
+ }
21
+ return ""
22
+ }
23
+
24
+ export function unsupportedPropsFactory(options: {
25
+ /** Optional function returning prop-specific notes appended to the comment. Returning an empty string falls back to the factory's default note for that prop. */
26
+ commentNotes?: (prop: string) => string
27
+ /** Optional condition that must be met for the transform to occur */
28
+ condition?: TransformCondition
29
+ /** List of prop names considered supported. aria-* and data-* attributes are always allowed. */
30
+ supportedProps: string[]
31
+ /** Component to target for attribute transformation */
32
+ targetComponent: string
33
+ /** Package the target component is imported from */
34
+ targetPackage: string
35
+ }): Transform {
36
+ const { commentNotes, condition, supportedProps, ...rest } = options
37
+ const messageSuffix = (prop: string): string =>
38
+ commentNotes?.(prop) || defaultCommentNotes(prop)
39
+ return attributeTransformFactory({
40
+ condition,
41
+ transform: (element, { j }) => {
42
+ const unsupportedProps = (element.openingElement.attributes || [])
43
+ .filter(
44
+ (attr) =>
45
+ attr.type === "JSXAttribute" &&
46
+ !supportedProps.includes(attr.name.name as string) &&
47
+ !isAriaOrDataAttribute(attr.name.name as string)
48
+ )
49
+ .map((attr) => (attr as JSXAttribute).name.name as string)
50
+
51
+ return addCommentToUnsupportedProps({
52
+ element,
53
+ j,
54
+ messageSuffix,
55
+ props: unsupportedProps,
56
+ })
57
+ },
58
+ ...rest,
59
+ })
60
+ }
@@ -1,35 +1,10 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { TEXTAREA_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
- const transform: Transform = attributeTransformFactory({
4
+ const transform = unsupportedPropsFactory({
5
+ supportedProps: TEXTAREA_SUPPORTED_PROPS,
8
6
  targetComponent: "TextArea",
9
7
  targetPackage: "@planningcenter/tapestry-react",
10
- transform: (element, { j }) => {
11
- const UNSUPPORTED_PROPS = (element.openingElement.attributes || [])
12
- .filter(
13
- (attr) =>
14
- attr.type === "JSXAttribute" &&
15
- !TEXTAREA_SUPPORTED_PROPS.includes(attr.name.name as string) &&
16
- !(attr.name.name as string).startsWith("aria-") &&
17
- !(attr.name.name as string).startsWith("data-")
18
- )
19
- .map((attr) => (attr as JSXAttribute).name.name as string)
20
-
21
- return addCommentToUnsupportedProps({
22
- element,
23
- j,
24
- messageSuffix: (prop) => {
25
- if (prop === "css") {
26
- return "\n * CSS prop is not supported. Use className or style prop instead.\n"
27
- }
28
- return ""
29
- },
30
- props: UNSUPPORTED_PROPS,
31
- })
32
- },
33
8
  })
34
9
 
35
10
  export default transform
@@ -1,37 +1,10 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { TIME_FIELD_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
- const transform: Transform = attributeTransformFactory({
4
+ const transform = unsupportedPropsFactory({
5
+ supportedProps: TIME_FIELD_SUPPORTED_PROPS,
8
6
  targetComponent: "TimeField",
9
7
  targetPackage: "@planningcenter/tapestry-react",
10
- transform: (element, { j }) => {
11
- const attrs = element.openingElement.attributes || []
12
-
13
- const UNSUPPORTED_PROPS = attrs
14
- .filter(
15
- (attr) =>
16
- attr.type === "JSXAttribute" &&
17
- !TIME_FIELD_SUPPORTED_PROPS.includes(attr.name.name as string) &&
18
- !(attr.name.name as string).startsWith("aria-") &&
19
- !(attr.name.name as string).startsWith("data-")
20
- )
21
- .map((attr) => (attr as JSXAttribute).name.name as string)
22
-
23
- return addCommentToUnsupportedProps({
24
- element,
25
- j,
26
- messageSuffix: (prop) => {
27
- if (prop === "css") {
28
- return "\n * CSS prop is not supported. Use className or style prop instead.\n"
29
- }
30
- return ""
31
- },
32
- props: UNSUPPORTED_PROPS,
33
- })
34
- },
35
8
  })
36
9
 
37
10
  export default transform
@@ -1,8 +1,5 @@
1
- import { JSXAttribute, Transform } from "jscodeshift"
2
-
3
- import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
4
1
  import { CHECKBOX_RADIO_SUPPORTED_PROPS } from "../../shared/helpers/unsupportedPropsHelpers"
5
- import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
2
+ import { unsupportedPropsFactory } from "../../shared/transformFactories/unsupportedPropsFactory"
6
3
 
7
4
  const TOGGLE_SWITCH_SPECIFIC_PROPS = ["hideLabel"]
8
5
 
@@ -11,32 +8,10 @@ const SUPPORTED_PROPS = [
11
8
  ...TOGGLE_SWITCH_SPECIFIC_PROPS,
12
9
  ]
13
10
 
14
- const transform: Transform = attributeTransformFactory({
11
+ const transform = unsupportedPropsFactory({
12
+ supportedProps: SUPPORTED_PROPS,
15
13
  targetComponent: "ToggleSwitch",
16
14
  targetPackage: "@planningcenter/tapestry-react",
17
- transform: (element, { j }) => {
18
- const UNSUPPORTED_PROPS = (element.openingElement.attributes || [])
19
- .filter(
20
- (attr) =>
21
- attr.type === "JSXAttribute" &&
22
- !SUPPORTED_PROPS.includes(attr.name.name as string) &&
23
- !(attr.name.name as string).startsWith("aria-") &&
24
- !(attr.name.name as string).startsWith("data-")
25
- )
26
- .map((attr) => (attr as JSXAttribute).name.name as string)
27
-
28
- return addCommentToUnsupportedProps({
29
- element,
30
- j,
31
- messageSuffix: (prop) => {
32
- if (prop === "css") {
33
- return "\n * CSS prop is not supported. Use className or style prop instead.\n"
34
- }
35
- return ""
36
- },
37
- props: UNSUPPORTED_PROPS,
38
- })
39
- },
40
15
  })
41
16
 
42
17
  export default transform
package/src/index.ts CHANGED
@@ -24,12 +24,17 @@ const COMPONENTS_SET = new Set([
24
24
  "toggle-switch",
25
25
  ])
26
26
 
27
+ const COMPONENTS_SENTENCE = new Intl.ListFormat("en", {
28
+ style: "long",
29
+ type: "conjunction",
30
+ }).format([...COMPONENTS_SET])
31
+
27
32
  program
28
33
  .command("run")
29
34
  .description("Run a migration of a component from Tapestry React to Tapestry")
30
35
  .argument(
31
36
  "<component-name>",
32
- "The name of the component to migrate (button, checkbox, input, link, radio, select, text-area, time-field, toggle-switch)"
37
+ `The name of the component to migrate (${COMPONENTS_SENTENCE})`
33
38
  )
34
39
  .requiredOption(
35
40
  "-p, --path <path>",