@fedify/lint 2.4.0-dev.2212 → 2.4.0-dev.2219

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/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/lint",
3
- "version": "2.4.0-dev.2212+26ce9858",
3
+ "version": "2.4.0-dev.2219+eb70f060",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts",
package/dist/index.cjs CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  enumerable: true
25
25
  }) : target, mod));
26
26
  //#endregion
27
- const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-zBDRIPHn.cjs");
27
+ const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-BfqE8XpF.cjs");
28
28
  let _fxts_core = require("@fxts/core");
29
29
  let _typescript_eslint_parser = require("@typescript-eslint/parser");
30
30
  _typescript_eslint_parser = __toESM(_typescript_eslint_parser, 1);
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-C-3lDna2.js";
1
+ import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-BuLF_Eq0.js";
2
2
  import { fromEntries, keys, map, pipe } from "@fxts/core";
3
3
  import parser from "@typescript-eslint/parser";
4
4
  //#region src/index.ts
@@ -1,7 +1,7 @@
1
1
  let _fxts_core = require("@fxts/core");
2
2
  //#region deno.json
3
3
  var name = "@fedify/lint";
4
- var version = "2.4.0-dev.2212+26ce9858";
4
+ var version = "2.4.0-dev.2219+eb70f060";
5
5
  //#endregion
6
6
  //#region src/lib/const.ts
7
7
  /**
@@ -1740,7 +1740,7 @@ function collectCalls(node, out) {
1740
1740
  if (n.type === "CallExpression") out.push(n);
1741
1741
  }, false);
1742
1742
  }
1743
- function enclosingFunction(node) {
1743
+ function enclosingFunction$1(node) {
1744
1744
  for (let current = parentOf(node); current != null;) {
1745
1745
  if (isFunctionLikeNode(current)) return current;
1746
1746
  current = parentOf(current);
@@ -1846,9 +1846,9 @@ function fateOf(expression, analysis, shape = "promise") {
1846
1846
  switch (parent.type) {
1847
1847
  case "AwaitExpression": return currentShape === "promise" ? {
1848
1848
  fate: "awaited",
1849
- owner: enclosingFunction(parent)
1849
+ owner: enclosingFunction$1(parent)
1850
1850
  } : DROPPED;
1851
- case "ReturnStatement": return currentShape === "promise" ? returnedOutcome(enclosingFunction(parent), analysis) : returnedContainerOutcome(enclosingFunction(parent), analysis);
1851
+ case "ReturnStatement": return currentShape === "promise" ? returnedOutcome(enclosingFunction$1(parent), analysis) : returnedContainerOutcome(enclosingFunction$1(parent), analysis);
1852
1852
  case "ArrowFunctionExpression":
1853
1853
  if (get(parent, "body") !== current) return HANDLED;
1854
1854
  return currentShape === "promise" ? returnedOutcome(parent, analysis) : returnedContainerOutcome(parent, analysis);
@@ -2121,6 +2121,36 @@ function createBindingIndex() {
2121
2121
  }
2122
2122
  };
2123
2123
  }
2124
+ const position = (node) => node.range?.[0] ?? node.start ?? -1;
2125
+ const enclosingFunction = (node) => {
2126
+ for (let parent = getParent(node); parent != null; parent = getParent(parent)) if (isFunctionLike(parent)) return parent;
2127
+ return null;
2128
+ };
2129
+ const isInside = (node, ancestor) => {
2130
+ for (let current = node; current != null; current = getParent(current)) if (current === ancestor) return true;
2131
+ return false;
2132
+ };
2133
+ const isDirectStatement = (node, fn) => {
2134
+ let current = node;
2135
+ while (getParent(current) !== fn.body) {
2136
+ const parent = getParent(current);
2137
+ if (parent == null || ![
2138
+ "AwaitExpression",
2139
+ "ExpressionStatement",
2140
+ "ReturnStatement",
2141
+ "VariableDeclarator",
2142
+ "VariableDeclaration",
2143
+ "BlockStatement"
2144
+ ].includes(parent.type)) return false;
2145
+ current = parent;
2146
+ }
2147
+ return [
2148
+ "ExpressionStatement",
2149
+ "ReturnStatement",
2150
+ "VariableDeclaration",
2151
+ "BlockStatement"
2152
+ ].includes(current.type);
2153
+ };
2124
2154
  const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
2125
2155
  if (expr.type !== "Identifier") return expr;
2126
2156
  const binding = bindings.lookup(expr, expr.name);
@@ -2290,7 +2320,7 @@ function buildContextExpressionPattern(contextName) {
2290
2320
  * later replacement's length change never shifts the still-unprocessed
2291
2321
  * offsets of an earlier one.
2292
2322
  */
