acrylblends 0.6.2 → 0.6.3
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/README.md +4 -1
- package/docs/framework/README.md +3 -2
- package/docs/framework/UPSTREAM.md +1 -1
- package/docs/framework/cordis/acryl-in-a-cordis-app.md +6 -0
- package/docs/framework/getting-started/basic.md +1 -0
- package/docs/framework/getting-started/desktop.md +41 -0
- package/docs/framework/guides/jsx-dom.md +0 -3
- package/docs/framework/guides/jsx.md +0 -3
- package/docs/framework/helpers/css.md +0 -3
- package/docs/framework/upstream.json +3 -3
- package/lib/accepts.d.ts +1 -1
- package/lib/acryl-base.d.ts +1 -1
- package/lib/adapter.d.ts +1 -1
- package/lib/{app-BdrVHBlJ.d.ts → app-wFwpg-bt.d.ts} +1 -1
- package/lib/base-BpQy2Rk7.d.ts +783 -0
- package/lib/base-ddiVmiE7.js +721 -0
- package/lib/basic-auth.d.ts +1 -1
- package/lib/bearer-auth.d.ts +1 -1
- package/lib/body-limit.d.ts +1 -1
- package/lib/cache.d.ts +1 -1
- package/lib/client.d.ts +2 -2
- package/lib/combine.d.ts +1 -1
- package/lib/common-D8Z4JRCw.d.ts +46 -0
- package/lib/components-Ce5FRyHU.js +268 -0
- package/lib/components-DYZdxJIJ.js +24 -0
- package/lib/compress.d.ts +1 -1
- package/lib/conninfo.d.ts +2 -2
- package/lib/constants-BR9jM9BC.js +9 -0
- package/lib/{context-B5Y1lLha.d.ts → context-DVQBjuns.d.ts} +1 -1
- package/lib/context-DxFdY_un.d.ts +250 -0
- package/lib/context-storage.d.ts +1 -1
- package/lib/{cookie-D9_nQbsM.d.ts → cookie-CCelEqky.d.ts} +1 -1
- package/lib/cookie.d.ts +3 -3
- package/lib/cors.d.ts +1 -1
- package/lib/csrf.d.ts +1 -1
- package/lib/css-D2jb7X8b.js +320 -0
- package/lib/css.d.ts +82 -0
- package/lib/css.js +134 -0
- package/lib/dev.d.ts +2 -2
- package/lib/etag.d.ts +1 -1
- package/lib/factory.d.ts +2 -2
- package/lib/hooks-Bq5q9IMl.js +762 -0
- package/lib/html-SllAe0YT.d.ts +46 -0
- package/lib/html.d.ts +1 -1
- package/lib/index-C850MeBA.d.ts +94 -0
- package/lib/{index-m12zcmph.d.ts → index-CUTfU69R.d.ts} +3 -3
- package/lib/{index-DtEoak1i.d.ts → index-Cqcdluyn.d.ts} +2 -2
- package/lib/{index-Kqou8Fjh.d.ts → index-CsvB-F9j.d.ts} +2 -2
- package/lib/index.d.ts +4 -4
- package/lib/ip-restriction.d.ts +2 -2
- package/lib/jsx/dom/client.d.ts +30 -0
- package/lib/jsx/dom/client.js +49 -0
- package/lib/jsx/dom/css.d.ts +84 -0
- package/lib/jsx/dom/css.js +2 -0
- package/lib/jsx/dom/jsx-dev-runtime.d.ts +3 -0
- package/lib/jsx/dom/jsx-dev-runtime.js +2 -0
- package/lib/jsx/dom/jsx-runtime.d.ts +3 -0
- package/lib/jsx/dom/jsx-runtime.js +2 -0
- package/lib/jsx/dom/server.d.ts +105 -0
- package/lib/jsx/dom/server.js +37 -0
- package/lib/jsx/dom.d.ts +5 -0
- package/lib/jsx/dom.js +74 -0
- package/lib/jsx/jsx-dev-runtime.d.ts +6 -0
- package/lib/jsx/jsx-dev-runtime.js +14 -0
- package/lib/jsx/jsx-runtime.d.ts +10 -0
- package/lib/jsx/jsx-runtime.js +30 -0
- package/lib/jsx/streaming.d.ts +2 -0
- package/lib/jsx/streaming.js +2 -0
- package/lib/jsx-dev-runtime-B22O-Npk.d.ts +7 -0
- package/lib/jsx-dev-runtime-kAhR_4C7.js +354 -0
- package/lib/jsx-runtime-BVVgDSdq.d.ts +1 -0
- package/lib/jsx.d.ts +3 -0
- package/lib/jsx.js +47 -0
- package/lib/jwk.d.ts +4 -4
- package/lib/{jwt-BA65QhnJ.d.ts → jwt-BARhotDz.d.ts} +1 -1
- package/lib/jwt.d.ts +4 -4
- package/lib/language.d.ts +1 -1
- package/lib/logger.d.ts +1 -1
- package/lib/loopback-only.d.ts +1 -1
- package/lib/method-not-allowed.d.ts +2 -2
- package/lib/method-override.d.ts +2 -2
- package/lib/mount.d.ts +1 -1
- package/lib/node-server/conninfo.d.ts +1 -1
- package/lib/node-server/serve-static.d.ts +1 -1
- package/lib/node-server/serve-static.js +1 -1
- package/lib/node-server.d.ts +1 -1
- package/lib/node-server.js +1 -1
- package/lib/powered-by.d.ts +1 -1
- package/lib/pretty-json.d.ts +1 -1
- package/lib/proxy.d.ts +1 -1
- package/lib/quick.d.ts +1 -1
- package/lib/request-id.d.ts +1 -1
- package/lib/request.d.ts +1 -1
- package/lib/route.d.ts +1 -1
- package/lib/schema-validator.d.ts +1 -1
- package/lib/secure-headers.d.ts +1 -1
- package/lib/{serve-static-Dw_m6KxR.js → serve-static-Cw23shXv.js} +1 -0
- package/lib/serve-static.d.ts +1 -1
- package/lib/streaming.d.ts +1 -1
- package/lib/testing.d.ts +4 -4
- package/lib/timeout.d.ts +1 -1
- package/lib/timing.d.ts +1 -1
- package/lib/tiny.d.ts +1 -1
- package/lib/trailing-slash.d.ts +1 -1
- package/lib/{types-Bd29gh2o.d.ts → types-CQiGmsWW.d.ts} +3 -3
- package/lib/{types-D9DEXMjE.d.ts → types-Lqx5BxQF.d.ts} +8 -1
- package/lib/{types-ZM9gIiKh.d.ts → types-nEL8VPDC.d.ts} +1 -1
- package/lib/types.d.ts +1 -1
- package/lib/utils/body.d.ts +1 -1
- package/lib/utils/cookie.d.ts +1 -1
- package/lib/utils/crypto.d.ts +1 -1
- package/lib/utils/html.d.ts +1 -45
- package/lib/utils/ipaddr.d.ts +1 -1
- package/lib/utils/jwt.d.ts +1 -1
- package/lib/utils/types.d.ts +2 -2
- package/lib/validator.d.ts +2 -2
- package/lib/ws.d.ts +1 -1
- package/package.json +45 -1
- package/plugins/acryl-inspect/upstream.json +1 -1
- package/plugins/acryl-node-server/upstream.json +1 -1
- package/plugins/acryl-router/src/core/helper/css/common.case.test.tsx +506 -0
- package/plugins/acryl-router/src/core/helper/css/common.ts +337 -0
- package/plugins/acryl-router/src/core/helper/css/index.test.tsx +678 -0
- package/plugins/acryl-router/src/core/helper/css/index.ts +262 -0
- package/plugins/acryl-router/src/core/helper/streaming/sse.test.tsx +557 -0
- package/plugins/acryl-router/src/core/jsx/base.test.tsx +97 -0
- package/plugins/acryl-router/src/core/jsx/base.ts +490 -0
- package/plugins/acryl-router/src/core/jsx/children.test.ts +52 -0
- package/plugins/acryl-router/src/core/jsx/children.ts +20 -0
- package/plugins/acryl-router/src/core/jsx/components.test.tsx +936 -0
- package/plugins/acryl-router/src/core/jsx/components.ts +257 -0
- package/plugins/acryl-router/src/core/jsx/constants.ts +6 -0
- package/plugins/acryl-router/src/core/jsx/context-isolation.test.ts +655 -0
- package/plugins/acryl-router/src/core/jsx/context.ts +266 -0
- package/plugins/acryl-router/src/core/jsx/dom/client.test.tsx +128 -0
- package/plugins/acryl-router/src/core/jsx/dom/client.ts +89 -0
- package/plugins/acryl-router/src/core/jsx/dom/components.test.tsx +330 -0
- package/plugins/acryl-router/src/core/jsx/dom/components.ts +39 -0
- package/plugins/acryl-router/src/core/jsx/dom/context.test.tsx +184 -0
- package/plugins/acryl-router/src/core/jsx/dom/context.ts +52 -0
- package/plugins/acryl-router/src/core/jsx/dom/css.test.tsx +149 -0
- package/plugins/acryl-router/src/core/jsx/dom/css.ts +263 -0
- package/plugins/acryl-router/src/core/jsx/dom/hooks/index.test.tsx +172 -0
- package/plugins/acryl-router/src/core/jsx/dom/hooks/index.ts +91 -0
- package/plugins/acryl-router/src/core/jsx/dom/index.test.tsx +3681 -0
- package/plugins/acryl-router/src/core/jsx/dom/index.ts +170 -0
- package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.test.tsx +1344 -0
- package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.ts +438 -0
- package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.ts +23 -0
- package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.ts +8 -0
- package/plugins/acryl-router/src/core/jsx/dom/render.ts +879 -0
- package/plugins/acryl-router/src/core/jsx/dom/server.test.tsx +199 -0
- package/plugins/acryl-router/src/core/jsx/dom/server.ts +76 -0
- package/plugins/acryl-router/src/core/jsx/dom/utils.ts +7 -0
- package/plugins/acryl-router/src/core/jsx/hooks/dom.test.tsx +859 -0
- package/plugins/acryl-router/src/core/jsx/hooks/index.ts +437 -0
- package/plugins/acryl-router/src/core/jsx/hooks/string.test.tsx +40 -0
- package/plugins/acryl-router/src/core/jsx/index.test.tsx +1744 -0
- package/plugins/acryl-router/src/core/jsx/index.ts +115 -0
- package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.ts +23 -0
- package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.test.tsx +666 -0
- package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.ts +210 -0
- package/plugins/acryl-router/src/core/jsx/intrinsic-elements.ts +938 -0
- package/plugins/acryl-router/src/core/jsx/jsx-dev-runtime.ts +26 -0
- package/plugins/acryl-router/src/core/jsx/jsx-runtime.test.tsx +85 -0
- package/plugins/acryl-router/src/core/jsx/jsx-runtime.ts +51 -0
- package/plugins/acryl-router/src/core/jsx/streaming.test.tsx +1281 -0
- package/plugins/acryl-router/src/core/jsx/streaming.ts +231 -0
- package/plugins/acryl-router/src/core/jsx/types.ts +41 -0
- package/plugins/acryl-router/src/core/jsx/utils.test.ts +366 -0
- package/plugins/acryl-router/src/core/jsx/utils.ts +202 -0
- package/plugins/acryl-router/src/core/utils/types.ts +6 -0
- package/plugins/acryl-router/src/helpers/index.ts +1 -0
- package/plugins/acryl-router/tests/jsx.spec.tsx +63 -0
- package/plugins/acryl-router/upstream.json +3 -3
- package/plugins/acryl-router-http/upstream.json +1 -1
- package/plugins/acryl-state/upstream.json +1 -1
- package/plugins/desktop/upstream.json +1 -1
- package/plugins/desktop-web/upstream.json +1 -1
- package/plugins/save/plugin.json +1 -1
- package/templates/todo/upstream.json +1 -1
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import type { JSX } from '../base.ts'
|
|
2
|
+
import { DOM_STASH } from '../constants.ts'
|
|
3
|
+
import { buildDataStack, update } from '../dom/render.ts'
|
|
4
|
+
import type { Context, Node, NodeObject, PendingType, UpdateHook } from '../dom/render.ts'
|
|
5
|
+
|
|
6
|
+
type UpdateStateFunction<T> = (newState: T | ((currentState: T) => T)) => void
|
|
7
|
+
|
|
8
|
+
const STASH_SATE = 0
|
|
9
|
+
export const STASH_EFFECT = 1
|
|
10
|
+
const STASH_CALLBACK = 2
|
|
11
|
+
const STASH_MEMO = 3
|
|
12
|
+
const STASH_REF = 4
|
|
13
|
+
|
|
14
|
+
export type EffectData = [
|
|
15
|
+
readonly unknown[] | undefined, // deps
|
|
16
|
+
(() => void | (() => void)) | undefined, // layout effect
|
|
17
|
+
(() => void) | undefined, // cleanup
|
|
18
|
+
(() => void) | undefined, // effect
|
|
19
|
+
(() => void) | undefined, // insertion effect
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
const resolvedPromiseValueMap: WeakMap<Promise<unknown>, unknown> = new WeakMap<
|
|
23
|
+
Promise<unknown>,
|
|
24
|
+
unknown
|
|
25
|
+
>()
|
|
26
|
+
|
|
27
|
+
const isDepsChanged = (
|
|
28
|
+
prevDeps: readonly unknown[] | undefined,
|
|
29
|
+
deps: readonly unknown[] | undefined
|
|
30
|
+
): boolean =>
|
|
31
|
+
!prevDeps ||
|
|
32
|
+
!deps ||
|
|
33
|
+
prevDeps.length !== deps.length ||
|
|
34
|
+
deps.some((dep, i) => dep !== prevDeps[i])
|
|
35
|
+
|
|
36
|
+
let viewTransitionState:
|
|
37
|
+
| [
|
|
38
|
+
boolean, // isUpdating
|
|
39
|
+
boolean, // useViewTransition() is called
|
|
40
|
+
]
|
|
41
|
+
| undefined = undefined
|
|
42
|
+
|
|
43
|
+
const documentStartViewTransition: (cb: () => void) => { finished: Promise<void> } = (cb) => {
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
45
|
+
if ((document as any)?.startViewTransition) {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
return (document as any).startViewTransition(cb)
|
|
48
|
+
} else {
|
|
49
|
+
cb()
|
|
50
|
+
return { finished: Promise.resolve() }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let updateHook: UpdateHook | undefined = undefined
|
|
55
|
+
const viewTransitionHook = (
|
|
56
|
+
context: Context,
|
|
57
|
+
node: Node,
|
|
58
|
+
cb: (context: Context) => void
|
|
59
|
+
): Promise<void> => {
|
|
60
|
+
const state: [boolean, boolean] = [true, false]
|
|
61
|
+
let lastVC = node.vC
|
|
62
|
+
return documentStartViewTransition(() => {
|
|
63
|
+
if (lastVC === node.vC) {
|
|
64
|
+
viewTransitionState = state
|
|
65
|
+
cb(context)
|
|
66
|
+
viewTransitionState = undefined
|
|
67
|
+
lastVC = node.vC
|
|
68
|
+
}
|
|
69
|
+
}).finished.then(() => {
|
|
70
|
+
if (state[1] && lastVC === node.vC) {
|
|
71
|
+
state[0] = false
|
|
72
|
+
viewTransitionState = state
|
|
73
|
+
cb(context)
|
|
74
|
+
viewTransitionState = undefined
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const startViewTransition = (callback: () => void): void => {
|
|
80
|
+
updateHook = viewTransitionHook
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
callback()
|
|
84
|
+
} finally {
|
|
85
|
+
updateHook = undefined
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const useViewTransition = (): [boolean, (callback: () => void) => void] => {
|
|
90
|
+
const buildData = buildDataStack.at(-1) as [Context, NodeObject]
|
|
91
|
+
if (!buildData) {
|
|
92
|
+
return [false, () => {}]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (viewTransitionState) {
|
|
96
|
+
viewTransitionState[1] = true
|
|
97
|
+
}
|
|
98
|
+
return [!!viewTransitionState?.[0], startViewTransition]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// PendingType is defined in "../dom/render", 3 is used for useDeferredValue
|
|
102
|
+
const pendingStack: [PendingType | 3, Promise<void>][] = []
|
|
103
|
+
const runCallback = (type: PendingType, callback: Function): void => {
|
|
104
|
+
let resolve: (() => void) | undefined
|
|
105
|
+
const promise = new Promise<void>((r) => (resolve = r))
|
|
106
|
+
pendingStack.push([type, promise])
|
|
107
|
+
try {
|
|
108
|
+
const res = callback()
|
|
109
|
+
if (res instanceof Promise) {
|
|
110
|
+
res.then(resolve, resolve)
|
|
111
|
+
} else {
|
|
112
|
+
resolve!()
|
|
113
|
+
}
|
|
114
|
+
} finally {
|
|
115
|
+
pendingStack.pop()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const startTransition = (callback: () => void): void => {
|
|
120
|
+
runCallback(1, callback)
|
|
121
|
+
}
|
|
122
|
+
const startTransitionHook = (callback: () => void | Promise<void>): void => {
|
|
123
|
+
runCallback(2, callback)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const useTransition = (): [boolean, (callback: () => void | Promise<void>) => void] => {
|
|
127
|
+
const buildData = buildDataStack.at(-1) as [Context, NodeObject]
|
|
128
|
+
if (!buildData) {
|
|
129
|
+
return [false, () => {}]
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const [error, setError] = useState<[Error]>()
|
|
133
|
+
const [state, updateState] = useState<boolean>()
|
|
134
|
+
if (error) {
|
|
135
|
+
throw error[0]
|
|
136
|
+
}
|
|
137
|
+
const startTransitionLocalHook = useCallback<typeof startTransitionHook>(
|
|
138
|
+
(callback) => {
|
|
139
|
+
startTransitionHook(() => {
|
|
140
|
+
updateState((state) => !state)
|
|
141
|
+
let res = callback()
|
|
142
|
+
if (res instanceof Promise) {
|
|
143
|
+
res = res.catch((e) => {
|
|
144
|
+
setError([e])
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
return res
|
|
148
|
+
})
|
|
149
|
+
},
|
|
150
|
+
[state]
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
const [context] = buildData
|
|
154
|
+
return [context[0] === 2, startTransitionLocalHook]
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
type UseDeferredValue = <T>(value: T, initialValue?: T) => T
|
|
158
|
+
export const useDeferredValue: UseDeferredValue = <T>(value: T, ...rest: [T | undefined]): T => {
|
|
159
|
+
const [values, setValues] = useState<[T, T]>(
|
|
160
|
+
(rest.length ? [rest[0], rest[0]] : [value, value]) as [T, T]
|
|
161
|
+
)
|
|
162
|
+
if (Object.is(values[1], value)) {
|
|
163
|
+
return values[1]
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
pendingStack.push([3, Promise.resolve()])
|
|
167
|
+
updateHook = async (context: Context, _, cb: (context: Context) => void) => {
|
|
168
|
+
cb(context)
|
|
169
|
+
values[0] = value
|
|
170
|
+
}
|
|
171
|
+
setValues([values[0], value])
|
|
172
|
+
updateHook = undefined
|
|
173
|
+
pendingStack.pop()
|
|
174
|
+
|
|
175
|
+
return values[0]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
type UseStateType = {
|
|
179
|
+
<T>(initialState: T | (() => T)): [T, UpdateStateFunction<T>]
|
|
180
|
+
<T = undefined>(): [T | undefined, UpdateStateFunction<T | undefined>]
|
|
181
|
+
}
|
|
182
|
+
export const useState: UseStateType = <T>(
|
|
183
|
+
initialState?: T | (() => T)
|
|
184
|
+
): [T, UpdateStateFunction<T>] => {
|
|
185
|
+
const resolveInitialState = () =>
|
|
186
|
+
typeof initialState === 'function' ? (initialState as () => T)() : (initialState as T)
|
|
187
|
+
|
|
188
|
+
const buildData = buildDataStack.at(-1) as [unknown, NodeObject]
|
|
189
|
+
if (!buildData) {
|
|
190
|
+
return [resolveInitialState(), () => {}]
|
|
191
|
+
}
|
|
192
|
+
const [, node] = buildData
|
|
193
|
+
|
|
194
|
+
const stateArray = (node[DOM_STASH][1][STASH_SATE] ||= [])
|
|
195
|
+
const hookIndex = node[DOM_STASH][0]++
|
|
196
|
+
|
|
197
|
+
return (stateArray[hookIndex] ||= [
|
|
198
|
+
resolveInitialState(),
|
|
199
|
+
(newState: T | ((currentState: T) => T)) => {
|
|
200
|
+
const localUpdateHook = updateHook
|
|
201
|
+
const stateData = stateArray[hookIndex]
|
|
202
|
+
if (typeof newState === 'function') {
|
|
203
|
+
newState = (newState as (currentState: T) => T)(stateData[0])
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!Object.is(newState, stateData[0])) {
|
|
207
|
+
stateData[0] = newState
|
|
208
|
+
if (pendingStack.length) {
|
|
209
|
+
const [pendingType, pendingPromise] = pendingStack.at(-1) as [
|
|
210
|
+
PendingType | 3,
|
|
211
|
+
Promise<void>,
|
|
212
|
+
]
|
|
213
|
+
Promise.all([
|
|
214
|
+
pendingType === 3
|
|
215
|
+
? node
|
|
216
|
+
: update([pendingType, false, localUpdateHook as UpdateHook], node),
|
|
217
|
+
pendingPromise,
|
|
218
|
+
]).then(([node]) => {
|
|
219
|
+
if (!node || !(pendingType === 2 || pendingType === 3)) {
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const lastVC = node.vC
|
|
224
|
+
|
|
225
|
+
const addUpdateTask = () => {
|
|
226
|
+
setTimeout(() => {
|
|
227
|
+
// return if `node` is rerendered after current transition
|
|
228
|
+
if (lastVC !== node.vC) {
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
update([pendingType === 3 ? 1 : 0, false, localUpdateHook as UpdateHook], node)
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
requestAnimationFrame(addUpdateTask)
|
|
236
|
+
})
|
|
237
|
+
} else {
|
|
238
|
+
update([0, false, localUpdateHook as UpdateHook], node)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
])
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export const useReducer = <T, A>(
|
|
246
|
+
reducer: (state: T, action: A) => T,
|
|
247
|
+
initialArg: T,
|
|
248
|
+
init?: (initialState: T) => T
|
|
249
|
+
): [T, (action: A) => void] => {
|
|
250
|
+
const handler = useCallback(
|
|
251
|
+
(action: A) => {
|
|
252
|
+
setState((state) => reducer(state, action))
|
|
253
|
+
},
|
|
254
|
+
[reducer]
|
|
255
|
+
)
|
|
256
|
+
const [state, setState] = useState(() => (init ? init(initialArg) : initialArg))
|
|
257
|
+
return [state, handler]
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const useEffectCommon = (
|
|
261
|
+
index: number,
|
|
262
|
+
effect: () => void | (() => void),
|
|
263
|
+
deps?: readonly unknown[]
|
|
264
|
+
): void => {
|
|
265
|
+
const buildData = buildDataStack.at(-1) as [unknown, NodeObject]
|
|
266
|
+
if (!buildData) {
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
const [, node] = buildData
|
|
270
|
+
|
|
271
|
+
const effectDepsArray = (node[DOM_STASH][1][STASH_EFFECT] ||= [])
|
|
272
|
+
const hookIndex = node[DOM_STASH][0]++
|
|
273
|
+
|
|
274
|
+
const [prevDeps, , prevCleanup] = (effectDepsArray[hookIndex] ||= [])
|
|
275
|
+
if (isDepsChanged(prevDeps, deps)) {
|
|
276
|
+
if (prevCleanup) {
|
|
277
|
+
prevCleanup()
|
|
278
|
+
}
|
|
279
|
+
const runner = () => {
|
|
280
|
+
data[index] = undefined // clear this effect in order to avoid calling effect twice
|
|
281
|
+
data[2] = effect() as (() => void) | undefined
|
|
282
|
+
}
|
|
283
|
+
const data: EffectData = [deps, undefined, undefined, undefined, undefined]
|
|
284
|
+
data[index] = runner
|
|
285
|
+
effectDepsArray[hookIndex] = data
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
export const useEffect = (effect: () => void | (() => void), deps?: readonly unknown[]): void =>
|
|
289
|
+
useEffectCommon(3, effect, deps)
|
|
290
|
+
export const useLayoutEffect = (
|
|
291
|
+
effect: () => void | (() => void),
|
|
292
|
+
deps?: readonly unknown[]
|
|
293
|
+
): void => useEffectCommon(1, effect, deps)
|
|
294
|
+
export const useInsertionEffect = (
|
|
295
|
+
effect: () => void | (() => void),
|
|
296
|
+
deps?: readonly unknown[]
|
|
297
|
+
): void => useEffectCommon(4, effect, deps)
|
|
298
|
+
|
|
299
|
+
export const useCallback = <T extends Function>(callback: T, deps: readonly unknown[]): T => {
|
|
300
|
+
const buildData = buildDataStack.at(-1) as [unknown, NodeObject]
|
|
301
|
+
if (!buildData) {
|
|
302
|
+
return callback
|
|
303
|
+
}
|
|
304
|
+
const [, node] = buildData
|
|
305
|
+
|
|
306
|
+
const callbackArray = (node[DOM_STASH][1][STASH_CALLBACK] ||= [])
|
|
307
|
+
const hookIndex = node[DOM_STASH][0]++
|
|
308
|
+
|
|
309
|
+
const prevDeps = callbackArray[hookIndex]
|
|
310
|
+
if (isDepsChanged(prevDeps?.[1], deps)) {
|
|
311
|
+
callbackArray[hookIndex] = [callback, deps]
|
|
312
|
+
} else {
|
|
313
|
+
callback = callbackArray[hookIndex][0] as T
|
|
314
|
+
}
|
|
315
|
+
return callback
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export type RefObject<T> = { current: T }
|
|
319
|
+
|
|
320
|
+
export function useRef<T>(initialValue: T): RefObject<T>
|
|
321
|
+
export function useRef<T>(initialValue: T | null): RefObject<T | null>
|
|
322
|
+
export function useRef<T>(initialValue: T | undefined): RefObject<T | undefined>
|
|
323
|
+
export function useRef<T>(initialValue?: T | null): RefObject<T | null | undefined> {
|
|
324
|
+
const buildData = buildDataStack.at(-1) as [unknown, NodeObject]
|
|
325
|
+
if (!buildData) {
|
|
326
|
+
return { current: initialValue } as RefObject<T | null | undefined>
|
|
327
|
+
}
|
|
328
|
+
const [, node] = buildData
|
|
329
|
+
|
|
330
|
+
const refArray = (node[DOM_STASH][1][STASH_REF] ||= [])
|
|
331
|
+
const hookIndex = node[DOM_STASH][0]++
|
|
332
|
+
|
|
333
|
+
return (refArray[hookIndex] ||= { current: initialValue })
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export const use = <T>(promise: Promise<T>): T => {
|
|
337
|
+
const cachedRes = resolvedPromiseValueMap.get(promise) as [T] | [undefined, unknown] | undefined
|
|
338
|
+
if (cachedRes) {
|
|
339
|
+
if (cachedRes.length === 2) {
|
|
340
|
+
throw cachedRes[1]
|
|
341
|
+
}
|
|
342
|
+
return cachedRes[0] as T
|
|
343
|
+
}
|
|
344
|
+
promise.then(
|
|
345
|
+
(res) => resolvedPromiseValueMap.set(promise, [res]),
|
|
346
|
+
(e) => resolvedPromiseValueMap.set(promise, [undefined, e])
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
throw promise
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export const useMemo = <T>(factory: () => T, deps: readonly unknown[]): T => {
|
|
353
|
+
const buildData = buildDataStack.at(-1) as [unknown, NodeObject]
|
|
354
|
+
if (!buildData) {
|
|
355
|
+
return factory()
|
|
356
|
+
}
|
|
357
|
+
const [, node] = buildData
|
|
358
|
+
|
|
359
|
+
const memoArray = (node[DOM_STASH][1][STASH_MEMO] ||= [])
|
|
360
|
+
const hookIndex = node[DOM_STASH][0]++
|
|
361
|
+
|
|
362
|
+
const prevDeps = memoArray[hookIndex]
|
|
363
|
+
if (isDepsChanged(prevDeps?.[1], deps)) {
|
|
364
|
+
memoArray[hookIndex] = [factory(), deps]
|
|
365
|
+
}
|
|
366
|
+
return memoArray[hookIndex][0] as T
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
let idCounter = 0
|
|
370
|
+
export const useId = (): string => useMemo(() => `:r${(idCounter++).toString(32)}:`, [])
|
|
371
|
+
|
|
372
|
+
// Define to avoid errors. This hook currently does nothing.
|
|
373
|
+
export const useDebugValue = (_value: unknown, _formatter?: (value: unknown) => string): void => {}
|
|
374
|
+
|
|
375
|
+
export const createRef = <T>(): RefObject<T | null> => {
|
|
376
|
+
return { current: null }
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export const forwardRef = <T, P = {}>(
|
|
380
|
+
Component: (props: P, ref?: RefObject<T | null>) => JSX.Element
|
|
381
|
+
): ((props: P & { ref?: RefObject<T | null> }) => JSX.Element) => {
|
|
382
|
+
return (props) => {
|
|
383
|
+
const { ref, ...rest } = props
|
|
384
|
+
return Component(rest as P, ref)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export const useImperativeHandle = <T>(
|
|
389
|
+
ref: RefObject<T | null>,
|
|
390
|
+
createHandle: () => T,
|
|
391
|
+
deps: readonly unknown[]
|
|
392
|
+
): void => {
|
|
393
|
+
useEffect(() => {
|
|
394
|
+
ref.current = createHandle()
|
|
395
|
+
return () => {
|
|
396
|
+
ref.current = null
|
|
397
|
+
}
|
|
398
|
+
}, deps)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
export const useSyncExternalStore = <T>(
|
|
402
|
+
subscribe: (callback: () => void) => () => void,
|
|
403
|
+
getSnapshot: () => T,
|
|
404
|
+
getServerSnapshot?: () => T
|
|
405
|
+
): T => {
|
|
406
|
+
const buildData = buildDataStack.at(-1) as [Context, unknown]
|
|
407
|
+
if (!buildData) {
|
|
408
|
+
// now a stringify process, maybe in server side
|
|
409
|
+
if (!getServerSnapshot) {
|
|
410
|
+
throw new Error('getServerSnapshot is required for server side rendering')
|
|
411
|
+
}
|
|
412
|
+
return getServerSnapshot()
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const snapshot =
|
|
416
|
+
buildData[0][4] && getServerSnapshot ? (getServerSnapshot as () => T)() : getSnapshot()
|
|
417
|
+
const [, setVersion] = useState(0)
|
|
418
|
+
const latestSnapshot = useRef<[T, () => T]>([snapshot, getSnapshot])
|
|
419
|
+
latestSnapshot.current = [snapshot, getSnapshot]
|
|
420
|
+
const unsubscribeRef = useRef<() => void>(null)
|
|
421
|
+
|
|
422
|
+
// Swap subscriptions at effect flush: a returned cleanup would run synchronously
|
|
423
|
+
// during render when `subscribe` changes, leaving a window with no subscription.
|
|
424
|
+
useEffect(() => {
|
|
425
|
+
const update = () => setVersion((version) => version + 1)
|
|
426
|
+
unsubscribeRef.current?.()
|
|
427
|
+
unsubscribeRef.current = subscribe(update)
|
|
428
|
+
const [snapshot, getSnapshot] = latestSnapshot.current!
|
|
429
|
+
if (!Object.is(snapshot, getSnapshot())) {
|
|
430
|
+
update()
|
|
431
|
+
}
|
|
432
|
+
}, [subscribe])
|
|
433
|
+
|
|
434
|
+
useEffect(() => () => unsubscribeRef.current?.(), [])
|
|
435
|
+
|
|
436
|
+
return snapshot
|
|
437
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/** @jsxImportSource ../ */
|
|
2
|
+
import { useState, useSyncExternalStore } from '../index.ts'
|
|
3
|
+
|
|
4
|
+
describe('useState', () => {
|
|
5
|
+
it('should be rendered with initial state', () => {
|
|
6
|
+
const Component = () => {
|
|
7
|
+
const [state] = useState('hello')
|
|
8
|
+
return <span>{state}</span>
|
|
9
|
+
}
|
|
10
|
+
const template = <Component />
|
|
11
|
+
expect(template.toString()).toBe('<span>hello</span>')
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe('useSyncExternalStore', () => {
|
|
16
|
+
it('should be rendered with result of getServerSnapshot()', () => {
|
|
17
|
+
const unsubscribe = vi.fn()
|
|
18
|
+
const subscribe = vi.fn(() => unsubscribe)
|
|
19
|
+
const getSnapshot = vi.fn()
|
|
20
|
+
const getServerSnapshot = vi.fn(() => 100)
|
|
21
|
+
const App = () => {
|
|
22
|
+
const count = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
|
|
23
|
+
return <div>{count}</div>
|
|
24
|
+
}
|
|
25
|
+
const template = <App />
|
|
26
|
+
expect(template.toString()).toBe('<div>100</div>')
|
|
27
|
+
expect(unsubscribe).not.toBeCalled()
|
|
28
|
+
expect(subscribe).not.toBeCalled()
|
|
29
|
+
expect(getSnapshot).not.toBeCalled()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('should raise an error if getServerShot() is not provided', () => {
|
|
33
|
+
const App = () => {
|
|
34
|
+
const count = useSyncExternalStore(vi.fn(), vi.fn())
|
|
35
|
+
return <div>{count}</div>
|
|
36
|
+
}
|
|
37
|
+
const template = <App />
|
|
38
|
+
expect(() => template.toString()).toThrowError()
|
|
39
|
+
})
|
|
40
|
+
})
|