@sarj/eslint-plugin 16.22.0 → 16.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +50 -128
- package/dist/index.js +354 -432
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -866,7 +866,7 @@ function isSearchParamReceiver(call, services) {
|
|
|
866
866
|
// package.json
|
|
867
867
|
var package_default = {
|
|
868
868
|
name: "@sarj/eslint-plugin",
|
|
869
|
-
version: "16.22.
|
|
869
|
+
version: "16.22.1",
|
|
870
870
|
packageManager: "npm@12.2.0",
|
|
871
871
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
872
872
|
type: "module",
|
|
@@ -7294,10 +7294,10 @@ var import_utils44 = require("@typescript-eslint/utils");
|
|
|
7294
7294
|
function stripSqlNoise(text) {
|
|
7295
7295
|
return scanSqlNoise(text);
|
|
7296
7296
|
}
|
|
7297
|
-
function
|
|
7298
|
-
const
|
|
7299
|
-
scanSqlNoise(text, (start, end) =>
|
|
7300
|
-
return
|
|
7297
|
+
function sqlNoiseInfo(text) {
|
|
7298
|
+
const singleQuotedRanges = [];
|
|
7299
|
+
const masked = scanSqlNoise(text, (start, end) => singleQuotedRanges.push([start, end]));
|
|
7300
|
+
return { text: masked, singleQuotedRanges };
|
|
7301
7301
|
}
|
|
7302
7302
|
function scanSqlNoise(text, onSingleQuoted) {
|
|
7303
7303
|
const out = text.split("");
|
|
@@ -7383,6 +7383,7 @@ function maskSqlRange(text, out, start, end) {
|
|
|
7383
7383
|
}
|
|
7384
7384
|
var SUBSTITUTION_MARKER = "?";
|
|
7385
7385
|
function sqlTextOf(node) {
|
|
7386
|
+
node = unwrapExpression(node);
|
|
7386
7387
|
switch (node.type) {
|
|
7387
7388
|
case import_utils44.AST_NODE_TYPES.Literal:
|
|
7388
7389
|
return typeof node.value === "string" ? node.value : null;
|
|
@@ -7410,7 +7411,7 @@ function fragmentArrayText(node) {
|
|
|
7410
7411
|
if (separator === null) return null;
|
|
7411
7412
|
const parts = [];
|
|
7412
7413
|
for (const element of node.elements) {
|
|
7413
|
-
if (element === null || join4 !== null && element.type === import_utils44.AST_NODE_TYPES.ArrayExpression) {
|
|
7414
|
+
if (element === null || join4 !== null && unwrapExpression(element).type === import_utils44.AST_NODE_TYPES.ArrayExpression) {
|
|
7414
7415
|
return null;
|
|
7415
7416
|
}
|
|
7416
7417
|
const part = sqlTextOf(element);
|
|
@@ -7422,8 +7423,10 @@ function fragmentArrayText(node) {
|
|
|
7422
7423
|
return parts.length > 0 ? parts.join(separator) : null;
|
|
7423
7424
|
}
|
|
7424
7425
|
function joinedFragmentCall(node) {
|
|
7425
|
-
const
|
|
7426
|
-
|
|
7426
|
+
const value = outerExpression(node);
|
|
7427
|
+
const parent = value.parent;
|
|
7428
|
+
const callee = parent?.type === import_utils44.AST_NODE_TYPES.MemberExpression ? outerExpression(parent) : null;
|
|
7429
|
+
return parent?.type === import_utils44.AST_NODE_TYPES.MemberExpression && parent.object === value && import_utils44.ASTUtils.getPropertyName(parent) === "join" && callee?.parent?.type === import_utils44.AST_NODE_TYPES.CallExpression && callee.parent.callee === callee ? callee.parent : null;
|
|
7427
7430
|
}
|
|
7428
7431
|
function markConsumed(node, consumed) {
|
|
7429
7432
|
consumed.add(node);
|
|
@@ -7465,6 +7468,8 @@ function createSqlListener(handler) {
|
|
|
7465
7468
|
markConsumed(node, consumed);
|
|
7466
7469
|
const separator = join4.arguments[0];
|
|
7467
7470
|
if (separator !== void 0 && staticString(separator) !== null) markConsumed(separator, consumed);
|
|
7471
|
+
const member = unwrapExpression(join4.callee);
|
|
7472
|
+
if (member.type === import_utils44.AST_NODE_TYPES.MemberExpression && member.computed) markConsumed(member.property, consumed);
|
|
7468
7473
|
},
|
|
7469
7474
|
TemplateLiteral: (node) => {
|
|
7470
7475
|
visit(node);
|
|
@@ -7475,11 +7480,11 @@ function createSqlListener(handler) {
|
|
|
7475
7480
|
};
|
|
7476
7481
|
}
|
|
7477
7482
|
function isDiscardedSqlText(node) {
|
|
7478
|
-
let current = node;
|
|
7483
|
+
let current = outerExpression(node);
|
|
7479
7484
|
let parent = current.parent;
|
|
7480
7485
|
while (parent !== void 0 && parent !== null) {
|
|
7481
|
-
if (
|
|
7482
|
-
current = parent;
|
|
7486
|
+
if (parent.type === import_utils44.AST_NODE_TYPES.BinaryExpression && parent.operator === "+") {
|
|
7487
|
+
current = outerExpression(parent);
|
|
7483
7488
|
parent = current.parent;
|
|
7484
7489
|
continue;
|
|
7485
7490
|
}
|
|
@@ -7561,8 +7566,7 @@ function isStaticFragment(expression) {
|
|
|
7561
7566
|
function runtimeInterpolations(template) {
|
|
7562
7567
|
const parts = template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw);
|
|
7563
7568
|
const statement = parts.join(RUNTIME_MARKER);
|
|
7564
|
-
const ranges =
|
|
7565
|
-
const visible = stripSqlNoise(statement);
|
|
7569
|
+
const { singleQuotedRanges: ranges, text: visible } = sqlNoiseInfo(statement);
|
|
7566
7570
|
let offset = 0;
|
|
7567
7571
|
return template.expressions.flatMap(
|
|
7568
7572
|
(expression, index) => {
|
|
@@ -7583,16 +7587,13 @@ function runtimeConcatOperands(node) {
|
|
|
7583
7587
|
return [];
|
|
7584
7588
|
}
|
|
7585
7589
|
const operands = concatOperands(node);
|
|
7586
|
-
const hasStringLiteral = operands.some(
|
|
7587
|
-
(operand) => operand.type === import_utils45.AST_NODE_TYPES.Literal && typeof operand.value === "string" || operand.type === import_utils45.AST_NODE_TYPES.TemplateLiteral && operand.expressions.length === 0
|
|
7588
|
-
);
|
|
7590
|
+
const hasStringLiteral = operands.some((operand) => staticString(operand) !== null);
|
|
7589
7591
|
if (!hasStringLiteral) {
|
|
7590
7592
|
return [];
|
|
7591
7593
|
}
|
|
7592
7594
|
const parts = operands.map((operand) => staticString(operand) ?? RUNTIME_MARKER);
|
|
7593
7595
|
const statement = parts.join("");
|
|
7594
|
-
const ranges =
|
|
7595
|
-
const visible = stripSqlNoise(statement);
|
|
7596
|
+
const { singleQuotedRanges: ranges, text: visible } = sqlNoiseInfo(statement);
|
|
7596
7597
|
let offset = 0;
|
|
7597
7598
|
return operands.flatMap((operand, index) => {
|
|
7598
7599
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
@@ -7606,10 +7607,17 @@ function runtimeConcatOperands(node) {
|
|
|
7606
7607
|
});
|
|
7607
7608
|
}
|
|
7608
7609
|
function concatOperands(node) {
|
|
7609
|
-
|
|
7610
|
-
|
|
7610
|
+
const operands = [];
|
|
7611
|
+
const pending = [node];
|
|
7612
|
+
while (pending.length > 0) {
|
|
7613
|
+
const next = pending.pop();
|
|
7614
|
+
if (next === void 0) continue;
|
|
7615
|
+
const value = unwrapExpression(next);
|
|
7616
|
+
if (value.type === import_utils45.AST_NODE_TYPES.BinaryExpression && value.operator === "+") {
|
|
7617
|
+
pending.push(value.right, value.left);
|
|
7618
|
+
} else operands.push(value);
|
|
7611
7619
|
}
|
|
7612
|
-
return
|
|
7620
|
+
return operands;
|
|
7613
7621
|
}
|
|
7614
7622
|
var SQL_STATEMENT_RE = /\b(?:select\s|insert\s+into\b|insert\s+or\b|update\s+\w|delete\s+from\b|replace\s+into\b|merge\s+into\b|upsert\s+into\b|create\s+(?:temp(?:orary)?\s+)?(?:table|index|view|trigger|schema|database)\b|alter\s+table\b|drop\s+(?:table|index|view|trigger)\b|truncate\s+table\b|pragma\s+\w|with\s+\w+\s+as\s*\(|from\s+\w+\s+where\b)/i;
|
|
7615
7623
|
var RUNTIME_MARKER = " ? ";
|
|
@@ -7617,6 +7625,7 @@ function looksLikeSql(node) {
|
|
|
7617
7625
|
return SQL_STATEMENT_RE.test(stripSqlNoise(staticStatementText(node)));
|
|
7618
7626
|
}
|
|
7619
7627
|
function staticStatementText(node) {
|
|
7628
|
+
node = unwrapExpression(node);
|
|
7620
7629
|
if (node.type === import_utils45.AST_NODE_TYPES.TemplateLiteral) {
|
|
7621
7630
|
return node.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw).join(RUNTIME_MARKER);
|
|
7622
7631
|
}
|
|
@@ -8686,46 +8695,15 @@ var STRONG_SECURITY_WORDS = /* @__PURE__ */ new Set([
|
|
|
8686
8695
|
"uuid",
|
|
8687
8696
|
"verificationcode"
|
|
8688
8697
|
]);
|
|
8689
|
-
var NON_SECURITY_ID_WORDS = /* @__PURE__ */ new Set([
|
|
8690
|
-
"aria",
|
|
8691
|
-
"cache",
|
|
8692
|
-
"component",
|
|
8693
|
-
"correlation",
|
|
8694
|
-
"dev",
|
|
8695
|
-
"dialog",
|
|
8696
|
-
"dom",
|
|
8697
|
-
"element",
|
|
8698
|
-
"execution",
|
|
8699
|
-
"field",
|
|
8700
|
-
"form",
|
|
8701
|
-
"hmr",
|
|
8702
|
-
"input",
|
|
8703
|
-
"marker",
|
|
8704
|
-
"menu",
|
|
8705
|
-
"mock",
|
|
8706
|
-
"perf",
|
|
8707
|
-
"req",
|
|
8708
|
-
"request",
|
|
8709
|
-
"select",
|
|
8710
|
-
"tab",
|
|
8711
|
-
"temp",
|
|
8712
|
-
"test",
|
|
8713
|
-
"tmp",
|
|
8714
|
-
"trace"
|
|
8715
|
-
]);
|
|
8716
|
-
function nameWords(name) {
|
|
8717
|
-
return name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1 $2").split(/[^A-Za-z0-9]+/u).filter(Boolean).map((word) => word.toLowerCase());
|
|
8718
|
-
}
|
|
8719
8698
|
function isStrongSecurityName(name) {
|
|
8720
8699
|
const words2 = nameWords(name);
|
|
8721
8700
|
return words2.some((word) => STRONG_SECURITY_WORDS.has(word)) || words2.some(
|
|
8722
8701
|
(word, index) => word === "api" && words2[index + 1] === "key" || word === "auth" && words2[index + 1] === "id" || word === "verification" && words2[index + 1] === "code"
|
|
8723
8702
|
);
|
|
8724
8703
|
}
|
|
8725
|
-
function
|
|
8726
|
-
return
|
|
8704
|
+
function nameWords(name) {
|
|
8705
|
+
return name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1 $2").split(/[^A-Za-z0-9]+/u).filter(Boolean).map((word) => word.toLowerCase());
|
|
8727
8706
|
}
|
|
8728
|
-
var PATH_OR_DOM_MARKER = /[\\/#]|\.[A-Za-z0-9]/;
|
|
8729
8707
|
function isMathRandomCall(node) {
|
|
8730
8708
|
if (node.type !== "CallExpression") {
|
|
8731
8709
|
return false;
|
|
@@ -8769,67 +8747,6 @@ function findEnclosingNames(node) {
|
|
|
8769
8747
|
}
|
|
8770
8748
|
return names;
|
|
8771
8749
|
}
|
|
8772
|
-
function isConcatenatedIntoPathOrDomId(node) {
|
|
8773
|
-
const valueNode = climbValueChain(node);
|
|
8774
|
-
let current = valueNode;
|
|
8775
|
-
let parent = current.parent;
|
|
8776
|
-
let top;
|
|
8777
|
-
while (parent) {
|
|
8778
|
-
if (parent.type === "BinaryExpression" && parent.operator === "+" && (parent.left === current || parent.right === current)) {
|
|
8779
|
-
top = parent;
|
|
8780
|
-
current = parent;
|
|
8781
|
-
parent = current.parent;
|
|
8782
|
-
continue;
|
|
8783
|
-
}
|
|
8784
|
-
if (parent.type === "TemplateLiteral") {
|
|
8785
|
-
top = parent;
|
|
8786
|
-
current = parent;
|
|
8787
|
-
parent = current.parent;
|
|
8788
|
-
continue;
|
|
8789
|
-
}
|
|
8790
|
-
break;
|
|
8791
|
-
}
|
|
8792
|
-
if (!top) {
|
|
8793
|
-
return false;
|
|
8794
|
-
}
|
|
8795
|
-
const parts = [];
|
|
8796
|
-
collectStaticStringParts(top, parts);
|
|
8797
|
-
return parts.some((part) => PATH_OR_DOM_MARKER.test(part));
|
|
8798
|
-
}
|
|
8799
|
-
function climbValueChain(node) {
|
|
8800
|
-
let current = node;
|
|
8801
|
-
let parent = current.parent;
|
|
8802
|
-
while (parent) {
|
|
8803
|
-
if (parent.type === "MemberExpression" && parent.object === current && import_utils51.ASTUtils.getPropertyName(parent) !== null) {
|
|
8804
|
-
current = parent;
|
|
8805
|
-
parent = current.parent;
|
|
8806
|
-
continue;
|
|
8807
|
-
}
|
|
8808
|
-
if (parent.type === "CallExpression" && parent.callee === current) {
|
|
8809
|
-
current = parent;
|
|
8810
|
-
parent = current.parent;
|
|
8811
|
-
continue;
|
|
8812
|
-
}
|
|
8813
|
-
break;
|
|
8814
|
-
}
|
|
8815
|
-
return current;
|
|
8816
|
-
}
|
|
8817
|
-
function collectStaticStringParts(node, out) {
|
|
8818
|
-
if (node.type === "Literal" && typeof node.value === "string") {
|
|
8819
|
-
out.push(node.value);
|
|
8820
|
-
return;
|
|
8821
|
-
}
|
|
8822
|
-
if (node.type === "TemplateLiteral") {
|
|
8823
|
-
for (const quasi of node.quasis) {
|
|
8824
|
-
out.push(quasi.value.cooked ?? quasi.value.raw);
|
|
8825
|
-
}
|
|
8826
|
-
return;
|
|
8827
|
-
}
|
|
8828
|
-
if (node.type === "BinaryExpression" && node.operator === "+") {
|
|
8829
|
-
collectStaticStringParts(node.left, out);
|
|
8830
|
-
collectStaticStringParts(node.right, out);
|
|
8831
|
-
}
|
|
8832
|
-
}
|
|
8833
8750
|
var no_insecure_random_id_default = createRule({
|
|
8834
8751
|
name: "no-insecure-random-id",
|
|
8835
8752
|
documentation: NO_INSECURE_RANDOM_ID_DOCUMENTATION,
|
|
@@ -8857,13 +8774,6 @@ var no_insecure_random_id_default = createRule({
|
|
|
8857
8774
|
const names = findEnclosingNames(node);
|
|
8858
8775
|
if (names.some(isStrongSecurityName)) {
|
|
8859
8776
|
context.report({ node, messageId: "insecureRandomId" });
|
|
8860
|
-
return;
|
|
8861
|
-
}
|
|
8862
|
-
if (names.some(isNonSecurityName)) {
|
|
8863
|
-
return;
|
|
8864
|
-
}
|
|
8865
|
-
if (isConcatenatedIntoPathOrDomId(node)) {
|
|
8866
|
-
return;
|
|
8867
8777
|
}
|
|
8868
8778
|
}
|
|
8869
8779
|
};
|
|
@@ -9795,15 +9705,24 @@ function typeMayContainObject(type, checker) {
|
|
|
9795
9705
|
function syntaxMayContainObject(node) {
|
|
9796
9706
|
const expression = unwrapComparisonExpression(node);
|
|
9797
9707
|
if (expression.type === import_utils55.AST_NODE_TYPES.ObjectExpression) return true;
|
|
9708
|
+
if (expression.type === import_utils55.AST_NODE_TYPES.Literal) return "regex" in expression;
|
|
9709
|
+
if (expression.type === import_utils55.AST_NODE_TYPES.TemplateLiteral || expression.type === import_utils55.AST_NODE_TYPES.UnaryExpression) return false;
|
|
9798
9710
|
if (expression.type !== import_utils55.AST_NODE_TYPES.ArrayExpression) return null;
|
|
9711
|
+
let unknown = false;
|
|
9799
9712
|
for (const element of expression.elements) {
|
|
9800
9713
|
if (element === null) continue;
|
|
9801
|
-
|
|
9802
|
-
if (
|
|
9803
|
-
|
|
9804
|
-
}
|
|
9714
|
+
const result = element.type === import_utils55.AST_NODE_TYPES.SpreadElement ? null : syntaxMayContainObject(element);
|
|
9715
|
+
if (result === true) return true;
|
|
9716
|
+
if (result === null) unknown = true;
|
|
9805
9717
|
}
|
|
9806
|
-
return false;
|
|
9718
|
+
return unknown ? null : false;
|
|
9719
|
+
}
|
|
9720
|
+
function untypedArrayMayContainObject(node) {
|
|
9721
|
+
return node.elements.some((element) => {
|
|
9722
|
+
if (element === null || element.type === import_utils55.AST_NODE_TYPES.SpreadElement) return false;
|
|
9723
|
+
const value = unwrapComparisonExpression(element);
|
|
9724
|
+
return value.type === import_utils55.AST_NODE_TYPES.ArrayExpression ? untypedArrayMayContainObject(value) : syntaxMayContainObject(value) !== false;
|
|
9725
|
+
});
|
|
9807
9726
|
}
|
|
9808
9727
|
var no_json_stringify_object_equality_default = createRule({
|
|
9809
9728
|
name: "no-json-stringify-object-equality",
|
|
@@ -9832,7 +9751,10 @@ var no_json_stringify_object_equality_default = createRule({
|
|
|
9832
9751
|
function mayContainObject(node) {
|
|
9833
9752
|
const syntax = syntaxMayContainObject(node);
|
|
9834
9753
|
if (syntax !== null) return syntax;
|
|
9835
|
-
if (services === null || checker === void 0)
|
|
9754
|
+
if (services === null || checker === void 0) {
|
|
9755
|
+
const expression = unwrapComparisonExpression(node);
|
|
9756
|
+
return expression.type === import_utils55.AST_NODE_TYPES.ArrayExpression && untypedArrayMayContainObject(expression);
|
|
9757
|
+
}
|
|
9836
9758
|
const type = checker.getTypeAtLocation(
|
|
9837
9759
|
services.esTreeNodeToTSNodeMap.get(node)
|
|
9838
9760
|
);
|