@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
package/dist/index.cjs CHANGED
@@ -23,16 +23,46 @@ const routeQueryRules = {
23
23
  "no-raw-route-query-client/no-raw-route-query-client": "error",
24
24
  "require-loader-prefetch/require-loader-prefetch": "error"
25
25
  };
26
+ const noWallClockInTestsPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-in-tests";
27
+ const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-timer-waits-in-tests";
28
+ const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
29
+ const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
30
+ const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
31
+ const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
32
+ const testReliabilityPluginSpecifiers = [
33
+ noWallClockInTestsPluginSpecifier,
34
+ noRealTimerWaitsInTestsPluginSpecifier,
35
+ noUnseededRandomInTestsPluginSpecifier,
36
+ noRealNetworkInTestsPluginSpecifier,
37
+ noCheckoutWritesInTestsPluginSpecifier,
38
+ noWallClockBoundPluginSpecifier
39
+ ];
40
+ const testReliabilityRules = {
41
+ "no-wall-clock-in-tests/no-wall-clock-in-tests": "error",
42
+ "no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error",
43
+ "no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error",
44
+ "no-real-network-in-tests/no-real-network-in-tests": "error",
45
+ "no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error",
46
+ "no-wall-clock-bound/no-wall-clock-bound": "error"
47
+ };
26
48
  //#endregion
27
49
  exports.noAnyCastsPluginSpecifier = noAnyCastsPluginSpecifier;
50
+ exports.noCheckoutWritesInTestsPluginSpecifier = noCheckoutWritesInTestsPluginSpecifier;
28
51
  exports.noDangerousTypeAssertionsPluginSpecifier = noDangerousTypeAssertionsPluginSpecifier;
29
52
  exports.noPartialRecordSatisfiesPluginSpecifier = noPartialRecordSatisfiesPluginSpecifier;
30
53
  exports.noRawRouteQueryClientPluginSpecifier = noRawRouteQueryClientPluginSpecifier;
54
+ exports.noRealNetworkInTestsPluginSpecifier = noRealNetworkInTestsPluginSpecifier;
55
+ exports.noRealTimerWaitsInTestsPluginSpecifier = noRealTimerWaitsInTestsPluginSpecifier;
31
56
  exports.noUnsafeInnerHtmlPluginSpecifier = noUnsafeInnerHtmlPluginSpecifier;
57
+ exports.noUnseededRandomInTestsPluginSpecifier = noUnseededRandomInTestsPluginSpecifier;
58
+ exports.noWallClockBoundPluginSpecifier = noWallClockBoundPluginSpecifier;
59
+ exports.noWallClockInTestsPluginSpecifier = noWallClockInTestsPluginSpecifier;
32
60
  exports.portableSafetyPluginSpecifiers = portableSafetyPluginSpecifiers;
33
61
  exports.portableSafetyRules = portableSafetyRules;
34
62
  exports.requireLoaderPrefetchPluginSpecifier = requireLoaderPrefetchPluginSpecifier;
35
63
  exports.routeQueryPluginSpecifiers = routeQueryPluginSpecifiers;
36
64
  exports.routeQueryRules = routeQueryRules;
65
+ exports.testReliabilityPluginSpecifiers = testReliabilityPluginSpecifiers;
66
+ exports.testReliabilityRules = testReliabilityRules;
37
67
 
