oxlint-plugin-arch 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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
896
- }
897
- }],
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
+ }
1064
+ }
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
  }
@@ -1082,29 +1252,152 @@ var chainNewline = defineRule2({
1082
1252
  }
1083
1253
  });
1084
1254
 
1085
- // src/rules/declaration-name.ts
1255
+ // src/rules/component-props.ts
1086
1256
  import { defineRule as defineRule3 } from "@oxlint/plugins";
1087
- var declarationName = defineRule3({
1257
+ function unwrapExport(statement) {
1258
+ return statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
1259
+ }
1260
+ function topLevelComponents(program) {
1261
+ return program.body.flatMap((statement, index) => {
1262
+ const declaration = unwrapExport(statement);
1263
+ if (declaration?.type === "FunctionDeclaration" && declaration.id) {
1264
+ return [{ candidate: { name: declaration.id.name, node: declaration, declaration }, index }];
1265
+ }
1266
+ if (declaration?.type !== "VariableDeclaration") {
1267
+ return [];
1268
+ }
1269
+ return declaration.declarations.flatMap((item) => item.id.type === "Identifier" ? [{ candidate: { name: item.id.name, node: item, declaration, initializer: item.init }, index }] : []);
1270
+ });
1271
+ }
1272
+ function propsTarget(node) {
1273
+ if (node?.type === "FunctionDeclaration" || node?.type === "FunctionExpression" || node?.type === "ArrowFunctionExpression") {
1274
+ return { fn: node };
1275
+ }
1276
+ if (node?.type !== "CallExpression") {
1277
+ return;
1278
+ }
1279
+ const name = astCallName(node);
1280
+ const inner = name === "memo" || name === "forwardRef" ? propsTarget(node.arguments[0]) : undefined;
1281
+ const typeArgument = name === "forwardRef" ? node.typeArguments?.params[1] : undefined;
1282
+ return inner && typeArgument ? { ...inner, typeArgument } : inner;
1283
+ }
1284
+ function parameterType(parameter) {
1285
+ if (parameter?.type === "AssignmentPattern") {
1286
+ return parameterType(parameter.left);
1287
+ }
1288
+ return parameter && parameter.type !== "TSParameterProperty" ? parameter.typeAnnotation?.typeAnnotation : undefined;
1289
+ }
1290
+ function containsTypeLiteral(node) {
1291
+ let found = false;
1292
+ astVisit(node, [], (candidate) => {
1293
+ found ||= candidate.type === "TSTypeLiteral";
1294
+ });
1295
+ return found;
1296
+ }
1297
+ function isPassThrough(type) {
1298
+ if (type.type !== "TSTypeReference") {
1299
+ return false;
1300
+ }
1301
+ const { typeName } = type;
1302
+ const name = typeName.type === "TSQualifiedName" ? typeName.right.name : typeName.type === "Identifier" ? typeName.name : "";
1303
+ const argument = type.typeArguments?.params.length === 1 ? type.typeArguments.params[0] : undefined;
1304
+ return name === "ComponentProps" && (argument?.type === "TSTypeQuery" || argument?.type === "TSLiteralType" && argument.literal.type === "Literal" && typeof argument.literal.value === "string");
1305
+ }
1306
+ var componentProps = defineRule3({
1088
1307
  meta: {
1089
1308
  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
1106
- }
1107
- }],
1309
+ schema: [
1310
+ {
1311
+ type: "object",
1312
+ additionalProperties: false,
1313
+ properties: {
1314
+ filePrefix: { type: "boolean" }
1315
+ }
1316
+ }
1317
+ ],
1318
+ messages: {
1319
+ inline: "'{{name}}' props are typed inline. Declare `interface {{expected}}` directly above it and use that.",
1320
+ name: "'{{name}}' props must use `interface {{expected}}` declared directly above it, not '{{actual}}'.",
1321
+ placement: "Move `interface {{expected}}` directly above '{{name}}', with only blank lines between them.",
1322
+ rename: "Local component '{{name}}' would need `interface {{expected}}`, which breaks the file prefix '{{prefix}}'. Rename the component to start with '{{prefix}}' (for example '{{prefix}}Item')."
1323
+ }
1324
+ },
1325
+ createOnce(context) {
1326
+ return {
1327
+ Program(program) {
1328
+ const { filePrefix = false } = optionsFirst(context, {});
1329
+ const stem = namingFileStem(namingFileBasename(context.filename).replace(/\.(tsx?|jsx?)$/, ""));
1330
+ const exported = new Set(exportsCollect(program).flatMap((binding) => binding.typeOnly ? [] : [binding.localName]));
1331
+ const interfaces = new Map(program.body.flatMap((statement, index) => {
1332
+ const declaration = unwrapExport(statement);
1333
+ return declaration?.type === "TSInterfaceDeclaration" ? [[declaration.id.name, index]] : [];
1334
+ }));
1335
+ for (const { candidate, index } of topLevelComponents(program)) {
1336
+ const target = propsTarget(candidate.initializer ?? candidate.declaration);
1337
+ const parameter = target?.fn.params[0];
1338
+ if (!target || !parameter || !reactComponentsIsLike(program, candidate)) {
1339
+ continue;
1340
+ }
1341
+ const type = target.typeArgument ?? parameterType(parameter);
1342
+ const expected = `${candidate.name}Props`;
1343
+ const data = { name: candidate.name, expected };
1344
+ if (filePrefix && !exported.has(candidate.name) && !candidate.name.toLowerCase().startsWith(namingNormalized(stem, "remove-separators"))) {
1345
+ context.report({ node: candidate.node, messageId: "rename", data: { ...data, prefix: namingPascalCase(stem) } });
1346
+ continue;
1347
+ }
1348
+ if (type && containsTypeLiteral(type)) {
1349
+ context.report({ node: type, messageId: "inline", data });
1350
+ continue;
1351
+ }
1352
+ if (type && isPassThrough(type)) {
1353
+ continue;
1354
+ }
1355
+ const declared = interfaces.get(expected);
1356
+ if (declared === undefined || type?.type !== "TSTypeReference" || type.typeName.type !== "Identifier" || type.typeName.name !== expected) {
1357
+ context.report({
1358
+ node: type ?? parameter,
1359
+ messageId: "name",
1360
+ data: { ...data, actual: type ? context.sourceCode.getText(type) : "untyped" }
1361
+ });
1362
+ continue;
1363
+ }
1364
+ const previous = program.body[declared];
1365
+ const statement = program.body[index];
1366
+ const between = previous && statement ? context.sourceCode.text.slice(previous.range[1], statement.range[0]) : "";
1367
+ if (declared !== index - 1 || between.trim() !== "") {
1368
+ context.report({ node: type, messageId: "placement", data });
1369
+ }
1370
+ }
1371
+ }
1372
+ };
1373
+ }
1374
+ });
1375
+
1376
+ // src/rules/declaration-name.ts
1377
+ import { defineRule as defineRule4 } from "@oxlint/plugins";
1378
+ var declarationName = defineRule4({
1379
+ meta: {
1380
+ type: "problem",
1381
+ schema: [
1382
+ {
1383
+ type: "object",
1384
+ additionalProperties: false,
1385
+ properties: {
1386
+ kinds: {
1387
+ type: "array",
1388
+ items: { type: "string", enum: [...declarationsKinds] }
1389
+ },
1390
+ pattern: optionsPatternSchema,
1391
+ flags: { type: "string" },
1392
+ stem: { type: "string", enum: [...namingStemModes] },
1393
+ trailingRoles: { type: "array", items: { type: "string" } },
1394
+ roleSeparators: { type: "array", items: { type: "string" } },
1395
+ normalize: { type: "string", enum: ["remove-separators", "none"] },
1396
+ singularize: { type: "string", enum: ["none", "trailing-s"] },
1397
+ allowPattern: optionsPatternSchema
1398
+ }
1399
+ }
1400
+ ],
1108
1401
  messages: {
1109
1402
  prefix: "'{{name}}' must start with file prefix '{{prefix}}'.",
1110
1403
  pattern: "'{{name}}' must match {{pattern}}."
@@ -1129,7 +1422,10 @@ var declarationName = defineRule3({
1129
1422
  context.report({
1130
1423
  node: item.node,
1131
1424
  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 ") }
1425
+ data: expected.length > 0 ? {
1426
+ name: item.name,
1427
+ pattern: optionsPatternLabel([options.pattern ?? []].flat().filter((source) => source !== ""))
1428
+ } : { name: item.name, prefix: prefixes.join(" or ") }
1133
1429
  });
1134
1430
  }
1135
1431
  return {
@@ -1156,23 +1452,27 @@ var declarationName = defineRule3({
1156
1452
  });
1157
1453
 
1158
1454
  // src/rules/export-file-prefix.ts
1159
- import { defineRule as defineRule4 } from "@oxlint/plugins";
1160
- var exportFilePrefix = defineRule4({
1455
+ import { defineRule as defineRule5 } from "@oxlint/plugins";
1456
+ var exportFilePrefix = defineRule5({
1161
1457
  meta: {
1162
1458
  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
1174
- }
1175
- }],
1459
+ schema: [
1460
+ {
1461
+ type: "object",
1462
+ additionalProperties: false,
1463
+ properties: {
1464
+ stem: { type: "string", enum: [...namingStemModes] },
1465
+ trailingRoles: { type: "array", items: { type: "string" } },
1466
+ roleSeparators: { type: "array", items: { type: "string" } },
1467
+ normalize: { type: "string", enum: ["remove-separators", "none"] },
1468
+ singularize: { type: "string", enum: ["none", "trailing-s"] },
1469
+ serviceMethods: { type: "boolean" },
1470
+ allFunctions: { type: "boolean" },
1471
+ allDeclarations: { type: "boolean" },
1472
+ allowPattern: optionsPatternSchema
1473
+ }
1474
+ }
1475
+ ],
1176
1476
  messages: {
1177
1477
  prefix: "'{{name}}' must start with file prefix '{{prefix}}'."
1178
1478
  }
@@ -1192,6 +1492,12 @@ var exportFilePrefix = defineRule4({
1192
1492
  name: binding.localName ?? binding.exportedName,
1193
1493
  node: binding.node
1194
1494
  }));
1495
+ if (options.allFunctions && !options.allDeclarations) {
1496
+ const exportedNames = new Set(names.map((item) => item.name));
1497
+ 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"))));
1498
+ }
1499
+ if (options.serviceMethods)
1500
+ names.push(...declarationsPublicServiceMethods(program));
1195
1501
  const seen = new Set;
1196
1502
  for (const item of names) {
1197
1503
  const key = `${item.name}:${item.node.start}:${item.node.end}`;
@@ -1212,22 +1518,24 @@ var exportFilePrefix = defineRule4({
1212
1518
  });
1213
1519
 
1214
1520
  // src/rules/export-name-pattern.ts
1215
- import { defineRule as defineRule5 } from "@oxlint/plugins";
1216
- var exportNamePattern = defineRule5({
1521
+ import { defineRule as defineRule6 } from "@oxlint/plugins";
1522
+ var exportNamePattern = defineRule6({
1217
1523
  meta: {
1218
1524
  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
- }],
1525
+ schema: [
1526
+ {
1527
+ type: "object",
1528
+ additionalProperties: false,
1529
+ properties: {
1530
+ pattern: optionsPatternSchema,
1531
+ flags: { type: "string" },
1532
+ ignoreTypeExports: { type: "boolean" },
1533
+ allDeclarations: { type: "boolean" },
1534
+ allowPattern: optionsPatternSchema
1535
+ },
1536
+ required: ["pattern"]
1537
+ }
1538
+ ],
1231
1539
  messages: {
1232
1540
  pattern: "'{{name}}' must match {{pattern}}."
1233
1541
  }
@@ -1264,24 +1572,27 @@ var exportNamePattern = defineRule5({
1264
1572
  });
1265
1573
 
1266
1574
  // src/rules/filename-export-name.ts
1267
- import { defineRule as defineRule6 } from "@oxlint/plugins";
1268
- var filenameExportName = defineRule6({
1575
+ import { defineRule as defineRule7 } from "@oxlint/plugins";
1576
+ var filenameExportName = defineRule7({
1269
1577
  meta: {
1270
1578
  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
- }],
1579
+ schema: [
1580
+ {
1581
+ type: "object",
1582
+ additionalProperties: false,
1583
+ properties: {
1584
+ file: { type: "string" },
1585
+ export: { type: "string" },
1586
+ mode: { type: "string", enum: ["all", "some"] },
1587
+ placeholderPattern: { type: "string" },
1588
+ camelCase: { type: "boolean" },
1589
+ allDeclarations: { type: "boolean" }
1590
+ },
1591
+ required: ["file", "export"]
1592
+ }
1593
+ ],
1283
1594
  messages: {
1284
- mismatch: "Function '{{actual}}' must match '{{expected}}'."
1595
+ mismatch: "Function '{{actual}}' must be named '{{expected}}' for file '{{file}}'."
1285
1596
  }
1286
1597
  },
1287
1598
  createOnce(context) {
@@ -1292,13 +1603,14 @@ var filenameExportName = defineRule6({
1292
1603
  export: exportTemplate,
1293
1604
  mode = "all",
1294
1605
  placeholderPattern,
1295
- allDeclarations = false
1606
+ allDeclarations = false,
1607
+ camelCase = false
1296
1608
  } = optionsFirst(context);
1297
1609
  const groups = namingMatchTemplate(namingFileBasename(context.filename), file, placeholderPattern);
1298
1610
  if (!groups) {
1299
1611
  return;
1300
1612
  }
1301
- const expected = factoriesExpandTemplate(exportTemplate, groups);
1613
+ const expected = factoriesExpandTemplate(exportTemplate, groups, camelCase);
1302
1614
  const functions = allDeclarations ? declarationsCollectFunctions(program) : exportsCollectFunctions(program);
1303
1615
  if (mode === "some") {
1304
1616
  if (!functions.some((item) => item.name === expected)) {
@@ -1306,7 +1618,7 @@ var filenameExportName = defineRule6({
1306
1618
  context.report({
1307
1619
  node: first?.node ?? program,
1308
1620
  messageId: "mismatch",
1309
- data: { actual: first?.name ?? "<none>", expected }
1621
+ data: { actual: first?.name ?? "<none>", expected, file: namingFileBasename(context.filename) }
1310
1622
  });
1311
1623
  }
1312
1624
  return;
@@ -1316,7 +1628,7 @@ var filenameExportName = defineRule6({
1316
1628
  context.report({
1317
1629
  node: item.node,
1318
1630
  messageId: "mismatch",
1319
- data: { actual: item.name, expected }
1631
+ data: { actual: item.name, expected, file: namingFileBasename(context.filename) }
1320
1632
  });
1321
1633
  }
1322
1634
  }
@@ -1326,20 +1638,22 @@ var filenameExportName = defineRule6({
1326
1638
  });
1327
1639
 
1328
1640
  // src/rules/filename-match.ts
1329
- import { defineRule as defineRule7 } from "@oxlint/plugins";
1330
- var filenameMatch = defineRule7({
1641
+ import { defineRule as defineRule8 } from "@oxlint/plugins";
1642
+ var filenameMatch = defineRule8({
1331
1643
  meta: {
1332
1644
  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
- }],
1645
+ schema: [
1646
+ {
1647
+ type: "object",
1648
+ additionalProperties: false,
1649
+ properties: {
1650
+ pattern: optionsPatternSchema,
1651
+ message: { type: "string" },
1652
+ flags: { type: "string" }
1653
+ },
1654
+ required: ["pattern", "message"]
1655
+ }
1656
+ ],
1343
1657
  messages: {
1344
1658
  mismatch: "{{message}}"
1345
1659
  }
@@ -1362,21 +1676,24 @@ var filenameMatch = defineRule7({
1362
1676
  });
1363
1677
 
1364
1678
  // src/rules/folder-prefix.ts
1365
- import { defineRule as defineRule8 } from "@oxlint/plugins";
1366
- var folderPrefix = defineRule8({
1679
+ import { defineRule as defineRule9 } from "@oxlint/plugins";
1680
+ var folderPrefix = defineRule9({
1367
1681
  meta: {
1368
1682
  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" }
1376
- }
1377
- }],
1683
+ schema: [
1684
+ {
1685
+ type: "object",
1686
+ additionalProperties: false,
1687
+ properties: {
1688
+ singularize: { type: "string", enum: ["none", "trailing-s"] },
1689
+ separators: { type: "array", items: { type: "string" } },
1690
+ after: { type: "string" },
1691
+ stripPrefixes: { type: "array", items: { type: "string", minLength: 1 } }
1692
+ }
1693
+ }
1694
+ ],
1378
1695
  messages: {
1379
- prefix: "File '{{file}}' must start with folder name '{{folder}}' or an allowed singular form."
1696
+ prefix: "File '{{file}}' must start with '{{expected}}' to match its domain folder '{{folder}}'."
1380
1697
  }
1381
1698
  },
1382
1699
  createOnce(context) {
@@ -1385,7 +1702,8 @@ var folderPrefix = defineRule8({
1385
1702
  const {
1386
1703
  singularize = "trailing-s",
1387
1704
  separators = ["-"],
1388
- after
1705
+ after,
1706
+ stripPrefixes = []
1389
1707
  } = optionsFirst(context, {});
1390
1708
  const filename = namingPosixPath(context.filename);
1391
1709
  const folders = after ? namingSegmentsAfter(filename, after) : [namingDirSegments(filename).at(-1) ?? ""];
@@ -1396,13 +1714,19 @@ var folderPrefix = defineRule8({
1396
1714
  const separator = separators[0] ?? "-";
1397
1715
  const prefixes = namingFolderPrefixes(resolved, separator, singularize);
1398
1716
  const file = namingFileBasename(filename);
1399
- const stem = file.replace(/\.(tsx?|jsx?)$/, "");
1717
+ const rawStem = file.replace(/\.(tsx?|jsx?)$/, "");
1718
+ const strip = stripPrefixes.find((prefix) => rawStem.startsWith(prefix));
1719
+ const stem = strip ? rawStem.slice(strip.length) : rawStem;
1400
1720
  const valid = prefixes.some((prefix) => stem === prefix || separators.some((item) => stem.startsWith(`${prefix}${item}`)));
1401
1721
  if (!valid) {
1402
1722
  context.report({
1403
1723
  node: program,
1404
1724
  messageId: "prefix",
1405
- data: { file, folder: prefixes[0] ?? "" }
1725
+ data: {
1726
+ file,
1727
+ folder: resolved.join("/"),
1728
+ expected: prefixes.map((prefix) => `${stripPrefixes[0] ?? ""}${prefix}`).join(" or ")
1729
+ }
1406
1730
  });
1407
1731
  }
1408
1732
  }
@@ -1411,7 +1735,7 @@ var folderPrefix = defineRule8({
1411
1735
  });
1412
1736
 
1413
1737
  // src/rules/jsx-attributes-multiline.ts
1414
- import { defineRule as defineRule9 } from "@oxlint/plugins";
1738
+ import { defineRule as defineRule10 } from "@oxlint/plugins";
1415
1739
  function jsxAttributesMultilineEdits(node, text, attributeIndent, baseIndent, newline) {
1416
1740
  const first = node.attributes[0];
1417
1741
  const last = node.attributes.at(-1);
@@ -1421,10 +1745,12 @@ function jsxAttributesMultilineEdits(node, text, attributeIndent, baseIndent, ne
1421
1745
  const openingRange = [prefix.range[1], first.range[0]];
1422
1746
  if (!/^\s*$/.test(text.slice(...openingRange)))
1423
1747
  return null;
1424
- const edits = [{
1425
- range: openingRange,
1426
- text: `${newline}${attributeIndent}`
1427
- }];
1748
+ const edits = [
1749
+ {
1750
+ range: openingRange,
1751
+ text: `${newline}${attributeIndent}`
1752
+ }
1753
+ ];
1428
1754
  for (let index = 1;index < node.attributes.length; index++) {
1429
1755
  const previous = node.attributes[index - 1];
1430
1756
  const current = node.attributes[index];
@@ -1440,18 +1766,20 @@ function jsxAttributesMultilineEdits(node, text, attributeIndent, baseIndent, ne
1440
1766
  edits.push({ range: closingRange, text: `${newline}${baseIndent}` });
1441
1767
  return edits;
1442
1768
  }
1443
- var jsxAttributesMultiline = defineRule9({
1769
+ var jsxAttributesMultiline = defineRule10({
1444
1770
  meta: {
1445
1771
  type: "layout",
1446
1772
  fixable: "whitespace",
1447
- schema: [{
1448
- type: "object",
1449
- additionalProperties: false,
1450
- properties: {
1451
- minAttributes: { type: "integer", minimum: 1 },
1452
- indent: layoutIndentSchema
1453
- }
1454
- }],
1773
+ schema: [
1774
+ {
1775
+ type: "object",
1776
+ additionalProperties: false,
1777
+ properties: {
1778
+ minAttributes: { type: "integer", minimum: 1 },
1779
+ indent: layoutIndentSchema
1780
+ }
1781
+ }
1782
+ ],
1455
1783
  messages: {
1456
1784
  multiline: "Put every JSX attribute and the closing token on its own line."
1457
1785
  }
@@ -1487,8 +1815,8 @@ var jsxAttributesMultiline = defineRule9({
1487
1815
  });
1488
1816
 
1489
1817
  // src/rules/key-value-same-line.ts
1490
- import { defineRule as defineRule10 } from "@oxlint/plugins";
1491
- var keyValueSameLine = defineRule10({
1818
+ import { defineRule as defineRule11 } from "@oxlint/plugins";
1819
+ var keyValueSameLine = defineRule11({
1492
1820
  meta: {
1493
1821
  type: "layout",
1494
1822
  fixable: "whitespace",
@@ -1537,7 +1865,7 @@ var keyValueSameLine = defineRule10({
1537
1865
  });
1538
1866
 
1539
1867
  // src/rules/no-comments.ts
1540
- import { defineRule as defineRule11 } from "@oxlint/plugins";
1868
+ import { defineRule as defineRule12 } from "@oxlint/plugins";
1541
1869
  var NO_COMMENTS_LINE_GAP = /^[\t \r\n\u2028\u2029]*$/;
1542
1870
  var NO_COMMENTS_LINE_TERMINATOR = /\r\n|[\n\r\u2028\u2029]/;
1543
1871
  var NO_COMMENTS_TRAILING_LINES = /^[\t ]*(?:\r\n|[\n\r\u2028\u2029])(?:[\t ]*(?:\r\n|[\n\r\u2028\u2029]))*/;
@@ -1629,19 +1957,21 @@ function noCommentsJsxEmptyContainer(containers, start, end) {
1629
1957
  const container = containers[low - 1];
1630
1958
  return container?.range[1] >= end ? container : undefined;
1631
1959
  }
1632
- var noComments = defineRule11({
1960
+ var noComments = defineRule12({
1633
1961
  meta: {
1634
1962
  type: "suggestion",
1635
1963
  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 }
1643
- }
1644
- }],
1964
+ schema: [
1965
+ {
1966
+ type: "object",
1967
+ additionalProperties: false,
1968
+ properties: {
1969
+ allowWhy: { type: "boolean", default: true },
1970
+ allowPatterns: { type: "array", items: { type: "string" } },
1971
+ allowJsdoc: { type: "boolean", default: false }
1972
+ }
1973
+ }
1974
+ ],
1645
1975
  messages: {
1646
1976
  comment: "Remove this comment."
1647
1977
  }
@@ -1759,23 +2089,25 @@ var noComments = defineRule11({
1759
2089
  });
1760
2090
 
1761
2091
  // src/rules/no-extra-exports.ts
1762
- import { defineRule as defineRule12 } from "@oxlint/plugins";
1763
- var noExtraExports = defineRule12({
2092
+ import { defineRule as defineRule13 } from "@oxlint/plugins";
2093
+ var noExtraExports = defineRule13({
1764
2094
  meta: {
1765
2095
  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
- }],
2096
+ schema: [
2097
+ {
2098
+ type: "object",
2099
+ additionalProperties: false,
2100
+ properties: {
2101
+ names: { type: "array", items: { type: "string" } },
2102
+ patterns: { type: "array", items: { type: "string" } },
2103
+ domainStem: { type: "string", enum: [...namingStemModes] },
2104
+ trailingRoles: { type: "array", items: { type: "string" } },
2105
+ roleSeparators: { type: "array", items: { type: "string" } },
2106
+ allowTypeExports: { type: "boolean" }
2107
+ },
2108
+ required: ["names"]
2109
+ }
2110
+ ],
1779
2111
  messages: {
1780
2112
  extraExport: "Export '{{name}}' is not allowed; expected one of: {{allowed}}."
1781
2113
  }
@@ -1817,7 +2149,7 @@ var noExtraExports = defineRule12({
1817
2149
  });
1818
2150
 
1819
2151
  // src/rules/no-extra-factory-keys.ts
1820
- import { defineRule as defineRule13 } from "@oxlint/plugins";
2152
+ import { defineRule as defineRule14 } from "@oxlint/plugins";
1821
2153
  function compileFactoryKeys(pattern, keys, requireKeys) {
1822
2154
  const allowedList = keys.concat(requireKeys.filter((key) => !keys.includes(key)));
1823
2155
  return {
@@ -1827,31 +2159,33 @@ function compileFactoryKeys(pattern, keys, requireKeys) {
1827
2159
  requireKeys
1828
2160
  };
1829
2161
  }
1830
- var noExtraFactoryKeys = defineRule13({
2162
+ var noExtraFactoryKeys = defineRule14({
1831
2163
  meta: {
1832
2164
  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"]
2165
+ schema: [
2166
+ {
2167
+ type: "object",
2168
+ additionalProperties: false,
2169
+ properties: {
2170
+ keys: { type: "array", items: { type: "string" } },
2171
+ factoryPattern: optionsPatternSchema,
2172
+ requireKeys: { type: "array", items: { type: "string" } },
2173
+ keySets: {
2174
+ type: "array",
2175
+ items: {
2176
+ type: "object",
2177
+ additionalProperties: false,
2178
+ properties: {
2179
+ factoryPattern: optionsPatternSchema,
2180
+ keys: { type: "array", items: { type: "string" } },
2181
+ requireKeys: { type: "array", items: { type: "string" } }
2182
+ },
2183
+ required: ["factoryPattern", "keys"]
2184
+ }
1851
2185
  }
1852
2186
  }
1853
2187
  }
1854
- }],
2188
+ ],
1855
2189
  messages: {
1856
2190
  extraKey: "Factory '{{factory}}' must not return key '{{key}}'; allowed keys: {{allowed}}.",
1857
2191
  missingKey: "Factory '{{factory}}' must return key '{{key}}'.",
@@ -1904,7 +2238,11 @@ var noExtraFactoryKeys = defineRule13({
1904
2238
  context.report({
1905
2239
  node: property,
1906
2240
  messageId: "extraKey",
1907
- data: { factory: factory.name, key: key ?? "<computed or spread>", allowed: resolved.allowedList.join(", ") }
2241
+ data: {
2242
+ factory: factory.name,
2243
+ key: key ?? "<computed or spread>",
2244
+ allowed: resolved.allowedList.join(", ")
2245
+ }
1908
2246
  });
1909
2247
  }
1910
2248
  }
@@ -1925,21 +2263,24 @@ var noExtraFactoryKeys = defineRule13({
1925
2263
  });
1926
2264
 
1927
2265
  // src/rules/no-file-level-helpers.ts
1928
- import { defineRule as defineRule14 } from "@oxlint/plugins";
1929
- var noFileLevelHelpers = defineRule14({
2266
+ import { defineRule as defineRule15 } from "@oxlint/plugins";
2267
+ var noFileLevelHelpers = defineRule15({
1930
2268
  meta: {
1931
2269
  type: "problem",
1932
- schema: [{
1933
- type: "object",
1934
- additionalProperties: false,
1935
- properties: {
1936
- allowPattern: optionsPatternSchema,
1937
- detectComponents: { type: "boolean" },
1938
- hookPattern: optionsPatternSchema
1939
- }
1940
- }],
2270
+ schema: [
2271
+ {
2272
+ type: "object",
2273
+ additionalProperties: false,
2274
+ properties: {
2275
+ allowPattern: optionsPatternSchema,
2276
+ message: { type: "string" },
2277
+ detectComponents: { type: "boolean" },
2278
+ hookPattern: optionsPatternSchema
2279
+ }
2280
+ }
2281
+ ],
1941
2282
  messages: {
1942
- helper: "File-level helper {{name}} must move inside its owning component or hook."
2283
+ helper: "{{name}}: {{message}}"
1943
2284
  }
1944
2285
  },
1945
2286
  createOnce(context) {
@@ -1947,6 +2288,7 @@ var noFileLevelHelpers = defineRule14({
1947
2288
  Program(program) {
1948
2289
  const {
1949
2290
  allowPattern,
2291
+ message = "Keep this helper inside its owning component or hook.",
1950
2292
  detectComponents = true,
1951
2293
  hookPattern = "^use[A-Z]"
1952
2294
  } = optionsFirst(context, {});
@@ -1959,7 +2301,7 @@ var noFileLevelHelpers = defineRule14({
1959
2301
  context.report({
1960
2302
  node: candidate.node,
1961
2303
  messageId: "helper",
1962
- data: { name: candidate.name }
2304
+ data: { name: candidate.name, message }
1963
2305
  });
1964
2306
  }
1965
2307
  }
@@ -1968,9 +2310,9 @@ var noFileLevelHelpers = defineRule14({
1968
2310
  });
1969
2311
 
1970
2312
  // src/rules/no-imported-type-alias.ts
1971
- import { defineRule as defineRule15 } from "@oxlint/plugins";
2313
+ import { defineRule as defineRule16 } from "@oxlint/plugins";
1972
2314
  function importedAliasTarget(declaration, imports) {
1973
- if (declaration?.type !== "TSTypeAliasDeclaration" || declaration.typeAnnotation.type !== "TSTypeReference" || declaration.typeAnnotation.typeName.type !== "Identifier" || !imports.has(declaration.typeAnnotation.typeName.name)) {
2315
+ 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
2316
  return;
1975
2317
  }
1976
2318
  return declaration.typeAnnotation.typeName.name;
@@ -1978,18 +2320,34 @@ function importedAliasTarget(declaration, imports) {
1978
2320
  function specifierLocalName(specifier) {
1979
2321
  return specifier.local.type === "Identifier" ? specifier.local.name : specifier.local.value;
1980
2322
  }
1981
- var noImportedTypeAlias = defineRule15({
2323
+ var noImportedTypeAlias = defineRule16({
1982
2324
  meta: {
1983
2325
  type: "problem",
1984
2326
  schema: [],
1985
2327
  messages: {
1986
- alias: "Exported type '{{alias}}' must not re-alias imported type '{{name}}'."
2328
+ alias: "Import '{{name}}' from '{{source}}' at the callers instead of exporting alias '{{alias}}'. Keep genuine derivations; do not substitute an empty interface."
1987
2329
  }
1988
2330
  },
1989
2331
  createOnce(context) {
1990
2332
  return {
1991
2333
  Program(program) {
1992
- const imports = new Set(program.body.flatMap((statement) => statement.type === "ImportDeclaration" ? statement.specifiers.map((specifier) => specifier.local.name) : []));
2334
+ const imports = new Set;
2335
+ const sources = new Map;
2336
+ const names = new Map;
2337
+ for (const statement of program.body) {
2338
+ if (statement.type !== "ImportDeclaration")
2339
+ continue;
2340
+ for (const specifier of statement.specifiers) {
2341
+ const local = specifier.local.name;
2342
+ let name = local;
2343
+ if (specifier.type === "ImportSpecifier") {
2344
+ name = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
2345
+ }
2346
+ imports.add(local);
2347
+ sources.set(local, statement.source.value);
2348
+ names.set(local, name);
2349
+ }
2350
+ }
1993
2351
  const aliases = new Map;
1994
2352
  for (const statement of program.body) {
1995
2353
  const declaration = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement;
@@ -2007,7 +2365,11 @@ var noImportedTypeAlias = defineRule15({
2007
2365
  context.report({
2008
2366
  node: statement.declaration,
2009
2367
  messageId: "alias",
2010
- data: { alias: statement.declaration.id.name, name: inline }
2368
+ data: {
2369
+ alias: statement.declaration.id.name,
2370
+ name: names.get(inline) ?? inline,
2371
+ source: sources.get(inline) ?? "<import source>"
2372
+ }
2011
2373
  });
2012
2374
  }
2013
2375
  for (const specifier of statement.specifiers) {
@@ -2017,7 +2379,11 @@ var noImportedTypeAlias = defineRule15({
2017
2379
  context.report({
2018
2380
  node: specifier,
2019
2381
  messageId: "alias",
2020
- data: { alias: local, name: imported }
2382
+ data: {
2383
+ alias: local,
2384
+ name: names.get(imported) ?? imported,
2385
+ source: sources.get(imported) ?? "<import source>"
2386
+ }
2021
2387
  });
2022
2388
  }
2023
2389
  }
@@ -2028,26 +2394,39 @@ var noImportedTypeAlias = defineRule15({
2028
2394
  });
2029
2395
 
2030
2396
  // src/rules/no-inline-schema-elements.ts
2031
- import { defineRule as defineRule16 } from "@oxlint/plugins";
2032
- var noInlineSchemaElements = defineRule16({
2397
+ import { defineRule as defineRule17 } from "@oxlint/plugins";
2398
+ var noInlineSchemaElements = defineRule17({
2033
2399
  meta: {
2034
2400
  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" }
2043
- }
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
- }],
2401
+ schema: [
2402
+ {
2403
+ type: "object",
2404
+ additionalProperties: false,
2405
+ properties: {
2406
+ namespaces: { type: "array", items: { type: "string" } },
2407
+ methods: { type: "array", items: { type: "string" } },
2408
+ structuralMethods: { type: "array", items: { type: "string" } },
2409
+ allowZodScalars: { type: "boolean" }
2410
+ }
2411
+ }
2412
+ ],
2413
+ defaultOptions: [
2414
+ {
2415
+ namespaces: ["z"],
2416
+ methods: ["array", "union", "record", "tuple"],
2417
+ structuralMethods: [
2418
+ "array",
2419
+ "discriminatedUnion",
2420
+ "intersection",
2421
+ "lazy",
2422
+ "object",
2423
+ "record",
2424
+ "tuple",
2425
+ "union"
2426
+ ],
2427
+ allowZodScalars: true
2428
+ }
2429
+ ],
2051
2430
  messages: { inline: "z.{{method}}() elements must use named schemas." }
2052
2431
  },
2053
2432
  createOnce(context) {
@@ -2077,7 +2456,7 @@ var noInlineSchemaElements = defineRule16({
2077
2456
  });
2078
2457
 
2079
2458
  // src/rules/no-inline-types.ts
2080
- import { defineRule as defineRule17 } from "@oxlint/plugins";
2459
+ import { defineRule as defineRule18 } from "@oxlint/plugins";
2081
2460
  function parameterAnnotation(parameter) {
2082
2461
  if (parameter.type === "TSParameterProperty") {
2083
2462
  return parameterAnnotation(parameter.parameter);
@@ -2087,19 +2466,21 @@ function parameterAnnotation(parameter) {
2087
2466
  }
2088
2467
  return parameter.typeAnnotation;
2089
2468
  }
2090
- var noInlineTypes = defineRule17({
2469
+ var noInlineTypes = defineRule18({
2091
2470
  meta: {
2092
2471
  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 }
2101
- }
2102
- }],
2472
+ schema: [
2473
+ {
2474
+ type: "object",
2475
+ additionalProperties: false,
2476
+ properties: {
2477
+ parameters: { type: "boolean" },
2478
+ returns: { type: "boolean" },
2479
+ functionTypes: { type: "boolean" },
2480
+ minMembers: { type: "integer", minimum: 1 }
2481
+ }
2482
+ }
2483
+ ],
2103
2484
  messages: {
2104
2485
  inlineType: "Use a named type instead of an inline {{kind}} type in this {{position}} annotation."
2105
2486
  }
@@ -2155,7 +2536,7 @@ var noInlineTypes = defineRule17({
2155
2536
  });
2156
2537
 
2157
2538
  // src/rules/no-literal-in.ts
2158
- import { defineRule as defineRule18 } from "@oxlint/plugins";
2539
+ import { defineRule as defineRule19 } from "@oxlint/plugins";
2159
2540
  function literalKey(node) {
2160
2541
  if (node.type === "Literal" && typeof node.value === "string") {
2161
2542
  return node.value;
@@ -2164,16 +2545,18 @@ function literalKey(node) {
2164
2545
  return node.quasis[0]?.value.cooked ?? node.quasis[0]?.value.raw;
2165
2546
  }
2166
2547
  }
2167
- var noLiteralIn = defineRule18({
2548
+ var noLiteralIn = defineRule19({
2168
2549
  meta: {
2169
2550
  type: "suggestion",
2170
- schema: [{
2171
- type: "object",
2172
- additionalProperties: false,
2173
- properties: {
2174
- allow: { type: "array", items: { type: "string" } }
2551
+ schema: [
2552
+ {
2553
+ type: "object",
2554
+ additionalProperties: false,
2555
+ properties: {
2556
+ allow: { type: "array", items: { type: "string" } }
2557
+ }
2175
2558
  }
2176
- }],
2559
+ ],
2177
2560
  defaultOptions: [{ allow: [] }],
2178
2561
  messages: {
2179
2562
  literalIn: "Avoid '{{key}}' in value; use Object.hasOwn() or a discriminated union."
@@ -2203,32 +2586,36 @@ var noLiteralIn = defineRule18({
2203
2586
  });
2204
2587
 
2205
2588
  // src/rules/no-local-schema-construction.ts
2206
- import { defineRule as defineRule19 } from "@oxlint/plugins";
2589
+ import { defineRule as defineRule20 } from "@oxlint/plugins";
2207
2590
  var defaultRuntimeImportMessage = "Do not import runtime schema constructors from {{source}}; import a named schema instead.";
2208
2591
  var defaultConstructionMessage = "Do not construct schemas locally; import a named schema instead.";
2209
2592
  function formatMessage(template, data) {
2210
2593
  return Object.entries(data).reduce((result, [key, value]) => result.replaceAll(`{{${key}}}`, value), template);
2211
2594
  }
2212
- var noLocalSchemaConstruction = defineRule19({
2595
+ var noLocalSchemaConstruction = defineRule20({
2213
2596
  meta: {
2214
2597
  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" }
2226
- }
2227
- }],
2228
- defaultOptions: [{
2229
- packages: ["zod"],
2230
- namespaces: ["z"]
2231
- }],
2598
+ schema: [
2599
+ {
2600
+ type: "object",
2601
+ additionalProperties: false,
2602
+ properties: {
2603
+ packages: { type: "array", items: { type: "string" } },
2604
+ namespaces: { type: "array", items: { type: "string" } },
2605
+ allowIn: { type: "array", items: { type: "string" } },
2606
+ allowPathPatterns: { type: "array", items: { type: "string" } },
2607
+ message: { type: "string" },
2608
+ runtimeImportMessage: { type: "string" },
2609
+ constructionMessage: { type: "string" }
2610
+ }
2611
+ }
2612
+ ],
2613
+ defaultOptions: [
2614
+ {
2615
+ packages: ["zod"],
2616
+ namespaces: ["z"]
2617
+ }
2618
+ ],
2232
2619
  messages: {
2233
2620
  runtimeImport: "{{message}}",
2234
2621
  construction: "{{message}}"
@@ -2265,7 +2652,9 @@ var noLocalSchemaConstruction = defineRule19({
2265
2652
  node,
2266
2653
  messageId: "runtimeImport",
2267
2654
  data: {
2268
- message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, { source: node.source.value })
2655
+ message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
2656
+ source: node.source.value
2657
+ })
2269
2658
  }
2270
2659
  });
2271
2660
  },
@@ -2282,7 +2671,9 @@ var noLocalSchemaConstruction = defineRule19({
2282
2671
  node,
2283
2672
  messageId: "runtimeImport",
2284
2673
  data: {
2285
- message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, { source: node.source.value })
2674
+ message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
2675
+ source: node.source.value
2676
+ })
2286
2677
  }
2287
2678
  });
2288
2679
  },
@@ -2299,7 +2690,9 @@ var noLocalSchemaConstruction = defineRule19({
2299
2690
  node,
2300
2691
  messageId: "runtimeImport",
2301
2692
  data: {
2302
- message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, { source: node.source.value })
2693
+ message: formatMessage(runtimeImportMessage ?? message ?? defaultRuntimeImportMessage, {
2694
+ source: node.source.value
2695
+ })
2303
2696
  }
2304
2697
  });
2305
2698
  },
@@ -2321,18 +2714,20 @@ var noLocalSchemaConstruction = defineRule19({
2321
2714
  });
2322
2715
 
2323
2716
  // src/rules/no-member-comments.ts
2324
- import { defineRule as defineRule20 } from "@oxlint/plugins";
2325
- var noMemberComments = defineRule20({
2717
+ import { defineRule as defineRule21 } from "@oxlint/plugins";
2718
+ var noMemberComments = defineRule21({
2326
2719
  meta: {
2327
2720
  type: "suggestion",
2328
2721
  fixable: "code",
2329
- schema: [{
2330
- type: "object",
2331
- additionalProperties: false,
2332
- properties: {
2333
- allowWhy: { type: "boolean", default: true }
2722
+ schema: [
2723
+ {
2724
+ type: "object",
2725
+ additionalProperties: false,
2726
+ properties: {
2727
+ allowWhy: { type: "boolean", default: true }
2728
+ }
2334
2729
  }
2335
- }],
2730
+ ],
2336
2731
  messages: {
2337
2732
  memberComment: "Remove this comment from the type member."
2338
2733
  }
@@ -2416,7 +2811,7 @@ var noMemberComments = defineRule20({
2416
2811
  });
2417
2812
 
2418
2813
  // src/rules/no-module-mutable-state.ts
2419
- import { defineRule as defineRule21 } from "@oxlint/plugins";
2814
+ import { defineRule as defineRule22 } from "@oxlint/plugins";
2420
2815
  function moduleMutableStateBindings(pattern) {
2421
2816
  if (pattern.type === "Identifier") {
2422
2817
  return [{ name: pattern.name, node: pattern }];
@@ -2455,20 +2850,22 @@ function moduleMutableStateInsideAmbient(node) {
2455
2850
  }
2456
2851
  return false;
2457
2852
  }
2458
- var noModuleMutableState = defineRule21({
2853
+ var noModuleMutableState = defineRule22({
2459
2854
  meta: {
2460
2855
  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
2856
+ schema: [
2857
+ {
2858
+ type: "object",
2859
+ additionalProperties: false,
2860
+ properties: {
2861
+ kinds: {
2862
+ type: "array",
2863
+ items: { type: "string", enum: ["let", "var"] }
2864
+ },
2865
+ allowNamePattern: optionsPatternSchema
2866
+ }
2470
2867
  }
2471
- }],
2868
+ ],
2472
2869
  messages: {
2473
2870
  moduleState: "module state is shared across requests/tests; keep it local."
2474
2871
  }
@@ -2505,7 +2902,7 @@ var noModuleMutableState = defineRule21({
2505
2902
  });
2506
2903
 
2507
2904
  // src/rules/no-promise-all-mutation.ts
2508
- import { defineRule as defineRule22 } from "@oxlint/plugins";
2905
+ import { defineRule as defineRule23 } from "@oxlint/plugins";
2509
2906
  function mutationBinding(node) {
2510
2907
  if (node.type === "Identifier") {
2511
2908
  return node;
@@ -2533,23 +2930,27 @@ function mutationBindings(node) {
2533
2930
  }
2534
2931
  }
2535
2932
  }
2536
- var noPromiseAllMutation = defineRule22({
2933
+ var noPromiseAllMutation = defineRule23({
2537
2934
  meta: {
2538
2935
  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" }
2546
- }
2547
- }],
2548
- defaultOptions: [{
2549
- combinators: ["Promise.all", "Promise.allSettled", "Promise.any", "Promise.race"],
2550
- methods: ["push", "unshift", "splice", "set", "add"],
2551
- checkAssignments: true
2552
- }],
2936
+ schema: [
2937
+ {
2938
+ type: "object",
2939
+ additionalProperties: false,
2940
+ properties: {
2941
+ combinators: { type: "array", items: { type: "string" } },
2942
+ methods: { type: "array", items: { type: "string" } },
2943
+ checkAssignments: { type: "boolean" }
2944
+ }
2945
+ }
2946
+ ],
2947
+ defaultOptions: [
2948
+ {
2949
+ combinators: ["Promise.all", "Promise.allSettled", "Promise.any", "Promise.race"],
2950
+ methods: ["push", "unshift", "splice", "set", "add"],
2951
+ checkAssignments: true
2952
+ }
2953
+ ],
2553
2954
  messages: {
2554
2955
  mutation: "Do not mutate outer binding '{{name}}' inside {{combinator}}()."
2555
2956
  }
@@ -2629,20 +3030,22 @@ var noPromiseAllMutation = defineRule22({
2629
3030
  });
2630
3031
 
2631
3032
  // src/rules/no-rederive-schema.ts
2632
- import { defineRule as defineRule23 } from "@oxlint/plugins";
2633
- var noRederiveSchema = defineRule23({
3033
+ import { defineRule as defineRule24 } from "@oxlint/plugins";
3034
+ var noRederiveSchema = defineRule24({
2634
3035
  meta: {
2635
3036
  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
- }],
3037
+ schema: [
3038
+ {
3039
+ type: "object",
3040
+ additionalProperties: false,
3041
+ properties: {
3042
+ from: { type: "array", items: { type: "string" } },
3043
+ namespaces: { type: "array", items: { type: "string" } },
3044
+ operators: { type: "array", items: { type: "string" } }
3045
+ },
3046
+ required: ["from"]
3047
+ }
3048
+ ],
2646
3049
  messages: {
2647
3050
  rederive: "Do not derive {{operator}} from imported schema {{schema}}; import its DTO type."
2648
3051
  }
@@ -2655,10 +3058,7 @@ var noRederiveSchema = defineRule23({
2655
3058
  imported = new Set(program.body.flatMap((statement) => statement.type === "ImportDeclaration" && from.includes(statement.source.value) ? statement.specifiers.map((specifier) => specifier.local.name) : []));
2656
3059
  },
2657
3060
  TSTypeReference(node) {
2658
- const {
2659
- namespaces = ["z"],
2660
- operators = ["infer", "input"]
2661
- } = optionsFirst(context);
3061
+ const { namespaces = ["z"], operators = ["infer", "input"] } = optionsFirst(context);
2662
3062
  const operator = schemasTypeOperator(node, new Set(namespaces), new Set(operators));
2663
3063
  const parameter = node.typeArguments?.params[0];
2664
3064
  if (!operator || parameter?.type !== "TSTypeQuery" || parameter.exprName.type !== "Identifier" || !imported.has(parameter.exprName.name)) {
@@ -2675,7 +3075,7 @@ var noRederiveSchema = defineRule23({
2675
3075
  });
2676
3076
 
2677
3077
  // src/rules/no-restricted-constructor.ts
2678
- import { defineRule as defineRule24 } from "@oxlint/plugins";
3078
+ import { defineRule as defineRule25 } from "@oxlint/plugins";
2679
3079
  function restrictedConstructorEntries(constructors) {
2680
3080
  const entries = new Map;
2681
3081
  for (const item of constructors) {
@@ -2691,35 +3091,37 @@ function restrictedConstructorEntries(constructors) {
2691
3091
  }
2692
3092
  return entries;
2693
3093
  }
2694
- var noRestrictedConstructor = defineRule24({
3094
+ var noRestrictedConstructor = defineRule25({
2695
3095
  meta: {
2696
3096
  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
- }
3097
+ schema: [
3098
+ {
3099
+ type: "object",
3100
+ additionalProperties: false,
3101
+ properties: {
3102
+ constructors: {
3103
+ type: "array",
3104
+ items: {
3105
+ anyOf: [
3106
+ { type: "string" },
3107
+ {
3108
+ type: "object",
3109
+ additionalProperties: false,
3110
+ properties: {
3111
+ name: { type: "string" },
3112
+ message: { type: "string" }
3113
+ },
3114
+ required: ["name"]
3115
+ }
3116
+ ]
3117
+ }
3118
+ },
3119
+ checkCalls: { type: "boolean", default: false },
3120
+ message: { type: "string" }
2717
3121
  },
2718
- checkCalls: { type: "boolean", default: false },
2719
- message: { type: "string" }
2720
- },
2721
- required: ["constructors"]
2722
- }],
3122
+ required: ["constructors"]
3123
+ }
3124
+ ],
2723
3125
  messages: {
2724
3126
  construct: "Do not construct '{{name}}'.{{suffix}}",
2725
3127
  call: "Do not call '{{name}}'.{{suffix}}"
@@ -2764,18 +3166,20 @@ var noRestrictedConstructor = defineRule24({
2764
3166
  });
2765
3167
 
2766
3168
  // src/rules/no-restricted-files.ts
2767
- import { defineRule as defineRule25 } from "@oxlint/plugins";
2768
- var noRestrictedFiles = defineRule25({
3169
+ import { defineRule as defineRule26 } from "@oxlint/plugins";
3170
+ var noRestrictedFiles = defineRule26({
2769
3171
  meta: {
2770
3172
  type: "problem",
2771
- schema: [{
2772
- type: "object",
2773
- additionalProperties: false,
2774
- properties: {
2775
- message: { type: "string" }
2776
- },
2777
- required: ["message"]
2778
- }],
3173
+ schema: [
3174
+ {
3175
+ type: "object",
3176
+ additionalProperties: false,
3177
+ properties: {
3178
+ message: { type: "string" }
3179
+ },
3180
+ required: ["message"]
3181
+ }
3182
+ ],
2779
3183
  messages: {
2780
3184
  restricted: "{{message}}"
2781
3185
  }
@@ -2795,7 +3199,7 @@ var noRestrictedFiles = defineRule25({
2795
3199
  });
2796
3200
 
2797
3201
  // src/rules/no-restricted-token.ts
2798
- import { defineRule as defineRule26 } from "@oxlint/plugins";
3202
+ import { defineRule as defineRule27 } from "@oxlint/plugins";
2799
3203
  function tokenIsTypeOnlyImport(node) {
2800
3204
  const parent = node.parent;
2801
3205
  if (parent?.type === "ImportSpecifier") {
@@ -2806,40 +3210,39 @@ function tokenIsTypeOnlyImport(node) {
2806
3210
  function memberMatches(node, restriction) {
2807
3211
  return restriction.startsWith("*.") ? astStaticMemberName(node) === restriction.slice(2) : astDottedName(node) === restriction;
2808
3212
  }
2809
- var noRestrictedToken = defineRule26({
3213
+ var noRestrictedToken = defineRule27({
2810
3214
  meta: {
2811
3215
  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
- ]
3216
+ schema: [
3217
+ {
3218
+ type: "object",
3219
+ additionalProperties: false,
3220
+ properties: {
3221
+ token: { type: "string" },
3222
+ allowIn: { type: "array", items: { type: "string" } },
3223
+ allowPathPatterns: { type: "array", items: { type: "string" } },
3224
+ restrictions: {
3225
+ type: "array",
3226
+ items: {
3227
+ type: "object",
3228
+ additionalProperties: false,
3229
+ properties: {
3230
+ token: { type: "string" },
3231
+ member: { type: "string" },
3232
+ allowIn: { type: "array", items: { type: "string" } },
3233
+ allowPathPatterns: { type: "array", items: { type: "string" } },
3234
+ message: { type: "string" }
3235
+ },
3236
+ oneOf: [
3237
+ { required: ["token"], not: { required: ["member"] } },
3238
+ { required: ["member"], not: { required: ["token"] } }
3239
+ ]
3240
+ }
2835
3241
  }
2836
- }
2837
- },
2838
- anyOf: [
2839
- { required: ["token"] },
2840
- { required: ["restrictions"] }
2841
- ]
2842
- }],
3242
+ },
3243
+ anyOf: [{ required: ["token"] }, { required: ["restrictions"] }]
3244
+ }
3245
+ ],
2843
3246
  messages: {
2844
3247
  restricted: "'{{token}}' may only appear in configured owner paths.",
2845
3248
  restrictedWithMessage: "'{{token}}' may only appear in configured owner paths. {{custom}}"
@@ -2858,11 +3261,13 @@ var noRestrictedToken = defineRule26({
2858
3261
  before() {
2859
3262
  const options = optionsFirst(context);
2860
3263
  const configured = [
2861
- ...options.token ? [{
2862
- token: options.token,
2863
- allowIn: options.allowIn,
2864
- allowPathPatterns: options.allowPathPatterns
2865
- }] : [],
3264
+ ...options.token ? [
3265
+ {
3266
+ token: options.token,
3267
+ allowIn: options.allowIn,
3268
+ allowPathPatterns: options.allowPathPatterns
3269
+ }
3270
+ ] : [],
2866
3271
  ...options.restrictions ?? []
2867
3272
  ];
2868
3273
  const filename = namingPosixPath(context.filename);
@@ -2902,7 +3307,7 @@ var noRestrictedToken = defineRule26({
2902
3307
  });
2903
3308
 
2904
3309
  // src/rules/no-runtime-in-types.ts
2905
- import { defineRule as defineRule27 } from "@oxlint/plugins";
3310
+ import { defineRule as defineRule28 } from "@oxlint/plugins";
2906
3311
  function isTypeOnlyImport(statement) {
2907
3312
  return statement.importKind === "type" || statement.specifiers.length > 0 && statement.specifiers.every((specifier) => specifier.type === "ImportSpecifier" && specifier.importKind === "type");
2908
3313
  }
@@ -3119,22 +3524,24 @@ function isRuntimeReExport(statement, runtimeImportLocals, valueNames, isAmbient
3119
3524
  }
3120
3525
  return statement.specifiers.some((specifier) => specifier.exportKind !== "type" && runtimeImportLocals.has(exportLocalName(specifier)));
3121
3526
  }
3122
- var noRuntimeInTypes = defineRule27({
3527
+ var noRuntimeInTypes = defineRule28({
3123
3528
  meta: {
3124
3529
  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" }
3530
+ schema: [
3531
+ {
3532
+ type: "object",
3533
+ additionalProperties: false,
3534
+ properties: {
3535
+ allow: {
3536
+ type: "array",
3537
+ items: { type: "string", enum: ["class", "default", "enum", "function", "variable"] }
3538
+ },
3539
+ runtimeImports: { type: "string", enum: ["allow", "ban"] },
3540
+ allowImportSources: { type: "array", items: { type: "string" } },
3541
+ banReExports: { type: "boolean" }
3542
+ }
3136
3543
  }
3137
- }],
3544
+ ],
3138
3545
  messages: {
3139
3546
  runtimeValue: "Types files must not contain runtime value {{name}}.",
3140
3547
  runtimeImport: "Types files must not contain runtime imports from {{source}}.",
@@ -3261,7 +3668,7 @@ var noRuntimeInTypes = defineRule27({
3261
3668
  });
3262
3669
 
3263
3670
  // src/rules/no-single-use-scalar-schema.ts
3264
- import { defineRule as defineRule28 } from "@oxlint/plugins";
3671
+ import { defineRule as defineRule29 } from "@oxlint/plugins";
3265
3672
  function isScalarSchema(node, namespaces, structuralMethods) {
3266
3673
  if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
3267
3674
  return false;
@@ -3295,25 +3702,42 @@ function isAllowedStructuralElement(identifier, initializer, ancestors, options,
3295
3702
  const method = call.callee.type === "MemberExpression" ? astStaticMemberName(call.callee) : undefined;
3296
3703
  return Boolean(method && options.methods.includes(method) && schemasElements(call).includes(identifier));
3297
3704
  }
3298
- var noSingleUseScalarSchema = defineRule28({
3705
+ var noSingleUseScalarSchema = defineRule29({
3299
3706
  meta: {
3300
3707
  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" }
3309
- }
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
- }],
3708
+ schema: [
3709
+ {
3710
+ type: "object",
3711
+ additionalProperties: false,
3712
+ properties: {
3713
+ namespaces: { type: "array", items: { type: "string" } },
3714
+ methods: { type: "array", items: { type: "string" } },
3715
+ structuralMethods: { type: "array", items: { type: "string" } },
3716
+ allowZodScalars: { type: "boolean" }
3717
+ }
3718
+ }
3719
+ ],
3720
+ defaultOptions: [
3721
+ {
3722
+ namespaces: ["z"],
3723
+ methods: ["array", "union", "record", "tuple"],
3724
+ structuralMethods: [
3725
+ "and",
3726
+ "array",
3727
+ "discriminatedUnion",
3728
+ "intersection",
3729
+ "lazy",
3730
+ "object",
3731
+ "or",
3732
+ "pipe",
3733
+ "record",
3734
+ "transform",
3735
+ "tuple",
3736
+ "union"
3737
+ ],
3738
+ allowZodScalars: true
3739
+ }
3740
+ ],
3317
3741
  messages: { inline: "Inline single-use scalar schema {{name}} at its only structural use." }
3318
3742
  },
3319
3743
  create(context) {
@@ -3363,18 +3787,20 @@ var noSingleUseScalarSchema = defineRule28({
3363
3787
  });
3364
3788
 
3365
3789
  // src/rules/no-top-level-functions.ts
3366
- import { defineRule as defineRule29 } from "@oxlint/plugins";
3367
- var noTopLevelFunctions = defineRule29({
3790
+ import { defineRule as defineRule30 } from "@oxlint/plugins";
3791
+ var noTopLevelFunctions = defineRule30({
3368
3792
  meta: {
3369
3793
  type: "problem",
3370
- schema: [{
3371
- type: "object",
3372
- additionalProperties: false,
3373
- properties: {
3374
- banReExports: { type: "boolean" },
3375
- allowPattern: optionsPatternSchema
3376
- }
3377
- }],
3794
+ schema: [
3795
+ {
3796
+ type: "object",
3797
+ additionalProperties: false,
3798
+ properties: {
3799
+ banReExports: { type: "boolean" },
3800
+ allowPattern: optionsPatternSchema
3801
+ }
3802
+ }
3803
+ ],
3378
3804
  defaultOptions: [{ banReExports: true }],
3379
3805
  messages: {
3380
3806
  noFunction: "Files in this scope must not contain top-level function {{name}}.",
@@ -3421,74 +3847,127 @@ var noTopLevelFunctions = defineRule29({
3421
3847
  });
3422
3848
 
3423
3849
  // src/rules/no-trivial-functions.ts
3424
- 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
- }
3451
- var noTrivialFunctions = defineRule30({
3850
+ import { defineRule as defineRule31 } from "@oxlint/plugins";
3851
+
3852
+ // src/constants/trivial.constants.ts
3853
+ var trivialBannedNames = [
3854
+ "isRecord",
3855
+ "isPlainObject",
3856
+ "isObject",
3857
+ "isString",
3858
+ "isNumber",
3859
+ "isBoolean",
3860
+ "isArray",
3861
+ "asRecord",
3862
+ "asArray"
3863
+ ];
3864
+
3865
+ // src/rules/no-trivial-functions.ts
3866
+ var noTrivialFunctions = defineRule31({
3452
3867
  meta: {
3453
3868
  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" }
3461
- }
3462
- }],
3869
+ schema: [
3870
+ {
3871
+ type: "object",
3872
+ additionalProperties: false,
3873
+ properties: {
3874
+ allowPattern: optionsPatternSchema,
3875
+ allowCallees: { type: "array", items: { type: "string" } },
3876
+ allowAsync: { type: "boolean" },
3877
+ checkServiceMethods: { type: "boolean" },
3878
+ mode: { type: "string", enum: ["legacy", "precise"] },
3879
+ bannedNames: { type: "array", items: { type: "string" }, uniqueItems: true },
3880
+ checkGenericGuards: { type: "boolean" }
3881
+ }
3882
+ }
3883
+ ],
3463
3884
  messages: {
3464
- trivial: "Function {{name}} does nothing useful; inline it at the call site."
3885
+ trivial: "Function {{name}} adds no transformation; inline it or use a value for a constant result. Keep deliberate adapters through an explicit rule exclusion.",
3886
+ 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.",
3887
+ 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
3888
  }
3466
3889
  },
3467
3890
  createOnce(context) {
3468
3891
  let allowed = [];
3469
3892
  let allowedCallees = [];
3470
3893
  let allowAsync = false;
3894
+ let mode = "legacy";
3895
+ let bannedNames = new Set;
3896
+ let checkGenericGuards = false;
3897
+ let checkServiceMethods = false;
3898
+ const reported = new Set;
3899
+ const checked = new Set;
3900
+ function globalArray(node) {
3901
+ let scope = context.sourceCode.getScope(node);
3902
+ while (scope) {
3903
+ const variable = scope.set.get("Array");
3904
+ if (variable)
3905
+ return variable.defs.length === 0;
3906
+ scope = scope.upper;
3907
+ }
3908
+ return true;
3909
+ }
3910
+ function checkHelper(node) {
3911
+ if (bannedNames.size === 0 && !checkGenericGuards || checked.has(node.start))
3912
+ return;
3913
+ checked.add(node.start);
3914
+ const parent = node.parent;
3915
+ 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>";
3916
+ if (optionsPatternsTest(allowed, name))
3917
+ return;
3918
+ if (bannedNames.has(name)) {
3919
+ reported.add(node.start);
3920
+ context.report({ node, messageId: "bannedName", data: { name } });
3921
+ return;
3922
+ }
3923
+ if (checkGenericGuards && guardsIsGenericHelper(node, globalArray)) {
3924
+ reported.add(node.start);
3925
+ context.report({ node, messageId: "genericGuard", data: { name } });
3926
+ }
3927
+ }
3471
3928
  return {
3472
3929
  before() {
3473
- const { allowPattern, allowCallees: calleePatterns = [], allowAsync: asyncAllowed = false } = optionsFirst(context, {});
3930
+ const options = optionsFirst(context, {});
3931
+ const { allowPattern, allowCallees: calleePatterns = [], allowAsync: asyncAllowed = false } = options;
3932
+ mode = options.mode ?? "legacy";
3933
+ checkServiceMethods = options.checkServiceMethods ?? false;
3934
+ bannedNames = new Set(options.bannedNames ?? (mode === "precise" ? trivialBannedNames : []));
3935
+ checkGenericGuards = options.checkGenericGuards ?? mode === "precise";
3936
+ reported.clear();
3937
+ checked.clear();
3474
3938
  allowed = optionsOptionalPatterns(allowPattern);
3475
3939
  allowedCallees = calleePatterns.map((pattern) => new RegExp(pattern));
3476
3940
  allowAsync = asyncAllowed;
3477
3941
  },
3942
+ FunctionDeclaration: checkHelper,
3943
+ FunctionExpression: checkHelper,
3944
+ ArrowFunctionExpression: checkHelper,
3478
3945
  Program(program) {
3479
3946
  const seen = new Set;
3480
- for (const item of [...exportsCollectFunctions(program), ...declarationsTopLevelUnexportedFunctions(program)]) {
3947
+ for (const item of [
3948
+ ...exportsCollectFunctions(program),
3949
+ ...declarationsTopLevelUnexportedFunctions(program),
3950
+ ...checkServiceMethods ? declarationsPublicServiceMethods(program) : []
3951
+ ]) {
3481
3952
  const key = `${item.name}:${item.node.start}:${item.node.end}`;
3482
- if (seen.has(key) || optionsPatternsTest(allowed, item.name) || !declarationsIsTrivialFunction(item.node)) {
3953
+ if (seen.has(key) || optionsPatternsTest(allowed, item.name)) {
3483
3954
  continue;
3484
3955
  }
3485
- const expression = forwardedExpression(item.node);
3486
- const call = expression && forwardedCall(expression);
3956
+ seen.add(key);
3957
+ checkHelper(item.node);
3958
+ if (reported.has(item.node.start))
3959
+ continue;
3960
+ const expression = trivialExpression(item.node);
3961
+ const trivial = mode === "legacy" ? declarationsIsTrivialFunction(item.node) : expression ? trivialIsForwarder(item.node, expression) : declarationsIsTrivialFunction(item.node);
3962
+ if (!trivial)
3963
+ continue;
3964
+ const value = expression && trivialUnwrap(expression);
3965
+ const call = value?.type === "CallExpression" ? value : null;
3487
3966
  const path = call && astDottedName(call.callee);
3488
3967
  if (allowAsync && item.node.async && call || path && allowedCallees.some((pattern) => pattern.test(path))) {
3489
3968
  continue;
3490
3969
  }
3491
- seen.add(key);
3970
+ reported.add(item.node.start);
3492
3971
  context.report({
3493
3972
  node: item.node,
3494
3973
  messageId: "trivial",
@@ -3501,17 +3980,19 @@ var noTrivialFunctions = defineRule30({
3501
3980
  });
3502
3981
 
3503
3982
  // src/rules/no-type-declarations.ts
3504
- import { defineRule as defineRule31 } from "@oxlint/plugins";
3505
- var noTypeDeclarations = defineRule31({
3983
+ import { defineRule as defineRule32 } from "@oxlint/plugins";
3984
+ var noTypeDeclarations = defineRule32({
3506
3985
  meta: {
3507
3986
  type: "problem",
3508
- schema: [{
3509
- type: "object",
3510
- additionalProperties: false,
3511
- properties: {
3512
- allowPattern: optionsPatternSchema
3987
+ schema: [
3988
+ {
3989
+ type: "object",
3990
+ additionalProperties: false,
3991
+ properties: {
3992
+ allowPattern: optionsPatternSchema
3993
+ }
3513
3994
  }
3514
- }],
3995
+ ],
3515
3996
  messages: {
3516
3997
  typeDeclaration: "Type '{{name}}' must not be declared in this file."
3517
3998
  }
@@ -3545,20 +4026,23 @@ var noTypeDeclarations = defineRule31({
3545
4026
  });
3546
4027
 
3547
4028
  // src/rules/no-unescaped-like.ts
3548
- import { defineRule as defineRule32 } from "@oxlint/plugins";
3549
- var noUnescapedLike = defineRule32({
4029
+ import { defineRule as defineRule33 } from "@oxlint/plugins";
4030
+ var noUnescapedLike = defineRule33({
3550
4031
  meta: {
3551
4032
  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
- }],
4033
+ schema: [
4034
+ {
4035
+ type: "object",
4036
+ additionalProperties: false,
4037
+ properties: {
4038
+ methods: { type: "array", items: { type: "string" } },
4039
+ sanitizers: { type: "array", items: { type: "string" } },
4040
+ allowSanitizedBindings: { type: "boolean" },
4041
+ operatorMethods: { type: "array", items: { type: "string" } }
4042
+ },
4043
+ required: ["methods", "sanitizers"]
4044
+ }
4045
+ ],
3562
4046
  messages: { unescaped: ".{{method}}() values must use a configured sanitizer." }
3563
4047
  },
3564
4048
  createOnce(context) {
@@ -3572,9 +4056,11 @@ var noUnescapedLike = defineRule32({
3572
4056
  if (node.callee.type !== "MemberExpression") {
3573
4057
  return;
3574
4058
  }
3575
- const { methods, sanitizers } = optionsFirst(context);
4059
+ const { methods, sanitizers, operatorMethods = [] } = optionsFirst(context);
3576
4060
  const method = astStaticMemberName(node.callee);
3577
- if (!method || !methods.includes(method)) {
4061
+ const operator = node.arguments[1];
4062
+ const operatorCall = method && operatorMethods.includes(method) && operator?.type === "Literal" && typeof operator.value === "string" && methods.includes(operator.value);
4063
+ if (!method || !methods.includes(method) && !operatorCall) {
3578
4064
  return;
3579
4065
  }
3580
4066
  const value = node.arguments.at(-1);
@@ -3592,7 +4078,7 @@ var noUnescapedLike = defineRule32({
3592
4078
  });
3593
4079
 
3594
4080
  // src/rules/object-multiline.ts
3595
- import { defineRule as defineRule33 } from "@oxlint/plugins";
4081
+ import { defineRule as defineRule34 } from "@oxlint/plugins";
3596
4082
  function objectMultilineIsCallArg(node) {
3597
4083
  const parent = node.parent;
3598
4084
  return (parent.type === "CallExpression" || parent.type === "NewExpression") && parent.arguments.includes(node);
@@ -3629,19 +4115,21 @@ function objectMultilineGaps(text, node) {
3629
4115
  }
3630
4116
  return gaps;
3631
4117
  }
3632
- var objectMultiline = defineRule33({
4118
+ var objectMultiline = defineRule34({
3633
4119
  meta: {
3634
4120
  type: "layout",
3635
4121
  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 }
3643
- }
3644
- }],
4122
+ schema: [
4123
+ {
4124
+ type: "object",
4125
+ additionalProperties: false,
4126
+ properties: {
4127
+ minProperties: { type: "integer", minimum: 1, default: 3 },
4128
+ scope: { type: "string", enum: ["call-args", "all"], default: "call-args" },
4129
+ indent: { ...layoutIndentSchema, default: 2 }
4130
+ }
4131
+ }
4132
+ ],
3645
4133
  messages: {
3646
4134
  callArgs: "Object with {{min}}+ properties in a call should have each property on its own line.",
3647
4135
  always: "Object with {{min}}+ properties should have each property on its own line."
@@ -3689,19 +4177,21 @@ var objectMultiline = defineRule33({
3689
4177
  });
3690
4178
 
3691
4179
  // src/rules/only-export-components.ts
3692
- import { defineRule as defineRule34 } from "@oxlint/plugins";
3693
- var onlyExportComponents = defineRule34({
4180
+ import { defineRule as defineRule35 } from "@oxlint/plugins";
4181
+ var onlyExportComponents = defineRule35({
3694
4182
  meta: {
3695
4183
  type: "problem",
3696
- schema: [{
3697
- type: "object",
3698
- additionalProperties: false,
3699
- properties: {
3700
- matchFileName: { type: "boolean" },
3701
- allowTypeExports: { type: "boolean" },
3702
- denyTypePattern: optionsPatternSchema
3703
- }
3704
- }],
4184
+ schema: [
4185
+ {
4186
+ type: "object",
4187
+ additionalProperties: false,
4188
+ properties: {
4189
+ matchFileName: { type: "boolean" },
4190
+ allowTypeExports: { type: "boolean" },
4191
+ denyTypePattern: optionsPatternSchema
4192
+ }
4193
+ }
4194
+ ],
3705
4195
  messages: {
3706
4196
  nonComponent: "Export '{{name}}' must be a React component or type.",
3707
4197
  nameMismatch: "Component export '{{name}}' must match '{{expected}}' or its prefix.",
@@ -3768,7 +4258,7 @@ var onlyExportComponents = defineRule34({
3768
4258
  });
3769
4259
 
3770
4260
  // src/rules/only-export-constants.ts
3771
- import { defineRule as defineRule35 } from "@oxlint/plugins";
4261
+ import { defineRule as defineRule36 } from "@oxlint/plugins";
3772
4262
  function bindingNames(pattern) {
3773
4263
  switch (pattern.type) {
3774
4264
  case "Identifier":
@@ -3823,18 +4313,20 @@ function collectBindings(program) {
3823
4313
  }
3824
4314
  return bindings;
3825
4315
  }
3826
- var onlyExportConstants = defineRule35({
4316
+ var onlyExportConstants = defineRule36({
3827
4317
  meta: {
3828
4318
  type: "problem",
3829
- schema: [{
3830
- type: "object",
3831
- additionalProperties: false,
3832
- properties: {
3833
- allowFunctionValues: { type: "boolean" },
3834
- allowTypeExports: { type: "boolean" },
3835
- allowReExports: { type: "boolean" }
3836
- }
3837
- }],
4319
+ schema: [
4320
+ {
4321
+ type: "object",
4322
+ additionalProperties: false,
4323
+ properties: {
4324
+ allowFunctionValues: { type: "boolean" },
4325
+ allowTypeExports: { type: "boolean" },
4326
+ allowReExports: { type: "boolean" }
4327
+ }
4328
+ }
4329
+ ],
3838
4330
  messages: {
3839
4331
  nonConstant: "Export '{{name}}' must be a local const binding with an allowed value; types and re-exports require explicit options."
3840
4332
  }
@@ -3842,7 +4334,11 @@ var onlyExportConstants = defineRule35({
3842
4334
  createOnce(context) {
3843
4335
  return {
3844
4336
  Program(program) {
3845
- const { allowFunctionValues = false, allowTypeExports = false, allowReExports = false } = optionsFirst(context, {});
4337
+ const {
4338
+ allowFunctionValues = false,
4339
+ allowTypeExports = false,
4340
+ allowReExports = false
4341
+ } = optionsFirst(context, {});
3846
4342
  const bindings = collectBindings(program);
3847
4343
  function hasForbiddenValue(binding, seen = new Set) {
3848
4344
  if (seen.has(binding) || binding.kind === "class") {
@@ -3905,7 +4401,7 @@ var onlyExportConstants = defineRule35({
3905
4401
  });
3906
4402
 
3907
4403
  // src/rules/padding-between-statements.ts
3908
- import { defineRule as defineRule36 } from "@oxlint/plugins";
4404
+ import { defineRule as defineRule37 } from "@oxlint/plugins";
3909
4405
  function paddingKind(node) {
3910
4406
  if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
3911
4407
  return node.declaration ? paddingKind(node.declaration) : "other";
@@ -3957,17 +4453,20 @@ function paddingBlankLineFix(sourceText, comments, prev, curr, newline) {
3957
4453
  }
3958
4454
  return { newlineEnd, newline };
3959
4455
  }
3960
- var paddingBetweenStatements = defineRule36({
4456
+ var paddingBetweenStatements = defineRule37({
3961
4457
  meta: {
3962
4458
  type: "layout",
3963
4459
  fixable: "whitespace",
3964
- schema: [{
3965
- type: "object",
3966
- additionalProperties: false,
3967
- properties: {
3968
- returnMinStatements: { type: "integer", minimum: 1, default: 3 }
4460
+ schema: [
4461
+ {
4462
+ type: "object",
4463
+ additionalProperties: false,
4464
+ properties: {
4465
+ returnMinStatements: { type: "integer", minimum: 1, default: 3 },
4466
+ multilineVariables: { type: "boolean" }
4467
+ }
3969
4468
  }
3970
- }],
4469
+ ],
3971
4470
  messages: {
3972
4471
  variableAndBlock: "Expected a blank line between a variable declaration and a function/class.",
3973
4472
  beforeBlock: "Expected a blank line before a function/class declaration.",
@@ -3975,11 +4474,13 @@ var paddingBetweenStatements = defineRule36({
3975
4474
  variableAndControl: "Expected a blank line between a variable declaration and control flow.",
3976
4475
  controlAndVariable: "Expected a blank line between control flow and a variable declaration.",
3977
4476
  consecutiveControl: "Expected a blank line between control flow statements.",
3978
- beforeReturn: "Expected a blank line before return statement."
4477
+ beforeReturn: "Expected a blank line before return statement.",
4478
+ multilineVariable: "Expected a blank line around a multiline variable declaration."
3979
4479
  }
3980
4480
  },
3981
4481
  createOnce(context) {
3982
4482
  let returnMinStatements = 3;
4483
+ let multilineVariables = false;
3983
4484
  let newline = `
3984
4485
  `;
3985
4486
  let source;
@@ -3993,7 +4494,8 @@ var paddingBetweenStatements = defineRule36({
3993
4494
  }
3994
4495
  const prevKind = paddingKind(prev);
3995
4496
  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;
4497
+ const multiline = multilineVariables && (prevKind === "variable" && prev.loc.start.line !== prev.loc.end.line || currKind === "variable" && curr.loc.start.line !== curr.loc.end.line);
4498
+ 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
4499
  if (!messageId) {
3998
4500
  continue;
3999
4501
  }
@@ -4010,6 +4512,7 @@ var paddingBetweenStatements = defineRule36({
4010
4512
  before() {
4011
4513
  const options = optionsFirst(context, {});
4012
4514
  returnMinStatements = options.returnMinStatements ?? 3;
4515
+ multilineVariables = options.multilineVariables ?? false;
4013
4516
  source = context.sourceCode;
4014
4517
  newline = layoutNewline(source.text);
4015
4518
  },
@@ -4024,7 +4527,7 @@ var paddingBetweenStatements = defineRule36({
4024
4527
  });
4025
4528
 
4026
4529
  // src/rules/prefer-namespace-type-import.ts
4027
- import { defineRule as defineRule37 } from "@oxlint/plugins";
4530
+ import { defineRule as defineRule38 } from "@oxlint/plugins";
4028
4531
  var bindingName = /^[A-Za-z_$][\w$]*$/;
4029
4532
  var reservedNames = new Set("await break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield implements interface let package private protected public static arguments eval".split(" "));
4030
4533
  function namespaceName(source, names) {
@@ -4048,21 +4551,23 @@ function safeReference(node) {
4048
4551
  }
4049
4552
  return parent?.type === "TSTypeReference" && parent.typeName === current || parent?.type === "TSTypeQuery" && parent.exprName === current || (parent?.type === "TSInterfaceHeritage" || parent?.type === "TSClassImplements") && parent.expression === current;
4050
4553
  }
4051
- var preferNamespaceTypeImport = defineRule37({
4554
+ var preferNamespaceTypeImport = defineRule38({
4052
4555
  meta: {
4053
4556
  type: "suggestion",
4054
4557
  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 } }
4061
- }
4062
- }],
4558
+ schema: [
4559
+ {
4560
+ type: "object",
4561
+ additionalProperties: false,
4562
+ properties: {
4563
+ max: { type: "integer", minimum: 0 },
4564
+ names: { type: "object", additionalProperties: { type: "string", pattern: bindingName.source } }
4565
+ }
4566
+ }
4567
+ ],
4063
4568
  defaultOptions: [{ max: 3, names: {} }],
4064
4569
  messages: {
4065
- namespace: "Use a type namespace '{{name}}' for {{count}} named imports (maximum {{max}}).{{reason}}"
4570
+ namespace: "Use `import type * as {{name}} from '{{source}}'` for {{count}} type imports (max {{max}}); keep exported domain names.{{reason}}"
4066
4571
  }
4067
4572
  },
4068
4573
  createOnce(context) {
@@ -4147,7 +4652,13 @@ var preferNamespaceTypeImport = defineRule37({
4147
4652
  context.report({
4148
4653
  node,
4149
4654
  messageId: "namespace",
4150
- data: { name, count: node.specifiers.length, max, reason: reason ? ` Autofix skipped: ${reason}.` : "" },
4655
+ data: {
4656
+ name,
4657
+ source: node.source.value,
4658
+ count: node.specifiers.length,
4659
+ max,
4660
+ reason: reason ? ` Autofix skipped: ${reason}.` : ""
4661
+ },
4151
4662
  fix: reason ? undefined : (fixer) => edits.map((edit) => fixer.replaceTextRange(edit.range, edit.text))
4152
4663
  });
4153
4664
  }
@@ -4157,19 +4668,21 @@ var preferNamespaceTypeImport = defineRule37({
4157
4668
  });
4158
4669
 
4159
4670
  // src/rules/require-file-factory.ts
4160
- import { defineRule as defineRule38 } from "@oxlint/plugins";
4161
- var requireFileFactory = defineRule38({
4671
+ import { defineRule as defineRule39 } from "@oxlint/plugins";
4672
+ var requireFileFactory = defineRule39({
4162
4673
  meta: {
4163
4674
  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
- }],
4675
+ schema: [
4676
+ {
4677
+ type: "object",
4678
+ additionalProperties: false,
4679
+ properties: {
4680
+ factory: { type: "string" },
4681
+ allDeclarations: { type: "boolean" }
4682
+ },
4683
+ required: ["factory"]
4684
+ }
4685
+ ],
4173
4686
  messages: {
4174
4687
  factory: "Function '{{actual}}' must be named '{{expected}}'."
4175
4688
  }
@@ -4204,19 +4717,22 @@ var requireFileFactory = defineRule38({
4204
4717
  });
4205
4718
 
4206
4719
  // src/rules/require-object-params.ts
4207
- import { defineRule as defineRule39 } from "@oxlint/plugins";
4208
- var requireObjectParams = defineRule39({
4720
+ import { defineRule as defineRule40 } from "@oxlint/plugins";
4721
+ var requireObjectParams = defineRule40({
4209
4722
  meta: {
4210
4723
  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
4218
- }
4219
- }],
4724
+ schema: [
4725
+ {
4726
+ type: "object",
4727
+ additionalProperties: false,
4728
+ properties: {
4729
+ maxParams: { type: "integer", minimum: 1 },
4730
+ serviceMethods: { type: "boolean" },
4731
+ allDeclarations: { type: "boolean" },
4732
+ allowPattern: optionsPatternSchema
4733
+ }
4734
+ }
4735
+ ],
4220
4736
  messages: {
4221
4737
  objectParams: "{{label}} {{name}} must accept at most {{maxParams}} object parameter{{s}}."
4222
4738
  }
@@ -4224,10 +4740,17 @@ var requireObjectParams = defineRule39({
4224
4740
  createOnce(context) {
4225
4741
  return {
4226
4742
  Program(program) {
4227
- const { maxParams = 1, allDeclarations = false, allowPattern } = optionsFirst(context, {});
4743
+ const {
4744
+ maxParams = 1,
4745
+ allDeclarations = false,
4746
+ serviceMethods = false,
4747
+ allowPattern
4748
+ } = optionsFirst(context, {});
4228
4749
  const allowed = optionsOptionalPatterns(allowPattern);
4229
4750
  const seen = new Set;
4230
4751
  const functions = allDeclarations ? [...exportsCollectFunctions(program), ...declarationsTopLevelUnexportedFunctions(program)] : exportsCollectFunctions(program);
4752
+ if (serviceMethods)
4753
+ functions.push(...declarationsPublicServiceMethods(program));
4231
4754
  for (const item of functions) {
4232
4755
  const key = `${item.name}:${item.node.start}:${item.node.end}`;
4233
4756
  if (seen.has(key) || optionsPatternsTest(allowed, item.name)) {
@@ -4253,20 +4776,22 @@ var requireObjectParams = defineRule39({
4253
4776
  });
4254
4777
 
4255
4778
  // src/rules/require-orpc-output.ts
4256
- import { defineRule as defineRule40 } from "@oxlint/plugins";
4257
- var requireOrpcOutput = defineRule40({
4779
+ import { defineRule as defineRule41 } from "@oxlint/plugins";
4780
+ var requireOrpcOutput = defineRule41({
4258
4781
  meta: {
4259
4782
  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
- }],
4783
+ schema: [
4784
+ {
4785
+ type: "object",
4786
+ additionalProperties: false,
4787
+ properties: {
4788
+ composers: { type: "array", items: { type: "string" } },
4789
+ handlerMethod: { type: "string" },
4790
+ outputMethod: { type: "string" }
4791
+ },
4792
+ required: ["composers"]
4793
+ }
4794
+ ],
4270
4795
  messages: {
4271
4796
  missing: "oRPC procedures must declare .output() before .handler().",
4272
4797
  invalid: "oRPC .output() must use a named schema or configured composer with a named schema."
@@ -4278,7 +4803,11 @@ var requireOrpcOutput = defineRule40({
4278
4803
  if (node.callee.type !== "MemberExpression") {
4279
4804
  return;
4280
4805
  }
4281
- const { composers, handlerMethod = "handler", outputMethod = "output" } = optionsFirst(context);
4806
+ const {
4807
+ composers,
4808
+ handlerMethod = "handler",
4809
+ outputMethod = "output"
4810
+ } = optionsFirst(context);
4282
4811
  if (astStaticMemberName(node.callee) !== handlerMethod) {
4283
4812
  return;
4284
4813
  }
@@ -4296,34 +4825,33 @@ var requireOrpcOutput = defineRule40({
4296
4825
  });
4297
4826
 
4298
4827
  // src/rules/require-paired-call.ts
4299
- import { defineRule as defineRule41 } from "@oxlint/plugins";
4300
- var requirePairedCall = defineRule41({
4828
+ import { defineRule as defineRule42 } from "@oxlint/plugins";
4829
+ var requirePairedCall = defineRule42({
4301
4830
  meta: {
4302
4831
  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"]
4832
+ schema: [
4833
+ {
4834
+ type: "object",
4835
+ additionalProperties: false,
4836
+ properties: {
4837
+ when: { type: "string" },
4838
+ require: { type: "string" },
4839
+ pairs: {
4840
+ type: "array",
4841
+ items: {
4842
+ type: "object",
4843
+ additionalProperties: false,
4844
+ properties: {
4845
+ when: { type: "string" },
4846
+ require: { type: "string" }
4847
+ },
4848
+ required: ["when", "require"]
4849
+ }
4319
4850
  }
4320
- }
4321
- },
4322
- anyOf: [
4323
- { required: ["when", "require"] },
4324
- { required: ["pairs"] }
4325
- ]
4326
- }],
4851
+ },
4852
+ anyOf: [{ required: ["when", "require"] }, { required: ["pairs"] }]
4853
+ }
4854
+ ],
4327
4855
  messages: {
4328
4856
  paired: "{{when}} requires a call to {{require}} in the same file."
4329
4857
  }
@@ -4372,20 +4900,24 @@ var requirePairedCall = defineRule41({
4372
4900
  });
4373
4901
 
4374
4902
  // src/rules/route-surface.ts
4375
- import { defineRule as defineRule42 } from "@oxlint/plugins";
4376
- var routeSurface = defineRule42({
4903
+ import { defineRule as defineRule43 } from "@oxlint/plugins";
4904
+ var routeSurface = defineRule43({
4377
4905
  meta: {
4378
4906
  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" }
4386
- }
4387
- }],
4388
- defaultOptions: [{ exportName: "Route", bannedHooks: ["useState", "useEffect", "useMutation"], banIntrinsicJsx: true }],
4907
+ schema: [
4908
+ {
4909
+ type: "object",
4910
+ additionalProperties: false,
4911
+ properties: {
4912
+ exportName: { type: "string" },
4913
+ bannedHooks: { type: "array", items: { type: "string" } },
4914
+ banIntrinsicJsx: { type: "boolean" }
4915
+ }
4916
+ }
4917
+ ],
4918
+ defaultOptions: [
4919
+ { exportName: "Route", bannedHooks: ["useState", "useEffect", "useMutation"], banIntrinsicJsx: true }
4920
+ ],
4389
4921
  messages: {
4390
4922
  extraExport: "Route files may only export '{{expected}}'; found '{{actual}}'.",
4391
4923
  hostJsx: "Route adapter {{name}} must not render intrinsic JSX.",
@@ -4415,8 +4947,8 @@ var routeSurface = defineRule42({
4415
4947
  });
4416
4948
 
4417
4949
  // src/rules/test-title-pattern.ts
4418
- import { defineRule as defineRule43 } from "@oxlint/plugins";
4419
- var TEST_MODIFIERS = new Set(["only", "skip", "concurrent", "todo", "each"]);
4950
+ import { defineRule as defineRule44 } from "@oxlint/plugins";
4951
+ var TEST_MODIFIERS = new Set(["only", "skip", "concurrent", "sequential", "todo", "each", "for"]);
4420
4952
  function isConfiguredName(name, callees) {
4421
4953
  if (!name) {
4422
4954
  return false;
@@ -4455,23 +4987,27 @@ function regexMatches(regex, value) {
4455
4987
  function titlePatterns(pattern, flags) {
4456
4988
  return pattern === undefined ? [] : [pattern].flat().map((source) => ({ regex: new RegExp(source, flags), pattern: source }));
4457
4989
  }
4458
- var testTitlePattern = defineRule43({
4990
+ var testTitlePattern = defineRule44({
4459
4991
  meta: {
4460
4992
  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]*$" }
4469
- }
4470
- }],
4471
- defaultOptions: [{
4472
- callees: ["describe", "it", "test"],
4473
- flags: ""
4474
- }],
4993
+ schema: [
4994
+ {
4995
+ type: "object",
4996
+ additionalProperties: false,
4997
+ properties: {
4998
+ callees: { type: "array", items: { type: "string" } },
4999
+ forbid: optionsPatternSchema,
5000
+ require: optionsPatternSchema,
5001
+ flags: { type: "string", pattern: "^[dgimsuvy]*$" }
5002
+ }
5003
+ }
5004
+ ],
5005
+ defaultOptions: [
5006
+ {
5007
+ callees: ["describe", "it", "test"],
5008
+ flags: ""
5009
+ }
5010
+ ],
4475
5011
  messages: {
4476
5012
  forbidden: "Test title matches forbidden pattern '{{pattern}}'.",
4477
5013
  required: "Test title must match required pattern '{{pattern}}'."
@@ -4532,6 +5068,7 @@ var src_default = eslintCompatPlugin({
4532
5068
  rules: {
4533
5069
  "call-array-multiline": callArrayMultiline,
4534
5070
  "chain-newline": chainNewline,
5071
+ "component-props": componentProps,
4535
5072
  "declaration-name": declarationName,
4536
5073
  "export-file-prefix": exportFilePrefix,
4537
5074
  "export-name-pattern": exportNamePattern,