/** * @author Yosuke ota * See LICENSE file in root directory for full license. */ 'use strict' // ----------------------------------------------------------------------------- // Requirements // ----------------------------------------------------------------------------- const htmlComments = require('../utils/html-comments') /** * @typedef { import('../utils/html-comments').ParsedHTMLComment } ParsedHTMLComment */ // ------------------------------------------------------------------------------ // Helpers // ------------------------------------------------------------------------------ /** * @param {any} param */ function parseOption(param) { if (param && typeof param === 'string') { return { singleline: param, multiline: param } } return Object.assign( { singleline: 'never', multiline: 'always' }, param ) } // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ 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 ''.", expectedAfterExceptionBlock: 'Expected line break after exception block.', expectedBeforeExceptionBlock: 'Expected line break before exception block.', unexpectedAfterHTMLCommentOpen: "Unexpected line breaks after ''." } }, /** @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]], ' ') }) } } } }