elearning/Frontend-Learner/node_modules/eslint-plugin-regexp/dist/rules/use-ignore-case.js
2026-01-13 10:48:02 +07:00

190 lines
7.1 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const refa_1 = require("refa");
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
const utils_1 = require("../utils");
const get_usage_of_pattern_1 = require("../utils/get-usage-of-pattern");
const mention_1 = require("../utils/mention");
const regexp_ast_1 = require("../utils/regexp-ast");
const util_1 = require("../utils/util");
const ELEMENT_ORDER = {
Character: 1,
CharacterClassRange: 2,
CharacterSet: 3,
CharacterClass: 4,
ExpressionCharacterClass: 5,
ClassStringDisjunction: 6,
StringAlternative: 7,
};
function findUseless(elements, getChars, other) {
const get = (0, util_1.cachedFn)(getChars);
const sortedElements = [...elements]
.reverse()
.sort((a, b) => ELEMENT_ORDER[a.type] - ELEMENT_ORDER[b.type]);
const useless = new Set();
for (const e of sortedElements) {
const cs = get(e);
if (cs.isSubsetOf(other)) {
useless.add(e);
continue;
}
const otherElements = elements.filter((o) => o !== e && !useless.has(o));
const total = other.union(...otherElements.map(get));
if (cs.isSubsetOf(total)) {
useless.add(e);
continue;
}
}
return useless;
}
function without(iter, set) {
const result = [];
for (const item of iter) {
if (!set.has(item)) {
result.push(item);
}
}
return result;
}
function removeAll(fixer, patternSource, nodes) {
const charSet = refa_1.CharSet.empty(Number.MAX_SAFE_INTEGER).union(nodes.map((n) => {
let min = n.start;
let max = n.end - 1;
if (n.type === "StringAlternative") {
const parent = n.parent;
if (parent.alternatives.length === 1 ||
parent.alternatives.every((a) => nodes.includes(a))) {
min = parent.start;
max = parent.end - 1;
}
else {
const isFirst = parent.alternatives.at(0) === n;
if (isFirst) {
max++;
}
else {
min--;
}
}
}
return { min, max };
}));
const sorted = charSet.ranges.map(({ min, max }) => ({ start: min, end: max + 1 }));
let pattern = patternSource.value;
let removed = 0;
for (const { start, end } of sorted) {
pattern =
pattern.slice(0, start - removed) + pattern.slice(end - removed);
removed += end - start;
}
const range = patternSource.getReplaceRange({
start: 0,
end: patternSource.value.length,
});
if (range) {
return range.replace(fixer, pattern);
}
return null;
}
function getIgnoreCaseFlagsString(flags) {
if (flags.includes("i")) {
return flags;
}
for (let i = 0; i < flags.length; i++) {
if (flags[i] > "i") {
return `${flags.slice(0, i)}i${flags.slice(i)}`;
}
}
return `${flags}i`;
}
exports.default = (0, utils_1.createRule)("use-ignore-case", {
meta: {
docs: {
description: "use the `i` flag if it simplifies the pattern",
category: "Best Practices",
recommended: true,
},
fixable: "code",
schema: [],
messages: {
unexpected: "The character class(es) {{ classes }} can be simplified using the `i` flag.",
},
type: "suggestion",
},
create(context) {
function createVisitor(regexpContext) {
const { node, flags, ownsFlags, flagsString, patternAst, patternSource, getUsageOfPattern, getFlagsLocation, fixReplaceFlags, } = regexpContext;
if (!ownsFlags || flagsString === null) {
return {};
}
if (flags.ignoreCase) {
return {};
}
if (getUsageOfPattern() === get_usage_of_pattern_1.UsageOfPattern.partial) {
return {};
}
if ((0, regexp_ast_1.isCaseVariant)(patternAst, flags)) {
return {};
}
const uselessElements = [];
const ccs = [];
return {
onCharacterClassEnter(ccNode) {
const elements = ccNode.elements.flatMap((e) => {
if (e.type === "ClassStringDisjunction") {
return e.alternatives;
}
return [e];
});
const invariantElement = elements.filter((e) => !(0, regexp_ast_1.isCaseVariant)(e, flags));
if (invariantElement.length === elements.length) {
return;
}
const empty = refa_1.JS.UnicodeSet.empty(regexp_ast_analysis_1.Chars.maxChar(flags));
const invariant = empty.union(...invariantElement.map((e) => (0, regexp_ast_analysis_1.toUnicodeSet)(e, flags)));
let variantElements = without(elements, new Set(invariantElement));
const alwaysUseless = findUseless(variantElements, (e) => (0, regexp_ast_analysis_1.toUnicodeSet)(e, flags), invariant);
variantElements = without(variantElements, alwaysUseless);
const iFlags = (0, regexp_ast_1.getIgnoreCaseFlags)(flags);
const useless = findUseless(variantElements, (e) => (0, regexp_ast_analysis_1.toUnicodeSet)(e, iFlags), invariant);
uselessElements.push(...useless);
ccs.push(ccNode);
},
onPatternLeave() {
if (uselessElements.length === 0) {
return;
}
context.report({
node,
loc: getFlagsLocation(),
messageId: "unexpected",
data: {
classes: ccs.map((cc) => (0, mention_1.mention)(cc)).join(", "),
},
fix(fixer) {
const patternFix = removeAll(fixer, patternSource, uselessElements);
if (!patternFix) {
return null;
}
const flagsFix = fixReplaceFlags(getIgnoreCaseFlagsString(flagsString), false)(fixer);
if (!flagsFix) {
return null;
}
const fix = [patternFix];
if (Array.isArray(flagsFix)) {
fix.push(...flagsFix);
}
else {
fix.push(flagsFix);
}
return fix;
},
});
},
};
}
return (0, utils_1.defineRegexpVisitor)(context, {
createVisitor,
});
},
});