@fedify/lint 2.2.14-dev.2082 → 2.2.15-dev.2196

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.2.14-dev.2082+79cd52e9",
3
+ "version": "2.2.15-dev.2196+10201be2",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts"
package/dist/index.cjs CHANGED
@@ -29,7 +29,7 @@ let _typescript_eslint_parser = require("@typescript-eslint/parser");
29
29
  _typescript_eslint_parser = __toESM(_typescript_eslint_parser);
30
30
  //#region deno.json
31
31
  var name = "@fedify/lint";
32
- var version = "2.2.14-dev.2082+79cd52e9";
32
+ var version = "2.2.15-dev.2196+10201be2";
33
33
  //#endregion
34
34
  //#region src/lib/const.ts
35
35
  /**
@@ -830,6 +830,37 @@ function createBindingIndex() {
830
830
  }
831
831
  };
832
832
  }
833
+ const position = (node) => node.range?.[0] ?? node.start ?? -1;
834
+ const endPosition = (node) => node.range?.[1] ?? node.end ?? -1;
835
+ const enclosingFunction = (node) => {
836
+ for (let parent = getParent(node); parent != null; parent = getParent(parent)) if (isFunctionLike(parent)) return parent;
837
+ return null;
838
+ };
839
+ const isInside = (node, ancestor) => {
840
+ for (let current = node; current != null; current = getParent(current)) if (current === ancestor) return true;
841
+ return false;
842
+ };
843
+ const isDirectStatement = (node, fn) => {
844
+ let current = node;
845
+ while (getParent(current) !== fn.body) {
846
+ const parent = getParent(current);
847
+ if (parent == null || ![
848
+ "AwaitExpression",
849
+ "ExpressionStatement",
850
+ "ReturnStatement",
851
+ "VariableDeclarator",
852
+ "VariableDeclaration",
853
+ "BlockStatement"
854
+ ].includes(parent.type)) return false;
855
+ current = parent;
856
+ }
857
+ return [
858
+ "ExpressionStatement",
859
+ "ReturnStatement",
860
+ "VariableDeclaration",
861
+ "BlockStatement"
862
+ ].includes(current.type);
863
+ };
833
864
  const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
834
865
  if (expr.type !== "Identifier") return expr;
835
866
  const binding = bindings.lookup(expr, expr.name);
@@ -858,12 +889,45 @@ function unwrapArgument(node) {
858
889
  while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
859
890
  return node;
860
891
  }
861
- function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
892
+ function ignoredLocalSetupFunctions(listener, bindings, calls, assignments) {
893
+ const ignored = [];
894
+ const directCalls = calls.filter((call) => enclosingFunction(call) === listener && isDirectStatement(call, listener));
895
+ for (const assignment of assignments) {
896
+ if (assignment.type !== "AssignmentExpression" || assignment.operator !== "=" || assignment.left.type !== "MemberExpression" || !isFunction(assignment.right)) continue;
897
+ const setup = enclosingFunction(assignment);
898
+ if (setup == null || setup === listener || !isInside(setup, listener)) continue;
899
+ const setupParent = getParent(setup);
900
+ const namedLocalSetup = enclosingFunction(setup) === listener && (setup.type === "FunctionDeclaration" || setupParent?.type === "VariableDeclarator" && setupParent.init === setup && setupParent.id.type === "Identifier");
901
+ const passedToCall = calls.some((call) => call.arguments.some((argument) => argument.type !== "SpreadElement" && resolveListenerReference(argument, bindings) === setup));
902
+ if (!namedLocalSetup || passedToCall) continue;
903
+ const installed = assignment.right;
904
+ if (!isDirectStatement(assignment, setup) || assignment.left.object.type !== "Identifier") {
905
+ ignored.push(installed);
906
+ continue;
907
+ }
908
+ const property = getMemberPropertyName(assignment.left);
909
+ if (property == null) continue;
910
+ const setupCalls = directCalls.filter((call) => resolveListenerReference(call.callee, bindings) === setup);
911
+ if (setupCalls.length === 0) {
912
+ const called = calls.some((call) => resolveListenerReference(call.callee, bindings) === setup);
913
+ ignored.push(called ? assignment.right : setup);
914
+ continue;
915
+ }
916
+ const objectBinding = bindings.lookup(assignment.left.object, assignment.left.object.name);
917
+ const deliveryCalls = directCalls.filter((call) => {
918
+ const callee = call.callee;
919
+ return callee.type === "MemberExpression" && callee.object.type === "Identifier" && getMemberPropertyName(callee) === property && bindings.lookup(callee.object, callee.object.name) === objectBinding;
920
+ });
921
+ if (!(objectBinding != null && setupCalls.some((setupCall) => deliveryCalls.some((deliveryCall) => position(setupCall) >= 0 && position(setupCall) < position(deliveryCall))))) ignored.push(installed);
922
+ }
923
+ return ignored;
924
+ }
925
+ function functionCallsDelivery(sourceCode, listener, bindings, calls, assignments, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
862
926
  let indices = visited.get(listener);
863
927
  if (indices?.has(contextIndex)) return false;
864
928
  if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
865
929
  indices.add(contextIndex);
866
- if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
930
+ if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex, ignoredLocalSetupFunctions(listener, bindings, calls, assignments))) return true;
867
931
  const param = unwrapContextParam(listener.params[contextIndex]);
868
932
  if (param?.type !== "Identifier") return false;
869
933
  const contextBinding = bindings.lookup(listener, param.name);
@@ -883,13 +947,21 @@ function functionCallsDelivery(sourceCode, listener, bindings, calls, contextInd
883
947
  if (argument.type === "SpreadElement") break;
884
948
  const arg = unwrapArgument(argument);
885
949
  if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
886
- if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
950
+ if (functionCallsDelivery(sourceCode, helper, bindings, calls, assignments, index, visited)) return true;
887
951
  }
888
952
  }
889
953
  return false;
890
954
  }
891
- const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
892
- const code = stripCommentsAndStrings(sourceCode.getText(listener));
955
+ const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0, ignored = []) => {
956
+ let text = sourceCode.getText(listener);
957
+ const start = position(listener);
958
+ for (const fn of ignored) {
959
+ const from = position(fn) - start;
960
+ const to = endPosition(fn) - start;
961
+ if (from < 0 || to > text.length || to < from) continue;
962
+ text = text.slice(0, from) + " ".repeat(to - from) + text.slice(to);
963
+ }
964
+ const code = stripCommentsAndStrings(text);
893
965
  const aliases = /* @__PURE__ */ new Set();
