@stll/oxlint-plugin 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/index.cjs +30 -0
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +16 -1
  4. package/dist/index.d.mts +16 -1
  5. package/dist/index.mjs +23 -1
  6. package/dist/index.mjs.map +1 -1
  7. package/dist/no-checkout-writes-in-tests.cjs +115 -0
  8. package/dist/no-checkout-writes-in-tests.cjs.map +1 -0
  9. package/dist/no-checkout-writes-in-tests.d.cts +4 -0
  10. package/dist/no-checkout-writes-in-tests.d.mts +5 -0
  11. package/dist/no-checkout-writes-in-tests.mjs +111 -0
  12. package/dist/no-checkout-writes-in-tests.mjs.map +1 -0
  13. package/dist/no-real-network-in-tests.cjs +71 -0
  14. package/dist/no-real-network-in-tests.cjs.map +1 -0
  15. package/dist/no-real-network-in-tests.d.cts +4 -0
  16. package/dist/no-real-network-in-tests.d.mts +5 -0
  17. package/dist/no-real-network-in-tests.mjs +67 -0
  18. package/dist/no-real-network-in-tests.mjs.map +1 -0
  19. package/dist/no-real-timer-waits-in-tests.cjs +32 -0
  20. package/dist/no-real-timer-waits-in-tests.cjs.map +1 -0
  21. package/dist/no-real-timer-waits-in-tests.d.cts +4 -0
  22. package/dist/no-real-timer-waits-in-tests.d.mts +5 -0
  23. package/dist/no-real-timer-waits-in-tests.mjs +28 -0
  24. package/dist/no-real-timer-waits-in-tests.mjs.map +1 -0
  25. package/dist/no-unseeded-random-in-tests.cjs +25 -0
  26. package/dist/no-unseeded-random-in-tests.cjs.map +1 -0
  27. package/dist/no-unseeded-random-in-tests.d.cts +4 -0
  28. package/dist/no-unseeded-random-in-tests.d.mts +5 -0
  29. package/dist/no-unseeded-random-in-tests.mjs +21 -0
  30. package/dist/no-unseeded-random-in-tests.mjs.map +1 -0
  31. package/dist/no-wall-clock-bound.cjs +153 -0
  32. package/dist/no-wall-clock-bound.cjs.map +1 -0
  33. package/dist/no-wall-clock-bound.d.cts +4 -0
  34. package/dist/no-wall-clock-bound.d.mts +5 -0
  35. package/dist/no-wall-clock-bound.mjs +149 -0
  36. package/dist/no-wall-clock-bound.mjs.map +1 -0
  37. package/dist/no-wall-clock-in-tests.cjs +35 -0
  38. package/dist/no-wall-clock-in-tests.cjs.map +1 -0
  39. package/dist/no-wall-clock-in-tests.d.cts +4 -0
  40. package/dist/no-wall-clock-in-tests.d.mts +5 -0
  41. package/dist/no-wall-clock-in-tests.mjs +31 -0
  42. package/dist/no-wall-clock-in-tests.mjs.map +1 -0
  43. package/dist/test-rule-utils.cjs +143 -0
  44. package/dist/test-rule-utils.cjs.map +1 -0
  45. package/dist/test-rule-utils.mjs +114 -0
  46. package/dist/test-rule-utils.mjs.map +1 -0
  47. package/package.json +67 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-wall-clock-bound.mjs","names":[],"sources":["../src/no-wall-clock-bound.ts"],"sourcesContent":["import type { Scope } from \"@oxlint/plugins\";\n\nimport {\n createBindingResolver,\n identifierName,\n isNode,\n propertyName,\n testOnlyPlugin,\n type Node,\n} from \"./test-rule-utils\";\n\nconst timerModules = new Set([\n \"node:timers/promises\",\n \"timers/promises\",\n \"@stll/test-kit\",\n]);\nconst timerHelpers = new Set([\"delay\", \"setTimeout\", \"sleep\"]);\nconst testModules = new Set([\"@jest/globals\", \"bun:test\", \"vitest\"]);\nconst functionTypes = new Set([\n \"ArrowFunctionExpression\",\n \"FunctionDeclaration\",\n \"FunctionExpression\",\n]);\nconst matcherModifiers = new Set([\"not\", \"rejects\", \"resolves\"]);\n\nconst isVariableReference = (identifier: Node): boolean => {\n const parent = identifier.parent;\n if (!isNode(parent)) return true;\n if (\n parent.type === \"MemberExpression\" &&\n parent.property === identifier &&\n parent.computed !== true\n )\n return false;\n if (\n (parent.type === \"Property\" ||\n parent.type === \"MethodDefinition\" ||\n parent.type === \"PropertyDefinition\") &&\n parent.key === identifier &&\n parent.computed !== true &&\n parent.value !== identifier\n )\n return false;\n if (\n (parent.type === \"LabeledStatement\" ||\n parent.type === \"BreakStatement\" ||\n parent.type === \"ContinueStatement\") &&\n parent.label === identifier\n )\n return false;\n if (\n (parent.type === \"VariableDeclarator\" ||\n parent.type === \"FunctionDeclaration\" ||\n parent.type === \"FunctionExpression\" ||\n parent.type === \"ClassDeclaration\" ||\n parent.type === \"ClassExpression\") &&\n parent.id === identifier\n )\n return false;\n return true;\n};\n\nconst enclosingFunction = (node: Node): Node | null => {\n let current = node.parent;\n while (isNode(current)) {\n if (functionTypes.has(current.type)) return current;\n current = current.parent;\n }\n return null;\n};\n\nexport default testOnlyPlugin(\n \"no-wall-clock-bound\",\n \"noWallClockBound\",\n \"A test must not assert on measured wall-clock time or race a timer, because such assertions fail on loaded runners; use a controlled clock or deferred for ordering.\",\n (report, getScope) => {\n const resolveBinding = createBindingResolver(getScope);\n\n const isGlobalMember = (\n node: Node,\n objectName: string,\n member: string,\n ): boolean => {\n if (node.type !== \"MemberExpression\") return false;\n if (propertyName(node.property) !== member || !isNode(node.object))\n return false;\n const binding = resolveBinding(node.object);\n return binding.type === \"global\" && binding.name === objectName;\n };\n\n const isClockRead = (value: unknown): boolean => {\n if (!isNode(value) || value.type !== \"CallExpression\") return false;\n if (!isNode(value.callee)) return false;\n if (\n isGlobalMember(value.callee, \"performance\", \"now\") ||\n isGlobalMember(value.callee, \"Date\", \"now\") ||\n isGlobalMember(value.callee, \"Bun\", \"nanoseconds\")\n )\n return true;\n const callee = value.callee;\n return (\n callee.type === \"MemberExpression\" &&\n propertyName(callee.property) === \"bigint\" &&\n isNode(callee.object) &&\n isGlobalMember(callee.object, \"process\", \"hrtime\")\n );\n };\n\n const constInitializerInSameFunction = (\n identifier: Node,\n use: Node,\n ): Node | null => {\n const name = identifierName(identifier);\n if (name === null) return null;\n let scope: Scope | null = getScope(identifier);\n while (scope !== null) {\n const variable = scope.set.get(name);\n if (variable === undefined) {\n scope = scope.upper;\n continue;\n }\n const definition = variable.defs.at(0);\n if (definition?.type !== \"Variable\") return null;\n const declarator = definition.node as unknown;\n if (\n !isNode(declarator) ||\n !isNode(declarator.parent) ||\n declarator.parent.type !== \"VariableDeclaration\" ||\n declarator.parent.kind !== \"const\" ||\n !isNode(declarator.init) ||\n enclosingFunction(declarator) !== enclosingFunction(use)\n )\n return null;\n return declarator.init;\n }\n return null;\n };\n\n const containsClockRead = (\n value: unknown,\n use: Node,\n followIdentifier: boolean,\n visitedIdentifiers = new Set<Node>(),\n ): boolean => {\n if (Array.isArray(value))\n return value.some((child) =>\n containsClockRead(child, use, followIdentifier, visitedIdentifiers),\n );\n if (!isNode(value)) return false;\n if (isClockRead(value)) return true;\n if (\n followIdentifier &&\n value.type === \"Identifier\" &&\n isVariableReference(value)\n ) {\n if (visitedIdentifiers.has(value)) return false;\n visitedIdentifiers.add(value);\n const initializer = constInitializerInSameFunction(value, use);\n if (\n initializer !== null &&\n containsClockRead(initializer, use, true, visitedIdentifiers)\n )\n return true;\n }\n return Object.values(value).some(\n (child) =>\n child !== value.parent &&\n containsClockRead(child, use, followIdentifier, visitedIdentifiers),\n );\n };\n\n const matcherExpectCall = (value: unknown): Node | null => {\n let current = value;\n while (\n isNode(current) &&\n current.type === \"MemberExpression\" &&\n matcherModifiers.has(propertyName(current.property) ?? \"\")\n )\n current = current.object;\n return isNode(current) && current.type === \"CallExpression\"\n ? current\n : null;\n };\n\n const containsSetTimeout = (value: unknown): boolean => {\n if (Array.isArray(value)) return value.some(containsSetTimeout);\n if (!isNode(value)) return false;\n if (value.type === \"CallExpression\" && isNode(value.callee)) {\n const binding = resolveBinding(value.callee);\n if (binding.type === \"global\" && binding.name === \"setTimeout\")\n return true;\n }\n for (const child of Object.values(value)) {\n if (child === value.parent) continue;\n if (Array.isArray(child)) {\n if (child.some(containsSetTimeout)) return true;\n } else if (containsSetTimeout(child)) return true;\n }\n return false;\n };\n\n const isTimer = (value: unknown): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"NewExpression\") {\n if (!isNode(value.callee)) return false;\n const binding = resolveBinding(value.callee);\n return (\n binding.type === \"global\" &&\n binding.name === \"Promise\" &&\n containsSetTimeout(value.arguments)\n );\n }\n if (value.type !== \"CallExpression\" || !isNode(value.callee))\n return false;\n if (isGlobalMember(value.callee, \"Bun\", \"sleep\")) return true;\n const binding = resolveBinding(value.callee);\n return (\n binding.type === \"import\" &&\n timerModules.has(binding.module) &&\n timerHelpers.has(binding.imported)\n );\n };\n\n const isTimerOrBoundTimer = (value: unknown, use: Node): boolean => {\n if (isTimer(value)) return true;\n if (!isNode(value) || value.type !== \"Identifier\") return false;\n const initializer = constInitializerInSameFunction(value, use);\n return initializer !== null && isTimer(initializer);\n };\n\n return {\n CallExpression(node) {\n if (!isNode(node.callee) || node.callee.type !== \"MemberExpression\")\n return;\n const member = propertyName(node.callee.property);\n if (member === \"race\" && isNode(node.callee.object)) {\n const binding = resolveBinding(node.callee.object);\n if (binding.type === \"global\" && binding.name === \"Promise\") {\n const firstArgument = Array.isArray(node.arguments)\n ? node.arguments.at(0)\n : null;\n if (\n isNode(firstArgument) &&\n firstArgument.type === \"ArrayExpression\" &&\n Array.isArray(firstArgument.elements) &&\n firstArgument.elements.some((element) =>\n isTimerOrBoundTimer(element, node),\n )\n )\n report(node);\n return;\n }\n }\n const expectCall = matcherExpectCall(node.callee.object);\n if (expectCall === null || !isNode(expectCall.callee)) return;\n const calleeBinding = resolveBinding(expectCall.callee);\n const isExpect =\n (calleeBinding.type === \"global\" &&\n calleeBinding.name === \"expect\") ||\n (calleeBinding.type === \"import\" &&\n calleeBinding.imported === \"expect\" &&\n testModules.has(calleeBinding.module));\n if (!isExpect) return;\n const assertionValues = [expectCall.arguments, node.arguments];\n if (containsClockRead(assertionValues, node, true)) report(node);\n },\n };\n },\n);\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ const require_test_rule_utils = require("./test-rule-utils.cjs");
