@sarj/eslint-plugin 2.0.2 → 2.1.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 +710 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +715 -62
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -321,9 +321,260 @@ var no_enum_default = ESLintUtils3.RuleCreator(
|
|
|
321
321
|
}
|
|
322
322
|
});
|
|
323
323
|
|
|
324
|
-
// src/rules/no-
|
|
324
|
+
// src/rules/no-insecure-random-id.ts
|
|
325
325
|
import { ESLintUtils as ESLintUtils4 } from "@typescript-eslint/utils";
|
|
326
|
-
var
|
|
326
|
+
var NAME_PATTERN = /id|token|key|secret|uuid|nonce|session|password|salt/i;
|
|
327
|
+
function isMathRandomCall(node) {
|
|
328
|
+
if (node.type !== "CallExpression") {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
const callee = node.callee;
|
|
332
|
+
if (callee.type !== "MemberExpression" || callee.computed) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
const { object, property } = callee;
|
|
336
|
+
return object.type === "Identifier" && object.name === "Math" && property.type === "Identifier" && property.name === "random";
|
|
337
|
+
}
|
|
338
|
+
function isPartOfToString36Chain(node) {
|
|
339
|
+
let current = node;
|
|
340
|
+
let parent = current.parent;
|
|
341
|
+
while (parent) {
|
|
342
|
+
if (parent.type === "MemberExpression" && parent.object === current && !parent.computed && parent.property.type === "Identifier" && parent.property.name === "toString") {
|
|
343
|
+
const grandparent = parent.parent;
|
|
344
|
+
if (grandparent && grandparent.type === "CallExpression" && grandparent.callee === parent) {
|
|
345
|
+
const firstArg = grandparent.arguments[0];
|
|
346
|
+
if (firstArg && firstArg.type === "Literal" && firstArg.value === 36) {
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (parent.type === "MemberExpression" && parent.object === current) {
|
|
352
|
+
current = parent;
|
|
353
|
+
parent = current.parent;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (parent.type === "CallExpression" && parent.callee === current) {
|
|
357
|
+
current = parent;
|
|
358
|
+
parent = current.parent;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
function findEnclosingName(node) {
|
|
366
|
+
let current = node;
|
|
367
|
+
let parent = current.parent;
|
|
368
|
+
while (parent) {
|
|
369
|
+
if (parent.type === "VariableDeclarator" && parent.init === current) {
|
|
370
|
+
if (parent.id.type === "Identifier") {
|
|
371
|
+
return parent.id.name;
|
|
372
|
+
}
|
|
373
|
+
return void 0;
|
|
374
|
+
}
|
|
375
|
+
if (parent.type === "Property" && parent.value === current) {
|
|
376
|
+
const key = parent.key;
|
|
377
|
+
if (!parent.computed && key.type === "Identifier") {
|
|
378
|
+
return key.name;
|
|
379
|
+
}
|
|
380
|
+
if (key.type === "Literal" && typeof key.value === "string") {
|
|
381
|
+
return key.value;
|
|
382
|
+
}
|
|
383
|
+
return void 0;
|
|
384
|
+
}
|
|
385
|
+
if (parent.type === "PropertyDefinition" && parent.value === current) {
|
|
386
|
+
const key = parent.key;
|
|
387
|
+
if (!parent.computed && key.type === "Identifier") {
|
|
388
|
+
return key.name;
|
|
389
|
+
}
|
|
390
|
+
if (key.type === "Literal" && typeof key.value === "string") {
|
|
391
|
+
return key.value;
|
|
392
|
+
}
|
|
393
|
+
return void 0;
|
|
394
|
+
}
|
|
395
|
+
if (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression" || parent.type === "BlockStatement" || parent.type === "ReturnStatement" || parent.type === "ExpressionStatement") {
|
|
396
|
+
return void 0;
|
|
397
|
+
}
|
|
398
|
+
current = parent;
|
|
399
|
+
parent = current.parent;
|
|
400
|
+
}
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
var no_insecure_random_id_default = ESLintUtils4.RuleCreator(
|
|
404
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
405
|
+
)({
|
|
406
|
+
name: "no-insecure-random-id",
|
|
407
|
+
meta: {
|
|
408
|
+
type: "problem",
|
|
409
|
+
docs: {
|
|
410
|
+
description: "Disallow using `Math.random()` to generate identifiers, tokens, or secrets; use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead."
|
|
411
|
+
},
|
|
412
|
+
schema: [],
|
|
413
|
+
messages: {
|
|
414
|
+
insecureRandomId: "`Math.random()` is not cryptographically secure and is predictable; do not use it to generate IDs, tokens, or secrets. Use `crypto.randomUUID()` or `crypto.getRandomValues(...)` instead."
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
defaultOptions: [],
|
|
418
|
+
create(context) {
|
|
419
|
+
return {
|
|
420
|
+
CallExpression(node) {
|
|
421
|
+
if (!isMathRandomCall(node)) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (isPartOfToString36Chain(node)) {
|
|
425
|
+
context.report({ node, messageId: "insecureRandomId" });
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const name = findEnclosingName(node);
|
|
429
|
+
if (name !== void 0 && NAME_PATTERN.test(name)) {
|
|
430
|
+
context.report({ node, messageId: "insecureRandomId" });
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// src/rules/no-json-stringify-error.ts
|
|
438
|
+
import { ESLintUtils as ESLintUtils5 } from "@typescript-eslint/utils";
|
|
439
|
+
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
440
|
+
function isCatchBinding(scope, name) {
|
|
441
|
+
let current = scope;
|
|
442
|
+
while (current) {
|
|
443
|
+
const variable = current.set.get(name);
|
|
444
|
+
if (variable) {
|
|
445
|
+
for (const def of variable.defs) {
|
|
446
|
+
if (def.type === "CatchClause") {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
current = current.upper;
|
|
452
|
+
}
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
function isJsonStringify(callee) {
|
|
456
|
+
return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
|
|
457
|
+
}
|
|
458
|
+
var no_json_stringify_error_default = ESLintUtils5.RuleCreator(
|
|
459
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
460
|
+
)({
|
|
461
|
+
name: "no-json-stringify-error",
|
|
462
|
+
meta: {
|
|
463
|
+
type: "problem",
|
|
464
|
+
docs: {
|
|
465
|
+
description: "Disallow `JSON.stringify` on an Error value; it yields `{}` because `message`/`stack` are non-enumerable."
|
|
466
|
+
},
|
|
467
|
+
schema: [],
|
|
468
|
+
messages: {
|
|
469
|
+
noJsonStringifyError: "`JSON.stringify` on an Error yields `{}` because `message`/`stack` are non-enumerable. Log `err.message` / `err.stack`, or use a proper error serializer."
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
defaultOptions: [],
|
|
473
|
+
create(context) {
|
|
474
|
+
return {
|
|
475
|
+
CallExpression(node) {
|
|
476
|
+
if (!isJsonStringify(node.callee)) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const firstArg = node.arguments[0];
|
|
480
|
+
if (!firstArg || firstArg.type !== "Identifier") {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
const name = firstArg.name;
|
|
484
|
+
const scope = context.sourceCode.getScope(firstArg);
|
|
485
|
+
if (ERROR_NAME_PATTERN.test(name) || isCatchBinding(scope, name)) {
|
|
486
|
+
context.report({
|
|
487
|
+
node,
|
|
488
|
+
messageId: "noJsonStringifyError"
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// src/rules/no-log-only-catch.ts
|
|
497
|
+
import { ESLintUtils as ESLintUtils6 } from "@typescript-eslint/utils";
|
|
498
|
+
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
499
|
+
/\.test\./,
|
|
500
|
+
/\.spec\./,
|
|
501
|
+
/[\\/]__tests__[\\/]/
|
|
502
|
+
];
|
|
503
|
+
var CONSOLE_METHODS = /* @__PURE__ */ new Set([
|
|
504
|
+
"log",
|
|
505
|
+
"error",
|
|
506
|
+
"warn",
|
|
507
|
+
"info",
|
|
508
|
+
"debug"
|
|
509
|
+
]);
|
|
510
|
+
function isConsoleCallStatement(statement) {
|
|
511
|
+
if (statement.type !== "ExpressionStatement") {
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
const expr = statement.expression;
|
|
515
|
+
if (expr.type !== "CallExpression") {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
const callee = expr.callee;
|
|
519
|
+
if (callee.type !== "MemberExpression") {
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
const { object, property } = callee;
|
|
523
|
+
if (object.type !== "Identifier" || object.name !== "console") {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
if (callee.computed) {
|
|
527
|
+
return false;
|
|
528
|
+
}
|
|
529
|
+
if (property.type !== "Identifier") {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
return CONSOLE_METHODS.has(property.name);
|
|
533
|
+
}
|
|
534
|
+
var no_log_only_catch_default = ESLintUtils6.RuleCreator(
|
|
535
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
536
|
+
)({
|
|
537
|
+
name: "no-log-only-catch",
|
|
538
|
+
meta: {
|
|
539
|
+
type: "problem",
|
|
540
|
+
docs: {
|
|
541
|
+
description: "Disallow `catch` clauses that only log (or do nothing) and then swallow the error; rethrow or handle it instead."
|
|
542
|
+
},
|
|
543
|
+
schema: [],
|
|
544
|
+
messages: {
|
|
545
|
+
noLogOnlyCatch: "Logging then swallowing the error hides failures. Rethrow the error or handle it for real."
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
defaultOptions: [],
|
|
549
|
+
create(context) {
|
|
550
|
+
const filename = context.filename;
|
|
551
|
+
const isIgnoredByDefault = DEFAULT_IGNORE_PATTERNS2.some(
|
|
552
|
+
(re) => re.test(filename)
|
|
553
|
+
);
|
|
554
|
+
if (isIgnoredByDefault) {
|
|
555
|
+
return {};
|
|
556
|
+
}
|
|
557
|
+
return {
|
|
558
|
+
CatchClause(node) {
|
|
559
|
+
const statements = node.body.body;
|
|
560
|
+
if (statements.length === 0) {
|
|
561
|
+
context.report({ node, messageId: "noLogOnlyCatch" });
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const everyStatementIsConsoleLog = statements.every(
|
|
565
|
+
(statement) => isConsoleCallStatement(statement)
|
|
566
|
+
);
|
|
567
|
+
if (everyStatementIsConsoleLog) {
|
|
568
|
+
context.report({ node, messageId: "noLogOnlyCatch" });
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// src/rules/no-raw-env.ts
|
|
576
|
+
import { ESLintUtils as ESLintUtils7 } from "@typescript-eslint/utils";
|
|
577
|
+
var no_raw_env_default = ESLintUtils7.RuleCreator(
|
|
327
578
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
328
579
|
)({
|
|
329
580
|
name: "no-raw-env",
|
|
@@ -355,10 +606,305 @@ var no_raw_env_default = ESLintUtils4.RuleCreator(
|
|
|
355
606
|
}
|
|
356
607
|
});
|
|
357
608
|
|
|
609
|
+
// src/rules/no-sentinel-return-on-catch.ts
|
|
610
|
+
import {
|
|
611
|
+
ESLintUtils as ESLintUtils8,
|
|
612
|
+
AST_NODE_TYPES as AST_NODE_TYPES3
|
|
613
|
+
} from "@typescript-eslint/utils";
|
|
614
|
+
function isSentinelArgument(arg) {
|
|
615
|
+
if (arg === null) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
if (arg.type === AST_NODE_TYPES3.Literal && arg.value === null) {
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
if (arg.type === AST_NODE_TYPES3.Literal && arg.value === false) {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
if (arg.type === AST_NODE_TYPES3.Identifier && arg.name === "undefined") {
|
|
625
|
+
return true;
|
|
626
|
+
}
|
|
627
|
+
if (arg.type === AST_NODE_TYPES3.ArrayExpression && arg.elements.length === 0) {
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
if (arg.type === AST_NODE_TYPES3.ObjectExpression && arg.properties.length === 0) {
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
function containsThrow(node) {
|
|
636
|
+
let found = false;
|
|
637
|
+
const visit = (current) => {
|
|
638
|
+
if (found) {
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (current.type === AST_NODE_TYPES3.ThrowStatement) {
|
|
642
|
+
found = true;
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
if (current.type === AST_NODE_TYPES3.FunctionDeclaration || current.type === AST_NODE_TYPES3.FunctionExpression || current.type === AST_NODE_TYPES3.ArrowFunctionExpression) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
for (const key of Object.keys(current)) {
|
|
649
|
+
if (key === "parent") {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
const value = current[key];
|
|
653
|
+
if (Array.isArray(value)) {
|
|
654
|
+
for (const child of value) {
|
|
655
|
+
if (isNode(child)) {
|
|
656
|
+
visit(child);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
} else if (isNode(value)) {
|
|
660
|
+
visit(value);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
visit(node);
|
|
665
|
+
return found;
|
|
666
|
+
}
|
|
667
|
+
function isNode(value) {
|
|
668
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
669
|
+
}
|
|
670
|
+
var no_sentinel_return_on_catch_default = ESLintUtils8.RuleCreator(
|
|
671
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
672
|
+
)({
|
|
673
|
+
name: "no-sentinel-return-on-catch",
|
|
674
|
+
meta: {
|
|
675
|
+
type: "problem",
|
|
676
|
+
docs: {
|
|
677
|
+
description: "Disallow swallowing a caught error by returning an empty sentinel (`null`, `undefined`, `false`, `[]`, `{}`) as the final statement of a `catch` block."
|
|
678
|
+
},
|
|
679
|
+
schema: [],
|
|
680
|
+
messages: {
|
|
681
|
+
noSentinelReturn: "This `catch` block swallows the error by returning an empty sentinel. Rethrow it, return a typed Result, or handle the error explicitly."
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
defaultOptions: [],
|
|
685
|
+
create(context) {
|
|
686
|
+
return {
|
|
687
|
+
CatchClause(node) {
|
|
688
|
+
const body = node.body.body;
|
|
689
|
+
if (body.length === 0) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const last = body[body.length - 1];
|
|
693
|
+
if (last === void 0 || last.type !== AST_NODE_TYPES3.ReturnStatement) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (!isSentinelArgument(last.argument)) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (containsThrow(node.body)) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
context.report({
|
|
703
|
+
node: last,
|
|
704
|
+
messageId: "noSentinelReturn"
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
// src/rules/no-sequential-await.ts
|
|
712
|
+
import { ESLintUtils as ESLintUtils9 } from "@typescript-eslint/utils";
|
|
713
|
+
function isFunctionLike(node) {
|
|
714
|
+
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
715
|
+
}
|
|
716
|
+
function isLoop(node) {
|
|
717
|
+
return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
|
|
718
|
+
}
|
|
719
|
+
var no_sequential_await_default = ESLintUtils9.RuleCreator(
|
|
720
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
721
|
+
)({
|
|
722
|
+
name: "no-sequential-await",
|
|
723
|
+
meta: {
|
|
724
|
+
type: "problem",
|
|
725
|
+
docs: {
|
|
726
|
+
description: "Disallow serial `await` inside a loop; use `await Promise.all(...)` to run the operations concurrently."
|
|
727
|
+
},
|
|
728
|
+
schema: [],
|
|
729
|
+
messages: {
|
|
730
|
+
noSequentialAwait: "Avoid `await` inside a loop \u2014 it serializes I/O. Collect the promises and `await Promise.all(xs.map(async (x) => ...))` instead."
|
|
731
|
+
}
|
|
732
|
+
},
|
|
733
|
+
defaultOptions: [],
|
|
734
|
+
create(context) {
|
|
735
|
+
function findAwaitInScope(node) {
|
|
736
|
+
if (node.type === "AwaitExpression") {
|
|
737
|
+
return node;
|
|
738
|
+
}
|
|
739
|
+
if (isFunctionLike(node)) {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
for (const key of Object.keys(node)) {
|
|
743
|
+
if (key === "parent") {
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
const value = node[key];
|
|
747
|
+
if (Array.isArray(value)) {
|
|
748
|
+
for (const child of value) {
|
|
749
|
+
if (isNode2(child) && !isLoop(child)) {
|
|
750
|
+
const found = findAwaitInScope(child);
|
|
751
|
+
if (found) {
|
|
752
|
+
return found;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
} else if (isNode2(value) && !isLoop(value)) {
|
|
757
|
+
const found = findAwaitInScope(value);
|
|
758
|
+
if (found) {
|
|
759
|
+
return found;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return null;
|
|
764
|
+
}
|
|
765
|
+
function isNode2(value) {
|
|
766
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
767
|
+
}
|
|
768
|
+
function checkLoop(node) {
|
|
769
|
+
const parts = [node.body];
|
|
770
|
+
if (node.type === "ForStatement") {
|
|
771
|
+
parts.push(node.init, node.test, node.update);
|
|
772
|
+
} else if (node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
773
|
+
parts.push(node.right);
|
|
774
|
+
} else {
|
|
775
|
+
parts.push(node.test);
|
|
776
|
+
}
|
|
777
|
+
for (const part of parts) {
|
|
778
|
+
if (part && !isLoop(part) && findAwaitInScope(part)) {
|
|
779
|
+
context.report({ node, messageId: "noSequentialAwait" });
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return {
|
|
785
|
+
ForStatement: checkLoop,
|
|
786
|
+
ForInStatement: checkLoop,
|
|
787
|
+
WhileStatement: checkLoop,
|
|
788
|
+
DoWhileStatement: checkLoop,
|
|
789
|
+
ForOfStatement(node) {
|
|
790
|
+
if (node.await) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
checkLoop(node);
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
// src/rules/no-string-concat-in-loop.ts
|
|
800
|
+
import { ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
|
|
801
|
+
var LOOP_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
802
|
+
"ForStatement",
|
|
803
|
+
"ForOfStatement",
|
|
804
|
+
"ForInStatement",
|
|
805
|
+
"WhileStatement",
|
|
806
|
+
"DoWhileStatement"
|
|
807
|
+
]);
|
|
808
|
+
function isStringLiteralInit(node) {
|
|
809
|
+
if (node === null) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
if (node.type === "TemplateLiteral") {
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
if (node.type === "Literal") {
|
|
816
|
+
return typeof node.value === "string";
|
|
817
|
+
}
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
function findVariable(scope, name) {
|
|
821
|
+
let current = scope;
|
|
822
|
+
while (current !== null) {
|
|
823
|
+
const variable = current.variables.find((v) => v.name === name);
|
|
824
|
+
if (variable !== void 0) {
|
|
825
|
+
return variable;
|
|
826
|
+
}
|
|
827
|
+
current = current.upper;
|
|
828
|
+
}
|
|
829
|
+
return void 0;
|
|
830
|
+
}
|
|
831
|
+
function isStringInitializedVariable(variable) {
|
|
832
|
+
if (variable.defs.length !== 1) {
|
|
833
|
+
return false;
|
|
834
|
+
}
|
|
835
|
+
const def = variable.defs[0];
|
|
836
|
+
if (def === void 0 || def.type !== "Variable") {
|
|
837
|
+
return false;
|
|
838
|
+
}
|
|
839
|
+
const declarator = def.node;
|
|
840
|
+
if (declarator.type !== "VariableDeclarator") {
|
|
841
|
+
return false;
|
|
842
|
+
}
|
|
843
|
+
return isStringLiteralInit(declarator.init);
|
|
844
|
+
}
|
|
845
|
+
function isInsideLoopBody(node) {
|
|
846
|
+
let child = node;
|
|
847
|
+
let parent = node.parent;
|
|
848
|
+
while (parent !== void 0 && parent !== null) {
|
|
849
|
+
if (LOOP_NODE_TYPES.has(parent.type)) {
|
|
850
|
+
const loop = parent;
|
|
851
|
+
if (loop.body === child) {
|
|
852
|
+
return true;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
child = parent;
|
|
856
|
+
parent = parent.parent;
|
|
857
|
+
}
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
var no_string_concat_in_loop_default = ESLintUtils10.RuleCreator(
|
|
861
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
862
|
+
)({
|
|
863
|
+
name: "no-string-concat-in-loop",
|
|
864
|
+
meta: {
|
|
865
|
+
type: "suggestion",
|
|
866
|
+
docs: {
|
|
867
|
+
description: "Disallow O(n^2) string building via `+=` on a string variable inside a loop; push parts to an array and `join` instead."
|
|
868
|
+
},
|
|
869
|
+
schema: [],
|
|
870
|
+
messages: {
|
|
871
|
+
noStringConcatInLoop: 'Avoid building a string with `+=` inside a loop \u2014 this is O(n^2). Push the parts onto an array and use `arr.join("")` after the loop.'
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
defaultOptions: [],
|
|
875
|
+
create(context) {
|
|
876
|
+
return {
|
|
877
|
+
AssignmentExpression(node) {
|
|
878
|
+
if (node.operator !== "+=") {
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
if (node.left.type !== "Identifier") {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (!isInsideLoopBody(node)) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
const scope = context.sourceCode.getScope(node);
|
|
888
|
+
const variable = findVariable(scope, node.left.name);
|
|
889
|
+
if (variable === void 0) {
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
if (!isStringInitializedVariable(variable)) {
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
context.report({
|
|
896
|
+
node,
|
|
897
|
+
messageId: "noStringConcatInLoop"
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
|
|
358
904
|
// src/rules/no-unnecessary-use-client.ts
|
|
359
905
|
import {
|
|
360
|
-
AST_NODE_TYPES as
|
|
361
|
-
ESLintUtils as
|
|
906
|
+
AST_NODE_TYPES as AST_NODE_TYPES4,
|
|
907
|
+
ESLintUtils as ESLintUtils11
|
|
362
908
|
} from "@typescript-eslint/utils";
|
|
363
909
|
var HOOK_REGEX = /^use([A-Z]|$)/;
|
|
364
910
|
var EVENT_PROP_REGEX = /^on[A-Z]/;
|
|
@@ -382,16 +928,16 @@ var BROWSER_GLOBALS = /* @__PURE__ */ new Set([
|
|
|
382
928
|
]);
|
|
383
929
|
var CLIENT_ONLY_PACKAGES_REGEX = /^(?:@radix-ui\/|framer-motion|react-dom|react-day-picker|@floating-ui\/|react-select|react-toastify|react-hook-form|recharts|react-dropzone|react-slick|react-swipeable|react-resizable|react-draggable|react-beautiful-dnd|@hello-pangea\/dnd|react-virtualized|react-window|@tanstack\/react-table|@tanstack\/react-query|react-redux|recoil|jotai|zustand|@tippyjs\/react|react-color|react-datepicker|next-themes|react-helmet|react-helmet-async|styled-components|@emotion\/)/;
|
|
384
930
|
var isUseClientDirective = (node) => {
|
|
385
|
-
return node.type ===
|
|
931
|
+
return node.type === AST_NODE_TYPES4.ExpressionStatement && node.expression.type === AST_NODE_TYPES4.Literal && node.expression.value === "use client";
|
|
386
932
|
};
|
|
387
933
|
var isGlobalReference = (node, context) => {
|
|
388
934
|
if (!BROWSER_GLOBALS.has(node.name)) return false;
|
|
389
935
|
const parent = node.parent;
|
|
390
936
|
if (parent !== void 0) {
|
|
391
|
-
if (parent.type ===
|
|
937
|
+
if (parent.type === AST_NODE_TYPES4.MemberExpression && parent.property === node && !parent.computed) {
|
|
392
938
|
return false;
|
|
393
939
|
}
|
|
394
|
-
if (parent.type ===
|
|
940
|
+
if (parent.type === AST_NODE_TYPES4.Property && parent.key === node && !parent.computed) {
|
|
395
941
|
return false;
|
|
396
942
|
}
|
|
397
943
|
if (parent.type.startsWith("TS")) {
|
|
@@ -408,7 +954,7 @@ var isGlobalReference = (node, context) => {
|
|
|
408
954
|
}
|
|
409
955
|
return true;
|
|
410
956
|
};
|
|
411
|
-
var no_unnecessary_use_client_default =
|
|
957
|
+
var no_unnecessary_use_client_default = ESLintUtils11.RuleCreator(
|
|
412
958
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
413
959
|
)({
|
|
414
960
|
name: "no-unnecessary-use-client",
|
|
@@ -431,13 +977,13 @@ var no_unnecessary_use_client_default = ESLintUtils5.RuleCreator(
|
|
|
431
977
|
let directiveNode = null;
|
|
432
978
|
let hasClientIndicator = false;
|
|
433
979
|
const markIfHookOrContext = (callee) => {
|
|
434
|
-
if (callee.type ===
|
|
980
|
+
if (callee.type === AST_NODE_TYPES4.Identifier) {
|
|
435
981
|
if (HOOK_REGEX.test(callee.name) || callee.name === "createContext") {
|
|
436
982
|
hasClientIndicator = true;
|
|
437
983
|
}
|
|
438
984
|
return;
|
|
439
985
|
}
|
|
440
|
-
if (callee.type ===
|
|
986
|
+
if (callee.type === AST_NODE_TYPES4.MemberExpression && callee.property.type === AST_NODE_TYPES4.Identifier) {
|
|
441
987
|
const name = callee.property.name;
|
|
442
988
|
if (HOOK_REGEX.test(name) || name === "createContext") {
|
|
443
989
|
hasClientIndicator = true;
|
|
@@ -447,7 +993,7 @@ var no_unnecessary_use_client_default = ESLintUtils5.RuleCreator(
|
|
|
447
993
|
return {
|
|
448
994
|
Program(node) {
|
|
449
995
|
for (const stmt of node.body) {
|
|
450
|
-
if (stmt.type !==
|
|
996
|
+
if (stmt.type !== AST_NODE_TYPES4.ExpressionStatement) break;
|
|
451
997
|
if (isUseClientDirective(stmt)) {
|
|
452
998
|
directiveNode = stmt;
|
|
453
999
|
break;
|
|
@@ -458,7 +1004,7 @@ var no_unnecessary_use_client_default = ESLintUtils5.RuleCreator(
|
|
|
458
1004
|
markIfHookOrContext(node.callee);
|
|
459
1005
|
},
|
|
460
1006
|
JSXAttribute(node) {
|
|
461
|
-
if (node.name.type ===
|
|
1007
|
+
if (node.name.type === AST_NODE_TYPES4.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
|
|
462
1008
|
hasClientIndicator = true;
|
|
463
1009
|
}
|
|
464
1010
|
},
|
|
@@ -500,17 +1046,101 @@ var no_unnecessary_use_client_default = ESLintUtils5.RuleCreator(
|
|
|
500
1046
|
}
|
|
501
1047
|
});
|
|
502
1048
|
|
|
1049
|
+
// src/rules/prefer-discriminated-union.ts
|
|
1050
|
+
import { ESLintUtils as ESLintUtils12 } from "@typescript-eslint/utils";
|
|
1051
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES5 } from "@typescript-eslint/utils";
|
|
1052
|
+
var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
|
|
1053
|
+
"success",
|
|
1054
|
+
"ok",
|
|
1055
|
+
"error",
|
|
1056
|
+
"failed",
|
|
1057
|
+
"isError"
|
|
1058
|
+
]);
|
|
1059
|
+
var MIN_OPTIONAL_MEMBERS = 2;
|
|
1060
|
+
function getMemberName(member) {
|
|
1061
|
+
if (member.type !== AST_NODE_TYPES5.TSPropertySignature) {
|
|
1062
|
+
return null;
|
|
1063
|
+
}
|
|
1064
|
+
const { key } = member;
|
|
1065
|
+
if (key.type === AST_NODE_TYPES5.Identifier) {
|
|
1066
|
+
return key.name;
|
|
1067
|
+
}
|
|
1068
|
+
if (key.type === AST_NODE_TYPES5.Literal && typeof key.value === "string") {
|
|
1069
|
+
return key.value;
|
|
1070
|
+
}
|
|
1071
|
+
return null;
|
|
1072
|
+
}
|
|
1073
|
+
function isBooleanTyped(member) {
|
|
1074
|
+
return member.typeAnnotation?.typeAnnotation.type === AST_NODE_TYPES5.TSBooleanKeyword;
|
|
1075
|
+
}
|
|
1076
|
+
function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
1077
|
+
let hasStatusBoolean = false;
|
|
1078
|
+
let optionalCount = 0;
|
|
1079
|
+
for (const member of typeLiteral.members) {
|
|
1080
|
+
if (member.type !== AST_NODE_TYPES5.TSPropertySignature) {
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
if (member.optional) {
|
|
1084
|
+
optionalCount += 1;
|
|
1085
|
+
}
|
|
1086
|
+
const name = getMemberName(member);
|
|
1087
|
+
if (name !== null && STATUS_MEMBER_NAMES.has(name) && isBooleanTyped(member)) {
|
|
1088
|
+
hasStatusBoolean = true;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
|
|
1092
|
+
}
|
|
1093
|
+
var prefer_discriminated_union_default = ESLintUtils12.RuleCreator(
|
|
1094
|
+
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1095
|
+
)({
|
|
1096
|
+
name: "prefer-discriminated-union",
|
|
1097
|
+
meta: {
|
|
1098
|
+
type: "suggestion",
|
|
1099
|
+
docs: {
|
|
1100
|
+
description: "Flag object types with a boolean status flag and many optionals; model them as a discriminated union instead."
|
|
1101
|
+
},
|
|
1102
|
+
schema: [],
|
|
1103
|
+
messages: {
|
|
1104
|
+
preferDiscriminatedUnion: "This object type uses a boolean status flag alongside several optional fields, which lets illegal states be representable. Model it as a `z.discriminatedUnion` / discriminated union (e.g. `{ ok: true; data: T } | { ok: false; error: E }`) to make illegal states unrepresentable."
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
defaultOptions: [],
|
|
1108
|
+
create(context) {
|
|
1109
|
+
function checkTypeLiteral(typeLiteral, reportNode) {
|
|
1110
|
+
if (looksLikeMutuallyExclusiveState(typeLiteral)) {
|
|
1111
|
+
context.report({
|
|
1112
|
+
node: reportNode,
|
|
1113
|
+
messageId: "preferDiscriminatedUnion"
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return {
|
|
1118
|
+
TSInterfaceDeclaration(node) {
|
|
1119
|
+
const synthetic = {
|
|
1120
|
+
...node.body,
|
|
1121
|
+
type: AST_NODE_TYPES5.TSTypeLiteral,
|
|
1122
|
+
members: node.body.body
|
|
1123
|
+
};
|
|
1124
|
+
checkTypeLiteral(synthetic, node);
|
|
1125
|
+
},
|
|
1126
|
+
"TSTypeAliasDeclaration > TSTypeLiteral"(node) {
|
|
1127
|
+
checkTypeLiteral(node, node.parent);
|
|
1128
|
+
}
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
|
|
503
1133
|
// src/rules/prefer-schema-for-api-payload.ts
|
|
504
1134
|
import {
|
|
505
|
-
AST_NODE_TYPES as
|
|
506
|
-
ESLintUtils as
|
|
1135
|
+
AST_NODE_TYPES as AST_NODE_TYPES6,
|
|
1136
|
+
ESLintUtils as ESLintUtils13
|
|
507
1137
|
} from "@typescript-eslint/utils";
|
|
508
1138
|
var unwrap = (node) => {
|
|
509
1139
|
let current = node;
|
|
510
1140
|
while (current !== null && current !== void 0) {
|
|
511
|
-
if (current.type ===
|
|
1141
|
+
if (current.type === AST_NODE_TYPES6.TSAsExpression || current.type === AST_NODE_TYPES6.TSTypeAssertion || current.type === AST_NODE_TYPES6.TSNonNullExpression || current.type === AST_NODE_TYPES6.TSSatisfiesExpression) {
|
|
512
1142
|
current = current.expression;
|
|
513
|
-
} else if (current.type ===
|
|
1143
|
+
} else if (current.type === AST_NODE_TYPES6.ChainExpression) {
|
|
514
1144
|
current = current.expression;
|
|
515
1145
|
} else {
|
|
516
1146
|
break;
|
|
@@ -521,20 +1151,20 @@ var unwrap = (node) => {
|
|
|
521
1151
|
var isJsonCall = (node) => {
|
|
522
1152
|
let current = unwrap(node);
|
|
523
1153
|
if (current === null) return false;
|
|
524
|
-
if (current.type ===
|
|
1154
|
+
if (current.type === AST_NODE_TYPES6.AwaitExpression) {
|
|
525
1155
|
current = unwrap(current.argument);
|
|
526
1156
|
}
|
|
527
|
-
if (current === null || current.type !==
|
|
1157
|
+
if (current === null || current.type !== AST_NODE_TYPES6.CallExpression) {
|
|
528
1158
|
return false;
|
|
529
1159
|
}
|
|
530
1160
|
const callee = unwrap(current.callee);
|
|
531
|
-
if (callee === null || callee.type !==
|
|
1161
|
+
if (callee === null || callee.type !== AST_NODE_TYPES6.MemberExpression) {
|
|
532
1162
|
return false;
|
|
533
1163
|
}
|
|
534
1164
|
const property = unwrap(callee.property);
|
|
535
|
-
return property !== null && property.type ===
|
|
1165
|
+
return property !== null && property.type === AST_NODE_TYPES6.Identifier && property.name === "json";
|
|
536
1166
|
};
|
|
537
|
-
var
|
|
1167
|
+
var findVariable2 = (scope, name) => {
|
|
538
1168
|
let current = scope;
|
|
539
1169
|
while (current !== null) {
|
|
540
1170
|
const variable = current.set.get(name);
|
|
@@ -545,13 +1175,13 @@ var findVariable = (scope, name) => {
|
|
|
545
1175
|
};
|
|
546
1176
|
var isUnvalidatedVariableRef = (node, scope, tracked) => {
|
|
547
1177
|
const unwrapped = unwrap(node);
|
|
548
|
-
if (unwrapped === null || unwrapped.type !==
|
|
1178
|
+
if (unwrapped === null || unwrapped.type !== AST_NODE_TYPES6.Identifier) {
|
|
549
1179
|
return false;
|
|
550
1180
|
}
|
|
551
|
-
const variable =
|
|
1181
|
+
const variable = findVariable2(scope, unwrapped.name);
|
|
552
1182
|
return variable !== null && tracked.has(variable);
|
|
553
1183
|
};
|
|
554
|
-
var prefer_schema_for_api_payload_default =
|
|
1184
|
+
var prefer_schema_for_api_payload_default = ESLintUtils13.RuleCreator(
|
|
555
1185
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
556
1186
|
)({
|
|
557
1187
|
name: "prefer-schema-for-api-payload",
|
|
@@ -579,11 +1209,11 @@ var prefer_schema_for_api_payload_default = ESLintUtils6.RuleCreator(
|
|
|
579
1209
|
return {
|
|
580
1210
|
VariableDeclarator(node) {
|
|
581
1211
|
const scope = context.sourceCode.getScope(node);
|
|
582
|
-
if (node.id.type ===
|
|
1212
|
+
if (node.id.type === AST_NODE_TYPES6.Identifier) {
|
|
583
1213
|
trackInitializer(node);
|
|
584
1214
|
return;
|
|
585
1215
|
}
|
|
586
|
-
if (node.id.type ===
|
|
1216
|
+
if (node.id.type === AST_NODE_TYPES6.ObjectPattern || node.id.type === AST_NODE_TYPES6.ArrayPattern) {
|
|
587
1217
|
if (isJsonCall(node.init)) {
|
|
588
1218
|
context.report({ node: node.id, messageId: "unparsedJsonAccess" });
|
|
589
1219
|
return;
|
|
@@ -595,8 +1225,8 @@ var prefer_schema_for_api_payload_default = ESLintUtils6.RuleCreator(
|
|
|
595
1225
|
},
|
|
596
1226
|
AssignmentExpression(node) {
|
|
597
1227
|
const scope = context.sourceCode.getScope(node);
|
|
598
|
-
if (node.left.type ===
|
|
599
|
-
const variable =
|
|
1228
|
+
if (node.left.type === AST_NODE_TYPES6.Identifier) {
|
|
1229
|
+
const variable = findVariable2(scope, node.left.name);
|
|
600
1230
|
if (variable === null) return;
|
|
601
1231
|
if (isJsonCall(node.right)) {
|
|
602
1232
|
unvalidatedVariables.add(variable);
|
|
@@ -605,7 +1235,7 @@ var prefer_schema_for_api_payload_default = ESLintUtils6.RuleCreator(
|
|
|
605
1235
|
}
|
|
606
1236
|
return;
|
|
607
1237
|
}
|
|
608
|
-
if (node.left.type ===
|
|
1238
|
+
if (node.left.type === AST_NODE_TYPES6.ObjectPattern || node.left.type === AST_NODE_TYPES6.ArrayPattern) {
|
|
609
1239
|
if (isJsonCall(node.right)) {
|
|
610
1240
|
context.report({
|
|
611
1241
|
node: node.left,
|
|
@@ -626,15 +1256,15 @@ var prefer_schema_for_api_payload_default = ESLintUtils6.RuleCreator(
|
|
|
626
1256
|
const obj = unwrap(node.object);
|
|
627
1257
|
if (isJsonCall(obj)) {
|
|
628
1258
|
const parent = node.parent;
|
|
629
|
-
if (parent.type ===
|
|
1259
|
+
if (parent.type === AST_NODE_TYPES6.CallExpression && parent.callee === node && node.property.type === AST_NODE_TYPES6.Identifier && (node.property.name === "parse" || node.property.name === "safeParse")) {
|
|
630
1260
|
return;
|
|
631
1261
|
}
|
|
632
1262
|
context.report({ node, messageId: "unparsedJsonAccess" });
|
|
633
1263
|
return;
|
|
634
1264
|
}
|
|
635
|
-
if (obj !== null && obj.type ===
|
|
1265
|
+
if (obj !== null && obj.type === AST_NODE_TYPES6.Identifier && isUnvalidatedVariableRef(obj, scope, unvalidatedVariables)) {
|
|
636
1266
|
context.report({ node, messageId: "unparsedJsonAccess" });
|
|
637
|
-
const variable =
|
|
1267
|
+
const variable = findVariable2(scope, obj.name);
|
|
638
1268
|
if (variable !== null) {
|
|
639
1269
|
unvalidatedVariables.delete(variable);
|
|
640
1270
|
}
|
|
@@ -645,7 +1275,7 @@ var prefer_schema_for_api_payload_default = ESLintUtils6.RuleCreator(
|
|
|
645
1275
|
});
|
|
646
1276
|
|
|
647
1277
|
// src/rules/prefer-server-actions.ts
|
|
648
|
-
import { ESLintUtils as
|
|
1278
|
+
import { ESLintUtils as ESLintUtils14 } from "@typescript-eslint/utils";
|
|
649
1279
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
650
1280
|
var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
|
|
651
1281
|
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
@@ -724,7 +1354,7 @@ function getPropertyNode(objNode, propName) {
|
|
|
724
1354
|
}
|
|
725
1355
|
return null;
|
|
726
1356
|
}
|
|
727
|
-
var prefer_server_actions_default =
|
|
1357
|
+
var prefer_server_actions_default = ESLintUtils14.RuleCreator(
|
|
728
1358
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
729
1359
|
)({
|
|
730
1360
|
name: "prefer-server-actions",
|
|
@@ -789,14 +1419,14 @@ var prefer_server_actions_default = ESLintUtils7.RuleCreator(
|
|
|
789
1419
|
});
|
|
790
1420
|
|
|
791
1421
|
// src/rules/prefer-shadcn.ts
|
|
792
|
-
import { ESLintUtils as
|
|
1422
|
+
import { ESLintUtils as ESLintUtils15 } from "@typescript-eslint/utils";
|
|
793
1423
|
var REPLACEMENTS = {
|
|
794
1424
|
input: "Input",
|
|
795
1425
|
select: "Select",
|
|
796
1426
|
textarea: "Textarea",
|
|
797
1427
|
dialog: "Dialog"
|
|
798
1428
|
};
|
|
799
|
-
var prefer_shadcn_default =
|
|
1429
|
+
var prefer_shadcn_default = ESLintUtils15.RuleCreator(
|
|
800
1430
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
801
1431
|
)({
|
|
802
1432
|
name: "prefer-shadcn",
|
|
@@ -837,25 +1467,25 @@ var prefer_shadcn_default = ESLintUtils8.RuleCreator(
|
|
|
837
1467
|
});
|
|
838
1468
|
|
|
839
1469
|
// src/rules/require-assert-never.ts
|
|
840
|
-
import { ESLintUtils as
|
|
1470
|
+
import { ESLintUtils as ESLintUtils16, AST_NODE_TYPES as AST_NODE_TYPES7 } from "@typescript-eslint/utils";
|
|
841
1471
|
var isAssertNeverCall = (expression) => {
|
|
842
|
-
if (expression.type !==
|
|
1472
|
+
if (expression.type !== AST_NODE_TYPES7.CallExpression) return false;
|
|
843
1473
|
const callee = expression.callee;
|
|
844
|
-
return callee.type ===
|
|
1474
|
+
return callee.type === AST_NODE_TYPES7.Identifier && callee.name === "assertNever";
|
|
845
1475
|
};
|
|
846
1476
|
var statementContainsAssertNever = (statement) => {
|
|
847
|
-
if (statement.type ===
|
|
1477
|
+
if (statement.type === AST_NODE_TYPES7.ExpressionStatement) {
|
|
848
1478
|
return isAssertNeverCall(statement.expression);
|
|
849
1479
|
}
|
|
850
|
-
if (statement.type ===
|
|
1480
|
+
if (statement.type === AST_NODE_TYPES7.ThrowStatement) {
|
|
851
1481
|
return isAssertNeverCall(statement.argument);
|
|
852
1482
|
}
|
|
853
|
-
if (statement.type ===
|
|
1483
|
+
if (statement.type === AST_NODE_TYPES7.BlockStatement) {
|
|
854
1484
|
return statement.body.some(statementContainsAssertNever);
|
|
855
1485
|
}
|
|
856
1486
|
return false;
|
|
857
1487
|
};
|
|
858
|
-
var require_assert_never_default =
|
|
1488
|
+
var require_assert_never_default = ESLintUtils16.RuleCreator(
|
|
859
1489
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
860
1490
|
)({
|
|
861
1491
|
name: "require-assert-never",
|
|
@@ -891,22 +1521,22 @@ var require_assert_never_default = ESLintUtils9.RuleCreator(
|
|
|
891
1521
|
});
|
|
892
1522
|
|
|
893
1523
|
// src/rules/require-zod-form-validation.ts
|
|
894
|
-
import { ESLintUtils as
|
|
1524
|
+
import { ESLintUtils as ESLintUtils17, AST_NODE_TYPES as AST_NODE_TYPES8 } from "@typescript-eslint/utils";
|
|
895
1525
|
var isFormDataGetCall = (node) => {
|
|
896
1526
|
const callee = node.callee;
|
|
897
|
-
if (callee.type !==
|
|
898
|
-
if (callee.property.type !==
|
|
1527
|
+
if (callee.type !== AST_NODE_TYPES8.MemberExpression) return false;
|
|
1528
|
+
if (callee.property.type !== AST_NODE_TYPES8.Identifier || callee.property.name !== "get") {
|
|
899
1529
|
return false;
|
|
900
1530
|
}
|
|
901
|
-
return callee.object.type ===
|
|
1531
|
+
return callee.object.type === AST_NODE_TYPES8.Identifier && callee.object.name === "formData";
|
|
902
1532
|
};
|
|
903
1533
|
var isParseCallExpression = (node) => {
|
|
904
|
-
if (node.type !==
|
|
1534
|
+
if (node.type !== AST_NODE_TYPES8.CallExpression) return false;
|
|
905
1535
|
const callee = node.callee;
|
|
906
|
-
if (callee.type !==
|
|
907
|
-
return callee.property.type ===
|
|
1536
|
+
if (callee.type !== AST_NODE_TYPES8.MemberExpression) return false;
|
|
1537
|
+
return callee.property.type === AST_NODE_TYPES8.Identifier && callee.property.name === "parse";
|
|
908
1538
|
};
|
|
909
|
-
var require_zod_form_validation_default =
|
|
1539
|
+
var require_zod_form_validation_default = ESLintUtils17.RuleCreator(
|
|
910
1540
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
911
1541
|
)({
|
|
912
1542
|
name: "require-zod-form-validation",
|
|
@@ -940,15 +1570,15 @@ var require_zod_form_validation_default = ESLintUtils10.RuleCreator(
|
|
|
940
1570
|
});
|
|
941
1571
|
|
|
942
1572
|
// src/rules/zod-naming-convention.ts
|
|
943
|
-
import { ESLintUtils as
|
|
1573
|
+
import { ESLintUtils as ESLintUtils18, AST_NODE_TYPES as AST_NODE_TYPES9 } from "@typescript-eslint/utils";
|
|
944
1574
|
var calleeChainStartsWithZ = (node) => {
|
|
945
1575
|
let current = node;
|
|
946
|
-
while (current.type ===
|
|
1576
|
+
while (current.type === AST_NODE_TYPES9.MemberExpression) {
|
|
947
1577
|
const receiver = current.object;
|
|
948
|
-
if (receiver.type ===
|
|
1578
|
+
if (receiver.type === AST_NODE_TYPES9.Identifier && receiver.name === "z") {
|
|
949
1579
|
return true;
|
|
950
1580
|
}
|
|
951
|
-
if (receiver.type ===
|
|
1581
|
+
if (receiver.type === AST_NODE_TYPES9.CallExpression) {
|
|
952
1582
|
current = receiver.callee;
|
|
953
1583
|
continue;
|
|
954
1584
|
}
|
|
@@ -956,7 +1586,7 @@ var calleeChainStartsWithZ = (node) => {
|
|
|
956
1586
|
}
|
|
957
1587
|
return false;
|
|
958
1588
|
};
|
|
959
|
-
var zod_naming_convention_default =
|
|
1589
|
+
var zod_naming_convention_default = ESLintUtils18.RuleCreator(
|
|
960
1590
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
961
1591
|
)({
|
|
962
1592
|
name: "zod-naming-convention",
|
|
@@ -976,11 +1606,11 @@ var zod_naming_convention_default = ESLintUtils11.RuleCreator(
|
|
|
976
1606
|
VariableDeclarator(node) {
|
|
977
1607
|
const init = node.init;
|
|
978
1608
|
if (init === null || init === void 0) return;
|
|
979
|
-
if (init.type !==
|
|
1609
|
+
if (init.type !== AST_NODE_TYPES9.CallExpression) return;
|
|
980
1610
|
const callee = init.callee;
|
|
981
|
-
if (callee.type !==
|
|
1611
|
+
if (callee.type !== AST_NODE_TYPES9.MemberExpression) return;
|
|
982
1612
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
983
|
-
if (node.id.type !==
|
|
1613
|
+
if (node.id.type !== AST_NODE_TYPES9.Identifier) return;
|
|
984
1614
|
const variableName = node.id.name;
|
|
985
1615
|
if (variableName.startsWith("Z")) return;
|
|
986
1616
|
context.report({
|
|
@@ -997,8 +1627,15 @@ var rules = {
|
|
|
997
1627
|
"enforce-file-structure": enforce_file_structure_default,
|
|
998
1628
|
"no-client-side-data-fetching": no_client_side_data_fetching_default,
|
|
999
1629
|
"no-enum": no_enum_default,
|
|
1630
|
+
"no-insecure-random-id": no_insecure_random_id_default,
|
|
1631
|
+
"no-json-stringify-error": no_json_stringify_error_default,
|
|
1632
|
+
"no-log-only-catch": no_log_only_catch_default,
|
|
1000
1633
|
"no-raw-env": no_raw_env_default,
|
|
1634
|
+
"no-sentinel-return-on-catch": no_sentinel_return_on_catch_default,
|
|
1635
|
+
"no-sequential-await": no_sequential_await_default,
|
|
1636
|
+
"no-string-concat-in-loop": no_string_concat_in_loop_default,
|
|
1001
1637
|
"no-unnecessary-use-client": no_unnecessary_use_client_default,
|
|
1638
|
+
"prefer-discriminated-union": prefer_discriminated_union_default,
|
|
1002
1639
|
"prefer-schema-for-api-payload": prefer_schema_for_api_payload_default,
|
|
1003
1640
|
"prefer-server-actions": prefer_server_actions_default,
|
|
1004
1641
|
"prefer-shadcn": prefer_shadcn_default,
|
|
@@ -1009,7 +1646,7 @@ var rules = {
|
|
|
1009
1646
|
var plugin = {
|
|
1010
1647
|
meta: {
|
|
1011
1648
|
name: "@sarj/eslint-plugin",
|
|
1012
|
-
version: "2.
|
|
1649
|
+
version: "2.1.1"
|
|
1013
1650
|
},
|
|
1014
1651
|
rules,
|
|
1015
1652
|
configs: {
|
|
@@ -1023,7 +1660,15 @@ var plugin = {
|
|
|
1023
1660
|
"@sarj/no-client-side-data-fetching": "warn",
|
|
1024
1661
|
"@sarj/prefer-server-actions": "warn",
|
|
1025
1662
|
"@sarj/no-unnecessary-use-client": "warn",
|
|
1026
|
-
"@sarj/prefer-schema-for-api-payload": "warn"
|
|
1663
|
+
"@sarj/prefer-schema-for-api-payload": "warn",
|
|
1664
|
+
// Distilled from sarj-audit skills — warn in recommended, error in strict.
|
|
1665
|
+
"@sarj/no-sequential-await": "warn",
|
|
1666
|
+
"@sarj/no-sentinel-return-on-catch": "warn",
|
|
1667
|
+
"@sarj/no-log-only-catch": "warn",
|
|
1668
|
+
"@sarj/no-insecure-random-id": "warn",
|
|
1669
|
+
"@sarj/no-json-stringify-error": "warn",
|
|
1670
|
+
"@sarj/no-string-concat-in-loop": "warn",
|
|
1671
|
+
"@sarj/prefer-discriminated-union": "warn"
|
|
1027
1672
|
}
|
|
1028
1673
|
},
|
|
1029
1674
|
strict: {
|
|
@@ -1039,7 +1684,15 @@ var plugin = {
|
|
|
1039
1684
|
"@sarj/no-client-side-data-fetching": "error",
|
|
1040
1685
|
"@sarj/prefer-server-actions": "error",
|
|
1041
1686
|
"@sarj/no-unnecessary-use-client": "error",
|
|
1042
|
-
"@sarj/prefer-schema-for-api-payload": "error"
|
|
1687
|
+
"@sarj/prefer-schema-for-api-payload": "error",
|
|
1688
|
+
// Distilled from sarj-audit skills.
|
|
1689
|
+
"@sarj/no-sequential-await": "error",
|
|
1690
|
+
"@sarj/no-sentinel-return-on-catch": "error",
|
|
1691
|
+
"@sarj/no-log-only-catch": "error",
|
|
1692
|
+
"@sarj/no-insecure-random-id": "error",
|
|
1693
|
+
"@sarj/no-json-stringify-error": "error",
|
|
1694
|
+
"@sarj/no-string-concat-in-loop": "error",
|
|
1695
|
+
"@sarj/prefer-discriminated-union": "error"
|
|
1043
1696
|
}
|
|
1044
1697
|
}
|
|
1045
1698
|
}
|