eslint-plugin-effector 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ //#region \0rolldown/runtime.js
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -18,18 +19,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
18
19
  value: mod,
19
20
  enumerable: true
20
21
  }) : target, mod));
22
+ //#endregion
21
23
  let _typescript_eslint_utils = require("@typescript-eslint/utils");
22
24
  let _typescript_eslint_type_utils = require("@typescript-eslint/type-utils");
23
25
  let typescript = require("typescript");
24
26
  let esquery = require("esquery");
25
27
  esquery = __toESM(esquery);
28
+ //#region package.json
26
29
  var name = "eslint-plugin-effector";
27
- var version = "0.17.0";
28
- const createRule = _typescript_eslint_utils.ESLintUtils.RuleCreator((name$1) => `https://eslint.effector.dev/rules/${name$1}`);
30
+ var version = "0.18.0";
31
+ //#endregion
32
+ //#region src/shared/create.ts
33
+ const createRule = _typescript_eslint_utils.ESLintUtils.RuleCreator((name) => `https://eslint.effector.dev/rules/${name}`);
34
+ //#endregion
35
+ //#region src/shared/is.ts
29
36
  const check = (symbol, types, from) => {
30
- const name$1 = symbol.getName();
37
+ const name = symbol.getName();
31
38
  const declarations = symbol.declarations ?? [];
32
- return types.includes(name$1) && declarations.map((decl) => decl.getSourceFile().fileName).some((fname) => fname.includes("node_modules") && fname.includes(from));
39
+ return types.includes(name) && declarations.map((decl) => decl.getSourceFile().fileName).some((fname) => fname.includes("node_modules") && fname.includes(from));
33
40
  };
34
41
  const isType = {
35
42
  store: (type, program) => (0, _typescript_eslint_type_utils.typeMatchesSpecifier)(type, {
@@ -56,7 +63,8 @@ const isType = {
56
63
  "StoreWritable",
57
64
  "Event",
58
65
  "EventCallable",
59
- "Effect"
66
+ "Effect",
67
+ "Domain"
60
68
  ]
61
69
  }, program);
62
70
  },
@@ -94,6 +102,8 @@ const isType = {
94
102
  }, program);
95
103
  }
96
104
  };
