@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.
Files changed (51) hide show
  1. package/README.md +17 -0
  2. package/dist/index.cjs +4224 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +1572 -0
  5. package/dist/index.d.ts +1572 -0
  6. package/dist/index.dev.js +4240 -0
  7. package/dist/index.dev.js.map +1 -0
  8. package/dist/index.js +4133 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/jsx-dev-runtime.cjs +44 -0
  11. package/dist/jsx-dev-runtime.cjs.map +1 -0
  12. package/dist/jsx-dev-runtime.js +14 -0
  13. package/dist/jsx-dev-runtime.js.map +1 -0
  14. package/dist/jsx-runtime.cjs +44 -0
  15. package/dist/jsx-runtime.cjs.map +1 -0
  16. package/dist/jsx-runtime.js +14 -0
  17. package/dist/jsx-runtime.js.map +1 -0
  18. package/dist/slim.cjs +3384 -0
  19. package/dist/slim.cjs.map +1 -0
  20. package/dist/slim.d.cts +475 -0
  21. package/dist/slim.d.ts +475 -0
  22. package/dist/slim.js +3335 -0
  23. package/dist/slim.js.map +1 -0
  24. package/package.json +68 -0
  25. package/src/binding.ts +2127 -0
  26. package/src/constants.ts +456 -0
  27. package/src/cycle-guard.ts +134 -0
  28. package/src/devtools.ts +17 -0
  29. package/src/dom.ts +683 -0
  30. package/src/effect.ts +83 -0
  31. package/src/error-boundary.ts +118 -0
  32. package/src/hooks.ts +72 -0
  33. package/src/index.ts +184 -0
  34. package/src/jsx-dev-runtime.ts +2 -0
  35. package/src/jsx-runtime.ts +2 -0
  36. package/src/jsx.ts +786 -0
  37. package/src/lifecycle.ts +273 -0
  38. package/src/list-helpers.ts +619 -0
  39. package/src/memo.ts +14 -0
  40. package/src/node-ops.ts +185 -0
  41. package/src/props.ts +212 -0
  42. package/src/reconcile.ts +151 -0
  43. package/src/ref.ts +25 -0
  44. package/src/scheduler.ts +12 -0
  45. package/src/signal.ts +1278 -0
  46. package/src/slim.ts +68 -0
  47. package/src/store.ts +210 -0
  48. package/src/suspense.ts +187 -0
  49. package/src/transition.ts +128 -0
  50. package/src/types.ts +172 -0
  51. package/src/versioned-signal.ts +58 -0
@@ -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
+ }