@sarj/eslint-plugin 2.4.1 → 2.6.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/dist/index.cjs +94 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +95 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -247,6 +247,8 @@ var no_client_side_data_fetching_default = import_utils2.ESLintUtils.RuleCreator
|
|
|
247
247
|
// src/rules/no-comment-cruft.ts
|
|
248
248
|
var import_utils3 = require("@typescript-eslint/utils");
|
|
249
249
|
var LEADING_PREAMBLE_MIN = 4;
|
|
250
|
+
var STEP_NARRATION_RE = /^(?:first(?:ly)?|second(?:ly)?|third(?:ly)?|then|next|after(?:wards| that)?|finally|lastly|now)\s*[,:]\s*\S|^step\s+\d+\b/i;
|
|
251
|
+
var META_COMMENTARY_RE = /\b(?:for now|keeping (?:it|this) simple|could be (?:refactored|improved|cleaned up|simplified)|refactor(?:ed|ing)? (?:later|this)|not sure (?:if|whether|why|how)|quick[- ](?:and[- ]dirty|fix)|(?:a |bit of a )?hacky|is a hack|temporary (?:solution|workaround|fix|hack)|revisit (?:this|later|below)|clean (?:this|it) up|not ideal|placeholder for now)\b/i;
|
|
250
252
|
var DIRECTIVE_RE = /^(eslint\b|eslint-|@ts-|prettier-ignore|prettier\b|biome-|c8\b|v8\b|istanbul\b|@type\b|@vite|webpack|<reference|<amd|global\b|noinspection|todo\b|fixme\b|hack\b|xxx\b)/i;
|
|
251
253
|
var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
|
|
252
254
|
var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
|
|
@@ -285,6 +287,13 @@ function isProse(text) {
|
|
|
285
287
|
}
|
|
286
288
|
return false;
|
|
287
289
|
}
|
|
290
|
+
function isRedundantNarration(body) {
|
|
291
|
+
const t = body.trim();
|
|
292
|
+
if (!t || looksLikeCode(t) || hasPseudocode(t)) return false;
|
|
293
|
+
if (STEP_NARRATION_RE.test(t)) return true;
|
|
294
|
+
if (META_COMMENTARY_RE.test(t)) return true;
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
288
297
|
function hasCommentedOutCode(texts, precedingProse) {
|
|
289
298
|
for (let i = 0; i < texts.length; i++) {
|
|
290
299
|
const line = texts[i];
|
|
@@ -310,7 +319,8 @@ var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
|
|
|
310
319
|
messages: {
|
|
311
320
|
commentedOutCode: "Commented-out code \u2014 delete it; git history remembers.",
|
|
312
321
|
sectionBanner: "Section-banner / region comment \u2014 structure code with functions, not ASCII rules.",
|
|
313
|
-
fileHeaderPreamble: "File-header comment preamble \u2014 use a brief doc comment for the why, not a block of `//` lines."
|
|
322
|
+
fileHeaderPreamble: "File-header comment preamble \u2014 use a brief doc comment for the why, not a block of `//` lines.",
|
|
323
|
+
redundantNarration: "Comment narrates the code \u2014 delete it or say *why*, not *what*. Code is self-documenting."
|
|
314
324
|
}
|
|
315
325
|
},
|
|
316
326
|
defaultOptions: [],
|
|
@@ -365,6 +375,13 @@ var no_comment_cruft_default = import_utils3.ESLintUtils.RuleCreator(
|
|
|
365
375
|
const precedingProse = prev !== void 0 && prev.type === "Line" && prev.loc.end.line === comment.loc.start.line - 1 && isProse(stripCommentMarker(prev.value));
|
|
366
376
|
if (hasCommentedOutCode(texts, precedingProse)) {
|
|
367
377
|
context.report({ node: comment, messageId: "commentedOutCode" });
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
if (comment.type === "Line" && texts.length === 1) {
|
|
381
|
+
const body = texts[0];
|
|
382
|
+
if (body !== void 0 && isRedundantNarration(body)) {
|
|
383
|
+
context.report({ node: comment, messageId: "redundantNarration" });
|
|
384
|
+
}
|
|
368
385
|
}
|
|
369
386
|
}
|
|
370
387
|
reportLeadingPreamble(comments, firstCodeLine);
|
|
@@ -3246,8 +3263,54 @@ var no_secret_in_log_default = import_utils25.ESLintUtils.RuleCreator(
|
|
|
3246
3263
|
}
|
|
3247
3264
|
});
|
|
3248
3265
|
|
|
3249
|
-
// src/rules/
|
|
3266
|
+
// src/rules/no-unsafe-cast.ts
|
|
3250
3267
|
var import_utils26 = require("@typescript-eslint/utils");
|
|
3268
|
+
var import_utils27 = require("@typescript-eslint/utils");
|
|
3269
|
+
function isAnyAnnotation(node) {
|
|
3270
|
+
return node.type === import_utils27.AST_NODE_TYPES.TSAnyKeyword;
|
|
3271
|
+
}
|
|
3272
|
+
function isConstAssertion(typeAnnotation) {
|
|
3273
|
+
return typeAnnotation.type === import_utils27.AST_NODE_TYPES.TSTypeReference && typeAnnotation.typeName.type === import_utils27.AST_NODE_TYPES.Identifier && typeAnnotation.typeName.name === "const";
|
|
3274
|
+
}
|
|
3275
|
+
var no_unsafe_cast_default = import_utils26.ESLintUtils.RuleCreator(
|
|
3276
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
3277
|
+
)({
|
|
3278
|
+
name: "no-unsafe-cast",
|
|
3279
|
+
meta: {
|
|
3280
|
+
type: "problem",
|
|
3281
|
+
docs: {
|
|
3282
|
+
description: "Disallow `as any`/`<any>` casts and `as unknown as T` double-casts, which fully disable type checking."
|
|
3283
|
+
},
|
|
3284
|
+
schema: [],
|
|
3285
|
+
messages: {
|
|
3286
|
+
asAny: "`as any` disables type checking on this value. Narrow with a type guard, model the shape, or cast to a specific type instead.",
|
|
3287
|
+
doubleCast: "`as unknown as T` double-cast bypasses the compiler's structural check. Prefer a runtime validator (e.g. a zod schema) or an accurate type at the boundary."
|
|
3288
|
+
}
|
|
3289
|
+
},
|
|
3290
|
+
defaultOptions: [],
|
|
3291
|
+
create(context) {
|
|
3292
|
+
function checkAssertion(node) {
|
|
3293
|
+
if (isConstAssertion(node.typeAnnotation)) {
|
|
3294
|
+
return;
|
|
3295
|
+
}
|
|
3296
|
+
if (isAnyAnnotation(node.typeAnnotation)) {
|
|
3297
|
+
context.report({ node, messageId: "asAny" });
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
3300
|
+
const inner = node.expression;
|
|
3301
|
+
if (inner.type === import_utils27.AST_NODE_TYPES.TSAsExpression || inner.type === import_utils27.AST_NODE_TYPES.TSTypeAssertion) {
|
|
3302
|
+
context.report({ node, messageId: "doubleCast" });
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
return {
|
|
3306
|
+
TSAsExpression: checkAssertion,
|
|
3307
|
+
TSTypeAssertion: checkAssertion
|
|
3308
|
+
};
|
|
3309
|
+
}
|
|
3310
|
+
});
|
|
3311
|
+
|
|
3312
|
+
// src/rules/prefer-string-literal-union.ts
|
|
3313
|
+
var import_utils28 = require("@typescript-eslint/utils");
|
|
3251
3314
|
var ts = __toESM(require("typescript"), 1);
|
|
3252
3315
|
var CHOICE_TOKENS = /* @__PURE__ */ new Set([
|
|
3253
3316
|
"status",
|
|
@@ -3290,19 +3353,19 @@ function isChoiceLikeName(name) {
|
|
|
3290
3353
|
return CHOICE_TOKENS.has(lastWord(name));
|
|
3291
3354
|
}
|
|
3292
3355
|
function keyName(key) {
|
|
3293
|
-
if (key.type ===
|
|
3356
|
+
if (key.type === import_utils28.AST_NODE_TYPES.Identifier) {
|
|
3294
3357
|
return key.name;
|
|
3295
3358
|
}
|
|
3296
|
-
if (key.type ===
|
|
3359
|
+
if (key.type === import_utils28.AST_NODE_TYPES.Literal && typeof key.value === "string") {
|
|
3297
3360
|
return key.value;
|
|
3298
3361
|
}
|
|
3299
3362
|
return null;
|
|
3300
3363
|
}
|
|
3301
3364
|
function isStringLiteralMember(t) {
|
|
3302
|
-
return t.type ===
|
|
3365
|
+
return t.type === import_utils28.AST_NODE_TYPES.TSLiteralType && t.literal.type === import_utils28.AST_NODE_TYPES.Literal && typeof t.literal.value === "string";
|
|
3303
3366
|
}
|
|
3304
3367
|
function isStringLiteralUnion(node) {
|
|
3305
|
-
if (node?.type !==
|
|
3368
|
+
if (node?.type !== import_utils28.AST_NODE_TYPES.TSUnionType) {
|
|
3306
3369
|
return false;
|
|
3307
3370
|
}
|
|
3308
3371
|
return node.types.filter(isStringLiteralMember).length >= MIN_CLUSTER_SIZE;
|
|
@@ -3331,12 +3394,12 @@ function bindingSourceExpression(decl) {
|
|
|
3331
3394
|
return ts.isForOfStatement(node) ? node.expression : node.initializer;
|
|
3332
3395
|
}
|
|
3333
3396
|
function refKey(node) {
|
|
3334
|
-
if (node.type ===
|
|
3397
|
+
if (node.type === import_utils28.AST_NODE_TYPES.Identifier) {
|
|
3335
3398
|
return node.name;
|
|
3336
3399
|
}
|
|
3337
|
-
if (node.type ===
|
|
3400
|
+
if (node.type === import_utils28.AST_NODE_TYPES.MemberExpression && !node.computed) {
|
|
3338
3401
|
const inner = refKey(node.object);
|
|
3339
|
-
if (inner === null || node.property.type !==
|
|
3402
|
+
if (inner === null || node.property.type !== import_utils28.AST_NODE_TYPES.Identifier) {
|
|
3340
3403
|
return null;
|
|
3341
3404
|
}
|
|
3342
3405
|
return `${inner}.${node.property.name}`;
|
|
@@ -3344,12 +3407,12 @@ function refKey(node) {
|
|
|
3344
3407
|
return null;
|
|
3345
3408
|
}
|
|
3346
3409
|
function strLiteral(node) {
|
|
3347
|
-
if (node.type ===
|
|
3410
|
+
if (node.type === import_utils28.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
3348
3411
|
return node.value;
|
|
3349
3412
|
}
|
|
3350
3413
|
return null;
|
|
3351
3414
|
}
|
|
3352
|
-
var prefer_string_literal_union_default =
|
|
3415
|
+
var prefer_string_literal_union_default = import_utils28.ESLintUtils.RuleCreator(
|
|
3353
3416
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
3354
3417
|
)({
|
|
3355
3418
|
name: "prefer-string-literal-union",
|
|
@@ -3373,7 +3436,7 @@ var prefer_string_literal_union_default = import_utils26.ESLintUtils.RuleCreator
|
|
|
3373
3436
|
}
|
|
3374
3437
|
let services;
|
|
3375
3438
|
try {
|
|
3376
|
-
services =
|
|
3439
|
+
services = import_utils28.ESLintUtils.getParserServices(context);
|
|
3377
3440
|
} catch {
|
|
3378
3441
|
services = null;
|
|
3379
3442
|
}
|
|
@@ -3457,7 +3520,7 @@ var prefer_string_literal_union_default = import_utils26.ESLintUtils.RuleCreator
|
|
|
3457
3520
|
containersWithUnion.add(container);
|
|
3458
3521
|
return;
|
|
3459
3522
|
}
|
|
3460
|
-
if (typeNode?.type !==
|
|
3523
|
+
if (typeNode?.type !== import_utils28.AST_NODE_TYPES.TSStringKeyword) {
|
|
3461
3524
|
return;
|
|
3462
3525
|
}
|
|
3463
3526
|
const name = keyName(key);
|
|
@@ -3545,10 +3608,10 @@ var prefer_string_literal_union_default = import_utils26.ESLintUtils.RuleCreator
|
|
|
3545
3608
|
}
|
|
3546
3609
|
};
|
|
3547
3610
|
function refKeyText(node) {
|
|
3548
|
-
if (node.type ===
|
|
3611
|
+
if (node.type === import_utils28.AST_NODE_TYPES.BinaryExpression) {
|
|
3549
3612
|
return refKey(node.left) ?? refKey(node.right) ?? "value";
|
|
3550
3613
|
}
|
|
3551
|
-
if (node.type ===
|
|
3614
|
+
if (node.type === import_utils28.AST_NODE_TYPES.SwitchStatement) {
|
|
3552
3615
|
return refKey(node.discriminant) ?? "value";
|
|
3553
3616
|
}
|
|
3554
3617
|
return "value";
|
|
@@ -3557,7 +3620,7 @@ var prefer_string_literal_union_default = import_utils26.ESLintUtils.RuleCreator
|
|
|
3557
3620
|
});
|
|
3558
3621
|
|
|
3559
3622
|
// src/rules/single-public-export.ts
|
|
3560
|
-
var
|
|
3623
|
+
var import_utils29 = require("@typescript-eslint/utils");
|
|
3561
3624
|
var JUNK_DRAWER_STEMS = /* @__PURE__ */ new Set([
|
|
3562
3625
|
"util",
|
|
3563
3626
|
"utils",
|
|
@@ -3591,12 +3654,12 @@ var kebabCase2 = (name) => {
|
|
|
3591
3654
|
}
|
|
3592
3655
|
return normalized.replace(CAMEL_BOUNDARY_RE, "-").toLowerCase();
|
|
3593
3656
|
};
|
|
3594
|
-
var isFunctionExpression = (node) => node !== null && (node.type ===
|
|
3657
|
+
var isFunctionExpression = (node) => node !== null && (node.type === import_utils29.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils29.AST_NODE_TYPES.FunctionExpression);
|
|
3595
3658
|
var functionConstName = (decl) => {
|
|
3596
3659
|
if (decl.declarations.length !== 1) return null;
|
|
3597
3660
|
const [declarator] = decl.declarations;
|
|
3598
3661
|
if (declarator === void 0) return null;
|
|
3599
|
-
if (declarator.id.type !==
|
|
3662
|
+
if (declarator.id.type !== import_utils29.AST_NODE_TYPES.Identifier) return null;
|
|
3600
3663
|
if (!isFunctionExpression(declarator.init)) return null;
|
|
3601
3664
|
return declarator.id.name;
|
|
3602
3665
|
};
|
|
@@ -3610,20 +3673,20 @@ var summarizeExports = (body) => {
|
|
|
3610
3673
|
};
|
|
3611
3674
|
for (const statement of body) {
|
|
3612
3675
|
switch (statement.type) {
|
|
3613
|
-
case
|
|
3676
|
+
case import_utils29.AST_NODE_TYPES.ExportAllDeclaration:
|
|
3614
3677
|
hasReExport = true;
|
|
3615
3678
|
break;
|
|
3616
|
-
case
|
|
3679
|
+
case import_utils29.AST_NODE_TYPES.ExportDefaultDeclaration: {
|
|
3617
3680
|
names += 1;
|
|
3618
3681
|
const decl = statement.declaration;
|
|
3619
|
-
if (decl.type ===
|
|
3682
|
+
if (decl.type === import_utils29.AST_NODE_TYPES.FunctionDeclaration && decl.id !== null) {
|
|
3620
3683
|
candidate = { name: decl.id.name, node: statement };
|
|
3621
|
-
} else if (decl.type ===
|
|
3684
|
+
} else if (decl.type === import_utils29.AST_NODE_TYPES.ClassDeclaration && decl.id !== null) {
|
|
3622
3685
|
candidate = { name: decl.id.name, node: statement };
|
|
3623
3686
|
}
|
|
3624
3687
|
break;
|
|
3625
3688
|
}
|
|
3626
|
-
case
|
|
3689
|
+
case import_utils29.AST_NODE_TYPES.ExportNamedDeclaration: {
|
|
3627
3690
|
if (statement.source !== null) {
|
|
3628
3691
|
hasReExport = true;
|
|
3629
3692
|
break;
|
|
@@ -3634,15 +3697,15 @@ var summarizeExports = (body) => {
|
|
|
3634
3697
|
break;
|
|
3635
3698
|
}
|
|
3636
3699
|
switch (decl.type) {
|
|
3637
|
-
case
|
|
3700
|
+
case import_utils29.AST_NODE_TYPES.FunctionDeclaration:
|
|
3638
3701
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
3639
3702
|
else names += 1;
|
|
3640
3703
|
break;
|
|
3641
|
-
case
|
|
3704
|
+
case import_utils29.AST_NODE_TYPES.ClassDeclaration:
|
|
3642
3705
|
if (decl.id !== null) addCandidate(decl.id.name, statement);
|
|
3643
3706
|
else names += 1;
|
|
3644
3707
|
break;
|
|
3645
|
-
case
|
|
3708
|
+
case import_utils29.AST_NODE_TYPES.VariableDeclaration: {
|
|
3646
3709
|
const fnName = functionConstName(decl);
|
|
3647
3710
|
if (fnName !== null && decl.declarations.length === 1) {
|
|
3648
3711
|
addCandidate(fnName, statement);
|
|
@@ -3662,7 +3725,7 @@ var summarizeExports = (body) => {
|
|
|
3662
3725
|
}
|
|
3663
3726
|
return { names, hasReExport, candidate };
|
|
3664
3727
|
};
|
|
3665
|
-
var single_public_export_default =
|
|
3728
|
+
var single_public_export_default = import_utils29.ESLintUtils.RuleCreator(
|
|
3666
3729
|
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
3667
3730
|
)({
|
|
3668
3731
|
name: "single-public-export",
|
|
@@ -3726,13 +3789,14 @@ var rules = {
|
|
|
3726
3789
|
"no-cors-wildcard-with-credentials": no_cors_wildcard_with_credentials_default,
|
|
3727
3790
|
"no-fat-try-blocks": no_fat_try_blocks_default,
|
|
3728
3791
|
"no-secret-in-log": no_secret_in_log_default,
|
|
3792
|
+
"no-unsafe-cast": no_unsafe_cast_default,
|
|
3729
3793
|
"prefer-string-literal-union": prefer_string_literal_union_default,
|
|
3730
3794
|
"single-public-export": single_public_export_default
|
|
3731
3795
|
};
|
|
3732
3796
|
var plugin = {
|
|
3733
3797
|
meta: {
|
|
3734
3798
|
name: "@sarj/eslint-plugin",
|
|
3735
|
-
version: "2.
|
|
3799
|
+
version: "2.6.0"
|
|
3736
3800
|
},
|
|
3737
3801
|
rules,
|
|
3738
3802
|
configs: {
|
|
@@ -3762,6 +3826,7 @@ var plugin = {
|
|
|
3762
3826
|
"@sarj/no-fat-try-blocks": "warn",
|
|
3763
3827
|
"@sarj/no-cors-wildcard-with-credentials": "warn",
|
|
3764
3828
|
"@sarj/no-secret-in-log": "warn",
|
|
3829
|
+
"@sarj/no-unsafe-cast": "warn",
|
|
3765
3830
|
"@sarj/single-public-export": "warn",
|
|
3766
3831
|
"@sarj/prefer-string-literal-union": "warn"
|
|
3767
3832
|
}
|
|
@@ -3796,6 +3861,7 @@ var plugin = {
|
|
|
3796
3861
|
"@sarj/no-fat-try-blocks": "error",
|
|
3797
3862
|
"@sarj/no-cors-wildcard-with-credentials": "error",
|
|
3798
3863
|
"@sarj/no-secret-in-log": "error",
|
|
3864
|
+
"@sarj/no-unsafe-cast": "warn",
|
|
3799
3865
|
"@sarj/single-public-export": "error",
|
|
3800
3866
|
// High-volume/stylistic — warn until rollout proves FP rate.
|
|
3801
3867
|
"@sarj/prefer-string-literal-union": "warn"
|