eslint-plugin-regexp
Version:
ESLint plugin for finding RegExp mistakes and RegExp style guide violations.
308 lines (307 loc) • 11 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.dereferenceVariable = exports.dereferenceOwnedVariable = exports.astRangeToLocation = exports.getPropertyName = exports.isStringLiteral = exports.isRegexpLiteral = exports.getStringValueRange = exports.parseReplacements = exports.isKnownMethodCall = exports.findFunction = exports.getScope = exports.getStaticValue = exports.getStringIfConstant = exports.findVariable = exports.getParent = void 0;
const replacements_utils_1 = require("../replacements-utils");
const string_literal_parser_1 = require("../string-literal-parser");
const eslintUtils = __importStar(require("@eslint-community/eslint-utils"));
function getParent(node) {
if (!node) {
return null;
}
return node.parent;
}
exports.getParent = getParent;
function findVariable(context, node) {
return eslintUtils.findVariable(getScope(context, node), node);
}
exports.findVariable = findVariable;
function findSimpleVariable(context, identifier) {
const variable = findVariable(context, identifier);
if (!variable || variable.defs.length !== 1) {
return null;
}
const def = variable.defs[0];
if (def.type !== "Variable" || def.node.id.type !== "Identifier") {
return null;
}
return variable;
}
function getStringIfConstant(context, node) {
if (node.type === "BinaryExpression" ||
node.type === "MemberExpression" ||
node.type === "Identifier" ||
node.type === "TemplateLiteral") {
const evaluated = getStaticValue(context, node);
return evaluated && String(evaluated.value);
}
return eslintUtils.getStringIfConstant(node, getScope(context, node));
}
exports.getStringIfConstant = getStringIfConstant;
function getStaticValue(context, node) {
if (node.type === "BinaryExpression") {
if (node.operator === "+") {
const left = getStaticValue(context, node.left);
if (left == null) {
return null;
}
const right = getStaticValue(context, node.right);
if (right == null) {
return null;
}
return {
value: left.value + right.value,
};
}
}
else if (node.type === "MemberExpression") {
const propName = getPropertyName(node, context);
if (propName === "source") {
const object = getStaticValue(context, node.object);
if (object && object.value instanceof RegExp) {
return { value: object.value.source };
}
}
}
else if (node.type === "TemplateLiteral") {
const expressions = [];
for (const expr of node.expressions) {
const exprValue = getStaticValue(context, expr);
if (!exprValue) {
return null;
}
expressions.push(exprValue);
}
let value = node.quasis[0].value.cooked;
for (let i = 0; i < expressions.length; ++i) {
value += String(expressions[i].value);
value += node.quasis[i + 1].value.cooked;
}
return { value };
}
else if (node.type === "Identifier") {
const deRef = dereferenceVariable(context, node);
if (deRef !== node) {
return getStaticValue(context, deRef);
}
}
return eslintUtils.getStaticValue(node, getScope(context, node));
}
exports.getStaticValue = getStaticValue;
function getScope(context, currentNode) {
const scopeManager = context.sourceCode.scopeManager;
let node = currentNode;
for (; node; node = node.parent || null) {
const scope = scopeManager.acquire(node, false);
if (scope) {
if (scope.type === "function-expression-name") {
return scope.childScopes[0];
}
return scope;
}
}
return scopeManager.scopes[0];
}
exports.getScope = getScope;
function findFunction(context, id) {
let target = id;
const set = new Set();
for (;;) {
if (set.has(target)) {
return null;
}
set.add(target);
const calleeVariable = findVariable(context, target);
if (!calleeVariable) {
return null;
}
if (calleeVariable.defs.length === 1) {
const def = calleeVariable.defs[0];
if (def.node.type === "FunctionDeclaration") {
return def.node;
}
if (def.type === "Variable" &&
def.parent.kind === "const" &&
def.node.init) {
if (def.node.init.type === "FunctionExpression" ||
def.node.init.type === "ArrowFunctionExpression") {
return def.node.init;
}
if (def.node.init.type === "Identifier") {
target = def.node.init;
continue;
}
}
}
return null;
}
}
exports.findFunction = findFunction;
function isKnownMethodCall(node, methods) {
const mem = node.callee;
if (mem.type !== "MemberExpression" ||
mem.computed ||
mem.property.type !== "Identifier") {
return false;
}
const argLength = methods[mem.property.name];
if (node.arguments.length !== argLength) {
return false;
}
if (node.arguments.some((arg) => arg.type === "SpreadElement")) {
return false;
}
const object = mem.object;
if (object.type === "Super") {
return false;
}
return true;
}
exports.isKnownMethodCall = isKnownMethodCall;
function parseReplacements(context, node) {
const stringLiteral = (0, string_literal_parser_1.parseStringLiteral)(context.sourceCode.text, {
start: node.range[0],
end: node.range[1],
});
const tokens = stringLiteral.tokens.filter((t) => t.value);
return (0, replacements_utils_1.baseParseReplacements)(tokens, (start, end) => {
return {
range: [start.range[0], end.range[1]],
};
});
}
exports.parseReplacements = parseReplacements;
function getStringValueRange(sourceCode, node, startOffset, endOffset) {
if (!node.range) {
return null;
}
if (node.value.length < endOffset) {
return null;
}
try {
const raw = sourceCode.text.slice(node.range[0] + 1, node.range[1] - 1);
let valueIndex = 0;
let start = null;
for (const t of (0, string_literal_parser_1.parseStringTokens)(raw)) {
const endIndex = valueIndex + t.value.length;
if (start == null &&
valueIndex <= startOffset &&
startOffset < endIndex) {
start = t.range[0];
}
if (start != null &&
valueIndex < endOffset &&
endOffset <= endIndex) {
const end = t.range[1];
const nodeStart = node.range[0] + 1;
return [nodeStart + start, nodeStart + end];
}
valueIndex = endIndex;
}
}
catch (_a) {
}
return null;
}
exports.getStringValueRange = getStringValueRange;
function isRegexpLiteral(node) {
return node.type === "Literal" && "regex" in node;
}
exports.isRegexpLiteral = isRegexpLiteral;
function isStringLiteral(node) {
return node.type === "Literal" && typeof node.value === "string";
}
exports.isStringLiteral = isStringLiteral;
function getPropertyName(node, context) {
const prop = node.property;
if (prop.type === "PrivateIdentifier") {
return null;
}
if (!node.computed) {
return prop.name;
}
if (context) {
return getStringIfConstant(context, prop);
}
if (isStringLiteral(prop)) {
return prop.value;
}
return null;
}
exports.getPropertyName = getPropertyName;
function astRangeToLocation(sourceCode, range) {
return {
start: sourceCode.getLocFromIndex(range[0]),
end: sourceCode.getLocFromIndex(range[1]),
};
}
exports.astRangeToLocation = astRangeToLocation;
function dereferenceOwnedVariable(context, expression) {
if (expression.type === "Identifier") {
const variable = findSimpleVariable(context, expression);
if (!variable) {
return expression;
}
const def = variable.defs[0];
const grandParent = getParent(def.parent);
if (grandParent && grandParent.type === "ExportNamedDeclaration") {
return expression;
}
if (variable.references.length !== 2) {
return expression;
}
const [initRef, thisRef] = variable.references;
if (!(initRef.init &&
initRef.writeExpr &&
initRef.writeExpr === def.node.init) ||
thisRef.identifier !== expression) {
return expression;
}
return dereferenceOwnedVariable(context, def.node.init);
}
return expression;
}
exports.dereferenceOwnedVariable = dereferenceOwnedVariable;
function dereferenceVariable(context, expression) {
if (expression.type === "Identifier") {
const variable = findSimpleVariable(context, expression);
if (variable) {
const def = variable.defs[0];
if (def.node.init) {
if (def.parent.kind === "const") {
return dereferenceVariable(context, def.node.init);
}
const refs = variable.references;
const inits = refs.filter((r) => r.init).length;
const reads = refs.filter((r) => r.isReadOnly()).length;
if (inits === 1 && reads + inits === refs.length) {
return dereferenceVariable(context, def.node.init);
}
}
}
}
return expression;
}
exports.dereferenceVariable = dereferenceVariable;