@fictjs/runtime 0.0.2
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 +17 -0
- package/dist/index.cjs +4224 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1572 -0
- package/dist/index.d.ts +1572 -0
- package/dist/index.dev.js +4240 -0
- package/dist/index.dev.js.map +1 -0
- package/dist/index.js +4133 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-dev-runtime.cjs +44 -0
- package/dist/jsx-dev-runtime.cjs.map +1 -0
- package/dist/jsx-dev-runtime.js +14 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.cjs +44 -0
- package/dist/jsx-runtime.cjs.map +1 -0
- package/dist/jsx-runtime.js +14 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/slim.cjs +3384 -0
- package/dist/slim.cjs.map +1 -0
- package/dist/slim.d.cts +475 -0
- package/dist/slim.d.ts +475 -0
- package/dist/slim.js +3335 -0
- package/dist/slim.js.map +1 -0
- package/package.json +68 -0
- package/src/binding.ts +2127 -0
- package/src/constants.ts +456 -0
- package/src/cycle-guard.ts +134 -0
- package/src/devtools.ts +17 -0
- package/src/dom.ts +683 -0
- package/src/effect.ts +83 -0
- package/src/error-boundary.ts +118 -0
- package/src/hooks.ts +72 -0
- package/src/index.ts +184 -0
- package/src/jsx-dev-runtime.ts +2 -0
- package/src/jsx-runtime.ts +2 -0
- package/src/jsx.ts +786 -0
- package/src/lifecycle.ts +273 -0
- package/src/list-helpers.ts +619 -0
- package/src/memo.ts +14 -0
- package/src/node-ops.ts +185 -0
- package/src/props.ts +212 -0
- package/src/reconcile.ts +151 -0
- package/src/ref.ts +25 -0
- package/src/scheduler.ts +12 -0
- package/src/signal.ts +1278 -0
- package/src/slim.ts +68 -0
- package/src/store.ts +210 -0
- package/src/suspense.ts +187 -0
- package/src/transition.ts +128 -0
- package/src/types.ts +172 -0
- package/src/versioned-signal.ts +58 -0
package/src/lifecycle.ts
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { enterRootGuard, exitRootGuard } from './cycle-guard'
|
|
2
|
+
import type { Cleanup, ErrorInfo, SuspenseToken } from './types'
|
|
3
|
+
|
|
4
|
+
type LifecycleFn = () => void | Cleanup
|
|
5
|
+
|
|
6
|
+
export interface RootContext {
|
|
7
|
+
parent?: RootContext | undefined
|
|
8
|
+
onMountCallbacks?: LifecycleFn[]
|
|
9
|
+
cleanups: Cleanup[]
|
|
10
|
+
destroyCallbacks: Cleanup[]
|
|
11
|
+
errorHandlers?: ErrorHandler[]
|
|
12
|
+
suspenseHandlers?: SuspenseHandler[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type ErrorHandler = (err: unknown, info?: ErrorInfo) => boolean | void
|
|
16
|
+
type SuspenseHandler = (token: SuspenseToken | PromiseLike<unknown>) => boolean | void
|
|
17
|
+
|
|
18
|
+
let currentRoot: RootContext | undefined
|
|
19
|
+
let currentEffectCleanups: Cleanup[] | undefined
|
|
20
|
+
const globalErrorHandlers = new WeakMap<RootContext, ErrorHandler[]>()
|
|
21
|
+
const globalSuspenseHandlers = new WeakMap<RootContext, SuspenseHandler[]>()
|
|
22
|
+
|
|
23
|
+
export function createRootContext(parent: RootContext | undefined = currentRoot): RootContext {
|
|
24
|
+
return { parent, cleanups: [], destroyCallbacks: [] }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function pushRoot(root: RootContext): RootContext | undefined {
|
|
28
|
+
if (!enterRootGuard(root)) {
|
|
29
|
+
return currentRoot
|
|
30
|
+
}
|
|
31
|
+
const prev = currentRoot
|
|
32
|
+
currentRoot = root
|
|
33
|
+
return prev
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getCurrentRoot(): RootContext | undefined {
|
|
37
|
+
return currentRoot
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function popRoot(prev: RootContext | undefined): void {
|
|
41
|
+
if (currentRoot) {
|
|
42
|
+
exitRootGuard(currentRoot)
|
|
43
|
+
}
|
|
44
|
+
currentRoot = prev
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function onMount(fn: LifecycleFn): void {
|
|
48
|
+
if (currentRoot) {
|
|
49
|
+
;(currentRoot.onMountCallbacks ||= []).push(fn)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
runLifecycle(fn)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function onDestroy(fn: LifecycleFn): void {
|
|
56
|
+
if (currentRoot) {
|
|
57
|
+
currentRoot.destroyCallbacks.push(() => runLifecycle(fn))
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
runLifecycle(fn)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function onCleanup(fn: Cleanup): void {
|
|
64
|
+
registerEffectCleanup(fn)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function flushOnMount(root: RootContext): void {
|
|
68
|
+
const cbs = root.onMountCallbacks
|
|
69
|
+
if (!cbs || cbs.length === 0) return
|
|
70
|
+
for (let i = 0; i < cbs.length; i++) {
|
|
71
|
+
const cleanup = cbs[i]!()
|
|
72
|
+
if (typeof cleanup === 'function') {
|
|
73
|
+
root.cleanups.push(cleanup)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
cbs.length = 0
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function registerRootCleanup(fn: Cleanup): void {
|
|
80
|
+
if (currentRoot) {
|
|
81
|
+
currentRoot.cleanups.push(fn)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function clearRoot(root: RootContext): void {
|
|
86
|
+
runCleanupList(root.cleanups)
|
|
87
|
+
if (root.onMountCallbacks) {
|
|
88
|
+
root.onMountCallbacks.length = 0
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function destroyRoot(root: RootContext): void {
|
|
93
|
+
clearRoot(root)
|
|
94
|
+
runCleanupList(root.destroyCallbacks)
|
|
95
|
+
if (root.errorHandlers) {
|
|
96
|
+
root.errorHandlers.length = 0
|
|
97
|
+
}
|
|
98
|
+
if (globalErrorHandlers.has(root)) {
|
|
99
|
+
globalErrorHandlers.delete(root)
|
|
100
|
+
}
|
|
101
|
+
if (root.suspenseHandlers) {
|
|
102
|
+
root.suspenseHandlers.length = 0
|
|
103
|
+
}
|
|
104
|
+
if (globalSuspenseHandlers.has(root)) {
|
|
105
|
+
globalSuspenseHandlers.delete(root)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function createRoot<T>(fn: () => T): { dispose: () => void; value: T } {
|
|
110
|
+
const root = createRootContext()
|
|
111
|
+
const prev = pushRoot(root)
|
|
112
|
+
let value: T
|
|
113
|
+
try {
|
|
114
|
+
value = fn()
|
|
115
|
+
} finally {
|
|
116
|
+
popRoot(prev)
|
|
117
|
+
}
|
|
118
|
+
flushOnMount(root)
|
|
119
|
+
return {
|
|
120
|
+
dispose: () => destroyRoot(root),
|
|
121
|
+
value,
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function withEffectCleanups<T>(bucket: Cleanup[], fn: () => T): T {
|
|
126
|
+
const prev = currentEffectCleanups
|
|
127
|
+
currentEffectCleanups = bucket
|
|
128
|
+
try {
|
|
129
|
+
return fn()
|
|
130
|
+
} finally {
|
|
131
|
+
currentEffectCleanups = prev
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function registerEffectCleanup(fn: Cleanup): void {
|
|
136
|
+
if (currentEffectCleanups) {
|
|
137
|
+
currentEffectCleanups.push(fn)
|
|
138
|
+
} else {
|
|
139
|
+
registerRootCleanup(fn)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function runCleanupList(list: Cleanup[]): void {
|
|
144
|
+
let error: unknown
|
|
145
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
146
|
+
try {
|
|
147
|
+
const cleanup = list[i]
|
|
148
|
+
if (cleanup) cleanup()
|
|
149
|
+
} catch (err) {
|
|
150
|
+
if (error === undefined) {
|
|
151
|
+
error = err
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
list.length = 0
|
|
156
|
+
if (error !== undefined) {
|
|
157
|
+
if (!handleError(error, { source: 'cleanup' })) {
|
|
158
|
+
throw error
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function runLifecycle(fn: LifecycleFn): void {
|
|
164
|
+
const cleanup = fn()
|
|
165
|
+
if (typeof cleanup === 'function') {
|
|
166
|
+
cleanup()
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function registerErrorHandler(fn: ErrorHandler): void {
|
|
171
|
+
if (!currentRoot) {
|
|
172
|
+
throw new Error('registerErrorHandler must be called within a root')
|
|
173
|
+
}
|
|
174
|
+
if (!currentRoot.errorHandlers) {
|
|
175
|
+
currentRoot.errorHandlers = []
|
|
176
|
+
}
|
|
177
|
+
currentRoot.errorHandlers.push(fn)
|
|
178
|
+
const existing = globalErrorHandlers.get(currentRoot)
|
|
179
|
+
if (existing) {
|
|
180
|
+
existing.push(fn)
|
|
181
|
+
} else {
|
|
182
|
+
globalErrorHandlers.set(currentRoot, [fn])
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function registerSuspenseHandler(fn: SuspenseHandler): void {
|
|
187
|
+
if (!currentRoot) {
|
|
188
|
+
throw new Error('registerSuspenseHandler must be called within a root')
|
|
189
|
+
}
|
|
190
|
+
if (!currentRoot.suspenseHandlers) {
|
|
191
|
+
currentRoot.suspenseHandlers = []
|
|
192
|
+
}
|
|
193
|
+
currentRoot.suspenseHandlers.push(fn)
|
|
194
|
+
const existing = globalSuspenseHandlers.get(currentRoot)
|
|
195
|
+
if (existing) {
|
|
196
|
+
existing.push(fn)
|
|
197
|
+
} else {
|
|
198
|
+
globalSuspenseHandlers.set(currentRoot, [fn])
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function handleError(err: unknown, info?: ErrorInfo, startRoot?: RootContext): boolean {
|
|
203
|
+
let root: RootContext | undefined = startRoot ?? currentRoot
|
|
204
|
+
let error = err
|
|
205
|
+
while (root) {
|
|
206
|
+
const handlers = root.errorHandlers
|
|
207
|
+
if (handlers && handlers.length) {
|
|
208
|
+
for (let i = handlers.length - 1; i >= 0; i--) {
|
|
209
|
+
const handler = handlers[i]!
|
|
210
|
+
try {
|
|
211
|
+
const handled = handler(error, info)
|
|
212
|
+
if (handled !== false) {
|
|
213
|
+
return true
|
|
214
|
+
}
|
|
215
|
+
} catch (nextErr) {
|
|
216
|
+
error = nextErr
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
root = root.parent
|
|
221
|
+
}
|
|
222
|
+
const globalForRoot = startRoot
|
|
223
|
+
? globalErrorHandlers.get(startRoot)
|
|
224
|
+
: currentRoot
|
|
225
|
+
? globalErrorHandlers.get(currentRoot)
|
|
226
|
+
: undefined
|
|
227
|
+
if (globalForRoot && globalForRoot.length) {
|
|
228
|
+
for (let i = globalForRoot.length - 1; i >= 0; i--) {
|
|
229
|
+
const handler = globalForRoot[i]!
|
|
230
|
+
try {
|
|
231
|
+
const handled = handler(error, info)
|
|
232
|
+
if (handled !== false) {
|
|
233
|
+
return true
|
|
234
|
+
}
|
|
235
|
+
} catch (nextErr) {
|
|
236
|
+
error = nextErr
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
throw error
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function handleSuspend(
|
|
244
|
+
token: SuspenseToken | PromiseLike<unknown>,
|
|
245
|
+
startRoot?: RootContext,
|
|
246
|
+
): boolean {
|
|
247
|
+
let root: RootContext | undefined = startRoot ?? currentRoot
|
|
248
|
+
while (root) {
|
|
249
|
+
const handlers = root.suspenseHandlers
|
|
250
|
+
if (handlers && handlers.length) {
|
|
251
|
+
for (let i = handlers.length - 1; i >= 0; i--) {
|
|
252
|
+
const handler = handlers[i]!
|
|
253
|
+
const handled = handler(token)
|
|
254
|
+
if (handled !== false) return true
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
root = root.parent
|
|
258
|
+
}
|
|
259
|
+
const globalForRoot =
|
|
260
|
+
startRoot && globalSuspenseHandlers.get(startRoot)
|
|
261
|
+
? globalSuspenseHandlers.get(startRoot)
|
|
262
|
+
: currentRoot
|
|
263
|
+
? globalSuspenseHandlers.get(currentRoot)
|
|
264
|
+
: undefined
|
|
265
|
+
if (globalForRoot && globalForRoot.length) {
|
|
266
|
+
for (let i = globalForRoot.length - 1; i >= 0; i--) {
|
|
267
|
+
const handler = globalForRoot[i]!
|
|
268
|
+
const handled = handler(token)
|
|
269
|
+
if (handled !== false) return true
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return false
|
|
273
|
+
}
|