6
+ //#region src/no-wall-clock-in-tests.ts
7
+ var no_wall_clock_in_tests_default = require_test_rule_utils.testOnlyPlugin("no-wall-clock-in-tests", "noWallClock", "Use the @stll/test-kit clock instead of reading the wall clock in tests.", (report, getScope) => {
8
+ const resolveBinding = require_test_rule_utils.createBindingResolver(getScope);
9
+ const isGlobal = (value, name) => {
10
+ if (!require_test_rule_utils.isNode(value)) return false;
11
+ const binding = resolveBinding(value);
12
+ return binding.type === "global" && binding.name === name;
13
+ };
14
+ const isClockMember = (value) => {
15
+ if (!require_test_rule_utils.isNode(value)) return false;
16
+ if (value.type === "Identifier") {
17
+ const binding = resolveBinding(value);
18
+ if (binding.type === "global") return binding.name === "Date.now" || binding.name === "performance.now";
19
+ return binding.type === "const" && isClockMember(binding.initializer);
20
+ }
21
+ return value.type === "MemberExpression" && require_test_rule_utils.propertyName(value.property) === "now" && (isGlobal(value.object, "Date") || isGlobal(value.object, "performance"));
22
+ };
23
+ return {
24
+ CallExpression(node) {
25
+ if (isClockMember(node.callee) || require_test_rule_utils.isNode(node.callee) && isGlobal(node.callee, "Date") && Array.isArray(node.arguments) && node.arguments.length === 0) report(node);
26
+ },
27
+ NewExpression(node) {
28
+ if (require_test_rule_utils.isNode(node.callee) && isGlobal(node.callee, "Date") && Array.isArray(node.arguments) && node.arguments.length === 0) report(node);
29
+ }
30
+ };
31
+ });
32
+ //#endregion
33
+ exports.default = no_wall_clock_in_tests_default;
34
+
35
+ //# sourceMappingURL=no-wall-clock-in-tests.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-wall-clock-in-tests.cjs","names":["testOnlyPlugin","createBindingResolver","isNode","propertyName"],"sources":["../src/no-wall-clock-in-tests.ts"],"sourcesContent":["import {\n createBindingResolver,\n isNode,\n propertyName,\n testOnlyPlugin,\n} from \"./test-rule-utils\";\n\nexport default testOnlyPlugin(\n \"no-wall-clock-in-tests\",\n \"noWallClock\",\n \"Use the @stll/test-kit clock instead of reading the wall clock in tests.\",\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 isClockMember = (value: unknown): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n if (binding.type === \"global\")\n return (\n binding.name === \"Date.now\" || binding.name === \"performance.now\"\n );\n return binding.type === \"const\" && isClockMember(binding.initializer);\n }\n return (\n value.type === \"MemberExpression\" &&\n propertyName(value.property) === \"now\" &&\n (isGlobal(value.object, \"Date\") ||\n isGlobal(value.object, \"performance\"))\n );\n };\n return {\n CallExpression(node) {\n if (\n isClockMember(node.callee) ||\n (isNode(node.callee) &&\n isGlobal(node.callee, \"Date\") &&\n Array.isArray(node.arguments) &&\n node.arguments.length === 0)\n )\n report(node);\n },\n NewExpression(node) {\n if (\n isNode(node.callee) &&\n isGlobal(node.callee, \"Date\") &&\n Array.isArray(node.arguments) &&\n node.arguments.length === 0\n )\n report(node);\n },\n };\n },\n);\n"],"mappings":";;;;;;AAOA,IAAA,iCAAeA,wBAAAA,eACb,0BACA,eACA,6EACC,QAAQ,aAAa;CACpB,MAAM,iBAAiBC,wBAAAA,sBAAsB,QAAQ;CACrD,MAAM,YAAY,OAAgB,SAA0B;EAC1D,IAAI,CAACC,wBAAAA,OAAO,KAAK,GAAG,OAAO;EAC3B,MAAM,UAAU,eAAe,KAAK;EACpC,OAAO,QAAQ,SAAS,YAAY,QAAQ,SAAS;CACvD;CACA,MAAM,iBAAiB,UAA4B;EACjD,IAAI,CAACA,wBAAAA,OAAO,KAAK,GAAG,OAAO;EAC3B,IAAI,MAAM,SAAS,cAAc;GAC/B,MAAM,UAAU,eAAe,KAAK;GACpC,IAAI,QAAQ,SAAS,UACnB,OACE,QAAQ,SAAS,cAAc,QAAQ,SAAS;GAEpD,OAAO,QAAQ,SAAS,WAAW,cAAc,QAAQ,WAAW;EACtE;EACA,OACE,MAAM,SAAS,sBACfC,wBAAAA,aAAa,MAAM,QAAQ,MAAM,UAChC,SAAS,MAAM,QAAQ,MAAM,KAC5B,SAAS,MAAM,QAAQ,aAAa;CAE1C;CACA,OAAO;EACL,eAAe,MAAM;GACnB,IACE,cAAc,KAAK,MAAM,KACxBD,wBAAAA,OAAO,KAAK,MAAM,KACjB,SAAS,KAAK,QAAQ,MAAM,KAC5B,MAAM,QAAQ,KAAK,SAAS,KAC5B,KAAK,UAAU,WAAW,GAE5B,OAAO,IAAI;EACf;EACA,cAAc,MAAM;GAClB,IACEA,wBAAAA,OAAO,KAAK,MAAM,KAClB,SAAS,KAAK,QAAQ,MAAM,KAC5B,MAAM,QAAQ,KAAK,SAAS,KAC5B,KAAK,UAAU,WAAW,GAE1B,OAAO,IAAI;EACf;CACF;AACF,CACF"}
@@ -0,0 +1,4 @@
1
+ //#region src/no-wall-clock-in-tests.d.ts
2
+ declare const _default: import("@oxlint/plugins").Plugin;
3
+ export = _default;
4
+ //# sourceMappingURL=no-wall-clock-in-tests.d.cts.map
@@ -0,0 +1,5 @@
1
+ //#region src/no-wall-clock-in-tests.d.ts
2
+ declare const _default: import("@oxlint/plugins").Plugin;
3
+ //#endregion
4
+ export { _default as default };
5
+ //# sourceMappingURL=no-wall-clock-in-tests.d.mts.map
@@ -0,0 +1,31 @@
1
+ import { a as testOnlyPlugin, i as propertyName, r as isNode, t as createBindingResolver } from "./test-rule-utils.mjs";
2
+ //#region src/no-wall-clock-in-tests.ts
3
+ var no_wall_clock_in_tests_default = testOnlyPlugin("no-wall-clock-in-tests", "noWallClock", "Use the @stll/test-kit clock instead of reading the wall clock in tests.", (report, getScope) => {
4
+ const resolveBinding = createBindingResolver(getScope);
5
+ const isGlobal = (value, name) => {
6
+ if (!isNode(value)) return false;
7
+ const binding = resolveBinding(value);
8
+ return binding.type === "global" && binding.name === name;
9
+ };
10
+ const isClockMember = (value) => {
11
+ if (!isNode(value)) return false;
12
+ if (value.type === "Identifier") {
13
+ const binding = resolveBinding(value);
14
+ if (binding.type === "global") return binding.name === "Date.now" || binding.name === "performance.now";
15
+ return binding.type === "const" && isClockMember(binding.initializer);
16
+ }
17
+ return value.type === "MemberExpression" && propertyName(value.property) === "now" && (isGlobal(value.object, "Date") || isGlobal(value.object, "performance"));
18
+ };
19
+ return {
20
+ CallExpression(node) {
21
+ if (isClockMember(node.callee) || isNode(node.callee) && isGlobal(node.callee, "Date") && Array.isArray(node.arguments) && node.arguments.length === 0) report(node);
22
+ },
23
+ NewExpression(node) {
24
+ if (isNode(node.callee) && isGlobal(node.callee, "Date") && Array.isArray(node.arguments) && node.arguments.length === 0) report(node);
25
+ }
26
+ };
27
+ });
28
+ //#endregion
29
+ export { no_wall_clock_in_tests_default as default };
30
+
31
+ //# sourceMappingURL=no-wall-clock-in-tests.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-wall-clock-in-tests.mjs","names":[],"sources":["../src/no-wall-clock-in-tests.ts"],"sourcesContent":["import {\n createBindingResolver,\n isNode,\n propertyName,\n testOnlyPlugin,\n} from \"./test-rule-utils\";\n\nexport default testOnlyPlugin(\n \"no-wall-clock-in-tests\",\n \"noWallClock\",\n \"Use the @stll/test-kit clock instead of reading the wall clock in tests.\",\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 isClockMember = (value: unknown): boolean => {\n if (!isNode(value)) return false;\n if (value.type === \"Identifier\") {\n const binding = resolveBinding(value);\n if (binding.type === \"global\")\n return (\n binding.name === \"Date.now\" || binding.name === \"performance.now\"\n );\n return binding.type === \"const\" && isClockMember(binding.initializer);\n }\n return (\n value.type === \"MemberExpression\" &&\n propertyName(value.property) === \"now\" &&\n (isGlobal(value.object, \"Date\") ||\n isGlobal(value.object, \"performance\"))\n );\n };\n return {\n CallExpression(node) {\n if (\n isClockMember(node.callee) ||\n (isNode(node.callee) &&\n isGlobal(node.callee, \"Date\") &&\n Array.isArray(node.arguments) &&\n node.arguments.length === 0)\n )\n report(node);\n },\n NewExpression(node) {\n if (\n isNode(node.callee) &&\n isGlobal(node.callee, \"Date\") &&\n Array.isArray(node.arguments) &&\n node.arguments.length === 0\n )\n report(node);\n },\n };\n },\n);\n"],"mappings":";;AAOA,IAAA,iCAAe,eACb,0BACA,eACA,6EACC,QAAQ,aAAa;CACpB,MAAM,iBAAiB,sBAAsB,QAAQ;CACrD,MAAM,YAAY,OAAgB,SAA0B;EAC1D,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;EAC3B,MAAM,UAAU,eAAe,KAAK;EACpC,OAAO,QAAQ,SAAS,YAAY,QAAQ,SAAS;CACvD;CACA,MAAM,iBAAiB,UAA4B;EACjD,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;EAC3B,IAAI,MAAM,SAAS,cAAc;GAC/B,MAAM,UAAU,eAAe,KAAK;GACpC,IAAI,QAAQ,SAAS,UACnB,OACE,QAAQ,SAAS,cAAc,QAAQ,SAAS;GAEpD,OAAO,QAAQ,SAAS,WAAW,cAAc,QAAQ,WAAW;EACtE;EACA,OACE,MAAM,SAAS,sBACf,aAAa,MAAM,QAAQ,MAAM,UAChC,SAAS,MAAM,QAAQ,MAAM,KAC5B,SAAS,MAAM,QAAQ,aAAa;CAE1C;CACA,OAAO;EACL,eAAe,MAAM;GACnB,IACE,cAAc,KAAK,MAAM,KACxB,OAAO,KAAK,MAAM,KACjB,SAAS,KAAK,QAAQ,MAAM,KAC5B,MAAM,QAAQ,KAAK,SAAS,KAC5B,KAAK,UAAU,WAAW,GAE5B,OAAO,IAAI;EACf;EACA,cAAc,MAAM;GAClB,IACE,OAAO,KAAK,MAAM,KAClB,SAAS,KAAK,QAAQ,MAAM,KAC5B,MAAM,QAAQ,KAAK,SAAS,KAC5B,KAAK,UAAU,WAAW,GAE1B,OAAO,IAAI;EACf;CACF;AACF,CACF"}
@@ -0,0 +1,143 @@
1
+ const require_utils = require("./utils.cjs");
2
+ let _oxlint_plugins = require("@oxlint/plugins");
3
+ //#region src/test-rule-utils.ts
4
+ const isNode = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "type") === "string";
5
+ /** `Node` is a loose view of oxlint's AST objects; every one of them carries its source span. */
6
+ const isScopeNode = (value) => typeof Reflect.get(value, "start") === "number" && typeof Reflect.get(value, "end") === "number";
7
+ const identifierName = (value) => isNode(value) && value.type === "Identifier" && typeof value.name === "string" ? value.name : null;
8
+ const propertyName = (value) => {
9
+ if (!isNode(value)) return null;
10
+ if (value.type === "Identifier" && typeof value.name === "string") return value.name;
11
+ if (value.type === "Literal" && typeof value.value === "string") return value.value;
12
+ return null;
13
+ };
14
+ const unknownBinding = { type: "unknown" };
15
+ const maximumBindingDepth = 32;
16
+ const createBindingResolver = (getScope) => {
17
+ const resolve = (node, visited, depth) => {
18
+ const name = identifierName(node);
19
+ if (name === null || depth >= maximumBindingDepth || visited.has(node)) return unknownBinding;
20
+ const nextVisited = new Set(visited);
21
+ nextVisited.add(node);
22
+ let scope = getScope(node);
23
+ while (scope !== null) {
24
+ const variable = scope.set.get(name);
25
+ if (variable === void 0) {
26
+ scope = scope.upper;
27
+ continue;
28
+ }
29
+ const definition = variable.defs.at(0);
30
+ if (definition === void 0) return {
31
+ type: "global",
32
+ name
33
+ };
34
+ if (definition.type === "ImportBinding") {
35
+ const specifier = definition.node;
36
+ if (!isNode(specifier) || !isNode(specifier.parent)) return unknownBinding;
37
+ const declaration = specifier.parent;
38
+ if (declaration.type !== "ImportDeclaration" || !isNode(declaration.source) || typeof declaration.source.value !== "string") return unknownBinding;
39
+ let imported = propertyName(specifier.imported);
40
+ if (specifier.type === "ImportDefaultSpecifier") imported = "default";
41
+ if (specifier.type === "ImportNamespaceSpecifier") imported = "*";
42
+ return imported === null ? unknownBinding : {
43
+ type: "import",
44
+ module: declaration.source.value,
45
+ imported
46
+ };
47
+ }
48
+ if (definition.type !== "Variable") return unknownBinding;
49
+ const declarator = definition.node;
50
+ if (!isNode(declarator) || declarator.type !== "VariableDeclarator" || !isNode(declarator.parent) || declarator.parent.type !== "VariableDeclaration" || declarator.parent.kind !== "const" || !isNode(declarator.init)) return unknownBinding;
51
+ if (identifierName(declarator.id) !== null) {
52
+ if (declarator.init.type !== "Identifier") return {
53
+ type: "const",
54
+ initializer: declarator.init
55
+ };
56
+ return resolve(declarator.init, nextVisited, depth + 1);
57
+ }
58
+ if (!isNode(declarator.id) || declarator.id.type !== "ObjectPattern") return unknownBinding;
59
+ const properties = Reflect.get(declarator.id, "properties");
60
+ if (!Array.isArray(properties)) return unknownBinding;
61
+ const property = properties.find((candidate) => isNode(candidate) && candidate.type === "Property" && candidate.computed !== true && identifierName(candidate.value) === name);
62
+ if (!isNode(property)) return unknownBinding;
63
+ const member = propertyName(property.key);
64
+ if (member === null || declarator.init.type !== "Identifier") return unknownBinding;
65
+ const source = resolve(declarator.init, nextVisited, depth + 1);
66
+ if (source.type === "import") return {
67
+ type: "import",
68
+ module: source.module,
69
+ imported: member
70
+ };
71
+ if (source.type !== "global") return unknownBinding;
72
+ if (source.name === "globalThis" || source.name === "window" || source.name === "self") return {
73
+ type: "global",
74
+ name: member
75
+ };
76
+ return {
77
+ type: "global",
78
+ name: `${source.name}.${member}`
79
+ };
80
+ }
81
+ return {
82
+ type: "global",
83
+ name
84
+ };
85
+ };
86
+ return (node) => resolve(node, /* @__PURE__ */ new Set(), 0);
87
+ };
88
+ const isTestFile = (context) => {
89
+ const filename = require_utils.filenameForContext(context);
90
+ return /(?:^|\/)(?:__tests__\/.*|[^/]+\.(?:test|spec)\.[^/]+)$/u.test(filename);
91
+ };
92
+ const testOnlyPlugin = (name, messageId, message, makeVisitors) => (0, _oxlint_plugins.eslintCompatPlugin)({
93
+ meta: { name },
94
+ rules: { [name]: {
95
+ meta: {
96
+ type: "problem",
97
+ messages: { [messageId]: message }
98
+ },
99
+ create(context) {
100
+ if (!isTestFile(context)) return {};
101
+ return makeVisitors((node) => context.report({
102
+ node,
103
+ messageId
104
+ }), (node) => {
105
+ if (!isScopeNode(node)) throw new Error(`Expected an oxlint AST node, got ${node.type}`);
106
+ return context.sourceCode.getScope(node);
107
+ });
108
+ }
109
+ } }
110
+ });
111
+ //#endregion
112
+ Object.defineProperty(exports, "createBindingResolver", {
113
+ enumerable: true,
114
+ get: function() {
115
+ return createBindingResolver;
116
+ }
117
+ });
118
+ Object.defineProperty(exports, "identifierName", {
119
+ enumerable: true,
120
+ get: function() {
121
+ return identifierName;
122
+ }
123
+ });
124
+ Object.defineProperty(exports, "isNode", {
125
+ enumerable: true,
126
+ get: function() {
127
+ return isNode;
128
+ }
129
+ });
130
+ Object.defineProperty(exports, "propertyName", {
131
+ enumerable: true,
132
+ get: function() {
133
+ return propertyName;
134
+ }
135
+ });
136
+ Object.defineProperty(exports, "testOnlyPlugin", {
137
+ enumerable: true,
138
+ get: function() {
139
+ return testOnlyPlugin;
140
+ }
141
+ });
142
+
143
+ //# sourceMappingURL=test-rule-utils.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-rule-utils.cjs","names":["filenameForContext","eslintCompatPlugin"],"sources":["../src/test-rule-utils.ts"],"sourcesContent":["import type { ESTree, Scope } from \"@oxlint/plugins\";\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nimport { filenameForContext } from \"./utils\";\n\nexport type Node = {\n type: string;\n range: [number, number];\n arguments?: unknown;\n callee?: unknown;\n computed?: unknown;\n expressions?: unknown;\n id?: unknown;\n imported?: unknown;\n init?: unknown;\n elements?: unknown;\n operator?: unknown;\n key?: unknown;\n label?: unknown;\n kind?: unknown;\n left?: unknown;\n local?: unknown;\n meta?: unknown;\n name?: unknown;\n object?: unknown;\n parent?: unknown;\n property?: unknown;\n quasis?: unknown;\n right?: unknown;\n body?: unknown;\n source?: unknown;\n specifiers?: unknown;\n value?: unknown;\n};\n\nexport const isNode = (value: unknown): value is Node =>\n typeof value === \"object\" &&\n value !== null &&\n typeof Reflect.get(value, \"type\") === \"string\";\n\n/** `Node` is a loose view of oxlint's AST objects; every one of them carries its source span. */\nconst isScopeNode = (value: Node): value is Node & ESTree.Node =>\n typeof Reflect.get(value, \"start\") === \"number\" &&\n typeof Reflect.get(value, \"end\") === \"number\";\n\nexport const identifierName = (value: unknown): string | null =>\n isNode(value) && value.type === \"Identifier\" && typeof value.name === \"string\"\n ? value.name\n : null;\n\nexport const propertyName = (value: unknown): string | null => {\n if (!isNode(value)) return null;\n if (value.type === \"Identifier\" && typeof value.name === \"string\")\n return value.name;\n if (value.type === \"Literal\" && typeof value.value === \"string\")\n return value.value;\n return null;\n};\n\nexport const memberName = (value: unknown): string | null => {\n if (!isNode(value) || value.type !== \"MemberExpression\") return null;\n const object = identifierName(value.object);\n const property = propertyName(value.property);\n return object !== null && property !== null ? `${object}.${property}` : null;\n};\n\nexport type Binding =\n | { type: \"global\"; name: string }\n | { type: \"import\"; module: string; imported: string }\n | { type: \"const\"; initializer: Node }\n | { type: \"unknown\" };\n\nconst unknownBinding = { type: \"unknown\" } as const;\nconst maximumBindingDepth = 32;\n\nexport const createBindingResolver = (getScope: (node: Node) => Scope) => {\n const resolve = (\n node: Node,\n visited: ReadonlySet<object>,\n depth: number,\n ): Binding => {\n const name = identifierName(node);\n if (name === null || depth >= maximumBindingDepth || visited.has(node))\n return unknownBinding;\n\n const nextVisited = new Set(visited);\n nextVisited.add(node);\n let scope: Scope | null = getScope(node);\n while (scope !== null) {\n const variable = scope.set.get(name);\n if (variable === undefined) {\n scope = scope.upper;\n continue;\n }\n const definition = variable.defs.at(0);\n if (definition === undefined) return { type: \"global\", name };\n if (definition.type === \"ImportBinding\") {\n const specifier = definition.node as unknown;\n if (!isNode(specifier) || !isNode(specifier.parent))\n return unknownBinding;\n const declaration = specifier.parent;\n if (\n declaration.type !== \"ImportDeclaration\" ||\n !isNode(declaration.source) ||\n typeof declaration.source.value !== \"string\"\n )\n return unknownBinding;\n let imported = propertyName(specifier.imported);\n if (specifier.type === \"ImportDefaultSpecifier\") imported = \"default\";\n if (specifier.type === \"ImportNamespaceSpecifier\") imported = \"*\";\n return imported === null\n ? unknownBinding\n : { type: \"import\", module: declaration.source.value, imported };\n }\n if (definition.type !== \"Variable\") return unknownBinding;\n const declarator = definition.node as unknown;\n if (\n !isNode(declarator) ||\n declarator.type !== \"VariableDeclarator\" ||\n !isNode(declarator.parent) ||\n declarator.parent.type !== \"VariableDeclaration\" ||\n declarator.parent.kind !== \"const\" ||\n !isNode(declarator.init)\n )\n return unknownBinding;\n if (identifierName(declarator.id) !== null) {\n if (declarator.init.type !== \"Identifier\")\n return { type: \"const\", initializer: declarator.init };\n return resolve(declarator.init, nextVisited, depth + 1);\n }\n if (!isNode(declarator.id) || declarator.id.type !== \"ObjectPattern\")\n return unknownBinding;\n const properties = Reflect.get(declarator.id, \"properties\");\n if (!Array.isArray(properties)) return unknownBinding;\n const property = properties.find(\n (candidate) =>\n isNode(candidate) &&\n candidate.type === \"Property\" &&\n candidate.computed !== true &&\n identifierName(candidate.value) === name,\n );\n if (!isNode(property)) return unknownBinding;\n const member = propertyName(property.key);\n if (member === null || declarator.init.type !== \"Identifier\")\n return unknownBinding;\n const source = resolve(declarator.init, nextVisited, depth + 1);\n if (source.type === \"import\")\n return { type: \"import\", module: source.module, imported: member };\n if (source.type !== \"global\") return unknownBinding;\n if (\n source.name === \"globalThis\" ||\n source.name === \"window\" ||\n source.name === \"self\"\n )\n return { type: \"global\", name: member };\n return { type: \"global\", name: `${source.name}.${member}` };\n }\n return { type: \"global\", name };\n };\n\n return (node: Node): Binding => resolve(node, new Set(), 0);\n};\n\nexport const isTestFile = (context: object): boolean => {\n const filename = filenameForContext(context);\n return /(?:^|\\/)(?:__tests__\\/.*|[^/]+\\.(?:test|spec)\\.[^/]+)$/u.test(\n filename,\n );\n};\n\nexport const testOnlyPlugin = (\n name: string,\n messageId: string,\n message: string,\n makeVisitors: (\n report: (node: Node) => void,\n getScope: (node: Node) => Scope,\n ) => Record<string, (node: Node) => void>,\n) =>\n eslintCompatPlugin({\n meta: { name },\n rules: {\n [name]: {\n meta: { type: \"problem\", messages: { [messageId]: message } },\n create(context) {\n if (!isTestFile(context)) return {};\n return makeVisitors(\n (node) => context.report({ node, messageId }),\n (node) => {\n if (!isScopeNode(node))\n throw new Error(\n `Expected an oxlint AST node, got ${node.type}`,\n );\n return context.sourceCode.getScope(node);\n },\n );\n },\n },\n },\n });\n"],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ import { t as filenameForContext } from "./utils.mjs";
2
+ import { eslintCompatPlugin } from "@oxlint/plugins";
3
+ //#region src/test-rule-utils.ts
4
+ const isNode = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "type") === "string";
5
+ /** `Node` is a loose view of oxlint's AST objects; every one of them carries its source span. */
6
+ const isScopeNode = (value) => typeof Reflect.get(value, "start") === "number" && typeof Reflect.get(value, "end") === "number";
7
+ const identifierName = (value) => isNode(value) && value.type === "Identifier" && typeof value.name === "string" ? value.name : null;
8
+ const propertyName = (value) => {
9
+ if (!isNode(value)) return null;
10
+ if (value.type === "Identifier" && typeof value.name === "string") return value.name;
11
+ if (value.type === "Literal" && typeof value.value === "string") return value.value;
12
+ return null;
13
+ };
14
+ const unknownBinding = { type: "unknown" };
15
+ const maximumBindingDepth = 32;
16
+ const createBindingResolver = (getScope) => {
17
+ const resolve = (node, visited, depth) => {
18
+ const name = identifierName(node);
19
+ if (name === null || depth >= maximumBindingDepth || visited.has(node)) return unknownBinding;
20
+ const nextVisited = new Set(visited);
21
+ nextVisited.add(node);
22
+ let scope = getScope(node);
23
+ while (scope !== null) {
24
+ const variable = scope.set.get(name);
25
+ if (variable === void 0) {
26
+ scope = scope.upper;
27
+ continue;
28
+ }
29
+ const definition = variable.defs.at(0);
30
+ if (definition === void 0) return {
31
+ type: "global",
32
+ name
33
+ };
34
+ if (definition.type === "ImportBinding") {
35
+ const specifier = definition.node;
36
+ if (!isNode(specifier) || !isNode(specifier.parent)) return unknownBinding;
37
+ const declaration = specifier.parent;
38
+ if (declaration.type !== "ImportDeclaration" || !isNode(declaration.source) || typeof declaration.source.value !== "string") return unknownBinding;
39
+ let imported = propertyName(specifier.imported);
40
+ if (specifier.type === "ImportDefaultSpecifier") imported = "default";
41
+ if (specifier.type === "ImportNamespaceSpecifier") imported = "*";
42
+ return imported === null ? unknownBinding : {
43
+ type: "import",
44
+ module: declaration.source.value,
45
+ imported
46
+ };
47
+ }
48
+ if (definition.type !== "Variable") return unknownBinding;
49
+ const declarator = definition.node;
50
+ if (!isNode(declarator) || declarator.type !== "VariableDeclarator" || !isNode(declarator.parent) || declarator.parent.type !== "VariableDeclaration" || declarator.parent.kind !== "const" || !isNode(declarator.init)) return unknownBinding;
51
+ if (identifierName(declarator.id) !== null) {
52
+ if (declarator.init.type !== "Identifier") return {
53
+ type: "const",
54
+ initializer: declarator.init
55
+ };
56
+ return resolve(declarator.init, nextVisited, depth + 1);
57
+ }
58
+ if (!isNode(declarator.id) || declarator.id.type !== "ObjectPattern") return unknownBinding;
59
+ const properties = Reflect.get(declarator.id, "properties");
60
+ if (!Array.isArray(properties)) return unknownBinding;
61
+ const property = properties.find((candidate) => isNode(candidate) && candidate.type === "Property" && candidate.computed !== true && identifierName(candidate.value) === name);
62
+ if (!isNode(property)) return unknownBinding;
63
+ const member = propertyName(property.key);
64
+ if (member === null || declarator.init.type !== "Identifier") return unknownBinding;
65
+ const source = resolve(declarator.init, nextVisited, depth + 1);
66
+ if (source.type === "import") return {
67
+ type: "import",
68
+ module: source.module,
69
+ imported: member
70
+ };
71
+ if (source.type !== "global") return unknownBinding;
72
+ if (source.name === "globalThis" || source.name === "window" || source.name === "self") return {
73
+ type: "global",
74
+ name: member
75
+ };
76
+ return {
77
+ type: "global",
78
+ name: `${source.name}.${member}`
79
+ };
80
+ }
81
+ return {
82
+ type: "global",
83
+ name
84
+ };
85
+ };
86
+ return (node) => resolve(node, /* @__PURE__ */ new Set(), 0);
87
+ };
88
+ const isTestFile = (context) => {
89
+ const filename = filenameForContext(context);
90
+ return /(?:^|\/)(?:__tests__\/.*|[^/]+\.(?:test|spec)\.[^/]+)$/u.test(filename);
91
+ };
92
+ const testOnlyPlugin = (name, messageId, message, makeVisitors) => eslintCompatPlugin({
93
+ meta: { name },
94
+ rules: { [name]: {
95
+ meta: {
96
+ type: "problem",
97
+ messages: { [messageId]: message }
98
+ },
99
+ create(context) {
100
+ if (!isTestFile(context)) return {};
101
+ return makeVisitors((node) => context.report({
102
+ node,
103
+ messageId
104
+ }), (node) => {
105
+ if (!isScopeNode(node)) throw new Error(`Expected an oxlint AST node, got ${node.type}`);
106
+ return context.sourceCode.getScope(node);
107
+ });
108
+ }
109
+ } }
110
+ });
111
+ //#endregion
112
+ export { testOnlyPlugin as a, propertyName as i, identifierName as n, isNode as r, createBindingResolver as t };
113
+
114
+ //# sourceMappingURL=test-rule-utils.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-rule-utils.mjs","names":[],"sources":["../src/test-rule-utils.ts"],"sourcesContent":["import type { ESTree, Scope } from \"@oxlint/plugins\";\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nimport { filenameForContext } from \"./utils\";\n\nexport type Node = {\n type: string;\n range: [number, number];\n arguments?: unknown;\n callee?: unknown;\n computed?: unknown;\n expressions?: unknown;\n id?: unknown;\n imported?: unknown;\n init?: unknown;\n elements?: unknown;\n operator?: unknown;\n key?: unknown;\n label?: unknown;\n kind?: unknown;\n left?: unknown;\n local?: unknown;\n meta?: unknown;\n name?: unknown;\n object?: unknown;\n parent?: unknown;\n property?: unknown;\n quasis?: unknown;\n right?: unknown;\n body?: unknown;\n source?: unknown;\n specifiers?: unknown;\n value?: unknown;\n};\n\nexport const isNode = (value: unknown): value is Node =>\n typeof value === \"object\" &&\n value !== null &&\n typeof Reflect.get(value, \"type\") === \"string\";\n\n/** `Node` is a loose view of oxlint's AST objects; every one of them carries its source span. */\nconst isScopeNode = (value: Node): value is Node & ESTree.Node =>\n typeof Reflect.get(value, \"start\") === \"number\" &&\n typeof Reflect.get(value, \"end\") === \"number\";\n\nexport const identifierName = (value: unknown): string | null =>\n isNode(value) && value.type === \"Identifier\" && typeof value.name === \"string\"\n ? value.name\n : null;\n\nexport const propertyName = (value: unknown): string | null => {\n if (!isNode(value)) return null;\n if (value.type === \"Identifier\" && typeof value.name === \"string\")\n return value.name;\n if (value.type === \"Literal\" && typeof value.value === \"string\")\n return value.value;\n return null;\n};\n\nexport const memberName = (value: unknown): string | null => {\n if (!isNode(value) || value.type !== \"MemberExpression\") return null;\n const object = identifierName(value.object);\n const property = propertyName(value.property);\n return object !== null && property !== null ? `${object}.${property}` : null;\n};\n\nexport type Binding =\n | { type: \"global\"; name: string }\n | { type: \"import\"; module: string; imported: string }\n | { type: \"const\"; initializer: Node }\n | { type: \"unknown\" };\n\nconst unknownBinding = { type: \"unknown\" } as const;\nconst maximumBindingDepth = 32;\n\nexport const createBindingResolver = (getScope: (node: Node) => Scope) => {\n const resolve = (\n node: Node,\n visited: ReadonlySet<object>,\n depth: number,\n ): Binding => {\n const name = identifierName(node);\n if (name === null || depth >= maximumBindingDepth || visited.has(node))\n return unknownBinding;\n\n const nextVisited = new Set(visited);\n nextVisited.add(node);\n let scope: Scope | null = getScope(node);\n while (scope !== null) {\n const variable = scope.set.get(name);\n if (variable === undefined) {\n scope = scope.upper;\n continue;\n }\n const definition = variable.defs.at(0);\n if (definition === undefined) return { type: \"global\", name };\n if (definition.type === \"ImportBinding\") {\n const specifier = definition.node as unknown;\n if (!isNode(specifier) || !isNode(specifier.parent))\n return unknownBinding;\n const declaration = specifier.parent;\n if (\n declaration.type !== \"ImportDeclaration\" ||\n !isNode(declaration.source) ||\n typeof declaration.source.value !== \"string\"\n )\n return unknownBinding;\n let imported = propertyName(specifier.imported);\n if (specifier.type === \"ImportDefaultSpecifier\") imported = \"default\";\n if (specifier.type === \"ImportNamespaceSpecifier\") imported = \"*\";\n return imported === null\n ? unknownBinding\n : { type: \"import\", module: declaration.source.value, imported };\n }\n if (definition.type !== \"Variable\") return unknownBinding;\n const declarator = definition.node as unknown;\n if (\n !isNode(declarator) ||\n declarator.type !== \"VariableDeclarator\" ||\n !isNode(declarator.parent) ||\n declarator.parent.type !== \"VariableDeclaration\" ||\n declarator.parent.kind !== \"const\" ||\n !isNode(declarator.init)\n )\n return unknownBinding;\n if (identifierName(declarator.id) !== null) {\n if (declarator.init.type !== \"Identifier\")\n return { type: \"const\", initializer: declarator.init };\n return resolve(declarator.init, nextVisited, depth + 1);\n }\n if (!isNode(declarator.id) || declarator.id.type !== \"ObjectPattern\")\n return unknownBinding;\n const properties = Reflect.get(declarator.id, \"properties\");\n if (!Array.isArray(properties)) return unknownBinding;\n const property = properties.find(\n (candidate) =>\n isNode(candidate) &&\n candidate.type === \"Property\" &&\n candidate.computed !== true &&\n identifierName(candidate.value) === name,\n );\n if (!isNode(property)) return unknownBinding;\n const member = propertyName(property.key);\n if (member === null || declarator.init.type !== \"Identifier\")\n return unknownBinding;\n const source = resolve(declarator.init, nextVisited, depth + 1);\n if (source.type === \"import\")\n return { type: \"import\", module: source.module, imported: member };\n if (source.type !== \"global\") return unknownBinding;\n if (\n source.name === \"globalThis\" ||\n source.name === \"window\" ||\n source.name === \"self\"\n )\n return { type: \"global\", name: member };\n return { type: \"global\", name: `${source.name}.${member}` };\n }\n return { type: \"global\", name };\n };\n\n return (node: Node): Binding => resolve(node, new Set(), 0);\n};\n\nexport const isTestFile = (context: object): boolean => {\n const filename = filenameForContext(context);\n return /(?:^|\\/)(?:__tests__\\/.*|[^/]+\\.(?:test|spec)\\.[^/]+)$/u.test(\n filename,\n );\n};\n\nexport const testOnlyPlugin = (\n name: string,\n messageId: string,\n message: string,\n makeVisitors: (\n report: (node: Node) => void,\n getScope: (node: Node) => Scope,\n ) => Record<string, (node: Node) => void>,\n) =>\n eslintCompatPlugin({\n meta: { name },\n rules: {\n [name]: {\n meta: { type: \"problem\", messages: { [messageId]: message } },\n create(context) {\n if (!isTestFile(context)) return {};\n return makeVisitors(\n (node) => context.report({ node, messageId }),\n (node) => {\n if (!isScopeNode(node))\n throw new Error(\n `Expected an oxlint AST node, got ${node.type}`,\n );\n return context.sourceCode.getScope(node);\n },\n );\n },\n },\n },\n });\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/oxlint-plugin",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Portable static-safety and route-query rules for Oxlint consumers.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -91,6 +91,72 @@
91
91
  "default": "./dist/require-loader-prefetch.cjs"