2293
- function collectDeliveryScanCode(sourceCode, root, used, visited) {
2323
+ function collectDeliveryScanCode(sourceCode, root, used, ignored, visited) {
2294
2324
  if (visited.has(root)) return "";
2295
2325
  visited.add(root);
2296
2326
  const statements = [];
@@ -2304,7 +2334,7 @@ function collectDeliveryScanCode(sourceCode, root, used, visited) {
2304
2334
  let result = text;
2305
2335
  for (const fn of byDescendingStart) {
2306
2336
  const [fnStart, fnEnd] = getRange(fn);
2307
- const replacement = used.has(fn) ? collectDeliveryScanCode(sourceCode, fn.body, used, visited) : "";
2337
+ const replacement = used.has(fn) && !ignored.has(fn) ? collectDeliveryScanCode(sourceCode, fn.body, used, ignored, visited) : "";
2308
2338
  result = result.slice(0, fnStart - statementStart) + `\n${replacement.length > 0 ? replacement : "()=>{}"}\n` + result.slice(fnEnd - statementStart);
2309
2339
  }
2310
2340
  return result;
@@ -2314,12 +2344,46 @@ function unwrapArgument(node) {
2314
2344
  while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
2315
2345
  return node;
2316
2346
  }
2317
- function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
2347
+ function ignoredLocalSetupFunctions(listener, bindings, calls, assignments) {
2348
+ const ignored = [];
2349
+ const directCalls = calls.filter((call) => enclosingFunction(call) === listener && isDirectStatement(call, listener));
2350
+ for (const assignment of assignments) {
2351
+ if (assignment.type !== "AssignmentExpression" || assignment.operator !== "=" || assignment.left.type !== "MemberExpression" || !isFunction(assignment.right)) continue;
2352
+ const setup = enclosingFunction(assignment);
2353
+ if (setup == null || setup === listener || !isInside(setup, listener)) continue;
2354
+ const setupParent = getParent(setup);
2355
+ const namedLocalSetup = enclosingFunction(setup) === listener && (setup.type === "FunctionDeclaration" || setupParent?.type === "VariableDeclarator" && setupParent.init === setup && setupParent.id.type === "Identifier");
2356
+ const passedToCall = calls.some((call) => call.arguments.some((argument) => argument.type !== "SpreadElement" && resolveListenerReference(argument, bindings) === setup));
2357
+ if (!namedLocalSetup || passedToCall) continue;
2358
+ const installed = assignment.right;
2359
+ if (!isDirectStatement(assignment, setup) || assignment.left.object.type !== "Identifier") {
2360
+ ignored.push(installed);
2361
+ continue;
2362
+ }
2363
+ const property = getMemberPropertyName(assignment.left);
2364
+ if (property == null) continue;
2365
+ const setupCalls = directCalls.filter((call) => resolveListenerReference(call.callee, bindings) === setup);
2366
+ if (setupCalls.length === 0) {
2367
+ const called = calls.some((call) => resolveListenerReference(call.callee, bindings) === setup);
2368
+ ignored.push(called ? assignment.right : setup);
2369
+ continue;
2370
+ }
2371
+ const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
2372
+ const deliveryCalls = directCalls.filter((call) => {
2373
+ const callee = call.callee;
2374
+ return callee.type === "MemberExpression" && callee.object.type === "Identifier" && getMemberPropertyName(callee) === property && bindings.lookup(callee.object, callee.object.name) === objectBinding;
2375
+ });
2376
+ if (!(objectBinding != null && setupCalls.some((setupCall) => deliveryCalls.some((deliveryCall) => position(setupCall) >= 0 && position(setupCall) < position(deliveryCall))))) ignored.push(installed);
2377
+ }
2378
+ return ignored;
2379
+ }
2380
+ function functionCallsDelivery(sourceCode, listener, bindings, calls, assignments, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
2318
2381
  let indices = visited.get(listener);
2319
2382
  if (indices?.has(contextIndex)) return false;
2320
2383
  if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
2321
2384
  indices.add(contextIndex);
2322
- if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
2385
+ const ignored = ignoredLocalSetupFunctions(listener, bindings, calls, assignments);
2386
+ if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex, ignored, bindings, calls, assignments)) return true;
2323
2387
  const param = unwrapContextParam(listener.params[contextIndex]);
2324
2388
  if (param?.type !== "Identifier") return false;
2325
2389
  const contextBinding = bindings.lookup(listener, param.name);
@@ -2346,14 +2410,24 @@ function functionCallsDelivery(sourceCode, listener, bindings, calls, contextInd
2346
2410
  if (argument.type === "SpreadElement") break;
2347
2411
  const arg = unwrapArgument(argument);
2348
2412
  if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
2349
- if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
2413
+ if (functionCallsDelivery(sourceCode, helper, bindings, calls, assignments, index, visited)) return true;
2350
2414
  }
2351
2415
  }
2352
2416
  return false;
2353
2417
  }
