diff --git a/README.md b/README.md index fc51eb5f..9fb88673 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,14 @@ jscodeshift -t react-codemod/transforms/React-PropTypes-to-prop-types.js * In addition to running the above codemod you will also need to install the 'prop-types' NPM package. +#### `rename-unsafe-lifecycles` + +Adds "UNSAFE_" prefix for deprecated lifecycle hooks. (For more information about this codemod, see [React RFC #6](https://github.com/reactjs/rfcs/pull/6)) + +```sh +jscodeshift -t react-codemod/transforms/rename-unsafe-lifecycles.js +``` + #### `react-to-react-dom` Updates code for the split of the `react` and `react-dom` packages (e.g., diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/arrow-functions.input.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/arrow-functions.input.js new file mode 100644 index 00000000..501cd6c6 --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/arrow-functions.input.js @@ -0,0 +1,14 @@ +const React = require('React'); + +class Component extends React.Component { + componentWillMount = logger('componentWillMount'); + componentDidMount = logger('componentDidMount'); + componentWillReceiveProps = logger('componentWillReceiveProps'); + shouldComponentUpdate = logger('shouldComponentUpdate'); + componentWillUpdate = logger('componentWillUpdate'); + componentDidUpdate = logger('componentDidUpdate'); + componentWillUnmount = logger('componentWillUnmount'); + render() { + return null; + } +} \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/arrow-functions.output.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/arrow-functions.output.js new file mode 100644 index 00000000..8087f104 --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/arrow-functions.output.js @@ -0,0 +1,14 @@ +const React = require('React'); + +class Component extends React.Component { + UNSAFE_componentWillMount = logger('componentWillMount'); + componentDidMount = logger('componentDidMount'); + UNSAFE_componentWillReceiveProps = logger('componentWillReceiveProps'); + shouldComponentUpdate = logger('shouldComponentUpdate'); + UNSAFE_componentWillUpdate = logger('componentWillUpdate'); + componentDidUpdate = logger('componentDidUpdate'); + componentWillUnmount = logger('componentWillUnmount'); + render() { + return null; + } +} \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/create-react-class.input.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/create-react-class.input.js new file mode 100644 index 00000000..5708b2d3 --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/create-react-class.input.js @@ -0,0 +1,48 @@ +const createReactClass = require('create-react-class'); + +const MyComponent = createReactClass({ + displayName: 'MyComponent', + mixins: [ + { + componentWillMount() { + // componentWillMount + }, + componentDidMount() { + // componentDidMount + }, + componentWillUpdate(nextProps, nextState) { + // componentWillUpdate + }, + componentDidUpdate(prevProps, prevState) { + // componentDidUpdate + }, + componentWillReceiveProps(nextProps) { + // componentWillReceiveProps + }, + componentWillUnmount() { + // componentWillUnmount + }, + }, + ], + componentWillMount() { + // componentWillMount + }, + componentDidMount() { + // componentDidMount + }, + componentWillUpdate(nextProps, nextState) { + // componentWillUpdate + }, + componentDidUpdate(prevProps, prevState) { + // componentDidUpdate + }, + componentWillReceiveProps(nextProps) { + // componentWillReceiveProps + }, + componentWillUnmount() { + // componentWillUnmount + }, + render() { + // render + }, +}); \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/create-react-class.output.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/create-react-class.output.js new file mode 100644 index 00000000..f9eab800 --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/create-react-class.output.js @@ -0,0 +1,48 @@ +const createReactClass = require('create-react-class'); + +const MyComponent = createReactClass({ + displayName: 'MyComponent', + mixins: [ + { + UNSAFE_componentWillMount() { + // componentWillMount + }, + componentDidMount() { + // componentDidMount + }, + UNSAFE_componentWillUpdate(nextProps, nextState) { + // componentWillUpdate + }, + componentDidUpdate(prevProps, prevState) { + // componentDidUpdate + }, + UNSAFE_componentWillReceiveProps(nextProps) { + // componentWillReceiveProps + }, + componentWillUnmount() { + // componentWillUnmount + }, + }, + ], + UNSAFE_componentWillMount() { + // componentWillMount + }, + componentDidMount() { + // componentDidMount + }, + UNSAFE_componentWillUpdate(nextProps, nextState) { + // componentWillUpdate + }, + componentDidUpdate(prevProps, prevState) { + // componentDidUpdate + }, + UNSAFE_componentWillReceiveProps(nextProps) { + // componentWillReceiveProps + }, + componentWillUnmount() { + // componentWillUnmount + }, + render() { + // render + }, +}); \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/instance-methods.input.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/instance-methods.input.js new file mode 100644 index 00000000..619bf50d --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/instance-methods.input.js @@ -0,0 +1,25 @@ +const React = require('React'); + +class ClassComponent extends React.Component { + componentWillMount() { + // componentWillMount + } + componentDidMount() { + // componentDidMount + } + componentWillUpdate(nextProps, nextState) { + // componentWillUpdate + } + componentDidUpdate(prevProps, prevState) { + // componentDidUpdate + } + componentWillReceiveProps(nextProps) { + // componentWillReceiveProps + } + componentWillUnmount() { + // componentWillUnmount + } + render() { + // render + } +} \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/instance-methods.output.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/instance-methods.output.js new file mode 100644 index 00000000..610364c3 --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/instance-methods.output.js @@ -0,0 +1,25 @@ +const React = require('React'); + +class ClassComponent extends React.Component { + UNSAFE_componentWillMount() { + // componentWillMount + } + componentDidMount() { + // componentDidMount + } + UNSAFE_componentWillUpdate(nextProps, nextState) { + // componentWillUpdate + } + componentDidUpdate(prevProps, prevState) { + // componentDidUpdate + } + UNSAFE_componentWillReceiveProps(nextProps) { + // componentWillReceiveProps + } + componentWillUnmount() { + // componentWillUnmount + } + render() { + // render + } +} \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/standalone-function.input.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/standalone-function.input.js new file mode 100644 index 00000000..c515ccc4 --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/standalone-function.input.js @@ -0,0 +1,9 @@ +function componentWillMount() { + // componentWillMount +} +function componentWillUpdate(nextProps, nextState) { + // componentWillUpdate +} +function componentWillReceiveProps(nextProps) { + // componentWillReceiveProps +} \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/standalone-function.output.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/standalone-function.output.js new file mode 100644 index 00000000..e69de29b diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/variable-within-class-method.input.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/variable-within-class-method.input.js new file mode 100644 index 00000000..eb8979af --- /dev/null +++ b/transforms/__testfixtures__/rename-unsafe-lifecycles/variable-within-class-method.input.js @@ -0,0 +1,7 @@ +class SomeClass { + someMethod() { + const componentWillMount = true; + let componentWillUpdate = 123; + var componentWillReceiveProps = 'abc'; + } +} \ No newline at end of file diff --git a/transforms/__testfixtures__/rename-unsafe-lifecycles/variable-within-class-method.output.js b/transforms/__testfixtures__/rename-unsafe-lifecycles/variable-within-class-method.output.js new file mode 100644 index 00000000..e69de29b diff --git a/transforms/__tests__/rename-unsafe-lifecycles-test.js b/transforms/__tests__/rename-unsafe-lifecycles-test.js new file mode 100644 index 00000000..59cdbeb2 --- /dev/null +++ b/transforms/__tests__/rename-unsafe-lifecycles-test.js @@ -0,0 +1,32 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +'use strict'; + +const tests = [ + 'arrow-functions', + 'create-react-class', + 'instance-methods', + 'standalone-function', + 'variable-within-class-method', +]; + +const defineTest = require('jscodeshift/dist/testUtils').defineTest; + +describe('rename-unsafe-lifecycles', () => { + tests.forEach(test => + defineTest( + __dirname, + 'rename-unsafe-lifecycles', + null, + `rename-unsafe-lifecycles/${test}` + ) + ); +}); diff --git a/transforms/rename-unsafe-lifecycles.js b/transforms/rename-unsafe-lifecycles.js new file mode 100644 index 00000000..eaea665b --- /dev/null +++ b/transforms/rename-unsafe-lifecycles.js @@ -0,0 +1,57 @@ +/** + * Copyright 2013-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + */ + +'use strict'; + +const DEPRECATED_APIS = Object.create(null); +DEPRECATED_APIS.componentWillMount = 'UNSAFE_componentWillMount'; +DEPRECATED_APIS.componentWillReceiveProps = 'UNSAFE_componentWillReceiveProps'; +DEPRECATED_APIS.componentWillUpdate = 'UNSAFE_componentWillUpdate'; + +export default (file, api, options) => { + const j = api.jscodeshift; + + const printOptions = options.printOptions || { + quote: 'single', + trailingComma: true, + }; + + const root = j(file.source); + + let hasModifications = false; + + const renameDeprecatedApis = path => { + const name = path.node.key.name; + + if (DEPRECATED_APIS[name]) { + path.value.key.name = DEPRECATED_APIS[name]; + hasModifications = true; + } + }; + + // Class methods + root + .find(j.MethodDefinition) + .forEach(renameDeprecatedApis); + + // Arrow functions + root + .find(j.ClassProperty) + .forEach(renameDeprecatedApis); + + // createReactClass and mixins + root + .find(j.Property) + .forEach(renameDeprecatedApis); + + return hasModifications + ? root.toSource(printOptions) + : null; +};