@mui/codemod 5.7.0 → 5.8.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/README.md +82 -0
- package/codemod.js +0 -0
- package/node/v5.0.0/jss-to-tss-react.js +457 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-from-material-ui-core-styles.js +52 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-from-material-ui-core.js +75 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-from-mui-styles.js +53 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-mixins-pattern.js +51 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-todo-comments.js +74 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-typescript-docs-example-params.js +59 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-typescript-docs-example.js +55 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-typescript.js +73 -0
- package/node/v5.0.0/jss-to-tss-react.test/actual-withStyles.js +123 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-from-material-ui-core-styles.js +56 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-from-material-ui-core.js +80 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-from-mui-styles.js +57 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-mixins-pattern.js +54 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-todo-comments.js +86 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-typescript-docs-example-params.js +61 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-typescript-docs-example.js +54 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-typescript.js +79 -0
- package/node/v5.0.0/jss-to-tss-react.test/expected-withStyles.js +124 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -605,6 +605,88 @@ You can find more details about this breaking change in [the migration guide](ht
|
|
|
605
605
|
> **Note:** This approach converts the first element in the return statement into styled component but also increases CSS specificity to override nested children.
|
|
606
606
|
> This codemod should be adopted after handling all breaking changes, [check out the migration documentation](https://mui.com/material-ui/guides/migration-v4/)
|
|
607
607
|
|
|
608
|
+
#### `jss-to-tss-react`
|
|
609
|
+
|
|
610
|
+
Migrate JSS styling with `makeStyles` or `withStyles` to the corresponding `tss-react` API.
|
|
611
|
+
|
|
612
|
+
```diff
|
|
613
|
+
-import clsx from 'clsx';
|
|
614
|
+
-import {makeStyles, createStyles} from '@material-ui/core/styles';
|
|
615
|
+
+import { makeStyles } from 'tss-react/mui';
|
|
616
|
+
|
|
617
|
+
-const useStyles = makeStyles((theme) => createStyles<
|
|
618
|
+
- 'root' | 'small' | 'child', {color: 'primary' | 'secondary', padding: number}
|
|
619
|
+
->
|
|
620
|
+
-({
|
|
621
|
+
- root: ({color, padding}) => ({
|
|
622
|
+
+const useStyles = makeStyles<{color: 'primary' | 'secondary', padding: number}, 'child' | 'small'>({name: 'App'})((theme, { color, padding }, classes) => ({
|
|
623
|
+
+ root: {
|
|
624
|
+
padding: padding,
|
|
625
|
+
- '&:hover $child': {
|
|
626
|
+
+ [`&:hover .${classes.child}`]: {
|
|
627
|
+
backgroundColor: theme.palette[color].main,
|
|
628
|
+
}
|
|
629
|
+
- }),
|
|
630
|
+
+ },
|
|
631
|
+
small: {},
|
|
632
|
+
child: {
|
|
633
|
+
border: '1px solid black',
|
|
634
|
+
height: 50,
|
|
635
|
+
- '&$small': {
|
|
636
|
+
+ [`&.${classes.small}`]: {
|
|
637
|
+
height: 30
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
-}), {name: 'App'});
|
|
641
|
+
+}));
|
|
642
|
+
|
|
643
|
+
function App({classes: classesProp}: {classes?: any}) {
|
|
644
|
+
- const classes = useStyles({color: 'primary', padding: 30, classes: classesProp});
|
|
645
|
+
+ const { classes, cx } = useStyles({
|
|
646
|
+
+ color: 'primary',
|
|
647
|
+
+ padding: 30
|
|
648
|
+
+ }, {
|
|
649
|
+
+ props: {
|
|
650
|
+
+ classes: classesProp
|
|
651
|
+
+ }
|
|
652
|
+
+ });
|
|
653
|
+
|
|
654
|
+
return (
|
|
655
|
+
<div className={classes.root}>
|
|
656
|
+
<div className={classes.child}>
|
|
657
|
+
The Background take the primary theme color when the mouse hovers the parent.
|
|
658
|
+
</div>
|
|
659
|
+
- <div className={clsx(classes.child, classes.small)}>
|
|
660
|
+
+ <div className={cx(classes.child, classes.small)}>
|
|
661
|
+
The Background take the primary theme color when the mouse hovers the parent.
|
|
662
|
+
I am smaller than the other child.
|
|
663
|
+
</div>
|
|
664
|
+
</div>
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export default App;
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
```sh
|
|
672
|
+
npx @mui/codemod v5.0.0/jss-to-tss-react <path>
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
The following scenarios are not currently handled by this codemod and will be marked with a
|
|
676
|
+
"TODO jss-to-tss-react codemod" comment:
|
|
677
|
+
|
|
678
|
+
- If the hook returned by `makeStyles` (e.g. `useStyles`) is exported and used in another file,
|
|
679
|
+
the usages in other files will not be converted.
|
|
680
|
+
- Arrow functions as the value for a CSS prop will not be converted. Arrow functions **are**
|
|
681
|
+
supported at the rule level, though with some caveats listed below.
|
|
682
|
+
- In order for arrow functions at the rule level to be converted, the parameter must use object
|
|
683
|
+
destructuring (e.g. `root: ({color, padding}) => (...)`). If the parameter is not destructured
|
|
684
|
+
(e.g. `root: (props) => (...)`), it will not be converted.
|
|
685
|
+
- If an arrow function at the rule level contains a code block (i.e. contains an explicit `return`
|
|
686
|
+
statement) rather than just an object expression, it will not be converted.
|
|
687
|
+
|
|
688
|
+
You can find more details about migrating from JSS to tss-react in [the migration guide](https://mui.com/guides/migration-v4/#2-use-tss-react).
|
|
689
|
+
|
|
608
690
|
#### `link-underline-hover`
|
|
609
691
|
|
|
610
692
|
Apply `underline="hover"` to `<Link />` that does not define `underline` prop (to get the same behavior as in v4).
|
package/codemod.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = transformer;
|
|
7
|
+
const ruleEndRegEx = /[^a-zA-Z0-9_]+/;
|
|
8
|
+
|
|
9
|
+
function transformNestedKeys(j, comments, propValueNode, ruleRegEx, nestedKeys) {
|
|
10
|
+
propValueNode.properties.forEach(prop => {
|
|
11
|
+
var _prop$value, _prop$value2;
|
|
12
|
+
|
|
13
|
+
if (((_prop$value = prop.value) == null ? void 0 : _prop$value.type) === 'ObjectExpression') {
|
|
14
|
+
if (typeof prop.key.value === 'string') {
|
|
15
|
+
let ruleIndex = prop.key.value.search(ruleRegEx);
|
|
16
|
+
let searchStartIndex = 0;
|
|
17
|
+
const elements = [];
|
|
18
|
+
const identifiers = [];
|
|
19
|
+
|
|
20
|
+
while (ruleIndex >= 0) {
|
|
21
|
+
const valueStartingAtRuleName = prop.key.value.substring(ruleIndex + 1);
|
|
22
|
+
const ruleEndIndex = valueStartingAtRuleName.search(ruleEndRegEx);
|
|
23
|
+
const ruleName = ruleEndIndex >= 0 ? prop.key.value.substring(ruleIndex + 1, ruleIndex + 1 + ruleEndIndex) : valueStartingAtRuleName;
|
|
24
|
+
|
|
25
|
+
if (!nestedKeys.includes(ruleName)) {
|
|
26
|
+
nestedKeys.push(ruleName);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const before = prop.key.value.substring(searchStartIndex, ruleIndex);
|
|
30
|
+
elements.push(j.templateElement({
|
|
31
|
+
raw: `${before}.`,
|
|
32
|
+
cooked: `${before}.`
|
|
33
|
+
}, false));
|
|
34
|
+
identifiers.push(j.identifier(`classes.${ruleName}`));
|
|
35
|
+
searchStartIndex = ruleIndex + ruleName.length + 1;
|
|
36
|
+
const after = prop.key.value.substring(searchStartIndex);
|
|
37
|
+
ruleIndex = after.search(ruleRegEx);
|
|
38
|
+
|
|
39
|
+
if (ruleIndex >= 0) {
|
|
40
|
+
ruleIndex += searchStartIndex;
|
|
41
|
+
} else {
|
|
42
|
+
elements.push(j.templateElement({
|
|
43
|
+
raw: after,
|
|
44
|
+
cooked: after
|
|
45
|
+
}, false));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (identifiers.length > 0) {
|
|
50
|
+
prop.key = j.templateLiteral(elements, identifiers);
|
|
51
|
+
prop.computed = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
transformNestedKeys(j, comments, prop.value, ruleRegEx, nestedKeys);
|
|
56
|
+
} else if (((_prop$value2 = prop.value) == null ? void 0 : _prop$value2.type) === 'ArrowFunctionExpression') {
|
|
57
|
+
comments.push(j.commentLine(' TODO jss-to-tss-react codemod: Unable to handle style definition reliably. ArrowFunctionExpression in CSS prop.', true));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function transformStylesExpression(j, comments, stylesExpression, nestedKeys, setStylesExpression) {
|
|
63
|
+
const ruleNames = [];
|
|
64
|
+
const paramNames = [];
|
|
65
|
+
let objectExpression;
|
|
66
|
+
|
|
67
|
+
if (stylesExpression.type === 'ObjectExpression') {
|
|
68
|
+
objectExpression = stylesExpression;
|
|
69
|
+
} else if (stylesExpression.type === 'ArrowFunctionExpression') {
|
|
70
|
+
if (stylesExpression.body.type === 'BlockStatement') {
|
|
71
|
+
const returnStatement = stylesExpression.body.body.find(b => b.type === 'ReturnStatement');
|
|
72
|
+
|
|
73
|
+
if (returnStatement.argument.type === 'ObjectExpression') {
|
|
74
|
+
objectExpression = returnStatement.argument;
|
|
75
|
+
}
|
|
76
|
+
} else if (stylesExpression.body.type === 'ObjectExpression') {
|
|
77
|
+
objectExpression = stylesExpression.body;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (objectExpression !== undefined) {
|
|
82
|
+
objectExpression.properties.forEach(prop => {
|
|
83
|
+
var _prop$key;
|
|
84
|
+
|
|
85
|
+
if ((_prop$key = prop.key) != null && _prop$key.name) {
|
|
86
|
+
ruleNames.push(prop.key.name);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
let ruleRegExString = '(';
|
|
90
|
+
ruleNames.forEach((ruleName, index) => {
|
|
91
|
+
if (index > 0) {
|
|
92
|
+
ruleRegExString += '|';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
ruleRegExString += `\\$${ruleName}`;
|
|
96
|
+
});
|
|
97
|
+
ruleRegExString += ')';
|
|
98
|
+
const ruleRegEx = new RegExp(ruleRegExString, 'g');
|
|
99
|
+
objectExpression.properties.forEach(prop => {
|
|
100
|
+
if (prop.value) {
|
|
101
|
+
if (prop.value.type !== 'ObjectExpression') {
|
|
102
|
+
if (prop.value.type === 'ArrowFunctionExpression' && prop.value.body.type === 'ObjectExpression' && prop.value.params[0].type === 'ObjectPattern') {
|
|
103
|
+
prop.value.params[0].properties.forEach(property => {
|
|
104
|
+
const name = property.key.name;
|
|
105
|
+
|
|
106
|
+
if (!paramNames.includes(name)) {
|
|
107
|
+
paramNames.push(name);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
prop.value = prop.value.body;
|
|
111
|
+
} else {
|
|
112
|
+
let extraComment = `Unexpected value type of ${prop.value.type}.`;
|
|
113
|
+
|
|
114
|
+
if (prop.value.type === 'ArrowFunctionExpression') {
|
|
115
|
+
if (prop.value.body.type === 'ObjectExpression') {
|
|
116
|
+
let example = '';
|
|
117
|
+
|
|
118
|
+
if (prop.value.params[0].type === 'Identifier') {
|
|
119
|
+
example = ' (e.g. `(props) => ({...})` instead of `({color}) => ({...})`)';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
extraComment = ` Arrow function has parameter type of ${prop.value.params[0].type} instead of ObjectPattern${example}.`;
|
|
123
|
+
} else {
|
|
124
|
+
extraComment = ` Arrow function has body type of ${prop.value.body.type} instead of ObjectExpression.`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
comments.push(j.commentLine(` TODO jss-to-tss-react codemod: Unable to handle style definition reliably. Unsupported arrow function syntax.`, true));
|
|
129
|
+
comments.push(j.commentLine(extraComment, true));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
transformNestedKeys(j, comments, prop.value, ruleRegEx, nestedKeys);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (paramNames.length > 0 || nestedKeys.length > 0) {
|
|
139
|
+
let arrowFunction;
|
|
140
|
+
|
|
141
|
+
if (stylesExpression.type === 'ArrowFunctionExpression') {
|
|
142
|
+
arrowFunction = stylesExpression;
|
|
143
|
+
} else {
|
|
144
|
+
arrowFunction = j.arrowFunctionExpression([], objectExpression);
|
|
145
|
+
setStylesExpression(arrowFunction);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (arrowFunction.params.length === 0) {
|
|
149
|
+
arrowFunction.params.push(j.identifier('_theme'));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let paramsString = '_params';
|
|
153
|
+
|
|
154
|
+
if (paramNames.length > 0) {
|
|
155
|
+
paramsString = `{ ${paramNames.join(', ')} }`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
arrowFunction.params.push(j.identifier(paramsString));
|
|
159
|
+
|
|
160
|
+
if (nestedKeys.length > 0) {
|
|
161
|
+
arrowFunction.params.push(j.identifier('classes'));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (arrowFunction.body.type === 'ObjectExpression') {
|
|
165
|
+
// In some cases, some needed parentheses were being lost without this.
|
|
166
|
+
arrowFunction.body = j.parenthesizedExpression(objectExpression);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function addCommentsToDeclaration(declaration, commentsToAdd) {
|
|
173
|
+
let commentsPath = declaration;
|
|
174
|
+
|
|
175
|
+
if (declaration.parentPath.node.type === 'ExportNamedDeclaration') {
|
|
176
|
+
commentsPath = declaration.parentPath;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!commentsPath.node.comments) {
|
|
180
|
+
commentsPath.node.comments = [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
commentsPath.node.comments.push(...commentsToAdd);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function addComments(j, path, commentsToAdd) {
|
|
187
|
+
j(path).closest(j.VariableDeclaration).forEach(declaration => {
|
|
188
|
+
addCommentsToDeclaration(declaration, commentsToAdd);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* @param {import('jscodeshift').FileInfo} file
|
|
193
|
+
* @param {import('jscodeshift').API} api
|
|
194
|
+
*/
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
function transformer(file, api, options) {
|
|
198
|
+
const j = api.jscodeshift;
|
|
199
|
+
const root = j(file.source);
|
|
200
|
+
const printOptions = options.printOptions || {
|
|
201
|
+
quote: 'single'
|
|
202
|
+
};
|
|
203
|
+
let importsChanged = false;
|
|
204
|
+
let foundCreateStyles = false;
|
|
205
|
+
let foundMakeStyles = false;
|
|
206
|
+
let foundWithStyles = false;
|
|
207
|
+
/**
|
|
208
|
+
* transform imports
|
|
209
|
+
*/
|
|
210
|
+
|
|
211
|
+
root.find(j.ImportDeclaration).forEach(path => {
|
|
212
|
+
const importSource = path.node.source.value;
|
|
213
|
+
|
|
214
|
+
if (importSource === '@material-ui/core/styles' || importSource === '@material-ui/core' || importSource === '@mui/styles') {
|
|
215
|
+
const specifiersToMove = [];
|
|
216
|
+
const specifiersToStay = [];
|
|
217
|
+
path.node.specifiers.forEach(specifier => {
|
|
218
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
219
|
+
if (specifier.imported.name === 'makeStyles') {
|
|
220
|
+
foundMakeStyles = true;
|
|
221
|
+
specifiersToMove.push(specifier);
|
|
222
|
+
} else if (specifier.imported.name === 'withStyles') {
|
|
223
|
+
foundWithStyles = true;
|
|
224
|
+
specifiersToMove.push(specifier);
|
|
225
|
+
} else if (specifier.imported.name === 'createStyles') {
|
|
226
|
+
foundCreateStyles = true;
|
|
227
|
+
} else {
|
|
228
|
+
specifiersToStay.push(specifier);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
if (specifiersToMove.length > 0) {
|
|
234
|
+
path.replace(j.importDeclaration(specifiersToMove, j.stringLiteral('tss-react/mui')), specifiersToStay.length > 0 ? j.importDeclaration(specifiersToStay, j.stringLiteral(importSource)) : undefined);
|
|
235
|
+
importsChanged = true;
|
|
236
|
+
}
|
|
237
|
+
} else if (importSource === '@material-ui/styles/makeStyles') {
|
|
238
|
+
foundMakeStyles = true;
|
|
239
|
+
path.replace(j.importDeclaration([j.importSpecifier(j.identifier('makeStyles'))], j.stringLiteral('tss-react/mui')));
|
|
240
|
+
importsChanged = true;
|
|
241
|
+
} else if (importSource === '@material-ui/styles/withStyles') {
|
|
242
|
+
foundWithStyles = true;
|
|
243
|
+
path.replace(j.importDeclaration([j.importSpecifier(j.identifier('withStyles'))], j.stringLiteral('tss-react/mui')));
|
|
244
|
+
importsChanged = true;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
if (!importsChanged) {
|
|
249
|
+
return file.source;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const isTypeScript = file.path.endsWith('.tsx') || file.path.endsWith('.ts');
|
|
253
|
+
|
|
254
|
+
if (foundMakeStyles) {
|
|
255
|
+
let clsxOrClassnamesName = null;
|
|
256
|
+
root.find(j.ImportDeclaration).forEach(path => {
|
|
257
|
+
const importSource = path.node.source.value;
|
|
258
|
+
|
|
259
|
+
if (importSource === 'clsx' || importSource === 'classnames') {
|
|
260
|
+
path.node.specifiers.forEach(specifier => {
|
|
261
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
262
|
+
clsxOrClassnamesName = specifier.local.name;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
j(path).remove();
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
/**
|
|
269
|
+
* Convert makeStyles syntax
|
|
270
|
+
*/
|
|
271
|
+
|
|
272
|
+
const styleHooks = [];
|
|
273
|
+
root.find(j.CallExpression, {
|
|
274
|
+
callee: {
|
|
275
|
+
name: 'makeStyles'
|
|
276
|
+
}
|
|
277
|
+
}).forEach(path => {
|
|
278
|
+
let paramsTypes = null;
|
|
279
|
+
|
|
280
|
+
if (foundCreateStyles) {
|
|
281
|
+
j(path).find(j.CallExpression, {
|
|
282
|
+
callee: {
|
|
283
|
+
name: 'createStyles'
|
|
284
|
+
}
|
|
285
|
+
}).replaceWith(createStylesPath => {
|
|
286
|
+
if (isTypeScript && createStylesPath.node.typeParameters && createStylesPath.node.typeParameters.params.length > 1) {
|
|
287
|
+
paramsTypes = createStylesPath.node.typeParameters.params[1];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return createStylesPath.node.arguments[0];
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const nestedKeys = [];
|
|
295
|
+
let makeStylesOptions = null;
|
|
296
|
+
|
|
297
|
+
if (path.node.arguments.length > 1) {
|
|
298
|
+
makeStylesOptions = path.node.arguments[1];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let stylesExpression = path.node.arguments[0];
|
|
302
|
+
const commentsToAdd = [];
|
|
303
|
+
transformStylesExpression(j, commentsToAdd, path.node.arguments[0], nestedKeys, newStylesExpression => {
|
|
304
|
+
stylesExpression = newStylesExpression;
|
|
305
|
+
});
|
|
306
|
+
addComments(j, path, commentsToAdd);
|
|
307
|
+
let makeStylesIdentifier = 'makeStyles';
|
|
308
|
+
|
|
309
|
+
if (isTypeScript && (nestedKeys.length > 0 || paramsTypes !== null)) {
|
|
310
|
+
let paramsTypeString = 'void';
|
|
311
|
+
|
|
312
|
+
if (paramsTypes !== null) {
|
|
313
|
+
paramsTypeString = j(paramsTypes).toSource(printOptions);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
let nestedKeysString = '';
|
|
317
|
+
|
|
318
|
+
if (nestedKeys.length > 0) {
|
|
319
|
+
const nestedKeysUnion = nestedKeys.join("' | '");
|
|
320
|
+
nestedKeysString = `, '${nestedKeysUnion}'`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
makeStylesIdentifier += `<${paramsTypeString}${nestedKeysString}>`;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
j(path).replaceWith(j.callExpression(j.callExpression(j.identifier(makeStylesIdentifier), makeStylesOptions === null ? [] : [makeStylesOptions]), [stylesExpression]));
|
|
327
|
+
}).closest(j.VariableDeclarator).forEach(path => {
|
|
328
|
+
styleHooks.push(path.node.id.name);
|
|
329
|
+
j(path).closest(j.ExportNamedDeclaration).forEach(() => {
|
|
330
|
+
const comments = [j.commentLine(` TODO jss-to-tss-react codemod: usages of this hook outside of this file will not be converted.`, true)];
|
|
331
|
+
addComments(j, path, comments);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
/**
|
|
335
|
+
* Convert classes assignment syntax in calls to the hook (e.g. useStyles) and
|
|
336
|
+
* convert usages of clsx or classnames to cx.
|
|
337
|
+
*/
|
|
338
|
+
|
|
339
|
+
styleHooks.forEach(hookName => {
|
|
340
|
+
root.find(j.CallExpression, {
|
|
341
|
+
callee: {
|
|
342
|
+
name: hookName
|
|
343
|
+
}
|
|
344
|
+
}).forEach(hookCall => {
|
|
345
|
+
if (hookCall.node.arguments.length === 1) {
|
|
346
|
+
const hookArg = hookCall.node.arguments[0];
|
|
347
|
+
|
|
348
|
+
if (hookArg.type === 'Identifier') {
|
|
349
|
+
const secondArg = j.objectExpression([]);
|
|
350
|
+
secondArg.properties.push(j.objectProperty(j.identifier('props'), j.identifier(hookArg.name)));
|
|
351
|
+
hookCall.node.arguments.push(secondArg);
|
|
352
|
+
} else if (hookArg.properties) {
|
|
353
|
+
const hookArgPropsMinusClasses = [];
|
|
354
|
+
let classesProp = null;
|
|
355
|
+
hookArg.properties.forEach(hookProp => {
|
|
356
|
+
if (hookProp.key.name === 'classes') {
|
|
357
|
+
classesProp = hookProp;
|
|
358
|
+
} else {
|
|
359
|
+
hookArgPropsMinusClasses.push(hookProp);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
if (classesProp !== null) {
|
|
364
|
+
if (hookArgPropsMinusClasses.length === 0) {
|
|
365
|
+
hookCall.node.arguments[0] = j.identifier('undefined');
|
|
366
|
+
} else {
|
|
367
|
+
hookArg.properties = hookArgPropsMinusClasses;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const secondArg = j.objectExpression([]);
|
|
371
|
+
secondArg.properties.push(j.objectProperty(j.identifier('props'), j.objectExpression([j.objectProperty(j.identifier('classes'), classesProp.value)])));
|
|
372
|
+
hookCall.node.arguments.push(secondArg);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}).closest(j.VariableDeclarator).forEach(path => {
|
|
377
|
+
let foundClsxOrClassnamesUsage = false;
|
|
378
|
+
const classesName = path.node.id.name;
|
|
379
|
+
const classesAssign = classesName === 'classes' ? 'classes' : `classes: ${classesName}`;
|
|
380
|
+
|
|
381
|
+
if (clsxOrClassnamesName !== null) {
|
|
382
|
+
j(path).closestScope().find(j.CallExpression, {
|
|
383
|
+
callee: {
|
|
384
|
+
name: clsxOrClassnamesName
|
|
385
|
+
}
|
|
386
|
+
}).forEach(callPath => {
|
|
387
|
+
callPath.node.callee.name = 'cx';
|
|
388
|
+
foundClsxOrClassnamesUsage = true;
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (foundClsxOrClassnamesUsage) {
|
|
393
|
+
path.node.id.name = `{ ${classesAssign}, cx }`;
|
|
394
|
+
} else {
|
|
395
|
+
path.node.id.name = `{ ${classesAssign} }`;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
root.find(j.ExportDefaultDeclaration, {
|
|
399
|
+
declaration: {
|
|
400
|
+
name: hookName
|
|
401
|
+
}
|
|
402
|
+
}).forEach(path => {
|
|
403
|
+
const comments = [j.commentLine(` TODO jss-to-tss-react codemod: usages of this hook outside of this file will not be converted.`, true)];
|
|
404
|
+
addCommentsToDeclaration(path, comments);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (foundWithStyles) {
|
|
410
|
+
/**
|
|
411
|
+
* Convert withStyles syntax
|
|
412
|
+
*/
|
|
413
|
+
const styleVariables = [];
|
|
414
|
+
root.find(j.CallExpression, {
|
|
415
|
+
callee: {
|
|
416
|
+
type: 'CallExpression',
|
|
417
|
+
callee: {
|
|
418
|
+
name: 'withStyles'
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}).replaceWith(path => {
|
|
422
|
+
const withStylesCall = path.node.callee;
|
|
423
|
+
const styles = path.node.callee.arguments[0];
|
|
424
|
+
|
|
425
|
+
if (styles.type === 'Identifier') {
|
|
426
|
+
styleVariables.push(styles.name);
|
|
427
|
+
} else {
|
|
428
|
+
const nestedKeys = [];
|
|
429
|
+
const commentsToAdd = [];
|
|
430
|
+
transformStylesExpression(j, commentsToAdd, styles, nestedKeys, newStylesExpression => {
|
|
431
|
+
path.node.callee.arguments[0] = newStylesExpression;
|
|
432
|
+
});
|
|
433
|
+
addComments(j, path, commentsToAdd);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const component = path.node.arguments[0];
|
|
437
|
+
withStylesCall.arguments.unshift(component);
|
|
438
|
+
return withStylesCall;
|
|
439
|
+
});
|
|
440
|
+
styleVariables.forEach(styleVar => {
|
|
441
|
+
root.find(j.VariableDeclarator, {
|
|
442
|
+
id: {
|
|
443
|
+
name: styleVar
|
|
444
|
+
}
|
|
445
|
+
}).forEach(path => {
|
|
446
|
+
const nestedKeys = [];
|
|
447
|
+
const commentsToAdd = [];
|
|
448
|
+
transformStylesExpression(j, commentsToAdd, path.node.init, nestedKeys, newStylesExpression => {
|
|
449
|
+
path.node.init = newStylesExpression;
|
|
450
|
+
});
|
|
451
|
+
addComments(j, path, commentsToAdd);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return root.toSource(printOptions);
|
|
457
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = ComponentUsingStyles;
|
|
9
|
+
|
|
10
|
+
var _react = _interopRequireDefault(require("react"));
|
|
11
|
+
|
|
12
|
+
var _styles = require("@material-ui/core/styles");
|
|
13
|
+
|
|
14
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
|
+
|
|
16
|
+
var _InnerComponent;
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
Sandboxes for verifying correct behavior:
|
|
20
|
+
JSS - https://codesandbox.io/s/case1-jss-dedp2f?file=/src/App.js
|
|
21
|
+
TSS - https://codesandbox.io/s/case1-tss-s0z7tx?file=/src/App.js
|
|
22
|
+
*/
|
|
23
|
+
const useStyles = (0, _styles.makeStyles)({
|
|
24
|
+
test: {
|
|
25
|
+
backgroundColor: "purple",
|
|
26
|
+
color: "white"
|
|
27
|
+
}
|
|
28
|
+
}, {
|
|
29
|
+
name: "TestName"
|
|
30
|
+
});
|
|
31
|
+
const useStyles2 = (0, _styles.makeStyles)(() => ({
|
|
32
|
+
test2: {
|
|
33
|
+
backgroundColor: "blue",
|
|
34
|
+
color: "lime"
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
function InnerComponent() {
|
|
39
|
+
const classes = useStyles2();
|
|
40
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
41
|
+
className: classes.test2,
|
|
42
|
+
children: "Inner Test"
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ComponentUsingStyles(props) {
|
|
47
|
+
const classes = useStyles();
|
|
48
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
49
|
+
className: classes.test,
|
|
50
|
+
children: ["Test", _InnerComponent || (_InnerComponent = /*#__PURE__*/(0, _jsxRuntime.jsx)(InnerComponent, {}))]
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = ComponentUsingStyles;
|
|
9
|
+
|
|
10
|
+
var _react = _interopRequireDefault(require("react"));
|
|
11
|
+
|
|
12
|
+
var _core = require("@material-ui/core");
|
|
13
|
+
|
|
14
|
+
var _clsx = _interopRequireDefault(require("clsx"));
|
|
15
|
+
|
|
16
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
17
|
+
|
|
18
|
+
var _InnerComponent;
|
|
19
|
+
|
|
20
|
+
const useStyles = (0, _core.makeStyles)(() => ({
|
|
21
|
+
test: {
|
|
22
|
+
backgroundColor: "purple",
|
|
23
|
+
color: "white",
|
|
24
|
+
"&$qualifier": {
|
|
25
|
+
textDecoration: "underline"
|
|
26
|
+
},
|
|
27
|
+
"&$qualifier$qualifier2": {
|
|
28
|
+
fontStyle: "italic"
|
|
29
|
+
},
|
|
30
|
+
"&$qualifier2 .testStuffInBetween $qualifier": {
|
|
31
|
+
color: "brown"
|
|
32
|
+
},
|
|
33
|
+
"&$qualifier:hover": {
|
|
34
|
+
backgroundColor: "red"
|
|
35
|
+
},
|
|
36
|
+
"&$qualifier2:not(:hover)": {
|
|
37
|
+
fontWeight: 700
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
qualifier: {},
|
|
41
|
+
qualifier2: {}
|
|
42
|
+
}));
|
|
43
|
+
const useStyles2 = (0, _core.makeStyles)({
|
|
44
|
+
test2: {
|
|
45
|
+
backgroundColor: "blue",
|
|
46
|
+
color: "lime"
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function InnerComponent() {
|
|
51
|
+
const classes = useStyles2();
|
|
52
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
53
|
+
className: classes.test2,
|
|
54
|
+
children: "Inner Test"
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function ComponentUsingStyles(props) {
|
|
59
|
+
const classes = useStyles(props);
|
|
60
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
61
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
62
|
+
className: classes.test,
|
|
63
|
+
children: ["Test", _InnerComponent || (_InnerComponent = /*#__PURE__*/(0, _jsxRuntime.jsx)(InnerComponent, {}))]
|
|
64
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
65
|
+
className: (0, _clsx.default)(classes.test, classes.qualifier),
|
|
66
|
+
children: "Qualifier Test"
|
|
67
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
68
|
+
className: (0, _clsx.default)(classes.test, classes.qualifier2),
|
|
69
|
+
children: "Qualifier 2 Test"
|
|
70
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
71
|
+
className: (0, _clsx.default)(classes.test, classes.qualifier, classes.qualifier2),
|
|
72
|
+
children: "Qualifier & Qualifier 2 Test"
|
|
73
|
+
})]
|
|
74
|
+
});
|
|
75
|
+
}
|