Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/future.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ module.exports = {
"effector/prefer-sample-over-forward-with-mapping": "off",
"effector/no-forward": "warn",
"effector/no-guard": "warn",
"effector/no-on": "error",
},
};
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = {
"mandatory-scope-binding": require("./rules/mandatory-scope-binding/mandatory-scope-binding"),
"prefer-useUnit": require("./rules/prefer-useUnit/prefer-useUnit"),
"no-patronum-debug": require("./rules/no-patronum-debug/no-patronum-debug"),
"no-on": require("./rules/no-on/no-on"),
},
configs: {
recommended: require("./config/recommended"),
Expand Down
10 changes: 10 additions & 0 deletions rules/no-on/examples/correct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createEvent, createStore, sample } from "effector";

const event = createEvent();

const $store = createStore(null);

sample({
clock: event,
target: $store,
});
9 changes: 9 additions & 0 deletions rules/no-on/examples/incorrect-chaining.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createEvent, createStore } from "effector";

const event = createEvent();
const event2 = createEvent();

const $store = createStore(null)
.on(event, (_, s) => s)
.on(event2, (_, s) => s)
.on(event, (_, s) => s);
5 changes: 5 additions & 0 deletions rules/no-on/examples/incorrect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createEvent, createStore } from "effector";

const event = createEvent();

const $store = createStore(null).on(event, (_, s) => s);
69 changes: 69 additions & 0 deletions rules/no-on/no-on.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const { createLinkToRule } = require("../../utils/create-link-to-rule");
const {
traverseNestedObjectNode,
} = require("../../utils/traverse-nested-object-node");
const { is } = require("../../utils/is");
const { ESLintUtils } = require("@typescript-eslint/utils");

module.exports = {
meta: {
type: "problem",
docs: {
description: "Forbids `.on` chaining on effector store.",
category: "Quality",
recommended: true,
url: createLinkToRule("no-on"),
},
messages: {
noOn: "Method `.on` is forbidden on any effector store.",
},
schema: [],
},
create(context) {
let parserServices;
try {
parserServices = ESLintUtils.getParserServices(context);
} catch (err) {
// no types information
}

if (!parserServices?.program) return {};

return {
'CallExpression[callee.property.name="on"]'(node) {
const storeObject = traverseNestedObjectNode(
getNestedCallee(node) ?? getAssignedVariable(node)
);

if (!is.store({ context, node: storeObject })) {
return;
}

context.report({
node,
messageId: "noOn",
});
},
};
},
};

function getNestedCallee(node) {
const { callee } = node;

if (callee.object?.type === "CallExpression") {
return getNestedCallee(callee.object);
}

return callee.object;
}

function getAssignedVariable(node) {
const { parent } = node;

if (parent.type === "VariableDeclarator") {
return parent;
}

return getAssignedVariable(parent);
}
1 change: 1 addition & 0 deletions rules/no-on/no-on.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://eslint.effector.dev/rules/no-on.html
53 changes: 53 additions & 0 deletions rules/no-on/no-on.ts.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const { RuleTester } = require("@typescript-eslint/rule-tester");
const { join } = require("path");

const { readExample } = require("../../utils/read-example");
const rule = require("./no-on");

const ruleTester = new RuleTester({
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaVersion: 2020,
sourceType: "module",
project: "./tsconfig.json",
tsconfigRootDir: join(__dirname, ".."),
},
});

const readExampleForTheRule = (name) => ({
code: readExample(__dirname, name),
filename: join(__dirname, "examples", name),
});

ruleTester.run("effector/no-on.ts.test", rule, {
valid: ["correct.ts"].map(readExampleForTheRule),

invalid: [
...["incorrect.ts"].map(readExampleForTheRule).map((result) => ({
...result,
errors: [
{
messageId: "noOn",
type: "CallExpression",
},
],
})),
...["incorrect-chaining.ts"].map(readExampleForTheRule).map((result) => ({
...result,
errors: [
{
messageId: "noOn",
type: "CallExpression",
},
{
messageId: "noOn",
type: "CallExpression",
},
{
messageId: "noOn",
type: "CallExpression",
},
],
})),
],
});