Skip to content

Commit e6e1c1b

Browse files
committed
add functionality for arrow functions
1 parent 6bec1d8 commit e6e1c1b

File tree

3 files changed

+212
-44
lines changed

3 files changed

+212
-44
lines changed

community/react/remove-default-props/motions/moveDefaultPropsToFunctionDeclaration.ts

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { JSCodeshift } from 'jscodeshift';
2-
import { Collection } from 'jscodeshift/src/Collection';
1+
import { JSCodeshift, FunctionDeclaration, Collection } from 'jscodeshift';
32

43
export function moveDefaultPropsToFunctionDeclaration(
54
source: Collection<any>,
@@ -32,23 +31,38 @@ export function moveDefaultPropsToFunctionDeclaration(
3231
);
3332
});
3433

35-
// Find the existing destructured props parameter, if any
36-
const existingPropsParam = component.node.params.find(
34+
const params = component.node.params;
35+
36+
const existingSingleProp = params.find(
37+
param => param.type === 'Identifier',
38+
);
39+
40+
// Destructured params
41+
const destructuredProps = params.find(
3742
param => param.type === 'ObjectPattern',
3843
);
3944

40-
// If an existing props parameter was found, extract its properties
41-
const existingPropsProperties =
42-
existingPropsParam && 'properties' in existingPropsParam
43-
? existingPropsParam.properties.map(prop =>
44-
j.property('init', (prop as any).key, (prop as any).value),
45-
)
46-
: [];
45+
const noExistingProps = params.length === 0;
4746

48-
// Generate the new params array by concatenating the existing props properties with the default params
49-
const newParams = [
50-
j.objectPattern(existingPropsProperties.concat(defaultParams)),
51-
];
47+
let newParams: FunctionDeclaration['params'] = [];
48+
49+
if (noExistingProps) {
50+
newParams = [j.objectPattern(defaultParams)];
51+
} else if (existingSingleProp) {
52+
newParams = [
53+
j.objectPattern([
54+
// @ts-ignore
55+
j.spreadProperty(existingSingleProp),
56+
...defaultParams,
57+
]),
58+
];
59+
} else {
60+
newParams = getNewDestructuredParams(
61+
destructuredProps,
62+
j,
63+
defaultParams,
64+
);
65+
}
5266

5367
// Replace the original function declaration with a new one
5468
j(component).replaceWith(nodePath =>
@@ -69,3 +83,38 @@ export function moveDefaultPropsToFunctionDeclaration(
6983
})
7084
.toSource();
7185
}
86+
87+
function getNewDestructuredParams(
88+
existingPropsParam: FunctionDeclaration['params'][number] | undefined,
89+
j: JSCodeshift,
90+
defaultParams: any,
91+
) {
92+
if (existingPropsParam && 'properties' in existingPropsParam) {
93+
const restProp = existingPropsParam.properties.find(
94+
// @ts-expect-error for some reason it does not exist
95+
prop => prop.type === 'RestElement',
96+
);
97+
98+
const existingPropsDestructuredProps = existingPropsParam.properties
99+
.filter(prop => prop.type !== restProp?.type)
100+
.map(prop => j.property('init', (prop as any).key, (prop as any).value))
101+
.filter(Boolean);
102+
103+
const restPropArg =
104+
restProp && 'argument' in restProp
105+
? restProp.argument
106+
: j.identifier('rest');
107+
108+
const newParams = [
109+
j.objectPattern([
110+
...existingPropsDestructuredProps,
111+
...defaultParams,
112+
// @ts-expect-error RestElement is not assignable as above
113+
...(restProp ? [j.restProperty(restPropArg)] : []),
114+
]),
115+
];
116+
return newParams;
117+
}
118+
119+
return [];
120+
}

community/react/remove-default-props/transform.spec.ts

Lines changed: 110 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,129 @@ import { applyTransform } from '@codeshift/test-utils';
22
import * as transformer from './transform';
33

