UNPKG

eslint-plugin-regexp

Version:

ESLint plugin for finding RegExp mistakes and RegExp style guide violations.

140 lines (139 loc) 5.92 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../utils"); const regex_syntax_1 = require("../utils/regex-syntax"); const REGEX_CHAR_CLASS_ESCAPES = new Set([ utils_1.CP_BACK_SLASH, utils_1.CP_CLOSING_BRACKET, utils_1.CP_MINUS, ]); const REGEX_CLASS_SET_CHAR_CLASS_ESCAPE = new Set([ utils_1.CP_BACK_SLASH, utils_1.CP_SLASH, utils_1.CP_OPENING_BRACKET, utils_1.CP_CLOSING_BRACKET, utils_1.CP_OPENING_BRACE, utils_1.CP_CLOSING_BRACE, utils_1.CP_PIPE, utils_1.CP_OPENING_PAREN, utils_1.CP_CLOSING_PAREN, utils_1.CP_MINUS, ]); const REGEX_ESCAPES = new Set([ utils_1.CP_BACK_SLASH, utils_1.CP_SLASH, utils_1.CP_CARET, utils_1.CP_DOT, utils_1.CP_DOLLAR, utils_1.CP_STAR, utils_1.CP_PLUS, utils_1.CP_QUESTION, utils_1.CP_OPENING_BRACKET, utils_1.CP_CLOSING_BRACKET, utils_1.CP_OPENING_BRACE, utils_1.CP_CLOSING_BRACE, utils_1.CP_PIPE, utils_1.CP_OPENING_PAREN, utils_1.CP_CLOSING_PAREN, ]); const POTENTIAL_ESCAPE_SEQUENCE = new Set("uxkpP"); const POTENTIAL_ESCAPE_SEQUENCE_FOR_CHAR_CLASS = new Set([ ...POTENTIAL_ESCAPE_SEQUENCE, "q", ]); exports.default = (0, utils_1.createRule)("no-useless-escape", { meta: { docs: { description: "disallow unnecessary escape characters in RegExp", category: "Stylistic Issues", recommended: true, }, fixable: "code", schema: [], messages: { unnecessary: "Unnecessary escape character: \\{{character}}.", }, type: "suggestion", }, create(context) { function createVisitor({ node, flags, pattern, getRegexpLocation, fixReplaceNode, }) { function report(cNode, offset, character, fix) { context.report({ node, loc: getRegexpLocation(cNode, [offset, offset + 1]), messageId: "unnecessary", data: { character, }, fix: fix ? fixReplaceNode(cNode, character) : null, }); } const characterClassStack = []; return { onCharacterClassEnter: (characterClassNode) => characterClassStack.unshift(characterClassNode), onCharacterClassLeave: () => characterClassStack.shift(), onExpressionCharacterClassEnter: (characterClassNode) => characterClassStack.unshift(characterClassNode), onExpressionCharacterClassLeave: () => characterClassStack.shift(), onCharacterEnter(cNode) { if (cNode.raw.startsWith("\\")) { const char = cNode.raw.slice(1); const escapedChar = String.fromCodePoint(cNode.value); if (char === escapedChar) { let allowedEscapes; if (characterClassStack.length) { allowedEscapes = flags.unicodeSets ? REGEX_CLASS_SET_CHAR_CLASS_ESCAPE : REGEX_CHAR_CLASS_ESCAPES; } else { allowedEscapes = REGEX_ESCAPES; } if (allowedEscapes.has(cNode.value)) { return; } if (characterClassStack.length) { const characterClassNode = characterClassStack[0]; if (cNode.value === utils_1.CP_CARET) { if (characterClassNode.start + 1 === cNode.start) { return; } } if (flags.unicodeSets) { if (regex_syntax_1.RESERVED_DOUBLE_PUNCTUATOR_CP.has(cNode.value)) { if (pattern[cNode.end] === escapedChar) { return; } const prevIndex = cNode.start - 1; if (pattern[prevIndex] === escapedChar) { if (escapedChar !== "^") { return; } const elementStartIndex = characterClassNode.start + 1 + (characterClassNode.negate ? 1 : 0); if (elementStartIndex <= prevIndex) { return; } } } } } if (!(0, utils_1.canUnwrapped)(cNode, char)) { return; } report(cNode, 0, char, !(characterClassStack.length ? POTENTIAL_ESCAPE_SEQUENCE_FOR_CHAR_CLASS : POTENTIAL_ESCAPE_SEQUENCE).has(char)); } } }, }; } return (0, utils_1.defineRegexpVisitor)(context, { createVisitor, }); }, });