npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eslint-plugin-effector-units-order

v0.0.3

Published

ESLint plugin for enforcing consistent ordering of Effector units

Readme

eslint-plugin-effector-units-order

An ESLint plugin that enforces a consistent order for Effector units (domains, events, effects, stores, and computed values) in your codebase. Keep your Effector code organized and maintainable with automatic code reordering.

Alt text

✨ Features

  • 🔧 Auto-fix — Automatically reorders your code to match the configured order
  • 📦 Grouping — Organize units into logical groups for better code structure
  • 📏 Empty lines — Configurable spacing between groups (0-3 lines)
  • 🏭 Custom factories — Support for custom unit factories and factory methods
  • 🌐 Domain support — Works with domain.createEvent(), domain.createEffect(), and other domain methods
  • 🧮 Computed stores — Supports combine() and or() from patronum
  • ⚙️ Flexible configuration — Configure via rule options or ESLint shared settings
  • 📋 ESLint 9+ — Supports both flat config and legacy .eslintrc format

📦 Installation

# npm
npm install -D eslint-plugin-effector-units-order

# pnpm
pnpm add -D eslint-plugin-effector-units-order

# yarn
yarn add -D eslint-plugin-effector-units-order

🚀 Quick Start

ESLint Flat Config (ESLint 9+)

// eslint.config.js
import effectorOrder from "eslint-plugin-effector-units-order";

export default [
  // Use recommended config
  effectorOrder.configs.recommended,
];

Legacy Config (.eslintrc)

{
  "extends": ["plugin:effector-order/recommended-legacy"]
}

📖 Usage

Recommended Configuration

The recommended configuration enforces the following order:

  1. domaincreateDomain()
  2. eventscreateEvent(), domain.createEvent()
  3. effectscreateEffect(), domain.createEffect()
  4. storescreateStore(), domain.createStore()
  5. computedcombine(), or()

With 1 empty line between groups for better readability.

Custom Configuration

// eslint.config.js
import effectorOrder from "eslint-plugin-effector-units-order";

export default [
  {
    plugins: {
      "effector-order": effectorOrder,
    },
    rules: {
      "effector-order/keep-units-order": [
        "error",
        {
          order: ["domain", "events", "effects", "stores", "computed"],
          emptyLinesBetweenGroups: 1,
        },
      ],
    },
  },
];

⚙️ Options

| Option | Type | Default | Description | | ------------------------- | ---------- | ------------------------------------------------------- | ----------------------------------------------------------- | | order | string[] | ['domain', 'events', 'effects', 'stores', 'computed'] | Defines the order of unit groups | | emptyLinesBetweenGroups | number | 1 | Number of empty lines between groups (0-3) | | groups | object | See below | Custom group definitions with factories and derived methods | | customFactories | object | {} | Maps custom factory functions to specific groups | | packages | string[] | ['effector', 'patronum'] | Packages to detect imports from |

Group Options:

Each group in groups can have:

  • factories: string[] — Factory functions that create units of this type (e.g., ['createStore'])
  • derivedMethods?: string[] — Methods and properties that create derived units (e.g., ['stores.map', 'effects.pending'])

Default Groups

{
  domain: {
    factories: ['createDomain']
  },
  events: {
    factories: ['createEvent'],
    derivedMethods: [
      'events.prepend',
      'events.map',
      'events.filter',
      'events.filterMap',
      'stores.updates',
      'stores.reinit',
      'effects.done',
      'effects.doneData',
      'effects.fail',
      'effects.failData',
      'effects.finally'
    ]
  },
  effects: {
    factories: ['createEffect'],
    derivedMethods: [
      'effects.map',
      'effects.prepend',
      'effects.filterMap'
    ]
  },
  stores: {
    factories: ['createStore']
  },
  computed: {
    factories: ['or', 'combine', 'merge', 'attach', 'forward'],
    derivedMethods: [
      'stores.map',
      'effects.pending',
      'effects.inFlight'
    ]
  }
}

📋 Examples

Basic Usage

❌ Incorrect Order

import { createStore, createEvent, createDomain } from "effector";

export const $user = createStore(null); // ❌ Store before event
export const userClicked = createEvent(); // ❌ Event before domain
export const userDomain = createDomain();

✅ Correct Order

import { createStore, createEvent, createDomain } from "effector";

export const userDomain = createDomain();

export const userClicked = createEvent();

export const $user = createStore(null);

Domain Methods

The plugin also recognizes units created via domain methods:

import { createDomain } from "effector";

export const userDomain = createDomain();

export const userClicked = userDomain.createEvent();
export const fetchUserFx = userDomain.createEffect();

export const $user = userDomain.createStore(null);

Computed Stores

Computed stores using combine(), merge(), attach(), forward(), and or() from patronum are properly recognized:

import { createStore, combine, merge, attach, forward } from "effector";
import { or } from "patronum";

