Skip to content

Commit 3b338a6

Browse files
Merge pull request #154 from pawk3k/EIWFY23Q3-16-remove-default-props-react
feat: enhance remove default props react
2 parents bc346bd + 67d5d36 commit 3b338a6

File tree

7 files changed

+452
-37
lines changed

7 files changed

+452
-37
lines changed

community/react/remove-default-props/README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,26 @@ _Credit_: [https://github.com/reactjs/react-codemod](https://github.com/reactjs/
66

77
```jsx
88
/* INPUT */
9-
import React from 'react'
9+
import React from 'react';
1010

11-
export const Greet = ({ name }) => <span>Hi {name}</span>
12-
Greet.defaultProps = { name: 'Stranger' }
11+
export const Greet = ({ name }) => <span>Hi {name}</span>;
12+
Greet.defaultProps = { text: 'Stranger' };
1313

1414
/* OUTPUT */
15-
import React from 'react'
15+
import React from 'react';
1616

17-
export const Greet = ({ name }) => <span>Hi {name}</span>
17+
export const Greet = ({ name, text = 'Stranger' }) => <span>Hi {name}</span>;
1818
```
19+
20+
```jsx
21+
/* INPUT */
22+
import React from 'react';
23+
24+
export const Greet = (props) => <span>Hi {name}</span>;
25+
Greet.defaultProps = { text: 'Stranger' };
26+
27+
/* OUTPUT */
28+
import React from 'react';
29+
30+
export const Greet = ({ ...props, text = 'Stranger' }) => <span>Hi {name}</span>;
31+
```
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import {
2+
JSCodeshift,
3+
FunctionDeclaration,
4+
ASTPath,
5+
ArrowFunctionExpression,
6+
} from 'jscodeshift';
7+
8+
export function getNewParams(
9+
j: JSCodeshift,
10+
component: ASTPath<FunctionDeclaration | ArrowFunctionExpression>,
11+
defaultParams: any,
12+
) {
13+
const params = component.node.params;
14+
15+
const existingSingleProp = params.find(param => param.type === 'Identifier');
16+
17+
const destructuredProps = params.find(
18+
param => param.type === 'ObjectPattern',
19+
);
20+
21+
const noExistingProps = params.length === 0;
22+
23+
let newParams: FunctionDeclaration['params'] = [];
24+
25+
if (noExistingProps) {
26+
newParams = [j.objectPattern(defaultParams)];
27+
} else if (existingSingleProp) {
28+
newParams = [
29+
j.objectPattern([
30+
// @ts-ignore
31+
j.spreadProperty(existingSingleProp),
32+
...defaultParams,
33+
]),
34+
];
35+
} else {
36+
newParams = getNewDestructuredParams(destructuredProps, j, defaultParams);
37+
}
38+
return newParams;
39+
}
40+
function getNewDestructuredParams(
41+
existingPropsParam: FunctionDeclaration['params'][number] | undefined,
42+
j: JSCodeshift,
43+
defaultParams: any,
44+
) {
45+
if (existingPropsParam && 'properties' in existingPropsParam) {
46+
const restProp = existingPropsParam.properties.find(
47+
// @ts-expect-error for some reason it does not exist
48+
prop => prop.type === 'RestElement',
49+
);
50+
51+
const existingPropsDestructuredProps = existingPropsParam.properties
52+
.filter(prop => prop.type !== restProp?.type)
53+
.map(prop => j.property('init', (prop as any).key, (prop as any).value))
54+
.filter(Boolean);
55+
56+
const restPropArg =
57+
restProp && 'argument' in restProp
58+
? restProp.argument
59+
: j.identifier('rest');
60+
61+
const newParams = [
62+
j.objectPattern([
63+
...existingPropsDestructuredProps,
64+
...defaultParams,
65+
// @ts-expect-error RestElement is not assignable as above
66+
...(restProp ? [j.restProperty(restPropArg)] : []),
67+
]),
68+
];
69+
return newParams;
70+
}
71+
72+
return [];
73+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { Collection, JSCodeshift } from 'jscodeshift';
2+
import { getNewParams } from './getNewParams';
3+
4+
export function moveDefaultPropsToArrowFunctionExpression(
5+
j: JSCodeshift,
6+
source: Collection<any>,
7+
) {
8+
source.find(j.VariableDeclarator).forEach(component => {
9+
const defaultProps = source.find(j.AssignmentExpression, {
10+
left: {
11+
// @ts-ignore
12+
object: { name: component.node.id?.name },
13+
property: { name: 'defaultProps' },
14+
},
15+
});
16+
17+
const defaultValues = defaultProps.get('right').value.properties;
18+
19+
if (defaultProps.length === 0) return;
20+
21+
// Generate a new function parameter for each default prop
22+
const defaultParams = defaultValues.map((prop: any) => {
23+
const key = prop.key.name;
24+
const value = prop.value.value;
25+
return j.objectProperty(
26+
j.identifier(key),
27+
j.assignmentPattern(j.identifier(key), j.literal(value)),
28+
);
29+
});
30+
31+
const arrowFunction = component.get('init');
32+
33+
const newParams = getNewParams(j, arrowFunction, defaultParams);
34+
35+
j(component).replaceWith(
36+
j.variableDeclarator(
37+
// @ts-ignore
38+
j.identifier(component.node.id.name),
39+
j.arrowFunctionExpression(newParams!, j.blockStatement([])),
40+
),
41+
);
42+
});
43+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { JSCodeshift, Collection } from 'jscodeshift';
2+
import { getNewParams } from './getNewParams';
3+
4+
export function moveDefaultPropsToFunctionDeclaration(
5+
j: JSCodeshift,
6+
source: Collection<any>,
7+
) {
8+
source
9+
.find(j.FunctionDeclaration)
10+
.forEach(component => {
11+
const defaultProps = source.find(j.AssignmentExpression, {
12+
left: {
13+
object: { name: component.node.id?.name },
14+
property: { name: 'defaultProps' },
15+
},
16+
});
17+
18+
if (defaultProps.length === 0) return;
19+
20+
// Extract the default props object
21+
const defaultValues = defaultProps.get('right').value.properties;
22+
23+
// Generate a new function parameter for each default prop
24+
const defaultParams = defaultValues.map((prop: any) => {
25+
const key = prop.key.name;
26+
const value = prop.value.value;
27+
// return j.objectPattern(`${key}=${JSON.stringify(value)}`);
28+
return j.objectProperty(
29+
j.identifier(key),
30+
j.assignmentPattern(j.identifier(key), j.literal(value)),
31+
);
32+
});
33+
// Find the defaultProps assignment expression
34+
const newParams = getNewParams(j, component, defaultParams);
35+
// Replace the original function declaration with a new one
36+
j(component).replaceWith(nodePath =>
37+
j.functionDeclaration(
38+
nodePath.node.id,
39+
newParams!,
40+
nodePath.node.body,
41+
nodePath.node.generator,
42+
nodePath.node.async,
43+
),
44+
);
45+
})
46+
.find(j.AssignmentExpression, {
47+
left: {
48+
object: { type: 'Identifier' },
49+
property: { name: 'defaultProps' },
50+
},
51+
})
52+
.toSource();
53+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { JSCodeshift } from 'jscodeshift';
2+
import { Collection } from 'jscodeshift/src/Collection';
3+
4+
export function removeDefaultPropsAssignment(
5+
j: JSCodeshift,
6+
source: Collection<any>,
7+
) {
8+
const removePath = (path: any) => j(path).remove();
9+
const isAssigningDefaultProps = (e: any) =>
10+
e.node.left &&
11+
e.node.left.property &&
12+
e.node.left.property.name === 'defaultProps';
13+
14+
return source
15+
.find(j.AssignmentExpression)
16+
.filter(isAssigningDefaultProps)
17+
.forEach(removePath);
18+
}

0 commit comments

Comments
 (0)