Skip to content

Commit 8cf415e

Browse files
authored
Merge pull request #62 from codemod-com/improve_replace_default_props
chore(codemods/react/19/replace-default-props): sync React 19 replace-default-props improvements
2 parents 06e012e + 737ffe4 commit 8cf415e

11 files changed

Lines changed: 202 additions & 57 deletions

codemods/react/19/replace-default-props/.codemodrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://codemod-utils.s3.us-west-1.amazonaws.com/configuration_schema.json",
33
"name": "react/19/replace-default-props",
4-
"version": "1.0.3",
4+
"version": "1.0.5",
55
"engine": "jscodeshift",
66
"private": false,
77
"arguments": [],

codemods/react/19/replace-default-props/__testfixtures__/button-jsx-example-input.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ const Button = ({ size, color }) => {
55
Button.defaultProps = {
66
size: "16px",
77
color: "blue",
8-
};
8+
};
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
const Button = ({ size = "16px", color = "blue" }) => {
22
return <button style={{ color, fontSize: size }}>Click me</button>;
3-
};
3+
};

codemods/react/19/replace-default-props/__testfixtures__/nested-destructuring.output.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
const Card = ({ user: { name, age } = {
1+
const cardDefaultPropUser = {
22
name: "Unknown",
33
age: 0,
4-
} }) => {
4+
};
5+
6+
const Card = ({ user: { name, age } = cardDefaultPropUser }) => {
57
return (
68
<div>
79
<p>{name}</p>
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const C = (props) => {
2+
console.log(props.helloWorld);
23
return <>{props.text}</>;
34
};
45

56
C.defaultProps = {
6-
text: "test",
7+
text: "Hello",
8+
test: 2,
9+
helloWorld: true,
710
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
const C = (props) => {
2+
props = {
3+
...props,
4+
text: typeof props.text === "undefined" ? "Hello" : props.text,
5+
test: typeof props.test === "undefined" ? 2 : props.test,
6+
helloWorld: typeof props.helloWorld === "undefined" ? true : props.helloWorld
7+
};
8+
9+
console.log(props.helloWorld);
210
return <>{props.text}</>;
311
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const MyComp = ({foo, ...props}) => {
2+
console.log(props.bar)
3+
}
4+
5+
MyComp.defaultProps = { foo: "hello", bar: "bye", test: 2 };
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const MyComp = ({foo = "hello", ...props}) => {
2+
props = {
3+
...props,
4+
bar: typeof props.bar === "undefined" ? "bye" : props.bar,
5+
test: typeof props.test === "undefined" ? 2 : props.test
6+
};
7+
8+
console.log(props.bar)
9+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
const List = ({ items = [], renderItem = (item) => <li key={item}>{item}</li> }) => {
1+
const listDefaultPropItems = [];
2+
const listDefaultPropRenderItem = (item) => <li key={item}>{item}</li>;
3+
const List = ({ items = listDefaultPropItems, renderItem = listDefaultPropRenderItem }) => {
24
return <ul>{items.map(renderItem)}</ul>;
35
};

codemods/react/19/replace-default-props/src/index.ts

Lines changed: 102 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@ import type {
77
MemberExpression,
88
ObjectProperty,
99
Property,
10+
VariableDeclaration,
11+
RestElement,
1012
} from "jscodeshift";
1113

12-
import { getFunctionName } from "@codemod.com/codemod-utils/dist/jscodeshift/function.js";
13-
import { getFunctionComponents } from "@codemod.com/codemod-utils/dist/jscodeshift/react.js";
14+
import {
15+
getFunctionName,
16+
getFunctionComponents,
17+
} from "@codemod.com/codemod-utils";
1418

1519
const getComponentStaticPropValue = (
1620
j: JSCodeshift,
1721
root: Collection<any>,
1822
componentName: string,
19-
name: string,
23+
name: string
2024
): ASTPath<MemberExpression> | null => {
2125
return (
2226
root
@@ -38,9 +42,8 @@ const getComponentStaticPropValue = (
3842
const buildPropertyWithDefaultValue = (
3943
j: JSCodeshift,
4044
property: ObjectProperty | Property,
41-
defaultValue: any,
45+
defaultValue: any
4246
) => {
43-
// Special handling for nested destructuring patterns
4447
if (property.value.type === "ObjectPattern") {
4548
return j.assignmentPattern(property.value, defaultValue);
4649
}
@@ -55,7 +58,7 @@ const buildPropertyWithDefaultValue = (
5558

5659
export default function transform(
5760
file: FileInfo,
58-
api: API,
61+
api: API
5962
): string | undefined {
6063
const j = api.jscodeshift;
6164
const root = j(file.source);
@@ -68,11 +71,21 @@ export default function transform(
6871
return;
6972
}
7073

74+
const componentFunction = j.functionDeclaration(
75+
j.identifier(componentName),
76+
path.value.params,
77+
path.value.body
78+
);
79+
80+
if (componentFunction === null) {
81+
return;
82+
}
83+
7184
const defaultProps = getComponentStaticPropValue(
7285
j,
7386
root,
7487
componentName,
75-
"defaultProps",
88+
"defaultProps"
7689
);
7790

7891
const defaultPropsRight = defaultProps?.parent?.value?.right ?? null;
@@ -82,17 +95,46 @@ export default function transform(
8295
}
8396

8497
const defaultPropsMap = new Map();
98+
const defaultPropsConstants: VariableDeclaration[] = [];
8599

86100
defaultPropsRight.properties?.forEach((property) => {
87101
if (
88102
(j.Property.check(property) || j.ObjectProperty.check(property)) &&
89103
j.Identifier.check(property.key)
90104
) {
91-
defaultPropsMap.set(property.key.name, property.value);
105+
if (
106+
property.value.type === "ObjectExpression" ||
107+
property.value.type === "ArrayExpression" ||
108+
property.value.type === "ArrowFunctionExpression"
109+
) {
110+
const constName = `${componentName[0]?.toLowerCase()}${componentName.slice(
111+
1
112+
)}DefaultProp${
113+
property.key.name[0]?.toUpperCase() + property.key.name.slice(1)
114+
}`;
115+
const constNamePath = root
116+
.find(j.Identifier, {
117+
name: constName,
118+
})
119+
.paths();
120+
if (constNamePath.length) {
121+
return defaultPropsMap.set(property.key.name, property.value);
122+
}
123+
defaultPropsConstants.push(
124+
j.variableDeclaration("const", [
125+
j.variableDeclarator(j.identifier(constName), property.value),
126+
])
127+
);
128+
defaultPropsMap.set(property.key.name, j.identifier(constName));
129+
} else {
130+
defaultPropsMap.set(property.key.name, property.value);
131+
}
92132
}
93133
});
94134

95135
const propsArg = path.value.params.at(0);
136+
let inlineDefaultProps: { key: string; value: any }[] = [];
137+
let propsArgName: string | undefined;
96138

97139
if (j.ObjectPattern.check(propsArg)) {
98140
propsArg.properties.forEach((property) => {
@@ -105,11 +147,62 @@ export default function transform(
105147
property.value = buildPropertyWithDefaultValue(
106148
j,
107149
property,
108-
defaultPropsMap.get(property.key.name),
150+
defaultPropsMap.get(property.key.name)
151+
);
152+
defaultPropsMap.delete(property.key.name);
153+
}
154+
} else if (j.RestElement.check(property)) {
155+
const restElement = property as RestElement;
156+
if (j.Identifier.check(restElement.argument)) {
157+
propsArgName = restElement.argument.name;
158+
inlineDefaultProps = Array.from(defaultPropsMap.entries()).map(
159+
([key, value]) => ({ key, value })
109160
);
110161
}
111162
}
112163
});
164+
} else if (j.Identifier.check(propsArg)) {
165+
propsArgName = propsArg.name;
166+
inlineDefaultProps = Array.from(defaultPropsMap.entries()).map(
167+
([key, value]) => ({ key, value })
168+
);
169+
}
170+
171+
if (propsArgName && inlineDefaultProps.length) {
172+
componentFunction.body.body.unshift(
173+
j.expressionStatement(
174+
j.assignmentExpression(
175+
"=",
176+
j.identifier(propsArgName),
177+
j.objectExpression([
178+
j.spreadElement(j.identifier(propsArgName)),
179+
...inlineDefaultProps.map(({ key, value }) =>
180+
j.objectProperty(
181+
j.identifier(key),
182+
j.conditionalExpression(
183+
j.binaryExpression(
184+
"===",
185+
j.unaryExpression(
186+
"typeof",
187+
j.identifier(`${propsArgName}.${key}`)
188+
),
189+
j.literal("undefined")
190+
),
191+
value,
192+
j.identifier(`${propsArgName}.${key}`)
193+
)
194+
)
195+
),
196+
])
197+
)
198+
)
199+
);
200+
}
201+
202+
if (defaultPropsConstants.length && path.parent) {
203+
for (let defaultPropsConstant of defaultPropsConstants) {
204+
path.parent.parent.insertBefore(defaultPropsConstant);
205+
}
113206
}
114207

115208
j(defaultProps).closest(j.ExpressionStatement).remove();

0 commit comments

Comments
 (0)