eslint-plugin-effector 0.16.0 → 0.17.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/README.md +24 -37
- package/dist/index.cjs +1259 -0
- package/dist/index.d.cts +177 -0
- package/dist/index.d.mts +178 -0
- package/dist/index.mjs +1233 -0
- package/package.json +71 -17
- package/.nvmrc +0 -1
- package/config/future.js +0 -7
- package/config/patronum.js +0 -5
- package/config/react.js +0 -7
- package/config/recommended.js +0 -15
- package/config/scope.js +0 -6
- package/index.js +0 -31
- package/rules/enforce-effect-naming-convention/enforce-effect-naming-convention.js +0 -143
- package/rules/enforce-gate-naming-convention/enforce-gate-naming-convention.js +0 -122
- package/rules/enforce-store-naming-convention/enforce-store-naming-convention.js +0 -205
- package/rules/keep-options-order/config.js +0 -3
- package/rules/keep-options-order/keep-options-order.js +0 -107
- package/rules/mandatory-scope-binding/mandatory-scope-binding.js +0 -81
- package/rules/no-ambiguity-target/no-ambiguity-target.js +0 -74
- package/rules/no-duplicate-clock-or-source-array-values/no-duplicate-clock-or-source-array-values.js +0 -124
- package/rules/no-duplicate-on/no-duplicate-on.js +0 -137
- package/rules/no-forward/no-forward.js +0 -73
- package/rules/no-getState/no-getState.js +0 -50
- package/rules/no-guard/no-guard.js +0 -78
- package/rules/no-patronum-debug/no-patronum-debug.js +0 -133
- package/rules/no-unnecessary-combination/no-unnecessary-combination.js +0 -88
- package/rules/no-unnecessary-duplication/no-unnecessary-duplication.js +0 -115
- package/rules/no-useless-methods/no-useless-methods.js +0 -93
- package/rules/no-watch/no-watch.js +0 -61
- package/rules/prefer-sample-over-forward-with-mapping/prefer-sample-over-forward-with-mapping.js +0 -111
- package/rules/prefer-useUnit/prefer-useUnit.js +0 -56
- package/rules/require-pickup-in-persist/require-pickup-in-persist.js +0 -47
- package/rules/strict-effect-handlers/strict-effect-handlers.js +0 -76
- package/utils/are-nodes-same-in-text.js +0 -22
- package/utils/builders.js +0 -19
- package/utils/create-link-to-rule.js +0 -5
- package/utils/extract-config.js +0 -26
- package/utils/extract-imported-from.js +0 -18
- package/utils/get-corrected-store-name.js +0 -45
- package/utils/get-nested-object-name.js +0 -18
- package/utils/get-store-name-convention.js +0 -6
- package/utils/is.js +0 -39
- package/utils/method.js +0 -23
- package/utils/naming.js +0 -47
- package/utils/node-is-type.js +0 -5
- package/utils/node-type-is.js +0 -106
- package/utils/react.js +0 -214
- package/utils/read-example.js +0 -63
- package/utils/replace-by-sample.js +0 -98
- package/utils/traverse-nested-object-node.js +0 -9
- package/utils/traverse-parent-by-type.js +0 -15
- package/utils/validate-store-name-convention.js +0 -13
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1233 @@
|
|
|
1
|
+
import { ASTUtils, AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
2
|
+
import { getContextualType, typeMatchesSpecifier } from "@typescript-eslint/type-utils";
|
|
3
|
+
import { isExpression } from "typescript";
|
|
4
|
+
import esquery from "esquery";
|
|
5
|
+
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();
|
|
10
|
+
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));
|
|
12
|
+
};
|
|
13
|
+
const isType = {
|
|
14
|
+
store: (type, program) => typeMatchesSpecifier(type, {
|
|
15
|
+
from: "package",
|
|
16
|
+
package: "effector",
|
|
17
|
+
name: ["Store", "StoreWritable"]
|
|
18
|
+
}, program),
|
|
19
|
+
event: (type, program) => typeMatchesSpecifier(type, {
|
|
20
|
+
from: "package",
|
|
21
|
+
package: "effector",
|
|
22
|
+
name: ["Event", "EventCallable"]
|
|
23
|
+
}, program),
|
|
24
|
+
effect: (type, program) => typeMatchesSpecifier(type, {
|
|
25
|
+
from: "package",
|
|
26
|
+
package: "effector",
|
|
27
|
+
name: "Effect"
|
|
28
|
+
}, program),
|
|
29
|
+
unit: (type, program) => {
|
|
30
|
+
return typeMatchesSpecifier(type, {
|
|
31
|
+
from: "package",
|
|
32
|
+
package: "effector",
|
|
33
|
+
name: [
|
|
34
|
+
"Store",
|
|
35
|
+
"StoreWritable",
|
|
36
|
+
"Event",
|
|
37
|
+
"EventCallable",
|
|
38
|
+
"Effect"
|
|
39
|
+
]
|
|
40
|
+
}, program);
|
|
41
|
+
},
|
|
42
|
+
domain: (type, program) => typeMatchesSpecifier(type, {
|
|
43
|
+
from: "package",
|
|
44
|
+
package: "effector",
|
|
45
|
+
name: "Domain"
|
|
46
|
+
}, program),
|
|
47
|
+
gate: (type) => {
|
|
48
|
+
const symbol = type.getSymbol() ?? type.aliasSymbol;
|
|
49
|
+
return symbol ? check(symbol, ["Gate"], "effector") : false;
|
|
50
|
+
},
|
|
51
|
+
jsx: (type, program) => {
|
|
52
|
+
return typeMatchesSpecifier(type, {
|
|
53
|
+
from: "package",
|
|
54
|
+
package: "react",
|
|
55
|
+
name: [
|
|
56
|
+
"Element",
|
|
57
|
+
"ReactNode",
|
|
58
|
+
"ReactElement"
|
|
59
|
+
]
|
|
60
|
+
}, program);
|
|
61
|
+
},
|
|
62
|
+
component: (type, program) => {
|
|
63
|
+
return typeMatchesSpecifier(type, {
|
|
64
|
+
from: "package",
|
|
65
|
+
package: "react",
|
|
66
|
+
name: [
|
|
67
|
+
"FC",
|
|
68
|
+
"FunctionComponent",
|
|
69
|
+
"ComponentType",
|
|
70
|
+
"ComponentClass",
|
|
71
|
+
"ForwardRefRenderFunction"
|
|
72
|
+
]
|
|
73
|
+
}, program);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var enforce_effect_naming_convention_default = createRule({
|
|
77
|
+
name: "enforce-effect-naming-convention",
|
|
78
|
+
meta: {
|
|
79
|
+
type: "problem",
|
|
80
|
+
docs: { description: "Enforce Fx as a suffix for any Effector Effect." },
|
|
81
|
+
messages: {
|
|
82
|
+
invalid: "Effect `{{ current }}` should be named with suffix, rename it to `{{ fixed }}`",
|
|
83
|
+
rename: "Rename \"{{ current }}\" to \"{{ fixed }}\""
|
|
84
|
+
},
|
|
85
|
+
schema: [],
|
|
86
|
+
hasSuggestions: true
|
|
87
|
+
},
|
|
88
|
+
defaultOptions: [],
|
|
89
|
+
create: (context) => {
|
|
90
|
+
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
|
+
} };
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
const FxRegex = /Fx$/;
|
|
118
|
+
var enforce_gate_naming_convention_default = createRule({
|
|
119
|
+
name: "enforce-gate-naming-convention",
|
|
120
|
+
meta: {
|
|
121
|
+
type: "problem",
|
|
122
|
+
docs: { description: "Enforce a Gate is named capitalized like a React Component" },
|
|
123
|
+
messages: {
|
|
124
|
+
invalid: "Gate \"{{ current }}\" should be named with first capital letter, rename it to \"{{ fixed }}\"",
|
|
125
|
+
rename: "Rename \"{{ current }}\" to \"{{ fixed }}\""
|
|
126
|
+
},
|
|
127
|
+
schema: [],
|
|
128
|
+
hasSuggestions: true
|
|
129
|
+
},
|
|
130
|
+
defaultOptions: [],
|
|
131
|
+
create: (context) => {
|
|
132
|
+
const services = ESLintUtils.getParserServices(context);
|
|
133
|
+
return { [`VariableDeclarator[id.name=${GateRegex}]`]: (node) => {
|
|
134
|
+
const type = services.getTypeAtLocation(node);
|
|
135
|
+
if (!isType.gate(type)) return;
|
|
136
|
+
const current = node.id.name;
|
|
137
|
+
const fixed = current[0].toUpperCase() + current.slice(1);
|
|
138
|
+
const data = {
|
|
139
|
+
current,
|
|
140
|
+
fixed
|
|
141
|
+
};
|
|
142
|
+
const suggestion = {
|
|
143
|
+
messageId: "rename",
|
|
144
|
+
data: {
|
|
145
|
+
current,
|
|
146
|
+
fixed
|
|
147
|
+
},
|
|
148
|
+
fix: (fixer) => fixer.replaceText(node.id, fixed)
|
|
149
|
+
};
|
|
150
|
+
context.report({
|
|
151
|
+
node: node.id,
|
|
152
|
+
messageId: "invalid",
|
|
153
|
+
data,
|
|
154
|
+
suggest: [suggestion]
|
|
155
|
+
});
|
|
156
|
+
} };
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
const GateRegex = /^[^A-Z]/;
|
|
160
|
+
var enforce_store_naming_convention_default = createRule({
|
|
161
|
+
name: "enforce-store-naming-convention",
|
|
162
|
+
meta: {
|
|
163
|
+
type: "problem",
|
|
164
|
+
docs: { description: "Enforce $ as a prefix/postfix for any Effector `Store`" },
|
|
165
|
+
messages: {
|
|
166
|
+
invalid: "Store \"{{ current }}\" should be named with a `$` {{ convention }}, rename it to \"{{ fixed }}\"",
|
|
167
|
+
rename: "Rename \"{{ current }}\" to \"{{ fixed }}\""
|
|
168
|
+
},
|
|
169
|
+
schema: [{
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: { mode: {
|
|
172
|
+
type: "string",
|
|
173
|
+
enum: ["prefix", "postfix"]
|
|
174
|
+
} }
|
|
175
|
+
}],
|
|
176
|
+
hasSuggestions: true
|
|
177
|
+
},
|
|
178
|
+
defaultOptions: [{ mode: "prefix" }],
|
|
179
|
+
create: (context, [options]) => {
|
|
180
|
+
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, "");
|
|
186
|
+
const fixed = options.mode === "prefix" ? `$${trimmed}` : `${trimmed}$`;
|
|
187
|
+
const data = {
|
|
188
|
+
current,
|
|
189
|
+
convention: options.mode,
|
|
190
|
+
fixed
|
|
191
|
+
};
|
|
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
|
+
} };
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
const PrefixRegex = /^[^$]/;
|
|
210
|
+
const PostfixRegex = /[^$]$/;
|
|
211
|
+
const PACKAGE_NAME$1 = {
|
|
212
|
+
core: /^effector(?:\u002Fcompat)?$/,
|
|
213
|
+
react: /^effector-react$/,
|
|
214
|
+
storage: /^@?effector-storage(\u002F[\w-]+)*$/
|
|
215
|
+
};
|
|
216
|
+
var keep_options_order_default = createRule({
|
|
217
|
+
name: "keep-options-order",
|
|
218
|
+
meta: {
|
|
219
|
+
type: "problem",
|
|
220
|
+
docs: { description: "Enforce options order for Effector methods" },
|
|
221
|
+
messages: {
|
|
222
|
+
invalidOrder: `Order of options should be \`{{ correctOrder }}\`, but found \`{{ currentOrder }}\`.`,
|
|
223
|
+
changeOrder: "Sort options to follow the recommended order."
|
|
224
|
+
},
|
|
225
|
+
schema: [],
|
|
226
|
+
hasSuggestions: true
|
|
227
|
+
},
|
|
228
|
+
defaultOptions: [],
|
|
229
|
+
create: (context) => {
|
|
230
|
+
const source = context.sourceCode;
|
|
231
|
+
const imports = /* @__PURE__ */ new Set();
|
|
232
|
+
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) => {
|
|
235
|
+
if (!imports.has(node.callee.name)) return;
|
|
236
|
+
const [config] = node.arguments;
|
|
237
|
+
if (config.properties.some((prop) => prop.type === AST_NODE_TYPES.SpreadElement || prop.key.type !== AST_NODE_TYPES.Identifier)) return;
|
|
238
|
+
const properties = config.properties;
|
|
239
|
+
const current = properties.map((prop) => prop.key.name);
|
|
240
|
+
if (isCorrectOrder(current)) return;
|
|
241
|
+
const correctOrder = TRUE_ORDER.filter((item) => current.includes(item));
|
|
242
|
+
const othersOrder = current.filter((item) => !TRUE_ORDER.includes(item));
|
|
243
|
+
const order = [...correctOrder, ...othersOrder];
|
|
244
|
+
const snippets = properties.toSorted((a, b) => order.indexOf(a.key.name) - order.indexOf(b.key.name)).map((prop) => source.getText(prop));
|
|
245
|
+
const suggestion = {
|
|
246
|
+
messageId: "changeOrder",
|
|
247
|
+
fix: (fixer) => [fixer.replaceText(config, `{ ${snippets.join(", ")} }`)]
|
|
248
|
+
};
|
|
249
|
+
const data = {
|
|
250
|
+
correctOrder: correctOrder.join(" -> "),
|
|
251
|
+
currentOrder: current.join(" -> ")
|
|
252
|
+
};
|
|
253
|
+
context.report({
|
|
254
|
+
node: config,
|
|
255
|
+
messageId: "invalidOrder",
|
|
256
|
+
data,
|
|
257
|
+
suggest: [suggestion]
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
const TRUE_ORDER = [
|
|
264
|
+
"clock",
|
|
265
|
+
"source",
|
|
266
|
+
"filter",
|
|
267
|
+
"fn",
|
|
268
|
+
"target",
|
|
269
|
+
"greedy",
|
|
270
|
+
"batch",
|
|
271
|
+
"name"
|
|
272
|
+
];
|
|
273
|
+
const selector$11 = {
|
|
274
|
+
method: `ImportSpecifier[imported.name=/(sample|guard)/]`,
|
|
275
|
+
call: `[callee.type="Identifier"][arguments.length=1]`,
|
|
276
|
+
argument: `ObjectExpression.arguments`
|
|
277
|
+
};
|
|
278
|
+
const isCorrectOrder = (current) => {
|
|
279
|
+
let seen = -1;
|
|
280
|
+
for (const item of current) {
|
|
281
|
+
const index = TRUE_ORDER.indexOf(item);
|
|
282
|
+
const placement = index === -1 ? Infinity : index;
|
|
283
|
+
if (placement <= seen) return false;
|
|
284
|
+
seen = placement;
|
|
285
|
+
}
|
|
286
|
+
return true;
|
|
287
|
+
};
|
|
288
|
+
function functionToName(node) {
|
|
289
|
+
if (node.id) return node.id;
|
|
290
|
+
if (node.parent.type === AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES.Identifier) return node.parent.id;
|
|
291
|
+
if (node.parent.type === AST_NODE_TYPES.AssignmentExpression && node.parent.left.type === AST_NODE_TYPES.Identifier) return node.parent.left;
|
|
292
|
+
if (node.parent.type === AST_NODE_TYPES.Property && node.parent.key.type === AST_NODE_TYPES.Identifier) return node.parent.key;
|
|
293
|
+
if (node.parent.type === AST_NODE_TYPES.AssignmentPattern && node.parent.left.type === AST_NODE_TYPES.Identifier) return node.parent.left;
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
const nameOf = { function: functionToName };
|
|
297
|
+
var mandatory_scope_binding_default = createRule({
|
|
298
|
+
name: "mandatory-scope-binding",
|
|
299
|
+
meta: {
|
|
300
|
+
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." },
|
|
303
|
+
schema: []
|
|
304
|
+
},
|
|
305
|
+
defaultOptions: [],
|
|
306
|
+
create: (context) => {
|
|
307
|
+
const services = ESLintUtils.getParserServices(context);
|
|
308
|
+
const checker = services.program.getTypeChecker();
|
|
309
|
+
const stack = {
|
|
310
|
+
render: [],
|
|
311
|
+
hook: []
|
|
312
|
+
};
|
|
313
|
+
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);
|
|
318
|
+
const tsnode = services.esTreeNodeToTSNodeMap.get(node);
|
|
319
|
+
const signature = checker.getSignatureFromDeclaration(tsnode);
|
|
320
|
+
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);
|
|
325
|
+
},
|
|
326
|
+
[`:matches(FunctionDeclaration, FunctionExpression, ArrowFunctionExpression):exit`]: () => void stack.render.pop(),
|
|
327
|
+
"ClassDeclaration": () => void stack.render.push(false),
|
|
328
|
+
"ClassDeclaration:exit": () => void stack.render.pop(),
|
|
329
|
+
"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);
|
|
342
|
+
},
|
|
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
|
+
}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
const UseRegex = /^use[A-Z0-9].*$/;
|
|
358
|
+
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
|
+
const locate = { property };
|
|
360
|
+
var no_ambiguity_target_default = createRule({
|
|
361
|
+
name: "no-ambiguity-target",
|
|
362
|
+
meta: {
|
|
363
|
+
type: "problem",
|
|
364
|
+
docs: { description: "Forbid ambiguous target in `sample` and `guard`." },
|
|
365
|
+
messages: { ambiguous: "Method `{{ method }}` both specifies `target` option and assigns the result to a variable. Consider removing one of them." },
|
|
366
|
+
schema: []
|
|
367
|
+
},
|
|
368
|
+
defaultOptions: [],
|
|
369
|
+
create: (context) => {
|
|
370
|
+
const imports = /* @__PURE__ */ new Set();
|
|
371
|
+
const importSelector = `ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`;
|
|
372
|
+
const usageStack = [];
|
|
373
|
+
return {
|
|
374
|
+
"ReturnStatement": () => usageStack.push(true),
|
|
375
|
+
"ReturnStatement:exit": () => usageStack.pop(),
|
|
376
|
+
"VariableDeclarator": () => usageStack.push(true),
|
|
377
|
+
"VariableDeclarator:exit": () => usageStack.pop(),
|
|
378
|
+
"ObjectExpression": () => usageStack.push(true),
|
|
379
|
+
"ObjectExpression:exit": () => usageStack.pop(),
|
|
380
|
+
"BlockStatement": () => usageStack.push(false),
|
|
381
|
+
"BlockStatement:exit": () => usageStack.pop(),
|
|
382
|
+
[`${importSelector} > ${selector$10.method}`]: (node) => imports.add(node.local.name),
|
|
383
|
+
[`CallExpression[callee.type="Identifier"]`]: (node) => {
|
|
384
|
+
if (!imports.has(node.callee.name)) return;
|
|
385
|
+
if (!(usageStack.at(-1) ?? false)) return;
|
|
386
|
+
const [config] = node.arguments;
|
|
387
|
+
if (config?.type !== AST_NODE_TYPES.ObjectExpression) return;
|
|
388
|
+
if (!locate.property("target", config)) return;
|
|
389
|
+
context.report({
|
|
390
|
+
node,
|
|
391
|
+
messageId: "ambiguous",
|
|
392
|
+
data: { method: node.callee.name }
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
const selector$10 = { method: `ImportSpecifier[imported.name=/(sample|guard)/]` };
|
|
399
|
+
var no_domain_unit_creators_default = createRule({
|
|
400
|
+
name: "no-domain-unit-creators",
|
|
401
|
+
meta: {
|
|
402
|
+
type: "suggestion",
|
|
403
|
+
docs: { description: "Disallow using Domain methods to create units." },
|
|
404
|
+
messages: { avoid: "Avoid using `.{{ method }}` on a Domain instance. Use a standard factory unit creator `{{ factory }}` with a `domain` option instead." },
|
|
405
|
+
schema: []
|
|
406
|
+
},
|
|
407
|
+
defaultOptions: [],
|
|
408
|
+
create: (context) => {
|
|
409
|
+
const services = ESLintUtils.getParserServices(context);
|
|
410
|
+
return { [`CallExpression:has(> ${selector$9.member})`]: (node) => {
|
|
411
|
+
const name$1 = node.callee.property.name;
|
|
412
|
+
if (!METHODS.has(name$1)) return;
|
|
413
|
+
const type = services.getTypeAtLocation(node.callee.object);
|
|
414
|
+
if (!isType.domain(type, services.program)) return;
|
|
415
|
+
const factory = ALIAS_MAP.get(name$1) ?? name$1;
|
|
416
|
+
context.report({
|
|
417
|
+
node,
|
|
418
|
+
messageId: "avoid",
|
|
419
|
+
data: {
|
|
420
|
+
method: name$1,
|
|
421
|
+
factory
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
} };
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
const ALIAS_MAP = (/* @__PURE__ */ new Map()).set("event", "createEvent").set("store", "createStore").set("effect", "createEffect").set("domain", "createDomain");
|
|
428
|
+
const METHODS = new Set([...ALIAS_MAP.values(), ...ALIAS_MAP.keys()]);
|
|
429
|
+
const selector$9 = { member: `MemberExpression.callee[property.type="Identifier"]` };
|
|
430
|
+
var no_duplicate_clock_or_source_array_values_default = createRule({
|
|
431
|
+
name: "no-duplicate-clock-or-source-array-values",
|
|
432
|
+
meta: {
|
|
433
|
+
type: "problem",
|
|
434
|
+
docs: { description: "Forbid providing duplicate units in `clock` and `source` arrays in `sample` and `guard`." },
|
|
435
|
+
messages: {
|
|
436
|
+
duplicate: "`{{ field }}` contains a duplicate unit `{{ unit }}`.",
|
|
437
|
+
remove: "Remove duplicate unit `{{ unit }}`."
|
|
438
|
+
},
|
|
439
|
+
schema: [],
|
|
440
|
+
hasSuggestions: true
|
|
441
|
+
},
|
|
442
|
+
defaultOptions: [],
|
|
443
|
+
create: (context) => {
|
|
444
|
+
const imports = /* @__PURE__ */ new Set();
|
|
445
|
+
const importSelector = `ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`;
|
|
446
|
+
const analyze = (node, field) => {
|
|
447
|
+
const seen = /* @__PURE__ */ new Map();
|
|
448
|
+
const entries = node.elements.filter((item) => item !== null).filter((item) => item.type !== AST_NODE_TYPES.SpreadElement);
|
|
449
|
+
for (const entry of entries) {
|
|
450
|
+
const root = traverseToRoot$1(entry);
|
|
451
|
+
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);
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
const report = (node, name$1, field) => {
|
|
458
|
+
const data = {
|
|
459
|
+
field,
|
|
460
|
+
unit: name$1
|
|
461
|
+
};
|
|
462
|
+
const suggestion = {
|
|
463
|
+
messageId: "remove",
|
|
464
|
+
data: { unit: name$1 },
|
|
465
|
+
fix: function* (fixer) {
|
|
466
|
+
yield fixer.remove(node);
|
|
467
|
+
const before = context.sourceCode.getTokenBefore(node);
|
|
468
|
+
if (before?.value === ",") yield fixer.remove(before);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
context.report({
|
|
472
|
+
node,
|
|
473
|
+
messageId: "duplicate",
|
|
474
|
+
data,
|
|
475
|
+
suggest: [suggestion]
|
|
476
|
+
});
|
|
477
|
+
};
|
|
478
|
+
return {
|
|
479
|
+
[`${importSelector} > ${selector$8.method}`]: (node) => imports.add(node.local.name),
|
|
480
|
+
[`CallExpression${selector$8.call}:has(${selector$8.argument})`]: (node) => {
|
|
481
|
+
if (!imports.has(node.callee.name)) return;
|
|
482
|
+
const [config] = node.arguments;
|
|
483
|
+
const clock = locate.property("clock", config);
|
|
484
|
+
const source = locate.property("source", config);
|
|
485
|
+
if (clock?.value?.type === AST_NODE_TYPES.ArrayExpression) analyze(clock.value, "clock");
|
|
486
|
+
if (source?.value?.type === AST_NODE_TYPES.ArrayExpression) analyze(source.value, "source");
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
const selector$8 = {
|
|
492
|
+
method: `ImportSpecifier[imported.name=/(sample|guard)/]`,
|
|
493
|
+
call: `[callee.type="Identifier"][arguments.length=1]`,
|
|
494
|
+
argument: `ObjectExpression.arguments`
|
|
495
|
+
};
|
|
496
|
+
function traverseToRoot$1(node, path = []) {
|
|
497
|
+
if (node.type === AST_NODE_TYPES.Identifier) return {
|
|
498
|
+
node,
|
|
499
|
+
path
|
|
500
|
+
};
|
|
501
|
+
if (node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier) return traverseToRoot$1(node.object, [node.property.name, ...path]);
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
var no_duplicate_on_default = createRule({
|
|
505
|
+
name: "no-duplicate-on",
|
|
506
|
+
meta: {
|
|
507
|
+
type: "problem",
|
|
508
|
+
docs: { description: "Forbid duplicate `.on` calls on Stores." },
|
|
509
|
+
messages: { duplicate: "Method `.on` is called on store `{{ store }}` more than once for `{{ unit }}`." },
|
|
510
|
+
schema: []
|
|
511
|
+
},
|
|
512
|
+
defaultOptions: [],
|
|
513
|
+
create: (context) => {
|
|
514
|
+
const services = ESLintUtils.getParserServices(context);
|
|
515
|
+
const map = /* @__PURE__ */ new Map();
|
|
516
|
+
return { [`CallExpression[callee.property.name="on"]`]: (node) => {
|
|
517
|
+
const type = services.getTypeAtLocation(node.callee.object);
|
|
518
|
+
if (!isType.store(type, services.program)) return;
|
|
519
|
+
const arg = node.arguments[0];
|
|
520
|
+
if (!arg || arg.type === AST_NODE_TYPES.SpreadElement) return;
|
|
521
|
+
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);
|
|
524
|
+
if (!store) return;
|
|
525
|
+
const set = map.get(store.id) ?? /* @__PURE__ */ new Set();
|
|
526
|
+
for (const unit of units) {
|
|
527
|
+
const instance = identify("unit", unit, scope$1);
|
|
528
|
+
if (!instance) continue;
|
|
529
|
+
if (set.has(instance.id)) {
|
|
530
|
+
const data = {
|
|
531
|
+
store: store.name,
|
|
532
|
+
unit: instance.name
|
|
533
|
+
};
|
|
534
|
+
context.report({
|
|
535
|
+
messageId: "duplicate",
|
|
536
|
+
node: unit,
|
|
537
|
+
data
|
|
538
|
+
});
|
|
539
|
+
} else set.add(instance.id);
|
|
540
|
+
}
|
|
541
|
+
map.set(store.id, set);
|
|
542
|
+
} };
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
function traverseToRoot(node, path = []) {
|
|
546
|
+
if (node.type === AST_NODE_TYPES.Identifier) return {
|
|
547
|
+
node,
|
|
548
|
+
path
|
|
549
|
+
};
|
|
550
|
+
if (node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier) return traverseToRoot(node.object, [node.property.name, ...path]);
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
const STORE_METHODS = ["on", "reset"];
|
|
554
|
+
function traverseStoreToRoot(node, path = []) {
|
|
555
|
+
if (node.type === AST_NODE_TYPES.Identifier) return {
|
|
556
|
+
node,
|
|
557
|
+
path
|
|
558
|
+
};
|
|
559
|
+
if (node.type === AST_NODE_TYPES.MemberExpression && node.property.type === AST_NODE_TYPES.Identifier) return traverseStoreToRoot(node.object, [node.property.name, ...path]);
|
|
560
|
+
if (node.type === AST_NODE_TYPES.CallExpression && node.callee.type === AST_NODE_TYPES.MemberExpression) {
|
|
561
|
+
if (node.callee.property.type === AST_NODE_TYPES.Identifier && STORE_METHODS.includes(node.callee.property.name)) return traverseStoreToRoot(node.callee.object, path);
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
function raiseStoreToVariable(node) {
|
|
566
|
+
let current = node;
|
|
567
|
+
while (current.parent) {
|
|
568
|
+
if (current.parent.type === AST_NODE_TYPES.VariableDeclarator) return current.parent;
|
|
569
|
+
if (current.parent.type !== AST_NODE_TYPES.MemberExpression || current.parent.object !== current) return null;
|
|
570
|
+
if (current.parent.property.type !== AST_NODE_TYPES.Identifier || !STORE_METHODS.includes(current.parent.property.name)) return null;
|
|
571
|
+
const grandparent = current.parent.parent;
|
|
572
|
+
if (grandparent?.type !== AST_NODE_TYPES.CallExpression || grandparent.callee !== current.parent) return null;
|
|
573
|
+
current = current.parent.parent;
|
|
574
|
+
}
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
function findSuitableRoot(type, node) {
|
|
578
|
+
if (type === "unit") return traverseToRoot(node);
|
|
579
|
+
const root = traverseStoreToRoot(node);
|
|
580
|
+
if (root) return root;
|
|
581
|
+
const declarator = raiseStoreToVariable(node);
|
|
582
|
+
if (declarator && declarator.id.type === AST_NODE_TYPES.Identifier) return {
|
|
583
|
+
node: declarator.id,
|
|
584
|
+
path: []
|
|
585
|
+
};
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
function identify(type, node, scope$1) {
|
|
589
|
+
const root = findSuitableRoot(type, node);
|
|
590
|
+
if (!root) return null;
|
|
591
|
+
const variable = ASTUtils.findVariable(scope$1, root.node);
|
|
592
|
+
if (!variable) return null;
|
|
593
|
+
return {
|
|
594
|
+
id: `${variable.$id}+${root.path.join(".")}`,
|
|
595
|
+
name: [variable.name, ...root.path].join(".")
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
var no_forward_default = createRule({
|
|
599
|
+
name: "no-forward",
|
|
600
|
+
meta: {
|
|
601
|
+
type: "problem",
|
|
602
|
+
docs: { description: "Prefer `sample` over `forward`." },
|
|
603
|
+
messages: {
|
|
604
|
+
noForward: "Use `sample` operator instead of `forward` as a more universal approach.",
|
|
605
|
+
replaceWithSample: "Replace `forward` with `sample`."
|
|
606
|
+
},
|
|
607
|
+
hasSuggestions: true,
|
|
608
|
+
schema: []
|
|
609
|
+
},
|
|
610
|
+
defaultOptions: [],
|
|
611
|
+
create: (context) => {
|
|
612
|
+
let sample;
|
|
613
|
+
const forwards = /* @__PURE__ */ new Map();
|
|
614
|
+
const source = context.sourceCode;
|
|
615
|
+
const visitorKeys = source.visitorKeys;
|
|
616
|
+
const importSelector = `ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`;
|
|
617
|
+
return {
|
|
618
|
+
[`${importSelector} > ${selector$7.forward}`]: (node) => forwards.set(node.local.name, node),
|
|
619
|
+
[`${importSelector} > ${selector$7.sample}`]: (node) => sample = node.local.name,
|
|
620
|
+
[`CallExpression${selector$7.call}:has(${selector$7.argument})`]: (node) => {
|
|
621
|
+
if (!forwards.has(node.callee.name)) return;
|
|
622
|
+
const config = {};
|
|
623
|
+
const arg = node.arguments[0];
|
|
624
|
+
config.clock = locate.property("from", arg)?.value;
|
|
625
|
+
config.target = locate.property("to", arg)?.value;
|
|
626
|
+
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);
|
|
628
|
+
if (call) [config.target, config.fn] = [call.callee.object, call.arguments[0]];
|
|
629
|
+
}
|
|
630
|
+
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);
|
|
632
|
+
if (call) [config.clock, config.fn] = [call.callee.object, call.arguments[0]];
|
|
633
|
+
}
|
|
634
|
+
const code = [
|
|
635
|
+
"clock",
|
|
636
|
+
"fn",
|
|
637
|
+
"target"
|
|
638
|
+
].filter((key) => config[key] !== void 0).map((key) => `${key}: ${source.getText(config[key])}`).join(", ");
|
|
639
|
+
context.report({
|
|
640
|
+
messageId: "noForward",
|
|
641
|
+
node: node.callee,
|
|
642
|
+
suggest: [{
|
|
643
|
+
messageId: "replaceWithSample",
|
|
644
|
+
fix: function* (fixer) {
|
|
645
|
+
const fn = sample ?? "sample";
|
|
646
|
+
yield fixer.replaceText(node, `${fn}({ ${code} })`);
|
|
647
|
+
if (!sample) yield fixer.insertTextAfter(forwards.get(node.callee.name), `, sample`);
|
|
648
|
+
}
|
|
649
|
+
}]
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
const selector$7 = {
|
|
656
|
+
forward: `ImportSpecifier[imported.name="forward"]`,
|
|
657
|
+
sample: `ImportSpecifier[imported.name="sample"]`,
|
|
658
|
+
call: `[callee.type="Identifier"][arguments.length=1]`,
|
|
659
|
+
argument: `ObjectExpression.arguments`
|
|
660
|
+
};
|
|
661
|
+
const query$2 = {
|
|
662
|
+
map: esquery.parse("CallExpression[arguments.length=1]:has(> :first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='map']))"),
|
|
663
|
+
prepend: esquery.parse("CallExpression[arguments.length=1]:has(> :first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='prepend']))")
|
|
664
|
+
};
|
|
665
|
+
var no_getState_default = createRule({
|
|
666
|
+
name: "no-getState",
|
|
667
|
+
meta: {
|
|
668
|
+
type: "problem",
|
|
669
|
+
docs: { description: "Forbid `.getState` calls on Effector stores." },
|
|
670
|
+
messages: {
|
|
671
|
+
named: "Method `.getState` used on store `{{ name }}` can lead to race conditions. Replace with with `sample` or `attach`.",
|
|
672
|
+
anonymous: "Method `.getState` used on store can lead to race conditions. Replace with with `sample` or `attach`."
|
|
673
|
+
},
|
|
674
|
+
schema: []
|
|
675
|
+
},
|
|
676
|
+
defaultOptions: [],
|
|
677
|
+
create: (context) => {
|
|
678
|
+
const services = ESLintUtils.getParserServices(context);
|
|
679
|
+
return { [`CallExpression[callee.type="MemberExpression"][callee.property.name="getState"]`]: (node) => {
|
|
680
|
+
const type = services.getTypeAtLocation(node.callee.object);
|
|
681
|
+
if (!isType.store(type, services.program)) return;
|
|
682
|
+
const name$1 = toName$1(node.callee.object);
|
|
683
|
+
if (name$1) context.report({
|
|
684
|
+
node,
|
|
685
|
+
messageId: "named",
|
|
686
|
+
data: { name: name$1 }
|
|
687
|
+
});
|
|
688
|
+
else context.report({
|
|
689
|
+
node,
|
|
690
|
+
messageId: "anonymous"
|
|
691
|
+
});
|
|
692
|
+
} };
|
|
693
|
+
}
|
|
694
|
+
});
|
|
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
|
+
};
|
|
700
|
+
var no_guard_default = createRule({
|
|
701
|
+
name: "no-guard",
|
|
702
|
+
meta: {
|
|
703
|
+
type: "problem",
|
|
704
|
+
docs: { description: "Prefer `sample` over `guard`." },
|
|
705
|
+
messages: {
|
|
706
|
+
noGuard: "Use `sample` operator instead of `guard` as a more universal approach.",
|
|
707
|
+
replaceWithSample: "Replace `guard` with `sample`."
|
|
708
|
+
},
|
|
709
|
+
hasSuggestions: true,
|
|
710
|
+
schema: []
|
|
711
|
+
},
|
|
712
|
+
defaultOptions: [],
|
|
713
|
+
create: (context) => {
|
|
714
|
+
let sample;
|
|
715
|
+
const guards = /* @__PURE__ */ new Map();
|
|
716
|
+
const source = context.sourceCode;
|
|
717
|
+
const visitorKeys = source.visitorKeys;
|
|
718
|
+
const importSelector = `ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`;
|
|
719
|
+
return {
|
|
720
|
+
[`${importSelector} > ${selector$6.guard}`]: (node) => guards.set(node.local.name, node),
|
|
721
|
+
[`${importSelector} > ${selector$6.sample}`]: (node) => sample = node.local.name,
|
|
722
|
+
[`CallExpression${selector$6.call}`]: (node) => {
|
|
723
|
+
if (!guards.has(node.callee.name)) return;
|
|
724
|
+
const config = {};
|
|
725
|
+
if (node.arguments.length === 1 && node.arguments[0].type === AST_NODE_TYPES.ObjectExpression) {
|
|
726
|
+
const [arg] = node.arguments;
|
|
727
|
+
for (const key of [
|
|
728
|
+
"clock",
|
|
729
|
+
"source",
|
|
730
|
+
"filter",
|
|
731
|
+
"target"
|
|
732
|
+
]) config[key] = locate.property(key, arg)?.value;
|
|
733
|
+
} else if (node.arguments.length === 2 && node.arguments[1].type === AST_NODE_TYPES.ObjectExpression) {
|
|
734
|
+
const [clock, arg] = node.arguments;
|
|
735
|
+
config.clock = clock;
|
|
736
|
+
for (const key of [
|
|
737
|
+
"source",
|
|
738
|
+
"filter",
|
|
739
|
+
"target"
|
|
740
|
+
]) config[key] = locate.property(key, arg)?.value;
|
|
741
|
+
} else return;
|
|
742
|
+
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);
|
|
744
|
+
if (call) [config.target, config.fn] = [call.callee.object, call.arguments[0]];
|
|
745
|
+
}
|
|
746
|
+
const code = [
|
|
747
|
+
"clock",
|
|
748
|
+
"source",
|
|
749
|
+
"filter",
|
|
750
|
+
"fn",
|
|
751
|
+
"target"
|
|
752
|
+
].filter((key) => config[key] !== void 0).map((key) => `${key}: ${source.getText(config[key])}`).join(", ");
|
|
753
|
+
context.report({
|
|
754
|
+
messageId: "noGuard",
|
|
755
|
+
node: node.callee,
|
|
756
|
+
suggest: [{
|
|
757
|
+
messageId: "replaceWithSample",
|
|
758
|
+
fix: function* (fixer) {
|
|
759
|
+
const fn = sample ?? "sample";
|
|
760
|
+
yield fixer.replaceText(node, `${fn}({ ${code} })`);
|
|
761
|
+
if (!sample) yield fixer.insertTextAfter(guards.get(node.callee.name), `, sample`);
|
|
762
|
+
}
|
|
763
|
+
}]
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
const selector$6 = {
|
|
770
|
+
guard: `ImportSpecifier[imported.name="guard"]`,
|
|
771
|
+
sample: `ImportSpecifier[imported.name="sample"]`,
|
|
772
|
+
call: `[callee.type="Identifier"]`
|
|
773
|
+
};
|
|
774
|
+
const query$1 = { prepend: esquery.parse("CallExpression[arguments.length=1]:has(:first-child:expression.arguments):has(> MemberExpression.callee:has(Identifier.property[name='prepend']))") };
|
|
775
|
+
var no_patronum_debug_default = createRule({
|
|
776
|
+
name: "no-patronum-debug",
|
|
777
|
+
meta: {
|
|
778
|
+
type: "problem",
|
|
779
|
+
docs: { description: "Disallow the use of `patronum` `debug`." },
|
|
780
|
+
messages: {
|
|
781
|
+
unexpected: "Unexpected `debug` call.",
|
|
782
|
+
remove: "Remove this `debug` call."
|
|
783
|
+
},
|
|
784
|
+
schema: [],
|
|
785
|
+
hasSuggestions: true
|
|
786
|
+
},
|
|
787
|
+
defaultOptions: [],
|
|
788
|
+
create: (context) => {
|
|
789
|
+
const debugs = /* @__PURE__ */ new Set();
|
|
790
|
+
return {
|
|
791
|
+
[`${`ImportDeclaration[source.value=${PACKAGE_NAME}]`} > ${selector$5.debug}`]: (node) => debugs.add(node.local.name),
|
|
792
|
+
[`CallExpression:matches(${selector$5.call})`]: (node) => {
|
|
793
|
+
const name$1 = toName(node);
|
|
794
|
+
if (!debugs.has(name$1)) return;
|
|
795
|
+
context.report({
|
|
796
|
+
messageId: "unexpected",
|
|
797
|
+
node: node.callee,
|
|
798
|
+
suggest: [{
|
|
799
|
+
messageId: "remove",
|
|
800
|
+
fix: (fixer) => {
|
|
801
|
+
if (node.parent.type === AST_NODE_TYPES.ExpressionStatement) return fixer.remove(node.parent);
|
|
802
|
+
else return fixer.replaceText(node, "undefined");
|
|
803
|
+
}
|
|
804
|
+
}]
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
const PACKAGE_NAME = /^patronum(?:\u002Fdebug)?$/;
|
|
811
|
+
const selector$5 = {
|
|
812
|
+
debug: `ImportSpecifier[imported.name="debug"]`,
|
|
813
|
+
call: `[callee.type=Identifier], [callee.object.type=Identifier]`
|
|
814
|
+
};
|
|
815
|
+
const toName = (node) => {
|
|
816
|
+
switch (node.callee.type) {
|
|
817
|
+
case AST_NODE_TYPES.Identifier: return node.callee.name;
|
|
818
|
+
case AST_NODE_TYPES.MemberExpression: return node.callee.object.name;
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
var no_unnecessary_combination_default = createRule({
|
|
822
|
+
name: "no-unnecessary-combination",
|
|
823
|
+
meta: {
|
|
824
|
+
type: "suggestion",
|
|
825
|
+
docs: { description: "Forbid unnecessary combinations in `clock` and `source`." },
|
|
826
|
+
messages: { unnecessary: "{{ method }} is used under the hood of {{ property }} in {{ operator }}, you can omit it." },
|
|
827
|
+
schema: []
|
|
828
|
+
},
|
|
829
|
+
defaultOptions: [],
|
|
830
|
+
create: (context) => {
|
|
831
|
+
const services = ESLintUtils.getParserServices(context);
|
|
832
|
+
const operators = /* @__PURE__ */ new Set();
|
|
833
|
+
const combinators = /* @__PURE__ */ new Map();
|
|
834
|
+
const importSelector = `ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`;
|
|
835
|
+
return {
|
|
836
|
+
[`${importSelector} > ${selector$4.operator}`]: (node) => operators.add(node.local.name),
|
|
837
|
+
[`${importSelector} > ${selector$4.combinator}`]: (node) => combinators.set(node.local.name, node.imported.name),
|
|
838
|
+
[`CallExpression${selector$4.call}:has(${selector$4.argument})`]: (node) => {
|
|
839
|
+
if (!operators.has(node.callee.name)) return;
|
|
840
|
+
const [config] = node.arguments;
|
|
841
|
+
const clock = locate.property("clock", config)?.value;
|
|
842
|
+
const source = locate.property("source", config)?.value;
|
|
843
|
+
if (clock?.type === AST_NODE_TYPES.CallExpression && clock.callee.type === AST_NODE_TYPES.Identifier) {
|
|
844
|
+
if (combinators.get(clock.callee.name) === "merge") {
|
|
845
|
+
const data = {
|
|
846
|
+
method: clock.callee.name,
|
|
847
|
+
property: "clock",
|
|
848
|
+
operator: node.callee.name
|
|
849
|
+
};
|
|
850
|
+
context.report({
|
|
851
|
+
node: clock,
|
|
852
|
+
messageId: "unnecessary",
|
|
853
|
+
data
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (source?.type === AST_NODE_TYPES.CallExpression && source.callee.type === AST_NODE_TYPES.Identifier) {
|
|
858
|
+
const method = combinators.get(source.callee.name);
|
|
859
|
+
if (!method) return;
|
|
860
|
+
if (method === "combine" && source.arguments.length > 1 && isFunction(source.arguments.at(-1), services)) return;
|
|
861
|
+
const data = {
|
|
862
|
+
method: source.callee.name,
|
|
863
|
+
property: "source",
|
|
864
|
+
operator: node.callee.name
|
|
865
|
+
};
|
|
866
|
+
context.report({
|
|
867
|
+
node: source,
|
|
868
|
+
messageId: "unnecessary",
|
|
869
|
+
data
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
const selector$4 = {
|
|
877
|
+
operator: `ImportSpecifier[imported.name=/(sample|guard)/]`,
|
|
878
|
+
combinator: `ImportSpecifier[imported.name=/(combine|merge)/]`,
|
|
879
|
+
call: `[callee.type="Identifier"][arguments.length=1]`,
|
|
880
|
+
argument: `ObjectExpression.arguments`
|
|
881
|
+
};
|
|
882
|
+
function isFunction(node, services) {
|
|
883
|
+
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) return true;
|
|
884
|
+
else if (node.type === AST_NODE_TYPES.FunctionExpression) return true;
|
|
885
|
+
else if (node.type === AST_NODE_TYPES.Identifier) {
|
|
886
|
+
const checker = services.program.getTypeChecker();
|
|
887
|
+
const tsnode = services.esTreeNodeToTSNodeMap.get(node);
|
|
888
|
+
return checker.getTypeAtLocation(tsnode).getCallSignatures().length > 0;
|
|
889
|
+
} else return false;
|
|
890
|
+
}
|
|
891
|
+
var no_unnecessary_duplication_default = createRule({
|
|
892
|
+
name: "no-unnecessary-duplication",
|
|
893
|
+
meta: {
|
|
894
|
+
type: "problem",
|
|
895
|
+
docs: { description: "Forbid duplicate `source` and `clock` in `sample` and `guard`." },
|
|
896
|
+
messages: {
|
|
897
|
+
duplicate: "Method `{{ method }}` has the same value for `source` and `clock`. Consider using only one of them.",
|
|
898
|
+
removeClock: "Remove the `clock`",
|
|
899
|
+
removeSource: "Remove the `source`"
|
|
900
|
+
},
|
|
901
|
+
schema: [],
|
|
902
|
+
hasSuggestions: true
|
|
903
|
+
},
|
|
904
|
+
defaultOptions: [],
|
|
905
|
+
create: (context) => {
|
|
906
|
+
const imports = /* @__PURE__ */ new Set();
|
|
907
|
+
return {
|
|
908
|
+
[`${`ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`} > ${selector$3.method}`]: (node) => imports.add(node.local.name),
|
|
909
|
+
[`CallExpression${selector$3.call}:has(${selector$3.argument})`]: (node) => {
|
|
910
|
+
if (!imports.has(node.callee.name)) return;
|
|
911
|
+
const [config] = node.arguments;
|
|
912
|
+
const source = locate.property("source", config)?.value;
|
|
913
|
+
if (!source) return;
|
|
914
|
+
const clock = locate.property("clock", config)?.value;
|
|
915
|
+
if (!clock) return;
|
|
916
|
+
if (!compare(clock, source)) return;
|
|
917
|
+
const suggestions = [{
|
|
918
|
+
messageId: "removeClock",
|
|
919
|
+
fix: function* (fixer) {
|
|
920
|
+
yield fixer.remove(clock.parent);
|
|
921
|
+
const after = context.sourceCode.getTokenAfter(clock.parent);
|
|
922
|
+
if (after?.value === ",") yield fixer.remove(after);
|
|
923
|
+
}
|
|
924
|
+
}, {
|
|
925
|
+
messageId: "removeSource",
|
|
926
|
+
fix: function* (fixer) {
|
|
927
|
+
yield fixer.remove(source.parent);
|
|
928
|
+
const after = context.sourceCode.getTokenAfter(source.parent);
|
|
929
|
+
if (after?.value === ",") yield fixer.remove(after);
|
|
930
|
+
}
|
|
931
|
+
}];
|
|
932
|
+
const data = { method: node.callee.name };
|
|
933
|
+
context.report({
|
|
934
|
+
node: config,
|
|
935
|
+
messageId: "duplicate",
|
|
936
|
+
data,
|
|
937
|
+
suggest: suggestions
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
const selector$3 = {
|
|
944
|
+
method: `ImportSpecifier[imported.name=/(sample|guard)/]`,
|
|
945
|
+
call: `[callee.type="Identifier"][arguments.length=1]`,
|
|
946
|
+
argument: `ObjectExpression.arguments`
|
|
947
|
+
};
|
|
948
|
+
function compare(clock, source, limit = 5) {
|
|
949
|
+
if (limit <= 0) return false;
|
|
950
|
+
if (clock.type === AST_NODE_TYPES.Identifier) return source.type === AST_NODE_TYPES.Identifier && clock.name === source.name;
|
|
951
|
+
if (clock.type === AST_NODE_TYPES.ArrayExpression) {
|
|
952
|
+
if (clock.elements.length !== 1) return false;
|
|
953
|
+
let a, b;
|
|
954
|
+
if (source.type === AST_NODE_TYPES.ArrayExpression) if (source.elements.length !== 1) return false;
|
|
955
|
+
else [a, b] = [clock.elements[0], source.elements[0]];
|
|
956
|
+
else [a, b] = [clock.elements[0], source];
|
|
957
|
+
return a.type === AST_NODE_TYPES.Identifier && b.type === AST_NODE_TYPES.Identifier && a.name === b.name;
|
|
958
|
+
}
|
|
959
|
+
if (clock.type === AST_NODE_TYPES.MemberExpression) {
|
|
960
|
+
if (source.type !== AST_NODE_TYPES.MemberExpression) return false;
|
|
961
|
+
if (clock.computed || source.computed) return false;
|
|
962
|
+
if (clock.property.name !== source.property.name) return false;
|
|
963
|
+
return compare(clock.object, source.object, limit - 1);
|
|
964
|
+
}
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
var no_useless_methods_default = createRule({
|
|
968
|
+
name: "no-useless-methods",
|
|
969
|
+
meta: {
|
|
970
|
+
type: "problem",
|
|
971
|
+
docs: { description: "Forbid useless calls of `sample` and `guard`." },
|
|
972
|
+
messages: { uselessMethod: "Method `{{ method }}` does nothing in this case. You should assign the result to variable or pass `target` to it." },
|
|
973
|
+
schema: []
|
|
974
|
+
},
|
|
975
|
+
defaultOptions: [],
|
|
976
|
+
create: (context) => {
|
|
977
|
+
const imports = /* @__PURE__ */ new Set();
|
|
978
|
+
const source = context.sourceCode;
|
|
979
|
+
const visitorKeys = source.visitorKeys;
|
|
980
|
+
const importSelector = `ImportDeclaration[source.value=${PACKAGE_NAME$1.core}]`;
|
|
981
|
+
const usageStack = [];
|
|
982
|
+
return {
|
|
983
|
+
"ReturnStatement": () => usageStack.push(true),
|
|
984
|
+
"ReturnStatement:exit": () => usageStack.pop(),
|
|
985
|
+
"VariableDeclarator": () => usageStack.push(true),
|
|
986
|
+
"VariableDeclarator:exit": () => usageStack.pop(),
|
|
987
|
+
"ObjectExpression": () => usageStack.push(true),
|
|
988
|
+
"ObjectExpression:exit": () => usageStack.pop(),
|
|
989
|
+
"BlockStatement": () => usageStack.push(false),
|
|
990
|
+
"BlockStatement:exit": () => usageStack.pop(),
|
|
991
|
+
[`${importSelector} > ${selector$2.method}`]: (node) => imports.add(node.local.name),
|
|
992
|
+
[`CallExpression[callee.type="Identifier"]`]: (node) => {
|
|
993
|
+
if (!imports.has(node.callee.name)) return;
|
|
994
|
+
if (usageStack.at(-1) ?? false) return;
|
|
995
|
+
if (node.parent.type === AST_NODE_TYPES.CallExpression) return;
|
|
996
|
+
const [config] = node.arguments;
|
|
997
|
+
if (config?.type === AST_NODE_TYPES.ObjectExpression) {
|
|
998
|
+
if (locate.property("target", config)?.value) return;
|
|
999
|
+
}
|
|
1000
|
+
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
|
+
}
|
|
1005
|
+
const method = node.callee.name;
|
|
1006
|
+
context.report({
|
|
1007
|
+
node,
|
|
1008
|
+
messageId: "uselessMethod",
|
|
1009
|
+
data: { method }
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
const selector$2 = { method: `ImportSpecifier[imported.name=/(sample|guard)/]` };
|
|
1016
|
+
const query = { watch: esquery.parse("CallExpression:has(> MemberExpression.callee[property.name=watch]:has(> CallExpression.object))") };
|
|
1017
|
+
var no_watch_default = createRule({
|
|
1018
|
+
name: "no-watch",
|
|
1019
|
+
meta: {
|
|
1020
|
+
type: "suggestion",
|
|
1021
|
+
docs: { description: "Restrict usage of `.watch` on any Effector Unit." },
|
|
1022
|
+
messages: { restricted: "Using `.watch` method leads to imperative code. Replace it with an operator `sample` or use the `target` parameter of `sample` operator." },
|
|
1023
|
+
schema: []
|
|
1024
|
+
},
|
|
1025
|
+
defaultOptions: [],
|
|
1026
|
+
create: (context) => {
|
|
1027
|
+
const services = ESLintUtils.getParserServices(context);
|
|
1028
|
+
return { [`CallExpression[callee.type="MemberExpression"][callee.property.name="watch"]`]: (node) => {
|
|
1029
|
+
const type = services.getTypeAtLocation(node.callee.object);
|
|
1030
|
+
if (!isType.unit(type, services.program)) return;
|
|
1031
|
+
context.report({
|
|
1032
|
+
node,
|
|
1033
|
+
messageId: "restricted"
|
|
1034
|
+
});
|
|
1035
|
+
} };
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
var prefer_useUnit_default = createRule({
|
|
1039
|
+
name: "prefer-useUnit",
|
|
1040
|
+
meta: {
|
|
1041
|
+
type: "suggestion",
|
|
1042
|
+
docs: { description: "Prefer `useUnit` over deprecated `useStore` and `useEvent` hooks." },
|
|
1043
|
+
messages: { useUseUnit: "`{{ name }}` should be replaced with `useUnit`." },
|
|
1044
|
+
schema: []
|
|
1045
|
+
},
|
|
1046
|
+
defaultOptions: [],
|
|
1047
|
+
create: (context) => {
|
|
1048
|
+
const imports = /* @__PURE__ */ new Map();
|
|
1049
|
+
const importSelector = `ImportDeclaration[source.value=${PACKAGE_NAME$1.react}]`;
|
|
1050
|
+
return {
|
|
1051
|
+
[`${importSelector} > ${selector$1.useStore}`]: (node) => void imports.set(node.local.name, "useStore"),
|
|
1052
|
+
[`${importSelector} > ${selector$1.useEvent}`]: (node) => void imports.set(node.local.name, "useEvent"),
|
|
1053
|
+
[`CallExpression[callee.type="Identifier"]`]: (node) => {
|
|
1054
|
+
const hook = imports.get(node.callee.name);
|
|
1055
|
+
if (!hook) return;
|
|
1056
|
+
context.report({
|
|
1057
|
+
node,
|
|
1058
|
+
messageId: "useUseUnit",
|
|
1059
|
+
data: { name: hook }
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
const selector$1 = {
|
|
1066
|
+
useStore: `ImportSpecifier[imported.name=useStore]`,
|
|
1067
|
+
useEvent: `ImportSpecifier[imported.name=useEvent]`
|
|
1068
|
+
};
|
|
1069
|
+
var require_pickup_in_persist_default = createRule({
|
|
1070
|
+
name: "require-pickup-in-persist",
|
|
1071
|
+
meta: {
|
|
1072
|
+
type: "problem",
|
|
1073
|
+
docs: { description: "Require every `persist` call of `effector-storage` to use `pickup`." },
|
|
1074
|
+
messages: { missing: "This `persist` call does not specify a `pickup` event that is required for scoped usage of `effector-storage`." },
|
|
1075
|
+
schema: []
|
|
1076
|
+
},
|
|
1077
|
+
defaultOptions: [],
|
|
1078
|
+
create: (context) => {
|
|
1079
|
+
const imports = /* @__PURE__ */ new Set();
|
|
1080
|
+
return {
|
|
1081
|
+
[`${`ImportDeclaration[source.value=${PACKAGE_NAME$1.storage}]`} > ${selector.persist}`]: (node) => imports.add(node.local.name),
|
|
1082
|
+
[`CallExpression${selector.call}${selector.config}`]: (node) => {
|
|
1083
|
+
if (!imports.has(node.callee.name)) return;
|
|
1084
|
+
if (node.arguments[0].properties.filter((prop) => prop.type === AST_NODE_TYPES.Property).map((prop) => prop.key).filter((key) => key.type === AST_NODE_TYPES.Identifier).some((key) => key.name === "pickup")) return;
|
|
1085
|
+
context.report({
|
|
1086
|
+
node,
|
|
1087
|
+
messageId: "missing"
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
const selector = {
|
|
1094
|
+
persist: `ImportSpecifier[imported.name="persist"]`,
|
|
1095
|
+
call: `[callee.type="Identifier"]`,
|
|
1096
|
+
config: `[arguments.length=1][arguments.0.type="ObjectExpression"]`
|
|
1097
|
+
};
|
|
1098
|
+
var strict_effect_handlers_default = createRule({
|
|
1099
|
+
name: "strict-effect-handlers",
|
|
1100
|
+
meta: {
|
|
1101
|
+
type: "problem",
|
|
1102
|
+
docs: { description: "Forbid mixing calls to both regular async functions and Effects in the same function." },
|
|
1103
|
+
messages: { mixed: "This function can lead to losing Scope in Effector Fork API." },
|
|
1104
|
+
schema: []
|
|
1105
|
+
},
|
|
1106
|
+
defaultOptions: [],
|
|
1107
|
+
create: (context) => {
|
|
1108
|
+
const services = ESLintUtils.getParserServices(context);
|
|
1109
|
+
const stack = [];
|
|
1110
|
+
const track = (node) => {
|
|
1111
|
+
const current = stack.at(-1);
|
|
1112
|
+
if (!current) return;
|
|
1113
|
+
const callee = node.argument.callee;
|
|
1114
|
+
const type = services.getTypeAtLocation(callee);
|
|
1115
|
+
if (isType.effect(type, services.program)) return current.effect = true;
|
|
1116
|
+
else return current.regular = true;
|
|
1117
|
+
};
|
|
1118
|
+
const enter = () => {
|
|
1119
|
+
stack.push({
|
|
1120
|
+
effect: false,
|
|
1121
|
+
regular: false
|
|
1122
|
+
});
|
|
1123
|
+
};
|
|
1124
|
+
const exit = (node) => {
|
|
1125
|
+
const scope$1 = stack.pop();
|
|
1126
|
+
if (!scope$1) return;
|
|
1127
|
+
if (scope$1.effect && scope$1.regular) context.report({
|
|
1128
|
+
node,
|
|
1129
|
+
messageId: "mixed"
|
|
1130
|
+
});
|
|
1131
|
+
};
|
|
1132
|
+
return {
|
|
1133
|
+
"ArrowFunctionExpression": enter,
|
|
1134
|
+
"ArrowFunctionExpression:exit": exit,
|
|
1135
|
+
"FunctionExpression": enter,
|
|
1136
|
+
"FunctionExpression:exit": exit,
|
|
1137
|
+
"FunctionDeclaration": enter,
|
|
1138
|
+
"FunctionDeclaration:exit": exit,
|
|
1139
|
+
"AwaitExpression:matches([argument.type='CallExpression'], [argument.type='NewExpression'])": track
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
const ruleset = {
|
|
1144
|
+
recommended: {
|
|
1145
|
+
"effector/enforce-effect-naming-convention": "error",
|
|
1146
|
+
"effector/enforce-store-naming-convention": "error",
|
|
1147
|
+
"effector/keep-options-order": "warn",
|
|
1148
|
+
"effector/no-ambiguity-target": "warn",
|
|
1149
|
+
"effector/no-duplicate-on": "error",
|
|
1150
|
+
"effector/no-forward": "error",
|
|
1151
|
+
"effector/no-getState": "error",
|
|
1152
|
+
"effector/no-guard": "error",
|
|
1153
|
+
"effector/no-unnecessary-combination": "warn",
|
|
1154
|
+
"effector/no-unnecessary-duplication": "warn",
|
|
1155
|
+
"effector/no-useless-methods": "error",
|
|
1156
|
+
"effector/no-watch": "warn"
|
|
1157
|
+
},
|
|
1158
|
+
patronum: { "effector/no-patronum-debug": "warn" },
|
|
1159
|
+
scope: {
|
|
1160
|
+
"effector/require-pickup-in-persist": "error",
|
|
1161
|
+
"effector/strict-effect-handlers": "error"
|
|
1162
|
+
},
|
|
1163
|
+
react: {
|
|
1164
|
+
"effector/enforce-gate-naming-convention": "error",
|
|
1165
|
+
"effector/mandatory-scope-binding": "error",
|
|
1166
|
+
"effector/prefer-useUnit": "error"
|
|
1167
|
+
},
|
|
1168
|
+
future: { "effector/no-domain-unit-creators": "warn" }
|
|
1169
|
+
};
|
|
1170
|
+
const base = {
|
|
1171
|
+
meta: {
|
|
1172
|
+
name,
|
|
1173
|
+
version,
|
|
1174
|
+
namespace: "effector"
|
|
1175
|
+
},
|
|
1176
|
+
rules: {
|
|
1177
|
+
"enforce-effect-naming-convention": enforce_effect_naming_convention_default,
|
|
1178
|
+
"enforce-gate-naming-convention": enforce_gate_naming_convention_default,
|
|
1179
|
+
"enforce-store-naming-convention": enforce_store_naming_convention_default,
|
|
1180
|
+
"keep-options-order": keep_options_order_default,
|
|
1181
|
+
"mandatory-scope-binding": mandatory_scope_binding_default,
|
|
1182
|
+
"no-ambiguity-target": no_ambiguity_target_default,
|
|
1183
|
+
"no-domain-unit-creators": no_domain_unit_creators_default,
|
|
1184
|
+
"no-duplicate-clock-or-source-array-values": no_duplicate_clock_or_source_array_values_default,
|
|
1185
|
+
"no-duplicate-on": no_duplicate_on_default,
|
|
1186
|
+
"no-forward": no_forward_default,
|
|
1187
|
+
"no-getState": no_getState_default,
|
|
1188
|
+
"no-guard": no_guard_default,
|
|
1189
|
+
"no-patronum-debug": no_patronum_debug_default,
|
|
1190
|
+
"no-unnecessary-combination": no_unnecessary_combination_default,
|
|
1191
|
+
"no-unnecessary-duplication": no_unnecessary_duplication_default,
|
|
1192
|
+
"no-useless-methods": no_useless_methods_default,
|
|
1193
|
+
"no-watch": no_watch_default,
|
|
1194
|
+
"prefer-useUnit": prefer_useUnit_default,
|
|
1195
|
+
"require-pickup-in-persist": require_pickup_in_persist_default,
|
|
1196
|
+
"strict-effect-handlers": strict_effect_handlers_default
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
const legacyConfigs = {
|
|
1200
|
+
recommended: { rules: ruleset.recommended },
|
|
1201
|
+
scope: { rules: ruleset.scope },
|
|
1202
|
+
react: { rules: ruleset.react },
|
|
1203
|
+
future: { rules: ruleset.future },
|
|
1204
|
+
patronum: { rules: ruleset.patronum }
|
|
1205
|
+
};
|
|
1206
|
+
const self = base;
|
|
1207
|
+
const flatConfigs = {
|
|
1208
|
+
recommended: {
|
|
1209
|
+
plugins: { effector: self },
|
|
1210
|
+
rules: ruleset.recommended
|
|
1211
|
+
},
|
|
1212
|
+
scope: {
|
|
1213
|
+
plugins: { effector: self },
|
|
1214
|
+
rules: ruleset.scope
|
|
1215
|
+
},
|
|
1216
|
+
react: {
|
|
1217
|
+
plugins: { effector: self },
|
|
1218
|
+
rules: ruleset.react
|
|
1219
|
+
},
|
|
1220
|
+
future: {
|
|
1221
|
+
plugins: { effector: self },
|
|
1222
|
+
rules: ruleset.future
|
|
1223
|
+
},
|
|
1224
|
+
patronum: {
|
|
1225
|
+
plugins: { effector: self },
|
|
1226
|
+
rules: ruleset.patronum
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
const plugin = base;
|
|
1230
|
+
plugin.configs = legacyConfigs;
|
|
1231
|
+
plugin.flatConfigs = flatConfigs;
|
|
1232
|
+
var src_default = plugin;
|
|
1233
|
+
export { src_default as default };
|