@sarj/eslint-plugin 2.13.0 → 2.14.0
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/README.md +32 -1
- package/dist/index.cjs +116 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +116 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -343,6 +343,9 @@ declare const rules: {
|
|
|
343
343
|
"trailing-value-narration": _typescript_eslint_utils_ts_eslint.RuleModule<"narratesValue", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
344
344
|
name: string;
|
|
345
345
|
};
|
|
346
|
+
"no-tautological-expect": _typescript_eslint_utils_ts_eslint.RuleModule<"tautologicalComparison" | "tautologicalMatcher", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
347
|
+
name: string;
|
|
348
|
+
};
|
|
346
349
|
};
|
|
347
350
|
declare const plugin: {
|
|
348
351
|
meta: {
|
|
@@ -496,6 +499,9 @@ declare const plugin: {
|
|
|
496
499
|
"trailing-value-narration": _typescript_eslint_utils_ts_eslint.RuleModule<"narratesValue", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
497
500
|
name: string;
|
|
498
501
|
};
|
|
502
|
+
"no-tautological-expect": _typescript_eslint_utils_ts_eslint.RuleModule<"tautologicalComparison" | "tautologicalMatcher", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
503
|
+
name: string;
|
|
504
|
+
};
|
|
499
505
|
};
|
|
500
506
|
configs: {
|
|
501
507
|
recommended: {
|
|
@@ -542,6 +548,7 @@ declare const plugin: {
|
|
|
542
548
|
"@sarj/no-restated-comment": string;
|
|
543
549
|
"@sarj/jsdoc-restates-signature": string;
|
|
544
550
|
"@sarj/trailing-value-narration": string;
|
|
551
|
+
"@sarj/no-tautological-expect": string;
|
|
545
552
|
};
|
|
546
553
|
};
|
|
547
554
|
strict: {
|
|
@@ -593,6 +600,7 @@ declare const plugin: {
|
|
|
593
600
|
"@sarj/no-restated-comment": string;
|
|
594
601
|
"@sarj/jsdoc-restates-signature": string;
|
|
595
602
|
"@sarj/trailing-value-narration": string;
|
|
603
|
+
"@sarj/no-tautological-expect": string;
|
|
596
604
|
};
|
|
597
605
|
};
|
|
598
606
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -343,6 +343,9 @@ declare const rules: {
|
|
|
343
343
|
"trailing-value-narration": _typescript_eslint_utils_ts_eslint.RuleModule<"narratesValue", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
344
344
|
name: string;
|
|
345
345
|
};
|
|
346
|
+
"no-tautological-expect": _typescript_eslint_utils_ts_eslint.RuleModule<"tautologicalComparison" | "tautologicalMatcher", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
347
|
+
name: string;
|
|
348
|
+
};
|
|
346
349
|
};
|
|
347
350
|
declare const plugin: {
|
|
348
351
|
meta: {
|
|
@@ -496,6 +499,9 @@ declare const plugin: {
|
|
|
496
499
|
"trailing-value-narration": _typescript_eslint_utils_ts_eslint.RuleModule<"narratesValue", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
497
500
|
name: string;
|
|
498
501
|
};
|
|
502
|
+
"no-tautological-expect": _typescript_eslint_utils_ts_eslint.RuleModule<"tautologicalComparison" | "tautologicalMatcher", readonly [], unknown, _typescript_eslint_utils_ts_eslint.RuleListener> & {
|
|
503
|
+
name: string;
|
|
504
|
+
};
|
|
499
505
|
};
|
|
500
506
|
configs: {
|
|
501
507
|
recommended: {
|
|
@@ -542,6 +548,7 @@ declare const plugin: {
|
|
|
542
548
|
"@sarj/no-restated-comment": string;
|
|
543
549
|
"@sarj/jsdoc-restates-signature": string;
|
|
544
550
|
"@sarj/trailing-value-narration": string;
|
|
551
|
+
"@sarj/no-tautological-expect": string;
|
|
545
552
|
};
|
|
546
553
|
};
|
|
547
554
|
strict: {
|
|
@@ -593,6 +600,7 @@ declare const plugin: {
|
|
|
593
600
|
"@sarj/no-restated-comment": string;
|
|
594
601
|
"@sarj/jsdoc-restates-signature": string;
|
|
595
602
|
"@sarj/trailing-value-narration": string;
|
|
603
|
+
"@sarj/no-tautological-expect": string;
|
|
596
604
|
};
|
|
597
605
|
};
|
|
598
606
|
};
|
package/dist/index.js
CHANGED
|
@@ -7157,6 +7157,107 @@ var trailing_value_narration_default = ESLintUtils44.RuleCreator(
|
|
|
7157
7157
|
}
|
|
7158
7158
|
});
|
|
7159
7159
|
|
|
7160
|
+
// src/rules/no-tautological-expect.ts
|
|
7161
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES32, ESLintUtils as ESLintUtils45 } from "@typescript-eslint/utils";
|
|
7162
|
+
var EQUALITY_MATCHERS = /* @__PURE__ */ new Set(["toBe", "toEqual", "toStrictEqual"]);
|
|
7163
|
+
var ZERO_ARG_MATCHERS = /* @__PURE__ */ new Set([
|
|
7164
|
+
"toBeDefined",
|
|
7165
|
+
"toBeUndefined",
|
|
7166
|
+
"toBeNull",
|
|
7167
|
+
"toBeTruthy",
|
|
7168
|
+
"toBeFalsy",
|
|
7169
|
+
"toBeNaN"
|
|
7170
|
+
]);
|
|
7171
|
+
var OPERAND_PREVIEW_CHARS = 40;
|
|
7172
|
+
var NUMERIC_SIGNS = /* @__PURE__ */ new Set(["-", "+"]);
|
|
7173
|
+
function isLiteral(node) {
|
|
7174
|
+
switch (node.type) {
|
|
7175
|
+
case AST_NODE_TYPES32.Literal:
|
|
7176
|
+
return true;
|
|
7177
|
+
case AST_NODE_TYPES32.TemplateLiteral:
|
|
7178
|
+
return node.expressions.length === 0;
|
|
7179
|
+
case AST_NODE_TYPES32.UnaryExpression:
|
|
7180
|
+
return NUMERIC_SIGNS.has(node.operator) && isLiteral(node.argument);
|
|
7181
|
+
case AST_NODE_TYPES32.ArrayExpression:
|
|
7182
|
+
return node.elements.every((element) => element !== null && isLiteral(element));
|
|
7183
|
+
case AST_NODE_TYPES32.ObjectExpression:
|
|
7184
|
+
return node.properties.every(
|
|
7185
|
+
(property) => property.type === AST_NODE_TYPES32.Property && !property.computed && isLiteral(property.value)
|
|
7186
|
+
);
|
|
7187
|
+
default:
|
|
7188
|
+
return false;
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
function expectOperand(callee) {
|
|
7192
|
+
const receiver = callee.object;
|
|
7193
|
+
if (receiver.type !== AST_NODE_TYPES32.CallExpression || receiver.callee.type !== AST_NODE_TYPES32.Identifier || receiver.callee.name !== "expect" || receiver.arguments.length !== 1) {
|
|
7194
|
+
return null;
|
|
7195
|
+
}
|
|
7196
|
+
return receiver.arguments[0] ?? null;
|
|
7197
|
+
}
|
|
7198
|
+
var no_tautological_expect_default = ESLintUtils45.RuleCreator(
|
|
7199
|
+
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
7200
|
+
)({
|
|
7201
|
+
name: "no-tautological-expect",
|
|
7202
|
+
meta: {
|
|
7203
|
+
type: "problem",
|
|
7204
|
+
docs: {
|
|
7205
|
+
description: "Disallow an assertion whose operands are all literals; its outcome is fixed before the code runs, so it can never fail."
|
|
7206
|
+
},
|
|
7207
|
+
schema: [],
|
|
7208
|
+
messages: {
|
|
7209
|
+
tautologicalComparison: "`expect({{operand}}).{{matcher}}({{operand}})` compares a literal with an identical literal \u2014 it passes even if the code under test is deleted. Assert on a value the code produced, or delete the test.",
|
|
7210
|
+
tautologicalMatcher: "`expect({{operand}}).{{matcher}}()` asserts on a literal, so its outcome is fixed before the code runs. Assert on a value the code produced, or delete the test."
|
|
7211
|
+
}
|
|
7212
|
+
},
|
|
7213
|
+
defaultOptions: [],
|
|
7214
|
+
create(context) {
|
|
7215
|
+
if (!isTestFile(context.filename)) {
|
|
7216
|
+
return {};
|
|
7217
|
+
}
|
|
7218
|
+
const preview2 = (node) => {
|
|
7219
|
+
const text = context.sourceCode.getText(node).replaceAll(/\s+/gu, " ");
|
|
7220
|
+
return text.length > OPERAND_PREVIEW_CHARS ? `${text.slice(0, OPERAND_PREVIEW_CHARS)}\u2026` : text;
|
|
7221
|
+
};
|
|
7222
|
+
return {
|
|
7223
|
+
CallExpression(node) {
|
|
7224
|
+
const callee = node.callee;
|
|
7225
|
+
if (callee.type !== AST_NODE_TYPES32.MemberExpression || callee.computed) {
|
|
7226
|
+
return;
|
|
7227
|
+
}
|
|
7228
|
+
if (callee.property.type !== AST_NODE_TYPES32.Identifier) {
|
|
7229
|
+
return;
|
|
7230
|
+
}
|
|
7231
|
+
const matcher = callee.property.name;
|
|
7232
|
+
const operand = expectOperand(callee);
|
|
7233
|
+
if (operand === null || !isLiteral(operand)) {
|
|
7234
|
+
return;
|
|
7235
|
+
}
|
|
7236
|
+
if (ZERO_ARG_MATCHERS.has(matcher) && node.arguments.length === 0) {
|
|
7237
|
+
context.report({
|
|
7238
|
+
node,
|
|
7239
|
+
messageId: "tautologicalMatcher",
|
|
7240
|
+
data: { operand: preview2(operand), matcher }
|
|
7241
|
+
});
|
|
7242
|
+
return;
|
|
7243
|
+
}
|
|
7244
|
+
const expected = node.arguments[0];
|
|
7245
|
+
if (!EQUALITY_MATCHERS.has(matcher) || node.arguments.length !== 1 || expected === void 0 || !isLiteral(expected)) {
|
|
7246
|
+
return;
|
|
7247
|
+
}
|
|
7248
|
+
if (context.sourceCode.getText(operand) !== context.sourceCode.getText(expected)) {
|
|
7249
|
+
return;
|
|
7250
|
+
}
|
|
7251
|
+
context.report({
|
|
7252
|
+
node,
|
|
7253
|
+
messageId: "tautologicalComparison",
|
|
7254
|
+
data: { operand: preview2(operand), matcher }
|
|
7255
|
+
});
|
|
7256
|
+
}
|
|
7257
|
+
};
|
|
7258
|
+
}
|
|
7259
|
+
});
|
|
7260
|
+
|
|
7160
7261
|
// src/index.ts
|
|
7161
7262
|
var rules = {
|
|
7162
7263
|
"enforce-file-structure": enforce_file_structure_default,
|
|
@@ -7202,12 +7303,13 @@ var rules = {
|
|
|
7202
7303
|
"prefer-module-level-constant": prefer_module_level_constant_default,
|
|
7203
7304
|
"jsdoc-restates-signature": jsdoc_restates_signature_default,
|
|
7204
7305
|
"no-restated-comment": no_restated_comment_default,
|
|
7205
|
-
"trailing-value-narration": trailing_value_narration_default
|
|
7306
|
+
"trailing-value-narration": trailing_value_narration_default,
|
|
7307
|
+
"no-tautological-expect": no_tautological_expect_default
|
|
7206
7308
|
};
|
|
7207
7309
|
var plugin = {
|
|
7208
7310
|
meta: {
|
|
7209
7311
|
name: "@sarj/eslint-plugin",
|
|
7210
|
-
version: "2.
|
|
7312
|
+
version: "2.14.0"
|
|
7211
7313
|
},
|
|
7212
7314
|
rules,
|
|
7213
7315
|
configs: {
|
|
@@ -7274,7 +7376,15 @@ var plugin = {
|
|
|
7274
7376
|
// wrong deletion is silent information loss.
|
|
7275
7377
|
"@sarj/no-restated-comment": "warn",
|
|
7276
7378
|
"@sarj/jsdoc-restates-signature": "warn",
|
|
7277
|
-
"@sarj/trailing-value-narration": "warn"
|
|
7379
|
+
"@sarj/trailing-value-narration": "warn",
|
|
7380
|
+
// The TS half of SARJ057 (2026-07). Python has caught the
|
|
7381
|
+
// assertion-FREE test since 0.15.0 (SARJ043) and had no TS
|
|
7382
|
+
// counterpart, which is how `expect(true).toBe(true); // placeholder`
|
|
7383
|
+
// survived in internal-automations: the file HAS an assertion.
|
|
7384
|
+
// Measured across 5,819 .ts/.tsx files (1,003 of them test files) in
|
|
7385
|
+
// six internal repos plus got / hono / swr / trpc: 3 hits, 3 true
|
|
7386
|
+
// positives, 0 false positives.
|
|
7387
|
+
"@sarj/no-tautological-expect": "warn"
|
|
7278
7388
|
}
|
|
7279
7389
|
},
|
|
7280
7390
|
strict: {
|
|
@@ -7342,7 +7452,9 @@ var plugin = {
|
|
|
7342
7452
|
// for the measured hit counts and false-positive rates.
|
|
7343
7453
|
"@sarj/no-restated-comment": "error",
|
|
7344
7454
|
"@sarj/jsdoc-restates-signature": "error",
|
|
7345
|
-
"@sarj/trailing-value-narration": "error"
|
|
7455
|
+
"@sarj/trailing-value-narration": "error",
|
|
7456
|
+
// TS half of SARJ057 — see the `recommended` block for the measurement.
|
|
7457
|
+
"@sarj/no-tautological-expect": "error"
|
|
7346
7458
|
}
|
|
7347
7459
|
}
|
|
7348
7460
|
}
|