@stll/oxlint-plugin 0.6.1 → 0.6.3

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.
Files changed (85) hide show
  1. package/dist/bun-test-hygiene.cjs +2 -2
  2. package/dist/bun-test-hygiene.cjs.map +1 -1
  3. package/dist/bun-test-hygiene.d.cts +3 -2
  4. package/dist/bun-test-hygiene.d.mts +3 -2
  5. package/dist/bun-test-hygiene.mjs +2 -2
  6. package/dist/bun-test-hygiene.mjs.map +1 -1
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/no-any-casts.cjs +2 -2
  10. package/dist/no-any-casts.cjs.map +1 -1
  11. package/dist/no-any-casts.d.cts +3 -2
  12. package/dist/no-any-casts.d.mts +3 -2
  13. package/dist/no-any-casts.mjs +2 -2
  14. package/dist/no-any-casts.mjs.map +1 -1
  15. package/dist/no-checkout-writes-in-tests.cjs +2 -2
  16. package/dist/no-checkout-writes-in-tests.cjs.map +1 -1
  17. package/dist/no-checkout-writes-in-tests.d.cts +3 -2
  18. package/dist/no-checkout-writes-in-tests.d.mts +3 -2
  19. package/dist/no-checkout-writes-in-tests.mjs +2 -2
  20. package/dist/no-checkout-writes-in-tests.mjs.map +1 -1
  21. package/dist/no-dangerous-type-assertions.cjs +2 -2
  22. package/dist/no-dangerous-type-assertions.cjs.map +1 -1
  23. package/dist/no-dangerous-type-assertions.d.cts +3 -2
  24. package/dist/no-dangerous-type-assertions.d.mts +3 -2
  25. package/dist/no-dangerous-type-assertions.mjs +2 -2
  26. package/dist/no-dangerous-type-assertions.mjs.map +1 -1
  27. package/dist/no-partial-record-satisfies.cjs +2 -2
  28. package/dist/no-partial-record-satisfies.cjs.map +1 -1
  29. package/dist/no-partial-record-satisfies.d.cts +3 -2
  30. package/dist/no-partial-record-satisfies.d.mts +3 -2
  31. package/dist/no-partial-record-satisfies.mjs +2 -2
  32. package/dist/no-partial-record-satisfies.mjs.map +1 -1
  33. package/dist/no-raw-route-query-client.cjs +2 -2
  34. package/dist/no-raw-route-query-client.cjs.map +1 -1
  35. package/dist/no-raw-route-query-client.d.cts +3 -2
  36. package/dist/no-raw-route-query-client.d.mts +3 -2
  37. package/dist/no-raw-route-query-client.mjs +2 -2
  38. package/dist/no-raw-route-query-client.mjs.map +1 -1
  39. package/dist/no-real-network-in-tests.cjs +2 -2
  40. package/dist/no-real-network-in-tests.cjs.map +1 -1
  41. package/dist/no-real-network-in-tests.d.cts +3 -2
  42. package/dist/no-real-network-in-tests.d.mts +3 -2
  43. package/dist/no-real-network-in-tests.mjs +2 -2
  44. package/dist/no-real-network-in-tests.mjs.map +1 -1
  45. package/dist/no-real-timer-waits-in-tests.cjs +2 -2
  46. package/dist/no-real-timer-waits-in-tests.cjs.map +1 -1
  47. package/dist/no-real-timer-waits-in-tests.d.cts +3 -2
  48. package/dist/no-real-timer-waits-in-tests.d.mts +3 -2
  49. package/dist/no-real-timer-waits-in-tests.mjs +2 -2
  50. package/dist/no-real-timer-waits-in-tests.mjs.map +1 -1
  51. package/dist/no-unsafe-inner-html.cjs +13 -3
  52. package/dist/no-unsafe-inner-html.cjs.map +1 -1
  53. package/dist/no-unsafe-inner-html.d.cts +3 -2
  54. package/dist/no-unsafe-inner-html.d.mts +3 -2
  55. package/dist/no-unsafe-inner-html.mjs +13 -3
  56. package/dist/no-unsafe-inner-html.mjs.map +1 -1
  57. package/dist/no-unseeded-random-in-tests.cjs +2 -2
  58. package/dist/no-unseeded-random-in-tests.cjs.map +1 -1
  59. package/dist/no-unseeded-random-in-tests.d.cts +3 -2
  60. package/dist/no-unseeded-random-in-tests.d.mts +3 -2
  61. package/dist/no-unseeded-random-in-tests.mjs +2 -2
  62. package/dist/no-unseeded-random-in-tests.mjs.map +1 -1
  63. package/dist/no-wall-clock-bound.cjs +2 -2
  64. package/dist/no-wall-clock-bound.cjs.map +1 -1
  65. package/dist/no-wall-clock-bound.d.cts +3 -2
  66. package/dist/no-wall-clock-bound.d.mts +3 -2
  67. package/dist/no-wall-clock-bound.mjs +2 -2
  68. package/dist/no-wall-clock-bound.mjs.map +1 -1
  69. package/dist/no-wall-clock-in-tests.cjs +2 -2
  70. package/dist/no-wall-clock-in-tests.cjs.map +1 -1
  71. package/dist/no-wall-clock-in-tests.d.cts +3 -2
  72. package/dist/no-wall-clock-in-tests.d.mts +3 -2
  73. package/dist/no-wall-clock-in-tests.mjs +2 -2
  74. package/dist/no-wall-clock-in-tests.mjs.map +1 -1
  75. package/dist/require-loader-prefetch.cjs +2 -2
  76. package/dist/require-loader-prefetch.cjs.map +1 -1
  77. package/dist/require-loader-prefetch.d.cts +3 -2
  78. package/dist/require-loader-prefetch.d.mts +3 -2
  79. package/dist/require-loader-prefetch.mjs +2 -2
  80. package/dist/require-loader-prefetch.mjs.map +1 -1
  81. package/dist/test-rule-utils.cjs.map +1 -1
  82. package/dist/test-rule-utils.mjs.map +1 -1
  83. package/dist/utils.cjs.map +1 -1
  84. package/dist/utils.mjs.map +1 -1
  85. package/package.json +3 -4
@@ -1,4 +1,5 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-checkout-writes-in-tests.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
- export = _default;
3
+ declare const plugin: Plugin;
4
+ export = plugin;
4
5
  //# sourceMappingURL=no-checkout-writes-in-tests.d.cts.map
@@ -1,5 +1,6 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-checkout-writes-in-tests.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
+ declare const plugin: Plugin;
3
4
  //#endregion
4
- export { _default as default };
5
+ export { plugin as default };
5
6
  //# sourceMappingURL=no-checkout-writes-in-tests.d.mts.map
@@ -41,7 +41,7 @@ const destinationWriteNames = /* @__PURE__ */ new Set([
41
41
  "symlink",
42
42
  "symlinkSync"
43
43
  ]);
