123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /**
- * @fileoverview Prevents usage of Function.prototype.bind and arrow functions
- * in React component props.
- * @author Daniel Lo Nigro <dan.cx>
- * @author Jacky Ho
- */
- 'use strict';
- const propName = require('jsx-ast-utils/propName');
- const Components = require('../util/Components');
- const docsUrl = require('../util/docsUrl');
- // -----------------------------------------------------------------------------
- // Rule Definition
- // -----------------------------------------------------------------------------
- const violationMessageStore = {
- bindCall: 'JSX props should not use .bind()',
- arrowFunc: 'JSX props should not use arrow functions',
- bindExpression: 'JSX props should not use ::',
- func: 'JSX props should not use functions'
- };
- module.exports = {
- meta: {
- docs: {
- description: 'Prevents usage of Function.prototype.bind and arrow functions in React component props',
- category: 'Best Practices',
- recommended: false,
- url: docsUrl('jsx-no-bind')
- },
- schema: [{
- type: 'object',
- properties: {
- allowArrowFunctions: {
- default: false,
- type: 'boolean'
- },
- allowBind: {
- default: false,
- type: 'boolean'
- },
- allowFunctions: {
- default: false,
- type: 'boolean'
- },
- ignoreRefs: {
- default: false,
- type: 'boolean'
- }
- },
- additionalProperties: false
- }]
- },
- create: Components.detect(context => {
- const configuration = context.options[0] || {};
- // Keep track of all the variable names pointing to a bind call,
- // bind expression or an arrow function in different block statements
- const blockVariableNameSets = {};
- function setBlockVariableNameSet(blockStart) {
- blockVariableNameSets[blockStart] = {
- arrowFunc: new Set(),
- bindCall: new Set(),
- bindExpression: new Set(),
- func: new Set()
- };
- }
- function getNodeViolationType(node) {
- const nodeType = node.type;
- if (
- !configuration.allowBind &&
- nodeType === 'CallExpression' &&
- node.callee.type === 'MemberExpression' &&
- node.callee.property.type === 'Identifier' &&
- node.callee.property.name === 'bind'
- ) {
- return 'bindCall';
- } else if (
- nodeType === 'ConditionalExpression'
- ) {
- return getNodeViolationType(node.test) ||
- getNodeViolationType(node.consequent) ||
- getNodeViolationType(node.alternate);
- } else if (
- !configuration.allowArrowFunctions &&
- nodeType === 'ArrowFunctionExpression'
- ) {
- return 'arrowFunc';
- } else if (
- !configuration.allowFunctions &&
- nodeType === 'FunctionExpression'
- ) {
- return 'func';
- } else if (
- !configuration.allowBind &&
- nodeType === 'BindExpression'
- ) {
- return 'bindExpression';
- }
- return null;
- }
- function addVariableNameToSet(violationType, variableName, blockStart) {
- blockVariableNameSets[blockStart][violationType].add(variableName);
- }
- function getBlockStatementAncestors(node) {
- return context.getAncestors(node).reverse().filter(
- ancestor => ancestor.type === 'BlockStatement'
- );
- }
- function reportVariableViolation(node, name, blockStart) {
- const blockSets = blockVariableNameSets[blockStart];
- const violationTypes = Object.keys(blockSets);
- return violationTypes.find(type => {
- if (blockSets[type].has(name)) {
- context.report({node: node, message: violationMessageStore[type]});
- return true;
- }
- return false;
- });
- }
- function findVariableViolation(node, name) {
- getBlockStatementAncestors(node).find(
- block => reportVariableViolation(node, name, block.start)
- );
- }
- return {
- BlockStatement(node) {
- setBlockVariableNameSet(node.start);
- },
- VariableDeclarator(node) {
- if (!node.init) {
- return;
- }
- const blockAncestors = getBlockStatementAncestors(node);
- const variableViolationType = getNodeViolationType(node.init);
- if (
- blockAncestors.length > 0 &&
- variableViolationType &&
- node.parent.kind === 'const' // only support const right now
- ) {
- addVariableNameToSet(
- variableViolationType, node.id.name, blockAncestors[0].start
- );
- }
- },
- JSXAttribute: function (node) {
- const isRef = configuration.ignoreRefs && propName(node) === 'ref';
- if (isRef || !node.value || !node.value.expression) {
- return;
- }
- const valueNode = node.value.expression;
- const valueNodeType = valueNode.type;
- const nodeViolationType = getNodeViolationType(valueNode);
- if (valueNodeType === 'Identifier') {
- findVariableViolation(node, valueNode.name);
- } else if (nodeViolationType) {
- context.report({
- node: node, message: violationMessageStore[nodeViolationType]
- });
- }
- }
- };
- })
- };
|