38
68
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { OxlintConfig } from \"oxlint\";\n\ntype JsPlugins = NonNullable<OxlintConfig[\"jsPlugins\"]>;\ntype Rules = NonNullable<OxlintConfig[\"rules\"]>;\n\nexport const noAnyCastsPluginSpecifier = \"@stll/oxlint-plugin/no-any-casts\";\nexport const noDangerousTypeAssertionsPluginSpecifier =\n \"@stll/oxlint-plugin/no-dangerous-type-assertions\";\nexport const noPartialRecordSatisfiesPluginSpecifier =\n \"@stll/oxlint-plugin/no-partial-record-satisfies\";\nexport const noUnsafeInnerHtmlPluginSpecifier =\n \"@stll/oxlint-plugin/no-unsafe-inner-html\";\nexport const noRawRouteQueryClientPluginSpecifier =\n \"@stll/oxlint-plugin/no-raw-route-query-client\";\nexport const requireLoaderPrefetchPluginSpecifier =\n \"@stll/oxlint-plugin/require-loader-prefetch\";\n\nexport const portableSafetyPluginSpecifiers = [\n noAnyCastsPluginSpecifier,\n noDangerousTypeAssertionsPluginSpecifier,\n noPartialRecordSatisfiesPluginSpecifier,\n noUnsafeInnerHtmlPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const portableSafetyRules = {\n \"no-any-casts/no-any-casts\": \"error\",\n \"no-dangerous-type-assertions/no-dangerous-type-assertions\": \"error\",\n \"no-partial-record-satisfies/no-partial-record-satisfies\": \"error\",\n \"no-unsafe-inner-html/no-unsafe-inner-html\": \"error\",\n} satisfies Rules;\n\nexport const routeQueryPluginSpecifiers = [\n noRawRouteQueryClientPluginSpecifier,\n requireLoaderPrefetchPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const routeQueryRules = {\n \"no-raw-route-query-client/no-raw-route-query-client\": \"error\",\n \"require-loader-prefetch/require-loader-prefetch\": \"error\",\n} satisfies Rules;\n"],"mappings":";;AAKA,MAAa,4BAA4B;AACzC,MAAa,2CACX;AACF,MAAa,0CACX;AACF,MAAa,mCACX;AACF,MAAa,uCACX;AACF,MAAa,uCACX;AAEF,MAAa,iCAAiC;CAC5C;CACA;CACA;CACA;AACF;AAEA,MAAa,sBAAsB;CACjC,6BAA6B;CAC7B,6DAA6D;CAC7D,2DAA2D;CAC3D,6CAA6C;AAC/C;AAEA,MAAa,6BAA6B,CACxC,sCACA,oCACF;AAEA,MAAa,kBAAkB;CAC7B,uDAAuD;CACvD,mDAAmD;AACrD"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { OxlintConfig } from \"oxlint\";\n\ntype JsPlugins = NonNullable<OxlintConfig[\"jsPlugins\"]>;\ntype Rules = NonNullable<OxlintConfig[\"rules\"]>;\n\nexport const noAnyCastsPluginSpecifier = \"@stll/oxlint-plugin/no-any-casts\";\nexport const noDangerousTypeAssertionsPluginSpecifier =\n \"@stll/oxlint-plugin/no-dangerous-type-assertions\";\nexport const noPartialRecordSatisfiesPluginSpecifier =\n \"@stll/oxlint-plugin/no-partial-record-satisfies\";\nexport const noUnsafeInnerHtmlPluginSpecifier =\n \"@stll/oxlint-plugin/no-unsafe-inner-html\";\nexport const noRawRouteQueryClientPluginSpecifier =\n \"@stll/oxlint-plugin/no-raw-route-query-client\";\nexport const requireLoaderPrefetchPluginSpecifier =\n \"@stll/oxlint-plugin/require-loader-prefetch\";\n\nexport const portableSafetyPluginSpecifiers = [\n noAnyCastsPluginSpecifier,\n noDangerousTypeAssertionsPluginSpecifier,\n noPartialRecordSatisfiesPluginSpecifier,\n noUnsafeInnerHtmlPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const portableSafetyRules = {\n \"no-any-casts/no-any-casts\": \"error\",\n \"no-dangerous-type-assertions/no-dangerous-type-assertions\": \"error\",\n \"no-partial-record-satisfies/no-partial-record-satisfies\": \"error\",\n \"no-unsafe-inner-html/no-unsafe-inner-html\": \"error\",\n} satisfies Rules;\n\nexport const routeQueryPluginSpecifiers = [\n noRawRouteQueryClientPluginSpecifier,\n requireLoaderPrefetchPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const routeQueryRules = {\n \"no-raw-route-query-client/no-raw-route-query-client\": \"error\",\n \"require-loader-prefetch/require-loader-prefetch\": \"error\",\n} satisfies Rules;\n\nexport const noWallClockInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-wall-clock-in-tests\";\nexport const noRealTimerWaitsInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-real-timer-waits-in-tests\";\nexport const noUnseededRandomInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-unseeded-random-in-tests\";\nexport const noRealNetworkInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-real-network-in-tests\";\nexport const noCheckoutWritesInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-checkout-writes-in-tests\";\nexport const noWallClockBoundPluginSpecifier =\n \"@stll/oxlint-plugin/no-wall-clock-bound\";\n\n// Intentionally separate from every preset: consumers adopt these with a\n// shrink-only test baseline.\nexport const testReliabilityPluginSpecifiers = [\n noWallClockInTestsPluginSpecifier,\n noRealTimerWaitsInTestsPluginSpecifier,\n noUnseededRandomInTestsPluginSpecifier,\n noRealNetworkInTestsPluginSpecifier,\n noCheckoutWritesInTestsPluginSpecifier,\n noWallClockBoundPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const testReliabilityRules = {\n \"no-wall-clock-in-tests/no-wall-clock-in-tests\": \"error\",\n \"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests\": \"error\",\n \"no-unseeded-random-in-tests/no-unseeded-random-in-tests\": \"error\",\n \"no-real-network-in-tests/no-real-network-in-tests\": \"error\",\n \"no-checkout-writes-in-tests/no-checkout-writes-in-tests\": \"error\",\n \"no-wall-clock-bound/no-wall-clock-bound\": \"error\",\n} satisfies Rules;\n"],"mappings":";;AAKA,MAAa,4BAA4B;AACzC,MAAa,2CACX;AACF,MAAa,0CACX;AACF,MAAa,mCACX;AACF,MAAa,uCACX;AACF,MAAa,uCACX;AAEF,MAAa,iCAAiC;CAC5C;CACA;CACA;CACA;AACF;AAEA,MAAa,sBAAsB;CACjC,6BAA6B;CAC7B,6DAA6D;CAC7D,2DAA2D;CAC3D,6CAA6C;AAC/C;AAEA,MAAa,6BAA6B,CACxC,sCACA,oCACF;AAEA,MAAa,kBAAkB;CAC7B,uDAAuD;CACvD,mDAAmD;AACrD;AAEA,MAAa,oCACX;AACF,MAAa,yCACX;AACF,MAAa,yCACX;AACF,MAAa,sCACX;AACF,MAAa,yCACX;AACF,MAAa,kCACX;AAIF,MAAa,kCAAkC;CAC7C;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,uBAAuB;CAClC,iDAAiD;CACjD,6DAA6D;CAC7D,2DAA2D;CAC3D,qDAAqD;CACrD,2DAA2D;CAC3D,2CAA2C;AAC7C"}
package/dist/index.d.cts CHANGED
@@ -17,6 +17,21 @@ declare const routeQueryRules: {
17
17
  "no-raw-route-query-client/no-raw-route-query-client": "error";
18
18
  "require-loader-prefetch/require-loader-prefetch": "error";
19
19
  };