44
- var no_checkout_writes_in_tests_default = testOnlyPlugin("no-checkout-writes-in-tests", "noCheckoutWrite", "Use @stll/test-kit's withTestTmpDir instead of writing inside the checkout.", (report, getScope) => {
44
+ const plugin = testOnlyPlugin("no-checkout-writes-in-tests", "noCheckoutWrite", "Use @stll/test-kit's withTestTmpDir instead of writing inside the checkout.", (report, getScope) => {
45
45
  const resolveBinding = createBindingResolver(getScope);
46
46
  const isGlobal = (value, name) => {
47
47
  if (!isNode(value)) return false;
@@ -106,6 +106,6 @@ var no_checkout_writes_in_tests_default = testOnlyPlugin("no-checkout-writes-in-
106
106
  } };
107
107
  });
108
108
  //#endregion
109
- export { no_checkout_writes_in_tests_default as default };
109
+ export { plugin as default };
110
110
 
111
111
  //# sourceMappingURL=no-checkout-writes-in-tests.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-checkout-writes-in-tests.mjs","names":[],"sources":["../src/no-checkout-writes-in-tests.ts"],"sourcesContent":["import {\n createBindingResolver,\n identifierName,\n isNode,\n propertyName,\n testOnlyPlugin,\n} from \"./test-rule-utils\";\n\nconst writeNames = new Set([\n \"appendFile\",\n \"appendFileSync\",\n \"copyFile\",\n \"copyFileSync\",\n \"createWriteStream\",\n \"cp\",\n \"cpSync\",\n \"mkdir\",\n \"mkdirSync\",\n \"rename\",\n \"renameSync\",\n \"rm\",\n \"rmSync\",\n \"rmdir\",\n \"rmdirSync\",\n \"symlink\",\n \"symlinkSync\",\n \"unlink\",\n \"unlinkSync\",\n \"writeFile\",\n \"writeFileSync\",\n]);\nconst fsModules = new Set([\"fs\", \"node:fs\", \"fs/promises\", \"node:fs/promises\"]);\nconst pathModules = new Set([\"path\", \"node:path\"]);\nconst absolutePath = /^(?:\\/|[A-Za-z]:[\\\\/]|\\\\\\\\)/u;\nconst destinationWriteNames = new Set([\n \"copyFile\",\n \"copyFileSync\",\n \"cp\",\n \"cpSync\",\n \"rename\",\n \"renameSync\",\n \"symlink\",\n \"symlinkSync\",\n]);\n\nexport default testOnlyPlugin(\n \"no-checkout-writes-in-tests\",\n \"noCheckoutWrite\",\n \"Use @stll/test-kit's withTestTmpDir instead of writing inside the checkout.\",\n (report, getScope) => {\n const resolveBinding = createBindingResolver(getScope);\n const isGlobal = (value: unknown, name: string): boolean => {\n if (!isNode(value)) return false;\n const binding = resolveBinding(value);\n return binding.type === \"global\" && binding.name === name;\n };\n const isPathBuilder = (value: unknown): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n return (\n binding.type === \"import\" &&\n pathModules.has(binding.module) &&\n (binding.imported === \"join\" || binding.imported === \"resolve\")\n );\n }\n if (value.type !== \"MemberExpression\") return false;\n const object = isNode(value.object) ? resolveBinding(value.object) : null;\n return (\n (propertyName(value.property) === \"join\" ||\n propertyName(value.property) === \"resolve\") &&\n object?.type === \"import\" &&\n pathModules.has(object.module) &&\n (object.imported === \"*\" || object.imported === \"default\")\n );\n };\n const rootedInCheckout = (value: unknown): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n if (isGlobal(value, \"__dirname\")) return true;\n const binding = resolveBinding(value);\n return (\n binding.type === \"const\" && rootedInCheckout(binding.initializer)\n );\n }\n if (value.type === \"Literal\" && typeof value.value === \"string\")\n return !absolutePath.test(value.value);\n if (\n value.type === \"TemplateLiteral\" &&\n Array.isArray(value.expressions) &&\n Array.isArray(value.quasis)\n ) {\n const quasi = value.quasis.at(0);\n const raw = isNode(quasi)\n ? Reflect.get(quasi.value ?? {}, \"raw\")\n : null;\n if (typeof raw !== \"string\") return false;\n if (raw.length > 0) return !absolutePath.test(raw);\n return rootedInCheckout(value.expressions.at(0));\n }\n if (\n value.type === \"MemberExpression\" &&\n isNode(value.object) &&\n value.object.type === \"MetaProperty\" &&\n identifierName(value.object.meta) === \"import\" &&\n identifierName(value.object.property) === \"meta\"\n )\n return (\n propertyName(value.property) === \"dir\" ||\n propertyName(value.property) === \"dirname\"\n );\n if (\n value.type === \"CallExpression\" &&\n isNode(value.callee) &&\n isGlobal(value.callee.object, \"process\") &&\n propertyName(value.callee.property) === \"cwd\"\n )\n return true;\n if (\n value.type === \"CallExpression\" &&\n isPathBuilder(value.callee) &&\n Array.isArray(value.arguments)\n )\n return rootedInCheckout(value.arguments.at(0));\n if (value.type === \"BinaryExpression\")\n return rootedInCheckout(value.left);\n return false;\n };\n const fsWriteName = (value: unknown): string | null => {\n if (!isNode(value)) return null;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n if (binding.type === \"const\") return fsWriteName(binding.initializer);\n return binding.type === \"import\" &&\n fsModules.has(binding.module) &&\n writeNames.has(binding.imported)\n ? binding.imported\n : null;\n }\n if (value.type !== \"MemberExpression\") return null;\n const method = propertyName(value.property);\n if (method === null || !writeNames.has(method)) return null;\n if (isNode(value.object)) {\n const object = resolveBinding(value.object);\n if (\n object.type === \"import\" &&\n fsModules.has(object.module) &&\n (object.imported === \"*\" ||\n object.imported === \"default\" ||\n object.imported === \"promises\")\n )\n return method;\n if (\n value.object.type === \"MemberExpression\" &&\n propertyName(value.object.property) === \"promises\" &&\n isNode(value.object.object)\n ) {\n const namespace = resolveBinding(value.object.object);\n return namespace.type === \"import\" &&\n fsModules.has(namespace.module) &&\n (namespace.imported === \"*\" || namespace.imported === \"default\")\n ? method\n : null;\n }\n }\n return null;\n };\n const bunWrite = (value: unknown): boolean =>\n isNode(value) &&\n value.type === \"MemberExpression\" &&\n propertyName(value.property) === \"write\" &&\n isGlobal(value.object, \"Bun\");\n return {\n CallExpression(node) {\n const writeName = fsWriteName(node.callee);\n if (\n (writeName !== null || bunWrite(node.callee)) &&\n Array.isArray(node.arguments) &&\n rootedInCheckout(\n node.arguments.at(\n writeName !== null && destinationWriteNames.has(writeName)\n ? 1\n : 0,\n ),\n )\n )\n report(node);\n },\n };\n },\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"no-checkout-writes-in-tests.mjs","names":[],"sources":["../src/no-checkout-writes-in-tests.ts"],"sourcesContent":["import type { Plugin } from \"@oxlint/plugins\";\n\nimport {\n createBindingResolver,\n identifierName,\n isNode,\n propertyName,\n testOnlyPlugin,\n} from \"./test-rule-utils\";\n\nconst writeNames = new Set([\n \"appendFile\",\n \"appendFileSync\",\n \"copyFile\",\n \"copyFileSync\",\n \"createWriteStream\",\n \"cp\",\n \"cpSync\",\n \"mkdir\",\n \"mkdirSync\",\n \"rename\",\n \"renameSync\",\n \"rm\",\n \"rmSync\",\n \"rmdir\",\n \"rmdirSync\",\n \"symlink\",\n \"symlinkSync\",\n \"unlink\",\n \"unlinkSync\",\n \"writeFile\",\n \"writeFileSync\",\n]);\nconst fsModules = new Set([\"fs\", \"node:fs\", \"fs/promises\", \"node:fs/promises\"]);\nconst pathModules = new Set([\"path\", \"node:path\"]);\nconst absolutePath = /^(?:\\/|[A-Za-z]:[\\\\/]|\\\\\\\\)/u;\nconst destinationWriteNames = new Set([\n \"copyFile\",\n \"copyFileSync\",\n \"cp\",\n \"cpSync\",\n \"rename\",\n \"renameSync\",\n \"symlink\",\n \"symlinkSync\",\n]);\n\nconst plugin: Plugin = testOnlyPlugin(\n \"no-checkout-writes-in-tests\",\n \"noCheckoutWrite\",\n \"Use @stll/test-kit's withTestTmpDir instead of writing inside the checkout.\",\n (report, getScope) => {\n const resolveBinding = createBindingResolver(getScope);\n const isGlobal = (value: unknown, name: string): boolean => {\n if (!isNode(value)) return false;\n const binding = resolveBinding(value);\n return binding.type === \"global\" && binding.name === name;\n };\n const isPathBuilder = (value: unknown): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n return (\n binding.type === \"import\" &&\n pathModules.has(binding.module) &&\n (binding.imported === \"join\" || binding.imported === \"resolve\")\n );\n }\n if (value.type !== \"MemberExpression\") return false;\n const object = isNode(value.object) ? resolveBinding(value.object) : null;\n return (\n (propertyName(value.property) === \"join\" ||\n propertyName(value.property) === \"resolve\") &&\n object?.type === \"import\" &&\n pathModules.has(object.module) &&\n (object.imported === \"*\" || object.imported === \"default\")\n );\n };\n const rootedInCheckout = (value: unknown): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n if (isGlobal(value, \"__dirname\")) return true;\n const binding = resolveBinding(value);\n return (\n binding.type === \"const\" && rootedInCheckout(binding.initializer)\n );\n }\n if (value.type === \"Literal\" && typeof value.value === \"string\")\n return !absolutePath.test(value.value);\n if (\n value.type === \"TemplateLiteral\" &&\n Array.isArray(value.expressions) &&\n Array.isArray(value.quasis)\n ) {\n const quasi = value.quasis.at(0);\n const raw = isNode(quasi)\n ? Reflect.get(quasi.value ?? {}, \"raw\")\n : null;\n if (typeof raw !== \"string\") return false;\n if (raw.length > 0) return !absolutePath.test(raw);\n return rootedInCheckout(value.expressions.at(0));\n }\n if (\n value.type === \"MemberExpression\" &&\n isNode(value.object) &&\n value.object.type === \"MetaProperty\" &&\n identifierName(value.object.meta) === \"import\" &&\n identifierName(value.object.property) === \"meta\"\n )\n return (\n propertyName(value.property) === \"dir\" ||\n propertyName(value.property) === \"dirname\"\n );\n if (\n value.type === \"CallExpression\" &&\n isNode(value.callee) &&\n isGlobal(value.callee.object, \"process\") &&\n propertyName(value.callee.property) === \"cwd\"\n )\n return true;\n if (\n value.type === \"CallExpression\" &&\n isPathBuilder(value.callee) &&\n Array.isArray(value.arguments)\n )\n return rootedInCheckout(value.arguments.at(0));\n if (value.type === \"BinaryExpression\")\n return rootedInCheckout(value.left);\n return false;\n };\n const fsWriteName = (value: unknown): string | null => {\n if (!isNode(value)) return null;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n if (binding.type === \"const\") return fsWriteName(binding.initializer);\n return binding.type === \"import\" &&\n fsModules.has(binding.module) &&\n writeNames.has(binding.imported)\n ? binding.imported\n : null;\n }\n if (value.type !== \"MemberExpression\") return null;\n const method = propertyName(value.property);\n if (method === null || !writeNames.has(method)) return null;\n if (isNode(value.object)) {\n const object = resolveBinding(value.object);\n if (\n object.type === \"import\" &&\n fsModules.has(object.module) &&\n (object.imported === \"*\" ||\n object.imported === \"default\" ||\n object.imported === \"promises\")\n )\n return method;\n if (\n value.object.type === \"MemberExpression\" &&\n propertyName(value.object.property) === \"promises\" &&\n isNode(value.object.object)\n ) {\n const namespace = resolveBinding(value.object.object);\n return namespace.type === \"import\" &&\n fsModules.has(namespace.module) &&\n (namespace.imported === \"*\" || namespace.imported === \"default\")\n ? method\n : null;\n }\n }\n return null;\n };\n const bunWrite = (value: unknown): boolean =>\n isNode(value) &&\n value.type === \"MemberExpression\" &&\n propertyName(value.property) === \"write\" &&\n isGlobal(value.object, \"Bun\");\n return {\n CallExpression(node) {\n const writeName = fsWriteName(node.callee);\n if (\n (writeName !== null || bunWrite(node.callee)) &&\n Array.isArray(node.arguments) &&\n rootedInCheckout(\n node.arguments.at(\n writeName !== null && destinationWriteNames.has(writeName)\n ? 1\n : 0,\n ),\n )\n )\n report(node);\n },\n };\n },\n);\n\nexport default plugin;\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  //#region src/no-dangerous-type-assertions.ts
6
- var no_dangerous_type_assertions_default = (0, require("@oxlint/plugins").eslintCompatPlugin)({
6
+ const plugin = (0, require("@oxlint/plugins").eslintCompatPlugin)({
7
7
  meta: { name: "no-dangerous-type-assertions" },
8
8
  rules: { "no-dangerous-type-assertions": {
9
9
  meta: {
@@ -35,6 +35,6 @@ var no_dangerous_type_assertions_default = (0, require("@oxlint/plugins").eslint
35
35
  } }
36
36
  });
37
37
  //#endregion
38
- exports.default = no_dangerous_type_assertions_default;
38
+ exports.default = plugin;
39
39
 
40
40
  //# sourceMappingURL=no-dangerous-type-assertions.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-dangerous-type-assertions.cjs","names":["eslintCompatPlugin"],"sources":["../src/no-dangerous-type-assertions.ts"],"sourcesContent":["// Disallow object-literal casts: `{...} as T`, `<T>{...}`.\n//\n// Casting an object literal to a type silently hides missing required\n// fields — the type system would otherwise catch the omission. Use a\n// typed binding (`const x: T = { ... }`) or `satisfies T` so missing\n// fields surface as type errors.\n//\n// Allows `as const` (legitimate widening control).\n// Allows `as any` / `as unknown` (those have dedicated rules / are\n// often used as the first step of an explicit launder).\n\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-dangerous-type-assertions\" },\n rules: {\n \"no-dangerous-type-assertions\": {\n meta: {\n type: \"problem\",\n messages: {\n noObjectLiteralCast:\n \"Don't cast an object literal with `as`. Type the binding \" +\n \"(`const x: T = { ... }`) or use `satisfies T` so missing \" +\n \"required fields fail typecheck.\",\n },\n },\n createOnce(context) {\n function unwrapExpression(expression) {\n let current = expression;\n while (\n current?.type === \"ParenthesizedExpression\" ||\n current?.type === \"TSAsExpression\" ||\n current?.type === \"TSTypeAssertion\" ||\n current?.type === \"TSNonNullExpression\" ||\n current?.type === \"TSSatisfiesExpression\"\n ) {\n current = current.expression;\n }\n return current;\n }\n\n function check(node) {\n const expression = unwrapExpression(node.expression);\n if (expression?.type !== \"ObjectExpression\") {\n return;\n }\n const ann = node.typeAnnotation;\n if (!ann) {\n return;\n }\n // Allow `as const`\n if (\n ann.type === \"TSTypeReference\" &&\n ann.typeName?.type === \"Identifier\" &&\n ann.typeName.name === \"const\"\n ) {\n return;\n }\n // `as any` and `as unknown` have their own rules / are\n // typically the laundering step, not the final shape claim.\n if (ann.type === \"TSAnyKeyword\" || ann.type === \"TSUnknownKeyword\") {\n return;\n }\n context.report({ node, messageId: \"noObjectLiteralCast\" });\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n"],"mappings":";;;;;AAaA,IAAA,wCAAA,4BAAeA,CAAAA,CAAAA,mBAAAA,CAAmB;CAChC,MAAM,EAAE,MAAM,+BAA+B;CAC7C,OAAO,EACL,gCAAgC;EAC9B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,qBACE,oJAGJ;EACF;EACA,WAAW,SAAS;GAClB,SAAS,iBAAiB,YAAY;IACpC,IAAI,UAAU;IACd,OACE,SAAS,SAAS,6BAClB,SAAS,SAAS,oBAClB,SAAS,SAAS,qBAClB,SAAS,SAAS,yBAClB,SAAS,SAAS,yBAElB,UAAU,QAAQ;IAEpB,OAAO;GACT;GAEA,SAAS,MAAM,MAAM;IAEnB,IADmB,iBAAiB,KAAK,UAC5B,CAAC,EAAE,SAAS,oBACvB;IAEF,MAAM,MAAM,KAAK;IACjB,IAAI,CAAC,KACH;IAGF,IACE,IAAI,SAAS,qBACb,IAAI,UAAU,SAAS,gBACvB,IAAI,SAAS,SAAS,SAEtB;IAIF,IAAI,IAAI,SAAS,kBAAkB,IAAI,SAAS,oBAC9C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAAsB,CAAC;GAC3D;GACA,OAAO;IACL,gBAAgB;IAChB,iBAAiB;GACnB;EACF;CACF,EACF;AACF,CAAC"}
1
+ {"version":3,"file":"no-dangerous-type-assertions.cjs","names":["eslintCompatPlugin"],"sources":["../src/no-dangerous-type-assertions.ts"],"sourcesContent":["// Disallow object-literal casts: `{...} as T`, `<T>{...}`.\n//\n// Casting an object literal to a type silently hides missing required\n// fields — the type system would otherwise catch the omission. Use a\n// typed binding (`const x: T = { ... }`) or `satisfies T` so missing\n// fields surface as type errors.\n//\n// Allows `as const` (legitimate widening control).\n// Allows `as any` / `as unknown` (those have dedicated rules / are\n// often used as the first step of an explicit launder).\n\nimport { eslintCompatPlugin, type Plugin } from \"@oxlint/plugins\";\n\nconst plugin: Plugin = eslintCompatPlugin({\n meta: { name: \"no-dangerous-type-assertions\" },\n rules: {\n \"no-dangerous-type-assertions\": {\n meta: {\n type: \"problem\",\n messages: {\n noObjectLiteralCast:\n \"Don't cast an object literal with `as`. Type the binding \" +\n \"(`const x: T = { ... }`) or use `satisfies T` so missing \" +\n \"required fields fail typecheck.\",\n },\n },\n createOnce(context) {\n function unwrapExpression(expression) {\n let current = expression;\n while (\n current?.type === \"ParenthesizedExpression\" ||\n current?.type === \"TSAsExpression\" ||\n current?.type === \"TSTypeAssertion\" ||\n current?.type === \"TSNonNullExpression\" ||\n current?.type === \"TSSatisfiesExpression\"\n ) {\n current = current.expression;\n }\n return current;\n }\n\n function check(node) {\n const expression = unwrapExpression(node.expression);\n if (expression?.type !== \"ObjectExpression\") {\n return;\n }\n const ann = node.typeAnnotation;\n if (!ann) {\n return;\n }\n // Allow `as const`\n if (\n ann.type === \"TSTypeReference\" &&\n ann.typeName?.type === \"Identifier\" &&\n ann.typeName.name === \"const\"\n ) {\n return;\n }\n // `as any` and `as unknown` have their own rules / are\n // typically the laundering step, not the final shape claim.\n if (ann.type === \"TSAnyKeyword\" || ann.type === \"TSUnknownKeyword\") {\n return;\n }\n context.report({ node, messageId: \"noObjectLiteralCast\" });\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n\nexport default plugin;\n"],"mappings":";;;;;AAaA,MAAM,UAAA,4BAAiBA,CAAAA,CAAAA,mBAAAA,CAAmB;CACxC,MAAM,EAAE,MAAM,+BAA+B;CAC7C,OAAO,EACL,gCAAgC;EAC9B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,qBACE,oJAGJ;EACF;EACA,WAAW,SAAS;GAClB,SAAS,iBAAiB,YAAY;IACpC,IAAI,UAAU;IACd,OACE,SAAS,SAAS,6BAClB,SAAS,SAAS,oBAClB,SAAS,SAAS,qBAClB,SAAS,SAAS,yBAClB,SAAS,SAAS,yBAElB,UAAU,QAAQ;IAEpB,OAAO;GACT;GAEA,SAAS,MAAM,MAAM;IAEnB,IADmB,iBAAiB,KAAK,UAC5B,CAAC,EAAE,SAAS,oBACvB;IAEF,MAAM,MAAM,KAAK;IACjB,IAAI,CAAC,KACH;IAGF,IACE,IAAI,SAAS,qBACb,IAAI,UAAU,SAAS,gBACvB,IAAI,SAAS,SAAS,SAEtB;IAIF,IAAI,IAAI,SAAS,kBAAkB,IAAI,SAAS,oBAC9C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAAsB,CAAC;GAC3D;GACA,OAAO;IACL,gBAAgB;IAChB,iBAAiB;GACnB;EACF;CACF,EACF;AACF,CAAC"}
@@ -1,4 +1,5 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-dangerous-type-assertions.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
- export = _default;
3
+ declare const plugin: Plugin;
4
+ export = plugin;
4
5
  //# sourceMappingURL=no-dangerous-type-assertions.d.cts.map
@@ -1,5 +1,6 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-dangerous-type-assertions.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
+ declare const plugin: Plugin;
3
4
  //#endregion
4
- export { _default as default };
5
+ export { plugin as default };
5
6
  //# sourceMappingURL=no-dangerous-type-assertions.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { eslintCompatPlugin } from "@oxlint/plugins";
2
2
  //#region src/no-dangerous-type-assertions.ts
3
- var no_dangerous_type_assertions_default = eslintCompatPlugin({
3
+ const plugin = eslintCompatPlugin({
4
4
  meta: { name: "no-dangerous-type-assertions" },
5
5
  rules: { "no-dangerous-type-assertions": {
6
6
  meta: {
@@ -32,6 +32,6 @@ var no_dangerous_type_assertions_default = eslintCompatPlugin({
32
32
  } }
33
33
  });
34
34
  //#endregion
35
- export { no_dangerous_type_assertions_default as default };
35
+ export { plugin as default };
36
36
 
37
37
  //# sourceMappingURL=no-dangerous-type-assertions.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-dangerous-type-assertions.mjs","names":[],"sources":["../src/no-dangerous-type-assertions.ts"],"sourcesContent":["// Disallow object-literal casts: `{...} as T`, `<T>{...}`.\n//\n// Casting an object literal to a type silently hides missing required\n// fields — the type system would otherwise catch the omission. Use a\n// typed binding (`const x: T = { ... }`) or `satisfies T` so missing\n// fields surface as type errors.\n//\n// Allows `as const` (legitimate widening control).\n// Allows `as any` / `as unknown` (those have dedicated rules / are\n// often used as the first step of an explicit launder).\n\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-dangerous-type-assertions\" },\n rules: {\n \"no-dangerous-type-assertions\": {\n meta: {\n type: \"problem\",\n messages: {\n noObjectLiteralCast:\n \"Don't cast an object literal with `as`. Type the binding \" +\n \"(`const x: T = { ... }`) or use `satisfies T` so missing \" +\n \"required fields fail typecheck.\",\n },\n },\n createOnce(context) {\n function unwrapExpression(expression) {\n let current = expression;\n while (\n current?.type === \"ParenthesizedExpression\" ||\n current?.type === \"TSAsExpression\" ||\n current?.type === \"TSTypeAssertion\" ||\n current?.type === \"TSNonNullExpression\" ||\n current?.type === \"TSSatisfiesExpression\"\n ) {\n current = current.expression;\n }\n return current;\n }\n\n function check(node) {\n const expression = unwrapExpression(node.expression);\n if (expression?.type !== \"ObjectExpression\") {\n return;\n }\n const ann = node.typeAnnotation;\n if (!ann) {\n return;\n }\n // Allow `as const`\n if (\n ann.type === \"TSTypeReference\" &&\n ann.typeName?.type === \"Identifier\" &&\n ann.typeName.name === \"const\"\n ) {\n return;\n }\n // `as any` and `as unknown` have their own rules / are\n // typically the laundering step, not the final shape claim.\n if (ann.type === \"TSAnyKeyword\" || ann.type === \"TSUnknownKeyword\") {\n return;\n }\n context.report({ node, messageId: \"noObjectLiteralCast\" });\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n"],"mappings":";;AAaA,IAAA,uCAAe,mBAAmB;CAChC,MAAM,EAAE,MAAM,+BAA+B;CAC7C,OAAO,EACL,gCAAgC;EAC9B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,qBACE,oJAGJ;EACF;EACA,WAAW,SAAS;GAClB,SAAS,iBAAiB,YAAY;IACpC,IAAI,UAAU;IACd,OACE,SAAS,SAAS,6BAClB,SAAS,SAAS,oBAClB,SAAS,SAAS,qBAClB,SAAS,SAAS,yBAClB,SAAS,SAAS,yBAElB,UAAU,QAAQ;IAEpB,OAAO;GACT;GAEA,SAAS,MAAM,MAAM;IAEnB,IADmB,iBAAiB,KAAK,UAC5B,CAAC,EAAE,SAAS,oBACvB;IAEF,MAAM,MAAM,KAAK;IACjB,IAAI,CAAC,KACH;IAGF,IACE,IAAI,SAAS,qBACb,IAAI,UAAU,SAAS,gBACvB,IAAI,SAAS,SAAS,SAEtB;IAIF,IAAI,IAAI,SAAS,kBAAkB,IAAI,SAAS,oBAC9C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAAsB,CAAC;GAC3D;GACA,OAAO;IACL,gBAAgB;IAChB,iBAAiB;GACnB;EACF;CACF,EACF;AACF,CAAC"}
1
+ {"version":3,"file":"no-dangerous-type-assertions.mjs","names":[],"sources":["../src/no-dangerous-type-assertions.ts"],"sourcesContent":["// Disallow object-literal casts: `{...} as T`, `<T>{...}`.\n//\n// Casting an object literal to a type silently hides missing required\n// fields — the type system would otherwise catch the omission. Use a\n// typed binding (`const x: T = { ... }`) or `satisfies T` so missing\n// fields surface as type errors.\n//\n// Allows `as const` (legitimate widening control).\n// Allows `as any` / `as unknown` (those have dedicated rules / are\n// often used as the first step of an explicit launder).\n\nimport { eslintCompatPlugin, type Plugin } from \"@oxlint/plugins\";\n\nconst plugin: Plugin = eslintCompatPlugin({\n meta: { name: \"no-dangerous-type-assertions\" },\n rules: {\n \"no-dangerous-type-assertions\": {\n meta: {\n type: \"problem\",\n messages: {\n noObjectLiteralCast:\n \"Don't cast an object literal with `as`. Type the binding \" +\n \"(`const x: T = { ... }`) or use `satisfies T` so missing \" +\n \"required fields fail typecheck.\",\n },\n },\n createOnce(context) {\n function unwrapExpression(expression) {\n let current = expression;\n while (\n current?.type === \"ParenthesizedExpression\" ||\n current?.type === \"TSAsExpression\" ||\n current?.type === \"TSTypeAssertion\" ||\n current?.type === \"TSNonNullExpression\" ||\n current?.type === \"TSSatisfiesExpression\"\n ) {\n current = current.expression;\n }\n return current;\n }\n\n function check(node) {\n const expression = unwrapExpression(node.expression);\n if (expression?.type !== \"ObjectExpression\") {\n return;\n }\n const ann = node.typeAnnotation;\n if (!ann) {\n return;\n }\n // Allow `as const`\n if (\n ann.type === \"TSTypeReference\" &&\n ann.typeName?.type === \"Identifier\" &&\n ann.typeName.name === \"const\"\n ) {\n return;\n }\n // `as any` and `as unknown` have their own rules / are\n // typically the laundering step, not the final shape claim.\n if (ann.type === \"TSAnyKeyword\" || ann.type === \"TSUnknownKeyword\") {\n return;\n }\n context.report({ node, messageId: \"noObjectLiteralCast\" });\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n\nexport default plugin;\n"],"mappings":";;AAaA,MAAM,SAAiB,mBAAmB;CACxC,MAAM,EAAE,MAAM,+BAA+B;CAC7C,OAAO,EACL,gCAAgC;EAC9B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,qBACE,oJAGJ;EACF;EACA,WAAW,SAAS;GAClB,SAAS,iBAAiB,YAAY;IACpC,IAAI,UAAU;IACd,OACE,SAAS,SAAS,6BAClB,SAAS,SAAS,oBAClB,SAAS,SAAS,qBAClB,SAAS,SAAS,yBAClB,SAAS,SAAS,yBAElB,UAAU,QAAQ;IAEpB,OAAO;GACT;GAEA,SAAS,MAAM,MAAM;IAEnB,IADmB,iBAAiB,KAAK,UAC5B,CAAC,EAAE,SAAS,oBACvB;IAEF,MAAM,MAAM,KAAK;IACjB,IAAI,CAAC,KACH;IAGF,IACE,IAAI,SAAS,qBACb,IAAI,UAAU,SAAS,gBACvB,IAAI,SAAS,SAAS,SAEtB;IAIF,IAAI,IAAI,SAAS,kBAAkB,IAAI,SAAS,oBAC9C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAAsB,CAAC;GAC3D;GACA,OAAO;IACL,gBAAgB;IAChB,iBAAiB;GACnB;EACF;CACF,EACF;AACF,CAAC"}
@@ -20,7 +20,7 @@ const isObjectLiteralExpression = (node) => {
20
20
  if (node?.type === "TSAsExpression" && node.typeAnnotation?.type === "TSTypeReference" && require_utils.isIdentifier(node.typeAnnotation.typeName, "const")) return isObjectLiteralExpression(node.expression);
21
21
  return false;
22
22
  };
23
- var no_partial_record_satisfies_default = (0, _oxlint_plugins.eslintCompatPlugin)({
23
+ const plugin = (0, _oxlint_plugins.eslintCompatPlugin)({
24
24
  meta: { name: "no-partial-record-satisfies" },
25
25
  rules: { "no-partial-record-satisfies": {
26
26
  meta: {
@@ -40,6 +40,6 @@ var no_partial_record_satisfies_default = (0, _oxlint_plugins.eslintCompatPlugin
40
40
  } }
41
41
  });
42
42
  //#endregion
43
- exports.default = no_partial_record_satisfies_default;
43
+ exports.default = plugin;
44
44
 
45
45
  //# sourceMappingURL=no-partial-record-satisfies.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-partial-record-satisfies.cjs","names":["isIdentifier","eslintCompatPlugin"],"sources":["../src/no-partial-record-satisfies.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\n// Ban `<object literal> satisfies Partial<Record<Union, T>>` (with or\n// without a leading `as const`).\n//\n// A const object literal pinned with `satisfies` against a `Record` keyed by\n// a union declares itself the classification of every union member —\n// `satisfies` fails typecheck the moment a new member is added and left\n// unhandled. Wrapping that `Record` in `Partial` cancels the guarantee: a\n// missing member typechecks silently, so the literal can drift out of sync\n// with the union with no compiler signal. That silent-drift surface can cause\n// a classification-table bug when a newly added member falls through a\n// `Partial<Record<...>>` lookup instead of failing typecheck.\n//\n// `Readonly<...>` wrapping either side (`Readonly<Partial<Record<...>>>` or\n// `Partial<Readonly<Record<...>>>`) is unwrapped before the check, since it\n// changes mutability, not totality.\n//\n// Only the `satisfies` expression on an object literal is flagged. Plain\n// type-annotation positions (`const x: Partial<Record<...>> = {...}`),\n// function parameters, and variable/return type annotations are exempt:\n// those are legitimate uses (override inputs, accumulators, genuinely sparse\n// data) and carry no compiler guarantee of totality to lose.\n//\n// Flagged:\n// const x = {\n// a: 1,\n// } as const satisfies Partial<Record<Union, number>>;\n// const y = { a: 1 } satisfies Partial<Record<Union, number>>;\n// const z = {\n// a: 1,\n// } as const satisfies Readonly<Partial<Record<Union, number>>>;\n//\n// Allowed:\n// const x = {\n// a: 1,\n// } as const satisfies Record<Union, number>;\n// const fn = (overrides: Partial<Record<Union, number>>) => overrides;\n// let acc: Partial<Record<Union, number>> = {};\n// type Sparse = Partial<Record<Union, number>>;\n//\n// Fix by making the record total over the union — an explicit \"none\" /\n// `false` / `null` value for members where nothing applies forces a new\n// union member to be classified before it typechecks. If only a subset of\n// the union is ever relevant, derive a narrower union for the keys instead\n// of falling back to `Partial`. For genuinely sparse data where absence has\n// documented meaning (e.g. an unbounded key space), add an eslint-disable\n// with a comment naming what absence means.\n\nimport { isIdentifier } from \"./utils.ts\";\n\nconst unwrapReadonlyType = (typeNode) => {\n if (\n typeNode?.type === \"TSTypeReference\" &&\n isIdentifier(typeNode.typeName, \"Readonly\")\n ) {\n return typeNode.typeArguments?.params?.at(0) ?? null;\n }\n return typeNode;\n};\n\n// Matches `Partial<Record<...>>`, unwrapping a `Readonly<...>` wrapper on\n// either the outer (`Readonly<Partial<Record<...>>>`) or inner\n// (`Partial<Readonly<Record<...>>>`) type.\nconst isPartialRecordType = (typeNode) => {\n const outer = unwrapReadonlyType(typeNode);\n if (\n outer?.type !== \"TSTypeReference\" ||\n !isIdentifier(outer.typeName, \"Partial\")\n ) {\n return false;\n }\n const inner = unwrapReadonlyType(outer.typeArguments?.params?.at(0) ?? null);\n return (\n inner?.type === \"TSTypeReference\" && isIdentifier(inner.typeName, \"Record\")\n );\n};\n\n// Matches an object literal, or an object literal wrapped in `as const`.\nconst isObjectLiteralExpression = (node) => {\n if (node?.type === \"ObjectExpression\") {\n return true;\n }\n if (\n node?.type === \"TSAsExpression\" &&\n node.typeAnnotation?.type === \"TSTypeReference\" &&\n isIdentifier(node.typeAnnotation.typeName, \"const\")\n ) {\n return isObjectLiteralExpression(node.expression);\n }\n return false;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-partial-record-satisfies\" },\n rules: {\n \"no-partial-record-satisfies\": {\n meta: {\n type: \"problem\",\n messages: {\n noPartialRecordSatisfies:\n \"Don't pin an object literal with `satisfies Partial<Record<...>>` \" +\n \"— Partial cancels the totality check satisfies would otherwise \" +\n \"give you, so a union member can silently fall through. Make the \" +\n \"record total (explicit 'none'/false/null per member), narrow the \" +\n \"key union, or add an eslint-disable naming what absence means \" +\n \"for genuinely sparse data.\",\n },\n },\n createOnce(context) {\n return {\n TSSatisfiesExpression(node) {\n if (!isPartialRecordType(node.typeAnnotation)) {\n return;\n }\n if (!isObjectLiteralExpression(node.expression)) {\n return;\n }\n context.report({ node, messageId: \"noPartialRecordSatisfies\" });\n },\n };\n },\n },\n },\n});\n"],"mappings":";;;;;;;AAkDA,MAAM,sBAAsB,aAAa;CACvC,IACE,UAAU,SAAS,qBACnBA,cAAAA,aAAa,SAAS,UAAU,UAAU,GAE1C,OAAO,SAAS,eAAe,QAAQ,GAAG,CAAC,KAAK;CAElD,OAAO;AACT;AAKA,MAAM,uBAAuB,aAAa;CACxC,MAAM,QAAQ,mBAAmB,QAAQ;CACzC,IACE,OAAO,SAAS,qBAChB,CAACA,cAAAA,aAAa,MAAM,UAAU,SAAS,GAEvC,OAAO;CAET,MAAM,QAAQ,mBAAmB,MAAM,eAAe,QAAQ,GAAG,CAAC,KAAK,IAAI;CAC3E,OACE,OAAO,SAAS,qBAAqBA,cAAAA,aAAa,MAAM,UAAU,QAAQ;AAE9E;AAGA,MAAM,6BAA6B,SAAS;CAC1C,IAAI,MAAM,SAAS,oBACjB,OAAO;CAET,IACE,MAAM,SAAS,oBACf,KAAK,gBAAgB,SAAS,qBAC9BA,cAAAA,aAAa,KAAK,eAAe,UAAU,OAAO,GAElD,OAAO,0BAA0B,KAAK,UAAU;CAElD,OAAO;AACT;AAEA,IAAA,uCAAA,GAAeC,gBAAAA,mBAAAA,CAAmB;CAChC,MAAM,EAAE,MAAM,8BAA8B;CAC5C,OAAO,EACL,+BAA+B;EAC7B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,0BACE,6VAMJ;EACF;EACA,WAAW,SAAS;GAClB,OAAO,EACL,sBAAsB,MAAM;IAC1B,IAAI,CAAC,oBAAoB,KAAK,cAAc,GAC1C;IAEF,IAAI,CAAC,0BAA0B,KAAK,UAAU,GAC5C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAA2B,CAAC;GAChE,EACF;EACF;CACF,EACF;AACF,CAAC"}
1
+ {"version":3,"file":"no-partial-record-satisfies.cjs","names":["isIdentifier","eslintCompatPlugin"],"sources":["../src/no-partial-record-satisfies.ts"],"sourcesContent":["import { eslintCompatPlugin, type Plugin } from \"@oxlint/plugins\";\n// Ban `<object literal> satisfies Partial<Record<Union, T>>` (with or\n// without a leading `as const`).\n//\n// A const object literal pinned with `satisfies` against a `Record` keyed by\n// a union declares itself the classification of every union member —\n// `satisfies` fails typecheck the moment a new member is added and left\n// unhandled. Wrapping that `Record` in `Partial` cancels the guarantee: a\n// missing member typechecks silently, so the literal can drift out of sync\n// with the union with no compiler signal. That silent-drift surface can cause\n// a classification-table bug when a newly added member falls through a\n// `Partial<Record<...>>` lookup instead of failing typecheck.\n//\n// `Readonly<...>` wrapping either side (`Readonly<Partial<Record<...>>>` or\n// `Partial<Readonly<Record<...>>>`) is unwrapped before the check, since it\n// changes mutability, not totality.\n//\n// Only the `satisfies` expression on an object literal is flagged. Plain\n// type-annotation positions (`const x: Partial<Record<...>> = {...}`),\n// function parameters, and variable/return type annotations are exempt:\n// those are legitimate uses (override inputs, accumulators, genuinely sparse\n// data) and carry no compiler guarantee of totality to lose.\n//\n// Flagged:\n// const x = {\n// a: 1,\n// } as const satisfies Partial<Record<Union, number>>;\n// const y = { a: 1 } satisfies Partial<Record<Union, number>>;\n// const z = {\n// a: 1,\n// } as const satisfies Readonly<Partial<Record<Union, number>>>;\n//\n// Allowed:\n// const x = {\n// a: 1,\n// } as const satisfies Record<Union, number>;\n// const fn = (overrides: Partial<Record<Union, number>>) => overrides;\n// let acc: Partial<Record<Union, number>> = {};\n// type Sparse = Partial<Record<Union, number>>;\n//\n// Fix by making the record total over the union — an explicit \"none\" /\n// `false` / `null` value for members where nothing applies forces a new\n// union member to be classified before it typechecks. If only a subset of\n// the union is ever relevant, derive a narrower union for the keys instead\n// of falling back to `Partial`. For genuinely sparse data where absence has\n// documented meaning (e.g. an unbounded key space), add an eslint-disable\n// with a comment naming what absence means.\n\nimport { isIdentifier } from \"./utils.ts\";\n\nconst unwrapReadonlyType = (typeNode) => {\n if (\n typeNode?.type === \"TSTypeReference\" &&\n isIdentifier(typeNode.typeName, \"Readonly\")\n ) {\n return typeNode.typeArguments?.params?.at(0) ?? null;\n }\n return typeNode;\n};\n\n// Matches `Partial<Record<...>>`, unwrapping a `Readonly<...>` wrapper on\n// either the outer (`Readonly<Partial<Record<...>>>`) or inner\n// (`Partial<Readonly<Record<...>>>`) type.\nconst isPartialRecordType = (typeNode) => {\n const outer = unwrapReadonlyType(typeNode);\n if (\n outer?.type !== \"TSTypeReference\" ||\n !isIdentifier(outer.typeName, \"Partial\")\n ) {\n return false;\n }\n const inner = unwrapReadonlyType(outer.typeArguments?.params?.at(0) ?? null);\n return (\n inner?.type === \"TSTypeReference\" && isIdentifier(inner.typeName, \"Record\")\n );\n};\n\n// Matches an object literal, or an object literal wrapped in `as const`.\nconst isObjectLiteralExpression = (node) => {\n if (node?.type === \"ObjectExpression\") {\n return true;\n }\n if (\n node?.type === \"TSAsExpression\" &&\n node.typeAnnotation?.type === \"TSTypeReference\" &&\n isIdentifier(node.typeAnnotation.typeName, \"const\")\n ) {\n return isObjectLiteralExpression(node.expression);\n }\n return false;\n};\n\nconst plugin: Plugin = eslintCompatPlugin({\n meta: { name: \"no-partial-record-satisfies\" },\n rules: {\n \"no-partial-record-satisfies\": {\n meta: {\n type: \"problem\",\n messages: {\n noPartialRecordSatisfies:\n \"Don't pin an object literal with `satisfies Partial<Record<...>>` \" +\n \"— Partial cancels the totality check satisfies would otherwise \" +\n \"give you, so a union member can silently fall through. Make the \" +\n \"record total (explicit 'none'/false/null per member), narrow the \" +\n \"key union, or add an eslint-disable naming what absence means \" +\n \"for genuinely sparse data.\",\n },\n },\n createOnce(context) {\n return {\n TSSatisfiesExpression(node) {\n if (!isPartialRecordType(node.typeAnnotation)) {\n return;\n }\n if (!isObjectLiteralExpression(node.expression)) {\n return;\n }\n context.report({ node, messageId: \"noPartialRecordSatisfies\" });\n },\n };\n },\n },\n },\n});\n\nexport default plugin;\n"],"mappings":";;;;;;;AAkDA,MAAM,sBAAsB,aAAa;CACvC,IACE,UAAU,SAAS,qBACnBA,cAAAA,aAAa,SAAS,UAAU,UAAU,GAE1C,OAAO,SAAS,eAAe,QAAQ,GAAG,CAAC,KAAK;CAElD,OAAO;AACT;AAKA,MAAM,uBAAuB,aAAa;CACxC,MAAM,QAAQ,mBAAmB,QAAQ;CACzC,IACE,OAAO,SAAS,qBAChB,CAACA,cAAAA,aAAa,MAAM,UAAU,SAAS,GAEvC,OAAO;CAET,MAAM,QAAQ,mBAAmB,MAAM,eAAe,QAAQ,GAAG,CAAC,KAAK,IAAI;CAC3E,OACE,OAAO,SAAS,qBAAqBA,cAAAA,aAAa,MAAM,UAAU,QAAQ;AAE9E;AAGA,MAAM,6BAA6B,SAAS;CAC1C,IAAI,MAAM,SAAS,oBACjB,OAAO;CAET,IACE,MAAM,SAAS,oBACf,KAAK,gBAAgB,SAAS,qBAC9BA,cAAAA,aAAa,KAAK,eAAe,UAAU,OAAO,GAElD,OAAO,0BAA0B,KAAK,UAAU;CAElD,OAAO;AACT;AAEA,MAAM,UAAA,GAAiBC,gBAAAA,mBAAAA,CAAmB;CACxC,MAAM,EAAE,MAAM,8BAA8B;CAC5C,OAAO,EACL,+BAA+B;EAC7B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,0BACE,6VAMJ;EACF;EACA,WAAW,SAAS;GAClB,OAAO,EACL,sBAAsB,MAAM;IAC1B,IAAI,CAAC,oBAAoB,KAAK,cAAc,GAC1C;IAEF,IAAI,CAAC,0BAA0B,KAAK,UAAU,GAC5C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAA2B,CAAC;GAChE,EACF;EACF;CACF,EACF;AACF,CAAC"}
@@ -1,4 +1,5 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-partial-record-satisfies.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
- export = _default;
3
+ declare const plugin: Plugin;
4
+ export = plugin;
4
5
  //# sourceMappingURL=no-partial-record-satisfies.d.cts.map
@@ -1,5 +1,6 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-partial-record-satisfies.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
+ declare const plugin: Plugin;
3
4
  //#endregion
4
- export { _default as default };
5
+ export { plugin as default };
5
6
  //# sourceMappingURL=no-partial-record-satisfies.d.mts.map
@@ -16,7 +16,7 @@ const isObjectLiteralExpression = (node) => {
16
16
  if (node?.type === "TSAsExpression" && node.typeAnnotation?.type === "TSTypeReference" && isIdentifier(node.typeAnnotation.typeName, "const")) return isObjectLiteralExpression(node.expression);
17
17
  return false;
18
18
  };
19
- var no_partial_record_satisfies_default = eslintCompatPlugin({
19
+ const plugin = eslintCompatPlugin({
20
20
  meta: { name: "no-partial-record-satisfies" },
21
21
  rules: { "no-partial-record-satisfies": {
22
22
  meta: {
@@ -36,6 +36,6 @@ var no_partial_record_satisfies_default = eslintCompatPlugin({
36
36
  } }
37
37
  });
38
38
  //#endregion
39
- export { no_partial_record_satisfies_default as default };
39
+ export { plugin as default };
40
40
 
41
41
  //# sourceMappingURL=no-partial-record-satisfies.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-partial-record-satisfies.mjs","names":[],"sources":["../src/no-partial-record-satisfies.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\n// Ban `<object literal> satisfies Partial<Record<Union, T>>` (with or\n// without a leading `as const`).\n//\n// A const object literal pinned with `satisfies` against a `Record` keyed by\n// a union declares itself the classification of every union member —\n// `satisfies` fails typecheck the moment a new member is added and left\n// unhandled. Wrapping that `Record` in `Partial` cancels the guarantee: a\n// missing member typechecks silently, so the literal can drift out of sync\n// with the union with no compiler signal. That silent-drift surface can cause\n// a classification-table bug when a newly added member falls through a\n// `Partial<Record<...>>` lookup instead of failing typecheck.\n//\n// `Readonly<...>` wrapping either side (`Readonly<Partial<Record<...>>>` or\n// `Partial<Readonly<Record<...>>>`) is unwrapped before the check, since it\n// changes mutability, not totality.\n//\n// Only the `satisfies` expression on an object literal is flagged. Plain\n// type-annotation positions (`const x: Partial<Record<...>> = {...}`),\n// function parameters, and variable/return type annotations are exempt:\n// those are legitimate uses (override inputs, accumulators, genuinely sparse\n// data) and carry no compiler guarantee of totality to lose.\n//\n// Flagged:\n// const x = {\n// a: 1,\n// } as const satisfies Partial<Record<Union, number>>;\n// const y = { a: 1 } satisfies Partial<Record<Union, number>>;\n// const z = {\n// a: 1,\n// } as const satisfies Readonly<Partial<Record<Union, number>>>;\n//\n// Allowed:\n// const x = {\n// a: 1,\n// } as const satisfies Record<Union, number>;\n// const fn = (overrides: Partial<Record<Union, number>>) => overrides;\n// let acc: Partial<Record<Union, number>> = {};\n// type Sparse = Partial<Record<Union, number>>;\n//\n// Fix by making the record total over the union — an explicit \"none\" /\n// `false` / `null` value for members where nothing applies forces a new\n// union member to be classified before it typechecks. If only a subset of\n// the union is ever relevant, derive a narrower union for the keys instead\n// of falling back to `Partial`. For genuinely sparse data where absence has\n// documented meaning (e.g. an unbounded key space), add an eslint-disable\n// with a comment naming what absence means.\n\nimport { isIdentifier } from \"./utils.ts\";\n\nconst unwrapReadonlyType = (typeNode) => {\n if (\n typeNode?.type === \"TSTypeReference\" &&\n isIdentifier(typeNode.typeName, \"Readonly\")\n ) {\n return typeNode.typeArguments?.params?.at(0) ?? null;\n }\n return typeNode;\n};\n\n// Matches `Partial<Record<...>>`, unwrapping a `Readonly<...>` wrapper on\n// either the outer (`Readonly<Partial<Record<...>>>`) or inner\n// (`Partial<Readonly<Record<...>>>`) type.\nconst isPartialRecordType = (typeNode) => {\n const outer = unwrapReadonlyType(typeNode);\n if (\n outer?.type !== \"TSTypeReference\" ||\n !isIdentifier(outer.typeName, \"Partial\")\n ) {\n return false;\n }\n const inner = unwrapReadonlyType(outer.typeArguments?.params?.at(0) ?? null);\n return (\n inner?.type === \"TSTypeReference\" && isIdentifier(inner.typeName, \"Record\")\n );\n};\n\n// Matches an object literal, or an object literal wrapped in `as const`.\nconst isObjectLiteralExpression = (node) => {\n if (node?.type === \"ObjectExpression\") {\n return true;\n }\n if (\n node?.type === \"TSAsExpression\" &&\n node.typeAnnotation?.type === \"TSTypeReference\" &&\n isIdentifier(node.typeAnnotation.typeName, \"const\")\n ) {\n return isObjectLiteralExpression(node.expression);\n }\n return false;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-partial-record-satisfies\" },\n rules: {\n \"no-partial-record-satisfies\": {\n meta: {\n type: \"problem\",\n messages: {\n noPartialRecordSatisfies:\n \"Don't pin an object literal with `satisfies Partial<Record<...>>` \" +\n \"— Partial cancels the totality check satisfies would otherwise \" +\n \"give you, so a union member can silently fall through. Make the \" +\n \"record total (explicit 'none'/false/null per member), narrow the \" +\n \"key union, or add an eslint-disable naming what absence means \" +\n \"for genuinely sparse data.\",\n },\n },\n createOnce(context) {\n return {\n TSSatisfiesExpression(node) {\n if (!isPartialRecordType(node.typeAnnotation)) {\n return;\n }\n if (!isObjectLiteralExpression(node.expression)) {\n return;\n }\n context.report({ node, messageId: \"noPartialRecordSatisfies\" });\n },\n };\n },\n },\n },\n});\n"],"mappings":";;;AAkDA,MAAM,sBAAsB,aAAa;CACvC,IACE,UAAU,SAAS,qBACnB,aAAa,SAAS,UAAU,UAAU,GAE1C,OAAO,SAAS,eAAe,QAAQ,GAAG,CAAC,KAAK;CAElD,OAAO;AACT;AAKA,MAAM,uBAAuB,aAAa;CACxC,MAAM,QAAQ,mBAAmB,QAAQ;CACzC,IACE,OAAO,SAAS,qBAChB,CAAC,aAAa,MAAM,UAAU,SAAS,GAEvC,OAAO;CAET,MAAM,QAAQ,mBAAmB,MAAM,eAAe,QAAQ,GAAG,CAAC,KAAK,IAAI;CAC3E,OACE,OAAO,SAAS,qBAAqB,aAAa,MAAM,UAAU,QAAQ;AAE9E;AAGA,MAAM,6BAA6B,SAAS;CAC1C,IAAI,MAAM,SAAS,oBACjB,OAAO;CAET,IACE,MAAM,SAAS,oBACf,KAAK,gBAAgB,SAAS,qBAC9B,aAAa,KAAK,eAAe,UAAU,OAAO,GAElD,OAAO,0BAA0B,KAAK,UAAU;CAElD,OAAO;AACT;AAEA,IAAA,sCAAe,mBAAmB;CAChC,MAAM,EAAE,MAAM,8BAA8B;CAC5C,OAAO,EACL,+BAA+B;EAC7B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,0BACE,6VAMJ;EACF;EACA,WAAW,SAAS;GAClB,OAAO,EACL,sBAAsB,MAAM;IAC1B,IAAI,CAAC,oBAAoB,KAAK,cAAc,GAC1C;IAEF,IAAI,CAAC,0BAA0B,KAAK,UAAU,GAC5C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAA2B,CAAC;GAChE,EACF;EACF;CACF,EACF;AACF,CAAC"}
1
+ {"version":3,"file":"no-partial-record-satisfies.mjs","names":[],"sources":["../src/no-partial-record-satisfies.ts"],"sourcesContent":["import { eslintCompatPlugin, type Plugin } from \"@oxlint/plugins\";\n// Ban `<object literal> satisfies Partial<Record<Union, T>>` (with or\n// without a leading `as const`).\n//\n// A const object literal pinned with `satisfies` against a `Record` keyed by\n// a union declares itself the classification of every union member —\n// `satisfies` fails typecheck the moment a new member is added and left\n// unhandled. Wrapping that `Record` in `Partial` cancels the guarantee: a\n// missing member typechecks silently, so the literal can drift out of sync\n// with the union with no compiler signal. That silent-drift surface can cause\n// a classification-table bug when a newly added member falls through a\n// `Partial<Record<...>>` lookup instead of failing typecheck.\n//\n// `Readonly<...>` wrapping either side (`Readonly<Partial<Record<...>>>` or\n// `Partial<Readonly<Record<...>>>`) is unwrapped before the check, since it\n// changes mutability, not totality.\n//\n// Only the `satisfies` expression on an object literal is flagged. Plain\n// type-annotation positions (`const x: Partial<Record<...>> = {...}`),\n// function parameters, and variable/return type annotations are exempt:\n// those are legitimate uses (override inputs, accumulators, genuinely sparse\n// data) and carry no compiler guarantee of totality to lose.\n//\n// Flagged:\n// const x = {\n// a: 1,\n// } as const satisfies Partial<Record<Union, number>>;\n// const y = { a: 1 } satisfies Partial<Record<Union, number>>;\n// const z = {\n// a: 1,\n// } as const satisfies Readonly<Partial<Record<Union, number>>>;\n//\n// Allowed:\n// const x = {\n// a: 1,\n// } as const satisfies Record<Union, number>;\n// const fn = (overrides: Partial<Record<Union, number>>) => overrides;\n// let acc: Partial<Record<Union, number>> = {};\n// type Sparse = Partial<Record<Union, number>>;\n//\n// Fix by making the record total over the union — an explicit \"none\" /\n// `false` / `null` value for members where nothing applies forces a new\n// union member to be classified before it typechecks. If only a subset of\n// the union is ever relevant, derive a narrower union for the keys instead\n// of falling back to `Partial`. For genuinely sparse data where absence has\n// documented meaning (e.g. an unbounded key space), add an eslint-disable\n// with a comment naming what absence means.\n\nimport { isIdentifier } from \"./utils.ts\";\n\nconst unwrapReadonlyType = (typeNode) => {\n if (\n typeNode?.type === \"TSTypeReference\" &&\n isIdentifier(typeNode.typeName, \"Readonly\")\n ) {\n return typeNode.typeArguments?.params?.at(0) ?? null;\n }\n return typeNode;\n};\n\n// Matches `Partial<Record<...>>`, unwrapping a `Readonly<...>` wrapper on\n// either the outer (`Readonly<Partial<Record<...>>>`) or inner\n// (`Partial<Readonly<Record<...>>>`) type.\nconst isPartialRecordType = (typeNode) => {\n const outer = unwrapReadonlyType(typeNode);\n if (\n outer?.type !== \"TSTypeReference\" ||\n !isIdentifier(outer.typeName, \"Partial\")\n ) {\n return false;\n }\n const inner = unwrapReadonlyType(outer.typeArguments?.params?.at(0) ?? null);\n return (\n inner?.type === \"TSTypeReference\" && isIdentifier(inner.typeName, \"Record\")\n );\n};\n\n// Matches an object literal, or an object literal wrapped in `as const`.\nconst isObjectLiteralExpression = (node) => {\n if (node?.type === \"ObjectExpression\") {\n return true;\n }\n if (\n node?.type === \"TSAsExpression\" &&\n node.typeAnnotation?.type === \"TSTypeReference\" &&\n isIdentifier(node.typeAnnotation.typeName, \"const\")\n ) {\n return isObjectLiteralExpression(node.expression);\n }\n return false;\n};\n\nconst plugin: Plugin = eslintCompatPlugin({\n meta: { name: \"no-partial-record-satisfies\" },\n rules: {\n \"no-partial-record-satisfies\": {\n meta: {\n type: \"problem\",\n messages: {\n noPartialRecordSatisfies:\n \"Don't pin an object literal with `satisfies Partial<Record<...>>` \" +\n \"— Partial cancels the totality check satisfies would otherwise \" +\n \"give you, so a union member can silently fall through. Make the \" +\n \"record total (explicit 'none'/false/null per member), narrow the \" +\n \"key union, or add an eslint-disable naming what absence means \" +\n \"for genuinely sparse data.\",\n },\n },\n createOnce(context) {\n return {\n TSSatisfiesExpression(node) {\n if (!isPartialRecordType(node.typeAnnotation)) {\n return;\n }\n if (!isObjectLiteralExpression(node.expression)) {\n return;\n }\n context.report({ node, messageId: \"noPartialRecordSatisfies\" });\n },\n };\n },\n },\n },\n});\n\nexport default plugin;\n"],"mappings":";;;AAkDA,MAAM,sBAAsB,aAAa;CACvC,IACE,UAAU,SAAS,qBACnB,aAAa,SAAS,UAAU,UAAU,GAE1C,OAAO,SAAS,eAAe,QAAQ,GAAG,CAAC,KAAK;CAElD,OAAO;AACT;AAKA,MAAM,uBAAuB,aAAa;CACxC,MAAM,QAAQ,mBAAmB,QAAQ;CACzC,IACE,OAAO,SAAS,qBAChB,CAAC,aAAa,MAAM,UAAU,SAAS,GAEvC,OAAO;CAET,MAAM,QAAQ,mBAAmB,MAAM,eAAe,QAAQ,GAAG,CAAC,KAAK,IAAI;CAC3E,OACE,OAAO,SAAS,qBAAqB,aAAa,MAAM,UAAU,QAAQ;AAE9E;AAGA,MAAM,6BAA6B,SAAS;CAC1C,IAAI,MAAM,SAAS,oBACjB,OAAO;CAET,IACE,MAAM,SAAS,oBACf,KAAK,gBAAgB,SAAS,qBAC9B,aAAa,KAAK,eAAe,UAAU,OAAO,GAElD,OAAO,0BAA0B,KAAK,UAAU;CAElD,OAAO;AACT;AAEA,MAAM,SAAiB,mBAAmB;CACxC,MAAM,EAAE,MAAM,8BAA8B;CAC5C,OAAO,EACL,+BAA+B;EAC7B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,0BACE,6VAMJ;EACF;EACA,WAAW,SAAS;GAClB,OAAO,EACL,sBAAsB,MAAM;IAC1B,IAAI,CAAC,oBAAoB,KAAK,cAAc,GAC1C;IAEF,IAAI,CAAC,0BAA0B,KAAK,UAAU,GAC5C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAA2B,CAAC;GAChE,EACF;EACF;CACF,EACF;AACF,CAAC"}
@@ -60,7 +60,7 @@ const getQuerySubscriptionHookName = (callee, queryHookAliases, tanstackQueryNam
60
60
  const hookName = require_utils.getPropertyName(callee.property);
61
61
  return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName) ? hookName : null;
62
62
  };
63
- var no_raw_route_query_client_default = (0, _oxlint_plugins.eslintCompatPlugin)({
63
+ const plugin = (0, _oxlint_plugins.eslintCompatPlugin)({
64
64
  meta: { name: "no-raw-route-query-client" },
65
65
  rules: { "no-raw-route-query-client": {
66
66
  meta: {
@@ -198,6 +198,6 @@ var no_raw_route_query_client_default = (0, _oxlint_plugins.eslintCompatPlugin)(
198
198
  } }
199
199
  });
200
200
  //#endregion
201
- exports.default = no_raw_route_query_client_default;
201
+ exports.default = plugin;
202
202
 
203
203
  //# sourceMappingURL=no-raw-route-query-client.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-raw-route-query-client.cjs","names":["getPropertyName","isIdentifier","eslintCompatPlugin","getImportedName"],"sources":["../src/no-raw-route-query-client.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\nimport type { Ranged } from \"@oxlint/plugins\";\n\nimport { getImportedName, getPropertyName, isIdentifier } from \"./utils.ts\";\n\nconst RAW_QUERY_CLIENT_METHODS = new Map([\n [\"ensureQueryData\", \"ensureRouteQueryData\"],\n [\"ensureInfiniteQueryData\", \"ensureRouteInfiniteQueryData\"],\n [\"fetchQuery\", \"fetchRouteQuery\"],\n [\"prefetchQuery\", \"prefetchRouteQuery\"],\n]);\nconst RAW_QUERY_CLIENT_RECEIVERS = new Set([\"qc\", \"queryClient\"]);\nconst TANSTACK_QUERY_MODULE = \"@tanstack/react-query\";\nconst ROUTE_QUERY_HELPERS_MODULE = \"@/lib/react-query\";\nconst GENERIC_ROUTE_QUERY_HELPERS = new Map([\n [\"ensureCriticalQueryData\", \"ensureRouteQueryData\"],\n [\"prefetchNonCriticalQuery\", \"prefetchRouteQuery\"],\n]);\nconst QUERY_SUBSCRIPTION_HOOKS = new Set([\n \"useInfiniteQuery\",\n \"useQueries\",\n \"useQuery\",\n \"useSuspenseInfiniteQuery\",\n \"useSuspenseQueries\",\n \"useSuspenseQuery\",\n]);\n\nconst ROUTE_LOADER_HOOKS = new Set([\"beforeLoad\", \"loader\"]);\nconst ROUTE_PENDING_COMPONENT_HOOK = \"pendingComponent\";\n\nconst isQueryClientReceiver = (node) => {\n if (!node) {\n return false;\n }\n\n if (node.type === \"Identifier\") {\n return RAW_QUERY_CLIENT_RECEIVERS.has(node.name);\n }\n\n if (node.type !== \"MemberExpression\" || node.computed) {\n return false;\n }\n\n const receiverName = getPropertyName(node.property);\n return receiverName === \"queryClient\";\n};\n\nconst findRouteLoaderHook = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name !== null && ROUTE_LOADER_HOOKS.has(name)) {\n return name;\n }\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst findPendingComponentCandidate = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name === ROUTE_PENDING_COMPONENT_HOOK) {\n return ROUTE_PENDING_COMPONENT_HOOK;\n }\n }\n\n if (current.type === \"FunctionDeclaration\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n if (current.type === \"VariableDeclarator\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst getQuerySubscriptionHookName = (\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n) => {\n if (isIdentifier(callee) && queryHookAliases.has(callee.name)) {\n return callee.name;\n }\n\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n !isIdentifier(callee.object) ||\n !tanstackQueryNamespaces.has(callee.object.name)\n ) {\n return null;\n }\n\n const hookName = getPropertyName(callee.property);\n return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName)\n ? hookName\n : null;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-raw-route-query-client\" },\n rules: {\n \"no-raw-route-query-client\": {\n meta: {\n type: \"problem\",\n messages: {\n noRawRouteQueryClient:\n \"Raw queryClient.{{method}} inside route {{hook}} \" +\n \"is forbidden. Use {{replacement}}(...), so route-seeded \" +\n \"queries carry a freshness window.\",\n noGenericRouteQueryHelper:\n \"Generic {{helper}} inside route {{hook}} is forbidden. \" +\n \"Use {{replacement}}(...), so route-seeded queries carry \" +\n \"a freshness window.\",\n noQueryHookInPendingComponent:\n \"TanStack Query subscription hook {{hook}} is forbidden \" +\n \"inside route pendingComponent {{component}}. Read cached \" +\n \"data synchronously with useQueryClient().getQueryData(...) \" +\n \"so abandoned pending renders cannot receive async state updates.\",\n },\n },\n createOnce(context) {\n const genericHelperAliases = new Map();\n const pendingComponentNames = new Set();\n const pendingQueryHookCalls: {\n component: string;\n hook: string;\n node: Ranged;\n }[] = [];\n const queryHookAliases = new Set();\n const routeQueryHelperNamespaces = new Set();\n const tanstackQueryNamespaces = new Set();\n\n return {\n before() {\n genericHelperAliases.clear();\n pendingComponentNames.clear();\n pendingQueryHookCalls.length = 0;\n queryHookAliases.clear();\n routeQueryHelperNamespaces.clear();\n tanstackQueryNamespaces.clear();\n },\n ImportDeclaration(node) {\n if (node.source?.value === TANSTACK_QUERY_MODULE) {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n tanstackQueryNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n if (\n importedName !== null &&\n QUERY_SUBSCRIPTION_HOOKS.has(importedName)\n ) {\n queryHookAliases.add(specifier.local.name);\n }\n }\n return;\n }\n\n if (node.source?.value !== ROUTE_QUERY_HELPERS_MODULE) {\n return;\n }\n\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n routeQueryHelperNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n const replacement =\n importedName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(importedName);\n if (replacement === undefined) {\n continue;\n }\n\n genericHelperAliases.set(specifier.local.name, {\n helper: importedName,\n replacement,\n });\n }\n },\n\n Property(node) {\n const name = getPropertyName(node.key);\n if (name !== ROUTE_PENDING_COMPONENT_HOOK) {\n return;\n }\n if (isIdentifier(node.value)) {\n pendingComponentNames.add(node.value.name);\n }\n },\n\n CallExpression(node) {\n const hook = findRouteLoaderHook(node);\n const callee = node.callee;\n\n if (hook !== null) {\n if (isIdentifier(callee)) {\n const helper = genericHelperAliases.get(callee.name);\n if (helper !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { hook, ...helper },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n callee.computed === false &&\n isIdentifier(callee.object) &&\n routeQueryHelperNamespaces.has(callee.object.name)\n ) {\n const helperName = getPropertyName(callee.property);\n const replacement =\n helperName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(helperName);\n\n if (replacement !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { helper: helperName, hook, replacement },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n isQueryClientReceiver(callee.object)\n ) {\n const method = getPropertyName(callee.property);\n const replacement =\n method === null\n ? undefined\n : RAW_QUERY_CLIENT_METHODS.get(method);\n\n if (replacement === undefined) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noRawRouteQueryClient\",\n data: { method, hook, replacement },\n });\n return;\n }\n }\n\n const pendingComponent = findPendingComponentCandidate(node);\n if (pendingComponent === null) {\n return;\n }\n\n const queryHookName = getQuerySubscriptionHookName(\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n );\n if (queryHookName === null) {\n return;\n }\n\n pendingQueryHookCalls.push({\n component: pendingComponent,\n hook: queryHookName,\n node,\n });\n },\n\n \"Program:exit\"() {\n for (const { component, hook, node } of pendingQueryHookCalls) {\n if (\n component !== ROUTE_PENDING_COMPONENT_HOOK &&\n !pendingComponentNames.has(component)\n ) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noQueryHookInPendingComponent\",\n data: { component, hook },\n });\n }\n },\n };\n },\n },\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"no-raw-route-query-client.cjs","names":["getPropertyName","isIdentifier","eslintCompatPlugin","getImportedName"],"sources":["../src/no-raw-route-query-client.ts"],"sourcesContent":["import { eslintCompatPlugin, type Plugin } from \"@oxlint/plugins\";\nimport type { Ranged } from \"@oxlint/plugins\";\n\nimport { getImportedName, getPropertyName, isIdentifier } from \"./utils.ts\";\n\nconst RAW_QUERY_CLIENT_METHODS = new Map([\n [\"ensureQueryData\", \"ensureRouteQueryData\"],\n [\"ensureInfiniteQueryData\", \"ensureRouteInfiniteQueryData\"],\n [\"fetchQuery\", \"fetchRouteQuery\"],\n [\"prefetchQuery\", \"prefetchRouteQuery\"],\n]);\nconst RAW_QUERY_CLIENT_RECEIVERS = new Set([\"qc\", \"queryClient\"]);\nconst TANSTACK_QUERY_MODULE = \"@tanstack/react-query\";\nconst ROUTE_QUERY_HELPERS_MODULE = \"@/lib/react-query\";\nconst GENERIC_ROUTE_QUERY_HELPERS = new Map([\n [\"ensureCriticalQueryData\", \"ensureRouteQueryData\"],\n [\"prefetchNonCriticalQuery\", \"prefetchRouteQuery\"],\n]);\nconst QUERY_SUBSCRIPTION_HOOKS = new Set([\n \"useInfiniteQuery\",\n \"useQueries\",\n \"useQuery\",\n \"useSuspenseInfiniteQuery\",\n \"useSuspenseQueries\",\n \"useSuspenseQuery\",\n]);\n\nconst ROUTE_LOADER_HOOKS = new Set([\"beforeLoad\", \"loader\"]);\nconst ROUTE_PENDING_COMPONENT_HOOK = \"pendingComponent\";\n\nconst isQueryClientReceiver = (node) => {\n if (!node) {\n return false;\n }\n\n if (node.type === \"Identifier\") {\n return RAW_QUERY_CLIENT_RECEIVERS.has(node.name);\n }\n\n if (node.type !== \"MemberExpression\" || node.computed) {\n return false;\n }\n\n const receiverName = getPropertyName(node.property);\n return receiverName === \"queryClient\";\n};\n\nconst findRouteLoaderHook = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name !== null && ROUTE_LOADER_HOOKS.has(name)) {\n return name;\n }\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst findPendingComponentCandidate = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name === ROUTE_PENDING_COMPONENT_HOOK) {\n return ROUTE_PENDING_COMPONENT_HOOK;\n }\n }\n\n if (current.type === \"FunctionDeclaration\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n if (current.type === \"VariableDeclarator\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst getQuerySubscriptionHookName = (\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n) => {\n if (isIdentifier(callee) && queryHookAliases.has(callee.name)) {\n return callee.name;\n }\n\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n !isIdentifier(callee.object) ||\n !tanstackQueryNamespaces.has(callee.object.name)\n ) {\n return null;\n }\n\n const hookName = getPropertyName(callee.property);\n return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName)\n ? hookName\n : null;\n};\n\nconst plugin: Plugin = eslintCompatPlugin({\n meta: { name: \"no-raw-route-query-client\" },\n rules: {\n \"no-raw-route-query-client\": {\n meta: {\n type: \"problem\",\n messages: {\n noRawRouteQueryClient:\n \"Raw queryClient.{{method}} inside route {{hook}} \" +\n \"is forbidden. Use {{replacement}}(...), so route-seeded \" +\n \"queries carry a freshness window.\",\n noGenericRouteQueryHelper:\n \"Generic {{helper}} inside route {{hook}} is forbidden. \" +\n \"Use {{replacement}}(...), so route-seeded queries carry \" +\n \"a freshness window.\",\n noQueryHookInPendingComponent:\n \"TanStack Query subscription hook {{hook}} is forbidden \" +\n \"inside route pendingComponent {{component}}. Read cached \" +\n \"data synchronously with useQueryClient().getQueryData(...) \" +\n \"so abandoned pending renders cannot receive async state updates.\",\n },\n },\n createOnce(context) {\n const genericHelperAliases = new Map();\n const pendingComponentNames = new Set();\n const pendingQueryHookCalls: {\n component: string;\n hook: string;\n node: Ranged;\n }[] = [];\n const queryHookAliases = new Set();\n const routeQueryHelperNamespaces = new Set();\n const tanstackQueryNamespaces = new Set();\n\n return {\n before() {\n genericHelperAliases.clear();\n pendingComponentNames.clear();\n pendingQueryHookCalls.length = 0;\n queryHookAliases.clear();\n routeQueryHelperNamespaces.clear();\n tanstackQueryNamespaces.clear();\n },\n ImportDeclaration(node) {\n if (node.source?.value === TANSTACK_QUERY_MODULE) {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n tanstackQueryNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n if (\n importedName !== null &&\n QUERY_SUBSCRIPTION_HOOKS.has(importedName)\n ) {\n queryHookAliases.add(specifier.local.name);\n }\n }\n return;\n }\n\n if (node.source?.value !== ROUTE_QUERY_HELPERS_MODULE) {\n return;\n }\n\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n routeQueryHelperNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n const replacement =\n importedName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(importedName);\n if (replacement === undefined) {\n continue;\n }\n\n genericHelperAliases.set(specifier.local.name, {\n helper: importedName,\n replacement,\n });\n }\n },\n\n Property(node) {\n const name = getPropertyName(node.key);\n if (name !== ROUTE_PENDING_COMPONENT_HOOK) {\n return;\n }\n if (isIdentifier(node.value)) {\n pendingComponentNames.add(node.value.name);\n }\n },\n\n CallExpression(node) {\n const hook = findRouteLoaderHook(node);\n const callee = node.callee;\n\n if (hook !== null) {\n if (isIdentifier(callee)) {\n const helper = genericHelperAliases.get(callee.name);\n if (helper !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { hook, ...helper },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n callee.computed === false &&\n isIdentifier(callee.object) &&\n routeQueryHelperNamespaces.has(callee.object.name)\n ) {\n const helperName = getPropertyName(callee.property);\n const replacement =\n helperName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(helperName);\n\n if (replacement !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { helper: helperName, hook, replacement },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n isQueryClientReceiver(callee.object)\n ) {\n const method = getPropertyName(callee.property);\n const replacement =\n method === null\n ? undefined\n : RAW_QUERY_CLIENT_METHODS.get(method);\n\n if (replacement === undefined) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noRawRouteQueryClient\",\n data: { method, hook, replacement },\n });\n return;\n }\n }\n\n const pendingComponent = findPendingComponentCandidate(node);\n if (pendingComponent === null) {\n return;\n }\n\n const queryHookName = getQuerySubscriptionHookName(\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n );\n if (queryHookName === null) {\n return;\n }\n\n pendingQueryHookCalls.push({\n component: pendingComponent,\n hook: queryHookName,\n node,\n });\n },\n\n \"Program:exit\"() {\n for (const { component, hook, node } of pendingQueryHookCalls) {\n if (\n component !== ROUTE_PENDING_COMPONENT_HOOK &&\n !pendingComponentNames.has(component)\n ) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noQueryHookInPendingComponent\",\n data: { component, hook },\n });\n }\n },\n };\n },\n },\n },\n});\n\nexport default plugin;\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-raw-route-query-client.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
- export = _default;
3
+ declare const plugin: Plugin;
4
+ export = plugin;
4
5
  //# sourceMappingURL=no-raw-route-query-client.d.cts.map
@@ -1,5 +1,6 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-raw-route-query-client.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
+ declare const plugin: Plugin;
3
4
  //#endregion
4
- export { _default as default };
5
+ export { plugin as default };
5
6
  //# sourceMappingURL=no-raw-route-query-client.d.mts.map
@@ -56,7 +56,7 @@ const getQuerySubscriptionHookName = (callee, queryHookAliases, tanstackQueryNam
56
56
  const hookName = getPropertyName(callee.property);
57
57
  return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName) ? hookName : null;
58
58
  };
59
- var no_raw_route_query_client_default = eslintCompatPlugin({
59
+ const plugin = eslintCompatPlugin({
60
60
  meta: { name: "no-raw-route-query-client" },
61
61
  rules: { "no-raw-route-query-client": {
62
62
  meta: {
@@ -194,6 +194,6 @@ var no_raw_route_query_client_default = eslintCompatPlugin({
194
194
  } }
195
195
  });
196
196
  //#endregion
197
- export { no_raw_route_query_client_default as default };
197
+ export { plugin as default };
198
198
 
199
199
  //# sourceMappingURL=no-raw-route-query-client.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-raw-route-query-client.mjs","names":[],"sources":["../src/no-raw-route-query-client.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\nimport type { Ranged } from \"@oxlint/plugins\";\n\nimport { getImportedName, getPropertyName, isIdentifier } from \"./utils.ts\";\n\nconst RAW_QUERY_CLIENT_METHODS = new Map([\n [\"ensureQueryData\", \"ensureRouteQueryData\"],\n [\"ensureInfiniteQueryData\", \"ensureRouteInfiniteQueryData\"],\n [\"fetchQuery\", \"fetchRouteQuery\"],\n [\"prefetchQuery\", \"prefetchRouteQuery\"],\n]);\nconst RAW_QUERY_CLIENT_RECEIVERS = new Set([\"qc\", \"queryClient\"]);\nconst TANSTACK_QUERY_MODULE = \"@tanstack/react-query\";\nconst ROUTE_QUERY_HELPERS_MODULE = \"@/lib/react-query\";\nconst GENERIC_ROUTE_QUERY_HELPERS = new Map([\n [\"ensureCriticalQueryData\", \"ensureRouteQueryData\"],\n [\"prefetchNonCriticalQuery\", \"prefetchRouteQuery\"],\n]);\nconst QUERY_SUBSCRIPTION_HOOKS = new Set([\n \"useInfiniteQuery\",\n \"useQueries\",\n \"useQuery\",\n \"useSuspenseInfiniteQuery\",\n \"useSuspenseQueries\",\n \"useSuspenseQuery\",\n]);\n\nconst ROUTE_LOADER_HOOKS = new Set([\"beforeLoad\", \"loader\"]);\nconst ROUTE_PENDING_COMPONENT_HOOK = \"pendingComponent\";\n\nconst isQueryClientReceiver = (node) => {\n if (!node) {\n return false;\n }\n\n if (node.type === \"Identifier\") {\n return RAW_QUERY_CLIENT_RECEIVERS.has(node.name);\n }\n\n if (node.type !== \"MemberExpression\" || node.computed) {\n return false;\n }\n\n const receiverName = getPropertyName(node.property);\n return receiverName === \"queryClient\";\n};\n\nconst findRouteLoaderHook = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name !== null && ROUTE_LOADER_HOOKS.has(name)) {\n return name;\n }\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst findPendingComponentCandidate = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name === ROUTE_PENDING_COMPONENT_HOOK) {\n return ROUTE_PENDING_COMPONENT_HOOK;\n }\n }\n\n if (current.type === \"FunctionDeclaration\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n if (current.type === \"VariableDeclarator\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst getQuerySubscriptionHookName = (\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n) => {\n if (isIdentifier(callee) && queryHookAliases.has(callee.name)) {\n return callee.name;\n }\n\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n !isIdentifier(callee.object) ||\n !tanstackQueryNamespaces.has(callee.object.name)\n ) {\n return null;\n }\n\n const hookName = getPropertyName(callee.property);\n return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName)\n ? hookName\n : null;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-raw-route-query-client\" },\n rules: {\n \"no-raw-route-query-client\": {\n meta: {\n type: \"problem\",\n messages: {\n noRawRouteQueryClient:\n \"Raw queryClient.{{method}} inside route {{hook}} \" +\n \"is forbidden. Use {{replacement}}(...), so route-seeded \" +\n \"queries carry a freshness window.\",\n noGenericRouteQueryHelper:\n \"Generic {{helper}} inside route {{hook}} is forbidden. \" +\n \"Use {{replacement}}(...), so route-seeded queries carry \" +\n \"a freshness window.\",\n noQueryHookInPendingComponent:\n \"TanStack Query subscription hook {{hook}} is forbidden \" +\n \"inside route pendingComponent {{component}}. Read cached \" +\n \"data synchronously with useQueryClient().getQueryData(...) \" +\n \"so abandoned pending renders cannot receive async state updates.\",\n },\n },\n createOnce(context) {\n const genericHelperAliases = new Map();\n const pendingComponentNames = new Set();\n const pendingQueryHookCalls: {\n component: string;\n hook: string;\n node: Ranged;\n }[] = [];\n const queryHookAliases = new Set();\n const routeQueryHelperNamespaces = new Set();\n const tanstackQueryNamespaces = new Set();\n\n return {\n before() {\n genericHelperAliases.clear();\n pendingComponentNames.clear();\n pendingQueryHookCalls.length = 0;\n queryHookAliases.clear();\n routeQueryHelperNamespaces.clear();\n tanstackQueryNamespaces.clear();\n },\n ImportDeclaration(node) {\n if (node.source?.value === TANSTACK_QUERY_MODULE) {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n tanstackQueryNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n if (\n importedName !== null &&\n QUERY_SUBSCRIPTION_HOOKS.has(importedName)\n ) {\n queryHookAliases.add(specifier.local.name);\n }\n }\n return;\n }\n\n if (node.source?.value !== ROUTE_QUERY_HELPERS_MODULE) {\n return;\n }\n\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n routeQueryHelperNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n const replacement =\n importedName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(importedName);\n if (replacement === undefined) {\n continue;\n }\n\n genericHelperAliases.set(specifier.local.name, {\n helper: importedName,\n replacement,\n });\n }\n },\n\n Property(node) {\n const name = getPropertyName(node.key);\n if (name !== ROUTE_PENDING_COMPONENT_HOOK) {\n return;\n }\n if (isIdentifier(node.value)) {\n pendingComponentNames.add(node.value.name);\n }\n },\n\n CallExpression(node) {\n const hook = findRouteLoaderHook(node);\n const callee = node.callee;\n\n if (hook !== null) {\n if (isIdentifier(callee)) {\n const helper = genericHelperAliases.get(callee.name);\n if (helper !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { hook, ...helper },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n callee.computed === false &&\n isIdentifier(callee.object) &&\n routeQueryHelperNamespaces.has(callee.object.name)\n ) {\n const helperName = getPropertyName(callee.property);\n const replacement =\n helperName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(helperName);\n\n if (replacement !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { helper: helperName, hook, replacement },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n isQueryClientReceiver(callee.object)\n ) {\n const method = getPropertyName(callee.property);\n const replacement =\n method === null\n ? undefined\n : RAW_QUERY_CLIENT_METHODS.get(method);\n\n if (replacement === undefined) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noRawRouteQueryClient\",\n data: { method, hook, replacement },\n });\n return;\n }\n }\n\n const pendingComponent = findPendingComponentCandidate(node);\n if (pendingComponent === null) {\n return;\n }\n\n const queryHookName = getQuerySubscriptionHookName(\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n );\n if (queryHookName === null) {\n return;\n }\n\n pendingQueryHookCalls.push({\n component: pendingComponent,\n hook: queryHookName,\n node,\n });\n },\n\n \"Program:exit\"() {\n for (const { component, hook, node } of pendingQueryHookCalls) {\n if (\n component !== ROUTE_PENDING_COMPONENT_HOOK &&\n !pendingComponentNames.has(component)\n ) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noQueryHookInPendingComponent\",\n data: { component, hook },\n });\n }\n },\n };\n },\n },\n },\n});\n"],"mappings":";;;AAKA,MAAM,2CAA2B,IAAI,IAAI;CACvC,CAAC,mBAAmB,sBAAsB;CAC1C,CAAC,2BAA2B,8BAA8B;CAC1D,CAAC,cAAc,iBAAiB;CAChC,CAAC,iBAAiB,oBAAoB;AACxC,CAAC;AACD,MAAM,6CAA6B,IAAI,IAAI,CAAC,MAAM,aAAa,CAAC;AAChE,MAAM,wBAAwB;AAC9B,MAAM,6BAA6B;AACnC,MAAM,8CAA8B,IAAI,IAAI,CAC1C,CAAC,2BAA2B,sBAAsB,GAClD,CAAC,4BAA4B,oBAAoB,CACnD,CAAC;AACD,MAAM,2CAA2B,IAAI,IAAI;CACvC;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,qCAAqB,IAAI,IAAI,CAAC,cAAc,QAAQ,CAAC;AAC3D,MAAM,+BAA+B;AAErC,MAAM,yBAAyB,SAAS;CACtC,IAAI,CAAC,MACH,OAAO;CAGT,IAAI,KAAK,SAAS,cAChB,OAAO,2BAA2B,IAAI,KAAK,IAAI;CAGjD,IAAI,KAAK,SAAS,sBAAsB,KAAK,UAC3C,OAAO;CAIT,OADqB,gBAAgB,KAAK,QACxB,MAAM;AAC1B;AAEA,MAAM,uBAAuB,SAAS;CACpC,IAAI,UAAU,KAAK;CAEnB,OAAO,SAAS;EACd,IAAI,QAAQ,SAAS,YAAY;GAC/B,MAAM,OAAO,gBAAgB,QAAQ,GAAG;GAExC,IAAI,SAAS,QAAQ,mBAAmB,IAAI,IAAI,GAC9C,OAAO;EAEX;EAEA,UAAU,QAAQ;CACpB;CAEA,OAAO;AACT;AAEA,MAAM,iCAAiC,SAAS;CAC9C,IAAI,UAAU,KAAK;CAEnB,OAAO,SAAS;EACd,IAAI,QAAQ,SAAS,YACN;OAAA,gBAAgB,QAAQ,GAE9B,MAAM,8BACX,OAAO;EAAA;EAIX,IAAI,QAAQ,SAAS,yBAAyB,aAAa,QAAQ,EAAE,GACnE,OAAO,QAAQ,GAAG;EAGpB,IAAI,QAAQ,SAAS,wBAAwB,aAAa,QAAQ,EAAE,GAClE,OAAO,QAAQ,GAAG;EAGpB,UAAU,QAAQ;CACpB;CAEA,OAAO;AACT;AAEA,MAAM,gCACJ,QACA,kBACA,4BACG;CACH,IAAI,aAAa,MAAM,KAAK,iBAAiB,IAAI,OAAO,IAAI,GAC1D,OAAO,OAAO;CAGhB,IACE,OAAO,SAAS,sBAChB,OAAO,YACP,CAAC,aAAa,OAAO,MAAM,KAC3B,CAAC,wBAAwB,IAAI,OAAO,OAAO,IAAI,GAE/C,OAAO;CAGT,MAAM,WAAW,gBAAgB,OAAO,QAAQ;CAChD,OAAO,aAAa,QAAQ,yBAAyB,IAAI,QAAQ,IAC7D,WACA;AACN;AAEA,IAAA,oCAAe,mBAAmB;CAChC,MAAM,EAAE,MAAM,4BAA4B;CAC1C,OAAO,EACL,6BAA6B;EAC3B,MAAM;GACJ,MAAM;GACN,UAAU;IACR,uBACE;IAGF,2BACE;IAGF,+BACE;GAIJ;EACF;EACA,WAAW,SAAS;GAClB,MAAM,uCAAuB,IAAI,IAAI;GACrC,MAAM,wCAAwB,IAAI,IAAI;GACtC,MAAM,wBAIA,CAAC;GACP,MAAM,mCAAmB,IAAI,IAAI;GACjC,MAAM,6CAA6B,IAAI,IAAI;GAC3C,MAAM,0CAA0B,IAAI,IAAI;GAExC,OAAO;IACL,SAAS;KACP,qBAAqB,MAAM;KAC3B,sBAAsB,MAAM;KAC5B,sBAAsB,SAAS;KAC/B,iBAAiB,MAAM;KACvB,2BAA2B,MAAM;KACjC,wBAAwB,MAAM;IAChC;IACA,kBAAkB,MAAM;KACtB,IAAI,KAAK,QAAQ,UAAU,uBAAuB;MAChD,KAAK,MAAM,aAAa,KAAK,YAAY;OACvC,IAAI,UAAU,SAAS,4BAA4B;QACjD,wBAAwB,IAAI,UAAU,MAAM,IAAI;QAChD;OACF;OACA,IAAI,UAAU,SAAS,mBACrB;OAGF,MAAM,eAAe,gBAAgB,SAAS;OAC9C,IACE,iBAAiB,QACjB,yBAAyB,IAAI,YAAY,GAEzC,iBAAiB,IAAI,UAAU,MAAM,IAAI;MAE7C;MACA;KACF;KAEA,IAAI,KAAK,QAAQ,UAAU,4BACzB;KAGF,KAAK,MAAM,aAAa,KAAK,YAAY;MACvC,IAAI,UAAU,SAAS,4BAA4B;OACjD,2BAA2B,IAAI,UAAU,MAAM,IAAI;OACnD;MACF;MACA,IAAI,UAAU,SAAS,mBACrB;MAGF,MAAM,eAAe,gBAAgB,SAAS;MAC9C,MAAM,cACJ,iBAAiB,OACb,KAAA,IACA,4BAA4B,IAAI,YAAY;MAClD,IAAI,gBAAgB,KAAA,GAClB;MAGF,qBAAqB,IAAI,UAAU,MAAM,MAAM;OAC7C,QAAQ;OACR;MACF,CAAC;KACH;IACF;IAEA,SAAS,MAAM;KAEb,IADa,gBAAgB,KAAK,GAC3B,MAAM,8BACX;KAEF,IAAI,aAAa,KAAK,KAAK,GACzB,sBAAsB,IAAI,KAAK,MAAM,IAAI;IAE7C;IAEA,eAAe,MAAM;KACnB,MAAM,OAAO,oBAAoB,IAAI;KACrC,MAAM,SAAS,KAAK;KAEpB,IAAI,SAAS,MAAM;MACjB,IAAI,aAAa,MAAM,GAAG;OACxB,MAAM,SAAS,qBAAqB,IAAI,OAAO,IAAI;OACnD,IAAI,WAAW,KAAA,GAAW;QACxB,QAAQ,OAAO;SACb;SACA,WAAW;SACX,MAAM;UAAE;UAAM,GAAG;SAAO;QAC1B,CAAC;QACD;OACF;MACF;MAEA,IACE,OAAO,SAAS,sBAChB,OAAO,aAAa,SACpB,aAAa,OAAO,MAAM,KAC1B,2BAA2B,IAAI,OAAO,OAAO,IAAI,GACjD;OACA,MAAM,aAAa,gBAAgB,OAAO,QAAQ;OAClD,MAAM,cACJ,eAAe,OACX,KAAA,IACA,4BAA4B,IAAI,UAAU;OAEhD,IAAI,gBAAgB,KAAA,GAAW;QAC7B,QAAQ,OAAO;SACb;SACA,WAAW;SACX,MAAM;UAAE,QAAQ;UAAY;UAAM;SAAY;QAChD,CAAC;QACD;OACF;MACF;MAEA,IACE,OAAO,SAAS,sBAChB,CAAC,OAAO,YACR,sBAAsB,OAAO,MAAM,GACnC;OACA,MAAM,SAAS,gBAAgB,OAAO,QAAQ;OAC9C,MAAM,cACJ,WAAW,OACP,KAAA,IACA,yBAAyB,IAAI,MAAM;OAEzC,IAAI,gBAAgB,KAAA,GAClB;OAGF,QAAQ,OAAO;QACb;QACA,WAAW;QACX,MAAM;SAAE;SAAQ;SAAM;QAAY;OACpC,CAAC;OACD;MACF;KACF;KAEA,MAAM,mBAAmB,8BAA8B,IAAI;KAC3D,IAAI,qBAAqB,MACvB;KAGF,MAAM,gBAAgB,6BACpB,QACA,kBACA,uBACF;KACA,IAAI,kBAAkB,MACpB;KAGF,sBAAsB,KAAK;MACzB,WAAW;MACX,MAAM;MACN;KACF,CAAC;IACH;IAEA,iBAAiB;KACf,KAAK,MAAM,EAAE,WAAW,MAAM,UAAU,uBAAuB;MAC7D,IACE,cAAc,gCACd,CAAC,sBAAsB,IAAI,SAAS,GAEpC;MAGF,QAAQ,OAAO;OACb;OACA,WAAW;OACX,MAAM;QAAE;QAAW;OAAK;MAC1B,CAAC;KACH;IACF;GACF;EACF;CACF,EACF;AACF,CAAC"}
1
+ {"version":3,"file":"no-raw-route-query-client.mjs","names":[],"sources":["../src/no-raw-route-query-client.ts"],"sourcesContent":["import { eslintCompatPlugin, type Plugin } from \"@oxlint/plugins\";\nimport type { Ranged } from \"@oxlint/plugins\";\n\nimport { getImportedName, getPropertyName, isIdentifier } from \"./utils.ts\";\n\nconst RAW_QUERY_CLIENT_METHODS = new Map([\n [\"ensureQueryData\", \"ensureRouteQueryData\"],\n [\"ensureInfiniteQueryData\", \"ensureRouteInfiniteQueryData\"],\n [\"fetchQuery\", \"fetchRouteQuery\"],\n [\"prefetchQuery\", \"prefetchRouteQuery\"],\n]);\nconst RAW_QUERY_CLIENT_RECEIVERS = new Set([\"qc\", \"queryClient\"]);\nconst TANSTACK_QUERY_MODULE = \"@tanstack/react-query\";\nconst ROUTE_QUERY_HELPERS_MODULE = \"@/lib/react-query\";\nconst GENERIC_ROUTE_QUERY_HELPERS = new Map([\n [\"ensureCriticalQueryData\", \"ensureRouteQueryData\"],\n [\"prefetchNonCriticalQuery\", \"prefetchRouteQuery\"],\n]);\nconst QUERY_SUBSCRIPTION_HOOKS = new Set([\n \"useInfiniteQuery\",\n \"useQueries\",\n \"useQuery\",\n \"useSuspenseInfiniteQuery\",\n \"useSuspenseQueries\",\n \"useSuspenseQuery\",\n]);\n\nconst ROUTE_LOADER_HOOKS = new Set([\"beforeLoad\", \"loader\"]);\nconst ROUTE_PENDING_COMPONENT_HOOK = \"pendingComponent\";\n\nconst isQueryClientReceiver = (node) => {\n if (!node) {\n return false;\n }\n\n if (node.type === \"Identifier\") {\n return RAW_QUERY_CLIENT_RECEIVERS.has(node.name);\n }\n\n if (node.type !== \"MemberExpression\" || node.computed) {\n return false;\n }\n\n const receiverName = getPropertyName(node.property);\n return receiverName === \"queryClient\";\n};\n\nconst findRouteLoaderHook = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name !== null && ROUTE_LOADER_HOOKS.has(name)) {\n return name;\n }\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst findPendingComponentCandidate = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name === ROUTE_PENDING_COMPONENT_HOOK) {\n return ROUTE_PENDING_COMPONENT_HOOK;\n }\n }\n\n if (current.type === \"FunctionDeclaration\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n if (current.type === \"VariableDeclarator\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst getQuerySubscriptionHookName = (\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n) => {\n if (isIdentifier(callee) && queryHookAliases.has(callee.name)) {\n return callee.name;\n }\n\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n !isIdentifier(callee.object) ||\n !tanstackQueryNamespaces.has(callee.object.name)\n ) {\n return null;\n }\n\n const hookName = getPropertyName(callee.property);\n return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName)\n ? hookName\n : null;\n};\n\nconst plugin: Plugin = eslintCompatPlugin({\n meta: { name: \"no-raw-route-query-client\" },\n rules: {\n \"no-raw-route-query-client\": {\n meta: {\n type: \"problem\",\n messages: {\n noRawRouteQueryClient:\n \"Raw queryClient.{{method}} inside route {{hook}} \" +\n \"is forbidden. Use {{replacement}}(...), so route-seeded \" +\n \"queries carry a freshness window.\",\n noGenericRouteQueryHelper:\n \"Generic {{helper}} inside route {{hook}} is forbidden. \" +\n \"Use {{replacement}}(...), so route-seeded queries carry \" +\n \"a freshness window.\",\n noQueryHookInPendingComponent:\n \"TanStack Query subscription hook {{hook}} is forbidden \" +\n \"inside route pendingComponent {{component}}. Read cached \" +\n \"data synchronously with useQueryClient().getQueryData(...) \" +\n \"so abandoned pending renders cannot receive async state updates.\",\n },\n },\n createOnce(context) {\n const genericHelperAliases = new Map();\n const pendingComponentNames = new Set();\n const pendingQueryHookCalls: {\n component: string;\n hook: string;\n node: Ranged;\n }[] = [];\n const queryHookAliases = new Set();\n const routeQueryHelperNamespaces = new Set();\n const tanstackQueryNamespaces = new Set();\n\n return {\n before() {\n genericHelperAliases.clear();\n pendingComponentNames.clear();\n pendingQueryHookCalls.length = 0;\n queryHookAliases.clear();\n routeQueryHelperNamespaces.clear();\n tanstackQueryNamespaces.clear();\n },\n ImportDeclaration(node) {\n if (node.source?.value === TANSTACK_QUERY_MODULE) {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n tanstackQueryNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n if (\n importedName !== null &&\n QUERY_SUBSCRIPTION_HOOKS.has(importedName)\n ) {\n queryHookAliases.add(specifier.local.name);\n }\n }\n return;\n }\n\n if (node.source?.value !== ROUTE_QUERY_HELPERS_MODULE) {\n return;\n }\n\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n routeQueryHelperNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n const replacement =\n importedName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(importedName);\n if (replacement === undefined) {\n continue;\n }\n\n genericHelperAliases.set(specifier.local.name, {\n helper: importedName,\n replacement,\n });\n }\n },\n\n Property(node) {\n const name = getPropertyName(node.key);\n if (name !== ROUTE_PENDING_COMPONENT_HOOK) {\n return;\n }\n if (isIdentifier(node.value)) {\n pendingComponentNames.add(node.value.name);\n }\n },\n\n CallExpression(node) {\n const hook = findRouteLoaderHook(node);\n const callee = node.callee;\n\n if (hook !== null) {\n if (isIdentifier(callee)) {\n const helper = genericHelperAliases.get(callee.name);\n if (helper !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { hook, ...helper },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n callee.computed === false &&\n isIdentifier(callee.object) &&\n routeQueryHelperNamespaces.has(callee.object.name)\n ) {\n const helperName = getPropertyName(callee.property);\n const replacement =\n helperName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(helperName);\n\n if (replacement !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { helper: helperName, hook, replacement },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n isQueryClientReceiver(callee.object)\n ) {\n const method = getPropertyName(callee.property);\n const replacement =\n method === null\n ? undefined\n : RAW_QUERY_CLIENT_METHODS.get(method);\n\n if (replacement === undefined) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noRawRouteQueryClient\",\n data: { method, hook, replacement },\n });\n return;\n }\n }\n\n const pendingComponent = findPendingComponentCandidate(node);\n if (pendingComponent === null) {\n return;\n }\n\n const queryHookName = getQuerySubscriptionHookName(\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n );\n if (queryHookName === null) {\n return;\n }\n\n pendingQueryHookCalls.push({\n component: pendingComponent,\n hook: queryHookName,\n node,\n });\n },\n\n \"Program:exit\"() {\n for (const { component, hook, node } of pendingQueryHookCalls) {\n if (\n component !== ROUTE_PENDING_COMPONENT_HOOK &&\n !pendingComponentNames.has(component)\n ) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noQueryHookInPendingComponent\",\n data: { component, hook },\n });\n }\n },\n };\n },\n },\n },\n});\n\nexport default plugin;\n"],"mappings":"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"}
@@ -45,7 +45,7 @@ const external = (value, resolveBinding) => {
45
45
  return false;
46
46
  }
47
47
  };
48
- var no_real_network_in_tests_default = require_test_rule_utils.testOnlyPlugin("no-real-network-in-tests", "noRealNetwork", "Use @stll/test-kit's withFakeServer instead of real network access in tests.", (report, getScope) => {
48
+ const plugin = require_test_rule_utils.testOnlyPlugin("no-real-network-in-tests", "noRealNetwork", "Use @stll/test-kit's withFakeServer instead of real network access in tests.", (report, getScope) => {
49
49
  const resolveBinding = require_test_rule_utils.createBindingResolver(getScope);
50
50
  const isGlobalCallee = (value, name) => {
51
51
  if (!require_test_rule_utils.isNode(value)) return false;
@@ -66,6 +66,6 @@ var no_real_network_in_tests_default = require_test_rule_utils.testOnlyPlugin("n
66
66
  };
67
67
  });
68
68
  //#endregion
69
- exports.default = no_real_network_in_tests_default;
69
+ exports.default = plugin;
70
70
 
71
71
  //# sourceMappingURL=no-real-network-in-tests.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"no-real-network-in-tests.cjs","names":["isNode","identifierName","testOnlyPlugin","createBindingResolver","propertyName"],"sources":["../src/no-real-network-in-tests.ts"],"sourcesContent":["import {\n createBindingResolver,\n identifierName,\n isNode,\n propertyName,\n testOnlyPlugin,\n} from \"./test-rule-utils\";\n\nconst ipv4Loopback = /^127(?:\\.\\d{1,3}){3}$/u;\n\nconst urlText = (\n value: unknown,\n resolveBinding: ReturnType<typeof createBindingResolver>,\n): string | null => {\n if (!isNode(value)) return null;\n if (value.type === \"Literal\" && typeof value.value === \"string\")\n return value.value;\n if (\n value.type === \"TemplateLiteral\" &&\n Array.isArray(value.quasis) &&\n Array.isArray(value.expressions)\n ) {\n let text = \"\";\n for (const [index, part] of value.quasis.entries()) {\n const raw = isNode(part) ? Reflect.get(part.value ?? {}, \"raw\") : null;\n if (typeof raw !== \"string\") return null;\n text += raw;\n const expression = value.expressions.at(index);\n if (expression === undefined) continue;\n const resolved = urlText(expression, resolveBinding);\n if (resolved === null) return null;\n text += resolved;\n }\n return text;\n }\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n return binding.type === \"const\"\n ? urlText(binding.initializer, resolveBinding)\n : null;\n }\n if (\n value.type === \"NewExpression\" &&\n isNode(value.callee) &&\n resolveBinding(value.callee).type === \"global\" &&\n identifierName(value.callee) === \"URL\" &&\n Array.isArray(value.arguments)\n )\n return urlText(value.arguments.at(0), resolveBinding);\n return null;\n};\nconst external = (\n value: unknown,\n resolveBinding: ReturnType<typeof createBindingResolver>,\n): boolean => {\n const text = urlText(value, resolveBinding);\n if (text === null) return false;\n try {\n const { hostname: host, protocol } = new URL(text);\n if (![\"http:\", \"https:\", \"ws:\", \"wss:\"].includes(protocol) || host === \"\")\n return false;\n return (\n host !== \"localhost\" &&\n !ipv4Loopback.test(host) &&\n host !== \"::1\" &&\n host !== \"[::1]\" &&\n !host.endsWith(\".localhost\")\n );\n } catch {\n return false;\n }\n};\n\nexport default testOnlyPlugin(\n \"no-real-network-in-tests\",\n \"noRealNetwork\",\n \"Use @stll/test-kit's withFakeServer instead of real network access in tests.\",\n (report, getScope) => {\n const resolveBinding = createBindingResolver(getScope);\n const isGlobalCallee = (value: unknown, name: string): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n return binding.type === \"global\" && binding.name === name;\n }\n if (\n value.type !== \"MemberExpression\" ||\n propertyName(value.property) !== name\n )\n return false;\n return (\n isNode(value.object) &&\n resolveBinding(value.object).type === \"global\" &&\n identifierName(value.object) === \"globalThis\"\n );\n };\n return {\n CallExpression(node) {\n if (\n isGlobalCallee(node.callee, \"fetch\") &&\n Array.isArray(node.arguments) &&\n external(node.arguments.at(0), resolveBinding)\n )\n report(node);\n },\n NewExpression(node) {\n if (\n (isGlobalCallee(node.callee, \"WebSocket\") ||\n isGlobalCallee(node.callee, \"EventSource\")) &&\n Array.isArray(node.arguments) &&\n external(node.arguments.at(0), resolveBinding)\n )\n report(node);\n },\n };\n },\n);\n"],"mappings":";;;;;;AAQA,MAAM,eAAe;AAErB,MAAM,WACJ,OACA,mBACkB;CAClB,IAAI,CAACA,wBAAAA,OAAO,KAAK,GAAG,OAAO;CAC3B,IAAI,MAAM,SAAS,aAAa,OAAO,MAAM,UAAU,UACrD,OAAO,MAAM;CACf,IACE,MAAM,SAAS,qBACf,MAAM,QAAQ,MAAM,MAAM,KAC1B,MAAM,QAAQ,MAAM,WAAW,GAC/B;EACA,IAAI,OAAO;EACX,KAAK,MAAM,CAAC,OAAO,SAAS,MAAM,OAAO,QAAQ,GAAG;GAClD,MAAM,MAAMA,wBAAAA,OAAO,IAAI,IAAI,QAAQ,IAAI,KAAK,SAAS,CAAC,GAAG,KAAK,IAAI;GAClE,IAAI,OAAO,QAAQ,UAAU,OAAO;GACpC,QAAQ;GACR,MAAM,aAAa,MAAM,YAAY,GAAG,KAAK;GAC7C,IAAI,eAAe,KAAA,GAAW;GAC9B,MAAM,WAAW,QAAQ,YAAY,cAAc;GACnD,IAAI,aAAa,MAAM,OAAO;GAC9B,QAAQ;EACV;EACA,OAAO;CACT;CACA,IAAI,MAAM,SAAS,cAAc;EAC/B,MAAM,UAAU,eAAe,KAAK;EACpC,OAAO,QAAQ,SAAS,UACpB,QAAQ,QAAQ,aAAa,cAAc,IAC3C;CACN;CACA,IACE,MAAM,SAAS,mBACfA,wBAAAA,OAAO,MAAM,MAAM,KACnB,eAAe,MAAM,MAAM,CAAC,CAAC,SAAS,YACtCC,wBAAAA,eAAe,MAAM,MAAM,MAAM,SACjC,MAAM,QAAQ,MAAM,SAAS,GAE7B,OAAO,QAAQ,MAAM,UAAU,GAAG,CAAC,GAAG,cAAc;CACtD,OAAO;AACT;AACA,MAAM,YACJ,OACA,mBACY;CACZ,MAAM,OAAO,QAAQ,OAAO,cAAc;CAC1C,IAAI,SAAS,MAAM,OAAO;CAC1B,IAAI;EACF,MAAM,EAAE,UAAU,MAAM,aAAa,IAAI,IAAI,IAAI;EACjD,IAAI,CAAC;GAAC;GAAS;GAAU;GAAO;EAAM,CAAC,CAAC,SAAS,QAAQ,KAAK,SAAS,IACrE,OAAO;EACT,OACE,SAAS,eACT,CAAC,aAAa,KAAK,IAAI,KACvB,SAAS,SACT,SAAS,WACT,CAAC,KAAK,SAAS,YAAY;CAE/B,QAAQ;EACN,OAAO;CACT;AACF;AAEA,IAAA,mCAAeC,wBAAAA,eACb,4BACA,iBACA,iFACC,QAAQ,aAAa;CACpB,MAAM,iBAAiBC,wBAAAA,sBAAsB,QAAQ;CACrD,MAAM,kBAAkB,OAAgB,SAA0B;EAChE,IAAI,CAACH,wBAAAA,OAAO,KAAK,GAAG,OAAO;EAC3B,IAAI,MAAM,SAAS,cAAc;GAC/B,MAAM,UAAU,eAAe,KAAK;GACpC,OAAO,QAAQ,SAAS,YAAY,QAAQ,SAAS;EACvD;EACA,IACE,MAAM,SAAS,sBACfI,wBAAAA,aAAa,MAAM,QAAQ,MAAM,MAEjC,OAAO;EACT,OACEJ,wBAAAA,OAAO,MAAM,MAAM,KACnB,eAAe,MAAM,MAAM,CAAC,CAAC,SAAS,YACtCC,wBAAAA,eAAe,MAAM,MAAM,MAAM;CAErC;CACA,OAAO;EACL,eAAe,MAAM;GACnB,IACE,eAAe,KAAK,QAAQ,OAAO,KACnC,MAAM,QAAQ,KAAK,SAAS,KAC5B,SAAS,KAAK,UAAU,GAAG,CAAC,GAAG,cAAc,GAE7C,OAAO,IAAI;EACf;EACA,cAAc,MAAM;GAClB,KACG,eAAe,KAAK,QAAQ,WAAW,KACtC,eAAe,KAAK,QAAQ,aAAa,MAC3C,MAAM,QAAQ,KAAK,SAAS,KAC5B,SAAS,KAAK,UAAU,GAAG,CAAC,GAAG,cAAc,GAE7C,OAAO,IAAI;EACf;CACF;AACF,CACF"}
1
+ {"version":3,"file":"no-real-network-in-tests.cjs","names":["isNode","identifierName","testOnlyPlugin","createBindingResolver","propertyName"],"sources":["../src/no-real-network-in-tests.ts"],"sourcesContent":["import type { Plugin } from \"@oxlint/plugins\";\n\nimport {\n createBindingResolver,\n identifierName,\n isNode,\n propertyName,\n testOnlyPlugin,\n} from \"./test-rule-utils\";\n\nconst ipv4Loopback = /^127(?:\\.\\d{1,3}){3}$/u;\n\nconst urlText = (\n value: unknown,\n resolveBinding: ReturnType<typeof createBindingResolver>,\n): string | null => {\n if (!isNode(value)) return null;\n if (value.type === \"Literal\" && typeof value.value === \"string\")\n return value.value;\n if (\n value.type === \"TemplateLiteral\" &&\n Array.isArray(value.quasis) &&\n Array.isArray(value.expressions)\n ) {\n let text = \"\";\n for (const [index, part] of value.quasis.entries()) {\n const raw = isNode(part) ? Reflect.get(part.value ?? {}, \"raw\") : null;\n if (typeof raw !== \"string\") return null;\n text += raw;\n const expression = value.expressions.at(index);\n if (expression === undefined) continue;\n const resolved = urlText(expression, resolveBinding);\n if (resolved === null) return null;\n text += resolved;\n }\n return text;\n }\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n return binding.type === \"const\"\n ? urlText(binding.initializer, resolveBinding)\n : null;\n }\n if (\n value.type === \"NewExpression\" &&\n isNode(value.callee) &&\n resolveBinding(value.callee).type === \"global\" &&\n identifierName(value.callee) === \"URL\" &&\n Array.isArray(value.arguments)\n )\n return urlText(value.arguments.at(0), resolveBinding);\n return null;\n};\nconst external = (\n value: unknown,\n resolveBinding: ReturnType<typeof createBindingResolver>,\n): boolean => {\n const text = urlText(value, resolveBinding);\n if (text === null) return false;\n try {\n const { hostname: host, protocol } = new URL(text);\n if (![\"http:\", \"https:\", \"ws:\", \"wss:\"].includes(protocol) || host === \"\")\n return false;\n return (\n host !== \"localhost\" &&\n !ipv4Loopback.test(host) &&\n host !== \"::1\" &&\n host !== \"[::1]\" &&\n !host.endsWith(\".localhost\")\n );\n } catch {\n return false;\n }\n};\n\nconst plugin: Plugin = testOnlyPlugin(\n \"no-real-network-in-tests\",\n \"noRealNetwork\",\n \"Use @stll/test-kit's withFakeServer instead of real network access in tests.\",\n (report, getScope) => {\n const resolveBinding = createBindingResolver(getScope);\n const isGlobalCallee = (value: unknown, name: string): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n return binding.type === \"global\" && binding.name === name;\n }\n if (\n value.type !== \"MemberExpression\" ||\n propertyName(value.property) !== name\n )\n return false;\n return (\n isNode(value.object) &&\n resolveBinding(value.object).type === \"global\" &&\n identifierName(value.object) === \"globalThis\"\n );\n };\n return {\n CallExpression(node) {\n if (\n isGlobalCallee(node.callee, \"fetch\") &&\n Array.isArray(node.arguments) &&\n external(node.arguments.at(0), resolveBinding)\n )\n report(node);\n },\n NewExpression(node) {\n if (\n (isGlobalCallee(node.callee, \"WebSocket\") ||\n isGlobalCallee(node.callee, \"EventSource\")) &&\n Array.isArray(node.arguments) &&\n external(node.arguments.at(0), resolveBinding)\n )\n report(node);\n },\n };\n },\n);\n\nexport default plugin;\n"],"mappings":";;;;;;AAUA,MAAM,eAAe;AAErB,MAAM,WACJ,OACA,mBACkB;CAClB,IAAI,CAACA,wBAAAA,OAAO,KAAK,GAAG,OAAO;CAC3B,IAAI,MAAM,SAAS,aAAa,OAAO,MAAM,UAAU,UACrD,OAAO,MAAM;CACf,IACE,MAAM,SAAS,qBACf,MAAM,QAAQ,MAAM,MAAM,KAC1B,MAAM,QAAQ,MAAM,WAAW,GAC/B;EACA,IAAI,OAAO;EACX,KAAK,MAAM,CAAC,OAAO,SAAS,MAAM,OAAO,QAAQ,GAAG;GAClD,MAAM,MAAMA,wBAAAA,OAAO,IAAI,IAAI,QAAQ,IAAI,KAAK,SAAS,CAAC,GAAG,KAAK,IAAI;GAClE,IAAI,OAAO,QAAQ,UAAU,OAAO;GACpC,QAAQ;GACR,MAAM,aAAa,MAAM,YAAY,GAAG,KAAK;GAC7C,IAAI,eAAe,KAAA,GAAW;GAC9B,MAAM,WAAW,QAAQ,YAAY,cAAc;GACnD,IAAI,aAAa,MAAM,OAAO;GAC9B,QAAQ;EACV;EACA,OAAO;CACT;CACA,IAAI,MAAM,SAAS,cAAc;EAC/B,MAAM,UAAU,eAAe,KAAK;EACpC,OAAO,QAAQ,SAAS,UACpB,QAAQ,QAAQ,aAAa,cAAc,IAC3C;CACN;CACA,IACE,MAAM,SAAS,mBACfA,wBAAAA,OAAO,MAAM,MAAM,KACnB,eAAe,MAAM,MAAM,CAAC,CAAC,SAAS,YACtCC,wBAAAA,eAAe,MAAM,MAAM,MAAM,SACjC,MAAM,QAAQ,MAAM,SAAS,GAE7B,OAAO,QAAQ,MAAM,UAAU,GAAG,CAAC,GAAG,cAAc;CACtD,OAAO;AACT;AACA,MAAM,YACJ,OACA,mBACY;CACZ,MAAM,OAAO,QAAQ,OAAO,cAAc;CAC1C,IAAI,SAAS,MAAM,OAAO;CAC1B,IAAI;EACF,MAAM,EAAE,UAAU,MAAM,aAAa,IAAI,IAAI,IAAI;EACjD,IAAI,CAAC;GAAC;GAAS;GAAU;GAAO;EAAM,CAAC,CAAC,SAAS,QAAQ,KAAK,SAAS,IACrE,OAAO;EACT,OACE,SAAS,eACT,CAAC,aAAa,KAAK,IAAI,KACvB,SAAS,SACT,SAAS,WACT,CAAC,KAAK,SAAS,YAAY;CAE/B,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,SAAiBC,wBAAAA,eACrB,4BACA,iBACA,iFACC,QAAQ,aAAa;CACpB,MAAM,iBAAiBC,wBAAAA,sBAAsB,QAAQ;CACrD,MAAM,kBAAkB,OAAgB,SAA0B;EAChE,IAAI,CAACH,wBAAAA,OAAO,KAAK,GAAG,OAAO;EAC3B,IAAI,MAAM,SAAS,cAAc;GAC/B,MAAM,UAAU,eAAe,KAAK;GACpC,OAAO,QAAQ,SAAS,YAAY,QAAQ,SAAS;EACvD;EACA,IACE,MAAM,SAAS,sBACfI,wBAAAA,aAAa,MAAM,QAAQ,MAAM,MAEjC,OAAO;EACT,OACEJ,wBAAAA,OAAO,MAAM,MAAM,KACnB,eAAe,MAAM,MAAM,CAAC,CAAC,SAAS,YACtCC,wBAAAA,eAAe,MAAM,MAAM,MAAM;CAErC;CACA,OAAO;EACL,eAAe,MAAM;GACnB,IACE,eAAe,KAAK,QAAQ,OAAO,KACnC,MAAM,QAAQ,KAAK,SAAS,KAC5B,SAAS,KAAK,UAAU,GAAG,CAAC,GAAG,cAAc,GAE7C,OAAO,IAAI;EACf;EACA,cAAc,MAAM;GAClB,KACG,eAAe,KAAK,QAAQ,WAAW,KACtC,eAAe,KAAK,QAAQ,aAAa,MAC3C,MAAM,QAAQ,KAAK,SAAS,KAC5B,SAAS,KAAK,UAAU,GAAG,CAAC,GAAG,cAAc,GAE7C,OAAO,IAAI;EACf;CACF;AACF,CACF"}
@@ -1,4 +1,5 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-real-network-in-tests.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
- export = _default;
3
+ declare const plugin: Plugin;
4
+ export = plugin;
4
5
  //# sourceMappingURL=no-real-network-in-tests.d.cts.map
@@ -1,5 +1,6 @@
1
+ import { Plugin } from "@oxlint/plugins";
1
2
  //#region src/no-real-network-in-tests.d.ts
2
- declare const _default: import("@oxlint/plugins").Plugin;
3
+ declare const plugin: Plugin;
3
4
  //#endregion
4
- export { _default as default };
5
+ export { plugin as default };
5
6
  //# sourceMappingURL=no-real-network-in-tests.d.mts.map