90 lines
3 KiB
JavaScript
90 lines
3 KiB
JavaScript
/**
|
|
* @author Toru Nagashima
|
|
* @copyright 2016 Toru Nagashima. All rights reserved.
|
|
* See LICENSE file in root directory for full license.
|
|
*/
|
|
'use strict'
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Requirements
|
|
// ------------------------------------------------------------------------------
|
|
|
|
const utils = require('../utils')
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Helpers
|
|
// ------------------------------------------------------------------------------
|
|
|
|
function getPhrase (lineBreaks) {
|
|
switch (lineBreaks) {
|
|
case 0: return 'no line breaks'
|
|
case 1: return '1 line break'
|
|
default: return `${lineBreaks} line breaks`
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
// ------------------------------------------------------------------------------
|
|
|
|
module.exports = {
|
|
meta: {
|
|
type: 'layout',
|
|
docs: {
|
|
description: "require or disallow a line break before tag's closing brackets",
|
|
category: '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
|
|
}]
|
|
},
|
|
|
|
create (context) {
|
|
const options = Object.assign({}, {
|
|
singleline: 'never',
|
|
multiline: 'always'
|
|
}, context.options[0] || {})
|
|
const template = context.parserServices.getTemplateBodyTokenStore && context.parserServices.getTemplateBodyTokenStore()
|
|
|
|
return utils.defineTemplateBodyVisitor(context, {
|
|
'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) {
|
|
const range = [prevToken.range[1], closingBracketToken.range[0]]
|
|
const text = '\n'.repeat(expectedLineBreaks)
|
|
return fixer.replaceTextRange(range, text)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|