@fedify/lint 2.3.9-dev.2048 → 2.3.10
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 +2 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{outbox-listener-delivery-required-CxugrN84.js → outbox-listener-delivery-required-BNgI_uik.js} +220 -29
- package/dist/{outbox-listener-delivery-required-C1Ho3dCQ.cjs → outbox-listener-delivery-required-CwToC306.cjs} +220 -29
- package/dist/oxlint.cjs +1 -1
- package/dist/oxlint.js +1 -1
- package/package.json +2 -2
package/deno.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/lint",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/mod.ts",
|
|
7
7
|
"./oxlint": "./src/oxlint.ts"
|
|
8
8
|
},
|
|
9
9
|
"imports": {
|
|
10
|
+
"@fedify/fixture": "jsr:@fedify/fixture@^2.2.14",
|
|
10
11
|
"eslint": "npm:eslint@^9.0.0",
|
|
11
12
|
"estree": "npm:@types/estree@^1.0.8",
|
|
12
13
|
"oxlint": "npm:oxlint@^1.66.0"
|
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-
|
|
27
|
+
const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-CwToC306.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 { C as RULE_IDS, S as eslint$19, T as version, _ as eslint$5, a as eslint$18, b as eslint$10, c as eslint$8, d as eslint$15, f as eslint$1, g as eslint$4, h as eslint$3, i as eslint$17, l as eslint$9, m as eslint$2, n as eslint$20, o as eslint$6, p as eslint, r as eslint$16, s as eslint$7, t as eslint$21, u as eslint$14, v as eslint$12, w as name, x as eslint$11, y as eslint$13 } from "./outbox-listener-delivery-required-
|
|
1
|
+
import { C as RULE_IDS, S as eslint$19, T as version, _ as eslint$5, a as eslint$18, b as eslint$10, c as eslint$8, d as eslint$15, f as eslint$1, g as eslint$4, h as eslint$3, i as eslint$17, l as eslint$9, m as eslint$2, n as eslint$20, o as eslint$6, p as eslint, r as eslint$16, s as eslint$7, t as eslint$21, u as eslint$14, v as eslint$12, w as name, x as eslint$11, y as eslint$13 } from "./outbox-listener-delivery-required-BNgI_uik.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
|
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.3.
|
|
4
|
+
var version = "2.3.10";
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/lib/const.ts
|
|
7
7
|
/**
|
|
@@ -595,7 +595,7 @@ const DELIVERY_METHOD_NAMES = new Set(["sendActivity", "forwardActivity"]);
|
|
|
595
595
|
const getMemberPropertyName = (expr) => {
|
|
596
596
|
if (expr.type !== "MemberExpression") return null;
|
|
597
597
|
const property = expr.property;
|
|
598
|
-
if (property.type === "Identifier") return property.name;
|
|
598
|
+
if (property.type === "Identifier" && !expr.computed) return property.name;
|
|
599
599
|
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
600
600
|
return null;
|
|
601
601
|
};
|
|
@@ -731,35 +731,211 @@ function buildContextExpressionPattern(contextName) {
|
|
|
731
731
|
const boundedName = String.raw`(?<![\w$])${name}(?![\w$])`;
|
|
732
732
|
return String.raw`(?:${boundedName}|\(\s*${boundedName}(?:\s+as\s+[^)]+)?\s*\))`;
|
|
733
733
|
}
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
734
|
+
const getParent = (node) => node.parent ?? null;
|
|
735
|
+
const isFunctionLike = (node) => node.type === "FunctionDeclaration" || isFunction(node);
|
|
736
|
+
function bindPattern(node, bindings) {
|
|
737
|
+
switch (node.type) {
|
|
738
|
+
case "Identifier":
|
|
739
|
+
bindings.set(node.name, { value: null });
|
|
740
|
+
break;
|
|
741
|
+
case "AssignmentPattern":
|
|
742
|
+
bindPattern(node.left, bindings);
|
|
743
|
+
break;
|
|
744
|
+
case "RestElement":
|
|
745
|
+
bindPattern(node.argument, bindings);
|
|
746
|
+
break;
|
|
747
|
+
case "ObjectPattern":
|
|
748
|
+
for (const property of node.properties) bindPattern(property.type === "Property" ? property.value : property, bindings);
|
|
749
|
+
break;
|
|
750
|
+
case "ArrayPattern":
|
|
751
|
+
for (const element of node.elements) if (element != null) bindPattern(element, bindings);
|
|
752
|
+
break;
|
|
744
753
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
754
|
+
}
|
|
755
|
+
function createBindingIndex() {
|
|
756
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
757
|
+
const scopeBindings = (scope) => {
|
|
758
|
+
let bindings = scopes.get(scope);
|
|
759
|
+
if (bindings == null) {
|
|
760
|
+
bindings = /* @__PURE__ */ new Map();
|
|
761
|
+
scopes.set(scope, bindings);
|
|
762
|
+
if (isFunctionLike(scope)) {
|
|
763
|
+
if (scope.type === "FunctionExpression" && scope.id != null) bindings.set(scope.id.name, { value: scope });
|
|
764
|
+
for (const param of scope.params) bindPattern(param, bindings);
|
|
765
|
+
}
|
|
766
|
+
if (scope.type === "ClassExpression" && scope.id != null) bindings.set(scope.id.name, { value: null });
|
|
767
|
+
if (scope.type === "CatchClause" && scope.param != null) bindPattern(scope.param, bindings);
|
|
768
|
+
}
|
|
769
|
+
return bindings;
|
|
770
|
+
};
|
|
771
|
+
const enclosingScope = (node, functionScope = false) => {
|
|
772
|
+
let scope = getParent(node);
|
|
773
|
+
while (scope.type !== "Program" && scope.type !== "StaticBlock" && scope.type !== "TSModuleBlock" && !isFunctionLike(scope) && (functionScope || ![
|
|
774
|
+
"BlockStatement",
|
|
775
|
+
"ForStatement",
|
|
776
|
+
"ForInStatement",
|
|
777
|
+
"ForOfStatement",
|
|
778
|
+
"SwitchStatement",
|
|
779
|
+
"CatchClause"
|
|
780
|
+
].includes(scope.type))) scope = getParent(scope);
|
|
781
|
+
return scope;
|
|
782
|
+
};
|
|
783
|
+
const lookup = (node, name) => {
|
|
784
|
+
for (let scope = node; scope != null; scope = getParent(scope)) {
|
|
785
|
+
const binding = scopeBindings(scope).get(name);
|
|
786
|
+
if (binding != null) return binding;
|
|
755
787
|
}
|
|
788
|
+
return null;
|
|
789
|
+
};
|
|
790
|
+
return {
|
|
791
|
+
lookup,
|
|
792
|
+
declare(node, pattern, value, hoisted = false) {
|
|
793
|
+
const bindings = scopeBindings(enclosingScope(node, hoisted));
|
|
794
|
+
if (pattern.type === "Identifier") {
|
|
795
|
+
if (hoisted && value == null && bindings.has(pattern.name)) return;
|
|
796
|
+
if (node.type === "FunctionDeclaration" && bindings.get(pattern.name)?.initializedVariable) return;
|
|
797
|
+
bindings.set(pattern.name, {
|
|
798
|
+
value,
|
|
799
|
+
initializedVariable: node.type === "VariableDeclarator" && value != null
|
|
800
|
+
});
|
|
801
|
+
} else bindPattern(pattern, bindings);
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
const position = (node) => node.range?.[0] ?? node.start ?? -1;
|
|
806
|
+
const endPosition = (node) => node.range?.[1] ?? node.end ?? -1;
|
|
807
|
+
const enclosingFunction = (node) => {
|
|
808
|
+
for (let parent = getParent(node); parent != null; parent = getParent(parent)) if (isFunctionLike(parent)) return parent;
|
|
809
|
+
return null;
|
|
810
|
+
};
|
|
811
|
+
const isInside = (node, ancestor) => {
|
|
812
|
+
for (let current = node; current != null; current = getParent(current)) if (current === ancestor) return true;
|
|
813
|
+
return false;
|
|
814
|
+
};
|
|
815
|
+
const isDirectStatement = (node, fn) => {
|
|
816
|
+
let current = node;
|
|
817
|
+
while (getParent(current) !== fn.body) {
|
|
818
|
+
const parent = getParent(current);
|
|
819
|
+
if (parent == null || ![
|
|
820
|
+
"AwaitExpression",
|
|
821
|
+
"ExpressionStatement",
|
|
822
|
+
"ReturnStatement",
|
|
823
|
+
"VariableDeclarator",
|
|
824
|
+
"VariableDeclaration",
|
|
825
|
+
"BlockStatement"
|
|
826
|
+
].includes(parent.type)) return false;
|
|
827
|
+
current = parent;
|
|
828
|
+
}
|
|
829
|
+
return [
|
|
830
|
+
"ExpressionStatement",
|
|
831
|
+
"ReturnStatement",
|
|
832
|
+
"VariableDeclaration",
|
|
833
|
+
"BlockStatement"
|
|
834
|
+
].includes(current.type);
|
|
835
|
+
};
|
|
836
|
+
const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
837
|
+
if (expr.type !== "Identifier") return expr;
|
|
838
|
+
const binding = bindings.lookup(expr, expr.name);
|
|
839
|
+
if (binding == null || binding.value == null || seen.has(binding)) return null;
|
|
840
|
+
seen.add(binding);
|
|
841
|
+
return resolveBindingValue(binding.value, bindings, seen);
|
|
842
|
+
};
|
|
843
|
+
const resolveListenerReference = (expr, bindings) => {
|
|
844
|
+
const target = resolveBindingValue(expr, bindings);
|
|
845
|
+
if (target == null) return null;
|
|
846
|
+
if (isFunctionLike(target)) return target;
|
|
847
|
+
if (target.type !== "MemberExpression") return null;
|
|
848
|
+
const object = resolveBindingValue(target.object, bindings);
|
|
849
|
+
if (object?.type !== "ObjectExpression") return null;
|
|
850
|
+
const propertyName = getMemberPropertyName(target);
|
|
851
|
+
if (propertyName == null) return null;
|
|
852
|
+
for (const prop of object.properties) {
|
|
853
|
+
if (prop.type !== "Property") continue;
|
|
854
|
+
if ((prop.key.type === "Identifier" && !prop.computed ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName) continue;
|
|
855
|
+
const value = resolveBindingValue(prop.value, bindings);
|
|
856
|
+
if (value != null && isFunctionLike(value)) return value;
|
|
756
857
|
}
|
|
757
858
|
return null;
|
|
758
859
|
};
|
|
759
|
-
|
|
760
|
-
|
|
860
|
+
function unwrapArgument(node) {
|
|
861
|
+
while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
|
|
862
|
+
return node;
|
|
863
|
+
}
|
|
864
|
+
function ignoredLocalSetupFunctions(listener, bindings, calls, assignments) {
|
|
865
|
+
const ignored = [];
|
|
866
|
+
const directCalls = calls.filter((call) => enclosingFunction(call) === listener && isDirectStatement(call, listener));
|
|
867
|
+
for (const assignment of assignments) {
|
|
868
|
+
if (assignment.type !== "AssignmentExpression" || assignment.operator !== "=" || assignment.left.type !== "MemberExpression" || !isFunction(assignment.right)) continue;
|
|
869
|
+
const setup = enclosingFunction(assignment);
|
|
870
|
+
if (setup == null || setup === listener || !isInside(setup, listener)) continue;
|
|
871
|
+
const setupParent = getParent(setup);
|
|
872
|
+
const namedLocalSetup = enclosingFunction(setup) === listener && (setup.type === "FunctionDeclaration" || setupParent?.type === "VariableDeclarator" && setupParent.init === setup && setupParent.id.type === "Identifier");
|
|
873
|
+
const passedToCall = calls.some((call) => call.arguments.some((argument) => argument.type !== "SpreadElement" && resolveListenerReference(argument, bindings) === setup));
|
|
874
|
+
if (!namedLocalSetup || passedToCall) continue;
|
|
875
|
+
const installed = assignment.right;
|
|
876
|
+
if (!isDirectStatement(assignment, setup) || assignment.left.object.type !== "Identifier") {
|
|
877
|
+
ignored.push(installed);
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
const property = getMemberPropertyName(assignment.left);
|
|
881
|
+
if (property == null) continue;
|
|
882
|
+
const setupCalls = directCalls.filter((call) => resolveListenerReference(call.callee, bindings) === setup);
|
|
883
|
+
if (setupCalls.length === 0) {
|
|
884
|
+
const called = calls.some((call) => resolveListenerReference(call.callee, bindings) === setup);
|
|
885
|
+
ignored.push(called ? assignment.right : setup);
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
|
|
889
|
+
const deliveryCalls = directCalls.filter((call) => {
|
|
890
|
+
const callee = call.callee;
|
|
891
|
+
return callee.type === "MemberExpression" && callee.object.type === "Identifier" && getMemberPropertyName(callee) === property && bindings.lookup(callee.object, callee.object.name) === objectBinding;
|
|
892
|
+
});
|
|
893
|
+
if (!(objectBinding != null && setupCalls.some((setupCall) => deliveryCalls.some((deliveryCall) => position(setupCall) >= 0 && position(setupCall) < position(deliveryCall))))) ignored.push(installed);
|
|
894
|
+
}
|
|
895
|
+
return ignored;
|
|
896
|
+
}
|
|
897
|
+
function functionCallsDelivery(sourceCode, listener, bindings, calls, assignments, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
|
|
898
|
+
let indices = visited.get(listener);
|
|
899
|
+
if (indices?.has(contextIndex)) return false;
|
|
900
|
+
if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
|
|
901
|
+
indices.add(contextIndex);
|
|
902
|
+
if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex, ignoredLocalSetupFunctions(listener, bindings, calls, assignments))) return true;
|
|
903
|
+
const param = unwrapContextParam(listener.params[contextIndex]);
|
|
904
|
+
if (param?.type !== "Identifier") return false;
|
|
905
|
+
const contextBinding = bindings.lookup(listener, param.name);
|
|
906
|
+
for (const call of calls) {
|
|
907
|
+
let child = call;
|
|
908
|
+
let owner = getParent(child);
|
|
909
|
+
while (owner != null && !isFunctionLike(owner)) {
|
|
910
|
+
const field = owner;
|
|
911
|
+
if ((field.type === "PropertyDefinition" || field.type === "AccessorProperty") && !field.static && field.value === child) break;
|
|
912
|
+
child = owner;
|
|
913
|
+
owner = getParent(owner);
|
|
914
|
+
}
|
|
915
|
+
if (owner !== listener) continue;
|
|
916
|
+
const helper = resolveListenerReference(call.callee, bindings);
|
|
917
|
+
if (helper == null || "generator" in helper && helper.generator) continue;
|
|
918
|
+
for (const [index, argument] of call.arguments.entries()) {
|
|
919
|
+
if (argument.type === "SpreadElement") break;
|
|
920
|
+
const arg = unwrapArgument(argument);
|
|
921
|
+
if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
|
|
922
|
+
if (functionCallsDelivery(sourceCode, helper, bindings, calls, assignments, index, visited)) return true;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return false;
|
|
926
|
+
}
|
|
927
|
+
const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0, ignored = []) => {
|
|
928
|
+
let text = sourceCode.getText(listener);
|
|
929
|
+
const start = position(listener);
|
|
930
|
+
for (const fn of ignored) {
|
|
931
|
+
const from = position(fn) - start;
|
|
932
|
+
const to = endPosition(fn) - start;
|
|
933
|
+
if (from < 0 || to > text.length || to < from) continue;
|
|
934
|
+
text = text.slice(0, from) + " ".repeat(to - from) + text.slice(to);
|
|
935
|
+
}
|
|
936
|
+
const code = stripCommentsAndStrings(text);
|
|
761
937
|
const aliases = /* @__PURE__ */ new Set();
|
|
762
|
-
const contextParam = unwrapContextParam(listener.params[
|
|
938
|
+
const contextParam = unwrapContextParam(listener.params[contextIndex]);
|
|
763
939
|
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
764
940
|
if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
|
|
765
941
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -788,8 +964,9 @@ const listenerCallsDeliveryMethod = (sourceCode, listener) => {
|
|
|
788
964
|
function createRule(buildReport) {
|
|
789
965
|
return (context) => {
|
|
790
966
|
const federationTracker = trackFederationVariables();
|
|
791
|
-
const bindings =
|
|
967
|
+
const bindings = createBindingIndex();
|
|
792
968
|
const pendingCalls = [];
|
|
969
|
+
const pendingAssignments = [];
|
|
793
970
|
const sourceCode = context.sourceCode;
|
|
794
971
|
const inspectCall = (node) => {
|
|
795
972
|
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
@@ -797,7 +974,7 @@ function createRule(buildReport) {
|
|
|
797
974
|
const listener = node.arguments[1];
|
|
798
975
|
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
|
|
799
976
|
if (resolvedListener == null) return;
|
|
800
|
-
if (
|
|
977
|
+
if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls, pendingAssignments)) return;
|
|
801
978
|
context.report({
|
|
802
979
|
node: resolvedListener,
|
|
803
980
|
...buildReport
|
|
@@ -806,14 +983,28 @@ function createRule(buildReport) {
|
|
|
806
983
|
return {
|
|
807
984
|
VariableDeclarator(node) {
|
|
808
985
|
federationTracker.VariableDeclarator(node);
|
|
809
|
-
|
|
986
|
+
const declaration = getParent(node);
|
|
987
|
+
bindings.declare(node, node.id, node.init, declaration?.kind === "var");
|
|
810
988
|
},
|
|
811
989
|
FunctionDeclaration(node) {
|
|
812
|
-
if (node.id
|
|
990
|
+
if (node.id == null) return;
|
|
991
|
+
const parent = getParent(node);
|
|
992
|
+
const owner = parent == null ? null : getParent(parent);
|
|
993
|
+
const inVarBody = parent?.type === "BlockStatement" && owner != null && (owner.type === "StaticBlock" || isFunctionLike(owner) && owner.body === parent);
|
|
994
|
+
bindings.declare(node, node.id, node, inVarBody);
|
|
995
|
+
},
|
|
996
|
+
ImportDeclaration(node) {
|
|
997
|
+
for (const specifier of node.specifiers) bindings.declare(node, specifier.local, null);
|
|
998
|
+
},
|
|
999
|
+
ClassDeclaration(node) {
|
|
1000
|
+
if (node.id != null) bindings.declare(node, node.id, null);
|
|
813
1001
|
},
|
|
814
1002
|
CallExpression(node) {
|
|
815
1003
|
pendingCalls.push(node);
|
|
816
1004
|
},
|
|
1005
|
+
AssignmentExpression(node) {
|
|
1006
|
+
pendingAssignments.push(node);
|
|
1007
|
+
},
|
|
817
1008
|
"Program:exit"() {
|
|
818
1009
|
for (const node of pendingCalls) inspectCall(node);
|
|
819
1010
|
}
|
|
@@ -2,7 +2,7 @@ require("./index.cjs");
|
|
|
2
2
|
let _fxts_core = require("@fxts/core");
|
|
3
3
|
//#region deno.json
|
|
4
4
|
var name = "@fedify/lint";
|
|
5
|
-
var version = "2.3.
|
|
5
|
+
var version = "2.3.10";
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/lib/const.ts
|
|
8
8
|
/**
|
|
@@ -596,7 +596,7 @@ const DELIVERY_METHOD_NAMES = new Set(["sendActivity", "forwardActivity"]);
|
|
|
596
596
|
const getMemberPropertyName = (expr) => {
|
|
597
597
|
if (expr.type !== "MemberExpression") return null;
|
|
598
598
|
const property = expr.property;
|
|
599
|
-
if (property.type === "Identifier") return property.name;
|
|
599
|
+
if (property.type === "Identifier" && !expr.computed) return property.name;
|
|
600
600
|
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
601
601
|
return null;
|
|
602
602
|
};
|
|
@@ -732,35 +732,211 @@ function buildContextExpressionPattern(contextName) {
|
|
|
732
732
|
const boundedName = String.raw`(?<![\w$])${name}(?![\w$])`;
|
|
733
733
|
return String.raw`(?:${boundedName}|\(\s*${boundedName}(?:\s+as\s+[^)]+)?\s*\))`;
|
|
734
734
|
}
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
735
|
+
const getParent = (node) => node.parent ?? null;
|
|
736
|
+
const isFunctionLike = (node) => node.type === "FunctionDeclaration" || isFunction(node);
|
|
737
|
+
function bindPattern(node, bindings) {
|
|
738
|
+
switch (node.type) {
|
|
739
|
+
case "Identifier":
|
|
740
|
+
bindings.set(node.name, { value: null });
|
|
741
|
+
break;
|
|
742
|
+
case "AssignmentPattern":
|
|
743
|
+
bindPattern(node.left, bindings);
|
|
744
|
+
break;
|
|
745
|
+
case "RestElement":
|
|
746
|
+
bindPattern(node.argument, bindings);
|
|
747
|
+
break;
|
|
748
|
+
case "ObjectPattern":
|
|
749
|
+
for (const property of node.properties) bindPattern(property.type === "Property" ? property.value : property, bindings);
|
|
750
|
+
break;
|
|
751
|
+
case "ArrayPattern":
|
|
752
|
+
for (const element of node.elements) if (element != null) bindPattern(element, bindings);
|
|
753
|
+
break;
|
|
745
754
|
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
755
|
+
}
|
|
756
|
+
function createBindingIndex() {
|
|
757
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
758
|
+
const scopeBindings = (scope) => {
|
|
759
|
+
let bindings = scopes.get(scope);
|
|
760
|
+
if (bindings == null) {
|
|
761
|
+
bindings = /* @__PURE__ */ new Map();
|
|
762
|
+
scopes.set(scope, bindings);
|
|
763
|
+
if (isFunctionLike(scope)) {
|
|
764
|
+
if (scope.type === "FunctionExpression" && scope.id != null) bindings.set(scope.id.name, { value: scope });
|
|
765
|
+
for (const param of scope.params) bindPattern(param, bindings);
|
|
766
|
+
}
|
|
767
|
+
if (scope.type === "ClassExpression" && scope.id != null) bindings.set(scope.id.name, { value: null });
|
|
768
|
+
if (scope.type === "CatchClause" && scope.param != null) bindPattern(scope.param, bindings);
|
|
769
|
+
}
|
|
770
|
+
return bindings;
|
|
771
|
+
};
|
|
772
|
+
const enclosingScope = (node, functionScope = false) => {
|
|
773
|
+
let scope = getParent(node);
|
|
774
|
+
while (scope.type !== "Program" && scope.type !== "StaticBlock" && scope.type !== "TSModuleBlock" && !isFunctionLike(scope) && (functionScope || ![
|
|
775
|
+
"BlockStatement",
|
|
776
|
+
"ForStatement",
|
|
777
|
+
"ForInStatement",
|
|
778
|
+
"ForOfStatement",
|
|
779
|
+
"SwitchStatement",
|
|
780
|
+
"CatchClause"
|
|
781
|
+
].includes(scope.type))) scope = getParent(scope);
|
|
782
|
+
return scope;
|
|
783
|
+
};
|
|
784
|
+
const lookup = (node, name) => {
|
|
785
|
+
for (let scope = node; scope != null; scope = getParent(scope)) {
|
|
786
|
+
const binding = scopeBindings(scope).get(name);
|
|
787
|
+
if (binding != null) return binding;
|
|
756
788
|
}
|
|
789
|
+
return null;
|
|
790
|
+
};
|
|
791
|
+
return {
|
|
792
|
+
lookup,
|
|
793
|
+
declare(node, pattern, value, hoisted = false) {
|
|
794
|
+
const bindings = scopeBindings(enclosingScope(node, hoisted));
|
|
795
|
+
if (pattern.type === "Identifier") {
|
|
796
|
+
if (hoisted && value == null && bindings.has(pattern.name)) return;
|
|
797
|
+
if (node.type === "FunctionDeclaration" && bindings.get(pattern.name)?.initializedVariable) return;
|
|
798
|
+
bindings.set(pattern.name, {
|
|
799
|
+
value,
|
|
800
|
+
initializedVariable: node.type === "VariableDeclarator" && value != null
|
|
801
|
+
});
|
|
802
|
+
} else bindPattern(pattern, bindings);
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
const position = (node) => node.range?.[0] ?? node.start ?? -1;
|
|
807
|
+
const endPosition = (node) => node.range?.[1] ?? node.end ?? -1;
|
|
808
|
+
const enclosingFunction = (node) => {
|
|
809
|
+
for (let parent = getParent(node); parent != null; parent = getParent(parent)) if (isFunctionLike(parent)) return parent;
|
|
810
|
+
return null;
|
|
811
|
+
};
|
|
812
|
+
const isInside = (node, ancestor) => {
|
|
813
|
+
for (let current = node; current != null; current = getParent(current)) if (current === ancestor) return true;
|
|
814
|
+
return false;
|
|
815
|
+
};
|
|
816
|
+
const isDirectStatement = (node, fn) => {
|
|
817
|
+
let current = node;
|
|
818
|
+
while (getParent(current) !== fn.body) {
|
|
819
|
+
const parent = getParent(current);
|
|
820
|
+
if (parent == null || ![
|
|
821
|
+
"AwaitExpression",
|
|
822
|
+
"ExpressionStatement",
|
|
823
|
+
"ReturnStatement",
|
|
824
|
+
"VariableDeclarator",
|
|
825
|
+
"VariableDeclaration",
|
|
826
|
+
"BlockStatement"
|
|
827
|
+
].includes(parent.type)) return false;
|
|
828
|
+
current = parent;
|
|
829
|
+
}
|
|
830
|
+
return [
|
|
831
|
+
"ExpressionStatement",
|
|
832
|
+
"ReturnStatement",
|
|
833
|
+
"VariableDeclaration",
|
|
834
|
+
"BlockStatement"
|
|
835
|
+
].includes(current.type);
|
|
836
|
+
};
|
|
837
|
+
const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
838
|
+
if (expr.type !== "Identifier") return expr;
|
|
839
|
+
const binding = bindings.lookup(expr, expr.name);
|
|
840
|
+
if (binding == null || binding.value == null || seen.has(binding)) return null;
|
|
841
|
+
seen.add(binding);
|
|
842
|
+
return resolveBindingValue(binding.value, bindings, seen);
|
|
843
|
+
};
|
|
844
|
+
const resolveListenerReference = (expr, bindings) => {
|
|
845
|
+
const target = resolveBindingValue(expr, bindings);
|
|
846
|
+
if (target == null) return null;
|
|
847
|
+
if (isFunctionLike(target)) return target;
|
|
848
|
+
if (target.type !== "MemberExpression") return null;
|
|
849
|
+
const object = resolveBindingValue(target.object, bindings);
|
|
850
|
+
if (object?.type !== "ObjectExpression") return null;
|
|
851
|
+
const propertyName = getMemberPropertyName(target);
|
|
852
|
+
if (propertyName == null) return null;
|
|
853
|
+
for (const prop of object.properties) {
|
|
854
|
+
if (prop.type !== "Property") continue;
|
|
855
|
+
if ((prop.key.type === "Identifier" && !prop.computed ? prop.key.name : prop.key.type === "Literal" && typeof prop.key.value === "string" ? prop.key.value : null) !== propertyName) continue;
|
|
856
|
+
const value = resolveBindingValue(prop.value, bindings);
|
|
857
|
+
if (value != null && isFunctionLike(value)) return value;
|
|
757
858
|
}
|
|
758
859
|
return null;
|
|
759
860
|
};
|
|
760
|
-
|
|
761
|
-
|
|
861
|
+
function unwrapArgument(node) {
|
|
862
|
+
while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
|
|
863
|
+
return node;
|
|
864
|
+
}
|
|
865
|
+
function ignoredLocalSetupFunctions(listener, bindings, calls, assignments) {
|
|
866
|
+
const ignored = [];
|
|
867
|
+
const directCalls = calls.filter((call) => enclosingFunction(call) === listener && isDirectStatement(call, listener));
|
|
868
|
+
for (const assignment of assignments) {
|
|
869
|
+
if (assignment.type !== "AssignmentExpression" || assignment.operator !== "=" || assignment.left.type !== "MemberExpression" || !isFunction(assignment.right)) continue;
|
|
870
|
+
const setup = enclosingFunction(assignment);
|
|
871
|
+
if (setup == null || setup === listener || !isInside(setup, listener)) continue;
|
|
872
|
+
const setupParent = getParent(setup);
|
|
873
|
+
const namedLocalSetup = enclosingFunction(setup) === listener && (setup.type === "FunctionDeclaration" || setupParent?.type === "VariableDeclarator" && setupParent.init === setup && setupParent.id.type === "Identifier");
|
|
874
|
+
const passedToCall = calls.some((call) => call.arguments.some((argument) => argument.type !== "SpreadElement" && resolveListenerReference(argument, bindings) === setup));
|
|
875
|
+
if (!namedLocalSetup || passedToCall) continue;
|
|
876
|
+
const installed = assignment.right;
|
|
877
|
+
if (!isDirectStatement(assignment, setup) || assignment.left.object.type !== "Identifier") {
|
|
878
|
+
ignored.push(installed);
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
const property = getMemberPropertyName(assignment.left);
|
|
882
|
+
if (property == null) continue;
|
|
883
|
+
const setupCalls = directCalls.filter((call) => resolveListenerReference(call.callee, bindings) === setup);
|
|
884
|
+
if (setupCalls.length === 0) {
|
|
885
|
+
const called = calls.some((call) => resolveListenerReference(call.callee, bindings) === setup);
|
|
886
|
+
ignored.push(called ? assignment.right : setup);
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
|
|
890
|
+
const deliveryCalls = directCalls.filter((call) => {
|
|
891
|
+
const callee = call.callee;
|
|
892
|
+
return callee.type === "MemberExpression" && callee.object.type === "Identifier" && getMemberPropertyName(callee) === property && bindings.lookup(callee.object, callee.object.name) === objectBinding;
|
|
893
|
+
});
|
|
894
|
+
if (!(objectBinding != null && setupCalls.some((setupCall) => deliveryCalls.some((deliveryCall) => position(setupCall) >= 0 && position(setupCall) < position(deliveryCall))))) ignored.push(installed);
|
|
895
|
+
}
|
|
896
|
+
return ignored;
|
|
897
|
+
}
|
|
898
|
+
function functionCallsDelivery(sourceCode, listener, bindings, calls, assignments, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
|
|
899
|
+
let indices = visited.get(listener);
|
|
900
|
+
if (indices?.has(contextIndex)) return false;
|
|
901
|
+
if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
|
|
902
|
+
indices.add(contextIndex);
|
|
903
|
+
if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex, ignoredLocalSetupFunctions(listener, bindings, calls, assignments))) return true;
|
|
904
|
+
const param = unwrapContextParam(listener.params[contextIndex]);
|
|
905
|
+
if (param?.type !== "Identifier") return false;
|
|
906
|
+
const contextBinding = bindings.lookup(listener, param.name);
|
|
907
|
+
for (const call of calls) {
|
|
908
|
+
let child = call;
|
|
909
|
+
let owner = getParent(child);
|
|
910
|
+
while (owner != null && !isFunctionLike(owner)) {
|
|
911
|
+
const field = owner;
|
|
912
|
+
if ((field.type === "PropertyDefinition" || field.type === "AccessorProperty") && !field.static && field.value === child) break;
|
|
913
|
+
child = owner;
|
|
914
|
+
owner = getParent(owner);
|
|
915
|
+
}
|
|
916
|
+
if (owner !== listener) continue;
|
|
917
|
+
const helper = resolveListenerReference(call.callee, bindings);
|
|
918
|
+
if (helper == null || "generator" in helper && helper.generator) continue;
|
|
919
|
+
for (const [index, argument] of call.arguments.entries()) {
|
|
920
|
+
if (argument.type === "SpreadElement") break;
|
|
921
|
+
const arg = unwrapArgument(argument);
|
|
922
|
+
if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
|
|
923
|
+
if (functionCallsDelivery(sourceCode, helper, bindings, calls, assignments, index, visited)) return true;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0, ignored = []) => {
|
|
929
|
+
let text = sourceCode.getText(listener);
|
|
930
|
+
const start = position(listener);
|
|
931
|
+
for (const fn of ignored) {
|
|
932
|
+
const from = position(fn) - start;
|
|
933
|
+
const to = endPosition(fn) - start;
|
|
934
|
+
if (from < 0 || to > text.length || to < from) continue;
|
|
935
|
+
text = text.slice(0, from) + " ".repeat(to - from) + text.slice(to);
|
|
936
|
+
}
|
|
937
|
+
const code = stripCommentsAndStrings(text);
|
|
762
938
|
const aliases = /* @__PURE__ */ new Set();
|
|
763
|
-
const contextParam = unwrapContextParam(listener.params[
|
|
939
|
+
const contextParam = unwrapContextParam(listener.params[contextIndex]);
|
|
764
940
|
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
765
941
|
if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
|
|
766
942
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -789,8 +965,9 @@ const listenerCallsDeliveryMethod = (sourceCode, listener) => {
|
|
|
789
965
|
function createRule(buildReport) {
|
|
790
966
|
return (context) => {
|
|
791
967
|
const federationTracker = trackFederationVariables();
|
|
792
|
-
const bindings =
|
|
968
|
+
const bindings = createBindingIndex();
|
|
793
969
|
const pendingCalls = [];
|
|
970
|
+
const pendingAssignments = [];
|
|
794
971
|
const sourceCode = context.sourceCode;
|
|
795
972
|
const inspectCall = (node) => {
|
|
796
973
|
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
@@ -798,7 +975,7 @@ function createRule(buildReport) {
|
|
|
798
975
|
const listener = node.arguments[1];
|
|
799
976
|
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
|
|
800
977
|
if (resolvedListener == null) return;
|
|
801
|
-
if (
|
|
978
|
+
if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls, pendingAssignments)) return;
|
|
802
979
|
context.report({
|
|
803
980
|
node: resolvedListener,
|
|
804
981
|
...buildReport
|
|
@@ -807,14 +984,28 @@ function createRule(buildReport) {
|
|
|
807
984
|
return {
|
|
808
985
|
VariableDeclarator(node) {
|
|
809
986
|
federationTracker.VariableDeclarator(node);
|
|
810
|
-
|
|
987
|
+
const declaration = getParent(node);
|
|
988
|
+
bindings.declare(node, node.id, node.init, declaration?.kind === "var");
|
|
811
989
|
},
|
|
812
990
|
FunctionDeclaration(node) {
|
|
813
|
-
if (node.id
|
|
991
|
+
if (node.id == null) return;
|
|
992
|
+
const parent = getParent(node);
|
|
993
|
+
const owner = parent == null ? null : getParent(parent);
|
|
994
|
+
const inVarBody = parent?.type === "BlockStatement" && owner != null && (owner.type === "StaticBlock" || isFunctionLike(owner) && owner.body === parent);
|
|
995
|
+
bindings.declare(node, node.id, node, inVarBody);
|
|
996
|
+
},
|
|
997
|
+
ImportDeclaration(node) {
|
|
998
|
+
for (const specifier of node.specifiers) bindings.declare(node, specifier.local, null);
|
|
999
|
+
},
|
|
1000
|
+
ClassDeclaration(node) {
|
|
1001
|
+
if (node.id != null) bindings.declare(node, node.id, null);
|
|
814
1002
|
},
|
|
815
1003
|
CallExpression(node) {
|
|
816
1004
|
pendingCalls.push(node);
|
|
817
1005
|
},
|
|
1006
|
+
AssignmentExpression(node) {
|
|
1007
|
+
pendingAssignments.push(node);
|
|
1008
|
+
},
|
|
818
1009
|
"Program:exit"() {
|
|
819
1010
|
for (const node of pendingCalls) inspectCall(node);
|
|
820
1011
|
}
|
package/dist/oxlint.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-
|
|
1
|
+
const require_outbox_listener_delivery_required = require("./outbox-listener-delivery-required-CwToC306.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$12,
|
package/dist/oxlint.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as RULE_IDS, S as eslint$19, T as version, _ as eslint$5, a as eslint$18, b as eslint$10, c as eslint$8, d as eslint$15, f as eslint$1, g as eslint$4, h as eslint$3, i as eslint$17, l as eslint$9, m as eslint$2, n as eslint$20, o as eslint$6, p as eslint, r as eslint$16, s as eslint$7, t as eslint$21, u as eslint$14, v as eslint$12, w as name, x as eslint$11, y as eslint$13 } from "./outbox-listener-delivery-required-
|
|
1
|
+
import { C as RULE_IDS, S as eslint$19, T as version, _ as eslint$5, a as eslint$18, b as eslint$10, c as eslint$8, d as eslint$15, f as eslint$1, g as eslint$4, h as eslint$3, i as eslint$17, l as eslint$9, m as eslint$2, n as eslint$20, o as eslint$6, p as eslint, r as eslint$16, s as eslint$7, t as eslint$21, u as eslint$14, v as eslint$12, w as name, x as eslint$11, y as eslint$13 } from "./outbox-listener-delivery-required-BNgI_uik.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.3.
|
|
3
|
+
"version": "2.3.10",
|
|
4
4
|
"description": "Fedify linting rules and plugins",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Fedify",
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
],
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"eslint": ">=9.0.0",
|
|
57
|
-
"@fedify/fedify": "^2.3.
|
|
57
|
+
"@fedify/fedify": "^2.3.10"
|
|
58
58
|
},
|
|
59
59
|
"peerDependenciesMeta": {
|
|
60
60
|
"eslint": {
|