@commercetools-frontend/codemod 22.37.0 → 22.38.1
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 +38 -0
- package/build/package.json +2 -1
- package/build/src/cli.js +14 -7
- package/build/src/transforms/react-default-props-migration.js +405 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -49,3 +49,41 @@ Remove code related to the old design when using the `useTheme` hook, for exampl
|
|
|
49
49
|
```
|
|
50
50
|
$ npx @commercetools-frontend/codemod@latest redesign-cleanup 'src/**/*.{jsx,tsx}'
|
|
51
51
|
```
|
|
52
|
+
|
|
53
|
+
### `react-default-props-migration`
|
|
54
|
+
|
|
55
|
+
Migrates the way React Components `defaultProps` to use JavaScript default parameters instead. This is needed for React v18 or later.
|
|
56
|
+
Example:
|
|
57
|
+
|
|
58
|
+
```jsx
|
|
59
|
+
// BEFORE
|
|
60
|
+
function MyComponent(props) {
|
|
61
|
+
return (
|
|
62
|
+
<ul>
|
|
63
|
+
<li>Prop 1: {props.prop1}</li>
|
|
64
|
+
<li>Prop 2: {props.prop2}</li>
|
|
65
|
+
<li>Prop 3: {props.prop3}</li>
|
|
66
|
+
</ul>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
MyComponent.defaultProps = {
|
|
70
|
+
prop1: 'My default value',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// AFTER
|
|
74
|
+
function MyComponent({ prop1: 'My default value', ...props }) {
|
|
75
|
+
return (
|
|
76
|
+
<ul>
|
|
77
|
+
<li>Prop 1: {prop1}</li>
|
|
78
|
+
<li>Prop 2: {props.prop2}</li>
|
|
79
|
+
<li>Prop 3: {props.prop3}</li>
|
|
80
|
+
</ul>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
You can run this codemod by using the following command:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
$ npx @commercetools-frontend/codemod@latest react-default-props-migration 'src/**/*.{jsx,tsx}'
|
|
89
|
+
```
|
package/build/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercetools-frontend/codemod",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.38.1",
|
|
4
4
|
"description": "Codemod transformations for Custom Applications",
|
|
5
5
|
"bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
|
|
6
6
|
"repository": {
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"@tsconfig/node16": "^16.1.1",
|
|
38
38
|
"@types/glob": "8.1.0",
|
|
39
39
|
"@types/jscodeshift": "0.11.11",
|
|
40
|
+
"@types/prettier": "2.7.3",
|
|
40
41
|
"rimraf": "5.0.7",
|
|
41
42
|
"typescript": "5.0.4"
|
|
42
43
|
},
|
package/build/src/cli.js
CHANGED
|
@@ -28,26 +28,33 @@ const transforms = [
|
|
|
28
28
|
name: 'redesign-cleanup',
|
|
29
29
|
description: 'Remove code related to the old design when using the "useTheme" hook, for example the usage of "themedValue".',
|
|
30
30
|
},
|
|
31
|
+
{
|
|
32
|
+
name: 'react-default-props-migration',
|
|
33
|
+
description: 'Migrate React components using defaultProps as a component property to a destructured object param.',
|
|
34
|
+
},
|
|
31
35
|
];
|
|
32
36
|
const executeCodemod = async (transform, globPattern, globalOptions) => {
|
|
33
|
-
const
|
|
37
|
+
const absoluteGlobPattern = path_1.default.resolve(globPattern);
|
|
38
|
+
const files = glob_1.default.sync(path_1.default.join(absoluteGlobPattern, '**/*.{ts,tsx,js,jsx}'), {
|
|
39
|
+
ignore: [
|
|
40
|
+
'**/node_modules/**',
|
|
41
|
+
'**/public/**',
|
|
42
|
+
'**/dist/**',
|
|
43
|
+
'**/build/**',
|
|
44
|
+
],
|
|
45
|
+
});
|
|
34
46
|
const runJscodeshift = async (transformPath, filePaths, options) => {
|
|
35
47
|
await Runner_1.default.run(transformPath, filePaths, options);
|
|
36
48
|
};
|
|
37
49
|
switch (transform) {
|
|
38
50
|
case 'redesign-cleanup':
|
|
51
|
+
case 'react-default-props-migration':
|
|
39
52
|
case 'remove-deprecated-modal-level-props':
|
|
40
53
|
case 'rename-js-to-jsx':
|
|
41
54
|
case 'rename-mod-css-to-module-css': {
|
|
42
55
|
const transformPath = path_1.default.join(__dirname, `transforms/${transform}.js`);
|
|
43
56
|
await runJscodeshift(transformPath, files, {
|
|
44
57
|
extensions: 'tsx,ts,jsx,js',
|
|
45
|
-
ignorePattern: [
|
|
46
|
-
'**/node_modules/**',
|
|
47
|
-
'**/public/**',
|
|
48
|
-
'**/dist/**',
|
|
49
|
-
'**/build/**',
|
|
50
|
-
],
|
|
51
58
|
parser: 'tsx',
|
|
52
59
|
verbose: 0,
|
|
53
60
|
dry: globalOptions.dryRun,
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const prettier_1 = __importDefault(require("prettier"));
|
|
7
|
+
// When adjusting the component body where the previous props object was used
|
|
8
|
+
// we don't need to adjust the function calls that are listed here
|
|
9
|
+
const IGNORED_FUNCTIONS_ON_BODY_ADJUSTMENTS = [
|
|
10
|
+
'filterAriaAttributes',
|
|
11
|
+
'filterDataAttributes',
|
|
12
|
+
];
|
|
13
|
+
/*
|
|
14
|
+
Given the component function parameter description, we extract the
|
|
15
|
+
Typescript type name (if any).
|
|
16
|
+
|
|
17
|
+
Somethibng like this:
|
|
18
|
+
(props: MyComponentProps) -> MyComponentProps
|
|
19
|
+
*/
|
|
20
|
+
function resolvePropsTypescriptType(propsParam) {
|
|
21
|
+
if (propsParam.type === 'ObjectPattern' &&
|
|
22
|
+
propsParam.typeAnnotation &&
|
|
23
|
+
propsParam.typeAnnotation.type === 'TSTypeAnnotation' &&
|
|
24
|
+
propsParam.typeAnnotation.typeAnnotation.type === 'TSTypeReference' &&
|
|
25
|
+
propsParam.typeAnnotation.typeAnnotation.typeName.type === 'Identifier') {
|
|
26
|
+
return propsParam.typeAnnotation.typeAnnotation.typeName.name;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
/*
|
|
31
|
+
This helper takes care of replacing the defaultProps usage in the component body
|
|
32
|
+
Previously the component code was relying on the props object to access the default values
|
|
33
|
+
but now the default props are destructured in the function signature
|
|
34
|
+
Example:
|
|
35
|
+
```
|
|
36
|
+
// BEFORE
|
|
37
|
+
const MyComponent = (props) => {
|
|
38
|
+
return <div>{props.prop1}</div>;
|
|
39
|
+
}
|
|
40
|
+
// AFTER
|
|
41
|
+
const MyComponent = ({ prop1 }) => {
|
|
42
|
+
return <div>{prop1}</div>;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
*/
|
|
46
|
+
function replacePropsUsage({ j, defaultPropsKeys, scope, }) {
|
|
47
|
+
/*
|
|
48
|
+
Next code block replaces destructured props usage in the component body.
|
|
49
|
+
```
|
|
50
|
+
// BEFORE
|
|
51
|
+
const MyComponent = (props) => {
|
|
52
|
+
return <div>{props.prop1}</div>;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// AFTER
|
|
57
|
+
const MyComponent = ({ prop1, ...props }) => {
|
|
58
|
+
return <div>{prop1}</div>;
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
*/
|
|
62
|
+
scope
|
|
63
|
+
.find(j.MemberExpression, {
|
|
64
|
+
object: { type: 'Identifier', name: 'props' },
|
|
65
|
+
})
|
|
66
|
+
.forEach((memberPath) => {
|
|
67
|
+
const property = memberPath.node.property;
|
|
68
|
+
// Add type guard for Identifier
|
|
69
|
+
if (property.type === 'Identifier' &&
|
|
70
|
+
defaultPropsKeys.includes(property.name)) {
|
|
71
|
+
j(memberPath).replaceWith(j.identifier(property.name));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
/*
|
|
75
|
+
Next code block replaces props usage in the component body where
|
|
76
|
+
props is passed as an argument to a function.
|
|
77
|
+
```
|
|
78
|
+
// BEFORE
|
|
79
|
+
const MyComponent = (props) => {
|
|
80
|
+
return <div>{getStyles(props)}</div>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// AFTER
|
|
84
|
+
const MyComponent = ({ prop1, ...props }) => {
|
|
85
|
+
return <div>{getStyles({ prop1, ...props })}</div>;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
*/
|
|
89
|
+
scope
|
|
90
|
+
.find(j.CallExpression, {
|
|
91
|
+
// There are some functions that don't need to be adjusted
|
|
92
|
+
// (example: filterAriaAttributes, filterDataAttributes)
|
|
93
|
+
callee: {
|
|
94
|
+
type: 'Identifier',
|
|
95
|
+
name: (name) => !IGNORED_FUNCTIONS_ON_BODY_ADJUSTMENTS.includes(name),
|
|
96
|
+
},
|
|
97
|
+
arguments: [{ type: 'Identifier', name: 'props' }],
|
|
98
|
+
})
|
|
99
|
+
.forEach((callPath) => {
|
|
100
|
+
// Create a destructured object
|
|
101
|
+
const properties = [
|
|
102
|
+
...defaultPropsKeys.map((key) => {
|
|
103
|
+
const id = j.identifier(key);
|
|
104
|
+
const newProp = j.property('init', id, id);
|
|
105
|
+
newProp.shorthand = true;
|
|
106
|
+
return newProp;
|
|
107
|
+
}),
|
|
108
|
+
j.spreadElement(j.identifier('props')),
|
|
109
|
+
];
|
|
110
|
+
const objectExpression = j.objectExpression(properties);
|
|
111
|
+
// Replace the 'props' argument with the destructured object
|
|
112
|
+
callPath.node.arguments[0] = objectExpression;
|
|
113
|
+
});
|
|
114
|
+
/*
|
|
115
|
+
Next code block replaces props spread in JSX elements
|
|
116
|
+
```
|
|
117
|
+
// BEFORE
|
|
118
|
+
const MyComponent = (props) => {
|
|
119
|
+
return <SubComponent {...props} />
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// AFTER
|
|
123
|
+
const MyComponent = ({ prop1, ...props }) => {
|
|
124
|
+
return <SubComponent prop1={prop1} prop2={prop2} {...props} />
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
*/
|
|
128
|
+
scope
|
|
129
|
+
.find(j.JSXSpreadAttribute, {
|
|
130
|
+
argument: { type: 'Identifier', name: 'props' },
|
|
131
|
+
})
|
|
132
|
+
.forEach((path) => {
|
|
133
|
+
const attributes = defaultPropsKeys.map((key) => j.jsxAttribute(j.jsxIdentifier(key), j.jsxExpressionContainer(j.identifier(key))));
|
|
134
|
+
// Replace the spread with individual attributes
|
|
135
|
+
j(path).replaceWith([
|
|
136
|
+
...attributes,
|
|
137
|
+
j.jsxSpreadAttribute(j.identifier('props')),
|
|
138
|
+
]);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/*
|
|
142
|
+
We need to make sure the component type definition is updated to reflect the
|
|
143
|
+
props that are now optional.
|
|
144
|
+
Example:
|
|
145
|
+
```
|
|
146
|
+
// BEFORE
|
|
147
|
+
type MyComponentProps = {
|
|
148
|
+
prop1: string;
|
|
149
|
+
prop2: string;
|
|
150
|
+
prop3: string;
|
|
151
|
+
}
|
|
152
|
+
function MyComponent(props: MyComponentProps) { ... }
|
|
153
|
+
MyComponent.defaultProps = {
|
|
154
|
+
prop1: 'default value',
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// AFTER
|
|
158
|
+
type MyComponentProps = {
|
|
159
|
+
prop1?: string;
|
|
160
|
+
prop2: string;
|
|
161
|
+
prop3: string;
|
|
162
|
+
}
|
|
163
|
+
function MyComponent({ prop1, ...props }: MyComponentProps) { ... }
|
|
164
|
+
```
|
|
165
|
+
*/
|
|
166
|
+
function updateComponentTypes({ j, root, typeName, destructuredKeys, }) {
|
|
167
|
+
// Find the type definition of the component props
|
|
168
|
+
root
|
|
169
|
+
.find(j.TSTypeAliasDeclaration)
|
|
170
|
+
.forEach((typePath) => {
|
|
171
|
+
if (typePath.node.id.name === typeName) {
|
|
172
|
+
const typeAnnotation = typePath.node.typeAnnotation;
|
|
173
|
+
if (typeAnnotation.type === 'TSTypeLiteral') {
|
|
174
|
+
typeAnnotation.members.forEach((member) => {
|
|
175
|
+
if (member.type === 'TSPropertySignature' &&
|
|
176
|
+
member.key.type === 'Identifier' &&
|
|
177
|
+
destructuredKeys.includes(member.key.name)) {
|
|
178
|
+
member.optional = true;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/*
|
|
186
|
+
This helper transforms the component function signature to use a destructured object
|
|
187
|
+
as first parameter, so we can append the default props to it.
|
|
188
|
+
Example:
|
|
189
|
+
```
|
|
190
|
+
// BEFORE
|
|
191
|
+
const MyComponent = (props) => { ... }
|
|
192
|
+
|
|
193
|
+
// AFTER
|
|
194
|
+
const MyComponent = ({ prop1, ...props }) => { ... }
|
|
195
|
+
```
|
|
196
|
+
*/
|
|
197
|
+
function transformComponentFunctionSignature({ functionPropsParam, defaultPropsMap, componentName, j, }) {
|
|
198
|
+
let refactoredParameter;
|
|
199
|
+
const defaultPropsKeys = Object.keys(defaultPropsMap);
|
|
200
|
+
switch (functionPropsParam.type) {
|
|
201
|
+
// In this case, the component already has a destructured object as first parameter
|
|
202
|
+
// so we need to append the defaultProps to it
|
|
203
|
+
// const MyComnponent = ({ prop1, ...props }) => { ... }
|
|
204
|
+
case 'ObjectPattern':
|
|
205
|
+
refactoredParameter = functionPropsParam;
|
|
206
|
+
refactoredParameter.properties = [
|
|
207
|
+
...transformDefaultPropsToAST(defaultPropsMap, j),
|
|
208
|
+
// If the destructured object already had one of the default props, filter it out
|
|
209
|
+
...functionPropsParam.properties.filter((prop) => {
|
|
210
|
+
return (prop.type !== 'ObjectProperty' ||
|
|
211
|
+
prop.key.type !== 'Identifier' ||
|
|
212
|
+
!defaultPropsKeys.includes(prop.key.name));
|
|
213
|
+
}),
|
|
214
|
+
];
|
|
215
|
+
break;
|
|
216
|
+
// In this case, the component has a simple parameter as first parameter
|
|
217
|
+
// so we need to refactor it to a destructured object
|
|
218
|
+
// const MyComnponent = (props) => { ... }
|
|
219
|
+
case 'Identifier':
|
|
220
|
+
refactoredParameter = j.objectPattern([
|
|
221
|
+
...transformDefaultPropsToAST(defaultPropsMap, j),
|
|
222
|
+
j.spreadProperty(j.identifier('props')),
|
|
223
|
+
]);
|
|
224
|
+
// Make sure the refactored parameter has the same type annotation
|
|
225
|
+
// as the original one
|
|
226
|
+
refactoredParameter.typeAnnotation = functionPropsParam.typeAnnotation;
|
|
227
|
+
break;
|
|
228
|
+
default:
|
|
229
|
+
console.warn(`[WARNING]: Could not parse component function first parameter "${componentName}"`);
|
|
230
|
+
}
|
|
231
|
+
return refactoredParameter;
|
|
232
|
+
}
|
|
233
|
+
/*
|
|
234
|
+
This helper extracts the default props keys/values from the defaultProps object node
|
|
235
|
+
*/
|
|
236
|
+
function extractDefaultPropsFromNode(defaultPropsNode) {
|
|
237
|
+
return defaultPropsNode.properties.reduce((acc, prop) => {
|
|
238
|
+
if ((prop.type === 'ObjectProperty' || prop.type === 'Property') &&
|
|
239
|
+
prop.key.type === 'Identifier') {
|
|
240
|
+
return {
|
|
241
|
+
...acc,
|
|
242
|
+
[prop.key.name]: prop.value,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return acc;
|
|
246
|
+
}, {});
|
|
247
|
+
}
|
|
248
|
+
/*
|
|
249
|
+
This helper transforms the default props keys/values to an AST representation
|
|
250
|
+
so we can easily append them to the component function signature.
|
|
251
|
+
```
|
|
252
|
+
*/
|
|
253
|
+
function transformDefaultPropsToAST(defaultPropsMap, j) {
|
|
254
|
+
return Object.entries(defaultPropsMap).map(([key, value]) => {
|
|
255
|
+
const propNode = j.objectProperty(j.identifier(key), j.assignmentPattern(j.identifier(key), value));
|
|
256
|
+
propNode.shorthand = true;
|
|
257
|
+
return propNode;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async function reactDefaultPropsMigration(file, api, options) {
|
|
261
|
+
const j = api.jscodeshift;
|
|
262
|
+
const root = j(file.source, { comment: false });
|
|
263
|
+
const originalSource = root.toSource();
|
|
264
|
+
console.log('Processing file:', file.path);
|
|
265
|
+
// 1. Search for "defaultProps" definitions
|
|
266
|
+
root
|
|
267
|
+
.find(j.AssignmentExpression, {
|
|
268
|
+
left: {
|
|
269
|
+
type: 'MemberExpression',
|
|
270
|
+
property: { name: 'defaultProps' },
|
|
271
|
+
},
|
|
272
|
+
})
|
|
273
|
+
.forEach((path) => {
|
|
274
|
+
// Types validation to please Typescript
|
|
275
|
+
if (path.node.left.type === 'MemberExpression' &&
|
|
276
|
+
path.node.left.object.type === 'Identifier') {
|
|
277
|
+
// The node path looks like this:
|
|
278
|
+
// defaultProps: MyComponent.defaultProps = defaultProps;
|
|
279
|
+
const componentName = path.node.left.object.name;
|
|
280
|
+
const defaultPropsNode = path.node.right;
|
|
281
|
+
let componentPropsTypescriptType; // Only TypeScript files have type annotations
|
|
282
|
+
let defaultPropsMap = {};
|
|
283
|
+
let functionScope;
|
|
284
|
+
// 2. We now extract the default props values
|
|
285
|
+
// Default props can be defined inline or as a reference to another object
|
|
286
|
+
// INLINE -- MyComponent.defaultProps: { prop1: 'value1', prop2: 'value2' }
|
|
287
|
+
// REFERENCE -- MyComponent.defaultProps: defaultProps
|
|
288
|
+
if (defaultPropsNode.type === 'Identifier') {
|
|
289
|
+
// REFERENCE -- MyComponent.defaultProps: defaultProps
|
|
290
|
+
// A) Look for the identifier declaration
|
|
291
|
+
const defaultPropsDeclarations = root.find(j.VariableDeclarator, {
|
|
292
|
+
id: { type: 'Identifier', name: defaultPropsNode.name },
|
|
293
|
+
});
|
|
294
|
+
if (defaultPropsDeclarations.size() === 1) {
|
|
295
|
+
// B) Extract default props keys/values
|
|
296
|
+
defaultPropsMap = extractDefaultPropsFromNode(defaultPropsDeclarations.nodes()[0].init);
|
|
297
|
+
// C) Remove the identifier declaration
|
|
298
|
+
defaultPropsDeclarations.remove();
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
console.warn(`[WARNING]: Could not find defaultProps declaration for "${componentName}"`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else if (defaultPropsNode.type === 'ObjectExpression') {
|
|
305
|
+
// INLINE -- MyComponent.defaultProps: { prop1: 'value1', prop2: 'value2' }
|
|
306
|
+
// Extract default props keys/values
|
|
307
|
+
defaultPropsMap = extractDefaultPropsFromNode(defaultPropsNode);
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
console.warn(`[WARNING]: Do not know how to process default props for component "${componentName}": ${j(path).toSource()}`);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
// 3. Next we update the component function signature
|
|
314
|
+
// We first look for classic function declarations
|
|
315
|
+
// function MyComnponent(props) { ... }
|
|
316
|
+
const functionComponentDeclaration = root.find(j.FunctionDeclaration, {
|
|
317
|
+
id: { name: componentName },
|
|
318
|
+
});
|
|
319
|
+
if (functionComponentDeclaration.length === 1) {
|
|
320
|
+
functionComponentDeclaration.nodes()[0].params[0] =
|
|
321
|
+
transformComponentFunctionSignature({
|
|
322
|
+
functionPropsParam: functionComponentDeclaration.nodes()[0].params[0],
|
|
323
|
+
defaultPropsMap,
|
|
324
|
+
componentName,
|
|
325
|
+
j,
|
|
326
|
+
});
|
|
327
|
+
// Extract the component props TS type name
|
|
328
|
+
componentPropsTypescriptType = resolvePropsTypescriptType(functionComponentDeclaration.nodes()[0].params[0]);
|
|
329
|
+
// Get the function body scope so we only do the replacement
|
|
330
|
+
// within the component function we're currently processing
|
|
331
|
+
functionScope = j(functionComponentDeclaration.nodes()[0].body);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// If we don't find a function declaration, we look for arrow function declarations
|
|
335
|
+
// const MyComnponent = (props) => { ... }
|
|
336
|
+
// const MyComnponent = ({ prop1, ...props }) => { ... }
|
|
337
|
+
const variableComponentDeclaration = root.find(j.VariableDeclaration, {
|
|
338
|
+
declarations: [
|
|
339
|
+
{
|
|
340
|
+
id: { name: componentName },
|
|
341
|
+
},
|
|
342
|
+
],
|
|
343
|
+
});
|
|
344
|
+
if (variableComponentDeclaration.length === 1) {
|
|
345
|
+
const functionFirstParamNode = variableComponentDeclaration.nodes()[0].declarations[0];
|
|
346
|
+
if (functionFirstParamNode.type === 'VariableDeclarator' &&
|
|
347
|
+
functionFirstParamNode.init?.type === 'ArrowFunctionExpression') {
|
|
348
|
+
functionFirstParamNode.init.params[0] =
|
|
349
|
+
transformComponentFunctionSignature({
|
|
350
|
+
functionPropsParam: functionFirstParamNode.init.params[0],
|
|
351
|
+
defaultPropsMap,
|
|
352
|
+
componentName,
|
|
353
|
+
j,
|
|
354
|
+
});
|
|
355
|
+
// Extract the component props TS type name
|
|
356
|
+
componentPropsTypescriptType = resolvePropsTypescriptType(functionFirstParamNode.init.params[0]);
|
|
357
|
+
// Get the function body scope so we only do the replacement
|
|
358
|
+
// within the component function we're currently processing
|
|
359
|
+
functionScope = j(functionFirstParamNode.init.body);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
console.warn(`[WARNING]: Could parse component function first parameter "${componentName}"`);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
console.warn(`[WARNING]: Could not find component declaration for "${componentName}" (class component are ignored)`);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// 4. Refactor the usages of the default props in the body of the component
|
|
372
|
+
replacePropsUsage({
|
|
373
|
+
j,
|
|
374
|
+
defaultPropsKeys: Object.keys(defaultPropsMap),
|
|
375
|
+
scope: functionScope,
|
|
376
|
+
});
|
|
377
|
+
// 5. Update the component TS type definition so we make sure the default props are optional
|
|
378
|
+
// (not needed for Javascript files)
|
|
379
|
+
if (componentPropsTypescriptType) {
|
|
380
|
+
updateComponentTypes({
|
|
381
|
+
j,
|
|
382
|
+
root,
|
|
383
|
+
typeName: componentPropsTypescriptType,
|
|
384
|
+
destructuredKeys: Object.keys(defaultPropsMap),
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
// 6. Remove the defaultProps assignment from the component
|
|
388
|
+
j(path).remove();
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
// Do not return anything if no changes were applied
|
|
392
|
+
// so we don't rewrite the file
|
|
393
|
+
if (originalSource === root.toSource()) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
if (!options.dry) {
|
|
397
|
+
// Format output code with prettier
|
|
398
|
+
const prettierConfig = await prettier_1.default.resolveConfig(file.path);
|
|
399
|
+
return prettier_1.default.format(root.toSource(), prettierConfig);
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
exports.default = reactDefaultPropsMigration;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercetools-frontend/codemod",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.38.1",
|
|
4
4
|
"description": "Codemod transformations for Custom Applications",
|
|
5
5
|
"bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
|
|
6
6
|
"repository": {
|
|
@@ -43,9 +43,10 @@
|
|
|
43
43
|
"@tsconfig/node16": "^16.1.1",
|
|
44
44
|
"@types/glob": "8.1.0",
|
|
45
45
|
"@types/jscodeshift": "0.11.11",
|
|
46
|
+
"@types/prettier": "2.7.3",
|
|
46
47
|
"rimraf": "5.0.7",
|
|
47
48
|
"typescript": "5.0.4",
|
|
48
|
-
"@commercetools-frontend/application-components": "22.
|
|
49
|
+
"@commercetools-frontend/application-components": "22.38.1"
|
|
49
50
|
},
|
|
50
51
|
"engines": {
|
|
51
52
|
"node": "16.x || >=18.0.0"
|