44
describe('react#remove-default-props transform', () => {
5-
// it('should remove default props', async () => {
5+
// describe('components with function declaration', () => {
6+
// it('should move default props to function declaration when there no existing props', async () => {
67
// const result = await applyTransform(
78
// transformer,
89
// `
9-
// import React from 'react';
10+
// import React from 'react';
11+
// export function Greet(){ <span>Hi {name}</span>; }
12+
// Greet.defaultProps = { name: 'Stranger' };
13+
// `,
14+
// { parser: 'tsx' },
15+
// );
16+
// expect(result).toMatchInlineSnapshot(`
17+
// import React from 'react'; export function Greet( { name: name = "Stranger"
18+
// } ) {
19+
// <span>Hi {name}</span>; }
20+
// `);
21+
// });
22+
// it('should remove default props and move them to intended place preserving other props', async () => {
23+
// const result = await applyTransform(
24+
// transformer,
25+
// `
26+
// import React from 'react';
27+
// export function Greet({text}){ <span>Hi {name} {text}</span>; }
28+
// Greet.defaultProps = { name: 'Stranger' };
29+
// `,
30+
// { parser: 'tsx' },
31+
// );
32+
// expect(result).toMatchInlineSnapshot(`
33+
// import React from 'react'; export function Greet( { text: text, name:
34+
// name = "Stranger" } ) {
35+
// <span>Hi {name} {text}</span>; }
36+
// `);
37+
// });
1038

11-
// export const Greet = ({ name }) => <span>Hi {name}</span>;
39+
// it('should remove default props and move them to intended place preserving other props even with destructured renamed props', async () => {
40+
// const result = await applyTransform(
41+
// transformer,
42+
// `
43+
// import React from 'react';
44+
// export function Greet({text:myText}){ <span>Hi {name} {text}</span>; }
1245
// Greet.defaultProps = { name: 'Stranger' };
1346
// `,
1447
// { parser: 'tsx' },
1548
// );
16-
1749
// expect(result).toMatchInlineSnapshot(`
18-
// import React from 'react'; export const Greet = ({ name }) =>
19-
// <span>Hi {name}</span>;
20-
// `);
50+
// import React from 'react'; export function Greet( { text: myText, name:
51+
// name = "Stranger" } ) {
52+
// <span>Hi {name} {text}</span>; }
53+
// `);
2154
// });
2255

23-
it('should replace default props for function component', async () => {
24-
const result = await applyTransform(
25-
transformer,
26-
`
27-
// import React from 'react';
28-
// export const Greet = ({ name }) => <span>Hi {name}</span>;
29-
// Greet.defaultProps = { name: 'Stranger' };
30-
`,
31-
{ parser: 'tsx' },
32-
);
33-
34-
expect(result).toMatchInlineSnapshot(`
35-
// import React from 'react'; // export const Greet = ({ name }) =>
36-
<span>Hi {name}</span>; // Greet.defaultProps = { name: 'Stranger' };
37-
`);
38-
});
56+
// it('preserves default values for destructured components', async () => {
57+
// const result = await applyTransform(
58+
// transformer,
59+
// `
60+
// import React from 'react';
61+
// export function Greet({text:myText, props='amazingText'}){ <span>Hi {name} {text}</span>; }
62+
// Greet.defaultProps = { name: 'Stranger' };
63+
// `,
64+
// { parser: 'tsx' },
65+
// );
66+
// expect(result).toMatchInlineSnapshot(`
67+
// import React from 'react'; export function Greet( { text: myText, props:
68+
// props='amazingText', name: name = "Stranger" } ) {
69+
// <span>Hi {name} {text}</span>; }
70+
// `);
71+
// });
3972

40-
// it('should replace default props for arrow function component', async () => {
41-
// const result = await applyTransform(transformer, input2, { parser: 'tsx' });
73+
// it(' with rest pworksarameters', async () => {
74+
// const result = await applyTransform(
75+
// transformer,
76+
// `
77+
// import React from 'react';
78+
// export function Greet({prop1,...someRest}){ <span>Hi {name} {text}</span>; }
79+
// Greet.defaultProps = { name: 'Stranger' };
80+
// `,
81+
// { parser: 'tsx' },
82+
// );
83+
// expect(result).toMatchInlineSnapshot(`
84+
// import React from 'react'; export function Greet( { prop1: prop1, name:
85+
// name = "Stranger", ...someRest } ) {
86+
// <span>Hi {name} {text}</span>; }
87+
// `);
88+
// });
4289

43-
// expect(result).toMatchInlineSnapshot(`
44-
// import React from 'react'; export const Component = () => { return
45-
// <span>Hi {name}</span>; };
90+
// it('works with any props parameter passed', async () => {
91+
// const result = await applyTransform(
92+
// transformer,
93+
// `
94+
// import React from 'react';
95+
// export function Greet(props){ <span>Hi {name} {text}</span>; }
96+
// Greet.defaultProps = { name: 'Stranger' };
97+
// `,
98+
// { parser: 'tsx' },
99+
// );
100+
// expect(result).toMatchInlineSnapshot(`
101+
// import React from 'react'; export function Greet( { ...props, name: name
102+
// = "Stranger" } ) {
103+
// <span>Hi {name} {text}</span>; }
46104
// `);
105+
// });
47106
// });
107+
108+
describe('components with as arrow functions', () => {
109+
it('should remove default props', async () => {
110+
expect(
111+
await applyTransform(
112+
transformer,
113+
`
114+
import React from 'react';
115+
export const Greet = ({ name }) => <span>Hi {name}</span>;
116+
Greet.defaultProps = { text: 'Stranger' };
117+
`,
118+
{ parser: 'tsx' },
119+
),
120+
).toMatchInlineSnapshot(`
121+
"import React from 'react';
122+
export const Greet = (
123+
{
124+
text: text = \\"Stranger\\"
125+
}
126+
) => {};"
127+
`);
128+
});
129+
});
48130
});

