| /** |
| * @fileoverview Prevent usage of deprecated methods |
| * @author Yannick Croissant |
| * @author Scott Feeney |
| * @author Sergei Startsev |
| */ |
| |
| 'use strict'; |
| |
| const values = require('object.values'); |
| |
| const Components = require('../util/Components'); |
| const astUtil = require('../util/ast'); |
| const docsUrl = require('../util/docsUrl'); |
| const pragmaUtil = require('../util/pragma'); |
| const versionUtil = require('../util/version'); |
| |
| // ------------------------------------------------------------------------------ |
| // Constants |
| // ------------------------------------------------------------------------------ |
| |
| const MODULES = { |
| react: ['React'], |
| 'react-addons-perf': ['ReactPerf', 'Perf'] |
| }; |
| |
| const DEPRECATED_MESSAGE = '{{oldMethod}} is deprecated since React {{version}}{{newMethod}}{{refs}}'; |
| |
| // ------------------------------------------------------------------------------ |
| // Rule Definition |
| // ------------------------------------------------------------------------------ |
| |
| module.exports = { |
| meta: { |
| docs: { |
| description: 'Prevent usage of deprecated methods', |
| category: 'Best Practices', |
| recommended: true, |
| url: docsUrl('no-deprecated') |
| }, |
| schema: [] |
| }, |
| |
| create: Components.detect((context, components, utils) => { |
| const pragma = pragmaUtil.getFromContext(context); |
| |
| function getDeprecated() { |
| const deprecated = {}; |
| // 0.12.0 |
| deprecated[`${pragma}.renderComponent`] = ['0.12.0', `${pragma}.render`]; |
| deprecated[`${pragma}.renderComponentToString`] = ['0.12.0', `${pragma}.renderToString`]; |
| deprecated[`${pragma}.renderComponentToStaticMarkup`] = ['0.12.0', `${pragma}.renderToStaticMarkup`]; |
| deprecated[`${pragma}.isValidComponent`] = ['0.12.0', `${pragma}.isValidElement`]; |
| deprecated[`${pragma}.PropTypes.component`] = ['0.12.0', `${pragma}.PropTypes.element`]; |
| deprecated[`${pragma}.PropTypes.renderable`] = ['0.12.0', `${pragma}.PropTypes.node`]; |
| deprecated[`${pragma}.isValidClass`] = ['0.12.0']; |
| deprecated['this.transferPropsTo'] = ['0.12.0', 'spread operator ({...})']; |
| // 0.13.0 |
| deprecated[`${pragma}.addons.classSet`] = ['0.13.0', 'the npm module classnames']; |
| deprecated[`${pragma}.addons.cloneWithProps`] = ['0.13.0', `${pragma}.cloneElement`]; |
| // 0.14.0 |
| deprecated[`${pragma}.render`] = ['0.14.0', 'ReactDOM.render']; |
| deprecated[`${pragma}.unmountComponentAtNode`] = ['0.14.0', 'ReactDOM.unmountComponentAtNode']; |
| deprecated[`${pragma}.findDOMNode`] = ['0.14.0', 'ReactDOM.findDOMNode']; |
| deprecated[`${pragma}.renderToString`] = ['0.14.0', 'ReactDOMServer.renderToString']; |
| deprecated[`${pragma}.renderToStaticMarkup`] = ['0.14.0', 'ReactDOMServer.renderToStaticMarkup']; |
| // 15.0.0 |
| deprecated[`${pragma}.addons.LinkedStateMixin`] = ['15.0.0']; |
| deprecated['ReactPerf.printDOM'] = ['15.0.0', 'ReactPerf.printOperations']; |
| deprecated['Perf.printDOM'] = ['15.0.0', 'Perf.printOperations']; |
| deprecated['ReactPerf.getMeasurementsSummaryMap'] = ['15.0.0', 'ReactPerf.getWasted']; |
| deprecated['Perf.getMeasurementsSummaryMap'] = ['15.0.0', 'Perf.getWasted']; |
| // 15.5.0 |
| deprecated[`${pragma}.createClass`] = ['15.5.0', 'the npm module create-react-class']; |
| deprecated[`${pragma}.addons.TestUtils`] = ['15.5.0', 'ReactDOM.TestUtils']; |
| deprecated[`${pragma}.PropTypes`] = ['15.5.0', 'the npm module prop-types']; |
| // 15.6.0 |
| deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories']; |
| // 16.999.0 |
| // For now the following life-cycle methods are just legacy, not deprecated: |
| // `componentWillMount`, `componentWillReceiveProps`, `componentWillUpdate` |
| // https://github.com/yannickcr/eslint-plugin-react/pull/1750#issuecomment-425975934 |
| deprecated.componentWillMount = [ |
| '16.999.0', |
| 'UNSAFE_componentWillMount', |
| 'https://reactjs.org/docs/react-component.html#unsafe_componentwillmount. ' + |
| 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.' |
| ]; |
| deprecated.componentWillReceiveProps = [ |
| '16.999.0', |
| 'UNSAFE_componentWillReceiveProps', |
| 'https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops. ' + |
| 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.' |
| ]; |
| deprecated.componentWillUpdate = [ |
| '16.999.0', |
| 'UNSAFE_componentWillUpdate', |
| 'https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate. ' + |
| 'Use https://github.com/reactjs/react-codemod#rename-unsafe-lifecycles to automatically update your components.' |
| ]; |
| return deprecated; |
| } |
| |
| function isDeprecated(method) { |
| const deprecated = getDeprecated(); |
| |
| return ( |
| deprecated && |
| deprecated[method] && |
| deprecated[method][0] && |
| versionUtil.testReactVersion(context, deprecated[method][0]) |
| ); |
| } |
| |
| function checkDeprecation(node, methodName, methodNode) { |
| if (!isDeprecated(methodName)) { |
| return; |
| } |
| const deprecated = getDeprecated(); |
| const version = deprecated[methodName][0]; |
| const newMethod = deprecated[methodName][1]; |
| const refs = deprecated[methodName][2]; |
| context.report({ |
| node: methodNode || node, |
| message: DEPRECATED_MESSAGE, |
| data: { |
| oldMethod: methodName, |
| version, |
| newMethod: newMethod ? `, use ${newMethod} instead` : '', |
| refs: refs ? `, see ${refs}` : '' |
| } |
| }); |
| } |
| |
| function getReactModuleName(node) { |
| let moduleName = false; |
| if (!node.init) { |
| return moduleName; |
| } |
| |
| values(MODULES).some((moduleNames) => { |
| moduleName = moduleNames.find(name => name === node.init.name); |
| return moduleName; |
| }); |
| |
| return moduleName; |
| } |
| |
| /** |
| * Returns life cycle methods if available |
| * @param {ASTNode} node The AST node being checked. |
| * @returns {Array} The array of methods. |
| */ |
| function getLifeCycleMethods(node) { |
| const properties = astUtil.getComponentProperties(node); |
| return properties.map(property => ({ |
| name: astUtil.getPropertyName(property), |
| node: astUtil.getPropertyNameNode(property) |
| })); |
| } |
| |
| /** |
| * Checks life cycle methods |
| * @param {ASTNode} node The AST node being checked. |
| */ |
| function checkLifeCycleMethods(node) { |
| if (utils.isES5Component(node) || utils.isES6Component(node)) { |
| const methods = getLifeCycleMethods(node); |
| methods.forEach(method => checkDeprecation(node, method.name, method.node)); |
| } |
| } |
| |
| // -------------------------------------------------------------------------- |
| // Public |
| // -------------------------------------------------------------------------- |
| |
| return { |
| MemberExpression(node) { |
| checkDeprecation(node, context.getSourceCode().getText(node)); |
| }, |
| |
| ImportDeclaration(node) { |
| const isReactImport = typeof MODULES[node.source.value] !== 'undefined'; |
| if (!isReactImport) { |
| return; |
| } |
| node.specifiers.forEach((specifier) => { |
| if (!specifier.imported) { |
| return; |
| } |
| checkDeprecation(node, `${MODULES[node.source.value][0]}.${specifier.imported.name}`); |
| }); |
| }, |
| |
| VariableDeclarator(node) { |
| const reactModuleName = getReactModuleName(node); |
| const isRequire = node.init && node.init.callee && node.init.callee.name === 'require'; |
| const isReactRequire = node.init && |
| node.init.arguments && |
| node.init.arguments.length && |
| typeof MODULES[node.init.arguments[0].value] !== 'undefined'; |
| const isDestructuring = node.id && node.id.type === 'ObjectPattern'; |
| |
| if ( |
| !(isDestructuring && reactModuleName) && |
| !(isDestructuring && isRequire && isReactRequire) |
| ) { |
| return; |
| } |
| node.id.properties.forEach((property) => { |
| checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`); |
| }); |
| }, |
| |
| ClassDeclaration: checkLifeCycleMethods, |
| ClassExpression: checkLifeCycleMethods, |
| ObjectExpression: checkLifeCycleMethods |
| }; |
| }) |
| }; |