oxlint-plugin-arch 0.12.5 → 0.13.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
@@ -1,3 +1,6 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
1
4
  // src/index.ts
2
5
  import { eslintCompatPlugin } from "@oxlint/plugins";
3
6
 
@@ -34,7 +37,7 @@ function optionsPatternsTest(patterns, value) {
34
37
  function optionsGlob(pattern) {
35
38
  const negated = [];
36
39
  const source = pattern.replaceAll(/!\(([^()]*)\)/g, (_, options) => {
37
- negated.push(options.split("|").map((option) => option.replaceAll(/[.+^$()|[\]\\*?{}]/g, "\\$&")).join("|"));
40
+ negated.push(options.split("|").map((option) => option.replaceAll(/[.+^$()|[\]\\?{}]/g, "\\$&").replaceAll("*", "[^/]*")).join("|"));
38
41
  return `\x02${negated.length - 1}\x03`;
39
42
  }).replaceAll(/[.+^$()|[\]\\]/g, "\\$&").replaceAll("?", "[^/]").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*").replaceAll(/\{([^}]*)\}/g, (_, options) => `(?:${options.split(",").join("|")})`);
40
43
  return new RegExp(`^${negated.reduce((text, names, index) => text.replace(`\x02${index}\x03`, `(?!(?:${names})(?:/|$))[^/]*`), source)}$`);
@@ -280,16 +283,14 @@ var backendOrpcOutput = defineRule2({
280
283
  type: "object",
281
284
  additionalProperties: false,
282
285
  properties: {
283
- composers: { type: "array", items: { type: "string" } },
284
286
  handler: { type: "string" },
285
287
  output: { type: "string" }
286
- },
287
- required: ["composers"]
288
+ }
288
289
  }
289
290
  ],
290
291
  messages: {
291
292
  missing: "oRPC procedures must declare .output() before .handler().",
292
- invalid: "oRPC .output() must use a named schema or configured composer with a named schema."
293
+ invalid: "oRPC .output() must use a named schema or a plain function called with a named schema."
293
294
  }
294
295
  },
