UNPKG

eslint-plugin-regexp

Version:

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

157 lines (156 loc) 6.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const utils_1 = require("../utils"); const regex_syntax_1 = require("../utils/regex-syntax"); const CASE_SCHEMA = ["lowercase", "uppercase", "ignore"]; const DEFAULTS = { caseInsensitive: "lowercase", unicodeEscape: "lowercase", hexadecimalEscape: "lowercase", controlEscape: "uppercase", }; function parseOptions(option) { if (!option) { return DEFAULTS; } return { caseInsensitive: option.caseInsensitive || DEFAULTS.caseInsensitive, unicodeEscape: option.unicodeEscape || DEFAULTS.unicodeEscape, hexadecimalEscape: option.hexadecimalEscape || DEFAULTS.hexadecimalEscape, controlEscape: option.controlEscape || DEFAULTS.controlEscape, }; } const CODE_POINT_CASE_CHECKER = { lowercase: utils_1.isLowercaseLetter, uppercase: utils_1.isUppercaseLetter, }; const STRING_CASE_CHECKER = { lowercase: (s) => s.toLowerCase() === s, uppercase: (s) => s.toUpperCase() === s, }; const CONVERTER = { lowercase: (s) => s.toLowerCase(), uppercase: (s) => s.toUpperCase(), }; exports.default = (0, utils_1.createRule)("letter-case", { meta: { docs: { description: "enforce into your favorite case", category: "Stylistic Issues", recommended: false, }, fixable: "code", schema: [ { type: "object", properties: { caseInsensitive: { enum: CASE_SCHEMA }, unicodeEscape: { enum: CASE_SCHEMA }, hexadecimalEscape: { enum: CASE_SCHEMA }, controlEscape: { enum: CASE_SCHEMA }, }, additionalProperties: false, }, ], messages: { unexpected: "'{{char}}' is not in {{case}}", }, type: "layout", }, create(context) { const options = parseOptions(context.options[0]); function report({ node, getRegexpLocation, fixReplaceNode }, reportNode, letterCase, convertText) { context.report({ node, loc: getRegexpLocation(reportNode), messageId: "unexpected", data: { char: reportNode.raw, case: letterCase, }, fix: fixReplaceNode(reportNode, () => convertText(CONVERTER[letterCase])), }); } function verifyCharacterInCaseInsensitive(regexpContext, cNode) { if (cNode.parent.type === "CharacterClassRange" || options.caseInsensitive === "ignore") { return; } if (CODE_POINT_CASE_CHECKER[options.caseInsensitive](cNode.value) || !(0, utils_1.isLetter)(cNode.value)) { return; } report(regexpContext, cNode, options.caseInsensitive, (converter) => converter(String.fromCodePoint(cNode.value))); } function verifyCharacterClassRangeInCaseInsensitive(regexpContext, ccrNode) { if (options.caseInsensitive === "ignore") { return; } if (CODE_POINT_CASE_CHECKER[options.caseInsensitive](ccrNode.min.value) || !(0, utils_1.isLetter)(ccrNode.min.value) || CODE_POINT_CASE_CHECKER[options.caseInsensitive](ccrNode.max.value) || !(0, utils_1.isLetter)(ccrNode.max.value)) { return; } report(regexpContext, ccrNode, options.caseInsensitive, (converter) => `${converter(String.fromCodePoint(ccrNode.min.value))}-${converter(String.fromCodePoint(ccrNode.max.value))}`); } function verifyCharacterInUnicodeEscape(regexpContext, cNode) { if (options.unicodeEscape === "ignore") { return; } const parts = /^(?<prefix>\\u\{?)(?<code>.*?)(?<suffix>\}?)$/u.exec(cNode.raw); if (STRING_CASE_CHECKER[options.unicodeEscape](parts.groups.code)) { return; } report(regexpContext, cNode, options.unicodeEscape, (converter) => `${parts.groups.prefix}${converter(parts.groups.code)}${parts.groups.suffix}`); } function verifyCharacterInHexadecimalEscape(regexpContext, cNode) { if (options.hexadecimalEscape === "ignore") { return; } const parts = /^\\x(?<code>.*)$/u.exec(cNode.raw); if (STRING_CASE_CHECKER[options.hexadecimalEscape](parts.groups.code)) { return; } report(regexpContext, cNode, options.hexadecimalEscape, (converter) => `\\x${converter(parts.groups.code)}`); } function verifyCharacterInControl(regexpContext, cNode) { if (options.controlEscape === "ignore") { return; } const parts = /^\\c(?<code>.*)$/u.exec(cNode.raw); if (STRING_CASE_CHECKER[options.controlEscape](parts.groups.code)) { return; } report(regexpContext, cNode, options.controlEscape, (converter) => `\\c${converter(parts.groups.code)}`); } function createVisitor(regexpContext) { const { flags } = regexpContext; return Object.assign({ onCharacterEnter(cNode) { if (flags.ignoreCase) { verifyCharacterInCaseInsensitive(regexpContext, cNode); } const escapeKind = (0, regex_syntax_1.getEscapeSequenceKind)(cNode.raw); if (escapeKind === regex_syntax_1.EscapeSequenceKind.unicode || escapeKind === regex_syntax_1.EscapeSequenceKind.unicodeCodePoint) { verifyCharacterInUnicodeEscape(regexpContext, cNode); } if (escapeKind === regex_syntax_1.EscapeSequenceKind.hexadecimal) { verifyCharacterInHexadecimalEscape(regexpContext, cNode); } if (escapeKind === regex_syntax_1.EscapeSequenceKind.control) { verifyCharacterInControl(regexpContext, cNode); } } }, (flags.ignoreCase ? { onCharacterClassRangeEnter(ccrNode) { verifyCharacterClassRangeInCaseInsensitive(regexpContext, ccrNode); }, } : {})); } return (0, utils_1.defineRegexpVisitor)(context, { createVisitor, }); }, });