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/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
- exportedName: name,
310
- localName: declaration.id?.name,
311
- node: declaration,
312
- declaration,
313
- typeOnly: declaration.type === "TSInterfaceDeclaration" || declaration.type === "TSTypeAliasDeclaration" || declaration.type === "TSDeclareFunction",
314
- default: false,
315
- reExport: false
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
- exportedName: "*",
340
- node: statement,
341
- typeOnly: statement.exportKind === "type",
342
- default: false,
343
- reExport: true
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
- ...local ?? {
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
- node: statement,
353
- typeOnly: false,
354
- default: true,
355
- reExport: false
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
- return /^[A-Z]/.test(key) ? namingPascalCase(group) : group;
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/comments.ts
778
- 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])/;
779
- var COMMENTS_DISABLE_NEXT_LINE = /^(?:eslint|oxlint)-disable-next-line\b/;
780
- var COMMENTS_LEGAL_BLOCK = "\x00legal-block\x00";
781
- function commentsNormalizedValue(comment) {
782
- const value = (comment.type === "Block" ? comment.value.replace(/^\s*\* ?/gm, "") : comment.value).trimStart();
783
- return comment.type === "Block" && comment.value.startsWith("!") ? COMMENTS_LEGAL_BLOCK + value : value;
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
- type: "object",
891
- additionalProperties: false,
892
- properties: {
893
- callees: { type: "array", items: { type: "string" } },
894
- minElements: { type: "integer", minimum: 1 },
895
- indent: layoutIndentSchema
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
- type: "object",
988
- additionalProperties: false,
989
- properties: {
990
- groups: {
991
- type: "array",
992
- items: {
993
- type: "object",
994
- additionalProperties: false,
995
- properties: {
996
- minDepth: { type: "integer", minimum: 1 },
997
- methods: { type: "array", items: { type: "string" } },
998
- onlyMethods: { type: "array", items: { type: "string" } },
999
- rootPattern: optionsPatternSchema
1000
- },
1001
- required: ["minDepth"]
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
- indent: layoutIndentSchema
1005
- },
1006
- required: ["groups"]
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
- type: "object",
1092
- additionalProperties: false,
1093
- properties: {
1094
- kinds: {
1095
- type: "array",
1096
- items: { type: "string", enum: [...declarationsKinds] }
1097
- },
1098
- pattern: optionsPatternSchema,
1099
- flags: { type: "string" },
1100
- stem: { type: "string", enum: [...namingStemModes] },
1101
- trailingRoles: { type: "array", items: { type: "string" } },
1102
- roleSeparators: { type: "array", items: { type: "string" } },
1103
- normalize: { type: "string", enum: ["remove-separators", "none"] },
1104
- singularize: { type: "string", enum: ["none", "trailing-s"] },
1105
- allowPattern: optionsPatternSchema
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 ? { name: item.name, pattern: optionsPatternLabel([options.pattern ?? []].flat().filter((source) => source !== "")) } : { name: item.name, prefix: prefixes.join(" or ") }
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
- type: "object",
1165
- additionalProperties: false,
1166
- properties: {
1167
- stem: { type: "string", enum: [...namingStemModes] },
1168
- trailingRoles: { type: "array", items: { type: "string" } },
1169
- roleSeparators: { type: "array", items: { type: "string" } },
1170
- normalize: { type: "string", enum: ["remove-separators", "none"] },
1171
- singularize: { type: "string", enum: ["none", "trailing-s"] },
1172
- allDeclarations: { type: "boolean" },
1173
- allowPattern: optionsPatternSchema
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
- type: "object",
1221
- additionalProperties: false,
1222
- properties: {
1223
- pattern: optionsPatternSchema,
1224
- flags: { type: "string" },
1225
- ignoreTypeExports: { type: "boolean" },
1226
- allDeclarations: { type: "boolean" },
1227
- allowPattern: optionsPatternSchema
1228
- },
1229
- required: ["pattern"]
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
- type: "object",
1273
- additionalProperties: false,
1274
- properties: {
1275
- file: { type: "string" },
1276
- export: { type: "string" },
1277
- mode: { type: "string", enum: ["all", "some"] },
1278
- placeholderPattern: { type: "string" },
1279
- allDeclarations: { type: "boolean" }
1280
- },
1281
- required: ["file", "export"]
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 match '{{expected}}'."
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
- type: "object",
1335
- additionalProperties: false,
1336
- properties: {
1337
- pattern: optionsPatternSchema,
1338
- message: { type: "string" },
1339
- flags: { type: "string" }
1340
- },
1341
- required: ["pattern", "message"]
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
- type: "object",
1371
- additionalProperties: false,
1372
- properties: {
1373
- singularize: { type: "string", enum: ["none", "trailing-s"] },
1374
- separators: { type: "array", items: { type: "string" } },
1375
- after: { type: "string" }
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 folder name '{{folder}}' or an allowed singular form."
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 stem = file.replace(/\.(tsx?|jsx?)$/, "");
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: { file, folder: prefixes[0] ?? "" }
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
- range: openingRange,
1426
- text: `${newline}${attributeIndent}`
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
- type: "object",
1449
- additionalProperties: false,
1450
- properties: {
1451
- minAttributes: { type: "integer", minimum: 1 },
1452
- indent: layoutIndentSchema
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
- type: "object",
1638
- additionalProperties: false,
1639
- properties: {
1640
- allowWhy: { type: "boolean", default: true },
1641
- allowPatterns: { type: "array", items: { type: "string" } },
1642
- allowJsdoc: { type: "boolean", default: false }
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
- type: "object",
1768
- additionalProperties: false,
1769
- properties: {
1770
- names: { type: "array", items: { type: "string" } },
1771
- patterns: { type: "array", items: { type: "string" } },
1772
- domainStem: { type: "string", enum: [...namingStemModes] },
1773
- trailingRoles: { type: "array", items: { type: "string" } },
1774
- roleSeparators: { type: "array", items: { type: "string" } },
1775
- allowTypeExports: { type: "boolean" }
1776
- },
1777
- required: ["names"]
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
- type: "object",
1835
- additionalProperties: false,
1836
- properties: {
1837
- keys: { type: "array", items: { type: "string" } },
1838
- factoryPattern: optionsPatternSchema,
1839
- requireKeys: { type: "array", items: { type: "string" } },
1840
- keySets: {
1841
- type: "array",
1842
- items: {
1843
- type: "object",
1844
- additionalProperties: false,
1845
- properties: {
1846
- factoryPattern: optionsPatternSchema,
1847
- keys: { type: "array", items: { type: "string" } },
1848
- requireKeys: { type: "array", items: { type: "string" } }
1849
- },
1850
- required: ["factoryPattern", "keys"]
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: { factory: factory.name, key: key ?? "<computed or spread>", allowed: resolved.allowedList.join(", ") }
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
- type: "object",
1934
- additionalProperties: false,
1935
- properties: {
1936
- allowPattern: optionsPatternSchema,
1937
- detectComponents: { type: "boolean" },
1938
- hookPattern: optionsPatternSchema
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: "File-level helper {{name}} must move inside its owning component or hook."
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: "Exported type '{{alias}}' must not re-alias imported type '{{name}}'."
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(program.body.flatMap((statement) => statement.type === "ImportDeclaration" ? statement.specifiers.map((specifier) => specifier.local.name) : []));
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: { alias: statement.declaration.id.name, name: inline }
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: { alias: local, name: imported }
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
- type: "object",
2037
- additionalProperties: false,
2038
- properties: {
2039
- namespaces: { type: "array", items: { type: "string" } },
2040
- methods: { type: "array", items: { type: "string" } },
2041
- structuralMethods: { type: "array", items: { type: "string" } },
2042
- allowZodScalars: { type: "boolean" }
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
- type: "object",
2095
- additionalProperties: false,
2096
- properties: {
2097
- parameters: { type: "boolean" },
2098
- returns: { type: "boolean" },
2099
- functionTypes: { type: "boolean" },
2100
- minMembers: { type: "integer", minimum: 1 }
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
- type: "object",
2172
- additionalProperties: false,
2173
- properties: {
2174
- allow: { type: "array", items: { type: "string" } }
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
- type: "object",
2217
- additionalProperties: false,
2218
- properties: {
2219
- packages: { type: "array", items: { type: "string" } },
2220
- namespaces: { type: "array", items: { type: "string" } },
2221
- allowIn: { type: "array", items: { type: "string" } },
2222
- allowPathPatterns: { type: "array", items: { type: "string" } },
2223
- message: { type: "string" },
2224
- runtimeImportMessage: { type: "string" },
2225
- constructionMessage: { type: "string" }
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
- packages: ["zod"],
2230
- namespaces: ["z"]
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, { source: node.source.value })
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, { source: node.source.value })
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, { source: node.source.value })
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
- type: "object",
2331
- additionalProperties: false,
2332
- properties: {
2333
- allowWhy: { type: "boolean", default: true }
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
- type: "object",
2463
- additionalProperties: false,
2464
- properties: {
2465
- kinds: {
2466
- type: "array",
2467
- items: { type: "string", enum: ["let", "var"] }
2468
- },
2469
- allowNamePattern: optionsPatternSchema
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
- type: "object",
2541
- additionalProperties: false,
2542
- properties: {
2543
- combinators: { type: "array", items: { type: "string" } },
2544
- methods: { type: "array", items: { type: "string" } },
2545
- checkAssignments: { type: "boolean" }
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
- type: "object",
2638
- additionalProperties: false,
2639
- properties: {
2640
- from: { type: "array", items: { type: "string" } },
2641
- namespaces: { type: "array", items: { type: "string" } },
2642
- operators: { type: "array", items: { type: "string" } }
2643
- },
2644
- required: ["from"]
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
- type: "object",
2699
- additionalProperties: false,
2700
- properties: {
2701
- constructors: {
2702
- type: "array",
2703
- items: {
2704
- anyOf: [
2705
- { type: "string" },
2706
- {
2707
- type: "object",
2708
- additionalProperties: false,
2709
- properties: {
2710
- name: { type: "string" },
2711
- message: { type: "string" }
2712
- },
2713
- required: ["name"]
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
- checkCalls: { type: "boolean", default: false },
2719
- message: { type: "string" }
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
- type: "object",
2773
- additionalProperties: false,
2774
- properties: {
2775
- message: { type: "string" }
2776
- },
2777
- required: ["message"]
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
- type: "object",
2814
- additionalProperties: false,
2815
- properties: {
2816
- token: { type: "string" },
2817
- allowIn: { type: "array", items: { type: "string" } },
2818
- allowPathPatterns: { type: "array", items: { type: "string" } },
2819
- restrictions: {
2820
- type: "array",
2821
- items: {
2822
- type: "object",
2823
- additionalProperties: false,
2824
- properties: {
2825
- token: { type: "string" },
2826
- member: { type: "string" },
2827
- allowIn: { type: "array", items: { type: "string" } },
2828
- allowPathPatterns: { type: "array", items: { type: "string" } },
2829
- message: { type: "string" }
2830
- },
2831
- oneOf: [
2832
- { required: ["token"], not: { required: ["member"] } },
2833
- { required: ["member"], not: { required: ["token"] } }
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
- anyOf: [
2839
- { required: ["token"] },
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
- token: options.token,
2863
- allowIn: options.allowIn,
2864
- allowPathPatterns: options.allowPathPatterns
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
- type: "object",
3127
- additionalProperties: false,
3128
- properties: {
3129
- allow: {
3130
- type: "array",
3131
- items: { type: "string", enum: ["class", "default", "enum", "function", "variable"] }
3132
- },
3133
- runtimeImports: { type: "string", enum: ["allow", "ban"] },
3134
- allowImportSources: { type: "array", items: { type: "string" } },
3135
- banReExports: { type: "boolean" }
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
- type: "object",
3303
- additionalProperties: false,
3304
- properties: {
3305
- namespaces: { type: "array", items: { type: "string" } },
3306
- methods: { type: "array", items: { type: "string" } },
3307
- structuralMethods: { type: "array", items: { type: "string" } },
3308
- allowZodScalars: { type: "boolean" }
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
- type: "object",
3372
- additionalProperties: false,
3373
- properties: {
3374
- banReExports: { type: "boolean" },
3375
- allowPattern: optionsPatternSchema
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
- function forwardedExpression(node) {
3426
- if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") {
3427
- return node.body;
3428
- }
3429
- if (!node.body || node.body.type !== "BlockStatement") {
3430
- return null;
3431
- }
3432
- const statements = node.body.body.filter((statement) => statement.type !== "EmptyStatement" && !(statement.type === "ExpressionStatement" && statement.directive));
3433
- if (statements.length !== 1) {
3434
- return null;
3435
- }
3436
- const statement = statements[0];
3437
- if (statement.type === "ReturnStatement") {
3438
- return statement.argument;
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
- type: "object",
3456
- additionalProperties: false,
3457
- properties: {
3458
- allowPattern: optionsPatternSchema,
3459
- allowCallees: { type: "array", items: { type: "string" } },
3460
- allowAsync: { type: "boolean" }
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}} does nothing useful; inline it at the call site."
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 { allowPattern, allowCallees: calleePatterns = [], allowAsync: asyncAllowed = false } = optionsFirst(context, {});
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 [...exportsCollectFunctions(program), ...declarationsTopLevelUnexportedFunctions(program)]) {
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) || !declarationsIsTrivialFunction(item.node)) {
3832
+ if (seen.has(key) || optionsPatternsTest(allowed, item.name)) {
3483
3833
  continue;
3484
3834
  }
3485
- const expression = forwardedExpression(item.node);
3486
- const call = expression && forwardedCall(expression);
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
- seen.add(key);
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
- type: "object",
3510
- additionalProperties: false,
3511
- properties: {
3512
- allowPattern: optionsPatternSchema
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
- type: "object",
3554
- additionalProperties: false,
3555
- properties: {
3556
- methods: { type: "array", items: { type: "string" } },
3557
- sanitizers: { type: "array", items: { type: "string" } },
3558
- allowSanitizedBindings: { type: "boolean" }
3559
- },
3560
- required: ["methods", "sanitizers"]
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
- if (!method || !methods.includes(method)) {
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
- type: "object",
3638
- additionalProperties: false,
3639
- properties: {
3640
- minProperties: { type: "integer", minimum: 1, default: 3 },
3641
- scope: { type: "string", enum: ["call-args", "all"], default: "call-args" },
3642
- indent: { ...layoutIndentSchema, default: 2 }
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
- type: "object",
3698
- additionalProperties: false,
3699
- properties: {
3700
- matchFileName: { type: "boolean" },
3701
- allowTypeExports: { type: "boolean" },
3702
- denyTypePattern: optionsPatternSchema
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
- type: "object",
3831
- additionalProperties: false,
3832
- properties: {
3833
- allowFunctionValues: { type: "boolean" },
3834
- allowTypeExports: { type: "boolean" },
3835
- allowReExports: { type: "boolean" }
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 { allowFunctionValues = false, allowTypeExports = false, allowReExports = false } = optionsFirst(context, {});
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
- type: "object",
3966
- additionalProperties: false,
3967
- properties: {
3968
- returnMinStatements: { type: "integer", minimum: 1, default: 3 }
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 messageId = 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;
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
- type: "object",
4057
- additionalProperties: false,
4058
- properties: {
4059
- max: { type: "integer", minimum: 0 },
4060
- names: { type: "object", additionalProperties: { type: "string", pattern: bindingName.source } }
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 a type namespace '{{name}}' for {{count}} named imports (maximum {{max}}).{{reason}}"
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: { name, count: node.specifiers.length, max, reason: reason ? ` Autofix skipped: ${reason}.` : "" },
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
- type: "object",
4166
- additionalProperties: false,
4167
- properties: {
4168
- factory: { type: "string" },
4169
- allDeclarations: { type: "boolean" }
4170
- },
4171
- required: ["factory"]
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
- type: "object",
4213
- additionalProperties: false,
4214
- properties: {
4215
- maxParams: { type: "integer", minimum: 1 },
4216
- allDeclarations: { type: "boolean" },
4217
- allowPattern: optionsPatternSchema
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 { maxParams = 1, allDeclarations = false, allowPattern } = optionsFirst(context, {});
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
- type: "object",
4262
- additionalProperties: false,
4263
- properties: {
4264
- composers: { type: "array", items: { type: "string" } },
4265
- handlerMethod: { type: "string" },
4266
- outputMethod: { type: "string" }
4267
- },
4268
- required: ["composers"]
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 { composers, handlerMethod = "handler", outputMethod = "output" } = optionsFirst(context);
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
- type: "object",
4305
- additionalProperties: false,
4306
- properties: {
4307
- when: { type: "string" },
4308
- require: { type: "string" },
4309
- pairs: {
4310
- type: "array",
4311
- items: {
4312
- type: "object",
4313
- additionalProperties: false,
4314
- properties: {
4315
- when: { type: "string" },
4316
- require: { type: "string" }
4317
- },
4318
- required: ["when", "require"]
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
- anyOf: [
4323
- { required: ["when", "require"] },
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
- type: "object",
4381
- additionalProperties: false,
4382
- properties: {
4383
- exportName: { type: "string" },
4384
- bannedHooks: { type: "array", items: { type: "string" } },
4385
- banIntrinsicJsx: { type: "boolean" }
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: [{ exportName: "Route", bannedHooks: ["useState", "useEffect", "useMutation"], banIntrinsicJsx: true }],
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
- type: "object",
4463
- additionalProperties: false,
4464
- properties: {
4465
- callees: { type: "array", items: { type: "string" } },
4466
- forbid: optionsPatternSchema,
4467
- require: optionsPatternSchema,
4468
- flags: { type: "string", pattern: "^[dgimsuvy]*$" }
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}}'."