eslint-plugin-effector 0.17.0 → 0.19.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.mjs CHANGED
@@ -1,14 +1,19 @@
1
1
  import { ASTUtils, AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
2
2
  import { getContextualType, typeMatchesSpecifier } from "@typescript-eslint/type-utils";
3
- import { isExpression } from "typescript";
3
+ import ts, { isExpression } from "typescript";
4
4
  import esquery from "esquery";
5
+ //#region package.json
5
6
  var name = "eslint-plugin-effector";
6
- var version = "0.17.0";
7
- const createRule = ESLintUtils.RuleCreator((name$1) => `https://eslint.effector.dev/rules/${name$1}`);
8
- const check = (symbol, types, from) => {
9
- const name$1 = symbol.getName();
7
+ var version = "0.19.0";
8
+ //#endregion
9
+ //#region src/shared/create.ts
10
+ const createRule = ESLintUtils.RuleCreator((name) => `https://eslint.effector.dev/rules/${name}`);
11
+ //#endregion
12
+ //#region src/shared/is.ts
13
+ const check$1 = (symbol, types, from) => {
14
+ const name = symbol.getName();
10
15
  const declarations = symbol.declarations ?? [];
11
- return types.includes(name$1) && declarations.map((decl) => decl.getSourceFile().fileName).some((fname) => fname.includes("node_modules") && fname.includes(from));
16
+ return types.includes(name) && declarations.map((decl) => decl.getSourceFile().fileName).some((fname) => fname.includes("node_modules") && fname.includes(from));
12
17
  };
13
18
  const isType = {
14
19
  store: (type, program) => typeMatchesSpecifier(type, {
@@ -26,6 +31,11 @@ const isType = {
26
31
  package: "effector",
27
32
  name: "Effect"
28
33
  }, program),
34
+ domain: (type, program) => typeMatchesSpecifier(type, {
35
+ from: "package",
36
+ package: "effector",
37
+ name: "Domain"
38
+ }, program),
29
39
  unit: (type, program) => {
30
40
  return typeMatchesSpecifier(type, {
31
41
  from: "package",
@@ -35,18 +45,14 @@ const isType = {
35
45
  "StoreWritable",
36
46
  "Event",
37
47
  "EventCallable",
38
- "Effect"
48
+ "Effect",
49
+ "Domain"
39
50
  ]
40
51
  }, program);
41
52
  },
42
- domain: (type, program) => typeMatchesSpecifier(type, {
43
- from: "package",
44
- package: "effector",
45
- name: "Domain"
46
- }, program),
47
53
  gate: (type) => {
48
54
  const symbol = type.getSymbol() ?? type.aliasSymbol;
49
- return symbol ? check(symbol, ["Gate"], "effector") : false;
55
+ return symbol ? check$1(symbol, ["Gate"], "effector") : false;
50
56
  },
51
57
  jsx: (type, program) => {
52
58
  return typeMatchesSpecifier(type, {
@@ -73,13 +79,15 @@ const isType = {
73
79
  }, program);
74
80
  }
75
81
  };
82
+ //#endregion
83
+ //#region src/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.ts
76
84
  var enforce_effect_naming_convention_default = createRule({
77
85
  name: "enforce-effect-naming-convention",
78
86
  meta: {
79
87
  type: "problem",
80
88
  docs: { description: "Enforce Fx as a suffix for any Effector Effect." },
81
89
  messages: {
82
- invalid: "Effect `{{ current }}` should be named with suffix, rename it to `{{ fixed }}`",
90
+ invalid: "Effect \"{{ current }}\" should be named with `Fx` suffix, rename it to \"{{ fixed }}\"",
83
91
  rename: "Rename \"{{ current }}\" to \"{{ fixed }}\""
84
92
  },
85
93
  schema: [],
@@ -88,33 +96,190 @@ var enforce_effect_naming_convention_default = createRule({
88
96
  defaultOptions: [],
89
97
  create: (context) => {
90
98
  const services = ESLintUtils.getParserServices(context);
91
- return { [`VariableDeclarator[id.name!=${FxRegex}]`]: (node) => {
92
- const type = services.getTypeAtLocation(node);
93
- if (!isType.effect(type, services.program)) return;
94
- const current = node.id.name;
95
- const fixed = current + "Fx";
96
- const data = {
97
- current,
98
- fixed
99
- };
100
- const suggestion = {
101
- messageId: "rename",
102
- data: {
103
- current,
104
- fixed
105
- },
106
- fix: (fixer) => fixer.replaceText(node.id, fixed)
107
- };
108
- context.report({
109
- node: node.id,
110
- messageId: "invalid",
111
- data,
112
- suggest: [suggestion]
113
- });
114
- } };
99
+ return {
100
+ [`${selector$14.variable}, ${selector$14.array.identifier}, ${selector$14.array.assignment}, ${selector$14.function.identifier}, ${selector$14.function.assignment}`]: (node) => {
101
+ const type = services.getTypeAtLocation(node);
102
+ if (!isType.effect(type, services.program)) return;
103
+ const data = {
104
+ current: node.name,
105
+ fixed: node.name + "Fx"
106
+ };
107
+ if (node.typeAnnotation) return context.report({
108
+ node,
109
+ messageId: "invalid",
110
+ data
111
+ });
112
+ const suggestion = {
113
+ messageId: "rename",
114
+ data: {
115
+ current: node.name,
116
+ fixed: data.fixed
117
+ },
118
+ fix: (fixer) => fixer.replaceText(node, data.fixed)
119
+ };
120
+ context.report({
121
+ node,
122
+ messageId: "invalid",
123
+ data,
124
+ suggest: [suggestion]
125
+ });
126
+ },
127
+ [`${selector$14.shape.identifier}, ${selector$14.shape.assignment}`]: (node) => {
128
+ const type = services.getTypeAtLocation(node.value);
129
+ const ident = node.value.type === AST_NODE_TYPES.Identifier ? node.value : node.value.left;
130
+ if (!isType.effect(type, services.program)) return;
131
+ const data = {
132
+ current: ident.name,
133
+ fixed: ident.name + "Fx"
134
+ };
135
+ const suggestion = {
136
+ messageId: "rename",
137
+ data: {
138
+ current: ident.name,
139
+ fixed: data.fixed
140
+ },
141
+ fix: (fixer) => node.shorthand ? fixer.insertTextAfter(node.key, `: ${data.fixed}`) : fixer.replaceText(ident, data.fixed)
142
+ };
143
+ context.report({
144
+ node: ident,
145
+ messageId: "invalid",
146
+ data,
147
+ suggest: [suggestion]
148
+ });
149
+ }
150
+ };
115
151
  }
116
152
  });
117
153
  const FxRegex = /Fx$/;
154
+ const selector$14 = {
155
+ variable: `VariableDeclarator > Identifier.id[name!=${FxRegex}]`,
156
+ array: {
157
+ identifier: `ArrayPattern > Identifier.elements[name!=${FxRegex}]`,
158
+ assignment: `ArrayPattern > AssignmentPattern > Identifier.left[name!=${FxRegex}]`
159
+ },
160
+ shape: {
161
+ identifier: `ObjectPattern > Property:has(> Identifier.value[name!=${FxRegex}])`,
162
+ assignment: `ObjectPattern > Property:has(> AssignmentPattern:has(> Identifier.left[name!=${FxRegex}]))`
163
+ },
164
+ function: {
165
+ identifier: `:function > Identifier.params[name!=${FxRegex}]`,
166
+ assignment: `:function > AssignmentPattern > Identifier.left[name!=${FxRegex}]`
167
+ }
168
+ };
169
+ //#endregion
170
+ //#region src/shared/package.ts
171
+ const PACKAGE_NAME$1 = {
172
+ core: /^effector(?:\u002Fcompat)?$/,
173
+ react: /^effector-react$/,
174
+ storage: /^@?effector-storage(\u002F[\w-]+)*$/
175
+ };
176
+ //#endregion
177
+ //#region src/rules/enforce-exhaustive-useUnit-destructuring/enforce-exhaustive-useUnit-destructuring.ts
178
+ var enforce_exhaustive_useUnit_destructuring_default = createRule({
179
+ name: "enforce-exhaustive-useUnit-destructuring",
180
+ meta: {
181
+ type: "problem",
182
+ docs: { description: "Ensure all units passed to useUnit are properly destructured." },
183
+ messages: {
184
+ unusedKey: "Property \"{{name}}\" is passed but not destructured.",
185
+ missingKey: "Property \"{{name}}\" is destructured but not passed in the unit object."
186
+ },
187
+ schema: [],
188
+ defaultOptions: []
189
+ },
190
+ create(context) {
191
+ const importedAs = /* @__PURE__ */ new Set();
192
+ return {
193
+ [selector$13.import]: (node) => void importedAs.add(node.local.name),
194
+ [`${selector$13.variable.shape}:has(> ${selector$13.call}:has(${selector$13.arg.shape}))`](node) {
195
+ if (!importedAs.has(node.init.callee.name)) return;
196
+ const provided = shapeToKeyMap(node.init.arguments[0]);
197
+ const consumed = shapeToKeyMap(node.id);
198
+ if (provided === null || consumed === null) return;
199
+ for (const { type, name } of check(provided, consumed)) if (type === "unused") context.report({
200
+ node: node.init.arguments[0],
201
+ messageId: "unusedKey",
202
+ data: { name }
203
+ });
204
+ else context.report({
205
+ node: node.id,
206
+ messageId: "missingKey",
207
+ data: { name }
208
+ });
209
+ },
210
+ [`${selector$13.variable.list}:has(> ${selector$13.call}:has(${selector$13.arg.list}))`](node) {
211
+ if (!importedAs.has(node.init.callee.name)) return;
212
+ const provided = listToKeyMap(node.init.arguments[0]);
213
+ const consumed = listToKeyMap(node.id);
214
+ if (provided === null || consumed === null) return;
215
+ for (const { type, name } of check(provided, consumed)) if (type === "unused") context.report({
216
+ node: node.init.arguments[0],
217
+ messageId: "unusedKey",
218
+ data: { name }
219
+ });
220
+ else context.report({
221
+ node: node.id,
222
+ messageId: "missingKey",
223
+ data: { name }
224
+ });
225
+ }
226
+ };
227
+ }
228
+ });
229
+ const selector$13 = {
230
+ import: `ImportDeclaration[source.value=${PACKAGE_NAME$1.react}] > ImportSpecifier[imported.name=useUnit]`,
231
+ variable: {
232
+ shape: "VariableDeclarator[id.type=ObjectPattern]",
233
+ list: "VariableDeclarator[id.type=ArrayPattern]"
234
+ },
235
+ call: "CallExpression.init[arguments.length=1][callee.type=Identifier]",
236
+ arg: {
237
+ shape: "ObjectExpression.arguments",
238
+ list: "ArrayExpression.arguments"
239
+ }
240
+ };
241
+ function toName$1(key, node) {
242
+ if (node.type === AST_NODE_TYPES.Identifier) return node.name;
243
+ if (node.type === AST_NODE_TYPES.Literal) return String(node.value);
244
+ if (node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier) return `${toName$1(key, node.object)}.${node.property.name}`;
245
+ return `<unknown at ${key}>`;
246
+ }
247
+ function toKey(prop) {
248
+ if (prop.computed) return null;
249
+ else if (prop.key.type === AST_NODE_TYPES.Identifier) return prop.key.name;
250
+ else return prop.key.value;
251
+ }
252
+ function* check(provided, consumed) {
253
+ for (const [key, node] of provided) if (!consumed.has(key)) yield {
254
+ type: "unused",
255
+ name: toName$1(key, node)
256
+ };
257
+ for (const [key, node] of consumed) if (!provided.has(key)) yield {
258
+ type: "missing",
259
+ name: toName$1(key, node)
260
+ };
261
+ }
262
+ function shapeToKeyMap(shape) {
263
+ const map = /* @__PURE__ */ new Map();
264
+ for (const prop of shape.properties) {
265
+ if (prop.type !== AST_NODE_TYPES.Property) return null;
266
+ const key = toKey(prop);
267
+ if (key === null) return null;
268
+ else map.set(key, prop.key);
269
+ }
270
+ return map;
271
+ }
272
+ function listToKeyMap(list) {
273
+ const map = /* @__PURE__ */ new Map();
274
+ for (const [index, element] of list.elements.entries()) {
275
+ if (element === null) continue;
276
+ if (element.type === AST_NODE_TYPES.RestElement || element.type === AST_NODE_TYPES.SpreadElement) return null;
277
+ map.set(index, element);
278
+ }
279
+ return map;
280
+ }
281
+ //#endregion
282
+ //#region src/rules/enforce-gate-naming-convention/enforce-gate-naming-convention.ts
118
283
  var enforce_gate_naming_convention_default = createRule({
119
284
  name: "enforce-gate-naming-convention",
120
285
  meta: {
@@ -157,6 +322,8 @@ var enforce_gate_naming_convention_default = createRule({
157
322
  }
158
323
  });
159
324
  const GateRegex = /^[^A-Z]/;
325
+ //#endregion
326
+ //#region src/rules/enforce-store-naming-convention/enforce-store-naming-convention.ts
160
327
  var enforce_store_naming_convention_default = createRule({
161
328
  name: "enforce-store-naming-convention",
162
329
  meta: {
@@ -178,41 +345,83 @@ var enforce_store_naming_convention_default = createRule({
178
345
  defaultOptions: [{ mode: "prefix" }],
179
346
  create: (context, [options]) => {
180
347
  const services = ESLintUtils.getParserServices(context);
181
- return { [`VariableDeclarator[id.name=${options.mode === "prefix" ? PrefixRegex : PostfixRegex}]`]: (node) => {
182
- const type = services.getTypeAtLocation(node);
183
- if (!isType.store(type, services.program)) return;
184
- const current = node.id.name;
185
- const trimmed = current.replaceAll(options.mode === "prefix" ? /\$+$/g : /^\$+/g, "");
348
+ const selector = createSelector(options.mode === "prefix" ? PrefixRegex : PostfixRegex);
349
+ const rename = (node) => {
350
+ const trimmed = node.name.replace(options.mode === "prefix" ? /\$+$/g : /^\$+/g, "");
186
351
  const fixed = options.mode === "prefix" ? `$${trimmed}` : `${trimmed}$`;
187
- const data = {
188
- current,
352
+ return {
353
+ current: node.name,
189
354
  convention: options.mode,
190
355
  fixed
191
356
  };
192
- const suggestion = {
193
- messageId: "rename",
194
- data: {
195
- current,
196
- fixed
197
- },
198
- fix: (fixer) => fixer.replaceText(node.id, fixed)
199
- };
200
- context.report({
201
- node: node.id,
202
- messageId: "invalid",
203
- data,
204
- suggest: [suggestion]
205
- });
206
- } };
357
+ };
358
+ return {
359
+ [`${selector.variable}, ${selector.array.identifier}, ${selector.array.assignment}, ${selector.function.identifier}, ${selector.function.assignment}`]: (node) => {
360
+ const type = services.getTypeAtLocation(node);
361
+ if (!isType.store(type, services.program)) return;
362
+ const data = rename(node);
363
+ if (node.typeAnnotation) return context.report({
364
+ node,
365
+ messageId: "invalid",
366
+ data
367
+ });
368
+ const suggestion = {
369
+ messageId: "rename",
370
+ data: {
371
+ current: node.name,
372
+ fixed: data.fixed
373
+ },
374
+ fix: (fixer) => fixer.replaceText(node, data.fixed)
375
+ };
376
+ context.report({
377
+ node,
378
+ messageId: "invalid",
379
+ data,
380
+ suggest: [suggestion]
381
+ });
382
+ },
383
+ [`${selector.shape.identifier}, ${selector.shape.assignment}`]: (node) => {
384
+ const type = services.getTypeAtLocation(node.value);
385
+ const ident = node.value.type === AST_NODE_TYPES.Identifier ? node.value : node.value.left;
386
+ if (!isType.store(type, services.program)) return;
387
+ const data = rename(ident);
388
+ const suggestion = {
389
+ messageId: "rename",
390
+ data: {
391
+ current: ident.name,
392
+ fixed: data.fixed
393
+ },
394
+ fix: (fixer) => node.shorthand ? fixer.insertTextAfter(node.key, `: ${data.fixed}`) : fixer.replaceText(ident, data.fixed)
395
+ };
396
+ context.report({
397
+ node: ident,
398
+ messageId: "invalid",
399
+ data,
400
+ suggest: [suggestion]
401
+ });
402
+ }
403
+ };
207
404
  }
208
405
  });
209
406
  const PrefixRegex = /^[^$]/;
210
407
  const PostfixRegex = /[^$]$/;
211
- const PACKAGE_NAME$1 = {
212
- core: /^effector(?:\u002Fcompat)?$/,
213
- react: /^effector-react$/,
214
- storage: /^@?effector-storage(\u002F[\w-]+)*$/
215
- };
408
+ const createSelector = (regex) => ({
409
+ variable: `VariableDeclarator > Identifier.id[name=${regex}]`,
410
+ array: {
411
+ identifier: `ArrayPattern > Identifier.elements[name=${regex}]`,
412
+ assignment: `ArrayPattern > AssignmentPattern > Identifier.left[name=${regex}]`
413
+ },
414
+ shape: {
415
+ identifier: `ObjectPattern > Property:has(> Identifier.value[name=${regex}])`,
416
+ assignment: `ObjectPattern > Property:has(> AssignmentPattern:has(> Identifier.left[name=${regex}]))`
417
+ },
418
+ function: {
419
+ identifier: `:function > Identifier.params[name=${regex}]`,
420
+ assignment: `:function > AssignmentPattern > Identifier.left[name=${regex}]`
421
+ }
422
+ });
423
+ //#endregion
424
+ //#region src/rules/keep-options-order/keep-options-order.ts
216
425
  var keep_options_order_default = createRule({
217
426
  name: "keep-options-order",
218
427
  meta: {
@@ -230,8 +439,8 @@ var keep_options_order_default = createRule({
230
439
  const source = context.sourceCode;
231
440
  const imports = /* @__PURE__ */ new Set();
232
441
  return {
233
- [`${`ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`} > ${selector$11.method}`]: (node) => imports.add(node.local.name),
234
- [`CallExpression${selector$11.call}:has(${selector$11.argument})`]: (node) => {
442
+ [`${`ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`} > ${selector$12.method}`]: (node) => imports.add(node.local.name),
443
+ [`CallExpression${selector$12.call}:has(${selector$12.argument})`]: (node) => {
235
444
  if (!imports.has(node.callee.name)) return;
236
445
  const [config] = node.arguments;
237
446
  if (config.properties.some((prop) => prop.type === AST_NODE_TYPES.SpreadElement || prop.key.type !== AST_NODE_TYPES.Identifier)) return;
@@ -270,7 +479,7 @@ const TRUE_ORDER = [
270
479
  "batch",
271
480
  "name"
272
481
  ];
273
- const selector$11 = {
482
+ const selector$12 = {
274
483
  method: `ImportSpecifier[imported.name=/(sample|guard)/]`,
275
484
  call: `[callee.type="Identifier"][arguments.length=1]`,
276
485
  argument: `ObjectExpression.arguments`
@@ -285,6 +494,8 @@ const isCorrectOrder = (current) => {
285
494
  }
286
495
  return true;
287
496
  };
497
+ //#endregion
498
+ //#region src/shared/name.ts
288
499
  function functionToName(node) {
289
500
  if (node.id) return node.id;
290
501
  if (node.parent.type === AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES.Identifier) return node.parent.id;
@@ -293,70 +504,115 @@ function functionToName(node) {
293
504
  if (node.parent.type === AST_NODE_TYPES.AssignmentPattern && node.parent.left.type === AST_NODE_TYPES.Identifier) return node.parent.left;
294
505
  return null;
295
506
  }
296
- const nameOf = { function: functionToName };
507
+ function calleeToName(callee) {
508
+ if (callee.type === AST_NODE_TYPES.Identifier) return callee;
509
+ else if (callee.type === AST_NODE_TYPES.MemberExpression && callee.property.type === AST_NODE_TYPES.Identifier) return callee.property;
510
+ else return null;
511
+ }
512
+ function simpleExpressionToName(node) {
513
+ if (node.type === AST_NODE_TYPES.Identifier) return node.name;
514
+ if (node.type === AST_NODE_TYPES.MemberExpression && !node.computed) return node.property.name;
515
+ return null;
516
+ }
517
+ const nameOf = {
518
+ function: functionToName,
519
+ callee: calleeToName,
520
+ expression: { simple: simpleExpressionToName }
521
+ };
522
+ //#endregion
523
+ //#region src/rules/mandatory-scope-binding/mandatory-scope-binding.ts
297
524
  var mandatory_scope_binding_default = createRule({
298
525
  name: "mandatory-scope-binding",
299
526
  meta: {
300
527
  type: "problem",
301
- docs: { description: "Forbid `Event` and `Effect` usage without `useUnit` in React components." },
302
- messages: { useUnitNeeded: "\"{{ name }}\" must be wrapped with `useUnit` from `effector-react` before usage inside React components." },
528
+ docs: { description: "Forbid `Event` and `Effect` usage without `useUnit` in React." },
529
+ messages: { useUnitNeeded: "\"{{ name }}\" must be wrapped with `useUnit` from `effector-react` before usage inside React." },
303
530
  schema: []
304
531
  },
305
532
  defaultOptions: [],
306
533
  create: (context) => {
307
534
  const services = ESLintUtils.getParserServices(context);
308
535
  const checker = services.program.getTypeChecker();
309
- const stack = {
310
- render: [],
311
- hook: []
536
+ const inRender = [];
537
+ const inHook = [];
538
+ /** check if the expression is used in a context specifically expecting a unit */
539
+ const isExpectingUnit = (slot) => {
540
+ const tsnode = services.esTreeNodeToTSNodeMap.get(slot);
541
+ const type = checker.getContextualType(tsnode);
542
+ if (type) return isType.event(type, services.program) || isType.effect(type, services.program);
543
+ else return false;
544
+ };
545
+ const check = (mode, node) => {
546
+ if (!(inRender.at(-1) ?? false)) return;
547
+ const type = services.getTypeAtLocation(node);
548
+ if (!isType.event(type, services.program) && !isType.effect(type, services.program)) return;
549
+ if (mode === "call") return report(node);
550
+ const delegated = isExpectingUnit(node);
551
+ if ((mode === "jsx" || (inHook.at(-1) ?? false)) && delegated) return;
552
+ else return report(node);
553
+ };
554
+ const report = (node) => {
555
+ const name = nameOf.expression.simple(node) ?? "<expression>";
556
+ context.report({
557
+ node,
558
+ messageId: "useUnitNeeded",
559
+ data: { name }
560
+ });
312
561
  };
313
562
  return {
314
- [`FunctionDeclaration, FunctionExpression, ArrowFunctionExpression`]: (node) => {
315
- if (stack.render.at(-1) ?? false) return void stack.render.push(true);
316
- const name$1 = nameOf.function(node);
317
- if (name$1 && UseRegex.test(name$1.name)) return void stack.render.push(true);
563
+ [`:matches(${selector$11.function})`]: (node) => {
564
+ if (inRender.at(-1) ?? false) return void inRender.push(true);
565
+ const name = nameOf.function(node);
566
+ if (name && UseRegex$1.test(name.name)) return void inRender.push(true);
318
567
  const tsnode = services.esTreeNodeToTSNodeMap.get(node);
319
568
  const signature = checker.getSignatureFromDeclaration(tsnode);
320
569
  const returnType = signature ? checker.getReturnTypeOfSignature(signature) : checker.getVoidType();
321
- if (isType.jsx(returnType, services.program)) return void stack.render.push(true);
322
- const inferred = isExpression(tsnode) ? getContextualType(checker, tsnode) : void 0;
323
- if (inferred ? isType.component(inferred, services.program) : false) return void stack.render.push(true);
324
- stack.render.push(false);
570
+ if (returnType.isUnion() ? returnType.types.some((type) => isType.jsx(type, services.program)) : isType.jsx(returnType, services.program)) return void inRender.push(true);
571
+ const inferred = ts.isExpression(tsnode) && getContextualType(checker, tsnode) || checker.getUnknownType();
572
+ if (inferred.isUnion() ? inferred.types.some((type) => isType.component(type, services.program)) : isType.component(inferred, services.program)) return void inRender.push(true);
573
+ inRender.push(false);
325
574
  },
326
- [`:matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression):exit`]: () => void stack.render.pop(),
327
- "ClassDeclaration": () => void stack.render.push(false),
328
- "ClassDeclaration:exit": () => void stack.render.pop(),
575
+ [`:matches(${selector$11.function}):exit`]: () => void inRender.pop(),
576
+ "ClassDeclaration": () => void inRender.push(false),
577
+ "ClassDeclaration:exit": () => void inRender.pop(),
329
578
  "CallExpression": (node) => {
330
- const isHook = typeMatchesSpecifier(services.getTypeAtLocation(node.callee), {
331
- from: "package",
332
- package: "effector-react",
333
- name: [
334
- "useStore",
335
- "useStoreMap",
336
- "useList",
337
- "useEvent",
338
- "useUnit"
339
- ]
340
- }, services.program);
341
- stack.hook.push(isHook);
579
+ const id = nameOf.callee(node.callee), isEnteringHook = id !== null && UseRegex$1.test(id.name);
580
+ inHook.push(isEnteringHook);
342
581
  },
343
- "Identifier": (node) => {
344
- if (!(stack.render.at(-1) ?? false)) return;
345
- if (stack.hook.at(-1) ?? false) return;
346
- const type = services.getTypeAtLocation(node);
347
- if (!isType.event(type, services.program) && !isType.effect(type, services.program)) return;
348
- context.report({
349
- node,
350
- messageId: "useUnitNeeded",
351
- data: { name: node.name }
352
- });
353
- }
582
+ "CallExpression:exit": () => void inHook.pop(),
583
+ [`${selector$11.callee.direct}, ${selector$11.callee.member}`]: (node) => check("call", node),
584
+ [`${selector$11.arg.direct}, ${selector$11.arg.member}`]: (node) => check("arg", node),
585
+ [`${selector$11.prop.direct}, ${selector$11.prop.member}`]: (node) => check("prop", node),
586
+ [`${selector$11.jsx.direct}, ${selector$11.jsx.member}`]: (node) => check("jsx", node)
354
587
  };
355
588
  }
356
589
  });
357
- const UseRegex = /^use[A-Z0-9].*$/;
590
+ const UseRegex$1 = /^use[A-Z0-9].*$/;
591
+ const selector$11 = {
592
+ function: "FunctionDeclaration, FunctionExpression, ArrowFunctionExpression",
593
+ callee: {
594
+ direct: "CallExpression > Identifier.callee",
595
+ member: "CallExpression > MemberExpression[computed=false].callee"
596
+ },
597
+ arg: {
598
+ direct: "CallExpression > Identifier:not(.callee)",
599
+ member: "CallExpression > MemberExpression[computed=false]:not(.callee)"
600
+ },
601
+ prop: {
602
+ direct: "CallExpression > ObjectExpression > Property > Identifier.value",
603
+ member: "CallExpression > ObjectExpression > Property > MemberExpression[computed=false].value"
604
+ },
605
+ jsx: {
606
+ direct: "JSXExpressionContainer > Identifier",
607
+ member: "JSXExpressionContainer > MemberExpression[computed=false]"
608
+ }
609
+ };
610
+ //#endregion
611
+ //#region src/shared/locate.ts
358
612
  const property = (key, node) => node.properties.find((prop) => prop.type == AST_NODE_TYPES.Property && prop.key.type === AST_NODE_TYPES.Identifier && prop.key.name === key);
359
613
  const locate = { property };
614
+ //#endregion
615
+ //#region src/rules/no-ambiguity-target/no-ambiguity-target.ts
360
616
  var no_ambiguity_target_default = createRule({
361
617
  name: "no-ambiguity-target",
362
618
  meta: {
@@ -396,6 +652,8 @@ var no_ambiguity_target_default = createRule({
396
652
  }
397
653
  });
398
654
  const selector$10 = { method: `ImportSpecifier[imported.name=/(sample|guard)/]` };
655
+ //#endregion
656
+ //#region src/rules/no-domain-unit-creators/no-domain-unit-creators.ts
399
657
  var no_domain_unit_creators_default = createRule({
400
658
  name: "no-domain-unit-creators",
401
659
  meta: {
@@ -408,16 +666,16 @@ var no_domain_unit_creators_default = createRule({
408
666
  create: (context) => {
409
667
  const services = ESLintUtils.getParserServices(context);
410
668
  return { [`CallExpression:has(> ${selector$9.member})`]: (node) => {
411
- const name$1 = node.callee.property.name;
412
- if (!METHODS.has(name$1)) return;
669
+ const name = node.callee.property.name;
670
+ if (!METHODS.has(name)) return;
413
671
  const type = services.getTypeAtLocation(node.callee.object);
414
672
  if (!isType.domain(type, services.program)) return;
415
- const factory = ALIAS_MAP.get(name$1) ?? name$1;
673
+ const factory = ALIAS_MAP.get(name) ?? name;
416
674
  context.report({
417
675
  node,
418
676
  messageId: "avoid",
419
677
  data: {
420
- method: name$1,
678
+ method: name,
421
679
  factory
422
680
  }
423
681
  });
@@ -427,6 +685,8 @@ var no_domain_unit_creators_default = createRule({
427
685
  const ALIAS_MAP = (/* @__PURE__ */ new Map()).set("event", "createEvent").set("store", "createStore").set("effect", "createEffect").set("domain", "createDomain");
428
686
  const METHODS = new Set([...ALIAS_MAP.values(), ...ALIAS_MAP.keys()]);
429
687
  const selector$9 = { member: `MemberExpression.callee[property.type="Identifier"]` };
688
+ //#endregion
689
+ //#region src/rules/no-duplicate-clock-or-source-array-values/no-duplicate-clock-or-source-array-values.ts
430
690
  var no_duplicate_clock_or_source_array_values_default = createRule({
431
691
  name: "no-duplicate-clock-or-source-array-values",
432
692
  meta: {
@@ -449,19 +709,19 @@ var no_duplicate_clock_or_source_array_values_default = createRule({
449
709
  for (const entry of entries) {
450
710
  const root = traverseToRoot$1(entry);
451
711
  if (!root) continue;
452
- const name$1 = [root.node.name, ...root.path].join(".");
453
- if (seen.has(name$1)) report(entry, name$1, field);
454
- else seen.set(name$1, entry);
712
+ const name = [root.node.name, ...root.path].join(".");
713
+ if (seen.has(name)) report(entry, name, field);
714
+ else seen.set(name, entry);
455
715
  }
456
716
  };
457
- const report = (node, name$1, field) => {
717
+ const report = (node, name, field) => {
458
718
  const data = {
459
719
  field,
460
- unit: name$1
720
+ unit: name
461
721
  };
462
722
  const suggestion = {
463
723
  messageId: "remove",
464
- data: { unit: name$1 },
724
+ data: { unit: name },
465
725
  fix: function* (fixer) {
466
726
  yield fixer.remove(node);
467
727
  const before = context.sourceCode.getTokenBefore(node);
@@ -501,6 +761,8 @@ function traverseToRoot$1(node, path = []) {
501
761
  if (node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier) return traverseToRoot$1(node.object, [node.property.name, ...path]);
502
762
  return null;
503
763
  }
764
+ //#endregion
765
+ //#region src/rules/no-duplicate-on/no-duplicate-on.ts
504
766
  var no_duplicate_on_default = createRule({
505
767
  name: "no-duplicate-on",
506
768
  meta: {
@@ -519,12 +781,12 @@ var no_duplicate_on_default = createRule({
519
781
  const arg = node.arguments[0];
520
782
  if (!arg || arg.type === AST_NODE_TYPES.SpreadElement) return;
521
783
  const units = arg.type === AST_NODE_TYPES.ArrayExpression ? arg.elements.filter((item) => item !== null && item.type !== AST_NODE_TYPES.SpreadElement) : [arg];
522
- const scope$1 = context.sourceCode.getScope(node);
523
- const store = identify("store", node.callee.object, scope$1);
784
+ const scope = context.sourceCode.getScope(node);
785
+ const store = identify("store", node.callee.object, scope);
524
786
  if (!store) return;
525
787
  const set = map.get(store.id) ?? /* @__PURE__ */ new Set();
526
788
  for (const unit of units) {
527
- const instance = identify("unit", unit, scope$1);
789
+ const instance = identify("unit", unit, scope);
528
790
  if (!instance) continue;
529
791
  if (set.has(instance.id)) {
530
792
  const data = {
@@ -585,16 +847,18 @@ function findSuitableRoot(type, node) {
585
847
  };
586
848
  return null;
587
849
  }
588
- function identify(type, node, scope$1) {
850
+ function identify(type, node, scope) {
589
851
  const root = findSuitableRoot(type, node);
590
852
  if (!root) return null;
591
- const variable = ASTUtils.findVariable(scope$1, root.node);
853
+ const variable = ASTUtils.findVariable(scope, root.node);
592
854
  if (!variable) return null;
593
855
  return {
594
856
  id: `${variable.$id}+${root.path.join(".")}`,
595
857
  name: [variable.name, ...root.path].join(".")
596
858
  };
597
859
  }
860
+ //#endregion
861
+ //#region src/rules/no-forward/no-forward.ts
598
862
  var no_forward_default = createRule({
599
863
  name: "no-forward",
600
864
  meta: {
@@ -624,11 +888,11 @@ var no_forward_default = createRule({
624
888
  config.clock = locate.property("from", arg)?.value;
625
889
  config.target = locate.property("to", arg)?.value;
626
890
  if (config.target) {
627
- const [call] = esquery.match(config.target, query$2.prepend, { visitorKeys }).map((node$1) => node$1).filter((node$1) => node$1 === config.target);
891
+ const [call] = esquery.match(config.target, query$2.prepend, { visitorKeys }).map((node) => node).filter((node) => node === config.target);
628
892
  if (call) [config.target, config.fn] = [call.callee.object, call.arguments[0]];
629
893
  }
630
894
  if (config.clock && !config.fn) {
631
- const [call] = esquery.match(config.clock, query$2.map, { visitorKeys }).map((node$1) => node$1).filter((node$1) => node$1 === config.clock);
895
+ const [call] = esquery.match(config.clock, query$2.map, { visitorKeys }).map((node) => node).filter((node) => node === config.clock);
632
896
  if (call) [config.clock, config.fn] = [call.callee.object, call.arguments[0]];
633
897
  }
634
898
  const code = [
@@ -662,6 +926,8 @@ const query$2 = {
662
926
  map: esquery.parse("CallExpression[arguments.length=1]:has(> :first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='map']))"),
663
927
  prepend: esquery.parse("CallExpression[arguments.length=1]:has(> :first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='prepend']))")
664
928
  };
929
+ //#endregion
930
+ //#region src/rules/no-getState/no-getState.ts
665
931
  var no_getState_default = createRule({
666
932
  name: "no-getState",
667
933
  meta: {
@@ -679,11 +945,11 @@ var no_getState_default = createRule({
679
945
  return { [`CallExpression[callee.type="MemberExpression"][callee.property.name="getState"]`]: (node) => {
680
946
  const type = services.getTypeAtLocation(node.callee.object);
681
947
  if (!isType.store(type, services.program)) return;
682
- const name$1 = toName$1(node.callee.object);
683
- if (name$1) context.report({
948
+ const name = nameOf.expression.simple(node.callee.object);
949
+ if (name) context.report({
684
950
  node,
685
951
  messageId: "named",
686
- data: { name: name$1 }
952
+ data: { name }
687
953
  });
688
954
  else context.report({
689
955
  node,
@@ -692,11 +958,8 @@ var no_getState_default = createRule({
692
958
  } };
693
959
  }
694
960
  });
695
- const toName$1 = (node) => {
696
- if (node.type === AST_NODE_TYPES.Identifier) return node.name;
697
- if (node.type === AST_NODE_TYPES.MemberExpression && !node.computed) return node.property.name;
698
- return null;
699
- };
961
+ //#endregion
962
+ //#region src/rules/no-guard/no-guard.ts
700
963
  var no_guard_default = createRule({
701
964
  name: "no-guard",
702
965
  meta: {
@@ -740,7 +1003,7 @@ var no_guard_default = createRule({
740
1003
  ]) config[key] = locate.property(key, arg)?.value;
741
1004
  } else return;
742
1005
  if (config.target) {
743
- const [call] = esquery.match(config.target, query$1.prepend, { visitorKeys }).map((node$1) => node$1).filter((node$1) => node$1 === config.target);
1006
+ const [call] = esquery.match(config.target, query$1.prepend, { visitorKeys }).map((node) => node).filter((node) => node === config.target);
744
1007
  if (call) [config.target, config.fn] = [call.callee.object, call.arguments[0]];
745
1008
  }
746
1009
  const code = [
@@ -772,6 +1035,8 @@ const selector$6 = {
772
1035
  call: `[callee.type="Identifier"]`
773
1036
  };
774
1037
  const query$1 = { prepend: esquery.parse("CallExpression[arguments.length=1]:has(:first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='prepend']))") };
1038
+ //#endregion
1039
+ //#region src/rules/no-patronum-debug/no-patronum-debug.ts
775
1040
  var no_patronum_debug_default = createRule({
776
1041
  name: "no-patronum-debug",
777
1042
  meta: {
@@ -790,8 +1055,8 @@ var no_patronum_debug_default = createRule({
790
1055
  return {
791
1056
  [`${`ImportDeclaration[source.value=${PACKAGE_NAME}]`} > ${selector$5.debug}`]: (node) => debugs.add(node.local.name),
792
1057
  [`CallExpression:matches(${selector$5.call})`]: (node) => {
793
- const name$1 = toName(node);
794
- if (!debugs.has(name$1)) return;
1058
+ const name = toName(node);
1059
+ if (!debugs.has(name)) return;
795
1060
  context.report({
796
1061
  messageId: "unexpected",
797
1062
  node: node.callee,
@@ -818,6 +1083,186 @@ const toName = (node) => {
818
1083
  case AST_NODE_TYPES.MemberExpression: return node.callee.object.name;
819
1084
  }
820
1085
  };
1086
+ //#endregion
1087
+ //#region src/rules/no-units-spawn-in-render/no-units-spawn-in-render.ts
1088
+ const EFFECTOR_FACTORIES = new Set([
1089
+ "createStore",
1090
+ "createEvent",
1091
+ "createEffect",
1092
+ "createDomain",
1093
+ "createApi",
1094
+ "restore"
1095
+ ]);
1096
+ const EFFECTOR_OPERATORS = new Set([
1097
+ "sample",
1098
+ "guard",
1099
+ "forward",
1100
+ "merge",
1101
+ "split",
1102
+ "combine",
1103
+ "attach"
1104
+ ]);
1105
+ const REACT_HOOKS_SPEC = {
1106
+ from: "package",
1107
+ package: "react",
1108
+ name: [
1109
+ "useState",
1110
+ "useEffect",
1111
+ "useLayoutEffect",
1112
+ "useCallback",
1113
+ "useMemo",
1114
+ "useRef",
1115
+ "useReducer",
1116
+ "useImperativeHandle",
1117
+ "useDebugValue",
1118
+ "useDeferredValue",
1119
+ "useTransition",
1120
+ "useId",
1121
+ "useSyncExternalStore",
1122
+ "useInsertionEffect",
1123
+ "useContext"
1124
+ ]
1125
+ };
1126
+ const EFFECTOR_FACTORY_SPEC = {
1127
+ from: "package",
1128
+ package: "effector",
1129
+ name: [...EFFECTOR_FACTORIES]
1130
+ };
1131
+ const EFFECTOR_OPERATOR_SPEC = {
1132
+ from: "package",
1133
+ package: "effector",
1134
+ name: [...EFFECTOR_OPERATORS]
1135
+ };
1136
+ const EFFECTOR_FACTORIO_SHAPE = [
1137
+ "useModel",
1138
+ "createModel",
1139
+ "Provider",
1140
+ "@@unitShape"
1141
+ ];
1142
+ var no_units_spawn_in_render_default = createRule({
1143
+ name: "no-units-spawn-in-render",
1144
+ meta: {
1145
+ type: "problem",
1146
+ docs: { description: "Forbid creating Effector units or calling operators inside React components or hooks." },
1147
+ messages: {
1148
+ noFactoryInRender: "Creating Effector units with \"{{ name }}\" inside React component or hook is forbidden, since it may cause memory leaks and other bugs.",
1149
+ noOperatorInRender: "Using Effector operator \"{{ name }}\" inside React component or hook is forbidden, since it may cause memory leaks and other bugs.",
1150
+ 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."
1151
+ },
1152
+ schema: [{
1153
+ type: "object",
1154
+ properties: { detectCustomFactories: { oneOf: [{ type: "boolean" }, {
1155
+ type: "object",
1156
+ properties: { allowlist: {
1157
+ type: "array",
1158
+ items: { type: "string" },
1159
+ uniqueItems: true
1160
+ } },
1161
+ required: ["allowlist"],
1162
+ additionalProperties: false
1163
+ }] } },
1164
+ additionalProperties: false
1165
+ }]
1166
+ },
1167
+ defaultOptions: [{ detectCustomFactories: true }],
1168
+ create: (context, [options]) => {
1169
+ const services = ESLintUtils.getParserServices(context);
1170
+ const checker = services.program.getTypeChecker();
1171
+ const { detectCustomFactories } = options;
1172
+ const allowlist = typeof detectCustomFactories === "object" ? new Set(detectCustomFactories.allowlist) : void 0;
1173
+ const stack = { render: [] };
1174
+ const effectorImports = /* @__PURE__ */ new Map();
1175
+ return {
1176
+ [`${`ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`} > ImportSpecifier[imported.type="Identifier"]`]: (node) => {
1177
+ const imported = node.imported.name;
1178
+ const local = node.local.name;
1179
+ if (EFFECTOR_FACTORIES.has(imported)) effectorImports.set(local, "factory");
1180
+ else if (EFFECTOR_OPERATORS.has(imported)) effectorImports.set(local, "operator");
1181
+ },
1182
+ [`FunctionDeclaration, FunctionExpression, ArrowFunctionExpression`]: (node) => {
1183
+ if (stack.render.at(-1) ?? false) return void stack.render.push(true);
1184
+ const name = nameOf.function(node);
1185
+ if (name && UseRegex.test(name.name)) return void stack.render.push(true);
1186
+ const tsnode = services.esTreeNodeToTSNodeMap.get(node);
1187
+ const signature = checker.getSignatureFromDeclaration(tsnode);
1188
+ const returnType = signature ? checker.getReturnTypeOfSignature(signature) : checker.getVoidType();
1189
+ if (returnType.isUnion() ? returnType.types.some((type) => isType.jsx(type, services.program)) : isType.jsx(returnType, services.program)) return void stack.render.push(true);
1190
+ const inferred = isExpression(tsnode) && getContextualType(checker, tsnode) || checker.getUnknownType();
1191
+ if (inferred.isUnion() ? inferred.types.some((type) => isType.component(type, services.program)) : isType.component(inferred, services.program)) return void stack.render.push(true);
1192
+ stack.render.push(false);
1193
+ },
1194
+ [`:matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression):exit`]: () => void stack.render.pop(),
1195
+ "ClassDeclaration": () => void stack.render.push(false),
1196
+ "ClassDeclaration:exit": () => void stack.render.pop(),
1197
+ "CallExpression": (node) => {
1198
+ if (!(stack.render.at(-1) ?? false)) return;
1199
+ const calleeName = getCalleeName(node.callee);
1200
+ switch (calleeName ? effectorImports.get(calleeName) : void 0) {
1201
+ case "factory": return context.report({
1202
+ node,
1203
+ messageId: "noFactoryInRender",
1204
+ data: { name: calleeName }
1205
+ });
1206
+ case "operator": return context.report({
1207
+ node,
1208
+ messageId: "noOperatorInRender",
1209
+ data: { name: calleeName }
1210
+ });
1211
+ }
1212
+ if (detectCustomFactories === false) return;
1213
+ const returnType = services.getTypeAtLocation(node);
1214
+ if (!hasEffectorUnitInType({
1215
+ node: services.esTreeNodeToTSNodeMap.get(node),
1216
+ checker,
1217
+ program: services.program
1218
+ }, returnType)) return;
1219
+ const calleeType = services.getTypeAtLocation(node.callee);
1220
+ const displayName = calleeName ?? "<expression>";
1221
+ if (typeMatchesSpecifier(calleeType, REACT_HOOKS_SPEC, services.program)) return;
1222
+ if (isEffectorFactorioHook(node.callee, services.getTypeAtLocation)) return;
1223
+ if (typeMatchesSpecifier(calleeType, EFFECTOR_FACTORY_SPEC, services.program)) return context.report({
1224
+ node,
1225
+ messageId: "noFactoryInRender",
1226
+ data: { name: displayName }
1227
+ });
1228
+ if (typeMatchesSpecifier(calleeType, EFFECTOR_OPERATOR_SPEC, services.program)) return context.report({
1229
+ node,
1230
+ messageId: "noOperatorInRender",
1231
+ data: { name: displayName }
1232
+ });
1233
+ if (allowlist && calleeName && allowlist.has(calleeName)) return;
1234
+ context.report({
1235
+ node,
1236
+ messageId: "noCustomFactoryInRender",
1237
+ data: { name: displayName }
1238
+ });
1239
+ }
1240
+ };
1241
+ }
1242
+ });
1243
+ const UseRegex = /^use[A-Z0-9].*$/;
1244
+ function getCalleeName(callee) {
1245
+ if (callee.type === AST_NODE_TYPES.Identifier) return callee.name;
1246
+ if (callee.type === AST_NODE_TYPES.MemberExpression && callee.property.type === AST_NODE_TYPES.Identifier) return callee.property.name;
1247
+ else return null;
1248
+ }
1249
+ function hasEffectorUnitInType(ctx, type, depth = 3) {
1250
+ if (isType.unit(type, ctx.program)) return true;
1251
+ if (depth <= 0) return false;
1252
+ if (type.isUnion()) return type.types.some((type) => hasEffectorUnitInType(ctx, type, depth));
1253
+ for (const property of type.getProperties()) if (hasEffectorUnitInType(ctx, ctx.checker.getTypeOfSymbolAtLocation(property, ctx.node), depth - 1)) return true;
1254
+ return false;
1255
+ }
1256
+ function isEffectorFactorioHook(callee, getTypeAtLocation) {
1257
+ if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;
1258
+ if (callee.property.type !== AST_NODE_TYPES.Identifier) return false;
1259
+ if (callee.property.name !== "useModel") return false;
1260
+ const objectType = getTypeAtLocation(callee.object);
1261
+ const propertyNames = new Set(objectType.getProperties().map((p) => p.getName()));
1262
+ return EFFECTOR_FACTORIO_SHAPE.every((name) => propertyNames.has(name));
1263
+ }
1264
+ //#endregion
1265
+ //#region src/rules/no-unnecessary-combination/no-unnecessary-combination.ts
821
1266
  var no_unnecessary_combination_default = createRule({
822
1267
  name: "no-unnecessary-combination",
823
1268
  meta: {
@@ -888,6 +1333,8 @@ function isFunction(node, services) {
888
1333
  return checker.getTypeAtLocation(tsnode).getCallSignatures().length > 0;
889
1334
  } else return false;
890
1335
  }
1336
+ //#endregion
1337
+ //#region src/rules/no-unnecessary-duplication/no-unnecessary-duplication.ts
891
1338
  var no_unnecessary_duplication_default = createRule({
892
1339
  name: "no-unnecessary-duplication",
893
1340
  meta: {
@@ -964,6 +1411,8 @@ function compare(clock, source, limit = 5) {
964
1411
  }
965
1412
  return false;
966
1413
  }
1414
+ //#endregion
1415
+ //#region src/rules/no-useless-methods/no-useless-methods.ts
967
1416
  var no_useless_methods_default = createRule({
968
1417
  name: "no-useless-methods",
969
1418
  meta: {
@@ -998,10 +1447,8 @@ var no_useless_methods_default = createRule({
998
1447
  if (locate.property("target", config)?.value) return;
999
1448
  }
1000
1449
  const grandparent = node.parent.parent;
1001
- if (grandparent) {
1002
- const ancestry = source.getAncestors(grandparent);
1003
- if (esquery.matches(grandparent, query.watch, ancestry, { visitorKeys })) return;
1004
- }
1450
+ const ancestry = source.getAncestors(grandparent);
1451
+ if (esquery.matches(grandparent, query.watch, ancestry, { visitorKeys })) return;
1005
1452
  const method = node.callee.name;
1006
1453
  context.report({
1007
1454
  node,
@@ -1014,6 +1461,8 @@ var no_useless_methods_default = createRule({
1014
1461
  });
1015
1462
  const selector$2 = { method: `ImportSpecifier[imported.name=/(sample|guard)/]` };
1016
1463
  const query = { watch: esquery.parse("CallExpression:has(> MemberExpression.callee[property.name=watch]:has(> CallExpression.object))") };
1464
+ //#endregion
1465
+ //#region src/rules/no-watch/no-watch.ts
1017
1466
  var no_watch_default = createRule({
1018
1467
  name: "no-watch",
1019
1468
  meta: {
@@ -1035,6 +1484,8 @@ var no_watch_default = createRule({
1035
1484
  } };
1036
1485
  }
1037
1486
  });
1487
+ //#endregion
1488
+ //#region src/rules/prefer-useUnit/prefer-useUnit.ts
1038
1489
  var prefer_useUnit_default = createRule({
1039
1490
  name: "prefer-useUnit",
1040
1491
  meta: {
@@ -1066,6 +1517,8 @@ const selector$1 = {
1066
1517
  useStore: `ImportSpecifier[imported.name=useStore]`,
1067
1518
  useEvent: `ImportSpecifier[imported.name=useEvent]`
1068
1519
  };
1520
+ //#endregion
1521
+ //#region src/rules/require-pickup-in-persist/require-pickup-in-persist.ts
1069
1522
  var require_pickup_in_persist_default = createRule({
1070
1523
  name: "require-pickup-in-persist",
1071
1524
  meta: {
@@ -1095,6 +1548,8 @@ const selector = {
1095
1548
  call: `[callee.type="Identifier"]`,
1096
1549
  config: `[arguments.length=1][arguments.0.type="ObjectExpression"]`
1097
1550
  };
1551
+ //#endregion
1552
+ //#region src/rules/strict-effect-handlers/strict-effect-handlers.ts
1098
1553
  var strict_effect_handlers_default = createRule({
1099
1554
  name: "strict-effect-handlers",
1100
1555
  meta: {
@@ -1122,9 +1577,8 @@ var strict_effect_handlers_default = createRule({
1122
1577
  });
1123
1578
  };
1124
1579
  const exit = (node) => {
1125
- const scope$1 = stack.pop();
1126
- if (!scope$1) return;
1127
- if (scope$1.effect && scope$1.regular) context.report({
1580
+ const scope = stack.pop();
1581
+ if (scope.effect && scope.regular) context.report({
1128
1582
  node,
1129
1583
  messageId: "mixed"
1130
1584
  });
@@ -1162,11 +1616,15 @@ const ruleset = {
1162
1616
  },
1163
1617
  react: {
1164
1618
  "effector/enforce-gate-naming-convention": "error",
1619
+ "effector/enforce-exhaustive-useUnit-destructuring": "warn",
1165
1620
  "effector/mandatory-scope-binding": "error",
1621
+ "effector/no-units-spawn-in-render": "error",
1166
1622
  "effector/prefer-useUnit": "error"
1167
1623
  },
1168
1624
  future: { "effector/no-domain-unit-creators": "warn" }
1169
1625
  };
1626
+ //#endregion
1627
+ //#region src/index.ts
1170
1628
  const base = {
1171
1629
  meta: {
1172
1630
  name,
@@ -1175,6 +1633,7 @@ const base = {
1175
1633
  },
1176
1634
  rules: {
1177
1635
  "enforce-effect-naming-convention": enforce_effect_naming_convention_default,
1636
+ "enforce-exhaustive-useUnit-destructuring": enforce_exhaustive_useUnit_destructuring_default,
1178
1637
  "enforce-gate-naming-convention": enforce_gate_naming_convention_default,
1179
1638
  "enforce-store-naming-convention": enforce_store_naming_convention_default,
1180
1639
  "keep-options-order": keep_options_order_default,
@@ -1187,6 +1646,7 @@ const base = {
1187
1646
  "no-getState": no_getState_default,
1188
1647
  "no-guard": no_guard_default,
1189
1648
  "no-patronum-debug": no_patronum_debug_default,
1649
+ "no-units-spawn-in-render": no_units_spawn_in_render_default,
1190
1650
  "no-unnecessary-combination": no_unnecessary_combination_default,
1191
1651
  "no-unnecessary-duplication": no_unnecessary_duplication_default,
1192
1652
  "no-useless-methods": no_useless_methods_default,
@@ -1229,5 +1689,5 @@ const flatConfigs = {
1229
1689
  const plugin = base;
1230
1690
  plugin.configs = legacyConfigs;
1231
1691
  plugin.flatConfigs = flatConfigs;
1232
- var src_default = plugin;
1233
- export { src_default as default };
1692
+ //#endregion
1693
+ export { plugin as default };