123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141 |
- /**
- * @fileoverview Prevent JSX prop spreading
- * @author Ashish Gambhir
- */
- 'use strict';
- const docsUrl = require('../util/docsUrl');
- const report = require('../util/report');
- // ------------------------------------------------------------------------------
- // Constants
- // ------------------------------------------------------------------------------
- const OPTIONS = { ignore: 'ignore', enforce: 'enforce' };
- const DEFAULTS = {
- html: OPTIONS.enforce,
- custom: OPTIONS.enforce,
- explicitSpread: OPTIONS.enforce,
- exceptions: [],
- };
- const isException = (tag, allExceptions) => allExceptions.indexOf(tag) !== -1;
- const isProperty = (property) => property.type === 'Property';
- const getTagNameFromMemberExpression = (node) => {
- if (node.property.parent) {
- return `${node.property.parent.object.name}.${node.property.name}`;
- }
- // for eslint 3
- return `${node.object.name}.${node.property.name}`;
- };
- // ------------------------------------------------------------------------------
- // Rule Definition
- // ------------------------------------------------------------------------------
- const messages = {
- noSpreading: 'Prop spreading is forbidden',
- };
- module.exports = {
- meta: {
- docs: {
- description: 'Disallow JSX prop spreading',
- category: 'Best Practices',
- recommended: false,
- url: docsUrl('jsx-props-no-spreading'),
- },
- messages,
- schema: [{
- allOf: [{
- type: 'object',
- properties: {
- html: {
- enum: [OPTIONS.enforce, OPTIONS.ignore],
- },
- custom: {
- enum: [OPTIONS.enforce, OPTIONS.ignore],
- },
- exceptions: {
- type: 'array',
- items: {
- type: 'string',
- uniqueItems: true,
- },
- },
- },
- }, {
- not: {
- type: 'object',
- required: ['html', 'custom'],
- properties: {
- html: {
- enum: [OPTIONS.ignore],
- },
- custom: {
- enum: [OPTIONS.ignore],
- },
- exceptions: {
- type: 'array',
- minItems: 0,
- maxItems: 0,
- },
- },
- },
- }],
- }],
- },
- create(context) {
- const configuration = context.options[0] || {};
- const ignoreHtmlTags = (configuration.html || DEFAULTS.html) === OPTIONS.ignore;
- const ignoreCustomTags = (configuration.custom || DEFAULTS.custom) === OPTIONS.ignore;
- const ignoreExplicitSpread = (configuration.explicitSpread || DEFAULTS.explicitSpread) === OPTIONS.ignore;
- const exceptions = configuration.exceptions || DEFAULTS.exceptions;
- return {
- JSXSpreadAttribute(node) {
- const jsxOpeningElement = node.parent.name;
- const type = jsxOpeningElement.type;
- let tagName;
- if (type === 'JSXIdentifier') {
- tagName = jsxOpeningElement.name;
- } else if (type === 'JSXMemberExpression') {
- tagName = getTagNameFromMemberExpression(jsxOpeningElement);
- } else {
- tagName = undefined;
- }
- const isHTMLTag = tagName && tagName[0] !== tagName[0].toUpperCase();
- const isCustomTag = tagName && (tagName[0] === tagName[0].toUpperCase() || tagName.includes('.'));
- if (
- isHTMLTag
- && ((ignoreHtmlTags && !isException(tagName, exceptions))
- || (!ignoreHtmlTags && isException(tagName, exceptions)))
- ) {
- return;
- }
- if (
- isCustomTag
- && ((ignoreCustomTags && !isException(tagName, exceptions))
- || (!ignoreCustomTags && isException(tagName, exceptions)))
- ) {
- return;
- }
- if (
- ignoreExplicitSpread
- && node.argument.type === 'ObjectExpression'
- && node.argument.properties.every(isProperty)
- ) {
- return;
- }
- report(context, messages.noSpreading, 'noSpreading', {
- node,
- });
- },
- };
- },
- };
|