Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[codemod] Add jss to tss-react codemod #31802

Merged
merged 48 commits into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
3823dcd
[codemod] Add jss to tss-react codemod
ryancogswell Mar 14, 2022
430fc65
Prettier
ryancogswell Mar 14, 2022
345f302
Add second `makeStyles` call and add sandbox links
ryancogswell Mar 15, 2022
070ade2
Add test cases for additional import possibilities
ryancogswell Mar 25, 2022
113abbb
Add failing test for TypeScript with nested selectors
ryancogswell Mar 26, 2022
22bf666
Remove createStyles from imports
ryancogswell Mar 26, 2022
6267946
Remove usages of createStyles
ryancogswell Mar 26, 2022
c4ca5e3
Fully handle one TypeScript case with nested selectors
ryancogswell Mar 28, 2022
8f6f5db
Add and handle additional cases for nested selectors
ryancogswell Mar 28, 2022
5c22b87
Address lint errors
ryancogswell Mar 28, 2022
8723b10
Handle nested selectors without arrow function
ryancogswell Mar 28, 2022
1142657
Add naive withStyles transformation
ryancogswell Mar 29, 2022
c556b4b
Address lint error
ryancogswell Mar 29, 2022
05f5e93
Handle nested selectors for withStyles
ryancogswell Mar 29, 2022
f7e1474
Handle inline styles for withStyles
ryancogswell Mar 29, 2022
94f3f80
Add test case with an arrow function with a code block
ryancogswell Mar 30, 2022
814513e
Add support for `@material-ui/styles/makeStyles` imports
ryancogswell Mar 30, 2022
a48db98
Add documentation for jss-to-tss-react codemod
ryancogswell Mar 30, 2022
7ea397d
Prettier
ryancogswell Mar 30, 2022
6fe7ccc
Replace clsx and classnames with cx and transform nested rule referen…
ryancogswell Apr 15, 2022
4cc5800
Fix lint error
ryancogswell Apr 15, 2022
f159bcb
Add clsx replacement to docs example
ryancogswell Apr 16, 2022
2ee9c61
Use single quotes consistently and tweak some test cases
ryancogswell Apr 16, 2022
5cb51af
Update migration guide example to include clsx replacement
ryancogswell Apr 16, 2022
d776428
Fix lint error
ryancogswell Apr 16, 2022
95e1ac2
Prettier
ryancogswell Apr 16, 2022
332ca88
Allow for a block inside makeStyles that returns something other than…
ryancogswell Apr 18, 2022
a82261b
Handle spread elements within object expressions and renamed "classes…
ryancogswell Apr 18, 2022
cdf74bc
Handle spread elements within a style rule
ryancogswell Apr 18, 2022
32d2a39
Handle style sheet "name" option
ryancogswell Apr 18, 2022
d14da5e
Handle at least some cases of params
ryancogswell Apr 19, 2022
38b4bfa
Address lint errors
ryancogswell Apr 19, 2022
f7273a6
Handle another props case
ryancogswell Apr 19, 2022
15fbd0d
Add todo comments for some unsupported cases
ryancogswell Apr 19, 2022
ebc6a86
Improve information in todo comments
ryancogswell Apr 19, 2022
0d2f2c8
Update migration guide with more complete jss-to-tss-react codemod info
ryancogswell Apr 19, 2022
f84957c
Merge branch 'master' into jssToTssReact
ryancogswell Apr 19, 2022
a9f87f3
Cleanup after resolving merge conflicts
ryancogswell Apr 19, 2022
28c1703
Add todo comment for exported hooks since usages won't be converted
ryancogswell Apr 21, 2022
dde9f3c
Address lint error
ryancogswell Apr 21, 2022
734d362
Fix first `useStyles` parameter when passing `classes` prop and no pa…
ryancogswell Apr 25, 2022
5096566
Detect default export of style hook to add comment
ryancogswell May 2, 2022
cbc8124
Update documentation links to go to docs.tss-react.dev instead of GitHub
ryancogswell May 3, 2022
59b8a45
Merge branch 'master' into jssToTssReact
ryancogswell May 8, 2022
d625dc5
Apply master callout changes in the migration guide
mnajdova May 10, 2022
f20875f
Add documentation about specific scenarios that are not supported by …
ryancogswell May 10, 2022
5ce079c
Reapply my migration guide changes that were overwritten when fixing …
ryancogswell May 10, 2022
264794f
Merge branch 'master' into jssToTssReact
mnajdova May 11, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# However, in order to prevent issues, they are ignored here.
.DS_STORE
.idea
# IntelliJ IDEA module file
*.iml
.vscode/*
!.vscode/launch.json
*.log
Expand Down
152 changes: 78 additions & 74 deletions docs/data/material/guides/migration-v4/migration-v4.md
Original file line number Diff line number Diff line change
Expand Up @@ -2917,7 +2917,7 @@ Note: This API will not work if you are [using `styled-components` as underlying
:::

The API is similar to JSS `makeStyles` but, under the hood, it uses `@emotion/react`.
It is also features a much better TypeScript support than v4's `makeStyles`.
It also features much better TypeScript support than v4's `makeStyles`.

In order to use it, you'll need to add it to your project's dependencies:

Expand Down Expand Up @@ -2951,7 +2951,15 @@ yarn add tss-react
);
```

Then here is one example:
#### Codemod

We provide [a codemod](https://github.com/mui/material-ui/blob/master/packages/mui-codemod/README.md#jss-to-tss-react) to help migrate JSS styles to the `tss-react` API.

```sh
npx @mui/codemod v5.0.0/jss-to-tss-react <path>
```

**Example transformation**:

```diff
import React from 'react';
Expand Down Expand Up @@ -2988,35 +2996,48 @@ Then here is one example:
export default Apply;
```

If you were using the `$` syntax, the transformation would look like this:
If you were using the `$` syntax and `clsx` to combine multiple CSS classes,
the transformation would look like this:

```diff
import * as React from 'react';
-import makeStyles from '@material-ui/styles/makeStyles';
-import { makeStyles } from '@material-ui/core/styles';
-import clsx from 'clsx';
+import { makeStyles } from 'tss-react/mui';

-const useStyles = makeStyles((theme) => {
+const useStyles = makeStyles<void, 'child'>()((_theme, _params, classes) => ({
-const useStyles = makeStyles((theme) => ({
+const useStyles = makeStyles<void, 'child' | 'small'>()((theme, _params, classes) => ({
parent: {
padding: 30,
- '&:hover $child': {
+ [`&:hover .${classes.child}`]: {
backgroundColor: 'red',
},
},
small: {},
child: {
backgroundColor: 'blue',
height: 50,
- '&$small': {
+ [`&.${classes.small}`]: {
backgroundColor: 'lightblue',
height: 30
}
},
});
}));

function App() {
- const classes = useStyles();
+ const { classes } = useStyles();

+ const { classes, cx } = useStyles();
return (
<div className={classes.parent}>
<div className={classes.children}>
Background turns red when the mouse is hover the parent
<div className={classes.child}>
Background turns red when the mouse hovers over the parent.
</div>
- <div className={clsx(classes.child, classes.small)}>
+ <div className={cx(classes.child, classes.small)}>
Background turns red when the mouse hovers over the parent.
I am smaller than the other child.
</div>
</div>
);
Expand All @@ -3026,47 +3047,54 @@ If you were using the `$` syntax, the transformation would look like this:
```

:::warning
**Note:** In plain JS projects (not using TypeScript), remove `<void, 'child'>`.
**Note:** In plain JS projects (not using TypeScript), remove `<void, 'child' | 'small'>`.
:::

Now, a comprehensive example using both the `$` syntax, `useStyles()` parameters
and [an explicit name for the stylesheet](https://github.com/garronej/tss-react#naming-the-stylesheets-useful-for-debugging).
Now, a comprehensive example using the `$` syntax, `useStyles()` parameters,
merging in classes from a `classes` prop ([see doc](https://docs.tss-react.dev/your-own-classes-prop)),
and [an explicit name for the stylesheet](https://docs.tss-react.dev/page-1/makestyles-usestyles#naming-the-stylesheets-useful-for-debugging-and-theme-style-overrides).

```diff
-import clsx from 'clsx';
-import { makeStyles, createStyles } from '@material-ui/core/styles';
+import { makeStyles } from 'tss-react/mui';

-const useStyles = makeStyles((theme) => createStyles<
- 'root' | 'small' | 'child', { color: 'primary' | 'secondary' }
->({
+const useStyles = makeStyles<
+ { color: 'primary' | 'secondary' }, 'child' | 'small'
+>({ name: 'App' })((theme, { color }, classes) => ({
- root: ({ color })=> ({
- 'root' | 'small' | 'child', {color: 'primary' | 'secondary', padding: number}
->
-({
- root: ({color, padding}) => ({
+const useStyles = makeStyles<{color: 'primary' | 'secondary', padding: number}, 'child' | 'small'>({name: 'App'})((theme, { color, padding }, classes) => ({
+ root: {
padding: 30,
- '&:hover .child': {
padding: padding,
- '&:hover $child': {
+ [`&:hover .${classes.child}`]: {
backgroundColor: theme.palette[color].main,
}
- }),
+ },
small: {},
child: {
border: '1px solid black',
height: 50,
- '&.small': {
small: {},
child: {
border: '1px solid black',
height: 50,
- '&$small': {
+ [`&.${classes.small}`]: {
height: 30
}
}
-}, { name: 'App' });
height: 30
}
}
-}), {name: 'App'});
+}));

function App() {
- const classes = useStyles({ color: 'primary' });
+ const { classes, cx } = useStyles({ color: 'primary' });
function App({classes: classesProp}: {classes?: any}) {
- const classes = useStyles({color: 'primary', padding: 30, classes: classesProp});
+ const { classes, cx } = useStyles({
+ color: 'primary',
+ padding: 30
+ }, {
+ props: {
+ classes: classesProp
+ }
+ });

return (
<div className={classes.root}>
Expand All @@ -3078,30 +3106,40 @@ and [an explicit name for the stylesheet](https://github.com/garronej/tss-react#
The Background take the primary theme color when the mouse hovers the parent.
I am smaller than the other child.
</div>
</div>
);
}
</div>
);
}

export default App;
export default App;
```

After running the codemod, search your code for "TODO jss-to-tss-react codemod" to find cases that
the codemod could not handle reliably; though there may be cases beyond those with TODO comments that
are not handled fully by the codemod particularly if parts of the styles are returned by functions.
If the styles buried within a function use the `$` syntax or `useStyles` params, then those styles won't
be migrated appropriately.

:::error
**WARNING**: You should drop [`clsx`](https://www.npmjs.com/package/clsx) in favor of [`cx`](https://emotion.sh/docs/@emotion/css#cx).
The key advantage of `cx` is that it detects emotion generated class names ensuring styles are overwritten in the correct order.
The default precedence of styles from multiple CSS classes is different between JSS and tss-react and some manual re-ordering of `cx` parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've pushed a commit here for applying some changes from master. Would be great if you can double check if this is the changed content on the migration guide (LGTM btw)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did a merge of those migration guide changes yesterday, but I should have installed the merge tool that I use at work onto my home computer. I used my IDE's merge resolution instead and looks like it didn't behave the way I thought and lost a lot of the changes that I thought I was merging in. Thanks for fixing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of my previous migration guide changes were lost during your fix of my bad merge. I've reapplied those changes now.

may be necessary (see [this issue comment](https://github.com/mui/material-ui/pull/31802#issuecomment-1093478971) for more details).
:::

:::warning
**Note**: To ensure that your class names always includes the actual name of your components, you can provide the `name` as an implicitly named key (`name: { App }`).
[See doc](https://docs.tss-react.dev/page-1/makestyles-usestyles#naming-the-stylesheets-useful-for-debugging-and-theme-style-overrides).
You may end up with eslint warnings [like this one](https://user-images.githubusercontent.com/6702424/148657837-eae48942-fb86-4516-abe4-5dc10f44f0be.png) if you deconstruct more than one item.
Don't hesitate to disable `eslint(prefer-const)`, [like this](https://github.com/thieryw/gitlanding/blob/b2b0c71d95cfd353979c86dfcfa1646ef1665043/.eslintrc.js#L17) in a regular project, or [like this](https://github.com/InseeFrLab/onyxia-web/blob/a264ec6a6a7110cb1a17b2e22cc0605901db6793/package.json#L133) in a CRA.
:::

#### `withStyles()`

`tss-react` also features a [type-safe implementation](https://github.com/garronej/tss-react#withstyles) of [v4's `withStyles()`](https://v4.mui.com/styles/api/#withstyles-styles-options-higher-order-component).
`tss-react` also features a [type-safe implementation](https://docs.tss-react.dev/page-1/withstyles) of [v4's `withStyles()`](https://v4.mui.com/styles/api/#withstyles-styles-options-higher-order-component).

:::info
**Note:** The equivalent of the `$` syntax is also supported in tss's `withStyles()`.
[See doc](https://github.com/garronej/tss-react#nested-selector-with-the-withstyles-api).
[See doc](https://docs.tss-react.dev/nested-selectors#withstyles).
:::

```diff
Expand Down Expand Up @@ -3131,40 +3169,6 @@ The key advantage of `cx` is that it detects emotion generated class names ensur
export default MyCustomButton;
```

#### Overriding styles - `classes` prop

[Documentation of the feature in v4](https://v4.mui.com/styles/advanced/#makestyles) - [Equivalent in `tss-react`](https://docs.tss-react.dev/your-own-classes-prop)

```diff
-import { makeStyles } from '@material-ui/core/styles';
+import { makeStyles } from 'tss-react/mui';

-const useStyles = makeStyles({
+const useStyles = makeStyles()({
root: {}, // a style rule
label: {}, // a nested style rule
});

function Nested(props) {
- const classes = useStyles(props);
+ const { classes } = useStyles(undefined, { props });
//NOTE: Only the classes will be read from props, you could write { props: { classes: props.classes } }
//Example with types: https://docs.tss-react.dev/your-own-classes-prop

return (
<button className={classes.root}>
<span className={classes.label}> // 'tss-xxxx-label my-label'
nested
</span>
</button>
);
}

function Parent() {
return <Nested classes={{ label: 'my-label' }} />
}
```

#### Theme style overrides

[Global theme overrides](https://v4.mui.com/customization/components/#global-theme-override) is supported out of the box by TSS.
Expand Down
82 changes: 82 additions & 0 deletions packages/mui-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,88 @@ You can find more details about this breaking change in [the migration guide](ht
> **Note:** This approach converts the first element in the return statement into styled component but also increases CSS specificity to override nested children.
> This codemod should be adopted after handling all breaking changes, [check out the migration documentation](https://mui.com/material-ui/guides/migration-v4/)

#### `jss-to-tss-react`

Migrate JSS styling with `makeStyles` or `withStyles` to the corresponding `tss-react` API.

```diff
-import clsx from 'clsx';
-import {makeStyles, createStyles} from '@material-ui/core/styles';
+import { makeStyles } from 'tss-react/mui';

-const useStyles = makeStyles((theme) => createStyles<
ryancogswell marked this conversation as resolved.
Show resolved Hide resolved
- 'root' | 'small' | 'child', {color: 'primary' | 'secondary', padding: number}
->
-({
- root: ({color, padding}) => ({
+const useStyles = makeStyles<{color: 'primary' | 'secondary', padding: number}, 'child' | 'small'>({name: 'App'})((theme, { color, padding }, classes) => ({
ryancogswell marked this conversation as resolved.
Show resolved Hide resolved
+ root: {
padding: padding,
- '&:hover $child': {
+ [`&:hover .${classes.child}`]: {
backgroundColor: theme.palette[color].main,
}
- }),
+ },
small: {},
child: {
border: '1px solid black',
height: 50,
- '&$small': {
+ [`&.${classes.small}`]: {
height: 30
}
}
-}), {name: 'App'});
+}));

function App({classes: classesProp}: {classes?: any}) {
ryancogswell marked this conversation as resolved.
Show resolved Hide resolved
- const classes = useStyles({color: 'primary', padding: 30, classes: classesProp});
+ const { classes, cx } = useStyles({
+ color: 'primary',
+ padding: 30
+ }, {
+ props: {
+ classes: classesProp
+ }
+ });
ryancogswell marked this conversation as resolved.
Show resolved Hide resolved

return (
<div className={classes.root}>
<div className={classes.child}>
The Background take the primary theme color when the mouse hovers the parent.
</div>
- <div className={clsx(classes.child, classes.small)}>
+ <div className={cx(classes.child, classes.small)}>
The Background take the primary theme color when the mouse hovers the parent.
I am smaller than the other child.
</div>
</div>
);
}

export default App;
```

```sh
npx @mui/codemod v5.0.0/jss-to-tss-react <path>
```

The following scenarios are not currently handled by this codemod and will be marked with a
"TODO jss-to-tss-react codemod" comment:

- If the hook returned by `makeStyles` (e.g. `useStyles`) is exported and used in another file,
the usages in other files will not be converted.
- Arrow functions as the value for a CSS prop will not be converted. Arrow functions **are**
supported at the rule level, though with some caveats listed below.
- In order for arrow functions at the rule level to be converted, the parameter must use object
destructuring (e.g. `root: ({color, padding}) => (...)`). If the parameter is not destructured
(e.g. `root: (props) => (...)`), it will not be converted.
- If an arrow function at the rule level contains a code block (i.e. contains an explicit `return`
statement) rather than just an object expression, it will not be converted.

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).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great if we can list here things that are not supported, for example the "TODOs" added in expected-todo-comments.js.

This will make sure we are transparent and will help future contributors to take features one by one and add them as part of the codemod.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added documentation about the scenarios that produce TODO comments.


#### `link-underline-hover`

Apply `underline="hover"` to `<Link />` that does not define `underline` prop (to get the same behavior as in v4).
Expand Down
Loading