muya 2.0.0-beta.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +124 -195
  2. package/cjs/index.js +1 -1
  3. package/esm/create-state.js +1 -0
  4. package/esm/create.js +1 -1
  5. package/esm/debug/development-tools.js +1 -1
  6. package/esm/index.js +1 -1
  7. package/esm/scheduler.js +1 -0
  8. package/esm/select.js +1 -0
  9. package/esm/use-value.js +1 -0
  10. package/esm/utils/__tests__/is.test.js +1 -1
  11. package/esm/utils/common.js +1 -1
  12. package/esm/utils/is.js +1 -1
  13. package/package.json +12 -12
  14. package/{packages/core → src}/__tests__/bench.test.tsx +3 -108
  15. package/src/__tests__/create.test.tsx +159 -0
  16. package/src/__tests__/scheduler.test.tsx +52 -0
  17. package/src/__tests__/select.test.tsx +127 -0
  18. package/src/__tests__/use-value.test.tsx +78 -0
  19. package/src/create-state.ts +50 -0
  20. package/src/create.ts +67 -0
  21. package/{packages/core → src}/debug/development-tools.ts +18 -3
  22. package/{packages/core → src}/index.ts +2 -1
  23. package/{packages/core/utils/global-scheduler.ts → src/scheduler.ts} +9 -3
  24. package/src/select.ts +69 -0
  25. package/src/types.ts +66 -0
  26. package/src/use-value.ts +22 -0
  27. package/{packages/core → src}/utils/__tests__/is.test.ts +24 -7
  28. package/{packages/core → src}/utils/common.ts +35 -10
  29. package/{packages/core → src}/utils/is.ts +5 -8
  30. package/types/create-state.d.ts +12 -0
  31. package/types/create.d.ts +6 -18
  32. package/types/debug/development-tools.d.ts +2 -9
  33. package/types/index.d.ts +2 -1
  34. package/types/{utils/scheduler.d.ts → scheduler.d.ts} +4 -1
  35. package/types/select.d.ts +10 -0
  36. package/types/types.d.ts +55 -5
  37. package/types/use-value.d.ts +2 -0
  38. package/types/utils/common.d.ts +6 -5
  39. package/types/utils/is.d.ts +3 -4
  40. package/esm/__tests__/create-async.test.js +0 -1
  41. package/esm/subscriber.js +0 -1
  42. package/esm/use.js +0 -1
  43. package/esm/utils/__tests__/context.test.js +0 -1
  44. package/esm/utils/__tests__/sub-memo.test.js +0 -1
  45. package/esm/utils/create-context.js +0 -1
  46. package/esm/utils/global-scheduler.js +0 -1
  47. package/esm/utils/scheduler.js +0 -1
  48. package/esm/utils/sub-memo.js +0 -1
  49. package/packages/core/__tests__/create-async.test.ts +0 -88
  50. package/packages/core/__tests__/create.test.tsx +0 -107
  51. package/packages/core/__tests__/subscriber.test.tsx +0 -89
  52. package/packages/core/__tests__/use-async.test.tsx +0 -45
  53. package/packages/core/__tests__/use.test.tsx +0 -125
  54. package/packages/core/create.ts +0 -98
  55. package/packages/core/subscriber.ts +0 -165
  56. package/packages/core/types.ts +0 -15
  57. package/packages/core/use.ts +0 -57
  58. package/packages/core/utils/__tests__/context.test.ts +0 -198
  59. package/packages/core/utils/__tests__/sub-memo.test.ts +0 -13
  60. package/packages/core/utils/create-context.ts +0 -60
  61. package/packages/core/utils/scheduler.ts +0 -59
  62. package/packages/core/utils/sub-memo.ts +0 -49
  63. package/types/subscriber.d.ts +0 -25
  64. package/types/use.d.ts +0 -2
  65. package/types/utils/create-context.d.ts +0 -5
  66. package/types/utils/global-scheduler.d.ts +0 -5
  67. package/types/utils/sub-memo.d.ts +0 -7
  68. /package/{packages/core → src}/__tests__/test-utils.ts +0 -0
  69. /package/{packages/core → src}/utils/__tests__/shallow.test.ts +0 -0
  70. /package/{packages/core → src}/utils/create-emitter.ts +0 -0
  71. /package/{packages/core → src}/utils/shallow.ts +0 -0
