oxlint-plugin-arch 0.4.0 → 0.5.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/CHANGELOG.md +46 -0
- package/README.md +203 -7
- package/dist/configs/boundaries.d.ts +2 -0
- package/dist/configs/modules.d.ts +2 -0
- package/dist/configs/plugins.d.ts +3 -0
- package/dist/configs/react.d.ts +2 -0
- package/dist/configs/source.d.ts +2 -0
- package/dist/configs/tests.d.ts +2 -0
- package/dist/configs/types.d.ts +2 -0
- package/dist/constants/preset.constants.d.ts +28 -0
- package/dist/index.js +1045 -630
- package/dist/presets/index.d.ts +2 -0
- package/dist/presets/index.js +1354 -0
- package/dist/presets/tanstack-start-react-modules-preset.d.ts +3 -0
- package/dist/presets/tanstack-start-react-modules-preset.js +1354 -0
- package/dist/rules/dillon-anti-slop/index.js +1588 -0
- package/dist/rules/module-policy/index.js +1743 -0
- package/dist/services/preset-compose.d.ts +3 -0
- package/dist/services/preset-context.d.ts +2 -0
- package/dist/types/preset.types.d.ts +119 -0
- package/dist/utils/helpers/preset.d.ts +6 -0
- package/package.json +45 -10
- package/src/rules/dillon-anti-slop/LICENSE +21 -0
- package/src/rules/dillon-anti-slop/UPSTREAM.md +19 -0
package/dist/index.js
CHANGED
|
@@ -5,16 +5,7 @@ import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
|
5
5
|
import { defineRule } from "@oxlint/plugins";
|
|
6
6
|
|
|
7
7
|
// src/utils/helpers/ast.ts
|
|
8
|
-
var astVisitSkip = new Set([
|
|
9
|
-
"parent",
|
|
10
|
-
"type",
|
|
11
|
-
"start",
|
|
12
|
-
"end",
|
|
13
|
-
"range",
|
|
14
|
-
"loc",
|
|
15
|
-
"tokens",
|
|
16
|
-
"comments"
|
|
17
|
-
]);
|
|
8
|
+
var astVisitSkip = new Set(["parent", "type", "start", "end", "range", "loc", "tokens", "comments"]);
|
|
18
9
|
function astIsNode(value) {
|
|
19
10
|
return value !== null && typeof value === "object" && typeof Reflect.get(value, "type") === "string";
|
|
20
11
|
}
|
|
@@ -103,6 +94,75 @@ function astResolvedCallName(node, aliases) {
|
|
|
103
94
|
const name = astCallName(node);
|
|
104
95
|
return name ? aliases.get(name) ?? name : undefined;
|
|
105
96
|
}
|
|
97
|
+
// src/utils/helpers/comments.ts
|
|
98
|
+
var COMMENTS_DIRECTIVE = /^\s*(?:\/\s*<(?:reference\b|amd-[\w-]+\b)|@ts-(?:check|ignore|expect-error|nocheck)\b|(?:#|@)__(?:PURE|NO_SIDE_EFFECTS)__\b|@jsx(?:ImportSource|Runtime|Frag)?\b|@vite-ignore\b|(?:[#@]\s*)?sourceMappingURL=|(?:es|ox)lint-(?:disable|enable)|prettier-ignore|biome-ignore|[cv]8 ignore|istanbul ignore|webpack[A-Z])/;
|
|
99
|
+
var COMMENTS_DISABLE_NEXT_LINE = /^(?:eslint|oxlint)-disable-next-line\b/;
|
|
100
|
+
var COMMENTS_LEGAL_BLOCK = "\x00legal-block\x00";
|
|
101
|
+
function commentsNormalizedValue(comment) {
|
|
102
|
+
const value = (comment.type === "Block" ? comment.value.replace(/^\s*\* ?/gm, "") : comment.value).trimStart();
|
|
103
|
+
return comment.type === "Block" && comment.value.startsWith("!") ? COMMENTS_LEGAL_BLOCK + value : value;
|
|
104
|
+
}
|
|
105
|
+
function commentsIsDirective(value) {
|
|
106
|
+
return value.startsWith(COMMENTS_LEGAL_BLOCK) || COMMENTS_DIRECTIVE.test(value);
|
|
107
|
+
}
|
|
108
|
+
function commentsIsDisableNextLine(value) {
|
|
109
|
+
return COMMENTS_DISABLE_NEXT_LINE.test(value);
|
|
110
|
+
}
|
|
111
|
+
function commentsIsWhy(value) {
|
|
112
|
+
return value.startsWith("Why: ");
|
|
113
|
+
}
|
|
114
|
+
function commentsIsJsdoc(source, comment) {
|
|
115
|
+
return comment.type === "Block" && source.startsWith("/**", comment.range[0]);
|
|
116
|
+
}
|
|
117
|
+
// src/utils/helpers/trivial.ts
|
|
118
|
+
function trivialExpression(node) {
|
|
119
|
+
if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
|
|
120
|
+
return node.body;
|
|
121
|
+
}
|
|
122
|
+
if (!node.body || node.body.type !== "BlockStatement")
|
|
123
|
+
return null;
|
|
124
|
+
const statements = node.body.body.filter((statement) => statement.type !== "EmptyStatement" && !(statement.type === "ExpressionStatement" && statement.directive));
|
|
125
|
+
if (statements.length !== 1)
|
|
126
|
+
return null;
|
|
127
|
+
const statement = statements[0];
|
|
128
|
+
if (statement.type === "ReturnStatement")
|
|
129
|
+
return statement.argument;
|
|
130
|
+
return statement.type === "ExpressionStatement" ? statement.expression : null;
|
|
131
|
+
}
|
|
132
|
+
function trivialUnwrap(expression) {
|
|
133
|
+
if (expression.type === "ParenthesizedExpression" || expression.type === "TSAsExpression" || expression.type === "TSSatisfiesExpression" || expression.type === "TSTypeAssertion" || expression.type === "TSNonNullExpression" || expression.type === "ChainExpression") {
|
|
134
|
+
return trivialUnwrap(expression.expression);
|
|
135
|
+
}
|
|
136
|
+
return expression.type === "AwaitExpression" ? trivialUnwrap(expression.argument) : expression;
|
|
137
|
+
}
|
|
138
|
+
function trivialCallRoot(expression) {
|
|
139
|
+
if (expression.type === "Identifier")
|
|
140
|
+
return expression.name;
|
|
141
|
+
if (expression.type === "MemberExpression" && !expression.computed && !expression.optional) {
|
|
142
|
+
return trivialCallRoot(expression.object);
|
|
143
|
+
}
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
function trivialIsForwarder(node, expression) {
|
|
147
|
+
const value = trivialUnwrap(expression);
|
|
148
|
+
if (value.type === "Literal")
|
|
149
|
+
return !("regex" in value);
|
|
150
|
+
if (value.type === "TemplateLiteral")
|
|
151
|
+
return value.expressions.length === 0;
|
|
152
|
+
if (value.type === "Identifier")
|
|
153
|
+
return true;
|
|
154
|
+
if (value.type !== "CallExpression" || value.optional)
|
|
155
|
+
return false;
|
|
156
|
+
const root = trivialCallRoot(value.callee);
|
|
157
|
+
if (!root || node.params.some((param) => param.type !== "Identifier"))
|
|
158
|
+
return false;
|
|
159
|
+
const parameters = node.params.flatMap((param) => param.type === "Identifier" ? [param.name] : []);
|
|
160
|
+
const arguments_ = value.arguments;
|
|
161
|
+
const receiver = value.callee.type === "MemberExpression" ? root : undefined;
|
|
162
|
+
const forwarded = parameters.filter((name) => name !== receiver);
|
|
163
|
+
return arguments_.length === forwarded.length && arguments_.every((argument, index) => argument.type === "Identifier" && argument.name === forwarded[index]);
|
|
164
|
+
}
|
|
165
|
+
|
|
106
166
|
// src/utils/helpers/declarations.ts
|
|
107
167
|
function declarationsTopLevelUnexportedFunctions(program) {
|
|
108
168
|
return program.body.flatMap((statement) => {
|
|
@@ -284,6 +344,21 @@ function declarationsCollectFunctions(root) {
|
|
|
284
344
|
return [];
|
|
285
345
|
});
|
|
286
346
|
}
|
|
347
|
+
function declarationsPublicServiceMethods(program) {
|
|
348
|
+
return program.body.flatMap((statement) => {
|
|
349
|
+
if (statement.type !== "ExportNamedDeclaration" || statement.declaration?.type !== "VariableDeclaration" || statement.declaration.kind !== "const")
|
|
350
|
+
return [];
|
|
351
|
+
return statement.declaration.declarations.flatMap((binding) => {
|
|
352
|
+
if (binding.id.type !== "Identifier" || !binding.init)
|
|
353
|
+
return [];
|
|
354
|
+
const value = trivialUnwrap(binding.init);
|
|
355
|
+
if (value.type !== "ObjectExpression" || value.properties.length === 0)
|
|
356
|
+
return [];
|
|
357
|
+
const methods = value.properties.flatMap((property) => property.type === "Property" && property.method && property.kind === "init" && !property.computed && property.key.type === "Identifier" && property.value.type === "FunctionExpression" ? [{ name: property.key.name, node: property.value, binding }] : []);
|
|
358
|
+
return methods.length === value.properties.length ? methods : [];
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
287
362
|
// src/utils/helpers/exports.ts
|
|
288
363
|
function moduleExportName(node) {
|
|
289
364
|
return node.type === "Identifier" ? node.name : node.value;
|
|
@@ -305,15 +380,17 @@ function declarationBindings(declaration) {
|
|
|
305
380
|
}));
|
|
306
381
|
}
|
|
307
382
|
const name = declaration.id?.name ?? "<anonymous>";
|
|
308
|
-
return [
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
383
|
+
return [
|
|
384
|
+
{
|
|
385
|
+
exportedName: name,
|
|
386
|
+
localName: declaration.id?.name,
|
|
387
|
+
node: declaration,
|
|
388
|
+
declaration,
|
|
389
|
+
typeOnly: declaration.type === "TSInterfaceDeclaration" || declaration.type === "TSTypeAliasDeclaration" || declaration.type === "TSDeclareFunction",
|
|
390
|
+
default: false,
|
|
391
|
+
reExport: false
|
|
392
|
+
}
|
|
393
|
+
];
|
|
317
394
|
}
|
|
318
395
|
function localBindings(program) {
|
|
319
396
|
const result = new Map;
|
|
@@ -335,29 +412,33 @@ function exportsCollect(program) {
|
|
|
335
412
|
const locals = localBindings(program);
|
|
336
413
|
return program.body.flatMap((statement) => {
|
|
337
414
|
if (statement.type === "ExportAllDeclaration") {
|
|
338
|
-
return [
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
415
|
+
return [
|
|
416
|
+
{
|
|
417
|
+
exportedName: "*",
|
|
418
|
+
node: statement,
|
|
419
|
+
typeOnly: statement.exportKind === "type",
|
|
420
|
+
default: false,
|
|
421
|
+
reExport: true
|
|
422
|
+
}
|
|
423
|
+
];
|
|
345
424
|
}
|
|
346
425
|
if (statement.type === "ExportDefaultDeclaration") {
|
|
347
426
|
if (statement.declaration.type === "Identifier") {
|
|
348
427
|
const local = locals.get(statement.declaration.name);
|
|
349
|
-
return [
|
|
350
|
-
|
|
428
|
+
return [
|
|
429
|
+
{
|
|
430
|
+
...local ?? {
|
|
431
|
+
exportedName: "default",
|
|
432
|
+
node: statement,
|
|
433
|
+
typeOnly: false,
|
|
434
|
+
default: true,
|
|
435
|
+
reExport: false
|
|
436
|
+
},
|
|
351
437
|
exportedName: "default",
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
},
|
|
357
|
-
exportedName: "default",
|
|
358
|
-
localName: local?.localName ?? statement.declaration.name,
|
|
359
|
-
default: true
|
|
360
|
-
}];
|
|
438
|
+
localName: local?.localName ?? statement.declaration.name,
|
|
439
|
+
default: true
|
|
440
|
+
}
|
|
441
|
+
];
|
|
361
442
|
}
|
|
362
443
|
if (statement.declaration.type.endsWith("Declaration")) {
|
|
363
444
|
const bindings = declarationBindings(statement.declaration);
|
|
@@ -464,7 +545,7 @@ function namingMatchTemplate(basename, template, placeholderPattern = "[a-z0-9-]
|
|
|
464
545
|
}
|
|
465
546
|
var namingStemModes = ["before-first-dot", "full-basename"];
|
|
466
547
|
function namingNormalized(value, normalize) {
|
|
467
|
-
return normalize === "none" ? value : value.replaceAll(/[-_]/g, "").toLowerCase();
|
|
548
|
+
return normalize === "none" ? value : value.replaceAll(/[-_.]/g, "").toLowerCase();
|
|
468
549
|
}
|
|
469
550
|
function namingFileStem(basename, mode = "before-first-dot", trailingRoles = [], roleSeparators = [".", "-"]) {
|
|
470
551
|
const stem = mode === "full-basename" ? basename : basename.split(".")[0] ?? "";
|
|
@@ -504,13 +585,16 @@ function namingFilePrefixes(stem, normalize = "remove-separators", singularize =
|
|
|
504
585
|
}
|
|
505
586
|
|
|
506
587
|
// src/utils/helpers/factories.ts
|
|
507
|
-
function factoriesExpandTemplate(template, groups) {
|
|
588
|
+
function factoriesExpandTemplate(template, groups, camelCase = false) {
|
|
508
589
|
return template.replaceAll(/\{([^}]+)\}/g, (placeholder, key) => {
|
|
509
590
|
const group = groups[key.toLowerCase()];
|
|
510
591
|
if (!group) {
|
|
511
592
|
return placeholder;
|
|
512
593
|
}
|
|
513
|
-
|
|
594
|
+
if (/^[A-Z]/.test(key)) {
|
|
595
|
+
return namingPascalCase(group);
|
|
596
|
+
}
|
|
597
|
+
return camelCase ? namingCamelCase(group) : group;
|
|
514
598
|
});
|
|
515
599
|
}
|
|
516
600
|
function factoriesCollectTopLevel(program, pattern) {
|
|
@@ -547,15 +631,130 @@ function factoriesObjectPropertyName(property) {
|
|
|
547
631
|
}
|
|
548
632
|
return property.key.type === "Literal" && typeof property.key.value === "string" ? property.key.value : undefined;
|
|
549
633
|
}
|
|
634
|
+
// src/utils/helpers/guards.ts
|
|
635
|
+
var guardsPrimitiveNames = new Set([
|
|
636
|
+
"string",
|
|
637
|
+
"number",
|
|
638
|
+
"boolean",
|
|
639
|
+
"object",
|
|
640
|
+
"undefined",
|
|
641
|
+
"bigint",
|
|
642
|
+
"symbol",
|
|
643
|
+
"function"
|
|
644
|
+
]);
|
|
645
|
+
function guardsValue(node, state) {
|
|
646
|
+
const value = trivialUnwrap(node);
|
|
647
|
+
if (value.type === "Identifier")
|
|
648
|
+
return state.values.has(value.name);
|
|
649
|
+
return value.type === "MemberExpression" && value.computed && value.object.type === "Identifier" && state.values.has(value.object.name) && value.property.type === "Identifier" && state.values.has(value.property.name);
|
|
650
|
+
}
|
|
651
|
+
function guardsFallback(node) {
|
|
652
|
+
return node.type === "Literal" && !("regex" in node) || node.type === "Identifier" && node.name === "undefined" || node.type === "ArrayExpression" && node.elements.length === 0 || node.type === "ObjectExpression" && node.properties.length === 0;
|
|
653
|
+
}
|
|
654
|
+
function guardsPredicate(expression, state) {
|
|
655
|
+
const node = trivialUnwrap(expression);
|
|
656
|
+
if (node.type === "Identifier")
|
|
657
|
+
return state.predicates.has(node.name);
|
|
658
|
+
if (node.type === "UnaryExpression" && node.operator === "!")
|
|
659
|
+
return guardsPredicate(node.argument, state);
|
|
660
|
+
if (node.type === "LogicalExpression" && (node.operator === "&&" || node.operator === "||")) {
|
|
661
|
+
return guardsPredicate(node.left, state) && guardsPredicate(node.right, state);
|
|
662
|
+
}
|
|
663
|
+
if (node.type === "BinaryExpression" && ["===", "!==", "==", "!="].includes(node.operator)) {
|
|
664
|
+
for (const [left, right] of [
|
|
665
|
+
[node.left, node.right],
|
|
666
|
+
[node.right, node.left]
|
|
667
|
+
]) {
|
|
668
|
+
if (left.type === "UnaryExpression" && left.operator === "typeof" && right.type === "Literal" && typeof right.value === "string" && guardsPrimitiveNames.has(right.value) && guardsValue(left.argument, state)) {
|
|
669
|
+
state.primitive = true;
|
|
670
|
+
return true;
|
|
671
|
+
}
|
|
672
|
+
if (left.type !== "PrivateIdentifier" && guardsValue(left, state) && (right.type === "Literal" && right.value === null || right.type === "Identifier" && right.name === "undefined"))
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
if (node.type === "CallExpression" && !node.optional && node.arguments.length === 1 && node.arguments[0].type !== "SpreadElement" && guardsValue(node.arguments[0], state) && node.callee.type === "MemberExpression" && !node.callee.computed && !node.callee.optional && node.callee.object.type === "Identifier" && node.callee.object.name === "Array" && node.callee.property.type === "Identifier" && node.callee.property.name === "isArray" && state.globalArray(node.callee.object)) {
|
|
677
|
+
state.primitive = true;
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
function guardsResult(node, state) {
|
|
683
|
+
const value = trivialUnwrap(node);
|
|
684
|
+
if (guardsValue(value, state) || guardsFallback(value) || guardsPredicate(value, state))
|
|
685
|
+
return true;
|
|
686
|
+
return value.type === "ConditionalExpression" && guardsPredicate(value.test, state) && guardsResult(value.consequent, state) && guardsResult(value.alternate, state);
|
|
687
|
+
}
|
|
688
|
+
function guardsStatement(node, state) {
|
|
689
|
+
if (node.type === "EmptyStatement" || node.type === "ExpressionStatement" && Boolean(node.directive))
|
|
690
|
+
return true;
|
|
691
|
+
if (node.type === "ReturnStatement" && node.argument) {
|
|
692
|
+
state.returned = true;
|
|
693
|
+
return guardsResult(node.argument, state);
|
|
694
|
+
}
|
|
695
|
+
if (node.type === "BlockStatement")
|
|
696
|
+
return node.body.every((statement) => guardsStatement(statement, state));
|
|
697
|
+
if (node.type === "IfStatement") {
|
|
698
|
+
return guardsPredicate(node.test, state) && guardsStatement(node.consequent, state) && (!node.alternate || guardsStatement(node.alternate, state));
|
|
699
|
+
}
|
|
700
|
+
if (node.type === "VariableDeclaration" && node.kind === "const") {
|
|
701
|
+
return node.declarations.every((declaration) => {
|
|
702
|
+
if (declaration.id.type !== "Identifier" || !declaration.init)
|
|
703
|
+
return false;
|
|
704
|
+
if (guardsValue(declaration.init, state)) {
|
|
705
|
+
state.values.add(declaration.id.name);
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
if (guardsPredicate(declaration.init, state)) {
|
|
709
|
+
state.predicates.add(declaration.id.name);
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
return guardsFallback(declaration.init);
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
function guardsIsGenericHelper(node, globalArray) {
|
|
718
|
+
if (!node.body || node.params.length === 0 || node.params.some((param) => param.type !== "Identifier"))
|
|
719
|
+
return false;
|
|
720
|
+
const state = {
|
|
721
|
+
values: new Set(node.params.flatMap((param) => param.type === "Identifier" ? [param.name] : [])),
|
|
722
|
+
predicates: new Set,
|
|
723
|
+
primitive: false,
|
|
724
|
+
returned: false,
|
|
725
|
+
globalArray
|
|
726
|
+
};
|
|
727
|
+
if (node.body.type !== "BlockStatement")
|
|
728
|
+
return guardsResult(node.body, state) && state.primitive;
|
|
729
|
+
return node.body.body.every((statement) => guardsStatement(statement, state)) && state.primitive && state.returned;
|
|
730
|
+
}
|
|
731
|
+
// src/utils/helpers/layout.ts
|
|
732
|
+
var layoutIndentSchema = {
|
|
733
|
+
anyOf: [
|
|
734
|
+
{ type: "integer", minimum: 0 },
|
|
735
|
+
{ type: "string", enum: ["tab"] }
|
|
736
|
+
]
|
|
737
|
+
};
|
|
738
|
+
function layoutLineIndent(text, offset) {
|
|
739
|
+
const lineStart = text.lastIndexOf(`
|
|
740
|
+
`, offset - 1) + 1;
|
|
741
|
+
return /^[\t ]*/.exec(text.slice(lineStart, offset))?.[0] ?? "";
|
|
742
|
+
}
|
|
743
|
+
function layoutIndentUnit(indent = 2) {
|
|
744
|
+
return indent === "tab" ? "\t" : " ".repeat(indent);
|
|
745
|
+
}
|
|
746
|
+
function layoutNewline(text) {
|
|
747
|
+
return text.includes(`\r
|
|
748
|
+
`) ? `\r
|
|
749
|
+
` : `
|
|
750
|
+
`;
|
|
751
|
+
}
|
|
550
752
|
// src/utils/helpers/options.ts
|
|
551
753
|
function optionsFirst(context, fallback) {
|
|
552
754
|
return context.options[0] ?? fallback;
|
|
553
755
|
}
|
|
554
756
|
var optionsPatternSchema = {
|
|
555
|
-
anyOf: [
|
|
556
|
-
{ type: "string" },
|
|
557
|
-
{ type: "array", items: { type: "string" }, minItems: 1 }
|
|
558
|
-
]
|
|
757
|
+
anyOf: [{ type: "string" }, { type: "array", items: { type: "string" }, minItems: 1 }]
|
|
559
758
|
};
|
|
560
759
|
function optionsPatterns(pattern, flags) {
|
|
561
760
|
return pattern === undefined ? [] : [pattern].flat().map((source) => new RegExp(source, flags));
|
|
@@ -774,46 +973,13 @@ function schemasIsAllowedElement(node, allowScalars, namespaces, structuralMetho
|
|
|
774
973
|
function schemasTypeOperator(node, namespaces, operators) {
|
|
775
974
|
return node.typeName.type === "TSQualifiedName" && node.typeName.left.type === "Identifier" && namespaces.has(node.typeName.left.name) && operators.has(node.typeName.right.name) ? node.typeName.right.name : undefined;
|
|
776
975
|
}
|
|
777
|
-
// src/utils/helpers/
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
const
|
|
783
|
-
return
|
|
784
|
-
}
|
|
785
|
-
function commentsIsDirective(value) {
|
|
786
|
-
return value.startsWith(COMMENTS_LEGAL_BLOCK) || COMMENTS_DIRECTIVE.test(value);
|
|
787
|
-
}
|
|
788
|
-
function commentsIsDisableNextLine(value) {
|
|
789
|
-
return COMMENTS_DISABLE_NEXT_LINE.test(value);
|
|
790
|
-
}
|
|
791
|
-
function commentsIsWhy(value) {
|
|
792
|
-
return value.startsWith("Why: ");
|
|
793
|
-
}
|
|
794
|
-
function commentsIsJsdoc(source, comment) {
|
|
795
|
-
return comment.type === "Block" && source.startsWith("/**", comment.range[0]);
|
|
796
|
-
}
|
|
797
|
-
// src/utils/helpers/layout.ts
|
|
798
|
-
var layoutIndentSchema = {
|
|
799
|
-
anyOf: [
|
|
800
|
-
{ type: "integer", minimum: 0 },
|
|
801
|
-
{ type: "string", enum: ["tab"] }
|
|
802
|
-
]
|
|
803
|
-
};
|
|
804
|
-
function layoutLineIndent(text, offset) {
|
|
805
|
-
const lineStart = text.lastIndexOf(`
|
|
806
|
-
`, offset - 1) + 1;
|
|
807
|
-
return /^[\t ]*/.exec(text.slice(lineStart, offset))?.[0] ?? "";
|
|
808
|
-
}
|
|
809
|
-
function layoutIndentUnit(indent = 2) {
|
|
810
|
-
return indent === "tab" ? "\t" : " ".repeat(indent);
|
|
811
|
-
}
|
|
812
|
-
function layoutNewline(text) {
|
|
813
|
-
return text.includes(`\r
|
|
814
|
-
`) ? `\r
|
|
815
|
-
` : `
|
|
816
|
-
`;
|
|
976
|
+
// src/utils/helpers/boundaries.ts
|
|
977
|
+
import { resolve, dirname } from "node:path";
|
|
978
|
+
function boundaryResolveTarget(options, file, source) {
|
|
979
|
+
const owner = [...options.web, ...options.backend].sort((a, b) => b.length - a.length).find((root) => file.startsWith(root + "/"));
|
|
980
|
+
const web = owner && options.web.includes(owner) ? owner : undefined;
|
|
981
|
+
const alias = Object.entries(options.aliases).sort(([a], [b]) => b.length - a.length).find(([prefix]) => source === prefix || source.startsWith(`${prefix}/`));
|
|
982
|
+
return alias ? resolve(alias[1], source.slice(alias[0].length).replace(/^\//, "")).replaceAll("\\", "/") : source.startsWith(".") ? resolve(dirname(file), source).replaceAll("\\", "/") : source.startsWith("@/") && web ? `${web}/${source.slice(2)}` : source.startsWith("#/") && owner ? `${owner}/${source.slice(2)}` : source.startsWith("#services/") && owner ? `${owner}/services/${source.slice(10)}` : source;
|
|
817
983
|
}
|
|
818
984
|
// src/rules/call-array-multiline.ts
|
|
819
985
|
function callArrayMultilineCallee(node) {
|
|
@@ -886,15 +1052,17 @@ var callArrayMultiline = defineRule({
|
|
|
886
1052
|
meta: {
|
|
887
1053
|
type: "layout",
|
|
888
1054
|
fixable: "whitespace",
|
|
889
|
-
schema: [
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1055
|
+
schema: [
|
|
1056
|
+
{
|
|
1057
|
+
type: "object",
|
|
1058
|
+
additionalProperties: false,
|
|
1059
|
+
properties: {
|
|
1060
|
+
callees: { type: "array", items: { type: "string" } },
|
|
1061
|
+
minElements: { type: "integer", minimum: 1 },
|
|
1062
|
+
indent: layoutIndentSchema
|
|
1063
|
+
}
|
|
896
1064
|
}
|
|
897
|
-
|
|
1065
|
+
],
|
|
898
1066
|
messages: {
|
|
899
1067
|
multiline: "Put every array element in this call on its own line."
|
|
900
1068
|
}
|
|
@@ -983,28 +1151,30 @@ var chainNewline = defineRule2({
|
|
|
983
1151
|
meta: {
|
|
984
1152
|
type: "layout",
|
|
985
1153
|
fixable: "whitespace",
|
|
986
|
-
schema: [
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1154
|
+
schema: [
|
|
1155
|
+
{
|
|
1156
|
+
type: "object",
|
|
1157
|
+
additionalProperties: false,
|
|
1158
|
+
properties: {
|
|
1159
|
+
groups: {
|
|
1160
|
+
type: "array",
|
|
1161
|
+
items: {
|
|
1162
|
+
type: "object",
|
|
1163
|
+
additionalProperties: false,
|
|
1164
|
+
properties: {
|
|
1165
|
+
minDepth: { type: "integer", minimum: 1 },
|
|
1166
|
+
methods: { type: "array", items: { type: "string" } },
|
|
1167
|
+
onlyMethods: { type: "array", items: { type: "string" } },
|
|
1168
|
+
rootPattern: optionsPatternSchema
|
|
1169
|
+
},
|
|
1170
|
+
required: ["minDepth"]
|
|
1171
|
+
}
|
|
1172
|
+
},
|
|
1173
|
+
indent: layoutIndentSchema
|
|
1003
1174
|
},
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
}],
|
|
1175
|
+
required: ["groups"]
|
|
1176
|
+
}
|
|
1177
|
+
],
|
|
1008
1178
|
messages: {
|
|
1009
1179
|
newline: "Put every method in this call chain on its own line."
|
|
1010
1180
|
}
|
|
@@ -1087,24 +1257,26 @@ import { defineRule as defineRule3 } from "@oxlint/plugins";
|
|
|
1087
1257
|
var declarationName = defineRule3({
|
|
1088
1258
|
meta: {
|
|
1089
1259
|
type: "problem",
|
|
1090
|
-
schema: [
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1260
|
+
schema: [
|
|
1261
|
+
{
|
|
1262
|
+
type: "object",
|
|
1263
|
+
additionalProperties: false,
|
|
1264
|
+
properties: {
|
|
1265
|
+
kinds: {
|
|
1266
|
+
type: "array",
|
|
1267
|
+
items: { type: "string", enum: [...declarationsKinds] }
|
|
1268
|
+
},
|
|
1269
|
+
pattern: optionsPatternSchema,
|
|
1270
|
+
flags: { type: "string" },
|
|
1271
|
+
stem: { type: "string", enum: [...namingStemModes] },
|
|
1272
|
+
trailingRoles: { type: "array", items: { type: "string" } },
|
|
1273
|
+
roleSeparators: { type: "array", items: { type: "string" } },
|
|
1274
|
+
normalize: { type: "string", enum: ["remove-separators", "none"] },
|
|
1275
|
+
singularize: { type: "string", enum: ["none", "trailing-s"] },
|
|
1276
|
+
allowPattern: optionsPatternSchema
|
|
1277
|
+
}
|
|
1106
1278
|
}
|
|
1107
|
-
|
|
1279
|
+
],
|
|
1108
1280
|
messages: {
|
|
1109
1281
|
prefix: "'{{name}}' must start with file prefix '{{prefix}}'.",
|
|
1110
1282
|
pattern: "'{{name}}' must match {{pattern}}."
|
|
@@ -1129,7 +1301,10 @@ var declarationName = defineRule3({
|
|
|
1129
1301
|
context.report({
|
|
1130
1302
|
node: item.node,
|
|
1131
1303
|
messageId: expected.length > 0 ? "pattern" : "prefix",
|
|
1132
|
-
data: expected.length > 0 ? {
|
|
1304
|
+
data: expected.length > 0 ? {
|
|
1305
|
+
name: item.name,
|
|
1306
|
+
pattern: optionsPatternLabel([options.pattern ?? []].flat().filter((source) => source !== ""))
|
|
1307
|
+
} : { name: item.name, prefix: prefixes.join(" or ") }
|
|
1133
1308
|
});
|
|
1134
1309
|
}
|
|
1135
1310
|
return {
|
|
@@ -1160,19 +1335,23 @@ import { defineRule as defineRule4 } from "@oxlint/plugins";
|
|
|
1160
1335
|
var exportFilePrefix = defineRule4({
|
|
1161
1336
|
meta: {
|
|
1162
1337
|
type: "problem",
|
|
1163
|
-
schema: [
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1338
|
+
schema: [
|
|
1339
|
+
{
|
|
1340
|
+
type: "object",
|
|
1341
|
+
additionalProperties: false,
|
|
1342
|
+
properties: {
|
|
1343
|
+
stem: { type: "string", enum: [...namingStemModes] },
|
|
1344
|
+
trailingRoles: { type: "array", items: { type: "string" } },
|
|
1345
|
+
roleSeparators: { type: "array", items: { type: "string" } },
|
|
1346
|
+
normalize: { type: "string", enum: ["remove-separators", "none"] },
|
|
1347
|
+
singularize: { type: "string", enum: ["none", "trailing-s"] },
|
|
1348
|
+
serviceMethods: { type: "boolean" },
|
|
1349
|
+
allFunctions: { type: "boolean" },
|
|
1350
|
+
allDeclarations: { type: "boolean" },
|
|
1351
|
+
allowPattern: optionsPatternSchema
|
|
1352
|
+
}
|
|
1174
1353
|
}
|
|
1175
|
-
|
|
1354
|
+
],
|
|
1176
1355
|
messages: {
|
|
1177
1356
|
prefix: "'{{name}}' must start with file prefix '{{prefix}}'."
|
|
1178
1357
|
}
|
|
@@ -1192,6 +1371,12 @@ var exportFilePrefix = defineRule4({
|
|
|
1192
1371
|
name: binding.localName ?? binding.exportedName,
|
|
1193
1372
|
node: binding.node
|
|
1194
1373
|
}));
|
|
1374
|
+
if (options.allFunctions && !options.allDeclarations) {
|
|
1375
|
+
const exportedNames = new Set(names.map((item) => item.name));
|
|
1376
|
+
names.push(...declarationsCollectNamed(program).filter((item) => !exportedNames.has(item.name) && (item.node.type === "FunctionDeclaration" || item.node.type === "VariableDeclarator" && (item.node.init?.type === "ArrowFunctionExpression" || item.node.init?.type === "FunctionExpression"))));
|
|
1377
|
+
}
|
|
1378
|
+
if (options.serviceMethods)
|
|
1379
|
+
names.push(...declarationsPublicServiceMethods(program));
|
|
1195
1380
|
const seen = new Set;
|
|
1196
1381
|
for (const item of names) {
|
|
1197
1382
|
const key = `${item.name}:${item.node.start}:${item.node.end}`;
|
|
@@ -1216,18 +1401,20 @@ import { defineRule as defineRule5 } from "@oxlint/plugins";
|
|
|
1216
1401
|
var exportNamePattern = defineRule5({
|
|
1217
1402
|
meta: {
|
|
1218
1403
|
type: "problem",
|
|
1219
|
-
schema: [
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1404
|
+
schema: [
|
|
1405
|
+
{
|
|
1406
|
+
type: "object",
|
|
1407
|
+
additionalProperties: false,
|
|
1408
|
+
properties: {
|
|
1409
|
+
pattern: optionsPatternSchema,
|
|
1410
|
+
flags: { type: "string" },
|
|
1411
|
+
ignoreTypeExports: { type: "boolean" },
|
|
1412
|
+
allDeclarations: { type: "boolean" },
|
|
1413
|
+
allowPattern: optionsPatternSchema
|
|
1414
|
+
},
|
|
1415
|
+
required: ["pattern"]
|
|
1416
|
+
}
|
|
1417
|
+
],
|
|
1231
1418
|
messages: {
|
|
1232
1419
|
pattern: "'{{name}}' must match {{pattern}}."
|
|
1233
1420
|
}
|
|
@@ -1268,20 +1455,23 @@ import { defineRule as defineRule6 } from "@oxlint/plugins";
|
|
|
1268
1455
|
var filenameExportName = defineRule6({
|
|
1269
1456
|
meta: {
|
|
1270
1457
|
type: "problem",
|
|
1271
|
-
schema: [
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1458
|
+
schema: [
|
|
1459
|
+
{
|
|
1460
|
+
type: "object",
|
|
1461
|
+
additionalProperties: false,
|
|
1462
|
+
properties: {
|
|
1463
|
+
file: { type: "string" },
|
|
1464
|
+
export: { type: "string" },
|
|
1465
|
+
mode: { type: "string", enum: ["all", "some"] },
|
|
1466
|
+
placeholderPattern: { type: "string" },
|
|
1467
|
+
camelCase: { type: "boolean" },
|
|
1468
|
+
allDeclarations: { type: "boolean" }
|
|
1469
|
+
},
|
|
1470
|
+
required: ["file", "export"]
|
|
1471
|
+
}
|
|
1472
|
+
],
|
|
1283
1473
|
messages: {
|
|
1284
|
-
mismatch: "Function '{{actual}}' must
|
|
1474
|
+
mismatch: "Function '{{actual}}' must be named '{{expected}}' for file '{{file}}'."
|
|
1285
1475
|
}
|
|
1286
1476
|
},
|
|
1287
1477
|
createOnce(context) {
|
|
@@ -1292,13 +1482,14 @@ var filenameExportName = defineRule6({
|
|
|
1292
1482
|
export: exportTemplate,
|
|
1293
1483
|
mode = "all",
|
|
1294
1484
|
placeholderPattern,
|
|
1295
|
-
allDeclarations = false
|
|
1485
|
+
allDeclarations = false,
|
|
1486
|
+
camelCase = false
|
|
1296
1487
|
} = optionsFirst(context);
|
|
1297
1488
|
const groups = namingMatchTemplate(namingFileBasename(context.filename), file, placeholderPattern);
|
|
1298
1489
|
if (!groups) {
|
|
1299
1490
|
return;
|
|
1300
1491
|
}
|
|
1301
|
-
const expected = factoriesExpandTemplate(exportTemplate, groups);
|
|
1492
|
+
const expected = factoriesExpandTemplate(exportTemplate, groups, camelCase);
|
|
1302
1493
|
const functions = allDeclarations ? declarationsCollectFunctions(program) : exportsCollectFunctions(program);
|
|
1303
1494
|
if (mode === "some") {
|
|
1304
1495
|
if (!functions.some((item) => item.name === expected)) {
|
|
@@ -1306,7 +1497,7 @@ var filenameExportName = defineRule6({
|
|
|
1306
1497
|
context.report({
|
|
1307
1498
|
node: first?.node ?? program,
|
|
1308
1499
|
messageId: "mismatch",
|
|
1309
|
-
data: { actual: first?.name ?? "<none>", expected }
|
|
1500
|
+
data: { actual: first?.name ?? "<none>", expected, file: namingFileBasename(context.filename) }
|
|
1310
1501
|
});
|
|
1311
1502
|
}
|
|
1312
1503
|
return;
|
|
@@ -1316,7 +1507,7 @@ var filenameExportName = defineRule6({
|
|
|
1316
1507
|
context.report({
|
|
1317
1508
|
node: item.node,
|
|
1318
1509
|
messageId: "mismatch",
|
|
1319
|
-
data: { actual: item.name, expected }
|
|
1510
|
+
data: { actual: item.name, expected, file: namingFileBasename(context.filename) }
|
|
1320
1511
|
});
|
|
1321
1512
|
}
|
|
1322
1513
|
}
|
|
@@ -1330,16 +1521,18 @@ import { defineRule as defineRule7 } from "@oxlint/plugins";
|
|
|
1330
1521
|
var filenameMatch = defineRule7({
|
|
1331
1522
|
meta: {
|
|
1332
1523
|
type: "problem",
|
|
1333
|
-
schema: [
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1524
|
+
schema: [
|
|
1525
|
+
{
|
|
1526
|
+
type: "object",
|
|
1527
|
+
additionalProperties: false,
|
|
1528
|
+
properties: {
|
|
1529
|
+
pattern: optionsPatternSchema,
|
|
1530
|
+
message: { type: "string" },
|
|
1531
|
+
flags: { type: "string" }
|
|
1532
|
+
},
|
|
1533
|
+
required: ["pattern", "message"]
|
|
1534
|
+
}
|
|
1535
|
+
],
|
|
1343
1536
|
messages: {
|
|
1344
1537
|
mismatch: "{{message}}"
|
|
1345
1538
|
}
|
|
@@ -1366,17 +1559,20 @@ import { defineRule as defineRule8 } from "@oxlint/plugins";
|
|
|
1366
1559
|
var folderPrefix = defineRule8({
|
|
1367
1560
|
meta: {
|
|
1368
1561
|
type: "problem",
|
|
1369
|
-
schema: [
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1562
|
+
schema: [
|
|
1563
|
+
{
|
|
1564
|
+
type: "object",
|
|
1565
|
+
additionalProperties: false,
|
|
1566
|
+
properties: {
|
|
1567
|
+
singularize: { type: "string", enum: ["none", "trailing-s"] },
|
|
1568
|
+
separators: { type: "array", items: { type: "string" } },
|
|
1569
|
+
after: { type: "string" },
|
|
1570
|
+
stripPrefixes: { type: "array", items: { type: "string", minLength: 1 } }
|
|
1571
|
+
}
|
|
1376
1572
|
}
|
|
1377
|
-
|
|
1573
|
+
],
|
|
1378
1574
|
messages: {
|
|
1379
|
-
prefix: "File '{{file}}' must start with
|
|
1575
|
+
prefix: "File '{{file}}' must start with '{{expected}}' to match its domain folder '{{folder}}'."
|
|
1380
1576
|
}
|
|
1381
1577
|
},
|
|
1382
1578
|
createOnce(context) {
|
|
@@ -1385,7 +1581,8 @@ var folderPrefix = defineRule8({
|
|
|
1385
1581
|
const {
|
|
1386
1582
|
singularize = "trailing-s",
|
|
1387
1583
|
separators = ["-"],
|
|
1388
|
-
after
|
|
1584
|
+
after,
|
|
1585
|
+
stripPrefixes = []
|
|
1389
1586
|
} = optionsFirst(context, {});
|
|
1390
1587
|
const filename = namingPosixPath(context.filename);
|
|
1391
1588
|
const folders = after ? namingSegmentsAfter(filename, after) : [namingDirSegments(filename).at(-1) ?? ""];
|
|
@@ -1396,13 +1593,19 @@ var folderPrefix = defineRule8({
|
|
|
1396
1593
|
const separator = separators[0] ?? "-";
|
|
1397
1594
|
const prefixes = namingFolderPrefixes(resolved, separator, singularize);
|
|
1398
1595
|
const file = namingFileBasename(filename);
|
|
1399
|
-
const
|
|
1596
|
+
const rawStem = file.replace(/\.(tsx?|jsx?)$/, "");
|
|
1597
|
+
const strip = stripPrefixes.find((prefix) => rawStem.startsWith(prefix));
|
|
1598
|
+
const stem = strip ? rawStem.slice(strip.length) : rawStem;
|
|
1400
1599
|
const valid = prefixes.some((prefix) => stem === prefix || separators.some((item) => stem.startsWith(`${prefix}${item}`)));
|
|
1401
1600
|
if (!valid) {
|
|
1402
1601
|
context.report({
|
|
1403
1602
|
node: program,
|
|
1404
1603
|
messageId: "prefix",
|
|
1405
|
-
data: {
|
|
1604
|
+
data: {
|
|
1605
|
+
file,
|
|
1606
|
+
folder: resolved.join("/"),
|
|
1607
|
+
expected: prefixes.map((prefix) => `${stripPrefixes[0] ?? ""}${prefix}`).join(" or ")
|
|
1608
|
+
}
|
|
1406
1609
|
});
|
|
1407
1610
|
}
|
|
1408
1611
|
}
|
|
@@ -1421,10 +1624,12 @@ function jsxAttributesMultilineEdits(node, text, attributeIndent, baseIndent, ne
|
|
|
1421
1624
|
const openingRange = [prefix.range[1], first.range[0]];
|
|
1422
1625
|
if (!/^\s*$/.test(text.slice(...openingRange)))
|
|
1423
1626
|
return null;
|
|
1424
|
-
const edits = [
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1627
|
+
const edits = [
|
|
1628
|
+
{
|
|
1629
|
+
range: openingRange,
|
|
1630
|
+
text: `${newline}${attributeIndent}`
|
|
1631
|
+
}
|
|
1632
|
+
];
|
|
1428
1633
|
for (let index = 1;index < node.attributes.length; index++) {
|
|
1429
1634
|
const previous = node.attributes[index - 1];
|
|
1430
1635
|
const current = node.attributes[index];
|
|
@@ -1444,14 +1649,16 @@ var jsxAttributesMultiline = defineRule9({
|
|
|
1444
1649
|
meta: {
|
|
1445
1650
|
type: "layout",
|
|
1446
1651
|
fixable: "whitespace",
|
|
1447
|
-
schema: [
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1652
|
+
schema: [
|
|
1653
|
+
{
|
|
1654
|
+
type: "object",
|
|
1655
|
+
additionalProperties: false,
|
|
1656
|
+
properties: {
|
|
1657
|
+
minAttributes: { type: "integer", minimum: 1 },
|
|
1658
|
+
indent: layoutIndentSchema
|
|
1659
|
+
}
|
|
1453
1660
|
}
|
|
1454
|
-
|
|
1661
|
+
],
|
|
1455
1662
|
messages: {
|
|
1456
1663
|
multiline: "Put every JSX attribute and the closing token on its own line."
|
|
1457
1664
|
}
|
|
@@ -1633,15 +1840,17 @@ var noComments = defineRule11({
|
|
|
1633
1840
|
meta: {
|
|
1634
1841
|
type: "suggestion",
|
|
1635
1842
|
fixable: "code",
|
|
1636
|
-
schema: [
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1843
|
+
schema: [
|
|
1844
|
+
{
|
|
1845
|
+
type: "object",
|
|
1846
|
+
additionalProperties: false,
|
|
1847
|
+
properties: {
|
|
1848
|
+
allowWhy: { type: "boolean", default: true },
|
|
1849
|
+
allowPatterns: { type: "array", items: { type: "string" } },
|
|
1850
|
+
allowJsdoc: { type: "boolean", default: false }
|
|
1851
|
+
}
|
|
1643
1852
|
}
|
|
1644
|
-
|
|
1853
|
+
],
|
|
1645
1854
|
messages: {
|
|
1646
1855
|
comment: "Remove this comment."
|
|
1647
1856
|
}
|
|
@@ -1763,19 +1972,21 @@ import { defineRule as defineRule12 } from "@oxlint/plugins";
|
|
|
1763
1972
|
var noExtraExports = defineRule12({
|
|
1764
1973
|
meta: {
|
|
1765
1974
|
type: "problem",
|
|
1766
|
-
schema: [
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1975
|
+
schema: [
|
|
1976
|
+
{
|
|
1977
|
+
type: "object",
|
|
1978
|
+
additionalProperties: false,
|
|
1979
|
+
properties: {
|
|
1980
|
+
names: { type: "array", items: { type: "string" } },
|
|
1981
|
+
patterns: { type: "array", items: { type: "string" } },
|
|
1982
|
+
domainStem: { type: "string", enum: [...namingStemModes] },
|
|
1983
|
+
trailingRoles: { type: "array", items: { type: "string" } },
|
|
1984
|
+
roleSeparators: { type: "array", items: { type: "string" } },
|
|
1985
|
+
allowTypeExports: { type: "boolean" }
|
|
1986
|
+
},
|
|
1987
|
+
required: ["names"]
|
|
1988
|
+
}
|
|
1989
|
+
],
|
|
1779
1990
|
messages: {
|
|
1780
1991
|
extraExport: "Export '{{name}}' is not allowed; expected one of: {{allowed}}."
|
|
1781
1992
|
}
|
|
@@ -1830,28 +2041,30 @@ function compileFactoryKeys(pattern, keys, requireKeys) {
|
|
|
1830
2041
|
var noExtraFactoryKeys = defineRule13({
|
|
1831
2042
|
meta: {
|
|
1832
2043
|
type: "problem",
|
|
1833
|
-
schema: [
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
2044
|
+
schema: [
|
|
2045
|
+
{
|
|
2046
|
+
type: "object",
|
|
2047
|
+
additionalProperties: false,
|
|
2048
|
+
properties: {
|
|
2049
|
+
keys: { type: "array", items: { type: "string" } },
|
|
2050
|
+
factoryPattern: optionsPatternSchema,
|
|
2051
|
+
requireKeys: { type: "array", items: { type: "string" } },
|
|
2052
|
+
keySets: {
|
|
2053
|
+
type: "array",
|
|
2054
|
+
items: {
|
|
2055
|
+
type: "object",
|
|
2056
|
+
additionalProperties: false,
|
|
2057
|
+
properties: {
|
|
2058
|
+
factoryPattern: optionsPatternSchema,
|
|
2059
|
+
keys: { type: "array", items: { type: "string" } },
|
|
2060
|
+
requireKeys: { type: "array", items: { type: "string" } }
|
|
2061
|
+
},
|
|
2062
|
+
required: ["factoryPattern", "keys"]
|
|
2063
|
+
}
|
|
1851
2064
|
}
|
|
1852
2065
|
}
|
|
1853
2066
|
}
|
|
1854
|
-
|
|
2067
|
+
],
|
|
1855
2068
|
messages: {
|
|
1856
2069
|
extraKey: "Factory '{{factory}}' must not return key '{{key}}'; allowed keys: {{allowed}}.",
|
|
1857
2070
|
missingKey: "Factory '{{factory}}' must return key '{{key}}'.",
|
|
@@ -1904,7 +2117,11 @@ var noExtraFactoryKeys = defineRule13({
|
|
|
1904
2117
|
context.report({
|
|
1905
2118
|
node: property,
|
|
1906
2119
|
messageId: "extraKey",
|
|
1907
|
-
data: {
|
|
2120
|
+
data: {
|
|
2121
|
+
factory: factory.name,
|
|
2122
|
+
key: key ?? "<computed or spread>",
|
|
2123
|
+
allowed: resolved.allowedList.join(", ")
|
|
2124
|
+
}
|
|
1908
2125
|
});
|
|
1909
2126
|
}
|
|
1910
2127
|
}
|
|
@@ -1929,17 +2146,20 @@ import { defineRule as defineRule14 } from "@oxlint/plugins";
|
|
|
1929
2146
|
var noFileLevelHelpers = defineRule14({
|
|
1930
2147
|
meta: {
|
|
1931
2148
|
type: "problem",
|
|
1932
|
-
schema: [
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
2149
|
+
schema: [
|
|
2150
|
+
{
|
|
2151
|
+
type: "object",
|
|
2152
|
+
additionalProperties: false,
|
|
2153
|
+
properties: {
|
|
2154
|
+
allowPattern: optionsPatternSchema,
|
|
2155
|
+
message: { type: "string" },
|
|
2156
|
+
detectComponents: { type: "boolean" },
|
|
2157
|
+
hookPattern: optionsPatternSchema
|
|
2158
|
+
}
|
|
1939
2159
|
}
|
|
1940
|
-
|
|
2160
|
+
],
|
|
1941
2161
|
messages: {
|
|
1942
|
-
helper: "
|
|
2162
|
+
helper: "{{name}}: {{message}}"
|
|
1943
2163
|
}
|
|
1944
2164
|
},
|
|
1945
2165
|
createOnce(context) {
|
|
@@ -1947,6 +2167,7 @@ var noFileLevelHelpers = defineRule14({
|
|
|
1947
2167
|
Program(program) {
|
|
1948
2168
|
const {
|
|
1949
2169
|
allowPattern,
|
|
2170
|
+
message = "Keep this helper inside its owning component or hook.",
|
|
1950
2171
|
detectComponents = true,
|
|
1951
2172
|
hookPattern = "^use[A-Z]"
|
|
1952
2173
|
} = optionsFirst(context, {});
|
|
@@ -1959,7 +2180,7 @@ var noFileLevelHelpers = defineRule14({
|
|
|
1959
2180
|
context.report({
|
|
1960
2181
|
node: candidate.node,
|
|
1961
2182
|
messageId: "helper",
|
|
1962
|
-
data: { name: candidate.name }
|
|
2183
|
+
data: { name: candidate.name, message }
|
|
1963
2184
|
});
|
|
1964
2185
|
}
|
|
1965
2186
|
}
|
|
@@ -1970,7 +2191,7 @@ var noFileLevelHelpers = defineRule14({
|
|
|
1970
2191
|
// src/rules/no-imported-type-alias.ts
|
|
1971
2192
|
import { defineRule as defineRule15 } from "@oxlint/plugins";
|
|
1972
2193
|
function importedAliasTarget(declaration, imports) {
|
|
1973
|
-
if (declaration?.type !== "TSTypeAliasDeclaration" || declaration.typeAnnotation.type !== "TSTypeReference" || declaration.typeAnnotation.typeName.type !== "Identifier" || !imports.has(declaration.typeAnnotation.typeName.name)) {
|
|
2194
|
+
if (declaration?.type !== "TSTypeAliasDeclaration" || declaration.typeAnnotation.type !== "TSTypeReference" || declaration.typeAnnotation.typeName.type !== "Identifier" || Boolean(declaration.typeAnnotation.typeArguments?.params.length) || !imports.has(declaration.typeAnnotation.typeName.name)) {
|
|
1974
2195
|
return;
|
|
1975
2196
|
}
|
|
1976
2197
|
return declaration.typeAnnotation.typeName.name;
|
|
@@ -1983,13 +2204,29 @@ var noImportedTypeAlias = defineRule15({
|
|
|
1983
2204
|
type: "problem",
|
|
1984
2205
|
schema: [],
|
|
1985
2206
|
messages: {
|
|
1986
|
-
alias: "
|
|
2207
|
+
alias: "Import '{{name}}' from '{{source}}' at the callers instead of exporting alias '{{alias}}'. Keep genuine derivations; do not substitute an empty interface."
|
|
1987
2208
|
}
|
|
1988
2209
|
},
|
|
1989
2210
|
createOnce(context) {
|
|
1990
2211
|
return {
|
|
1991
2212
|
Program(program) {
|
|
1992
|
-
const imports = new Set
|
|
2213
|
+
const imports = new Set;
|
|
2214
|
+
const sources = new Map;
|
|
2215
|
+
const names = new Map;
|
|
2216
|
+
for (const statement of program.body) {
|
|
2217
|
+
if (statement.type !== "ImportDeclaration")
|
|
2218
|
+
continue;
|
|
2219
|
+
for (const specifier of statement.specifiers) {
|
|
2220
|
+
const local = specifier.local.name;
|
|
2221
|
+
let name = local;
|
|
2222
|
+
if (specifier.type === "ImportSpecifier") {
|
|
2223
|
+
name = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
2224
|
+
}
|
|
2225
|
+
imports.add(local);
|
|
2226
|
+
sources.set(local, statement.source.value);
|
|
2227
|
+
names.set(local, name);
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
1993
2230
|
const aliases = new Map;
|
|
1994
2231
|
for (const statement of program.body) {
|
|
1995
2232
|
const declaration = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
|
|
@@ -2007,7 +2244,11 @@ var noImportedTypeAlias = defineRule15({
|
|
|
2007
2244
|
context.report({
|
|
2008
2245
|
node: statement.declaration,
|
|
2009
2246
|
messageId: "alias",
|
|
2010
|
-
data: {
|
|
2247
|
+
data: {
|
|
2248
|
+
alias: statement.declaration.id.name,
|
|
2249
|
+
name: names.get(inline) ?? inline,
|
|
2250
|
+
source: sources.get(inline) ?? "<import source>"
|
|
2251
|
+
}
|
|
2011
2252
|
});
|
|
2012
2253
|
}
|
|
2013
2254
|
for (const specifier of statement.specifiers) {
|
|
@@ -2017,7 +2258,11 @@ var noImportedTypeAlias = defineRule15({
|
|
|
2017
2258
|
context.report({
|
|
2018
2259
|
node: specifier,
|
|
2019
2260
|
messageId: "alias",
|
|
2020
|
-
data: {
|
|
2261
|
+
data: {
|
|
2262
|
+
alias: local,
|
|
2263
|
+
name: names.get(imported) ?? imported,
|
|
2264
|
+
source: sources.get(imported) ?? "<import source>"
|
|
2265
|
+
}
|
|
2021
2266
|
});
|
|
2022
2267
|
}
|
|
2023
2268
|
}
|
|
@@ -2032,22 +2277,35 @@ import { defineRule as defineRule16 } from "@oxlint/plugins";
|
|
|
2032
2277
|
var noInlineSchemaElements = defineRule16({
|
|
2033
2278
|
meta: {
|
|
2034
2279
|
type: "problem",
|
|
2035
|
-
schema: [
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2280
|
+
schema: [
|
|
2281
|
+
{
|
|
2282
|
+
type: "object",
|
|
2283
|
+
additionalProperties: false,
|
|
2284
|
+
properties: {
|
|
2285
|
+
namespaces: { type: "array", items: { type: "string" } },
|
|
2286
|
+
methods: { type: "array", items: { type: "string" } },
|
|
2287
|
+
structuralMethods: { type: "array", items: { type: "string" } },
|
|
2288
|
+
allowZodScalars: { type: "boolean" }
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
],
|
|
2292
|
+
defaultOptions: [
|
|
2293
|
+
{
|
|
2294
|
+
namespaces: ["z"],
|
|
2295
|
+
methods: ["array", "union", "record", "tuple"],
|
|
2296
|
+
structuralMethods: [
|
|
2297
|
+
"array",
|
|
2298
|
+
"discriminatedUnion",
|
|
2299
|
+
"intersection",
|
|
2300
|
+
"lazy",
|
|
2301
|
+
"object",
|
|
2302
|
+
"record",
|
|
2303
|
+
"tuple",
|
|
2304
|
+
"union"
|
|
2305
|
+
],
|
|
2306
|
+
allowZodScalars: true
|
|
2043
2307
|
}
|
|
2044
|
-
|
|
2045
|
-
defaultOptions: [{
|
|
2046
|
-
namespaces: ["z"],
|
|
2047
|
-
methods: ["array", "union", "record", "tuple"],
|
|
2048
|
-
structuralMethods: ["array", "discriminatedUnion", "intersection", "lazy", "object", "record", "tuple", "union"],
|
|
2049
|
-
allowZodScalars: true
|
|
2050
|
-
}],
|
|
2308
|
+
],
|
|
2051
2309
|
messages: { inline: "z.{{method}}() elements must use named schemas." }
|
|
2052
2310
|
},
|
|
2053
2311
|
createOnce(context) {
|
|
@@ -2090,16 +2348,18 @@ function parameterAnnotation(parameter) {
|
|
|
2090
2348
|
var noInlineTypes = defineRule17({
|
|
2091
2349
|
meta: {
|
|
2092
2350
|
type: "problem",
|
|
2093
|
-
schema: [
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2351
|
+
schema: [
|
|
2352
|
+
{
|
|
2353
|
+
type: "object",
|
|
2354
|
+
additionalProperties: false,
|
|
2355
|
+
properties: {
|
|
2356
|
+
parameters: { type: "boolean" },
|
|
2357
|
+
returns: { type: "boolean" },
|
|
2358
|
+
functionTypes: { type: "boolean" },
|
|
2359
|
+
minMembers: { type: "integer", minimum: 1 }
|
|
2360
|
+
}
|
|
2101
2361
|
}
|
|
2102
|
-
|
|
2362
|
+
],
|
|
2103
2363
|
messages: {
|
|
2104
2364
|
inlineType: "Use a named type instead of an inline {{kind}} type in this {{position}} annotation."
|
|
2105
2365
|
}
|
|
@@ -2167,13 +2427,15 @@ function literalKey(node) {
|
|
|
2167
2427
|
var noLiteralIn = defineRule18({
|
|
2168
2428
|
meta: {
|
|
2169
2429
|
type: "suggestion",
|
|
2170
|
-
schema: [
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2430
|
+
schema: [
|
|
2431
|
+
{
|
|
2432
|
+
type: "object",
|
|
2433
|
+
additionalProperties: false,
|
|
2434
|
+
properties: {
|
|
2435
|
+
allow: { type: "array", items: { type: "string" } }
|
|
2436
|
+
}
|
|
2175
2437
|
}
|
|
2176
|
-
|
|
2438
|
+
],
|
|
2177
2439
|
defaultOptions: [{ allow: [] }],
|
|
2178
2440
|
messages: {
|
|
2179
2441
|
literalIn: "Avoid '{{key}}' in value; use Object.hasOwn() or a discriminated union."
|
|
@@ -2212,23 +2474,27 @@ function formatMessage(template, data) {
|
|
|
2212
2474
|
var noLocalSchemaConstruction = defineRule19({
|
|
2213
2475
|
meta: {
|
|
2214
2476
|
type: "problem",
|
|
2215
|
-
schema: [
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2477
|
+
schema: [
|
|
2478
|
+
{
|
|
2479
|
+
type: "object",
|
|
2480
|
+
additionalProperties: false,
|
|
2481
|
+
properties: {
|
|
2482
|
+
packages: { type: "array", items: { type: "string" } },
|
|
2483
|
+
namespaces: { type: "array", items: { type: "string" } },
|
|
2484
|
+
allowIn: { type: "array", items: { type: "string" } },
|
|
2485
|
+
allowPathPatterns: { type: "array", items: { type: "string" } },
|
|
2486
|
+
message: { type: "string" },
|
|
2487
|
+
runtimeImportMessage: { type: "string" },
|
|
2488
|
+
constructionMessage: { type: "string" }
|
|
2489
|
+
}
|
|
2226
2490
|
}
|
|
2227
|
-
|
|
2228
|
-
defaultOptions: [
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2491
|
+
],
|
|
2492
|
+
defaultOptions: [
|
|
2493
|
+
{
|
|
2494
|
+
packages: ["zod"],
|
|
2495
|
+
namespaces: ["z"]
|
|
2496
|
+
}
|
|
2497
|
+
],
|
|
2232
2498
|
messages: {
|
|
2233
2499
|
runtimeImport: "{{message}}",
|
|
2234
2500
|
construction: "{{message}}"
|
|
@@ -2265,7 +2531,9 @@ var noLocalSchemaConstruction = defineRule19({
|
|
|
2265
2531
|
node,
|
|
2266
2532
|
messageId: "runtimeImport",
|
|
2267
2533
|
data: {
|
|
2268
|
-
message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
|
|
2534
|
+
message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
|
|
2535
|
+
source: node.source.value
|
|
2536
|
+
})
|
|
2269
2537
|
}
|
|
2270
2538
|
});
|
|
2271
2539
|
},
|
|
@@ -2282,7 +2550,9 @@ var noLocalSchemaConstruction = defineRule19({
|
|
|
2282
2550
|
node,
|
|
2283
2551
|
messageId: "runtimeImport",
|
|
2284
2552
|
data: {
|
|
2285
|
-
message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
|
|
2553
|
+
message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
|
|
2554
|
+
source: node.source.value
|
|
2555
|
+
})
|
|
2286
2556
|
}
|
|
2287
2557
|
});
|
|
2288
2558
|
},
|
|
@@ -2299,7 +2569,9 @@ var noLocalSchemaConstruction = defineRule19({
|
|
|
2299
2569
|
node,
|
|
2300
2570
|
messageId: "runtimeImport",
|
|
2301
2571
|
data: {
|
|
2302
|
-
message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
|
|
2572
|
+
message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
|
|
2573
|
+
source: node.source.value
|
|
2574
|
+
})
|
|
2303
2575
|
}
|
|
2304
2576
|
});
|
|
2305
2577
|
},
|
|
@@ -2326,13 +2598,15 @@ var noMemberComments = defineRule20({
|
|
|
2326
2598
|
meta: {
|
|
2327
2599
|
type: "suggestion",
|
|
2328
2600
|
fixable: "code",
|
|
2329
|
-
schema: [
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2601
|
+
schema: [
|
|
2602
|
+
{
|
|
2603
|
+
type: "object",
|
|
2604
|
+
additionalProperties: false,
|
|
2605
|
+
properties: {
|
|
2606
|
+
allowWhy: { type: "boolean", default: true }
|
|
2607
|
+
}
|
|
2334
2608
|
}
|
|
2335
|
-
|
|
2609
|
+
],
|
|
2336
2610
|
messages: {
|
|
2337
2611
|
memberComment: "Remove this comment from the type member."
|
|
2338
2612
|
}
|
|
@@ -2458,17 +2732,19 @@ function moduleMutableStateInsideAmbient(node) {
|
|
|
2458
2732
|
var noModuleMutableState = defineRule21({
|
|
2459
2733
|
meta: {
|
|
2460
2734
|
type: "problem",
|
|
2461
|
-
schema: [
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2735
|
+
schema: [
|
|
2736
|
+
{
|
|
2737
|
+
type: "object",
|
|
2738
|
+
additionalProperties: false,
|
|
2739
|
+
properties: {
|
|
2740
|
+
kinds: {
|
|
2741
|
+
type: "array",
|
|
2742
|
+
items: { type: "string", enum: ["let", "var"] }
|
|
2743
|
+
},
|
|
2744
|
+
allowNamePattern: optionsPatternSchema
|
|
2745
|
+
}
|
|
2470
2746
|
}
|
|
2471
|
-
|
|
2747
|
+
],
|
|
2472
2748
|
messages: {
|
|
2473
2749
|
moduleState: "module state is shared across requests/tests; keep it local."
|
|
2474
2750
|
}
|
|
@@ -2536,20 +2812,24 @@ function mutationBindings(node) {
|
|
|
2536
2812
|
var noPromiseAllMutation = defineRule22({
|
|
2537
2813
|
meta: {
|
|
2538
2814
|
type: "problem",
|
|
2539
|
-
schema: [
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2815
|
+
schema: [
|
|
2816
|
+
{
|
|
2817
|
+
type: "object",
|
|
2818
|
+
additionalProperties: false,
|
|
2819
|
+
properties: {
|
|
2820
|
+
combinators: { type: "array", items: { type: "string" } },
|
|
2821
|
+
methods: { type: "array", items: { type: "string" } },
|
|
2822
|
+
checkAssignments: { type: "boolean" }
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
],
|
|
2826
|
+
defaultOptions: [
|
|
2827
|
+
{
|
|
2828
|
+
combinators: ["Promise.all", "Promise.allSettled", "Promise.any", "Promise.race"],
|
|
2829
|
+
methods: ["push", "unshift", "splice", "set", "add"],
|
|
2830
|
+
checkAssignments: true
|
|
2546
2831
|
}
|
|
2547
|
-
|
|
2548
|
-
defaultOptions: [{
|
|
2549
|
-
combinators: ["Promise.all", "Promise.allSettled", "Promise.any", "Promise.race"],
|
|
2550
|
-
methods: ["push", "unshift", "splice", "set", "add"],
|
|
2551
|
-
checkAssignments: true
|
|
2552
|
-
}],
|
|
2832
|
+
],
|
|
2553
2833
|
messages: {
|
|
2554
2834
|
mutation: "Do not mutate outer binding '{{name}}' inside {{combinator}}()."
|
|
2555
2835
|
}
|
|
@@ -2633,16 +2913,18 @@ import { defineRule as defineRule23 } from "@oxlint/plugins";
|
|
|
2633
2913
|
var noRederiveSchema = defineRule23({
|
|
2634
2914
|
meta: {
|
|
2635
2915
|
type: "problem",
|
|
2636
|
-
schema: [
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2916
|
+
schema: [
|
|
2917
|
+
{
|
|
2918
|
+
type: "object",
|
|
2919
|
+
additionalProperties: false,
|
|
2920
|
+
properties: {
|
|
2921
|
+
from: { type: "array", items: { type: "string" } },
|
|
2922
|
+
namespaces: { type: "array", items: { type: "string" } },
|
|
2923
|
+
operators: { type: "array", items: { type: "string" } }
|
|
2924
|
+
},
|
|
2925
|
+
required: ["from"]
|
|
2926
|
+
}
|
|
2927
|
+
],
|
|
2646
2928
|
messages: {
|
|
2647
2929
|
rederive: "Do not derive {{operator}} from imported schema {{schema}}; import its DTO type."
|
|
2648
2930
|
}
|
|
@@ -2655,10 +2937,7 @@ var noRederiveSchema = defineRule23({
|
|
|
2655
2937
|
imported = new Set(program.body.flatMap((statement) => statement.type === "ImportDeclaration" && from.includes(statement.source.value) ? statement.specifiers.map((specifier) => specifier.local.name) : []));
|
|
2656
2938
|
},
|
|
2657
2939
|
TSTypeReference(node) {
|
|
2658
|
-
const {
|
|
2659
|
-
namespaces = ["z"],
|
|
2660
|
-
operators = ["infer", "input"]
|
|
2661
|
-
} = optionsFirst(context);
|
|
2940
|
+
const { namespaces = ["z"], operators = ["infer", "input"] } = optionsFirst(context);
|
|
2662
2941
|
const operator = schemasTypeOperator(node, new Set(namespaces), new Set(operators));
|
|
2663
2942
|
const parameter = node.typeArguments?.params[0];
|
|
2664
2943
|
if (!operator || parameter?.type !== "TSTypeQuery" || parameter.exprName.type !== "Identifier" || !imported.has(parameter.exprName.name)) {
|
|
@@ -2694,32 +2973,34 @@ function restrictedConstructorEntries(constructors) {
|
|
|
2694
2973
|
var noRestrictedConstructor = defineRule24({
|
|
2695
2974
|
meta: {
|
|
2696
2975
|
type: "problem",
|
|
2697
|
-
schema: [
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2976
|
+
schema: [
|
|
2977
|
+
{
|
|
2978
|
+
type: "object",
|
|
2979
|
+
additionalProperties: false,
|
|
2980
|
+
properties: {
|
|
2981
|
+
constructors: {
|
|
2982
|
+
type: "array",
|
|
2983
|
+
items: {
|
|
2984
|
+
anyOf: [
|
|
2985
|
+
{ type: "string" },
|
|
2986
|
+
{
|
|
2987
|
+
type: "object",
|
|
2988
|
+
additionalProperties: false,
|
|
2989
|
+
properties: {
|
|
2990
|
+
name: { type: "string" },
|
|
2991
|
+
message: { type: "string" }
|
|
2992
|
+
},
|
|
2993
|
+
required: ["name"]
|
|
2994
|
+
}
|
|
2995
|
+
]
|
|
2996
|
+
}
|
|
2997
|
+
},
|
|
2998
|
+
checkCalls: { type: "boolean", default: false },
|
|
2999
|
+
message: { type: "string" }
|
|
2717
3000
|
},
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
required: ["constructors"]
|
|
2722
|
-
}],
|
|
3001
|
+
required: ["constructors"]
|
|
3002
|
+
}
|
|
3003
|
+
],
|
|
2723
3004
|
messages: {
|
|
2724
3005
|
construct: "Do not construct '{{name}}'.{{suffix}}",
|
|
2725
3006
|
call: "Do not call '{{name}}'.{{suffix}}"
|
|
@@ -2768,14 +3049,16 @@ import { defineRule as defineRule25 } from "@oxlint/plugins";
|
|
|
2768
3049
|
var noRestrictedFiles = defineRule25({
|
|
2769
3050
|
meta: {
|
|
2770
3051
|
type: "problem",
|
|
2771
|
-
schema: [
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
3052
|
+
schema: [
|
|
3053
|
+
{
|
|
3054
|
+
type: "object",
|
|
3055
|
+
additionalProperties: false,
|
|
3056
|
+
properties: {
|
|
3057
|
+
message: { type: "string" }
|
|
3058
|
+
},
|
|
3059
|
+
required: ["message"]
|
|
3060
|
+
}
|
|
3061
|
+
],
|
|
2779
3062
|
messages: {
|
|
2780
3063
|
restricted: "{{message}}"
|
|
2781
3064
|
}
|
|
@@ -2809,37 +3092,36 @@ function memberMatches(node, restriction) {
|
|
|
2809
3092
|
var noRestrictedToken = defineRule26({
|
|
2810
3093
|
meta: {
|
|
2811
3094
|
type: "problem",
|
|
2812
|
-
schema: [
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
3095
|
+
schema: [
|
|
3096
|
+
{
|
|
3097
|
+
type: "object",
|
|
3098
|
+
additionalProperties: false,
|
|
3099
|
+
properties: {
|
|
3100
|
+
token: { type: "string" },
|
|
3101
|
+
allowIn: { type: "array", items: { type: "string" } },
|
|
3102
|
+
allowPathPatterns: { type: "array", items: { type: "string" } },
|
|
3103
|
+
restrictions: {
|
|
3104
|
+
type: "array",
|
|
3105
|
+
items: {
|
|
3106
|
+
type: "object",
|
|
3107
|
+
additionalProperties: false,
|
|
3108
|
+
properties: {
|
|
3109
|
+
token: { type: "string" },
|
|
3110
|
+
member: { type: "string" },
|
|
3111
|
+
allowIn: { type: "array", items: { type: "string" } },
|
|
3112
|
+
allowPathPatterns: { type: "array", items: { type: "string" } },
|
|
3113
|
+
message: { type: "string" }
|
|
3114
|
+
},
|
|
3115
|
+
oneOf: [
|
|
3116
|
+
{ required: ["token"], not: { required: ["member"] } },
|
|
3117
|
+
{ required: ["member"], not: { required: ["token"] } }
|
|
3118
|
+
]
|
|
3119
|
+
}
|
|
2835
3120
|
}
|
|
2836
|
-
}
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
{ required: ["restrictions"] }
|
|
2841
|
-
]
|
|
2842
|
-
}],
|
|
3121
|
+
},
|
|
3122
|
+
anyOf: [{ required: ["token"] }, { required: ["restrictions"] }]
|
|
3123
|
+
}
|
|
3124
|
+
],
|
|
2843
3125
|
messages: {
|
|
2844
3126
|
restricted: "'{{token}}' may only appear in configured owner paths.",
|
|
2845
3127
|
restrictedWithMessage: "'{{token}}' may only appear in configured owner paths. {{custom}}"
|
|
@@ -2858,11 +3140,13 @@ var noRestrictedToken = defineRule26({
|
|
|
2858
3140
|
before() {
|
|
2859
3141
|
const options = optionsFirst(context);
|
|
2860
3142
|
const configured = [
|
|
2861
|
-
...options.token ? [
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
3143
|
+
...options.token ? [
|
|
3144
|
+
{
|
|
3145
|
+
token: options.token,
|
|
3146
|
+
allowIn: options.allowIn,
|
|
3147
|
+
allowPathPatterns: options.allowPathPatterns
|
|
3148
|
+
}
|
|
3149
|
+
] : [],
|
|
2866
3150
|
...options.restrictions ?? []
|
|
2867
3151
|
];
|
|
2868
3152
|
const filename = namingPosixPath(context.filename);
|
|
@@ -3122,19 +3406,21 @@ function isRuntimeReExport(statement, runtimeImportLocals, valueNames, isAmbient
|
|
|
3122
3406
|
var noRuntimeInTypes = defineRule27({
|
|
3123
3407
|
meta: {
|
|
3124
3408
|
type: "problem",
|
|
3125
|
-
schema: [
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3409
|
+
schema: [
|
|
3410
|
+
{
|
|
3411
|
+
type: "object",
|
|
3412
|
+
additionalProperties: false,
|
|
3413
|
+
properties: {
|
|
3414
|
+
allow: {
|
|
3415
|
+
type: "array",
|
|
3416
|
+
items: { type: "string", enum: ["class", "default", "enum", "function", "variable"] }
|
|
3417
|
+
},
|
|
3418
|
+
runtimeImports: { type: "string", enum: ["allow", "ban"] },
|
|
3419
|
+
allowImportSources: { type: "array", items: { type: "string" } },
|
|
3420
|
+
banReExports: { type: "boolean" }
|
|
3421
|
+
}
|
|
3136
3422
|
}
|
|
3137
|
-
|
|
3423
|
+
],
|
|
3138
3424
|
messages: {
|
|
3139
3425
|
runtimeValue: "Types files must not contain runtime value {{name}}.",
|
|
3140
3426
|
runtimeImport: "Types files must not contain runtime imports from {{source}}.",
|
|
@@ -3298,22 +3584,39 @@ function isAllowedStructuralElement(identifier, initializer, ancestors, options,
|
|
|
3298
3584
|
var noSingleUseScalarSchema = defineRule28({
|
|
3299
3585
|
meta: {
|
|
3300
3586
|
type: "suggestion",
|
|
3301
|
-
schema: [
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3587
|
+
schema: [
|
|
3588
|
+
{
|
|
3589
|
+
type: "object",
|
|
3590
|
+
additionalProperties: false,
|
|
3591
|
+
properties: {
|
|
3592
|
+
namespaces: { type: "array", items: { type: "string" } },
|
|
3593
|
+
methods: { type: "array", items: { type: "string" } },
|
|
3594
|
+
structuralMethods: { type: "array", items: { type: "string" } },
|
|
3595
|
+
allowZodScalars: { type: "boolean" }
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
],
|
|
3599
|
+
defaultOptions: [
|
|
3600
|
+
{
|
|
3601
|
+
namespaces: ["z"],
|
|
3602
|
+
methods: ["array", "union", "record", "tuple"],
|
|
3603
|
+
structuralMethods: [
|
|
3604
|
+
"and",
|
|
3605
|
+
"array",
|
|
3606
|
+
"discriminatedUnion",
|
|
3607
|
+
"intersection",
|
|
3608
|
+
"lazy",
|
|
3609
|
+
"object",
|
|
3610
|
+
"or",
|
|
3611
|
+
"pipe",
|
|
3612
|
+
"record",
|
|
3613
|
+
"transform",
|
|
3614
|
+
"tuple",
|
|
3615
|
+
"union"
|
|
3616
|
+
],
|
|
3617
|
+
allowZodScalars: true
|
|
3309
3618
|
}
|
|
3310
|
-
|
|
3311
|
-
defaultOptions: [{
|
|
3312
|
-
namespaces: ["z"],
|
|
3313
|
-
methods: ["array", "union", "record", "tuple"],
|
|
3314
|
-
structuralMethods: ["and", "array", "discriminatedUnion", "intersection", "lazy", "object", "or", "pipe", "record", "transform", "tuple", "union"],
|
|
3315
|
-
allowZodScalars: true
|
|
3316
|
-
}],
|
|
3619
|
+
],
|
|
3317
3620
|
messages: { inline: "Inline single-use scalar schema {{name}} at its only structural use." }
|
|
3318
3621
|
},
|
|
3319
3622
|
create(context) {
|
|
@@ -3367,14 +3670,16 @@ import { defineRule as defineRule29 } from "@oxlint/plugins";
|
|
|
3367
3670
|
var noTopLevelFunctions = defineRule29({
|
|
3368
3671
|
meta: {
|
|
3369
3672
|
type: "problem",
|
|
3370
|
-
schema: [
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3673
|
+
schema: [
|
|
3674
|
+
{
|
|
3675
|
+
type: "object",
|
|
3676
|
+
additionalProperties: false,
|
|
3677
|
+
properties: {
|
|
3678
|
+
banReExports: { type: "boolean" },
|
|
3679
|
+
allowPattern: optionsPatternSchema
|
|
3680
|
+
}
|
|
3376
3681
|
}
|
|
3377
|
-
|
|
3682
|
+
],
|
|
3378
3683
|
defaultOptions: [{ banReExports: true }],
|
|
3379
3684
|
messages: {
|
|
3380
3685
|
noFunction: "Files in this scope must not contain top-level function {{name}}.",
|
|
@@ -3422,73 +3727,126 @@ var noTopLevelFunctions = defineRule29({
|
|
|
3422
3727
|
|
|
3423
3728
|
// src/rules/no-trivial-functions.ts
|
|
3424
3729
|
import { defineRule as defineRule30 } from "@oxlint/plugins";
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
return statement.type === "ExpressionStatement" ? statement.expression : null;
|
|
3441
|
-
}
|
|
3442
|
-
function forwardedCall(expression) {
|
|
3443
|
-
if (expression.type === "ParenthesizedExpression" || expression.type === "TSAsExpression" || expression.type === "TSSatisfiesExpression" || expression.type === "TSTypeAssertion" || expression.type === "TSNonNullExpression" || expression.type === "ChainExpression") {
|
|
3444
|
-
return forwardedCall(expression.expression);
|
|
3445
|
-
}
|
|
3446
|
-
if (expression.type === "AwaitExpression") {
|
|
3447
|
-
return forwardedCall(expression.argument);
|
|
3448
|
-
}
|
|
3449
|
-
return expression.type === "CallExpression" ? expression : null;
|
|
3450
|
-
}
|
|
3730
|
+
|
|
3731
|
+
// src/constants/trivial.constants.ts
|
|
3732
|
+
var trivialBannedNames = [
|
|
3733
|
+
"isRecord",
|
|
3734
|
+
"isPlainObject",
|
|
3735
|
+
"isObject",
|
|
3736
|
+
"isString",
|
|
3737
|
+
"isNumber",
|
|
3738
|
+
"isBoolean",
|
|
3739
|
+
"isArray",
|
|
3740
|
+
"asRecord",
|
|
3741
|
+
"asArray"
|
|
3742
|
+
];
|
|
3743
|
+
|
|
3744
|
+
// src/rules/no-trivial-functions.ts
|
|
3451
3745
|
var noTrivialFunctions = defineRule30({
|
|
3452
3746
|
meta: {
|
|
3453
3747
|
type: "problem",
|
|
3454
|
-
schema: [
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3748
|
+
schema: [
|
|
3749
|
+
{
|
|
3750
|
+
type: "object",
|
|
3751
|
+
additionalProperties: false,
|
|
3752
|
+
properties: {
|
|
3753
|
+
allowPattern: optionsPatternSchema,
|
|
3754
|
+
allowCallees: { type: "array", items: { type: "string" } },
|
|
3755
|
+
allowAsync: { type: "boolean" },
|
|
3756
|
+
checkServiceMethods: { type: "boolean" },
|
|
3757
|
+
mode: { type: "string", enum: ["legacy", "precise"] },
|
|
3758
|
+
bannedNames: { type: "array", items: { type: "string" }, uniqueItems: true },
|
|
3759
|
+
checkGenericGuards: { type: "boolean" }
|
|
3760
|
+
}
|
|
3461
3761
|
}
|
|
3462
|
-
|
|
3762
|
+
],
|
|
3463
3763
|
messages: {
|
|
3464
|
-
trivial: "Function {{name}}
|
|
3764
|
+
trivial: "Function {{name}} adds no transformation; inline it or use a value for a constant result. Keep deliberate adapters through an explicit rule exclusion.",
|
|
3765
|
+
bannedName: "Helper {{name}} is forbidden by this policy. Inline the local check, or validate external input at its boundary; renaming the guard does not fix it.",
|
|
3766
|
+
genericGuard: "Helper {{name}} only performs generic runtime type checks or fallback reads. Inline the local check, or validate external input at its boundary; do not add a schema merely to retain this helper."
|
|
3465
3767
|
}
|
|
3466
3768
|
},
|
|
3467
3769
|
createOnce(context) {
|
|
3468
3770
|
let allowed = [];
|
|
3469
3771
|
let allowedCallees = [];
|
|
3470
3772
|
let allowAsync = false;
|
|
3773
|
+
let mode = "legacy";
|
|
3774
|
+
let bannedNames = new Set;
|
|
3775
|
+
let checkGenericGuards = false;
|
|
3776
|
+
let checkServiceMethods = false;
|
|
3777
|
+
const reported = new Set;
|
|
3778
|
+
const checked = new Set;
|
|
3779
|
+
function globalArray(node) {
|
|
3780
|
+
let scope = context.sourceCode.getScope(node);
|
|
3781
|
+
while (scope) {
|
|
3782
|
+
const variable = scope.set.get("Array");
|
|
3783
|
+
if (variable)
|
|
3784
|
+
return variable.defs.length === 0;
|
|
3785
|
+
scope = scope.upper;
|
|
3786
|
+
}
|
|
3787
|
+
return true;
|
|
3788
|
+
}
|
|
3789
|
+
function checkHelper(node) {
|
|
3790
|
+
if (bannedNames.size === 0 && !checkGenericGuards || checked.has(node.start))
|
|
3791
|
+
return;
|
|
3792
|
+
checked.add(node.start);
|
|
3793
|
+
const parent = node.parent;
|
|
3794
|
+
const name = node.type !== "ArrowFunctionExpression" && node.id ? node.id.name : parent.type === "VariableDeclarator" && parent.id.type === "Identifier" ? parent.id.name : parent.type === "Property" && !parent.computed && parent.key.type === "Identifier" ? parent.key.name : "<anonymous>";
|
|
3795
|
+
if (optionsPatternsTest(allowed, name))
|
|
3796
|
+
return;
|
|
3797
|
+
if (bannedNames.has(name)) {
|
|
3798
|
+
reported.add(node.start);
|
|
3799
|
+
context.report({ node, messageId: "bannedName", data: { name } });
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
if (checkGenericGuards && guardsIsGenericHelper(node, globalArray)) {
|
|
3803
|
+
reported.add(node.start);
|
|
3804
|
+
context.report({ node, messageId: "genericGuard", data: { name } });
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3471
3807
|
return {
|
|
3472
3808
|
before() {
|
|
3473
|
-
const
|
|
3809
|
+
const options = optionsFirst(context, {});
|
|
3810
|
+
const { allowPattern, allowCallees: calleePatterns = [], allowAsync: asyncAllowed = false } = options;
|
|
3811
|
+
mode = options.mode ?? "legacy";
|
|
3812
|
+
checkServiceMethods = options.checkServiceMethods ?? false;
|
|
3813
|
+
bannedNames = new Set(options.bannedNames ?? (mode === "precise" ? trivialBannedNames : []));
|
|
3814
|
+
checkGenericGuards = options.checkGenericGuards ?? mode === "precise";
|
|
3815
|
+
reported.clear();
|
|
3816
|
+
checked.clear();
|
|
3474
3817
|
allowed = optionsOptionalPatterns(allowPattern);
|
|
3475
3818
|
allowedCallees = calleePatterns.map((pattern) => new RegExp(pattern));
|
|
3476
3819
|
allowAsync = asyncAllowed;
|
|
3477
3820
|
},
|
|
3821
|
+
FunctionDeclaration: checkHelper,
|
|
3822
|
+
FunctionExpression: checkHelper,
|
|
3823
|
+
ArrowFunctionExpression: checkHelper,
|
|
3478
3824
|
Program(program) {
|
|
3479
3825
|
const seen = new Set;
|
|
3480
|
-
for (const item of [
|
|
3826
|
+
for (const item of [
|
|
3827
|
+
...exportsCollectFunctions(program),
|
|
3828
|
+
...declarationsTopLevelUnexportedFunctions(program),
|
|
3829
|
+
...checkServiceMethods ? declarationsPublicServiceMethods(program) : []
|
|
3830
|
+
]) {
|
|
3481
3831
|
const key = `${item.name}:${item.node.start}:${item.node.end}`;
|
|
3482
|
-
if (seen.has(key) || optionsPatternsTest(allowed, item.name)
|
|
3832
|
+
if (seen.has(key) || optionsPatternsTest(allowed, item.name)) {
|
|
3483
3833
|
continue;
|
|
3484
3834
|
}
|
|
3485
|
-
|
|
3486
|
-
|
|
3835
|
+
seen.add(key);
|
|
3836
|
+
checkHelper(item.node);
|
|
3837
|
+
if (reported.has(item.node.start))
|
|
3838
|
+
continue;
|
|
3839
|
+
const expression = trivialExpression(item.node);
|
|
3840
|
+
const trivial = mode === "legacy" ? declarationsIsTrivialFunction(item.node) : expression ? trivialIsForwarder(item.node, expression) : declarationsIsTrivialFunction(item.node);
|
|
3841
|
+
if (!trivial)
|
|
3842
|
+
continue;
|
|
3843
|
+
const value = expression && trivialUnwrap(expression);
|
|
3844
|
+
const call = value?.type === "CallExpression" ? value : null;
|
|
3487
3845
|
const path = call && astDottedName(call.callee);
|
|
3488
3846
|
if (allowAsync && item.node.async && call || path && allowedCallees.some((pattern) => pattern.test(path))) {
|
|
3489
3847
|
continue;
|
|
3490
3848
|
}
|
|
3491
|
-
|
|
3849
|
+
reported.add(item.node.start);
|
|
3492
3850
|
context.report({
|
|
3493
3851
|
node: item.node,
|
|
3494
3852
|
messageId: "trivial",
|
|
@@ -3505,13 +3863,15 @@ import { defineRule as defineRule31 } from "@oxlint/plugins";
|
|
|
3505
3863
|
var noTypeDeclarations = defineRule31({
|
|
3506
3864
|
meta: {
|
|
3507
3865
|
type: "problem",
|
|
3508
|
-
schema: [
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3866
|
+
schema: [
|
|
3867
|
+
{
|
|
3868
|
+
type: "object",
|
|
3869
|
+
additionalProperties: false,
|
|
3870
|
+
properties: {
|
|
3871
|
+
allowPattern: optionsPatternSchema
|
|
3872
|
+
}
|
|
3513
3873
|
}
|
|
3514
|
-
|
|
3874
|
+
],
|
|
3515
3875
|
messages: {
|
|
3516
3876
|
typeDeclaration: "Type '{{name}}' must not be declared in this file."
|
|
3517
3877
|
}
|
|
@@ -3549,16 +3909,19 @@ import { defineRule as defineRule32 } from "@oxlint/plugins";
|
|
|
3549
3909
|
var noUnescapedLike = defineRule32({
|
|
3550
3910
|
meta: {
|
|
3551
3911
|
type: "problem",
|
|
3552
|
-
schema: [
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3912
|
+
schema: [
|
|
3913
|
+
{
|
|
3914
|
+
type: "object",
|
|
3915
|
+
additionalProperties: false,
|
|
3916
|
+
properties: {
|
|
3917
|
+
methods: { type: "array", items: { type: "string" } },
|
|
3918
|
+
sanitizers: { type: "array", items: { type: "string" } },
|
|
3919
|
+
allowSanitizedBindings: { type: "boolean" },
|
|
3920
|
+
operatorMethods: { type: "array", items: { type: "string" } }
|
|
3921
|
+
},
|
|
3922
|
+
required: ["methods", "sanitizers"]
|
|
3923
|
+
}
|
|
3924
|
+
],
|
|
3562
3925
|
messages: { unescaped: ".{{method}}() values must use a configured sanitizer." }
|
|
3563
3926
|
},
|
|
3564
3927
|
createOnce(context) {
|
|
@@ -3572,9 +3935,11 @@ var noUnescapedLike = defineRule32({
|
|
|
3572
3935
|
if (node.callee.type !== "MemberExpression") {
|
|
3573
3936
|
return;
|
|
3574
3937
|
}
|
|
3575
|
-
const { methods, sanitizers } = optionsFirst(context);
|
|
3938
|
+
const { methods, sanitizers, operatorMethods = [] } = optionsFirst(context);
|
|
3576
3939
|
const method = astStaticMemberName(node.callee);
|
|
3577
|
-
|
|
3940
|
+
const operator = node.arguments[1];
|
|
3941
|
+
const operatorCall = method && operatorMethods.includes(method) && operator?.type === "Literal" && typeof operator.value === "string" && methods.includes(operator.value);
|
|
3942
|
+
if (!method || !methods.includes(method) && !operatorCall) {
|
|
3578
3943
|
return;
|
|
3579
3944
|
}
|
|
3580
3945
|
const value = node.arguments.at(-1);
|
|
@@ -3633,15 +3998,17 @@ var objectMultiline = defineRule33({
|
|
|
3633
3998
|
meta: {
|
|
3634
3999
|
type: "layout",
|
|
3635
4000
|
fixable: "whitespace",
|
|
3636
|
-
schema: [
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
4001
|
+
schema: [
|
|
4002
|
+
{
|
|
4003
|
+
type: "object",
|
|
4004
|
+
additionalProperties: false,
|
|
4005
|
+
properties: {
|
|
4006
|
+
minProperties: { type: "integer", minimum: 1, default: 3 },
|
|
4007
|
+
scope: { type: "string", enum: ["call-args", "all"], default: "call-args" },
|
|
4008
|
+
indent: { ...layoutIndentSchema, default: 2 }
|
|
4009
|
+
}
|
|
3643
4010
|
}
|
|
3644
|
-
|
|
4011
|
+
],
|
|
3645
4012
|
messages: {
|
|
3646
4013
|
callArgs: "Object with {{min}}+ properties in a call should have each property on its own line.",
|
|
3647
4014
|
always: "Object with {{min}}+ properties should have each property on its own line."
|
|
@@ -3693,15 +4060,17 @@ import { defineRule as defineRule34 } from "@oxlint/plugins";
|
|
|
3693
4060
|
var onlyExportComponents = defineRule34({
|
|
3694
4061
|
meta: {
|
|
3695
4062
|
type: "problem",
|
|
3696
|
-
schema: [
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
4063
|
+
schema: [
|
|
4064
|
+
{
|
|
4065
|
+
type: "object",
|
|
4066
|
+
additionalProperties: false,
|
|
4067
|
+
properties: {
|
|
4068
|
+
matchFileName: { type: "boolean" },
|
|
4069
|
+
allowTypeExports: { type: "boolean" },
|
|
4070
|
+
denyTypePattern: optionsPatternSchema
|
|
4071
|
+
}
|
|
3703
4072
|
}
|
|
3704
|
-
|
|
4073
|
+
],
|
|
3705
4074
|
messages: {
|
|
3706
4075
|
nonComponent: "Export '{{name}}' must be a React component or type.",
|
|
3707
4076
|
nameMismatch: "Component export '{{name}}' must match '{{expected}}' or its prefix.",
|
|
@@ -3826,15 +4195,17 @@ function collectBindings(program) {
|
|
|
3826
4195
|
var onlyExportConstants = defineRule35({
|
|
3827
4196
|
meta: {
|
|
3828
4197
|
type: "problem",
|
|
3829
|
-
schema: [
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
4198
|
+
schema: [
|
|
4199
|
+
{
|
|
4200
|
+
type: "object",
|
|
4201
|
+
additionalProperties: false,
|
|
4202
|
+
properties: {
|
|
4203
|
+
allowFunctionValues: { type: "boolean" },
|
|
4204
|
+
allowTypeExports: { type: "boolean" },
|
|
4205
|
+
allowReExports: { type: "boolean" }
|
|
4206
|
+
}
|
|
3836
4207
|
}
|
|
3837
|
-
|
|
4208
|
+
],
|
|
3838
4209
|
messages: {
|
|
3839
4210
|
nonConstant: "Export '{{name}}' must be a local const binding with an allowed value; types and re-exports require explicit options."
|
|
3840
4211
|
}
|
|
@@ -3842,7 +4213,11 @@ var onlyExportConstants = defineRule35({
|
|
|
3842
4213
|
createOnce(context) {
|
|
3843
4214
|
return {
|
|
3844
4215
|
Program(program) {
|
|
3845
|
-
const {
|
|
4216
|
+
const {
|
|
4217
|
+
allowFunctionValues = false,
|
|
4218
|
+
allowTypeExports = false,
|
|
4219
|
+
allowReExports = false
|
|
4220
|
+
} = optionsFirst(context, {});
|
|
3846
4221
|
const bindings = collectBindings(program);
|
|
3847
4222
|
function hasForbiddenValue(binding, seen = new Set) {
|
|
3848
4223
|
if (seen.has(binding) || binding.kind === "class") {
|
|
@@ -3961,13 +4336,16 @@ var paddingBetweenStatements = defineRule36({
|
|
|
3961
4336
|
meta: {
|
|
3962
4337
|
type: "layout",
|
|
3963
4338
|
fixable: "whitespace",
|
|
3964
|
-
schema: [
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
4339
|
+
schema: [
|
|
4340
|
+
{
|
|
4341
|
+
type: "object",
|
|
4342
|
+
additionalProperties: false,
|
|
4343
|
+
properties: {
|
|
4344
|
+
returnMinStatements: { type: "integer", minimum: 1, default: 3 },
|
|
4345
|
+
multilineVariables: { type: "boolean" }
|
|
4346
|
+
}
|
|
3969
4347
|
}
|
|
3970
|
-
|
|
4348
|
+
],
|
|
3971
4349
|
messages: {
|
|
3972
4350
|
variableAndBlock: "Expected a blank line between a variable declaration and a function/class.",
|
|
3973
4351
|
beforeBlock: "Expected a blank line before a function/class declaration.",
|
|
@@ -3975,11 +4353,13 @@ var paddingBetweenStatements = defineRule36({
|
|
|
3975
4353
|
variableAndControl: "Expected a blank line between a variable declaration and control flow.",
|
|
3976
4354
|
controlAndVariable: "Expected a blank line between control flow and a variable declaration.",
|
|
3977
4355
|
consecutiveControl: "Expected a blank line between control flow statements.",
|
|
3978
|
-
beforeReturn: "Expected a blank line before return statement."
|
|
4356
|
+
beforeReturn: "Expected a blank line before return statement.",
|
|
4357
|
+
multilineVariable: "Expected a blank line around a multiline variable declaration."
|
|
3979
4358
|
}
|
|
3980
4359
|
},
|
|
3981
4360
|
createOnce(context) {
|
|
3982
4361
|
let returnMinStatements = 3;
|
|
4362
|
+
let multilineVariables = false;
|
|
3983
4363
|
let newline = `
|
|
3984
4364
|
`;
|
|
3985
4365
|
let source;
|
|
@@ -3993,7 +4373,8 @@ var paddingBetweenStatements = defineRule36({
|
|
|
3993
4373
|
}
|
|
3994
4374
|
const prevKind = paddingKind(prev);
|
|
3995
4375
|
const currKind = paddingKind(curr);
|
|
3996
|
-
const
|
|
4376
|
+
const multiline = multilineVariables && (prevKind === "variable" && prev.loc.start.line !== prev.loc.end.line || currKind === "variable" && curr.loc.start.line !== curr.loc.end.line);
|
|
4377
|
+
const messageId = multiline ? "multilineVariable" : prevKind === "variable" && currKind === "block" || prevKind === "block" && currKind === "variable" ? "variableAndBlock" : currKind === "block" ? "beforeBlock" : prevKind === "block" ? "afterBlock" : prevKind === "variable" && currKind === "control" ? "variableAndControl" : prevKind === "control" && currKind === "variable" ? "controlAndVariable" : prevKind === "control" && currKind === "control" ? "consecutiveControl" : currKind === "return" && prevKind !== "variable" && statements.length >= returnMinStatements ? "beforeReturn" : null;
|
|
3997
4378
|
if (!messageId) {
|
|
3998
4379
|
continue;
|
|
3999
4380
|
}
|
|
@@ -4010,6 +4391,7 @@ var paddingBetweenStatements = defineRule36({
|
|
|
4010
4391
|
before() {
|
|
4011
4392
|
const options = optionsFirst(context, {});
|
|
4012
4393
|
returnMinStatements = options.returnMinStatements ?? 3;
|
|
4394
|
+
multilineVariables = options.multilineVariables ?? false;
|
|
4013
4395
|
source = context.sourceCode;
|
|
4014
4396
|
newline = layoutNewline(source.text);
|
|
4015
4397
|
},
|
|
@@ -4052,17 +4434,19 @@ var preferNamespaceTypeImport = defineRule37({
|
|
|
4052
4434
|
meta: {
|
|
4053
4435
|
type: "suggestion",
|
|
4054
4436
|
fixable: "code",
|
|
4055
|
-
schema: [
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4437
|
+
schema: [
|
|
4438
|
+
{
|
|
4439
|
+
type: "object",
|
|
4440
|
+
additionalProperties: false,
|
|
4441
|
+
properties: {
|
|
4442
|
+
max: { type: "integer", minimum: 0 },
|
|
4443
|
+
names: { type: "object", additionalProperties: { type: "string", pattern: bindingName.source } }
|
|
4444
|
+
}
|
|
4061
4445
|
}
|
|
4062
|
-
|
|
4446
|
+
],
|
|
4063
4447
|
defaultOptions: [{ max: 3, names: {} }],
|
|
4064
4448
|
messages: {
|
|
4065
|
-
namespace: "Use
|
|
4449
|
+
namespace: "Use `import type * as {{name}} from '{{source}}'` for {{count}} type imports (max {{max}}); keep exported domain names.{{reason}}"
|
|
4066
4450
|
}
|
|
4067
4451
|
},
|
|
4068
4452
|
createOnce(context) {
|
|
@@ -4147,7 +4531,13 @@ var preferNamespaceTypeImport = defineRule37({
|
|
|
4147
4531
|
context.report({
|
|
4148
4532
|
node,
|
|
4149
4533
|
messageId: "namespace",
|
|
4150
|
-
data: {
|
|
4534
|
+
data: {
|
|
4535
|
+
name,
|
|
4536
|
+
source: node.source.value,
|
|
4537
|
+
count: node.specifiers.length,
|
|
4538
|
+
max,
|
|
4539
|
+
reason: reason ? ` Autofix skipped: ${reason}.` : ""
|
|
4540
|
+
},
|
|
4151
4541
|
fix: reason ? undefined : (fixer) => edits.map((edit) => fixer.replaceTextRange(edit.range, edit.text))
|
|
4152
4542
|
});
|
|
4153
4543
|
}
|
|
@@ -4161,15 +4551,17 @@ import { defineRule as defineRule38 } from "@oxlint/plugins";
|
|
|
4161
4551
|
var requireFileFactory = defineRule38({
|
|
4162
4552
|
meta: {
|
|
4163
4553
|
type: "problem",
|
|
4164
|
-
schema: [
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4554
|
+
schema: [
|
|
4555
|
+
{
|
|
4556
|
+
type: "object",
|
|
4557
|
+
additionalProperties: false,
|
|
4558
|
+
properties: {
|
|
4559
|
+
factory: { type: "string" },
|
|
4560
|
+
allDeclarations: { type: "boolean" }
|
|
4561
|
+
},
|
|
4562
|
+
required: ["factory"]
|
|
4563
|
+
}
|
|
4564
|
+
],
|
|
4173
4565
|
messages: {
|
|
4174
4566
|
factory: "Function '{{actual}}' must be named '{{expected}}'."
|
|
4175
4567
|
}
|
|
@@ -4208,15 +4600,18 @@ import { defineRule as defineRule39 } from "@oxlint/plugins";
|
|
|
4208
4600
|
var requireObjectParams = defineRule39({
|
|
4209
4601
|
meta: {
|
|
4210
4602
|
type: "problem",
|
|
4211
|
-
schema: [
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4603
|
+
schema: [
|
|
4604
|
+
{
|
|
4605
|
+
type: "object",
|
|
4606
|
+
additionalProperties: false,
|
|
4607
|
+
properties: {
|
|
4608
|
+
maxParams: { type: "integer", minimum: 1 },
|
|
4609
|
+
serviceMethods: { type: "boolean" },
|
|
4610
|
+
allDeclarations: { type: "boolean" },
|
|
4611
|
+
allowPattern: optionsPatternSchema
|
|
4612
|
+
}
|
|
4218
4613
|
}
|
|
4219
|
-
|
|
4614
|
+
],
|
|
4220
4615
|
messages: {
|
|
4221
4616
|
objectParams: "{{label}} {{name}} must accept at most {{maxParams}} object parameter{{s}}."
|
|
4222
4617
|
}
|
|
@@ -4224,10 +4619,17 @@ var requireObjectParams = defineRule39({
|
|
|
4224
4619
|
createOnce(context) {
|
|
4225
4620
|
return {
|
|
4226
4621
|
Program(program) {
|
|
4227
|
-
const {
|
|
4622
|
+
const {
|
|
4623
|
+
maxParams = 1,
|
|
4624
|
+
allDeclarations = false,
|
|
4625
|
+
serviceMethods = false,
|
|
4626
|
+
allowPattern
|
|
4627
|
+
} = optionsFirst(context, {});
|
|
4228
4628
|
const allowed = optionsOptionalPatterns(allowPattern);
|
|
4229
4629
|
const seen = new Set;
|
|
4230
4630
|
const functions = allDeclarations ? [...exportsCollectFunctions(program), ...declarationsTopLevelUnexportedFunctions(program)] : exportsCollectFunctions(program);
|
|
4631
|
+
if (serviceMethods)
|
|
4632
|
+
functions.push(...declarationsPublicServiceMethods(program));
|
|
4231
4633
|
for (const item of functions) {
|
|
4232
4634
|
const key = `${item.name}:${item.node.start}:${item.node.end}`;
|
|
4233
4635
|
if (seen.has(key) || optionsPatternsTest(allowed, item.name)) {
|
|
@@ -4257,16 +4659,18 @@ import { defineRule as defineRule40 } from "@oxlint/plugins";
|
|
|
4257
4659
|
var requireOrpcOutput = defineRule40({
|
|
4258
4660
|
meta: {
|
|
4259
4661
|
type: "problem",
|
|
4260
|
-
schema: [
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4662
|
+
schema: [
|
|
4663
|
+
{
|
|
4664
|
+
type: "object",
|
|
4665
|
+
additionalProperties: false,
|
|
4666
|
+
properties: {
|
|
4667
|
+
composers: { type: "array", items: { type: "string" } },
|
|
4668
|
+
handlerMethod: { type: "string" },
|
|
4669
|
+
outputMethod: { type: "string" }
|
|
4670
|
+
},
|
|
4671
|
+
required: ["composers"]
|
|
4672
|
+
}
|
|
4673
|
+
],
|
|
4270
4674
|
messages: {
|
|
4271
4675
|
missing: "oRPC procedures must declare .output() before .handler().",
|
|
4272
4676
|
invalid: "oRPC .output() must use a named schema or configured composer with a named schema."
|
|
@@ -4278,7 +4682,11 @@ var requireOrpcOutput = defineRule40({
|
|
|
4278
4682
|
if (node.callee.type !== "MemberExpression") {
|
|
4279
4683
|
return;
|
|
4280
4684
|
}
|
|
4281
|
-
const {
|
|
4685
|
+
const {
|
|
4686
|
+
composers,
|
|
4687
|
+
handlerMethod = "handler",
|
|
4688
|
+
outputMethod = "output"
|
|
4689
|
+
} = optionsFirst(context);
|
|
4282
4690
|
if (astStaticMemberName(node.callee) !== handlerMethod) {
|
|
4283
4691
|
return;
|
|
4284
4692
|
}
|
|
@@ -4300,30 +4708,29 @@ import { defineRule as defineRule41 } from "@oxlint/plugins";
|
|
|
4300
4708
|
var requirePairedCall = defineRule41({
|
|
4301
4709
|
meta: {
|
|
4302
4710
|
type: "problem",
|
|
4303
|
-
schema: [
|
|
4304
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4711
|
+
schema: [
|
|
4712
|
+
{
|
|
4713
|
+
type: "object",
|
|
4714
|
+
additionalProperties: false,
|
|
4715
|
+
properties: {
|
|
4716
|
+
when: { type: "string" },
|
|
4717
|
+
require: { type: "string" },
|
|
4718
|
+
pairs: {
|
|
4719
|
+
type: "array",
|
|
4720
|
+
items: {
|
|
4721
|
+
type: "object",
|
|
4722
|
+
additionalProperties: false,
|
|
4723
|
+
properties: {
|
|
4724
|
+
when: { type: "string" },
|
|
4725
|
+
require: { type: "string" }
|
|
4726
|
+
},
|
|
4727
|
+
required: ["when", "require"]
|
|
4728
|
+
}
|
|
4319
4729
|
}
|
|
4320
|
-
}
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
{ required: ["pairs"] }
|
|
4325
|
-
]
|
|
4326
|
-
}],
|
|
4730
|
+
},
|
|
4731
|
+
anyOf: [{ required: ["when", "require"] }, { required: ["pairs"] }]
|
|
4732
|
+
}
|
|
4733
|
+
],
|
|
4327
4734
|
messages: {
|
|
4328
4735
|
paired: "{{when}} requires a call to {{require}} in the same file."
|
|
4329
4736
|
}
|
|
@@ -4376,16 +4783,20 @@ import { defineRule as defineRule42 } from "@oxlint/plugins";
|
|
|
4376
4783
|
var routeSurface = defineRule42({
|
|
4377
4784
|
meta: {
|
|
4378
4785
|
type: "problem",
|
|
4379
|
-
schema: [
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4786
|
+
schema: [
|
|
4787
|
+
{
|
|
4788
|
+
type: "object",
|
|
4789
|
+
additionalProperties: false,
|
|
4790
|
+
properties: {
|
|
4791
|
+
exportName: { type: "string" },
|
|
4792
|
+
bannedHooks: { type: "array", items: { type: "string" } },
|
|
4793
|
+
banIntrinsicJsx: { type: "boolean" }
|
|
4794
|
+
}
|
|
4386
4795
|
}
|
|
4387
|
-
|
|
4388
|
-
defaultOptions: [
|
|
4796
|
+
],
|
|
4797
|
+
defaultOptions: [
|
|
4798
|
+
{ exportName: "Route", bannedHooks: ["useState", "useEffect", "useMutation"], banIntrinsicJsx: true }
|
|
4799
|
+
],
|
|
4389
4800
|
messages: {
|
|
4390
4801
|
extraExport: "Route files may only export '{{expected}}'; found '{{actual}}'.",
|
|
4391
4802
|
hostJsx: "Route adapter {{name}} must not render intrinsic JSX.",
|
|
@@ -4458,20 +4869,24 @@ function titlePatterns(pattern, flags) {
|
|
|
4458
4869
|
var testTitlePattern = defineRule43({
|
|
4459
4870
|
meta: {
|
|
4460
4871
|
type: "suggestion",
|
|
4461
|
-
schema: [
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4872
|
+
schema: [
|
|
4873
|
+
{
|
|
4874
|
+
type: "object",
|
|
4875
|
+
additionalProperties: false,
|
|
4876
|
+
properties: {
|
|
4877
|
+
callees: { type: "array", items: { type: "string" } },
|
|
4878
|
+
forbid: optionsPatternSchema,
|
|
4879
|
+
require: optionsPatternSchema,
|
|
4880
|
+
flags: { type: "string", pattern: "^[dgimsuvy]*$" }
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
],
|
|
4884
|
+
defaultOptions: [
|
|
4885
|
+
{
|
|
4886
|
+
callees: ["describe", "it", "test"],
|
|
4887
|
+
flags: ""
|
|
4469
4888
|
}
|
|
4470
|
-
|
|
4471
|
-
defaultOptions: [{
|
|
4472
|
-
callees: ["describe", "it", "test"],
|
|
4473
|
-
flags: ""
|
|
4474
|
-
}],
|
|
4889
|
+
],
|
|
4475
4890
|
messages: {
|
|
4476
4891
|
forbidden: "Test title matches forbidden pattern '{{pattern}}'.",
|
|
4477
4892
|
required: "Test title must match required pattern '{{pattern}}'."
|