@planningcenter/tapestry-migration-cli 2.4.1 → 2.5.0-qa-459.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 +10 -10
- package/dist/tapestry-react-shim.cjs +1 -0
- package/package.json +2 -2
- package/src/components/button/transforms/childrenToLabel.test.ts +18 -2
- package/src/components/button/transforms/childrenToLabel.ts +13 -1
- package/src/components/button/transforms/iconLeftToPrefix.ts +2 -2
- package/src/components/button/transforms/iconRightToSuffix.ts +2 -2
- package/src/components/button/transforms/iconToIconButton.ts +13 -1
- package/src/components/button/transforms/innerRefToRef.ts +2 -2
- package/src/components/button/transforms/spinnerToLoadingButton.ts +2 -2
- package/src/components/button/transforms/titleToLabel.ts +2 -2
- package/src/components/checkbox/transforms/childrenToLabel.test.ts +18 -2
- package/src/components/checkbox/transforms/childrenToLabel.ts +13 -1
- package/src/components/checkbox/transforms/innerRefToRef.test.ts +20 -2
- package/src/components/checkbox/transforms/innerRefToRef.ts +2 -2
- package/src/components/link/transforms/childrenToLabel.test.ts +18 -2
- package/src/components/link/transforms/childrenToLabel.ts +13 -1
- package/src/components/link/transforms/innerRefToRef.test.ts +19 -2
- package/src/components/link/transforms/innerRefToRef.ts +2 -2
- package/src/components/link/transforms/removeAs.test.ts +18 -2
- package/src/components/link/transforms/removeAs.ts +13 -2
- package/src/components/link/transforms/removeInlineProp.test.ts +20 -2
- package/src/components/link/transforms/removeInlineProp.ts +14 -1
- package/src/components/link/transforms/targetBlankToExternal.test.ts +18 -2
- package/src/components/link/transforms/targetBlankToExternal.ts +13 -4
- package/src/components/link/transforms/toToHref.test.ts +19 -2
- package/src/components/link/transforms/toToHref.ts +2 -2
- package/src/components/shared/actions/addComment.ts +10 -3
- package/src/components/shared/actions/addCommentToAttribute.ts +3 -1
- package/src/components/shared/actions/transformAttributeName.ts +16 -2
- package/src/components/shared/transformFactories/attributeCombineFactory.ts +33 -1
- package/src/components/shared/transformFactories/stylePropTransformFactory.ts +20 -0
- package/src/index.ts +3 -3
- package/src/jscodeshiftRunner.ts +1 -1
- package/src/reportGenerator.ts +69 -5
- package/src/shared/types.ts +1 -1
package/README.md
CHANGED
|
@@ -14,25 +14,25 @@ yarn add --dev @planningcenter/tapestry-migration-cli
|
|
|
14
14
|
|
|
15
15
|
For optimal results, migrate components in this order:
|
|
16
16
|
|
|
17
|
-
1. **Button components first**: `npx @planningcenter/tapestry-migration-cli run button -p ./src/components
|
|
18
|
-
2. **Link components second**: `npx @planningcenter/tapestry-migration-cli run link -p ./src/components
|
|
17
|
+
1. **Button components first**: `npx @planningcenter/tapestry-migration-cli run button -p ./src/components`
|
|
18
|
+
2. **Link components second**: `npx @planningcenter/tapestry-migration-cli run link -p ./src/components`
|
|
19
19
|
|
|
20
20
|
This order ensures that Button components with navigation props are properly handled before Link components are migrated.
|
|
21
21
|
|
|
22
22
|
### Basic Commands
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
|
-
#
|
|
25
|
+
# Apply the migration changes (default behavior)
|
|
26
26
|
npx @planningcenter/tapestry-migration-cli run button -p ./src/components
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
npx @planningcenter/tapestry-migration-cli run button -p ./src/components --
|
|
28
|
+
# Preview changes without writing (dry run)
|
|
29
|
+
npx @planningcenter/tapestry-migration-cli run button -p ./src/components --dry-run
|
|
30
30
|
|
|
31
31
|
# Run with verbose output
|
|
32
|
-
npx @planningcenter/tapestry-migration-cli run button -p ./src/components --
|
|
32
|
+
npx @planningcenter/tapestry-migration-cli run button -p ./src/components --verbose
|
|
33
33
|
|
|
34
34
|
# Generate a migration report
|
|
35
|
-
npx @planningcenter/tapestry-migration-cli run button -p ./src/components --
|
|
35
|
+
npx @planningcenter/tapestry-migration-cli run button -p ./src/components --report-path ./migration-report.md
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
## Available Components
|
|
@@ -46,7 +46,7 @@ npx @planningcenter/tapestry-migration-cli run button -p ./src/components --fix
|
|
|
46
46
|
|
|
47
47
|
## Optional Arguments
|
|
48
48
|
|
|
49
|
-
- `-
|
|
49
|
+
- `-d, --dry-run` - Preview changes without writing to files (default: writes changes)
|
|
50
50
|
- `-v, --verbose` - Show detailed output
|
|
51
51
|
- `-j, --js-theme <path>` - Path to JavaScript theme file
|
|
52
52
|
- `-r, --report-path <path>` - Path for migration report (default: MIGRATION_REPORT.md)
|
|
@@ -101,6 +101,6 @@ yarn add --dev @planningcenter/tapestry-migration-cli
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
# Then run (following recommended order)
|
|
104
|
-
yarn migrate run button -p app/javascript/components
|
|
105
|
-
yarn migrate run link -p app/javascript/components
|
|
104
|
+
yarn migrate run button -p app/javascript/components
|
|
105
|
+
yarn migrate run link -p app/javascript/components
|
|
106
106
|
```
|
|
@@ -2571,6 +2571,7 @@ const COMPONENT_KIND_CLASS_MAP = {
|
|
|
2571
2571
|
"ghost-interaction": "tds-btn--ghost-interaction",
|
|
2572
2572
|
"inline-text": "tds-btn--inline-text",
|
|
2573
2573
|
neutral: "tds-btn--neutral",
|
|
2574
|
+
"neutral-inline": "tds-btn--neutral-inline",
|
|
2574
2575
|
pill: "tds-btn--pill",
|
|
2575
2576
|
primary: "tds-btn--interaction",
|
|
2576
2577
|
"primary-page-header": "tds-btn--primary-page-header",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planningcenter/tapestry-migration-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0-qa-459.0",
|
|
4
4
|
"description": "CLI tool for Tapestry migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|
|
53
53
|
},
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "c09a62a934592af9a96f33a9fff32ece2e33aed6"
|
|
55
55
|
}
|
|
@@ -5,12 +5,12 @@ import transform from "./childrenToLabel"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string | null {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string | null {
|
|
9
9
|
const fileInfo = { path: "test.tsx", source }
|
|
10
10
|
return transform(
|
|
11
11
|
fileInfo,
|
|
12
12
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
-
{}
|
|
13
|
+
{ verbose }
|
|
14
14
|
) as string | null
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -360,5 +360,21 @@ function Test() {
|
|
|
360
360
|
"take time to find the right text for the component"
|
|
361
361
|
)
|
|
362
362
|
})
|
|
363
|
+
|
|
364
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
365
|
+
const input = `
|
|
366
|
+
import { Button } from "@planningcenter/tapestry-react"
|
|
367
|
+
|
|
368
|
+
function Test() {
|
|
369
|
+
return <Button>Save</Button>
|
|
370
|
+
}
|
|
371
|
+
`.trim()
|
|
372
|
+
|
|
373
|
+
const result = applyTransform(input, true)
|
|
374
|
+
expect(result).toContain('<Button label="Save" />')
|
|
375
|
+
expect(result).toContain(
|
|
376
|
+
"CHANGED: tapestry-migration (children): children converted to label prop"
|
|
377
|
+
)
|
|
378
|
+
})
|
|
363
379
|
})
|
|
364
380
|
})
|
|
@@ -15,7 +15,7 @@ const transform: Transform = attributeTransformFactory({
|
|
|
15
15
|
condition: hasChildren,
|
|
16
16
|
targetComponent: "Button",
|
|
17
17
|
targetPackage: "@planningcenter/tapestry-react",
|
|
18
|
-
transform: (element, { j, source }) => {
|
|
18
|
+
transform: (element, { j, options, source }) => {
|
|
19
19
|
if (hasAttribute("label")(element)) {
|
|
20
20
|
addComment({
|
|
21
21
|
element,
|
|
@@ -32,6 +32,18 @@ const transform: Transform = attributeTransformFactory({
|
|
|
32
32
|
if (isSimpleText && textContent) {
|
|
33
33
|
addAttribute({ element, j, name: "label", value: textContent })
|
|
34
34
|
removeChildren(element)
|
|
35
|
+
|
|
36
|
+
if (options?.verbose) {
|
|
37
|
+
addComment({
|
|
38
|
+
commentKind: "change",
|
|
39
|
+
element,
|
|
40
|
+
j,
|
|
41
|
+
scope: "children",
|
|
42
|
+
source,
|
|
43
|
+
text: buildComment("children converted to label prop", false),
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
return true
|
|
36
48
|
} else if (!isSimpleText) {
|
|
37
49
|
addComment({
|
|
@@ -8,7 +8,7 @@ const transform = attributeTransformFactory({
|
|
|
8
8
|
condition: hasAttribute("iconLeft"),
|
|
9
9
|
targetComponent: "Button",
|
|
10
10
|
targetPackage: "@planningcenter/tapestry-react",
|
|
11
|
-
transform: (element, { j, source }) => {
|
|
11
|
+
transform: (element, { j, options, source }) => {
|
|
12
12
|
const name = addImport({
|
|
13
13
|
component: "Icon",
|
|
14
14
|
conflictAlias: "TRIcon",
|
|
@@ -25,7 +25,7 @@ const transform = attributeTransformFactory({
|
|
|
25
25
|
})
|
|
26
26
|
if (!updatedElement) return false
|
|
27
27
|
|
|
28
|
-
transformAttributeName("iconLeft", "prefix", { element })
|
|
28
|
+
transformAttributeName("iconLeft", "prefix", { element, j, options })
|
|
29
29
|
return true
|
|
30
30
|
},
|
|
31
31
|
})
|
|
@@ -8,7 +8,7 @@ const transform = attributeTransformFactory({
|
|
|
8
8
|
condition: hasAttribute("iconRight"),
|
|
9
9
|
targetComponent: "Button",
|
|
10
10
|
targetPackage: "@planningcenter/tapestry-react",
|
|
11
|
-
transform: (element, { j, source }) => {
|
|
11
|
+
transform: (element, { j, options, source }) => {
|
|
12
12
|
const name = addImport({
|
|
13
13
|
component: "Icon",
|
|
14
14
|
conflictAlias: "TRIcon",
|
|
@@ -25,7 +25,7 @@ const transform = attributeTransformFactory({
|
|
|
25
25
|
})
|
|
26
26
|
if (!updatedElement) return false
|
|
27
27
|
|
|
28
|
-
transformAttributeName("iconRight", "suffix", { element })
|
|
28
|
+
transformAttributeName("iconRight", "suffix", { element, j, options })
|
|
29
29
|
return true
|
|
30
30
|
},
|
|
31
31
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { JSXIdentifier } from "jscodeshift"
|
|
2
2
|
|
|
3
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
3
4
|
import { convertAttributeFromObjectToJSXElement } from "../../shared/actions/convertAttributeFromObjectToJSXElement"
|
|
4
5
|
import { removeUnusedImport } from "../../shared/actions/removeUnusedImport"
|
|
5
6
|
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
@@ -10,7 +11,7 @@ const transform = attributeTransformFactory({
|
|
|
10
11
|
condition: hasAttribute("icon"),
|
|
11
12
|
targetComponent: "Button",
|
|
12
13
|
targetPackage: "@planningcenter/tapestry-react",
|
|
13
|
-
transform: (element, { j, source }) => {
|
|
14
|
+
transform: (element, { j, options, source }) => {
|
|
14
15
|
const name = addImport({
|
|
15
16
|
component: "Icon",
|
|
16
17
|
conflictAlias: "TRIcon",
|
|
@@ -46,6 +47,17 @@ const transform = attributeTransformFactory({
|
|
|
46
47
|
source,
|
|
47
48
|
})
|
|
48
49
|
|
|
50
|
+
if (options?.verbose) {
|
|
51
|
+
addComment({
|
|
52
|
+
commentKind: "change",
|
|
53
|
+
element,
|
|
54
|
+
j,
|
|
55
|
+
scope: "component",
|
|
56
|
+
source,
|
|
57
|
+
text: `converted Button with icon prop to IconButton`,
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
49
61
|
return true
|
|
50
62
|
},
|
|
51
63
|
})
|
|
@@ -6,8 +6,8 @@ const transform = attributeTransformFactory({
|
|
|
6
6
|
condition: hasAttribute("innerRef"),
|
|
7
7
|
targetComponent: "Button",
|
|
8
8
|
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
-
transform: (element) => {
|
|
10
|
-
return transformAttributeName("innerRef", "ref", { element })
|
|
9
|
+
transform: (element, { j, options }) => {
|
|
10
|
+
return transformAttributeName("innerRef", "ref", { element, j, options })
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
|
|
@@ -6,8 +6,8 @@ export default attributeTransformFactory({
|
|
|
6
6
|
condition: hasAttribute("spinner"),
|
|
7
7
|
targetComponent: "Button",
|
|
8
8
|
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
-
transform: (element) => {
|
|
10
|
-
transformAttributeName("spinner", "loading", { element })
|
|
9
|
+
transform: (element, { j, options }) => {
|
|
10
|
+
transformAttributeName("spinner", "loading", { element, j, options })
|
|
11
11
|
|
|
12
12
|
return true
|
|
13
13
|
},
|
|
@@ -8,11 +8,11 @@ const transform: Transform = attributeTransformFactory({
|
|
|
8
8
|
condition: hasAttribute("title"),
|
|
9
9
|
targetComponent: "Button",
|
|
10
10
|
targetPackage: "@planningcenter/tapestry-react",
|
|
11
|
-
transform: (element: JSXElement) =>
|
|
11
|
+
transform: (element: JSXElement, { j, options }) =>
|
|
12
12
|
transformAttributeName(
|
|
13
13
|
"title",
|
|
14
14
|
() => (hasAttribute("icon")(element) ? "aria-label" : "label"),
|
|
15
|
-
{ element }
|
|
15
|
+
{ element, j, options }
|
|
16
16
|
),
|
|
17
17
|
})
|
|
18
18
|
|
|
@@ -5,12 +5,12 @@ import transform from "./childrenToLabel"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string {
|
|
9
9
|
const fileInfo = { path: "test.tsx", source }
|
|
10
10
|
const result = transform(
|
|
11
11
|
fileInfo,
|
|
12
12
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
-
{}
|
|
13
|
+
{ verbose }
|
|
14
14
|
) as string | null
|
|
15
15
|
return result || source
|
|
16
16
|
}
|
|
@@ -142,5 +142,21 @@ function Test() {
|
|
|
142
142
|
'<Checkbox checked disabled label="Accept terms" />'
|
|
143
143
|
)
|
|
144
144
|
})
|
|
145
|
+
|
|
146
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
147
|
+
const input = `
|
|
148
|
+
import { Checkbox } from "@planningcenter/tapestry-react"
|
|
149
|
+
|
|
150
|
+
function Test() {
|
|
151
|
+
return <Checkbox>Accept terms</Checkbox>
|
|
152
|
+
}
|
|
153
|
+
`.trim()
|
|
154
|
+
|
|
155
|
+
const result = applyTransform(input, true)
|
|
156
|
+
expect(result).toContain('<Checkbox label="Accept terms" />')
|
|
157
|
+
expect(result).toContain(
|
|
158
|
+
"CHANGED: tapestry-migration (children): children converted to label prop"
|
|
159
|
+
)
|
|
160
|
+
})
|
|
145
161
|
})
|
|
146
162
|
})
|
|
@@ -15,7 +15,7 @@ const transform: Transform = attributeTransformFactory({
|
|
|
15
15
|
condition: hasChildren,
|
|
16
16
|
targetComponent: "Checkbox",
|
|
17
17
|
targetPackage: "@planningcenter/tapestry-react",
|
|
18
|
-
transform: (element, { j, source }) => {
|
|
18
|
+
transform: (element, { j, options, source }) => {
|
|
19
19
|
if (hasAttribute("label")(element)) {
|
|
20
20
|
addComment({
|
|
21
21
|
element,
|
|
@@ -32,6 +32,18 @@ const transform: Transform = attributeTransformFactory({
|
|
|
32
32
|
if (isSimpleText && textContent) {
|
|
33
33
|
addAttribute({ element, j, name: "label", value: textContent })
|
|
34
34
|
removeChildren(element)
|
|
35
|
+
|
|
36
|
+
if (options?.verbose) {
|
|
37
|
+
addComment({
|
|
38
|
+
commentKind: "change",
|
|
39
|
+
element,
|
|
40
|
+
j,
|
|
41
|
+
scope: "children",
|
|
42
|
+
source,
|
|
43
|
+
text: buildComment("children converted to label prop", false),
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
return true
|
|
36
48
|
} else if (!isSimpleText) {
|
|
37
49
|
addComment({
|
|
@@ -5,12 +5,12 @@ import transform from "./innerRefToRef"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string {
|
|
9
9
|
const fileInfo = { path: "test.tsx", source }
|
|
10
10
|
const result = transform(
|
|
11
11
|
fileInfo,
|
|
12
12
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
-
{}
|
|
13
|
+
{ verbose }
|
|
14
14
|
) as string | null
|
|
15
15
|
return result || source
|
|
16
16
|
}
|
|
@@ -157,5 +157,23 @@ function Test() {
|
|
|
157
157
|
expect(result).toContain("disabled")
|
|
158
158
|
expect(result).not.toContain("innerRef=")
|
|
159
159
|
})
|
|
160
|
+
|
|
161
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
162
|
+
const input = `
|
|
163
|
+
import { Checkbox } from "@planningcenter/tapestry-react"
|
|
164
|
+
|
|
165
|
+
function Test() {
|
|
166
|
+
const checkboxRef = React.useRef()
|
|
167
|
+
return <Checkbox innerRef={checkboxRef} label="Test" />
|
|
168
|
+
}
|
|
169
|
+
`.trim()
|
|
170
|
+
|
|
171
|
+
const result = applyTransform(input, true)
|
|
172
|
+
expect(result).toContain("ref={checkboxRef}")
|
|
173
|
+
expect(result).not.toContain("innerRef=")
|
|
174
|
+
expect(result).toContain(
|
|
175
|
+
"CHANGED: tapestry-migration (ref): renamed from innerRef"
|
|
176
|
+
)
|
|
177
|
+
})
|
|
160
178
|
})
|
|
161
179
|
})
|
|
@@ -6,8 +6,8 @@ const transform = attributeTransformFactory({
|
|
|
6
6
|
condition: hasAttribute("innerRef"),
|
|
7
7
|
targetComponent: "Checkbox",
|
|
8
8
|
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
-
transform: (element) => {
|
|
10
|
-
return transformAttributeName("innerRef", "ref", { element })
|
|
9
|
+
transform: (element, { j, options }) => {
|
|
10
|
+
return transformAttributeName("innerRef", "ref", { element, j, options })
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
|
|
@@ -5,12 +5,12 @@ import transform from "./childrenToLabel"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string | null {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string | null {
|
|
9
9
|
const fileInfo = { path: "test.tsx", source }
|
|
10
10
|
return transform(
|
|
11
11
|
fileInfo,
|
|
12
12
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
-
{}
|
|
13
|
+
{ verbose }
|
|
14
14
|
) as string | null
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -327,5 +327,21 @@ function Test() {
|
|
|
327
327
|
"If icons are used in the component, you can use prefix and suffix to correctly display those icons"
|
|
328
328
|
)
|
|
329
329
|
})
|
|
330
|
+
|
|
331
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
332
|
+
const input = `
|
|
333
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
334
|
+
|
|
335
|
+
function Test() {
|
|
336
|
+
return <Link href="/home">Home</Link>
|
|
337
|
+
}
|
|
338
|
+
`.trim()
|
|
339
|
+
|
|
340
|
+
const result = applyTransform(input, true)
|
|
341
|
+
expect(result).toContain('<Link href="/home" label="Home" />')
|
|
342
|
+
expect(result).toContain(
|
|
343
|
+
"CHANGED: tapestry-migration (children): children converted to label prop"
|
|
344
|
+
)
|
|
345
|
+
})
|
|
330
346
|
})
|
|
331
347
|
})
|
|
@@ -15,7 +15,7 @@ const transform: Transform = attributeTransformFactory({
|
|
|
15
15
|
condition: hasChildren,
|
|
16
16
|
targetComponent: "Link",
|
|
17
17
|
targetPackage: "@planningcenter/tapestry-react",
|
|
18
|
-
transform: (element, { j, source }) => {
|
|
18
|
+
transform: (element, { j, options, source }) => {
|
|
19
19
|
if (hasAttribute("label")(element)) {
|
|
20
20
|
addComment({
|
|
21
21
|
element,
|
|
@@ -32,6 +32,18 @@ const transform: Transform = attributeTransformFactory({
|
|
|
32
32
|
if (isSimpleText && textContent) {
|
|
33
33
|
addAttribute({ element, j, name: "label", value: textContent })
|
|
34
34
|
removeChildren(element)
|
|
35
|
+
|
|
36
|
+
if (options?.verbose) {
|
|
37
|
+
addComment({
|
|
38
|
+
commentKind: "change",
|
|
39
|
+
element,
|
|
40
|
+
j,
|
|
41
|
+
scope: "children",
|
|
42
|
+
source,
|
|
43
|
+
text: buildComment("children converted to label prop", false),
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
return true
|
|
36
48
|
} else if (!isSimpleText) {
|
|
37
49
|
addComment({
|
|
@@ -5,12 +5,12 @@ import transform from "./innerRefToRef"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string | null {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string | null {
|
|
9
9
|
const fileInfo = { path: "test.tsx", source }
|
|
10
10
|
return transform(
|
|
11
11
|
fileInfo,
|
|
12
12
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
-
{}
|
|
13
|
+
{ verbose }
|
|
14
14
|
) as string | null
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -166,5 +166,22 @@ export default function Test() {
|
|
|
166
166
|
const result = applyTransform(input)
|
|
167
167
|
expect(result).toBe(expected)
|
|
168
168
|
})
|
|
169
|
+
|
|
170
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
171
|
+
const input = `
|
|
172
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
173
|
+
|
|
174
|
+
export default function Test() {
|
|
175
|
+
return <Link innerRef={linkRef}>Go somewhere</Link>
|
|
176
|
+
}
|
|
177
|
+
`.trim()
|
|
178
|
+
|
|
179
|
+
const result = applyTransform(input, true)
|
|
180
|
+
expect(result).toContain("ref={linkRef}")
|
|
181
|
+
expect(result).not.toContain("innerRef=")
|
|
182
|
+
expect(result).toContain(
|
|
183
|
+
"CHANGED: tapestry-migration (ref): renamed from innerRef"
|
|
184
|
+
)
|
|
185
|
+
})
|
|
169
186
|
})
|
|
170
187
|
})
|
|
@@ -6,8 +6,8 @@ const transform = attributeTransformFactory({
|
|
|
6
6
|
condition: hasAttribute("innerRef"),
|
|
7
7
|
targetComponent: "Link",
|
|
8
8
|
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
-
transform: (element) => {
|
|
10
|
-
return transformAttributeName("innerRef", "ref", { element })
|
|
9
|
+
transform: (element, { j, options }) => {
|
|
10
|
+
return transformAttributeName("innerRef", "ref", { element, j, options })
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
|
|
@@ -5,11 +5,11 @@ import removeAs from "./removeAs"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string | null {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string | null {
|
|
9
9
|
return removeAs(
|
|
10
10
|
{ path: "test.tsx", source },
|
|
11
11
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
12
|
-
{}
|
|
12
|
+
{ verbose }
|
|
13
13
|
) as string | null
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -188,5 +188,21 @@ export default function Test() {
|
|
|
188
188
|
expect(result).toContain('href="/external"')
|
|
189
189
|
expect(result).not.toContain('as="a"')
|
|
190
190
|
})
|
|
191
|
+
|
|
192
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
193
|
+
const input = `
|
|
194
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
195
|
+
|
|
196
|
+
export default function Test() {
|
|
197
|
+
return <Link href="/home" as="a">Home</Link>
|
|
198
|
+
}
|
|
199
|
+
`.trim()
|
|
200
|
+
|
|
201
|
+
const result = applyTransform(input, true)
|
|
202
|
+
expect(result).not.toContain('as="a"')
|
|
203
|
+
expect(result).toContain(
|
|
204
|
+
"CHANGED: tapestry-migration (as): as='a' prop removed"
|
|
205
|
+
)
|
|
206
|
+
})
|
|
191
207
|
})
|
|
192
208
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
1
2
|
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
2
3
|
import { hasAttributeValue } from "../../shared/conditions/hasAttributeValue"
|
|
3
4
|
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
@@ -6,10 +7,20 @@ const transform = attributeTransformFactory({
|
|
|
6
7
|
condition: hasAttributeValue("as", "a"),
|
|
7
8
|
targetComponent: "Link",
|
|
8
9
|
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
-
transform: (element, { j, source }) => {
|
|
10
|
-
// Remove as attribute
|
|
10
|
+
transform: (element, { j, options, source }) => {
|
|
11
11
|
removeAttribute("as", { element, j, source })
|
|
12
12
|
|
|
13
|
+
if (options?.verbose) {
|
|
14
|
+
addComment({
|
|
15
|
+
commentKind: "change",
|
|
16
|
+
element,
|
|
17
|
+
j,
|
|
18
|
+
scope: "as",
|
|
19
|
+
source,
|
|
20
|
+
text: "as='a' prop removed",
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
13
24
|
return true
|
|
14
25
|
},
|
|
15
26
|
})
|
|
@@ -5,12 +5,12 @@ import transform from "./removeInlineProp"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string | null {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string | null {
|
|
9
9
|
const fileInfo = { path: "test.tsx", source }
|
|
10
10
|
return transform(
|
|
11
11
|
fileInfo,
|
|
12
12
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
-
{}
|
|
13
|
+
{ verbose }
|
|
14
14
|
) as string | null
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -291,5 +291,23 @@ export default function Test() {
|
|
|
291
291
|
const result = applyTransform(input)
|
|
292
292
|
expect(result).toBe(null)
|
|
293
293
|
})
|
|
294
|
+
|
|
295
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
296
|
+
const input = `
|
|
297
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
298
|
+
|
|
299
|
+
export default function Test() {
|
|
300
|
+
return <Link href="/profile" inline>Profile</Link>
|
|
301
|
+
}
|
|
302
|
+
`.trim()
|
|
303
|
+
|
|
304
|
+
const result = applyTransform(input, true)
|
|
305
|
+
expect(result).not.toContain(" inline>")
|
|
306
|
+
expect(result).not.toContain(' inline"')
|
|
307
|
+
expect(result).not.toContain(" inline=")
|
|
308
|
+
expect(result).toContain(
|
|
309
|
+
"CHANGED: tapestry-migration (inline): inline prop removed"
|
|
310
|
+
)
|
|
311
|
+
})
|
|
294
312
|
})
|
|
295
313
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
1
2
|
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
2
3
|
import { hasAttribute } from "../../shared/conditions/hasAttribute"
|
|
3
4
|
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
@@ -6,8 +7,20 @@ const transform = attributeTransformFactory({
|
|
|
6
7
|
condition: hasAttribute("inline"),
|
|
7
8
|
targetComponent: "Link",
|
|
8
9
|
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
-
transform: (element, { j, source }) => {
|
|
10
|
+
transform: (element, { j, options, source }) => {
|
|
10
11
|
removeAttribute("inline", { element, j, source })
|
|
12
|
+
|
|
13
|
+
if (options?.verbose) {
|
|
14
|
+
addComment({
|
|
15
|
+
commentKind: "change",
|
|
16
|
+
element,
|
|
17
|
+
j,
|
|
18
|
+
scope: "inline",
|
|
19
|
+
source,
|
|
20
|
+
text: "inline prop removed",
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
return true
|
|
12
25
|
},
|
|
13
26
|
})
|
|
@@ -5,11 +5,11 @@ import targetBlankToExternal from "./targetBlankToExternal"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string | null {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string | null {
|
|
9
9
|
return targetBlankToExternal(
|
|
10
10
|
{ path: "test.tsx", source },
|
|
11
11
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
12
|
-
{}
|
|
12
|
+
{ verbose }
|
|
13
13
|
) as string | null
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -201,5 +201,21 @@ export default function Test() {
|
|
|
201
201
|
expect(result).toContain("external")
|
|
202
202
|
expect(result).not.toContain('target="_blank"')
|
|
203
203
|
})
|
|
204
|
+
|
|
205
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
206
|
+
const input = `
|
|
207
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
208
|
+
|
|
209
|
+
export default function Test() {
|
|
210
|
+
return <Link href="/external" target="_blank">External Link</Link>
|
|
211
|
+
}
|
|
212
|
+
`.trim()
|
|
213
|
+
|
|
214
|
+
const result = applyTransform(input, true)
|
|
215
|
+
expect(result).toContain("external")
|
|
216
|
+
expect(result).toContain(
|
|
217
|
+
"CHANGED: tapestry-migration (external): target='_blank' converted to external prop"
|
|
218
|
+
)
|
|
219
|
+
})
|
|
204
220
|
})
|
|
205
221
|
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { addAttribute } from "../../shared/actions/addAttribute"
|
|
2
|
+
import { addComment } from "../../shared/actions/addComment"
|
|
2
3
|
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
3
4
|
import { hasAttributeValue } from "../../shared/conditions/hasAttributeValue"
|
|
4
5
|
import { attributeTransformFactory } from "../../shared/transformFactories/attributeTransformFactory"
|
|
@@ -7,14 +8,11 @@ const transform = attributeTransformFactory({
|
|
|
7
8
|
condition: hasAttributeValue("target", "_blank"),
|
|
8
9
|
targetComponent: "Link",
|
|
9
10
|
targetPackage: "@planningcenter/tapestry-react",
|
|
10
|
-
transform: (element, { j, source }) => {
|
|
11
|
-
// Remove target attribute
|
|
11
|
+
transform: (element, { j, options, source }) => {
|
|
12
12
|
removeAttribute("target", { element, j, source })
|
|
13
13
|
|
|
14
|
-
// Remove rel attribute if it exists
|
|
15
14
|
removeAttribute("rel", { element, j, source })
|
|
16
15
|
|
|
17
|
-
// Add external attribute as shorthand boolean
|
|
18
16
|
addAttribute({
|
|
19
17
|
booleanAsShorthand: true,
|
|
20
18
|
element,
|
|
@@ -23,6 +21,17 @@ const transform = attributeTransformFactory({
|
|
|
23
21
|
value: true, // This will create just "external" not "external={true}"
|
|
24
22
|
})
|
|
25
23
|
|
|
24
|
+
if (options?.verbose) {
|
|
25
|
+
addComment({
|
|
26
|
+
commentKind: "change",
|
|
27
|
+
element,
|
|
28
|
+
j,
|
|
29
|
+
scope: "external",
|
|
30
|
+
source,
|
|
31
|
+
text: "target='_blank' converted to external prop",
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
return true
|
|
27
36
|
},
|
|
28
37
|
})
|
|
@@ -5,12 +5,12 @@ import transform from "./toToHref"
|
|
|
5
5
|
|
|
6
6
|
const j = jscodeshift.withParser("tsx")
|
|
7
7
|
|
|
8
|
-
function applyTransform(source: string): string | null {
|
|
8
|
+
function applyTransform(source: string, verbose = false): string | null {
|
|
9
9
|
const fileInfo = { path: "test.tsx", source }
|
|
10
10
|
return transform(
|
|
11
11
|
fileInfo,
|
|
12
12
|
{ j, jscodeshift: j, report: () => {}, stats: () => {} },
|
|
13
|
-
{}
|
|
13
|
+
{ verbose }
|
|
14
14
|
) as string | null
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -208,6 +208,23 @@ export default function Test({ items }) {
|
|
|
208
208
|
expect(result).not.toContain("to={item.url}")
|
|
209
209
|
expect(result).not.toContain('to="/conditional"')
|
|
210
210
|
})
|
|
211
|
+
|
|
212
|
+
it("should add CHANGED comment when verbose is enabled", () => {
|
|
213
|
+
const input = `
|
|
214
|
+
import { Link } from "@planningcenter/tapestry-react"
|
|
215
|
+
|
|
216
|
+
export default function Test() {
|
|
217
|
+
return <Link to="/dashboard">Dashboard</Link>
|
|
218
|
+
}
|
|
219
|
+
`.trim()
|
|
220
|
+
|
|
221
|
+
const result = applyTransform(input, true)
|
|
222
|
+
expect(result).toContain('href="/dashboard"')
|
|
223
|
+
expect(result).not.toContain('to="/dashboard"')
|
|
224
|
+
expect(result).toContain(
|
|
225
|
+
"CHANGED: tapestry-migration (href): renamed from to"
|
|
226
|
+
)
|
|
227
|
+
})
|
|
211
228
|
})
|
|
212
229
|
|
|
213
230
|
describe("no changes scenarios", () => {
|
|
@@ -6,8 +6,8 @@ const transform = attributeTransformFactory({
|
|
|
6
6
|
condition: hasAttribute("to"),
|
|
7
7
|
targetComponent: "Link",
|
|
8
8
|
targetPackage: "@planningcenter/tapestry-react",
|
|
9
|
-
transform: (element) => {
|
|
10
|
-
return transformAttributeName("to", "href", { element })
|
|
9
|
+
transform: (element, { j, options }) => {
|
|
10
|
+
return transformAttributeName("to", "href", { element, j, options })
|
|
11
11
|
},
|
|
12
12
|
})
|
|
13
13
|
|
|
@@ -12,14 +12,16 @@ export function addComment({
|
|
|
12
12
|
j,
|
|
13
13
|
source,
|
|
14
14
|
scope,
|
|
15
|
+
commentKind = "todo",
|
|
15
16
|
}: {
|
|
17
|
+
commentKind?: "todo" | "change"
|
|
16
18
|
element: JSXElement
|
|
17
19
|
j: JSCodeshift
|
|
18
20
|
scope: string
|
|
19
21
|
source: Collection
|
|
20
22
|
text: string
|
|
21
23
|
}) {
|
|
22
|
-
const commentText = formatComment(text, scope)
|
|
24
|
+
const commentText = formatComment(text, scope, commentKind)
|
|
23
25
|
if (tryInsertJSXComment(source, element, commentText, j)) {
|
|
24
26
|
return
|
|
25
27
|
}
|
|
@@ -64,6 +66,11 @@ function tryInsertJSXComment(
|
|
|
64
66
|
return found
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
export function formatComment(
|
|
68
|
-
|
|
69
|
+
export function formatComment(
|
|
70
|
+
text: string,
|
|
71
|
+
scope: string,
|
|
72
|
+
commentKind: "todo" | "change" = "todo"
|
|
73
|
+
): string {
|
|
74
|
+
const prefix = commentKind === "change" ? "CHANGED" : "TODO"
|
|
75
|
+
return ` ${prefix}: tapestry-migration (${scope}): ${text} `
|
|
69
76
|
}
|
|
@@ -6,8 +6,10 @@ export function addCommentToAttribute({
|
|
|
6
6
|
text,
|
|
7
7
|
attribute,
|
|
8
8
|
j,
|
|
9
|
+
commentKind = "todo",
|
|
9
10
|
}: {
|
|
10
11
|
attribute: JSXAttribute | JSXSpreadAttribute
|
|
12
|
+
commentKind?: "todo" | "change"
|
|
11
13
|
j: JSCodeshift
|
|
12
14
|
text: string
|
|
13
15
|
}) {
|
|
@@ -15,7 +17,7 @@ export function addCommentToAttribute({
|
|
|
15
17
|
((attribute.type === "JSXAttribute" && attribute.name.name) as string) ||
|
|
16
18
|
"spreadAttribute"
|
|
17
19
|
const comment = j.commentBlock(
|
|
18
|
-
formatComment(text, attributeName),
|
|
20
|
+
formatComment(text, attributeName, commentKind),
|
|
19
21
|
true,
|
|
20
22
|
false
|
|
21
23
|
)
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
import { JSXElement } from "jscodeshift"
|
|
1
|
+
import { JSCodeshift, JSXElement, Options } from "jscodeshift"
|
|
2
2
|
|
|
3
3
|
import { findAttribute } from "../findAttribute"
|
|
4
|
+
import { addCommentToAttribute } from "./addCommentToAttribute"
|
|
4
5
|
|
|
5
6
|
export function transformAttributeName(
|
|
6
7
|
name: string,
|
|
7
8
|
nameTransform: string | ((element: JSXElement) => string),
|
|
8
|
-
{
|
|
9
|
+
{
|
|
10
|
+
element,
|
|
11
|
+
j,
|
|
12
|
+
options,
|
|
13
|
+
}: { element: JSXElement; j?: JSCodeshift; options?: Options }
|
|
9
14
|
): boolean {
|
|
10
15
|
if (!nameTransform) return false
|
|
11
16
|
const attributes = element.openingElement.attributes || []
|
|
@@ -16,5 +21,14 @@ export function transformAttributeName(
|
|
|
16
21
|
typeof nameTransform === "string" ? nameTransform : nameTransform(element)
|
|
17
22
|
attribute.name.name = resolvedName
|
|
18
23
|
|
|
24
|
+
if (options?.verbose && j && attribute.type === "JSXAttribute") {
|
|
25
|
+
addCommentToAttribute({
|
|
26
|
+
attribute,
|
|
27
|
+
commentKind: "change",
|
|
28
|
+
j,
|
|
29
|
+
text: `renamed from ${name}`,
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
19
33
|
return true
|
|
20
34
|
}
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
JSCodeshift,
|
|
4
4
|
JSXAttribute,
|
|
5
5
|
JSXElement,
|
|
6
|
+
Options,
|
|
6
7
|
Transform,
|
|
7
8
|
} from "jscodeshift"
|
|
8
9
|
|
|
9
10
|
import { addAttribute, Conditional } from "../actions/addAttribute"
|
|
11
|
+
import { addCommentToAttribute } from "../actions/addCommentToAttribute"
|
|
10
12
|
import { getAttribute } from "../actions/getAttribute"
|
|
11
13
|
import { removeAttribute } from "../actions/removeAttribute"
|
|
12
14
|
import { hasAttribute } from "../conditions/hasAttribute"
|
|
@@ -263,7 +265,11 @@ export function attributeCombineFactory({
|
|
|
263
265
|
targetPackage: packageName,
|
|
264
266
|
transform: (
|
|
265
267
|
element: JSXElement,
|
|
266
|
-
{
|
|
268
|
+
{
|
|
269
|
+
j,
|
|
270
|
+
options,
|
|
271
|
+
source,
|
|
272
|
+
}: { j: JSCodeshift; options: Options; source: Collection }
|
|
267
273
|
) => {
|
|
268
274
|
const attributes: AttributeWithName[] = sourceAttributes.map(
|
|
269
275
|
(name: string) => ({
|
|
@@ -294,6 +300,32 @@ export function attributeCombineFactory({
|
|
|
294
300
|
}
|
|
295
301
|
addAttribute({ element, j, name: targetAttribute, value: mappingResult })
|
|
296
302
|
|
|
303
|
+
if (options?.verbose) {
|
|
304
|
+
const addedAttr = getAttribute({ element, name: targetAttribute })
|
|
305
|
+
if (addedAttr && addedAttr.type === "JSXAttribute") {
|
|
306
|
+
const v1 = getAttributeStringValue(
|
|
307
|
+
attributes[0].attribute,
|
|
308
|
+
defaults[sourceAttributes[0]] || "",
|
|
309
|
+
valueNormalizers[sourceAttributes[0]]
|
|
310
|
+
)
|
|
311
|
+
const v2 = getAttributeStringValue(
|
|
312
|
+
attributes[1].attribute,
|
|
313
|
+
defaults[sourceAttributes[1]] || "",
|
|
314
|
+
valueNormalizers[sourceAttributes[1]]
|
|
315
|
+
)
|
|
316
|
+
const fromParts = [
|
|
317
|
+
`${sourceAttributes[0]}=${v1 ?? "<unsupported>"}`,
|
|
318
|
+
`${sourceAttributes[1]}=${v2 ?? "<unsupported>"}`,
|
|
319
|
+
]
|
|
320
|
+
addCommentToAttribute({
|
|
321
|
+
attribute: addedAttr,
|
|
322
|
+
commentKind: "change",
|
|
323
|
+
j,
|
|
324
|
+
text: `derived from ${fromParts.join(" ")}`,
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
297
329
|
return true
|
|
298
330
|
},
|
|
299
331
|
})
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
stylePropNames,
|
|
14
14
|
} from "../../../../dist/tapestry-react-shim.cjs"
|
|
15
15
|
import { addComment } from "../../shared/actions/addComment"
|
|
16
|
+
import { addCommentToAttribute } from "../../shared/actions/addCommentToAttribute"
|
|
16
17
|
import { getAttribute } from "../../shared/actions/getAttribute"
|
|
17
18
|
import { removeAttribute } from "../../shared/actions/removeAttribute"
|
|
18
19
|
import { attributeTransformFactory } from "./attributeTransformFactory"
|
|
@@ -323,6 +324,25 @@ export function stylePropTransformFactory(config: {
|
|
|
323
324
|
// Only apply styles if there are actual CSS properties to add
|
|
324
325
|
if (Object.keys(styles).length > 0) {
|
|
325
326
|
applyStylesToComponent({ element, j, styles })
|
|
327
|
+
|
|
328
|
+
if (options.verbose) {
|
|
329
|
+
const styleAttr = getAttribute({ element, name: "style" })
|
|
330
|
+
if (styleAttr && styleAttr.type === "JSXAttribute") {
|
|
331
|
+
const folded = Object.keys(allStyleProps)
|
|
332
|
+
const direct = Object.keys(directStyleProps)
|
|
333
|
+
const parts = [] as string[]
|
|
334
|
+
if (folded.length) parts.push(`props: ${folded.join(", ")}`)
|
|
335
|
+
if (direct.length) parts.push(`direct: ${direct.join(", ")}`)
|
|
336
|
+
if (parts.length) {
|
|
337
|
+
addCommentToAttribute({
|
|
338
|
+
attribute: styleAttr,
|
|
339
|
+
commentKind: "change",
|
|
340
|
+
j,
|
|
341
|
+
text: `migrated style from ${parts.join("; ")}`,
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
326
346
|
}
|
|
327
347
|
} catch (error) {
|
|
328
348
|
console.log("Error processing style props:", error)
|
package/src/index.ts
CHANGED
|
@@ -23,7 +23,7 @@ program
|
|
|
23
23
|
"-p, --path <path>",
|
|
24
24
|
"REQUIRED: The path to the folder/file to migrate"
|
|
25
25
|
)
|
|
26
|
-
.option("-
|
|
26
|
+
.option("-d, --dry-run", "Preview changes without writing to files")
|
|
27
27
|
.option("-v, --verbose", "Verbose output")
|
|
28
28
|
.option(
|
|
29
29
|
"-j, --js-theme <path>",
|
|
@@ -31,7 +31,7 @@ program
|
|
|
31
31
|
)
|
|
32
32
|
.option(
|
|
33
33
|
"-r, --report-path <path>",
|
|
34
|
-
"Path for the migration report.
|
|
34
|
+
"Path for the migration report. Creates a report when running with changes applied and does not create a report if no additional changes are required.",
|
|
35
35
|
"MIGRATION_REPORT.md"
|
|
36
36
|
)
|
|
37
37
|
.action((componentName, options) => {
|
|
@@ -41,7 +41,7 @@ program
|
|
|
41
41
|
console.log(`🎨 Migrating ${componentName} components...`)
|
|
42
42
|
console.log(`📁 Target: ${options.path}`)
|
|
43
43
|
console.log(
|
|
44
|
-
`🔧 Mode: ${options.
|
|
44
|
+
`🔧 Mode: ${options.dryRun ? "Dry run (preview only)" : "Apply changes"}`
|
|
45
45
|
)
|
|
46
46
|
runTransforms(key, options)
|
|
47
47
|
} else {
|
package/src/jscodeshiftRunner.ts
CHANGED
package/src/reportGenerator.ts
CHANGED
|
@@ -12,6 +12,7 @@ interface CommentData {
|
|
|
12
12
|
line: number
|
|
13
13
|
scope: string
|
|
14
14
|
text: string
|
|
15
|
+
type: "todo" | "changed"
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
interface CommentStats {
|
|
@@ -20,6 +21,7 @@ interface CommentStats {
|
|
|
20
21
|
files: string[]
|
|
21
22
|
normalizedText: string
|
|
22
23
|
scope: string
|
|
24
|
+
type: "todo" | "changed"
|
|
23
25
|
weight: number
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -60,14 +62,17 @@ function extractCommentsFromFile(filePath: string): CommentData[] {
|
|
|
60
62
|
// Match both JSX and regular comment formats:
|
|
61
63
|
// {/* TODO: tapestry-migration (scope): text */}
|
|
62
64
|
// /* TODO: tapestry-migration (scope): text */
|
|
65
|
+
// {/* CHANGED: tapestry-migration (scope): text */}
|
|
66
|
+
// /* CHANGED: tapestry-migration (scope): text */
|
|
63
67
|
// Use lazy matching with [\s\S]*? to capture everything including * until we hit the closing */
|
|
64
68
|
const commentRegex =
|
|
65
|
-
/(?:\{\/\*|\/\*)\s*TODO:\s*tapestry-migration\s*\(([^)]+)\):\s*([\s\S]*?)\s*\*\/\}?/g
|
|
69
|
+
/(?:\{\/\*|\/\*)\s*(TODO|CHANGED):\s*tapestry-migration\s*\(([^)]+)\):\s*([\s\S]*?)\s*\*\/\}?/g
|
|
66
70
|
|
|
67
71
|
let match
|
|
68
72
|
while ((match = commentRegex.exec(content)) !== null) {
|
|
69
|
-
const
|
|
70
|
-
const
|
|
73
|
+
const commentType = match[1].trim().toLowerCase() as "todo" | "changed"
|
|
74
|
+
const scope = match[2].trim()
|
|
75
|
+
const text = match[3].trim()
|
|
71
76
|
|
|
72
77
|
// Calculate line number
|
|
73
78
|
const beforeMatch = content.substring(0, match.index)
|
|
@@ -78,6 +83,7 @@ function extractCommentsFromFile(filePath: string): CommentData[] {
|
|
|
78
83
|
line,
|
|
79
84
|
scope,
|
|
80
85
|
text,
|
|
86
|
+
type: commentType,
|
|
81
87
|
})
|
|
82
88
|
}
|
|
83
89
|
|
|
@@ -228,7 +234,7 @@ function aggregateComments(
|
|
|
228
234
|
|
|
229
235
|
for (const comment of comments) {
|
|
230
236
|
const normalizedText = normalizeCommentText(comment.text)
|
|
231
|
-
const key = `${comment.scope}|||${normalizedText}`
|
|
237
|
+
const key = `${comment.type}|||${comment.scope}|||${normalizedText}`
|
|
232
238
|
|
|
233
239
|
if (statsMap.has(key)) {
|
|
234
240
|
const stats = statsMap.get(key)!
|
|
@@ -248,6 +254,7 @@ function aggregateComments(
|
|
|
248
254
|
files: [comment.file],
|
|
249
255
|
normalizedText,
|
|
250
256
|
scope: comment.scope,
|
|
257
|
+
type: comment.type,
|
|
251
258
|
weight,
|
|
252
259
|
})
|
|
253
260
|
}
|
|
@@ -314,6 +321,19 @@ function generateMarkdownReport(
|
|
|
314
321
|
lines.push(`- **Total Comments:** ${totalComments}`)
|
|
315
322
|
lines.push(`- **Unique Comment Types:** ${uniqueCommentTypes}`)
|
|
316
323
|
lines.push(`- **Affected Files:** ${affectedFiles}`)
|
|
324
|
+
|
|
325
|
+
// Add breakdown by comment type
|
|
326
|
+
const todoComments = allComments.filter((c) => c.type === "todo")
|
|
327
|
+
const changedComments = allComments.filter((c) => c.type === "changed")
|
|
328
|
+
lines.push("")
|
|
329
|
+
lines.push("### Comment Breakdown")
|
|
330
|
+
lines.push("")
|
|
331
|
+
lines.push(
|
|
332
|
+
`- **TODO Comments:** ${todoComments.length} (requires manual attention)`
|
|
333
|
+
)
|
|
334
|
+
lines.push(
|
|
335
|
+
`- **CHANGED Comments:** ${changedComments.length} (automatic transformations)`
|
|
336
|
+
)
|
|
317
337
|
lines.push("")
|
|
318
338
|
lines.push("### Effort by Difficulty")
|
|
319
339
|
lines.push("")
|
|
@@ -343,6 +363,48 @@ function generateMarkdownReport(
|
|
|
343
363
|
lines.push(`- **${difficulty}:** ${data.count} occurrences`)
|
|
344
364
|
}
|
|
345
365
|
|
|
366
|
+
lines.push("")
|
|
367
|
+
lines.push("## Automatic Changes")
|
|
368
|
+
lines.push("")
|
|
369
|
+
lines.push(
|
|
370
|
+
"The following changes were applied automatically during migration:"
|
|
371
|
+
)
|
|
372
|
+
lines.push("")
|
|
373
|
+
|
|
374
|
+
const changedStats = stats.filter((s) => s.type === "changed")
|
|
375
|
+
|
|
376
|
+
if (changedStats.length > 0) {
|
|
377
|
+
const changedByScope = new Map<string, CommentStats[]>()
|
|
378
|
+
for (const stat of changedStats) {
|
|
379
|
+
if (!changedByScope.has(stat.scope)) {
|
|
380
|
+
changedByScope.set(stat.scope, [])
|
|
381
|
+
}
|
|
382
|
+
changedByScope.get(stat.scope)!.push(stat)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
for (const [scope, scopeStats] of Array.from(changedByScope.entries()).sort(
|
|
386
|
+
(a, b) => {
|
|
387
|
+
const totalA = a[1].reduce((sum, s) => sum + s.count, 0)
|
|
388
|
+
const totalB = b[1].reduce((sum, s) => sum + s.count, 0)
|
|
389
|
+
return totalB - totalA
|
|
390
|
+
}
|
|
391
|
+
)) {
|
|
392
|
+
lines.push(`### ${scope}`)
|
|
393
|
+
lines.push("")
|
|
394
|
+
|
|
395
|
+
for (const stat of scopeStats) {
|
|
396
|
+
lines.push(
|
|
397
|
+
`- **${stat.normalizedText}** (${stat.count} occurrence${stat.count > 1 ? "s" : ""})`
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
lines.push("")
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
lines.push("_No automatic changes were applied._")
|
|
405
|
+
lines.push("")
|
|
406
|
+
}
|
|
407
|
+
|
|
346
408
|
lines.push("")
|
|
347
409
|
lines.push("## Comments by Effort (sorted by total effort)")
|
|
348
410
|
lines.push("")
|
|
@@ -373,7 +435,9 @@ function generateMarkdownReport(
|
|
|
373
435
|
lines.push("")
|
|
374
436
|
|
|
375
437
|
for (const stat of scopeStats) {
|
|
376
|
-
|
|
438
|
+
const typeLabel = stat.type === "changed" ? "CHANGED" : "TODO"
|
|
439
|
+
const typeEmoji = stat.type === "changed" ? "✅" : "⚠️"
|
|
440
|
+
lines.push(`#### ${typeEmoji} [${typeLabel}] ${stat.normalizedText}`)
|
|
377
441
|
lines.push("")
|
|
378
442
|
lines.push(`- **Occurrences:** ${stat.count}`)
|
|
379
443
|
lines.push(`- **Files affected:** ${stat.files.length}`)
|
package/src/shared/types.ts
CHANGED