@stll/oxlint-plugin 0.4.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.
- package/dist/index.cjs +6 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +6 -3
- package/dist/index.mjs.map +1 -1
- package/dist/no-wall-clock-bound.cjs +153 -0
- package/dist/no-wall-clock-bound.cjs.map +1 -0
- package/dist/no-wall-clock-bound.d.cts +4 -0
- package/dist/no-wall-clock-bound.d.mts +5 -0
- package/dist/no-wall-clock-bound.mjs +149 -0
- package/dist/no-wall-clock-bound.mjs.map +1 -0
- package/dist/test-rule-utils.cjs.map +1 -1
- package/dist/test-rule-utils.mjs.map +1 -1
- package/package.json +12 -1
package/dist/index.cjs
CHANGED
|
@@ -28,19 +28,22 @@ const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-time
|
|
|
28
28
|
const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
|
|
29
29
|
const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
|
|
30
30
|
const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
|
|
31
|
+
const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
|
|
31
32
|
const testReliabilityPluginSpecifiers = [
|
|
32
33
|
noWallClockInTestsPluginSpecifier,
|
|
33
34
|
noRealTimerWaitsInTestsPluginSpecifier,
|
|
34
35
|
noUnseededRandomInTestsPluginSpecifier,
|
|
35
36
|
noRealNetworkInTestsPluginSpecifier,
|
|
36
|
-
noCheckoutWritesInTestsPluginSpecifier
|
|
37
|
+
noCheckoutWritesInTestsPluginSpecifier,
|
|
38
|
+
noWallClockBoundPluginSpecifier
|
|
37
39
|
];
|
|
38
40
|
const testReliabilityRules = {
|
|
39
41
|
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error",
|
|
40
42
|
"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error",
|
|
41
43
|
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error",
|
|
42
44
|
"no-real-network-in-tests/no-real-network-in-tests": "error",
|
|
43
|
-
"no-checkout-writes-in-tests/no-checkout-writes-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"
|
|
44
47
|
};
|
|
45
48
|
//#endregion
|
|
46
49
|
exports.noAnyCastsPluginSpecifier = noAnyCastsPluginSpecifier;
|
|
@@ -52,6 +55,7 @@ exports.noRealNetworkInTestsPluginSpecifier = noRealNetworkInTestsPluginSpecifie
|
|
|
52
55
|
exports.noRealTimerWaitsInTestsPluginSpecifier = noRealTimerWaitsInTestsPluginSpecifier;
|
|
53
56
|
exports.noUnsafeInnerHtmlPluginSpecifier = noUnsafeInnerHtmlPluginSpecifier;
|
|
54
57
|
exports.noUnseededRandomInTestsPluginSpecifier = noUnseededRandomInTestsPluginSpecifier;
|
|
58
|
+
exports.noWallClockBoundPluginSpecifier = noWallClockBoundPluginSpecifier;
|
|
55
59
|
exports.noWallClockInTestsPluginSpecifier = noWallClockInTestsPluginSpecifier;
|
|
56
60
|
exports.portableSafetyPluginSpecifiers = portableSafetyPluginSpecifiers;
|
|
57
61
|
exports.portableSafetyRules = portableSafetyRules;
|
package/dist/index.cjs.map
CHANGED
|
@@ -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\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\";\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] 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} 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;AAIF,MAAa,kCAAkC;CAC7C;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,uBAAuB;CAClC,iDAAiD;CACjD,6DAA6D;CAC7D,2DAA2D;CAC3D,qDAAqD;CACrD,2DAA2D;
|
|
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
|
@@ -22,6 +22,7 @@ declare const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-r
|
|
|
22
22
|
declare const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
|
|
23
23
|
declare const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
|
|
24
24
|
declare const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
|
|
25
|
+
declare const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
|
|
25
26
|
declare const testReliabilityPluginSpecifiers: string[];
|
|
26
27
|
declare const testReliabilityRules: {
|
|
27
28
|
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error";
|
|
@@ -29,7 +30,8 @@ declare const testReliabilityRules: {
|
|
|
29
30
|
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error";
|
|
30
31
|
"no-real-network-in-tests/no-real-network-in-tests": "error";
|
|
31
32
|
"no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error";
|
|
33
|
+
"no-wall-clock-bound/no-wall-clock-bound": "error";
|
|
32
34
|
};
|
|
33
35
|
//#endregion
|
|
34
|
-
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
36
|
+
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
35
37
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -22,6 +22,7 @@ declare const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-r
|
|
|
22
22
|
declare const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
|
|
23
23
|
declare const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
|
|
24
24
|
declare const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
|
|
25
|
+
declare const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
|
|
25
26
|
declare const testReliabilityPluginSpecifiers: string[];
|
|
26
27
|
declare const testReliabilityRules: {
|
|
27
28
|
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error";
|
|
@@ -29,7 +30,8 @@ declare const testReliabilityRules: {
|
|
|
29
30
|
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error";
|
|
30
31
|
"no-real-network-in-tests/no-real-network-in-tests": "error";
|
|
31
32
|
"no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error";
|
|
33
|
+
"no-wall-clock-bound/no-wall-clock-bound": "error";
|
|
32
34
|
};
|
|
33
35
|
//#endregion
|
|
34
|
-
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
36
|
+
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
35
37
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -27,21 +27,24 @@ const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-time
|
|
|
27
27
|
const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
|
|
28
28
|
const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
|
|
29
29
|
const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
|
|
30
|
+
const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
|
|
30
31
|
const testReliabilityPluginSpecifiers = [
|
|
31
32
|
noWallClockInTestsPluginSpecifier,
|
|
32
33
|
noRealTimerWaitsInTestsPluginSpecifier,
|
|
33
34
|
noUnseededRandomInTestsPluginSpecifier,
|
|
34
35
|
noRealNetworkInTestsPluginSpecifier,
|
|
35
|
-
noCheckoutWritesInTestsPluginSpecifier
|
|
36
|
+
noCheckoutWritesInTestsPluginSpecifier,
|
|
37
|
+
noWallClockBoundPluginSpecifier
|
|
36
38
|
];
|
|
37
39
|
const testReliabilityRules = {
|
|
38
40
|
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error",
|
|
39
41
|
"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error",
|
|
40
42
|
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error",
|
|
41
43
|
"no-real-network-in-tests/no-real-network-in-tests": "error",
|
|
42
|
-
"no-checkout-writes-in-tests/no-checkout-writes-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"
|
|
43
46
|
};
|
|
44
47
|
//#endregion
|
|
45
|
-
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
48
|
+
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
46
49
|
|
|
47
50
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -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\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\";\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] 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} 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;AAIF,MAAa,kCAAkC;CAC7C;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,uBAAuB;CAClC,iDAAiD;CACjD,6DAA6D;CAC7D,2DAA2D;CAC3D,qDAAqD;CACrD,2DAA2D;
|
|
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,153 @@
|
|
|
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-bound.ts
|
|
7
|
+
const timerModules = /* @__PURE__ */ new Set([
|
|
8
|
+
"node:timers/promises",
|
|
9
|
+
"timers/promises",
|
|
10
|
+
"@stll/test-kit"
|
|
11
|
+
]);
|
|
12
|
+
const timerHelpers = /* @__PURE__ */ new Set([
|
|
13
|
+
"delay",
|
|
14
|
+
"setTimeout",
|
|
15
|
+
"sleep"
|
|
16
|
+
]);
|
|
17
|
+
const testModules = /* @__PURE__ */ new Set([
|
|
18
|
+
"@jest/globals",
|
|
19
|
+
"bun:test",
|
|
20
|
+
"vitest"
|
|
21
|
+
]);
|
|
22
|
+
const functionTypes = /* @__PURE__ */ new Set([
|
|
23
|
+
"ArrowFunctionExpression",
|
|
24
|
+
"FunctionDeclaration",
|
|
25
|
+
"FunctionExpression"
|
|
26
|
+
]);
|
|
27
|
+
const matcherModifiers = /* @__PURE__ */ new Set([
|
|
28
|
+
"not",
|
|
29
|
+
"rejects",
|
|
30
|
+
"resolves"
|
|
31
|
+
]);
|
|
32
|
+
const isVariableReference = (identifier) => {
|
|
33
|
+
const parent = identifier.parent;
|
|
34
|
+
if (!require_test_rule_utils.isNode(parent)) return true;
|
|
35
|
+
if (parent.type === "MemberExpression" && parent.property === identifier && parent.computed !== true) return false;
|
|
36
|
+
if ((parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") && parent.key === identifier && parent.computed !== true && parent.value !== identifier) return false;
|
|
37
|
+
if ((parent.type === "LabeledStatement" || parent.type === "BreakStatement" || parent.type === "ContinueStatement") && parent.label === identifier) return false;
|
|
38
|
+
if ((parent.type === "VariableDeclarator" || parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ClassDeclaration" || parent.type === "ClassExpression") && parent.id === identifier) return false;
|
|
39
|
+
return true;
|
|
40
|
+
};
|
|
41
|
+
const enclosingFunction = (node) => {
|
|
42
|
+
let current = node.parent;
|
|
43
|
+
while (require_test_rule_utils.isNode(current)) {
|
|
44
|
+
if (functionTypes.has(current.type)) return current;
|
|
45
|
+
current = current.parent;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
49
|
+
var no_wall_clock_bound_default = require_test_rule_utils.testOnlyPlugin("no-wall-clock-bound", "noWallClockBound", "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.", (report, getScope) => {
|
|
50
|
+
const resolveBinding = require_test_rule_utils.createBindingResolver(getScope);
|
|
51
|
+
const isGlobalMember = (node, objectName, member) => {
|
|
52
|
+
if (node.type !== "MemberExpression") return false;
|
|
53
|
+
if (require_test_rule_utils.propertyName(node.property) !== member || !require_test_rule_utils.isNode(node.object)) return false;
|
|
54
|
+
const binding = resolveBinding(node.object);
|
|
55
|
+
return binding.type === "global" && binding.name === objectName;
|
|
56
|
+
};
|
|
57
|
+
const isClockRead = (value) => {
|
|
58
|
+
if (!require_test_rule_utils.isNode(value) || value.type !== "CallExpression") return false;
|
|
59
|
+
if (!require_test_rule_utils.isNode(value.callee)) return false;
|
|
60
|
+
if (isGlobalMember(value.callee, "performance", "now") || isGlobalMember(value.callee, "Date", "now") || isGlobalMember(value.callee, "Bun", "nanoseconds")) return true;
|
|
61
|
+
const callee = value.callee;
|
|
62
|
+
return callee.type === "MemberExpression" && require_test_rule_utils.propertyName(callee.property) === "bigint" && require_test_rule_utils.isNode(callee.object) && isGlobalMember(callee.object, "process", "hrtime");
|
|
63
|
+
};
|
|
64
|
+
const constInitializerInSameFunction = (identifier, use) => {
|
|
65
|
+
const name = require_test_rule_utils.identifierName(identifier);
|
|
66
|
+
if (name === null) return null;
|
|
67
|
+
let scope = getScope(identifier);
|
|
68
|
+
while (scope !== null) {
|
|
69
|
+
const variable = scope.set.get(name);
|
|
70
|
+
if (variable === void 0) {
|
|
71
|
+
scope = scope.upper;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const definition = variable.defs.at(0);
|
|
75
|
+
if (definition?.type !== "Variable") return null;
|
|
76
|
+
const declarator = definition.node;
|
|
77
|
+
if (!require_test_rule_utils.isNode(declarator) || !require_test_rule_utils.isNode(declarator.parent) || declarator.parent.type !== "VariableDeclaration" || declarator.parent.kind !== "const" || !require_test_rule_utils.isNode(declarator.init) || enclosingFunction(declarator) !== enclosingFunction(use)) return null;
|
|
78
|
+
return declarator.init;
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
};
|
|
82
|
+
const containsClockRead = (value, use, followIdentifier, visitedIdentifiers = /* @__PURE__ */ new Set()) => {
|
|
83
|
+
if (Array.isArray(value)) return value.some((child) => containsClockRead(child, use, followIdentifier, visitedIdentifiers));
|
|
84
|
+
if (!require_test_rule_utils.isNode(value)) return false;
|
|
85
|
+
if (isClockRead(value)) return true;
|
|
86
|
+
if (followIdentifier && value.type === "Identifier" && isVariableReference(value)) {
|
|
87
|
+
if (visitedIdentifiers.has(value)) return false;
|
|
88
|
+
visitedIdentifiers.add(value);
|
|
89
|
+
const initializer = constInitializerInSameFunction(value, use);
|
|
90
|
+
if (initializer !== null && containsClockRead(initializer, use, true, visitedIdentifiers)) return true;
|
|
91
|
+
}
|
|
92
|
+
return Object.values(value).some((child) => child !== value.parent && containsClockRead(child, use, followIdentifier, visitedIdentifiers));
|
|
93
|
+
};
|
|
94
|
+
const matcherExpectCall = (value) => {
|
|
95
|
+
let current = value;
|
|
96
|
+
while (require_test_rule_utils.isNode(current) && current.type === "MemberExpression" && matcherModifiers.has(require_test_rule_utils.propertyName(current.property) ?? "")) current = current.object;
|
|
97
|
+
return require_test_rule_utils.isNode(current) && current.type === "CallExpression" ? current : null;
|
|
98
|
+
};
|
|
99
|
+
const containsSetTimeout = (value) => {
|
|
100
|
+
if (Array.isArray(value)) return value.some(containsSetTimeout);
|
|
101
|
+
if (!require_test_rule_utils.isNode(value)) return false;
|
|
102
|
+
if (value.type === "CallExpression" && require_test_rule_utils.isNode(value.callee)) {
|
|
103
|
+
const binding = resolveBinding(value.callee);
|
|
104
|
+
if (binding.type === "global" && binding.name === "setTimeout") return true;
|
|
105
|
+
}
|
|
106
|
+
for (const child of Object.values(value)) {
|
|
107
|
+
if (child === value.parent) continue;
|
|
108
|
+
if (Array.isArray(child)) {
|
|
109
|
+
if (child.some(containsSetTimeout)) return true;
|
|
110
|
+
} else if (containsSetTimeout(child)) return true;
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
};
|
|
114
|
+
const isTimer = (value) => {
|
|
115
|
+
if (!require_test_rule_utils.isNode(value)) return false;
|
|
116
|
+
if (value.type === "NewExpression") {
|
|
117
|
+
if (!require_test_rule_utils.isNode(value.callee)) return false;
|
|
118
|
+
const binding = resolveBinding(value.callee);
|
|
119
|
+
return binding.type === "global" && binding.name === "Promise" && containsSetTimeout(value.arguments);
|
|
120
|
+
}
|
|
121
|
+
if (value.type !== "CallExpression" || !require_test_rule_utils.isNode(value.callee)) return false;
|
|
122
|
+
if (isGlobalMember(value.callee, "Bun", "sleep")) return true;
|
|
123
|
+
const binding = resolveBinding(value.callee);
|
|
124
|
+
return binding.type === "import" && timerModules.has(binding.module) && timerHelpers.has(binding.imported);
|
|
125
|
+
};
|
|
126
|
+
const isTimerOrBoundTimer = (value, use) => {
|
|
127
|
+
if (isTimer(value)) return true;
|
|
128
|
+
if (!require_test_rule_utils.isNode(value) || value.type !== "Identifier") return false;
|
|
129
|
+
const initializer = constInitializerInSameFunction(value, use);
|
|
130
|
+
return initializer !== null && isTimer(initializer);
|
|
131
|
+
};
|
|
132
|
+
return { CallExpression(node) {
|
|
133
|
+
if (!require_test_rule_utils.isNode(node.callee) || node.callee.type !== "MemberExpression") return;
|
|
134
|
+
if (require_test_rule_utils.propertyName(node.callee.property) === "race" && require_test_rule_utils.isNode(node.callee.object)) {
|
|
135
|
+
const binding = resolveBinding(node.callee.object);
|
|
136
|
+
if (binding.type === "global" && binding.name === "Promise") {
|
|
137
|
+
const firstArgument = Array.isArray(node.arguments) ? node.arguments.at(0) : null;
|
|
138
|
+
if (require_test_rule_utils.isNode(firstArgument) && firstArgument.type === "ArrayExpression" && Array.isArray(firstArgument.elements) && firstArgument.elements.some((element) => isTimerOrBoundTimer(element, node))) report(node);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const expectCall = matcherExpectCall(node.callee.object);
|
|
143
|
+
if (expectCall === null || !require_test_rule_utils.isNode(expectCall.callee)) return;
|
|
144
|
+
const calleeBinding = resolveBinding(expectCall.callee);
|
|
145
|
+
if (!(calleeBinding.type === "global" && calleeBinding.name === "expect" || calleeBinding.type === "import" && calleeBinding.imported === "expect" && testModules.has(calleeBinding.module))) return;
|
|
146
|
+
const assertionValues = [expectCall.arguments, node.arguments];
|
|
147
|
+
if (containsClockRead(assertionValues, node, true)) report(node);
|
|
148
|
+
} };
|
|
149
|
+
});
|
|
150
|
+
//#endregion
|
|
151
|
+
exports.default = no_wall_clock_bound_default;
|
|
152
|
+
|
|
153
|
+
//# sourceMappingURL=no-wall-clock-bound.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-wall-clock-bound.cjs","names":["isNode","testOnlyPlugin","createBindingResolver","propertyName","identifierName"],"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,149 @@
|
|
|
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-wall-clock-bound.ts
|
|
3
|
+
const timerModules = /* @__PURE__ */ new Set([
|
|
4
|
+
"node:timers/promises",
|
|
5
|
+
"timers/promises",
|
|
6
|
+
"@stll/test-kit"
|
|
7
|
+
]);
|
|
8
|
+
const timerHelpers = /* @__PURE__ */ new Set([
|
|
9
|
+
"delay",
|
|
10
|
+
"setTimeout",
|
|
11
|
+
"sleep"
|
|
12
|
+
]);
|
|
13
|
+
const testModules = /* @__PURE__ */ new Set([
|
|
14
|
+
"@jest/globals",
|
|
15
|
+
"bun:test",
|
|
16
|
+
"vitest"
|
|
17
|
+
]);
|
|
18
|
+
const functionTypes = /* @__PURE__ */ new Set([
|
|
19
|
+
"ArrowFunctionExpression",
|
|
20
|
+
"FunctionDeclaration",
|
|
21
|
+
"FunctionExpression"
|
|
22
|
+
]);
|
|
23
|
+
const matcherModifiers = /* @__PURE__ */ new Set([
|
|
24
|
+
"not",
|
|
25
|
+
"rejects",
|
|
26
|
+
"resolves"
|
|
27
|
+
]);
|
|
28
|
+
const isVariableReference = (identifier) => {
|
|
29
|
+
const parent = identifier.parent;
|
|
30
|
+
if (!isNode(parent)) return true;
|
|
31
|
+
if (parent.type === "MemberExpression" && parent.property === identifier && parent.computed !== true) return false;
|
|
32
|
+
if ((parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") && parent.key === identifier && parent.computed !== true && parent.value !== identifier) return false;
|
|
33
|
+
if ((parent.type === "LabeledStatement" || parent.type === "BreakStatement" || parent.type === "ContinueStatement") && parent.label === identifier) return false;
|
|
34
|
+
if ((parent.type === "VariableDeclarator" || parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ClassDeclaration" || parent.type === "ClassExpression") && parent.id === identifier) return false;
|
|
35
|
+
return true;
|
|
36
|
+
};
|
|
37
|
+
const enclosingFunction = (node) => {
|
|
38
|
+
let current = node.parent;
|
|
39
|
+
while (isNode(current)) {
|
|
40
|
+
if (functionTypes.has(current.type)) return current;
|
|
41
|
+
current = current.parent;
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
};
|
|
45
|
+
var no_wall_clock_bound_default = testOnlyPlugin("no-wall-clock-bound", "noWallClockBound", "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.", (report, getScope) => {
|
|
46
|
+
const resolveBinding = createBindingResolver(getScope);
|
|
47
|
+
const isGlobalMember = (node, objectName, member) => {
|
|
48
|
+
if (node.type !== "MemberExpression") return false;
|
|
49
|
+
if (propertyName(node.property) !== member || !isNode(node.object)) return false;
|
|
50
|
+
const binding = resolveBinding(node.object);
|
|
51
|
+
return binding.type === "global" && binding.name === objectName;
|
|
52
|
+
};
|
|
53
|
+
const isClockRead = (value) => {
|
|
54
|
+
if (!isNode(value) || value.type !== "CallExpression") return false;
|
|
55
|
+
if (!isNode(value.callee)) return false;
|
|
56
|
+
if (isGlobalMember(value.callee, "performance", "now") || isGlobalMember(value.callee, "Date", "now") || isGlobalMember(value.callee, "Bun", "nanoseconds")) return true;
|
|
57
|
+
const callee = value.callee;
|
|
58
|
+
return callee.type === "MemberExpression" && propertyName(callee.property) === "bigint" && isNode(callee.object) && isGlobalMember(callee.object, "process", "hrtime");
|
|
59
|
+
};
|
|
60
|
+
const constInitializerInSameFunction = (identifier, use) => {
|
|
61
|
+
const name = identifierName(identifier);
|
|
62
|
+
if (name === null) return null;
|
|
63
|
+
let scope = getScope(identifier);
|
|
64
|
+
while (scope !== null) {
|
|
65
|
+
const variable = scope.set.get(name);
|
|
66
|
+
if (variable === void 0) {
|
|
67
|
+
scope = scope.upper;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const definition = variable.defs.at(0);
|
|
71
|
+
if (definition?.type !== "Variable") return null;
|
|
72
|
+
const declarator = definition.node;
|
|
73
|
+
if (!isNode(declarator) || !isNode(declarator.parent) || declarator.parent.type !== "VariableDeclaration" || declarator.parent.kind !== "const" || !isNode(declarator.init) || enclosingFunction(declarator) !== enclosingFunction(use)) return null;
|
|
74
|
+
return declarator.init;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
};
|
|
78
|
+
const containsClockRead = (value, use, followIdentifier, visitedIdentifiers = /* @__PURE__ */ new Set()) => {
|
|
79
|
+
if (Array.isArray(value)) return value.some((child) => containsClockRead(child, use, followIdentifier, visitedIdentifiers));
|
|
80
|
+
if (!isNode(value)) return false;
|
|
81
|
+
if (isClockRead(value)) return true;
|
|
82
|
+
if (followIdentifier && value.type === "Identifier" && isVariableReference(value)) {
|
|
83
|
+
if (visitedIdentifiers.has(value)) return false;
|
|
84
|
+
visitedIdentifiers.add(value);
|
|
85
|
+
const initializer = constInitializerInSameFunction(value, use);
|
|
86
|
+
if (initializer !== null && containsClockRead(initializer, use, true, visitedIdentifiers)) return true;
|
|
87
|
+
}
|
|
88
|
+
return Object.values(value).some((child) => child !== value.parent && containsClockRead(child, use, followIdentifier, visitedIdentifiers));
|
|
89
|
+
};
|
|
90
|
+
const matcherExpectCall = (value) => {
|
|
91
|
+
let current = value;
|
|
92
|
+
while (isNode(current) && current.type === "MemberExpression" && matcherModifiers.has(propertyName(current.property) ?? "")) current = current.object;
|
|
93
|
+
return isNode(current) && current.type === "CallExpression" ? current : null;
|
|
94
|
+
};
|
|
95
|
+
const containsSetTimeout = (value) => {
|
|
96
|
+
if (Array.isArray(value)) return value.some(containsSetTimeout);
|
|
97
|
+
if (!isNode(value)) return false;
|
|
98
|
+
if (value.type === "CallExpression" && isNode(value.callee)) {
|
|
99
|
+
const binding = resolveBinding(value.callee);
|
|
100
|
+
if (binding.type === "global" && binding.name === "setTimeout") return true;
|
|
101
|
+
}
|
|
102
|
+
for (const child of Object.values(value)) {
|
|
103
|
+
if (child === value.parent) continue;
|
|
104
|
+
if (Array.isArray(child)) {
|
|
105
|
+
if (child.some(containsSetTimeout)) return true;
|
|
106
|
+
} else if (containsSetTimeout(child)) return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
};
|
|
110
|
+
const isTimer = (value) => {
|
|
111
|
+
if (!isNode(value)) return false;
|
|
112
|
+
if (value.type === "NewExpression") {
|
|
113
|
+
if (!isNode(value.callee)) return false;
|
|
114
|
+
const binding = resolveBinding(value.callee);
|
|
115
|
+
return binding.type === "global" && binding.name === "Promise" && containsSetTimeout(value.arguments);
|
|
116
|
+
}
|
|
117
|
+
if (value.type !== "CallExpression" || !isNode(value.callee)) return false;
|
|
118
|
+
if (isGlobalMember(value.callee, "Bun", "sleep")) return true;
|
|
119
|
+
const binding = resolveBinding(value.callee);
|
|
120
|
+
return binding.type === "import" && timerModules.has(binding.module) && timerHelpers.has(binding.imported);
|
|
121
|
+
};
|
|
122
|
+
const isTimerOrBoundTimer = (value, use) => {
|
|
123
|
+
if (isTimer(value)) return true;
|
|
124
|
+
if (!isNode(value) || value.type !== "Identifier") return false;
|
|
125
|
+
const initializer = constInitializerInSameFunction(value, use);
|
|
126
|
+
return initializer !== null && isTimer(initializer);
|
|
127
|
+
};
|
|
128
|
+
return { CallExpression(node) {
|
|
129
|
+
if (!isNode(node.callee) || node.callee.type !== "MemberExpression") return;
|
|
130
|
+
if (propertyName(node.callee.property) === "race" && isNode(node.callee.object)) {
|
|
131
|
+
const binding = resolveBinding(node.callee.object);
|
|
132
|
+
if (binding.type === "global" && binding.name === "Promise") {
|
|
133
|
+
const firstArgument = Array.isArray(node.arguments) ? node.arguments.at(0) : null;
|
|
134
|
+
if (isNode(firstArgument) && firstArgument.type === "ArrayExpression" && Array.isArray(firstArgument.elements) && firstArgument.elements.some((element) => isTimerOrBoundTimer(element, node))) report(node);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const expectCall = matcherExpectCall(node.callee.object);
|
|
139
|
+
if (expectCall === null || !isNode(expectCall.callee)) return;
|
|
140
|
+
const calleeBinding = resolveBinding(expectCall.callee);
|
|
141
|
+
if (!(calleeBinding.type === "global" && calleeBinding.name === "expect" || calleeBinding.type === "import" && calleeBinding.imported === "expect" && testModules.has(calleeBinding.module))) return;
|
|
142
|
+
const assertionValues = [expectCall.arguments, node.arguments];
|
|
143
|
+
if (containsClockRead(assertionValues, node, true)) report(node);
|
|
144
|
+
} };
|
|
145
|
+
});
|
|
146
|
+
//#endregion
|
|
147
|
+
export { no_wall_clock_bound_default as default };
|
|
148
|
+
|
|
149
|
+
//# sourceMappingURL=no-wall-clock-bound.mjs.map
|
|
@@ -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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"}
|
|
@@ -1 +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 key?: 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 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(`Expected an oxlint AST node, got ${node.type}`);\n return context.sourceCode.getScope(node);\n },\n );\n },\n },\n },\n });\n"],"mappings":";;;AA+BA,MAAa,UAAU,UACrB,OAAO,UAAU,YACjB,UAAU,QACV,OAAO,QAAQ,IAAI,OAAO,MAAM,MAAM;;AAGxC,MAAM,eAAe,UACnB,OAAO,QAAQ,IAAI,OAAO,OAAO,MAAM,YACvC,OAAO,QAAQ,IAAI,OAAO,KAAK,MAAM;AAEvC,MAAa,kBAAkB,UAC7B,OAAO,KAAK,KAAK,MAAM,SAAS,gBAAgB,OAAO,MAAM,SAAS,WAClE,MAAM,OACN;AAEN,MAAa,gBAAgB,UAAkC;CAC7D,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;CAC3B,IAAI,MAAM,SAAS,gBAAgB,OAAO,MAAM,SAAS,UACvD,OAAO,MAAM;CACf,IAAI,MAAM,SAAS,aAAa,OAAO,MAAM,UAAU,UACrD,OAAO,MAAM;CACf,OAAO;AACT;AAeA,MAAM,iBAAiB,EAAE,MAAM,UAAU;AACzC,MAAM,sBAAsB;AAE5B,MAAa,yBAAyB,aAAoC;CACxE,MAAM,WACJ,MACA,SACA,UACY;EACZ,MAAM,OAAO,eAAe,IAAI;EAChC,IAAI,SAAS,QAAQ,SAAS,uBAAuB,QAAQ,IAAI,IAAI,GACnE,OAAO;EAET,MAAM,cAAc,IAAI,IAAI,OAAO;EACnC,YAAY,IAAI,IAAI;EACpB,IAAI,QAAsB,SAAS,IAAI;EACvC,OAAO,UAAU,MAAM;GACrB,MAAM,WAAW,MAAM,IAAI,IAAI,IAAI;GACnC,IAAI,aAAa,KAAA,GAAW;IAC1B,QAAQ,MAAM;IACd;GACF;GACA,MAAM,aAAa,SAAS,KAAK,GAAG,CAAC;GACrC,IAAI,eAAe,KAAA,GAAW,OAAO;IAAE,MAAM;IAAU;GAAK;GAC5D,IAAI,WAAW,SAAS,iBAAiB;IACvC,MAAM,YAAY,WAAW;IAC7B,IAAI,CAAC,OAAO,SAAS,KAAK,CAAC,OAAO,UAAU,MAAM,GAChD,OAAO;IACT,MAAM,cAAc,UAAU;IAC9B,IACE,YAAY,SAAS,uBACrB,CAAC,OAAO,YAAY,MAAM,KAC1B,OAAO,YAAY,OAAO,UAAU,UAEpC,OAAO;IACT,IAAI,WAAW,aAAa,UAAU,QAAQ;IAC9C,IAAI,UAAU,SAAS,0BAA0B,WAAW;IAC5D,IAAI,UAAU,SAAS,4BAA4B,WAAW;IAC9D,OAAO,aAAa,OAChB,iBACA;KAAE,MAAM;KAAU,QAAQ,YAAY,OAAO;KAAO;IAAS;GACnE;GACA,IAAI,WAAW,SAAS,YAAY,OAAO;GAC3C,MAAM,aAAa,WAAW;GAC9B,IACE,CAAC,OAAO,UAAU,KAClB,WAAW,SAAS,wBACpB,CAAC,OAAO,WAAW,MAAM,KACzB,WAAW,OAAO,SAAS,yBAC3B,WAAW,OAAO,SAAS,WAC3B,CAAC,OAAO,WAAW,IAAI,GAEvB,OAAO;GACT,IAAI,eAAe,WAAW,EAAE,MAAM,MAAM;IAC1C,IAAI,WAAW,KAAK,SAAS,cAC3B,OAAO;KAAE,MAAM;KAAS,aAAa,WAAW;IAAK;IACvD,OAAO,QAAQ,WAAW,MAAM,aAAa,QAAQ,CAAC;GACxD;GACA,IAAI,CAAC,OAAO,WAAW,EAAE,KAAK,WAAW,GAAG,SAAS,iBACnD,OAAO;GACT,MAAM,aAAa,QAAQ,IAAI,WAAW,IAAI,YAAY;GAC1D,IAAI,CAAC,MAAM,QAAQ,UAAU,GAAG,OAAO;GACvC,MAAM,WAAW,WAAW,MACzB,cACC,OAAO,SAAS,KAChB,UAAU,SAAS,cACnB,UAAU,aAAa,QACvB,eAAe,UAAU,KAAK,MAAM,IACxC;GACA,IAAI,CAAC,OAAO,QAAQ,GAAG,OAAO;GAC9B,MAAM,SAAS,aAAa,SAAS,GAAG;GACxC,IAAI,WAAW,QAAQ,WAAW,KAAK,SAAS,cAC9C,OAAO;GACT,MAAM,SAAS,QAAQ,WAAW,MAAM,aAAa,QAAQ,CAAC;GAC9D,IAAI,OAAO,SAAS,UAClB,OAAO;IAAE,MAAM;IAAU,QAAQ,OAAO;IAAQ,UAAU;GAAO;GACnE,IAAI,OAAO,SAAS,UAAU,OAAO;GACrC,IACE,OAAO,SAAS,gBAChB,OAAO,SAAS,YAChB,OAAO,SAAS,QAEhB,OAAO;IAAE,MAAM;IAAU,MAAM;GAAO;GACxC,OAAO;IAAE,MAAM;IAAU,MAAM,GAAG,OAAO,KAAK,GAAG;GAAS;EAC5D;EACA,OAAO;GAAE,MAAM;GAAU;EAAK;CAChC;CAEA,QAAQ,SAAwB,QAAQ,sBAAM,IAAI,IAAI,GAAG,CAAC;AAC5D;AAEA,MAAa,cAAc,YAA6B;CACtD,MAAM,WAAWA,cAAAA,mBAAmB,OAAO;CAC3C,OAAO,0DAA0D,KAC/D,QACF;AACF;AAEA,MAAa,kBACX,MACA,WACA,SACA,kBAAA,GAKAC,gBAAAA,mBAAAA,CAAmB;CACjB,MAAM,EAAE,KAAK;CACb,OAAO,GACJ,OAAO;EACN,MAAM;GAAE,MAAM;GAAW,UAAU,GAAG,YAAY,QAAQ;EAAE;EAC5D,OAAO,SAAS;GACd,IAAI,CAAC,WAAW,OAAO,GAAG,OAAO,CAAC;GAClC,OAAO,cACJ,SAAS,QAAQ,OAAO;IAAE;IAAM;GAAU,CAAC,IAC3C,SAAS;IACR,IAAI,CAAC,YAAY,IAAI,GACnB,MAAM,IAAI,MAAM,oCAAoC,KAAK,MAAM;IACjE,OAAO,QAAQ,WAAW,SAAS,IAAI;GACzC,CACF;EACF;CACF,EACF;AACF,CAAC"}
|
|
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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"}
|
|
@@ -1 +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 key?: 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 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(`Expected an oxlint AST node, got ${node.type}`);\n return context.sourceCode.getScope(node);\n },\n );\n },\n },\n },\n });\n"],"mappings":"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"}
|
|
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
|
+
"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": {
|
|
@@ -146,6 +146,17 @@
|
|
|
146
146
|
"default": "./dist/no-checkout-writes-in-tests.cjs"
|
|
147
147
|
},
|
|
148
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"
|
|
149
160
|
}
|
|
150
161
|
},
|
|
151
162
|
"publishConfig": {
|