export const $isLoading = createStore(false);
export const $error = createStore(null);

export const $hasIssues = or($isLoading, $error);
export const $state = combine({
  isLoading: $isLoading,
  error: $error,
});

Derived Units

The plugin automatically recognizes derived stores and events created from existing units:

Derived Stores

import { createStore, createEffect } from "effector";

export const $products = createStore([]);
export const fetchProductsFx = createEffect();

export const $enrichedProducts = $products.map((products) =>
  products.map((p) => ({ ...p, priceWithTax: p.price * 1.2 }))
);
export const $loading = fetchProductsFx.pending;
export const $isLoading = $loading.map((loading) => loading);

Derived Events

import { createEvent, createEffect, createStore } from "effector";

export const userLogin = createEvent();
export const fetchUserFx = createEffect();
export const $user = createStore(null);

export const loginWithEmail = userLogin.map((email) => ({ email })); /
export const fetchSucceeded = fetchUserFx.done;
export const fetchFailed = fetchUserFx.fail;
export const userUpdated = $user.updates;
export const resetUser = $user.reinit;

❌ Incorrect Order (Auto-fixed)

import { createStore, createEffect } from "effector";

export const $loading = fetchProductsFx.pending;
export const fetchProductsFx = createEffect();
export const $products = createStore([]);
export const $enriched = $products.map((p) => p);

✅ Correct Order (After auto-fix)

import { createStore, createEffect } from "effector";

export const fetchProductsFx = createEffect();

export const $products = createStore([]);

export const $loading = fetchProductsFx.pending;
export const $enriched = $products.map((p) => p);

Custom Factories

You can map custom factory functions to specific groups:

// eslint.config.js
export default [
  {
    plugins: { "effector-order": effectorOrder },
    rules: {
      "effector-order/keep-units-order": [
        "error",
        {
          order: ["domain", "events", "effects", "stores", "computed"],
          customFactories: {
            tableStateFactory: "stores",
            permissionSelectionFactory: "stores",
          },
        },
      ],
    },
  },
];

Custom Groups

Define your own groups with custom factory mappings:

// eslint.config.js
export default [
  {
    plugins: { "effector-order": effectorOrder },
    rules: {
      "effector-order/keep-units-order": [
        "error",
        {
          order: ["domain", "actions", "data", "computed"],
          groups: {
            actions: { factories: ["createEvent", "createEffect"] },
            data: { factories: ["createStore"] },
          },
        },
      ],
    },
  },
];

Custom Derived Methods

You can configure which methods and properties create derived units:

// eslint.config.js
export default [
  {
    plugins: { "effector-order": effectorOrder },
    rules: {
      "effector-order/keep-units-order": [
        "error",
        {
          order: ["domain", "events", "effects", "stores", "computed"],
          groups: {
            events: {
              factories: ["createEvent"],
              // Configure which methods create derived events
              derivedMethods: [
                "events.map",
                "events.prepend",
                "effects.done",
                "stores.updates",
              ],
            },
            effects: {
              factories: ["createEffect"],
              derivedMethods: ["effects.map", "effects.prepend"],
            },
            computed: {
              factories: ["combine", "merge", "attach", "forward"],
              // Configure which methods create derived stores
              derivedMethods: [
                "stores.map",
                "effects.pending",
                "effects.inFlight",
              ],
            },
          },
        },
      ],
    },
  },
];

Format for derivedMethods:

  • "unitType.methodName" — for methods like $store.map(), event.prepend()
  • "unitType.property" — for properties like effect.pending, store.updates

Where unitType is the type of the parent unit (stores, events, effects, computed), and the result will be categorized into the group that contains this derivedMethod in its configuration.

No Empty Lines Between Groups

You can disable empty lines between groups by setting emptyLinesBetweenGroups to 0:

// eslint.config.js
export default [
  {
    plugins: { "effector-order": effectorOrder },
    rules: {
      "effector-order/keep-units-order": [
        "error",
        {
          order: ["domain", "events", "effects", "stores"],
          emptyLinesBetweenGroups: 0,
        },
      ],
    },
  },
];

Result:

export const userDomain = createDomain();
export const userClicked = createEvent();
export const fetchUserFx = createEffect();
export const $user = createStore(null);

🔧 Shared Settings

You can also configure the plugin via ESLint shared settings. This is useful when you want to share the same configuration across multiple rules or files:

// eslint.config.js
export default [
  {
    plugins: { "effector-order": effectorOrder },
    settings: {
      "effector-units-order": {
        order: ["domain", "events", "effects", "stores", "computed"],
        emptyLinesBetweenGroups: 1,
      },
    },
    rules: {
      "effector-order/keep-units-order": "error",
    },
  },
];

Note: Rule options take precedence over shared settings. If you specify options directly in the rule configuration, they will override the shared settings.

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

📄 License

MIT © Nikita