2354
- const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
2418
+ const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex, ignored, bindings, calls, assignments) => {
2355
2419
  const used = computeUsedFunctions(listener.body);
2356
- const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, /* @__PURE__ */ new Set()));
2420
+ const ignoredSet = new Set(ignored);
2421
+ for (const assignment of assignments) {
2422
+ if (assignment.type !== "AssignmentExpression" || assignment.left.type !== "MemberExpression" || assignment.left.object.type !== "Identifier" || !isFunction(assignment.right) || !isInside(assignment, listener)) continue;
2423
+ const property = getMemberPropertyName(assignment.left);
2424
+ const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
2425
+ if (property == null || objectBinding == null || !calls.some((call) => enclosingFunction(call) === listener && call.callee.type === "MemberExpression" && call.callee.object.type === "Identifier" && getMemberPropertyName(call.callee) === property && bindings.lookup(call.callee.object, call.callee.object.name) === objectBinding)) continue;
2426
+ const installed = assignment.right;
2427
+ if (ignoredSet.has(installed)) continue;
2428
+ for (let fn = installed; fn != null && fn !== listener && !ignoredSet.has(fn); fn = enclosingFunction(fn)) used.add(fn);
2429
+ }
2430
+ const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, ignoredSet, /* @__PURE__ */ new Set()));
2357
2431
  const aliases = /* @__PURE__ */ new Set();
2358
2432
  const contextParam = unwrapContextParam(listener.params[contextIndex]);
