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.

104 lines
2.8 KiB

1 year ago
/**
* @author Toru Nagashima
* @copyright 2016 Toru Nagashima. All rights reserved.
* See LICENSE file in root directory for full license.
*/
'use strict'
const utils = require('../utils')
/**
* @param {number} lineBreaks
*/
function getPhrase(lineBreaks) {
switch (lineBreaks) {
case 0:
return 'no line breaks'
case 1:
return '1 line break'
default:
return `${lineBreaks} line breaks`
}
}
module.exports = {
meta: {
type: 'layout',
docs: {
description:
"require or disallow a line break before tag's closing brackets",
categories: ['vue3-strongly-recommended', 'strongly-recommended'],
url: 'https://eslint.vuejs.org/rules/html-closing-bracket-newline.html'
},
fixable: 'whitespace',
schema: [
{
type: 'object',
properties: {
singleline: { enum: ['always', 'never'] },
multiline: { enum: ['always', 'never'] }
},
additionalProperties: false
}
]
},
/** @param {RuleContext} context */
create(context) {
const options = Object.assign(
{},
{
singleline: 'never',
multiline: 'always'
},
context.options[0] || {}
)
const template =
context.parserServices.getTemplateBodyTokenStore &&
context.parserServices.getTemplateBodyTokenStore()
return utils.defineDocumentVisitor(context, {
/** @param {VStartTag | VEndTag} node */
'VStartTag, VEndTag'(node) {
const closingBracketToken = template.getLastToken(node)
if (
closingBracketToken.type !== 'HTMLSelfClosingTagClose' &&
closingBracketToken.type !== 'HTMLTagClose'
) {
return
}
const prevToken = template.getTokenBefore(closingBracketToken)
const type =
node.loc.start.line === prevToken.loc.end.line
? 'singleline'
: 'multiline'
const expectedLineBreaks = options[type] === 'always' ? 1 : 0
const actualLineBreaks =
closingBracketToken.loc.start.line - prevToken.loc.end.line
if (actualLineBreaks !== expectedLineBreaks) {
context.report({
node,
loc: {
start: prevToken.loc.end,
end: closingBracketToken.loc.start
},
message:
'Expected {{expected}} before closing bracket, but {{actual}} found.',
data: {
expected: getPhrase(expectedLineBreaks),
actual: getPhrase(actualLineBreaks)
},
fix(fixer) {
/** @type {Range} */
const range = [prevToken.range[1], closingBracketToken.range[0]]
const text = '\n'.repeat(expectedLineBreaks)
return fixer.replaceTextRange(range, text)
}
})
}
}
})
}
}