@stll/oxlint-plugin 0.2.2 → 0.4.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 +26 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -1
- package/dist/index.d.mts +14 -1
- package/dist/index.mjs +20 -1
- package/dist/index.mjs.map +1 -1
- package/dist/no-any-casts.cjs.map +1 -1
- package/dist/no-checkout-writes-in-tests.cjs +115 -0
- package/dist/no-checkout-writes-in-tests.cjs.map +1 -0
- package/dist/no-checkout-writes-in-tests.d.cts +4 -0
- package/dist/no-checkout-writes-in-tests.d.mts +5 -0
- package/dist/no-checkout-writes-in-tests.mjs +111 -0
- package/dist/no-checkout-writes-in-tests.mjs.map +1 -0
- package/dist/no-dangerous-type-assertions.cjs.map +1 -1
- package/dist/no-partial-record-satisfies.cjs.map +1 -1
- package/dist/no-partial-record-satisfies.mjs +1 -1
- package/dist/no-raw-route-query-client.cjs.map +1 -1
- package/dist/no-raw-route-query-client.mjs +1 -1
- package/dist/no-real-network-in-tests.cjs +71 -0
- package/dist/no-real-network-in-tests.cjs.map +1 -0
- package/dist/no-real-network-in-tests.d.cts +4 -0
- package/dist/no-real-network-in-tests.d.mts +5 -0
- package/dist/no-real-network-in-tests.mjs +67 -0
- package/dist/no-real-network-in-tests.mjs.map +1 -0
- package/dist/no-real-timer-waits-in-tests.cjs +32 -0
- package/dist/no-real-timer-waits-in-tests.cjs.map +1 -0
- package/dist/no-real-timer-waits-in-tests.d.cts +4 -0
- package/dist/no-real-timer-waits-in-tests.d.mts +5 -0
- package/dist/no-real-timer-waits-in-tests.mjs +28 -0
- package/dist/no-real-timer-waits-in-tests.mjs.map +1 -0
- package/dist/no-unsafe-inner-html.cjs +458 -66
- package/dist/no-unsafe-inner-html.cjs.map +1 -1
- package/dist/no-unsafe-inner-html.d.cts +106 -1
- package/dist/no-unsafe-inner-html.d.mts +105 -1
- package/dist/no-unsafe-inner-html.mjs +458 -68
- package/dist/no-unsafe-inner-html.mjs.map +1 -1
- package/dist/no-unseeded-random-in-tests.cjs +25 -0
- package/dist/no-unseeded-random-in-tests.cjs.map +1 -0
- package/dist/no-unseeded-random-in-tests.d.cts +4 -0
- package/dist/no-unseeded-random-in-tests.d.mts +5 -0
- package/dist/no-unseeded-random-in-tests.mjs +21 -0
- package/dist/no-unseeded-random-in-tests.mjs.map +1 -0
- package/dist/no-wall-clock-in-tests.cjs +35 -0
- package/dist/no-wall-clock-in-tests.cjs.map +1 -0
- package/dist/no-wall-clock-in-tests.d.cts +4 -0
- package/dist/no-wall-clock-in-tests.d.mts +5 -0
- package/dist/no-wall-clock-in-tests.mjs +31 -0
- package/dist/no-wall-clock-in-tests.mjs.map +1 -0
- package/dist/require-loader-prefetch.cjs +8 -7
- package/dist/require-loader-prefetch.cjs.map +1 -1
- package/dist/require-loader-prefetch.mjs +8 -7
- package/dist/require-loader-prefetch.mjs.map +1 -1
- package/dist/test-rule-utils.cjs +143 -0
- package/dist/test-rule-utils.cjs.map +1 -0
- package/dist/test-rule-utils.mjs +114 -0
- package/dist/test-rule-utils.mjs.map +1 -0
- package/dist/utils.cjs +85 -10
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.mjs +54 -9
- package/dist/utils.mjs.map +1 -1
- package/package.json +57 -2
package/dist/index.cjs
CHANGED
|
@@ -23,16 +23,42 @@ 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 testReliabilityPluginSpecifiers = [
|
|
32
|
+
noWallClockInTestsPluginSpecifier,
|
|
33
|
+
noRealTimerWaitsInTestsPluginSpecifier,
|
|
34
|
+
noUnseededRandomInTestsPluginSpecifier,
|
|
35
|
+
noRealNetworkInTestsPluginSpecifier,
|
|
36
|
+
noCheckoutWritesInTestsPluginSpecifier
|
|
37
|
+
];
|
|
38
|
+
const testReliabilityRules = {
|
|
39
|
+
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error",
|
|
40
|
+
"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error",
|
|
41
|
+
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error",
|
|
42
|
+
"no-real-network-in-tests/no-real-network-in-tests": "error",
|
|
43
|
+
"no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error"
|
|
44
|
+
};
|
|
26
45
|
//#endregion
|
|
27
46
|
exports.noAnyCastsPluginSpecifier = noAnyCastsPluginSpecifier;
|
|
47
|
+
exports.noCheckoutWritesInTestsPluginSpecifier = noCheckoutWritesInTestsPluginSpecifier;
|
|
28
48
|
exports.noDangerousTypeAssertionsPluginSpecifier = noDangerousTypeAssertionsPluginSpecifier;
|
|
29
49
|
exports.noPartialRecordSatisfiesPluginSpecifier = noPartialRecordSatisfiesPluginSpecifier;
|
|
30
50
|
exports.noRawRouteQueryClientPluginSpecifier = noRawRouteQueryClientPluginSpecifier;
|
|
51
|
+
exports.noRealNetworkInTestsPluginSpecifier = noRealNetworkInTestsPluginSpecifier;
|
|
52
|
+
exports.noRealTimerWaitsInTestsPluginSpecifier = noRealTimerWaitsInTestsPluginSpecifier;
|
|
31
53
|
exports.noUnsafeInnerHtmlPluginSpecifier = noUnsafeInnerHtmlPluginSpecifier;
|
|
54
|
+
exports.noUnseededRandomInTestsPluginSpecifier = noUnseededRandomInTestsPluginSpecifier;
|
|
55
|
+
exports.noWallClockInTestsPluginSpecifier = noWallClockInTestsPluginSpecifier;
|
|
32
56
|
exports.portableSafetyPluginSpecifiers = portableSafetyPluginSpecifiers;
|
|
33
57
|
exports.portableSafetyRules = portableSafetyRules;
|
|
34
58
|
exports.requireLoaderPrefetchPluginSpecifier = requireLoaderPrefetchPluginSpecifier;
|
|
35
59
|
exports.routeQueryPluginSpecifiers = routeQueryPluginSpecifiers;
|
|
36
60
|
exports.routeQueryRules = routeQueryRules;
|
|
61
|
+
exports.testReliabilityPluginSpecifiers = testReliabilityPluginSpecifiers;
|
|
62
|
+
exports.testReliabilityRules = testReliabilityRules;
|
|
37
63
|
|
|
38
64
|
//# sourceMappingURL=index.cjs.map
|
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"],"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\";\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;AAC7D"}
|
package/dist/index.d.cts
CHANGED
|
@@ -17,6 +17,19 @@ 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 testReliabilityPluginSpecifiers: string[];
|
|
26
|
+
declare const testReliabilityRules: {
|
|
27
|
+
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error";
|
|
28
|
+
"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error";
|
|
29
|
+
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error";
|
|
30
|
+
"no-real-network-in-tests/no-real-network-in-tests": "error";
|
|
31
|
+
"no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error";
|
|
32
|
+
};
|
|
20
33
|
//#endregion
|
|
21
|
-
export { noAnyCastsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules };
|
|
34
|
+
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
22
35
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
CHANGED
|
@@ -17,6 +17,19 @@ 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 testReliabilityPluginSpecifiers: string[];
|
|
26
|
+
declare const testReliabilityRules: {
|
|
27
|
+
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error";
|
|
28
|
+
"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error";
|
|
29
|
+
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error";
|
|
30
|
+
"no-real-network-in-tests/no-real-network-in-tests": "error";
|
|
31
|
+
"no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error";
|
|
32
|
+
};
|
|
20
33
|
//#endregion
|
|
21
|
-
export { noAnyCastsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules };
|
|
34
|
+
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
22
35
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -22,7 +22,26 @@ 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 testReliabilityPluginSpecifiers = [
|
|
31
|
+
noWallClockInTestsPluginSpecifier,
|
|
32
|
+
noRealTimerWaitsInTestsPluginSpecifier,
|
|
33
|
+
noUnseededRandomInTestsPluginSpecifier,
|
|
34
|
+
noRealNetworkInTestsPluginSpecifier,
|
|
35
|
+
noCheckoutWritesInTestsPluginSpecifier
|
|
36
|
+
];
|
|
37
|
+
const testReliabilityRules = {
|
|
38
|
+
"no-wall-clock-in-tests/no-wall-clock-in-tests": "error",
|
|
39
|
+
"no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error",
|
|
40
|
+
"no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error",
|
|
41
|
+
"no-real-network-in-tests/no-real-network-in-tests": "error",
|
|
42
|
+
"no-checkout-writes-in-tests/no-checkout-writes-in-tests": "error"
|
|
43
|
+
};
|
|
25
44
|
//#endregion
|
|
26
|
-
export { noAnyCastsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules };
|
|
45
|
+
export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
|
|
27
46
|
|
|
28
47
|
//# 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"],"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\";\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;AAC7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-any-casts.cjs","names":[],"sources":["../src/no-any-casts.ts"],"sourcesContent":["// Disallow `as any` and `<any>` casts.\n//\n// Distinct from `no-explicit-any` (which targets `: any` annotations\n// in declarations). This rule catches the cast form — typically\n// `value as any` or the laundering `value as any as Target` — which\n// bypasses brand checks, discriminated-union narrowing, and every\n// other safety the type system provides.\n\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-any-casts\" },\n rules: {\n \"no-any-casts\": {\n meta: {\n type: \"problem\",\n messages: {\n noAnyCast:\n \"Avoid `as any` casts. They bypass branded IDs and \" +\n \"discriminated-union narrowing. Narrow with a type guard, \" +\n \"use `unknown` + a guard, or refactor the source so the \" +\n \"cast isn't needed.\",\n },\n },\n createOnce(context) {\n function check(node) {\n if (node.typeAnnotation?.type === \"TSAnyKeyword\") {\n context.report({ node, messageId: \"noAnyCast\" });\n }\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n"],"mappings":";;;;;AAUA,IAAA,wBAAA,
|
|
1
|
+
{"version":3,"file":"no-any-casts.cjs","names":["eslintCompatPlugin"],"sources":["../src/no-any-casts.ts"],"sourcesContent":["// Disallow `as any` and `<any>` casts.\n//\n// Distinct from `no-explicit-any` (which targets `: any` annotations\n// in declarations). This rule catches the cast form — typically\n// `value as any` or the laundering `value as any as Target` — which\n// bypasses brand checks, discriminated-union narrowing, and every\n// other safety the type system provides.\n\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-any-casts\" },\n rules: {\n \"no-any-casts\": {\n meta: {\n type: \"problem\",\n messages: {\n noAnyCast:\n \"Avoid `as any` casts. They bypass branded IDs and \" +\n \"discriminated-union narrowing. Narrow with a type guard, \" +\n \"use `unknown` + a guard, or refactor the source so the \" +\n \"cast isn't needed.\",\n },\n },\n createOnce(context) {\n function check(node) {\n if (node.typeAnnotation?.type === \"TSAnyKeyword\") {\n context.report({ node, messageId: \"noAnyCast\" });\n }\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n"],"mappings":";;;;;AAUA,IAAA,wBAAA,4BAAeA,CAAAA,CAAAA,mBAAAA,CAAmB;CAChC,MAAM,EAAE,MAAM,eAAe;CAC7B,OAAO,EACL,gBAAgB;EACd,MAAM;GACJ,MAAM;GACN,UAAU,EACR,WACE,uLAIJ;EACF;EACA,WAAW,SAAS;GAClB,SAAS,MAAM,MAAM;IACnB,IAAI,KAAK,gBAAgB,SAAS,gBAChC,QAAQ,OAAO;KAAE;KAAM,WAAW;IAAY,CAAC;GAEnD;GACA,OAAO;IACL,gBAAgB;IAChB,iBAAiB;GACnB;EACF;CACF,EACF;AACF,CAAC"}
|
|
@@ -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,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":";;AAQA,MAAM,6BAAa,IAAI,IAAI;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AACD,MAAM,4BAAY,IAAI,IAAI;CAAC;CAAM;CAAW;CAAe;AAAkB,CAAC;AAC9E,MAAM,8BAAc,IAAI,IAAI,CAAC,QAAQ,WAAW,CAAC;AACjD,MAAM,eAAe;AACrB,MAAM,wCAAwB,IAAI,IAAI;CACpC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,IAAA,sCAAe,eACb,+BACA,mBACA,gFACC,QAAQ,aAAa;CACpB,MAAM,iBAAiB,sBAAsB,QAAQ;CACrD,MAAM,YAAY,OAAgB,SAA0B;EAC1D,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;EAC3B,MAAM,UAAU,eAAe,KAAK;EACpC,OAAO,QAAQ,SAAS,YAAY,QAAQ,SAAS;CACvD;CACA,MAAM,iBAAiB,UAA4B;EACjD,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;EAC3B,IAAI,MAAM,SAAS,cAAc;GAC/B,MAAM,UAAU,eAAe,KAAK;GACpC,OACE,QAAQ,SAAS,YACjB,YAAY,IAAI,QAAQ,MAAM,MAC7B,QAAQ,aAAa,UAAU,QAAQ,aAAa;EAEzD;EACA,IAAI,MAAM,SAAS,oBAAoB,OAAO;EAC9C,MAAM,SAAS,OAAO,MAAM,MAAM,IAAI,eAAe,MAAM,MAAM,IAAI;EACrE,QACG,aAAa,MAAM,QAAQ,MAAM,UAChC,aAAa,MAAM,QAAQ,MAAM,cACnC,QAAQ,SAAS,YACjB,YAAY,IAAI,OAAO,MAAM,MAC5B,OAAO,aAAa,OAAO,OAAO,aAAa;CAEpD;CACA,MAAM,oBAAoB,UAA4B;EACpD,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;EAC3B,IAAI,MAAM,SAAS,cAAc;GAC/B,IAAI,SAAS,OAAO,WAAW,GAAG,OAAO;GACzC,MAAM,UAAU,eAAe,KAAK;GACpC,OACE,QAAQ,SAAS,WAAW,iBAAiB,QAAQ,WAAW;EAEpE;EACA,IAAI,MAAM,SAAS,aAAa,OAAO,MAAM,UAAU,UACrD,OAAO,CAAC,aAAa,KAAK,MAAM,KAAK;EACvC,IACE,MAAM,SAAS,qBACf,MAAM,QAAQ,MAAM,WAAW,KAC/B,MAAM,QAAQ,MAAM,MAAM,GAC1B;GACA,MAAM,QAAQ,MAAM,OAAO,GAAG,CAAC;GAC/B,MAAM,MAAM,OAAO,KAAK,IACpB,QAAQ,IAAI,MAAM,SAAS,CAAC,GAAG,KAAK,IACpC;GACJ,IAAI,OAAO,QAAQ,UAAU,OAAO;GACpC,IAAI,IAAI,SAAS,GAAG,OAAO,CAAC,aAAa,KAAK,GAAG;GACjD,OAAO,iBAAiB,MAAM,YAAY,GAAG,CAAC,CAAC;EACjD;EACA,IACE,MAAM,SAAS,sBACf,OAAO,MAAM,MAAM,KACnB,MAAM,OAAO,SAAS,kBACtB,eAAe,MAAM,OAAO,IAAI,MAAM,YACtC,eAAe,MAAM,OAAO,QAAQ,MAAM,QAE1C,OACE,aAAa,MAAM,QAAQ,MAAM,SACjC,aAAa,MAAM,QAAQ,MAAM;EAErC,IACE,MAAM,SAAS,oBACf,OAAO,MAAM,MAAM,KACnB,SAAS,MAAM,OAAO,QAAQ,SAAS,KACvC,aAAa,MAAM,OAAO,QAAQ,MAAM,OAExC,OAAO;EACT,IACE,MAAM,SAAS,oBACf,cAAc,MAAM,MAAM,KAC1B,MAAM,QAAQ,MAAM,SAAS,GAE7B,OAAO,iBAAiB,MAAM,UAAU,GAAG,CAAC,CAAC;EAC/C,IAAI,MAAM,SAAS,oBACjB,OAAO,iBAAiB,MAAM,IAAI;EACpC,OAAO;CACT;CACA,MAAM,eAAe,UAAkC;EACrD,IAAI,CAAC,OAAO,KAAK,GAAG,OAAO;EAC3B,IAAI,MAAM,SAAS,cAAc;GAC/B,MAAM,UAAU,eAAe,KAAK;GACpC,IAAI,QAAQ,SAAS,SAAS,OAAO,YAAY,QAAQ,WAAW;GACpE,OAAO,QAAQ,SAAS,YACtB,UAAU,IAAI,QAAQ,MAAM,KAC5B,WAAW,IAAI,QAAQ,QAAQ,IAC7B,QAAQ,WACR;EACN;EACA,IAAI,MAAM,SAAS,oBAAoB,OAAO;EAC9C,MAAM,SAAS,aAAa,MAAM,QAAQ;EAC1C,IAAI,WAAW,QAAQ,CAAC,WAAW,IAAI,MAAM,GAAG,OAAO;EACvD,IAAI,OAAO,MAAM,MAAM,GAAG;GACxB,MAAM,SAAS,eAAe,MAAM,MAAM;GAC1C,IACE,OAAO,SAAS,YAChB,UAAU,IAAI,OAAO,MAAM,MAC1B,OAAO,aAAa,OACnB,OAAO,aAAa,aACpB,OAAO,aAAa,aAEtB,OAAO;GACT,IACE,MAAM,OAAO,SAAS,sBACtB,aAAa,MAAM,OAAO,QAAQ,MAAM,cACxC,OAAO,MAAM,OAAO,MAAM,GAC1B;IACA,MAAM,YAAY,eAAe,MAAM,OAAO,MAAM;IACpD,OAAO,UAAU,SAAS,YACxB,UAAU,IAAI,UAAU,MAAM,MAC7B,UAAU,aAAa,OAAO,UAAU,aAAa,aACpD,SACA;GACN;EACF;EACA,OAAO;CACT;CACA,MAAM,YAAY,UAChB,OAAO,KAAK,KACZ,MAAM,SAAS,sBACf,aAAa,MAAM,QAAQ,MAAM,WACjC,SAAS,MAAM,QAAQ,KAAK;CAC9B,OAAO,EACL,eAAe,MAAM;EACnB,MAAM,YAAY,YAAY,KAAK,MAAM;EACzC,KACG,cAAc,QAAQ,SAAS,KAAK,MAAM,MAC3C,MAAM,QAAQ,KAAK,SAAS,KAC5B,iBACE,KAAK,UAAU,GACb,cAAc,QAAQ,sBAAsB,IAAI,SAAS,IACrD,IACA,CACN,CACF,GAEA,OAAO,IAAI;CACf,EACF;AACF,CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dangerous-type-assertions.cjs","names":[],"sources":["../src/no-dangerous-type-assertions.ts"],"sourcesContent":["// Disallow object-literal casts: `{...} as T`, `<T>{...}`.\n//\n// Casting an object literal to a type silently hides missing required\n// fields — the type system would otherwise catch the omission. Use a\n// typed binding (`const x: T = { ... }`) or `satisfies T` so missing\n// fields surface as type errors.\n//\n// Allows `as const` (legitimate widening control).\n// Allows `as any` / `as unknown` (those have dedicated rules / are\n// often used as the first step of an explicit launder).\n\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-dangerous-type-assertions\" },\n rules: {\n \"no-dangerous-type-assertions\": {\n meta: {\n type: \"problem\",\n messages: {\n noObjectLiteralCast:\n \"Don't cast an object literal with `as`. Type the binding \" +\n \"(`const x: T = { ... }`) or use `satisfies T` so missing \" +\n \"required fields fail typecheck.\",\n },\n },\n createOnce(context) {\n function unwrapExpression(expression) {\n let current = expression;\n while (\n current?.type === \"ParenthesizedExpression\" ||\n current?.type === \"TSAsExpression\" ||\n current?.type === \"TSTypeAssertion\" ||\n current?.type === \"TSNonNullExpression\" ||\n current?.type === \"TSSatisfiesExpression\"\n ) {\n current = current.expression;\n }\n return current;\n }\n\n function check(node) {\n const expression = unwrapExpression(node.expression);\n if (expression?.type !== \"ObjectExpression\") {\n return;\n }\n const ann = node.typeAnnotation;\n if (!ann) {\n return;\n }\n // Allow `as const`\n if (\n ann.type === \"TSTypeReference\" &&\n ann.typeName?.type === \"Identifier\" &&\n ann.typeName.name === \"const\"\n ) {\n return;\n }\n // `as any` and `as unknown` have their own rules / are\n // typically the laundering step, not the final shape claim.\n if (ann.type === \"TSAnyKeyword\" || ann.type === \"TSUnknownKeyword\") {\n return;\n }\n context.report({ node, messageId: \"noObjectLiteralCast\" });\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n"],"mappings":";;;;;AAaA,IAAA,wCAAA,
|
|
1
|
+
{"version":3,"file":"no-dangerous-type-assertions.cjs","names":["eslintCompatPlugin"],"sources":["../src/no-dangerous-type-assertions.ts"],"sourcesContent":["// Disallow object-literal casts: `{...} as T`, `<T>{...}`.\n//\n// Casting an object literal to a type silently hides missing required\n// fields — the type system would otherwise catch the omission. Use a\n// typed binding (`const x: T = { ... }`) or `satisfies T` so missing\n// fields surface as type errors.\n//\n// Allows `as const` (legitimate widening control).\n// Allows `as any` / `as unknown` (those have dedicated rules / are\n// often used as the first step of an explicit launder).\n\nimport { eslintCompatPlugin } from \"@oxlint/plugins\";\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-dangerous-type-assertions\" },\n rules: {\n \"no-dangerous-type-assertions\": {\n meta: {\n type: \"problem\",\n messages: {\n noObjectLiteralCast:\n \"Don't cast an object literal with `as`. Type the binding \" +\n \"(`const x: T = { ... }`) or use `satisfies T` so missing \" +\n \"required fields fail typecheck.\",\n },\n },\n createOnce(context) {\n function unwrapExpression(expression) {\n let current = expression;\n while (\n current?.type === \"ParenthesizedExpression\" ||\n current?.type === \"TSAsExpression\" ||\n current?.type === \"TSTypeAssertion\" ||\n current?.type === \"TSNonNullExpression\" ||\n current?.type === \"TSSatisfiesExpression\"\n ) {\n current = current.expression;\n }\n return current;\n }\n\n function check(node) {\n const expression = unwrapExpression(node.expression);\n if (expression?.type !== \"ObjectExpression\") {\n return;\n }\n const ann = node.typeAnnotation;\n if (!ann) {\n return;\n }\n // Allow `as const`\n if (\n ann.type === \"TSTypeReference\" &&\n ann.typeName?.type === \"Identifier\" &&\n ann.typeName.name === \"const\"\n ) {\n return;\n }\n // `as any` and `as unknown` have their own rules / are\n // typically the laundering step, not the final shape claim.\n if (ann.type === \"TSAnyKeyword\" || ann.type === \"TSUnknownKeyword\") {\n return;\n }\n context.report({ node, messageId: \"noObjectLiteralCast\" });\n }\n return {\n TSAsExpression: check,\n TSTypeAssertion: check,\n };\n },\n },\n },\n});\n"],"mappings":";;;;;AAaA,IAAA,wCAAA,4BAAeA,CAAAA,CAAAA,mBAAAA,CAAmB;CAChC,MAAM,EAAE,MAAM,+BAA+B;CAC7C,OAAO,EACL,gCAAgC;EAC9B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,qBACE,oJAGJ;EACF;EACA,WAAW,SAAS;GAClB,SAAS,iBAAiB,YAAY;IACpC,IAAI,UAAU;IACd,OACE,SAAS,SAAS,6BAClB,SAAS,SAAS,oBAClB,SAAS,SAAS,qBAClB,SAAS,SAAS,yBAClB,SAAS,SAAS,yBAElB,UAAU,QAAQ;IAEpB,OAAO;GACT;GAEA,SAAS,MAAM,MAAM;IAEnB,IADmB,iBAAiB,KAAK,UAC5B,CAAC,EAAE,SAAS,oBACvB;IAEF,MAAM,MAAM,KAAK;IACjB,IAAI,CAAC,KACH;IAGF,IACE,IAAI,SAAS,qBACb,IAAI,UAAU,SAAS,gBACvB,IAAI,SAAS,SAAS,SAEtB;IAIF,IAAI,IAAI,SAAS,kBAAkB,IAAI,SAAS,oBAC9C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAAsB,CAAC;GAC3D;GACA,OAAO;IACL,gBAAgB;IAChB,iBAAiB;GACnB;EACF;CACF,EACF;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-partial-record-satisfies.cjs","names":["isIdentifier"],"sources":["../src/no-partial-record-satisfies.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\n// Ban `<object literal> satisfies Partial<Record<Union, T>>` (with or\n// without a leading `as const`).\n//\n// A const object literal pinned with `satisfies` against a `Record` keyed by\n// a union declares itself the classification of every union member —\n// `satisfies` fails typecheck the moment a new member is added and left\n// unhandled. Wrapping that `Record` in `Partial` cancels the guarantee: a\n// missing member typechecks silently, so the literal can drift out of sync\n// with the union with no compiler signal. That silent-drift surface can cause\n// a classification-table bug when a newly added member falls through a\n// `Partial<Record<...>>` lookup instead of failing typecheck.\n//\n// `Readonly<...>` wrapping either side (`Readonly<Partial<Record<...>>>` or\n// `Partial<Readonly<Record<...>>>`) is unwrapped before the check, since it\n// changes mutability, not totality.\n//\n// Only the `satisfies` expression on an object literal is flagged. Plain\n// type-annotation positions (`const x: Partial<Record<...>> = {...}`),\n// function parameters, and variable/return type annotations are exempt:\n// those are legitimate uses (override inputs, accumulators, genuinely sparse\n// data) and carry no compiler guarantee of totality to lose.\n//\n// Flagged:\n// const x = {\n// a: 1,\n// } as const satisfies Partial<Record<Union, number>>;\n// const y = { a: 1 } satisfies Partial<Record<Union, number>>;\n// const z = {\n// a: 1,\n// } as const satisfies Readonly<Partial<Record<Union, number>>>;\n//\n// Allowed:\n// const x = {\n// a: 1,\n// } as const satisfies Record<Union, number>;\n// const fn = (overrides: Partial<Record<Union, number>>) => overrides;\n// let acc: Partial<Record<Union, number>> = {};\n// type Sparse = Partial<Record<Union, number>>;\n//\n// Fix by making the record total over the union — an explicit \"none\" /\n// `false` / `null` value for members where nothing applies forces a new\n// union member to be classified before it typechecks. If only a subset of\n// the union is ever relevant, derive a narrower union for the keys instead\n// of falling back to `Partial`. For genuinely sparse data where absence has\n// documented meaning (e.g. an unbounded key space), add an eslint-disable\n// with a comment naming what absence means.\n\nimport { isIdentifier } from \"./utils.ts\";\n\nconst unwrapReadonlyType = (typeNode) => {\n if (\n typeNode?.type === \"TSTypeReference\" &&\n isIdentifier(typeNode.typeName, \"Readonly\")\n ) {\n return typeNode.typeArguments?.params?.at(0) ?? null;\n }\n return typeNode;\n};\n\n// Matches `Partial<Record<...>>`, unwrapping a `Readonly<...>` wrapper on\n// either the outer (`Readonly<Partial<Record<...>>>`) or inner\n// (`Partial<Readonly<Record<...>>>`) type.\nconst isPartialRecordType = (typeNode) => {\n const outer = unwrapReadonlyType(typeNode);\n if (\n outer?.type !== \"TSTypeReference\" ||\n !isIdentifier(outer.typeName, \"Partial\")\n ) {\n return false;\n }\n const inner = unwrapReadonlyType(outer.typeArguments?.params?.at(0) ?? null);\n return (\n inner?.type === \"TSTypeReference\" && isIdentifier(inner.typeName, \"Record\")\n );\n};\n\n// Matches an object literal, or an object literal wrapped in `as const`.\nconst isObjectLiteralExpression = (node) => {\n if (node?.type === \"ObjectExpression\") {\n return true;\n }\n if (\n node?.type === \"TSAsExpression\" &&\n node.typeAnnotation?.type === \"TSTypeReference\" &&\n isIdentifier(node.typeAnnotation.typeName, \"const\")\n ) {\n return isObjectLiteralExpression(node.expression);\n }\n return false;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-partial-record-satisfies\" },\n rules: {\n \"no-partial-record-satisfies\": {\n meta: {\n type: \"problem\",\n messages: {\n noPartialRecordSatisfies:\n \"Don't pin an object literal with `satisfies Partial<Record<...>>` \" +\n \"— Partial cancels the totality check satisfies would otherwise \" +\n \"give you, so a union member can silently fall through. Make the \" +\n \"record total (explicit 'none'/false/null per member), narrow the \" +\n \"key union, or add an eslint-disable naming what absence means \" +\n \"for genuinely sparse data.\",\n },\n },\n createOnce(context) {\n return {\n TSSatisfiesExpression(node) {\n if (!isPartialRecordType(node.typeAnnotation)) {\n return;\n }\n if (!isObjectLiteralExpression(node.expression)) {\n return;\n }\n context.report({ node, messageId: \"noPartialRecordSatisfies\" });\n },\n };\n },\n },\n },\n});\n"],"mappings":";;;;;;;AAkDA,MAAM,sBAAsB,aAAa;CACvC,IACE,UAAU,SAAS,qBACnBA,cAAAA,aAAa,SAAS,UAAU,UAAU,GAE1C,OAAO,SAAS,eAAe,QAAQ,GAAG,CAAC,KAAK;CAElD,OAAO;AACT;AAKA,MAAM,uBAAuB,aAAa;CACxC,MAAM,QAAQ,mBAAmB,QAAQ;CACzC,IACE,OAAO,SAAS,qBAChB,CAACA,cAAAA,aAAa,MAAM,UAAU,SAAS,GAEvC,OAAO;CAET,MAAM,QAAQ,mBAAmB,MAAM,eAAe,QAAQ,GAAG,CAAC,KAAK,IAAI;CAC3E,OACE,OAAO,SAAS,qBAAqBA,cAAAA,aAAa,MAAM,UAAU,QAAQ;AAE9E;AAGA,MAAM,6BAA6B,SAAS;CAC1C,IAAI,MAAM,SAAS,oBACjB,OAAO;CAET,IACE,MAAM,SAAS,oBACf,KAAK,gBAAgB,SAAS,qBAC9BA,cAAAA,aAAa,KAAK,eAAe,UAAU,OAAO,GAElD,OAAO,0BAA0B,KAAK,UAAU;CAElD,OAAO;AACT;AAEA,IAAA,uCAAA,
|
|
1
|
+
{"version":3,"file":"no-partial-record-satisfies.cjs","names":["isIdentifier","eslintCompatPlugin"],"sources":["../src/no-partial-record-satisfies.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\n// Ban `<object literal> satisfies Partial<Record<Union, T>>` (with or\n// without a leading `as const`).\n//\n// A const object literal pinned with `satisfies` against a `Record` keyed by\n// a union declares itself the classification of every union member —\n// `satisfies` fails typecheck the moment a new member is added and left\n// unhandled. Wrapping that `Record` in `Partial` cancels the guarantee: a\n// missing member typechecks silently, so the literal can drift out of sync\n// with the union with no compiler signal. That silent-drift surface can cause\n// a classification-table bug when a newly added member falls through a\n// `Partial<Record<...>>` lookup instead of failing typecheck.\n//\n// `Readonly<...>` wrapping either side (`Readonly<Partial<Record<...>>>` or\n// `Partial<Readonly<Record<...>>>`) is unwrapped before the check, since it\n// changes mutability, not totality.\n//\n// Only the `satisfies` expression on an object literal is flagged. Plain\n// type-annotation positions (`const x: Partial<Record<...>> = {...}`),\n// function parameters, and variable/return type annotations are exempt:\n// those are legitimate uses (override inputs, accumulators, genuinely sparse\n// data) and carry no compiler guarantee of totality to lose.\n//\n// Flagged:\n// const x = {\n// a: 1,\n// } as const satisfies Partial<Record<Union, number>>;\n// const y = { a: 1 } satisfies Partial<Record<Union, number>>;\n// const z = {\n// a: 1,\n// } as const satisfies Readonly<Partial<Record<Union, number>>>;\n//\n// Allowed:\n// const x = {\n// a: 1,\n// } as const satisfies Record<Union, number>;\n// const fn = (overrides: Partial<Record<Union, number>>) => overrides;\n// let acc: Partial<Record<Union, number>> = {};\n// type Sparse = Partial<Record<Union, number>>;\n//\n// Fix by making the record total over the union — an explicit \"none\" /\n// `false` / `null` value for members where nothing applies forces a new\n// union member to be classified before it typechecks. If only a subset of\n// the union is ever relevant, derive a narrower union for the keys instead\n// of falling back to `Partial`. For genuinely sparse data where absence has\n// documented meaning (e.g. an unbounded key space), add an eslint-disable\n// with a comment naming what absence means.\n\nimport { isIdentifier } from \"./utils.ts\";\n\nconst unwrapReadonlyType = (typeNode) => {\n if (\n typeNode?.type === \"TSTypeReference\" &&\n isIdentifier(typeNode.typeName, \"Readonly\")\n ) {\n return typeNode.typeArguments?.params?.at(0) ?? null;\n }\n return typeNode;\n};\n\n// Matches `Partial<Record<...>>`, unwrapping a `Readonly<...>` wrapper on\n// either the outer (`Readonly<Partial<Record<...>>>`) or inner\n// (`Partial<Readonly<Record<...>>>`) type.\nconst isPartialRecordType = (typeNode) => {\n const outer = unwrapReadonlyType(typeNode);\n if (\n outer?.type !== \"TSTypeReference\" ||\n !isIdentifier(outer.typeName, \"Partial\")\n ) {\n return false;\n }\n const inner = unwrapReadonlyType(outer.typeArguments?.params?.at(0) ?? null);\n return (\n inner?.type === \"TSTypeReference\" && isIdentifier(inner.typeName, \"Record\")\n );\n};\n\n// Matches an object literal, or an object literal wrapped in `as const`.\nconst isObjectLiteralExpression = (node) => {\n if (node?.type === \"ObjectExpression\") {\n return true;\n }\n if (\n node?.type === \"TSAsExpression\" &&\n node.typeAnnotation?.type === \"TSTypeReference\" &&\n isIdentifier(node.typeAnnotation.typeName, \"const\")\n ) {\n return isObjectLiteralExpression(node.expression);\n }\n return false;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-partial-record-satisfies\" },\n rules: {\n \"no-partial-record-satisfies\": {\n meta: {\n type: \"problem\",\n messages: {\n noPartialRecordSatisfies:\n \"Don't pin an object literal with `satisfies Partial<Record<...>>` \" +\n \"— Partial cancels the totality check satisfies would otherwise \" +\n \"give you, so a union member can silently fall through. Make the \" +\n \"record total (explicit 'none'/false/null per member), narrow the \" +\n \"key union, or add an eslint-disable naming what absence means \" +\n \"for genuinely sparse data.\",\n },\n },\n createOnce(context) {\n return {\n TSSatisfiesExpression(node) {\n if (!isPartialRecordType(node.typeAnnotation)) {\n return;\n }\n if (!isObjectLiteralExpression(node.expression)) {\n return;\n }\n context.report({ node, messageId: \"noPartialRecordSatisfies\" });\n },\n };\n },\n },\n },\n});\n"],"mappings":";;;;;;;AAkDA,MAAM,sBAAsB,aAAa;CACvC,IACE,UAAU,SAAS,qBACnBA,cAAAA,aAAa,SAAS,UAAU,UAAU,GAE1C,OAAO,SAAS,eAAe,QAAQ,GAAG,CAAC,KAAK;CAElD,OAAO;AACT;AAKA,MAAM,uBAAuB,aAAa;CACxC,MAAM,QAAQ,mBAAmB,QAAQ;CACzC,IACE,OAAO,SAAS,qBAChB,CAACA,cAAAA,aAAa,MAAM,UAAU,SAAS,GAEvC,OAAO;CAET,MAAM,QAAQ,mBAAmB,MAAM,eAAe,QAAQ,GAAG,CAAC,KAAK,IAAI;CAC3E,OACE,OAAO,SAAS,qBAAqBA,cAAAA,aAAa,MAAM,UAAU,QAAQ;AAE9E;AAGA,MAAM,6BAA6B,SAAS;CAC1C,IAAI,MAAM,SAAS,oBACjB,OAAO;CAET,IACE,MAAM,SAAS,oBACf,KAAK,gBAAgB,SAAS,qBAC9BA,cAAAA,aAAa,KAAK,eAAe,UAAU,OAAO,GAElD,OAAO,0BAA0B,KAAK,UAAU;CAElD,OAAO;AACT;AAEA,IAAA,uCAAA,GAAeC,gBAAAA,mBAAAA,CAAmB;CAChC,MAAM,EAAE,MAAM,8BAA8B;CAC5C,OAAO,EACL,+BAA+B;EAC7B,MAAM;GACJ,MAAM;GACN,UAAU,EACR,0BACE,6VAMJ;EACF;EACA,WAAW,SAAS;GAClB,OAAO,EACL,sBAAsB,MAAM;IAC1B,IAAI,CAAC,oBAAoB,KAAK,cAAc,GAC1C;IAEF,IAAI,CAAC,0BAA0B,KAAK,UAAU,GAC5C;IAEF,QAAQ,OAAO;KAAE;KAAM,WAAW;IAA2B,CAAC;GAChE,EACF;EACF;CACF,EACF;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-raw-route-query-client.cjs","names":["getPropertyName","isIdentifier","getImportedName"],"sources":["../src/no-raw-route-query-client.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\nimport type { Ranged } from \"@oxlint/plugins\";\n\nimport { getImportedName, getPropertyName, isIdentifier } from \"./utils.ts\";\n\nconst RAW_QUERY_CLIENT_METHODS = new Map([\n [\"ensureQueryData\", \"ensureRouteQueryData\"],\n [\"ensureInfiniteQueryData\", \"ensureRouteInfiniteQueryData\"],\n [\"fetchQuery\", \"fetchRouteQuery\"],\n [\"prefetchQuery\", \"prefetchRouteQuery\"],\n]);\nconst RAW_QUERY_CLIENT_RECEIVERS = new Set([\"qc\", \"queryClient\"]);\nconst TANSTACK_QUERY_MODULE = \"@tanstack/react-query\";\nconst ROUTE_QUERY_HELPERS_MODULE = \"@/lib/react-query\";\nconst GENERIC_ROUTE_QUERY_HELPERS = new Map([\n [\"ensureCriticalQueryData\", \"ensureRouteQueryData\"],\n [\"prefetchNonCriticalQuery\", \"prefetchRouteQuery\"],\n]);\nconst QUERY_SUBSCRIPTION_HOOKS = new Set([\n \"useInfiniteQuery\",\n \"useQueries\",\n \"useQuery\",\n \"useSuspenseInfiniteQuery\",\n \"useSuspenseQueries\",\n \"useSuspenseQuery\",\n]);\n\nconst ROUTE_LOADER_HOOKS = new Set([\"beforeLoad\", \"loader\"]);\nconst ROUTE_PENDING_COMPONENT_HOOK = \"pendingComponent\";\n\nconst isQueryClientReceiver = (node) => {\n if (!node) {\n return false;\n }\n\n if (node.type === \"Identifier\") {\n return RAW_QUERY_CLIENT_RECEIVERS.has(node.name);\n }\n\n if (node.type !== \"MemberExpression\" || node.computed) {\n return false;\n }\n\n const receiverName = getPropertyName(node.property);\n return receiverName === \"queryClient\";\n};\n\nconst findRouteLoaderHook = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name !== null && ROUTE_LOADER_HOOKS.has(name)) {\n return name;\n }\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst findPendingComponentCandidate = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name === ROUTE_PENDING_COMPONENT_HOOK) {\n return ROUTE_PENDING_COMPONENT_HOOK;\n }\n }\n\n if (current.type === \"FunctionDeclaration\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n if (current.type === \"VariableDeclarator\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst getQuerySubscriptionHookName = (\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n) => {\n if (isIdentifier(callee) && queryHookAliases.has(callee.name)) {\n return callee.name;\n }\n\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n !isIdentifier(callee.object) ||\n !tanstackQueryNamespaces.has(callee.object.name)\n ) {\n return null;\n }\n\n const hookName = getPropertyName(callee.property);\n return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName)\n ? hookName\n : null;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-raw-route-query-client\" },\n rules: {\n \"no-raw-route-query-client\": {\n meta: {\n type: \"problem\",\n messages: {\n noRawRouteQueryClient:\n \"Raw queryClient.{{method}} inside route {{hook}} \" +\n \"is forbidden. Use {{replacement}}(...), so route-seeded \" +\n \"queries carry a freshness window.\",\n noGenericRouteQueryHelper:\n \"Generic {{helper}} inside route {{hook}} is forbidden. \" +\n \"Use {{replacement}}(...), so route-seeded queries carry \" +\n \"a freshness window.\",\n noQueryHookInPendingComponent:\n \"TanStack Query subscription hook {{hook}} is forbidden \" +\n \"inside route pendingComponent {{component}}. Read cached \" +\n \"data synchronously with useQueryClient().getQueryData(...) \" +\n \"so abandoned pending renders cannot receive async state updates.\",\n },\n },\n createOnce(context) {\n const genericHelperAliases = new Map();\n const pendingComponentNames = new Set();\n const pendingQueryHookCalls: {\n component: string;\n hook: string;\n node: Ranged;\n }[] = [];\n const queryHookAliases = new Set();\n const routeQueryHelperNamespaces = new Set();\n const tanstackQueryNamespaces = new Set();\n\n return {\n before() {\n genericHelperAliases.clear();\n pendingComponentNames.clear();\n pendingQueryHookCalls.length = 0;\n queryHookAliases.clear();\n routeQueryHelperNamespaces.clear();\n tanstackQueryNamespaces.clear();\n },\n ImportDeclaration(node) {\n if (node.source?.value === TANSTACK_QUERY_MODULE) {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n tanstackQueryNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n if (\n importedName !== null &&\n QUERY_SUBSCRIPTION_HOOKS.has(importedName)\n ) {\n queryHookAliases.add(specifier.local.name);\n }\n }\n return;\n }\n\n if (node.source?.value !== ROUTE_QUERY_HELPERS_MODULE) {\n return;\n }\n\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n routeQueryHelperNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n const replacement =\n importedName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(importedName);\n if (replacement === undefined) {\n continue;\n }\n\n genericHelperAliases.set(specifier.local.name, {\n helper: importedName,\n replacement,\n });\n }\n },\n\n Property(node) {\n const name = getPropertyName(node.key);\n if (name !== ROUTE_PENDING_COMPONENT_HOOK) {\n return;\n }\n if (isIdentifier(node.value)) {\n pendingComponentNames.add(node.value.name);\n }\n },\n\n CallExpression(node) {\n const hook = findRouteLoaderHook(node);\n const callee = node.callee;\n\n if (hook !== null) {\n if (isIdentifier(callee)) {\n const helper = genericHelperAliases.get(callee.name);\n if (helper !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { hook, ...helper },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n callee.computed === false &&\n isIdentifier(callee.object) &&\n routeQueryHelperNamespaces.has(callee.object.name)\n ) {\n const helperName = getPropertyName(callee.property);\n const replacement =\n helperName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(helperName);\n\n if (replacement !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { helper: helperName, hook, replacement },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n isQueryClientReceiver(callee.object)\n ) {\n const method = getPropertyName(callee.property);\n const replacement =\n method === null\n ? undefined\n : RAW_QUERY_CLIENT_METHODS.get(method);\n\n if (replacement === undefined) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noRawRouteQueryClient\",\n data: { method, hook, replacement },\n });\n return;\n }\n }\n\n const pendingComponent = findPendingComponentCandidate(node);\n if (pendingComponent === null) {\n return;\n }\n\n const queryHookName = getQuerySubscriptionHookName(\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n );\n if (queryHookName === null) {\n return;\n }\n\n pendingQueryHookCalls.push({\n component: pendingComponent,\n hook: queryHookName,\n node,\n });\n },\n\n \"Program:exit\"() {\n for (const { component, hook, node } of pendingQueryHookCalls) {\n if (\n component !== ROUTE_PENDING_COMPONENT_HOOK &&\n !pendingComponentNames.has(component)\n ) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noQueryHookInPendingComponent\",\n data: { component, hook },\n });\n }\n },\n };\n },\n },\n },\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"no-raw-route-query-client.cjs","names":["getPropertyName","isIdentifier","eslintCompatPlugin","getImportedName"],"sources":["../src/no-raw-route-query-client.ts"],"sourcesContent":["import { eslintCompatPlugin } from \"@oxlint/plugins\";\nimport type { Ranged } from \"@oxlint/plugins\";\n\nimport { getImportedName, getPropertyName, isIdentifier } from \"./utils.ts\";\n\nconst RAW_QUERY_CLIENT_METHODS = new Map([\n [\"ensureQueryData\", \"ensureRouteQueryData\"],\n [\"ensureInfiniteQueryData\", \"ensureRouteInfiniteQueryData\"],\n [\"fetchQuery\", \"fetchRouteQuery\"],\n [\"prefetchQuery\", \"prefetchRouteQuery\"],\n]);\nconst RAW_QUERY_CLIENT_RECEIVERS = new Set([\"qc\", \"queryClient\"]);\nconst TANSTACK_QUERY_MODULE = \"@tanstack/react-query\";\nconst ROUTE_QUERY_HELPERS_MODULE = \"@/lib/react-query\";\nconst GENERIC_ROUTE_QUERY_HELPERS = new Map([\n [\"ensureCriticalQueryData\", \"ensureRouteQueryData\"],\n [\"prefetchNonCriticalQuery\", \"prefetchRouteQuery\"],\n]);\nconst QUERY_SUBSCRIPTION_HOOKS = new Set([\n \"useInfiniteQuery\",\n \"useQueries\",\n \"useQuery\",\n \"useSuspenseInfiniteQuery\",\n \"useSuspenseQueries\",\n \"useSuspenseQuery\",\n]);\n\nconst ROUTE_LOADER_HOOKS = new Set([\"beforeLoad\", \"loader\"]);\nconst ROUTE_PENDING_COMPONENT_HOOK = \"pendingComponent\";\n\nconst isQueryClientReceiver = (node) => {\n if (!node) {\n return false;\n }\n\n if (node.type === \"Identifier\") {\n return RAW_QUERY_CLIENT_RECEIVERS.has(node.name);\n }\n\n if (node.type !== \"MemberExpression\" || node.computed) {\n return false;\n }\n\n const receiverName = getPropertyName(node.property);\n return receiverName === \"queryClient\";\n};\n\nconst findRouteLoaderHook = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name !== null && ROUTE_LOADER_HOOKS.has(name)) {\n return name;\n }\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst findPendingComponentCandidate = (node) => {\n let current = node.parent;\n\n while (current) {\n if (current.type === \"Property\") {\n const name = getPropertyName(current.key);\n\n if (name === ROUTE_PENDING_COMPONENT_HOOK) {\n return ROUTE_PENDING_COMPONENT_HOOK;\n }\n }\n\n if (current.type === \"FunctionDeclaration\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n if (current.type === \"VariableDeclarator\" && isIdentifier(current.id)) {\n return current.id.name;\n }\n\n current = current.parent;\n }\n\n return null;\n};\n\nconst getQuerySubscriptionHookName = (\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n) => {\n if (isIdentifier(callee) && queryHookAliases.has(callee.name)) {\n return callee.name;\n }\n\n if (\n callee.type !== \"MemberExpression\" ||\n callee.computed ||\n !isIdentifier(callee.object) ||\n !tanstackQueryNamespaces.has(callee.object.name)\n ) {\n return null;\n }\n\n const hookName = getPropertyName(callee.property);\n return hookName !== null && QUERY_SUBSCRIPTION_HOOKS.has(hookName)\n ? hookName\n : null;\n};\n\nexport default eslintCompatPlugin({\n meta: { name: \"no-raw-route-query-client\" },\n rules: {\n \"no-raw-route-query-client\": {\n meta: {\n type: \"problem\",\n messages: {\n noRawRouteQueryClient:\n \"Raw queryClient.{{method}} inside route {{hook}} \" +\n \"is forbidden. Use {{replacement}}(...), so route-seeded \" +\n \"queries carry a freshness window.\",\n noGenericRouteQueryHelper:\n \"Generic {{helper}} inside route {{hook}} is forbidden. \" +\n \"Use {{replacement}}(...), so route-seeded queries carry \" +\n \"a freshness window.\",\n noQueryHookInPendingComponent:\n \"TanStack Query subscription hook {{hook}} is forbidden \" +\n \"inside route pendingComponent {{component}}. Read cached \" +\n \"data synchronously with useQueryClient().getQueryData(...) \" +\n \"so abandoned pending renders cannot receive async state updates.\",\n },\n },\n createOnce(context) {\n const genericHelperAliases = new Map();\n const pendingComponentNames = new Set();\n const pendingQueryHookCalls: {\n component: string;\n hook: string;\n node: Ranged;\n }[] = [];\n const queryHookAliases = new Set();\n const routeQueryHelperNamespaces = new Set();\n const tanstackQueryNamespaces = new Set();\n\n return {\n before() {\n genericHelperAliases.clear();\n pendingComponentNames.clear();\n pendingQueryHookCalls.length = 0;\n queryHookAliases.clear();\n routeQueryHelperNamespaces.clear();\n tanstackQueryNamespaces.clear();\n },\n ImportDeclaration(node) {\n if (node.source?.value === TANSTACK_QUERY_MODULE) {\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n tanstackQueryNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n if (\n importedName !== null &&\n QUERY_SUBSCRIPTION_HOOKS.has(importedName)\n ) {\n queryHookAliases.add(specifier.local.name);\n }\n }\n return;\n }\n\n if (node.source?.value !== ROUTE_QUERY_HELPERS_MODULE) {\n return;\n }\n\n for (const specifier of node.specifiers) {\n if (specifier.type === \"ImportNamespaceSpecifier\") {\n routeQueryHelperNamespaces.add(specifier.local.name);\n continue;\n }\n if (specifier.type !== \"ImportSpecifier\") {\n continue;\n }\n\n const importedName = getImportedName(specifier);\n const replacement =\n importedName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(importedName);\n if (replacement === undefined) {\n continue;\n }\n\n genericHelperAliases.set(specifier.local.name, {\n helper: importedName,\n replacement,\n });\n }\n },\n\n Property(node) {\n const name = getPropertyName(node.key);\n if (name !== ROUTE_PENDING_COMPONENT_HOOK) {\n return;\n }\n if (isIdentifier(node.value)) {\n pendingComponentNames.add(node.value.name);\n }\n },\n\n CallExpression(node) {\n const hook = findRouteLoaderHook(node);\n const callee = node.callee;\n\n if (hook !== null) {\n if (isIdentifier(callee)) {\n const helper = genericHelperAliases.get(callee.name);\n if (helper !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { hook, ...helper },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n callee.computed === false &&\n isIdentifier(callee.object) &&\n routeQueryHelperNamespaces.has(callee.object.name)\n ) {\n const helperName = getPropertyName(callee.property);\n const replacement =\n helperName === null\n ? undefined\n : GENERIC_ROUTE_QUERY_HELPERS.get(helperName);\n\n if (replacement !== undefined) {\n context.report({\n node,\n messageId: \"noGenericRouteQueryHelper\",\n data: { helper: helperName, hook, replacement },\n });\n return;\n }\n }\n\n if (\n callee.type === \"MemberExpression\" &&\n !callee.computed &&\n isQueryClientReceiver(callee.object)\n ) {\n const method = getPropertyName(callee.property);\n const replacement =\n method === null\n ? undefined\n : RAW_QUERY_CLIENT_METHODS.get(method);\n\n if (replacement === undefined) {\n return;\n }\n\n context.report({\n node,\n messageId: \"noRawRouteQueryClient\",\n data: { method, hook, replacement },\n });\n return;\n }\n }\n\n const pendingComponent = findPendingComponentCandidate(node);\n if (pendingComponent === null) {\n return;\n }\n\n const queryHookName = getQuerySubscriptionHookName(\n callee,\n queryHookAliases,\n tanstackQueryNamespaces,\n );\n if (queryHookName === null) {\n return;\n }\n\n pendingQueryHookCalls.push({\n component: pendingComponent,\n hook: queryHookName,\n node,\n });\n },\n\n \"Program:exit\"() {\n for (const { component, hook, node } of pendingQueryHookCalls) {\n if (\n component !== ROUTE_PENDING_COMPONENT_HOOK &&\n !pendingComponentNames.has(component)\n ) {\n continue;\n }\n\n context.report({\n node,\n messageId: \"noQueryHookInPendingComponent\",\n data: { component, hook },\n });\n }\n },\n };\n },\n },\n },\n});\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as getImportedName, o as isIdentifier, r as getPropertyName } from "./utils.mjs";
|
|
2
2
|
import { eslintCompatPlugin } from "@oxlint/plugins";
|
|
3
3
|
//#region src/no-raw-route-query-client.ts
|
|
4
4
|
const RAW_QUERY_CLIENT_METHODS = /* @__PURE__ */ new Map([
|