123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- /**
- * @author Yosuke Ota
- * @fileoverview Rule to disalow whitespace that is not a tab or space, whitespace inside strings and comments are allowed
- */
- 'use strict'
- const utils = require('../utils')
- const ALL_IRREGULARS =
- /[\f\v\u0085\uFEFF\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000\u2028\u2029]/u
- const IRREGULAR_WHITESPACE =
- /[\f\v\u0085\uFEFF\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u200B\u202F\u205F\u3000]+/gmu
- const IRREGULAR_LINE_TERMINATORS = /[\u2028\u2029]/gmu
- module.exports = {
- meta: {
- type: 'problem',
- docs: {
- description: 'disallow irregular whitespace in `.vue` files',
- categories: undefined,
- url: 'https://eslint.vuejs.org/rules/no-irregular-whitespace.html',
- extensionSource: {
- url: 'https://eslint.org/docs/rules/no-irregular-whitespace',
- name: 'ESLint core'
- }
- },
- schema: [
- {
- type: 'object',
- properties: {
- skipComments: {
- type: 'boolean',
- default: false
- },
- skipStrings: {
- type: 'boolean',
- default: true
- },
- skipTemplates: {
- type: 'boolean',
- default: false
- },
- skipRegExps: {
- type: 'boolean',
- default: false
- },
- skipHTMLAttributeValues: {
- type: 'boolean',
- default: false
- },
- skipHTMLTextContents: {
- type: 'boolean',
- default: false
- }
- },
- additionalProperties: false
- }
- ],
- messages: {
- disallow: 'Irregular whitespace not allowed.'
- }
- },
- /**
- * @param {RuleContext} context - The rule context.
- * @returns {RuleListener} AST event handlers.
- */
- create(context) {
- // Module store of error indexes that we have found
- /** @type {number[]} */
- let errorIndexes = []
- // Lookup the `skipComments` option, which defaults to `false`.
- const options = context.options[0] || {}
- const skipComments = !!options.skipComments
- const skipStrings = options.skipStrings !== false
- const skipRegExps = !!options.skipRegExps
- const skipTemplates = !!options.skipTemplates
- const skipHTMLAttributeValues = !!options.skipHTMLAttributeValues
- const skipHTMLTextContents = !!options.skipHTMLTextContents
- const sourceCode = context.getSourceCode()
- /**
- * Removes errors that occur inside a string node
- * @param {ASTNode | Token} node to check for matching errors.
- * @returns {void}
- * @private
- */
- function removeWhitespaceError(node) {
- const [startIndex, endIndex] = node.range
- errorIndexes = errorIndexes.filter(
- (errorIndex) => errorIndex < startIndex || endIndex <= errorIndex
- )
- }
- /**
- * Checks literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
- * @param {Literal} node to check for matching errors.
- * @returns {void}
- * @private
- */
- function removeInvalidNodeErrorsInLiteral(node) {
- const shouldCheckStrings = skipStrings && typeof node.value === 'string'
- const shouldCheckRegExps = skipRegExps && Boolean(node.regex)
- // If we have irregular characters, remove them from the errors list
- if (
- (shouldCheckStrings || shouldCheckRegExps) &&
- ALL_IRREGULARS.test(sourceCode.getText(node))
- ) {
- removeWhitespaceError(node)
- }
- }
- /**
- * Checks template string literal nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
- * @param {TemplateElement} node to check for matching errors.
- * @returns {void}
- * @private
- */
- function removeInvalidNodeErrorsInTemplateLiteral(node) {
- if (ALL_IRREGULARS.test(node.value.raw)) {
- removeWhitespaceError(node)
- }
- }
- /**
- * Checks HTML attribute value nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
- * @param {VLiteral} node to check for matching errors.
- * @returns {void}
- * @private
- */
- function removeInvalidNodeErrorsInHTMLAttributeValue(node) {
- if (ALL_IRREGULARS.test(sourceCode.getText(node))) {
- removeWhitespaceError(node)
- }
- }
- /**
- * Checks HTML text content nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
- * @param {VText} node to check for matching errors.
- * @returns {void}
- * @private
- */
- function removeInvalidNodeErrorsInHTMLTextContent(node) {
- if (ALL_IRREGULARS.test(sourceCode.getText(node))) {
- removeWhitespaceError(node)
- }
- }
- /**
- * Checks comment nodes for errors that we are choosing to ignore and calls the relevant methods to remove the errors
- * @param {Comment | HTMLComment | HTMLBogusComment} node to check for matching errors.
- * @returns {void}
- * @private
- */
- function removeInvalidNodeErrorsInComment(node) {
- if (ALL_IRREGULARS.test(node.value)) {
- removeWhitespaceError(node)
- }
- }
- /**
- * Checks the program source for irregular whitespaces and irregular line terminators
- * @returns {void}
- * @private
- */
- function checkForIrregularWhitespace() {
- const source = sourceCode.getText()
- let match
- while ((match = IRREGULAR_WHITESPACE.exec(source)) !== null) {
- errorIndexes.push(match.index)
- }
- while ((match = IRREGULAR_LINE_TERMINATORS.exec(source)) !== null) {
- errorIndexes.push(match.index)
- }
- }
- checkForIrregularWhitespace()
- if (errorIndexes.length === 0) {
- return {}
- }
- const bodyVisitor = utils.defineTemplateBodyVisitor(context, {
- ...(skipHTMLAttributeValues
- ? {
- 'VAttribute[directive=false] > VLiteral':
- removeInvalidNodeErrorsInHTMLAttributeValue
- }
- : {}),
- ...(skipHTMLTextContents
- ? { VText: removeInvalidNodeErrorsInHTMLTextContent }
- : {}),
- // inline scripts
- Literal: removeInvalidNodeErrorsInLiteral,
- ...(skipTemplates
- ? { TemplateElement: removeInvalidNodeErrorsInTemplateLiteral }
- : {})
- })
- return {
- ...bodyVisitor,
- Literal: removeInvalidNodeErrorsInLiteral,
- ...(skipTemplates
- ? { TemplateElement: removeInvalidNodeErrorsInTemplateLiteral }
- : {}),
- 'Program:exit'(node) {
- if (bodyVisitor['Program:exit']) {
- bodyVisitor['Program:exit'](node)
- }
- const templateBody = node.templateBody
- if (skipComments) {
- // First strip errors occurring in comment nodes.
- for (const node of sourceCode.getAllComments()) {
- removeInvalidNodeErrorsInComment(node)
- }
- if (templateBody) {
- for (const node of templateBody.comments) {
- removeInvalidNodeErrorsInComment(node)
- }
- }
- }
- // Removes errors that occur outside script and template
- const [scriptStart, scriptEnd] = node.range
- const [templateStart, templateEnd] = templateBody
- ? templateBody.range
- : [0, 0]
- errorIndexes = errorIndexes.filter(
- (errorIndex) =>
- (scriptStart <= errorIndex && errorIndex < scriptEnd) ||
- (templateStart <= errorIndex && errorIndex < templateEnd)
- )
- // If we have any errors remaining, report on them
- for (const errorIndex of errorIndexes) {
- context.report({
- loc: sourceCode.getLocFromIndex(errorIndex),
- messageId: 'disallow'
- })
- }
- }
- }
- }
- }
|