92
92
  },
93
93
  "default": "./dist/require-loader-prefetch.mjs"
94
+ },
95
+ "./no-wall-clock-in-tests": {
96
+ "import": {
97
+ "types": "./dist/no-wall-clock-in-tests.d.mts",
98
+ "default": "./dist/no-wall-clock-in-tests.mjs"
99
+ },
100
+ "require": {
101
+ "types": "./dist/no-wall-clock-in-tests.d.cts",
102
+ "default": "./dist/no-wall-clock-in-tests.cjs"
103
+ },
104
+ "default": "./dist/no-wall-clock-in-tests.mjs"
105
+ },
106
+ "./no-real-timer-waits-in-tests": {
107
+ "import": {
108
+ "types": "./dist/no-real-timer-waits-in-tests.d.mts",
109
+ "default": "./dist/no-real-timer-waits-in-tests.mjs"
110
+ },
111
+ "require": {
112
+ "types": "./dist/no-real-timer-waits-in-tests.d.cts",
113
+ "default": "./dist/no-real-timer-waits-in-tests.cjs"
114
+ },
115
+ "default": "./dist/no-real-timer-waits-in-tests.mjs"
116
+ },
117
+ "./no-unseeded-random-in-tests": {
118
+ "import": {
119
+ "types": "./dist/no-unseeded-random-in-tests.d.mts",
120
+ "default": "./dist/no-unseeded-random-in-tests.mjs"
121
+ },
122
+ "require": {
123
+ "types": "./dist/no-unseeded-random-in-tests.d.cts",
124
+ "default": "./dist/no-unseeded-random-in-tests.cjs"
125
+ },
126
+ "default": "./dist/no-unseeded-random-in-tests.mjs"
127
+ },
128
+ "./no-real-network-in-tests": {
129
+ "import": {
130
+ "types": "./dist/no-real-network-in-tests.d.mts",
131
+ "default": "./dist/no-real-network-in-tests.mjs"
132
+ },
133
+ "require": {
134
+ "types": "./dist/no-real-network-in-tests.d.cts",
135
+ "default": "./dist/no-real-network-in-tests.cjs"
136
+ },
137
+ "default": "./dist/no-real-network-in-tests.mjs"
138
+ },
139
+ "./no-checkout-writes-in-tests": {
140
+ "import": {
141
+ "types": "./dist/no-checkout-writes-in-tests.d.mts",
142
+ "default": "./dist/no-checkout-writes-in-tests.mjs"
143
+ },
144
+ "require": {
145
+ "types": "./dist/no-checkout-writes-in-tests.d.cts",
146
+ "default": "./dist/no-checkout-writes-in-tests.cjs"
147
+ },
148
+ "default": "./dist/no-checkout-writes-in-tests.mjs"
149
+ },
150
+ "./no-wall-clock-bound": {
151
+ "import": {
152
+ "types": "./dist/no-wall-clock-bound.d.mts",
153
+ "default": "./dist/no-wall-clock-bound.mjs"
154
+ },
155
+ "require": {
156
+ "types": "./dist/no-wall-clock-bound.d.cts",
157
+ "default": "./dist/no-wall-clock-bound.cjs"
158
+ },
159
+ "default": "./dist/no-wall-clock-bound.mjs"
94
160
  }
95
161
  },
96
162
  "publishConfig": {