295
296
  createOnce(context) {
@@ -298,7 +299,7 @@ var backendOrpcOutput = defineRule2({
298
299
  if (node.callee.type !== "MemberExpression") {
299
300
  return;
300
301
  }
301
- const { composers, handler: handlerMethod = "handler", output: outputMethod = "output" } = optionsFirst(context);
302
+ const { handler: handlerMethod = "handler", output: outputMethod = "output" } = optionsFirst(context, {});
302
303
  if (astMemberName(node.callee) !== handlerMethod) {
303
304
  return;
304
305
  }
@@ -308,7 +309,7 @@ var backendOrpcOutput = defineRule2({
308
309
  return;
309
310
  }
310
311
  const schema = output.arguments.at(0);
311
- const named = schema?.type === "Identifier" || schema?.type === "CallExpression" && schema.callee.type === "Identifier" && composers.includes(schema.callee.name) && schema.arguments[0]?.type === "Identifier";
312
+ const named = schema?.type === "Identifier" || schema?.type === "CallExpression" && schema.callee.type === "Identifier" && schema.arguments[0]?.type === "Identifier";
312
313
  if (!named) {
313
314
  context.report({ node: output, messageId: "invalid" });
314
315
  }
@@ -968,6 +969,12 @@ function boundariesResolveTarget(options, file, source) {
968
969
  }
969
970
 
970
971
  // src/rules/backend/backend-sql-like.ts
972
+ var backendSqlLikeDefaults = {
973
+ methods: ["like", "ilike"],
974
+ sanitizers: ["escapeLikeWildcards"],
975
+ bindings: true,
976
+ operators: ["where"]
977
+ };
971
978
  var backendSqlLike = defineRule5({
972
979
  meta: {
973
980
  type: "problem",
@@ -980,17 +987,16 @@ var backendSqlLike = defineRule5({
980
987
  sanitizers: { type: "array", items: { type: "string" } },
981
988
  bindings: { type: "boolean" },
982
989
  operators: { type: "array", items: { type: "string" } }
983
- },
984
- required: ["methods", "sanitizers"]
990
+ }
985
991
  }
986
992
  ],
987
993
  messages: { unescaped: ".{{method}}() values must use a configured sanitizer." }
988
994
  },
989
995
  createOnce(context) {
990
- let options = { methods: [], sanitizers: [] };
996
+ let options = backendSqlLikeDefaults;
991
997
  const seen = new Set;
992
998
  function sanitizedBinding(identifier, name) {
993
- const variable = options.bindings === false ? null : boundariesDeclaredVariable(context.sourceCode.getScope(identifier), name);
999
+ const variable = options.bindings ? boundariesDeclaredVariable(context.sourceCode.getScope(identifier), name) : null;
994
1000
  const declarator = variable?.defs.length === 1 && variable.defs[0].type === "Variable" ? variable.defs[0].node : undefined;
995
1001
  const statement = declarator?.parent?.parent;
996
1002
  if (!variable || declarator?.type !== "VariableDeclarator" || seen.has(variable) || !(statement?.type === "Program" || statement?.type === "ExportNamedDeclaration" && statement.parent.type === "Program") || variable.references.some((reference) => reference.isWrite() && !reference.init)) {
@@ -1031,8 +1037,8 @@ var backendSqlLike = defineRule5({
1031
1037
  if (node.callee.type !== "MemberExpression") {
1032
1038
  return;
1033
1039
  }
1034
- options = optionsFirst(context);
1035
- const { methods, operators: operatorMethods = [] } = options;
1040
+ options = { ...backendSqlLikeDefaults, ...optionsFirst(context, {}) };
1041
+ const { methods, operators: operatorMethods } = options;
1036
1042
  const method = astMemberName(node.callee);
1037
1043
  if (!method) {
1038
1044
  return;
@@ -2013,12 +2019,82 @@ var codeConstructor = defineRule13({
2013
2019
  }
2014
2020
  });
2015
2021
 
2022
+ // src/rules/code/code-delegate.ts
2023
+ import { defineRule as defineRule14 } from "@oxlint/plugins";
2024
+ var codeDelegate = defineRule14({
2025
+ meta: {
2026
+ type: "suggestion",
2027
+ schema: [
2028
+ {
2029
+ type: "object",
2030
+ additionalProperties: false,
2031
+ properties: {
2032
+ methods: { type: "array", items: { type: "string" } },
2033
+ reads: { type: "array", items: { type: "string" } },
2034
+ max: { type: "integer", minimum: 0 }
2035
+ }
2036
+ }
2037
+ ],
2038
+ messages: {
2039
+ statement: "A handler only delegates: read services, make one call, return. Move this to an action.",
2040
+ calls: "A handler's yielded or awaited calls are limited to {{max}}; move the rest into the action it calls.",
2041
+ ternary: "A handler does not branch; move the condition into the action it calls.",
2042
+ nested: "A handler holds no function; move it into the action it calls."
2043
+ }
2044
+ },
2045
+ createOnce(context) {
2046
+ return {
2047
+ CallExpression(node) {
2048
+ const { methods = ["handler", "effect"], reads = ["Effect.serviceOption"], max = 1 } = optionsFirst(context, {});
2049
+ if (!methods.includes(astCallName(node) ?? "")) {
2050
+ return;
2051
+ }
2052
+ const awaited = (value) => value?.type === "YieldExpression" || value?.type === "AwaitExpression";
2053
+ for (const handler of node.arguments.filter(astIsInlineFunction)) {
2054
+ let calls = 0;
2055
+ const scan = (value) => astSome(value, (child) => {
2056
+ if (child.type === "ConditionalExpression") {
2057
+ context.report({ node: child, messageId: "ternary" });
2058
+ }
2059
+ if (child.type === "FunctionDeclaration" || astIsInlineFunction(child)) {
2060
+ context.report({ node: child, messageId: "nested" });
2061
+ }
2062
+ const target = awaited(child) && "argument" in child ? child.argument : null;
2063
+ if (target?.type === "CallExpression" && !reads.includes(astDottedName(target.callee) ?? "")) {
2064
+ calls += 1;
2065
+ if (calls > max) {
2066
+ context.report({
2067
+ node: child,
2068
+ messageId: "calls",
2069
+ data: { max: String(max) }
2070
+ });
2071
+ }
2072
+ }
2073
+ return false;
2074
+ }, (child) => child.type !== "FunctionDeclaration" && !astIsInlineFunction(child));
2075
+ const { body } = handler;
2076
+ if (body && body.type !== "BlockStatement") {
2077
+ scan(body);
2078
+ }
2079
+ for (const statement of body?.type === "BlockStatement" ? body.body : []) {
2080
+ const allowed = statement.type === "ReturnStatement" || statement.type === "VariableDeclaration" && statement.declarations.every((declarator) => awaited(declarator.init)) || statement.type === "ExpressionStatement" && awaited(statement.expression) && "argument" in statement.expression && statement.expression.argument?.type === "CallExpression";
2081
+ if (!allowed) {
2082
+ context.report({ node: statement, messageId: "statement" });
2083
+ }
2084
+ scan(statement);
2085
+ }
2086
+ }
2087
+ }
2088
+ };
2089
+ }
2090
+ });
2091
+
2016
2092
  // src/rules/code/code-double-negation.ts
2017
2093
  var codeDoubleNegation = astSelectorRule('UnaryExpression[operator="!"] > UnaryExpression[operator="!"]', "Use Boolean(value) or an explicit comparison.");
2018
2094
 
2019
2095
  // src/rules/code/code-function-nesting.ts
2020
- import { defineRule as defineRule14 } from "@oxlint/plugins";
2021
- var codeFunctionNesting = defineRule14({
2096
+ import { defineRule as defineRule15 } from "@oxlint/plugins";
2097
+ var codeFunctionNesting = defineRule15({
2022
2098
  meta: {
2023
2099
  type: "suggestion",
2024
2100
  schema: [
@@ -2059,7 +2135,7 @@ var codeFunctionNesting = defineRule14({
2059
2135
  });
2060
2136
 
2061
2137
  // src/rules/code/code-inline.ts
2062
- import { defineRule as defineRule15 } from "@oxlint/plugins";
2138
+ import { defineRule as defineRule16 } from "@oxlint/plugins";
2063
2139
 
2064
2140
  // src/utils/source.utils.ts
2065
2141
  var sourceDirective = /^\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])/;
@@ -2144,7 +2220,7 @@ var codeInlineEffects = new Set([
2144
2220
  function codeInlineEffectBetween(program, start, end) {
2145
2221
  return astSome(program, (node) => node.range[0] >= start && node.range[1] <= end && (codeInlineEffects.has(node.type) || node.type === "UnaryExpression" && node.operator === "delete"), (node) => node.range[1] > start && node.range[0] < end && !(node.range[0] >= start && (node.type === "FunctionDeclaration" || astIsInlineFunction(node))));
2146
2222
  }
2147
- var codeInline = defineRule15({
2223
+ var codeInline = defineRule16({
2148
2224
  meta: {
2149
2225
  type: "suggestion",
2150
2226
  schema: [
@@ -2257,8 +2333,8 @@ var codeInline = defineRule15({
2257
2333
  });
2258
2334
 
2259
2335
  // src/rules/code/code-justified-call.ts
2260
- import { defineRule as defineRule16 } from "@oxlint/plugins";
2261
- var codeJustifiedCall = defineRule16({
2336
+ import { defineRule as defineRule17 } from "@oxlint/plugins";
2337
+ var codeJustifiedCall = defineRule17({
2262
2338
  meta: {
2263
2339
  type: "problem",
2264
2340
  schema: [
@@ -2312,8 +2388,8 @@ var codeJustifiedCall = defineRule16({
2312
2388
  });
2313
2389
 
2314
2390
  // src/rules/code/code-literal-in.ts
2315
- import { defineRule as defineRule17 } from "@oxlint/plugins";
2316
- var codeLiteralIn = defineRule17({
2391
+ import { defineRule as defineRule18 } from "@oxlint/plugins";
2392
+ var codeLiteralIn = defineRule18({
2317
2393
  meta: {
2318
2394
  type: "suggestion",
2319
2395
  schema: [
@@ -2354,7 +2430,7 @@ var codeLiteralIn = defineRule17({
2354
2430
  });
2355
2431
 
2356
2432
  // src/rules/code/code-mutable-state.ts
2357
- import { defineRule as defineRule18 } from "@oxlint/plugins";
2433
+ import { defineRule as defineRule19 } from "@oxlint/plugins";
2358
2434
  var functionScopes = new Set([
2359
2435
  "FunctionDeclaration",
2360
2436
  "FunctionExpression",
@@ -2371,7 +2447,7 @@ function hasAncestor(node, test) {
2371
2447
  }
2372
2448
  return false;
2373
2449
  }
2374
- var codeMutableState = defineRule18({
2450
+ var codeMutableState = defineRule19({
2375
2451
  meta: {
2376
2452
  type: "problem",
2377
2453
  schema: [
@@ -2429,7 +2505,7 @@ var codeMutableState = defineRule18({
2429
2505
  });
2430
2506
 
2431
2507
  // src/rules/code/code-object-params.ts
2432
- import { defineRule as defineRule19 } from "@oxlint/plugins";
2508
+ import { defineRule as defineRule20 } from "@oxlint/plugins";
2433
2509
  function isObjectParam(node) {
2434
2510
  if (node.type === "ObjectPattern") {
2435
2511
  return true;
@@ -2443,7 +2519,7 @@ function isObjectParam(node) {
2443
2519
  const annotation = node.typeAnnotation?.typeAnnotation;
2444
2520
  return !annotation || annotation.type === "TSTypeReference" || annotation.type === "TSTypeLiteral" || annotation.type === "TSIntersectionType";
2445
2521
  }
2446
- var codeObjectParams = defineRule19({
2522
+ var codeObjectParams = defineRule20({
2447
2523
  meta: {
2448
2524
  type: "problem",
2449
2525
  schema: [
@@ -2492,8 +2568,8 @@ var codeObjectParams = defineRule19({
2492
2568
  });
2493
2569
 
2494
2570
  // src/rules/code/code-paired-call.ts
2495
- import { defineRule as defineRule20 } from "@oxlint/plugins";
2496
- var codePairedCall = defineRule20({
2571
+ import { defineRule as defineRule21 } from "@oxlint/plugins";
2572
+ var codePairedCall = defineRule21({
2497
2573
  meta: {
2498
2574
  type: "problem",
2499
2575
  schema: [
@@ -2560,7 +2636,7 @@ var codePairedCall = defineRule20({
2560
2636
  });
2561
2637
 
2562
2638
  // src/rules/code/code-promise-mutation.ts
2563
- import { defineRule as defineRule21 } from "@oxlint/plugins";
2639
+ import { defineRule as defineRule22 } from "@oxlint/plugins";
2564
2640
  function mutationBinding(node) {
2565
2641
  if (node.type === "Identifier") {
2566
2642
  return node;
@@ -2589,7 +2665,7 @@ function mutationBindings(node) {
2589
2665
  const binding = mutationBinding(node);
2590
2666
  return binding ? [binding] : [];
2591
2667
  }
2592
- var codePromiseMutation = defineRule21({
2668
+ var codePromiseMutation = defineRule22({
2593
2669
  meta: {
2594
2670
  type: "problem",
2595
2671
  schema: [
@@ -2682,8 +2758,8 @@ var codePromiseMutation = defineRule21({
2682
2758
  var codeSwitch = astSelectorRule("SwitchStatement", "Avoid switch statements.");
2683
2759
 
2684
2760
  // src/rules/code/code-traced.ts
2685
- import { defineRule as defineRule22 } from "@oxlint/plugins";
2686
- var codeTraced = defineRule22({
2761
+ import { defineRule as defineRule23 } from "@oxlint/plugins";
2762
+ var codeTraced = defineRule23({
2687
2763
  meta: {
2688
2764
  type: "problem",
2689
2765
  schema: [
@@ -2770,7 +2846,7 @@ var codeTraced = defineRule22({
2770
2846
  });
2771
2847
 
2772
2848
  // src/rules/code/code-trivial-functions.ts
2773
- import { defineRule as defineRule23 } from "@oxlint/plugins";
2849
+ import { defineRule as defineRule24 } from "@oxlint/plugins";
2774
2850
  var codeTrivialFunctionsBanned = [
2775
2851
  "isRecord",
2776
2852
  "isPlainObject",
@@ -2900,7 +2976,35 @@ function guardStatement(node, state) {
2900
2976
  return guardFallback(declaration.init);
2901
2977
  });
2902
2978
  }
2903
- var codeTrivialFunctions = defineRule23({
2979
+ function codeTrivialFunctionsDefaults(program) {
2980
+ const starts = [];
2981
+ const names = new Set;
2982
+ for (const statement of program.body) {
2983
+ const declaration = statement.type === "ExportDefaultDeclaration" ? statement.declaration : undefined;
2984
+ if (declaration?.type === "FunctionDeclaration" || declaration && astIsInlineFunction(declaration)) {
2985
+ starts.push(declaration.start);
2986
+ } else if (declaration?.type === "Identifier") {
2987
+ names.add(declaration.name);
2988
+ } else if (statement.type === "ExportNamedDeclaration" && !statement.source) {
2989
+ const specifier = statement.specifiers.find((item) => astModuleName(item.exported) === "default");
2990
+ if (specifier) {
2991
+ names.add(astModuleName(specifier.local));
2992
+ }
2993
+ }
2994
+ }
2995
+ for (const statement of program.body) {
2996
+ if (statement.type === "FunctionDeclaration" && statement.id && names.has(statement.id.name)) {
2997
+ starts.push(statement.start);
2998
+ }
2999
+ for (const declarator of statement.type === "VariableDeclaration" ? statement.declarations : []) {
3000
+ if (declarator.id.type === "Identifier" && names.has(declarator.id.name) && declarator.init && astIsInlineFunction(declarator.init)) {
3001
+ starts.push(declarator.init.start);
3002
+ }
3003
+ }
3004
+ }
3005
+ return starts;
3006
+ }
3007
+ var codeTrivialFunctions = defineRule24({
2904
3008
  meta: {
2905
3009
  type: "problem",
2906
3010
  schema: [
@@ -2914,7 +3018,8 @@ var codeTrivialFunctions = defineRule23({
2914
3018
  services: { type: "boolean" },
2915
3019
  mode: { type: "string", enum: ["legacy", "precise"] },
2916
3020
  deny: { type: "array", items: { type: "string" }, uniqueItems: true },
2917
- guards: { type: "boolean" }
3021
+ guards: { type: "boolean" },
3022
+ defaults: { type: "boolean" }
2918
3023
  }
2919
3024
  }
2920
3025
  ],
@@ -2934,8 +3039,10 @@ var codeTrivialFunctions = defineRule23({
2934
3039
  let checkServiceMethods = false;
2935
3040
  const reported = new Set;
2936
3041
  const checked = new Set;
3042
+ const defaults = new Set;
3043
+ let checkDefaults = true;
2937
3044
  function checkHelper(node) {
2938
- if (bannedNames.size === 0 && !checkGenericGuards || checked.has(node.start)) {
3045
+ if (bannedNames.size === 0 && !checkGenericGuards || checked.has(node.start) || defaults.has(node.start)) {
2939
3046
  return;
2940
3047
  }
2941
3048
  checked.add(node.start);
@@ -3000,12 +3107,20 @@ var codeTrivialFunctions = defineRule23({
3000
3107
  allowAsync = options.async ?? false;
3001
3108
  reported.clear();
3002
3109
  checked.clear();
3110
+ defaults.clear();
3111
+ checkDefaults = options.defaults ?? true;
3003
3112
  },
3004
3113
  FunctionDeclaration: checkHelper,
3005
3114
  FunctionExpression: checkHelper,
3006
3115
  ArrowFunctionExpression: checkHelper,
3007
3116
  Program(program) {
3117
+ for (const start of checkDefaults ? [] : codeTrivialFunctionsDefaults(program)) {
3118
+ defaults.add(start);
3119
+ }
3008
3120
  for (const { name, node } of exportsCheckedFunctions(program, true, checkServiceMethods, allowed)) {
3121
+ if (defaults.has(node.start)) {
3122
+ continue;
3123
+ }
3009
3124
  checkHelper(node);
3010
3125
  if (reported.has(node.start) || node.returnType?.typeAnnotation.type === "TSTypePredicate") {
3011
3126
  continue;
@@ -3031,8 +3146,8 @@ var codeTrivialFunctions = defineRule23({
3031
3146
  });
3032
3147
 
3033
3148
  // src/rules/code/code-yield-argument.ts
3034
- import { defineRule as defineRule24 } from "@oxlint/plugins";
3035
- var codeYieldArgument = defineRule24({
3149
+ import { defineRule as defineRule25 } from "@oxlint/plugins";
3150
+ var codeYieldArgument = defineRule25({
3036
3151
  meta: {
3037
3152
  type: "suggestion",
3038
3153
  messages: {
@@ -3054,7 +3169,7 @@ var codeYieldArgument = defineRule24({
3054
3169
  });
3055
3170
 
3056
3171
  // src/rules/comments/comments-code.ts
3057
- import { defineRule as defineRule25 } from "@oxlint/plugins";
3172
+ import { defineRule as defineRule26 } from "@oxlint/plugins";
3058
3173
  var commentsLineGap = /^[\t \r\n\u2028\u2029]*$/;
3059
3174
  var commentsLineTerminator = /\r\n|[\n\r\u2028\u2029]/;
3060
3175
  var commentsUnicodeTerminator = /[\u2028\u2029]/;
@@ -3105,7 +3220,7 @@ function commentsFollowsOpeningBrace(text, offset) {
3105
3220
  }
3106
3221
  return false;
3107
3222
  }
3108
- var commentsCode = defineRule25({
3223
+ var commentsCode = defineRule26({
3109
3224
  meta: {
3110
3225
  type: "suggestion",
3111
3226
  fixable: "code",
@@ -3271,9 +3386,9 @@ var commentsCode = defineRule25({
3271
3386
  });
3272
3387
 
3273
3388
  // src/rules/comments/comments-directives.ts
3274
- import { defineRule as defineRule26 } from "@oxlint/plugins";
3389
+ import { defineRule as defineRule27 } from "@oxlint/plugins";
3275
3390
  var directive = /^\s*(?:oxlint|eslint)-disable(?:-next-line|-line)?\b(.*)$/s;
3276
- var commentsDirectives = defineRule26({
3391
+ var commentsDirectives = defineRule27({
3277
3392
  meta: {
3278
3393
  type: "problem",
3279
3394
  schema: [],
@@ -3293,17 +3408,665 @@ var commentsDirectives = defineRule26({
3293
3408
  if (rules.trim() && reason.join("--").trim()) {
3294
3409
  continue;
3295
3410
  }
3296
- context.report({ loc: comment.loc, messageId: "directive" });
3411
+ context.report({ loc: comment.loc, messageId: "directive" });
3412
+ }
3413
+ }
3414
+ };
3415
+ }
3416
+ });
3417
+
3418
+ // src/rules/copy/copy-dashes.ts
3419
+ import { defineRule as defineRule28 } from "@oxlint/plugins";
3420
+ var copyDashesMessage = "Use a comma, colon, period or parentheses instead.";
3421
+ var copyDashes = defineRule28({
3422
+ meta: {
3423
+ type: "suggestion",
3424
+ schema: [
3425
+ {
3426
+ type: "object",
3427
+ additionalProperties: false,
3428
+ properties: {
3429
+ deny: { type: "array", items: { type: "string", minLength: 1 } },
3430
+ message: { type: "string" }
3431
+ }
3432
+ }
3433
+ ],
3434
+ messages: {
3435
+ dash: "Remove '{{dash}}' from this text. {{message}}"
3436
+ }
3437
+ },
3438
+ createOnce(context) {
3439
+ let deny = ["—"];
3440
+ let message = copyDashesMessage;
3441
+ function check(node) {
3442
+ const text = context.sourceCode.text;
3443
+ const raw = text.slice(node.range[0], node.range[1]);
3444
+ for (const dash of deny) {
3445
+ for (let index = raw.indexOf(dash);index !== -1; index = raw.indexOf(dash, index + dash.length)) {
3446
+ const start = node.range[0] + index;
3447
+ context.report({
3448
+ loc: { start: context.sourceCode.getLocFromIndex(start), end: context.sourceCode.getLocFromIndex(start + dash.length) },
3449
+ messageId: "dash",
3450
+ data: { dash, message }
3451
+ });
3452
+ }
3453
+ }
3454
+ }
3455
+ return {
3456
+ before() {
3457
+ const options = optionsFirst(context, {});
3458
+ const text = context.sourceCode.text;
3459
+ deny = options.deny ?? ["—"];
3460
+ message = options.message ?? copyDashesMessage;
3461
+ if (!deny.some((dash) => text.includes(dash))) {
3462
+ return false;
3463
+ }
3464
+ },
3465
+ Literal(node) {
3466
+ if (typeof node.value === "string") {
3467
+ check(node);
3468
+ }
3469
+ },
3470
+ TemplateElement: check,
3471
+ JSXText: check
3472
+ };
3473
+ }
3474
+ });
3475
+
3476
+ // src/rules/effect/effect-layer-file.ts
3477
+ import { defineRule as defineRule29 } from "@oxlint/plugins";
3478
+
3479
+ // src/utils/effect.utils.ts
3480
+ var effectBuilders = ["Effect.gen", "Effect.fn", "Effect.fnUntraced"];
3481
+ function effectIsBuilder(node, builders) {
3482
+ const value = node ? astUnwrap(node) : undefined;
3483
+ if (value?.type !== "CallExpression") {
3484
+ return false;
3485
+ }
3486
+ const callee = value.callee.type === "CallExpression" ? value.callee.callee : value.callee;
3487
+ return builders.has(astDottedName(callee) ?? "");
3488
+ }
3489
+ function effectGenerator(node, builders) {
3490
+ const value = node ? astUnwrap(node) : undefined;
3491
+ return effectIsBuilder(value, builders) ? value.arguments.find((argument) => argument.type === "FunctionExpression") : undefined;
3492
+ }
3493
+ function effectMatches(node, patterns) {
3494
+ if (!node || node.type === "Super") {
3495
+ return false;
3496
+ }
3497
+ const value = astUnwrap(node);
3498
+ const target = value.type === "CallExpression" ? value.callee : value;
3499
+ const name = astDottedName(target);
3500
+ if (name !== undefined && optionsPatternsTest(patterns, name)) {
3501
+ return true;
3502
+ }
3503
+ return value.type === "CallExpression" && target.type === "MemberExpression" && astMemberName(target) === "pipe" && effectMatches(target.object, patterns);
3504
+ }
3505
+
3506
+ // src/rules/effect/effect-layer-file.ts
3507
+ var effectLayerFileDefaultLayers = ["^Layer\\.", "^HttpRouter\\.add(?:All)?$", "(?:^|\\.)layer[^.]*$"];
3508
+ var effectLayerFileDefaultServices = ["(?:^|\\.)of$", "^Context\\."];
3509
+ var effectLayerFileDefaultConstructors = [
3510
+ "Layer.effect",
3511
+ "Layer.effectContext",
3512
+ "Layer.effectDiscard",
3513
+ "Layer.scoped",
3514
+ "Layer.scopedContext",
3515
+ "Layer.scopedDiscard",
3516
+ "Layer.sync",
3517
+ "Layer.unwrap"
3518
+ ];
3519
+ var effectLayerFile = defineRule29({
3520
+ meta: {
3521
+ type: "problem",
3522
+ schema: [
3523
+ {
3524
+ type: "object",
3525
+ additionalProperties: false,
3526
+ properties: {
3527
+ layer: { type: "string" },
3528
+ make: { type: "string" },
3529
+ prefix: { type: "string" },
3530
+ builders: { type: "array", items: { type: "string" } },
3531
+ layers: optionsPatternSchema,
3532
+ services: optionsPatternSchema,
3533
+ constructors: { type: "array", items: { type: "string" } }
3534
+ }
3535
+ }
3536
+ ],
3537
+ messages: {
3538
+ missing: "A layer file exports `{{layer}}`.",
3539
+ notLayer: "`{{layer}}` is built with Layer: a Layer call, a Layer pipe, or a function returning one.",
3540
+ builderName: "The Effect that builds the service is named `{{make}}`, one per layer file.",
3541
+ moduleMethod: "Only `{{make}}` is an Effect at module level; move this into `{{make}}`, or into a utils file if two files use it.",
3542
+ moduleHelper: "A layer file holds only `{{make}}`, {{prefix}}<Role> layers and `{{layer}}`; move this into `{{make}}`, or into a utils file if two files use it.",
3543
+ localLayer: "A Layer other than `{{layer}}` is named {{prefix}}<Role> and is not exported.",
3544
+ makeReturn: "`{{make}}` returns the service through its tag (Tag.of({ … })), a Context, or a Layer.",
3545
+ inlineBuilder: "Build this layer from `{{make}}`, not an inline Effect: Layer.effect(Tag, {{make}}). One built layer per file."
3546
+ }
3547
+ },
3548
+ createOnce(context) {
3549
+ let layer = "layer";
3550
+ let make = "make";
3551
+ let prefix = "layer";
3552
+ let builders = new Set(effectBuilders);
3553
+ let layers = [];
3554
+ let services = [];
3555
+ let constructors = new Set(effectLayerFileDefaultConstructors);
3556
+ const returned = (node) => {
3557
+ const value = node ? astUnwrap(node) : node;
3558
+ if (!value || !astIsInlineFunction(value)) {
3559
+ return value;
3560
+ }
3561
+ return value.body?.type === "BlockStatement" ? returned(value.body.body.findLast((statement) => statement.type === "ReturnStatement")?.argument) : returned(value.body);
3562
+ };
3563
+ return {
3564
+ before() {
3565
+ const options = optionsFirst(context, {});
3566
+ layer = options.layer ?? "layer";
3567
+ make = options.make ?? "make";
3568
+ prefix = options.prefix ?? "layer";
3569
+ builders = new Set(options.builders ?? effectBuilders);
3570
+ constructors = new Set(options.constructors ?? effectLayerFileDefaultConstructors);
3571
+ layers = optionsPatterns(options.layers ?? effectLayerFileDefaultLayers);
3572
+ services = [...optionsPatterns(options.services ?? effectLayerFileDefaultServices), ...layers];
3573
+ },
3574
+ Program(program) {
3575
+ const data = { layer, make, prefix };
3576
+ const declarators = program.body.flatMap((statement) => {
3577
+ const declaration = astDeclaration(statement);
3578
+ const exported = statement.type === "ExportNamedDeclaration";
3579
+ return declaration?.type === "VariableDeclaration" ? declaration.declarations.map((node) => ({ exported, node, name: node.id.type === "Identifier" ? node.id.name : "" })) : [];
3580
+ });
3581
+ const exportedLayer = declarators.find(({ exported, name }) => exported && name === layer);
3582
+ if (!exportedLayer) {
3583
+ context.report({
3584
+ node: program,
3585
+ messageId: "missing",
3586
+ data
3587
+ });
3588
+ } else if (!effectMatches(returned(exportedLayer.node.init), layers)) {
3589
+ context.report({
3590
+ node: exportedLayer.node,
3591
+ messageId: "notLayer",
3592
+ data
3593
+ });
3594
+ }
3595
+ const wired = new Set;
3596
+ const wiring = declarators.filter((item) => item === exportedLayer || effectMatches(item.node.init, layers));
3597
+ astVisit(wiring.map(({ node }) => node.init), [], (call) => {
3598
+ if (call.type !== "CallExpression" || !optionsPatternsTest(layers, astDottedName(call.callee) ?? "")) {
3599
+ return;
3600
+ }
3601
+ for (const argument of call.arguments) {
3602
+ const target = argument.type === "CallExpression" ? argument.callee : argument;
3603
+ if (target.type === "Identifier") {
3604
+ wired.add(target.name);
3605
+ }
3606
+ }
3607
+ });
3608
+ const locals = declarators.filter(({ exported }) => !exported);
3609
+ const effects = locals.filter(({ node }) => effectIsBuilder(node.init, builders));
3610
+ const builder = effects.find(({ name }) => name === make) ?? effects.find(({ name }) => wired.has(name));
3611
+ if (builder && builder.name !== make) {
3612
+ context.report({
3613
+ node: builder.node,
3614
+ messageId: "builderName",
3615
+ data
3616
+ });
3617
+ }
3618
+ for (const { node, name, exported } of declarators) {
3619
+ const isLayer = effectMatches(node.init, layers);
3620
+ const isEffect = effectIsBuilder(node.init, builders);
3621
+ if (!exported && isEffect && name !== builder?.name) {
3622
+ context.report({
3623
+ node,
3624
+ messageId: "moduleMethod",
3625
+ data
3626
+ });
3627
+ }
3628
+ if (!exported && !isEffect && !isLayer && name !== make) {
3629
+ context.report({
3630
+ node,
3631
+ messageId: "moduleHelper",
3632
+ data
3633
+ });
3634
+ }
3635
+ if (isLayer && name !== layer && (exported || !name.startsWith(prefix) || !/^[A-Z]\w*$/.test(name.slice(prefix.length)))) {
3636
+ context.report({
3637
+ node,
3638
+ messageId: "localLayer",
3639
+ data
3640
+ });
3641
+ }
3642
+ }
3643
+ for (const statement of program.body) {
3644
+ if (statement.type === "FunctionDeclaration") {
3645
+ context.report({
3646
+ node: statement,
3647
+ messageId: "moduleHelper",
3648
+ data
3649
+ });
3650
+ }
3651
+ }
3652
+ const generator = builder?.name === make ? effectGenerator(builder.node.init, builders) : undefined;
3653
+ for (const statement of generator?.body?.body ?? []) {
3654
+ if (statement.type === "ReturnStatement" && !effectMatches(statement.argument, services)) {
3655
+ context.report({
3656
+ node: statement,
3657
+ messageId: "makeReturn",
3658
+ data
3659
+ });
3660
+ }
3661
+ }
3662
+ },
3663
+ CallExpression(node) {
3664
+ if (!constructors.has(astDottedName(node.callee) ?? "")) {
3665
+ return;
3666
+ }
3667
+ for (const argument of node.arguments) {
3668
+ if (argument.type !== "SpreadElement" && (astIsInlineFunction(astUnwrap(argument)) || effectIsBuilder(argument, builders))) {
3669
+ context.report({
3670
+ node: argument,
3671
+ messageId: "inlineBuilder",
3672
+ data: { layer, make, prefix }
3673
+ });
3674
+ }
3675
+ }
3676
+ }
3677
+ };
3678
+ }
3679
+ });
3680
+
3681
+ // src/rules/effect/effect-layers.ts
3682
+ import { defineRule as defineRule30 } from "@oxlint/plugins";
3683
+ var effectLayersDefaultMerge = ["Layer.mergeAll"];
3684
+ var effectLayersDefaultProvide = ["Effect.provide", "Layer.provide", "Layer.provideMerge"];
3685
+ var effectLayersDefaultMin = 2;
3686
+ var effectLayersGroupSchema = {
3687
+ type: "object",
3688
+ additionalProperties: false,
3689
+ properties: {
3690
+ callees: { type: "array", items: { type: "string" } },
3691
+ min: { type: "integer", minimum: 1 }
3692
+ }
3693
+ };
3694
+ var effectLayers = defineRule30({
3695
+ meta: {
3696
+ type: "layout",
3697
+ fixable: "whitespace",
3698
+ schema: [
3699
+ {
3700
+ type: "object",
3701
+ additionalProperties: false,
3702
+ properties: {
3703
+ merge: effectLayersGroupSchema,
3704
+ provide: effectLayersGroupSchema,
3705
+ indent: sourceIndentSchema
3706
+ }
3707
+ }
3708
+ ],
3709
+ messages: {
3710
+ merge: "Put every layer in this merge on its own line.",
3711
+ provide: "Put every layer in this array on its own line."
3712
+ }
3713
+ },
3714
+ createOnce(context) {
3715
+ let merge = new Set(effectLayersDefaultMerge);
3716
+ let provide = new Set(effectLayersDefaultProvide);
3717
+ let mergeMin = effectLayersDefaultMin;
3718
+ let provideMin = effectLayersDefaultMin;
3719
+ let indentUnit = sourceIndentUnit();
3720
+ const check = (node) => {
3721
+ const source = context.sourceCode;
3722
+ const call = node.type === "CallExpression";
3723
+ const items = call ? node.arguments : node.elements;
3724
+ const present = items.filter((item) => item !== null);
3725
+ const open = call ? source.getTokenAfter(node.typeArguments ?? node.callee) : source.getFirstToken(node);
3726
+ const close = source.getLastToken(node);
3727
+ if (open?.value !== (call ? "(" : "[") || close === null || present.length < (call ? mergeMin : provideMin)) {
3728
+ return;
3729
+ }
3730
+ const packed = [open, ...present].some((item, index) => item.loc.end.line === (present.at(index) ?? close).loc.start.line);
3731
+ if (!packed) {
3732
+ return;
3733
+ }
3734
+ const text = source.text;
3735
+ const newline = sourceNewline(text);
3736
+ const base = sourceLineIndent(text, open.range[0]);
3737
+ const gaps = sourceItemGaps(open.range[1], present, close.range[0]);
3738
+ context.report({
3739
+ node,
3740
+ messageId: call ? "merge" : "provide",
3741
+ fix: (fixer) => {
3742
+ if (present.length !== items.length) {
3743
+ return null;
3744
+ }
3745
+ return sourceRespace(fixer, text, gaps, (_, end) => `${newline}${base}${end ? "" : indentUnit}`);
3746
+ }
3747
+ });
3748
+ };
3749
+ return {
3750
+ before() {
3751
+ const options = optionsFirst(context, {});
3752
+ merge = new Set(options.merge?.callees ?? effectLayersDefaultMerge);
3753
+ provide = new Set(options.provide?.callees ?? effectLayersDefaultProvide);
3754
+ mergeMin = options.merge?.min ?? effectLayersDefaultMin;
3755
+ provideMin = options.provide?.min ?? effectLayersDefaultMin;
3756
+ indentUnit = sourceIndentUnit(options.indent);
3757
+ },
3758
+ CallExpression(node) {
3759
+ const name = astDottedName(node.callee);
3760
+ if (name !== undefined && merge.has(name)) {
3761
+ check(node);
3762
+ }
3763
+ if (name !== undefined && provide.has(name)) {
3764
+ for (const argument of node.arguments) {
3765
+ if (argument.type === "ArrayExpression") {
3766
+ check(argument);
3767
+ }
3768
+ }
3769
+ }
3770
+ }
3771
+ };
3772
+ }
3773
+ });
3774
+
3775
+ // src/rules/effect/effect-merge-pipe.ts
3776
+ import { defineRule as defineRule31 } from "@oxlint/plugins";
3777
+ var effectMergePipeDefaultMerge = ["Layer.mergeAll"];
3778
+ var effectMergePipeDefaultPipe = ["pipe", "Function.pipe"];
3779
+ var effectMergePipeDefaultMethods = ["pipe"];
3780
+ var effectMergePipe = defineRule31({
3781
+ meta: {
3782
+ type: "layout",
3783
+ schema: [
3784
+ {
3785
+ type: "object",
3786
+ additionalProperties: false,
3787
+ properties: {
3788
+ merge: { type: "array", items: { type: "string" } },
3789
+ pipe: { type: "array", items: { type: "string" } },
3790
+ methods: { type: "array", items: { type: "string" } }
3791
+ }
3792
+ }
3793
+ ],
3794
+ messages: {
3795
+ piped: "A merge that spans lines is never piped. Name it first, then pipe the name."
3796
+ }
3797
+ },
3798
+ createOnce(context) {
3799
+ let merge = new Set(effectMergePipeDefaultMerge);
3800
+ let pipe = new Set(effectMergePipeDefaultPipe);
3801
+ let methods = new Set(effectMergePipeDefaultMethods);
3802
+ const isMultilineMerge = (node) => node?.type === "CallExpression" && merge.has(astDottedName(node.callee) ?? "") && !astIsOneLine(node);
3803
+ return {
3804
+ before() {
3805
+ const options = optionsFirst(context, {});
3806
+ merge = new Set(options.merge ?? effectMergePipeDefaultMerge);
3807
+ pipe = new Set(options.pipe ?? effectMergePipeDefaultPipe);
3808
+ methods = new Set(options.methods ?? effectMergePipeDefaultMethods);
3809
+ },
3810
+ CallExpression(node) {
3811
+ const callee = node.callee;
3812
+ const first = node.arguments.at(0);
3813
+ if (callee.type === "MemberExpression" && methods.has(astMemberName(callee) ?? "") && isMultilineMerge(callee.object)) {
3814
+ context.report({ node: callee.property, messageId: "piped" });
3815
+ }
3816
+ if (pipe.has(astDottedName(callee) ?? "") && first !== undefined && isMultilineMerge(first)) {
3817
+ context.report({ node: first, messageId: "piped" });
3818
+ }
3819
+ }
3820
+ };
3821
+ }
3822
+ });
3823
+
3824
+ // src/rules/effect/effect-schema-first.ts
3825
+ import { defineRule as defineRule32 } from "@oxlint/plugins";
3826
+ var effectSchemaFirstDefaultRuntime = [
3827
+ "Effect",
3828
+ "Layer",
3829
+ "Stream",
3830
+ "Context",
3831
+ "Scope",
3832
+ "Kysely",
3833
+ "React",
3834
+ "Fiber",
3835
+ "Queue",
3836
+ "PubSub",
3837
+ "Ref",
3838
+ "Deferred",
3839
+ "ManagedRuntime",
3840
+ "Sink",
3841
+ "Channel",
3842
+ "Request",
3843
+ "Response",
3844
+ "Headers",
3845
+ "ReadableStream",
3846
+ "WritableStream",
3847
+ "Blob",
3848
+ "File",
3849
+ "AbortSignal",
3850
+ "WebSocket",
3851
+ "Socket",
3852
+ "Worker",
3853
+ "Uint8Array",
3854
+ "ArrayBuffer",
3855
+ "globalThis"
3856
+ ];
3857
+ var effectSchemaFirstSections = {
3858
+ values: ["VariableDeclaration", "ClassDeclaration"],
3859
+ interfaces: ["TSInterfaceDeclaration"],
3860
+ modules: ["TSModuleDeclaration"],
3861
+ aliases: ["TSTypeAliasDeclaration"]
3862
+ };
3863
+ var effectSchemaFirstLabels = {
3864
+ values: "Schemas and values",
3865
+ interfaces: "interfaces",
3866
+ modules: "`declare module`",
3867
+ aliases: "type aliases"
3868
+ };
3869
+ var effectSchemaFirstDefaultOrder = ["values", ["interfaces", "modules"], "aliases"];
3870
+ var effectSchemaFirstSectionSchema = { type: "string", enum: ["values", "interfaces", "modules", "aliases"] };
3871
+ function effectSchemaFirstTypeName(node) {
3872
+ if (node.type === "TSQualifiedName") {
3873
+ const left = effectSchemaFirstTypeName(node.left);
3874
+ return left === undefined ? undefined : `${left}.${node.right.name}`;
3875
+ }
3876
+ return astDottedName(node);
3877
+ }
3878
+ function effectSchemaFirstReference(node) {
3879
+ if (node.type === "TSTypeReference") {
3880
+ return effectSchemaFirstTypeName(node.typeName);
3881
+ }
3882
+ if (node.type === "TSTypeQuery") {
3883
+ return effectSchemaFirstTypeName(node.exprName);
3884
+ }
3885
+ return node.type === "TSInterfaceHeritage" ? astDottedName(node.expression) : undefined;
3886
+ }
3887
+ var effectSchemaFirst = defineRule32({
3888
+ meta: {
3889
+ type: "suggestion",
3890
+ schema: [
3891
+ {
3892
+ type: "object",
3893
+ additionalProperties: false,
3894
+ properties: {
3895
+ member: { type: "string" },
3896
+ keep: optionsPatternSchema,
3897
+ runtime: { type: "array", items: { type: "string" } },
3898
+ rows: {
3899
+ type: "object",
3900
+ additionalProperties: false,
3901
+ properties: { types: { type: "array", items: { type: "string" } }, suffix: { type: "string" } }
3902
+ },
3903
+ order: {
3904
+ type: "array",
3905
+ items: { anyOf: [effectSchemaFirstSectionSchema, { type: "array", items: effectSchemaFirstSectionSchema, minItems: 1 }] }
3906
+ },
3907
+ derived: {
3908
+ type: "object",
3909
+ additionalProperties: false,
3910
+ properties: { first: { type: "boolean" }, sorted: { type: "boolean" } }
3911
+ }
3912
+ }
3913
+ }
3914
+ ],
3915
+ messages: {
3916
+ order: "Order a types file as {{order}}.",
3917
+ schemaType: "Add `export type {{name}} = typeof {{name}}.{{member}}` to the final type block.",
3918
+ typeOrder: "Move {{name}}: the type block lists the `typeof` aliases first, in Schema order, then the other aliases.",
3919
+ dataInterface: "Every field of {{name}} fits a Schema: make it a Schema and derive its type.",
3920
+ rowItem: "{{name}} is built from a database row: make it a Schema; only a `…{{suffix}}` alias may read the row type."
3921
+ }
3922
+ },
3923
+ createOnce(context) {
3924
+ let member = "Type";
3925
+ let keep = [];
3926
+ let runtime = new Set(effectSchemaFirstDefaultRuntime);
3927
+ let rows;
3928
+ let suffix = "Record";
3929
+ let ranks = new Map;
3930
+ let orderText = "";
3931
+ let derivedFirst = true;
3932
+ let derivedSorted = true;
3933
+ const derives = (alias) => {
3934
+ const annotation = alias.typeAnnotation;
3935
+ const name = annotation.type === "TSTypeQuery" ? annotation.exprName : undefined;
3936
+ return name?.type === "TSQualifiedName" && name.left.type === "Identifier" && name.left.name === alias.id.name && name.right.name === member;
3937
+ };
3938
+ const refers = (node, names) => astSome(node, (child) => {
3939
+ if (child.type === "TSFunctionType" || child.type === "TSConstructorType" || child.type === "TSMethodSignature") {
3940
+ return true;
3941
+ }
3942
+ return (effectSchemaFirstReference(child)?.split(".") ?? []).some((name) => names.has(name));
3943
+ });
3944
+ const readsRow = (node) => astSome(node, (child) => Boolean(rows?.has(effectSchemaFirstReference(child) ?? "")));
3945
+ const checkOrder = (program) => {
3946
+ let highest = 0;
3947
+ for (const statement of program.body) {
3948
+ const rank = ranks.get(astDeclaration(statement)?.type ?? "") ?? 0;
3949
+ if (rank > 0 && rank < highest) {
3950
+ context.report({
3951
+ node: statement,
3952
+ messageId: "order",
3953
+ data: { order: orderText }
3954
+ });
3955
+ }
3956
+ highest = Math.max(highest, rank);
3957
+ }
3958
+ };
3959
+ const keptNames = (program, aliases, interfaces) => {
3960
+ const kept = new Set(aliases.filter((alias) => refers(alias.typeAnnotation, runtime)).map((alias) => alias.id.name));
3961
+ for (const statement of program.body) {
3962
+ if (statement.type === "ImportDeclaration" && statement.source.value.startsWith(".")) {
3963
+ for (const specifier of statement.specifiers) {
3964
+ if (statement.importKind === "type" || specifier.type === "ImportSpecifier" && specifier.importKind === "type") {
3965
+ kept.add(specifier.local.name);
3966
+ }
3967
+ }
3968
+ }
3969
+ }
3970
+ const names = new Set([...runtime, ...kept]);
3971
+ let grew = true;
3972
+ while (grew) {
3973
+ grew = false;
3974
+ for (const node of interfaces) {
3975
+ const stays = node.body.body.length === 0 || Boolean(node.typeParameters) || optionsPatternsTest(keep, node.id.name) || refers(node, names);
3976
+ if (!kept.has(node.id.name) && stays) {
3977
+ names.add(node.id.name);
3978
+ kept.add(node.id.name);
3979
+ grew = true;
3980
+ }
3981
+ }
3982
+ }
3983
+ return kept;
3984
+ };
3985
+ return {
3986
+ before() {
3987
+ const options = optionsFirst(context, {});
3988
+ member = options.member ?? "Type";
3989
+ keep = optionsPatterns(options.keep);
3990
+ runtime = new Set(options.runtime ?? effectSchemaFirstDefaultRuntime);
3991
+ rows = options.rows ? new Set(options.rows.types ?? ["Kysely.Selectable"]) : undefined;
3992
+ suffix = options.rows?.suffix ?? "Record";
3993
+ const order = (options.order ?? effectSchemaFirstDefaultOrder).map((group) => [group].flat());
3994
+ ranks = new Map(order.flatMap((group, index) => group.flatMap((section) => effectSchemaFirstSections[section].map((type) => [type, index + 1]))));
3995
+ orderText = order.map((group) => group.map((section) => effectSchemaFirstLabels[section]).join(" and ")).join(", then ");
3996
+ derivedFirst = options.derived?.first ?? true;
3997
+ derivedSorted = options.derived?.sorted ?? true;
3998
+ },
3999
+ Program(program) {
4000
+ checkOrder(program);
4001
+ const declarations = program.body.flatMap((statement) => astDeclaration(statement) ?? []);
4002
+ const aliases = declarations.filter((node) => node.type === "TSTypeAliasDeclaration");
4003
+ const interfaces = declarations.filter((node) => node.type === "TSInterfaceDeclaration");
4004
+ const schemas = declarations.flatMap((node) => node.type === "VariableDeclaration" ? node.declarations : []).map(({ id }) => id.type === "Identifier" ? id.name : "");
4005
+ const exported = program.body.flatMap((statement) => statement.type === "ExportNamedDeclaration" && statement.declaration ? [statement.declaration] : []);
4006
+ const typed = new Set(exported.flatMap((node) => node.type === "TSTypeAliasDeclaration" && derives(node) ? [node.id.name] : []));
4007
+ for (const node of exported.flatMap((declaration) => declaration.type === "VariableDeclaration" ? declaration.declarations : [])) {
4008
+ const isSchema = node.init?.type === "CallExpression" || node.init?.type === "MemberExpression";
4009
+ if (isSchema && node.id.type === "Identifier" && !typed.has(node.id.name)) {
4010
+ context.report({
4011
+ node,
4012
+ messageId: "schemaType",
4013
+ data: { name: node.id.name, member }
4014
+ });
4015
+ }
4016
+ }
4017
+ let order = -1;
4018
+ let other = false;
4019
+ for (const alias of aliases) {
4020
+ if (!derives(alias)) {
4021
+ other = true;
4022
+ continue;
4023
+ }
4024
+ const index = schemas.indexOf(alias.id.name);
4025
+ if (derivedFirst && other || derivedSorted && index < order) {
4026
+ context.report({
4027
+ node: alias,
4028
+ messageId: "typeOrder",
4029
+ data: { name: alias.id.name }
4030
+ });
4031
+ }
4032
+ order = Math.max(order, index);
4033
+ }
4034
+ const kept = keptNames(program, aliases, interfaces);
4035
+ for (const node of interfaces) {
4036
+ if (!kept.has(node.id.name)) {
4037
+ context.report({
4038
+ node,
4039
+ messageId: "dataInterface",
4040
+ data: { name: node.id.name }
4041
+ });
4042
+ }
4043
+ }
4044
+ for (const node of rows ? declarations : []) {
4045
+ const fromRow = node.type === "TSTypeAliasDeclaration" && !node.id.name.endsWith(suffix) && readsRow(node.typeAnnotation) || node.type === "TSInterfaceDeclaration" && node.extends.some(readsRow);
4046
+ if (fromRow) {
4047
+ context.report({
4048
+ node,
4049
+ messageId: "rowItem",
4050
+ data: { name: node.id.name, suffix }
4051
+ });
4052
+ }
3297
4053
  }
3298
4054
  }
3299
4055
  };
3300
4056
  }
3301
4057
  });
3302
4058
 
3303
- // src/rules/copy/copy-dashes.ts
3304
- import { defineRule as defineRule27 } from "@oxlint/plugins";
3305
- var copyDashesMessage = "Use a comma, colon, period or parentheses instead.";
3306
- var copyDashes = defineRule27({
4059
+ // src/rules/effect/effect-values-first.ts
4060
+ import { defineRule as defineRule33 } from "@oxlint/plugins";
4061
+ var effectValuesFirstDefaultPinned = [
4062
+ "^Layer\\.",
4063
+ "^Context\\.",
4064
+ "^HttpRouter\\.add(?:All)?$",
4065
+ "(?:^|\\.)of$",
4066
+ "(?:^|\\.)layer[^.]*$",
4067
+ "^Schema\\.(?:asserts|decode\\w*|encode\\w*|is|isSchema|isSchemaError|resolveAnnotations)$"
4068
+ ];
4069
+ var effectValuesFirst = defineRule33({
3307
4070
  meta: {
3308
4071
  type: "suggestion",
3309
4072
  schema: [
@@ -3311,55 +4074,82 @@ var copyDashes = defineRule27({
3311
4074
  type: "object",
3312
4075
  additionalProperties: false,
3313
4076
  properties: {
3314
- deny: { type: "array", items: { type: "string", minLength: 1 } },
3315
- message: { type: "string" }
4077
+ builders: { type: "array", items: { type: "string" } },
4078
+ pinned: optionsPatternSchema
3316
4079
  }
3317
4080
  }
3318
4081
  ],
3319
4082
  messages: {
3320
- dash: "Remove '{{dash}}' from this text. {{message}}"
4083
+ value: "`{{name}}` is a value declared after `{{after}}`: declare values first, then helpers and methods."
3321
4084
  }
3322
4085
  },
3323
4086
  createOnce(context) {
3324
- let deny = ["—"];
3325
- let message = copyDashesMessage;
3326
- function check(node) {
3327
- const text = context.sourceCode.text;
3328
- const raw = text.slice(node.range[0], node.range[1]);
3329
- for (const dash of deny) {
3330
- for (let index = raw.indexOf(dash);index !== -1; index = raw.indexOf(dash, index + dash.length)) {
3331
- const start = node.range[0] + index;
3332
- context.report({
3333
- loc: { start: context.sourceCode.getLocFromIndex(start), end: context.sourceCode.getLocFromIndex(start + dash.length) },
3334
- messageId: "dash",
3335
- data: { dash, message }
3336
- });
4087
+ let builders = new Set(effectBuilders);
4088
+ let pinned = [];
4089
+ const check = (statements) => {
4090
+ const tied = new Set;
4091
+ let first;
4092
+ let barrier = false;
4093
+ for (const statement of statements) {
4094
+ const declaration = astDeclaration(statement);
4095
+ if (declaration?.type === "FunctionDeclaration" && declaration.id) {
4096
+ tied.add(declaration.id.name);
4097
+ first ??= declaration.id.name;
4098
+ continue;
4099
+ }
4100
+ if (declaration?.type !== "VariableDeclaration") {
4101
+ barrier ||= statement.type === "ExpressionStatement" && first !== undefined;
4102
+ continue;
4103
+ }
4104
+ for (const declarator of declaration.declarations) {
4105
+ const init = declarator.init;
4106
+ const names = astBindings(declarator.id).map((binding) => binding.name);
4107
+ const helper = Boolean(init && (astIsInlineFunction(astUnwrap(init)) || effectIsBuilder(init, builders)));
4108
+ const kept = helper || effectMatches(init, pinned) || astSome(init, (node) => node.type === "Identifier" && tied.has(node.name));
4109
+ if (first !== undefined && !kept && !barrier) {
4110
+ context.report({
4111
+ node: declarator,
4112
+ messageId: "value",
4113
+ data: { name: names.join(", "), after: first }
4114
+ });
4115
+ }
4116
+ if (kept) {
4117
+ names.forEach((name) => tied.add(name));
4118
+ }
4119
+ barrier ||= kept && first !== undefined && astSome(init, (node) => node.type === "YieldExpression");
4120
+ first ??= helper ? names.join(", ") : undefined;
3337
4121
  }
3338
4122
  }
3339
- }
4123
+ };
4124
+ const body = (init) => {
4125
+ const value = init ? astUnwrap(init) : null;
4126
+ const target = value && astIsInlineFunction(value) ? value : effectGenerator(value, builders);
4127
+ return target?.body?.type === "BlockStatement" ? target.body.body : [];
4128
+ };
3340
4129
  return {
3341
4130
  before() {
3342
4131
  const options = optionsFirst(context, {});
3343
- const text = context.sourceCode.text;
3344
- deny = options.deny ?? ["—"];
3345
- message = options.message ?? copyDashesMessage;
3346
- if (!deny.some((dash) => text.includes(dash))) {
3347
- return false;
3348
- }
4132
+ builders = new Set(options.builders ?? effectBuilders);
4133
+ pinned = optionsPatterns(options.pinned ?? effectValuesFirstDefaultPinned);
3349
4134
  },
3350
- Literal(node) {
3351
- if (typeof node.value === "string") {
3352
- check(node);
4135
+ Program(program) {
4136
+ check(program.body);
4137
+ for (const statement of program.body) {
4138
+ const declaration = astDeclaration(statement);
4139
+ if (declaration?.type === "FunctionDeclaration") {
4140
+ check(declaration.body?.body ?? []);
4141
+ }
4142
+ if (declaration?.type === "VariableDeclaration") {
4143
+ declaration.declarations.forEach((declarator) => check(body(declarator.init)));
4144
+ }
3353
4145
  }
3354
- },
3355
- TemplateElement: check,
3356
- JSXText: check
4146
+ }
3357
4147
  };
3358
4148
  }
3359
4149
  });
3360
4150
 
3361
4151
  // src/rules/files/files-components-only.ts
3362
- import { defineRule as defineRule28 } from "@oxlint/plugins";
4152
+ import { defineRule as defineRule34 } from "@oxlint/plugins";
3363
4153
 
3364
4154
  // src/utils/react.utils.ts
3365
4155
  function reactReturnsOutput(program, node) {
@@ -3431,7 +4221,7 @@ function reactIsComponent(program, candidate) {
3431
4221
  function componentName(basename2) {
3432
4222
  return basename2.replace(/\.(tsx?|jsx?)$/, "").replaceAll(/(?:^[-.]*|[-.]+)(.?)/g, (_, char) => char.toUpperCase());
3433
4223
  }
3434
- var filesComponentsOnly = defineRule28({
4224
+ var filesComponentsOnly = defineRule34({
3435
4225
  meta: {
3436
4226
  type: "problem",
3437
4227
  schema: [
@@ -3519,7 +4309,7 @@ var filesComponentsOnly = defineRule28({
3519
4309
  });
3520
4310
 
3521
4311
  // src/rules/files/files-constants-only.ts
3522
- import { defineRule as defineRule29 } from "@oxlint/plugins";
4312
+ import { defineRule as defineRule35 } from "@oxlint/plugins";
3523
4313
  function unwrap(expression) {
3524
4314
  if (expression.type === "ParenthesizedExpression" || expression.type === "TSAsExpression" || expression.type === "TSSatisfiesExpression" || expression.type === "TSTypeAssertion" || expression.type === "TSNonNullExpression" || expression.type === "TSInstantiationExpression") {
3525
4315
  return unwrap(expression.expression);
@@ -3574,7 +4364,7 @@ function collectBindings(program) {
3574
4364
  }
3575
4365
  return bindings;
3576
4366
  }
3577
- var filesConstantsOnly = defineRule29({
4367
+ var filesConstantsOnly = defineRule35({
3578
4368
  meta: {
3579
4369
  type: "problem",
3580
4370
  schema: [
@@ -3673,7 +4463,7 @@ var filesConstantsOnly = defineRule29({
3673
4463
  });
3674
4464
 
3675
4465
  // src/rules/files/files-domain-constants.ts
3676
- import { defineRule as defineRule30 } from "@oxlint/plugins";
4466
+ import { defineRule as defineRule36 } from "@oxlint/plugins";
3677
4467
  import { basename as basename2, dirname as dirname2 } from "node:path";
3678
4468
  function isData(expression) {
3679
4469
  const value = astUnwrap(expression);
@@ -3691,7 +4481,7 @@ function isData(expression) {
3691
4481
  }
3692
4482
  return value.type === "NewExpression" && value.callee.type === "Identifier" && ["Map", "Set"].includes(value.callee.name) && value.arguments[0]?.type === "ArrayExpression" && value.arguments[0].elements.length > 0;
3693
4483
  }
3694
- var filesDomainConstants = defineRule30({
4484
+ var filesDomainConstants = defineRule36({
3695
4485
  meta: {
3696
4486
  type: "problem",
3697
4487
  schema: [
@@ -3756,11 +4546,12 @@ var filesDomainConstants = defineRule30({
3756
4546
  });
3757
4547
 
3758
4548
  // src/rules/files/files-exports.ts
3759
- import { defineRule as defineRule31 } from "@oxlint/plugins";
4549
+ import { defineRule as defineRule37 } from "@oxlint/plugins";
4550
+ import { posix as posix2 } from "node:path";
3760
4551
  function escapeRegExp(value) {
3761
4552
  return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
3762
4553
  }
3763
- var filesExports = defineRule31({
4554
+ var filesExports = defineRule37({
3764
4555
  meta: {
3765
4556
  type: "problem",
3766
4557
  schema: [
@@ -3770,7 +4561,8 @@ var filesExports = defineRule31({
3770
4561
  properties: {
3771
4562
  names: { type: "array", items: { type: "string" } },
3772
4563
  patterns: { type: "array", items: { type: "string" } },
3773
- stem: { type: "string", enum: [...namingStemModes] },
4564
+ stem: { type: "string", enum: [...namingStemModes, "folder"] },
4565
+ singularize: { type: "string", enum: ["none", "trailing-s"] },
3774
4566
  roles: { type: "array", items: { type: "string" } },
3775
4567
  separators: { type: "array", items: { type: "string" } },
3776
4568
  types: { type: "boolean" },
@@ -3813,7 +4605,9 @@ var filesExports = defineRule31({
3813
4605
  if (options.names === undefined && options.patterns === undefined) {
3814
4606
  return;
3815
4607
  }
3816
- const domain = namingFileStem(basename3, options.stem, options.roles, options.separators);
4608
+ const read = options.stem === "folder" ? namingBasename(posix2.dirname(context.filename.replaceAll("\\", "/"))) : namingFileStem(basename3, options.stem, options.roles, options.separators);
4609
+ const last = read.split(/[-_]+/).at(-1) ?? "";
4610
+ const domain = options.singularize === "trailing-s" && last.length > 1 && last.endsWith("s") ? read.slice(0, -1) : read;
3817
4611
  const pascalDomain = namingPascalCase(domain);
3818
4612
  const camelDomain = namingCamelCase(domain);
3819
4613
  const allowed = (options.names ?? []).map((template) => template.replaceAll("{Domain}", pascalDomain).replaceAll("{domain}", camelDomain));
@@ -3841,12 +4635,12 @@ var filesExports = defineRule31({
3841
4635
  });
3842
4636
 
3843
4637
  // src/rules/files/files-externals.ts
3844
- import { defineRule as defineRule32 } from "@oxlint/plugins";
4638
+ import { defineRule as defineRule38 } from "@oxlint/plugins";
3845
4639
  import { existsSync as existsSync2, readFileSync } from "node:fs";
3846
4640
  import { builtinModules as builtinModules2 } from "node:module";
3847
4641
  import { dirname as dirname3, join as join2 } from "node:path";
3848
4642
  var filesExternalsConfigs = "^(?:vite|next)\\.config\\.[cm]?[jt]s$";
3849
- var filesExternals = defineRule32({
4643
+ var filesExternals = defineRule38({
3850
4644
  meta: {
3851
4645
  type: "problem",
3852
4646
  schema: [
@@ -3939,7 +4733,7 @@ var filesExternals = defineRule32({
3939
4733
  });
3940
4734
 
3941
4735
  // src/rules/files/files-factory-keys.ts
3942
- import { defineRule as defineRule33 } from "@oxlint/plugins";
4736
+ import { defineRule as defineRule39 } from "@oxlint/plugins";
3943
4737
  function returnedObject(expression) {
3944
4738
  if (expression?.type === "TSAsExpression" || expression?.type === "TSSatisfiesExpression" || expression?.type === "TSTypeAssertion" || expression?.type === "TSNonNullExpression") {
3945
4739
  return returnedObject(expression.expression);
@@ -3950,7 +4744,7 @@ function compileKeys(pattern, keys, requireKeys) {
3950
4744
  const allowedList = keys.concat(requireKeys.filter((key) => !keys.includes(key)));
3951
4745
  return { patterns: optionsPatternsWithEmpty(pattern), allowed: new Set(allowedList), allowedList, required: requireKeys };
3952
4746
  }
3953
- var filesFactoryKeys = defineRule33({
4747
+ var filesFactoryKeys = defineRule39({
3954
4748
  meta: {
3955
4749
  type: "problem",
3956
4750
  schema: [
@@ -4068,8 +4862,8 @@ var filesFactoryKeys = defineRule33({
4068
4862
  });
4069
4863
 
4070
4864
  // src/rules/files/files-helpers.ts
4071
- import { defineRule as defineRule34 } from "@oxlint/plugins";
4072
- var filesHelpers = defineRule34({
4865
+ import { defineRule as defineRule40 } from "@oxlint/plugins";
4866
+ var filesHelpers = defineRule40({
4073
4867
  meta: {
4074
4868
  type: "problem",
4075
4869
  schema: [
@@ -4117,8 +4911,8 @@ var filesHelpers = defineRule34({
4117
4911
  });
4118
4912
 
4119
4913
  // src/rules/files/files-no-functions.ts
4120
- import { defineRule as defineRule35 } from "@oxlint/plugins";
4121
- var filesNoFunctions = defineRule35({
4914
+ import { defineRule as defineRule41 } from "@oxlint/plugins";
4915
+ var filesNoFunctions = defineRule41({
4122
4916
  meta: {
4123
4917
  type: "problem",
4124
4918
  schema: [
@@ -4172,8 +4966,8 @@ var filesNoFunctions = defineRule35({
4172
4966
  });
4173
4967
 
4174
4968
  // src/rules/files/files-restricted.ts
4175
- import { defineRule as defineRule36 } from "@oxlint/plugins";
4176
- var filesRestricted = defineRule36({
4969
+ import { defineRule as defineRule42 } from "@oxlint/plugins";
4970
+ var filesRestricted = defineRule42({
4177
4971
  meta: {
4178
4972
  type: "problem",
4179
4973
  schema: [
@@ -4205,7 +4999,7 @@ var filesRestricted = defineRule36({
4205
4999
  });
4206
5000
 
4207
5001
  // src/rules/files/files-route.ts
4208
- import { defineRule as defineRule37 } from "@oxlint/plugins";
5002
+ import { defineRule as defineRule43 } from "@oxlint/plugins";
4209
5003
  function inspectAdapter(context, item, bannedHooks, banIntrinsicJsx) {
4210
5004
  astVisit(item.node.body, [item.node], (node) => {
4211
5005
  if (banIntrinsicJsx && node.type === "JSXOpeningElement" && node.name.type === "JSXIdentifier" && /^[a-z]/.test(node.name.name)) {
@@ -4225,7 +5019,7 @@ function inspectAdapter(context, item, bannedHooks, banIntrinsicJsx) {
4225
5019
  }
4226
5020
  });
4227
5021
  }
4228
- var filesRoute = defineRule37({
5022
+ var filesRoute = defineRule43({
4229
5023
  meta: {
4230
5024
  type: "problem",
4231
5025
  schema: [
@@ -4295,7 +5089,7 @@ var filesRoute = defineRule37({
4295
5089
  });
4296
5090
 
4297
5091
  // src/rules/files/files-types-only.ts
4298
- import { defineRule as defineRule38 } from "@oxlint/plugins";
5092
+ import { defineRule as defineRule44 } from "@oxlint/plugins";
4299
5093
  function runtimeImportLocalNames(statement) {
4300
5094
  return statement.specifiers.flatMap((specifier) => specifier.type !== "ImportSpecifier" || specifier.importKind !== "type" ? [specifier.local.name] : []);
4301
5095
  }
@@ -4464,7 +5258,7 @@ function runtimeKind(node) {
4464
5258
  }
4465
5259
  return node.type === "FunctionDeclaration" ? "function" : "variable";
4466
5260
  }
4467
- var filesTypesOnly = defineRule38({
5261
+ var filesTypesOnly = defineRule44({
4468
5262
  meta: {
4469
5263
  type: "problem",
4470
5264
  schema: [
@@ -4572,8 +5366,8 @@ var filesTypesOnly = defineRule38({
4572
5366
  });
4573
5367
 
4574
5368
  // src/rules/layout/layout-arrow-body.ts
4575
- import { defineRule as defineRule39 } from "@oxlint/plugins";
4576
- var layoutArrowBody = defineRule39({
5369
+ import { defineRule as defineRule45 } from "@oxlint/plugins";
5370
+ var layoutArrowBody = defineRule45({
4577
5371
  meta: {
4578
5372
  type: "layout",
4579
5373
  fixable: "whitespace",
@@ -4625,12 +5419,12 @@ var layoutArrowBody = defineRule39({
4625
5419
  });
4626
5420
 
4627
5421
  // src/rules/layout/layout-call-arguments.ts
4628
- import { defineRule as defineRule40 } from "@oxlint/plugins";
5422
+ import { defineRule as defineRule46 } from "@oxlint/plugins";
4629
5423
  function layoutCallArgumentsHuggable(node) {
4630
5424
  return astIsInlineFunction(node) || node.type === "ObjectExpression" || node.type === "ArrayExpression" || astIsCall(node) && node.arguments.length === 1 && (node.callee.type !== "MemberExpression" || astIsOneLine(node.callee)) && node.arguments.every(layoutCallArgumentsHuggable);
4631
5425
  }
4632
5426
  var layoutCallArgumentsMax = { type: "integer", minimum: 1 };
4633
- var layoutCallArguments = defineRule40({
5427
+ var layoutCallArguments = defineRule46({
4634
5428
  meta: {
4635
5429
  type: "layout",
4636
5430
  fixable: "whitespace",
@@ -4812,12 +5606,12 @@ var layoutCallArguments = defineRule40({
4812
5606
  });
4813
5607
 
4814
5608
  // src/rules/layout/layout-call-array.ts
4815
- import { defineRule as defineRule41 } from "@oxlint/plugins";
5609
+ import { defineRule as defineRule47 } from "@oxlint/plugins";
4816
5610
  var layoutCallArrayDefaultCallees = ["Promise.all", "Promise.allSettled"];
4817
5611
  function layoutCallArrayRoot(node) {
4818
5612
  return node.type === "MemberExpression" ? layoutCallArrayRoot(node.object) : node;
4819
5613
  }
4820
- var layoutCallArray = defineRule41({
5614
+ var layoutCallArray = defineRule47({
4821
5615
  meta: {
4822
5616
  type: "layout",
4823
5617
  fixable: "whitespace",
@@ -4933,7 +5727,7 @@ var layoutCallArray = defineRule41({
4933
5727
  });
4934
5728
 
4935
5729
  // src/rules/layout/layout-call-depth.ts
4936
- import { defineRule as defineRule42 } from "@oxlint/plugins";
5730
+ import { defineRule as defineRule48 } from "@oxlint/plugins";
4937
5731
  function layoutCallDepthOuter(node) {
4938
5732
  const parent = node.parent;
4939
5733
  if (!parent || parent.type === "ArrowFunctionExpression" || parent.type === "FunctionExpression" || parent.type.endsWith("Statement")) {
@@ -4941,7 +5735,7 @@ function layoutCallDepthOuter(node) {
4941
5735
  }
4942
5736
  return astIsCall(parent) && parent.arguments.some((argument) => argument === node) ? parent : layoutCallDepthOuter(parent);
4943
5737
  }
4944
- var layoutCallDepth = defineRule42({
5738
+ var layoutCallDepth = defineRule48({
4945
5739
  meta: {
4946
5740
  type: "layout",
4947
5741
  schema: [
@@ -4980,8 +5774,8 @@ var layoutCallDepth = defineRule42({
4980
5774
  });
4981
5775
 
4982
5776
  // src/rules/layout/layout-chain.ts
4983
- import { defineRule as defineRule43 } from "@oxlint/plugins";
4984
- var layoutChain = defineRule43({
5777
+ import { defineRule as defineRule49 } from "@oxlint/plugins";
5778
+ var layoutChain = defineRule49({
4985
5779
  meta: {
4986
5780
  type: "layout",
4987
5781
  fixable: "whitespace",
@@ -5100,8 +5894,8 @@ var layoutChain = defineRule43({
5100
5894
  });
5101
5895
 
5102
5896
  // src/rules/layout/layout-items.ts
5103
- import { defineRule as defineRule44 } from "@oxlint/plugins";
5104
- var layoutItems = defineRule44({
5897
+ import { defineRule as defineRule50 } from "@oxlint/plugins";
5898
+ var layoutItems = defineRule50({
5105
5899
  meta: {
5106
5900
  type: "layout",
5107
5901
  fixable: "whitespace",
@@ -5156,8 +5950,8 @@ var layoutItems = defineRule44({
5156
5950
  });
5157
5951
 
5158
5952
  // src/rules/layout/layout-jsx-attributes.ts
5159
- import { defineRule as defineRule45 } from "@oxlint/plugins";
5160
- var layoutJsxAttributes = defineRule45({
5953
+ import { defineRule as defineRule51 } from "@oxlint/plugins";
5954
+ var layoutJsxAttributes = defineRule51({
5161
5955
  meta: {
5162
5956
  type: "layout",
5163
5957
  fixable: "whitespace",
@@ -5245,8 +6039,8 @@ var layoutJsxAttributes = defineRule45({
5245
6039
  });
5246
6040
 
5247
6041
  // src/rules/layout/layout-key-value.ts
5248
- import { defineRule as defineRule46 } from "@oxlint/plugins";
5249
- var layoutKeyValue = defineRule46({
6042
+ import { defineRule as defineRule52 } from "@oxlint/plugins";
6043
+ var layoutKeyValue = defineRule52({
5250
6044
  meta: {
5251
6045
  type: "layout",
5252
6046
  fixable: "whitespace",
@@ -5282,7 +6076,7 @@ var layoutKeyValue = defineRule46({
5282
6076
  });
5283
6077
 
5284
6078
  // src/rules/layout/layout-object.ts
5285
- import { defineRule as defineRule47 } from "@oxlint/plugins";
6079
+ import { defineRule as defineRule53 } from "@oxlint/plugins";
5286
6080
  function layoutObjectIsWhitespace(text) {
5287
6081
  return /^[\t\n\r\f ]*$/.test(text);
5288
6082
  }
@@ -5290,7 +6084,7 @@ function layoutObjectIsFlat(property) {
5290
6084
  const value = property.type === "SpreadElement" ? property.argument : property.value;
5291
6085
  return astIsOneLine(property) && (property.type === "SpreadElement" || !property.method && property.kind === "init") && value.type !== "ObjectExpression" && value.type !== "ArrayExpression" && !astIsInlineFunction(value);
5292
6086
  }
5293
- var layoutObject = defineRule47({
6087
+ var layoutObject = defineRule53({
5294
6088
  meta: {
5295
6089
  type: "layout",
5296
6090
  fixable: "whitespace",
@@ -5407,7 +6201,7 @@ var layoutObject = defineRule47({
5407
6201
  });
5408
6202
 
5409
6203
  // src/rules/layout/layout-padding.ts
5410
- import { defineRule as defineRule48 } from "@oxlint/plugins";
6204
+ import { defineRule as defineRule54 } from "@oxlint/plugins";
5411
6205
  var layoutPaddingControl = new Set([
5412
6206
  "IfStatement",
5413
6207
  "ForStatement",
@@ -5436,7 +6230,7 @@ function layoutPaddingKind(node) {
5436
6230
  function layoutPaddingIsMultilineVariable(node, kind) {
5437
6231
  return kind === "variable" && node.loc.start.line !== node.loc.end.line;
5438
6232
  }
5439
- var layoutPadding = defineRule48({
6233
+ var layoutPadding = defineRule54({
5440
6234
  meta: {
5441
6235
  type: "layout",
5442
6236
  fixable: "whitespace",
@@ -5525,8 +6319,8 @@ var layoutPadding = defineRule48({
5525
6319
  });
5526
6320
 
5527
6321
  // src/rules/layout/layout-pipe.ts
5528
- import { defineRule as defineRule49 } from "@oxlint/plugins";
5529
- var layoutPipe = defineRule49({
6322
+ import { defineRule as defineRule55 } from "@oxlint/plugins";
6323
+ var layoutPipe = defineRule55({
5530
6324
  meta: {
5531
6325
  type: "layout",
5532
6326
  messages: {
@@ -5557,7 +6351,7 @@ var layoutPipe = defineRule49({
5557
6351
  });
5558
6352
 
5559
6353
  // src/rules/layout/layout-ternary.ts
5560
- import { defineRule as defineRule50 } from "@oxlint/plugins";
6354
+ import { defineRule as defineRule56 } from "@oxlint/plugins";
5561
6355
  function layoutTernaryLines(node) {
5562
6356
  return node.loc.end.line - node.loc.start.line + 1;
5563
6357
  }
@@ -5565,7 +6359,7 @@ function layoutTernaryBreak(between, operator, newline) {
5565
6359
  const at = between.indexOf(operator);
5566
6360
  return `${between.slice(0, at).trimEnd()}${newline}${operator} ${between.slice(at + 1).trimStart()}`;
5567
6361
  }
5568
- var layoutTernary = defineRule50({
6362
+ var layoutTernary = defineRule56({
5569
6363
  meta: {
5570
6364
  type: "layout",
5571
6365
  fixable: "whitespace",
@@ -5681,9 +6475,9 @@ var layoutTernary = defineRule50({
5681
6475
  });
5682
6476
 
5683
6477
  // src/rules/naming/naming-declarations.ts
5684
- import { defineRule as defineRule51 } from "@oxlint/plugins";
6478
+ import { defineRule as defineRule57 } from "@oxlint/plugins";
5685
6479
  var namingDeclarationsKinds = ["function", "const", "let", "var", "class", "type", "interface", "enum"];
5686
- var namingDeclarations = defineRule51({
6480
+ var namingDeclarations = defineRule57({
5687
6481
  meta: {
5688
6482
  type: "problem",
5689
6483
  schema: [
@@ -5760,7 +6554,7 @@ var namingDeclarations = defineRule51({
5760
6554
  });
5761
6555
 
5762
6556
  // src/rules/naming/naming-exports.ts
5763
- import { defineRule as defineRule52 } from "@oxlint/plugins";
6557
+ import { defineRule as defineRule58 } from "@oxlint/plugins";
5764
6558
  var namingExportsScopeProperties = {
5765
6559
  scope: { type: "string", enum: ["exports", "functions", "declarations"] },
5766
6560
  types: { type: "boolean" },
@@ -5786,7 +6580,7 @@ function matchTemplate(basename3, template, placeholder = "[a-z0-9-]+") {
5786
6580
  }
5787
6581
  return new RegExp(`^${pattern}$`).exec(basename3)?.groups ?? null;
5788
6582
  }
5789
- var namingExports = defineRule52({
6583
+ var namingExports = defineRule58({
5790
6584
  meta: {
5791
6585
  type: "problem",
5792
6586
  schema: {
@@ -5923,10 +6717,10 @@ var namingExports = defineRule52({
5923
6717
  });
5924
6718
 
5925
6719
  // src/rules/naming/naming-file.ts
5926
- import { defineRule as defineRule53 } from "@oxlint/plugins";
6720
+ import { defineRule as defineRule59 } from "@oxlint/plugins";
5927
6721
  import { readdirSync as readdirSync2 } from "node:fs";
5928
6722
  import { dirname as dirname4 } from "node:path";
5929
- var namingFile = defineRule53({
6723
+ var namingFile = defineRule59({
5930
6724
  meta: {
5931
6725
  type: "problem",
5932
6726
  schema: {
@@ -5996,9 +6790,9 @@ var namingFile = defineRule53({
5996
6790
  });
5997
6791
 
5998
6792
  // src/rules/naming/naming-folder.ts
5999
- import { defineRule as defineRule54 } from "@oxlint/plugins";
6000
- import { posix as posix2 } from "node:path";
6001
- var namingFolder = defineRule54({
6793
+ import { defineRule as defineRule60 } from "@oxlint/plugins";
6794
+ import { posix as posix3 } from "node:path";
6795
+ var namingFolder = defineRule60({
6002
6796
  meta: {
6003
6797
  type: "problem",
6004
6798
  schema: [
@@ -6021,7 +6815,7 @@ var namingFolder = defineRule54({
6021
6815
  return {
6022
6816
  Program(program) {
6023
6817
  const { singularize = "trailing-s", separators = ["-"], after, prefixes: stripPrefixes = [] } = optionsFirst(context, {});
6024
- const all = posix2.dirname(context.filename.replaceAll("\\", "/")).split("/").filter((part) => part && part !== ".");
6818
+ const all = posix3.dirname(context.filename.replaceAll("\\", "/")).split("/").filter((part) => part && part !== ".");
6025
6819
  const parent = [all.at(-1) ?? ""];
6026
6820
  let folders = after ? undefined : parent;
6027
6821
  const marker = (after ?? "").split("/").filter(Boolean);
@@ -6072,9 +6866,9 @@ var reactDynamicClasses = astSelectorRule(':matches(JSXAttribute[name.name="clas
6072
6866
  var reactEmptyEffect = astSelectorRule(':matches(CallExpression[callee.name=/^use(Layout)?Effect$/], CallExpression[callee.object.name="React"][callee.property.name=/^use(Layout)?Effect$/]) > :matches(ArrowFunctionExpression, FunctionExpression) > BlockStatement[body.length=0]', "Remove the empty effect.");
6073
6867
 
6074
6868
  // src/rules/react/react-memo.ts
6075
- import { defineRule as defineRule55 } from "@oxlint/plugins";
6869
+ import { defineRule as defineRule61 } from "@oxlint/plugins";
6076
6870
  var reactMemoNames = ["memo", "useMemo", "useCallback"];
6077
- var reactMemo = defineRule55({
6871
+ var reactMemo = defineRule61({
6078
6872
  meta: {
6079
6873
  type: "problem",
6080
6874
  schema: [],
@@ -6109,7 +6903,7 @@ var reactMemo = defineRule55({
6109
6903
  });
6110
6904
 
6111
6905
  // src/rules/react/react-props.ts
6112
- import { defineRule as defineRule56 } from "@oxlint/plugins";
6906
+ import { defineRule as defineRule62 } from "@oxlint/plugins";
6113
6907
  function reactPropsTarget(node) {
6114
6908
  if (node?.type === "FunctionDeclaration" || node?.type === "FunctionExpression" || node?.type === "ArrowFunctionExpression") {
6115
6909
  return { fn: node };
@@ -6136,7 +6930,7 @@ function reactIsPassThrough(type) {
6136
6930
  const argument = type.typeArguments?.params.length === 1 ? type.typeArguments.params[0] : undefined;
6137
6931
  return name.type === "Identifier" && name.name === "ComponentProps" && (argument?.type === "TSTypeQuery" || argument?.type === "TSLiteralType" && argument.literal.type === "Literal" && typeof argument.literal.value === "string");
6138
6932
  }
6139
- var reactProps = defineRule56({
6933
+ var reactProps = defineRule62({
6140
6934
  meta: {
6141
6935
  type: "problem",
6142
6936
  schema: [
@@ -6234,8 +7028,90 @@ var reactProps = defineRule56({
6234
7028
  }
6235
7029
  });
6236
7030
 
7031
+ // src/rules/react/react-query-props.ts
7032
+ import { defineRule as defineRule63 } from "@oxlint/plugins";
7033
+ var reactQueryProps = defineRule63({
7034
+ meta: {
7035
+ type: "problem",
7036
+ schema: [
7037
+ {
7038
+ type: "object",
7039
+ additionalProperties: false,
7040
+ properties: {
7041
+ hooks: { type: "array", items: { type: "string" } },
7042
+ functions: { type: "array", items: { type: "string" } }
7043
+ }
7044
+ }
7045
+ ],
7046
+ messages: {
7047
+ options: "Build the {{hook}} options here from props; ready-made options hide where the data comes from.",
7048
+ source: "{{key}} must be a prop or call one; pass the data function in from the app."
7049
+ }
7050
+ },
7051
+ createOnce(context) {
7052
+ return {
7053
+ CallExpression(node) {
7054
+ const { hooks = ["useQuery", "useMutation", "useInfiniteQuery", "useSuspenseQuery"], functions = ["queryFn", "mutationFn"] } = optionsFirst(context, {});
7055
+ const hook = astCallName(node);
7056
+ let owner = node.parent;
7057
+ while (owner && owner.type !== "FunctionDeclaration" && !astIsInlineFunction(owner)) {
7058
+ owner = owner.parent;
7059
+ }
7060
+ if (!hook || !hooks.includes(hook) || !owner || owner.type !== "FunctionDeclaration" && !astIsInlineFunction(owner)) {
7061
+ return;
7062
+ }
7063
+ const component = owner;
7064
+ const first = component.params.at(0);
7065
+ const parameter = first?.type === "AssignmentPattern" ? first.left : first;
7066
+ const definition = (identifier) => boundariesDeclaredVariable(context.sourceCode.getScope(identifier), identifier.name)?.defs.at(0);
7067
+ const isProps = (value) => {
7068
+ const found = value?.type === "Identifier" ? definition(value) : undefined;
7069
+ return found?.type === "Parameter" && found.node === component && parameter?.type === "Identifier";
7070
+ };
7071
+ const isProp = (value) => {
7072
+ if (value.type === "MemberExpression") {
7073
+ return isProps(value.object);
7074
+ }
7075
+ const found = value.type === "Identifier" ? definition(value) : undefined;
7076
+ if (found?.type === "Parameter") {
7077
+ return found.node === component && parameter?.type === "ObjectPattern";
7078
+ }
7079
+ return found?.type === "Variable" && found.node.type === "VariableDeclarator" && found.node.id.type === "ObjectPattern" && isProps(found.node.init);
7080
+ };
7081
+ const options = node.arguments.at(0);
7082
+ if (options?.type !== "ObjectExpression" || options.properties.some((property) => property.type === "SpreadElement")) {
7083
+ context.report({
7084
+ node: options ?? node,
7085
+ messageId: "options",
7086
+ data: { hook }
7087
+ });
7088
+ return;
7089
+ }
7090
+ for (const property of options.properties) {
7091
+ if (property.type !== "Property") {
7092
+ continue;
7093
+ }
7094
+ const key = property.key.type === "Identifier" && !property.computed ? property.key.name : astStaticString(property.key);
7095
+ if (key === undefined || !functions.includes(key)) {
7096
+ continue;
7097
+ }
7098
+ const { value } = property;
7099
+ const calls = astIsInlineFunction(value) && astSome(value.body, (child) => child.type === "CallExpression" && isProp(child.callee));
7100
+ if (!isProp(value) && !calls) {
7101
+ context.report({
7102
+ node: value,
7103
+ messageId: "source",
7104
+ data: { key }
7105
+ });
7106
+ }
7107
+ }
7108
+ }
7109
+ };
7110
+ }
7111
+ });
7112
+
6237
7113
  // src/rules/schemas/schemas-inline.ts
6238
- import { defineRule as defineRule57 } from "@oxlint/plugins";
7114
+ import { defineRule as defineRule64 } from "@oxlint/plugins";
6239
7115
 
6240
7116
  // src/utils/schemas.utils.ts
6241
7117
  var schemasElementOptionsSchema = {
@@ -6271,7 +7147,7 @@ function schemasIsAllowedElement(node, allowScalars, namespaces, structuralMetho
6271
7147
  }
6272
7148
 
6273
7149
  // src/rules/schemas/schemas-inline.ts
6274
- var schemasInline = defineRule57({
7150
+ var schemasInline = defineRule64({
6275
7151
  meta: {
6276
7152
  type: "problem",
6277
7153
  schema: [schemasElementOptionsSchema],
@@ -6320,11 +7196,11 @@ var schemasInline = defineRule57({
6320
7196
  });
6321
7197
 
6322
7198
  // src/rules/schemas/schemas-local.ts
6323
- import { defineRule as defineRule58 } from "@oxlint/plugins";
7199
+ import { defineRule as defineRule65 } from "@oxlint/plugins";
6324
7200
  var schemasLocalImportMessage = "Do not import runtime schema constructors from {{source}}; import a named schema instead.";
6325
7201
  var schemasLocalConstructionMessage = "Do not construct schemas locally; import a named schema instead.";
6326
7202
  var schemasLocalCalls = ["config", "prettifyError", "treeifyError", "flattenError", "formatError"];
6327
- var schemasLocal = defineRule58({
7203
+ var schemasLocal = defineRule65({
6328
7204
  meta: {
6329
7205
  type: "problem",
6330
7206
  schema: [
@@ -6337,6 +7213,8 @@ var schemasLocal = defineRule58({
6337
7213
  calls: { type: "array", items: { type: "string" } },
6338
7214
  allow: { type: "array", items: { type: "string" } },
6339
7215
  patterns: { type: "array", items: { type: "string" } },
7216
+ imports: { type: "boolean" },
7217
+ values: { type: "boolean" },
6340
7218
  message: { type: "string" },
6341
7219
  importMessage: { type: "string" },
6342
7220
  constructionMessage: { type: "string" }
@@ -6356,6 +7234,32 @@ var schemasLocal = defineRule58({
6356
7234
  let pending = [];
6357
7235
  let importMessage = schemasLocalImportMessage;
6358
7236
  let constructionMessage = schemasLocalConstructionMessage;
7237
+ let imports = true;
7238
+ let values = false;
7239
+ let inline = new Set;
7240
+ function isInline(node) {
7241
+ if (inline.size === 0) {
7242
+ return false;
7243
+ }
7244
+ for (let parent = node.parent;parent; parent = parent.parent) {
7245
+ if (inline.has(parent)) {
7246
+ return true;
7247
+ }
7248
+ }
7249
+ return false;
7250
+ }
7251
+ function readsLocal(node) {
7252
+ const scope = context.sourceCode.getScope(node);
7253
+ return astSome(node, (child) => {
7254
+ if (child.type !== "Identifier") {
7255
+ return false;
7256
+ }
7257
+ const parent = child.parent;
7258
+ const named = parent.type === "MemberExpression" && parent.property === child && !parent.computed || parent.type === "Property" && parent.key === child && !parent.computed && !parent.shorthand;
7259
+ const type = named ? undefined : boundariesDeclaredVariable(scope, child.name)?.scope.type;
7260
+ return type !== undefined && type !== "module" && type !== "global";
7261
+ });
7262
+ }
6359
7263
  function isAllowedCall(node, member) {
6360
7264
  return member?.type === "MemberExpression" && member.object === node && !member.computed && member.property.type === "Identifier" && calls.has(member.property.name) && member.parent.type === "CallExpression" && member.parent.callee === member;
6361
7265
  }
@@ -6363,6 +7267,9 @@ var schemasLocal = defineRule58({
6363
7267
  return packages.has(source) || [...packages].some((name) => source.startsWith(`${name}/`));
6364
7268
  }
6365
7269
  function reportRuntimeImport(node, source) {
7270
+ if (!imports) {
7271
+ return;
7272
+ }
6366
7273
  context.report({
6367
7274
  node,
6368
7275
  messageId: "runtimeImport",
@@ -6387,6 +7294,9 @@ var schemasLocal = defineRule58({
6387
7294
  pending = [];
6388
7295
  importMessage = options.importMessage ?? options.message ?? schemasLocalImportMessage;
6389
7296
  constructionMessage = options.constructionMessage ?? options.message ?? schemasLocalConstructionMessage;
7297
+ imports = options.imports ?? true;
7298
+ values = options.values ?? false;
7299
+ inline = new Set;
6390
7300
  if ((options.allow ?? []).some((suffix) => filename.endsWith(suffix)) || (options.patterns ?? []).some((pattern) => new RegExp(pattern).test(filename))) {
6391
7301
  return false;
6392
7302
  }
@@ -6409,7 +7319,7 @@ var schemasLocal = defineRule58({
6409
7319
  pending.push({ node, locals });
6410
7320
  },
6411
7321
  "Program:exit"() {
6412
- const runtime = pending.filter(({ node, locals }) => context.sourceCode.getDeclaredVariables(node).filter((variable) => locals.includes(variable.name)).some((variable) => variable.references.length === 0 || variable.references.some((reference) => !isAllowedCall(reference.identifier, reference.identifier.parent))));
7322
+ const runtime = pending.filter(({ node, locals }) => context.sourceCode.getDeclaredVariables(node).filter((variable) => locals.includes(variable.name)).some((variable) => variable.references.length === 0 || variable.references.some((reference) => !isAllowedCall(reference.identifier, reference.identifier.parent) && !isInline(reference.identifier))));
6413
7323
  for (const { node } of runtime) {
6414
7324
  reportRuntimeImport(node, node.source.value);
6415
7325
  }
@@ -6424,6 +7334,10 @@ var schemasLocal = defineRule58({
6424
7334
  if (root.type !== "Identifier" || !constructors.has(root.name) || node.callee.type === "MemberExpression" && node.callee.object === root && isAllowedCall(root, root.parent)) {
6425
7335
  return;
6426
7336
  }
7337
+ if (values && (isInline(node) || readsLocal(node))) {
7338
+ inline.add(node);
7339
+ return;
7340
+ }
6427
7341
  context.report({
6428
7342
  node,
6429
7343
  messageId: "construction",
@@ -6435,8 +7349,8 @@ var schemasLocal = defineRule58({
6435
7349
  });
6436
7350
 
6437
7351
  // src/rules/schemas/schemas-rederive.ts
6438
- import { defineRule as defineRule59 } from "@oxlint/plugins";
6439
- var schemasRederive = defineRule59({
7352
+ import { defineRule as defineRule66 } from "@oxlint/plugins";
7353
+ var schemasRederive = defineRule66({
6440
7354
  meta: {
6441
7355
  type: "problem",
6442
7356
  schema: [
@@ -6482,7 +7396,7 @@ var schemasRederive = defineRule59({
6482
7396
  });
6483
7397
 
6484
7398
  // src/rules/schemas/schemas-single-use.ts
6485
- import { defineRule as defineRule60 } from "@oxlint/plugins";
7399
+ import { defineRule as defineRule67 } from "@oxlint/plugins";
6486
7400
  function isScalarSchema(node, namespaces, structuralMethods) {
6487
7401
  if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
6488
7402
  return false;
@@ -6496,7 +7410,7 @@ function isScalarSchema(node, namespaces, structuralMethods) {
6496
7410
  }
6497
7411
  return node.callee.object.type === "CallExpression" && isScalarSchema(node.callee.object, namespaces, structuralMethods);
6498
7412
  }
6499
- var schemasSingleUse = defineRule60({
7413
+ var schemasSingleUse = defineRule67({
6500
7414
  meta: {
6501
7415
  type: "suggestion",
6502
7416
  schema: [schemasElementOptionsSchema],
@@ -6572,7 +7486,7 @@ var schemasSingleUse = defineRule60({
6572
7486
  });
6573
7487
 
6574
7488
  // src/rules/tests/tests-assertions.ts
6575
- import { defineRule as defineRule61 } from "@oxlint/plugins";
7489
+ import { defineRule as defineRule68 } from "@oxlint/plugins";
6576
7490
  var testsAssertionsModifiers = new Set(["for", "each", "concurrent", "sequential", "only", "skip"]);
6577
7491
  var testsAssertionsFunctions = [
6578
7492
  "expect",
@@ -6762,7 +7676,7 @@ function isAssertedTest(registration, sourceCode, state) {
6762
7676
  }
6763
7677
  return rows.length > 0 && rows.every((row) => hasAssertion(callback, row.bindings, new Set, state));
6764
7678
  }
6765
- var testsAssertions = defineRule61({
7679
+ var testsAssertions = defineRule68({
6766
7680
  meta: {
6767
7681
  type: "problem",
6768
7682
  schema: [
@@ -6888,8 +7802,8 @@ var testsAssertions = defineRule61({
6888
7802
  });
6889
7803
 
6890
7804
  // src/rules/tests/tests-concurrent-db.ts
6891
- import { defineRule as defineRule62 } from "@oxlint/plugins";
6892
- var testsConcurrentDb = defineRule62({
7805
+ import { defineRule as defineRule69 } from "@oxlint/plugins";
7806
+ var testsConcurrentDb = defineRule69({
6893
7807
  meta: {
6894
7808
  type: "problem",
6895
7809
  schema: [
@@ -6945,7 +7859,7 @@ var testsConcurrentDb = defineRule62({
6945
7859
  });
6946
7860
 
6947
7861
  // src/rules/tests/tests-modifiers.ts
6948
- import { defineRule as defineRule63 } from "@oxlint/plugins";
7862
+ import { defineRule as defineRule70 } from "@oxlint/plugins";
6949
7863
  var testsModifiersNames = ["test", "it", "describe", "suite"];
6950
7864
  var testsModifiersPackages = new Set(["vitest", "bun:test", "@playwright/test", "@jest/globals"]);
6951
7865
  var testsModifiersBanned = ["only", "skip", "todo"];
@@ -6964,7 +7878,7 @@ function modifierName(node) {
6964
7878
  const property = astMemberName(node);
6965
7879
  return object && property ? `${object}.${property}` : undefined;
6966
7880
  }
6967
- var testsModifiers = defineRule63({
7881
+ var testsModifiers = defineRule70({
6968
7882
  meta: {
6969
7883
  type: "problem",
6970
7884
  schema: [
@@ -7018,9 +7932,9 @@ var testsModifiers = defineRule63({
7018
7932
  });
7019
7933
 
7020
7934
  // src/rules/tests/tests-titles.ts
7021
- import { defineRule as defineRule64 } from "@oxlint/plugins";
7935
+ import { defineRule as defineRule71 } from "@oxlint/plugins";
7022
7936
  var testsTitlesModifiers = ["only", "skip", "concurrent", "sequential", "todo", "each", "for"];
7023
- var testsTitles = defineRule64({
7937
+ var testsTitles = defineRule71({
7024
7938
  meta: {
7025
7939
  type: "suggestion",
7026
7940
  schema: [
@@ -7098,11 +8012,11 @@ var testsTitles = defineRule64({
7098
8012
  });
7099
8013
 
7100
8014
  // src/rules/types/types-declarations.ts
7101
- import { defineRule as defineRule65 } from "@oxlint/plugins";
8015
+ import { defineRule as defineRule72 } from "@oxlint/plugins";
7102
8016
  function isValuePattern(source) {
7103
8017
  return /^\^?=/.test(source);
7104
8018
  }
7105
- var typesDeclarations = defineRule65({
8019
+ var typesDeclarations = defineRule72({
7106
8020
  meta: {
7107
8021
  type: "problem",
7108
8022
  schema: [
@@ -7110,7 +8024,8 @@ var typesDeclarations = defineRule65({
7110
8024
  type: "object",
7111
8025
  additionalProperties: false,
7112
8026
  properties: {
7113
- allow: optionsPatternSchema
8027
+ allow: optionsPatternSchema,
8028
+ modules: { type: "boolean" }
7114
8029
  }
7115
8030
  }
7116
8031
  ],
@@ -7121,8 +8036,14 @@ var typesDeclarations = defineRule65({
7121
8036
  createOnce(context) {
7122
8037
  let allowedNames = [];
7123
8038
  let allowedValues = [];
8039
+ let allowModules = false;
7124
8040
  function check(node) {
7125
- if (optionsPatternsTest(allowedNames, node.id.name) || node.type === "TSTypeAliasDeclaration" && allowedValues.length > 0 && optionsPatternsTest(allowedValues, `= ${context.sourceCode.getText(node.typeAnnotation)}`)) {
8041
+ let ancestor = node.parent;
8042
+ while (allowModules && ancestor && ancestor.type !== "TSModuleDeclaration") {
8043
+ ancestor = ancestor.parent;
8044
+ }
8045
+ const ambient = ancestor?.type === "TSModuleDeclaration" && (ancestor.kind === "global" || ancestor.id.type === "Literal");
8046
+ if (allowModules && ambient || optionsPatternsTest(allowedNames, node.id.name) || node.type === "TSTypeAliasDeclaration" && allowedValues.length > 0 && optionsPatternsTest(allowedValues, `= ${context.sourceCode.getText(node.typeAnnotation)}`)) {
7126
8047
  return;
7127
8048
  }
7128
8049
  context.report({
@@ -7133,7 +8054,8 @@ var typesDeclarations = defineRule65({
7133
8054
  }
7134
8055
  return {
7135
8056
  before() {
7136
- const { allow: allowPattern } = optionsFirst(context, {});
8057
+ const { allow: allowPattern, modules = false } = optionsFirst(context, {});
8058
+ allowModules = modules;
7137
8059
  const sources = allowPattern === undefined ? [] : [allowPattern].flat();
7138
8060
  allowedNames = optionsPatterns(sources.filter((source) => !isValuePattern(source)));
7139
8061
  allowedValues = optionsPatterns(sources.filter(isValuePattern));
@@ -7144,8 +8066,73 @@ var typesDeclarations = defineRule65({
7144
8066
  }
7145
8067
  });
7146
8068
 
8069
+ // src/rules/types/types-duplicate.ts
8070
+ import { defineRule as defineRule73 } from "@oxlint/plugins";
8071
+ var typesDuplicate = defineRule73({
8072
+ meta: {
8073
+ type: "suggestion",
8074
+ schema: [
8075
+ {
8076
+ type: "object",
8077
+ additionalProperties: false,
8078
+ properties: {
8079
+ structs: { type: "array", items: { type: "string" } },
8080
+ min: { type: "integer", minimum: 1 }
8081
+ }
8082
+ }
8083
+ ],
8084
+ messages: { duplicate: "{{name}} has the same members as {{first}}; use {{first}}, or derive one from the other." }
8085
+ },
8086
+ createOnce(context) {
8087
+ let structs = new Set(["Schema.Struct"]);
8088
+ let min = 2;
8089
+ let seen = new Map;
8090
+ const check = (node, name, kind, members) => {
8091
+ if (members.length < min) {
8092
+ return;
8093
+ }
8094
+ const signature = members.map((item) => context.sourceCode.getText(item).replaceAll(/\breadonly\s+/g, "").replaceAll(/\s+/g, "").replace(/[;,]$/, "")).sort().join(";");
8095
+ const key = `${kind}:${signature}`;
8096
+ const first = seen.get(key);
8097
+ if (first === undefined) {
8098
+ seen.set(key, name);
8099
+ return;
8100
+ }
8101
+ context.report({
8102
+ node,
8103
+ messageId: "duplicate",
8104
+ data: { name, first }
8105
+ });
8106
+ };
8107
+ return {
8108
+ before() {
8109
+ const options = optionsFirst(context, {});
8110
+ structs = new Set(options.structs ?? ["Schema.Struct"]);
8111
+ min = options.min ?? 2;
8112
+ seen = new Map;
8113
+ },
8114
+ TSInterfaceDeclaration(node) {
8115
+ const heritage = node.extends.map((item) => context.sourceCode.getText(item)).join(",");
8116
+ check(node, node.id.name, `type:${heritage}`, node.body.body);
8117
+ },
8118
+ TSTypeAliasDeclaration(node) {
8119
+ if (node.typeAnnotation.type === "TSTypeLiteral") {
8120
+ check(node, node.id.name, "type:", node.typeAnnotation.members);
8121
+ }
8122
+ },
8123
+ VariableDeclarator(node) {
8124
+ const init = node.init ? astUnwrap(node.init) : undefined;
8125
+ const fields = init?.type === "CallExpression" && structs.has(astDottedName(init.callee) ?? "") ? init.arguments[0] : undefined;
8126
+ if (fields?.type === "ObjectExpression" && node.id.type === "Identifier") {
8127
+ check(node, node.id.name, "struct", fields.properties);
8128
+ }
8129
+ }
8130
+ };
8131
+ }
8132
+ });
8133
+
7147
8134
  // src/rules/types/types-imported-alias.ts
7148
- import { defineRule as defineRule66 } from "@oxlint/plugins";
8135
+ import { defineRule as defineRule74 } from "@oxlint/plugins";
7149
8136
  function importedAliasTarget(declaration, imports) {
7150
8137
  if (declaration?.type !== "TSTypeAliasDeclaration") {
7151
8138
  return;
@@ -7157,7 +8144,7 @@ function importedAliasTarget(declaration, imports) {
7157
8144
  const { name } = type.typeName;
7158
8145
  return imports.has(name) && !declaration.typeParameters?.params.some((parameter) => parameter.name.name === name) ? name : undefined;
7159
8146
  }
7160
- var typesImportedAlias = defineRule66({
8147
+ var typesImportedAlias = defineRule74({
7161
8148
  meta: {
7162
8149
  type: "problem",
7163
8150
  schema: [],
@@ -7219,7 +8206,7 @@ var typesImportedAlias = defineRule66({
7219
8206
  });
7220
8207
 
7221
8208
  // src/rules/types/types-inline.ts
7222
- import { defineRule as defineRule67 } from "@oxlint/plugins";
8209
+ import { defineRule as defineRule75 } from "@oxlint/plugins";
7223
8210
  function parameterAnnotation(parameter) {
7224
8211
  if (parameter.type === "TSParameterProperty") {
7225
8212
  return parameterAnnotation(parameter.parameter);
@@ -7229,7 +8216,7 @@ function parameterAnnotation(parameter) {
7229
8216
  }
7230
8217
  return parameter.typeAnnotation;
7231
8218
  }
7232
- var typesInline = defineRule67({
8219
+ var typesInline = defineRule75({
7233
8220
  meta: {
7234
8221
  type: "problem",
7235
8222
  schema: [
@@ -7240,6 +8227,9 @@ var typesInline = defineRule67({
7240
8227
  parameters: { type: "boolean" },
7241
8228
  returns: { type: "boolean" },
7242
8229
  functions: { type: "boolean" },
8230
+ variables: { type: "boolean" },
8231
+ assertions: { type: "boolean" },
8232
+ arguments: { type: "boolean" },
7243
8233
  min: { type: "integer", minimum: 1 }
7244
8234
  }
7245
8235
  }
@@ -7252,6 +8242,9 @@ var typesInline = defineRule67({
7252
8242
  let checkParameters = true;
7253
8243
  let checkReturns = true;
7254
8244
  let checkFunctionTypes = true;
8245
+ let checkVariables = false;
8246
+ let checkAssertions = false;
8247
+ let checkArguments = false;
7255
8248
  let minMembers = 1;
7256
8249
  function isInline(node) {
7257
8250
  if (node.type === "TSTypeLiteral") {
@@ -7287,11 +8280,44 @@ var typesInline = defineRule67({
7287
8280
  checkParameters = options.parameters ?? true;
7288
8281
  checkReturns = options.returns ?? true;
7289
8282
  checkFunctionTypes = options.functions ?? true;
8283
+ checkVariables = options.variables ?? false;
8284
+ checkAssertions = options.assertions ?? false;
8285
+ checkArguments = options.arguments ?? false;
7290
8286
  minMembers = options.min ?? 1;
7291
- if (!checkParameters && !checkReturns) {
8287
+ if (!checkParameters && !checkReturns && !checkVariables && !checkAssertions && !checkArguments) {
7292
8288
  return false;
7293
8289
  }
7294
8290
  },
8291
+ VariableDeclarator(node) {
8292
+ if (checkVariables) {
8293
+ check(parameterAnnotation(node.id), "variable");
8294
+ }
8295
+ },
8296
+ PropertyDefinition(node) {
8297
+ if (checkVariables) {
8298
+ check(node.typeAnnotation, "variable");
8299
+ }
8300
+ },
8301
+ TSAsExpression(node) {
8302
+ if (checkAssertions) {
8303
+ check(node.typeAnnotation, "assertion");
8304
+ }
8305
+ },
8306
+ TSSatisfiesExpression(node) {
8307
+ if (checkAssertions) {
8308
+ check(node.typeAnnotation, "assertion");
8309
+ }
8310
+ },
8311
+ CallExpression(node) {
8312
+ if (checkArguments) {
8313
+ check(node.typeArguments, "type argument");
8314
+ }
8315
+ },
8316
+ NewExpression(node) {
8317
+ if (checkArguments) {
8318
+ check(node.typeArguments, "type argument");
8319
+ }
8320
+ },
7295
8321
  FunctionDeclaration: checkFunction,
7296
8322
  FunctionExpression: checkFunction,
7297
8323
  ArrowFunctionExpression: checkFunction,
@@ -7305,8 +8331,8 @@ var typesInline = defineRule67({
7305
8331
  var typesLocalAlias = astSelectorRule("TSTypeAliasDeclaration", "Local component/hook contracts use interfaces named for their owner. Derived or reusable aliases belong in domain types; do not substitute an empty interface.");
7306
8332
 
7307
8333
  // src/rules/types/types-namespace-import.ts
7308
- import { defineRule as defineRule68 } from "@oxlint/plugins";
7309
- var typesNamespaceImport = defineRule68({
8334
+ import { defineRule as defineRule76 } from "@oxlint/plugins";
8335
+ var typesNamespaceImport = defineRule76({
7310
8336
  meta: {
7311
8337
  type: "suggestion",
7312
8338
  schema: [
@@ -7348,8 +8374,8 @@ var typesNamespaceImport = defineRule68({
7348
8374
  });
7349
8375
 
7350
8376
  // src/rules/types/types-service.ts
7351
- import { defineRule as defineRule69 } from "@oxlint/plugins";
7352
- var typesService = defineRule69({
8377
+ import { defineRule as defineRule77 } from "@oxlint/plugins";
8378
+ var typesService = defineRule77({
7353
8379
  meta: {
7354
8380
  type: "problem",
7355
8381
  schema: [
@@ -7399,12 +8425,12 @@ var typesService = defineRule69({
7399
8425
  });
7400
8426
 
7401
8427
  // src/rules/types/types-unknown.ts
7402
- import { defineRule as defineRule70 } from "@oxlint/plugins";
8428
+ import { defineRule as defineRule78 } from "@oxlint/plugins";
7403
8429
  function isCatchName(node) {
7404
8430
  const key = "key" in node && !(("computed" in node) && node.computed) ? node.key : undefined;
7405
8431
  return key?.type === "Identifier" && key.name === "catch";
7406
8432
  }
7407
- var typesUnknown = defineRule70({
8433
+ var typesUnknown = defineRule78({
7408
8434
  meta: { type: "problem", schema: [], messages: { forbidden: "Use an owned domain type rather than unknown." } },
7409
8435
  createOnce(context) {
7410
8436
  return {
@@ -7443,6 +8469,7 @@ var src_default = eslintCompatPlugin({
7443
8469
  "boundaries-token-owner": boundariesTokenOwner,
7444
8470
  "code-branch-once": codeBranchOnce,
7445
8471
  "code-constructor": codeConstructor,
8472
+ "code-delegate": codeDelegate,
7446
8473
  "code-double-negation": codeDoubleNegation,
7447
8474
  "code-function-nesting": codeFunctionNesting,
7448
8475
  "code-inline": codeInline,
@@ -7459,6 +8486,11 @@ var src_default = eslintCompatPlugin({
7459
8486
  "comments-code": commentsCode,
7460
8487
  "comments-directives": commentsDirectives,
7461
8488
  "copy-dashes": copyDashes,
8489
+ "effect-layer-file": effectLayerFile,
8490
+ "effect-layers": effectLayers,
8491
+ "effect-merge-pipe": effectMergePipe,
8492
+ "effect-schema-first": effectSchemaFirst,
8493
+ "effect-values-first": effectValuesFirst,
7462
8494
  "files-components-only": filesComponentsOnly,
7463
8495
  "files-constants-only": filesConstantsOnly,
7464
8496
  "files-domain-constants": filesDomainConstants,
@@ -7491,6 +8523,7 @@ var src_default = eslintCompatPlugin({
7491
8523
  "react-empty-effect": reactEmptyEffect,
7492
8524
  "react-memo": reactMemo,
7493
8525
  "react-props": reactProps,
8526
+ "react-query-props": reactQueryProps,
7494
8527
  "schemas-inline": schemasInline,
7495
8528
  "schemas-local": schemasLocal,
7496
8529
  "schemas-rederive": schemasRederive,
@@ -7500,6 +8533,7 @@ var src_default = eslintCompatPlugin({
7500
8533
  "tests-modifiers": testsModifiers,
7501
8534
  "tests-titles": testsTitles,
7502
8535
  "types-declarations": typesDeclarations,
8536
+ "types-duplicate": typesDuplicate,
7503
8537
  "types-imported-alias": typesImportedAlias,
7504
8538
  "types-inline": typesInline,
7505
8539
  "types-local-alias": typesLocalAlias,