123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /**
- * @fileoverview Lifecycle methods should be methods on the prototype, not class fields
- * @author Tan Nguyen
- */
- 'use strict';
- const values = require('object.values');
- const Components = require('../util/Components');
- const astUtil = require('../util/ast');
- const componentUtil = require('../util/componentUtil');
- const docsUrl = require('../util/docsUrl');
- const lifecycleMethods = require('../util/lifecycleMethods');
- const report = require('../util/report');
- function getText(node) {
- const params = node.value.params.map((p) => p.name);
- if (node.type === 'Property') {
- return `: function(${params.join(', ')}) `;
- }
- if (node.type === 'ClassProperty' || node.type === 'PropertyDefinition') {
- return `(${params.join(', ')}) `;
- }
- return null;
- }
- const messages = {
- lifecycle: '{{propertyName}} is a React lifecycle method, and should not be an arrow function or in a class field. Use an instance method instead.',
- };
- module.exports = {
- meta: {
- docs: {
- description: 'Lifecycle methods should be methods on the prototype, not class fields',
- category: 'Best Practices',
- recommended: false,
- url: docsUrl('no-arrow-function-lifecycle'),
- },
- messages,
- schema: [],
- fixable: 'code',
- },
- create: Components.detect((context, components) => {
- /**
- * @param {Array} properties list of component properties
- */
- function reportNoArrowFunctionLifecycle(properties) {
- properties.forEach((node) => {
- if (!node || !node.value) {
- return;
- }
- const propertyName = astUtil.getPropertyName(node);
- const nodeType = node.value.type;
- const isLifecycleMethod = (
- node.static && !componentUtil.isES5Component(node, context)
- ? lifecycleMethods.static
- : lifecycleMethods.instance
- ).indexOf(propertyName) > -1;
- if (nodeType === 'ArrowFunctionExpression' && isLifecycleMethod) {
- const body = node.value.body;
- const isBlockBody = body.type === 'BlockStatement';
- const sourceCode = context.getSourceCode();
- let nextComment = [];
- let previousComment = [];
- let bodyRange;
- if (!isBlockBody) {
- const previousToken = sourceCode.getTokenBefore(body);
- if (sourceCode.getCommentsBefore) {
- // eslint >=4.x
- previousComment = sourceCode.getCommentsBefore(body);
- } else {
- // eslint 3.x
- const potentialComment = sourceCode.getTokenBefore(body, { includeComments: true });
- previousComment = previousToken === potentialComment ? [] : [potentialComment];
- }
- if (sourceCode.getCommentsAfter) {
- // eslint >=4.x
- nextComment = sourceCode.getCommentsAfter(body);
- } else {
- // eslint 3.x
- const potentialComment = sourceCode.getTokenAfter(body, { includeComments: true });
- const nextToken = sourceCode.getTokenAfter(body);
- nextComment = nextToken === potentialComment ? [] : [potentialComment];
- }
- bodyRange = [
- (previousComment.length > 0 ? previousComment[0] : body).range[0],
- (nextComment.length > 0 ? nextComment[nextComment.length - 1] : body).range[1]
- + (node.value.body.type === 'ObjectExpression' ? 1 : 0), // to account for a wrapped end paren
- ];
- }
- const headRange = [
- node.key.range[1],
- (previousComment.length > 0 ? previousComment[0] : body).range[0],
- ];
- const hasSemi = node.value.expression && sourceCode.getText(node).slice(node.value.range[1] - node.range[0]) === ';';
- report(
- context,
- messages.lifecycle,
- 'lifecycle',
- {
- node,
- data: {
- propertyName,
- },
- fix(fixer) {
- if (!sourceCode.getCommentsAfter) {
- // eslint 3.x
- return isBlockBody && fixer.replaceTextRange(headRange, getText(node));
- }
- return [].concat(
- fixer.replaceTextRange(headRange, getText(node)),
- isBlockBody ? [] : fixer.replaceTextRange(
- [bodyRange[0], bodyRange[1] + (hasSemi ? 1 : 0)],
- `{ return ${previousComment.map((x) => sourceCode.getText(x)).join('')}${sourceCode.getText(body)}${nextComment.map((x) => sourceCode.getText(x)).join('')}; }`
- )
- );
- },
- }
- );
- }
- });
- }
- return {
- 'Program:exit'() {
- values(components.list()).forEach((component) => {
- const properties = astUtil.getComponentProperties(component.node);
- reportNoArrowFunctionLifecycle(properties);
- });
- },
- };
- }),
- };
|