You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							164 lines
						
					
					
						
							4.0 KiB
						
					
					
				
			
		
		
	
	
							164 lines
						
					
					
						
							4.0 KiB
						
					
					
				/** | 
						|
 * @author Flo Edelmann | 
						|
 * See LICENSE file in root directory for full license. | 
						|
 */ | 
						|
'use strict' | 
						|
const { defineTemplateBodyVisitor } = require('../utils') | 
						|
 | 
						|
/** | 
						|
 * @typedef {object} RuleOption | 
						|
 * @property {string[]} additionalDirectives | 
						|
 */ | 
						|
 | 
						|
/** | 
						|
 * @param {VNode | Token} node | 
						|
 * @returns {boolean} | 
						|
 */ | 
						|
function isWhiteSpaceTextNode(node) { | 
						|
  return node.type === 'VText' && node.value.trim() === '' | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * @param {Position} pos1 | 
						|
 * @param {Position} pos2 | 
						|
 * @returns {'less' | 'equal' | 'greater'} | 
						|
 */ | 
						|
function comparePositions(pos1, pos2) { | 
						|
  if ( | 
						|
    pos1.line < pos2.line || | 
						|
    (pos1.line === pos2.line && pos1.column < pos2.column) | 
						|
  ) { | 
						|
    return 'less' | 
						|
  } | 
						|
 | 
						|
  if ( | 
						|
    pos1.line > pos2.line || | 
						|
    (pos1.line === pos2.line && pos1.column > pos2.column) | 
						|
  ) { | 
						|
    return 'greater' | 
						|
  } | 
						|
 | 
						|
  return 'equal' | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * @param {(VNode | Token)[]} nodes | 
						|
 * @returns {SourceLocation | undefined} | 
						|
 */ | 
						|
function getLocationRange(nodes) { | 
						|
  /** @type {Position | undefined} */ | 
						|
  let start | 
						|
  /** @type {Position | undefined} */ | 
						|
  let end | 
						|
 | 
						|
  for (const node of nodes) { | 
						|
    if (!start || comparePositions(node.loc.start, start) === 'less') { | 
						|
      start = node.loc.start | 
						|
    } | 
						|
 | 
						|
    if (!end || comparePositions(node.loc.end, end) === 'greater') { | 
						|
      end = node.loc.end | 
						|
    } | 
						|
  } | 
						|
 | 
						|
  if (start === undefined || end === undefined) { | 
						|
    return undefined | 
						|
  } | 
						|
 | 
						|
  return { start, end } | 
						|
} | 
						|
 | 
						|
// ------------------------------------------------------------------------------ | 
						|
// Rule Definition | 
						|
// ------------------------------------------------------------------------------ | 
						|
 | 
						|
module.exports = { | 
						|
  meta: { | 
						|
    hasSuggestions: true, | 
						|
    type: 'problem', | 
						|
    docs: { | 
						|
      description: | 
						|
        "disallow element's child contents which would be overwritten by a directive like `v-html` or `v-text`", | 
						|
      categories: undefined, | 
						|
      url: 'https://eslint.vuejs.org/rules/no-child-content.html' | 
						|
    }, | 
						|
    fixable: null, | 
						|
    schema: [ | 
						|
      { | 
						|
        type: 'object', | 
						|
        additionalProperties: false, | 
						|
        properties: { | 
						|
          additionalDirectives: { | 
						|
            type: 'array', | 
						|
            uniqueItems: true, | 
						|
            minItems: 1, | 
						|
            items: { | 
						|
              type: 'string' | 
						|
            } | 
						|
          } | 
						|
        }, | 
						|
        required: ['additionalDirectives'] | 
						|
      } | 
						|
    ] | 
						|
  }, | 
						|
  /** @param {RuleContext} context */ | 
						|
  create(context) { | 
						|
    const directives = new Set(['html', 'text']) | 
						|
 | 
						|
    /** @type {RuleOption | undefined} */ | 
						|
    const option = context.options[0] | 
						|
    if (option !== undefined) { | 
						|
      for (const directive of option.additionalDirectives) { | 
						|
        directives.add(directive) | 
						|
      } | 
						|
    } | 
						|
 | 
						|
    return defineTemplateBodyVisitor(context, { | 
						|
      /** @param {VDirective} directiveNode */ | 
						|
      'VAttribute[directive=true]'(directiveNode) { | 
						|
        const directiveName = directiveNode.key.name.name | 
						|
        const elementNode = directiveNode.parent.parent | 
						|
 | 
						|
        if (elementNode.endTag === null) { | 
						|
          return | 
						|
        } | 
						|
 | 
						|
        const tokenStore = context.parserServices.getTemplateBodyTokenStore() | 
						|
        const elementComments = tokenStore.getTokensBetween( | 
						|
          elementNode.startTag, | 
						|
          elementNode.endTag, | 
						|
          { | 
						|
            includeComments: true, | 
						|
            filter: (token) => token.type === 'HTMLComment' | 
						|
          } | 
						|
        ) | 
						|
 | 
						|
        const childNodes = [...elementNode.children, ...elementComments] | 
						|
 | 
						|
        if ( | 
						|
          directives.has(directiveName) && | 
						|
          childNodes.length > 0 && | 
						|
          childNodes.some((childNode) => !isWhiteSpaceTextNode(childNode)) | 
						|
        ) { | 
						|
          context.report({ | 
						|
            node: elementNode, | 
						|
            loc: getLocationRange(childNodes), | 
						|
            message: | 
						|
              'Child content is disallowed because it will be overwritten by the v-{{ directiveName }} directive.', | 
						|
            data: { directiveName }, | 
						|
            suggest: [ | 
						|
              { | 
						|
                desc: 'Remove child content.', | 
						|
                *fix(fixer) { | 
						|
                  for (const childNode of childNodes) { | 
						|
                    yield fixer.remove(childNode) | 
						|
                  } | 
						|
                } | 
						|
              } | 
						|
            ] | 
						|
          }) | 
						|
        } | 
						|
      } | 
						|
    }) | 
						|
  } | 
						|
}
 | 
						|
 |