2359
2433
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
@@ -2386,6 +2460,7 @@ function createRule(buildReport) {
2386
2460
  const federationTracker = trackFederationVariables();
2387
2461
  const bindings = createBindingIndex();
2388
2462
  const pendingCalls = [];
2463
+ const pendingAssignments = [];
2389
2464
  const sourceCode = context.sourceCode;
2390
2465
  const inspectCall = (node) => {
2391
2466
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
@@ -2393,7 +2468,7 @@ function createRule(buildReport) {
2393
2468
  const listener = node.arguments[1];
2394
2469
  const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
2395
2470
  if (resolvedListener == null) return;
2396
- if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
2471
+ if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls, pendingAssignments)) return;
2397
2472
  context.report({
2398
2473
  node: resolvedListener,
2399
2474
  ...buildReport
@@ -2421,6 +2496,9 @@ function createRule(buildReport) {
2421
2496
  CallExpression(node) {
2422
2497
  pendingCalls.push(node);
2423
2498
  },
2499
+ AssignmentExpression(node) {
2500
+ pendingAssignments.push(node);
2501
+ },
2424
2502
  "Program:exit"() {
2425
2503
  for (const node of pendingCalls) inspectCall(node);
2426
2504
  }
@@ -1,7 +1,7 @@
1
1
  import { always, every, head, isEmpty, isObject, negate, pipe, pipeLazy, prop, some, toArray, unless, when } from "@fxts/core";
2
2
  //#region deno.json
3
3
  var name = "@fedify/lint";
4
- var version = "2.4.0-dev.2212+26ce9858";
4
+ var version = "2.4.0-dev.2219+eb70f060";
5
5
  //#endregion
6
6
  //#region src/lib/const.ts
7
7
  /**
@@ -1740,7 +1740,7 @@ function collectCalls(node, out) {
1740
1740
  if (n.type === "CallExpression") out.push(n);
1741
1741
  }, false);
1742
1742
  }
1743
- function enclosingFunction(node) {
1743
+ function enclosingFunction$1(node) {
1744
1744
  for (let current = parentOf(node); current != null;) {
1745
1745
  if (isFunctionLikeNode(current)) return current;
1746
1746
  current = parentOf(current);
@@ -1846,9 +1846,9 @@ function fateOf(expression, analysis, shape = "promise") {
1846
1846
  switch (parent.type) {
1847
1847
  case "AwaitExpression": return currentShape === "promise" ? {
1848
1848
  fate: "awaited",
1849
- owner: enclosingFunction(parent)
1849
+ owner: enclosingFunction$1(parent)
1850
1850
  } : DROPPED;
1851
- case "ReturnStatement": return currentShape === "promise" ? returnedOutcome(enclosingFunction(parent), analysis) : returnedContainerOutcome(enclosingFunction(parent), analysis);
1851
+ case "ReturnStatement": return currentShape === "promise" ? returnedOutcome(enclosingFunction$1(parent), analysis) : returnedContainerOutcome(enclosingFunction$1(parent), analysis);
1852
1852
  case "ArrowFunctionExpression":
1853
1853
  if (get(parent, "body") !== current) return HANDLED;
1854
1854
  return currentShape === "promise" ? returnedOutcome(parent, analysis) : returnedContainerOutcome(parent, analysis);
@@ -2121,6 +2121,36 @@ function createBindingIndex() {
2121
2121
  }
2122
2122
  };
2123
2123
  }
2124
+ const position = (node) => node.range?.[0] ?? node.start ?? -1;
2125
+ const enclosingFunction = (node) => {
2126
+ for (let parent = getParent(node); parent != null; parent = getParent(parent)) if (isFunctionLike(parent)) return parent;
2127
+ return null;
2128
+ };
2129
+ const isInside = (node, ancestor) => {
2130
+ for (let current = node; current != null; current = getParent(current)) if (current === ancestor) return true;
2131
+ return false;
2132
+ };
2133
+ const isDirectStatement = (node, fn) => {
2134
+ let current = node;
2135
+ while (getParent(current) !== fn.body) {
2136
+ const parent = getParent(current);
2137
+ if (parent == null || ![
2138
+ "AwaitExpression",
2139
+ "ExpressionStatement",
2140
+ "ReturnStatement",
2141
+ "VariableDeclarator",
2142
+ "VariableDeclaration",
2143
+ "BlockStatement"
2144
+ ].includes(parent.type)) return false;
2145
+ current = parent;
2146
+ }
2147
+ return [
2148
+ "ExpressionStatement",
2149
+ "ReturnStatement",
2150
+ "VariableDeclaration",
2151
+ "BlockStatement"
2152
+ ].includes(current.type);
2153
+ };
2124
2154
  const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
2125
2155
  if (expr.type !== "Identifier") return expr;
2126
2156
  const binding = bindings.lookup(expr, expr.name);
@@ -2290,7 +2320,7 @@ function buildContextExpressionPattern(contextName) {
2290
2320
  * later replacement's length change never shifts the still-unprocessed
2291
2321
  * offsets of an earlier one.
2292
2322
  */
2293
- function collectDeliveryScanCode(sourceCode, root, used, visited) {
2323
+ function collectDeliveryScanCode(sourceCode, root, used, ignored, visited) {
2294
2324
  if (visited.has(root)) return "";
2295
2325
  visited.add(root);
2296
2326
  const statements = [];
@@ -2304,7 +2334,7 @@ function collectDeliveryScanCode(sourceCode, root, used, visited) {
2304
2334
  let result = text;
2305
2335
  for (const fn of byDescendingStart) {
2306
2336
  const [fnStart, fnEnd] = getRange(fn);
2307
- const replacement = used.has(fn) ? collectDeliveryScanCode(sourceCode, fn.body, used, visited) : "";
2337
+ const replacement = used.has(fn) && !ignored.has(fn) ? collectDeliveryScanCode(sourceCode, fn.body, used, ignored, visited) : "";
2308
2338
  result = result.slice(0, fnStart - statementStart) + `\n${replacement.length > 0 ? replacement : "()=>{}"}\n` + result.slice(fnEnd - statementStart);
2309
2339
  }
2310
2340
  return result;
@@ -2314,12 +2344,46 @@ function unwrapArgument(node) {
2314
2344
  while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
2315
2345
  return node;
2316
2346
  }
2317
- function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
2347
+ function ignoredLocalSetupFunctions(listener, bindings, calls, assignments) {
2348
+ const ignored = [];
2349
+ const directCalls = calls.filter((call) => enclosingFunction(call) === listener && isDirectStatement(call, listener));
2350
+ for (const assignment of assignments) {
2351
+ if (assignment.type !== "AssignmentExpression" || assignment.operator !== "=" || assignment.left.type !== "MemberExpression" || !isFunction(assignment.right)) continue;
2352
+ const setup = enclosingFunction(assignment);
2353
+ if (setup == null || setup === listener || !isInside(setup, listener)) continue;
2354
+ const setupParent = getParent(setup);
2355
+ const namedLocalSetup = enclosingFunction(setup) === listener && (setup.type === "FunctionDeclaration" || setupParent?.type === "VariableDeclarator" && setupParent.init === setup && setupParent.id.type === "Identifier");
2356
+ const passedToCall = calls.some((call) => call.arguments.some((argument) => argument.type !== "SpreadElement" && resolveListenerReference(argument, bindings) === setup));
2357
+ if (!namedLocalSetup || passedToCall) continue;
2358
+ const installed = assignment.right;
2359
+ if (!isDirectStatement(assignment, setup) || assignment.left.object.type !== "Identifier") {
2360
+ ignored.push(installed);
2361
+ continue;
2362
+ }
2363
+ const property = getMemberPropertyName(assignment.left);
2364
+ if (property == null) continue;
2365
+ const setupCalls = directCalls.filter((call) => resolveListenerReference(call.callee, bindings) === setup);
2366
+ if (setupCalls.length === 0) {
2367
+ const called = calls.some((call) => resolveListenerReference(call.callee, bindings) === setup);
2368
+ ignored.push(called ? assignment.right : setup);
2369
+ continue;
2370
+ }
2371
+ const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
2372
+ const deliveryCalls = directCalls.filter((call) => {
2373
+ const callee = call.callee;
2374
+ return callee.type === "MemberExpression" && callee.object.type === "Identifier" && getMemberPropertyName(callee) === property && bindings.lookup(callee.object, callee.object.name) === objectBinding;
2375
+ });
2376
+ if (!(objectBinding != null && setupCalls.some((setupCall) => deliveryCalls.some((deliveryCall) => position(setupCall) >= 0 && position(setupCall) < position(deliveryCall))))) ignored.push(installed);
2377
+ }
2378
+ return ignored;
2379
+ }
2380
+ function functionCallsDelivery(sourceCode, listener, bindings, calls, assignments, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
2318
2381
  let indices = visited.get(listener);
2319
2382
  if (indices?.has(contextIndex)) return false;
2320
2383
  if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
2321
2384
  indices.add(contextIndex);
2322
- if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
2385
+ const ignored = ignoredLocalSetupFunctions(listener, bindings, calls, assignments);
2386
+ if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex, ignored, bindings, calls, assignments)) return true;
2323
2387
  const param = unwrapContextParam(listener.params[contextIndex]);
2324
2388
  if (param?.type !== "Identifier") return false;
2325
2389
  const contextBinding = bindings.lookup(listener, param.name);
@@ -2346,14 +2410,24 @@ function functionCallsDelivery(sourceCode, listener, bindings, calls, contextInd
2346
2410
  if (argument.type === "SpreadElement") break;
2347
2411
  const arg = unwrapArgument(argument);
2348
2412
  if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
2349
- if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
2413
+ if (functionCallsDelivery(sourceCode, helper, bindings, calls, assignments, index, visited)) return true;
2350
2414
  }
2351
2415
  }
2352
2416
  return false;
2353
2417
  }
2354
- const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
2418
+ const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex, ignored, bindings, calls, assignments) => {
2355
2419
  const used = computeUsedFunctions(listener.body);
2356
- const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, /* @__PURE__ */ new Set()));
2420
+ const ignoredSet = new Set(ignored);
2421
+ for (const assignment of assignments) {
2422
+ if (assignment.type !== "AssignmentExpression" || assignment.left.type !== "MemberExpression" || assignment.left.object.type !== "Identifier" || !isFunction(assignment.right) || !isInside(assignment, listener)) continue;
2423
+ const property = getMemberPropertyName(assignment.left);
2424
+ const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
2425
+ if (property == null || objectBinding == null || !calls.some((call) => enclosingFunction(call) === listener && call.callee.type === "MemberExpression" && call.callee.object.type === "Identifier" && getMemberPropertyName(call.callee) === property && bindings.lookup(call.callee.object, call.callee.object.name) === objectBinding)) continue;
2426
+ const installed = assignment.right;
2427
+ if (ignoredSet.has(installed)) continue;
2428
+ for (let fn = installed; fn != null && fn !== listener && !ignoredSet.has(fn); fn = enclosingFunction(fn)) used.add(fn);
2429
+ }
2430
+ const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, ignoredSet, /* @__PURE__ */ new Set()));
2357
2431
  const aliases = /* @__PURE__ */ new Set();
