123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- /**
- * @fileoverview Prevent extra closing tags for components without children
- * @author Yannick Croissant
- */
- 'use strict';
- const docsUrl = require('../util/docsUrl');
- const jsxUtil = require('../util/jsx');
- // ------------------------------------------------------------------------------
- // Rule Definition
- // ------------------------------------------------------------------------------
- const optionDefaults = {component: true, html: true};
- module.exports = {
- meta: {
- docs: {
- description: 'Prevent extra closing tags for components without children',
- category: 'Stylistic Issues',
- recommended: false,
- url: docsUrl('self-closing-comp')
- },
- fixable: 'code',
- messages: {
- notSelfClosing: 'Empty components are self-closing'
- },
- schema: [{
- type: 'object',
- properties: {
- component: {
- default: optionDefaults.component,
- type: 'boolean'
- },
- html: {
- default: optionDefaults.html,
- type: 'boolean'
- }
- },
- additionalProperties: false
- }]
- },
- create(context) {
- function isComponent(node) {
- return (
- node.name
- && (node.name.type === 'JSXIdentifier' || node.name.type === 'JSXMemberExpression')
- && !jsxUtil.isDOMComponent(node)
- );
- }
- function childrenIsEmpty(node) {
- return node.parent.children.length === 0;
- }
- function childrenIsMultilineSpaces(node) {
- const childrens = node.parent.children;
- return (
- childrens.length === 1
- && (childrens[0].type === 'Literal' || childrens[0].type === 'JSXText')
- && childrens[0].value.indexOf('\n') !== -1
- && childrens[0].value.replace(/(?!\xA0)\s/g, '') === ''
- );
- }
- function isShouldBeSelfClosed(node) {
- const configuration = Object.assign({}, optionDefaults, context.options[0]);
- return (
- (configuration.component && isComponent(node))
- || (configuration.html && jsxUtil.isDOMComponent(node))
- ) && !node.selfClosing && (childrenIsEmpty(node) || childrenIsMultilineSpaces(node));
- }
- // --------------------------------------------------------------------------
- // Public
- // --------------------------------------------------------------------------
- return {
- JSXOpeningElement(node) {
- if (!isShouldBeSelfClosed(node)) {
- return;
- }
- context.report({
- node,
- messageId: 'notSelfClosing',
- fix(fixer) {
- // Represents the last character of the JSXOpeningElement, the '>' character
- const openingElementEnding = node.range[1] - 1;
- // Represents the last character of the JSXClosingElement, the '>' character
- const closingElementEnding = node.parent.closingElement.range[1];
- // Replace />.*<\/.*>/ with '/>'
- const range = [openingElementEnding, closingElementEnding];
- return fixer.replaceTextRange(range, ' />');
- }
- });
- }
- };
- }
- };
|