@fedify/lint 2.4.0-dev.2149 → 2.4.0-dev.2157
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-DVwxPr9s.cjs → outbox-listener-delivery-required-BOTBsL2X.cjs} +217 -26
- package/dist/{outbox-listener-delivery-required-BBzr448Z.js → outbox-listener-delivery-required-CCxxKD7J.js} +217 -26
- package/dist/oxlint.cjs +1 -1
- package/dist/oxlint.js +1 -1
- package/package.json +3 -3
package/deno.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fedify/lint",
|
|
3
|
-
"version": "2.4.0-dev.
|
|
3
|
+
"version": "2.4.0-dev.2157+b76e5888",
|
|
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-BOTBsL2X.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-
|
|
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-CCxxKD7J.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.
|
|
4
|
+
var version = "2.4.0-dev.2157+b76e5888";
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/lib/const.ts
|
|
7
7
|
/**
|
|
@@ -621,7 +621,7 @@ const eslint$4 = {
|
|
|
621
621
|
//#endregion
|
|
622
622
|
//#region src/rules/media-uploader-authorization-required.ts
|
|
623
623
|
const MESSAGE$3 = "setMediaUploader() should be protected with .authorize(); without it the endpoint accepts uploads from anyone who can reach the URL. Add .authorize() unless a public upload endpoint is intended.";
|
|
624
|
-
function getParent(node) {
|
|
624
|
+
function getParent$1(node) {
|
|
625
625
|
if (node == null || typeof node !== "object") return null;
|
|
626
626
|
const parent = node.parent;
|
|
627
627
|
return isNode(parent) ? parent : null;
|
|
@@ -631,11 +631,11 @@ function getParent(node) {
|
|
|
631
631
|
* `.authorize(...)`, i.e. `federation.setMediaUploader(...).authorize(...)`.
|
|
632
632
|
*/
|
|
633
633
|
function isDirectlyAuthorized(callNode) {
|
|
634
|
-
const member = getParent(callNode);
|
|
634
|
+
const member = getParent$1(callNode);
|
|
635
635
|
if (member == null || member.type !== "MemberExpression" || member.computed === true) return false;
|
|
636
636
|
const property = member.property;
|
|
637
637
|
if (!isNode(property) || property.type !== "Identifier" || property.name !== "authorize") return false;
|
|
638
|
-
const call = getParent(member);
|
|
638
|
+
const call = getParent$1(member);
|
|
639
639
|
return call != null && call.type === "CallExpression" && call.callee === member;
|
|
640
640
|
}
|
|
641
641
|
function createRule$3(buildReport) {
|
|
@@ -653,7 +653,7 @@ function createRule$3(buildReport) {
|
|
|
653
653
|
}
|
|
654
654
|
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || !federationTracker.isFederationObject(node.callee.object)) return;
|
|
655
655
|
if (isDirectlyAuthorized(node)) return;
|
|
656
|
-
const parent = getParent(node);
|
|
656
|
+
const parent = getParent$1(node);
|
|
657
657
|
let varName = null;
|
|
658
658
|
if (parent != null && parent.type === "VariableDeclarator") {
|
|
659
659
|
const id = parent.id;
|
|
@@ -692,7 +692,7 @@ const eslint$3 = {
|
|
|
692
692
|
//#region src/rules/media-uploader-object-uri-required.ts
|
|
693
693
|
const MESSAGE$2 = "setMediaUploader() callbacks should return a value derived from ctx.getObjectUri(): either return ctx.getObjectUri(...) directly, or an object whose id is ctx.getObjectUri(...).";
|
|
694
694
|
const GETTER_NAME = "getObjectUri";
|
|
695
|
-
const getMemberPropertyName$
|
|
695
|
+
const getMemberPropertyName$2 = (node) => {
|
|
696
696
|
if (node.type !== "MemberExpression") return null;
|
|
697
697
|
const property = node.property;
|
|
698
698
|
if (property.type === "Identifier") return property.name;
|
|
@@ -722,7 +722,7 @@ const resolveCallbackReference = (expr, bindings, seen = /* @__PURE__ */ new Set
|
|
|
722
722
|
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
723
723
|
const binding = bindings.get(expr.object.name);
|
|
724
724
|
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
725
|
-
const propertyName = getMemberPropertyName$
|
|
725
|
+
const propertyName = getMemberPropertyName$2(expr);
|
|
726
726
|
if (propertyName == null) return null;
|
|
727
727
|
for (const prop of binding.properties) {
|
|
728
728
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -815,7 +815,7 @@ function toNode(value) {
|
|
|
815
815
|
function isPromiseResolveCall(expr) {
|
|
816
816
|
if (expr.type !== "CallExpression") return false;
|
|
817
817
|
const callee = toNode(expr.callee);
|
|
818
|
-
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$
|
|
818
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== "resolve") return false;
|
|
819
819
|
const object = toNode(callee.object);
|
|
820
820
|
return object != null && object.type === "Identifier" && object.name === "Promise";
|
|
821
821
|
}
|
|
@@ -846,7 +846,7 @@ function isGetObjectUriCall(expr, ctx) {
|
|
|
846
846
|
if (expr.type !== "CallExpression") return false;
|
|
847
847
|
if (ctx.ctxName == null) return false;
|
|
848
848
|
const callee = toNode(expr.callee);
|
|
849
|
-
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$
|
|
849
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== GETTER_NAME) return false;
|
|
850
850
|
const object = unwrapExpression(callee.object);
|
|
851
851
|
return object != null && object.type === "Identifier" && object.name === ctx.ctxName;
|
|
852
852
|
}
|
|
@@ -1012,23 +1012,23 @@ const eslint$2 = {
|
|
|
1012
1012
|
};
|
|
1013
1013
|
//#endregion
|
|
1014
1014
|
//#region src/lib/outbox-listener.ts
|
|
1015
|
-
const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
|
|
1016
|
-
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
1015
|
+
const DELIVERY_METHOD_NAMES$1 = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
|
|
1016
|
+
const isChainedFromOutboxListeners$1 = (expr, federationTracker) => {
|
|
1017
1017
|
if (expr.type !== "CallExpression") return false;
|
|
1018
1018
|
if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
|
|
1019
1019
|
const methodName = expr.callee.property.name;
|
|
1020
1020
|
if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
|
|
1021
|
-
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
|
|
1021
|
+
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners$1(expr.callee.object, federationTracker);
|
|
1022
1022
|
return false;
|
|
1023
1023
|
};
|
|
1024
|
-
const getMemberPropertyName = (expr) => {
|
|
1024
|
+
const getMemberPropertyName$1 = (expr) => {
|
|
1025
1025
|
if (expr.type !== "MemberExpression") return null;
|
|
1026
1026
|
const property = expr.property;
|
|
1027
1027
|
if (property.type === "Identifier") return property.name;
|
|
1028
1028
|
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
1029
1029
|
return null;
|
|
1030
1030
|
};
|
|
1031
|
-
function unwrapContextParam(node) {
|
|
1031
|
+
function unwrapContextParam$1(node) {
|
|
1032
1032
|
let current = node ?? null;
|
|
1033
1033
|
while (current?.type === "AssignmentPattern") current = current.left;
|
|
1034
1034
|
return current;
|
|
@@ -1054,7 +1054,7 @@ const resolveFunctionBinding = (expr, bindings, seen = /* @__PURE__ */ new Set()
|
|
|
1054
1054
|
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
1055
1055
|
const binding = bindings.get(expr.object.name);
|
|
1056
1056
|
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
1057
|
-
const propertyName = getMemberPropertyName(expr);
|
|
1057
|
+
const propertyName = getMemberPropertyName$1(expr);
|
|
1058
1058
|
if (propertyName == null) return null;
|
|
1059
1059
|
for (const prop of binding.properties) {
|
|
1060
1060
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -1078,7 +1078,7 @@ function createOutboxListenerVisitor(onListener) {
|
|
|
1078
1078
|
const pendingCalls = [];
|
|
1079
1079
|
const inspectCall = (node) => {
|
|
1080
1080
|
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
1081
|
-
if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
|
|
1081
|
+
if (!isChainedFromOutboxListeners$1(node.callee.object, federationTracker)) return;
|
|
1082
1082
|
const listener = node.arguments[1];
|
|
1083
1083
|
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveFunctionBinding(listener, bindings) : null;
|
|
1084
1084
|
if (resolvedListener == null) return;
|
|
@@ -1737,7 +1737,7 @@ function deliveryAliasesOf(pattern, out) {
|
|
|
1737
1737
|
if (prop.type !== "Property") continue;
|
|
1738
1738
|
const key = asNode(get(prop, "key"));
|
|
1739
1739
|
const keyName = key?.type === "Identifier" ? key.name : key?.type === "Literal" && typeof key.value === "string" ? key.value : null;
|
|
1740
|
-
if (keyName == null || !DELIVERY_METHOD_NAMES.has(keyName)) continue;
|
|
1740
|
+
if (keyName == null || !DELIVERY_METHOD_NAMES$1.has(keyName)) continue;
|
|
1741
1741
|
const value = asNode(get(prop, "value"));
|
|
1742
1742
|
if (value?.type === "Identifier") out.add(value.name);
|
|
1743
1743
|
else if (value?.type === "AssignmentPattern") {
|
|
@@ -1755,7 +1755,7 @@ function deliveryAliasesOf(pattern, out) {
|
|
|
1755
1755
|
*/
|
|
1756
1756
|
function findDeliveryTargets(listener, scopes) {
|
|
1757
1757
|
const aliases = /* @__PURE__ */ new Set();
|
|
1758
|
-
const contextParam = unwrapContextParam(listener.params[0]);
|
|
1758
|
+
const contextParam = unwrapContextParam$1(listener.params[0]);
|
|
1759
1759
|
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
1760
1760
|
if (contextParam != null) deliveryAliasesOf(contextParam, aliases);
|
|
1761
1761
|
if (contextName != null) for (const scope of scopes) for (const statement of scope.statements) visitAll(statement, (node) => {
|
|
@@ -1771,7 +1771,7 @@ function findDeliveryTargets(listener, scopes) {
|
|
|
1771
1771
|
if (id.type !== "Identifier" || source.type !== "MemberExpression") return;
|
|
1772
1772
|
const object = unwrap(asNode(get(source, "object")) ?? source);
|
|
1773
1773
|
const name = memberName(source);
|
|
1774
|
-
if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES.has(name)) aliases.add(id.name);
|
|
1774
|
+
if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES$1.has(name)) aliases.add(id.name);
|
|
1775
1775
|
}, false);
|
|
1776
1776
|
return {
|
|
1777
1777
|
contextName,
|
|
@@ -1929,7 +1929,7 @@ function checkListener(listener, report) {
|
|
|
1929
1929
|
const callee = unwrap(call.callee);
|
|
1930
1930
|
if (callee.type === "MemberExpression") {
|
|
1931
1931
|
const name = memberName(callee);
|
|
1932
|
-
if (name == null || !DELIVERY_METHOD_NAMES.has(name)) return false;
|
|
1932
|
+
if (name == null || !DELIVERY_METHOD_NAMES$1.has(name)) return false;
|
|
1933
1933
|
const object = unwrap(asNode(get(callee, "object")) ?? callee);
|
|
1934
1934
|
return object.type === "Identifier" && object.name === targets.contextName;
|
|
1935
1935
|
}
|
|
@@ -2012,6 +2012,122 @@ const eslint$1 = {
|
|
|
2012
2012
|
//#endregion
|
|
2013
2013
|
//#region src/rules/outbox-listener-delivery-required.ts
|
|
2014
2014
|
const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
|
|
2015
|
+
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
2016
|
+
if (expr.type !== "CallExpression") return false;
|
|
2017
|
+
if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
|
|
2018
|
+
const methodName = expr.callee.property.name;
|
|
2019
|
+
if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
|
|
2020
|
+
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
|
|
2021
|
+
return false;
|
|
2022
|
+
};
|
|
2023
|
+
const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
|
|
2024
|
+
const getMemberPropertyName = (expr) => {
|
|
2025
|
+
if (expr.type !== "MemberExpression") return null;
|
|
2026
|
+
const property = expr.property;
|
|
2027
|
+
if (property.type === "Identifier" && !expr.computed) return property.name;
|
|
2028
|
+
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
2029
|
+
return null;
|
|
2030
|
+
};
|
|
2031
|
+
function unwrapContextParam(node) {
|
|
2032
|
+
let current = node ?? null;
|
|
2033
|
+
while (current?.type === "AssignmentPattern") current = current.left;
|
|
2034
|
+
return current;
|
|
2035
|
+
}
|
|
2036
|
+
const getParent = (node) => node.parent ?? null;
|
|
2037
|
+
const isFunctionLike = (node) => node.type === "FunctionDeclaration" || isFunction(node);
|
|
2038
|
+
function bindPattern(node, bindings) {
|
|
2039
|
+
switch (node.type) {
|
|
2040
|
+
case "Identifier":
|
|
2041
|
+
bindings.set(node.name, { value: null });
|
|
2042
|
+
break;
|
|
2043
|
+
case "AssignmentPattern":
|
|
2044
|
+
bindPattern(node.left, bindings);
|
|
2045
|
+
break;
|
|
2046
|
+
case "RestElement":
|
|
2047
|
+
bindPattern(node.argument, bindings);
|
|
2048
|
+
break;
|
|
2049
|
+
case "ObjectPattern":
|
|
2050
|
+
for (const property of node.properties) bindPattern(property.type === "Property" ? property.value : property, bindings);
|
|
2051
|
+
break;
|
|
2052
|
+
case "ArrayPattern":
|
|
2053
|
+
for (const element of node.elements) if (element != null) bindPattern(element, bindings);
|
|
2054
|
+
break;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
function createBindingIndex() {
|
|
2058
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
2059
|
+
const scopeBindings = (scope) => {
|
|
2060
|
+
let bindings = scopes.get(scope);
|
|
2061
|
+
if (bindings == null) {
|
|
2062
|
+
bindings = /* @__PURE__ */ new Map();
|
|
2063
|
+
scopes.set(scope, bindings);
|
|
2064
|
+
if (isFunctionLike(scope)) {
|
|
2065
|
+
if (scope.type === "FunctionExpression" && scope.id != null) bindings.set(scope.id.name, { value: scope });
|
|
2066
|
+
for (const param of scope.params) bindPattern(param, bindings);
|
|
2067
|
+
}
|
|
2068
|
+
if (scope.type === "ClassExpression" && scope.id != null) bindings.set(scope.id.name, { value: null });
|
|
2069
|
+
if (scope.type === "CatchClause" && scope.param != null) bindPattern(scope.param, bindings);
|
|
2070
|
+
}
|
|
2071
|
+
return bindings;
|
|
2072
|
+
};
|
|
2073
|
+
const enclosingScope = (node, functionScope = false) => {
|
|
2074
|
+
let scope = getParent(node);
|
|
2075
|
+
while (scope.type !== "Program" && scope.type !== "StaticBlock" && scope.type !== "TSModuleBlock" && !isFunctionLike(scope) && (functionScope || ![
|
|
2076
|
+
"BlockStatement",
|
|
2077
|
+
"ForStatement",
|
|
2078
|
+
"ForInStatement",
|
|
2079
|
+
"ForOfStatement",
|
|
2080
|
+
"SwitchStatement",
|
|
2081
|
+
"CatchClause"
|
|
2082
|
+
].includes(scope.type))) scope = getParent(scope);
|
|
2083
|
+
return scope;
|
|
2084
|
+
};
|
|
2085
|
+
const lookup = (node, name) => {
|
|
2086
|
+
for (let scope = node; scope != null; scope = getParent(scope)) {
|
|
2087
|
+
const binding = scopeBindings(scope).get(name);
|
|
2088
|
+
if (binding != null) return binding;
|
|
2089
|
+
}
|
|
2090
|
+
return null;
|
|
2091
|
+
};
|
|
2092
|
+
return {
|
|
2093
|
+
lookup,
|
|
2094
|
+
declare(node, pattern, value, hoisted = false) {
|
|
2095
|
+
const bindings = scopeBindings(enclosingScope(node, hoisted));
|
|
2096
|
+
if (pattern.type === "Identifier") {
|
|
2097
|
+
if (hoisted && value == null && bindings.has(pattern.name)) return;
|
|
2098
|
+
if (node.type === "FunctionDeclaration" && bindings.get(pattern.name)?.initializedVariable) return;
|
|
2099
|
+
bindings.set(pattern.name, {
|
|
2100
|
+
value,
|
|
2101
|
+
initializedVariable: node.type === "VariableDeclarator" && value != null
|
|
2102
|
+
});
|
|
2103
|
+
} else bindPattern(pattern, bindings);
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
2108
|
+
if (expr.type !== "Identifier") return expr;
|
|
2109
|
+
const binding = bindings.lookup(expr, expr.name);
|
|
2110
|
+
if (binding == null || binding.value == null || seen.has(binding)) return null;
|
|
2111
|
+
seen.add(binding);
|
|
2112
|
+
return resolveBindingValue(binding.value, bindings, seen);
|
|
2113
|
+
};
|
|
2114
|
+
const resolveListenerReference = (expr, bindings) => {
|
|
2115
|
+
const target = resolveBindingValue(expr, bindings);
|
|
2116
|
+
if (target == null) return null;
|
|
2117
|
+
if (isFunctionLike(target)) return target;
|
|
2118
|
+
if (target.type !== "MemberExpression") return null;
|
|
2119
|
+
const object = resolveBindingValue(target.object, bindings);
|
|
2120
|
+
if (object?.type !== "ObjectExpression") return null;
|
|
2121
|
+
const propertyName = getMemberPropertyName(target);
|
|
2122
|
+
if (propertyName == null) return null;
|
|
2123
|
+
for (const prop of object.properties) {
|
|
2124
|
+
if (prop.type !== "Property") continue;
|
|
2125
|
+
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;
|
|
2126
|
+
const value = resolveBindingValue(prop.value, bindings);
|
|
2127
|
+
if (value != null && isFunctionLike(value)) return value;
|
|
2128
|
+
}
|
|
2129
|
+
return null;
|
|
2130
|
+
};
|
|
2015
2131
|
function escapeRegExp(value) {
|
|
2016
2132
|
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2017
2133
|
}
|
|
@@ -2177,11 +2293,52 @@ function collectDeliveryScanCode(sourceCode, root, used, visited) {
|
|
|
2177
2293
|
return result;
|
|
2178
2294
|
}).join("\n");
|
|
2179
2295
|
}
|
|
2180
|
-
|
|
2296
|
+
function unwrapArgument(node) {
|
|
2297
|
+
while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
|
|
2298
|
+
return node;
|
|
2299
|
+
}
|
|
2300
|
+
function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
|
|
2301
|
+
let indices = visited.get(listener);
|
|
2302
|
+
if (indices?.has(contextIndex)) return false;
|
|
2303
|
+
if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
|
|
2304
|
+
indices.add(contextIndex);
|
|
2305
|
+
if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
|
|
2306
|
+
const param = unwrapContextParam(listener.params[contextIndex]);
|
|
2307
|
+
if (param?.type !== "Identifier") return false;
|
|
2308
|
+
const contextBinding = bindings.lookup(listener, param.name);
|
|
2309
|
+
const reachable = [];
|
|
2310
|
+
collectReachableStatements(listener.body, reachable);
|
|
2311
|
+
for (const call of calls) {
|
|
2312
|
+
let child = call;
|
|
2313
|
+
let owner = getParent(child);
|
|
2314
|
+
while (owner != null && !isFunctionLike(owner)) {
|
|
2315
|
+
const field = owner;
|
|
2316
|
+
if ((field.type === "PropertyDefinition" || field.type === "AccessorProperty") && !field.static && field.value === child) break;
|
|
2317
|
+
child = owner;
|
|
2318
|
+
owner = getParent(owner);
|
|
2319
|
+
}
|
|
2320
|
+
if (owner !== listener) continue;
|
|
2321
|
+
const [callStart, callEnd] = getRange(call);
|
|
2322
|
+
if (!reachable.some((statement) => {
|
|
2323
|
+
const [start, end] = getRange(statement);
|
|
2324
|
+
return start <= callStart && callEnd <= end;
|
|
2325
|
+
})) continue;
|
|
2326
|
+
const helper = resolveListenerReference(call.callee, bindings);
|
|
2327
|
+
if (helper == null || "generator" in helper && helper.generator) continue;
|
|
2328
|
+
for (const [index, argument] of call.arguments.entries()) {
|
|
2329
|
+
if (argument.type === "SpreadElement") break;
|
|
2330
|
+
const arg = unwrapArgument(argument);
|
|
2331
|
+
if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
|
|
2332
|
+
if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
return false;
|
|
2336
|
+
}
|
|
2337
|
+
const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
|
|
2181
2338
|
const used = computeUsedFunctions(listener.body);
|
|
2182
2339
|
const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, /* @__PURE__ */ new Set()));
|
|
2183
2340
|
const aliases = /* @__PURE__ */ new Set();
|
|
2184
|
-
const contextParam = unwrapContextParam(listener.params[
|
|
2341
|
+
const contextParam = unwrapContextParam(listener.params[contextIndex]);
|
|
2185
2342
|
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
2186
2343
|
if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
|
|
2187
2344
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -2209,14 +2366,48 @@ const listenerCallsDeliveryMethod = (sourceCode, listener) => {
|
|
|
2209
2366
|
};
|
|
2210
2367
|
function createRule(buildReport) {
|
|
2211
2368
|
return (context) => {
|
|
2369
|
+
const federationTracker = trackFederationVariables();
|
|
2370
|
+
const bindings = createBindingIndex();
|
|
2371
|
+
const pendingCalls = [];
|
|
2212
2372
|
const sourceCode = context.sourceCode;
|
|
2213
|
-
|
|
2214
|
-
if (
|
|
2373
|
+
const inspectCall = (node) => {
|
|
2374
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
2375
|
+
if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
|
|
2376
|
+
const listener = node.arguments[1];
|
|
2377
|
+
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
|
|
2378
|
+
if (resolvedListener == null) return;
|
|
2379
|
+
if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
|
|
2215
2380
|
context.report({
|
|
2216
|
-
node:
|
|
2381
|
+
node: resolvedListener,
|
|
2217
2382
|
...buildReport
|
|
2218
2383
|
});
|
|
2219
|
-
}
|
|
2384
|
+
};
|
|
2385
|
+
return {
|
|
2386
|
+
VariableDeclarator(node) {
|
|
2387
|
+
federationTracker.VariableDeclarator(node);
|
|
2388
|
+
const declaration = getParent(node);
|
|
2389
|
+
bindings.declare(node, node.id, node.init, declaration?.kind === "var");
|
|
2390
|
+
},
|
|
2391
|
+
FunctionDeclaration(node) {
|
|
2392
|
+
if (node.id == null) return;
|
|
2393
|
+
const parent = getParent(node);
|
|
2394
|
+
const owner = parent == null ? null : getParent(parent);
|
|
2395
|
+
const inVarBody = parent?.type === "BlockStatement" && owner != null && (owner.type === "StaticBlock" || isFunctionLike(owner) && owner.body === parent);
|
|
2396
|
+
bindings.declare(node, node.id, node, inVarBody);
|
|
2397
|
+
},
|
|
2398
|
+
ImportDeclaration(node) {
|
|
2399
|
+
for (const specifier of node.specifiers) bindings.declare(node, specifier.local, null);
|
|
2400
|
+
},
|
|
2401
|
+
ClassDeclaration(node) {
|
|
2402
|
+
if (node.id != null) bindings.declare(node, node.id, null);
|
|
2403
|
+
},
|
|
2404
|
+
CallExpression(node) {
|
|
2405
|
+
pendingCalls.push(node);
|
|
2406
|
+
},
|
|
2407
|
+
"Program:exit"() {
|
|
2408
|
+
for (const node of pendingCalls) inspectCall(node);
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2220
2411
|
};
|
|
2221
2412
|
}
|
|
2222
2413
|
const eslint = {
|
|
@@ -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.
|
|
4
|
+
var version = "2.4.0-dev.2157+b76e5888";
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/lib/const.ts
|
|
7
7
|
/**
|
|
@@ -621,7 +621,7 @@ const eslint$4 = {
|
|
|
621
621
|
//#endregion
|
|
622
622
|
//#region src/rules/media-uploader-authorization-required.ts
|
|
623
623
|
const MESSAGE$3 = "setMediaUploader() should be protected with .authorize(); without it the endpoint accepts uploads from anyone who can reach the URL. Add .authorize() unless a public upload endpoint is intended.";
|
|
624
|
-
function getParent(node) {
|
|
624
|
+
function getParent$1(node) {
|
|
625
625
|
if (node == null || typeof node !== "object") return null;
|
|
626
626
|
const parent = node.parent;
|
|
627
627
|
return isNode(parent) ? parent : null;
|
|
@@ -631,11 +631,11 @@ function getParent(node) {
|
|
|
631
631
|
* `.authorize(...)`, i.e. `federation.setMediaUploader(...).authorize(...)`.
|
|
632
632
|
*/
|
|
633
633
|
function isDirectlyAuthorized(callNode) {
|
|
634
|
-
const member = getParent(callNode);
|
|
634
|
+
const member = getParent$1(callNode);
|
|
635
635
|
if (member == null || member.type !== "MemberExpression" || member.computed === true) return false;
|
|
636
636
|
const property = member.property;
|
|
637
637
|
if (!isNode(property) || property.type !== "Identifier" || property.name !== "authorize") return false;
|
|
638
|
-
const call = getParent(member);
|
|
638
|
+
const call = getParent$1(member);
|
|
639
639
|
return call != null && call.type === "CallExpression" && call.callee === member;
|
|
640
640
|
}
|
|
641
641
|
function createRule$3(buildReport) {
|
|
@@ -653,7 +653,7 @@ function createRule$3(buildReport) {
|
|
|
653
653
|
}
|
|
654
654
|
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("setMediaUploader")(node) || !federationTracker.isFederationObject(node.callee.object)) return;
|
|
655
655
|
if (isDirectlyAuthorized(node)) return;
|
|
656
|
-
const parent = getParent(node);
|
|
656
|
+
const parent = getParent$1(node);
|
|
657
657
|
let varName = null;
|
|
658
658
|
if (parent != null && parent.type === "VariableDeclarator") {
|
|
659
659
|
const id = parent.id;
|
|
@@ -692,7 +692,7 @@ const eslint$3 = {
|
|
|
692
692
|
//#region src/rules/media-uploader-object-uri-required.ts
|
|
693
693
|
const MESSAGE$2 = "setMediaUploader() callbacks should return a value derived from ctx.getObjectUri(): either return ctx.getObjectUri(...) directly, or an object whose id is ctx.getObjectUri(...).";
|
|
694
694
|
const GETTER_NAME = "getObjectUri";
|
|
695
|
-
const getMemberPropertyName$
|
|
695
|
+
const getMemberPropertyName$2 = (node) => {
|
|
696
696
|
if (node.type !== "MemberExpression") return null;
|
|
697
697
|
const property = node.property;
|
|
698
698
|
if (property.type === "Identifier") return property.name;
|
|
@@ -722,7 +722,7 @@ const resolveCallbackReference = (expr, bindings, seen = /* @__PURE__ */ new Set
|
|
|
722
722
|
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
723
723
|
const binding = bindings.get(expr.object.name);
|
|
724
724
|
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
725
|
-
const propertyName = getMemberPropertyName$
|
|
725
|
+
const propertyName = getMemberPropertyName$2(expr);
|
|
726
726
|
if (propertyName == null) return null;
|
|
727
727
|
for (const prop of binding.properties) {
|
|
728
728
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -815,7 +815,7 @@ function toNode(value) {
|
|
|
815
815
|
function isPromiseResolveCall(expr) {
|
|
816
816
|
if (expr.type !== "CallExpression") return false;
|
|
817
817
|
const callee = toNode(expr.callee);
|
|
818
|
-
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$
|
|
818
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== "resolve") return false;
|
|
819
819
|
const object = toNode(callee.object);
|
|
820
820
|
return object != null && object.type === "Identifier" && object.name === "Promise";
|
|
821
821
|
}
|
|
@@ -846,7 +846,7 @@ function isGetObjectUriCall(expr, ctx) {
|
|
|
846
846
|
if (expr.type !== "CallExpression") return false;
|
|
847
847
|
if (ctx.ctxName == null) return false;
|
|
848
848
|
const callee = toNode(expr.callee);
|
|
849
|
-
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$
|
|
849
|
+
if (callee == null || callee.type !== "MemberExpression" || getMemberPropertyName$2(callee) !== GETTER_NAME) return false;
|
|
850
850
|
const object = unwrapExpression(callee.object);
|
|
851
851
|
return object != null && object.type === "Identifier" && object.name === ctx.ctxName;
|
|
852
852
|
}
|
|
@@ -1012,23 +1012,23 @@ const eslint$2 = {
|
|
|
1012
1012
|
};
|
|
1013
1013
|
//#endregion
|
|
1014
1014
|
//#region src/lib/outbox-listener.ts
|
|
1015
|
-
const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
|
|
1016
|
-
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
1015
|
+
const DELIVERY_METHOD_NAMES$1 = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
|
|
1016
|
+
const isChainedFromOutboxListeners$1 = (expr, federationTracker) => {
|
|
1017
1017
|
if (expr.type !== "CallExpression") return false;
|
|
1018
1018
|
if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
|
|
1019
1019
|
const methodName = expr.callee.property.name;
|
|
1020
1020
|
if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
|
|
1021
|
-
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
|
|
1021
|
+
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners$1(expr.callee.object, federationTracker);
|
|
1022
1022
|
return false;
|
|
1023
1023
|
};
|
|
1024
|
-
const getMemberPropertyName = (expr) => {
|
|
1024
|
+
const getMemberPropertyName$1 = (expr) => {
|
|
1025
1025
|
if (expr.type !== "MemberExpression") return null;
|
|
1026
1026
|
const property = expr.property;
|
|
1027
1027
|
if (property.type === "Identifier") return property.name;
|
|
1028
1028
|
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
1029
1029
|
return null;
|
|
1030
1030
|
};
|
|
1031
|
-
function unwrapContextParam(node) {
|
|
1031
|
+
function unwrapContextParam$1(node) {
|
|
1032
1032
|
let current = node ?? null;
|
|
1033
1033
|
while (current?.type === "AssignmentPattern") current = current.left;
|
|
1034
1034
|
return current;
|
|
@@ -1054,7 +1054,7 @@ const resolveFunctionBinding = (expr, bindings, seen = /* @__PURE__ */ new Set()
|
|
|
1054
1054
|
if (expr.type === "MemberExpression" && expr.object.type === "Identifier" && !expr.computed) {
|
|
1055
1055
|
const binding = bindings.get(expr.object.name);
|
|
1056
1056
|
if (binding == null || !isNode(binding) || binding.type !== "ObjectExpression") return null;
|
|
1057
|
-
const propertyName = getMemberPropertyName(expr);
|
|
1057
|
+
const propertyName = getMemberPropertyName$1(expr);
|
|
1058
1058
|
if (propertyName == null) return null;
|
|
1059
1059
|
for (const prop of binding.properties) {
|
|
1060
1060
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -1078,7 +1078,7 @@ function createOutboxListenerVisitor(onListener) {
|
|
|
1078
1078
|
const pendingCalls = [];
|
|
1079
1079
|
const inspectCall = (node) => {
|
|
1080
1080
|
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
1081
|
-
if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
|
|
1081
|
+
if (!isChainedFromOutboxListeners$1(node.callee.object, federationTracker)) return;
|
|
1082
1082
|
const listener = node.arguments[1];
|
|
1083
1083
|
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveFunctionBinding(listener, bindings) : null;
|
|
1084
1084
|
if (resolvedListener == null) return;
|
|
@@ -1737,7 +1737,7 @@ function deliveryAliasesOf(pattern, out) {
|
|
|
1737
1737
|
if (prop.type !== "Property") continue;
|
|
1738
1738
|
const key = asNode(get(prop, "key"));
|
|
1739
1739
|
const keyName = key?.type === "Identifier" ? key.name : key?.type === "Literal" && typeof key.value === "string" ? key.value : null;
|
|
1740
|
-
if (keyName == null || !DELIVERY_METHOD_NAMES.has(keyName)) continue;
|
|
1740
|
+
if (keyName == null || !DELIVERY_METHOD_NAMES$1.has(keyName)) continue;
|
|
1741
1741
|
const value = asNode(get(prop, "value"));
|
|
1742
1742
|
if (value?.type === "Identifier") out.add(value.name);
|
|
1743
1743
|
else if (value?.type === "AssignmentPattern") {
|
|
@@ -1755,7 +1755,7 @@ function deliveryAliasesOf(pattern, out) {
|
|
|
1755
1755
|
*/
|
|
1756
1756
|
function findDeliveryTargets(listener, scopes) {
|
|
1757
1757
|
const aliases = /* @__PURE__ */ new Set();
|
|
1758
|
-
const contextParam = unwrapContextParam(listener.params[0]);
|
|
1758
|
+
const contextParam = unwrapContextParam$1(listener.params[0]);
|
|
1759
1759
|
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
1760
1760
|
if (contextParam != null) deliveryAliasesOf(contextParam, aliases);
|
|
1761
1761
|
if (contextName != null) for (const scope of scopes) for (const statement of scope.statements) visitAll(statement, (node) => {
|
|
@@ -1771,7 +1771,7 @@ function findDeliveryTargets(listener, scopes) {
|
|
|
1771
1771
|
if (id.type !== "Identifier" || source.type !== "MemberExpression") return;
|
|
1772
1772
|
const object = unwrap(asNode(get(source, "object")) ?? source);
|
|
1773
1773
|
const name = memberName(source);
|
|
1774
|
-
if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES.has(name)) aliases.add(id.name);
|
|
1774
|
+
if (object.type === "Identifier" && object.name === contextName && name != null && DELIVERY_METHOD_NAMES$1.has(name)) aliases.add(id.name);
|
|
1775
1775
|
}, false);
|
|
1776
1776
|
return {
|
|
1777
1777
|
contextName,
|
|
@@ -1929,7 +1929,7 @@ function checkListener(listener, report) {
|
|
|
1929
1929
|
const callee = unwrap(call.callee);
|
|
1930
1930
|
if (callee.type === "MemberExpression") {
|
|
1931
1931
|
const name = memberName(callee);
|
|
1932
|
-
if (name == null || !DELIVERY_METHOD_NAMES.has(name)) return false;
|
|
1932
|
+
if (name == null || !DELIVERY_METHOD_NAMES$1.has(name)) return false;
|
|
1933
1933
|
const object = unwrap(asNode(get(callee, "object")) ?? callee);
|
|
1934
1934
|
return object.type === "Identifier" && object.name === targets.contextName;
|
|
1935
1935
|
}
|
|
@@ -2012,6 +2012,122 @@ const eslint$1 = {
|
|
|
2012
2012
|
//#endregion
|
|
2013
2013
|
//#region src/rules/outbox-listener-delivery-required.ts
|
|
2014
2014
|
const MESSAGE = "Outbox listeners should deliver posted activities explicitly with ctx.sendActivity() or ctx.forwardActivity().";
|
|
2015
|
+
const isChainedFromOutboxListeners = (expr, federationTracker) => {
|
|
2016
|
+
if (expr.type !== "CallExpression") return false;
|
|
2017
|
+
if (!hasMemberExpressionCallee(expr) || !hasIdentifierProperty(expr)) return false;
|
|
2018
|
+
const methodName = expr.callee.property.name;
|
|
2019
|
+
if (methodName === "setOutboxListeners") return federationTracker.isFederationObject(expr.callee.object);
|
|
2020
|
+
if (methodName === "authorize" || methodName === "onError" || methodName === "on") return isChainedFromOutboxListeners(expr.callee.object, federationTracker);
|
|
2021
|
+
return false;
|
|
2022
|
+
};
|
|
2023
|
+
const DELIVERY_METHOD_NAMES = /* @__PURE__ */ new Set(["sendActivity", "forwardActivity"]);
|
|
2024
|
+
const getMemberPropertyName = (expr) => {
|
|
2025
|
+
if (expr.type !== "MemberExpression") return null;
|
|
2026
|
+
const property = expr.property;
|
|
2027
|
+
if (property.type === "Identifier" && !expr.computed) return property.name;
|
|
2028
|
+
if (property.type === "Literal" && typeof property.value === "string") return property.value;
|
|
2029
|
+
return null;
|
|
2030
|
+
};
|
|
2031
|
+
function unwrapContextParam(node) {
|
|
2032
|
+
let current = node ?? null;
|
|
2033
|
+
while (current?.type === "AssignmentPattern") current = current.left;
|
|
2034
|
+
return current;
|
|
2035
|
+
}
|
|
2036
|
+
const getParent = (node) => node.parent ?? null;
|
|
2037
|
+
const isFunctionLike = (node) => node.type === "FunctionDeclaration" || isFunction(node);
|
|
2038
|
+
function bindPattern(node, bindings) {
|
|
2039
|
+
switch (node.type) {
|
|
2040
|
+
case "Identifier":
|
|
2041
|
+
bindings.set(node.name, { value: null });
|
|
2042
|
+
break;
|
|
2043
|
+
case "AssignmentPattern":
|
|
2044
|
+
bindPattern(node.left, bindings);
|
|
2045
|
+
break;
|
|
2046
|
+
case "RestElement":
|
|
2047
|
+
bindPattern(node.argument, bindings);
|
|
2048
|
+
break;
|
|
2049
|
+
case "ObjectPattern":
|
|
2050
|
+
for (const property of node.properties) bindPattern(property.type === "Property" ? property.value : property, bindings);
|
|
2051
|
+
break;
|
|
2052
|
+
case "ArrayPattern":
|
|
2053
|
+
for (const element of node.elements) if (element != null) bindPattern(element, bindings);
|
|
2054
|
+
break;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
function createBindingIndex() {
|
|
2058
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
2059
|
+
const scopeBindings = (scope) => {
|
|
2060
|
+
let bindings = scopes.get(scope);
|
|
2061
|
+
if (bindings == null) {
|
|
2062
|
+
bindings = /* @__PURE__ */ new Map();
|
|
2063
|
+
scopes.set(scope, bindings);
|
|
2064
|
+
if (isFunctionLike(scope)) {
|
|
2065
|
+
if (scope.type === "FunctionExpression" && scope.id != null) bindings.set(scope.id.name, { value: scope });
|
|
2066
|
+
for (const param of scope.params) bindPattern(param, bindings);
|
|
2067
|
+
}
|
|
2068
|
+
if (scope.type === "ClassExpression" && scope.id != null) bindings.set(scope.id.name, { value: null });
|
|
2069
|
+
if (scope.type === "CatchClause" && scope.param != null) bindPattern(scope.param, bindings);
|
|
2070
|
+
}
|
|
2071
|
+
return bindings;
|
|
2072
|
+
};
|
|
2073
|
+
const enclosingScope = (node, functionScope = false) => {
|
|
2074
|
+
let scope = getParent(node);
|
|
2075
|
+
while (scope.type !== "Program" && scope.type !== "StaticBlock" && scope.type !== "TSModuleBlock" && !isFunctionLike(scope) && (functionScope || ![
|
|
2076
|
+
"BlockStatement",
|
|
2077
|
+
"ForStatement",
|
|
2078
|
+
"ForInStatement",
|
|
2079
|
+
"ForOfStatement",
|
|
2080
|
+
"SwitchStatement",
|
|
2081
|
+
"CatchClause"
|
|
2082
|
+
].includes(scope.type))) scope = getParent(scope);
|
|
2083
|
+
return scope;
|
|
2084
|
+
};
|
|
2085
|
+
const lookup = (node, name) => {
|
|
2086
|
+
for (let scope = node; scope != null; scope = getParent(scope)) {
|
|
2087
|
+
const binding = scopeBindings(scope).get(name);
|
|
2088
|
+
if (binding != null) return binding;
|
|
2089
|
+
}
|
|
2090
|
+
return null;
|
|
2091
|
+
};
|
|
2092
|
+
return {
|
|
2093
|
+
lookup,
|
|
2094
|
+
declare(node, pattern, value, hoisted = false) {
|
|
2095
|
+
const bindings = scopeBindings(enclosingScope(node, hoisted));
|
|
2096
|
+
if (pattern.type === "Identifier") {
|
|
2097
|
+
if (hoisted && value == null && bindings.has(pattern.name)) return;
|
|
2098
|
+
if (node.type === "FunctionDeclaration" && bindings.get(pattern.name)?.initializedVariable) return;
|
|
2099
|
+
bindings.set(pattern.name, {
|
|
2100
|
+
value,
|
|
2101
|
+
initializedVariable: node.type === "VariableDeclarator" && value != null
|
|
2102
|
+
});
|
|
2103
|
+
} else bindPattern(pattern, bindings);
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
|
|
2108
|
+
if (expr.type !== "Identifier") return expr;
|
|
2109
|
+
const binding = bindings.lookup(expr, expr.name);
|
|
2110
|
+
if (binding == null || binding.value == null || seen.has(binding)) return null;
|
|
2111
|
+
seen.add(binding);
|
|
2112
|
+
return resolveBindingValue(binding.value, bindings, seen);
|
|
2113
|
+
};
|
|
2114
|
+
const resolveListenerReference = (expr, bindings) => {
|
|
2115
|
+
const target = resolveBindingValue(expr, bindings);
|
|
2116
|
+
if (target == null) return null;
|
|
2117
|
+
if (isFunctionLike(target)) return target;
|
|
2118
|
+
if (target.type !== "MemberExpression") return null;
|
|
2119
|
+
const object = resolveBindingValue(target.object, bindings);
|
|
2120
|
+
if (object?.type !== "ObjectExpression") return null;
|
|
2121
|
+
const propertyName = getMemberPropertyName(target);
|
|
2122
|
+
if (propertyName == null) return null;
|
|
2123
|
+
for (const prop of object.properties) {
|
|
2124
|
+
if (prop.type !== "Property") continue;
|
|
2125
|
+
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;
|
|
2126
|
+
const value = resolveBindingValue(prop.value, bindings);
|
|
2127
|
+
if (value != null && isFunctionLike(value)) return value;
|
|
2128
|
+
}
|
|
2129
|
+
return null;
|
|
2130
|
+
};
|
|
2015
2131
|
function escapeRegExp(value) {
|
|
2016
2132
|
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2017
2133
|
}
|
|
@@ -2177,11 +2293,52 @@ function collectDeliveryScanCode(sourceCode, root, used, visited) {
|
|
|
2177
2293
|
return result;
|
|
2178
2294
|
}).join("\n");
|
|
2179
2295
|
}
|
|
2180
|
-
|
|
2296
|
+
function unwrapArgument(node) {
|
|
2297
|
+
while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
|
|
2298
|
+
return node;
|
|
2299
|
+
}
|
|
2300
|
+
function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
|
|
2301
|
+
let indices = visited.get(listener);
|
|
2302
|
+
if (indices?.has(contextIndex)) return false;
|
|
2303
|
+
if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
|
|
2304
|
+
indices.add(contextIndex);
|
|
2305
|
+
if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
|
|
2306
|
+
const param = unwrapContextParam(listener.params[contextIndex]);
|
|
2307
|
+
if (param?.type !== "Identifier") return false;
|
|
2308
|
+
const contextBinding = bindings.lookup(listener, param.name);
|
|
2309
|
+
const reachable = [];
|
|
2310
|
+
collectReachableStatements(listener.body, reachable);
|
|
2311
|
+
for (const call of calls) {
|
|
2312
|
+
let child = call;
|
|
2313
|
+
let owner = getParent(child);
|
|
2314
|
+
while (owner != null && !isFunctionLike(owner)) {
|
|
2315
|
+
const field = owner;
|
|
2316
|
+
if ((field.type === "PropertyDefinition" || field.type === "AccessorProperty") && !field.static && field.value === child) break;
|
|
2317
|
+
child = owner;
|
|
2318
|
+
owner = getParent(owner);
|
|
2319
|
+
}
|
|
2320
|
+
if (owner !== listener) continue;
|
|
2321
|
+
const [callStart, callEnd] = getRange(call);
|
|
2322
|
+
if (!reachable.some((statement) => {
|
|
2323
|
+
const [start, end] = getRange(statement);
|
|
2324
|
+
return start <= callStart && callEnd <= end;
|
|
2325
|
+
})) continue;
|
|
2326
|
+
const helper = resolveListenerReference(call.callee, bindings);
|
|
2327
|
+
if (helper == null || "generator" in helper && helper.generator) continue;
|
|
2328
|
+
for (const [index, argument] of call.arguments.entries()) {
|
|
2329
|
+
if (argument.type === "SpreadElement") break;
|
|
2330
|
+
const arg = unwrapArgument(argument);
|
|
2331
|
+
if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
|
|
2332
|
+
if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
return false;
|
|
2336
|
+
}
|
|
2337
|
+
const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
|
|
2181
2338
|
const used = computeUsedFunctions(listener.body);
|
|
2182
2339
|
const code = stripCommentsAndStrings(collectDeliveryScanCode(sourceCode, listener.body, used, /* @__PURE__ */ new Set()));
|
|
2183
2340
|
const aliases = /* @__PURE__ */ new Set();
|
|
2184
|
-
const contextParam = unwrapContextParam(listener.params[
|
|
2341
|
+
const contextParam = unwrapContextParam(listener.params[contextIndex]);
|
|
2185
2342
|
const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
|
|
2186
2343
|
if (contextParam?.type === "ObjectPattern") for (const prop of contextParam.properties) {
|
|
2187
2344
|
if (!isNode(prop) || prop.type !== "Property") continue;
|
|
@@ -2209,14 +2366,48 @@ const listenerCallsDeliveryMethod = (sourceCode, listener) => {
|
|
|
2209
2366
|
};
|
|
2210
2367
|
function createRule(buildReport) {
|
|
2211
2368
|
return (context) => {
|
|
2369
|
+
const federationTracker = trackFederationVariables();
|
|
2370
|
+
const bindings = createBindingIndex();
|
|
2371
|
+
const pendingCalls = [];
|
|
2212
2372
|
const sourceCode = context.sourceCode;
|
|
2213
|
-
|
|
2214
|
-
if (
|
|
2373
|
+
const inspectCall = (node) => {
|
|
2374
|
+
if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
|
|
2375
|
+
if (!isChainedFromOutboxListeners(node.callee.object, federationTracker)) return;
|
|
2376
|
+
const listener = node.arguments[1];
|
|
2377
|
+
const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
|
|
2378
|
+
if (resolvedListener == null) return;
|
|
2379
|
+
if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
|
|
2215
2380
|
context.report({
|
|
2216
|
-
node:
|
|
2381
|
+
node: resolvedListener,
|
|
2217
2382
|
...buildReport
|
|
2218
2383
|
});
|
|
2219
|
-
}
|
|
2384
|
+
};
|
|
2385
|
+
return {
|
|
2386
|
+
VariableDeclarator(node) {
|
|
2387
|
+
federationTracker.VariableDeclarator(node);
|
|
2388
|
+
const declaration = getParent(node);
|
|
2389
|
+
bindings.declare(node, node.id, node.init, declaration?.kind === "var");
|
|
2390
|
+
},
|
|
2391
|
+
FunctionDeclaration(node) {
|
|
2392
|
+
if (node.id == null) return;
|
|
2393
|
+
const parent = getParent(node);
|
|
2394
|
+
const owner = parent == null ? null : getParent(parent);
|
|
2395
|
+
const inVarBody = parent?.type === "BlockStatement" && owner != null && (owner.type === "StaticBlock" || isFunctionLike(owner) && owner.body === parent);
|
|
2396
|
+
bindings.declare(node, node.id, node, inVarBody);
|
|
2397
|
+
},
|
|
2398
|
+
ImportDeclaration(node) {
|
|
2399
|
+
for (const specifier of node.specifiers) bindings.declare(node, specifier.local, null);
|
|
2400
|
+
},
|
|
2401
|
+
ClassDeclaration(node) {
|
|
2402
|
+
if (node.id != null) bindings.declare(node, node.id, null);
|
|
2403
|
+
},
|
|
2404
|
+
CallExpression(node) {
|
|
2405
|
+
pendingCalls.push(node);
|
|
2406
|
+
},
|
|
2407
|
+
"Program:exit"() {
|
|
2408
|
+
for (const node of pendingCalls) inspectCall(node);
|
|
2409
|
+
}
|
|
2410
|
+
};
|
|
2220
2411
|
};
|
|
2221
2412
|
}
|
|
2222
2413
|
const eslint = {
|
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-BOTBsL2X.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-
|
|
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-CCxxKD7J.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.
|
|
3
|
+
"version": "2.4.0-dev.2157+b76e5888",
|
|
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.
|
|
54
|
+
"@fedify/fedify": "^2.4.0-dev.2157+b76e5888"
|
|
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.
|
|
74
|
+
"@fedify/vocab": "2.4.0-dev.2157+b76e5888"
|
|
75
75
|
},
|
|
76
76
|
"scripts": {
|
|
77
77
|
"build:self": "tsdown",
|