@@ -1,88 +0,0 @@
1
- import { create } from '../create'
2
- import { waitFor } from '@testing-library/react'
3
- import { longPromise } from './test-utils'
4
- import { isPromise } from '../utils/is'
5
- import { subscriber } from '../subscriber'
6
-
7
- describe('create', () => {
8
- it('should subscribe to context and notified it with parameters', async () => {
9
- const state1 = create(1)
10
- const state2 = create(2)
11
-
12
- function derivedNested() {
13
- return state1() + state2()
14
- }
15
- async function derived(plus: number) {
16
- return state1() + state2() + derivedNested() + plus
17
- }
18
-
19
- let updatesCounter = 0
20
- const sub = subscriber(() => derived(10))
21
- expect(isPromise(sub.emitter.getSnapshot())).toBe(true)
22
- sub.listen(async () => {
23
- updatesCounter++
24
- })
25
- expect(updatesCounter).toBe(0)
26
- // check if there is not maximum call stack
27
- expect(await sub()).toBe(16)
28
-
29
- state1.set(2)
30
- //
31
- await waitFor(async () => {})
32
- expect(await sub()).toBe(18)
33
-
34
- expect(updatesCounter).toBe(4)
35
- })
36
-
37
- it('should async subscribe to context and notified it', async () => {
38
- const state1 = create(1)
39
- const state2 = create(Promise.resolve(2))
40
-
41
- async function derivedNested() {
42
- await longPromise()
43
- return state1() + (await state2())
44
- }
45
- async function derived() {
46
- return state1() + (await state2()) + (await derivedNested())
47
- }
48
-
49
- let updatesCounter = 0
50
-
51
- const sub = subscriber(derived)
52
-
53
- sub.listen(() => {
54
- updatesCounter++
55
- })
56
-
57
- // check if there is not maximum call stack
58
- sub()
59
-
60
- // check if not assigned multiple times, but only once
61
- expect(state1.emitter.getSize()).toBe(1)
62
- expect(state2.emitter.getSize()).toBe(1)
63
- expect(sub.emitter.getSize()).toBe(1)
64
- state1.set(2)
65
-
66
- await waitFor(async () => {
67
- expect(await sub()).toBe(8)
68
- expect(updatesCounter).toBe(5)
69
- })
70
-
71
- state2.set(3)
72
-
73
- await waitFor(async () => {
74
- expect(await sub()).toBe(10)
75
- expect(updatesCounter).toBe(10)
76
- })
77
-
78
- expect(state1.emitter.getSize()).toBe(1)
79
- expect(state2.emitter.getSize()).toBe(1)
80
- expect(sub.emitter.getSize()).toBe(1)
81
-
82
- sub.destroy()
83
-
84
- expect(state1.emitter.getSize()).toBe(0)
85
- expect(state2.emitter.getSize()).toBe(0)
86
- expect(sub.emitter.getSize()).toBe(0)
87
- })
88
- })
@@ -1,107 +0,0 @@
1
- import { create } from '../create'
2
- import { waitFor } from '@testing-library/react'
3
- import { subscriber } from '../subscriber'
4
-
5
- describe('create', () => {
6
- it('should get basic value states', async () => {
7
- const state1 = create(1)
8
- const state2 = create(2)
9
- expect(state1()).toBe(1)
10
- expect(state2()).toBe(2)
11
-
12
- state1.set(2)
13
- state2.set(3)
14
-
15
- await waitFor(() => {
16
- expect(state1()).toBe(2)
17
- expect(state2()).toBe(3)
18
- })
19
- })
20
- it('should get basic and derived value', async () => {
21
- const state1 = create(1)
22
- const state2 = create(2)
23
-
24
- function derived1() {
25
- return state1() + state2()
26
- }
27
-
28
- expect(state1()).toBe(1)
29
- expect(state2()).toBe(2)
30
- expect(derived1()).toBe(3)
31
-
32
- state1.set(2)
33
- state2.set(3)
34
-
35
- await waitFor(() => {
36
- expect(state1()).toBe(2)
37
- expect(state2()).toBe(3)
38
- expect(derived1()).toBe(5)
39
- })
40
- })
41
- it('should subscribe to context and notified it', async () => {
42
- const state1 = create(1)
43
- const state2 = create(2)
44
-
45
- function derivedNested() {
46
- return state1() + state2()
47
- }
48
- function derived() {
49
- return state1() + state2() + derivedNested()
50
- }
51
-
52
- let updatesCounter = 0
53
-
54
- const sub = subscriber(derived)
55
-
56
- sub.listen(() => {
57
- updatesCounter++
58
- })
59
-
60
- // check if there is not maximum call stack
61
- sub()
62
- sub()
63
- sub()
64
-
65
- // check if not assigned multiple times, but only once
66
- expect(state1.emitter.getSize()).toBe(1)
67
- expect(state2.emitter.getSize()).toBe(1)
68
- expect(sub.emitter.getSize()).toBe(1)
69
- expect(sub.emitter.getSnapshot()).toBe(6)
70
- state1.set(2)
71
-
72
- await waitFor(() => {
73
- expect(sub()).toBe(8)
74
- expect(updatesCounter).toBe(1)
75
- })
76
-
77
- state2.set(3)
78
-
79
- await waitFor(() => {
80
- expect(sub()).toBe(10)
81
- expect(updatesCounter).toBe(2)
82
- })
83
-
84
- expect(state1.emitter.getSize()).toBe(1)
85
- expect(state2.emitter.getSize()).toBe(1)
86
- expect(sub.emitter.getSize()).toBe(1)
87
- expect(sub.emitter.getSnapshot()).toBe(10)
88
-
89
- sub.destroy()
90
-
91
- expect(state1.emitter.getSize()).toBe(0)
92
- expect(state2.emitter.getSize()).toBe(0)
93
- expect(sub.emitter.getSize()).toBe(0)
94
- })
95
- it('should subscribe and set snapshot', async () => {
96
- const state = create(1)
97
- const sub = subscriber(state)
98
- sub()
99
-
100
- expect(sub.emitter.getSnapshot()).toBe(1)
101
-
102
- state.set(2)
103
- await waitFor(() => {
104
- expect(sub.emitter.getSnapshot()).toBe(2)
105
- })
106
- })
107
- })
@@ -1,89 +0,0 @@
1
- import { waitFor } from '@testing-library/react'
2
- import { create } from '../create'
3
- import { subscriber } from '../subscriber'
4
- import { longPromise } from './test-utils'
5
-
6
- describe('subscriber', () => {
7
- it('should test subscriber and cleaning the emitters', () => {
8
- const state1 = create(1)
9
- const sub = subscriber(state1)
10
- // at this point, the emitter is not subscribed yet, as it need to be called first.
11
- expect(state1.emitter.getSize()).toBe(0)
12
-
13
- // check if the value is correct
14
- expect(sub()).toBe(1)
15
-
16
- // now we can check if the value is subscribed
17
- expect(state1.emitter.getSize()).toBe(1)
18
- // we destroy the subscriber, meaning that the emitter should be cleaned
19
-
20
- sub.destroy()
21
-
22
- expect(state1.emitter.getSize()).toBe(0)
23
-
24
- // and test re-aligning the subscriber
25
- expect(sub()).toBe(1)
26
- expect(state1.emitter.getSize()).toBe(1)
27
- })
28
- it('should test how many events are emitter via singleton state', async () => {
29
- const state1 = create(1)
30
- const sub = subscriber(state1)
31
-
32
- let updateCount = 0
33
-
34
- sub.listen(() => {
35
- updateCount++
36
- })
37
- sub()
38
- await waitFor(() => {})
39
- // we do not received initial value as it is not changed
40
- expect(updateCount).toBe(0)
41
-
42
- state1.set(2)
43
- await waitFor(() => {})
44
- expect(updateCount).toBe(1)
45
- })
46
- it('should test how many events are emitter via singleton async state', async () => {
47
- const state1 = create(longPromise())
48
- const sub = subscriber(state1)
49
-
50
- let updateCount = 0
51
-
52
- sub.listen(() => {
53
- updateCount++
54
- })
55
- sub()
56
- await waitFor(() => {})
57
- // we do not received initial value as it is not changed
58
- expect(updateCount).toBe(0)
59
-
60
- state1.set(2)
61
- await waitFor(() => {})
62
- expect(updateCount).toBe(1)
63
- })
64
-
65
- it('should test how many events are emitter via derived state', async () => {
66
- const state1 = create(longPromise())
67
-
68
- async function derived() {
69
- // await longPromise()
70
- return await state1()
71
- }
72
- const sub = subscriber(derived)
73
-
74
- let updateCount = 0
75
-
76
- sub.listen(() => {
77
- updateCount++
78
- })
79
- await sub()
80
- await waitFor(() => {})
81
- expect(await sub()).toBe(0)
82
- // // we do not received initial value as it is not changed
83
- expect(updateCount).toBe(2)
84
-
85
- // state1.set(2)
86
- // await waitFor(() => {})
87
- // expect(updateCount).toBe(1)
88
- })
89
- })
@@ -1,45 +0,0 @@
1
- /* eslint-disable @typescript-eslint/ban-ts-comment */
2
- import { renderHook } from '@testing-library/react-hooks'
3
- import { create } from '../create'
4
- import { use } from '../use'
5
- import { Suspense } from 'react'
6
- import { waitFor } from '@testing-library/react'
7
-
8
- describe('use-create', () => {
9
- const reRendersBefore = jest.fn()
10
-
11
- beforeEach(() => {
12
- jest.clearAllMocks()
13
- })
14
-
15
- it('should test sub hook', async () => {
16
- const userState = create({ name: 'John', age: 30 })
17
- async function getDataWithUser() {
18
- const result = await fetch('https://jsonplaceholder.typicode.com/todos/1')
19
- const json = await result.json()
20
- return { age: userState().age, ...json }
21
- }
22
-
23
- const suspenseFunction = jest.fn()
24
- function Loading() {
25
- suspenseFunction()
26
- return <div>Loading...</div>
27
- }
28
-
29
- const { result } = renderHook(
30
- () => {
31
- reRendersBefore()
32
- const data = use(getDataWithUser)
33
- return data
34
- },
35
- // @ts-expect-error
36
- { wrapper: ({ children }) => <Suspense fallback={<Loading />}>{children}</Suspense> },
37
- )
38
-
39
- await waitFor(() => {
40
- expect(result.current).toEqual({ userId: 1, id: 1, title: 'delectus aut autem', completed: false, age: 30 })
41
- })
42
- expect(suspenseFunction).toHaveBeenCalledTimes(1)
43
- expect(result.current).toEqual({ userId: 1, id: 1, title: 'delectus aut autem', completed: false, age: 30 })
44
- })
45
- })
@@ -1,125 +0,0 @@
1
- import { act, renderHook } from '@testing-library/react-hooks'
2
- import { create } from '../create'
3
- import { use } from '../use'
4
- import { waitFor } from '@testing-library/react'
5
- import { useCallback } from 'react'
6
- import { getDebugCacheCreation } from '../utils/sub-memo'
7
-
8
- describe('use-create', () => {
9
- const reRendersBefore = jest.fn()
10
-
11
- beforeEach(() => {
12
- jest.clearAllMocks()
13
- })
14
-
15
- it('should test use hook', async () => {
16
- const state = create(1)
17
-
18
- const { result } = renderHook(() => {
19
- reRendersBefore()
20
- return use(state)
21
- })
22
-
23
- state.set(2)
24
-
25
- await waitFor(() => {})
26
- expect(result.current).toBe(2)
27
- expect(reRendersBefore).toHaveBeenCalledTimes(2)
28
-
29
- state.set(3)
30
-
31
- await waitFor(() => {})
32
- expect(result.current).toBe(3)
33
- expect(reRendersBefore).toHaveBeenCalledTimes(3)
34
- })
35
-
36
- it('should test derived state with multiple states', async () => {
37
- const state1 = create(1)
38
- const state2 = create(2)
39
-
40
- function derivedBefore(plusValue: number) {
41
- return state1() + state2() + plusValue
42
- }
43
-
44
- function derived() {
45
- return state1() + state2() + derivedBefore(10)
46
- }
47
-
48
- const { result } = renderHook(() => {
49
- reRendersBefore()
50
- return use(derived)
51
- })
52
-
53
- await waitFor(() => {})
54
- expect(reRendersBefore).toHaveBeenCalledTimes(1)
55
- act(() => {
56
- state1.set(2)
57
- state2.set(3)
58
- })
59
-
60
- await waitFor(() => {})
61
- expect(result.current).toBe(20)
62
- expect(reRendersBefore).toHaveBeenCalledTimes(2)
63
- })
64
-
65
- it('should test use hook without memoize fn', async () => {
66
- const state1 = create(1)
67
- const state2 = create(2)
68
-
69
- function derivedBefore(plusValue: number) {
70
- return state1() + state2() + plusValue
71
- }
72
-
73
- function derived(add: number) {
74
- return state1() + state2() + derivedBefore(add)
75
- }
76
-
77
- const { result } = renderHook(() => {
78
- reRendersBefore()
79
- return use(() => derived(10))
80
- })
81
- expect(getDebugCacheCreation()).toBe(1)
82
-
83
- await waitFor(() => {})
84
- expect(reRendersBefore).toHaveBeenCalledTimes(1)
85
- act(() => {
86
- state1.set(2)
87
- state2.set(3)
88
- })
89
- expect(getDebugCacheCreation()).toBe(1)
90
- await waitFor(() => {})
91
- expect(result.current).toBe(20)
92
- expect(reRendersBefore).toHaveBeenCalledTimes(2)
93
- })
94
-
95
- it('should test use hook with memoize fn', async () => {
96
- const state1 = create(1)
97
- const state2 = create(2)
98
-
99
- function derivedBefore(plusValue: number) {
100
- return state1() + state2() + plusValue
101
- }
102
-
103
- function derived(add: number) {
104
- return state1() + state2() + derivedBefore(add)
105
- }
106
-
107
- const { result } = renderHook(() => {
108
- reRendersBefore()
109
- const memoized = useCallback(() => derived(10), [])
110
- return use(memoized)
111
- })
112
- expect(getDebugCacheCreation()).toBe(1)
113
-
114
- await waitFor(() => {})
115
- expect(reRendersBefore).toHaveBeenCalledTimes(1)
116
- act(() => {
117
- state1.set(2)
118
- state2.set(3)
119
- })
120
- expect(getDebugCacheCreation()).toBe(1)
121
- await waitFor(() => {})
122
- expect(result.current).toBe(20)
123
- expect(reRendersBefore).toHaveBeenCalledTimes(2)
124
- })
125
- })
@@ -1,98 +0,0 @@
1
- import { canUpdate, generateId } from './utils/common'
2
- import type { Emitter } from './utils/create-emitter'
3
- import { createEmitter } from './utils/create-emitter'
4
- import { isEqualBase, isFunction, isSetValueFunction, isUndefined } from './utils/is'
5
- // import { createScheduler } from './utils/scheduler'
6
- import type { Cache, Callable, DefaultValue, IsEqual, Listener, SetValue } from './types'
7
- import { context } from './subscriber'
8
- import { createGlobalScheduler } from './utils/global-scheduler'
9
-
10
- export const createScheduler = createGlobalScheduler()
11
- interface RawState<T> {
12
- (): T
13
- id: number
14
- set: (value: SetValue<T>) => void
15
- emitter: Emitter<T>
16
- listen: Listener<T>
17
- destroy: () => void
18
- withName: (name: string) => RawState<T>
19
- stateName?: string
20
- }
21
-
22
- export type State<T> = {
23
- readonly [K in keyof RawState<T>]: RawState<T>[K]
24
- } & Callable<T>
25
-
26
- export function create<T>(initialValue: DefaultValue<T>, isEqual: IsEqual<T> = isEqualBase): State<T> {
27
- const cache: Cache<T> = {}
28
-
29
- function getValue(): T {
30
- if (isUndefined(cache.current)) {
31
- cache.current = isFunction(initialValue) ? initialValue() : initialValue
32
- }
33
- return cache.current
34
- }
35
- function resolveValue(value: SetValue<T>) {
36
- const previous = getValue()
37
- cache.current = isSetValueFunction(value) ? value(previous) : value
38
- }
39
-
40
- // const schedule = createScheduler<SetValue<T>>({
41
- // onFinish() {
42
- // cache.current = getValue()
43
- // if (!canUpdate(cache, isEqual)) {
44
- // return
45
- // }
46
- // state.emitter.emit()
47
- // },
48
- // onResolveItem: resolveValue,
49
- // })
50
-
51
- const state: RawState<T> = function () {
52
- const stateValue = getValue()
53
- const ctx = context.use()
54
- // console.log('CTX', ctx?.id, 'STATE', state.id)
55
- if (ctx && !state.emitter.contains(ctx.sub)) {
56
- ctx.addEmitter(state.emitter)
57
- }
58
- return stateValue
59
- }
60
- state.listen = function (listener: (value: T) => void) {
61
- return state.emitter.subscribe(() => {
62
- const final = cache.current
63
- if (isUndefined(final)) {
64
- throw new Error('The value is undefined')
65
- }
66
- listener(final)
67
- })
68
- }
69
- state.emitter = createEmitter<T>(() => state())
70
- state.id = generateId()
71
-
72
- const clearScheduler = createScheduler.add(state.id, {
73
- onFinish() {
74
- cache.current = getValue()
75
- if (!canUpdate(cache, isEqual)) {
76
- return
77
- }
78
- state.emitter.emit()
79
- },
80
- onResolveItem: resolveValue,
81
- })
82
- state.set = function (value) {
83
- createScheduler.schedule(state.id, value)
84
- }
85
-
86
- state.destroy = function () {
87
- cache.current = undefined
88
- getValue()
89
- clearScheduler()
90
- state.emitter.clear()
91
- }
92
- state.withName = function (name: string) {
93
- state.stateName = name
94
- return state
95
- }
96
-
97
- return state
98
- }