forked from zhurui/management
114 lines
3.8 KiB
JavaScript
114 lines
3.8 KiB
JavaScript
/**
|
|
* @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]])
|
|
})
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|