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.

204 lines
5.5 KiB

/**
* @author Yosuke ota
* See LICENSE file in root directory for full license.
*/
'use strict'
const htmlComments = require('../utils/html-comments')
/**
* @typedef { import('../utils/html-comments').ParsedHTMLComment } ParsedHTMLComment
*/
/**
* @param {any} param
*/
function parseOption(param) {
if (param && typeof param === 'string') {
return {
singleline: param,
multiline: param
}
}
return Object.assign(
{
singleline: 'never',
multiline: 'always'
},
param
)
}
module.exports = {
meta: {
type: 'layout',
docs: {
description: 'enforce unified line brake in HTML comments',
categories: undefined,
url: 'https://eslint.vuejs.org/rules/html-comment-content-newline.html'
},
fixable: 'whitespace',
schema: [
{
anyOf: [
{
enum: ['always', 'never']
},
{
type: 'object',
properties: {
singleline: { enum: ['always', 'never', 'ignore'] },
multiline: { enum: ['always', 'never', 'ignore'] }
},
additionalProperties: false
}
]
},
{
type: 'object',
properties: {
exceptions: {
type: 'array',
items: {
type: 'string'
}
}
},
additionalProperties: false
}
],
messages: {
expectedAfterHTMLCommentOpen: "Expected line break after '<!--'.",
expectedBeforeHTMLCommentOpen: "Expected line break before '-->'.",
expectedAfterExceptionBlock: 'Expected line break after exception block.',
expectedBeforeExceptionBlock:
'Expected line break before exception block.',
unexpectedAfterHTMLCommentOpen: "Unexpected line breaks after '<!--'.",
unexpectedBeforeHTMLCommentOpen: "Unexpected line breaks before '-->'."
}
},
/** @param {RuleContext} context */
create(context) {
const option = parseOption(context.options[0])
return htmlComments.defineVisitor(
context,
context.options[1],
(comment) => {
const { value, openDecoration, closeDecoration } = comment
if (!value) {
return
}
const startLine = openDecoration
? openDecoration.loc.end.line
: value.loc.start.line
const endLine = closeDecoration
? closeDecoration.loc.start.line
: value.loc.end.line
const newlineType =
startLine === endLine ? option.singleline : option.multiline
if (newlineType === 'ignore') {
return
}
checkCommentOpen(comment, newlineType !== 'never')
checkCommentClose(comment, newlineType !== 'never')
}
)
/**
* Reports the newline before the contents of a given comment if it's invalid.
* @param {ParsedHTMLComment} comment - comment data.
* @param {boolean} requireNewline - `true` if line breaks are required.
* @returns {void}
*/
function checkCommentOpen(comment, requireNewline) {
const { value, openDecoration, open } = comment
if (!value) {
return
}
const beforeToken = openDecoration || open
if (requireNewline) {
if (beforeToken.loc.end.line < value.loc.start.line) {
// Is valid
return
}
context.report({
loc: {
start: beforeToken.loc.end,
end: value.loc.start
},
messageId: openDecoration
? 'expectedAfterExceptionBlock'
: 'expectedAfterHTMLCommentOpen',
fix: openDecoration
? undefined
: (fixer) => fixer.insertTextAfter(beforeToken, '\n')
})
} else {
if (beforeToken.loc.end.line === value.loc.start.line) {
// Is valid
return
}
context.report({
loc: {
start: beforeToken.loc.end,
end: value.loc.start
},
messageId: 'unexpectedAfterHTMLCommentOpen',
fix: (fixer) =>
fixer.replaceTextRange([beforeToken.range[1], value.range[0]], ' ')
})
}
}
/**
* Reports the space after the contents of a given comment if it's invalid.
* @param {ParsedHTMLComment} comment - comment data.
* @param {boolean} requireNewline - `true` if line breaks are required.
* @returns {void}
*/
function checkCommentClose(comment, requireNewline) {
const { value, closeDecoration, close } = comment
if (!value) {
return
}
const afterToken = closeDecoration || close
if (requireNewline) {
if (value.loc.end.line < afterToken.loc.start.line) {
// Is valid
return
}
context.report({
loc: {
start: value.loc.end,
end: afterToken.loc.start
},
messageId: closeDecoration
? 'expectedBeforeExceptionBlock'
: 'expectedBeforeHTMLCommentOpen',
fix: closeDecoration
? undefined
: (fixer) => fixer.insertTextBefore(afterToken, '\n')
})
} else {
if (value.loc.end.line === afterToken.loc.start.line) {
// Is valid
return
}
context.report({
loc: {
start: value.loc.end,
end: afterToken.loc.start
},
messageId: 'unexpectedBeforeHTMLCommentOpen',
fix: (fixer) =>
fixer.replaceTextRange([value.range[1], afterToken.range[0]], ' ')
})
}
}
}
}