You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP

eslint-plugin-regexp

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-regexp - npm Package Compare versions

Comparing version

to
0.12.0

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const regexp_ast_analysis_1 = require("regexp-ast-analysis");
const utils_1 = require("../utils");
const no_empty_capturing_group_1 = __importDefault(require("./no-empty-capturing-group"));
exports.default = utils_1.createRule("no-assertion-capturing-group", {
meta: {
docs: {
description: "disallow capturing group that captures assertions.",
category: "Possible Errors",
recommended: true,
},
schema: [],
messages: {
unexpected: "Unexpected capture assertions.",
},
type: "suggestion",
},
meta: Object.assign(Object.assign({}, no_empty_capturing_group_1.default.meta), { docs: Object.assign(Object.assign({}, no_empty_capturing_group_1.default.meta.docs), { recommended: true, replacedBy: ["no-empty-capturing-group"] }) }),
create(context) {
function createVisitor({ node, getRegexpLocation, }) {
return {
onCapturingGroupEnter(cgNode) {
if (regexp_ast_analysis_1.isZeroLength(cgNode)) {
context.report({
node,
loc: getRegexpLocation(cgNode),
messageId: "unexpected",
});
}
},
};
}
return utils_1.defineRegexpVisitor(context, {
createVisitor,
});
return no_empty_capturing_group_1.default.create(context);
},
});
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const get_usage_of_pattern_1 = require("../utils/get-usage-of-pattern");
function* extractLazyEndQuantifiers(alternatives) {

@@ -39,3 +40,11 @@ for (const { elements } of alternatives) {

},
schema: [],
schema: [
{
type: "object",
properties: {
ignorePartial: { type: "boolean" },
},
additionalProperties: false,
},
],
messages: {

@@ -49,3 +58,11 @@ uselessElement: "The quantifier and the quantified element can be removed because the quantifier is lazy and has a minimum of 0.",

create(context) {
function createVisitor({ node, getRegexpLocation, }) {
var _a, _b;
const ignorePartial = (_b = (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.ignorePartial) !== null && _b !== void 0 ? _b : true;
function createVisitor({ node, getRegexpLocation, getUsageOfPattern, }) {
if (ignorePartial) {
const usageOfPattern = getUsageOfPattern();
if (usageOfPattern !== get_usage_of_pattern_1.UsageOfPattern.whole) {
return {};
}
}
return {

@@ -52,0 +69,0 @@ onPatternEnter(pNode) {

@@ -96,118 +96,2 @@ "use strict";

}
function getProperty(node) {
if (node.type === "MemberExpression") {
if (node.computed) {
if (node.property.type === "Literal") {
if (typeof node.property.value === "string" ||
typeof node.property.value === "number")
return String(node.property.value);
}
}
else if (node.property.type === "Identifier") {
return node.property.name;
}
return null;
}
if (node.type === "Property") {
if (node.computed) {
if (node.key.type === "Literal") {
if (typeof node.key.value === "string" ||
typeof node.key.value === "number")
return String(node.key.value);
}
}
else if (node.key.type === "Identifier") {
return node.key.name;
}
return null;
}
return null;
}
function extractUsedReferencesFromPattern(node, context) {
const references = [];
if (node.type === "ArrayPattern") {
for (let index = 0; index < node.elements.length; index++) {
const element = node.elements[index];
if (!element) {
continue;
}
if (element.type === "RestElement") {
return null;
}
references.push({
ref: String(index),
node: element,
getSubReferences: () => extractUsedReferencesFromPattern(element, context),
});
}
return references;
}
if (node.type === "ObjectPattern") {
for (const prop of node.properties) {
if (prop.type === "RestElement") {
return null;
}
const property = getProperty(prop);
if (property == null)
return null;
references.push({
ref: property,
node: prop.value,
getSubReferences: () => extractUsedReferencesFromPattern(prop.value, context),
});
}
return references;
}
if (node.type === "AssignmentPattern") {
return extractUsedReferencesFromPattern(node.left, context);
}
if (node.type === "Identifier") {
const variable = ast_utils_1.findVariable(context, node);
if (!variable) {
return null;
}
for (const reference of variable.references) {
if (reference.isRead()) {
const res = extractUsedReferencesFromExpression(reference.identifier, context);
if (res == null) {
return null;
}
references.push(...res);
}
}
return references;
}
return null;
}
function extractUsedReferencesFromExpression(node, context) {
const parent = node.parent;
if (parent.type === "MemberExpression") {
if (parent.object !== node) {
return null;
}
const property = getProperty(parent);
if (property == null)
return null;
return [
{
ref: property,
node: parent,
getSubReferences: () => extractUsedReferencesFromExpression(parent, context),
},
];
}
else if (parent.type === "AssignmentExpression") {
if (parent.right !== node || parent.operator !== "=") {
return null;
}
return extractUsedReferencesFromPattern(parent.left, context);
}
else if (parent.type === "VariableDeclarator") {
if (parent.init !== node) {
return null;
}
return extractUsedReferencesFromPattern(parent.id, context);
}
return null;
}
exports.default = utils_1.createRule("no-unused-capturing-group", {

@@ -231,20 +115,2 @@ meta: {

const capturingDataMap = new Map();
function getCapturingData(node) {
const re = capturingDataMap.get(node);
if (re) {
return re;
}
if (node.type === "Identifier") {
const variable = ast_utils_1.findVariable(context, node);
if (variable && variable.defs.length === 1) {
const def = variable.defs[0];
if (def.type === "Variable" &&
def.parent.kind === "const" &&
def.node.init) {
return getCapturingData(def.node.init);
}
}
}
return null;
}
function reportUnused(capturingData) {

@@ -273,3 +139,3 @@ const { node, getRegexpLocation } = capturingData.regexpContext;

function verifyForMatch(node) {
const capturingData = getCapturingData(node.arguments[0]);
const capturingData = capturingDataMap.get(node.arguments[0]);
if (capturingData == null || capturingData.isAllUsed()) {

@@ -291,3 +157,3 @@ return;

function verifyForSearch(node) {
const capturingData = getCapturingData(node.arguments[0]);
const capturingData = capturingDataMap.get(node.arguments[0]);
if (capturingData == null || capturingData.isAllUsed()) {

@@ -303,3 +169,3 @@ return;

function verifyForTest(node) {
const capturingData = getCapturingData(node.callee.object);
const capturingData = capturingDataMap.get(node.callee.object);
if (capturingData == null || capturingData.isAllUsed()) {

@@ -311,3 +177,3 @@ return;

function verifyForReplace(node) {
const capturingData = getCapturingData(node.arguments[0]);
const capturingData = capturingDataMap.get(node.arguments[0]);
if (capturingData == null || capturingData.isAllUsed()) {

@@ -367,3 +233,3 @@ return;

function verifyForExec(node) {
const capturingData = getCapturingData(node.callee.object);
const capturingData = capturingDataMap.get(node.callee.object);
if (capturingData == null || capturingData.isAllUsed()) {

@@ -376,21 +242,21 @@ return;

function verifyForExecResult(node, capturingData) {
const refs = extractUsedReferencesFromExpression(node, context);
if (refs == null) {
capturingData.markAsCannotTrack();
return;
}
for (const ref of refs) {
if (ref.ref === "groups") {
const sub = ref.getSubReferences();
if (sub == null) {
capturingData.usedAllNames();
for (const ref of ast_utils_1.extractPropertyReferences(node, context)) {
if (hasNameRef(ref)) {
if (ref.name === "groups") {
for (const namedRef of ref.extractPropertyReferences()) {
if (hasNameRef(namedRef)) {
capturingData.usedName(namedRef.name);
}
else {
capturingData.usedAllNames();
}
}
}
else {
for (const namedRef of sub) {
capturingData.usedName(namedRef.ref);
}
capturingData.usedIndex(Number(ref.name));
}
}
else {
capturingData.usedIndex(Number(ref.ref));
capturingData.markAsCannotTrack();
return;
}

@@ -400,3 +266,3 @@ }

function verifyForMatchAll(node) {
const capturingData = getCapturingData(node.arguments[0]);
const capturingData = capturingDataMap.get(node.arguments[0]);
if (capturingData == null || capturingData.isAllUsed()) {

@@ -410,72 +276,37 @@ return;

capturingData.markAsUsed();
const refs = extractUsedReferencesForIteration(node);
if (refs == null) {
capturingData.markAsCannotTrack();
return;
}
for (const ref of refs) {
if (ref.ref === "groups") {
const sub = ref.getSubReferences();
if (sub == null) {
capturingData.usedAllNames();
}
else {
for (const namedRef of sub) {
capturingData.usedName(namedRef.ref);
}
}
for (const iterationRef of ast_utils_1.extractPropertyReferences(node, context)) {
if (!iterationRef.extractPropertyReferences) {
capturingData.markAsCannotTrack();
return;
}
else {
capturingData.usedIndex(Number(ref.ref));
}
}
function extractUsedReferencesForIteration(expr) {
const parent = expr.parent;
if (parent.type === "AssignmentExpression") {
if (parent.right !== expr || parent.operator !== "=") {
return null;
if (hasNameRef(iterationRef)) {
if (Number.isNaN(Number(iterationRef.name))) {
continue;
}
return extractUsedReferencesForIdIteration(parent.left);
}
else if (parent.type === "VariableDeclarator") {
if (parent.init !== expr) {
return null;
}
return extractUsedReferencesForIdIteration(parent.id);
}
else if (parent.type === "ForOfStatement") {
if (parent.right !== expr) {
return null;
}
let left = parent.left;
if (left.type === "VariableDeclaration") {
left = left.declarations[0].id;
}
return extractUsedReferencesFromPattern(left, context);
}
return null;
}
function extractUsedReferencesForIdIteration(ptn) {
if (ptn.type === "Identifier") {
const references = [];
const variable = ast_utils_1.findVariable(context, ptn);
if (!variable) {
return null;
}
for (const reference of variable.references) {
if (reference.isRead()) {
const resForId = extractUsedReferencesForIteration(reference.identifier);
if (resForId == null) {
return null;
for (const ref of iterationRef.extractPropertyReferences()) {
if (hasNameRef(ref)) {
if (ref.name === "groups") {
for (const namedRef of ref.extractPropertyReferences()) {
if (hasNameRef(namedRef)) {
capturingData.usedName(namedRef.name);
}
else {
capturingData.usedAllNames();
}
}
references.push(...resForId);
}
else {
capturingData.usedIndex(Number(ref.name));
}
}
return references;
else {
capturingData.markAsCannotTrack();
return;
}
}
return null;
}
}
function verifyForSplit(node) {
const capturingData = getCapturingData(node.arguments[0]);
const capturingData = capturingDataMap.get(node.arguments[0]);
if (capturingData == null || capturingData.isAllUsed()) {

@@ -495,2 +326,10 @@ return;

capturingDataMap.set(regexpNode, capturingData);
for (const ref of ast_utils_1.extractExpressionReferences(regexpNode, context)) {
if (ref.type === "argument" || ref.type === "member") {
capturingDataMap.set(ref.node, capturingData);
}
else {
capturingData.markAsCannotTrack();
}
}
return capturingData.visitor();

@@ -502,3 +341,3 @@ }

"Program:exit"() {
for (const capturingData of capturingDataMap.values()) {
for (const capturingData of new Set(capturingDataMap.values())) {
if (capturingData.isNeedReport()) {

@@ -548,1 +387,4 @@ reportUnused(capturingData);

});
function hasNameRef(ref) {
return ref.type === "destructuring" || ref.type === "member";
}

@@ -26,2 +26,3 @@ "use strict";

]);
const POTENTIAL_ESCAPE_SEQUENCE = new Set("uxkpP");
exports.default = utils_1.createRule("no-useless-escape", {

@@ -34,2 +35,3 @@ meta: {

},
fixable: "code",
schema: [],

@@ -42,4 +44,4 @@ messages: {

create(context) {
function createVisitor({ node, getRegexpLocation, }) {
function report(cNode, offset, character) {
function createVisitor({ node, getRegexpLocation, fixReplaceNode, }) {
function report(cNode, offset, character, fix) {
context.report({

@@ -52,2 +54,3 @@ node,

},
fix: fix ? fixReplaceNode(cNode, character) : null,
});

@@ -87,3 +90,3 @@ }

}
report(cNode, 0, char);
report(cNode, 0, char, !POTENTIAL_ESCAPE_SEQUENCE.has(char));
}

@@ -90,0 +93,0 @@ }

@@ -6,7 +6,7 @@ "use strict";

const type_tracker_1 = require("../utils/type-tracker");
class GlobalRegExpData {
class RegExpReference {
constructor(defineNode) {
this.readNodes = new Map();
this.state = {
used: false,
usedIn: {},
track: true,

@@ -16,42 +16,3 @@ };

}
isNeedReport() {
if (!this.readNodes.size) {
return false;
}
if (this.state.used) {
return false;
}
if (!this.state.track) {
return false;
}
let countOfUsedInExecOrTest = 0;
for (const readData of this.readNodes.values()) {
if (!readData.marked) {
return false;
}
if (readData.usedInSearchOrSplit) {
continue;
}
if (readData.usedInExecOrTest) {
if (!this.defineId) {
return false;
}
if (this.defineId.codePathId ===
readData.usedInExecOrTest.id.codePathId &&
this.defineId.loopNode ===
readData.usedInExecOrTest.id.loopNode) {
countOfUsedInExecOrTest++;
if (countOfUsedInExecOrTest > 1) {
return false;
}
continue;
}
else {
return false;
}
}
}
return true;
}
pushReadNode(node) {
addReadNode(node) {
this.readNodes.set(node, {});

@@ -62,22 +23,48 @@ }

}
markAsUsedInSearchOrSplit(node) {
markAsUsedInSearch(node) {
const exprState = this.readNodes.get(node);
if (exprState) {
exprState.marked = true;
exprState.usedInSearchOrSplit = true;
exprState.usedInSearch = true;
}
this.state.usedIn.search = true;
}
markAsUsedInExecOrTest(node, codePathId, loopNode) {
markAsUsedInSplit(node) {
const exprState = this.readNodes.get(node);
if (exprState) {
exprState.marked = true;
exprState.usedInExecOrTest = { id: { codePathId, loopNode } };
exprState.usedInSplit = true;
}
this.state.usedIn.split = true;
}
isUsed() {
return this.state.used;
markAsUsedInExec(node, codePathId, loopNode) {
const exprState = this.readNodes.get(node);
if (exprState) {
exprState.marked = true;
exprState.usedInExec = { id: { codePathId, loopNode } };
}
this.state.usedIn.exec = true;
}
markAsUsed() {
this.state.used = true;
markAsUsedInTest(node, codePathId, loopNode) {
const exprState = this.readNodes.get(node);
if (exprState) {
exprState.marked = true;
exprState.usedInTest = { id: { codePathId, loopNode } };
}
this.state.usedIn.test = true;
}
isUsed(kinds) {
for (const kind of kinds) {
if (this.state.usedIn[kind]) {
return true;
}
}
return false;
}
isCannotTrack() {
return !this.state.track;
}
markAsUsed(kind) {
this.state.usedIn[kind] = true;
}
markAsCannotTrack() {

@@ -87,16 +74,2 @@ this.state.track = false;

}
function getVariableId(node) {
const parent = ast_utils_1.getParent(node);
if (!parent ||
parent.type !== "VariableDeclarator" ||
parent.init !== node ||
parent.id.type !== "Identifier") {
return null;
}
const decl = ast_utils_1.getParent(parent);
if (decl && decl.type === "VariableDeclaration" && decl.kind === "const") {
return parent.id;
}
return null;
}
function getFlagLocation(context, node, flag) {

@@ -230,8 +203,4 @@ const sourceCode = context.getSourceCode();

function createUselessGlobalFlagVisitor(context) {
const typeTracer = type_tracker_1.createTypeTracker(context);
let stack = null;
const globalRegExpMap = new Map();
const globalRegExpList = [];
function reportUselessGlobalFlag(globalRegExp) {
const node = globalRegExp.defineNode;
function reportUselessGlobalFlag(regExpReference) {
const node = regExpReference.defineNode;
context.report({

@@ -243,56 +212,136 @@ node,

}
function extractReadReferences(node) {
const references = [];
const variable = ast_utils_1.findVariable(context, node);
if (!variable) {
return references;
}
for (const reference of variable.references) {
if (reference.isRead()) {
const id = getVariableId(reference.identifier);
if (id) {
references.push(...extractReadReferences(id));
function isNeedReport(regExpReference) {
let countOfUsedInExecOrTest = 0;
for (const readData of regExpReference.readNodes.values()) {
if (!readData.marked) {
return false;
}
const usedInExecOrTest = readData.usedInExec || readData.usedInTest;
if (usedInExecOrTest) {
if (!regExpReference.defineId) {
return false;
}
if (regExpReference.defineId.codePathId ===
usedInExecOrTest.id.codePathId &&
regExpReference.defineId.loopNode ===
usedInExecOrTest.id.loopNode) {
countOfUsedInExecOrTest++;
if (countOfUsedInExecOrTest > 1) {
return false;
}
continue;
}
else {
references.push(reference.identifier);
return false;
}
}
}
return references;
return true;
}
function verifyForSearchOrSplit(node) {
const globalRegExp = globalRegExpMap.get(node.arguments[0]);
if (globalRegExp == null || globalRegExp.isUsed()) {
return createRegExpReferenceExtractVisitor(context, {
flag: "global",
exit(regExpReferenceList) {
for (const regExpReference of regExpReferenceList) {
if (isNeedReport(regExpReference)) {
reportUselessGlobalFlag(regExpReference);
}
}
},
isUsedShortCircuit(regExpReference) {
return regExpReference.isUsed([
"match",
"matchAll",
"replace",
"replaceAll",
]);
},
});
}
function createUselessStickyFlagVisitor(context) {
function reportUselessGlobalFlag(regExpReference) {
const node = regExpReference.defineNode;
context.report({
node,
loc: getFlagLocation(context, node, "y"),
messageId: "uselessStickyFlag",
});
}
function isNeedReport(regExpReference) {
for (const readData of regExpReference.readNodes.values()) {
if (!readData.marked) {
return false;
}
}
return true;
}
return createRegExpReferenceExtractVisitor(context, {
flag: "sticky",
exit(regExpReferenceList) {
for (const regExpReference of regExpReferenceList) {
if (isNeedReport(regExpReference)) {
reportUselessGlobalFlag(regExpReference);
}
}
},
isUsedShortCircuit(regExpReference) {
return regExpReference.isUsed([
"search",
"exec",
"test",
"match",
"matchAll",
"replace",
"replaceAll",
]);
},
});
}
function createRegExpReferenceExtractVisitor(context, { flag, exit, isUsedShortCircuit, }) {
const typeTracer = type_tracker_1.createTypeTracker(context);
let stack = null;
const regExpReferenceMap = new Map();
const regExpReferenceList = [];
function verifyForSearchOrSplit(node, kind) {
const regExpReference = regExpReferenceMap.get(node.arguments[0]);
if (regExpReference == null || isUsedShortCircuit(regExpReference)) {
return;
}
if (!typeTracer.isString(node.callee.object)) {
globalRegExp.markAsCannotTrack();
regExpReference.markAsCannotTrack();
return;
}
globalRegExp.markAsUsedInSearchOrSplit(node.arguments[0]);
if (kind === "search") {
regExpReference.markAsUsedInSearch(node.arguments[0]);
}
else {
regExpReference.markAsUsedInSplit(node.arguments[0]);
}
}
function verifyForExecOrTest(node) {
const globalRegExp = globalRegExpMap.get(node.callee.object);
if (globalRegExp == null || globalRegExp.isUsed()) {
function verifyForExecOrTest(node, kind) {
const regExpReference = regExpReferenceMap.get(node.callee.object);
if (regExpReference == null || isUsedShortCircuit(regExpReference)) {
return;
}
globalRegExp.markAsUsedInExecOrTest(node.callee.object, stack.codePathId, stack.loopStack[0]);
if (kind === "exec") {
regExpReference.markAsUsedInExec(node.callee.object, stack.codePathId, stack.loopStack[0]);
}
else {
regExpReference.markAsUsedInTest(node.callee.object, stack.codePathId, stack.loopStack[0]);
}
}
return utils_1.compositingVisitors(utils_1.defineRegexpVisitor(context, {
createVisitor({ flags, regexpNode }) {
if (flags.global) {
const globalRegExp = new GlobalRegExpData(regexpNode);
globalRegExpList.push(globalRegExp);
globalRegExpMap.set(regexpNode, globalRegExp);
const id = getVariableId(regexpNode);
if (id) {
const readReferences = extractReadReferences(id);
for (const ref of readReferences) {
globalRegExpMap.set(ref, globalRegExp);
globalRegExp.pushReadNode(ref);
if (flags[flag]) {
const regExpReference = new RegExpReference(regexpNode);
regExpReferenceList.push(regExpReference);
regExpReferenceMap.set(regexpNode, regExpReference);
for (const ref of ast_utils_1.extractExpressionReferences(regexpNode, context)) {
if (ref.type === "argument" || ref.type === "member") {
regExpReferenceMap.set(ref.node, regExpReference);
regExpReference.addReadNode(ref.node);
}
else {
regExpReference.markAsCannotTrack();
}
}
else {
globalRegExp.pushReadNode(regexpNode);
}
}

@@ -303,7 +352,14 @@ return {};

"Program:exit"() {
for (const globalRegExp of globalRegExpList) {
if (globalRegExp.isNeedReport()) {
reportUselessGlobalFlag(globalRegExp);
exit(regExpReferenceList.filter((regExpReference) => {
if (!regExpReference.readNodes.size) {
return false;
}
}
if (regExpReference.isCannotTrack()) {
return false;
}
if (isUsedShortCircuit(regExpReference)) {
return false;
}
return true;
}));
},

@@ -334,7 +390,7 @@ onCodePathStart(codePath) {

}
const globalRegExp = globalRegExpMap.get(node);
if (!globalRegExp || globalRegExp.defineNode !== node) {
const regExpReference = regExpReferenceMap.get(node);
if (!regExpReference || regExpReference.defineNode !== node) {
return;
}
globalRegExp.setDefineId(stack.codePathId, stack.loopStack[0]);
regExpReference.setDefineId(stack.codePathId, stack.loopStack[0]);
},

@@ -359,7 +415,7 @@ "CallExpression:exit"(node) {

node.callee.property.name === "split") {
verifyForSearchOrSplit(node);
verifyForSearchOrSplit(node, node.callee.property.name);
}
else if (node.callee.property.name === "test" ||
node.callee.property.name === "exec") {
verifyForExecOrTest(node);
verifyForExecOrTest(node, node.callee.property.name);
}

@@ -370,4 +426,4 @@ else if (node.callee.property.name === "match" ||

node.callee.property.name === "replaceAll") {
const globalRegExp = globalRegExpMap.get(node.arguments[0]);
globalRegExp === null || globalRegExp === void 0 ? void 0 : globalRegExp.markAsUsed();
const regExpReference = regExpReferenceMap.get(node.arguments[0]);
regExpReference === null || regExpReference === void 0 ? void 0 : regExpReference.markAsUsed(node.callee.property.name);
}

@@ -393,3 +449,3 @@ },

items: {
enum: ["i", "m", "s", "g"],
enum: ["i", "m", "s", "g", "y"],
},

@@ -407,2 +463,3 @@ uniqueItems: true,

uselessGlobalFlag: "The 'g' flag is unnecessary because not using global testing.",
uselessStickyFlag: "The 'y' flag is unnecessary because not using sticky search.",
},

@@ -427,4 +484,7 @@ type: "suggestion",

}
if (!ignore.has("y")) {
visitor = utils_1.compositingVisitors(visitor, createUselessStickyFlagVisitor(context));
}
return visitor;
},
});
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const get_usage_of_pattern_1 = require("../utils/get-usage-of-pattern");
function isTopLevel(group) {

@@ -27,3 +28,10 @@ const parent = group.parent;

properties: {
allowTop: { type: "boolean" },
allowTop: {
anyOf: [
{
type: "boolean",
},
{ enum: ["always", "never", "partial"] },
],
},
},

@@ -39,8 +47,27 @@ additionalProperties: false,

create(context) {
var _a, _b;
const allowTop = (_b = (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.allowTop) !== null && _b !== void 0 ? _b : false;
function createVisitor({ node, getRegexpLocation, fixReplaceNode, }) {
var _a, _b, _c, _d;
const allowTop = ((_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.allowTop) === true
? "always"
: ((_b = context.options[0]) === null || _b === void 0 ? void 0 : _b.allowTop) === false
? "never"
: (_d = (_c = context.options[0]) === null || _c === void 0 ? void 0 : _c.allowTop) !== null && _d !== void 0 ? _d : "partial";
function createVisitor({ node, getRegexpLocation, fixReplaceNode, getUsageOfPattern, }) {
let isIgnored;
if (allowTop === "always") {
isIgnored = isTopLevel;
}
else if (allowTop === "partial") {
if (getUsageOfPattern() !== get_usage_of_pattern_1.UsageOfPattern.whole) {
isIgnored = isTopLevel;
}
else {
isIgnored = () => false;
}
}
else {
isIgnored = () => false;
}
return {
onGroupEnter(gNode) {
if (allowTop && isTopLevel(gNode)) {
if (isIgnored(gNode)) {
return;

@@ -76,3 +103,10 @@ }

messageId: "unexpected",
fix: fixReplaceNode(gNode, gNode.raw.slice(3, -1)),
fix: fixReplaceNode(gNode, () => {
if (allowTop === "never" &&
isTopLevel(gNode) &&
getUsageOfPattern() !== get_usage_of_pattern_1.UsageOfPattern.whole) {
return null;
}
return gNode.raw.slice(3, -1);
}),
});

@@ -79,0 +113,0 @@ },

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../utils");
const DEFAULT_ORDER = [
"\\s",
"\\w",
"\\d",
"\\p",
"*",
];
function getCharacterClassElementKind(node) {
if (node.type === "CharacterSet") {
return node.kind === "word"
? "\\w"
: node.kind === "digit"
? "\\d"
: node.kind === "space"
? "\\s"
: "\\p";
}
return "*";
}
const sort_character_class_elements_1 = __importDefault(require("./sort-character-class-elements"));
exports.default = utils_1.createRule("order-in-character-class", {
meta: {
docs: {
description: "enforces elements order in character class",
category: "Stylistic Issues",
recommended: false,
},
fixable: "code",
schema: [
{
type: "object",
properties: {
order: {
type: "array",
items: { enum: ["\\w", "\\d", "\\s", "\\p", "*"] },
},
},
additionalProperties: false,
},
],
messages: {
sortElements: "Expected character class elements to be in ascending order. '{{next}}' should be before '{{prev}}'.",
},
type: "layout",
},
meta: Object.assign(Object.assign({}, sort_character_class_elements_1.default.meta), { docs: Object.assign(Object.assign({}, sort_character_class_elements_1.default.meta.docs), { recommended: false, replacedBy: ["sort-character-class-elements"] }) }),
create(context) {
var _a, _b;
const orderOption = { "*": Infinity };
((_b = (_a = context.options[0]) === null || _a === void 0 ? void 0 : _a.order) !== null && _b !== void 0 ? _b : DEFAULT_ORDER).forEach((o, i) => {
orderOption[o] = i + 1;
});
function createVisitor({ node, fixerApplyEscape, getRegexpLocation, getRegexpRange, }) {
return {
onCharacterClassEnter(ccNode) {
const prevList = [];
for (const next of ccNode.elements) {
if (prevList.length) {
const prev = prevList[0];
if (!isValidOrder(prev, next)) {
let moveTarget = prev;
for (const p of prevList) {
if (isValidOrder(p, next)) {
break;
}
else {
moveTarget = p;
}
}
context.report({
node,
loc: getRegexpLocation(next),
messageId: "sortElements",
data: {
next: next.raw,
prev: moveTarget.raw,
},
*fix(fixer) {
const nextRange = getRegexpRange(next);
const targetRange = getRegexpRange(moveTarget);
if (!targetRange || !nextRange) {
return;
}
yield fixer.insertTextBeforeRange(targetRange, fixerApplyEscape(escapeRaw(next, moveTarget)));
yield fixer.removeRange(nextRange);
},
});
}
}
prevList.unshift(next);
}
},
};
}
function isValidOrder(prev, next) {
var _a, _b;
const prevKind = getCharacterClassElementKind(prev);
const nextKind = getCharacterClassElementKind(next);
const prevOrder = (_a = orderOption[prevKind]) !== null && _a !== void 0 ? _a : orderOption["*"];
const nextOrder = (_b = orderOption[nextKind]) !== null && _b !== void 0 ? _b : orderOption["*"];
if (prevOrder < nextOrder) {
return true;
}
else if (prevOrder > nextOrder) {
return false;
}
if (prev.type === "CharacterSet" && prev.kind === "property") {
if (next.type === "CharacterSet") {
if (next.kind === "property") {
return isValidOrderForUnicodePropertyCharacterSet(prev, next);
}
return false;
}
return true;
}
else if (next.type === "CharacterSet" &&
next.kind === "property") {
if (prev.type === "CharacterSet") {
return true;
}
return false;
}
if (prev.type === "CharacterSet" && next.type === "CharacterSet") {
if (prev.kind === "word" && next.kind === "digit") {
return true;
}
if (prev.kind === "digit" && next.kind === "word") {
return false;
}
}
const prevCP = getTargetCodePoint(prev);
const nextCP = getTargetCodePoint(next);
if (prevCP <= nextCP) {
return true;
}
return false;
}
function isValidOrderForUnicodePropertyCharacterSet(prev, next) {
if (prev.key < next.key) {
return true;
}
else if (prev.key > next.key) {
return false;
}
if (prev.value) {
if (next.value) {
if (prev.value <= next.value) {
return true;
}
return false;
}
return false;
}
return true;
}
function getTargetCodePoint(node) {
if (node.type === "CharacterSet") {
if (node.kind === "digit" || node.kind === "word") {
return utils_1.CP_DIGIT_ZERO;
}
if (node.kind === "space") {
return utils_1.CP_SPACE;
}
return Infinity;
}
if (node.type === "CharacterClassRange") {
return node.min.value;
}
return node.value;
}
return utils_1.defineRegexpVisitor(context, {
createVisitor,
});
return sort_character_class_elements_1.default.create(context);
},
});
function escapeRaw(node, target) {
let raw = node.raw;
if (raw.startsWith("-")) {
const parent = target.parent;
const prev = parent.elements[parent.elements.indexOf(target) - 1];
if (prev &&
(prev.type === "Character" || prev.type === "CharacterSet")) {
raw = `\\${raw}`;
}
}
if (target.raw.startsWith("-")) {
if (node.type === "Character" || node.type === "CharacterSet") {
raw = `${raw}\\`;
}
}
return raw;
}

@@ -75,4 +75,12 @@ "use strict";

}
const group = groups.find((gp) => gp.min.value - 1 <= data.max.value &&
data.min.value <= gp.max.value + 1);
const group = groups.find((gp) => {
const adjacent = gp.min.value - 1 <= data.max.value &&
data.min.value <= gp.max.value + 1;
if (!adjacent) {
return false;
}
const min = Math.min(gp.min.value, data.min.value);
const max = Math.max(gp.max.value, data.max.value);
return char_ranges_1.inRange(allowedRanges, min, max);
});
if (group) {

@@ -79,0 +87,0 @@ if (data.min.value < group.min.value) {

@@ -14,3 +14,3 @@ "use strict";

const usageSet = new Set();
for (const usage of iterateUsageOfPatternForExpression(node, context, new Map())) {
for (const usage of iterateUsageOfPattern(node, context)) {
if (usage === UsageOfPattern.unknown) {

@@ -31,41 +31,14 @@ return UsageOfPattern.unknown;

exports.getUsageOfPattern = getUsageOfPattern;
function* iterateUsageOfPatternForExpression(node, context, alreadyFn) {
const parent = ast_utils_1.getParent(node);
if (!parent) {
return;
}
if (parent.type === "MemberExpression") {
if (parent.object === node) {
yield* iterateUsageOfPatternForMemberExpression(parent, context);
function* iterateUsageOfPattern(node, context) {
for (const ref of ast_utils_1.extractExpressionReferences(node, context)) {
if (ref.type === "member") {
yield* iterateUsageOfPatternForMemberExpression(ref.memberExpression, context);
}
else {
yield UsageOfPattern.unknown;
else if (ref.type === "destructuring") {
if (ref.pattern.type === "ObjectPattern")
yield* iterateUsageOfPatternForObjectPattern(ref.pattern, context);
}
}
else if (parent.type === "AssignmentExpression") {
if (parent.right === node) {
yield* iterateUsageOfPatternForESPattern(parent.left, context, alreadyFn);
else if (ref.type === "unused") {
}
else {
yield UsageOfPattern.unknown;
}
}
else if (parent.type === "VariableDeclarator") {
if (parent.init === node) {
yield* iterateUsageOfPatternForESPattern(parent.id, context, alreadyFn);
}
else {
yield UsageOfPattern.unknown;
}
}
else if (parent.type === "CallExpression") {
const argIndex = parent.arguments.indexOf(node);
if (argIndex > -1) {
if (parent.callee.type === "Identifier") {
const fn = findFunction(context, parent.callee);
if (fn) {
yield* iterateUsageOfPatternForFunctionArgument(fn, argIndex, context, alreadyFn);
return;
}
}
else if (ref.type === "argument") {
yield UsageOfPattern.whole;

@@ -77,8 +50,2 @@ }

}
else if (parent.type === "ChainExpression") {
yield* iterateUsageOfPatternForExpression(parent, context, alreadyFn);
}
else {
yield UsageOfPattern.unknown;
}
}

@@ -109,13 +76,2 @@ function* iterateUsageOfPatternForMemberExpression(node, context) {

}
function* iterateUsageOfPatternForESPattern(node, context, alreadyFn) {
if (node.type === "Identifier") {
yield* iterateUsageOfPatternForVariable(node, context, alreadyFn);
}
else if (node.type === "ObjectPattern") {
yield* iterateUsageOfPatternForObjectPattern(node, context);
}
else {
yield UsageOfPattern.unknown;
}
}
function* iterateUsageOfPatternForObjectPattern(node, context) {

@@ -139,58 +95,1 @@ for (const prop of node.properties) {

}
function* iterateUsageOfPatternForVariable(node, context, alreadyFn) {
const variable = ast_utils_1.findVariable(context, node);
if (!variable) {
yield UsageOfPattern.unknown;
return;
}
const readReferences = variable.references.filter((ref) => ref.isRead());
if (!readReferences.length) {
yield UsageOfPattern.unknown;
return;
}
for (const reference of readReferences) {
yield* iterateUsageOfPatternForExpression(reference.identifier, context, alreadyFn);
}
}
function* iterateUsageOfPatternForFunctionArgument(node, argIndex, context, alreadyFn) {
let alreadyIndexes = alreadyFn.get(node);
if (!alreadyIndexes) {
alreadyIndexes = new Set();
alreadyFn.set(node, alreadyIndexes);
}
if (alreadyIndexes.has(argIndex)) {
return;
}
alreadyIndexes.add(argIndex);
const argNode = node.params[argIndex];
if (argNode && argNode.type === "Identifier") {
yield* iterateUsageOfPatternForVariable(argNode, context, alreadyFn);
}
else {
yield UsageOfPattern.unknown;
}
}
function findFunction(context, id) {
const calleeVariable = ast_utils_1.findVariable(context, id);
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") {
return findFunction(context, def.node.init);
}
}
}
return null;
}

@@ -17,2 +17,3 @@ "use strict";

const no_empty_alternative_1 = __importDefault(require("../rules/no-empty-alternative"));
const no_empty_capturing_group_1 = __importDefault(require("../rules/no-empty-capturing-group"));
const no_empty_group_1 = __importDefault(require("../rules/no-empty-group"));

@@ -65,3 +66,6 @@ const no_empty_lookarounds_assertion_1 = __importDefault(require("../rules/no-empty-lookarounds-assertion"));

const prefer_w_1 = __importDefault(require("../rules/prefer-w"));
const sort_alternatives_1 = __importDefault(require("../rules/sort-alternatives"));
const sort_character_class_elements_1 = __importDefault(require("../rules/sort-character-class-elements"));
const sort_flags_1 = __importDefault(require("../rules/sort-flags"));
const strict_1 = __importDefault(require("../rules/strict"));
const unicode_escape_1 = __importDefault(require("../rules/unicode-escape"));

@@ -79,2 +83,3 @@ exports.rules = [

no_empty_alternative_1.default,
no_empty_capturing_group_1.default,
no_empty_group_1.default,

@@ -127,4 +132,7 @@ no_empty_lookarounds_assertion_1.default,

prefer_w_1.default,
sort_alternatives_1.default,
sort_character_class_elements_1.default,
sort_flags_1.default,
strict_1.default,
unicode_escape_1.default,
];
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CP_SMALL_Z = exports.CP_SMALL_A = exports.CP_DIGIT_NINE = exports.CP_DIGIT_ZERO = exports.CP_BOM = exports.CP_IDEOGRAPHIC_SPACE = exports.CP_BRAILLE_PATTERN_BLANK = exports.CP_MMSP = exports.CP_NNBSP = exports.CP_PARAGRAPH_SEPARATOR = exports.CP_LINE_SEPARATOR = exports.CP_RLM = exports.CP_LRM = exports.CP_ZWJ = exports.CP_ZWNJ = exports.CP_ZWSP = exports.CP_HAIR_SPACE = exports.CP_EN_QUAD = exports.CP_MONGOLIAN_VOWEL_SEPARATOR = exports.CP_OGHAM_SPACE_MARK = exports.CP_NBSP = exports.CP_NEL = exports.CP_TILDE = exports.CP_CLOSING_BRACE = exports.CP_PIPE = exports.CP_OPENING_BRACE = exports.CP_BACKTICK = exports.CP_CARET = exports.CP_CLOSING_BRACKET = exports.CP_BACK_SLASH = exports.CP_OPENING_BRACKET = exports.CP_AT = exports.CP_QUESTION = exports.CP_COLON = exports.CP_SLASH = exports.CP_DOT = exports.CP_MINUS = exports.CP_PLUS = exports.CP_STAR = exports.CP_CLOSING_PAREN = exports.CP_OPENING_PAREN = exports.CP_DOLLAR = exports.CP_BAN = exports.CP_SPACE = exports.CP_CR = exports.CP_FF = exports.CP_VT = exports.CP_LF = exports.CP_TAB = exports.CP_BACKSPACE = void 0;
exports.isInvisible = exports.isWord = exports.isSpace = exports.isSymbol = exports.toUpperCodePoint = exports.toLowerCodePoint = exports.isLetter = exports.isUppercaseLetter = exports.isLowercaseLetter = exports.isDigit = exports.CP_RANGE_CAPITAL_LETTER = exports.CP_RANGE_SMALL_LETTER = exports.CP_LOW_LINE = exports.CP_CAPITAL_Z = exports.CP_CAPITAL_A = void 0;
exports.CP_SMALL_A = exports.CP_DIGIT_NINE = exports.CP_DIGIT_ZERO = exports.CP_BOM = exports.CP_IDEOGRAPHIC_SPACE = exports.CP_BRAILLE_PATTERN_BLANK = exports.CP_MMSP = exports.CP_NNBSP = exports.CP_PARAGRAPH_SEPARATOR = exports.CP_LINE_SEPARATOR = exports.CP_RLM = exports.CP_LRM = exports.CP_ZWJ = exports.CP_ZWNJ = exports.CP_ZWSP = exports.CP_HAIR_SPACE = exports.CP_EN_QUAD = exports.CP_MONGOLIAN_VOWEL_SEPARATOR = exports.CP_OGHAM_SPACE_MARK = exports.CP_NBSP = exports.CP_NEL = exports.CP_TILDE = exports.CP_CLOSING_BRACE = exports.CP_PIPE = exports.CP_OPENING_BRACE = exports.CP_APOSTROPHE = exports.CP_BACKTICK = exports.CP_CARET = exports.CP_CLOSING_BRACKET = exports.CP_BACK_SLASH = exports.CP_OPENING_BRACKET = exports.CP_AT = exports.CP_QUESTION = exports.CP_COLON = exports.CP_SLASH = exports.CP_DOT = exports.CP_MINUS = exports.CP_PLUS = exports.CP_STAR = exports.CP_CLOSING_PAREN = exports.CP_OPENING_PAREN = exports.CP_DOLLAR = exports.CP_BAN = exports.CP_SPACE = exports.CP_CR = exports.CP_FF = exports.CP_VT = exports.CP_LF = exports.CP_TAB = exports.CP_BACKSPACE = void 0;
exports.isInvisible = exports.isWord = exports.isSpace = exports.isSymbol = exports.toUpperCodePoint = exports.toLowerCodePoint = exports.isLetter = exports.isUppercaseLetter = exports.isLowercaseLetter = exports.isDigit = exports.CP_RANGE_CAPITAL_LETTER = exports.CP_RANGE_SMALL_LETTER = exports.CP_LOW_LINE = exports.CP_CAPITAL_Z = exports.CP_CAPITAL_A = exports.CP_SMALL_Z = void 0;
const regexp_ast_analysis_1 = require("regexp-ast-analysis");

@@ -30,2 +30,3 @@ exports.CP_BACKSPACE = 8;

exports.CP_BACKTICK = "`".codePointAt(0);
exports.CP_APOSTROPHE = "'".codePointAt(0);
exports.CP_OPENING_BRACE = "{".codePointAt(0);

@@ -32,0 +33,0 @@ exports.CP_PIPE = "|".codePointAt(0);

{
"name": "eslint-plugin-regexp",
"version": "0.11.0",
"version": "0.12.0",
"description": "ESLint plugin for finding RegExp mistakes and RegExp style guide violations.",

@@ -55,3 +55,3 @@ "main": "dist/index.js",

"@types/eslint-scope": "^3.7.0",
"@types/estree": "^0.0.47",
"@types/estree": "^0.0.48",
"@types/mocha": "^8.0.0",

@@ -71,7 +71,7 @@ "@types/node": "^14.14.39",

"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-regexp": "^0.10.0",
"eslint-plugin-regexp": "^0.11.0",
"eslint-plugin-vue": "^7.5.0",
"eslint-plugin-yml": "^0.9.0",
"eslint4b": "^7.3.1",
"mocha": "^8.0.0",
"mocha": "^9.0.0",
"nyc": "^15.1.0",

@@ -82,3 +82,3 @@ "prettier": "^2.0.5",

"stylelint-config-standard": "^22.0.0",
"stylelint-plugin-stylus": "^0.10.0",
"stylelint-plugin-stylus": "^0.11.0",
"ts-node": "^10.0.0",

@@ -95,5 +95,5 @@ "typescript": "^4.0.0",

"refa": "^0.8.0",
"regexp-ast-analysis": "^0.2.0",
"regexp-ast-analysis": "^0.2.2",
"regexpp": "^3.1.0"
}
}

@@ -102,5 +102,6 @@ # Introduction

|:--------|:------------|:---|
| [regexp/no-assertion-capturing-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-assertion-capturing-group.html) | disallow capturing group that captures assertions. | :star: |
| [regexp/no-assertion-capturing-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-assertion-capturing-group.html) | disallow capturing group that captures empty. | :star: |
| [regexp/no-dupe-disjunctions](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-dupe-disjunctions.html) | disallow duplicate disjunctions | |
| [regexp/no-empty-alternative](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-alternative.html) | disallow alternatives without elements | |
| [regexp/no-empty-capturing-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-capturing-group.html) | disallow capturing group that captures empty. | |
| [regexp/no-empty-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-group.html) | disallow empty group | :star: |

@@ -115,2 +116,3 @@ | [regexp/no-empty-lookarounds-assertion](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-lookarounds-assertion.html) | disallow empty lookahead assertion or empty lookbehind assertion | :star: |

| [regexp/no-useless-dollar-replacements](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-dollar-replacements.html) | disallow useless `$` replacements in replacement string | |
| [regexp/strict](https://ota-meshi.github.io/eslint-plugin-regexp/rules/strict.html) | disallow not strictly valid regular expressions | :wrench: |

@@ -151,2 +153,3 @@ ### Best Practices

| [regexp/prefer-regexp-test](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-regexp-test.html) | enforce that `RegExp#test` is used instead of `String#match` and `RegExp#exec` | :wrench: |
| [regexp/sort-alternatives](https://ota-meshi.github.io/eslint-plugin-regexp/rules/sort-alternatives.html) | sort alternatives if order doesn't matter | :wrench: |

@@ -160,3 +163,3 @@ ### Stylistic Issues

| [regexp/match-any](https://ota-meshi.github.io/eslint-plugin-regexp/rules/match-any.html) | enforce match any character style | :star::wrench: |
| [regexp/no-useless-escape](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-escape.html) | disallow unnecessary escape characters in RegExp | |
| [regexp/no-useless-escape](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-escape.html) | disallow unnecessary escape characters in RegExp | :wrench: |
| [regexp/no-useless-non-capturing-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-useless-non-capturing-group.html) | disallow unnecessary Non-capturing group | :wrench: |

@@ -173,2 +176,3 @@ | [regexp/order-in-character-class](https://ota-meshi.github.io/eslint-plugin-regexp/rules/order-in-character-class.html) | enforces elements order in character class | :wrench: |

| [regexp/prefer-w](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-w.html) | enforce using `\w` | :star::wrench: |
| [regexp/sort-character-class-elements](https://ota-meshi.github.io/eslint-plugin-regexp/rules/sort-character-class-elements.html) | enforces elements order in character class | :wrench: |
| [regexp/sort-flags](https://ota-meshi.github.io/eslint-plugin-regexp/rules/sort-flags.html) | require regex flags to be sorted | :wrench: |

@@ -175,0 +179,0 @@ | [regexp/unicode-escape](https://ota-meshi.github.io/eslint-plugin-regexp/rules/unicode-escape.html) | enforce consistent usage of unicode escape or unicode codepoint escape | :wrench: |