@sarj/eslint-plugin 2.1.1 → 2.2.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 +444 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +410 -102
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,45 +1,30 @@
|
|
|
1
1
|
// src/rules/enforce-file-structure.ts
|
|
2
2
|
import { ESLintUtils, AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
3
3
|
var SECTION = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
functions: 3,
|
|
8
|
-
exports: 4
|
|
4
|
+
declarations: 0,
|
|
5
|
+
functions: 1,
|
|
6
|
+
exports: 2
|
|
9
7
|
};
|
|
10
|
-
var SECTION_NAMES = [
|
|
11
|
-
"imports",
|
|
12
|
-
"types",
|
|
13
|
-
"constants",
|
|
14
|
-
"functions",
|
|
15
|
-
"exports"
|
|
16
|
-
];
|
|
8
|
+
var SECTION_NAMES = ["declarations", "functions", "exports"];
|
|
17
9
|
var sectionName = (ordinal) => {
|
|
18
10
|
const name = SECTION_NAMES[ordinal];
|
|
19
11
|
return name ?? "unknown";
|
|
20
12
|
};
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
var SERVER_ACTION_FILE_RE = /(?:^|\/)actions\/|\.action\.[jt]sx?$|(?:^|\/)actions\.[jt]sx?$/;
|
|
14
|
+
var isFunctionExpression = (node) => node.type === AST_NODE_TYPES.ArrowFunctionExpression || node.type === AST_NODE_TYPES.FunctionExpression;
|
|
15
|
+
var isFunctionLikeVariable = (statement) => statement.declarations.length > 0 && statement.declarations.every(
|
|
16
|
+
(decl) => decl.init !== null && isFunctionExpression(decl.init)
|
|
17
|
+
);
|
|
26
18
|
var getStatementSection = (statement) => {
|
|
27
19
|
switch (statement.type) {
|
|
28
20
|
case AST_NODE_TYPES.ImportDeclaration:
|
|
29
|
-
return SECTION.imports;
|
|
30
21
|
case AST_NODE_TYPES.TSTypeAliasDeclaration:
|
|
31
22
|
case AST_NODE_TYPES.TSInterfaceDeclaration:
|
|
32
23
|
case AST_NODE_TYPES.TSEnumDeclaration:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (firstDeclarator !== void 0 && isConstantNamed(firstDeclarator)) {
|
|
38
|
-
return SECTION.constants;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return SECTION.functions;
|
|
42
|
-
}
|
|
24
|
+
case AST_NODE_TYPES.ClassDeclaration:
|
|
25
|
+
return SECTION.declarations;
|
|
26
|
+
case AST_NODE_TYPES.VariableDeclaration:
|
|
27
|
+
return isFunctionLikeVariable(statement) ? SECTION.functions : SECTION.declarations;
|
|
43
28
|
case AST_NODE_TYPES.FunctionDeclaration:
|
|
44
29
|
return SECTION.functions;
|
|
45
30
|
case AST_NODE_TYPES.ExportNamedDeclaration:
|
|
@@ -64,7 +49,7 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
64
49
|
meta: {
|
|
65
50
|
type: "suggestion",
|
|
66
51
|
docs: {
|
|
67
|
-
description: "Enforce
|
|
52
|
+
description: "Enforce that function definitions follow the file's top-of-file declarations (imports, types, constants, classes) \u2014 the stepdown rule. Ordering among non-function declarations is not enforced. Server-action files (under `/actions/`, named `*.action.ts`, or `actions.ts`) must also begin with a `use server` directive."
|
|
68
53
|
},
|
|
69
54
|
schema: [],
|
|
70
55
|
messages: {
|
|
@@ -75,7 +60,7 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
75
60
|
defaultOptions: [],
|
|
76
61
|
create(context) {
|
|
77
62
|
const filename = context.filename;
|
|
78
|
-
const isServerAction =
|
|
63
|
+
const isServerAction = SERVER_ACTION_FILE_RE.test(filename);
|
|
79
64
|
return {
|
|
80
65
|
Program(node) {
|
|
81
66
|
const body = node.body;
|
|
@@ -88,9 +73,8 @@ var enforce_file_structure_default = ESLintUtils.RuleCreator(
|
|
|
88
73
|
});
|
|
89
74
|
}
|
|
90
75
|
}
|
|
91
|
-
let currentSection = SECTION.
|
|
76
|
+
let currentSection = SECTION.declarations;
|
|
92
77
|
for (const statement of body) {
|
|
93
|
-
if (isUseServerDirective(statement)) continue;
|
|
94
78
|
if (statement.type === AST_NODE_TYPES.ExpressionStatement && statement.expression.type === AST_NODE_TYPES.Literal && typeof statement.expression.value === "string" && statement.expression.value.startsWith("use ")) {
|
|
95
79
|
continue;
|
|
96
80
|
}
|
|
@@ -129,7 +113,7 @@ var HTTP_METHOD_NAMES = /* @__PURE__ */ new Set([
|
|
|
129
113
|
"head",
|
|
130
114
|
"options"
|
|
131
115
|
]);
|
|
132
|
-
var
|
|
116
|
+
var ANALYTICS_SEGMENTS = /* @__PURE__ */ new Set([
|
|
133
117
|
"analytics",
|
|
134
118
|
"telemetry",
|
|
135
119
|
"track",
|
|
@@ -138,7 +122,7 @@ var ANALYTICS_KEYWORDS = [
|
|
|
138
122
|
"beacon",
|
|
139
123
|
"metrics",
|
|
140
124
|
"event"
|
|
141
|
-
];
|
|
125
|
+
]);
|
|
142
126
|
function isEffectHookCall(node) {
|
|
143
127
|
const callee = node.callee;
|
|
144
128
|
if (callee.type === AST_NODE_TYPES2.Identifier) {
|
|
@@ -212,7 +196,7 @@ function extractUrlString(node) {
|
|
|
212
196
|
function isAnalyticsCall(node) {
|
|
213
197
|
const url = extractUrlString(node).toLowerCase();
|
|
214
198
|
if (url === "") return false;
|
|
215
|
-
return
|
|
199
|
+
return url.split(/[/.]/).some((segment) => ANALYTICS_SEGMENTS.has(segment));
|
|
216
200
|
}
|
|
217
201
|
var no_client_side_data_fetching_default = ESLintUtils2.RuleCreator(
|
|
218
202
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
@@ -251,8 +235,105 @@ var no_client_side_data_fetching_default = ESLintUtils2.RuleCreator(
|
|
|
251
235
|
}
|
|
252
236
|
});
|
|
253
237
|
|
|
254
|
-
// src/rules/no-
|
|
238
|
+
// src/rules/no-comment-cruft.ts
|
|
255
239
|
import { ESLintUtils as ESLintUtils3 } from "@typescript-eslint/utils";
|
|
240
|
+
var LEADING_PREAMBLE_MIN = 4;
|
|
241
|
+
var DIRECTIVE_RE = /^(eslint\b|eslint-|@ts-|prettier-ignore|prettier\b|biome-|c8\b|v8\b|istanbul\b|@type\b|@vite|webpack|<reference|global\b|noinspection|todo\b|fixme\b|hack\b|xxx\b)/i;
|
|
242
|
+
var LICENSE_RE = /copyright|licen[cs]ed?|spdx|permission is hereby granted|all rights reserved/i;
|
|
243
|
+
var BANNER_FULL_RE = /^[\s\-=*#~_+.]{4,}$/;
|
|
244
|
+
var BANNER_RUN_RE = /={4,}|-{4,}|#{4,}|\*{4,}|~{4,}/;
|
|
245
|
+
var REGION_RE = /^#?(?:end)?region\b/i;
|
|
246
|
+
var CODE_KEYWORD_RE = /^(import |export |const |let |var |function\b|class |interface |type \w|enum |return\b|throw |await |async |if\s*\(|for\s*\(|while\s*\(|switch\s*\(|new |console\.)/;
|
|
247
|
+
var CODE_TAIL_RE = /[;{}()]\s*$|=>\s*$|,\s*$/;
|
|
248
|
+
var CALL_OR_ASSIGN_RE = /^[A-Za-z_$][\w.$[\]]*\s*(?:=(?![=>])|\+=|-=|\*=)\s*\S.*[;)}\]]\s*$|^[A-Za-z_$][\w.$]*\([^)]*\)\s*;?\s*$/;
|
|
249
|
+
function stripCommentMarker(line) {
|
|
250
|
+
return line.replace(/^\s*\/\//, "").replace(/^\s*\*+/, "").trim();
|
|
251
|
+
}
|
|
252
|
+
function isDirective(text) {
|
|
253
|
+
return DIRECTIVE_RE.test(text.trim());
|
|
254
|
+
}
|
|
255
|
+
function isBanner(text) {
|
|
256
|
+
const t = text.trim();
|
|
257
|
+
if (!t) return false;
|
|
258
|
+
return BANNER_FULL_RE.test(t) || BANNER_RUN_RE.test(t) || REGION_RE.test(t);
|
|
259
|
+
}
|
|
260
|
+
function looksLikeCode(text) {
|
|
261
|
+
const t = text.trim();
|
|
262
|
+
if (!t) return false;
|
|
263
|
+
if (CODE_KEYWORD_RE.test(t) && CODE_TAIL_RE.test(t)) return true;
|
|
264
|
+
return CALL_OR_ASSIGN_RE.test(t);
|
|
265
|
+
}
|
|
266
|
+
var no_comment_cruft_default = ESLintUtils3.RuleCreator(
|
|
267
|
+
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
268
|
+
)({
|
|
269
|
+
name: "no-comment-cruft",
|
|
270
|
+
meta: {
|
|
271
|
+
type: "suggestion",
|
|
272
|
+
docs: {
|
|
273
|
+
description: "Flag commented-out code, section-banner comments, and leading file-header comment preambles."
|
|
274
|
+
},
|
|
275
|
+
schema: [],
|
|
276
|
+
messages: {
|
|
277
|
+
commentedOutCode: "Commented-out code \u2014 delete it; git history remembers.",
|
|
278
|
+
sectionBanner: "Section-banner / region comment \u2014 structure code with functions, not ASCII rules.",
|
|
279
|
+
fileHeaderPreamble: "File-header comment preamble \u2014 use a brief doc comment for the why, not a block of `//` lines."
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
defaultOptions: [],
|
|
283
|
+
create(context) {
|
|
284
|
+
const sourceCode = context.sourceCode;
|
|
285
|
+
function isStandalone(comment) {
|
|
286
|
+
const before = sourceCode.getTokenBefore(comment, {
|
|
287
|
+
includeComments: false
|
|
288
|
+
});
|
|
289
|
+
return !before || before.loc.end.line < comment.loc.start.line;
|
|
290
|
+
}
|
|
291
|
+
function isJsDoc(comment) {
|
|
292
|
+
return comment.type === "Block" && /^\*/.test(comment.value);
|
|
293
|
+
}
|
|
294
|
+
function reportLeadingPreamble(comments, firstCodeLine) {
|
|
295
|
+
const leading = [];
|
|
296
|
+
let prevLine = null;
|
|
297
|
+
for (const comment of comments) {
|
|
298
|
+
if (comment.type !== "Line") break;
|
|
299
|
+
if (comment.loc.start.line >= firstCodeLine) break;
|
|
300
|
+
if (!isStandalone(comment)) break;
|
|
301
|
+
const body = stripCommentMarker(comment.value);
|
|
302
|
+
if (isDirective(body) || body.startsWith("!")) continue;
|
|
303
|
+
if (prevLine !== null && comment.loc.start.line !== prevLine + 1) break;
|
|
304
|
+
leading.push(comment);
|
|
305
|
+
prevLine = comment.loc.start.line;
|
|
306
|
+
}
|
|
307
|
+
const first = leading[0];
|
|
308
|
+
if (first === void 0 || leading.length < LEADING_PREAMBLE_MIN) return;
|
|
309
|
+
const isLicense = leading.some(
|
|
310
|
+
(c) => LICENSE_RE.test(stripCommentMarker(c.value))
|
|
311
|
+
);
|
|
312
|
+
if (!isLicense) {
|
|
313
|
+
context.report({ node: first, messageId: "fileHeaderPreamble" });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
Program() {
|
|
318
|
+
const comments = sourceCode.getAllComments();
|
|
319
|
+
const firstCodeLine = sourceCode.ast.tokens[0]?.loc.start.line ?? Number.MAX_SAFE_INTEGER;
|
|
320
|
+
for (const comment of comments) {
|
|
321
|
+
if (isJsDoc(comment) || !isStandalone(comment)) continue;
|
|
322
|
+
const texts = comment.value.split("\n").map(stripCommentMarker).filter((l) => l.length > 0 && !isDirective(l));
|
|
323
|
+
if (texts.some(isBanner)) {
|
|
324
|
+
context.report({ node: comment, messageId: "sectionBanner" });
|
|
325
|
+
} else if (texts.some(looksLikeCode)) {
|
|
326
|
+
context.report({ node: comment, messageId: "commentedOutCode" });
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
reportLeadingPreamble(comments, firstCodeLine);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// src/rules/no-enum.ts
|
|
336
|
+
import { ESLintUtils as ESLintUtils4 } from "@typescript-eslint/utils";
|
|
256
337
|
var DEFAULT_IGNORE_PATTERNS = [
|
|
257
338
|
/[\\/]generated[\\/]/,
|
|
258
339
|
/\.gen\.tsx?$/,
|
|
@@ -271,7 +352,7 @@ function hasGeneratedMarker(sourceText) {
|
|
|
271
352
|
const head = sourceText.slice(0, 1024);
|
|
272
353
|
return /@generated\b/.test(head);
|
|
273
354
|
}
|
|
274
|
-
var no_enum_default =
|
|
355
|
+
var no_enum_default = ESLintUtils4.RuleCreator(
|
|
275
356
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
276
357
|
)({
|
|
277
358
|
name: "no-enum",
|
|
@@ -322,7 +403,7 @@ var no_enum_default = ESLintUtils3.RuleCreator(
|
|
|
322
403
|
});
|
|
323
404
|
|
|
324
405
|
// src/rules/no-insecure-random-id.ts
|
|
325
|
-
import { ESLintUtils as
|
|
406
|
+
import { ESLintUtils as ESLintUtils5 } from "@typescript-eslint/utils";
|
|
326
407
|
var NAME_PATTERN = /id|token|key|secret|uuid|nonce|session|password|salt/i;
|
|
327
408
|
function isMathRandomCall(node) {
|
|
328
409
|
if (node.type !== "CallExpression") {
|
|
@@ -400,7 +481,7 @@ function findEnclosingName(node) {
|
|
|
400
481
|
}
|
|
401
482
|
return void 0;
|
|
402
483
|
}
|
|
403
|
-
var no_insecure_random_id_default =
|
|
484
|
+
var no_insecure_random_id_default = ESLintUtils5.RuleCreator(
|
|
404
485
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
405
486
|
)({
|
|
406
487
|
name: "no-insecure-random-id",
|
|
@@ -435,7 +516,7 @@ var no_insecure_random_id_default = ESLintUtils4.RuleCreator(
|
|
|
435
516
|
});
|
|
436
517
|
|
|
437
518
|
// src/rules/no-json-stringify-error.ts
|
|
438
|
-
import { ESLintUtils as
|
|
519
|
+
import { ESLintUtils as ESLintUtils6 } from "@typescript-eslint/utils";
|
|
439
520
|
var ERROR_NAME_PATTERN = /^(e|err|error|ex|exc)$/i;
|
|
440
521
|
function isCatchBinding(scope, name) {
|
|
441
522
|
let current = scope;
|
|
@@ -455,7 +536,7 @@ function isCatchBinding(scope, name) {
|
|
|
455
536
|
function isJsonStringify(callee) {
|
|
456
537
|
return callee.type === "MemberExpression" && !callee.computed && callee.object.type === "Identifier" && callee.object.name === "JSON" && callee.property.type === "Identifier" && callee.property.name === "stringify";
|
|
457
538
|
}
|
|
458
|
-
var no_json_stringify_error_default =
|
|
539
|
+
var no_json_stringify_error_default = ESLintUtils6.RuleCreator(
|
|
459
540
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
460
541
|
)({
|
|
461
542
|
name: "no-json-stringify-error",
|
|
@@ -494,7 +575,7 @@ var no_json_stringify_error_default = ESLintUtils5.RuleCreator(
|
|
|
494
575
|
});
|
|
495
576
|
|
|
496
577
|
// src/rules/no-log-only-catch.ts
|
|
497
|
-
import { ESLintUtils as
|
|
578
|
+
import { ESLintUtils as ESLintUtils7 } from "@typescript-eslint/utils";
|
|
498
579
|
var DEFAULT_IGNORE_PATTERNS2 = [
|
|
499
580
|
/\.test\./,
|
|
500
581
|
/\.spec\./,
|
|
@@ -531,7 +612,7 @@ function isConsoleCallStatement(statement) {
|
|
|
531
612
|
}
|
|
532
613
|
return CONSOLE_METHODS.has(property.name);
|
|
533
614
|
}
|
|
534
|
-
var no_log_only_catch_default =
|
|
615
|
+
var no_log_only_catch_default = ESLintUtils7.RuleCreator(
|
|
535
616
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
536
617
|
)({
|
|
537
618
|
name: "no-log-only-catch",
|
|
@@ -573,8 +654,8 @@ var no_log_only_catch_default = ESLintUtils6.RuleCreator(
|
|
|
573
654
|
});
|
|
574
655
|
|
|
575
656
|
// src/rules/no-raw-env.ts
|
|
576
|
-
import { ESLintUtils as
|
|
577
|
-
var no_raw_env_default =
|
|
657
|
+
import { ESLintUtils as ESLintUtils8 } from "@typescript-eslint/utils";
|
|
658
|
+
var no_raw_env_default = ESLintUtils8.RuleCreator(
|
|
578
659
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
579
660
|
)({
|
|
580
661
|
name: "no-raw-env",
|
|
@@ -608,7 +689,7 @@ var no_raw_env_default = ESLintUtils7.RuleCreator(
|
|
|
608
689
|
|
|
609
690
|
// src/rules/no-sentinel-return-on-catch.ts
|
|
610
691
|
import {
|
|
611
|
-
ESLintUtils as
|
|
692
|
+
ESLintUtils as ESLintUtils9,
|
|
612
693
|
AST_NODE_TYPES as AST_NODE_TYPES3
|
|
613
694
|
} from "@typescript-eslint/utils";
|
|
614
695
|
function isSentinelArgument(arg) {
|
|
@@ -667,7 +748,7 @@ function containsThrow(node) {
|
|
|
667
748
|
function isNode(value) {
|
|
668
749
|
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
669
750
|
}
|
|
670
|
-
var no_sentinel_return_on_catch_default =
|
|
751
|
+
var no_sentinel_return_on_catch_default = ESLintUtils9.RuleCreator(
|
|
671
752
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
672
753
|
)({
|
|
673
754
|
name: "no-sentinel-return-on-catch",
|
|
@@ -709,14 +790,14 @@ var no_sentinel_return_on_catch_default = ESLintUtils8.RuleCreator(
|
|
|
709
790
|
});
|
|
710
791
|
|
|
711
792
|
// src/rules/no-sequential-await.ts
|
|
712
|
-
import { ESLintUtils as
|
|
793
|
+
import { ESLintUtils as ESLintUtils10 } from "@typescript-eslint/utils";
|
|
713
794
|
function isFunctionLike(node) {
|
|
714
795
|
return node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
|
|
715
796
|
}
|
|
716
797
|
function isLoop(node) {
|
|
717
798
|
return node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement" || node.type === "WhileStatement" || node.type === "DoWhileStatement";
|
|
718
799
|
}
|
|
719
|
-
var no_sequential_await_default =
|
|
800
|
+
var no_sequential_await_default = ESLintUtils10.RuleCreator(
|
|
720
801
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
721
802
|
)({
|
|
722
803
|
name: "no-sequential-await",
|
|
@@ -797,7 +878,7 @@ var no_sequential_await_default = ESLintUtils9.RuleCreator(
|
|
|
797
878
|
});
|
|
798
879
|
|
|
799
880
|
// src/rules/no-string-concat-in-loop.ts
|
|
800
|
-
import { ESLintUtils as
|
|
881
|
+
import { ESLintUtils as ESLintUtils11 } from "@typescript-eslint/utils";
|
|
801
882
|
var LOOP_NODE_TYPES = /* @__PURE__ */ new Set([
|
|
802
883
|
"ForStatement",
|
|
803
884
|
"ForOfStatement",
|
|
@@ -857,7 +938,7 @@ function isInsideLoopBody(node) {
|
|
|
857
938
|
}
|
|
858
939
|
return false;
|
|
859
940
|
}
|
|
860
|
-
var no_string_concat_in_loop_default =
|
|
941
|
+
var no_string_concat_in_loop_default = ESLintUtils11.RuleCreator(
|
|
861
942
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
862
943
|
)({
|
|
863
944
|
name: "no-string-concat-in-loop",
|
|
@@ -904,7 +985,7 @@ var no_string_concat_in_loop_default = ESLintUtils10.RuleCreator(
|
|
|
904
985
|
// src/rules/no-unnecessary-use-client.ts
|
|
905
986
|
import {
|
|
906
987
|
AST_NODE_TYPES as AST_NODE_TYPES4,
|
|
907
|
-
ESLintUtils as
|
|
988
|
+
ESLintUtils as ESLintUtils12
|
|
908
989
|
} from "@typescript-eslint/utils";
|
|
909
990
|
var HOOK_REGEX = /^use([A-Z]|$)/;
|
|
910
991
|
var EVENT_PROP_REGEX = /^on[A-Z]/;
|
|
@@ -954,7 +1035,7 @@ var isGlobalReference = (node, context) => {
|
|
|
954
1035
|
}
|
|
955
1036
|
return true;
|
|
956
1037
|
};
|
|
957
|
-
var no_unnecessary_use_client_default =
|
|
1038
|
+
var no_unnecessary_use_client_default = ESLintUtils12.RuleCreator(
|
|
958
1039
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
959
1040
|
)({
|
|
960
1041
|
name: "no-unnecessary-use-client",
|
|
@@ -1001,35 +1082,43 @@ var no_unnecessary_use_client_default = ESLintUtils11.RuleCreator(
|
|
|
1001
1082
|
}
|
|
1002
1083
|
},
|
|
1003
1084
|
CallExpression(node) {
|
|
1085
|
+
if (directiveNode === null) return;
|
|
1004
1086
|
markIfHookOrContext(node.callee);
|
|
1005
1087
|
},
|
|
1006
1088
|
JSXAttribute(node) {
|
|
1089
|
+
if (directiveNode === null) return;
|
|
1007
1090
|
if (node.name.type === AST_NODE_TYPES4.JSXIdentifier && EVENT_PROP_REGEX.test(node.name.name)) {
|
|
1008
1091
|
hasClientIndicator = true;
|
|
1009
1092
|
}
|
|
1010
1093
|
},
|
|
1011
1094
|
ImportDeclaration(node) {
|
|
1095
|
+
if (directiveNode === null) return;
|
|
1012
1096
|
if (typeof node.source.value === "string" && CLIENT_ONLY_PACKAGES_REGEX.test(node.source.value)) {
|
|
1013
1097
|
hasClientIndicator = true;
|
|
1014
1098
|
}
|
|
1015
1099
|
},
|
|
1016
1100
|
ExportNamedDeclaration(node) {
|
|
1101
|
+
if (directiveNode === null) return;
|
|
1017
1102
|
if (node.source !== null) {
|
|
1018
1103
|
hasClientIndicator = true;
|
|
1019
1104
|
}
|
|
1020
1105
|
},
|
|
1021
1106
|
ExportAllDeclaration(node) {
|
|
1107
|
+
if (directiveNode === null) return;
|
|
1022
1108
|
if (node.source !== null) {
|
|
1023
1109
|
hasClientIndicator = true;
|
|
1024
1110
|
}
|
|
1025
1111
|
},
|
|
1026
1112
|
ClassDeclaration() {
|
|
1113
|
+
if (directiveNode === null) return;
|
|
1027
1114
|
hasClientIndicator = true;
|
|
1028
1115
|
},
|
|
1029
1116
|
ClassExpression() {
|
|
1117
|
+
if (directiveNode === null) return;
|
|
1030
1118
|
hasClientIndicator = true;
|
|
1031
1119
|
},
|
|
1032
1120
|
Identifier(node) {
|
|
1121
|
+
if (directiveNode === null) return;
|
|
1033
1122
|
if (isGlobalReference(node, context)) {
|
|
1034
1123
|
hasClientIndicator = true;
|
|
1035
1124
|
}
|
|
@@ -1047,7 +1136,7 @@ var no_unnecessary_use_client_default = ESLintUtils11.RuleCreator(
|
|
|
1047
1136
|
});
|
|
1048
1137
|
|
|
1049
1138
|
// src/rules/prefer-discriminated-union.ts
|
|
1050
|
-
import { ESLintUtils as
|
|
1139
|
+
import { ESLintUtils as ESLintUtils13 } from "@typescript-eslint/utils";
|
|
1051
1140
|
import { AST_NODE_TYPES as AST_NODE_TYPES5 } from "@typescript-eslint/utils";
|
|
1052
1141
|
var STATUS_MEMBER_NAMES = /* @__PURE__ */ new Set([
|
|
1053
1142
|
"success",
|
|
@@ -1090,7 +1179,7 @@ function looksLikeMutuallyExclusiveState(typeLiteral) {
|
|
|
1090
1179
|
}
|
|
1091
1180
|
return hasStatusBoolean && optionalCount >= MIN_OPTIONAL_MEMBERS;
|
|
1092
1181
|
}
|
|
1093
|
-
var prefer_discriminated_union_default =
|
|
1182
|
+
var prefer_discriminated_union_default = ESLintUtils13.RuleCreator(
|
|
1094
1183
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1095
1184
|
)({
|
|
1096
1185
|
name: "prefer-discriminated-union",
|
|
@@ -1133,7 +1222,7 @@ var prefer_discriminated_union_default = ESLintUtils12.RuleCreator(
|
|
|
1133
1222
|
// src/rules/prefer-schema-for-api-payload.ts
|
|
1134
1223
|
import {
|
|
1135
1224
|
AST_NODE_TYPES as AST_NODE_TYPES6,
|
|
1136
|
-
ESLintUtils as
|
|
1225
|
+
ESLintUtils as ESLintUtils14
|
|
1137
1226
|
} from "@typescript-eslint/utils";
|
|
1138
1227
|
var unwrap = (node) => {
|
|
1139
1228
|
let current = node;
|
|
@@ -1181,7 +1270,7 @@ var isUnvalidatedVariableRef = (node, scope, tracked) => {
|
|
|
1181
1270
|
const variable = findVariable2(scope, unwrapped.name);
|
|
1182
1271
|
return variable !== null && tracked.has(variable);
|
|
1183
1272
|
};
|
|
1184
|
-
var prefer_schema_for_api_payload_default =
|
|
1273
|
+
var prefer_schema_for_api_payload_default = ESLintUtils14.RuleCreator(
|
|
1185
1274
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1186
1275
|
)({
|
|
1187
1276
|
name: "prefer-schema-for-api-payload",
|
|
@@ -1274,8 +1363,153 @@ var prefer_schema_for_api_payload_default = ESLintUtils13.RuleCreator(
|
|
|
1274
1363
|
}
|
|
1275
1364
|
});
|
|
1276
1365
|
|
|
1366
|
+
// src/rules/prefer-semantic-colors.ts
|
|
1367
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES7, ESLintUtils as ESLintUtils15 } from "@typescript-eslint/utils";
|
|
1368
|
+
|
|
1369
|
+
// src/rules/_tailwind.ts
|
|
1370
|
+
var tailwindBase = (token) => token.replace(/^(?:[a-z0-9-]+:)+/i, "").replace(/^!/, "");
|
|
1371
|
+
var classTokens = (value) => value.split(/\s+/).filter(Boolean);
|
|
1372
|
+
|
|
1373
|
+
// src/rules/prefer-semantic-colors.ts
|
|
1374
|
+
var COLOR_PREFIXES = "text|bg|border(?:-[trblxyse])?|ring(?:-offset)?|fill|stroke|from|via|to|divide|decoration|placeholder|accent|caret|shadow|outline";
|
|
1375
|
+
var PALETTE = "red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose|slate|gray|zinc|neutral|stone";
|
|
1376
|
+
var COLOR_FN = "rgba?|hsla?|hwb|oklch|oklab|lab|lch|color";
|
|
1377
|
+
var RAW_PALETTE_RE = new RegExp(`^(?:${COLOR_PREFIXES})-(?:${PALETTE})-\\d{2,3}(?:/\\d{1,3})?$`);
|
|
1378
|
+
var ARBITRARY_COLOR_RE = new RegExp(
|
|
1379
|
+
`^(?:${COLOR_PREFIXES})-\\[(?:#[0-9a-fA-F]{3,8}|(?:${COLOR_FN})\\([^\\]]*\\))\\]$`,
|
|
1380
|
+
"i"
|
|
1381
|
+
);
|
|
1382
|
+
var CLASS_FNS = /* @__PURE__ */ new Set(["cn", "clsx", "cva", "tv", "cx", "twMerge", "classnames", "classNames"]);
|
|
1383
|
+
var CLASS_NAME_RE = /class/i;
|
|
1384
|
+
var STYLE_COLOR_PROPS = /* @__PURE__ */ new Set([
|
|
1385
|
+
"color",
|
|
1386
|
+
"background",
|
|
1387
|
+
"backgroundColor",
|
|
1388
|
+
"borderColor",
|
|
1389
|
+
"borderTopColor",
|
|
1390
|
+
"borderRightColor",
|
|
1391
|
+
"borderBottomColor",
|
|
1392
|
+
"borderLeftColor",
|
|
1393
|
+
"outlineColor",
|
|
1394
|
+
"caretColor",
|
|
1395
|
+
"textDecorationColor",
|
|
1396
|
+
"columnRuleColor",
|
|
1397
|
+
"fill",
|
|
1398
|
+
"stroke",
|
|
1399
|
+
"stopColor",
|
|
1400
|
+
"floodColor",
|
|
1401
|
+
"lightingColor"
|
|
1402
|
+
]);
|
|
1403
|
+
var RAW_COLOR_VALUE_RE = new RegExp(`#[0-9a-fA-F]{3,8}\\b|\\b(?:${COLOR_FN})\\s*\\(`, "i");
|
|
1404
|
+
var propName = (key) => {
|
|
1405
|
+
if (key.type === AST_NODE_TYPES7.Identifier) return key.name;
|
|
1406
|
+
if (key.type === AST_NODE_TYPES7.Literal && typeof key.value === "string") return key.value;
|
|
1407
|
+
return null;
|
|
1408
|
+
};
|
|
1409
|
+
var prefer_semantic_colors_default = ESLintUtils15.RuleCreator(
|
|
1410
|
+
(name) => `https://github.com/sarj-ai/standards/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1411
|
+
)({
|
|
1412
|
+
name: "prefer-semantic-colors",
|
|
1413
|
+
meta: {
|
|
1414
|
+
type: "suggestion",
|
|
1415
|
+
docs: {
|
|
1416
|
+
description: "Enforce design-system semantic color tokens (bg-primary, text-destructive, \u2026) over raw Tailwind palette classes (text-red-500), arbitrary color values (bg-[#fff]), and inline color literals."
|
|
1417
|
+
},
|
|
1418
|
+
schema: [],
|
|
1419
|
+
messages: {
|
|
1420
|
+
rawPalette: "Raw palette class '{{class}}' \u2014 use a semantic token (e.g. text-foreground, bg-primary, text-destructive, bg-muted).",
|
|
1421
|
+
arbitraryColor: "Hardcoded color '{{class}}' \u2014 use a semantic token, or var(--\u2026). For charts/brand add an eslint-disable with a reason.",
|
|
1422
|
+
inlineColor: "Hardcoded color '{{value}}' \u2014 use a semantic token / CSS variable. For charts/standalone pages add an eslint-disable with a reason."
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
defaultOptions: [],
|
|
1426
|
+
create(context) {
|
|
1427
|
+
const reportClasses = (value, node) => {
|
|
1428
|
+
for (const token of classTokens(value)) {
|
|
1429
|
+
const base = tailwindBase(token);
|
|
1430
|
+
if (RAW_PALETTE_RE.test(base)) {
|
|
1431
|
+
context.report({ node, messageId: "rawPalette", data: { class: token } });
|
|
1432
|
+
} else if (ARBITRARY_COLOR_RE.test(base)) {
|
|
1433
|
+
context.report({ node, messageId: "arbitraryColor", data: { class: token } });
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
const checkClassNode = (node) => {
|
|
1438
|
+
if (node === null) return;
|
|
1439
|
+
switch (node.type) {
|
|
1440
|
+
case AST_NODE_TYPES7.Literal:
|
|
1441
|
+
if (typeof node.value === "string") reportClasses(node.value, node);
|
|
1442
|
+
break;
|
|
1443
|
+
case AST_NODE_TYPES7.TemplateLiteral:
|
|
1444
|
+
for (const quasi of node.quasis) reportClasses(quasi.value.cooked ?? "", quasi);
|
|
1445
|
+
break;
|
|
1446
|
+
case AST_NODE_TYPES7.ArrayExpression:
|
|
1447
|
+
for (const element of node.elements) {
|
|
1448
|
+
if (element !== null && element.type !== AST_NODE_TYPES7.SpreadElement) checkClassNode(element);
|
|
1449
|
+
}
|
|
1450
|
+
break;
|
|
1451
|
+
case AST_NODE_TYPES7.ObjectExpression:
|
|
1452
|
+
for (const property of node.properties) {
|
|
1453
|
+
if (property.type === AST_NODE_TYPES7.Property) checkClassNode(property.value);
|
|
1454
|
+
}
|
|
1455
|
+
break;
|
|
1456
|
+
case AST_NODE_TYPES7.ConditionalExpression:
|
|
1457
|
+
checkClassNode(node.consequent);
|
|
1458
|
+
checkClassNode(node.alternate);
|
|
1459
|
+
break;
|
|
1460
|
+
case AST_NODE_TYPES7.LogicalExpression:
|
|
1461
|
+
checkClassNode(node.right);
|
|
1462
|
+
break;
|
|
1463
|
+
default:
|
|
1464
|
+
break;
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
const checkColorValueNode = (node) => {
|
|
1468
|
+
if (node.type === AST_NODE_TYPES7.Literal && typeof node.value === "string" && RAW_COLOR_VALUE_RE.test(node.value)) {
|
|
1469
|
+
context.report({ node, messageId: "inlineColor", data: { value: node.value } });
|
|
1470
|
+
}
|
|
1471
|
+
};
|
|
1472
|
+
return {
|
|
1473
|
+
"JSXAttribute[name.name='className']"(node) {
|
|
1474
|
+
if (node.value === null) return;
|
|
1475
|
+
if (node.value.type === AST_NODE_TYPES7.Literal) checkClassNode(node.value);
|
|
1476
|
+
else if (node.value.type === AST_NODE_TYPES7.JSXExpressionContainer) {
|
|
1477
|
+
if (node.value.expression.type !== AST_NODE_TYPES7.JSXEmptyExpression) {
|
|
1478
|
+
checkClassNode(node.value.expression);
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
},
|
|
1482
|
+
CallExpression(node) {
|
|
1483
|
+
if (node.callee.type === AST_NODE_TYPES7.Identifier && CLASS_FNS.has(node.callee.name)) {
|
|
1484
|
+
for (const arg of node.arguments) {
|
|
1485
|
+
if (arg.type !== AST_NODE_TYPES7.SpreadElement) checkClassNode(arg);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
},
|
|
1489
|
+
VariableDeclarator(node) {
|
|
1490
|
+
if (node.id.type === AST_NODE_TYPES7.Identifier && CLASS_NAME_RE.test(node.id.name)) {
|
|
1491
|
+
checkClassNode(node.init);
|
|
1492
|
+
}
|
|
1493
|
+
},
|
|
1494
|
+
Property(node) {
|
|
1495
|
+
const name = propName(node.key);
|
|
1496
|
+
if (name !== null && CLASS_NAME_RE.test(name)) checkClassNode(node.value);
|
|
1497
|
+
},
|
|
1498
|
+
// SVG presentation attributes: <path fill="#000" stroke="#fff" />
|
|
1499
|
+
"JSXAttribute[name.name=/^(fill|stroke|color)$/]"(node) {
|
|
1500
|
+
if (node.value?.type === AST_NODE_TYPES7.Literal) checkColorValueNode(node.value);
|
|
1501
|
+
},
|
|
1502
|
+
// Inline style objects: style={{ color: "#111827", backgroundColor: "#fff" }}
|
|
1503
|
+
"JSXAttribute[name.name='style'] ObjectExpression > Property"(node) {
|
|
1504
|
+
const name = propName(node.key);
|
|
1505
|
+
if (name !== null && STYLE_COLOR_PROPS.has(name)) checkColorValueNode(node.value);
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1277
1511
|
// src/rules/prefer-server-actions.ts
|
|
1278
|
-
import { ESLintUtils as
|
|
1512
|
+
import { ESLintUtils as ESLintUtils16 } from "@typescript-eslint/utils";
|
|
1279
1513
|
var MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "DELETE", "PATCH"]);
|
|
1280
1514
|
var AXIOS_MUTATION_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
|
|
1281
1515
|
var SKIP_FILE_REGEX = /(?:\.test\.[jt]sx?$|\.spec\.[jt]sx?$|\/tests?\/|\/__tests__\/|\/scripts?\/|\/app\/api\/.*\/route\.[jt]sx?$|\/pages\/api\/)/;
|
|
@@ -1335,7 +1569,7 @@ function isMutationMethod(node, context) {
|
|
|
1335
1569
|
}
|
|
1336
1570
|
return false;
|
|
1337
1571
|
}
|
|
1338
|
-
function getPropertyNode(objNode,
|
|
1572
|
+
function getPropertyNode(objNode, propName2) {
|
|
1339
1573
|
if (!objNode || objNode.type !== "ObjectExpression") return null;
|
|
1340
1574
|
for (const prop of objNode.properties) {
|
|
1341
1575
|
if (prop.type !== "Property") continue;
|
|
@@ -1345,7 +1579,7 @@ function getPropertyNode(objNode, propName) {
|
|
|
1345
1579
|
} else if (prop.key.type === "Literal" && typeof prop.key.value === "string") {
|
|
1346
1580
|
keyName = prop.key.value;
|
|
1347
1581
|
}
|
|
1348
|
-
if (keyName ===
|
|
1582
|
+
if (keyName === propName2) {
|
|
1349
1583
|
if (prop.value.type === "AssignmentPattern" || prop.value.type === "ArrayPattern" || prop.value.type === "ObjectPattern") {
|
|
1350
1584
|
return null;
|
|
1351
1585
|
}
|
|
@@ -1354,7 +1588,7 @@ function getPropertyNode(objNode, propName) {
|
|
|
1354
1588
|
}
|
|
1355
1589
|
return null;
|
|
1356
1590
|
}
|
|
1357
|
-
var prefer_server_actions_default =
|
|
1591
|
+
var prefer_server_actions_default = ESLintUtils16.RuleCreator(
|
|
1358
1592
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1359
1593
|
)({
|
|
1360
1594
|
name: "prefer-server-actions",
|
|
@@ -1393,7 +1627,10 @@ var prefer_server_actions_default = ESLintUtils14.RuleCreator(
|
|
|
1393
1627
|
const methodName = node.callee.property.name.toLowerCase();
|
|
1394
1628
|
if (AXIOS_MUTATION_METHODS.has(methodName)) {
|
|
1395
1629
|
const urlArg = node.arguments[0];
|
|
1396
|
-
|
|
1630
|
+
const hasHandlerArg = node.arguments.some(
|
|
1631
|
+
(arg) => arg.type === "ArrowFunctionExpression" || arg.type === "FunctionExpression"
|
|
1632
|
+
);
|
|
1633
|
+
if (urlArg && urlArg.type !== "SpreadElement" && !hasHandlerArg && isApiUrl(urlArg, context)) {
|
|
1397
1634
|
isMutation = true;
|
|
1398
1635
|
}
|
|
1399
1636
|
}
|
|
@@ -1419,14 +1656,14 @@ var prefer_server_actions_default = ESLintUtils14.RuleCreator(
|
|
|
1419
1656
|
});
|
|
1420
1657
|
|
|
1421
1658
|
// src/rules/prefer-shadcn.ts
|
|
1422
|
-
import { ESLintUtils as
|
|
1659
|
+
import { ESLintUtils as ESLintUtils17 } from "@typescript-eslint/utils";
|
|
1423
1660
|
var REPLACEMENTS = {
|
|
1424
1661
|
input: "Input",
|
|
1425
1662
|
select: "Select",
|
|
1426
1663
|
textarea: "Textarea",
|
|
1427
1664
|
dialog: "Dialog"
|
|
1428
1665
|
};
|
|
1429
|
-
var prefer_shadcn_default =
|
|
1666
|
+
var prefer_shadcn_default = ESLintUtils17.RuleCreator(
|
|
1430
1667
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1431
1668
|
)({
|
|
1432
1669
|
name: "prefer-shadcn",
|
|
@@ -1467,36 +1704,52 @@ var prefer_shadcn_default = ESLintUtils15.RuleCreator(
|
|
|
1467
1704
|
});
|
|
1468
1705
|
|
|
1469
1706
|
// src/rules/require-assert-never.ts
|
|
1470
|
-
import { ESLintUtils as
|
|
1707
|
+
import { ESLintUtils as ESLintUtils18, AST_NODE_TYPES as AST_NODE_TYPES8 } from "@typescript-eslint/utils";
|
|
1471
1708
|
var isAssertNeverCall = (expression) => {
|
|
1472
|
-
if (expression.type !==
|
|
1709
|
+
if (expression.type !== AST_NODE_TYPES8.CallExpression) return false;
|
|
1473
1710
|
const callee = expression.callee;
|
|
1474
|
-
|
|
1711
|
+
if (callee.type === AST_NODE_TYPES8.Identifier) {
|
|
1712
|
+
return callee.name === "assertNever";
|
|
1713
|
+
}
|
|
1714
|
+
if (callee.type === AST_NODE_TYPES8.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES8.Identifier) {
|
|
1715
|
+
return callee.property.name === "assertNever";
|
|
1716
|
+
}
|
|
1717
|
+
return false;
|
|
1475
1718
|
};
|
|
1476
1719
|
var statementContainsAssertNever = (statement) => {
|
|
1477
|
-
if (statement.type ===
|
|
1720
|
+
if (statement.type === AST_NODE_TYPES8.ExpressionStatement) {
|
|
1478
1721
|
return isAssertNeverCall(statement.expression);
|
|
1479
1722
|
}
|
|
1480
|
-
if (statement.type ===
|
|
1723
|
+
if (statement.type === AST_NODE_TYPES8.ThrowStatement) {
|
|
1481
1724
|
return isAssertNeverCall(statement.argument);
|
|
1482
1725
|
}
|
|
1483
|
-
if (statement.type ===
|
|
1726
|
+
if (statement.type === AST_NODE_TYPES8.ReturnStatement) {
|
|
1727
|
+
return statement.argument !== null && isAssertNeverCall(statement.argument);
|
|
1728
|
+
}
|
|
1729
|
+
if (statement.type === AST_NODE_TYPES8.BlockStatement) {
|
|
1484
1730
|
return statement.body.some(statementContainsAssertNever);
|
|
1485
1731
|
}
|
|
1486
1732
|
return false;
|
|
1487
1733
|
};
|
|
1488
|
-
var
|
|
1734
|
+
var isRuntimeHandlingStatement = (statement) => {
|
|
1735
|
+
if (statement.type === AST_NODE_TYPES8.EmptyStatement) return false;
|
|
1736
|
+
if (statement.type === AST_NODE_TYPES8.BlockStatement) {
|
|
1737
|
+
return statement.body.some(isRuntimeHandlingStatement);
|
|
1738
|
+
}
|
|
1739
|
+
return true;
|
|
1740
|
+
};
|
|
1741
|
+
var require_assert_never_default = ESLintUtils18.RuleCreator(
|
|
1489
1742
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1490
1743
|
)({
|
|
1491
1744
|
name: "require-assert-never",
|
|
1492
1745
|
meta: {
|
|
1493
1746
|
type: "problem",
|
|
1494
1747
|
docs: {
|
|
1495
|
-
description: "Require switch
|
|
1748
|
+
description: "Require an exhaustive-style switch whose `default` case does no runtime work to call `assertNever(_)` so that discriminated unions are exhaustively checked at compile time. Switches with a legitimate runtime default (a reducer's `return state`, an HTTP-status `return fallback()`, a `break`, a `throw`, etc.) are left alone."
|
|
1496
1749
|
},
|
|
1497
1750
|
schema: [],
|
|
1498
1751
|
messages: {
|
|
1499
|
-
missingAssertNever: "
|
|
1752
|
+
missingAssertNever: "Empty switch `default` case \u2014 add runtime handling or call `assertNever()` so the discriminated union is exhaustively checked at compile time."
|
|
1500
1753
|
}
|
|
1501
1754
|
},
|
|
1502
1755
|
defaultOptions: [],
|
|
@@ -1507,10 +1760,8 @@ var require_assert_never_default = ESLintUtils16.RuleCreator(
|
|
|
1507
1760
|
(caseNode) => caseNode.test === null
|
|
1508
1761
|
);
|
|
1509
1762
|
if (!defaultCase) return;
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
);
|
|
1513
|
-
if (hasAssertNever) return;
|
|
1763
|
+
if (defaultCase.consequent.some(statementContainsAssertNever)) return;
|
|
1764
|
+
if (defaultCase.consequent.some(isRuntimeHandlingStatement)) return;
|
|
1514
1765
|
context.report({
|
|
1515
1766
|
node: defaultCase,
|
|
1516
1767
|
messageId: "missingAssertNever"
|
|
@@ -1521,43 +1772,91 @@ var require_assert_never_default = ESLintUtils16.RuleCreator(
|
|
|
1521
1772
|
});
|
|
1522
1773
|
|
|
1523
1774
|
// src/rules/require-zod-form-validation.ts
|
|
1524
|
-
import { ESLintUtils as
|
|
1525
|
-
var
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1775
|
+
import { ESLintUtils as ESLintUtils19, AST_NODE_TYPES as AST_NODE_TYPES9 } from "@typescript-eslint/utils";
|
|
1776
|
+
var ZOD_SCHEMA_NAME_RE = /Schema$|^Z[A-Z]/;
|
|
1777
|
+
var looksLikeZodSchema = (node) => {
|
|
1778
|
+
let current = node;
|
|
1779
|
+
while (true) {
|
|
1780
|
+
if (current.type === AST_NODE_TYPES9.Identifier) {
|
|
1781
|
+
return current.name === "z" || ZOD_SCHEMA_NAME_RE.test(current.name);
|
|
1782
|
+
}
|
|
1783
|
+
if (current.type === AST_NODE_TYPES9.CallExpression) {
|
|
1784
|
+
current = current.callee;
|
|
1785
|
+
continue;
|
|
1786
|
+
}
|
|
1787
|
+
if (current.type === AST_NODE_TYPES9.MemberExpression) {
|
|
1788
|
+
current = current.object;
|
|
1789
|
+
continue;
|
|
1790
|
+
}
|
|
1529
1791
|
return false;
|
|
1530
1792
|
}
|
|
1531
|
-
return callee.object.type === AST_NODE_TYPES8.Identifier && callee.object.name === "formData";
|
|
1532
1793
|
};
|
|
1533
|
-
var
|
|
1534
|
-
if (node.type !==
|
|
1794
|
+
var isZodParseCall = (node) => {
|
|
1795
|
+
if (node.type !== AST_NODE_TYPES9.CallExpression) return false;
|
|
1535
1796
|
const callee = node.callee;
|
|
1536
|
-
if (callee.type !==
|
|
1537
|
-
|
|
1797
|
+
if (callee.type !== AST_NODE_TYPES9.MemberExpression) return false;
|
|
1798
|
+
if (callee.computed) return false;
|
|
1799
|
+
if (callee.property.type !== AST_NODE_TYPES9.Identifier) return false;
|
|
1800
|
+
const method = callee.property.name;
|
|
1801
|
+
if (method !== "parse" && method !== "safeParse") return false;
|
|
1802
|
+
return looksLikeZodSchema(callee.object);
|
|
1803
|
+
};
|
|
1804
|
+
var isFormDataMethodCall = (node) => {
|
|
1805
|
+
let current = node;
|
|
1806
|
+
if (current.type === AST_NODE_TYPES9.AwaitExpression) {
|
|
1807
|
+
current = current.argument;
|
|
1808
|
+
}
|
|
1809
|
+
if (current.type !== AST_NODE_TYPES9.CallExpression) return false;
|
|
1810
|
+
const callee = current.callee;
|
|
1811
|
+
return callee.type === AST_NODE_TYPES9.MemberExpression && !callee.computed && callee.property.type === AST_NODE_TYPES9.Identifier && callee.property.name === "formData";
|
|
1538
1812
|
};
|
|
1539
|
-
var require_zod_form_validation_default =
|
|
1813
|
+
var require_zod_form_validation_default = ESLintUtils19.RuleCreator(
|
|
1540
1814
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1541
1815
|
)({
|
|
1542
1816
|
name: "require-zod-form-validation",
|
|
1543
1817
|
meta: {
|
|
1544
1818
|
type: "problem",
|
|
1545
1819
|
docs: {
|
|
1546
|
-
description: "Require Zod validation (`Schema.parse(...)`) when reading values out of a `FormData` object."
|
|
1820
|
+
description: "Require Zod validation (`Schema.parse(...)` / `Schema.safeParse(...)`) when reading values out of a `FormData` object."
|
|
1547
1821
|
},
|
|
1548
1822
|
schema: [],
|
|
1549
1823
|
messages: {
|
|
1550
|
-
missingZodValidation: "FormData parsing must use Zod schema validation (e.g., Schema.parse())"
|
|
1824
|
+
missingZodValidation: "FormData parsing must use Zod schema validation (e.g., Schema.parse() / Schema.safeParse())"
|
|
1551
1825
|
}
|
|
1552
1826
|
},
|
|
1553
1827
|
defaultOptions: [],
|
|
1554
1828
|
create(context) {
|
|
1829
|
+
const isFormSourceIdentifier = (node) => {
|
|
1830
|
+
if (node.type !== AST_NODE_TYPES9.Identifier) return false;
|
|
1831
|
+
if (/formdata/i.test(node.name)) return true;
|
|
1832
|
+
let scope = context.sourceCode.getScope(node);
|
|
1833
|
+
while (scope !== null) {
|
|
1834
|
+
const variable = scope.set.get(node.name);
|
|
1835
|
+
if (variable !== void 0 && variable.defs.length === 1) {
|
|
1836
|
+
const def = variable.defs[0];
|
|
1837
|
+
if (def !== void 0 && def.type === "Variable" && def.node.type === AST_NODE_TYPES9.VariableDeclarator && def.node.init !== null) {
|
|
1838
|
+
return isFormDataMethodCall(def.node.init);
|
|
1839
|
+
}
|
|
1840
|
+
return false;
|
|
1841
|
+
}
|
|
1842
|
+
scope = scope.upper;
|
|
1843
|
+
}
|
|
1844
|
+
return false;
|
|
1845
|
+
};
|
|
1846
|
+
const isFormDataGetCall = (node) => {
|
|
1847
|
+
const callee = node.callee;
|
|
1848
|
+
if (callee.type !== AST_NODE_TYPES9.MemberExpression) return false;
|
|
1849
|
+
if (callee.property.type !== AST_NODE_TYPES9.Identifier || callee.property.name !== "get") {
|
|
1850
|
+
return false;
|
|
1851
|
+
}
|
|
1852
|
+
return isFormSourceIdentifier(callee.object);
|
|
1853
|
+
};
|
|
1555
1854
|
return {
|
|
1556
1855
|
CallExpression(node) {
|
|
1557
1856
|
if (!isFormDataGetCall(node)) return;
|
|
1558
1857
|
let parent = node.parent;
|
|
1559
1858
|
while (parent !== null && parent !== void 0) {
|
|
1560
|
-
if (
|
|
1859
|
+
if (isZodParseCall(parent)) return;
|
|
1561
1860
|
parent = parent.parent;
|
|
1562
1861
|
}
|
|
1563
1862
|
context.report({
|
|
@@ -1570,15 +1869,15 @@ var require_zod_form_validation_default = ESLintUtils17.RuleCreator(
|
|
|
1570
1869
|
});
|
|
1571
1870
|
|
|
1572
1871
|
// src/rules/zod-naming-convention.ts
|
|
1573
|
-
import { ESLintUtils as
|
|
1872
|
+
import { ESLintUtils as ESLintUtils20, AST_NODE_TYPES as AST_NODE_TYPES10 } from "@typescript-eslint/utils";
|
|
1574
1873
|
var calleeChainStartsWithZ = (node) => {
|
|
1575
1874
|
let current = node;
|
|
1576
|
-
while (current.type ===
|
|
1875
|
+
while (current.type === AST_NODE_TYPES10.MemberExpression) {
|
|
1577
1876
|
const receiver = current.object;
|
|
1578
|
-
if (receiver.type ===
|
|
1877
|
+
if (receiver.type === AST_NODE_TYPES10.Identifier && receiver.name === "z") {
|
|
1579
1878
|
return true;
|
|
1580
1879
|
}
|
|
1581
|
-
if (receiver.type ===
|
|
1880
|
+
if (receiver.type === AST_NODE_TYPES10.CallExpression) {
|
|
1582
1881
|
current = receiver.callee;
|
|
1583
1882
|
continue;
|
|
1584
1883
|
}
|
|
@@ -1586,7 +1885,7 @@ var calleeChainStartsWithZ = (node) => {
|
|
|
1586
1885
|
}
|
|
1587
1886
|
return false;
|
|
1588
1887
|
};
|
|
1589
|
-
var zod_naming_convention_default =
|
|
1888
|
+
var zod_naming_convention_default = ESLintUtils20.RuleCreator(
|
|
1590
1889
|
(name) => `https://github.com/sarj-ai/linting/blob/main/packages/typescript/src/rules/${name}.ts`
|
|
1591
1890
|
)({
|
|
1592
1891
|
name: "zod-naming-convention",
|
|
@@ -1606,11 +1905,11 @@ var zod_naming_convention_default = ESLintUtils18.RuleCreator(
|
|
|
1606
1905
|
VariableDeclarator(node) {
|
|
1607
1906
|
const init = node.init;
|
|
1608
1907
|
if (init === null || init === void 0) return;
|
|
1609
|
-
if (init.type !==
|
|
1908
|
+
if (init.type !== AST_NODE_TYPES10.CallExpression) return;
|
|
1610
1909
|
const callee = init.callee;
|
|
1611
|
-
if (callee.type !==
|
|
1910
|
+
if (callee.type !== AST_NODE_TYPES10.MemberExpression) return;
|
|
1612
1911
|
if (!calleeChainStartsWithZ(callee)) return;
|
|
1613
|
-
if (node.id.type !==
|
|
1912
|
+
if (node.id.type !== AST_NODE_TYPES10.Identifier) return;
|
|
1614
1913
|
const variableName = node.id.name;
|
|
1615
1914
|
if (variableName.startsWith("Z")) return;
|
|
1616
1915
|
context.report({
|
|
@@ -1626,6 +1925,7 @@ var zod_naming_convention_default = ESLintUtils18.RuleCreator(
|
|
|
1626
1925
|
var rules = {
|
|
1627
1926
|
"enforce-file-structure": enforce_file_structure_default,
|
|
1628
1927
|
"no-client-side-data-fetching": no_client_side_data_fetching_default,
|
|
1928
|
+
"no-comment-cruft": no_comment_cruft_default,
|
|
1629
1929
|
"no-enum": no_enum_default,
|
|
1630
1930
|
"no-insecure-random-id": no_insecure_random_id_default,
|
|
1631
1931
|
"no-json-stringify-error": no_json_stringify_error_default,
|
|
@@ -1637,6 +1937,7 @@ var rules = {
|
|
|
1637
1937
|
"no-unnecessary-use-client": no_unnecessary_use_client_default,
|
|
1638
1938
|
"prefer-discriminated-union": prefer_discriminated_union_default,
|
|
1639
1939
|
"prefer-schema-for-api-payload": prefer_schema_for_api_payload_default,
|
|
1940
|
+
"prefer-semantic-colors": prefer_semantic_colors_default,
|
|
1640
1941
|
"prefer-server-actions": prefer_server_actions_default,
|
|
1641
1942
|
"prefer-shadcn": prefer_shadcn_default,
|
|
1642
1943
|
"require-assert-never": require_assert_never_default,
|
|
@@ -1646,7 +1947,7 @@ var rules = {
|
|
|
1646
1947
|
var plugin = {
|
|
1647
1948
|
meta: {
|
|
1648
1949
|
name: "@sarj/eslint-plugin",
|
|
1649
|
-
version: "2.
|
|
1950
|
+
version: "2.2.0"
|
|
1650
1951
|
},
|
|
1651
1952
|
rules,
|
|
1652
1953
|
configs: {
|
|
@@ -1668,7 +1969,10 @@ var plugin = {
|
|
|
1668
1969
|
"@sarj/no-insecure-random-id": "warn",
|
|
1669
1970
|
"@sarj/no-json-stringify-error": "warn",
|
|
1670
1971
|
"@sarj/no-string-concat-in-loop": "warn",
|
|
1671
|
-
"@sarj/prefer-discriminated-union": "warn"
|
|
1972
|
+
"@sarj/prefer-discriminated-union": "warn",
|
|
1973
|
+
"@sarj/no-comment-cruft": "warn",
|
|
1974
|
+
// Frontend / styling — distilled from frontend PR-review mining.
|
|
1975
|
+
"@sarj/prefer-semantic-colors": "warn"
|
|
1672
1976
|
}
|
|
1673
1977
|
},
|
|
1674
1978
|
strict: {
|
|
@@ -1692,7 +1996,11 @@ var plugin = {
|
|
|
1692
1996
|
"@sarj/no-insecure-random-id": "error",
|
|
1693
1997
|
"@sarj/no-json-stringify-error": "error",
|
|
1694
1998
|
"@sarj/no-string-concat-in-loop": "error",
|
|
1695
|
-
"@sarj/prefer-discriminated-union": "error"
|
|
1999
|
+
"@sarj/prefer-discriminated-union": "error",
|
|
2000
|
+
"@sarj/no-comment-cruft": "error",
|
|
2001
|
+
// Frontend / styling — distilled from frontend PR-review mining. Stylistic,
|
|
2002
|
+
// no autofix → warn (rollout should prove the FP rate before raising it).
|
|
2003
|
+
"@sarj/prefer-semantic-colors": "warn"
|
|
1696
2004
|
}
|
|
1697
2005
|
}
|
|
1698
2006
|
}
|