Repository: effector/effector
Pull request #523 (2021-09-10 06:33:25)

fix some grammar rules; re-structure


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 396311cf..dff398e8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,10 +2,6 @@
 
 See also [separate changelogs for each library](https://changelog.effector.dev/)
 
-## effector 22.1.1
-
-- Fix data races that cause obsolete states to appear in the `.on` and `.reset` methods
-
 ## effector 22.1.0
 
 - Added option `debugSids` to `effector/babel-plugin`
diff --git a/docs/api/effector/babel-plugin.md b/docs/api/effector/babel-plugin.md
index f2f9419c..ca0901e6 100644
--- a/docs/api/effector/babel-plugin.md
+++ b/docs/api/effector/babel-plugin.md
@@ -3,9 +3,9 @@ id: babel-plugin
 title: Babel plugin
 ---
 
-Built-in plugin for babel, which can be used for ssr and debugging. It inserts a name of a unit, inferred from variable name and `sid` (stable identifier), computed from location in the source code.
+Built-in plugin for babel can be used for ssr and debugging. It inserts a name an [Unit](../../glossary#unit), inferred from variable name and `sid` (stable identifier), computed from the location in the source code.
 
-For example, in case of [effects without handlers](./Effect.md#usehandler), it will improve error message by clearly showing in which effect error happened.
+For example, in case [effects without handlers](./Effect.md#usehandler), it improves error message by clearly showing in which effect error happened.
 
 ```js
 import {createEffect} from 'effector'
@@ -29,7 +29,7 @@ In the simplest case, it can be used without any configuration:
 }
 ```
 
-### .sid
+### sid
 
 :::note since
 effector 20.2.0
@@ -37,7 +37,7 @@ effector 20.2.0
 
 Stable hash identifier for events, effects, stores and domains, preserved between environments, to handle client-server interaction within the same codebase.
 
-The key is that sid can be autogenerated by effector/babel-plugin with default config and it will be stable between builds
+The crucial value of sid is that it can be autogenerated by effector/babel-plugin with default config and it will be stable between builds.
 
 See [example project](https://github.com/effector/effector/tree/master/examples/worker-rpc)
 
@@ -88,150 +88,151 @@ getUsers.use(
 
 ## Configuration
 
-### noDefaults
+### importName
+
+- Type: `string | string[]`
+- Default: `['effector', 'effector/compat']`
+
+Specifying import name or names to process by plugin. Import should be used in the code as specifed.
 
-> `noDefaults: boolean`
+### factories
 
 :::note since
-effector 20.2.0
+effector 21.6.0
 :::
 
-Option for effector/babel-plugin for making custom unit factories with clean configuration.
+- Type: `string[]`
+
+Accepts an array of module names which exports treat as custom factories therefore each function call provides unique prefix for [sids](./babel-plugin.md#sid) of units inside them. Used to SSR([Server Side Rendering](./Scope.md)) and it's not required for client-only application.
+
+- Factories can have any amount of arguments.
+- Factories can create any amount of units.
+- Factories can call any effector methods.
+- Factories can call another factories from others modules.
+- Modules with factories can export any amount of functions.
+- Factories should be compiled with `effector/babel-plugin` as well as code which use them.
+
+#### Example
 
 ```json title=".babelrc"
 {
   "plugins": [
-    ["effector/babel-plugin", {"addLoc": true}],
     [
       "effector/babel-plugin",
       {
-        "importName": "@lib/createInputField",
-        "storeCreators": ["createInputField"],
-        "noDefaults": true
-      },
-      "createInputField"
+        "factories": ["src/createEffectStatus", "~/createCommonPending"]
+      }
     ]
   ]
 }
 ```
 
-```js title="@lib/createInputField.js"
-import {createStore} from 'effector'
-import {resetForm} from './form'
+```js title="./src/createEffectStatus.js"
+import {rootDomain} from './rootDomain'
 
-export function createInputField(defaultState, {sid, name}) {
-  return createStore(defaultState, {sid, name}).reset(resetForm)
+export function createEffectStatus(fx) {
+  const $status = rootDomain
+    .createStore('init')
+    .on(fx.finally, (_, {status}) => status)
+  return $status
 }
 ```
 
-```js title="src/state.js"
-import {createInputField} from '@lib/createInputField'
-
-const foo = createInputField('-')
-/*
+```js title="./src/statuses.js"
+import {createEffectStatus} from './createEffectStatus'
+import {fetchUserFx, fetchFriendsFx} from './api'
 
-will be treated as store creator and compiled to
+export const $fetchUserStatus = createEffectStatus(fetchUserFx)
+export const $fetchFriendsStatus = createEffectStatus(fetchFriendsFx)
+```
 
-const foo = createInputField('-', {
-  name: 'foo',
-  sid: 'z&si65'
-})
+Import `createEffectStatus` from `'./createEffectStatus'` was treated as factory function so each store created by it has its own [sid](./babel-plugin.md#sid) and will be handled by [serialize](./serialize.md) independently, although without `factories` they will share the same `sid`.
 
-*/
-```
+### reactSsr
 
-### importName
+:::note since
+effector 21.5.0
+:::
 
-> `importName: string | string[]`
+- Type: `boolean`
+- Default: `false`
 
-Specify import to process by plugin. By default has value `["effector", "effector/compat"]`
+Replace imports from `effector-react` to `effector-react/scope`. Useful for building both server-side and client-side builds from the same codebase.
 
 ### addNames
 
-> `addNames: boolean`
-
 :::note since
 effector 21.8.0
 :::
 
-Add names to units factories calls. By default has value `true`. Useful for minification and obfuscation of production builds.
+- Type: `boolean`
+- Default: `true`
+
+Add names to units factories calls. Useful for minification and obfuscation of production builds.
 
 ### addLoc
 
-> `addLoc: boolean`
+- Type: `boolean`
+- Default: `false`
 
-Add location to methods' calls. Used by devtools, for example [effector-logger](https://github.com/effector/logger)
+Add location to methods' calls. Used by devtools, for example [effector-logger](https://github.com/effector/logger).
 
 ### debugSids
 
-> `debugSids: boolean`
+- Type: `boolean`
+- Default: `false`
 
 Add path of a file and a variable name whether a unit was defined to a sid. Useful for debugging SSR.
 
-### reactSsr
-
-> `reactSsr: boolean`
-
-:::note since
-effector 21.5.0
-:::
-
-Replace imports from `effector-react` to `effector-react/ssr`. Useful for building both server-side and client-side builds from the same codebase.
-
-### factories
-
-> `factories: string[]`
+### noDefaults
 
 :::note since
-effector 21.6.0
+effector 20.2.0
 :::
 
-Accepts array of module names which exports will be treated as custom factories therefore each function call will provide unique prefix for [sids](./babel-plugin.md#sid) of units inside them. Useful for SSR support, not required for client-only application
-
-- Factories can have any amount of arguments.
-
-- Factories can create any amount of units.
-
-- Factories can call any effector methods.
-
-- Factories can call another factories from other modules.
-
-- Modules with factories can export any amount of functions.
+- Type: `boolean`
+- Default: `false`
 
-- Factories should be compiled with `effector/babel-plugin` as well as code which use them.
-
-#### Example
+Option for effector/babel-plugin for making custom unit factories with clean configuration.
 
 ```json title=".babelrc"
 {
   "plugins": [
+    ["effector/babel-plugin", {"addLoc": true}],
     [
       "effector/babel-plugin",
       {
-        "factories": ["src/createEffectStatus", "~/createCommonPending"]
-      }
+        "importName": "@lib/createInputField",
+        "storeCreators": ["createInputField"],
+        "noDefaults": true
+      },
+      "createInputField"
     ]
   ]
 }
 ```
 
-```js title="./src/createEffectStatus.js"
-import {rootDomain} from './rootDomain'
+```js title="@lib/createInputField.js"
+import {createStore} from 'effector'
+import {resetForm} from './form'
 
-export function createEffectStatus(fx) {
-  const $status = rootDomain
-    .createStore('init')
-    .on(fx.finally, (_, {status}) => status)
-  return $status
+export function createInputField(defaultState, {sid, name}) {
+  return createStore(defaultState, {sid, name}).reset(resetForm)
 }
 ```
 
-```js title="./src/statuses.js"
-import {createEffectStatus} from './createEffectStatus'
-import {fetchUserFx, fetchFriendsFx} from './api'
+```js title="src/state.js"
+import {createInputField} from '@lib/createInputField'
 
-export const $fetchUserStatus = createEffectStatus(fetchUserFx)
-export const $fetchFriendsStatus = createEffectStatus(fetchFriendsFx)
-```
+const foo = createInputField('-')
+/*
+
+will be treated as store creator and compiled to
 
-Import `createEffectStatus` from `'./createEffectStatus'` was treated as factory function so each store created by it has its own [sid](./babel-plugin.md#sid) and will be handled by [serialize](./serialize.md) independently, although without `factories` they will share the same `sid`
+const foo = createInputField('-', {
+  name: 'foo',
+  sid: 'z&si65'
+})
+
+*/
+```
diff --git a/packages/effector/package.json b/packages/effector/package.json
index 6e79bbd4..2a2e4058 100644
--- a/packages/effector/package.json
+++ b/packages/effector/package.json
@@ -1,6 +1,6 @@
 {
   "name": "effector",
-  "version": "22.1.1",
+  "version": "22.1.0",
   "description": "The state manager",
   "main": "effector.cjs.js",
   "module": "effector.mjs",
diff --git a/src/effector/createEffect.ts b/src/effector/createEffect.ts
index 6e20e614..db174363 100644
--- a/src/effector/createEffect.ts
+++ b/src/effector/createEffect.ts
@@ -57,20 +57,16 @@ export function createEffect<Payload, Done>(
         (() => assert(false, `no handler used in ${instance.getType()}`)),
     },
     node: [
-      calc(
-        (upd, scope_, stack) => {
-          const scope: {handlerId: string; handler: Function} = scope_ as any
-          let handler = scope.handler
-          if (getForkPage(stack)) {
-            const handler_ = getForkPage(stack)!.handlers[scope.handlerId]
-            if (handler_) handler = handler_
-          }
-          upd.handler = handler
-          return upd
-        },
-        false,
-        true,
-      ),
+      calc((upd, scope_, stack) => {
+        const scope: {handlerId: string; handler: Function} = scope_ as any
+        let handler = scope.handler
+        if (getForkPage(stack)) {
+          const handler_ = getForkPage(stack)!.handlers[scope.handlerId]
+          if (handler_) handler = handler_
+        }
+        upd.handler = handler
+        return upd
+      }),
       calc(
         ({params, req, handler, args = [params]}, _, stack) => {
           const onResolve = onSettled(params, req, true, anyway, stack)
diff --git a/src/effector/createUnit.ts b/src/effector/createUnit.ts
index b5e47545..9215c0b0 100644
--- a/src/effector/createUnit.ts
+++ b/src/effector/createUnit.ts
@@ -39,7 +39,7 @@ import {
   getMeta,
 } from './getter'
 import {assert, deprecate} from './throw'
-import {DOMAIN, STORE, EVENT, MAP, FILTER, STACK, REG_A} from './tag'
+import {DOMAIN, STORE, EVENT, MAP, FILTER, STACK} from './tag'
 import {applyTemplate} from './template'
 import {forEach} from './collection'
 import {flattenConfig} from './config'
@@ -334,11 +334,7 @@ const updateStore = (
   fn: Function,
 ) => {
   const storeRef = getStoreState(store)
-  const reader = mov({
-    store: storeRef,
-    to: REG_A,
-    priority: 'read',
-  })
+  const reader = read(storeRef)
   if (op === MAP) reader.data.softRead = true
   const node = [reader, compute({fn: caller})]
   applyTemplate(
diff --git a/src/effector/kernel.ts b/src/effector/kernel.ts
index 7bdb9307..1a34d7dd 100644
--- a/src/effector/kernel.ts
+++ b/src/effector/kernel.ts
@@ -8,9 +8,9 @@ import type {Scope} from './unit.h'
 import {add, forEach} from './collection'
 
 /** Names of priority groups */
-type PriorityTag = 'child' | 'pure' | 'read' | 'barrier' | 'sampler' | 'effect'
+type PriorityTag = 'child' | 'pure' | 'barrier' | 'sampler' | 'effect'
 
-export type BarrierPriorityTag = 'read' | 'barrier' | 'sampler' | 'effect'
+export type BarrierPriorityTag = 'barrier' | 'sampler' | 'effect'
 
 /**
  * Position in the current branch,
@@ -64,17 +64,15 @@ const merge = (a: QueueItem | null, b: QueueItem | null): QueueItem | null => {
   if (!b) return a
 
   let ret
+  const isSameType = a.v.type === b.v.type
   if (
     /**
      * if both nodes has the same PriorityType
      * and first node is created after second one
      */
-    (a.v.type === b.v.type && a.v.id > b.v.id) ||
-    /**
-     * greater priority mean bucket of first node is executed later
-     * e.g  a: "sampler", b: "barrier"
-     */
-    getPriority(a.v.type) > getPriority(b.v.type)
+    (isSameType && a.v.id > b.v.id) ||
+    /** if first node is "sampler" and second node is "barrier" */
+    (!isSameType && a.v.type === SAMPLER)
   ) {
     ret = a
     a = b
@@ -90,7 +88,7 @@ const merge = (a: QueueItem | null, b: QueueItem | null): QueueItem | null => {
 /** queue buckets for each PriorityType */
 const queue: QueueBucket[] = []
 let ix = 0
-while (ix < 6) {
+while (ix < 5) {
   /**
    * although "sampler" and "barrier" are using heap instead of linked list,
    * their buckets are still useful: they maintains size of heap queue
@@ -100,14 +98,14 @@ while (ix < 6) {
 }
 
 const deleteMin = () => {
-  for (let i = 0; i < 6; i++) {
+  for (let i = 0; i < 5; i++) {
     const list = queue[i]
     if (list.size > 0) {
       /**
-       * bucket 3 is for "barrier" PriorityType (used in combine)
-       * bucket 4 is for "sampler" PriorityType (used in sample and guard)
+       * second bucket is for "barrier" PriorityType (used in combine)
+       * and third bucket is for "sampler" PriorityType (used in sample and guard)
        */
-      if (i === 3 || i === 4) {
+      if (i === 2 || i === 3) {
         list.size -= 1
         const value = heap!.v
         heap = merge(heap!.l, heap!.r)
@@ -163,10 +161,10 @@ const pushHeap = (
     r: null,
   }
   /**
-   * bucket 3 is for "barrier" PriorityType (used in combine)
-   * bucket 4 is for "sampler" PriorityType (used in sample and guard)
+   * second bucket is for "barrier" PriorityType (used in combine)
+   * and third bucket is for "sampler" PriorityType (used in sample and guard)
    */
-  if (priority === 3 || priority === 4) {
+  if (priority === 2 || priority === 3) {
     heap = merge(heap, item)
   } else {
     if (bucket.size === 0) {
@@ -185,14 +183,12 @@ const getPriority = (t: PriorityTag) => {
       return 0
     case 'pure':
       return 1
-    case 'read':
-      return 2
     case BARRIER:
-      return 3
+      return 2
     case SAMPLER:
-      return 4
+      return 3
     case EFFECT:
-      return 5
+      return 4
     default:
       return -1
   }
diff --git a/tools/builder/packages.config.ts b/tools/builder/packages.config.ts
index 8f7fae58..1770f4fc 100644
--- a/tools/builder/packages.config.ts
+++ b/tools/builder/packages.config.ts
@@ -46,7 +46,7 @@ const keywords = [
 ]
 
 const version = {
-  effector: '22.1.1',
+  effector: '22.1.0',
   'effector-react': '22.0.4',
   'effector-vue': '22.0.2',
   forest: '0.20.2',


Test patch:
diff --git a/src/effector/__tests__/effect/index.test.ts b/src/effector/__tests__/effect/index.test.ts
index 6bac3b23..fbc2adec 100644
--- a/src/effector/__tests__/effect/index.test.ts
+++ b/src/effector/__tests__/effect/index.test.ts
@@ -285,51 +285,8 @@ describe('execution order', () => {
         "handler",
         "finally",
         "done",
-        "doneData",
         "inFlight 0",
-        "pending false",
-        "promise resolver",
-      ]
-    `)
-  })
-  it('should run watchers and promise resolvers in order (async)', async () => {
-    const fn = jest.fn()
-    const fx = createEffect({
-      async handler() {
-        fn('handler')
-      },
-    })
-    fx.watch(() => {
-      fn('start')
-    })
-    fx.done.watch(() => {
-      fn('done')
-    })
-    fx.doneData.watch(() => {
-      fn('doneData')
-    })
-    fx.finally.watch(() => {
-      fn('finally')
-    })
-    fx.inFlight.updates.watch(n => {
-      fn(`inFlight ${n}`)
-    })
-    fx.pending.updates.watch(pending => {
-      fn(`pending ${pending}`)
-    })
-    await fx().then(() => {
-      fn('promise resolver')
-    })
-    expect(argumentHistory(fn)).toMatchInlineSnapshot(`
-      Array [
-        "start",
-        "inFlight 1",
-        "pending true",
-        "handler",
-        "finally",
-        "done",
         "doneData",
-        "inFlight 0",
         "pending false",
         "promise resolver",
       ]
diff --git a/src/effector/__tests__/kernelEdgeCases.test.ts b/src/effector/__tests__/kernelEdgeCases.test.ts
index d6546da2..1146ce89 100644
--- a/src/effector/__tests__/kernelEdgeCases.test.ts
+++ b/src/effector/__tests__/kernelEdgeCases.test.ts
@@ -1,11 +1,4 @@
-import {
-  createEvent,
-  createStore,
-  sample,
-  step,
-  createNode,
-  createEffect,
-} from 'effector'
+import {createEvent, createStore, sample, step, createNode} from 'effector'
 import {argumentHistory} from 'effector/fixtures'
 
 it('should call watcher as many times, as many store updates occured', () => {
@@ -66,7 +59,7 @@ it('should avoid data races', () => {
 
   routePush('v 1')
   routePush('v 2')
-  expect(argumentHistory(fn)).toEqual([-2, 0, 1])
+  expect(argumentHistory(fn)).toEqual([-2, -1, 0])
 })
 
 it('should not erase sibling branches', () => {
@@ -121,30 +114,3 @@ test('watch behavior should be consistent', () => {
     ]
   `)
 })
-
-test('stale reads', async () => {
-  const fn = jest.fn()
-  const fx = createEffect((tag: string) => `${tag}/end`)
-  const x = createStore([] as (string | boolean)[])
-    .on(fx, (words, x) => [...words, x])
-    .on(fx.pending, (words, x) => [...words, x])
-    .on(fx.doneData, (words, x) => [...words, x])
-
-  x.watch(upd => fn(upd.join(', ')))
-
-  await fx('a')
-  await fx('b')
-  expect(argumentHistory(fn)).toMatchInlineSnapshot(`
-    Array [
-      "",
-      "a",
-      "a, true",
-      "a, true, a/end",
-      "a, true, a/end, false",
-      "a, true, a/end, false, b",
-      "a, true, a/end, false, b, true",
-      "a, true, a/end, false, b, true, b/end",
-      "a, true, a/end, false, b, true, b/end, false",
-    ]
-  `)
-})
diff --git a/src/effector/__tests__/store/reset.test.ts b/src/effector/__tests__/store/reset.test.ts
index 67ae720b..23ea8265 100644
--- a/src/effector/__tests__/store/reset.test.ts
+++ b/src/effector/__tests__/store/reset.test.ts
@@ -176,7 +176,7 @@ test('late forwarding', () => {
   reset() // reset
 
   expect(A.getState()).toMatchInlineSnapshot(`"A"`)
-  expect(B.getState()).toMatchInlineSnapshot(`"B"`)
+  expect(B.getState()).toMatchInlineSnapshot(`"A"`) // should be B
 
   reset() // reset again