diff --git a/.eslintignore b/.eslintignore
deleted file mode 100644
index f06235c..0000000
--- a/.eslintignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules
-dist
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index f68e0e7..0000000
--- a/.eslintrc
+++ /dev/null
@@ -1,55 +0,0 @@
-{
- "parser": "@typescript-eslint/parser",
- "extends": [
- "eslint:recommended",
- "plugin:import/recommended",
- "plugin:react/recommended",
- "plugin:prettier/recommended"
- // "plugin:@typescript-eslint/recommended"
- ],
- "settings": {
- "react": {
- "version": "detect"
- },
- "import/ignore": ["react-native"],
- "import/resolver": {
- "node": {
- "extensions": [".js", ".ts", ".tsx"]
- }
- }
- },
- "parserOptions": {
- "ecmaVersion": 6,
- "sourceType": "module",
- "ecmaFeatures": {
- "jsx": true,
- "experimentalObjectRestSpread": true
- }
- },
- "env": {
- "browser": true,
- "mocha": true,
- "node": true,
- "jest/globals": true
- },
- "rules": {
- "valid-jsdoc": 2,
- "react/jsx-uses-react": 1,
- "react/jsx-no-undef": 2,
- "react/jsx-wrap-multilines": 2,
- "react/no-string-refs": 0,
- "no-unused-vars": "off",
- "@typescript-eslint/no-unused-vars": "off",
- "no-redeclare": "off",
- "@typescript-eslint/no-redeclare": ["error"],
- "jest/no-disabled-tests": "warn",
- "jest/no-focused-tests": "error",
- "jest/no-identical-title": "error",
- "jest/prefer-to-have-length": "warn",
- "jest/valid-expect": "error"
- },
- "plugins": ["jest", "@typescript-eslint", "import", "react"],
- "globals": {
- "JSX": true
- }
-}
diff --git a/.prettierrc b/.prettierrc
index 75a8109..db53a3e 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -3,5 +3,6 @@
"printWidth": 140,
"tabWidth": 4,
"singleQuote": true,
- "trailingComma": "all"
+ "trailingComma": "all",
+ "endOfLine": "lf"
}
diff --git a/docs/code_of_conduct.md b/CODE_OF_CONDUCT.md
similarity index 100%
rename from docs/code_of_conduct.md
rename to CODE_OF_CONDUCT.md
diff --git a/README.md b/README.md
index c8e7d0b..ef97ff2 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,23 @@
![Eventrix](assets/logo_br.svg)
-STATE MANAGMENT FOR **REACT** AND **REACT NATIVE** APPS
+State Management for **React** and **React Native** Applications
[![Build Status](https://travis-ci.org/rstgroup/eventrix.svg?branch=master)](https://travis-ci.org/rstgroup/eventrix)
[![npm](https://img.shields.io/npm/l/eventrix.svg)](https://npmjs.org/package/eventrix)
[![npm](https://img.shields.io/npm/v/eventrix.svg)](https://npmjs.org/package/eventrix)
-### Support
+# Support
- React
- React Native
- SSR (Next.js)
-### Installation
+# Installation
```bash
$ npm install eventrix --save
```
-### Documentation
+# Documentation
[**Get started**](https://eventrix.gitbook.io/eventrix/getting-started)
|
@@ -27,14 +27,15 @@ $ npm install eventrix --save
|
[**Demo**](https://eventrix.gitbook.io/eventrix/demo)
-### Home page
+# Home page
We have website dedicated to eventrix. Go to [**eventrix.io**](https://eventrix.io) and see what eventrix has to offer.
-### Quickstart
+# Quickstart
-eventrixStore.js
```js
+// eventrixStore.js
+
import { Eventrix } from 'eventrix';
import receiversList from './receivers';
@@ -46,8 +47,9 @@ const eventrixStore = new Eventrix(initialState, receiversList);
export default eventrixStore;
```
-App.jsx
```jsx harmony
+// App.tsx
+
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { EventrixProvider } from 'eventrix';
@@ -64,8 +66,9 @@ ReactDOM.render(
);
```
-UsersList.jsx
```jsx harmony
+// UsersList.jsx
+
import React, { useCallback } from 'react';
import { useEventrixState, useEmit } from 'eventrix';
@@ -88,8 +91,9 @@ const UsersList = () => {
}
```
-receivers.js
```js
+// receivers.js
+
import axios from 'axios';
import { EventsReceiver } from 'eventrix';
@@ -105,7 +109,9 @@ const receiversList = [fetchUsersReceiver];
export default receiversList;
```
-### About
+For more usage examples see the `examples` directory.
+
+# About
[Eventrix](https://eventrix.io/) is a scaling and predictable JS library for state managing and centralizing application global state.
@@ -131,17 +137,32 @@ Greater control of data flow thanks to additional tools (devtools) and a small t
[Eventrix DevTools](https://github.com/rstgroup/eventrix-devtools)
-### Contribute
+# Contribute
- use eslint rules
- write clean code
- unit tests (min 85% of your code should be tested)
-- [code of conduct](https://github.com/rstgroup/eventrix/blob/master/docs/code_of_conduct.md)
-### License
+## Development
+
+To start development, clone the repository and install the dependencies. Then you can develop the library using the example application.
+
+```bash
+$ git clone https://github.com/rstgroup/eventrix.git
+$ npm install
+
+# Run the example application
+cd examples/todo-list
+npm install
+npm run dev
+```
+
+For more example applications see the `examples` directory.
+
+# License
eventrix package are [MIT licensed](https://github.com/rstgroup/eventrix/blob/master/LICENSE)
-### Powered by
+# Powered by
-[RST Software Masters](https://rst.software) look on RST [Github](https://github.com/rstgroup)
+[RST Software](https://rst.software), see our [Github](https://github.com/rstgroup)
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..c463772
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,48 @@
+import eslint from '@eslint/js';
+import tseslint from 'typescript-eslint';
+import jsdoc from 'eslint-plugin-jsdoc';
+import reactPlugin from 'eslint-plugin-react';
+
+export default tseslint.config(eslint.configs.recommended, tseslint.configs.recommended, {
+ settings: {
+ react: {
+ version: 'detect',
+ },
+ 'import/ignore': ['react-native'],
+ 'import/resolver': {
+ node: {
+ extensions: ['.js', '.ts', '.tsx'],
+ },
+ },
+ },
+ files: ['src/**/*.{js,jsx,mjs,cjs,ts,tsx}'],
+ ignores: ['node_modules', 'dist', 'devtools'],
+ plugins: {
+ jsdoc: jsdoc,
+ react: reactPlugin,
+ },
+ languageOptions: {
+ parser: tseslint.parser,
+ parserOptions: {
+ ecmaVersion: 6,
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true,
+ experimentalObjectRestSpread: true,
+ },
+ },
+ },
+ rules: {
+ 'linebreak-style': ['error', 'unix'],
+ 'react/jsx-uses-react': 1,
+ 'react/jsx-no-undef': 2,
+ 'react/jsx-wrap-multilines': 2,
+ 'react/no-string-refs': 0,
+ 'jsdoc/no-unused-vars': 'off',
+ '@typescript-eslint/no-unused-vars': 'off',
+ 'jsdoc/no-redeclare': 'off',
+ '@typescript-eslint/no-redeclare': ['error'],
+ '@typescript-eslint/no-explicit-any': 'warn',
+ '@typescript-eslint/ban-ts-comment': 'off',
+ },
+});
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..a6df29f
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,10 @@
+# Eventrix usage examples
+
+This directory contains examples of how to use Eventrix in different scenarios.
+
+To run the example simply run the following commands in each example directory:
+
+```bash
+npm install
+npm run dev
+```
diff --git a/examples/alert-manager/.gitignore b/examples/alert-manager/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/examples/alert-manager/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/alert-manager/index.html b/examples/alert-manager/index.html
new file mode 100644
index 0000000..096ab24
--- /dev/null
+++ b/examples/alert-manager/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Eventrix - alert manager
+
+
+
+
+
+
diff --git a/examples/alert-manager/package.json b/examples/alert-manager/package.json
new file mode 100644
index 0000000..8badd7d
--- /dev/null
+++ b/examples/alert-manager/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "eexample-alert-manager",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint --ext .ts,.tsx src --fix",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@emotion/react": "11.10.5",
+ "@emotion/styled": "11.10.5",
+ "@mui/material": "5.10.16",
+ "@mui/types": "7.2.2",
+ "axios": "^1.7.7",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.3",
+ "globals": "^15.11.0",
+ "typescript": "~5.6.2",
+ "vite": "^6.0.1"
+ }
+}
diff --git a/examples/alert-manager/src/App.tsx b/examples/alert-manager/src/App.tsx
new file mode 100644
index 0000000..3e59c74
--- /dev/null
+++ b/examples/alert-manager/src/App.tsx
@@ -0,0 +1,16 @@
+import Alerts from './components/Alerts';
+import Logo from './components/Logo';
+import Buttons from './components/Buttons';
+import './index.css';
+import React from 'react';
+
+export default function App() {
+ return (
+
+ );
+}
diff --git a/examples/alert-manager/src/appEvents/alerts.ts b/examples/alert-manager/src/appEvents/alerts.ts
new file mode 100644
index 0000000..b6c4e1d
--- /dev/null
+++ b/examples/alert-manager/src/appEvents/alerts.ts
@@ -0,0 +1,5 @@
+export const ALERTS_SHOW_SUCCESS = 'Alerts:show.success';
+export const ALERTS_SHOW_ERROR = 'Alerts:show.error';
+export const ALERTS_SHOW_WARNING = 'Alerts:show.warning';
+export const ALERTS_SHOW_INFO = 'Alerts:show.info';
+export const ALERTS_REMOVE = 'Alerts:remove';
diff --git a/examples/alert-manager/src/components/Alerts/Alerts.tsx b/examples/alert-manager/src/components/Alerts/Alerts.tsx
new file mode 100644
index 0000000..a6fc685
--- /dev/null
+++ b/examples/alert-manager/src/components/Alerts/Alerts.tsx
@@ -0,0 +1,42 @@
+import { useEvent } from 'eventrix';
+import React, { useState } from 'react';
+import { Stack } from '@mui/material';
+import { ALERTS_REMOVE, ALERTS_SHOW_ERROR, ALERTS_SHOW_INFO, ALERTS_SHOW_SUCCESS, ALERTS_SHOW_WARNING } from '../../appEvents/alerts';
+import { Alert, AlertEventPayload, AlertTypes } from '../../types/alerts';
+import AlertMessage from './AlertsMessage';
+
+const createId = (): string => {
+ return String(new Date().getTime());
+};
+
+const Alerts = () => {
+ const [alerts, setAlerts] = useState([]);
+ useEvent(ALERTS_SHOW_SUCCESS, (message: AlertEventPayload) => {
+ const id = createId();
+ setAlerts([{ message, type: AlertTypes.success, id }, ...alerts]);
+ });
+ useEvent(ALERTS_SHOW_WARNING, (message: AlertEventPayload) => {
+ const id = createId();
+ setAlerts([{ message, type: AlertTypes.warning, id }, ...alerts]);
+ });
+ useEvent(ALERTS_SHOW_ERROR, (message: AlertEventPayload) => {
+ const id = createId();
+ setAlerts([{ message, type: AlertTypes.error, id }, ...alerts]);
+ });
+ useEvent(ALERTS_SHOW_INFO, (message: AlertEventPayload) => {
+ const id = createId();
+ setAlerts([{ message, type: AlertTypes.info, id }, ...alerts]);
+ });
+ useEvent(ALERTS_REMOVE, (id: string) => {
+ setAlerts(alerts.filter((alert) => alert.id !== id));
+ });
+ return (
+
+ {alerts.map(({ message, type, id }) => (
+
+ ))}
+
+ );
+};
+
+export default Alerts;
diff --git a/examples/alert-manager/src/components/Alerts/AlertsMessage.tsx b/examples/alert-manager/src/components/Alerts/AlertsMessage.tsx
new file mode 100644
index 0000000..195f95d
--- /dev/null
+++ b/examples/alert-manager/src/components/Alerts/AlertsMessage.tsx
@@ -0,0 +1,26 @@
+import Alert from '@mui/material/Alert';
+import { useEmit } from 'eventrix';
+import React, { useEffect } from 'react';
+import { ALERTS_REMOVE } from '../../appEvents/alerts';
+import { AlertTypes } from '../../types/alerts';
+
+type AlertMessageProps = {
+ message: string;
+ type: AlertTypes;
+ id: string;
+};
+
+const AlertMessage = ({ message, type, id }: AlertMessageProps) => {
+ const emit = useEmit();
+ useEffect(() => {
+ const timeout = setTimeout(() => {
+ emit(ALERTS_REMOVE, id);
+ }, 5000);
+ return () => {
+ clearTimeout(timeout);
+ };
+ }, [emit, id]);
+ return {message};
+};
+
+export default AlertMessage;
diff --git a/examples/alert-manager/src/components/Alerts/index.ts b/examples/alert-manager/src/components/Alerts/index.ts
new file mode 100644
index 0000000..41c97d3
--- /dev/null
+++ b/examples/alert-manager/src/components/Alerts/index.ts
@@ -0,0 +1 @@
+export { default } from './Alerts';
diff --git a/examples/alert-manager/src/components/Buttons.tsx b/examples/alert-manager/src/components/Buttons.tsx
new file mode 100644
index 0000000..1dc2117
--- /dev/null
+++ b/examples/alert-manager/src/components/Buttons.tsx
@@ -0,0 +1,48 @@
+import { useEmit } from 'eventrix';
+import Stack from '@mui/material/Stack';
+import Button from '@mui/material/Button';
+import { AlertEventPayload } from '../types/alerts';
+import { ALERTS_SHOW_ERROR, ALERTS_SHOW_INFO, ALERTS_SHOW_SUCCESS, ALERTS_SHOW_WARNING } from '../appEvents/alerts';
+import React from 'react';
+
+const Buttons = () => {
+ const emit = useEmit();
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default Buttons;
diff --git a/examples/alert-manager/src/components/Logo.tsx b/examples/alert-manager/src/components/Logo.tsx
new file mode 100644
index 0000000..84de0d4
--- /dev/null
+++ b/examples/alert-manager/src/components/Logo.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+const Logo = () => (
+
+
+
+);
+
+export default Logo;
diff --git a/examples/alert-manager/src/eventrixStore.ts b/examples/alert-manager/src/eventrixStore.ts
new file mode 100644
index 0000000..7e18b07
--- /dev/null
+++ b/examples/alert-manager/src/eventrixStore.ts
@@ -0,0 +1,5 @@
+import Eventrix from 'eventrix';
+
+const eventrixStore = new Eventrix();
+
+export default eventrixStore;
diff --git a/examples/alert-manager/src/index.css b/examples/alert-manager/src/index.css
new file mode 100644
index 0000000..c9ed2d8
--- /dev/null
+++ b/examples/alert-manager/src/index.css
@@ -0,0 +1,4 @@
+.App {
+ font-family: sans-serif;
+ text-align: center;
+}
diff --git a/examples/alert-manager/src/main.tsx b/examples/alert-manager/src/main.tsx
new file mode 100644
index 0000000..55de41e
--- /dev/null
+++ b/examples/alert-manager/src/main.tsx
@@ -0,0 +1,14 @@
+import React, { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App';
+import { EventrixProvider } from 'eventrix';
+import eventrixStore from './eventrixStore';
+
+createRoot(document.getElementById('root')!).render(
+
+
+
+
+ ,
+);
diff --git a/examples/alert-manager/src/types/alerts.ts b/examples/alert-manager/src/types/alerts.ts
new file mode 100644
index 0000000..8c432f2
--- /dev/null
+++ b/examples/alert-manager/src/types/alerts.ts
@@ -0,0 +1,14 @@
+export enum AlertTypes {
+ error = 'error',
+ warning = 'warning',
+ success = 'success',
+ info = 'info',
+}
+
+export type Alert = {
+ message: string;
+ type: AlertTypes;
+ id: string;
+};
+
+export type AlertEventPayload = string;
diff --git a/examples/alert-manager/tsconfig.json b/examples/alert-manager/tsconfig.json
new file mode 100644
index 0000000..45a7d45
--- /dev/null
+++ b/examples/alert-manager/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "paths": {
+ "eventrix": ["../../src/index.ts"]
+ },
+ "baseUrl": "./",
+ "rootDir": "../../",
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "jsx": "react-jsx",
+ "types": ["vite/client", "node", "react"],
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "allowJs": true,
+ "allowSyntheticDefaultImports": true,
+ "useDefineForClassFields": true,
+ "strict": true,
+ "skipLibCheck": false,
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noImplicitReturns": false,
+ "forceConsistentCasingInFileNames": true
+
+ },
+}
diff --git a/examples/alert-manager/vite.config.ts b/examples/alert-manager/vite.config.ts
new file mode 100644
index 0000000..c7f2182
--- /dev/null
+++ b/examples/alert-manager/vite.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import path from 'path';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ eventrix: path.join(__dirname, '../../src/index.ts'),
+ },
+ },
+});
diff --git a/examples/forms/.gitignore b/examples/forms/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/examples/forms/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/forms/index.html b/examples/forms/index.html
new file mode 100644
index 0000000..1dd75e4
--- /dev/null
+++ b/examples/forms/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Eventrix - Form
+
+
+
+
+
+
diff --git a/examples/forms/package.json b/examples/forms/package.json
new file mode 100644
index 0000000..a21e227
--- /dev/null
+++ b/examples/forms/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "example-forms",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "lint": "eslint --ext .ts,.tsx src --fix",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@emotion/react": "11.10.5",
+ "@emotion/styled": "11.10.5",
+ "@mui/material": "5.10.16",
+ "@mui/types": "7.2.2",
+ "axios": "^1.7.7",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.3",
+ "globals": "^15.11.0",
+ "typescript": "~5.6.2",
+ "vite": "^6.0.1"
+ }
+}
diff --git a/examples/forms/src/App.tsx b/examples/forms/src/App.tsx
new file mode 100644
index 0000000..914b410
--- /dev/null
+++ b/examples/forms/src/App.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { EventrixProvider } from 'eventrix';
+import eventrix from './services/Eventrix';
+import Logo from './Logo';
+import './index.css';
+import UsersModule from './modules/Users/Users';
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/examples/forms/src/Logo.tsx b/examples/forms/src/Logo.tsx
new file mode 100644
index 0000000..84de0d4
--- /dev/null
+++ b/examples/forms/src/Logo.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+const Logo = () => (
+
+
+
+);
+
+export default Logo;
diff --git a/examples/forms/src/index.css b/examples/forms/src/index.css
new file mode 100644
index 0000000..65e7708
--- /dev/null
+++ b/examples/forms/src/index.css
@@ -0,0 +1,82 @@
+.App {
+ font-family: sans-serif;
+ text-align: center;
+}
+
+.logo {
+ text-align: left;
+ margin-bottom: 15px;
+}
+
+.logo img {
+ height: 80px;
+}
+
+.usersList {
+ display: block;
+ margin-top: 15px;
+}
+
+.usersItem {
+ display: flex;
+ border-top: 1px solid #ddd;
+}
+
+.userItemColl {
+ padding: 15px;
+ width: 120px;
+}
+
+.usersListHeader {
+ display: flex;
+}
+
+.usersListHeaderColl {
+ padding: 15px;
+ background: #333;
+ color: #fff;
+ width: 120px;
+}
+
+.usersListPlaceholder {
+ padding: 50px;
+}
+
+.inputWrapper {
+ text-align: left;
+ margin-top: 10px;
+}
+
+.inputWrapper label {
+ display: inline-block;
+ margin-right: 10px;
+ text-align: left;
+ padding-left: 5px;
+ width: 100px;
+}
+
+.inputWrapper input {
+ padding: 6px 10px;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+}
+
+.formContainer {
+ display: flex;
+}
+
+.formContainer button {
+ margin-top: 15px;
+}
+
+.formContainer > div {
+ width: 45%;
+}
+
+.userPreview {
+ padding: 20px;
+ background: #ddd;
+ width: 100%;
+ height: 90%;
+ text-align: left;
+}
diff --git a/examples/forms/src/main.tsx b/examples/forms/src/main.tsx
new file mode 100644
index 0000000..1fdb340
--- /dev/null
+++ b/examples/forms/src/main.tsx
@@ -0,0 +1,10 @@
+import React, { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App';
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+);
diff --git a/examples/forms/src/modules/Users/Form/Form.tsx b/examples/forms/src/modules/Users/Form/Form.tsx
new file mode 100644
index 0000000..3363210
--- /dev/null
+++ b/examples/forms/src/modules/Users/Form/Form.tsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { useEmit } from 'eventrix';
+import Input from './Input';
+import UserPreview from './UserPreview';
+
+const CREATE_USER_EVENT_NAME = 'users:createUser';
+export const CHANGE_PREVIEW_COLOR = 'changePreviewColor';
+
+const UserForm = () => {
+ const emit = useEmit();
+ const onSubmit = (e) => {
+ e.preventDefault();
+ emit(CREATE_USER_EVENT_NAME);
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default UserForm;
diff --git a/examples/forms/src/modules/Users/Form/Input.tsx b/examples/forms/src/modules/Users/Form/Input.tsx
new file mode 100644
index 0000000..1560402
--- /dev/null
+++ b/examples/forms/src/modules/Users/Form/Input.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { useEventrixState } from 'eventrix';
+
+interface InputProps {
+ name: string;
+ label?: string;
+ placeholder: string;
+}
+
+const Input: React.FC = ({ name, label, placeholder }) => {
+ const [value, setValue] = useEventrixState(`user.${name}`);
+ return (
+
+
+ setValue(e.target.value)} placeholder={placeholder} />
+
+ );
+};
+
+export default Input;
diff --git a/examples/forms/src/modules/Users/Form/UserPreview.tsx b/examples/forms/src/modules/Users/Form/UserPreview.tsx
new file mode 100644
index 0000000..59a176f
--- /dev/null
+++ b/examples/forms/src/modules/Users/Form/UserPreview.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react';
+import { useEventrixState, useEvent } from 'eventrix';
+import { CHANGE_PREVIEW_COLOR } from './Form';
+
+const UserPreview = () => {
+ const [color, setColor] = useState('black');
+ const [user] = useEventrixState('user');
+ useEvent(CHANGE_PREVIEW_COLOR, (data) => {
+ setColor(data.color);
+ });
+ return (
+
+
+ "user"
+ {' '}
+ state in eventrix store:
+
{JSON.stringify(user, null, 2)}
+
+ );
+};
+
+export default UserPreview;
diff --git a/examples/forms/src/modules/Users/List/Item.tsx b/examples/forms/src/modules/Users/List/Item.tsx
new file mode 100644
index 0000000..55cc615
--- /dev/null
+++ b/examples/forms/src/modules/Users/List/Item.tsx
@@ -0,0 +1,28 @@
+import React from 'react';
+
+interface UsersItemProps {
+ id: number;
+ username: string;
+ name: string;
+ surname: string;
+ country: string;
+ city: string;
+ street: string;
+ phone: string;
+}
+
+const UsersItem: React.FC = ({ username, name, surname, country, city, street, phone }) => {
+ return (
+
+
{username}
+
{name}
+
{surname}
+
{country}
+
{city}
+
{street}
+
{phone}
+
+ );
+};
+
+export default UsersItem;
diff --git a/examples/forms/src/modules/Users/List/List.tsx b/examples/forms/src/modules/Users/List/List.tsx
new file mode 100644
index 0000000..d6c12b1
--- /dev/null
+++ b/examples/forms/src/modules/Users/List/List.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { useEventrixState } from 'eventrix';
+import UserItem from './Item';
+
+const UsersList = () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const [users] = useEventrixState('users');
+ return (
+
+
+
username
+
name
+
surname
+
country
+
city
+
street
+
phone
+
+ {users.length === 0 &&
Users list is empty
}
+ {users.map((user) => (
+
+ ))}
+
+ );
+};
+
+export default UsersList;
diff --git a/examples/forms/src/modules/Users/Users.tsx b/examples/forms/src/modules/Users/Users.tsx
new file mode 100644
index 0000000..3592091
--- /dev/null
+++ b/examples/forms/src/modules/Users/Users.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import UsersList from './List/List';
+import UserForm from './Form/Form';
+
+const UsersModule = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default UsersModule;
diff --git a/examples/forms/src/services/Eventrix/index.ts b/examples/forms/src/services/Eventrix/index.ts
new file mode 100644
index 0000000..4c03b0e
--- /dev/null
+++ b/examples/forms/src/services/Eventrix/index.ts
@@ -0,0 +1,11 @@
+import { Eventrix } from 'eventrix';
+import usersEventsReceiver from './users';
+
+const initialState = {
+ user: {},
+ users: [],
+};
+
+const eventsReceivers = [usersEventsReceiver];
+
+export default new Eventrix(initialState, eventsReceivers);
diff --git a/examples/forms/src/services/Eventrix/users.ts b/examples/forms/src/services/Eventrix/users.ts
new file mode 100644
index 0000000..fd8c98d
--- /dev/null
+++ b/examples/forms/src/services/Eventrix/users.ts
@@ -0,0 +1,12 @@
+import { EventsReceiver } from 'eventrix';
+
+const CREATE_USER_EVENT_NAME = 'users:createUser';
+
+const usersEventsReceiver = new EventsReceiver(CREATE_USER_EVENT_NAME, (eventName, eventData, storeManager) => {
+ const user = storeManager.getState('user');
+ const users = storeManager.getState('users');
+ storeManager.setState('users', [user, ...users]);
+ storeManager.setState('user', {});
+});
+
+export default usersEventsReceiver;
diff --git a/examples/forms/tsconfig.json b/examples/forms/tsconfig.json
new file mode 100644
index 0000000..45a7d45
--- /dev/null
+++ b/examples/forms/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "paths": {
+ "eventrix": ["../../src/index.ts"]
+ },
+ "baseUrl": "./",
+ "rootDir": "../../",
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "jsx": "react-jsx",
+ "types": ["vite/client", "node", "react"],
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "allowJs": true,
+ "allowSyntheticDefaultImports": true,
+ "useDefineForClassFields": true,
+ "strict": true,
+ "skipLibCheck": false,
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noImplicitReturns": false,
+ "forceConsistentCasingInFileNames": true
+
+ },
+}
diff --git a/examples/forms/vite.config.ts b/examples/forms/vite.config.ts
new file mode 100644
index 0000000..c7f2182
--- /dev/null
+++ b/examples/forms/vite.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import path from 'path';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ eventrix: path.join(__dirname, '../../src/index.ts'),
+ },
+ },
+});
diff --git a/examples/todo-list/.gitignore b/examples/todo-list/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/examples/todo-list/.gitignore
@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/examples/todo-list/index.html b/examples/todo-list/index.html
new file mode 100644
index 0000000..88d775a
--- /dev/null
+++ b/examples/todo-list/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Eventrix - To Do List
+
+
+
+
+
+
diff --git a/examples/todo-list/package.json b/examples/todo-list/package.json
new file mode 100644
index 0000000..b086c73
--- /dev/null
+++ b/examples/todo-list/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "example-todo-list",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "lint": "eslint --ext .ts,.tsx src --fix",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@emotion/react": "11.10.5",
+ "@emotion/styled": "11.10.5",
+ "@mui/material": "5.10.16",
+ "@mui/types": "7.2.2",
+ "axios": "^1.7.7",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1",
+ "@vitejs/plugin-react": "^4.3.3",
+ "globals": "^15.11.0",
+ "typescript": "~5.6.2",
+ "vite": "^6.0.1"
+ }
+}
diff --git a/examples/todo-list/src/App.tsx b/examples/todo-list/src/App.tsx
new file mode 100644
index 0000000..5388546
--- /dev/null
+++ b/examples/todo-list/src/App.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+import { EventrixProvider } from 'eventrix';
+import eventrix from './eventrix/index';
+import './index.css';
+import Logo from './components/Logo';
+import CreateTaskForm from './components/CreateTaskForm/CreateTaskForm';
+import TodoList from './components/TodoList/TodoList';
+import TodoFooter from './components/TodoFooter';
+
+export default function App() {
+ return (
+
+
+
+
TO DO LIST
+
+
+
+
+
+
+
+ );
+}
diff --git a/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.css b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.css
new file mode 100644
index 0000000..a49a9c3
--- /dev/null
+++ b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.css
@@ -0,0 +1,36 @@
+.taskFormWrapper {
+ display: block;
+ width: 100%;
+ background: #efefef;
+ padding: 10px;
+ text-align: left;
+ margin-left: auto;
+ margin-right: auto;
+ border-radius: 4px 4px 0 0;
+}
+
+.taskFormWrapper form {
+ display: flex;
+}
+
+.taskFormWrapper input {
+ width: 100%;
+ padding: 8px 10px;
+ border-radius: 4px 0 0 4px;
+ border: 1px solid #909090;
+ border-right: 0;
+}
+
+.taskFormWrapper button {
+ width: 120px;
+ padding: 8px 10px;
+ border-radius: 0 4px 4px 0;
+ background: #333;
+ color: #fff;
+ border: 1px solid #333;
+ cursor: pointer;
+}
+
+.taskFormWrapper button:hover {
+ background: #000;
+}
diff --git a/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.tsx b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.tsx
new file mode 100644
index 0000000..f90ab3c
--- /dev/null
+++ b/examples/todo-list/src/components/CreateTaskForm/CreateTaskForm.tsx
@@ -0,0 +1,35 @@
+import React, { useState } from 'react';
+import { CREATE_TASK_EVENT_NAME } from '../../eventrix/tasks';
+import { makeid } from '../../utils/helpers';
+import './CreateTaskForm.css';
+import { useEmit } from 'eventrix';
+
+const CreateTaskForm: React.FC = () => {
+ const emit = useEmit();
+ const [title, setTitle] = useState('');
+
+ const onSubmit = (e) => {
+ e.preventDefault();
+ if (title) {
+ const task = {
+ id: makeid(),
+ title,
+ status: 'todo',
+ };
+ emit(CREATE_TASK_EVENT_NAME, { task });
+ setTitle('');
+ return;
+ }
+ };
+
+ return (
+
+
+
+ );
+};
+
+export default CreateTaskForm;
diff --git a/examples/todo-list/src/components/Logo.tsx b/examples/todo-list/src/components/Logo.tsx
new file mode 100644
index 0000000..84de0d4
--- /dev/null
+++ b/examples/todo-list/src/components/Logo.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+const Logo = () => (
+
+
+
+);
+
+export default Logo;
diff --git a/examples/todo-list/src/components/Task.tsx b/examples/todo-list/src/components/Task.tsx
new file mode 100644
index 0000000..4ee93a8
--- /dev/null
+++ b/examples/todo-list/src/components/Task.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { MARK_TASK_AS_DONE_EVENT_NAME, MARK_TASK_AS_TODO_EVENT_NAME, REMOVE_TASK_EVENT_NAME } from '../eventrix/tasks';
+import { useEmit } from 'eventrix';
+
+interface TaskProps {
+ task: {
+ id: string;
+ title: string;
+ status: string;
+ };
+}
+
+const Task: React.FC = ({ task }) => {
+ const emit = useEmit();
+ return (
+
+
emit(task.status === 'todo' ? MARK_TASK_AS_DONE_EVENT_NAME : MARK_TASK_AS_TODO_EVENT_NAME, { id: task.id })}
+ />
+
{task.title}
+
{task.status}
+
+
+ );
+};
+
+export default Task;
diff --git a/examples/todo-list/src/components/TodoFooter.tsx b/examples/todo-list/src/components/TodoFooter.tsx
new file mode 100644
index 0000000..bdbb30b
--- /dev/null
+++ b/examples/todo-list/src/components/TodoFooter.tsx
@@ -0,0 +1,13 @@
+import React from 'react';
+import './TodoList/TodoList.css';
+import { useEventrixState } from 'eventrix';
+
+const TodoFooter: React.FC = () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const [tasks] = useEventrixState('tasks');
+ const [statusFilter] = useEventrixState('filter.status');
+ const tasksList = statusFilter ? tasks.filter((task) => task.status === statusFilter) : tasks;
+ return Tasks count: {tasksList.length}
;
+};
+
+export default TodoFooter;
diff --git a/examples/todo-list/src/components/TodoList/TodoList.css b/examples/todo-list/src/components/TodoList/TodoList.css
new file mode 100644
index 0000000..ef7bd68
--- /dev/null
+++ b/examples/todo-list/src/components/TodoList/TodoList.css
@@ -0,0 +1,102 @@
+.list {
+ width: 350px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.list-item {
+ text-align: left;
+ display: flex;
+ align-items: center;
+ padding: 10px;
+ background: #ddd;
+ width: 100%;
+ border-top: 1px solid #c1c1c1;
+}
+
+.task-title {
+ width: 100%;
+ display: inline-block;
+}
+
+.list-item input {
+ display: inline-block;
+ width: 40px;
+ height: 25px;
+ margin-right: 10px;
+}
+
+.list-item button {
+ width: 120px;
+ margin-right: 15px;
+}
+
+.status {
+ background: #333;
+ color: #fff;
+ display: inline-block;
+ padding: 5px 8px;
+ border-radius: 4px;
+ margin-left: 10px;
+ width: 100px;
+ text-align: center;
+}
+
+.todo {
+ background: #333;
+ color: #fff;
+}
+
+.done {
+ background: green;
+ color: #fff;
+}
+
+.removeButton {
+ width: 25px !important;
+ height: 25px;
+ display: inline-block;
+ text-align: center;
+ background: red;
+ color: #fff;
+ border: 1px solid #333;
+ border-radius: 100%;
+ margin-left: 10px;
+ cursor: pointer;
+ opacity: 0.7;
+}
+
+.removeButton:hover {
+ opacity: 1;
+}
+
+.filters {
+ display: flex;
+ flex-wrap: nowrap;
+ background: #efefef;
+ width: 100%;
+ padding: 0 10px;
+}
+
+.status-button {
+ display: inline-block;
+ width: 100%;
+ padding: 8px 0;
+ cursor: pointer;
+}
+
+.status-button.active {
+ border-bottom: 3px solid #4796ff;
+}
+
+.status-button:hover {
+ background: #ddd;
+}
+
+.list-placeholder {
+ border-top: 1px solid #c1c1c1;
+ padding: 20px 10px;
+ width: 100%;
+ background: #ddd;
+ color: #8a8a8a;
+}
diff --git a/examples/todo-list/src/components/TodoList/TodoList.tsx b/examples/todo-list/src/components/TodoList/TodoList.tsx
new file mode 100644
index 0000000..765533a
--- /dev/null
+++ b/examples/todo-list/src/components/TodoList/TodoList.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import Task from '../Task';
+import './TodoList.css';
+import { useEventrixState } from 'eventrix';
+
+const TodoList: React.FC = () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const [tasks] = useEventrixState('tasks');
+ const [statusFilter, setStatusFilter] = useEventrixState('filter.status');
+ const tasksList = statusFilter ? tasks.filter((task) => task.status === statusFilter) : tasks;
+ return (
+
+
+
setStatusFilter('')}>
+ All
+
+
setStatusFilter('todo')}>
+ To do
+
+
setStatusFilter('done')}>
+ Done
+
+
+
+ {tasksList.length === 0 &&
Tasks list empty
}
+ {tasksList.map((task) => (
+
+ ))}
+
+
+ );
+};
+
+export default TodoList;
diff --git a/examples/todo-list/src/eventrix/index.ts b/examples/todo-list/src/eventrix/index.ts
new file mode 100644
index 0000000..d37c800
--- /dev/null
+++ b/examples/todo-list/src/eventrix/index.ts
@@ -0,0 +1,8 @@
+import { Eventrix } from 'eventrix';
+import taskEventsReceivers from './tasks';
+
+const initialState = {
+ tasks: [],
+};
+
+export default new Eventrix(initialState, taskEventsReceivers);
diff --git a/examples/todo-list/src/eventrix/tasks.ts b/examples/todo-list/src/eventrix/tasks.ts
new file mode 100644
index 0000000..8a4d945
--- /dev/null
+++ b/examples/todo-list/src/eventrix/tasks.ts
@@ -0,0 +1,34 @@
+import findIndex from 'lodash/findIndex';
+import { EventsReceiver } from 'eventrix';
+
+export const CREATE_TASK_EVENT_NAME = 'tasks:create';
+export const REMOVE_TASK_EVENT_NAME = 'tasks:remove';
+export const MARK_TASK_AS_DONE_EVENT_NAME = 'tasks:markAsDone';
+export const MARK_TASK_AS_TODO_EVENT_NAME = 'tasks:markAsTodo';
+
+const createTaskReceiver = new EventsReceiver(CREATE_TASK_EVENT_NAME, (eventName, { task }, stateManager) => {
+ const tasks = stateManager.getState('tasks');
+ stateManager.setState('tasks', [task, ...tasks]);
+});
+
+const removeTaskReceiver = new EventsReceiver(REMOVE_TASK_EVENT_NAME, (eventName, { id }, stateManager) => {
+ const tasks = stateManager.getState('tasks');
+ const newTasks = tasks.filter((task) => task.id !== id);
+ stateManager.setState('tasks', newTasks);
+});
+
+const markAsDoneReceiver = new EventsReceiver(MARK_TASK_AS_DONE_EVENT_NAME, (eventName, { id }, stateManager) => {
+ const tasks = stateManager.getState('tasks');
+ const taskIndex = findIndex(tasks, (task) => task.id === id);
+ const task = { ...tasks[taskIndex], status: 'done' };
+ stateManager.setState(`tasks.${taskIndex}`, task);
+});
+
+const markAsTodoReceiver = new EventsReceiver(MARK_TASK_AS_TODO_EVENT_NAME, (eventName, { id }, stateManager) => {
+ const tasks = stateManager.getState('tasks');
+ const taskIndex = findIndex(tasks, (task) => task.id === id);
+ const task = { ...tasks[taskIndex], status: 'todo' };
+ stateManager.setState(`tasks.${taskIndex}`, task);
+});
+
+export default [markAsDoneReceiver, markAsTodoReceiver, createTaskReceiver, removeTaskReceiver];
diff --git a/examples/todo-list/src/index.css b/examples/todo-list/src/index.css
new file mode 100644
index 0000000..3c5c70b
--- /dev/null
+++ b/examples/todo-list/src/index.css
@@ -0,0 +1,27 @@
+.App {
+ font-family: sans-serif;
+ text-align: center;
+}
+
+.logo {
+ text-align: left;
+ margin-bottom: 15px;
+}
+
+.logo img {
+ height: 80px;
+}
+
+.todo-app {
+ width: 350px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.todo-footer {
+ width: 100%;
+ background: #efefef;
+ border-radius: 0 0 4px 4px;
+ border-top: 1px solid #c1c1c1;
+ padding: 10px;
+}
diff --git a/examples/todo-list/src/main.tsx b/examples/todo-list/src/main.tsx
new file mode 100644
index 0000000..1fdb340
--- /dev/null
+++ b/examples/todo-list/src/main.tsx
@@ -0,0 +1,10 @@
+import React, { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import './index.css';
+import App from './App';
+
+createRoot(document.getElementById('root')!).render(
+
+
+ ,
+);
diff --git a/examples/todo-list/src/utils/helpers.ts b/examples/todo-list/src/utils/helpers.ts
new file mode 100644
index 0000000..c894a4c
--- /dev/null
+++ b/examples/todo-list/src/utils/helpers.ts
@@ -0,0 +1,4 @@
+let id = 0;
+export const makeid = () => {
+ return id++;
+};
diff --git a/examples/todo-list/tsconfig.json b/examples/todo-list/tsconfig.json
new file mode 100644
index 0000000..45a7d45
--- /dev/null
+++ b/examples/todo-list/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "compilerOptions": {
+ "paths": {
+ "eventrix": ["../../src/index.ts"]
+ },
+ "baseUrl": "./",
+ "rootDir": "../../",
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "Node",
+ "jsx": "react-jsx",
+ "types": ["vite/client", "node", "react"],
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
+ "allowJs": true,
+ "allowSyntheticDefaultImports": true,
+ "useDefineForClassFields": true,
+ "strict": true,
+ "skipLibCheck": false,
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noUnusedLocals": false,
+ "noUnusedParameters": false,
+ "noImplicitReturns": false,
+ "forceConsistentCasingInFileNames": true
+
+ },
+}
diff --git a/examples/todo-list/vite.config.ts b/examples/todo-list/vite.config.ts
new file mode 100644
index 0000000..c7f2182
--- /dev/null
+++ b/examples/todo-list/vite.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import path from 'path';
+
+// https://vite.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ eventrix: path.join(__dirname, '../../src/index.ts'),
+ },
+ },
+});
diff --git a/package.json b/package.json
index b071094..7f69c3d 100644
--- a/package.json
+++ b/package.json
@@ -2,18 +2,27 @@
"name": "eventrix",
"version": "2.10.2",
"description": "A system for managing the state of the application based on the events broadcast in the application",
- "main": "dist/index.js",
- "repository": "https://github.com/rstgroup/eventrix",
+ "main": "dist/index.cjs.js",
+ "module": "dist/index.es.js",
+ "types": "dist/index.d.ts",
+ "type": "module",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/rstgroup/eventrix.git"
+ },
+ "files": [
+ "dist"
+ ],
"homepage": "https://eventrix.js.org",
"scripts": {
- "test": "jest --runInBand",
- "test:watch": "jest --watch",
- "test:clean": "jest --clearCache",
- "build": "webpack --config webpack-prod.config.js --stats-error-details",
- "coverage": "jest --coverage --runInBand",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:clean": "vitest --clearCache",
+ "build": "vite build",
+ "coverage": "vitest run --coverage",
"coverage:report": "cat ./coverage/lcov.info | coveralls",
- "lint": "eslint src --ext ts,tsx,js",
- "lint:fix": "eslint src --ext ts,tsx,js --fix "
+ "lint": "eslint",
+ "lint:fix": "eslint --fix"
},
"pre-commit": [
"lint",
@@ -31,6 +40,7 @@
"use-sync-external-store": "^1.0.0"
},
"devDependencies": {
+ "@eslint/js": "^9.16.0",
"@babel/cli": "^7.7.0",
"@babel/core": "^7.7.0",
"@babel/plugin-proposal-class-properties": "^7.2.1",
@@ -51,34 +61,35 @@
"@babel/traverse": "^7.7.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^12.1.15 || ^13.1.1",
- "@types/jest": "^27.4.1",
"@types/lodash": "^4.14.170",
"@types/react": "^17.0.11 || ^18.0.0",
"@types/use-sync-external-store": "0.0.3",
- "@typescript-eslint/eslint-plugin": "^4.28.0",
- "@typescript-eslint/parser": "^4.28.0",
+ "@typescript-eslint/eslint-plugin": "^8.17.0",
+ "@typescript-eslint/parser": "^8.17.0",
+ "@vitejs/plugin-react": "^4.3.4",
+ "@vitest/coverage-v8": "^2.1.8",
"babel-eslint": "^10.1.0",
"babel-jest": "^27.5.1",
"babel-loader": "^8.0.6",
"clean-webpack-plugin": "^4.0.0",
"coveralls": "^3.1.0",
- "eslint": "^7.32.0",
- "eslint-config-prettier": "^6.14.0",
- "eslint-plugin-import": "^2.22.1",
- "eslint-plugin-jest": "^25.0.1",
- "eslint-plugin-prettier": "^3.1.4",
- "eslint-plugin-react": "^7.21.5",
+ "eslint": "^9.16.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-jsdoc": "^50.6.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "eslint-plugin-react": "^7.37.2",
"jest": "^27.5.1",
"loader-utils": "^2.0.0",
"pre-commit": "^1.2.2",
- "prettier": "^2.4.1",
- "ts-jest": "^27.1.4",
+ "prettier": "^3.4.2",
"ts-loader": "^9.2.3",
- "typescript": "^4.2.4",
- "webpack": "^5.31.2",
- "webpack-cli": "^4.6.0"
+ "typescript": "~5.6.2",
+ "typescript-eslint": "^8.17.0",
+ "vite": "^6.0.1",
+ "vite-plugin-dts": "^4.3.0",
+ "vitest": "^2.1.8"
},
- "types": "./types/index.d.ts",
"jest": {
"testEnvironment": "jsdom",
"moduleFileExtensions": [
@@ -96,5 +107,12 @@
}
},
"collectCoverage": true
+ },
+ "directories": {
+ "lib": "dist",
+ "test": "__tests__"
+ },
+ "bugs": {
+ "url": "https://github.com/rstgroup/eventrix/issues"
}
}
diff --git a/src/Eventrix.test.ts b/src/Eventrix.test.ts
index 9d39386..d5ac954 100644
--- a/src/Eventrix.test.ts
+++ b/src/Eventrix.test.ts
@@ -24,9 +24,9 @@ describe('Eventrix', () => {
},
},
};
- mockReceiver = jest.fn(() => 'testReceiverData');
+ mockReceiver = vi.fn(() => 'testReceiverData');
eventsReceivers = [new EventsReceiver('getFoo', mockReceiver)];
- mockListener = jest.fn();
+ mockListener = vi.fn();
eventrix = new Eventrix(initialState, eventsReceivers);
});
it('should getState from store', () => {
@@ -84,14 +84,14 @@ describe('Eventrix', () => {
expect(eventrix.getState('bar')).toEqual(newbarState);
});
it('should create new eventrix instance with event scope', () => {
- const mockedListener = jest.fn();
+ const mockedListener = vi.fn();
eventrix.listen('Test:setTest', mockedListener);
const testInstance = eventrix.create({ eventScope: 'Test' });
testInstance.emit('setTest', 'test');
expect(mockedListener).toHaveBeenCalledWith('test', []);
});
it('should create new second level eventrix instance with event scope', () => {
- const mockedListener = jest.fn();
+ const mockedListener = vi.fn();
eventrix.listen('Test:List:setTest', mockedListener);
const testInstance = eventrix.create({ eventScope: 'Test' });
const testListInstance = testInstance.create({ eventScope: 'List' });
@@ -141,7 +141,7 @@ describe('Eventrix', () => {
const eventsReceiver = new EventsReceiver(eventName, (name, data, store) => {
throw 'failedReceiver';
});
- const errorCallback = jest.fn();
+ const errorCallback = vi.fn();
eventrix.onError(errorCallback);
eventrix.useReceiver(eventsReceiver);
return eventrix.emit(eventName, eventData).catch(() => {
@@ -157,7 +157,7 @@ describe('Eventrix', () => {
const failedListener = () => {
throw 'failedListener';
};
- const errorCallback = jest.fn();
+ const errorCallback = vi.fn();
eventrix.onError(errorCallback);
eventrix.listen(eventName, failedListener);
return eventrix.emit(eventName, eventData).catch(() => {
diff --git a/src/EventrixDebugger.test.ts b/src/EventrixDebugger.test.ts
index 8cf1a4b..3e63488 100644
--- a/src/EventrixDebugger.test.ts
+++ b/src/EventrixDebugger.test.ts
@@ -17,7 +17,7 @@ describe('EventrixDebugger', () => {
foo: 'bar',
},
};
- mockReceiver = jest.fn(() => 'testReceiverData');
+ mockReceiver = vi.fn(() => 'testReceiverData');
eventsReceivers = [new EventsReceiver('getFoo', mockReceiver)];
eventrix = new Eventrix(initialState, eventsReceivers);
eDebugger = new EventrixDebugger(eventrix);
diff --git a/src/EventsEmitter.test.ts b/src/EventsEmitter.test.ts
index 7c20e35..2e5c2e7 100644
--- a/src/EventsEmitter.test.ts
+++ b/src/EventsEmitter.test.ts
@@ -3,11 +3,11 @@ import EventsEmitter from './EventsEmitter';
describe('EventsEmitter', () => {
describe('listen', () => {
const eventsEmitter = new EventsEmitter();
- const mockListener = jest.fn();
- const mockListener2 = jest.fn();
+ const mockListener = vi.fn();
+ const mockListener2 = vi.fn();
it('should not register listener if is not a function', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
// @ts-ignore
eventsEmitter.listen('testEvent', {});
expect(eventsEmitter.listeners.testEvent).toHaveLength(0);
@@ -15,21 +15,21 @@ describe('EventsEmitter', () => {
});
it('should register listener', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
eventsEmitter.listen('testEvent', mockListener);
expect(eventsEmitter.listeners.testEvent).toHaveLength(1);
expect(console.warn).not.toBeCalled();
});
it('should not register the same listener two times', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
eventsEmitter.listen('testEvent', mockListener);
expect(eventsEmitter.listeners.testEvent).toHaveLength(1);
expect(console.warn).toBeCalledWith('EventsEmitter->listen - "testEvent" events listener is already registered');
});
it('should register next listeners if is not the same', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
eventsEmitter.listen('testEvent', mockListener2);
expect(eventsEmitter.listeners.testEvent).toHaveLength(2);
expect(console.warn).not.toBeCalled();
@@ -37,19 +37,19 @@ describe('EventsEmitter', () => {
});
describe('unlisten', () => {
const eventsEmitter = new EventsEmitter();
- const mockListener = jest.fn();
- const mockListener2 = jest.fn();
+ const mockListener = vi.fn();
+ const mockListener2 = vi.fn();
eventsEmitter.listen('testEvent', mockListener);
it('should not unregistered listener if event not exist', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
eventsEmitter.unlisten('testEvent3', mockListener);
expect(eventsEmitter.listeners.testEvent3).toBeUndefined();
expect(console.warn).toBeCalledWith('EventsEmitter->unlisten - "testEvent3" event not registered');
});
it('should not unregistered listener if event dont have registred listeners', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
// @ts-ignore
eventsEmitter.listen('testEvent2', {});
eventsEmitter.unlisten('testEvent2', mockListener);
@@ -58,14 +58,14 @@ describe('EventsEmitter', () => {
});
it('should not unregistered listener if not exists', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
eventsEmitter.unlisten('testEvent', mockListener2);
expect(eventsEmitter.listeners.testEvent).toHaveLength(1);
expect(console.warn).toBeCalledWith('EventsEmitter->unlisten - "testEvent" listener not exists');
});
it('should unregistered listener', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
eventsEmitter.unlisten('testEvent', mockListener);
expect(eventsEmitter.listeners.testEvent).toBe(undefined);
expect(console.warn).not.toBeCalled();
@@ -74,34 +74,34 @@ describe('EventsEmitter', () => {
describe('emit', () => {
let eventsEmitter = new EventsEmitter();
- let mockListener = jest.fn();
+ let mockListener = vi.fn();
let store: any = {
state: {},
receivers: {},
- setState: jest.fn(),
- getState: jest.fn(),
- useReceiver: jest.fn(),
- removeReceiver: jest.fn(),
- getEventData: jest.fn(),
- runListeners: jest.fn(),
- emitWild: jest.fn(),
+ setState: vi.fn(),
+ getState: vi.fn(),
+ useReceiver: vi.fn(),
+ removeReceiver: vi.fn(),
+ getEventData: vi.fn(),
+ runListeners: vi.fn(),
+ emitWild: vi.fn(),
runReceivers: () => ['test'],
};
beforeEach(() => {
store = {
state: {},
receivers: {},
- setState: jest.fn(),
- getState: jest.fn(),
- useReceiver: jest.fn(),
- removeReceiver: jest.fn(),
- getEventData: jest.fn(),
- runListeners: jest.fn(),
- emitWild: jest.fn(),
+ setState: vi.fn(),
+ getState: vi.fn(),
+ useReceiver: vi.fn(),
+ removeReceiver: vi.fn(),
+ getEventData: vi.fn(),
+ runListeners: vi.fn(),
+ emitWild: vi.fn(),
runReceivers: () => ['test'],
};
eventsEmitter = new EventsEmitter();
- mockListener = jest.fn();
+ mockListener = vi.fn();
eventsEmitter.listen('testEvent', mockListener);
});
@@ -121,9 +121,9 @@ describe('EventsEmitter', () => {
describe('emitWild', () => {
it('should call all matched events listeners with data and set matched listeners cache', () => {
eventsEmitter.useStore(store);
- const fooMockListener = jest.fn();
- const fooBarMockListener = jest.fn();
- const fooBarFooMockListener = jest.fn();
+ const fooMockListener = vi.fn();
+ const fooBarMockListener = vi.fn();
+ const fooBarFooMockListener = vi.fn();
eventsEmitter.listen('foo', fooMockListener);
eventsEmitter.listen('foo.bar', fooBarMockListener);
eventsEmitter.listen('foo.bar.foo', fooBarFooMockListener);
@@ -142,9 +142,9 @@ describe('EventsEmitter', () => {
it('should use matched listeners cache when is available', () => {
eventsEmitter.useStore(store);
- const fooMockListener = jest.fn();
- const fooBarMockListener = jest.fn();
- const fooBarFooMockListener = jest.fn();
+ const fooMockListener = vi.fn();
+ const fooBarMockListener = vi.fn();
+ const fooBarFooMockListener = vi.fn();
eventsEmitter.listen('foo', fooMockListener);
eventsEmitter.listen('foo.bar', fooBarMockListener);
eventsEmitter.listen('foo.bar.foo', fooBarFooMockListener);
@@ -165,13 +165,13 @@ describe('EventsEmitter', () => {
const storeWithPromiseReceivers: any = {
state: {},
receivers: {},
- setState: jest.fn(),
- getState: jest.fn(),
- useReceiver: jest.fn(),
- removeReceiver: jest.fn(),
- getEventData: jest.fn(),
- runListeners: jest.fn(),
- emitWild: jest.fn(),
+ setState: vi.fn(),
+ getState: vi.fn(),
+ useReceiver: vi.fn(),
+ removeReceiver: vi.fn(),
+ getEventData: vi.fn(),
+ runListeners: vi.fn(),
+ emitWild: vi.fn(),
runReceivers: () => Promise.resolve(['testResponse']),
};
eventsEmitter.useStore(storeWithPromiseReceivers);
diff --git a/src/EventsReceiver.test.ts b/src/EventsReceiver.test.ts
index a541b28..c35fce7 100644
--- a/src/EventsReceiver.test.ts
+++ b/src/EventsReceiver.test.ts
@@ -11,7 +11,7 @@ describe('EventsReceiver', () => {
expect(eventsReceiver.getEventsNames()).toEqual(['testEvent']);
});
it('should handle event by receiver', () => {
- const receiver = jest.fn();
+ const receiver = vi.fn();
const stateManager: any = {};
const eventsReceiver = new EventsReceiver('testEvent', receiver);
eventsReceiver.handleEvent('testEvent', {}, stateManager);
@@ -23,10 +23,10 @@ describe('fetchToStateReceiver', () => {
let stateManager: any = {};
beforeEach(() => {
stateManager = {
- setState: jest.fn(),
- getState: jest.fn(() => ({})),
+ setState: vi.fn(),
+ getState: vi.fn(() => ({})),
eventsEmitter: {
- emit: jest.fn(),
+ emit: vi.fn(),
},
};
});
@@ -72,10 +72,10 @@ describe('fetchStateReceiver', () => {
let stateManager: any = {};
beforeEach(() => {
stateManager = {
- setState: jest.fn(),
- getState: jest.fn(() => ({})),
+ setState: vi.fn(),
+ getState: vi.fn(() => ({})),
eventsEmitter: {
- emit: jest.fn(),
+ emit: vi.fn(),
},
};
});
@@ -127,7 +127,7 @@ describe('fetchStateReceiver', () => {
describe('fetchHandler', () => {
let emit: EmitI;
beforeEach(() => {
- emit = jest.fn();
+ emit = vi.fn();
});
it('should fetch data and emit success event when promise resolved', () => {
const fetchResponse = { test: 'test' };
diff --git a/src/StateManager.test.ts b/src/StateManager.test.ts
index f5e0d59..6599e54 100644
--- a/src/StateManager.test.ts
+++ b/src/StateManager.test.ts
@@ -4,13 +4,13 @@ import EventsReceiver from './EventsReceiver';
describe('StateManager', () => {
let eventsEmitter: any = {
listeners: {},
- listen: jest.fn(),
- unlisten: jest.fn(),
- useStore: jest.fn(),
- emit: jest.fn(),
- emitWild: jest.fn(),
- getEventData: jest.fn(),
- runListeners: jest.fn(),
+ listen: vi.fn(),
+ unlisten: vi.fn(),
+ useStore: vi.fn(),
+ emit: vi.fn(),
+ emitWild: vi.fn(),
+ getEventData: vi.fn(),
+ runListeners: vi.fn(),
};
let stateManager = new StateManager(eventsEmitter);
let initialState: object;
@@ -23,15 +23,15 @@ describe('StateManager', () => {
};
eventsEmitter = {
listeners: {},
- listen: jest.fn(),
- unlisten: jest.fn(),
- useStore: jest.fn(),
- emit: jest.fn(),
- emitWild: jest.fn(),
- getEventData: jest.fn(),
- runListeners: jest.fn(),
+ listen: vi.fn(),
+ unlisten: vi.fn(),
+ useStore: vi.fn(),
+ emit: vi.fn(),
+ emitWild: vi.fn(),
+ getEventData: vi.fn(),
+ runListeners: vi.fn(),
};
- eventsReceiver = jest.fn(() => ({ testData: {} }));
+ eventsReceiver = vi.fn(() => ({ testData: {} }));
eventsReceivers = [new EventsReceiver(['testEvent', 'secondTestEvent'], eventsReceiver)];
stateManager = new StateManager(eventsEmitter, initialState, eventsReceivers);
});
@@ -52,15 +52,15 @@ describe('StateManager', () => {
});
});
it('should register events receiver only one time', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
stateManager.useReceiver(eventsReceivers[0]);
expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "testEvent" events receiver is already registered`);
expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "secondTestEvent" events receiver is already registered`);
});
it('should not register events receiver when dont have handleEvent function', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
// @ts-ignore
- stateManager.useReceiver({ getEventsNames: () => ['testEvent', 'secondTestEvent'], receiver: jest.fn(), eventsNames: [] });
+ stateManager.useReceiver({ getEventsNames: () => ['testEvent', 'secondTestEvent'], receiver: vi.fn(), eventsNames: [] });
expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "testEvent" receiver is not a function`);
expect(console.warn).toHaveBeenCalledWith(`Store->registerReceiver - "secondTestEvent" receiver is not a function`);
});
@@ -73,12 +73,12 @@ describe('StateManager', () => {
});
});
it('should not unregister events receiver when event not exists', () => {
- console.warn = jest.fn();
- stateManager.removeReceiver({ getEventsNames: () => ['testEvent2'], handleEvent: jest.fn(), receiver: jest.fn(), eventsNames: [] });
+ console.warn = vi.fn();
+ stateManager.removeReceiver({ getEventsNames: () => ['testEvent2'], handleEvent: vi.fn(), receiver: vi.fn(), eventsNames: [] });
expect(console.warn).toHaveBeenCalledWith(`Store->unregisterReceiver - "testEvent2" event not registered`);
});
it('should not unregister events receiver when event receivers are empty array', () => {
- console.warn = jest.fn();
+ console.warn = vi.fn();
stateManager.receivers = {
testEvent: [],
secondTestEvent: [],
@@ -88,8 +88,8 @@ describe('StateManager', () => {
expect(console.warn).toHaveBeenCalledWith(`Store->unregisterReceiver - "secondTestEvent" event dont have registered receiver`);
});
it('should not unregister events receiver when event receiver not registered', () => {
- console.warn = jest.fn();
- stateManager.removeReceiver({ getEventsNames: () => ['testEvent'], handleEvent: jest.fn(), receiver: jest.fn(), eventsNames: [] });
+ console.warn = vi.fn();
+ stateManager.removeReceiver({ getEventsNames: () => ['testEvent'], handleEvent: vi.fn(), receiver: vi.fn(), eventsNames: [] });
expect(console.warn).toHaveBeenCalledWith(`Store->unregisterReceiver - "testEvent" receiver not exists`);
});
it('should emit change state event for parents and children', () => {
@@ -114,7 +114,7 @@ describe('StateManager', () => {
expect(stateManager.getParentPath('a')).toEqual('');
});
it('should handle promise from events receivers', () => {
- const receiver = jest.fn(() => Promise.resolve({ asyncData: {} }));
+ const receiver = vi.fn(() => Promise.resolve({ asyncData: {} }));
const asyncReceiver = new EventsReceiver('testEvent', receiver);
stateManager.useReceiver(asyncReceiver);
return stateManager.runReceivers('testEvent', { eventData: {} }).then((results: any) => {
diff --git a/src/decorators/listener.test.ts b/src/decorators/listener.test.ts
index 7f8e3ec..dda5b3d 100644
--- a/src/decorators/listener.test.ts
+++ b/src/decorators/listener.test.ts
@@ -8,7 +8,7 @@ describe('listener', () => {
const GET_LIST_EVENT_NAME = 'Test:loadList';
const EXTEND_LIST_EVENT_NAME = 'Test:extendList';
it('should invoke listener when get list event is emitted', () => {
- const callback = jest.fn();
+ const callback = vi.fn();
const data: string[] = ['test'];
@useEventrix
@@ -33,8 +33,8 @@ describe('listener', () => {
});
it('should invoke listener when extend list event is emitted', () => {
- const callback = jest.fn();
- const extendCallback = jest.fn();
+ const callback = vi.fn();
+ const extendCallback = vi.fn();
const extendEventData: string[] = ['test', 'test2'];
@useEventrix
diff --git a/src/decorators/useEventrix.test.ts b/src/decorators/useEventrix.test.ts
index 4dee3ad..8b42464 100644
--- a/src/decorators/useEventrix.test.ts
+++ b/src/decorators/useEventrix.test.ts
@@ -14,7 +14,7 @@ describe('useEventrix', () => {
}
}
const eventrix = new Eventrix({});
- const testClassInstance = new FetchToStateTestClass({ eventrix, ajax: jest.fn() });
+ const testClassInstance = new FetchToStateTestClass({ eventrix, ajax: vi.fn() });
expect(testClassInstance.eventrix).toEqual(eventrix);
});
});
diff --git a/src/decorators/useEventrix.ts b/src/decorators/useEventrix.ts
index bc5d858..d1ee429 100644
--- a/src/decorators/useEventrix.ts
+++ b/src/decorators/useEventrix.ts
@@ -23,6 +23,7 @@ export interface ServicesI {
}
export interface EventrixClassI {
+ // eslint-disable-next-line @typescript-eslint/no-misused-new
new (services: ServicesI, ...args: any[]): EventrixClassI;
eventrix?: EventrixI;
eventrixReceiver?: ReceiverRegister[];
diff --git a/src/helpers.test.ts b/src/helpers.test.ts
index 5ecc6b1..1c1f02d 100644
--- a/src/helpers.test.ts
+++ b/src/helpers.test.ts
@@ -246,7 +246,7 @@ describe('helpers', () => {
describe('registerListeners', () => {
it('should register listeners all wild listeners', () => {
const eventrix = new Eventrix();
- const mockListener = jest.fn();
+ const mockListener = vi.fn();
registerListeners(eventrix, 'a.b.c.d', mockListener);
expect(eventrix.eventsEmitter.listeners['setState:a.*']).toHaveLength(1);
expect(eventrix.eventsEmitter.listeners['setState:a.b.*']).toHaveLength(1);
@@ -255,7 +255,7 @@ describe('helpers', () => {
});
it('should unregister all register listeners', () => {
const eventrix = new Eventrix();
- const mockListener = jest.fn();
+ const mockListener = vi.fn();
const unregisterListeners = registerListeners(eventrix, 'a.b.c.d', mockListener);
unregisterListeners();
expect(eventrix.eventsEmitter.listeners['setState:a.*']).toEqual(undefined);
diff --git a/src/persistStore.test.ts b/src/persistStore.test.ts
index ec02020..493da87 100644
--- a/src/persistStore.test.ts
+++ b/src/persistStore.test.ts
@@ -21,8 +21,8 @@ describe('persistStore', () => {
beforeEach(() => {
storage = {
- setItem: jest.fn(),
- getItem: jest.fn(() => ''),
+ setItem: vi.fn(),
+ getItem: vi.fn(() => ''),
};
initialState = {
a: 'a state',
@@ -117,8 +117,8 @@ describe('persistStore', () => {
beforeEach(() => {
storage = {
- setItem: jest.fn(() => Promise.resolve()),
- getItem: jest.fn(() => Promise.resolve('')),
+ setItem: vi.fn(() => Promise.resolve()),
+ getItem: vi.fn(() => Promise.resolve('')),
};
initialState = {
a: 'a state',
diff --git a/src/persistStore.ts b/src/persistStore.ts
index 8e42b54..7ae51bb 100644
--- a/src/persistStore.ts
+++ b/src/persistStore.ts
@@ -14,10 +14,12 @@ const getStorageData = (storage: SyncStorage | AsyncStorage, storageKey: string)
return JSON.parse(storageData || defaultData).data;
};
+// eslint-disable-next-line @typescript-eslint/no-empty-object-type
const getStateKeys = (storeState: StateI): StateKeysList => {
return Object.keys(storeState) as StateKeysList;
};
+// eslint-disable-next-line @typescript-eslint/no-empty-object-type
const connectPersistStore = (eventrix: EventrixI, config: PersistStoreConfig): void => {
const {
blackList,
diff --git a/src/react/context/PersistStoreGate.test.tsx b/src/react/context/PersistStoreGate.test.tsx
index 3a98be1..5245180 100644
--- a/src/react/context/PersistStoreGate.test.tsx
+++ b/src/react/context/PersistStoreGate.test.tsx
@@ -12,8 +12,8 @@ describe('PersistStoreGate', () => {
let storage: AsyncStorage;
beforeEach(() => {
storage = {
- setItem: jest.fn(() => Promise.resolve()),
- getItem: jest.fn(() => Promise.resolve('')),
+ setItem: vi.fn(() => Promise.resolve()),
+ getItem: vi.fn(() => Promise.resolve('')),
};
});
@@ -58,8 +58,8 @@ describe('PersistStoreGate', () => {
let storage: SyncStorage;
beforeEach(() => {
storage = {
- setItem: jest.fn(),
- getItem: jest.fn(() => ''),
+ setItem: vi.fn(),
+ getItem: vi.fn(() => ''),
};
});
diff --git a/src/react/context/context.test.tsx b/src/react/context/context.test.tsx
index 41ae02c..0cdc18f 100644
--- a/src/react/context/context.test.tsx
+++ b/src/react/context/context.test.tsx
@@ -15,7 +15,7 @@ describe('context with default eventrix instance', () => {
it('should invoke callback when event emitted', () => {
const eventrixInstance = defaultEventrixInstance;
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
@@ -28,7 +28,7 @@ describe('context with default eventrix instance', () => {
it('should invoke callback when event emitted with scope', () => {
const eventrixInstance = defaultEventrixInstance;
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
@@ -43,7 +43,7 @@ describe('context with default eventrix instance', () => {
it('should invoke callback when event emitted with deep scope', () => {
const eventrixInstance = defaultEventrixInstance;
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
diff --git a/src/react/decorators/eventrixComponent.test.tsx b/src/react/decorators/eventrixComponent.test.tsx
index ba4d79a..691c37a 100644
--- a/src/react/decorators/eventrixComponent.test.tsx
+++ b/src/react/decorators/eventrixComponent.test.tsx
@@ -25,7 +25,7 @@ describe('listener', () => {
it('should use eventrix context and extend component by eventrix', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
diff --git a/src/react/decorators/eventrixState.test.tsx b/src/react/decorators/eventrixState.test.tsx
index 2a3929d..4ced8e3 100644
--- a/src/react/decorators/eventrixState.test.tsx
+++ b/src/react/decorators/eventrixState.test.tsx
@@ -49,7 +49,7 @@ describe('eventrixState', () => {
bar: 'empty',
},
});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
const { getByTestId } = render(
@@ -69,7 +69,7 @@ describe('eventrixState', () => {
bar: 'empty',
},
});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
const { getByTestId } = render(
diff --git a/src/react/decorators/eventrixState.ts b/src/react/decorators/eventrixState.ts
index 5e268d1..58f8f26 100644
--- a/src/react/decorators/eventrixState.ts
+++ b/src/react/decorators/eventrixState.ts
@@ -10,7 +10,7 @@ function eventrixState(statePath: string, stateName: string): StateDecor
return class extends Class {
eventrixStates?: DecoratorEventrixStateI[];
eventrixListeners?: DecoratorEventrixListenerI[];
- // eslint-disable-next-line no-undef
+
[key: string]: any;
constructor(props: any, context: any) {
diff --git a/src/react/decorators/listener.test.tsx b/src/react/decorators/listener.test.tsx
index 33a801c..21e012b 100644
--- a/src/react/decorators/listener.test.tsx
+++ b/src/react/decorators/listener.test.tsx
@@ -70,7 +70,7 @@ describe('listener', () => {
it('should invoke callback when event emitted', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
@@ -83,8 +83,8 @@ describe('listener', () => {
it('should invoke callback when event emitted and extend componentDidMount method', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
- const didMountCallbackMock = jest.fn();
+ const callbackMock = vi.fn();
+ const didMountCallbackMock = vi.fn();
render(
@@ -98,8 +98,8 @@ describe('listener', () => {
it('should invoke callback when event emitted and extend componentWillUnmount method', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
- const willUnmountCallbackMock = jest.fn();
+ const callbackMock = vi.fn();
+ const willUnmountCallbackMock = vi.fn();
const { unmount } = render(
diff --git a/src/react/decorators/stateListener.test.tsx b/src/react/decorators/stateListener.test.tsx
index 4ae88b3..08e48c0 100644
--- a/src/react/decorators/stateListener.test.tsx
+++ b/src/react/decorators/stateListener.test.tsx
@@ -24,7 +24,7 @@ describe('stateListener', () => {
it('should invoke callback when state changed', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
diff --git a/src/react/hocs/withEventrix.test.tsx b/src/react/hocs/withEventrix.test.tsx
index bcd840f..ca6b5c8 100644
--- a/src/react/hocs/withEventrix.test.tsx
+++ b/src/react/hocs/withEventrix.test.tsx
@@ -19,7 +19,7 @@ describe('withEventrix', () => {
const TestContainer = ({ eventrix, children }: any) => {children};
it('should run callback when component render', async () => {
- const mockedCallback = jest.fn();
+ const mockedCallback = vi.fn();
const eventrixInstance = new Eventrix({});
eventrixInstance.listen('testEvent', mockedCallback);
render(
diff --git a/src/react/hocs/withEventrix.tsx b/src/react/hocs/withEventrix.tsx
index 1afafff..9c3d2e7 100644
--- a/src/react/hocs/withEventrix.tsx
+++ b/src/react/hocs/withEventrix.tsx
@@ -3,7 +3,7 @@ import { EventrixContext } from '../context';
import { EventrixI } from '../../interfaces';
function withEventrix(BaseComponent: React.ComponentType | React.FC): React.FC {
- // eslint-disable-next-line react/display-name
+
return (props: PropsI & { eventrix: EventrixI }): JSX.Element => {
const context = useContext(EventrixContext);
return ;
diff --git a/src/react/hooks/useEmit.test.tsx b/src/react/hooks/useEmit.test.tsx
index 28ceaee..51bcaac 100644
--- a/src/react/hooks/useEmit.test.tsx
+++ b/src/react/hooks/useEmit.test.tsx
@@ -19,7 +19,7 @@ describe('useEmit', () => {
it('should emit event when component did mount', () => {
const eventrixInstance = new Eventrix({});
- const mockListener = jest.fn();
+ const mockListener = vi.fn();
eventrixInstance.listen('testEvent', mockListener);
render(
@@ -32,7 +32,7 @@ describe('useEmit', () => {
it('should emit event when component did mount with scope', () => {
const eventrixInstance = new Eventrix({});
- const mockListener = jest.fn();
+ const mockListener = vi.fn();
eventrixInstance.listen('Test:testEvent', mockListener);
render(
@@ -47,7 +47,7 @@ describe('useEmit', () => {
it('should emit event when component did mount with deep scope', () => {
const eventrixInstance = new Eventrix({});
- const mockListener = jest.fn();
+ const mockListener = vi.fn();
eventrixInstance.listen('Test:List:testEvent', mockListener);
render(
diff --git a/src/react/hooks/useEvent.test.tsx b/src/react/hooks/useEvent.test.tsx
index 8021890..2229a7a 100644
--- a/src/react/hooks/useEvent.test.tsx
+++ b/src/react/hooks/useEvent.test.tsx
@@ -14,7 +14,7 @@ describe('useEvent', () => {
it('should invoke callback when event emitted', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
@@ -27,7 +27,7 @@ describe('useEvent', () => {
it('should invoke callback when event emitted with scope', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
@@ -42,7 +42,7 @@ describe('useEvent', () => {
it('should invoke callback when event emitted with deep scope', () => {
const eventrixInstance = new Eventrix({});
- const callbackMock = jest.fn();
+ const callbackMock = vi.fn();
render(
diff --git a/src/react/hooks/useFetchState.test.tsx b/src/react/hooks/useFetchState.test.tsx
index 98dabd4..722c269 100644
--- a/src/react/hooks/useFetchState.test.tsx
+++ b/src/react/hooks/useFetchState.test.tsx
@@ -49,7 +49,7 @@ describe('useFetchState', () => {
it('should start fetch users and set loading', () => {
const mockedUsersList: UserI[] = [];
- const mockedFetchMetchod = jest.fn(() => Promise.resolve(mockedUsersList));
+ const mockedFetchMetchod = vi.fn(() => Promise.resolve(mockedUsersList));
const usersFetchReceiver = fetchStateReceiver('users', mockedFetchMetchod);
const eventrixInstance = new Eventrix({}, [usersFetchReceiver]);
const { getByTestId } = render(
@@ -69,7 +69,7 @@ describe('useFetchState', () => {
{ name: 'Jan', surname: 'Kowalski' },
{ name: 'Jan', surname: 'Nowak' },
];
- const mockedFetchMetchod = jest.fn(() => Promise.resolve(mockedUsersList));
+ const mockedFetchMetchod = vi.fn(() => Promise.resolve(mockedUsersList));
const usersFetchReceiver = fetchStateReceiver('users', mockedFetchMetchod);
const eventrixInstance = new Eventrix({}, [usersFetchReceiver]);
const { getByTestId } = render(
@@ -87,7 +87,7 @@ describe('useFetchState', () => {
});
it('should fetch users and show fetch error message', async () => {
const fetchErrorMessage = 'fetch error';
- const mockedFetchMetchod = jest.fn(() => Promise.reject({ message: fetchErrorMessage }));
+ const mockedFetchMetchod = vi.fn(() => Promise.reject({ message: fetchErrorMessage }));
const usersFetchReceiver = fetchStateReceiver('users', mockedFetchMetchod);
const eventrixInstance = new Eventrix({}, [usersFetchReceiver]);
const { getByTestId } = render(
@@ -106,7 +106,7 @@ describe('useFetchState', () => {
it('should start fetch users and set loading with scope', () => {
const mockedUsersList: UserI[] = [];
- const mockedFetchMetchod = jest.fn(() => Promise.resolve(mockedUsersList));
+ const mockedFetchMetchod = vi.fn(() => Promise.resolve(mockedUsersList));
const usersFetchReceiver = fetchStateReceiver('selectData.users', mockedFetchMetchod);
const eventrixInstance = new Eventrix({}, [usersFetchReceiver]);
const { getByTestId } = render(
diff --git a/tsconfig.json b/tsconfig.json
index ed492e3..4579ddd 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -18,6 +18,7 @@
"dom",
"es2016"
],
+ "types": ["vitest/globals"],
"experimentalDecorators": true,
"suppressImplicitAnyIndexErrors": false,
"baseUrl": "./",
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..4241f06
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,42 @@
+import { defineConfig } from 'vite';
+import dts from 'vite-plugin-dts';
+import { resolve } from 'node:path';
+import { existsSync, readdirSync, rmSync } from 'node:fs';
+import path from 'path';
+import react from '@vitejs/plugin-react';
+
+emptyDir(resolve(__dirname, 'dist'));
+
+export default defineConfig({
+ plugins: [dts({ rollupTypes: true }), react()],
+ base: './',
+ build: {
+ sourcemap: true,
+ outDir: './dist',
+ lib: {
+ entry: path.resolve(__dirname, 'src/index.ts'),
+ name: 'eventrix',
+ formats: ['es', 'cjs', 'umd', 'iife'],
+ fileName: (format) => `index.${format}.js`,
+ },
+ rollupOptions: {
+ external: ['react', 'react-dom'],
+ output: {
+ globals: {
+ react: 'React',
+ 'react-dom': 'ReactDOM',
+ },
+ },
+ },
+ },
+});
+
+function emptyDir(dir: string) {
+ if (!existsSync(dir)) {
+ return;
+ }
+
+ for (const file of readdirSync(dir)) {
+ rmSync(resolve(dir, file), { recursive: true, force: true });
+ }
+}
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..386659d
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,11 @@
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ coverage: {
+ reporter: ['html', 'lcov', 'text'],
+ },
+ },
+});
diff --git a/webpack-prod.config.js b/webpack-prod.config.js
deleted file mode 100644
index 1c046f1..0000000
--- a/webpack-prod.config.js
+++ /dev/null
@@ -1,48 +0,0 @@
-const package = require('./package.json');
-
-const externals = new Set([...Object.keys(package.dependencies || {}), ...Object.keys(package.peerDependencies || {})]);
-
-const config = {
- mode: 'production',
- devtool: 'inline-source-map',
- entry: {
- index: './src/index.ts',
- 'redux-adapter': './src/redux/index.ts',
- },
- output: {
- path: __dirname + '/dist',
- filename: '[name].js',
- sourceMapFilename: '[name].map',
- library: 'eventrix',
- libraryTarget: 'umd',
- globalObject: 'this',
- umdNamedDefine: true,
- },
- externals(context, request, callback) {
- if (externals.has(request)) {
- return callback(null, `${config.output.libraryTarget} ${request}`);
- }
- return callback();
- },
- resolve: {
- extensions: ['.tsx', '.ts', '.js'],
- },
- module: {
- rules: [
- {
- test: /\.ts(x?)$/,
- exclude: /node_modules/,
- use: [
- {
- loader: 'babel-loader',
- },
- {
- loader: 'ts-loader',
- },
- ],
- },
- ],
- },
-};
-
-module.exports = config;