@sarj/eslint-plugin 16.30.2 → 16.30.4
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 +53 -45
- package/dist/index.js +55 -51
- package/package.json +1 -2
package/dist/index.cjs
CHANGED
|
@@ -1580,7 +1580,7 @@ function isSearchParamReceiver(call, services) {
|
|
|
1580
1580
|
// package.json
|
|
1581
1581
|
var package_default = {
|
|
1582
1582
|
name: "@sarj/eslint-plugin",
|
|
1583
|
-
version: "16.30.
|
|
1583
|
+
version: "16.30.4",
|
|
1584
1584
|
packageManager: "npm@12.2.1",
|
|
1585
1585
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
1586
1586
|
type: "module",
|
|
@@ -1636,7 +1636,6 @@ var package_default = {
|
|
|
1636
1636
|
dependencies: {
|
|
1637
1637
|
"@typescript-eslint/typescript-estree": "^8.72.0",
|
|
1638
1638
|
"@typescript-eslint/utils": "^8.72.0",
|
|
1639
|
-
"eslint-plugin-sonarjs": "4.2.2",
|
|
1640
1639
|
"jsonc-parser": "^3.3.1",
|
|
1641
1640
|
"ts-api-utils": "2.5.0"
|
|
1642
1641
|
},
|
|
@@ -4857,7 +4856,6 @@ function showWitness(value) {
|
|
|
4857
4856
|
|
|
4858
4857
|
// src/rules/no-boolean-selector-parameter.ts
|
|
4859
4858
|
var import_utils32 = require("@typescript-eslint/utils");
|
|
4860
|
-
var import_eslint_plugin_sonarjs = require("eslint-plugin-sonarjs");
|
|
4861
4859
|
|
|
4862
4860
|
// src/rules/_boolean-selector-contract.ts
|
|
4863
4861
|
var import_utils30 = require("@typescript-eslint/utils");
|
|
@@ -5182,7 +5180,6 @@ function literalCallers(sourceCode, owner, binding) {
|
|
|
5182
5180
|
}
|
|
5183
5181
|
|
|
5184
5182
|
// src/rules/no-boolean-selector-parameter.ts
|
|
5185
|
-
var UPSTREAM_SELECTOR_RULE = import_eslint_plugin_sonarjs.rules["no-selector-parameter"];
|
|
5186
5183
|
var NO_BOOLEAN_SELECTOR_PARAMETER_DOCUMENTATION = {
|
|
5187
5184
|
summary: "Prefer intention-revealing operations over a boolean parameter selecting between actions.",
|
|
5188
5185
|
rationale: "A positional boolean hides which operation a caller requests. Splitting independent actions can make that intent explicit, while boolean data, pure mappings and externally required callback or method contracts remain valid.",
|
|
@@ -5191,15 +5188,14 @@ var NO_BOOLEAN_SELECTOR_PARAMETER_DOCUMENTATION = {
|
|
|
5191
5188
|
defaultLevel: "warning",
|
|
5192
5189
|
autofix: "safe",
|
|
5193
5190
|
limitations: [
|
|
5194
|
-
"Requires type information.
|
|
5191
|
+
"Requires type information. Checks an owned single-statement if/else, returned conditional or conditional expression statement, and expression-bodied arrow conditionals. Only a direct broad-boolean parameter or its negation is considered; optional, nullable, literal-only, generic, destructured and indirect predicate conditions are excluded.",
|
|
5195
5192
|
"Preserves setters, pure mappings, selected pure data passed to one stable lexical operation or assigned to one simple target, independently authored callable and inherited member contracts, inline callbacks, and statically proven named callback references. Independent helpers remain checked in JSX files; callback identity is established from references rather than handler names.",
|
|
5196
|
-
"The
|
|
5193
|
+
"The block-body detector preserves the existing literal on[A-Z] object-property exemption; the concise-arrow extension does not infer a callback contract from that name. Multi-statement bodies, short-circuit guards, local parameter aliases and arbitrary control flow are not inferred. Noncomputed property reads count as data without proving getter effects; computed reads remain outside that exemption, including literal keys. Side-effect detection does not prove whether a called function is pure, and a warning requires manual design review. Generated files are excluded; tests remain checked.",
|
|
5197
5194
|
"Automatic fixes only reuse two already authored private zero-argument ordinary operations returning primitive string literals. A complete comment-free four-declaration module must contain those operations, one private const broad-boolean arrow selecting their direct calls, and one exported zero-argument ordinary function with an explicit string-array return that calls the selector exclusively with literal true and false. Calls are routed to the existing named operations and the exclusively used selector declaration is removed. All other APIs, callbacks, dynamic arguments, contracts and operation bodies remain manual."
|
|
5198
5195
|
],
|
|
5199
5196
|
references: [
|
|
5200
5197
|
"https://understandlegacycode.com/blog/what-is-wrong-with-boolean-parameters/",
|
|
5201
|
-
"https://martinfowler.com/bliki/FlagArgument.html"
|
|
5202
|
-
"https://github.com/SonarSource/SonarJS/tree/master/packages/analysis/src/jsts/rules/S2301"
|
|
5198
|
+
"https://martinfowler.com/bliki/FlagArgument.html"
|
|
5203
5199
|
],
|
|
5204
5200
|
examples: [
|
|
5205
5201
|
{
|
|
@@ -5289,38 +5285,26 @@ var no_boolean_selector_parameter_default = createRule({
|
|
|
5289
5285
|
fix
|
|
5290
5286
|
});
|
|
5291
5287
|
}
|
|
5292
|
-
function
|
|
5293
|
-
if (
|
|
5294
|
-
const
|
|
5295
|
-
|
|
5296
|
-
context.sourceCode.
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
UPSTREAM_SELECTOR_RULE.create.bind(UPSTREAM_SELECTOR_RULE),
|
|
5306
|
-
void 0,
|
|
5307
|
-
nativeArguments
|
|
5308
|
-
);
|
|
5309
|
-
const native = {};
|
|
5310
|
-
if (typeof listeners !== "object" || listeners === null) return native;
|
|
5311
|
-
for (const selector of Object.keys(listeners)) {
|
|
5312
|
-
const listener = Reflect.get(listeners, selector);
|
|
5313
|
-
if (typeof listener === "function")
|
|
5314
|
-
native[selector] = (...nodes) => {
|
|
5315
|
-
Reflect.apply(listener, void 0, nodes);
|
|
5316
|
-
};
|
|
5288
|
+
function inspectSingleStatementBody(owner) {
|
|
5289
|
+
if (owner.body?.type !== import_utils32.AST_NODE_TYPES.BlockStatement || owner.body.body.length !== 1 || preservesBlockObjectCallback(owner)) return;
|
|
5290
|
+
const statement = owner.body.body[0];
|
|
5291
|
+
if (statement?.type === import_utils32.AST_NODE_TYPES.IfStatement) {
|
|
5292
|
+
if (statement.alternate === null || !import_utils32.ASTUtils.hasSideEffect(statement.consequent, context.sourceCode) && !import_utils32.ASTUtils.hasSideEffect(statement.alternate, context.sourceCode)) return;
|
|
5293
|
+
const identifier2 = testIdentifier(statement.test);
|
|
5294
|
+
if (identifier2 !== void 0) report2(identifier2);
|
|
5295
|
+
return;
|
|
5296
|
+
}
|
|
5297
|
+
const selected = statement?.type === import_utils32.AST_NODE_TYPES.ReturnStatement ? statement.argument : statement?.type === import_utils32.AST_NODE_TYPES.ExpressionStatement ? statement.expression : void 0;
|
|
5298
|
+
if (selected?.type !== import_utils32.AST_NODE_TYPES.ConditionalExpression || !import_utils32.ASTUtils.hasSideEffect(selected.consequent, context.sourceCode) && !import_utils32.ASTUtils.hasSideEffect(selected.alternate, context.sourceCode)) return;
|
|
5299
|
+
const identifier = testIdentifier(selected.test);
|
|
5300
|
+
if (identifier !== void 0) report2(identifier);
|
|
5317
5301
|
}
|
|
5318
5302
|
return {
|
|
5319
|
-
|
|
5303
|
+
FunctionDeclaration: inspectSingleStatementBody,
|
|
5304
|
+
FunctionExpression: inspectSingleStatementBody,
|
|
5305
|
+
ArrowFunctionExpression: inspectSingleStatementBody,
|
|
5320
5306
|
ConditionalExpression(node) {
|
|
5321
|
-
|
|
5322
|
-
if (node.parent.type !== import_utils32.AST_NODE_TYPES.ArrowFunctionExpression || node.parent.body !== node || !import_utils32.ASTUtils.hasSideEffect(node.consequent, context.sourceCode) && !import_utils32.ASTUtils.hasSideEffect(node.alternate, context.sourceCode))
|
|
5323
|
-
return;
|
|
5307
|
+
if (node.parent.type !== import_utils32.AST_NODE_TYPES.ArrowFunctionExpression || node.parent.body !== node || !import_utils32.ASTUtils.hasSideEffect(node.consequent, context.sourceCode) && !import_utils32.ASTUtils.hasSideEffect(node.alternate, context.sourceCode)) return;
|
|
5324
5308
|
const callable = node.parent;
|
|
5325
5309
|
if (callable.parent.type === import_utils32.AST_NODE_TYPES.CallExpression && callable.parent.arguments.includes(callable) || callable.parent.type === import_utils32.AST_NODE_TYPES.JSXExpressionContainer)
|
|
5326
5310
|
return;
|
|
@@ -5330,6 +5314,13 @@ var no_boolean_selector_parameter_default = createRule({
|
|
|
5330
5314
|
};
|
|
5331
5315
|
}
|
|
5332
5316
|
});
|
|
5317
|
+
function preservesBlockObjectCallback(owner) {
|
|
5318
|
+
const property = owner.parent;
|
|
5319
|
+
if (property.type !== import_utils32.AST_NODE_TYPES.Property || property.computed) return false;
|
|
5320
|
+
const key = property.key;
|
|
5321
|
+
const name = key.type === import_utils32.AST_NODE_TYPES.Identifier ? key.name : key.type === import_utils32.AST_NODE_TYPES.Literal && typeof key.value === "string" ? key.value : void 0;
|
|
5322
|
+
return name !== void 0 && /^on[A-Z]/u.test(name);
|
|
5323
|
+
}
|
|
5333
5324
|
function testIdentifier(test) {
|
|
5334
5325
|
const value = test.type === import_utils32.AST_NODE_TYPES.UnaryExpression && test.operator === "!" ? test.argument : test;
|
|
5335
5326
|
return value.type === import_utils32.AST_NODE_TYPES.Identifier ? value : void 0;
|
|
@@ -32282,7 +32273,7 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
|
32282
32273
|
"The rule follows stable lexical bindings, static source paths including JSX/TSX, CSS and HTML, awaited reads, and common text operations. Named imports of node:path/path join and resolve are followed when their complete trailing static path proves a source suffix. Reassigned bindings, dynamic trailing segments, namespace path wrappers, unknown path wrappers, iterator pipelines, and interprocedural flows remain unreported.",
|
|
32283
32274
|
"Assertion roots are scope-resolved for supported test runners and Node assert imports; local or unknown helpers with assertion-like names are not inferred.",
|
|
32284
32275
|
"Known fixture, golden, snapshot, and generated source paths are representation contracts and remain unreported; dot segments in the known path are normalized first.",
|
|
32285
|
-
"Contained static paths beneath a
|
|
32276
|
+
"Contained static paths, including stable nested join/resolve descendants, beneath a native mkdtemp/mkdtempSync result are generated artifacts. Absolute or drive-anchored resolve segments and traversal above the proven directory remain unproven. Literal replace/replaceAll nonidentity guards comparing the same raw read remain unreported; fixed-string comparisons remain candidates.",
|
|
32286
32277
|
"When raw representation is genuinely the contract (for example a golden or compatibility sentinel), use an exact line suppression with the reason."
|
|
32287
32278
|
],
|
|
32288
32279
|
examples: [
|
|
@@ -32325,6 +32316,16 @@ function stringValue(node) {
|
|
|
32325
32316
|
}
|
|
32326
32317
|
return null;
|
|
32327
32318
|
}
|
|
32319
|
+
function relativePathDepth(normalized, initialDepth) {
|
|
32320
|
+
let relativeDepth = initialDepth;
|
|
32321
|
+
for (const part of normalized.split("/")) {
|
|
32322
|
+
if (part === "..") {
|
|
32323
|
+
if (relativeDepth === 0) return null;
|
|
32324
|
+
relativeDepth -= 1;
|
|
32325
|
+
} else if (part !== "" && part !== ".") relativeDepth += 1;
|
|
32326
|
+
}
|
|
32327
|
+
return relativeDepth;
|
|
32328
|
+
}
|
|
32328
32329
|
function staticPathSuffix(pathArguments, builder) {
|
|
32329
32330
|
const trailing = [];
|
|
32330
32331
|
for (const argument of pathArguments.toReversed()) {
|
|
@@ -32450,20 +32451,27 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
32450
32451
|
}
|
|
32451
32452
|
if (current.type !== import_utils169.AST_NODE_TYPES.CallExpression) return false;
|
|
32452
32453
|
const callee = unwrap6(current.callee);
|
|
32453
|
-
|
|
32454
|
+
if (callee.type !== import_utils169.AST_NODE_TYPES.Identifier) return false;
|
|
32455
|
+
if (["mkdtemp", "mkdtempSync"].includes(nativeImportedName(callee, FS_MODULES) ?? "")) return true;
|
|
32456
|
+
const builder = nativePathBuilder(callee);
|
|
32457
|
+
return builder !== null && temporaryDescendant(current, builder, depth + 1);
|
|
32454
32458
|
};
|
|
32455
|
-
const temporaryDescendant = (node, builder) => {
|
|
32459
|
+
const temporaryDescendant = (node, builder, depth = 0) => {
|
|
32460
|
+
if (depth > 8) return false;
|
|
32456
32461
|
const [directory, ...segments] = node.arguments;
|
|
32457
|
-
if (directory === void 0 || directory.type === import_utils169.AST_NODE_TYPES.SpreadElement || !temporaryDirectory(directory)) return false;
|
|
32458
|
-
|
|
32462
|
+
if (directory === void 0 || directory.type === import_utils169.AST_NODE_TYPES.SpreadElement || !temporaryDirectory(directory, depth)) return false;
|
|
32463
|
+
let relativeDepth = 0;
|
|
32459
32464
|
for (const segment of segments) {
|
|
32460
32465
|
if (segment.type === import_utils169.AST_NODE_TYPES.SpreadElement) return false;
|
|
32461
32466
|
const value = stringValue(segment);
|
|
32462
32467
|
if (value === null) return false;
|
|
32463
|
-
|
|
32468
|
+
const normalized = value.replaceAll("\\", "/");
|
|
32469
|
+
if (builder === "resolve" && (import_node_path3.posix.isAbsolute(normalized) || import_node_path3.win32.parse(normalized).root !== "")) return false;
|
|
32470
|
+
const nextDepth = relativePathDepth(normalized, relativeDepth);
|
|
32471
|
+
if (nextDepth === null) return false;
|
|
32472
|
+
relativeDepth = nextDepth;
|
|
32464
32473
|
}
|
|
32465
|
-
|
|
32466
|
-
return import_node_path3.posix[builder](sentinel, ...values).startsWith(`${sentinel}/`);
|
|
32474
|
+
return relativeDepth > 0;
|
|
32467
32475
|
};
|
|
32468
32476
|
const sourcePathValue = (value) => {
|
|
32469
32477
|
const normalized = import_node_path3.posix.normalize(value.replaceAll("\\", "/"));
|
package/dist/index.js
CHANGED
|
@@ -1533,7 +1533,7 @@ function isSearchParamReceiver(call, services) {
|
|
|
1533
1533
|
// package.json
|
|
1534
1534
|
var package_default = {
|
|
1535
1535
|
name: "@sarj/eslint-plugin",
|
|
1536
|
-
version: "16.30.
|
|
1536
|
+
version: "16.30.4",
|
|
1537
1537
|
packageManager: "npm@12.2.1",
|
|
1538
1538
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
1539
1539
|
type: "module",
|
|
@@ -1589,7 +1589,6 @@ var package_default = {
|
|
|
1589
1589
|
dependencies: {
|
|
1590
1590
|
"@typescript-eslint/typescript-estree": "^8.72.0",
|
|
1591
1591
|
"@typescript-eslint/utils": "^8.72.0",
|
|
1592
|
-
"eslint-plugin-sonarjs": "4.2.2",
|
|
1593
1592
|
"jsonc-parser": "^3.3.1",
|
|
1594
1593
|
"ts-api-utils": "2.5.0"
|
|
1595
1594
|
},
|
|
@@ -4835,12 +4834,7 @@ function showWitness(value) {
|
|
|
4835
4834
|
}
|
|
4836
4835
|
|
|
4837
4836
|
// src/rules/no-boolean-selector-parameter.ts
|
|
4838
|
-
import {
|
|
4839
|
-
AST_NODE_TYPES as AST_NODE_TYPES30,
|
|
4840
|
-
ASTUtils as ASTUtils16,
|
|
4841
|
-
ESLintUtils as ESLintUtils14
|
|
4842
|
-
} from "@typescript-eslint/utils";
|
|
4843
|
-
import { rules as sonarRules } from "eslint-plugin-sonarjs";
|
|
4837
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES30, ASTUtils as ASTUtils16, ESLintUtils as ESLintUtils14 } from "@typescript-eslint/utils";
|
|
4844
4838
|
|
|
4845
4839
|
// src/rules/_boolean-selector-contract.ts
|
|
4846
4840
|
import { AST_NODE_TYPES as AST_NODE_TYPES28, ASTUtils as ASTUtils14 } from "@typescript-eslint/utils";
|
|
@@ -5168,7 +5162,6 @@ function literalCallers(sourceCode, owner, binding) {
|
|
|
5168
5162
|
}
|
|
5169
5163
|
|
|
5170
5164
|
// src/rules/no-boolean-selector-parameter.ts
|
|
5171
|
-
var UPSTREAM_SELECTOR_RULE = sonarRules["no-selector-parameter"];
|
|
5172
5165
|
var NO_BOOLEAN_SELECTOR_PARAMETER_DOCUMENTATION = {
|
|
5173
5166
|
summary: "Prefer intention-revealing operations over a boolean parameter selecting between actions.",
|
|
5174
5167
|
rationale: "A positional boolean hides which operation a caller requests. Splitting independent actions can make that intent explicit, while boolean data, pure mappings and externally required callback or method contracts remain valid.",
|
|
@@ -5177,15 +5170,14 @@ var NO_BOOLEAN_SELECTOR_PARAMETER_DOCUMENTATION = {
|
|
|
5177
5170
|
defaultLevel: "warning",
|
|
5178
5171
|
autofix: "safe",
|
|
5179
5172
|
limitations: [
|
|
5180
|
-
"Requires type information.
|
|
5173
|
+
"Requires type information. Checks an owned single-statement if/else, returned conditional or conditional expression statement, and expression-bodied arrow conditionals. Only a direct broad-boolean parameter or its negation is considered; optional, nullable, literal-only, generic, destructured and indirect predicate conditions are excluded.",
|
|
5181
5174
|
"Preserves setters, pure mappings, selected pure data passed to one stable lexical operation or assigned to one simple target, independently authored callable and inherited member contracts, inline callbacks, and statically proven named callback references. Independent helpers remain checked in JSX files; callback identity is established from references rather than handler names.",
|
|
5182
|
-
"The
|
|
5175
|
+
"The block-body detector preserves the existing literal on[A-Z] object-property exemption; the concise-arrow extension does not infer a callback contract from that name. Multi-statement bodies, short-circuit guards, local parameter aliases and arbitrary control flow are not inferred. Noncomputed property reads count as data without proving getter effects; computed reads remain outside that exemption, including literal keys. Side-effect detection does not prove whether a called function is pure, and a warning requires manual design review. Generated files are excluded; tests remain checked.",
|
|
5183
5176
|
"Automatic fixes only reuse two already authored private zero-argument ordinary operations returning primitive string literals. A complete comment-free four-declaration module must contain those operations, one private const broad-boolean arrow selecting their direct calls, and one exported zero-argument ordinary function with an explicit string-array return that calls the selector exclusively with literal true and false. Calls are routed to the existing named operations and the exclusively used selector declaration is removed. All other APIs, callbacks, dynamic arguments, contracts and operation bodies remain manual."
|
|
5184
5177
|
],
|
|
5185
5178
|
references: [
|
|
5186
5179
|
"https://understandlegacycode.com/blog/what-is-wrong-with-boolean-parameters/",
|
|
5187
|
-
"https://martinfowler.com/bliki/FlagArgument.html"
|
|
5188
|
-
"https://github.com/SonarSource/SonarJS/tree/master/packages/analysis/src/jsts/rules/S2301"
|
|
5180
|
+
"https://martinfowler.com/bliki/FlagArgument.html"
|
|
5189
5181
|
],
|
|
5190
5182
|
examples: [
|
|
5191
5183
|
{
|
|
@@ -5275,38 +5267,26 @@ var no_boolean_selector_parameter_default = createRule({
|
|
|
5275
5267
|
fix
|
|
5276
5268
|
});
|
|
5277
5269
|
}
|
|
5278
|
-
function
|
|
5279
|
-
if (
|
|
5280
|
-
const
|
|
5281
|
-
|
|
5282
|
-
context.sourceCode.
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
UPSTREAM_SELECTOR_RULE.create.bind(UPSTREAM_SELECTOR_RULE),
|
|
5292
|
-
void 0,
|
|
5293
|
-
nativeArguments
|
|
5294
|
-
);
|
|
5295
|
-
const native = {};
|
|
5296
|
-
if (typeof listeners !== "object" || listeners === null) return native;
|
|
5297
|
-
for (const selector of Object.keys(listeners)) {
|
|
5298
|
-
const listener = Reflect.get(listeners, selector);
|
|
5299
|
-
if (typeof listener === "function")
|
|
5300
|
-
native[selector] = (...nodes) => {
|
|
5301
|
-
Reflect.apply(listener, void 0, nodes);
|
|
5302
|
-
};
|
|
5270
|
+
function inspectSingleStatementBody(owner) {
|
|
5271
|
+
if (owner.body?.type !== AST_NODE_TYPES30.BlockStatement || owner.body.body.length !== 1 || preservesBlockObjectCallback(owner)) return;
|
|
5272
|
+
const statement = owner.body.body[0];
|
|
5273
|
+
if (statement?.type === AST_NODE_TYPES30.IfStatement) {
|
|
5274
|
+
if (statement.alternate === null || !ASTUtils16.hasSideEffect(statement.consequent, context.sourceCode) && !ASTUtils16.hasSideEffect(statement.alternate, context.sourceCode)) return;
|
|
5275
|
+
const identifier2 = testIdentifier(statement.test);
|
|
5276
|
+
if (identifier2 !== void 0) report2(identifier2);
|
|
5277
|
+
return;
|
|
5278
|
+
}
|
|
5279
|
+
const selected = statement?.type === AST_NODE_TYPES30.ReturnStatement ? statement.argument : statement?.type === AST_NODE_TYPES30.ExpressionStatement ? statement.expression : void 0;
|
|
5280
|
+
if (selected?.type !== AST_NODE_TYPES30.ConditionalExpression || !ASTUtils16.hasSideEffect(selected.consequent, context.sourceCode) && !ASTUtils16.hasSideEffect(selected.alternate, context.sourceCode)) return;
|
|
5281
|
+
const identifier = testIdentifier(selected.test);
|
|
5282
|
+
if (identifier !== void 0) report2(identifier);
|
|
5303
5283
|
}
|
|
5304
5284
|
return {
|
|
5305
|
-
|
|
5285
|
+
FunctionDeclaration: inspectSingleStatementBody,
|
|
5286
|
+
FunctionExpression: inspectSingleStatementBody,
|
|
5287
|
+
ArrowFunctionExpression: inspectSingleStatementBody,
|
|
5306
5288
|
ConditionalExpression(node) {
|
|
5307
|
-
|
|
5308
|
-
if (node.parent.type !== AST_NODE_TYPES30.ArrowFunctionExpression || node.parent.body !== node || !ASTUtils16.hasSideEffect(node.consequent, context.sourceCode) && !ASTUtils16.hasSideEffect(node.alternate, context.sourceCode))
|
|
5309
|
-
return;
|
|
5289
|
+
if (node.parent.type !== AST_NODE_TYPES30.ArrowFunctionExpression || node.parent.body !== node || !ASTUtils16.hasSideEffect(node.consequent, context.sourceCode) && !ASTUtils16.hasSideEffect(node.alternate, context.sourceCode)) return;
|
|
5310
5290
|
const callable = node.parent;
|
|
5311
5291
|
if (callable.parent.type === AST_NODE_TYPES30.CallExpression && callable.parent.arguments.includes(callable) || callable.parent.type === AST_NODE_TYPES30.JSXExpressionContainer)
|
|
5312
5292
|
return;
|
|
@@ -5316,6 +5296,13 @@ var no_boolean_selector_parameter_default = createRule({
|
|
|
5316
5296
|
};
|
|
5317
5297
|
}
|
|
5318
5298
|
});
|
|
5299
|
+
function preservesBlockObjectCallback(owner) {
|
|
5300
|
+
const property = owner.parent;
|
|
5301
|
+
if (property.type !== AST_NODE_TYPES30.Property || property.computed) return false;
|
|
5302
|
+
const key = property.key;
|
|
5303
|
+
const name = key.type === AST_NODE_TYPES30.Identifier ? key.name : key.type === AST_NODE_TYPES30.Literal && typeof key.value === "string" ? key.value : void 0;
|
|
5304
|
+
return name !== void 0 && /^on[A-Z]/u.test(name);
|
|
5305
|
+
}
|
|
5319
5306
|
function testIdentifier(test) {
|
|
5320
5307
|
const value = test.type === AST_NODE_TYPES30.UnaryExpression && test.operator === "!" ? test.argument : test;
|
|
5321
5308
|
return value.type === AST_NODE_TYPES30.Identifier ? value : void 0;
|
|
@@ -32339,7 +32326,7 @@ function classMethodCounts(body3, methods) {
|
|
|
32339
32326
|
}
|
|
32340
32327
|
|
|
32341
32328
|
// src/rules/source-coupled-test.ts
|
|
32342
|
-
import { posix as posix2 } from "path";
|
|
32329
|
+
import { posix as posix2, win32 as win322 } from "path";
|
|
32343
32330
|
import { AST_NODE_TYPES as AST_NODE_TYPES150, ASTUtils as ASTUtils98 } from "@typescript-eslint/utils";
|
|
32344
32331
|
var GENERAL_SOURCE_SUFFIX_RE = /\.(?:bash|sh|ya?ml|jsonc|toml|py|css|html|[cm]?[jt]s|[jt]sx)$|(?<!\.(?:tf|tftest))\.json$/iu;
|
|
32345
32332
|
var REPRESENTATION_DIR_RE = /(?:^|\/)(?:__fixtures__|fixtures?|goldens?|snapshots?)(?:\/|$)/iu;
|
|
@@ -32391,7 +32378,7 @@ var SOURCE_COUPLED_TEST_DOCUMENTATION = {
|
|
|
32391
32378
|
"The rule follows stable lexical bindings, static source paths including JSX/TSX, CSS and HTML, awaited reads, and common text operations. Named imports of node:path/path join and resolve are followed when their complete trailing static path proves a source suffix. Reassigned bindings, dynamic trailing segments, namespace path wrappers, unknown path wrappers, iterator pipelines, and interprocedural flows remain unreported.",
|
|
32392
32379
|
"Assertion roots are scope-resolved for supported test runners and Node assert imports; local or unknown helpers with assertion-like names are not inferred.",
|
|
32393
32380
|
"Known fixture, golden, snapshot, and generated source paths are representation contracts and remain unreported; dot segments in the known path are normalized first.",
|
|
32394
|
-
"Contained static paths beneath a
|
|
32381
|
+
"Contained static paths, including stable nested join/resolve descendants, beneath a native mkdtemp/mkdtempSync result are generated artifacts. Absolute or drive-anchored resolve segments and traversal above the proven directory remain unproven. Literal replace/replaceAll nonidentity guards comparing the same raw read remain unreported; fixed-string comparisons remain candidates.",
|
|
32395
32382
|
"When raw representation is genuinely the contract (for example a golden or compatibility sentinel), use an exact line suppression with the reason."
|
|
32396
32383
|
],
|
|
32397
32384
|
examples: [
|
|
@@ -32434,6 +32421,16 @@ function stringValue(node) {
|
|
|
32434
32421
|
}
|
|
32435
32422
|
return null;
|
|
32436
32423
|
}
|
|
32424
|
+
function relativePathDepth(normalized, initialDepth) {
|
|
32425
|
+
let relativeDepth = initialDepth;
|
|
32426
|
+
for (const part of normalized.split("/")) {
|
|
32427
|
+
if (part === "..") {
|
|
32428
|
+
if (relativeDepth === 0) return null;
|
|
32429
|
+
relativeDepth -= 1;
|
|
32430
|
+
} else if (part !== "" && part !== ".") relativeDepth += 1;
|
|
32431
|
+
}
|
|
32432
|
+
return relativeDepth;
|
|
32433
|
+
}
|
|
32437
32434
|
function staticPathSuffix(pathArguments, builder) {
|
|
32438
32435
|
const trailing = [];
|
|
32439
32436
|
for (const argument of pathArguments.toReversed()) {
|
|
@@ -32559,20 +32556,27 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
|
|
|
32559
32556
|
}
|
|
32560
32557
|
if (current.type !== AST_NODE_TYPES150.CallExpression) return false;
|
|
32561
32558
|
const callee = unwrap6(current.callee);
|
|
32562
|
-
|
|
32559
|
+
if (callee.type !== AST_NODE_TYPES150.Identifier) return false;
|
|
32560
|
+
if (["mkdtemp", "mkdtempSync"].includes(nativeImportedName(callee, FS_MODULES) ?? "")) return true;
|
|
32561
|
+
const builder = nativePathBuilder(callee);
|
|
32562
|
+
return builder !== null && temporaryDescendant(current, builder, depth + 1);
|
|
32563
32563
|
};
|
|
32564
|
-
const temporaryDescendant = (node, builder) => {
|
|
32564
|
+
const temporaryDescendant = (node, builder, depth = 0) => {
|
|
32565
|
+
if (depth > 8) return false;
|
|
32565
32566
|
const [directory, ...segments] = node.arguments;
|
|
32566
|
-
if (directory === void 0 || directory.type === AST_NODE_TYPES150.SpreadElement || !temporaryDirectory(directory)) return false;
|
|
32567
|
-
|
|
32567
|
+
if (directory === void 0 || directory.type === AST_NODE_TYPES150.SpreadElement || !temporaryDirectory(directory, depth)) return false;
|
|
32568
|
+
let relativeDepth = 0;
|
|
32568
32569
|
for (const segment of segments) {
|
|
32569
32570
|
if (segment.type === AST_NODE_TYPES150.SpreadElement) return false;
|
|
32570
32571
|
const value = stringValue(segment);
|
|
32571
32572
|
if (value === null) return false;
|
|
32572
|
-
|
|
32573
|
+
const normalized = value.replaceAll("\\", "/");
|
|
32574
|
+
if (builder === "resolve" && (posix2.isAbsolute(normalized) || win322.parse(normalized).root !== "")) return false;
|
|
32575
|
+
const nextDepth = relativePathDepth(normalized, relativeDepth);
|
|
32576
|
+
if (nextDepth === null) return false;
|
|
32577
|
+
relativeDepth = nextDepth;
|
|
32573
32578
|
}
|
|
32574
|
-
|
|
32575
|
-
return posix2[builder](sentinel, ...values).startsWith(`${sentinel}/`);
|
|
32579
|
+
return relativeDepth > 0;
|
|
32576
32580
|
};
|
|
32577
32581
|
const sourcePathValue = (value) => {
|
|
32578
32582
|
const normalized = posix2.normalize(value.replaceAll("\\", "/"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sarj/eslint-plugin",
|
|
3
|
-
"version": "16.30.
|
|
3
|
+
"version": "16.30.4",
|
|
4
4
|
"packageManager": "npm@12.2.1",
|
|
5
5
|
"description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"dependencies": {
|
|
57
57
|
"@typescript-eslint/typescript-estree": "^8.72.0",
|
|
58
58
|
"@typescript-eslint/utils": "^8.72.0",
|
|
59
|
-
"eslint-plugin-sonarjs": "4.2.2",
|
|
60
59
|
"jsonc-parser": "^3.3.1",
|
|
61
60
|
"ts-api-utils": "2.5.0"
|
|
62
61
|
},
|