eslint-plugin-regexp
Version:
ESLint plugin for finding RegExp mistakes and RegExp style guide violations.
117 lines (116 loc) • 5.2 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const ts_util_1 = require("../utils/ts-util");
const eslint_utils_1 = require("@eslint-community/eslint-utils");
exports.default = (0, utils_1.createRule)("prefer-result-array-groups", {
meta: {
docs: {
description: "enforce using result array `groups`",
category: "Stylistic Issues",
recommended: false,
},
fixable: "code",
schema: [
{
type: "object",
properties: {
strictTypes: { type: "boolean" },
},
additionalProperties: false,
},
],
messages: {
unexpected: "Unexpected indexed access for the named capturing group '{{ name }}' from regexp result array.",
},
type: "suggestion",
},
create(context) {
var _a, _b;
const strictTypes = (_b = (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.strictTypes) !== null && _b !== void 0 ? _b : true;
const sourceCode = context.sourceCode;
function createVisitor(regexpContext) {
const { getAllCapturingGroups, getCapturingGroupReferences } = regexpContext;
const capturingGroups = getAllCapturingGroups();
if (!capturingGroups.length) {
return {};
}
for (const ref of getCapturingGroupReferences({ strictTypes })) {
if (ref.type === "ArrayRef" &&
ref.kind === "index" &&
ref.ref != null) {
const cgNode = capturingGroups[ref.ref - 1];
if (cgNode && cgNode.name) {
const memberNode = ref.prop.type === "member" ? ref.prop.node : null;
context.report({
node: ref.prop.node,
messageId: "unexpected",
data: {
name: cgNode.name,
},
fix: memberNode && memberNode.computed
? (fixer) => {
const tokens = sourceCode.getTokensBetween(memberNode.object, memberNode.property);
let openingBracket = tokens.pop();
while (openingBracket &&
!(0, eslint_utils_1.isOpeningBracketToken)(openingBracket)) {
openingBracket = tokens.pop();
}
if (!openingBracket) {
return null;
}
const kind = getRegExpArrayTypeKind(memberNode.object);
if (kind === "unknown") {
return null;
}
const needNonNull = kind === "RegExpXArray";
return fixer.replaceTextRange([
openingBracket.range[0],
memberNode.range[1],
], `${memberNode.optional ? "" : "."}groups${needNonNull ? "!" : ""}.${cgNode.name}`);
}
: null,
});
}
}
}
return {};
}
return (0, utils_1.defineRegexpVisitor)(context, {
createVisitor,
});
function getRegExpArrayTypeKind(node) {
const { tsNodeMap, checker, usedTS, hasFullTypeInformation } = (0, ts_util_1.getTypeScriptTools)(context);
if (!usedTS) {
return null;
}
if (!hasFullTypeInformation) {
return "unknown";
}
const tsNode = tsNodeMap.get(node);
const tsType = (tsNode && (checker === null || checker === void 0 ? void 0 : checker.getTypeAtLocation(tsNode))) || null;
if (!tsType) {
return "unknown";
}
if ((0, ts_util_1.isAny)(tsType)) {
return "any";
}
if (isRegExpMatchArrayOrRegExpExecArray(tsType)) {
return "RegExpXArray";
}
if ((0, ts_util_1.isUnionOrIntersection)(tsType)) {
if (tsType.types.every((t) => isRegExpMatchArrayOrRegExpExecArray(t) || (0, ts_util_1.isNull)(t))) {
return "RegExpXArray";
}
}
return "unknown";
}
function isRegExpMatchArrayOrRegExpExecArray(tsType) {
if ((0, ts_util_1.isClassOrInterface)(tsType)) {
const name = tsType.symbol.escapedName;
return name === "RegExpMatchArray" || name === "RegExpExecArray";
}
return false;
}
},
});
;