20
+ declare const noWallClockInTestsPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-in-tests";
21
+ declare const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-timer-waits-in-tests";
22
+ declare const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
23
+ declare const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
24
+ declare const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
25
+ declare const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
26
+ declare const testReliabilityPluginSpecifiers: string[];
27
+ declare const testReliabilityRules: {
28
+ "no-wall-clock-in-tests/no-wall-clock-in-tests": "error";
29
+ "no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error";
30
+ "no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error";
31
+ "no-real-network-in-tests/no-real-network-in-tests": "error";
32
+ "no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error";
33
+ "no-wall-clock-bound/no-wall-clock-bound": "error";
34
+ };
20
35
  //#endregion
21
- export { noAnyCastsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules };
36
+ export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
22
37
  //# sourceMappingURL=index.d.cts.map
package/dist/index.d.mts CHANGED
@@ -17,6 +17,21 @@ declare const routeQueryRules: {
17
17
  "no-raw-route-query-client/no-raw-route-query-client": "error";
18
18
  "require-loader-prefetch/require-loader-prefetch": "error";
19
19
  };
20
+ declare const noWallClockInTestsPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-in-tests";
21
+ declare const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-timer-waits-in-tests";
22
+ declare const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
23
+ declare const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
24
+ declare const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
25
+ declare const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
26
+ declare const testReliabilityPluginSpecifiers: string[];
27
+ declare const testReliabilityRules: {
28
+ "no-wall-clock-in-tests/no-wall-clock-in-tests": "error";
29
+ "no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error";
30
+ "no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error";
31
+ "no-real-network-in-tests/no-real-network-in-tests": "error";
32
+ "no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error";
33
+ "no-wall-clock-bound/no-wall-clock-bound": "error";
34
+ };
20
35
  //#endregion
21
- export { noAnyCastsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules };
36
+ export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
22
37
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -22,7 +22,29 @@ const routeQueryRules = {
22
22
  "no-raw-route-query-client/no-raw-route-query-client": "error",
23
23
  "require-loader-prefetch/require-loader-prefetch": "error"
24
24
  };
25
+ const noWallClockInTestsPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-in-tests";
26
+ const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-timer-waits-in-tests";
27
+ const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
28
+ const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
29
+ const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
30
+ const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
31
+ const testReliabilityPluginSpecifiers = [
32
+ noWallClockInTestsPluginSpecifier,
33
+ noRealTimerWaitsInTestsPluginSpecifier,
34
+ noUnseededRandomInTestsPluginSpecifier,
35
+ noRealNetworkInTestsPluginSpecifier,
36
+ noCheckoutWritesInTestsPluginSpecifier,
37
+ noWallClockBoundPluginSpecifier
38
+ ];
39
+ const testReliabilityRules = {
40
+ "no-wall-clock-in-tests/no-wall-clock-in-tests": "error",
41
+ "no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error",
42
+ "no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error",
43
+ "no-real-network-in-tests/no-real-network-in-tests": "error",
44
+ "no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error",
45
+ "no-wall-clock-bound/no-wall-clock-bound": "error"
46
+ };
25
47
  //#endregion
26
- export { noAnyCastsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules };
48
+ export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
27
49
 
