@planningcenter/tapestry-migration-cli 2.4.0-rc.1 → 2.4.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/package.json +2 -2
- package/src/components/button/transforms/childrenToLabel.test.ts +5 -4
- package/src/components/button/transforms/childrenToLabel.ts +9 -39
- package/src/components/button/transforms/unsupportedProps.ts +7 -31
- package/src/components/link/index.ts +16 -2
- package/src/components/link/transforms/childrenToLabel.test.ts +331 -0
- package/src/components/link/transforms/childrenToLabel.ts +54 -0
- package/src/components/link/transforms/convertStyleProps.test.ts +391 -0
- package/src/components/link/transforms/convertStyleProps.ts +10 -0
- package/src/components/link/transforms/{inlineToKind.test.ts → inlineMemberToKind.test.ts} +2 -2
- package/src/components/link/transforms/{inlineToKind.ts → inlineMemberToKind.ts} +0 -2
- package/src/components/link/transforms/inlinePropToKind.test.ts +312 -0
- package/src/components/link/transforms/inlinePropToKind.ts +24 -0
- package/src/components/link/transforms/moveLinkImport.test.ts +295 -0
- package/src/components/link/transforms/moveLinkImport.ts +14 -0
- package/src/components/link/transforms/removeAs.test.ts +192 -0
- package/src/components/link/transforms/removeAs.ts +17 -0
- package/src/components/link/transforms/reviewStyles.test.ts +172 -0
- package/src/components/link/transforms/reviewStyles.ts +17 -0
- package/src/components/link/transforms/targetBlankToExternal.test.ts +14 -0
- package/src/components/link/transforms/unsupportedProps.test.ts +265 -0
- package/src/components/link/transforms/unsupportedProps.ts +58 -0
- package/src/components/shared/conditions/hasAttributeValue.test.ts +22 -1
- package/src/components/shared/conditions/hasAttributeValue.ts +24 -6
- package/src/components/shared/helpers/childrenToLabelHelpers.ts +43 -0
- package/src/components/shared/helpers/unsupportedPropsHelpers.ts +35 -0
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +5 -1
- package/src/stubs/textPlugin.ts +45 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { JSXAttribute, Transform } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
import { addCommentToUnsupportedProps } from "../../shared/actions/addCommentToUnsupportedProps"
|
|
4
|
+
import { SUPPORTED_PROPS_BASE } from "../../shared/helpers/unsupportedPropsHelpers"
|
|
5
|
+
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
6
|
+
|
|
7
|
+
// Note: 'target' and 'rel' are NOT included because they are handled by targetBlankToExternal transform
|
|
8
|
+
const LINK_SPECIFIC_PROPS = [
|
|
9
|
+
"download",
|
|
10
|
+
"external",
|
|
11
|
+
"href",
|
|
12
|
+
"hrefLang",
|
|
13
|
+
"media",
|
|
14
|
+
"ping",
|
|
15
|
+
"referrerPolicy",
|
|
16
|
+
"type",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
const SUPPORTED_PROPS = [...SUPPORTED_PROPS_BASE, ...LINK_SPECIFIC_PROPS]
|
|
20
|
+
|
|
21
|
+
const transform: Transform = attributeTransformFactory({
|
|
22
|
+
targetComponent: "Link",
|
|
23
|
+
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
|
+
})
|
|
57
|
+
|
|
58
|
+
export default transform
|
|
@@ -32,7 +32,28 @@ describe("hasAttributeValue", () => {
|
|
|
32
32
|
expect(condition(element)).toBe(false)
|
|
33
33
|
})
|
|
34
34
|
|
|
35
|
-
it("should return
|
|
35
|
+
it("should return true for expression with string literal", () => {
|
|
36
|
+
const condition = hasAttributeValue("target", "_blank")
|
|
37
|
+
const element = createJSXElement(" target={'_blank'}")
|
|
38
|
+
|
|
39
|
+
expect(condition(element)).toBe(true)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it("should return true for expression with double quotes", () => {
|
|
43
|
+
const condition = hasAttributeValue("target", "_blank")
|
|
44
|
+
const element = createJSXElement(' target={"_blank"}')
|
|
45
|
+
|
|
46
|
+
expect(condition(element)).toBe(true)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it("should return false for expression with different value", () => {
|
|
50
|
+
const condition = hasAttributeValue("target", "_blank")
|
|
51
|
+
const element = createJSXElement(" target={'_self'}")
|
|
52
|
+
|
|
53
|
+
expect(condition(element)).toBe(false)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("should return false for complex expression values", () => {
|
|
36
57
|
const condition = hasAttributeValue("onClick", "handleClick")
|
|
37
58
|
const element = createJSXElement(" onClick={handleClick}")
|
|
38
59
|
|
|
@@ -4,6 +4,7 @@ import { TransformCondition } from "../types"
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Helper function to create a condition that checks for an attribute with a specific value
|
|
7
|
+
* Handles both string literals and expressions with string literals
|
|
7
8
|
*/
|
|
8
9
|
export function hasAttributeValue(
|
|
9
10
|
attributeName: string,
|
|
@@ -11,13 +12,30 @@ export function hasAttributeValue(
|
|
|
11
12
|
): TransformCondition {
|
|
12
13
|
return (element: JSXElement) => {
|
|
13
14
|
const attributes = element.openingElement.attributes || []
|
|
14
|
-
return attributes.some(
|
|
15
|
-
|
|
15
|
+
return attributes.some((attr) => {
|
|
16
|
+
const hasAttribute =
|
|
16
17
|
attr.type === "JSXAttribute" &&
|
|
17
18
|
attr.name?.type === "JSXIdentifier" &&
|
|
18
|
-
attr.name.name === attributeName
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
attr.name.name === attributeName
|
|
20
|
+
|
|
21
|
+
if (!hasAttribute) {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Handle string literal: attribute="value"
|
|
26
|
+
if (attr.value?.type === "StringLiteral") {
|
|
27
|
+
return attr.value.value === value
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Handle expression: attribute={"value"} or attribute={'value'}
|
|
31
|
+
if (attr.value?.type === "JSXExpressionContainer") {
|
|
32
|
+
const { expression } = attr.value
|
|
33
|
+
if (expression.type === "StringLiteral") {
|
|
34
|
+
return expression.value === value
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false
|
|
39
|
+
})
|
|
22
40
|
}
|
|
23
41
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { JSXElement } from "jscodeshift"
|
|
2
|
+
|
|
3
|
+
export function extractTextContent(
|
|
4
|
+
children: NonNullable<JSXElement["children"]>
|
|
5
|
+
): {
|
|
6
|
+
isSimpleText: boolean
|
|
7
|
+
textContent: string
|
|
8
|
+
} {
|
|
9
|
+
let textContent = ""
|
|
10
|
+
|
|
11
|
+
for (const child of children) {
|
|
12
|
+
if (child.type === "JSXText") {
|
|
13
|
+
const text = child.value.trim()
|
|
14
|
+
if (text) textContent += text
|
|
15
|
+
} else if (
|
|
16
|
+
child.type === "JSXExpressionContainer" &&
|
|
17
|
+
child.expression.type === "StringLiteral"
|
|
18
|
+
) {
|
|
19
|
+
textContent += child.expression.value
|
|
20
|
+
} else if (
|
|
21
|
+
child.type === "JSXExpressionContainer" &&
|
|
22
|
+
child.expression.type === "TemplateLiteral" &&
|
|
23
|
+
child.expression.expressions.length === 0
|
|
24
|
+
) {
|
|
25
|
+
textContent += child.expression.quasis[0].value.raw
|
|
26
|
+
} else {
|
|
27
|
+
return { isSimpleText: false, textContent: "" }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { isSimpleText: true, textContent }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function buildComment(
|
|
35
|
+
message: string,
|
|
36
|
+
includeIconGuidance: boolean
|
|
37
|
+
): string {
|
|
38
|
+
const baseMessage = `${message} - take time to find the right text for the component.`
|
|
39
|
+
if (includeIconGuidance) {
|
|
40
|
+
return `${baseMessage} If icons are used in the component, you can use prefix and suffix to correctly display those icons.`
|
|
41
|
+
}
|
|
42
|
+
return baseMessage
|
|
43
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { stylePropNames } from "../../../../dist/tapestry-react-shim.cjs"
|
|
2
|
+
|
|
3
|
+
export const STYLE_PROP_NAMES_WITHOUT_CSS = stylePropNames.filter(
|
|
4
|
+
(prop: string) => prop !== "css"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
export const COMMON_PROPS = [
|
|
8
|
+
"className",
|
|
9
|
+
"id",
|
|
10
|
+
"key",
|
|
11
|
+
"kind",
|
|
12
|
+
"label",
|
|
13
|
+
"onBlur",
|
|
14
|
+
"onFocus",
|
|
15
|
+
"onClick",
|
|
16
|
+
"onKeyDown",
|
|
17
|
+
"onKeyUp",
|
|
18
|
+
"onMouseDown",
|
|
19
|
+
"onMouseOut",
|
|
20
|
+
"onMouseOver",
|
|
21
|
+
"onMouseUp",
|
|
22
|
+
"prefix",
|
|
23
|
+
"ref",
|
|
24
|
+
"role",
|
|
25
|
+
"size",
|
|
26
|
+
"style",
|
|
27
|
+
"suffix",
|
|
28
|
+
"tabIndex",
|
|
29
|
+
"title",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
export const SUPPORTED_PROPS_BASE = [
|
|
33
|
+
...STYLE_PROP_NAMES_WITHOUT_CSS,
|
|
34
|
+
...COMMON_PROPS,
|
|
35
|
+
]
|
|
@@ -251,6 +251,7 @@ export function stylePropTransformFactory(config: {
|
|
|
251
251
|
const name = attr.name?.name as string
|
|
252
252
|
return (
|
|
253
253
|
name &&
|
|
254
|
+
name !== "css" &&
|
|
254
255
|
(stylePropNames.includes(name) ||
|
|
255
256
|
name in stylePropMapping ||
|
|
256
257
|
stylesToKeep.includes(name) ||
|
|
@@ -319,7 +320,10 @@ export function stylePropTransformFactory(config: {
|
|
|
319
320
|
styles = { ...styles, ...directStyleProps }
|
|
320
321
|
if (options.verbose) console.log("Final generated styles:", styles)
|
|
321
322
|
|
|
322
|
-
|
|
323
|
+
// Only apply styles if there are actual CSS properties to add
|
|
324
|
+
if (Object.keys(styles).length > 0) {
|
|
325
|
+
applyStylesToComponent({ element, j, styles })
|
|
326
|
+
}
|
|
323
327
|
} catch (error) {
|
|
324
328
|
console.log("Error processing style props:", error)
|
|
325
329
|
console.log("Style props that caused error:", allStyleProps)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// copied from packages/tapestry-react/src/Text/Text.tsx in tapestry-react
|
|
2
|
+
// size prop is not included since it is a supported prop in tapestry link
|
|
3
|
+
|
|
4
|
+
export const textPlugin = {
|
|
5
|
+
getStyles({
|
|
6
|
+
align,
|
|
7
|
+
italic,
|
|
8
|
+
truncate,
|
|
9
|
+
underline,
|
|
10
|
+
weight,
|
|
11
|
+
wrap = true,
|
|
12
|
+
...styles
|
|
13
|
+
}: Record<string, unknown>) {
|
|
14
|
+
if (align) {
|
|
15
|
+
styles.textAlign = align
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (italic) {
|
|
19
|
+
styles.fontStyle = "italic"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (truncate) {
|
|
23
|
+
if (styles.display !== "block" && styles.display !== "flex") {
|
|
24
|
+
styles.display = "block"
|
|
25
|
+
}
|
|
26
|
+
styles.overflow = "hidden"
|
|
27
|
+
styles.textOverflow = "ellipsis"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (truncate || wrap === false) {
|
|
31
|
+
styles.whiteSpace = "nowrap"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (underline) {
|
|
35
|
+
styles.textDecoration = underline
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (weight) {
|
|
39
|
+
styles.fontWeight = weight
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return styles
|
|
43
|
+
},
|
|
44
|
+
styleProps: ["align", "italic", "truncate", "underline", "weight", "wrap"],
|
|
45
|
+
}
|