894
966
  const contextParam = unwrapContextParam(listener.params[contextIndex]);
895
967
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
@@ -922,6 +994,7 @@ function createRule(buildReport) {
922
994
  const federationTracker = trackFederationVariables();
923
995
  const bindings = createBindingIndex();
924
996
  const pendingCalls = [];
997
+ const pendingAssignments = [];
925
998
  const sourceCode = context.sourceCode;
926
999
  const inspectCall = (node) => {
927
1000
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
@@ -929,7 +1002,7 @@ function createRule(buildReport) {
929
1002
  const listener = node.arguments[1];
930
1003
  const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
931
1004
  if (resolvedListener == null) return;
932
- if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
1005
+ if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls, pendingAssignments)) return;
933
1006
  context.report({
934
1007
  node: resolvedListener,
935
1008
  ...buildReport
@@ -957,6 +1030,9 @@ function createRule(buildReport) {
957
1030
  CallExpression(node) {
958
1031
  pendingCalls.push(node);
959
1032
  },
1033
+ AssignmentExpression(node) {
1034
+ pendingAssignments.push(node);
1035
+ },
960
1036
  "Program:exit"() {
961
1037
  for (const node of pendingCalls) inspectCall(node);
962
1038
  }
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { always, every, fromEntries, head, isEmpty, isObject, keys, map, negate,
2
2
  import parser from "@typescript-eslint/parser";
3
3
  //#region deno.json
4
4
  var name = "@fedify/lint";
5
- var version = "2.2.14-dev.2082+79cd52e9";
5
+ var version = "2.2.15-dev.2196+10201be2";
6
6
  //#endregion
7
7
  //#region src/lib/const.ts
8
8
  /**
@@ -803,6 +803,37 @@ function createBindingIndex() {
803
803
  }
804
804
  };
805
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
+ };
806
837
  const resolveBindingValue = (expr, bindings, seen = /* @__PURE__ */ new Set()) => {
807
838
  if (expr.type !== "Identifier") return expr;
808
839
  const binding = bindings.lookup(expr, expr.name);
@@ -831,12 +862,45 @@ function unwrapArgument(node) {
831
862
  while (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSNonNullExpression") node = node.expression;
832
863
  return node;
833
864
  }
834
- function functionCallsDelivery(sourceCode, listener, bindings, calls, contextIndex = 0, visited = /* @__PURE__ */ new Map()) {
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()) {
835
899
  let indices = visited.get(listener);
836
900
  if (indices?.has(contextIndex)) return false;
837
901
  if (indices == null) visited.set(listener, indices = /* @__PURE__ */ new Set());
838
902
  indices.add(contextIndex);
839
- if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex)) return true;
903
+ if (listenerCallsDeliveryMethod(sourceCode, listener, contextIndex, ignoredLocalSetupFunctions(listener, bindings, calls, assignments))) return true;
840
904
  const param = unwrapContextParam(listener.params[contextIndex]);
841
905
  if (param?.type !== "Identifier") return false;
842
906
  const contextBinding = bindings.lookup(listener, param.name);
@@ -856,13 +920,21 @@ function functionCallsDelivery(sourceCode, listener, bindings, calls, contextInd
856
920
  if (argument.type === "SpreadElement") break;
857
921
  const arg = unwrapArgument(argument);
858
922
  if (arg.type !== "Identifier" || arg.name !== param.name || bindings.lookup(arg, arg.name) !== contextBinding || helper.params[index]?.type === "RestElement") continue;
859
- if (functionCallsDelivery(sourceCode, helper, bindings, calls, index, visited)) return true;
923
+ if (functionCallsDelivery(sourceCode, helper, bindings, calls, assignments, index, visited)) return true;
860
924
  }
861
925
  }
862
926
  return false;
863
927
  }
864
- const listenerCallsDeliveryMethod = (sourceCode, listener, contextIndex = 0) => {
865
- const code = stripCommentsAndStrings(sourceCode.getText(listener));
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);
866
938
  const aliases = /* @__PURE__ */ new Set();
867
939
  const contextParam = unwrapContextParam(listener.params[contextIndex]);
868
940
  const contextName = contextParam?.type === "Identifier" ? contextParam.name : null;
@@ -895,6 +967,7 @@ function createRule(buildReport) {
895
967
  const federationTracker = trackFederationVariables();
896
968
  const bindings = createBindingIndex();
897
969
  const pendingCalls = [];
970
+ const pendingAssignments = [];
898
971
  const sourceCode = context.sourceCode;
899
972
  const inspectCall = (node) => {
900
973
  if (!hasMemberExpressionCallee(node) || !hasIdentifierProperty(node) || !hasMethodName("on")(node) || node.arguments.length < 2) return;
@@ -902,7 +975,7 @@ function createRule(buildReport) {
902
975
  const listener = node.arguments[1];
903
976
  const resolvedListener = isNode(listener) && isFunction(listener) ? listener : isNode(listener) ? resolveListenerReference(listener, bindings) : null;
904
977
  if (resolvedListener == null) return;
905
- if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls)) return;
978
+ if (functionCallsDelivery(sourceCode, resolvedListener, bindings, pendingCalls, pendingAssignments)) return;
906
979
  context.report({
907
980
  node: resolvedListener,
908
981
  ...buildReport
@@ -930,6 +1003,9 @@ function createRule(buildReport) {
930
1003
  CallExpression(node) {
931
1004
  pendingCalls.push(node);
932
1005
  },
1006
+ AssignmentExpression(node) {
1007
+ pendingAssignments.push(node);
1008
+ },
933
1009
  "Program:exit"() {
934
1010
  for (const node of pendingCalls) inspectCall(node);
935
1011
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/lint",
3
- "version": "2.2.14-dev.2082+79cd52e9",
3
+ "version": "2.2.15-dev.2196+10201be2",
4
4
  "description": "Fedify linting rules and plugins",
5
5
  "keywords": [
6
6
  "Fedify",
@@ -47,7 +47,7 @@
47
47
  ],
48
48
  "peerDependencies": {
49
49
  "eslint": ">=9.0.0",
50
- "@fedify/fedify": "^2.2.14-dev.2082+79cd52e9"
50
+ "@fedify/fedify": "^2.2.15-dev.2196+10201be2"
51
51
  },
52
52
  "peerDependenciesMeta": {
53
53
  "eslint": {