105
+ //#endregion
106
+ //#region src/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.ts
97
107
  var enforce_effect_naming_convention_default = createRule({
98
108
  name: "enforce-effect-naming-convention",
99
109
  meta: {
@@ -136,6 +146,8 @@ var enforce_effect_naming_convention_default = createRule({
136
146
  }
137
147
  });
138
148
  const FxRegex = /Fx$/;
149
+ //#endregion
150
+ //#region src/rules/enforce-gate-naming-convention/enforce-gate-naming-convention.ts
139
151
  var enforce_gate_naming_convention_default = createRule({
140
152
  name: "enforce-gate-naming-convention",
141
153
  meta: {
@@ -178,6 +190,8 @@ var enforce_gate_naming_convention_default = createRule({
178
190
  }
179
191
  });
180
192
  const GateRegex = /^[^A-Z]/;
193
+ //#endregion
194
+ //#region src/rules/enforce-store-naming-convention/enforce-store-naming-convention.ts
181
195
  var enforce_store_naming_convention_default = createRule({
182
196
  name: "enforce-store-naming-convention",
183
197
  meta: {
@@ -229,11 +243,15 @@ var enforce_store_naming_convention_default = createRule({
229
243
  });
230
244
  const PrefixRegex = /^[^$]/;
231
245
  const PostfixRegex = /[^$]$/;
246
+ //#endregion
247
+ //#region src/shared/package.ts
232
248
  const PACKAGE_NAME$1 = {
233
249
  core: /^effector(?:\u002Fcompat)?$/,
234
250
  react: /^effector-react$/,
235
251
  storage: /^@?effector-storage(\u002F[\w-]+)*$/
236
252
  };
253
+ //#endregion
254
+ //#region src/rules/keep-options-order/keep-options-order.ts
237
255
  var keep_options_order_default = createRule({
238
256
  name: "keep-options-order",
239
257
  meta: {
@@ -306,6 +324,8 @@ const isCorrectOrder = (current) => {
306
324
  }
307
325
  return true;
308
326
  };
327
+ //#endregion
328
+ //#region src/shared/name.ts
309
329
  function functionToName(node) {
310
330
  if (node.id) return node.id;
311
331
  if (node.parent.type === _typescript_eslint_utils.AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return node.parent.id;
@@ -315,6 +335,8 @@ function functionToName(node) {
315
335
  return null;
316
336
  }
317
337
  const nameOf = { function: functionToName };
338
+ //#endregion
339
+ //#region src/rules/mandatory-scope-binding/mandatory-scope-binding.ts
318
340
  var mandatory_scope_binding_default = createRule({
319
341
  name: "mandatory-scope-binding",
320
342
  meta: {
@@ -334,14 +356,14 @@ var mandatory_scope_binding_default = createRule({
334
356
  return {
335
357
  [`FunctionDeclaration, FunctionExpression, ArrowFunctionExpression`]: (node) => {
336
358
  if (stack.render.at(-1) ?? false) return void stack.render.push(true);
337
- const name$1 = nameOf.function(node);
338
- if (name$1 && UseRegex.test(name$1.name)) return void stack.render.push(true);
359
+ const name = nameOf.function(node);
360
+ if (name && UseRegex$1.test(name.name)) return void stack.render.push(true);
339
361
  const tsnode = services.esTreeNodeToTSNodeMap.get(node);
340
362
  const signature = checker.getSignatureFromDeclaration(tsnode);
341
363
  const returnType = signature ? checker.getReturnTypeOfSignature(signature) : checker.getVoidType();
342
- if (isType.jsx(returnType, services.program)) return void stack.render.push(true);
343
- const inferred = (0, typescript.isExpression)(tsnode) ? (0, _typescript_eslint_type_utils.getContextualType)(checker, tsnode) : void 0;
344
- if (inferred ? isType.component(inferred, services.program) : false) return void stack.render.push(true);
364
+ if (returnType.isUnion() ? returnType.types.some((type) => isType.jsx(type, services.program)) : isType.jsx(returnType, services.program)) return void stack.render.push(true);
365
+ const inferred = (0, typescript.isExpression)(tsnode) && (0, _typescript_eslint_type_utils.getContextualType)(checker, tsnode) || checker.getUnknownType();
366
+ if (inferred.isUnion() ? inferred.types.some((type) => isType.component(type, services.program)) : isType.component(inferred, services.program)) return void stack.render.push(true);
345
367
  stack.render.push(false);
346
368
  },
347
369
  [`:matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression):exit`]: () => void stack.render.pop(),
@@ -375,9 +397,13 @@ var mandatory_scope_binding_default = createRule({
375
397
  };
376
398
  }
377
399
  });
378
- const UseRegex = /^use[A-Z0-9].*$/;
400
+ const UseRegex$1 = /^use[A-Z0-9].*$/;
401
+ //#endregion
402
+ //#region src/shared/locate.ts
379
403
  const property = (key, node) => node.properties.find((prop) => prop.type == _typescript_eslint_utils.AST_NODE_TYPES.Property && prop.key.type === _typescript_eslint_utils.AST_NODE_TYPES.Identifier && prop.key.name === key);
380
404
  const locate = { property };
405
+ //#endregion
406
+ //#region src/rules/no-ambiguity-target/no-ambiguity-target.ts
381
407
  var no_ambiguity_target_default = createRule({
382
408
  name: "no-ambiguity-target",
383
409
  meta: {
@@ -417,6 +443,8 @@ var no_ambiguity_target_default = createRule({
417
443
  }
418
444
  });
419
445
  const selector$10 = { method: `ImportSpecifier[imported.name=/(sample|guard)/]` };
446
+ //#endregion
447
+ //#region src/rules/no-domain-unit-creators/no-domain-unit-creators.ts
420
448
  var no_domain_unit_creators_default = createRule({
421
449
  name: "no-domain-unit-creators",
422
450
  meta: {
@@ -429,16 +457,16 @@ var no_domain_unit_creators_default = createRule({
429
457
  create: (context) => {
430
458
  const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
431
459
  return { [`CallExpression:has(> ${selector$9.member})`]: (node) => {
432
- const name$1 = node.callee.property.name;
433
- if (!METHODS.has(name$1)) return;
460
+ const name = node.callee.property.name;
461
+ if (!METHODS.has(name)) return;
434
462
  const type = services.getTypeAtLocation(node.callee.object);
435
463
  if (!isType.domain(type, services.program)) return;
436
- const factory = ALIAS_MAP.get(name$1) ?? name$1;
464
+ const factory = ALIAS_MAP.get(name) ?? name;
437
465
  context.report({
438
466
  node,
439
467
  messageId: "avoid",
440
468
  data: {
441
- method: name$1,
469
+ method: name,
442
470
  factory
443
471
  }
444
472
  });
@@ -448,6 +476,8 @@ var no_domain_unit_creators_default = createRule({
448
476
  const ALIAS_MAP = (/* @__PURE__ */ new Map()).set("event", "createEvent").set("store", "createStore").set("effect", "createEffect").set("domain", "createDomain");
449
477
  const METHODS = new Set([...ALIAS_MAP.values(), ...ALIAS_MAP.keys()]);
450
478
  const selector$9 = { member: `MemberExpression.callee[property.type="Identifier"]` };
479
+ //#endregion
480
+ //#region src/rules/no-duplicate-clock-or-source-array-values/no-duplicate-clock-or-source-array-values.ts
451
481
  var no_duplicate_clock_or_source_array_values_default = createRule({
452
482
  name: "no-duplicate-clock-or-source-array-values",
453
483
  meta: {
@@ -470,19 +500,19 @@ var no_duplicate_clock_or_source_array_values_default = createRule({
470
500
  for (const entry of entries) {
471
501
  const root = traverseToRoot$1(entry);
472
502
  if (!root) continue;
473
- const name$1 = [root.node.name, ...root.path].join(".");
474
- if (seen.has(name$1)) report(entry, name$1, field);
475
- else seen.set(name$1, entry);
503
+ const name = [root.node.name, ...root.path].join(".");
504
+ if (seen.has(name)) report(entry, name, field);
505
+ else seen.set(name, entry);
476
506
  }
477
507
  };
478
- const report = (node, name$1, field) => {
508
+ const report = (node, name, field) => {
479
509
  const data = {
480
510
  field,
481
- unit: name$1
511
+ unit: name
482
512
  };
483
513
  const suggestion = {
484
514
  messageId: "remove",
485
- data: { unit: name$1 },
515
+ data: { unit: name },
486
516
  fix: function* (fixer) {
487
517
  yield fixer.remove(node);
488
518
  const before = context.sourceCode.getTokenBefore(node);
@@ -522,6 +552,8 @@ function traverseToRoot$1(node, path = []) {
522
552
  if (node.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && node.property.type === _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return traverseToRoot$1(node.object, [node.property.name, ...path]);
523
553
  return null;
524
554
  }
555
+ //#endregion
556
+ //#region src/rules/no-duplicate-on/no-duplicate-on.ts
525
557
  var no_duplicate_on_default = createRule({
526
558
  name: "no-duplicate-on",
527
559
  meta: {
@@ -540,12 +572,12 @@ var no_duplicate_on_default = createRule({
540
572
  const arg = node.arguments[0];
541
573
  if (!arg || arg.type === _typescript_eslint_utils.AST_NODE_TYPES.SpreadElement) return;
542
574
  const units = arg.type === _typescript_eslint_utils.AST_NODE_TYPES.ArrayExpression ? arg.elements.filter((item) => item !== null && item.type !== _typescript_eslint_utils.AST_NODE_TYPES.SpreadElement) : [arg];
543
- const scope$1 = context.sourceCode.getScope(node);
544
- const store = identify("store", node.callee.object, scope$1);
575
+ const scope = context.sourceCode.getScope(node);
576
+ const store = identify("store", node.callee.object, scope);
545
577
  if (!store) return;
546
578
  const set = map.get(store.id) ?? /* @__PURE__ */ new Set();
547
579
  for (const unit of units) {
548
- const instance = identify("unit", unit, scope$1);
580
+ const instance = identify("unit", unit, scope);
549
581
  if (!instance) continue;
550
582
  if (set.has(instance.id)) {
551
583
  const data = {
@@ -606,16 +638,18 @@ function findSuitableRoot(type, node) {
606
638
  };
607
639
  return null;
608
640
  }
609
- function identify(type, node, scope$1) {
641
+ function identify(type, node, scope) {
610
642
  const root = findSuitableRoot(type, node);
611
643
  if (!root) return null;
612
- const variable = _typescript_eslint_utils.ASTUtils.findVariable(scope$1, root.node);
644
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(scope, root.node);
613
645
  if (!variable) return null;
614
646
  return {
615
647
  id: `${variable.$id}+${root.path.join(".")}`,
616
648
  name: [variable.name, ...root.path].join(".")
617
649
  };
618
650
  }
651
+ //#endregion
652
+ //#region src/rules/no-forward/no-forward.ts
619
653
  var no_forward_default = createRule({
620
654
  name: "no-forward",
621
655
  meta: {
@@ -645,11 +679,11 @@ var no_forward_default = createRule({
645
679
  config.clock = locate.property("from", arg)?.value;
646
680
  config.target = locate.property("to", arg)?.value;
647
681
  if (config.target) {
648
- const [call] = esquery.default.match(config.target, query$2.prepend, { visitorKeys }).map((node$1) => node$1).filter((node$1) => node$1 === config.target);
682
+ const [call] = esquery.default.match(config.target, query$2.prepend, { visitorKeys }).map((node) => node).filter((node) => node === config.target);
649
683
  if (call) [config.target, config.fn] = [call.callee.object, call.arguments[0]];
650
684
  }
651
685
  if (config.clock && !config.fn) {
652
- const [call] = esquery.default.match(config.clock, query$2.map, { visitorKeys }).map((node$1) => node$1).filter((node$1) => node$1 === config.clock);
686
+ const [call] = esquery.default.match(config.clock, query$2.map, { visitorKeys }).map((node) => node).filter((node) => node === config.clock);
653
687
  if (call) [config.clock, config.fn] = [call.callee.object, call.arguments[0]];
654
688
  }
655
689
  const code = [
@@ -683,6 +717,8 @@ const query$2 = {
683
717
  map: esquery.default.parse("CallExpression[arguments.length=1]:has(> :first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='map']))"),
684
718
  prepend: esquery.default.parse("CallExpression[arguments.length=1]:has(> :first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='prepend']))")
685
719
  };
720
+ //#endregion
721
+ //#region src/rules/no-getState/no-getState.ts
686
722
  var no_getState_default = createRule({
687
723
  name: "no-getState",
688
724
  meta: {
@@ -700,11 +736,11 @@ var no_getState_default = createRule({
700
736
  return { [`CallExpression[callee.type="MemberExpression"][callee.property.name="getState"]`]: (node) => {
701
737
  const type = services.getTypeAtLocation(node.callee.object);
702
738
  if (!isType.store(type, services.program)) return;
703
- const name$1 = toName$1(node.callee.object);
704
- if (name$1) context.report({
739
+ const name = toName$1(node.callee.object);
740
+ if (name) context.report({
705
741
  node,
706
742
  messageId: "named",
707
- data: { name: name$1 }
743
+ data: { name }
708
744
  });
709
745
  else context.report({
710
746
  node,
@@ -718,6 +754,8 @@ const toName$1 = (node) => {
718
754
  if (node.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && !node.computed) return node.property.name;
719
755
  return null;
720
756
  };
757
+ //#endregion
758
+ //#region src/rules/no-guard/no-guard.ts
721
759
  var no_guard_default = createRule({
722
760
  name: "no-guard",
723
761
  meta: {
@@ -761,7 +799,7 @@ var no_guard_default = createRule({
761
799
  ]) config[key] = locate.property(key, arg)?.value;
762
800
  } else return;
763
801
  if (config.target) {
764
- const [call] = esquery.default.match(config.target, query$1.prepend, { visitorKeys }).map((node$1) => node$1).filter((node$1) => node$1 === config.target);
802
+ const [call] = esquery.default.match(config.target, query$1.prepend, { visitorKeys }).map((node) => node).filter((node) => node === config.target);
765
803
  if (call) [config.target, config.fn] = [call.callee.object, call.arguments[0]];
766
804
  }
767
805
  const code = [
@@ -793,6 +831,8 @@ const selector$6 = {
793
831
  call: `[callee.type="Identifier"]`
794
832
  };
795
833
  const query$1 = { prepend: esquery.default.parse("CallExpression[arguments.length=1]:has(:first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='prepend']))") };
834
+ //#endregion
835
+ //#region src/rules/no-patronum-debug/no-patronum-debug.ts
796
836
  var no_patronum_debug_default = createRule({
797
837
  name: "no-patronum-debug",
798
838
  meta: {
@@ -811,8 +851,8 @@ var no_patronum_debug_default = createRule({
811
851
  return {
812
852
  [`${`ImportDeclaration[source.value=${PACKAGE_NAME}]`} > ${selector$5.debug}`]: (node) => debugs.add(node.local.name),
813
853
  [`CallExpression:matches(${selector$5.call})`]: (node) => {
814
- const name$1 = toName(node);
815
- if (!debugs.has(name$1)) return;
854
+ const name = toName(node);
855
+ if (!debugs.has(name)) return;
816
856
  context.report({
817
857
  messageId: "unexpected",
818
858
  node: node.callee,
@@ -839,6 +879,184 @@ const toName = (node) => {
839
879
  case _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression: return node.callee.object.name;
840
880
  }
841
881
  };
882
+ //#endregion
883
+ //#region src/rules/no-units-spawn-in-render/no-units-spawn-in-render.ts
884
+ const EFFECTOR_FACTORIES = new Set([
885
+ "createStore",
886
+ "createEvent",
887
+ "createEffect",
888
+ "createDomain",
889
+ "createApi",
890
+ "restore"
891
+ ]);
892
+ const EFFECTOR_OPERATORS = new Set([
893
+ "sample",
894
+ "guard",
895
+ "forward",
896
+ "merge",
897
+ "split",
898
+ "combine",
899
+ "attach"
900
+ ]);
901
+ const REACT_HOOKS_SPEC = {
902
+ from: "package",
903
+ package: "react",
904
+ name: [
905
+ "useState",
906
+ "useEffect",
907
+ "useLayoutEffect",
908
+ "useCallback",
909
+ "useMemo",
910
+ "useRef",
911
+ "useReducer",
912
+ "useImperativeHandle",
913
+ "useDebugValue",
914
+ "useDeferredValue",
915
+ "useTransition",
916
+ "useId",
917
+ "useSyncExternalStore",
918
+ "useInsertionEffect",
919
+ "useContext"
920
+ ]
921
+ };
922
+ const EFFECTOR_FACTORY_SPEC = {
923
+ from: "package",
924
+ package: "effector",
925
+ name: [...EFFECTOR_FACTORIES]
926
+ };
927
+ const EFFECTOR_OPERATOR_SPEC = {
928
+ from: "package",
929
+ package: "effector",
930
+ name: [...EFFECTOR_OPERATORS]
931
+ };
932
+ const EFFECTOR_FACTORIO_SHAPE = [
933
+ "useModel",
934
+ "createModel",
935
+ "Provider",
936
+ "@@unitShape"
937
+ ];
938
+ var no_units_spawn_in_render_default = createRule({
939
+ name: "no-units-spawn-in-render",
940
+ meta: {
941
+ type: "problem",
942
+ docs: { description: "Forbid creating Effector units or calling operators inside React components or hooks." },
943
+ messages: {
944
+ noFactoryInRender: "Creating Effector units with \"{{ name }}\" inside React component or hook is forbidden, since it may cause memory leaks and other bugs.",
945
+ noOperatorInRender: "Using Effector operator \"{{ name }}\" inside React component or hook is forbidden, since it may cause memory leaks and other bugs.",
946
+ noCustomFactoryInRender: "Creating Effector units with \"{{ name }}\" inside React component or hook is forbidden, since it may cause memory leaks and other bugs. If this is a false positive, add \"{{ name }}\" to the allowlist in the detectCustomFactories option."
947
+ },
948
+ schema: [{
949
+ type: "object",
950
+ properties: { detectCustomFactories: { oneOf: [{ type: "boolean" }, {
951
+ type: "object",
952
+ properties: { allowlist: {
953
+ type: "array",
954
+ items: { type: "string" },
955
+ uniqueItems: true
956
+ } },
957
+ required: ["allowlist"],
958
+ additionalProperties: false
959
+ }] } },
960
+ additionalProperties: false
961
+ }]
962
+ },
963
+ defaultOptions: [{ detectCustomFactories: true }],
964
+ create: (context, [options]) => {
965
+ const services = _typescript_eslint_utils.ESLintUtils.getParserServices(context);
966
+ const checker = services.program.getTypeChecker();
967
+ const { detectCustomFactories } = options;
968
+ const allowlist = typeof detectCustomFactories === "object" ? new Set(detectCustomFactories.allowlist) : void 0;
969
+ const stack = { render: [] };
970
+ const effectorImports = /* @__PURE__ */ new Map();
971
+ return {
972
+ [`${`ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`} > ImportSpecifier[imported.type="Identifier"]`]: (node) => {
973
+ const imported = node.imported.name;
974
+ const local = node.local.name;
975
+ if (EFFECTOR_FACTORIES.has(imported)) effectorImports.set(local, "factory");
976
+ else if (EFFECTOR_OPERATORS.has(imported)) effectorImports.set(local, "operator");
977
+ },
978
+ [`FunctionDeclaration, FunctionExpression, ArrowFunctionExpression`]: (node) => {
979
+ if (stack.render.at(-1) ?? false) return void stack.render.push(true);
980
+ const name = nameOf.function(node);
981
+ if (name && UseRegex.test(name.name)) return void stack.render.push(true);
982
+ const tsnode = services.esTreeNodeToTSNodeMap.get(node);
983
+ const signature = checker.getSignatureFromDeclaration(tsnode);
984
+ const returnType = signature ? checker.getReturnTypeOfSignature(signature) : checker.getVoidType();
985
+ if (returnType.isUnion() ? returnType.types.some((type) => isType.jsx(type, services.program)) : isType.jsx(returnType, services.program)) return void stack.render.push(true);
986
+ const inferred = (0, typescript.isExpression)(tsnode) && (0, _typescript_eslint_type_utils.getContextualType)(checker, tsnode) || checker.getUnknownType();
987
+ if (inferred.isUnion() ? inferred.types.some((type) => isType.component(type, services.program)) : isType.component(inferred, services.program)) return void stack.render.push(true);
988
+ stack.render.push(false);
989
+ },
990
+ [`:matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression):exit`]: () => void stack.render.pop(),
991
+ "ClassDeclaration": () => void stack.render.push(false),
992
+ "ClassDeclaration:exit": () => void stack.render.pop(),
993
+ "CallExpression": (node) => {
994
+ if (!(stack.render.at(-1) ?? false)) return;
995
+ const calleeName = getCalleeName(node.callee);
996
+ switch (calleeName ? effectorImports.get(calleeName) : void 0) {
997
+ case "factory": return context.report({
998
+ node,
999
+ messageId: "noFactoryInRender",
1000
+ data: { name: calleeName }
1001
+ });
1002
+ case "operator": return context.report({
1003
+ node,
1004
+ messageId: "noOperatorInRender",
1005
+ data: { name: calleeName }
1006
+ });
1007
+ }
1008
+ if (detectCustomFactories === false) return;
1009
+ const returnType = services.getTypeAtLocation(node);
1010
+ if (!hasEffectorUnitInType({
1011
+ node: services.esTreeNodeToTSNodeMap.get(node),
1012
+ checker,
1013
+ program: services.program
1014
+ }, returnType)) return;
1015
+ const calleeType = services.getTypeAtLocation(node.callee);
1016
+ const displayName = calleeName ?? "<expression>";
1017
+ if ((0, _typescript_eslint_type_utils.typeMatchesSpecifier)(calleeType, REACT_HOOKS_SPEC, services.program)) return;
1018
+ if (isEffectorFactorioHook(node.callee, services.getTypeAtLocation)) return;
1019
+ if ((0, _typescript_eslint_type_utils.typeMatchesSpecifier)(calleeType, EFFECTOR_FACTORY_SPEC, services.program)) return context.report({
1020
+ node,
1021
+ messageId: "noFactoryInRender",
1022
+ data: { name: displayName }
1023
+ });
1024
+ if ((0, _typescript_eslint_type_utils.typeMatchesSpecifier)(calleeType, EFFECTOR_OPERATOR_SPEC, services.program)) return context.report({
1025
+ node,
1026
+ messageId: "noOperatorInRender",
1027
+ data: { name: displayName }
1028
+ });
1029
+ if (allowlist && calleeName && allowlist.has(calleeName)) return;
1030
+ context.report({
1031
+ node,
1032
+ messageId: "noCustomFactoryInRender",
1033
+ data: { name: displayName }
1034
+ });
1035
+ }
1036
+ };
1037
+ }
1038
+ });
1039
+ const UseRegex = /^use[A-Z0-9].*$/;
1040
+ function getCalleeName(callee) {
1041
+ if (callee.type === _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return callee.name;
1042
+ if (callee.type === _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression && callee.property.type === _typescript_eslint_utils.AST_NODE_TYPES.Identifier) return callee.property.name;
1043
+ else return null;
1044
+ }
1045
+ function hasEffectorUnitInType(ctx, type, depth = 3) {
1046
+ if (isType.unit(type, ctx.program)) return true;
1047
+ if (depth <= 0) return false;
1048
+ if (type.isUnion()) return type.types.some((type) => hasEffectorUnitInType(ctx, type, depth));
1049
+ for (const property of type.getProperties()) if (hasEffectorUnitInType(ctx, ctx.checker.getTypeOfSymbolAtLocation(property, ctx.node), depth - 1)) return true;
1050
+ return false;
1051
+ }
1052
+ function isEffectorFactorioHook(callee, getTypeAtLocation) {
1053
+ if (callee.type !== _typescript_eslint_utils.AST_NODE_TYPES.MemberExpression) return false;
1054
+ const objectType = getTypeAtLocation(callee.object);
1055
+ const propertyNames = new Set(objectType.getProperties().map((p) => p.getName()));
1056
+ return EFFECTOR_FACTORIO_SHAPE.every((name) => propertyNames.has(name));
1057
+ }
1058
+ //#endregion
1059
+ //#region src/rules/no-unnecessary-combination/no-unnecessary-combination.ts
842
1060
  var no_unnecessary_combination_default = createRule({
843
1061
  name: "no-unnecessary-combination",
844
1062
  meta: {
@@ -909,6 +1127,8 @@ function isFunction(node, services) {
909
1127
  return checker.getTypeAtLocation(tsnode).getCallSignatures().length > 0;
910
1128
  } else return false;
911
1129
  }
1130
+ //#endregion
1131
+ //#region src/rules/no-unnecessary-duplication/no-unnecessary-duplication.ts
912
1132
  var no_unnecessary_duplication_default = createRule({
913
1133
  name: "no-unnecessary-duplication",
914
1134
  meta: {
@@ -985,6 +1205,8 @@ function compare(clock, source, limit = 5) {
985
1205
  }
986
1206
  return false;
987
1207
  }
1208
+ //#endregion
1209
+ //#region src/rules/no-useless-methods/no-useless-methods.ts
988
1210
  var no_useless_methods_default = createRule({
989
1211
  name: "no-useless-methods",
990
1212
  meta: {
@@ -1035,6 +1257,8 @@ var no_useless_methods_default = createRule({
1035
1257
  });
1036
1258
  const selector$2 = { method: `ImportSpecifier[imported.name=/(sample|guard)/]` };
1037
1259
  const query = { watch: esquery.default.parse("CallExpression:has(> MemberExpression.callee[property.name=watch]:has(> CallExpression.object))") };
1260
+ //#endregion
1261
+ //#region src/rules/no-watch/no-watch.ts
1038
1262
  var no_watch_default = createRule({
1039
1263
  name: "no-watch",
1040
1264
  meta: {
@@ -1056,6 +1280,8 @@ var no_watch_default = createRule({
1056
1280
  } };
1057
1281
  }
1058
1282
  });
1283
+ //#endregion
1284
+ //#region src/rules/prefer-useUnit/prefer-useUnit.ts
1059
1285
  var prefer_useUnit_default = createRule({
1060
1286
  name: "prefer-useUnit",
1061
1287
  meta: {
@@ -1087,6 +1313,8 @@ const selector$1 = {
1087
1313
  useStore: `ImportSpecifier[imported.name=useStore]`,
1088
1314
  useEvent: `ImportSpecifier[imported.name=useEvent]`
1089
1315
  };
1316
+ //#endregion
1317
+ //#region src/rules/require-pickup-in-persist/require-pickup-in-persist.ts
1090
1318
  var require_pickup_in_persist_default = createRule({
1091
1319
  name: "require-pickup-in-persist",
1092
1320
  meta: {
@@ -1116,6 +1344,8 @@ const selector = {
1116
1344
  call: `[callee.type="Identifier"]`,
1117
1345
  config: `[arguments.length=1][arguments.0.type="ObjectExpression"]`
1118
1346
  };
1347
+ //#endregion
1348
+ //#region src/rules/strict-effect-handlers/strict-effect-handlers.ts
1119
1349
  var strict_effect_handlers_default = createRule({
1120
1350
  name: "strict-effect-handlers",
1121
1351
  meta: {
@@ -1143,9 +1373,9 @@ var strict_effect_handlers_default = createRule({
1143
1373
  });
1144
1374
  };
1145
1375
  const exit = (node) => {
1146
- const scope$1 = stack.pop();
1147
- if (!scope$1) return;
1148
- if (scope$1.effect && scope$1.regular) context.report({
1376
+ const scope = stack.pop();
1377
+ if (!scope) return;
1378
+ if (scope.effect && scope.regular) context.report({
1149
1379
  node,
1150
1380
  messageId: "mixed"
1151
1381
  });
@@ -1161,38 +1391,36 @@ var strict_effect_handlers_default = createRule({
1161
1391
  };
1162
1392
  }
1163
1393
  });
1164
- const recommended = {
1165
- "effector/enforce-effect-naming-convention": "error",
1166
- "effector/enforce-store-naming-convention": "error",
1167
- "effector/keep-options-order": "warn",
1168
- "effector/no-ambiguity-target": "warn",
1169
- "effector/no-duplicate-on": "error",
1170
- "effector/no-forward": "error",
1171
- "effector/no-getState": "error",
1172
- "effector/no-guard": "error",
1173
- "effector/no-unnecessary-combination": "warn",
1174
- "effector/no-unnecessary-duplication": "warn",
1175
- "effector/no-useless-methods": "error",
1176
- "effector/no-watch": "warn"
1177
- };
1178
- const patronum = { "effector/no-patronum-debug": "warn" };
1179
- const scope = {
1180
- "effector/require-pickup-in-persist": "error",
1181
- "effector/strict-effect-handlers": "error"
1182
- };
1183
- const react = {
1184
- "effector/enforce-gate-naming-convention": "error",
1185
- "effector/mandatory-scope-binding": "error",
1186
- "effector/prefer-useUnit": "error"
1187
- };
1188
- const future = { "effector/no-domain-unit-creators": "warn" };
1189
1394
  const ruleset = {
1190
- recommended,
1191
- patronum,
1192
- scope,
1193
- react,
1194
- future
1395
+ recommended: {
1396
+ "effector/enforce-effect-naming-convention": "error",
1397
+ "effector/enforce-store-naming-convention": "error",
1398
+ "effector/keep-options-order": "warn",
1399
+ "effector/no-ambiguity-target": "warn",
1400
+ "effector/no-duplicate-on": "error",
1401
+ "effector/no-forward": "error",
1402
+ "effector/no-getState": "error",
1403
+ "effector/no-guard": "error",
1404
+ "effector/no-unnecessary-combination": "warn",
1405
+ "effector/no-unnecessary-duplication": "warn",
1406
+ "effector/no-useless-methods": "error",
1407
+ "effector/no-watch": "warn"
1408
+ },
1409
+ patronum: { "effector/no-patronum-debug": "warn" },
1410
+ scope: {
1411
+ "effector/require-pickup-in-persist": "error",
1412
+ "effector/strict-effect-handlers": "error"
1413
+ },
1414
+ react: {
1415
+ "effector/enforce-gate-naming-convention": "error",
1416
+ "effector/mandatory-scope-binding": "error",
1417
+ "effector/no-units-spawn-in-render": "error",
1418
+ "effector/prefer-useUnit": "error"
1419
+ },
1420
+ future: { "effector/no-domain-unit-creators": "warn" }
1195
1421
  };
1422
+ //#endregion
1423
+ //#region src/index.ts
1196
1424
  const base = {
1197
1425
  meta: {
1198
1426
  name,
@@ -1213,6 +1441,7 @@ const base = {
1213
1441
  "no-getState": no_getState_default,
1214
1442
  "no-guard": no_guard_default,
1215
1443
  "no-patronum-debug": no_patronum_debug_default,
1444
+ "no-units-spawn-in-render": no_units_spawn_in_render_default,
1216
1445
  "no-unnecessary-combination": no_unnecessary_combination_default,
1217
1446
  "no-unnecessary-duplication": no_unnecessary_duplication_default,
1218
1447
  "no-useless-methods": no_useless_methods_default,
@@ -1255,5 +1484,5 @@ const flatConfigs = {
1255
1484
  const plugin = base;
1256
1485
  plugin.configs = legacyConfigs;
1257
1486
  plugin.flatConfigs = flatConfigs;
1258
- var src_default = plugin;
1259
- module.exports = src_default;
1487
+ //#endregion
1488
+ module.exports = plugin;