@stll/oxlint-plugin 0.5.0 → 0.6.1

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.d.mts CHANGED
@@ -1,30 +1,39 @@
1
1
  //#region src/index.d.ts
2
- declare const noAnyCastsPluginSpecifier = "@stll/oxlint-plugin/no-any-casts";
3
- declare const noDangerousTypeAssertionsPluginSpecifier = "@stll/oxlint-plugin/no-dangerous-type-assertions";
4
- declare const noPartialRecordSatisfiesPluginSpecifier = "@stll/oxlint-plugin/no-partial-record-satisfies";
5
- declare const noUnsafeInnerHtmlPluginSpecifier = "@stll/oxlint-plugin/no-unsafe-inner-html";
6
- declare const noRawRouteQueryClientPluginSpecifier = "@stll/oxlint-plugin/no-raw-route-query-client";
7
- declare const requireLoaderPrefetchPluginSpecifier = "@stll/oxlint-plugin/require-loader-prefetch";
8
- declare const portableSafetyPluginSpecifiers: string[];
9
- declare const portableSafetyRules: {
2
+ export declare const noAnyCastsPluginSpecifier = "@stll/oxlint-plugin/no-any-casts";
3
+ export declare const noDangerousTypeAssertionsPluginSpecifier = "@stll/oxlint-plugin/no-dangerous-type-assertions";
4
+ export declare const noPartialRecordSatisfiesPluginSpecifier = "@stll/oxlint-plugin/no-partial-record-satisfies";
5
+ export declare const noUnsafeInnerHtmlPluginSpecifier = "@stll/oxlint-plugin/no-unsafe-inner-html";
6
+ export declare const noRawRouteQueryClientPluginSpecifier = "@stll/oxlint-plugin/no-raw-route-query-client";
7
+ export declare const requireLoaderPrefetchPluginSpecifier = "@stll/oxlint-plugin/require-loader-prefetch";
8
+ export declare const bunTestHygienePluginSpecifier = "@stll/oxlint-plugin/bun-test-hygiene";
9
+ export declare const bunTestHygienePluginSpecifiers: string[];
10
+ export declare const bunTestHygieneRules: {
11
+ "bun-test-hygiene/no-disabled-tests": "error";
12
+ "bun-test-hygiene/no-focused-tests": "error";
13
+ "bun-test-hygiene/no-forced-database-drop": "error";
14
+ "bun-test-hygiene/no-identical-title": "error";
15
+ "bun-test-hygiene/no-promise-matchers": "error";
16
+ };
17
+ export declare const portableSafetyPluginSpecifiers: string[];
18
+ export declare const portableSafetyRules: {
10
19
  "no-any-casts/no-any-casts": "error";
11
20
  "no-dangerous-type-assertions/no-dangerous-type-assertions": "error";
12
21
  "no-partial-record-satisfies/no-partial-record-satisfies": "error";
13
22
  "no-unsafe-inner-html/no-unsafe-inner-html": "error";
14
23
  };
15
- declare const routeQueryPluginSpecifiers: string[];
16
- declare const routeQueryRules: {
24
+ export declare const routeQueryPluginSpecifiers: string[];
25
+ export declare const routeQueryRules: {
17
26
  "no-raw-route-query-client/no-raw-route-query-client": "error";
18
27
  "require-loader-prefetch/require-loader-prefetch": "error";
19
28
  };
20
- declare const noWallClockInTestsPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-in-tests";
21
- declare const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-timer-waits-in-tests";
22
- declare const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
23
- declare const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
24
- declare const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
25
- declare const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
26
- declare const testReliabilityPluginSpecifiers: string[];
27
- declare const testReliabilityRules: {
29
+ export declare const noWallClockInTestsPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-in-tests";
30
+ export declare const noRealTimerWaitsInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-timer-waits-in-tests";
31
+ export declare const noUnseededRandomInTestsPluginSpecifier = "@stll/oxlint-plugin/no-unseeded-random-in-tests";
32
+ export declare const noRealNetworkInTestsPluginSpecifier = "@stll/oxlint-plugin/no-real-network-in-tests";
33
+ export declare const noCheckoutWritesInTestsPluginSpecifier = "@stll/oxlint-plugin/no-checkout-writes-in-tests";
34
+ export declare const noWallClockBoundPluginSpecifier = "@stll/oxlint-plugin/no-wall-clock-bound";
35
+ export declare const testReliabilityPluginSpecifiers: string[];
36
+ export declare const testReliabilityRules: {
28
37
  "no-wall-clock-in-tests/no-wall-clock-in-tests": "error";
29
38
  "no-real-timer-waits-in-tests/no-real-timer-waits-in-tests": "error";
30
39
  "no-unseeded-random-in-tests/no-unseeded-random-in-tests": "error";
@@ -33,5 +42,4 @@ declare const testReliabilityRules: {
33
42
  "no-wall-clock-bound/no-wall-clock-bound": "error";
34
43
  };
35
44
  //#endregion
36
- export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
37
45
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -5,6 +5,15 @@ const noPartialRecordSatisfiesPluginSpecifier = "@stll/oxlint-plugin/no-partial-
5
5
  const noUnsafeInnerHtmlPluginSpecifier = "@stll/oxlint-plugin/no-unsafe-inner-html";
6
6
  const noRawRouteQueryClientPluginSpecifier = "@stll/oxlint-plugin/no-raw-route-query-client";
7
7
  const requireLoaderPrefetchPluginSpecifier = "@stll/oxlint-plugin/require-loader-prefetch";
8
+ const bunTestHygienePluginSpecifier = "@stll/oxlint-plugin/bun-test-hygiene";
9
+ const bunTestHygienePluginSpecifiers = [bunTestHygienePluginSpecifier];
10
+ const bunTestHygieneRules = {
11
+ "bun-test-hygiene/no-disabled-tests": "error",
12
+ "bun-test-hygiene/no-focused-tests": "error",
13
+ "bun-test-hygiene/no-forced-database-drop": "error",
14
+ "bun-test-hygiene/no-identical-title": "error",
15
+ "bun-test-hygiene/no-promise-matchers": "error"
16
+ };
8
17
  const portableSafetyPluginSpecifiers = [
9
18
  noAnyCastsPluginSpecifier,
10
19
  noDangerousTypeAssertionsPluginSpecifier,
@@ -45,6 +54,6 @@ const testReliabilityRules = {
45
54
  "no-wall-clock-bound/no-wall-clock-bound": "error"
46
55
  };
47
56
  //#endregion
48
- export { noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
57
+ export { bunTestHygienePluginSpecifier, bunTestHygienePluginSpecifiers, bunTestHygieneRules, noAnyCastsPluginSpecifier, noCheckoutWritesInTestsPluginSpecifier, noDangerousTypeAssertionsPluginSpecifier, noPartialRecordSatisfiesPluginSpecifier, noRawRouteQueryClientPluginSpecifier, noRealNetworkInTestsPluginSpecifier, noRealTimerWaitsInTestsPluginSpecifier, noUnsafeInnerHtmlPluginSpecifier, noUnseededRandomInTestsPluginSpecifier, noWallClockBoundPluginSpecifier, noWallClockInTestsPluginSpecifier, portableSafetyPluginSpecifiers, portableSafetyRules, requireLoaderPrefetchPluginSpecifier, routeQueryPluginSpecifiers, routeQueryRules, testReliabilityPluginSpecifiers, testReliabilityRules };
49
58
 
50
59
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { OxlintConfig } from \"oxlint\";\n\ntype JsPlugins = NonNullable<OxlintConfig[\"jsPlugins\"]>;\ntype Rules = NonNullable<OxlintConfig[\"rules\"]>;\n\nexport const noAnyCastsPluginSpecifier = \"@stll/oxlint-plugin/no-any-casts\";\nexport const noDangerousTypeAssertionsPluginSpecifier =\n \"@stll/oxlint-plugin/no-dangerous-type-assertions\";\nexport const noPartialRecordSatisfiesPluginSpecifier =\n \"@stll/oxlint-plugin/no-partial-record-satisfies\";\nexport const noUnsafeInnerHtmlPluginSpecifier =\n \"@stll/oxlint-plugin/no-unsafe-inner-html\";\nexport const noRawRouteQueryClientPluginSpecifier =\n \"@stll/oxlint-plugin/no-raw-route-query-client\";\nexport const requireLoaderPrefetchPluginSpecifier =\n \"@stll/oxlint-plugin/require-loader-prefetch\";\n\nexport const portableSafetyPluginSpecifiers = [\n noAnyCastsPluginSpecifier,\n noDangerousTypeAssertionsPluginSpecifier,\n noPartialRecordSatisfiesPluginSpecifier,\n noUnsafeInnerHtmlPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const portableSafetyRules = {\n \"no-any-casts/no-any-casts\": \"error\",\n \"no-dangerous-type-assertions/no-dangerous-type-assertions\": \"error\",\n \"no-partial-record-satisfies/no-partial-record-satisfies\": \"error\",\n \"no-unsafe-inner-html/no-unsafe-inner-html\": \"error\",\n} satisfies Rules;\n\nexport const routeQueryPluginSpecifiers = [\n noRawRouteQueryClientPluginSpecifier,\n requireLoaderPrefetchPluginSpecifier,\n] satisfies JsPlugins;\n\nexport const routeQueryRules = {\n \"no-raw-route-query-client/no-raw-route-query-client\": \"error\",\n \"require-loader-prefetch/require-loader-prefetch\": \"error\",\n} satisfies Rules;\n\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"}
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\";\nexport const bunTestHygienePluginSpecifier =\n \"@stll/oxlint-plugin/bun-test-hygiene\";\n\nexport const bunTestHygienePluginSpecifiers = [\n bunTestHygienePluginSpecifier,\n] satisfies JsPlugins;\n\nexport const bunTestHygieneRules = {\n \"bun-test-hygiene/no-disabled-tests\": \"error\",\n \"bun-test-hygiene/no-focused-tests\": \"error\",\n \"bun-test-hygiene/no-forced-database-drop\": \"error\",\n \"bun-test-hygiene/no-identical-title\": \"error\",\n \"bun-test-hygiene/no-promise-matchers\": \"error\",\n} satisfies Rules;\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;AACF,MAAa,gCACX;AAEF,MAAa,iCAAiC,CAC5C,6BACF;AAEA,MAAa,sBAAsB;CACjC,sCAAsC;CACtC,qCAAqC;CACrC,4CAA4C;CAC5C,uCAAuC;CACvC,wCAAwC;AAC1C;AAEA,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"}
@@ -91,6 +91,11 @@ const DOM_ELEMENT_PRODUCERS = [
91
91
  kind: "collection",
92
92
  receiver: "either"
93
93
  },
94
+ {
95
+ name: "children",
96
+ kind: "collection-property",
97
+ receiver: "element"
98
+ },
94
99
  {
95
100
  name: "parentElement",
96
101
  kind: "property",
@@ -152,11 +157,35 @@ const HTML_PROPERTIES = /* @__PURE__ */ new Set([
152
157
  ]);
153
158
  const HTML_FIRST_ARGUMENT_METHODS = /* @__PURE__ */ new Set(["createContextualFragment", "setHTMLUnsafe"]);
154
159
  const DOCUMENT_WRITE_METHODS = /* @__PURE__ */ new Set(["write", "writeln"]);
160
+ const HTML_ATTRIBUTE_SINKS = [{
161
+ name: "setAttribute",
162
+ namespaceIndex: null,
163
+ nameIndex: 0,
164
+ valueIndex: 1
165
+ }, {
166
+ name: "setAttributeNS",
167
+ namespaceIndex: 0,
168
+ nameIndex: 1,
169
+ valueIndex: 2
170
+ }];
155
171
  const DOCUMENT_MEMBERS = /* @__PURE__ */ new Set([
156
172
  "contentDocument",
157
173
  "document",
158
174
  "ownerDocument"
159
175
  ]);
176
+ const FILE_SYSTEM_MODULES = /* @__PURE__ */ new Set([
177
+ "fs",
178
+ "fs/promises",
179
+ "node:fs",
180
+ "node:fs/promises"
181
+ ]);
182
+ const CHILD_PROCESS_MODULES = /* @__PURE__ */ new Set(["child_process", "node:child_process"]);
183
+ const CHILD_PROCESS_FACTORIES = /* @__PURE__ */ new Set([
184
+ "exec",
185
+ "execFile",
186
+ "fork",
187
+ "spawn"
188
+ ]);
160
189
  const HTML_SANITIZER_LEAF_RE = /^(?:escape(?:html|xml)|sanitize(?:html|dom)|purify(?:html|dom)|dompurify)$/iu;
161
190
  const SANITIZER_RESULT_MEMBERS = /* @__PURE__ */ new Set([
162
191
  "value",
@@ -223,7 +252,15 @@ const isOpaqueProperty = (property) => property.type === "SpreadElement" || prop
223
252
  const objectProperties = (node) => {
224
253
  const expression = require_utils.unwrapExpression(node);
225
254
  if (expression?.type !== "ObjectExpression" || !Array.isArray(expression.properties)) return [];
226
- return expression.properties.filter(require_utils.isAstNode);
255
+ const properties = [];
256
+ for (const property of expression.properties.filter(require_utils.isAstNode)) {
257
+ if (property.type === "SpreadElement" && require_utils.unwrapExpression(property.argument)?.type === "ObjectExpression") {
258
+ properties.push(...objectProperties(property.argument));
259
+ continue;
260
+ }
261
+ properties.push(property);
262
+ }
263
+ return properties;
227
264
  };
228
265
  const appliedArgumentsFor = (args) => {
229
266
  const applied = require_utils.unwrapExpression(args.at(1));
@@ -264,13 +301,6 @@ const invokedArguments = (call) => {
264
301
  }
265
302
  return appliedArgumentsFor(resolved.args);
266
303
  };
267
- const isDocumentReceiver = (node) => {
268
- const receiver = require_utils.unwrapExpression(node);
269
- if (require_utils.isIdentifier(receiver, "document")) return true;
270
- if (receiver?.type !== "MemberExpression") return false;
271
- const name = require_utils.memberPropertyName(receiver);
272
- return name !== null && DOCUMENT_MEMBERS.has(name);
273
- };
274
304
  var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
275
305
  meta: { name: "no-unsafe-inner-html" },
276
306
  rules: { "no-unsafe-inner-html": {
@@ -286,35 +316,155 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
286
316
  }],
287
317
  messages: {
288
318
  unsafeInnerHtml: "Dynamic HTML injected into the DOM must be static, proven by the configured sanitizer policy, or have an adjacent `// safe-html: <provenance>` comment naming the trusted source. Function names outside the allowlist are not proof because a local identity function can shadow them.",
289
- unsafeInnerHtmlSpread: "Do not spread an object into dangerouslySetInnerHTML. Keep `__html` inline so this rule can prove the HTML value is sanitized or escaped."
319
+ unsafeInnerHtmlSpread: "Do not spread an object in dangerouslySetInnerHTML. Keep `__html` inline so this rule can prove the HTML value is sanitized or escaped.",
320
+ unsafeInnerHtmlComputedKey: "Do not use an unresolved computed key in dangerouslySetInnerHTML. Write an explicit `__html` property so this rule can prove the HTML value is sanitized or escaped."
290
321
  }
291
322
  },
292
323
  createOnce(context) {
293
- const isKnownDomTarget = ({ node, scope, seen = /* @__PURE__ */ new Set(), kind = "element" }) => {
294
- const target = require_utils.unwrapExpression(node);
295
- if (require_utils.isIdentifier(target)) {
324
+ const resolveStableBinding = ({ node, scope, seen }) => {
325
+ let target = require_utils.unwrapExpression(node);
326
+ while (require_utils.isIdentifier(target)) {
296
327
  let current = scope;
328
+ let variable;
297
329
  while (current !== null) {
298
- const variable = current.set.get(target.name);
299
- if (variable === void 0) {
300
- current = current.upper;
301
- continue;
302
- }
303
- if (variable.defs.length === 0) return kind === "element" && isDocumentReceiver(target);
304
- if (seen.has(variable)) return false;
305
- seen.add(variable);
306
- const definition = variable.defs.at(0);
307
- if (variable.defs.length !== 1 || definition?.type !== "Variable" || definition.node.type !== "VariableDeclarator" || definition.node.id.type !== "Identifier" || definition.node.init === null || variable.references.some((reference) => reference.isWrite() && !reference.init)) return false;
308
- return isKnownDomTarget({
309
- node: definition.node.init,
310
- scope: context.sourceCode.getScope(definition.node),
311
- seen,
312
- kind
313
- });
330
+ variable = current.set.get(target.name);
331
+ if (variable !== void 0) break;
332
+ current = current.upper;
333
+ }
334
+ if (variable === void 0 || variable.defs.length === 0) return {
335
+ kind: "expression",
336
+ node: target,
337
+ scope
338
+ };
339
+ if (seen.has(variable)) return null;
340
+ seen.add(variable);
341
+ const definition = variable.defs.at(0);
342
+ if (variable.defs.length === 1 && (definition?.type === "ClassName" || definition?.type === "FunctionName") && (definition.node.type === "ClassDeclaration" || definition.node.type === "FunctionDeclaration") && !variable.references.some((reference) => reference.isWrite())) return {
343
+ kind: "expression",
344
+ node: require_utils.unwrapExpression(definition.node),
345
+ scope
346
+ };
347
+ if (variable.defs.length !== 1 || definition?.type !== "Variable" || definition.node.type !== "VariableDeclarator" || definition.node.id.type !== "Identifier" || variable.references.some((reference) => reference.isWrite() && !reference.init)) return null;
348
+ const declarator = definition.node;
349
+ if (declarator.init !== null) {
350
+ target = require_utils.unwrapExpression(declarator.init);
351
+ scope = context.sourceCode.getScope(declarator);
352
+ continue;
314
353
  }
354
+ const declaration = declarator.parent;
355
+ if (declaration.type !== "VariableDeclaration") return null;
356
+ const loop = declaration.parent;
357
+ if (loop.type !== "ForOfStatement" || loop.left !== declaration) return null;
358
+ return {
359
+ kind: "iteration",
360
+ node: require_utils.unwrapExpression(loop.right),
361
+ scope: context.sourceCode.getScope(loop.right)
362
+ };
315
363
  }
364
+ return {
365
+ kind: "expression",
366
+ node: target,
367
+ scope
368
+ };
369
+ };
370
+ const isKnownNonEmptyNamespace = (namespace, scope) => {
371
+ const source = resolveStableBinding({
372
+ node: namespace,
373
+ scope,
374
+ seen: /* @__PURE__ */ new Set()
375
+ });
376
+ if (source?.kind !== "expression") return false;
377
+ const text = require_utils.staticStringValue(source.node);
378
+ if (text !== null) return text !== "";
379
+ if (source.node?.type !== "Literal") return false;
380
+ const { value } = source.node;
381
+ return typeof value === "number" || typeof value === "boolean" || typeof value === "bigint";
382
+ };
383
+ const variableFor = (identifier, initialScope) => {
384
+ let current = initialScope;
385
+ while (current !== null) {
386
+ const variable = current.set.get(identifier.name);
387
+ if (variable !== void 0) return variable;
388
+ current = current.upper;
389
+ }
390
+ };
391
+ const isKnownNonDocumentReceiver = (node, scope) => {
392
+ const isUnshadowedGlobal = (identifier, name) => require_utils.isIdentifier(identifier, name) && variableFor(identifier, scope) === void 0;
393
+ const isFileSystemImport = (identifier) => {
394
+ if (!require_utils.isIdentifier(identifier)) return false;
395
+ const variable = variableFor(identifier, scope);
396
+ if (variable?.defs.length !== 1) return false;
397
+ const definition = variable.defs.at(0);
398
+ return definition?.type === "ImportBinding" && definition.parent?.type === "ImportDeclaration" && FILE_SYSTEM_MODULES.has(definition.parent.source.value);
399
+ };
400
+ const isChildProcess = (identifier) => {
401
+ if (!require_utils.isIdentifier(identifier)) return false;
402
+ const variable = variableFor(identifier, scope);
403
+ const definition = variable?.defs.at(0);
404
+ if (variable?.defs.length !== 1 || definition?.type !== "Variable" || definition.node.type !== "VariableDeclarator" || definition.node.id.type !== "Identifier" || definition.node.parent.type !== "VariableDeclaration" || definition.node.parent.kind !== "const") return false;
405
+ const init = require_utils.unwrapExpression(definition.node.init);
406
+ if (init?.type !== "CallExpression") return false;
407
+ const callee = require_utils.unwrapExpression(init.callee);
408
+ if (callee?.type === "MemberExpression") return require_utils.memberPropertyName(callee) === "spawn" && isUnshadowedGlobal(require_utils.unwrapExpression(callee.object), "Bun");
409
+ if (!require_utils.isIdentifier(callee)) return false;
410
+ const factory = variableFor(callee, scope);
411
+ const binding = factory?.defs.at(0);
412
+ return factory?.defs.length === 1 && binding?.type === "ImportBinding" && binding.node.type === "ImportSpecifier" && binding.node.imported.type === "Identifier" && CHILD_PROCESS_FACTORIES.has(binding.node.imported.name) && binding.parent?.type === "ImportDeclaration" && CHILD_PROCESS_MODULES.has(binding.parent.source.value);
413
+ };
414
+ const root = require_utils.unwrapExpression(node);
415
+ if (isUnshadowedGlobal(root, "Bun") || isFileSystemImport(root)) return true;
416
+ if (root?.type !== "MemberExpression") return false;
417
+ const stream = require_utils.memberPropertyName(root);
418
+ const owner = require_utils.unwrapExpression(root.object);
419
+ return (stream === "stdout" || stream === "stderr") && isUnshadowedGlobal(owner, "process") || stream === "stdin" && isChildProcess(owner);
420
+ };
421
+ const isDocumentReceiver = (node, scope) => {
422
+ if (isKnownNonDocumentReceiver(node, scope)) return false;
423
+ const source = resolveStableBinding({
424
+ node,
425
+ scope,
426
+ seen: /* @__PURE__ */ new Set()
427
+ });
428
+ if (source === null || source.kind !== "expression") return true;
429
+ const receiver = source.node;
430
+ if (require_utils.isIdentifier(receiver, "document")) return true;
431
+ if (receiver?.type !== "MemberExpression") return false;
432
+ const name = require_utils.memberPropertyName(receiver);
433
+ return name !== null && DOCUMENT_MEMBERS.has(name);
434
+ };
435
+ const isDocumentSinkReceiver = (node, scope) => {
436
+ if (isKnownNonDocumentReceiver(node, scope)) return false;
437
+ const receiver = require_utils.unwrapExpression(node);
438
+ if (!require_utils.isIdentifier(receiver)) return true;
439
+ let current = scope;
440
+ let variable;
441
+ while (current !== null) {
442
+ variable = current.set.get(receiver.name);
443
+ if (variable !== void 0) break;
444
+ current = current.upper;
445
+ }
446
+ if (variable === void 0 || variable.defs.length !== 1) return true;
447
+ const definition = variable.defs.at(0);
448
+ if (definition?.type !== "Variable" || definition.node.type !== "VariableDeclarator" || definition.node.id.type !== "Identifier" || definition.node.init === null || require_utils.unwrapExpression(definition.node.init)?.type !== "ObjectExpression" || definition.node.parent.type !== "VariableDeclaration" || definition.node.parent.kind !== "const" || variable.scope.variableScope !== scope.variableScope || variable.references.some((reference) => reference.isWrite() && !reference.init)) return true;
449
+ return false;
450
+ };
451
+ const isKnownDomTarget = ({ node, scope, seen = /* @__PURE__ */ new Set(), kind = "element" }) => {
452
+ const source = resolveStableBinding({
453
+ node,
454
+ scope,
455
+ seen
456
+ });
457
+ if (source === null) return true;
458
+ if (source.kind === "iteration") return kind === "element" && isKnownDomTarget({
459
+ node: source.node,
460
+ scope: source.scope,
461
+ seen,
462
+ kind: "collection"
463
+ });
464
+ const target = source.node;
465
+ scope = source.scope;
316
466
  const hasProducerReceiver = (receiver, producer) => {
317
- if (producer.receiver !== "element" && isDocumentReceiver(receiver)) return true;
467
+ if (producer.receiver !== "element" && isDocumentReceiver(receiver, scope)) return true;
318
468
  return producer.receiver !== "document" && isKnownDomTarget({
319
469
  node: receiver,
320
470
  scope,
@@ -327,12 +477,12 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
327
477
  seen,
328
478
  kind: "collection"
329
479
  });
330
- if (kind === "element" && target?.type === "MemberExpression") {
480
+ if (target?.type === "MemberExpression") {
331
481
  const property = require_utils.memberPropertyName(target);
332
- const producer = DOM_ELEMENT_PRODUCERS.find((entry) => entry.kind === "property" && entry.name === property);
482
+ const producer = DOM_ELEMENT_PRODUCERS.find((entry) => entry.kind === (kind === "collection" ? "collection-property" : "property") && entry.name === property);
333
483
  if (producer !== void 0) return hasProducerReceiver(target.object, producer);
334
484
  const index = require_utils.unwrapExpression(target.property);
335
- if (target.computed === true && index?.type === "Literal" && typeof index.value === "number" && Number.isInteger(index.value) && index.value >= 0) return isElementCollection(target.object);
485
+ if (kind === "element" && target.computed === true && index?.type === "Literal" && typeof index.value === "number" && Number.isInteger(index.value) && index.value >= 0) return isElementCollection(target.object);
336
486
  }
337
487
  if (target?.type === "CallExpression") {
338
488
  const callee = require_utils.invokedCallee(target);
@@ -342,7 +492,7 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
342
492
  const producer = DOM_ELEMENT_PRODUCERS.find((entry) => entry.kind === (kind === "collection" ? "collection" : "method") && entry.name === method);
343
493
  return producer !== void 0 && hasProducerReceiver(callee.object, producer);
344
494
  }
345
- return kind === "element" && isDocumentReceiver(target);
495
+ return kind === "element" && isDocumentReceiver(target, scope);
346
496
  };
347
497
  let sanitizerPolicy = SANITIZER_POLICY.allowlist;
348
498
  const waiverLines = /* @__PURE__ */ new Set();
@@ -353,6 +503,11 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
353
503
  kind
354
504
  });
355
505
  };
506
+ const messageIdFor = (kind) => {
507
+ if (kind === "opaque-payload-spread") return "unsafeInnerHtmlSpread";
508
+ if (kind === "opaque-payload-computed-key") return "unsafeInnerHtmlComputedKey";
509
+ return "unsafeInnerHtml";
510
+ };
356
511
  const consumeWaiver = (node) => {
357
512
  const line = startLine(node) - 1;
358
513
  if (!waiverLines.has(line)) return false;
@@ -370,7 +525,7 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
370
525
  }
371
526
  for (const property of objectProperties(expression)) {
372
527
  if (isOpaqueProperty(property)) {
373
- observeSink(property, "opaque-payload");
528
+ observeSink(property, property.type === "SpreadElement" ? "opaque-payload-spread" : "opaque-payload-computed-key");
374
529
  continue;
375
530
  }
376
531
  if (property.type === "Property" && objectPropertyKey(property) === htmlKey) reportIfUnsafe(property.value);
@@ -387,6 +542,14 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
387
542
  default: throw new Error(`Unknown React HTML prop: ${String(prop)}`);
388
543
  }
389
544
  };
545
+ const checkReactProps = (value) => {
546
+ for (const property of objectProperties(value)) {
547
+ if (property.type !== "Property") continue;
548
+ const key = objectPropertyKey(property);
549
+ const prop = REACT_HTML_PROPS.find((entry) => entry.name === key);
550
+ if (prop?.kind === "value") checkReactHtmlProp(prop, property.value);
551
+ }
552
+ };
390
553
  const checkHtmlPropertySources = (sources, domTarget) => {
391
554
  for (const source of sources) {
392
555
  if (require_utils.unwrapExpression(source)?.type !== "ObjectExpression") {
@@ -416,7 +579,7 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
416
579
  if (kind === "safe" || kind === "unsafe" && waived) continue;
417
580
  context.report({
418
581
  node,
419
- messageId: kind === "opaque-payload" ? "unsafeInnerHtmlSpread" : "unsafeInnerHtml"
582
+ messageId: messageIdFor(kind)
420
583
  });
421
584
  }
422
585
  sinks.length = 0;
@@ -432,6 +595,9 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
432
595
  const value = require_utils.unwrapExpression(node.value);
433
596
  checkReactHtmlProp(prop, value?.type === "JSXExpressionContainer" ? value.expression : value);
434
597
  },
598
+ JSXSpreadAttribute(node) {
599
+ checkReactProps(node.argument);
600
+ },
435
601
  Property(node) {
436
602
  const key = objectPropertyKey(node);
437
603
  const prop = REACT_HTML_PROPS.find((entry) => entry.name === key);
@@ -451,12 +617,7 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
451
617
  const invoked = invokedArguments(node);
452
618
  const factory = staticCalleeName(callee);
453
619
  if (factory !== null && REACT_ELEMENT_FACTORIES.has(factory)) {
454
- for (const property of objectProperties(invoked.args.at(1))) {
455
- if (property.type !== "Property") continue;
456
- const key = objectPropertyKey(property);
457
- const prop = REACT_HTML_PROPS.find((entry) => entry.name === key);
458
- if (prop?.kind === "value") checkReactHtmlProp(prop, property.value);
459
- }
620
+ checkReactProps(invoked.args.at(1));
460
621
  return;
461
622
  }
462
623
  if (callee?.type !== "MemberExpression") return;
@@ -478,15 +639,17 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
478
639
  reportIfUnsafe(invoked.args.at(1));
479
640
  return;
480
641
  }
481
- if (method === "setAttribute") {
642
+ const attributeSink = HTML_ATTRIBUTE_SINKS.find((entry) => entry.name === method);
643
+ if (attributeSink !== void 0) {
644
+ if (attributeSink.namespaceIndex !== null && isKnownNonEmptyNamespace(invoked.args.at(attributeSink.namespaceIndex), context.sourceCode.getScope(node))) return;
482
645
  if (invoked.status === "opaque") {
483
646
  reportIfUnsafe(node);
484
647
  return;
485
648
  }
486
- if (require_utils.staticStringValue(invoked.args.at(0))?.toLowerCase() === "srcdoc") reportIfUnsafe(invoked.args.at(1));
649
+ if (require_utils.staticStringValue(invoked.args.at(attributeSink.nameIndex))?.toLowerCase() === "srcdoc") reportIfUnsafe(invoked.args.at(attributeSink.valueIndex));
487
650
  return;
488
651
  }
489
- if (DOCUMENT_WRITE_METHODS.has(method) && isDocumentReceiver(callee.object)) {
652
+ if (DOCUMENT_WRITE_METHODS.has(method) && isDocumentSinkReceiver(callee.object, context.sourceCode.getScope(node))) {
490
653
  if (invoked.status === "opaque") {
491
654
  reportIfUnsafe(node);
492
655
  return;
@@ -508,7 +671,10 @@ var no_unsafe_inner_html_default = (0, _oxlint_plugins.eslintCompatPlugin)({
508
671
  } }
509
672
  });
510
673
  //#endregion
674
+ exports.DOCUMENT_WRITE_METHODS = DOCUMENT_WRITE_METHODS;
511
675
  exports.DOM_ELEMENT_PRODUCERS = DOM_ELEMENT_PRODUCERS;
676
+ exports.HTML_ATTRIBUTE_SINKS = HTML_ATTRIBUTE_SINKS;
677
+ exports.REACT_ELEMENT_FACTORIES = REACT_ELEMENT_FACTORIES;
512
678
  exports.REACT_HTML_PROPS = REACT_HTML_PROPS;
513
679
  exports.default = no_unsafe_inner_html_default;
514
680