Skip to content

Commit db10da2

Browse files
authored
Merge pull request #320 from codemod-com/codemods-v3
Add react 19 codemods and recommend `codemod` command
2 parents 243edf6 + 1a4ea2e commit db10da2

File tree

111 files changed

+2737
-235
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+2737
-235
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
2-
parser: 'babel-eslint',
2+
parser: '@typescript-eslint/parser',
33

44
extends: './node_modules/fbjs-scripts/eslint/.eslintrc.js',
55

LEGACY.md

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
## Legacy `react-codemod`
2+
3+
All codemods in this repo can be run using the legacy `react-codemod` command, which runs the codemods via jscodeshift directly.
4+
5+
### Usage
6+
7+
We recommend using the [`codemod`](github.com/reactjs/react-codemod) command for improved experience and support.
8+
9+
To use `react-codemod` directly:
10+
11+
`npx react-codemod <transform> <path> [...options]`
12+
* `transform` - name of transform, see available transforms below.
13+
* `path` - files or directory to transform
14+
* use the `--dry` option for a dry-run and use `--print` to print the output for comparison
15+
16+
This will start an interactive wizard, and then run the specified transform.
17+
18+
### Included Transforms
19+
20+
#### `remove-context-provider`
21+
22+
Converts `Context.Provider` JSX opening and closing elements into `Context`.
23+
24+
```sh
25+
npx react-codemod remove-context-provider <path>
26+
```
27+
28+
#### `remove-forward-ref`
29+
30+
Removes usages of `forwardRef`.
31+
32+
```sh
33+
npx react-codemod remove-forward-ref <path>
34+
```
35+
36+
#### `use-context-hook`
37+
38+
```sh
39+
npx react-codemod use-context-hook <path>
40+
```
41+
42+
#### `replace-act-import`
43+
44+
```sh
45+
npx react-codemod replace-act-import <path>
46+
```
47+
48+
#### `replace-string-ref`
49+
50+
Replaces deprecated string refs with callback refs.
51+
52+
```sh
53+
npx react-codemod replace-string-ref <path>
54+
```
55+
56+
#### `replace-use-form-state`
57+
58+
Replaces usages of useFormState() to use useActionState().
59+
60+
```sh
61+
npx react-codemod replace-use-form-state <path>
62+
```
63+
64+
#### `replace-reactdom-render`
65+
66+
Replaces usages of ReactDom.render() with createRoot(node).render().
67+
68+
```sh
69+
npx react-codemod replace-reactdom-render <path>
70+
```
71+
72+
#### `create-element-to-jsx`
73+
74+
Converts calls to `React.createElement` into JSX elements.
75+
76+
```sh
77+
npx react-codemod create-element-to-jsx <path>
78+
```
79+
80+
#### `error-boundaries`
81+
82+
Renames the experimental `unstable_handleError` lifecycle hook to `componentDidCatch`.
83+
84+
```sh
85+
npx react-codemod error-boundaries <path>
86+
```
87+
88+
#### `findDOMNode`
89+
90+
Updates `this.getDOMNode()` or `this.refs.foo.getDOMNode()` calls inside of
91+
`React.createClass` components to `React.findDOMNode(foo)`. Note that it will
92+
only look at code inside of `React.createClass` calls and only update calls on
93+
the component instance or its refs. You can use this script to update most calls
94+
to `getDOMNode` and then manually go through the remaining calls.
95+
96+
```sh
97+
npx react-codemod findDOMNode <path>
98+
```
99+
100+
#### `manual-bind-to-arrow`
101+
102+
Converts manual function bindings in a class (e.g., `this.f = this.f.bind(this)`) to arrow property initializer functions (e.g., `f = () => {}`).
103+
104+
```sh
105+
npx react-codemod manual-bind-to-arrow <path>
106+
```
107+
108+
#### `pure-component`
109+
110+
Converts ES6 classes that only have a render method, only have safe properties
111+
(statics and props), and do not have refs to Functional Components.
112+
113+
The wizard will ask for 2 options -
114+
115+
* **Use arrow functions?**: converts to arrow function. Converts to `function` by default.
116+
* **Destructure props?**: will destructure props in the argument where it is safe to do so.
117+
118+
```sh
119+
npx react-codemod pure-component <path>
120+
```
121+
122+
#### `pure-render-mixin`
123+
124+
Removes `PureRenderMixin` and inlines `shouldComponentUpdate` so that the ES2015
125+
class transform can pick up the React component and turn it into an ES2015
126+
class. NOTE: This currently only works if you are using the master version
127+
(>0.13.1) of React as it is using `React.addons.shallowCompare`
128+
129+
```sh
130+
npx react-codemod pure-render-mixin <path>
131+
```
132+
133+
* The wizard will ask to optionally override `mixin-name`, and look for it
134+
instead of `PureRenderMixin`. Note that it is not possible to use a
135+
namespaced name for the mixin. `mixins: [React.addons.PureRenderMixin]` will
136+
not currently work.
137+
138+
#### `React-PropTypes-to-prop-types`
139+
140+
Replaces `React.PropTypes` references with `prop-types` and adds the appropriate `import` or `require` statement. This codemod is intended for React 15.5+.
141+
142+
```sh
143+
npx react-codemod React-PropTypes-to-prop-types <path>
144+
```
145+
146+
* In addition to running the above codemod you will also need to install the `prop-types` NPM package.
147+
148+
#### `rename-unsafe-lifecycles`
149+
150+
Adds `UNSAFE_` prefix for deprecated lifecycle hooks. (For more information about this codemod, see [React RFC #6](https://github.com/reactjs/rfcs/pull/6))
151+
152+
```sh
153+
npx react-codemod rename-unsafe-lifecycles <path>
154+
```
155+
156+
#### `react-to-react-dom`
157+
158+
Updates code for the split of the `react` and `react-dom` packages (e.g.,
159+
`React.render` to `ReactDOM.render`). It looks for `require('react')` and
160+
replaces the appropriate property accesses using `require('react-dom')`. It does
161+
not support ES6 modules or other non-CommonJS systems. We recommend performing
162+
the `findDOMNode` conversion first.
163+
164+
165+
```sh
166+
npx react-codemod react-to-react-dom <path>
167+
```
168+
169+
* After running the automated codemod, you may want to run a regex-based
170+
find-and-replace to remove extra whitespace between the added requires, such
171+
as `codemod.py -m -d src --extensions js '(var
172+
React\s*=\s*require\(.react.\);)\n\n(\s*var ReactDOM)' '\1\n\2'` using
173+
https://github.com/facebook/codemod.
174+
175+
#### `React-DOM-to-react-dom-factories`
176+
177+
Converts calls like `React.DOM.div(...)` to `React.createElement('div', ...)`.
178+
179+
```sh
180+
npx react-codemod React-DOM-to-react-dom-factories <path>
181+
```
182+
183+
#### `ReactNative-View-propTypes`
184+
185+
Replaces `View.propTypes` references with `ViewPropTypes` and adds the appropriate `import` or `require` statement. This codemod is intended for ReactNative 44+.
186+
187+
```sh
188+
npx react-codemod ReactNative-View-propTypes <path>
189+
```
190+
191+
#### `sort-comp`
192+
193+
Reorders React component methods to match the [ESLint](http://eslint.org/)
194+
[react/sort-comp
195+
rule](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md). (Defaults to ordering of the [Airbnb style
196+
guide](https://github.com/airbnb/javascript/blob/7684892951ef663e1c4e62ad57d662e9b2748b9e/packages/eslint-config-airbnb/rules/react.js#L122-L134).
197+
198+
`react-codemod`:
199+
```sh
200+
npx react-codemod sort-comp <path>
201+
```
202+
203+
#### `update-react-imports`
204+
205+
[As of Babel 7.9.0](https://babeljs.io/blog/2020/03/16/7.9.0#a-new-jsx-transform-11154-https-githubcom-babel-babel-pull-11154), when using `runtime: automatic` in `@babel/preset-react` or `@babel/plugin-transform-react-jsx`, you will not need to explicitly import React for compiling jsx. This codemod removes the redundant import statements. It also converts default imports (`import React from 'react'`) to named imports (e.g. `import { useState } from 'react'`).
206+
207+
The wizard will ask for 1 option -
208+
209+
* **Destructure namespace imports as well?**: If chosen, *namespace* imports like `import * as React` will *also* be converted. By default, it's false, so only default imports (`import React`) are converted.
210+
211+
```sh
212+
npx react-codemod update-react-imports <path>
213+
```
214+
215+
### Explanation of the new ES2015 class transform with property initializers
216+
1. Determine if mixins are convertible. We only transform a `createClass` call to an ES6 class component when:
217+
- There are no mixins on the class, or
218+
- `options['pure-component']` is true, the `mixins` property is an array and it _only_ contains pure render mixin (the specific module name can be specified using `options['mixin-module-name']`, which defaults to `react-addons-pure-render-mixin`)
219+
2. Ignore components that:
220+
- Call deprecated APIs. This is very defensive, if the script finds any identifiers called `isMounted`, `getDOMNode`, `replaceProps`, `replaceState` or `setProps` it will skip the component
221+
- Explicitly call `this.getInitialState()` and/or `this.getDefaultProps()` since an ES6 class component will no longer have these methods
222+
- Use `arguments` in methods since arrow functions don't have `arguments`. Also please notice that `arguments` should be [very carefully used](https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments) and it's generally better to switch to spread (`...args`) instead
223+
- Have inconvertible `getInitialState()`. Specifically if you have variable declarations like `var props = ...` and the right hand side is not `this.props` then we can't inline the state initialization in the `constructor` due to variable shadowing issues
224+
- Have non-primitive right hand side values (like `foo: getStuff()`) in the class spec
225+
3. Transform it to an ES6 class component
226+
1. Replace `var A = React.createClass(spec)` with `class A extends React.Component {spec}`. If a component uses pure render mixin and passes the mixins test (as described above), it will extend `React.PureComponent` instead
227+
- Remove the `require`/`import` statement that imports pure render mixin when it's no longer being referenced
228+
2. Pull out all statics defined on `statics` plus the few special cased statics like `childContextTypes`, `contextTypes`, `displayName`, `getDefaultProps()`, and `propTypes` and transform them to `static` properties (`static propTypes = {...};`)
229+
- If `getDefaultProps()` is simple (i.e. it only contains a return statement that returns something) it will be converted to a simple assignment (`static defaultProps = ...;`). Otherwise an IIFE (immediately-invoked function expression) will be created (`static defaultProps = function() { ... }();`). Note that this means that the function will be executed only a single time per app-lifetime. In practice this hasn't caused any issues — `getDefaultProps` should not contain any side-effects
230+
3. Transform `getInitialState()`
231+
- If there's no `getInitialState()` or the `getInitialState()` function is simple (i.e., it only contains a return statement that returns something) then we don't need a constructor; `state` will be lifted to a property initializer (`state = ...;`)
232+
- However, if the RHS of `return` contains references to `this` other than `this.props` and/or `this.context`, we can't be sure about what you'll need from `this`. We need to ensure that our property initializers' evaluation order is safe, so we defer `state`'s initialization by moving it all the way down until all other property initializers have been initialized
233+
- If `getInitialState()` is not simple, we create a `constructor` and convert `getInitialState()` to an assignment to `this.state`
234+
- `constructor` always have `props` as the first parameter
235+
- We only put `context` as the second parameter when (one of) the following things happen in `getInitialState()`:
236+
- It accesses `this.context`, or
237+
- There's a direct method call `this.x()`, or
238+
- `this` is referenced alone
239+
- Rewrite accesses to `this.props` to `props` and accesses to `this.context` to `context` since the values will be passed as `constructor` arguments
240+
- Remove _simple_ variable declarations like `var props = this.props;` and `var context = this.context`
241+
- Rewrite top-level return statements (`return {...};`) to `this.state = {...}`
242+
- Add `return;` after the assignment when the return statement is part of a control flow statement (not a direct child of `getInitialState()`'s body) and not in an inner function declaration
243+
4. Transform all non-lifecycle methods and fields to class property initializers (like `onClick = () => {};`). All your Flow annotations will be preserved
244+
- It's actually not necessary to transform all methods to arrow functions (i.e., to bind them), but this behavior is the same as `createClass()` and we can make sure that we won't accidentally break stuff
245+
4. Generate Flow annotations from `propTypes` and put it on the class (this only happens when there's `/* @flow */` in your code and `options['flow']` is `true`)
246+
- Flow actually understands `propTypes` in `createClass` calls but not ES6 class components. Here the transformation logic is identical to [how](https://github.com/facebook/flow/blob/master/src/typing/statement.ml#L3526) Flow treats `propTypes`
247+
- Notice that Flow treats an optional propType as non-nullable
248+
- For example, `foo: React.PropTypes.number` is valid when you pass `{}`, `{foo: null}`, or `{foo: undefined}` as props at **runtime**. However, when Flow infers type from a `createClass` call, only `{}` and `{foo: undefined}` are valid; `{foo: null}` is not. Thus the equivalent type annotation in Flow is actually `{foo?: number}`. The question mark on the left hand side indicates `{}` and `{foo: undefined}` are fine, but when `foo` is present it must be a `number`
249+
- For `propTypes` fields that can't be recognized by Flow, `$FlowFixMe` will be used
250+
5. `React.createClass` is no longer present in React 16. So, if a `createClass` call cannot be converted to a plain class, the script will fallback to using the `create-react-class` package.
251+
- Replaces `React.createClass` with `ReactCreateClass`.
252+
- Adds a `require` or `import` statement for `create-react-class`. The import style is inferred from the import style of the `react` import. The default module name can be overridden with the `--create-class-module-name` option.
253+
- Prunes the `react` import if there are no more references to it.
254+
255+
256+
## `react-codemod` options
257+
258+
### jscodeshift options
259+
260+
To pass more options directly to jscodeshift, use `--jscodeshift="..."`. For example:
261+
```sh
262+
npx react-codemod --jscodeshift="--run-in-band --verbose=2"
263+
```
264+
265+
See all available options [here](https://github.com/facebook/jscodeshift#usage-cli).
266+
267+
### Recast Options
268+
269+
Options to [recast](https://github.com/benjamn/recast)'s printer can be provided
270+
through jscodeshift's `printOptions` command line argument
271+
272+
```sh
273+
npx react-codemod <transform> <path> --jscodeshift="--printOptions='{\"quote\":\"double\"}'"
274+
```
275+
276+
### `explicit-require=false`
277+
278+
If you're not explicitly importing React in your files (eg: if you're loading React with a script tag), you should add `--explicit-require=false`.
279+

0 commit comments

Comments
 (0)