2358
2432
  const contextParam = unwrapContextParam(listener.params[contextIndex]);
2359
2433
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
@@ -2386,6 +2460,7 @@ function createRule(buildReport) {
2386
2460
  const federationTracker = trackFederationVariables();
2387
2461
  const bindings = createBindingIndex();
2388
2462
  const pendingCalls = [];
2463
+ const pendingAssignments = [];
2389
2464
  const sourceCode = context.sourceCode;
2390
2465
  const inspectCall = (node) => {
2391
2466
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
@@ -2393,7 +2468,7 @@ function createRule(buildReport) {
2393
2468
  const listener = node.arguments[1];
2394
2469
  const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
2395
2470
  if (resolvedListener == null) return;
2396
- if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
2471
+ if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls, pendingAssignments)) return;
2397
2472
  context.report({
2398
2473
  node: resolvedListener,
2399
2474
  ...buildReport
@@ -2421,6 +2496,9 @@ function createRule(buildReport) {
2421
2496
  CallExpression(node) {
2422
2497
  pendingCalls.push(node);
2423
2498
  },
2499
+ AssignmentExpression(node) {
2500
+ pendingAssignments.push(node);
2501
+ },
2424
2502
  "Program:exit"() {
2425
2503
  for (const node of pendingCalls) inspectCall(node);
2426
2504
  }
package/dist/oxlint.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-zBDRIPHn.cjs");
1
+ const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-BfqE8XpF.cjs");
2
2
  //#region src/oxlint.ts
3
3
  const rules = {
4
4
  [require_outbox_listener_delivery_required.RULE_IDS.actorIdMismatch]: require_outbox_listener_delivery_required.eslint$18,
package/dist/oxlint.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-C-3lDna2.js";
1
+ import { A as name, C as eslint$5, D as eslint$11, E as eslint$10, O as eslint$21, S as eslint$4, T as eslint$13, _ as eslint$15, a as eslint$23, b as eslint$2, c as eslint$16, d as eslint$22, f as eslint$6, g as eslint$14, h as eslint$9, i as eslint$27, j as version, k as RULE_IDS, l as eslint$17, m as eslint$8, n as eslint$25, o as eslint$18, p as eslint$7, r as eslint$26, s as eslint$19, t as eslint$24, u as eslint$20, v as eslint$1, w as eslint$12, x as eslint$3, y as eslint } from "./outbox-listener-delivery-required-BuLF_Eq0.js";
2
2
  //#region src/oxlint.ts
3
3
  const rules = {
4
4
  [RULE_IDS.actorIdMismatch]: eslint,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/lint",
3
- "version": "2.4.0-dev.2212+26ce9858",
3
+ "version": "2.4.0-dev.2219+eb70f060",
4
4
  "description": "Fedify linting rules and plugins",
5
5
  "keywords": [
6
6
  "Fedify",
@@ -51,7 +51,7 @@
51
51
  ],
52
52
  "peerDependencies": {
53
53
  "eslint": ">=9.0.0",
54
- "@fedify/fedify": "^2.4.0-dev.2212+26ce9858"
54
+ "@fedify/fedify": "^2.4.0-dev.2219+eb70f060"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "eslint": {
@@ -71,7 +71,7 @@
71
71
  "tsdown": "^0.22.0",
72
72
  "typescript": "^6.0.0",
73
73
  "@fedify/fixture": "2.0.0",
74
- "@fedify/vocab": "2.4.0-dev.2212+26ce9858"
74
+ "@fedify/vocab": "2.4.0-dev.2219+eb70f060"
75
75
  },
76
76
  "scripts": {
77
77
  "build:self": "tsdown",