Skip to content

Commit 2e689d9

Browse files
authored
feat: Codemod: handle legacy Link API (#6907)
* handle legacy Link API by removing a element * handle custom link components
1 parent 859615a commit 2e689d9

File tree

5 files changed

+86
-1
lines changed

5 files changed

+86
-1
lines changed

.storybook-s2/docs/Migrating.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ export function Migrating() {
186186
<H3>Link</H3>
187187
<ul className="sb-unstyled">
188188
<li className={style({font: 'body', marginY: 8})}>Change <Code>variant="overBackground"</Code> to <Code>staticColor="white"</Code></li>
189+
<li className={style({font: 'body', marginY: 8})}>If <Code>a</Code> was used inside <Code>Link</Code> (legacy API), remove the <Code>a</Code> and apply props (i.e <Code>href</Code>) directly to <Code>Link</Code></li>
189190
</ul>
190191

191192
<H3>ListBox</H3>

packages/dev/codemods/src/s1-to-s2/__tests__/__snapshots__/link.test.ts.snap

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,21 @@ let props = {variant: 'overBackground'};
2121
</Link>
2222
</div>"
2323
`;
24+
25+
exports[`Leaves comment if inner link element is a custom router link (deprecated API) 1`] = `
26+
"import { Link } from "@react-spectrum/s2";
27+
import { Link as RouterLink } from "react-router-dom";
28+
29+
<div>
30+
// TODO(S2-upgrade): You may have been using a custom link component here. You'll need to update this manually.
31+
<Link to="https://www.imdb.com/title/tt6348138/">The missing link.</Link>
32+
</div>"
33+
`;
34+
35+
exports[`Remove inner anchor element (deprecated API) 1`] = `
36+
"import { Link } from "@react-spectrum/s2";
37+
38+
<div>
39+
<Link href="https://www.imdb.com/title/tt6348138/" target="_blank">The missing link.</Link>
40+
</div>"
41+
`;

packages/dev/codemods/src/s1-to-s2/__tests__/link.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,28 @@ let props = {variant: 'overBackground'};
2525
</Link>
2626
</div>
2727
`);
28+
29+
test('Remove inner anchor element (deprecated API)', `
30+
import {Link} from '@adobe/react-spectrum';
31+
32+
<div>
33+
<Link>
34+
<a href="https://www.imdb.com/title/tt6348138/" target="_blank">
35+
The missing link.
36+
</a>
37+
</Link>
38+
</div>
39+
`);
40+
41+
test('Leaves comment if inner link element is a custom router link (deprecated API)', `
42+
import {Link} from '@adobe/react-spectrum';
43+
import { Link as RouterLink } from "react-router-dom";
44+
45+
<div>
46+
<Link>
47+
<RouterLink to="https://www.imdb.com/title/tt6348138/">
48+
The missing link.
49+
</RouterLink>
50+
</Link>
51+
</div>
52+
`);

packages/dev/codemods/src/s1-to-s2/src/codemods/changes.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ type FunctionInfo =
7171
| {
7272
name: 'updateAvatarSize',
7373
args: {}
74+
}
75+
| {
76+
name: 'updateLegacyLink',
77+
args: {}
7478
};
7579

7680
type Change = {
@@ -591,6 +595,14 @@ export const changes: ChangesJSON = {
591595
newValue: 'white'
592596
}
593597
}
598+
},
599+
{
600+
description: 'Remove inner anchor element if used (legacy API)',
601+
reason: 'Updated API',
602+
function: {
603+
name: 'updateLegacyLink',
604+
args: {}
605+
}
594606
}
595607
]
596608
},

packages/dev/codemods/src/s1-to-s2/src/codemods/transforms.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,34 @@ function updateAvatarSize(
918918
}
919919
}
920920

921+
/**
922+
* Handles the legacy `Link` API where an `a` tag or custom router component could be used within a `Link` component.
923+
* Removes the inner component and moves its attributes to the `Link` component.
924+
*/
925+
function updateLegacyLink(
926+
path: NodePath<t.JSXElement>
927+
) {
928+
let missingOuterHref = t.isJSXElement(path.node) && !path.node.openingElement.attributes.some((attr) => t.isJSXAttribute(attr) && attr.name.name === 'href');
929+
if (missingOuterHref) {
930+
let innerLink = path.node.children.find((child) => t.isJSXElement(child) && t.isJSXIdentifier(child.openingElement.name));
931+
if (innerLink && t.isJSXElement(innerLink)) {
932+
let innerAttributes = innerLink.openingElement.attributes;
933+
let outerAttributes = path.node.openingElement.attributes;
934+
innerAttributes.forEach((attr) => {
935+
outerAttributes.push(attr);
936+
});
937+
938+
if (
939+
t.isJSXIdentifier(innerLink.openingElement.name) &&
940+
innerLink.openingElement.name.name !== 'a'
941+
) {
942+
addComment(path.node, ' TODO(S2-upgrade): You may have been using a custom link component here. You\'ll need to update this manually.');
943+
}
944+
path.node.children = innerLink.children;
945+
}
946+
}
947+
}
948+
921949
export const functionMap = {
922950
updatePropNameAndValue,
923951
updatePropValueAndAddNewProp,
@@ -936,5 +964,6 @@ export const functionMap = {
936964
convertDimensionValueToPx,
937965
updatePlacementToSingleValue,
938966
removeComponentIfWithinParent,
939-
updateAvatarSize
967+
updateAvatarSize,
968+
updateLegacyLink
940969
};

0 commit comments

Comments
 (0)