community/react/remove-default-props/transform.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { FileInfo, API } from 'jscodeshift';
1+
import { FileInfo, API, Collection, JSCodeshift } from 'jscodeshift';
22
import { moveDefaultPropsToFunctionDeclaration } from './motions/moveDefaultPropsToFunctionDeclaration';
33
import { removeDefaultPropsAssignment } from './motions/removeDefaultPropsAssignment';
44

@@ -9,8 +9,45 @@ export default function transformer(file: FileInfo, api: API) {
99

1010
// Find all function declarations with name "Component"
1111
moveDefaultPropsToFunctionDeclaration(source, j);
12+
arrowFunctions(source, j);
1213

1314
removeDefaultPropsAssignment(source, j);
1415

1516
return source.toSource();
1617
}
18+
19+
export function arrowFunctions(source: Collection<any>, j: JSCodeshift) {
20+
source.find(j.VariableDeclarator).forEach(component => {
21+
const defaultProps = source.find(j.AssignmentExpression, {
22+
left: {
23+
// @ts-ignore
24+
object: { name: component.node.id?.name },
25+
property: { name: 'defaultProps' },
26+
},
27+
});
28+
29+
const defaultValues = defaultProps.get('right').value.properties;
30+
31+
if (defaultProps.length === 0) return;
32+
33+
// Generate a new function parameter for each default prop
34+
const defaultParams = defaultValues.map((prop: any) => {
35+
const key = prop.key.name;
36+
const value = prop.value.value;
37+
return j.objectProperty(
38+
j.identifier(key),
39+
j.assignmentPattern(j.identifier(key), j.literal(value)),
40+
);
41+
});
42+
43+
const newParams = [j.objectPattern(defaultParams)];
44+
45+
j(component).replaceWith(
46+
j.variableDeclarator(
47+
// @ts-ignore
48+
j.identifier(component.node.id.name),
49+
j.arrowFunctionExpression([...newParams], j.blockStatement([])),
50+
),
51+
);
52+
});
53+
}

0 commit comments

Comments
 (0)