@mui/codemod 5.1.1 → 5.2.0

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/codemod.js CHANGED
File without changes
@@ -164,21 +164,30 @@ function transformer(file, api, options) {
164
164
 
165
165
  return declaration;
166
166
  }
167
+
168
+ const classesCount = {};
167
169
  /**
168
170
  *
169
171
  * @param {import('jscodeshift').ObjectExpression} objExpression
172
+ * @param {import('jscodeshift').ObjectExpression} prevObj
170
173
  */
171
174
 
172
-
173
- function createClasses(objExpression) {
174
- const classes = j.objectExpression([]);
175
+ function createClasses(objExpression, prevObj) {
176
+ const classes = prevObj || j.objectExpression([]);
175
177
  objExpression.properties.forEach(prop => {
176
- classes.properties.push(j.objectProperty(prop.key, j.templateLiteral([j.templateElement({
178
+ if (!classesCount[prop.key.name]) {
179
+ classesCount[prop.key.name] = 1;
180
+ } else {
181
+ classesCount[prop.key.name] += 1;
182
+ }
183
+
184
+ const resolvedKey = classesCount[prop.key.name] === 1 ? prop.key.name : `${prop.key.name}${classesCount[prop.key.name]}`;
185
+ classes.properties.push(j.objectProperty(j.identifier(resolvedKey), j.templateLiteral([j.templateElement({
177
186
  raw: '',
178
187
  cooked: ''
179
188
  }, false), j.templateElement({
180
- raw: `-${prop.key.name}`,
181
- cooked: `-${prop.key.name}`
189
+ raw: `-${resolvedKey}`,
190
+ cooked: `-${resolvedKey}`
182
191
  }, true)], [j.identifier('PREFIX')])));
183
192
  });
184
193
  return classes;
@@ -225,46 +234,66 @@ function transformer(file, api, options) {
225
234
  return null;
226
235
  }
227
236
  /**
228
- *
229
- * @param {import('jscodeshift').ArrowFunctionExpression | import('jscodeshift').FunctionDeclaration} functionExpression
237
+ * @param {import('jscodeshift').ObjectExpression | import('jscodeshift').ArrowFunctionExpression | import('jscodeshift').FunctionDeclaration} expression
230
238
  */
231
239
 
232
240
 
233
- function convertToStyledArg(functionExpression, rootKeys = []) {
241
+ function getObjectExpression(expression) {
234
242
  let objectExpression;
235
243
 
236
- if (functionExpression.type === 'ObjectExpression') {
237
- objectExpression = functionExpression;
244
+ if (expression.type === 'ObjectExpression') {
245
+ objectExpression = expression;
238
246
  }
239
247
 
240
- if (functionExpression.type === 'ArrowFunctionExpression') {
241
- if (functionExpression.body.type === 'BlockStatement') {
242
- const returnStatement = functionExpression.body.body.find(b => b.type === 'ReturnStatement');
248
+ if (expression.type === 'ArrowFunctionExpression') {
249
+ if (expression.body.type === 'BlockStatement') {
250
+ const returnStatement = expression.body.body.find(b => b.type === 'ReturnStatement');
243
251
  objectExpression = returnStatement.argument;
244
252
  }
245
253
 
246
- if (functionExpression.body.type === 'ObjectExpression') {
247
- functionExpression.body.extra.parenthesized = false;
248
- objectExpression = functionExpression.body;
254
+ if (expression.body.type === 'ObjectExpression') {
255
+ expression.body.extra.parenthesized = false;
256
+ objectExpression = expression.body;
249
257
  }
250
258
  }
251
259
 
252
- if (functionExpression.type === 'FunctionDeclaration') {
253
- functionExpression.type = 'FunctionExpression';
254
- const returnStatement = functionExpression.body.body.find(b => b.type === 'ReturnStatement');
260
+ if (expression.type === 'FunctionDeclaration') {
261
+ expression.type = 'FunctionExpression';
262
+ const returnStatement = expression.body.body.find(b => b.type === 'ReturnStatement');
255
263
  objectExpression = returnStatement.argument;
256
264
  }
257
265
 
266
+ return objectExpression;
267
+ }
268
+
269
+ const stylesCount = {};
270
+ /**
271
+ *
272
+ * @param {import('jscodeshift').ObjectExpression | import('jscodeshift').ArrowFunctionExpression | import('jscodeshift').FunctionDeclaration} functionExpression
273
+ * @param {string[]} rootKeys
274
+ * @param {import('jscodeshift').ObjectExpression | import('jscodeshift').ArrowFunctionExpression | import('jscodeshift').FunctionDeclaration} prevStyleArg
275
+ */
276
+
277
+ function convertToStyledArg(functionExpression, rootKeys = [], prevStyleArg) {
278
+ const objectExpression = getObjectExpression(functionExpression);
279
+
258
280
  if (objectExpression) {
259
281
  objectExpression.properties.forEach(prop => {
260
- const selector = rootKeys.includes(prop.key.name) ? '&.' : '& .';
282
+ if (!stylesCount[prop.key.name]) {
283
+ stylesCount[prop.key.name] = 1;
284
+ } else {
285
+ stylesCount[prop.key.name] += 1;
286
+ }
287
+
288
+ const resolvedKey = stylesCount[prop.key.name] === 1 ? prop.key.name : `${prop.key.name}${stylesCount[prop.key.name]}`;
289
+ const selector = rootKeys.includes(resolvedKey) ? '&.' : '& .';
261
290
  prop.key = j.templateLiteral([j.templateElement({
262
291
  raw: selector,
263
292
  cooked: selector
264
293
  }, false), j.templateElement({
265
294
  raw: '',
266
295
  cooked: ''
267
- }, true)], [j.identifier(`classes.${prop.key.name}`)]);
296
+ }, true)], [j.identifier(`classes.${resolvedKey}`)]);
268
297
  prop.computed = true;
269
298
  return prop;
270
299
  });
@@ -282,6 +311,22 @@ function transformer(file, api, options) {
282
311
  });
283
312
  }
284
313
 
314
+ if (prevStyleArg) {
315
+ const prevObjectExpression = getObjectExpression(prevStyleArg);
316
+
317
+ if (objectExpression) {
318
+ // merge object
319
+ prevObjectExpression.properties = [...prevObjectExpression.properties, ...objectExpression.properties];
320
+ }
321
+
322
+ if (functionExpression.params && prevStyleArg.type === 'ObjectExpression') {
323
+ // turn prevStyleArg to ArrowFunction
324
+ prevStyleArg = j.arrowFunctionExpression(functionExpression.params, prevStyleArg);
325
+ }
326
+
327
+ return prevStyleArg;
328
+ }
329
+
285
330
  return functionExpression;
286
331
  }
287
332
 
@@ -326,6 +371,8 @@ function transformer(file, api, options) {
326
371
  const prefix = getPrefix(withStylesCall || makeStylesCall);
327
372
  const rootClassKeys = getRootClassKeys();
328
373
  const result = {};
374
+ const componentClassesCount = {};
375
+ const withStylesComponents = [];
329
376
 
330
377
  if (withStylesCall) {
331
378
  let stylesFnName;
@@ -333,7 +380,7 @@ function transformer(file, api, options) {
333
380
  callee: {
334
381
  name: 'withStyles'
335
382
  }
336
- }).at(0).forEach(path => {
383
+ }).forEach(path => {
337
384
  const arg = path.node.arguments[0];
338
385
 
339
386
  if (arg.type === 'Identifier') {
@@ -343,15 +390,33 @@ function transformer(file, api, options) {
343
390
  const objectExpression = getReturnStatement(arg);
344
391
 
345
392
  if (objectExpression) {
346
- result.classes = createClasses(objectExpression, prefix);
347
- result.styledArg = convertToStyledArg(arg, rootClassKeys);
393
+ // do this first, because objectExpression will be mutated in `createClasses` below.
394
+ if (path.parent.parent && path.parent.parent.node.id) {
395
+ // save withStylesComponent name, to add classes on JSX
396
+ withStylesComponents.push({
397
+ variableName: path.parent.parent.node.id.name,
398
+ classes: j.objectExpression(objectExpression.properties.map(prop => {
399
+ if (!componentClassesCount[prop.key.name]) {
400
+ componentClassesCount[prop.key.name] = 1;
401
+ } else {
402
+ componentClassesCount[prop.key.name] += 1;
403
+ }
404
+
405
+ const resolvedKey = componentClassesCount[prop.key.name] === 1 ? prop.key.name : `${prop.key.name}${componentClassesCount[prop.key.name]}`;
406
+ return j.property('init', j.identifier(prop.key.name), j.memberExpression(j.identifier('classes'), j.identifier(resolvedKey)));
407
+ }))
408
+ });
409
+ }
410
+
411
+ result.classes = createClasses(objectExpression, result.classes);
412
+ result.styledArg = convertToStyledArg(arg, rootClassKeys, result.styledArg);
348
413
  }
349
414
  });
350
415
  root.find(j.VariableDeclarator, {
351
416
  id: {
352
417
  name: stylesFnName
353
418
  }
354
- }).at(0).forEach(path => {
419
+ }).forEach(path => {
355
420
  let fnArg = path.node.init;
356
421
  const objectExpression = getReturnStatement(fnArg);
357
422
 
@@ -370,7 +435,7 @@ function transformer(file, api, options) {
370
435
  }
371
436
 
372
437
  if (objectExpression) {
373
- result.classes = createClasses(objectExpression, prefix);
438
+ result.classes = createClasses(objectExpression, result.classes);
374
439
  result.styledArg = convertToStyledArg(fnArg, rootClassKeys);
375
440
  }
376
441
  }).remove();
@@ -378,9 +443,9 @@ function transformer(file, api, options) {
378
443
  id: {
379
444
  name: stylesFnName
380
445
  }
381
- }).at(0).forEach(path => {
446
+ }).forEach(path => {
382
447
  const returnStatement = path.node.body.body.find(b => b.type === 'ReturnStatement');
383
- result.classes = createClasses(returnStatement.argument, prefix);
448
+ result.classes = createClasses(returnStatement.argument, result.classes);
384
449
  result.styledArg = convertToStyledArg(path.node, rootClassKeys);
385
450
  }).remove();
386
451
  }
@@ -415,7 +480,7 @@ function transformer(file, api, options) {
415
480
  }
416
481
 
417
482
  if (objectExpression) {
418
- result.classes = createClasses(objectExpression, prefix);
483
+ result.classes = createClasses(objectExpression, result.classes);
419
484
  result.styledArg = convertToStyledArg(arg, rootClassKeys);
420
485
  }
421
486
  });
@@ -427,7 +492,7 @@ function transformer(file, api, options) {
427
492
  const objectExpression = getReturnStatement(path.node.init);
428
493
 
429
494
  if (objectExpression) {
430
- result.classes = createClasses(objectExpression, prefix);
495
+ result.classes = createClasses(objectExpression, result.classes);
431
496
  result.styledArg = convertToStyledArg(path.node.init, rootClassKeys);
432
497
  }
433
498
  }).remove();
@@ -437,7 +502,7 @@ function transformer(file, api, options) {
437
502
  }
438
503
  }).at(0).forEach(path => {
439
504
  const returnStatement = path.node.body.body.find(b => b.type === 'ReturnStatement');
440
- result.classes = createClasses(returnStatement.argument, prefix);
505
+ result.classes = createClasses(returnStatement.argument, result.classes);
441
506
  result.styledArg = convertToStyledArg(path.node, rootClassKeys);
442
507
  }).remove();
443
508
  root.find(j.VariableDeclaration).filter(path => path.node.declarations.some(d => d.id.name === 'useStyles')).remove();
@@ -500,10 +565,28 @@ function transformer(file, api, options) {
500
565
  root.findJSXElements(rootJsxName).at(0).forEach(transformJsxRootToStyledComponent);
501
566
  }
502
567
  /**
503
- * import styled if not exist
568
+ * Attach classes to components created by withStyles
569
+ * ex. const Button1 = withStyles(...)(Button)
504
570
  */
505
571
 
506
572
 
573
+ withStylesComponents.forEach(data => {
574
+ root.find(j.JSXOpeningElement, {
575
+ name: {
576
+ name: data.variableName
577
+ }
578
+ }).forEach(path => {
579
+ if (!path.node.attributes) {
580
+ path.node.attributes = [];
581
+ }
582
+
583
+ path.node.attributes.push(j.jsxAttribute(j.jsxIdentifier('classes'), j.jsxExpressionContainer(data.classes)));
584
+ });
585
+ });
586
+ /**
587
+ * import styled if not exist
588
+ */
589
+
507
590
  const imports = root.find(j.ImportDeclaration).filter(({
508
591
  node
509
592
  }) => node.source.value.match(/^@material-ui\/core\/styles$/)).forEach(({
@@ -524,8 +607,21 @@ function transformer(file, api, options) {
524
607
  */
525
608
 
526
609
 
527
- root.find(j.ImportDeclaration).filter(path => path.node.source.value.match(/^@material-ui\/styles\/?(withStyles|makeStyles|createStyles)?$/)).forEach(path => {
610
+ root.find(j.ImportDeclaration).filter(path => path.node.source.value.match(/^(@material-ui|@mui)\/styles\/?(withStyles|makeStyles|createStyles)?$/)).forEach(path => {
528
611
  path.node.specifiers = path.node.specifiers.filter(s => s.local.name !== 'withStyles' && s.local.name !== 'makeStyles' && s.local.name !== 'createStyles');
529
612
  }).filter(path => !path.node.specifiers.length).remove();
613
+ /**
614
+ * remove withStyles calls that create new component
615
+ */
616
+
617
+ root.find(j.CallExpression, {
618
+ callee: {
619
+ name: 'withStyles'
620
+ }
621
+ }).forEach(path => {
622
+ if (path.parent.parent.parent.node.type === 'VariableDeclaration' && path.parent.parent.parent.parent.node.type !== 'ExportNamedDeclaration' && path.parent.node.arguments[0].type === 'Identifier') {
623
+ path.parent.parent.node.init = j.identifier(path.parent.node.arguments[0].name);
624
+ }
625
+ });
530
626
  return root.toSource(printOptions).replace(/withStyles\([^)]*\),?/gm, '').replace(/([^=]{.*)classes[^.],?(.*})/gm, '$1$2').replace(/^.*useStyles(.*);?/gm, '');
531
627
  }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.Test = void 0;
9
+
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var _Button = _interopRequireDefault(require("@mui/material/Button"));
13
+
14
+ var _withStyles = _interopRequireDefault(require("@mui/styles/withStyles"));
15
+
16
+ var _jsxRuntime = require("react/jsx-runtime");
17
+
18
+ var _React$Fragment;
19
+
20
+ const Button1 = (0, _withStyles.default)({
21
+ root: {
22
+ backgroundColor: 'red'
23
+ }
24
+ })(_Button.default);
25
+ const Button2 = (0, _withStyles.default)(theme => ({
26
+ root: {
27
+ backgroundColor: theme.palette.primary.main
28
+ },
29
+ actions: {
30
+ padding: theme.spacing(1)
31
+ }
32
+ }))(_Button.default);
33
+ const Button3 = (0, _withStyles.default)({
34
+ root: {
35
+ backgroundColor: 'blue'
36
+ },
37
+ actions: {
38
+ padding: '0px'
39
+ }
40
+ })(_Button.default);
41
+
42
+ const Test = () => _React$Fragment || (_React$Fragment = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_react.default.Fragment, {
43
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(Button1, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(Button2, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(Button3, {})]
44
+ }));
45
+
46
+ exports.Test = Test;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.Test = void 0;
9
+
10
+ var _react = _interopRequireDefault(require("react"));
11
+
12
+ var _styles = require("@mui/material/styles");
13
+
14
+ var _Button = _interopRequireDefault(require("@mui/material/Button"));
15
+
16
+ var _jsxRuntime = require("react/jsx-runtime");
17
+
18
+ const PREFIX = 'Test';
19
+ const classes = {
20
+ root: `${PREFIX}-root`,
21
+ root2: `${PREFIX}-root2`,
22
+ actions: `${PREFIX}-actions`,
23
+ root3: `${PREFIX}-root3`,
24
+ actions2: `${PREFIX}-actions2`
25
+ }; // TODO jss-to-styled codemod: The Fragment root was replaced by div. Change the tag if needed.
26
+
27
+ const Root = (0, _styles.styled)('div')(({
28
+ theme
29
+ }) => ({
30
+ [`& .${classes.root}`]: {
31
+ backgroundColor: 'red'
32
+ },
33
+ [`& .${classes.root2}`]: {
34
+ backgroundColor: theme.palette.primary.main
35
+ },
36
+ [`& .${classes.actions}`]: {
37
+ padding: theme.spacing(1)
38
+ },
39
+ [`& .${classes.root3}`]: {
40
+ backgroundColor: 'blue'
41
+ },
42
+ [`& .${classes.actions2}`]: {
43
+ padding: '0px'
44
+ }
45
+ }));
46
+ const Button1 = _Button.default;
47
+ const Button2 = _Button.default;
48
+ const Button3 = _Button.default;
49
+
50
+ const Test = () => /*#__PURE__*/(0, _jsxRuntime.jsxs)(Root, {
51
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(Button1, {
52
+ classes: {
53
+ root: classes.root
54
+ }
55
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(Button2, {
56
+ classes: {
57
+ root: classes.root2,
58
+ actions: classes.actions
59
+ }
60
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(Button3, {
61
+ classes: {
62
+ root: classes.root3,
63
+ actions: classes.actions2
64
+ }
65
+ })]
66
+ });
67
+
68
+ exports.Test = Test;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mui/codemod",
3
- "version": "5.1.1",
3
+ "version": "5.2.0",
4
4
  "bin": "./codemod.js",
5
5
  "private": false,
6
6
  "author": "MUI Team",
@@ -34,7 +34,7 @@
34
34
  "yargs": "^17.2.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/jscodeshift": "0.11.2"
37
+ "@types/jscodeshift": "0.11.3"
38
38
  },
39
39
  "sideEffects": false,
40
40
  "publishConfig": {