28
50
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { OxlintConfig } from \"oxlint\";\n\ntype JsPlugins = NonNullable<OxlintConfig[\"jsPlugins\"]>;\ntype Rules = NonNullable<OxlintConfig[\"rules\"]>;\n\nexport const noAnyCastsPluginSpecifier = \"@stll/oxlint-plugin/no-any-casts\";\nexport const noDangerousTypeAssertionsPluginSpecifier =\n \"@stll/oxlint-plugin/no-dangerous-type-assertions\";\nexport const noPartialRecordSatisfiesPluginSpecifier =\n \"@stll/oxlint-plugin/no-partial-record-satisfies\";\nexport const noUnsafeInnerHtmlPluginSpecifier =\n \"@stll/oxlint-plugin/no-unsafe-inner-html\";\nexport const noRawRouteQueryClientPluginSpecifier =\n \"@stll/oxlint-plugin/no-raw-route-query-client\";\nexport const requireLoaderPrefetchPluginSpecifier =\n \"@stll/oxlint-plugin/require-loader-prefetch\";\n\nexport const portableSafetyPluginSpecifiers = [\n noAnyCastsPluginSpecifier,\n noDangerousTypeAssertionsPluginSpecifier,\n noPartialRecordSatisfiesPluginSpecifier,\n noUnsafeInnerHtmlPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const portableSafetyRules = {\n \"no-any-casts/no-any-casts\": \"error\",\n \"no-dangerous-type-assertions/no-dangerous-type-assertions\": \"error\",\n \"no-partial-record-satisfies/no-partial-record-satisfies\": \"error\",\n \"no-unsafe-inner-html/no-unsafe-inner-html\": \"error\",\n} satisfies Rules;\n\nexport const routeQueryPluginSpecifiers = [\n noRawRouteQueryClientPluginSpecifier,\n requireLoaderPrefetchPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const routeQueryRules = {\n \"no-raw-route-query-client/no-raw-route-query-client\": \"error\",\n \"require-loader-prefetch/require-loader-prefetch\": \"error\",\n} satisfies Rules;\n"],"mappings":";AAKA,MAAa,4BAA4B;AACzC,MAAa,2CACX;AACF,MAAa,0CACX;AACF,MAAa,mCACX;AACF,MAAa,uCACX;AACF,MAAa,uCACX;AAEF,MAAa,iCAAiC;CAC5C;CACA;CACA;CACA;AACF;AAEA,MAAa,sBAAsB;CACjC,6BAA6B;CAC7B,6DAA6D;CAC7D,2DAA2D;CAC3D,6CAA6C;AAC/C;AAEA,MAAa,6BAA6B,CACxC,sCACA,oCACF;AAEA,MAAa,kBAAkB;CAC7B,uDAAuD;CACvD,mDAAmD;AACrD"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { OxlintConfig } from \"oxlint\";\n\ntype JsPlugins = NonNullable<OxlintConfig[\"jsPlugins\"]>;\ntype Rules = NonNullable<OxlintConfig[\"rules\"]>;\n\nexport const noAnyCastsPluginSpecifier = \"@stll/oxlint-plugin/no-any-casts\";\nexport const noDangerousTypeAssertionsPluginSpecifier =\n \"@stll/oxlint-plugin/no-dangerous-type-assertions\";\nexport const noPartialRecordSatisfiesPluginSpecifier =\n \"@stll/oxlint-plugin/no-partial-record-satisfies\";\nexport const noUnsafeInnerHtmlPluginSpecifier =\n \"@stll/oxlint-plugin/no-unsafe-inner-html\";\nexport const noRawRouteQueryClientPluginSpecifier =\n \"@stll/oxlint-plugin/no-raw-route-query-client\";\nexport const requireLoaderPrefetchPluginSpecifier =\n \"@stll/oxlint-plugin/require-loader-prefetch\";\n\nexport const portableSafetyPluginSpecifiers = [\n noAnyCastsPluginSpecifier,\n noDangerousTypeAssertionsPluginSpecifier,\n noPartialRecordSatisfiesPluginSpecifier,\n noUnsafeInnerHtmlPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const portableSafetyRules = {\n \"no-any-casts/no-any-casts\": \"error\",\n \"no-dangerous-type-assertions/no-dangerous-type-assertions\": \"error\",\n \"no-partial-record-satisfies/no-partial-record-satisfies\": \"error\",\n \"no-unsafe-inner-html/no-unsafe-inner-html\": \"error\",\n} satisfies Rules;\n\nexport const routeQueryPluginSpecifiers = [\n noRawRouteQueryClientPluginSpecifier,\n requireLoaderPrefetchPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const routeQueryRules = {\n \"no-raw-route-query-client/no-raw-route-query-client\": \"error\",\n \"require-loader-prefetch/require-loader-prefetch\": \"error\",\n} satisfies Rules;\n\nexport const noWallClockInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-wall-clock-in-tests\";\nexport const noRealTimerWaitsInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-real-timer-waits-in-tests\";\nexport const noUnseededRandomInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-unseeded-random-in-tests\";\nexport const noRealNetworkInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-real-network-in-tests\";\nexport const noCheckoutWritesInTestsPluginSpecifier =\n \"@stll/oxlint-plugin/no-checkout-writes-in-tests\";\nexport const noWallClockBoundPluginSpecifier =\n \"@stll/oxlint-plugin/no-wall-clock-bound\";\n\n// Intentionally separate from every preset: consumers adopt these with a\n// shrink-only test baseline.\nexport const testReliabilityPluginSpecifiers = [\n noWallClockInTestsPluginSpecifier,\n noRealTimerWaitsInTestsPluginSpecifier,\n noUnseededRandomInTestsPluginSpecifier,\n noRealNetworkInTestsPluginSpecifier,\n noCheckoutWritesInTestsPluginSpecifier,\n noWallClockBoundPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const testReliabilityRules = {\n \"no-wall-clock-in-tests/no-wall-clock-in-tests\": \"error\",\n \"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests\": \"error\",\n \"no-unseeded-random-in-tests/no-unseeded-random-in-tests\": \"error\",\n \"no-real-network-in-tests/no-real-network-in-tests\": \"error\",\n \"no-checkout-writes-in-tests/no-checkout-writes-in-tests\": \"error\",\n \"no-wall-clock-bound/no-wall-clock-bound\": \"error\",\n} satisfies Rules;\n"],"mappings":";AAKA,MAAa,4BAA4B;AACzC,MAAa,2CACX;AACF,MAAa,0CACX;AACF,MAAa,mCACX;AACF,MAAa,uCACX;AACF,MAAa,uCACX;AAEF,MAAa,iCAAiC;CAC5C;CACA;CACA;CACA;AACF;AAEA,MAAa,sBAAsB;CACjC,6BAA6B;CAC7B,6DAA6D;CAC7D,2DAA2D;CAC3D,6CAA6C;AAC/C;AAEA,MAAa,6BAA6B,CACxC,sCACA,oCACF;AAEA,MAAa,kBAAkB;CAC7B,uDAAuD;CACvD,mDAAmD;AACrD;AAEA,MAAa,oCACX;AACF,MAAa,yCACX;AACF,MAAa,yCACX;AACF,MAAa,sCACX;AACF,MAAa,yCACX;AACF,MAAa,kCACX;AAIF,MAAa,kCAAkC;CAC7C;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,uBAAuB;CAClC,iDAAiD;CACjD,6DAA6D;CAC7D,2DAA2D;CAC3D,qDAAqD;CACrD,2DAA2D;CAC3D,2CAA2C;AAC7C"}
@@ -0,0 +1,115 @@
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-checkout-writes-in-tests.ts
7
+ const writeNames = /* @__PURE__ */ new Set([
8
+ "appendFile",
9
+ "appendFileSync",
10
+ "copyFile",
11
+ "copyFileSync",
12
+ "createWriteStream",
13
+ "cp",
14
+ "cpSync",
15
+ "mkdir",
16
+ "mkdirSync",
17
+ "rename",
18
+ "renameSync",
19
+ "rm",
20
+ "rmSync",
21
+ "rmdir",
22
+ "rmdirSync",
23
+ "symlink",
24
+ "symlinkSync",
25
+ "unlink",
26
+ "unlinkSync",
27
+ "writeFile",
28
+ "writeFileSync"
29
+ ]);
30
+ const fsModules = /* @__PURE__ */ new Set([
31
+ "fs",
32
+ "node:fs",
33
+ "fs/promises",
34
+ "node:fs/promises"
35
+ ]);
36
+ const pathModules = /* @__PURE__ */ new Set(["path", "node:path"]);
37
+ const absolutePath = /^(?:\/|[A-Za-z]:[\\/]|\\\\)/u;
38
+ const destinationWriteNames = /* @__PURE__ */ new Set([
39
+ "copyFile",
40
+ "copyFileSync",
41
+ "cp",
42
+ "cpSync",
43
+ "rename",
44
+ "renameSync",
45
+ "symlink",
46
+ "symlinkSync"
47
+ ]);
48
+ var no_checkout_writes_in_tests_default = require_test_rule_utils.testOnlyPlugin("no-checkout-writes-in-tests", "noCheckoutWrite", "Use @stll/test-kit's withTestTmpDir instead of writing inside the checkout.", (report, getScope) => {
49
+ const resolveBinding = require_test_rule_utils.createBindingResolver(getScope);
50
+ const isGlobal = (value, name) => {
51
+ if (!require_test_rule_utils.isNode(value)) return false;
52
+ const binding = resolveBinding(value);
53
+ return binding.type === "global" && binding.name === name;
54
+ };
55
+ const isPathBuilder = (value) => {
56
+ if (!require_test_rule_utils.isNode(value)) return false;
57
+ if (value.type === "Identifier") {
58
+ const binding = resolveBinding(value);
59
+ return binding.type === "import" && pathModules.has(binding.module) && (binding.imported === "join" || binding.imported === "resolve");
60
+ }
61
+ if (value.type !== "MemberExpression") return false;
62
+ const object = require_test_rule_utils.isNode(value.object) ? resolveBinding(value.object) : null;
63
+ return (require_test_rule_utils.propertyName(value.property) === "join" || require_test_rule_utils.propertyName(value.property) === "resolve") && object?.type === "import" && pathModules.has(object.module) && (object.imported === "*" || object.imported === "default");
64
+ };
65
+ const rootedInCheckout = (value) => {
66
+ if (!require_test_rule_utils.isNode(value)) return false;
67
+ if (value.type === "Identifier") {
68
+ if (isGlobal(value, "__dirname")) return true;
69
+ const binding = resolveBinding(value);
70
+ return binding.type === "const" && rootedInCheckout(binding.initializer);
71
+ }
72
+ if (value.type === "Literal" && typeof value.value === "string") return !absolutePath.test(value.value);
73
+ if (value.type === "TemplateLiteral" && Array.isArray(value.expressions) && Array.isArray(value.quasis)) {
74
+ const quasi = value.quasis.at(0);
75
+ const raw = require_test_rule_utils.isNode(quasi) ? Reflect.get(quasi.value ?? {}, "raw") : null;
76
+ if (typeof raw !== "string") return false;
77
+ if (raw.length > 0) return !absolutePath.test(raw);
78
+ return rootedInCheckout(value.expressions.at(0));
79
+ }
80
+ if (value.type === "MemberExpression" && require_test_rule_utils.isNode(value.object) && value.object.type === "MetaProperty" && require_test_rule_utils.identifierName(value.object.meta) === "import" && require_test_rule_utils.identifierName(value.object.property) === "meta") return require_test_rule_utils.propertyName(value.property) === "dir" || require_test_rule_utils.propertyName(value.property) === "dirname";
81
+ if (value.type === "CallExpression" && require_test_rule_utils.isNode(value.callee) && isGlobal(value.callee.object, "process") && require_test_rule_utils.propertyName(value.callee.property) === "cwd") return true;
82
+ if (value.type === "CallExpression" && isPathBuilder(value.callee) && Array.isArray(value.arguments)) return rootedInCheckout(value.arguments.at(0));
83
+ if (value.type === "BinaryExpression") return rootedInCheckout(value.left);
84
+ return false;
85
+ };
86
+ const fsWriteName = (value) => {
87
+ if (!require_test_rule_utils.isNode(value)) return null;
88
+ if (value.type === "Identifier") {
89
+ const binding = resolveBinding(value);
90
+ if (binding.type === "const") return fsWriteName(binding.initializer);
91
+ return binding.type === "import" && fsModules.has(binding.module) && writeNames.has(binding.imported) ? binding.imported : null;
92
+ }
93
+ if (value.type !== "MemberExpression") return null;
94
+ const method = require_test_rule_utils.propertyName(value.property);
95
+ if (method === null || !writeNames.has(method)) return null;
96
+ if (require_test_rule_utils.isNode(value.object)) {
97
+ const object = resolveBinding(value.object);
98
+ if (object.type === "import" && fsModules.has(object.module) && (object.imported === "*" || object.imported === "default" || object.imported === "promises")) return method;
99
+ if (value.object.type === "MemberExpression" && require_test_rule_utils.propertyName(value.object.property) === "promises" && require_test_rule_utils.isNode(value.object.object)) {
100
+ const namespace = resolveBinding(value.object.object);
101
+ return namespace.type === "import" && fsModules.has(namespace.module) && (namespace.imported === "*" || namespace.imported === "default") ? method : null;
102
+ }
103
+ }
104
+ return null;
105
+ };
106
+ const bunWrite = (value) => require_test_rule_utils.isNode(value) && value.type === "MemberExpression" && require_test_rule_utils.propertyName(value.property) === "write" && isGlobal(value.object, "Bun");
107
+ return { CallExpression(node) {
108
+ const writeName = fsWriteName(node.callee);
109
+ if ((writeName !== null || bunWrite(node.callee)) && Array.isArray(node.arguments) && rootedInCheckout(node.arguments.at(writeName !== null && destinationWriteNames.has(writeName) ? 1 : 0))) report(node);
110
+ } };
111
+ });
112
+ //#endregion
113
+ exports.default = no_checkout_writes_in_tests_default;
114
+
115
+ //# sourceMappingURL=no-checkout-writes-in-tests.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-checkout-writes-in-tests.cjs","names":["testOnlyPlugin","createBindingResolver","isNode","propertyName","identifierName"],"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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"}
@@ -0,0 +1,4 @@
1
+ //#region src/no-checkout-writes-in-tests.d.ts
2
+ declare const _default: import("@oxlint/plugins").Plugin;
3
+ export = _default;
4
+ //# sourceMappingURL=no-checkout-writes-in-tests.d.cts.map
@@ -0,0 +1,5 @@
1
+ //#region src/no-checkout-writes-in-tests.d.ts
2
+ declare const _default: import("@oxlint/plugins").Plugin;
3
+ //#endregion
4
+ export { _default as default };
5
+ //# sourceMappingURL=no-checkout-writes-in-tests.d.mts.map
@@ -0,0 +1,111 @@
1
+ import { a as testOnlyPlugin, i as propertyName, n as identifierName, r as isNode, t as createBindingResolver } from "./test-rule-utils.mjs";
2
+ //#region src/no-checkout-writes-in-tests.ts
3
+ const writeNames = /* @__PURE__ */ new Set([
4
+ "appendFile",
5
+ "appendFileSync",
6
+ "copyFile",
7
+ "copyFileSync",
8
+ "createWriteStream",
9
+ "cp",
10
+ "cpSync",
11
+ "mkdir",
12
+ "mkdirSync",
13
+ "rename",
14
+ "renameSync",
15
+ "rm",
16
+ "rmSync",
17
+ "rmdir",
18
+ "rmdirSync",
19
+ "symlink",
20
+ "symlinkSync",
21
+ "unlink",
22
+ "unlinkSync",
23
+ "writeFile",
24
+ "writeFileSync"
25
+ ]);
26
+ const fsModules = /* @__PURE__ */ new Set([
27
+ "fs",
28
+ "node:fs",
29
+ "fs/promises",
30
+ "node:fs/promises"
31
+ ]);
32
+ const pathModules = /* @__PURE__ */ new Set(["path", "node:path"]);
33
+ const absolutePath = /^(?:\/|[A-Za-z]:[\\/]|\\\\)/u;
34
+ const destinationWriteNames = /* @__PURE__ */ new Set([
35
+ "copyFile",
36
+ "copyFileSync",
37
+ "cp",
38
+ "cpSync",
39
+ "rename",
40
+ "renameSync",
41
+ "symlink",
42
+ "symlinkSync"
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) => {
45
+ const resolveBinding = createBindingResolver(getScope);
46
+ const isGlobal = (value, name) => {
47
+ if (!isNode(value)) return false;
48
+ const binding = resolveBinding(value);
49
+ return binding.type === "global" && binding.name === name;
50
+ };
51
+ const isPathBuilder = (value) => {
52
+ if (!isNode(value)) return false;
53
+ if (value.type === "Identifier") {
54
+ const binding = resolveBinding(value);
55
+ return binding.type === "import" && pathModules.has(binding.module) && (binding.imported === "join" || binding.imported === "resolve");
56
+ }
57
+ if (value.type !== "MemberExpression") return false;
58
+ const object = isNode(value.object) ? resolveBinding(value.object) : null;
59
+ return (propertyName(value.property) === "join" || propertyName(value.property) === "resolve") && object?.type === "import" && pathModules.has(object.module) && (object.imported === "*" || object.imported === "default");
60
+ };
61
+ const rootedInCheckout = (value) => {
62
+ if (!isNode(value)) return false;
63
+ if (value.type === "Identifier") {
64
+ if (isGlobal(value, "__dirname")) return true;
65
+ const binding = resolveBinding(value);
66
+ return binding.type === "const" && rootedInCheckout(binding.initializer);
67
+ }
68
+ if (value.type === "Literal" && typeof value.value === "string") return !absolutePath.test(value.value);
69
+ if (value.type === "TemplateLiteral" && Array.isArray(value.expressions) && Array.isArray(value.quasis)) {
70
+ const quasi = value.quasis.at(0);
71
+ const raw = isNode(quasi) ? Reflect.get(quasi.value ?? {}, "raw") : null;
72
+ if (typeof raw !== "string") return false;
73
+ if (raw.length > 0) return !absolutePath.test(raw);
74
+ return rootedInCheckout(value.expressions.at(0));
75
+ }
76
+ if (value.type === "MemberExpression" && isNode(value.object) && value.object.type === "MetaProperty" && identifierName(value.object.meta) === "import" && identifierName(value.object.property) === "meta") return propertyName(value.property) === "dir" || propertyName(value.property) === "dirname";
77
+ if (value.type === "CallExpression" && isNode(value.callee) && isGlobal(value.callee.object, "process") && propertyName(value.callee.property) === "cwd") return true;
78
+ if (value.type === "CallExpression" && isPathBuilder(value.callee) && Array.isArray(value.arguments)) return rootedInCheckout(value.arguments.at(0));
79
+ if (value.type === "BinaryExpression") return rootedInCheckout(value.left);
80
+ return false;
81
+ };
82
+ const fsWriteName = (value) => {
83
+ if (!isNode(value)) return null;
84
+ if (value.type === "Identifier") {
85
+ const binding = resolveBinding(value);
86
+ if (binding.type === "const") return fsWriteName(binding.initializer);
87
+ return binding.type === "import" && fsModules.has(binding.module) && writeNames.has(binding.imported) ? binding.imported : null;
88
+ }
89
+ if (value.type !== "MemberExpression") return null;
90
+ const method = propertyName(value.property);
91
+ if (method === null || !writeNames.has(method)) return null;
92
+ if (isNode(value.object)) {
93
+ const object = resolveBinding(value.object);
94
+ if (object.type === "import" && fsModules.has(object.module) && (object.imported === "*" || object.imported === "default" || object.imported === "promises")) return method;
95
+ if (value.object.type === "MemberExpression" && propertyName(value.object.property) === "promises" && isNode(value.object.object)) {
96
+ const namespace = resolveBinding(value.object.object);
97
+ return namespace.type === "import" && fsModules.has(namespace.module) && (namespace.imported === "*" || namespace.imported === "default") ? method : null;
98
+ }
99
+ }
100
+ return null;
101
+ };
102
+ const bunWrite = (value) => isNode(value) && value.type === "MemberExpression" && propertyName(value.property) === "write" && isGlobal(value.object, "Bun");
103
+ return { CallExpression(node) {
104
+ const writeName = fsWriteName(node.callee);
105
+ if ((writeName !== null || bunWrite(node.callee)) && Array.isArray(node.arguments) && rootedInCheckout(node.arguments.at(writeName !== null && destinationWriteNames.has(writeName) ? 1 : 0))) report(node);
106
+ } };
107
+ });
108
+ //#endregion
109
+ export { no_checkout_writes_in_tests_default as default };
110
+
111
+ //# sourceMappingURL=no-checkout-writes-in-tests.mjs.map
@@ -0,0 +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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"}
@@ -0,0 +1,71 @@
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-real-network-in-tests.ts
7
+ const ipv4Loopback = /^127(?:\.\d{1,3}){3}$/u;
8
+ const urlText = (value, resolveBinding) => {
9
+ if (!require_test_rule_utils.isNode(value)) return null;
10
+ if (value.type === "Literal" && typeof value.value === "string") return value.value;
11
+ if (value.type === "TemplateLiteral" && Array.isArray(value.quasis) && Array.isArray(value.expressions)) {
12
+ let text = "";
13
+ for (const [index, part] of value.quasis.entries()) {
14
+ const raw = require_test_rule_utils.isNode(part) ? Reflect.get(part.value ?? {}, "raw") : null;
15
+ if (typeof raw !== "string") return null;
16
+ text += raw;
17
+ const expression = value.expressions.at(index);
18
+ if (expression === void 0) continue;
19
+ const resolved = urlText(expression, resolveBinding);
20
+ if (resolved === null) return null;
21
+ text += resolved;
22
+ }
23
+ return text;
24
+ }
25
+ if (value.type === "Identifier") {
26
+ const binding = resolveBinding(value);
27
+ return binding.type === "const" ? urlText(binding.initializer, resolveBinding) : null;
28
+ }
29
+ if (value.type === "NewExpression" && require_test_rule_utils.isNode(value.callee) && resolveBinding(value.callee).type === "global" && require_test_rule_utils.identifierName(value.callee) === "URL" && Array.isArray(value.arguments)) return urlText(value.arguments.at(0), resolveBinding);
30
+ return null;
31
+ };
32
+ const external = (value, resolveBinding) => {
33
+ const text = urlText(value, resolveBinding);
34
+ if (text === null) return false;
35
+ try {
36
+ const { hostname: host, protocol } = new URL(text);
37
+ if (![
38
+ "http:",
39
+ "https:",
40
+ "ws:",
41
+ "wss:"
42
+ ].includes(protocol) || host === "") return false;
43
+ return host !== "localhost" && !ipv4Loopback.test(host) && host !== "::1" && host !== "[::1]" && !host.endsWith(".localhost");
44
+ } catch {
45
+ return false;
46
+ }
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) => {
49
+ const resolveBinding = require_test_rule_utils.createBindingResolver(getScope);
50
+ const isGlobalCallee = (value, name) => {
51
+ if (!require_test_rule_utils.isNode(value)) return false;
52
+ if (value.type === "Identifier") {
53
+ const binding = resolveBinding(value);
54
+ return binding.type === "global" && binding.name === name;
55
+ }
56
+ if (value.type !== "MemberExpression" || require_test_rule_utils.propertyName(value.property) !== name) return false;
57
+ return require_test_rule_utils.isNode(value.object) && resolveBinding(value.object).type === "global" && require_test_rule_utils.identifierName(value.object) === "globalThis";
58
+ };
59
+ return {
60
+ CallExpression(node) {
61
+ if (isGlobalCallee(node.callee, "fetch") && Array.isArray(node.arguments) && external(node.arguments.at(0), resolveBinding)) report(node);
62
+ },
63
+ NewExpression(node) {
64
+ if ((isGlobalCallee(node.callee, "WebSocket") || isGlobalCallee(node.callee, "EventSource")) && Array.isArray(node.arguments) && external(node.arguments.at(0), resolveBinding)) report(node);
65
+ }
66
+ };
67
+ });
68
+ //#endregion
69
+ exports.default = no_real_network_in_tests_default;
70
+
71
+ //# sourceMappingURL=no-real-network-in-tests.cjs.map
@@ -0,0 +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"}
@@ -0,0 +1,4 @@
1
+ //#region src/no-real-network-in-tests.d.ts
2
+ declare const _default: import("@oxlint/plugins").Plugin;
3
+ export = _default;
4
+ //# sourceMappingURL=no-real-network-in-tests.d.cts.map
@@ -0,0 +1,5 @@
1
+ //#region src/no-real-network-in-tests.d.ts
2
+ declare const _default: import("@oxlint/plugins").Plugin;
3
+ //#endregion
4
+ export { _default as default };
5
+ //# sourceMappingURL=no-real-network-in-tests.d.mts.map