kiru 0.44.4
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.
- package/LICENSE +7 -0
- package/README.md +5 -0
- package/package.json +81 -0
- package/src/appContext.ts +186 -0
- package/src/cloneVNode.ts +14 -0
- package/src/constants.ts +146 -0
- package/src/context.ts +56 -0
- package/src/dom.ts +712 -0
- package/src/element.ts +54 -0
- package/src/env.ts +6 -0
- package/src/error.ts +85 -0
- package/src/flags.ts +15 -0
- package/src/form/index.ts +662 -0
- package/src/form/types.ts +261 -0
- package/src/form/utils.ts +19 -0
- package/src/generateId.ts +19 -0
- package/src/globalContext.ts +161 -0
- package/src/globals.ts +21 -0
- package/src/hmr.ts +178 -0
- package/src/hooks/index.ts +14 -0
- package/src/hooks/useAsync.ts +136 -0
- package/src/hooks/useCallback.ts +31 -0
- package/src/hooks/useContext.ts +79 -0
- package/src/hooks/useEffect.ts +44 -0
- package/src/hooks/useEffectEvent.ts +24 -0
- package/src/hooks/useId.ts +42 -0
- package/src/hooks/useLayoutEffect.ts +47 -0
- package/src/hooks/useMemo.ts +33 -0
- package/src/hooks/useReducer.ts +50 -0
- package/src/hooks/useRef.ts +40 -0
- package/src/hooks/useState.ts +62 -0
- package/src/hooks/useSyncExternalStore.ts +59 -0
- package/src/hooks/useViewTransition.ts +26 -0
- package/src/hooks/utils.ts +259 -0
- package/src/hydration.ts +67 -0
- package/src/index.ts +61 -0
- package/src/jsx.ts +11 -0
- package/src/lazy.ts +238 -0
- package/src/memo.ts +48 -0
- package/src/portal.ts +43 -0
- package/src/profiling.ts +105 -0
- package/src/props.ts +36 -0
- package/src/reconciler.ts +531 -0
- package/src/renderToString.ts +91 -0
- package/src/router/index.ts +2 -0
- package/src/router/route.ts +51 -0
- package/src/router/router.ts +275 -0
- package/src/router/routerUtils.ts +49 -0
- package/src/scheduler.ts +522 -0
- package/src/signals/base.ts +237 -0
- package/src/signals/computed.ts +139 -0
- package/src/signals/effect.ts +60 -0
- package/src/signals/globals.ts +11 -0
- package/src/signals/index.ts +12 -0
- package/src/signals/jsx.ts +45 -0
- package/src/signals/types.ts +10 -0
- package/src/signals/utils.ts +12 -0
- package/src/signals/watch.ts +151 -0
- package/src/ssr/client.ts +29 -0
- package/src/ssr/hydrationBoundary.ts +63 -0
- package/src/ssr/index.ts +1 -0
- package/src/ssr/server.ts +124 -0
- package/src/store.ts +241 -0
- package/src/swr.ts +360 -0
- package/src/transition.ts +80 -0
- package/src/types.dom.ts +1250 -0
- package/src/types.ts +209 -0
- package/src/types.utils.ts +39 -0
- package/src/utils.ts +581 -0
- package/src/warning.ts +9 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AppContext, AppContextOptions } from "../appContext"
|
|
2
|
+
import { hydrationStack } from "../hydration.js"
|
|
3
|
+
import { renderMode } from "../globals.js"
|
|
4
|
+
import { mount } from "../index.js"
|
|
5
|
+
|
|
6
|
+
export function hydrate<T extends Record<string, unknown>>(
|
|
7
|
+
appFunc: (props: T) => JSX.Element,
|
|
8
|
+
container: AppContextOptions,
|
|
9
|
+
appProps?: T
|
|
10
|
+
): Promise<AppContext<T>>
|
|
11
|
+
|
|
12
|
+
export function hydrate<T extends Record<string, unknown>>(
|
|
13
|
+
appFunc: (props: T) => JSX.Element,
|
|
14
|
+
container: HTMLElement,
|
|
15
|
+
appProps?: T
|
|
16
|
+
): Promise<AppContext<T>>
|
|
17
|
+
|
|
18
|
+
export function hydrate<T extends Record<string, unknown>>(
|
|
19
|
+
appFunc: (props: T) => JSX.Element,
|
|
20
|
+
optionsOrRoot: HTMLElement | AppContextOptions,
|
|
21
|
+
appProps = {} as T
|
|
22
|
+
) {
|
|
23
|
+
hydrationStack.clear()
|
|
24
|
+
const prevRenderMode = renderMode.current
|
|
25
|
+
renderMode.current = "hydrate"
|
|
26
|
+
return mount(appFunc, optionsOrRoot as any, appProps).finally(() => {
|
|
27
|
+
renderMode.current = prevRenderMode
|
|
28
|
+
})
|
|
29
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createContext } from "../context.js"
|
|
2
|
+
import { $HYDRATION_BOUNDARY } from "../constants.js"
|
|
3
|
+
import { createElement, Fragment } from "../element.js"
|
|
4
|
+
import { renderMode } from "../globals.js"
|
|
5
|
+
|
|
6
|
+
type EventsArray = (keyof GlobalEventHandlersEventMap)[]
|
|
7
|
+
|
|
8
|
+
export const HYDRATION_BOUNDARY_MARKER = "kaioken:h-boundary"
|
|
9
|
+
export const DEFAULT_INTERACTION_EVENTS = [
|
|
10
|
+
"pointerdown",
|
|
11
|
+
"keydown",
|
|
12
|
+
"focus",
|
|
13
|
+
"input",
|
|
14
|
+
] as const satisfies EventsArray
|
|
15
|
+
|
|
16
|
+
export type HydrationBoundaryMode = "eager" | "interaction"
|
|
17
|
+
export type HydrationBoundaryProps<T extends HydrationBoundaryMode> = {
|
|
18
|
+
/**
|
|
19
|
+
* Determines the strategy to use when hydrating the boundary.
|
|
20
|
+
* - `eager`: hydrate immediately.
|
|
21
|
+
* - `interaction`: hydrate upon the first user interaction.
|
|
22
|
+
* @default "eager"
|
|
23
|
+
*/
|
|
24
|
+
mode?: T
|
|
25
|
+
children: JSX.Children
|
|
26
|
+
} & (T extends "interaction"
|
|
27
|
+
? {
|
|
28
|
+
/**
|
|
29
|
+
* List of events that will trigger the hydration.
|
|
30
|
+
* @default ["pointerdown", "keydown", "focus", "input"]
|
|
31
|
+
*/
|
|
32
|
+
events?: EventsArray
|
|
33
|
+
}
|
|
34
|
+
: {})
|
|
35
|
+
|
|
36
|
+
export const HydrationBoundaryContext = createContext<{
|
|
37
|
+
mode: HydrationBoundaryMode
|
|
38
|
+
events: string[]
|
|
39
|
+
}>(null!)
|
|
40
|
+
|
|
41
|
+
export function Experimental_HydrationBoundary<T extends HydrationBoundaryMode>(
|
|
42
|
+
props: HydrationBoundaryProps<T>
|
|
43
|
+
) {
|
|
44
|
+
const provider = createElement(
|
|
45
|
+
HydrationBoundaryContext.Provider,
|
|
46
|
+
{
|
|
47
|
+
value: {
|
|
48
|
+
mode: props.mode || "eager",
|
|
49
|
+
// @ts-expect-error this is fine
|
|
50
|
+
events: props.events ?? DEFAULT_INTERACTION_EVENTS,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
createElement($HYDRATION_BOUNDARY, props)
|
|
54
|
+
)
|
|
55
|
+
if (renderMode.current === "string" || renderMode.current === "stream") {
|
|
56
|
+
/**
|
|
57
|
+
* in order to ensure consistent tree structure, we're simulating
|
|
58
|
+
* the generated loader + wrapper components here.
|
|
59
|
+
*/
|
|
60
|
+
return Fragment({ children: Fragment({ children: provider }) })
|
|
61
|
+
}
|
|
62
|
+
return provider
|
|
63
|
+
}
|
package/src/ssr/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./hydrationBoundary.js"
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Readable } from "node:stream"
|
|
2
|
+
import { Fragment } from "../element.js"
|
|
3
|
+
import { AppContext, createAppContext } from "../appContext.js"
|
|
4
|
+
import { renderMode, ctx, node, nodeToCtxMap } from "../globals.js"
|
|
5
|
+
import {
|
|
6
|
+
isVNode,
|
|
7
|
+
encodeHtmlEntities,
|
|
8
|
+
propsToElementAttributes,
|
|
9
|
+
isExoticType,
|
|
10
|
+
} from "../utils.js"
|
|
11
|
+
import { Signal } from "../signals/base.js"
|
|
12
|
+
import { $HYDRATION_BOUNDARY, voidElements } from "../constants.js"
|
|
13
|
+
import { assertValidElementProps } from "../props.js"
|
|
14
|
+
import { HYDRATION_BOUNDARY_MARKER } from "./hydrationBoundary.js"
|
|
15
|
+
import { __DEV__ } from "../env.js"
|
|
16
|
+
|
|
17
|
+
type RequestState = {
|
|
18
|
+
stream: Readable
|
|
19
|
+
ctx: AppContext
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function renderToReadableStream<T extends Record<string, unknown>>(
|
|
23
|
+
appFunc: (props: T) => JSX.Element,
|
|
24
|
+
appProps = {} as T
|
|
25
|
+
): Readable {
|
|
26
|
+
const prev = renderMode.current
|
|
27
|
+
renderMode.current = "stream"
|
|
28
|
+
const state: RequestState = {
|
|
29
|
+
stream: new Readable(),
|
|
30
|
+
ctx: createAppContext(appFunc, appProps, { rootType: Fragment }),
|
|
31
|
+
}
|
|
32
|
+
const prevCtx = ctx.current
|
|
33
|
+
ctx.current = state.ctx
|
|
34
|
+
renderToStream_internal(state, state.ctx.rootNode, null, 0)
|
|
35
|
+
state.stream.push(null)
|
|
36
|
+
renderMode.current = prev
|
|
37
|
+
ctx.current = prevCtx
|
|
38
|
+
|
|
39
|
+
return state.stream
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function renderToStream_internal(
|
|
43
|
+
state: RequestState,
|
|
44
|
+
el: unknown,
|
|
45
|
+
parent: Kaioken.VNode | null,
|
|
46
|
+
idx: number
|
|
47
|
+
): void {
|
|
48
|
+
if (el === null) return
|
|
49
|
+
if (el === undefined) return
|
|
50
|
+
if (typeof el === "boolean") return
|
|
51
|
+
if (typeof el === "string") {
|
|
52
|
+
state.stream.push(encodeHtmlEntities(el))
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
if (typeof el === "number" || typeof el === "bigint") {
|
|
56
|
+
state.stream.push(el.toString())
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
if (el instanceof Array) {
|
|
60
|
+
el.forEach((c, i) => renderToStream_internal(state, c, parent, i))
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
if (Signal.isSignal(el)) {
|
|
64
|
+
state.stream.push(String(el.peek()))
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
if (!isVNode(el)) {
|
|
68
|
+
state.stream.push(String(el))
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
el.parent = parent
|
|
72
|
+
el.depth = (parent?.depth ?? -1) + 1
|
|
73
|
+
el.index = idx
|
|
74
|
+
const props = el.props ?? {}
|
|
75
|
+
const children = props.children
|
|
76
|
+
const type = el.type
|
|
77
|
+
if (type === "#text") {
|
|
78
|
+
state.stream.push(encodeHtmlEntities(props.nodeValue ?? ""))
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
if (isExoticType(type)) {
|
|
82
|
+
if (type === $HYDRATION_BOUNDARY) {
|
|
83
|
+
state.stream.push(`<!--${HYDRATION_BOUNDARY_MARKER}-->`)
|
|
84
|
+
renderToStream_internal(state, children, el, idx)
|
|
85
|
+
state.stream.push(`<!--/${HYDRATION_BOUNDARY_MARKER}-->`)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
return renderToStream_internal(state, children, el, idx)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeof type !== "string") {
|
|
92
|
+
nodeToCtxMap.set(el, state.ctx)
|
|
93
|
+
node.current = el
|
|
94
|
+
const res = type(props)
|
|
95
|
+
node.current = null
|
|
96
|
+
return renderToStream_internal(state, res, parent, idx)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (__DEV__) {
|
|
100
|
+
assertValidElementProps(el)
|
|
101
|
+
}
|
|
102
|
+
const attrs = propsToElementAttributes(props)
|
|
103
|
+
state.stream.push(`<${type}${attrs.length ? ` ${attrs}` : ""}>`)
|
|
104
|
+
|
|
105
|
+
if (!voidElements.has(type)) {
|
|
106
|
+
if ("innerHTML" in props) {
|
|
107
|
+
state.stream.push(
|
|
108
|
+
String(
|
|
109
|
+
Signal.isSignal(props.innerHTML)
|
|
110
|
+
? props.innerHTML.peek()
|
|
111
|
+
: props.innerHTML
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
} else {
|
|
115
|
+
if (Array.isArray(children)) {
|
|
116
|
+
children.forEach((c, i) => renderToStream_internal(state, c, el, i))
|
|
117
|
+
} else {
|
|
118
|
+
renderToStream_internal(state, children, el, 0)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
state.stream.push(`</${type}>`)
|
|
123
|
+
}
|
|
124
|
+
}
|
package/src/store.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import type { Prettify } from "./types.utils.js"
|
|
2
|
+
import { __DEV__ } from "./env.js"
|
|
3
|
+
import { sideEffectsEnabled, useHook } from "./hooks/utils.js"
|
|
4
|
+
import { safeStringify, shallowCompare } from "./utils.js"
|
|
5
|
+
import { $HMR_ACCEPT } from "./constants.js"
|
|
6
|
+
import { HMRAccept } from "./hmr.js"
|
|
7
|
+
|
|
8
|
+
export { createStore }
|
|
9
|
+
export type { Store, MethodFactory }
|
|
10
|
+
|
|
11
|
+
type MethodFactory<T> = (
|
|
12
|
+
setState: (setter: Kaioken.StateSetter<T>) => void,
|
|
13
|
+
getState: () => T
|
|
14
|
+
) => Record<string, Function>
|
|
15
|
+
|
|
16
|
+
type StoreHook<T, U extends Record<string, Function>> = {
|
|
17
|
+
<R>(sliceFn: (state: T) => R): Prettify<{ value: R } & U>
|
|
18
|
+
<
|
|
19
|
+
F extends null | ((state: T) => unknown),
|
|
20
|
+
R extends F extends Function ? ReturnType<F> : T
|
|
21
|
+
>(
|
|
22
|
+
sliceFn: F,
|
|
23
|
+
equality: (prev: R, next: R, compare: typeof shallowCompare) => boolean
|
|
24
|
+
): Prettify<{ value: R } & U>
|
|
25
|
+
(): Prettify<{ value: T } & U>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type Subscribe<T> = {
|
|
29
|
+
(fn: (value: T) => void): () => void
|
|
30
|
+
<R>(sliceFn: (value: T) => R, fn: (value: R) => void): () => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type Store<T, U extends Record<string, Function>> = StoreHook<T, U> & {
|
|
34
|
+
getState: () => T
|
|
35
|
+
setState: (setter: Kaioken.StateSetter<T>) => void
|
|
36
|
+
methods: U
|
|
37
|
+
subscribe: Subscribe<T>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type NodeState = {
|
|
41
|
+
update: () => void
|
|
42
|
+
slices: {
|
|
43
|
+
sliceFn: Function | null
|
|
44
|
+
eq:
|
|
45
|
+
| ((prev: any, next: any, compare: typeof shallowCompare) => boolean)
|
|
46
|
+
| undefined
|
|
47
|
+
value: any
|
|
48
|
+
}[]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type InternalStoreState<T, U extends MethodFactory<T>> = {
|
|
52
|
+
value: T
|
|
53
|
+
epoch: number
|
|
54
|
+
subscribers: Set<Kaioken.VNode | Function>
|
|
55
|
+
nodeStateMap: WeakMap<Kaioken.VNode, NodeState>
|
|
56
|
+
methods: ReturnType<U>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function createStore<T, U extends MethodFactory<T>>(
|
|
60
|
+
initial: T,
|
|
61
|
+
methodFactory: U
|
|
62
|
+
): Store<T, ReturnType<U>> {
|
|
63
|
+
const state = {
|
|
64
|
+
$initial: null as string | null,
|
|
65
|
+
current: {
|
|
66
|
+
value: initial,
|
|
67
|
+
epoch: 0,
|
|
68
|
+
subscribers: new Set<Kaioken.VNode | Function>(),
|
|
69
|
+
nodeStateMap: new WeakMap<Kaioken.VNode, NodeState>(),
|
|
70
|
+
methods: null as any as ReturnType<U>,
|
|
71
|
+
} satisfies InternalStoreState<T, U>,
|
|
72
|
+
}
|
|
73
|
+
if (__DEV__) {
|
|
74
|
+
state.$initial = safeStringify(initial)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const getState = () => state.current.value
|
|
78
|
+
|
|
79
|
+
const setState = (setter: Kaioken.StateSetter<T>) => {
|
|
80
|
+
state.current.value =
|
|
81
|
+
typeof setter === "function"
|
|
82
|
+
? (setter as Function)(state.current.value)
|
|
83
|
+
: setter
|
|
84
|
+
|
|
85
|
+
const { value, subscribers, nodeStateMap } = state.current
|
|
86
|
+
|
|
87
|
+
subscribers.forEach((n) => {
|
|
88
|
+
if (typeof n === "function") return n(value)
|
|
89
|
+
const nodeState = nodeStateMap.get(n)
|
|
90
|
+
if (!nodeState) return
|
|
91
|
+
const { slices, update } = nodeState
|
|
92
|
+
let computeChanged = slices.length === 0
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < slices.length; i++) {
|
|
95
|
+
const slice = slices[i]
|
|
96
|
+
if (!slice) continue
|
|
97
|
+
const { sliceFn, eq, value: sliceVal } = slice
|
|
98
|
+
const newSliceVal = sliceFn === null ? value : sliceFn(value)
|
|
99
|
+
slices[i] = { sliceFn, eq, value: newSliceVal }
|
|
100
|
+
|
|
101
|
+
if (computeChanged) continue
|
|
102
|
+
if (eq && eq(sliceVal, newSliceVal, shallowCompare)) {
|
|
103
|
+
continue
|
|
104
|
+
} else if (!eq && shallowCompare(sliceVal, newSliceVal)) {
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
computeChanged = true
|
|
109
|
+
}
|
|
110
|
+
if (computeChanged) {
|
|
111
|
+
update()
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
state.current.epoch++
|
|
115
|
+
}
|
|
116
|
+
state.current.methods = methodFactory(setState, getState) as ReturnType<U>
|
|
117
|
+
|
|
118
|
+
function useStore<R>(
|
|
119
|
+
sliceFn: null | ((state: T) => R) = null,
|
|
120
|
+
equality?: (prev: R, next: R, compare: typeof shallowCompare) => boolean
|
|
121
|
+
) {
|
|
122
|
+
if (!sideEffectsEnabled()) {
|
|
123
|
+
return {
|
|
124
|
+
value: sliceFn ? sliceFn(state.current.value) : state.current.value,
|
|
125
|
+
...state.current.methods,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return useHook(
|
|
129
|
+
"useStore",
|
|
130
|
+
{ value: null as any as T | R, lastChangeSync: -1 },
|
|
131
|
+
({ hook, isInit, isHMR, vNode, index, update }) => {
|
|
132
|
+
if (__DEV__) {
|
|
133
|
+
if (isInit) {
|
|
134
|
+
hook.dev = {
|
|
135
|
+
devtools: {
|
|
136
|
+
get: () => ({ value: hook.value }),
|
|
137
|
+
},
|
|
138
|
+
initialArgs: [sliceFn, equality],
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (isHMR) {
|
|
142
|
+
const [fn, eq] = hook.dev!.initialArgs
|
|
143
|
+
if (fn !== sliceFn || eq !== equality) {
|
|
144
|
+
isInit = true
|
|
145
|
+
hook.dev!.initialArgs = [sliceFn, equality]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const { subscribers, nodeStateMap, epoch, value, methods } =
|
|
150
|
+
state.current
|
|
151
|
+
|
|
152
|
+
const nodeState = nodeStateMap.get(vNode) ?? {
|
|
153
|
+
slices: [],
|
|
154
|
+
update,
|
|
155
|
+
}
|
|
156
|
+
let slice: (typeof nodeState.slices)[number]
|
|
157
|
+
if (isInit || !nodeState.slices[index]) {
|
|
158
|
+
subscribers.add(vNode)
|
|
159
|
+
hook.lastChangeSync = epoch
|
|
160
|
+
hook.value = sliceFn === null ? value : sliceFn(value)
|
|
161
|
+
slice = nodeState.slices[index] = {
|
|
162
|
+
sliceFn,
|
|
163
|
+
eq: equality,
|
|
164
|
+
value: hook.value,
|
|
165
|
+
}
|
|
166
|
+
nodeStateMap.set(vNode, nodeState)
|
|
167
|
+
|
|
168
|
+
hook.cleanup = () => {
|
|
169
|
+
nodeStateMap.delete(vNode)
|
|
170
|
+
subscribers.delete(vNode)
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
slice = nodeState.slices[index]
|
|
174
|
+
if (slice.sliceFn !== sliceFn) {
|
|
175
|
+
slice.value = hook.value = sliceFn ? sliceFn(value) : value
|
|
176
|
+
slice.sliceFn = sliceFn
|
|
177
|
+
}
|
|
178
|
+
slice.eq = equality
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (hook.lastChangeSync !== epoch) {
|
|
182
|
+
hook.value = slice ? slice.value : state.current.value
|
|
183
|
+
hook.lastChangeSync = epoch
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { value: hook.value, ...methods }
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const store = Object.assign(useStore, {
|
|
192
|
+
get getState() {
|
|
193
|
+
return getState
|
|
194
|
+
},
|
|
195
|
+
get setState() {
|
|
196
|
+
return setState
|
|
197
|
+
},
|
|
198
|
+
get methods() {
|
|
199
|
+
return { ...state.current.methods }
|
|
200
|
+
},
|
|
201
|
+
subscribe: ((sliceOrCb, cb) => {
|
|
202
|
+
if (cb === undefined) {
|
|
203
|
+
state.current.subscribers.add(sliceOrCb)
|
|
204
|
+
return () => (state.current.subscribers.delete(sliceOrCb), void 0)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const selection = {
|
|
208
|
+
current: null as ReturnType<typeof sliceOrCb> | null,
|
|
209
|
+
}
|
|
210
|
+
const sliceWrapper = (newState: T) => {
|
|
211
|
+
const next = sliceOrCb(newState)
|
|
212
|
+
if (shallowCompare(next, selection.current)) return
|
|
213
|
+
selection.current = next
|
|
214
|
+
cb(next)
|
|
215
|
+
}
|
|
216
|
+
state.current.subscribers.add(sliceWrapper)
|
|
217
|
+
return () => (state.current.subscribers.delete(sliceWrapper), void 0)
|
|
218
|
+
}) as Subscribe<T>,
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
if (__DEV__) {
|
|
222
|
+
return Object.assign(store, {
|
|
223
|
+
[$HMR_ACCEPT]: {
|
|
224
|
+
provide: () => state,
|
|
225
|
+
inject: (prev) => {
|
|
226
|
+
state.current = prev.current
|
|
227
|
+
state.current.methods = methodFactory(
|
|
228
|
+
setState,
|
|
229
|
+
getState
|
|
230
|
+
) as ReturnType<U>
|
|
231
|
+
if (state.$initial !== prev.$initial) {
|
|
232
|
+
setState(initial)
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
destroy: () => {},
|
|
236
|
+
} satisfies HMRAccept<typeof state>,
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return store
|
|
241
|
+
}
|