| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 | /** * @author Toru Nagashima <https://github.com/mysticatea> */'use strict'// -----------------------------------------------------------------------------// Requirements// -----------------------------------------------------------------------------const utils = require('../utils')// -----------------------------------------------------------------------------// Helpers// -----------------------------------------------------------------------------/** * Normalize options. * @param {{startTag?:"always"|"never",endTag?:"always"|"never",selfClosingTag?:"always"|"never"}} options The options user configured. * @param {TokenStore} tokens The token store of template body. * @returns {{startTag:"always"|"never",endTag:"always"|"never",selfClosingTag:"always"|"never"}} The normalized options. */function parseOptions (options, tokens) {  return Object.assign({    startTag: 'never',    endTag: 'never',    selfClosingTag: 'always',    detectType (node) {      const openType = tokens.getFirstToken(node).type      const closeType = tokens.getLastToken(node).type      if (openType === 'HTMLEndTagOpen' && closeType === 'HTMLTagClose') {        return this.endTag      }      if (openType === 'HTMLTagOpen' && closeType === 'HTMLTagClose') {        return this.startTag      }      if (openType === 'HTMLTagOpen' && closeType === 'HTMLSelfClosingTagClose') {        return this.selfClosingTag      }      return null    }  }, options)}// -----------------------------------------------------------------------------// Rule Definition// -----------------------------------------------------------------------------module.exports = {  meta: {    docs: {      description: 'require or disallow a space before tag\'s closing brackets',      category: undefined,      url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v4.7.1/docs/rules/html-closing-bracket-spacing.md'    },    schema: [{      type: 'object',      properties: {        startTag: { enum: ['always', 'never'] },        endTag: { enum: ['always', 'never'] },        selfClosingTag: { enum: ['always', 'never'] }      },      additionalProperties: false    }],    fixable: 'whitespace'  },  create (context) {    const sourceCode = context.getSourceCode()    const tokens =      context.parserServices.getTemplateBodyTokenStore &&      context.parserServices.getTemplateBodyTokenStore()    const options = parseOptions(context.options[0], tokens)    return utils.defineTemplateBodyVisitor(context, {      'VStartTag, VEndTag' (node) {        const type = options.detectType(node)        const lastToken = tokens.getLastToken(node)        const prevToken = tokens.getLastToken(node, 1)        // Skip if EOF exists in the tag or linebreak exists before `>`.        if (type == null || prevToken == null || prevToken.loc.end.line !== lastToken.loc.start.line) {          return        }        // Check and report.        const hasSpace = (prevToken.range[1] !== lastToken.range[0])        if (type === 'always' && !hasSpace) {          context.report({            node,            loc: lastToken.loc,            message: "Expected a space before '{{bracket}}', but not found.",            data: { bracket: sourceCode.getText(lastToken) },            fix: (fixer) => fixer.insertTextBefore(lastToken, ' ')          })        } else if (type === 'never' && hasSpace) {          context.report({            node,            loc: {              start: prevToken.loc.end,              end: lastToken.loc.end            },            message: "Expected no space before '{{bracket}}', but found.",            data: { bracket: sourceCode.getText(lastToken) },            fix: (fixer) => fixer.removeRange([prevToken.range[1], lastToken.range[0]])          })        }      }    })  }}
 |