@fictjs/runtime 0.16.0 → 0.17.1
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/dist/advanced.cjs +9 -9
- package/dist/advanced.d.cts +3 -3
- package/dist/advanced.d.ts +3 -3
- package/dist/advanced.js +4 -4
- package/dist/{binding-CQUGLLBI.d.ts → binding-BfzY9rae.d.ts} +2 -2
- package/dist/{binding-BlABuUiG.d.cts → binding-CDR2ERoq.d.cts} +2 -2
- package/dist/{chunk-CBRGOLTR.cjs → chunk-2J4INHDT.cjs} +40 -40
- package/dist/{chunk-CBRGOLTR.cjs.map → chunk-2J4INHDT.cjs.map} +1 -1
- package/dist/{chunk-BADX4WTQ.cjs → chunk-CKKZDUHM.cjs} +21 -18
- package/dist/chunk-CKKZDUHM.cjs.map +1 -0
- package/dist/{chunk-ZWQLXWSV.js → chunk-DHRRJJ6W.js} +8 -5
- package/dist/chunk-DHRRJJ6W.js.map +1 -0
- package/dist/{chunk-4P4DYWLQ.js → chunk-LFLFSJFU.js} +3 -3
- package/dist/{chunk-WJMZ7X46.cjs → chunk-NBDEMBBX.cjs} +47 -85
- package/dist/chunk-NBDEMBBX.cjs.map +1 -0
- package/dist/{chunk-MAHWGB55.js → chunk-OKPQWORE.js} +47 -85
- package/dist/chunk-OKPQWORE.js.map +1 -0
- package/dist/{chunk-RK2WSQYL.js → chunk-OLHZBAIF.js} +3 -3
- package/dist/{chunk-ZJZ6LMDN.js → chunk-R2HYEOP7.js} +470 -172
- package/dist/chunk-R2HYEOP7.js.map +1 -0
- package/dist/{chunk-AR2T7JEX.cjs → chunk-UG2IFQOY.cjs} +650 -352
- package/dist/chunk-UG2IFQOY.cjs.map +1 -0
- package/dist/{chunk-ECNK25S4.cjs → chunk-VP2WC7X3.cjs} +8 -8
- package/dist/{chunk-ECNK25S4.cjs.map → chunk-VP2WC7X3.cjs.map} +1 -1
- package/dist/{devtools-DWIZRe7L.d.cts → devtools-BwkkQ6DN.d.cts} +1 -1
- package/dist/{devtools-DNnnDGu1.d.ts → devtools-CK3SVU_w.d.ts} +1 -1
- package/dist/index.cjs +55 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.dev.js +260 -156
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +16 -3
- package/dist/index.js.map +1 -1
- package/dist/internal-list.cjs +4 -4
- package/dist/internal-list.js +3 -3
- package/dist/internal.cjs +5 -5
- package/dist/internal.d.cts +4 -4
- package/dist/internal.d.ts +4 -4
- package/dist/internal.js +4 -4
- package/dist/jsx-dev-runtime.cjs.map +1 -1
- package/dist/jsx-dev-runtime.d.cts +46 -0
- package/dist/jsx-dev-runtime.d.ts +46 -0
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.cjs.map +1 -1
- package/dist/jsx-runtime.d.cts +46 -0
- package/dist/jsx-runtime.d.ts +46 -0
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/loader.cjs +143 -26
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.d.cts +1 -1
- package/dist/loader.d.ts +1 -1
- package/dist/loader.js +122 -5
- package/dist/loader.js.map +1 -1
- package/dist/{props-DabFQwLR.d.ts → props-CFoQ471Y.d.ts} +47 -1
- package/dist/{props-tImUZAty.d.cts → props-D4tK8Gn0.d.cts} +47 -1
- package/dist/{resume-C5IKAIdh.d.ts → resume-C166aAVg.d.ts} +2 -2
- package/dist/{resume-DPZxmA95.d.cts → resume-C20cRVj9.d.cts} +2 -2
- package/dist/{scope-gpOMWTlf.d.ts → scope-BFzD_7hx.d.ts} +1 -1
- package/dist/{scope-GwC4DJ50.d.cts → scope-Ck3mTQVS.d.cts} +1 -1
- package/package.json +1 -1
- package/src/binding.ts +561 -166
- package/src/context.ts +8 -1
- package/src/dom.ts +26 -44
- package/src/effect.ts +9 -12
- package/src/error-boundary.ts +8 -0
- package/src/hydration.ts +25 -6
- package/src/jsx.ts +46 -0
- package/src/lifecycle.ts +31 -79
- package/src/loader.ts +153 -4
- package/src/resume.ts +5 -5
- package/src/signal.ts +4 -1
- package/src/suspense.ts +8 -0
- package/dist/chunk-AR2T7JEX.cjs.map +0 -1
- package/dist/chunk-BADX4WTQ.cjs.map +0 -1
- package/dist/chunk-MAHWGB55.js.map +0 -1
- package/dist/chunk-WJMZ7X46.cjs.map +0 -1
- package/dist/chunk-ZJZ6LMDN.js.map +0 -1
- package/dist/chunk-ZWQLXWSV.js.map +0 -1
- /package/dist/{chunk-4P4DYWLQ.js.map → chunk-LFLFSJFU.js.map} +0 -0
- /package/dist/{chunk-RK2WSQYL.js.map → chunk-OLHZBAIF.js.map} +0 -0
package/src/context.ts
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
type RootContext,
|
|
57
57
|
} from './lifecycle'
|
|
58
58
|
import { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'
|
|
59
|
+
import { untrack } from './signal'
|
|
59
60
|
import type { BaseProps, FictNode } from './types'
|
|
60
61
|
|
|
61
62
|
// ============================================================================
|
|
@@ -222,7 +223,13 @@ export function createContext<T>(defaultValue: T): Context<T> {
|
|
|
222
223
|
createRenderEffect(() => {
|
|
223
224
|
// Update context value on re-render (if value prop changes reactively)
|
|
224
225
|
contextMap.set(id, props.value)
|
|
225
|
-
|
|
226
|
+
|
|
227
|
+
// Provider value updates should not subscribe this effect to arbitrary
|
|
228
|
+
// signal reads that happen while rendering descendants. Child trees own
|
|
229
|
+
// their own reactivity; the provider only needs to react to its props.
|
|
230
|
+
untrack(() => {
|
|
231
|
+
renderChildren(props.children)
|
|
232
|
+
})
|
|
226
233
|
})
|
|
227
234
|
|
|
228
235
|
return fragment
|
package/src/dom.ts
CHANGED
|
@@ -19,7 +19,9 @@ import {
|
|
|
19
19
|
createClassBinding,
|
|
20
20
|
createChildBinding,
|
|
21
21
|
bindEvent,
|
|
22
|
+
bindRef,
|
|
22
23
|
isReactive,
|
|
24
|
+
registerCreateElement,
|
|
23
25
|
type MaybeReactive,
|
|
24
26
|
type AttributeSetter,
|
|
25
27
|
type BindingHandle,
|
|
@@ -27,7 +29,7 @@ import {
|
|
|
27
29
|
import { Properties, ChildProperties, getPropAlias, SVGElements, SVGNamespace } from './constants'
|
|
28
30
|
import { getDevtoolsHook } from './devtools'
|
|
29
31
|
import { __fictPushContext, __fictPopContext, __fictGetCurrentComponentId } from './hooks'
|
|
30
|
-
import { claimNodes, isHydratingActive, withHydration } from './hydration'
|
|
32
|
+
import { claimNodes, claimText, isHydratingActive, withHydration } from './hydration'
|
|
31
33
|
import { Fragment } from './jsx'
|
|
32
34
|
import {
|
|
33
35
|
createRootContext,
|
|
@@ -51,7 +53,7 @@ import {
|
|
|
51
53
|
__fictExitHydration,
|
|
52
54
|
} from './resume'
|
|
53
55
|
import { untrack } from './scheduler'
|
|
54
|
-
import type { DOMElement, FictNode, FictVNode
|
|
56
|
+
import type { DOMElement, FictNode, FictVNode } from './types'
|
|
55
57
|
|
|
56
58
|
type NamespaceContext = 'svg' | 'mathml' | null
|
|
57
59
|
|
|
@@ -214,6 +216,8 @@ export function createElement(node: FictNode): DOMElement {
|
|
|
214
216
|
return createElementWithContext(node, null, resolveOwnerDocument())
|
|
215
217
|
}
|
|
216
218
|
|
|
219
|
+
registerCreateElement(createElement)
|
|
220
|
+
|
|
217
221
|
function resolveNamespace(tagName: string, namespace: NamespaceContext): NamespaceContext {
|
|
218
222
|
if (tagName === 'svg') return 'svg'
|
|
219
223
|
if (tagName === 'math') return 'mathml'
|
|
@@ -227,6 +231,13 @@ function resolveOwnerDocument(ownerDocument?: Document): Document {
|
|
|
227
231
|
return ownerDocument ?? getCurrentRoot()?.ownerDocument ?? document
|
|
228
232
|
}
|
|
229
233
|
|
|
234
|
+
function createTextNodeWithHydration(value: string, ownerDocument: Document): Text {
|
|
235
|
+
if (!isHydratingActive()) {
|
|
236
|
+
return ownerDocument.createTextNode(value)
|
|
237
|
+
}
|
|
238
|
+
return claimText(value, () => ownerDocument.createTextNode(value))
|
|
239
|
+
}
|
|
240
|
+
|
|
230
241
|
function createElementWithContext(
|
|
231
242
|
node: FictNode,
|
|
232
243
|
namespace: NamespaceContext,
|
|
@@ -239,14 +250,14 @@ function createElementWithContext(
|
|
|
239
250
|
|
|
240
251
|
// Null/undefined/false - empty placeholder
|
|
241
252
|
if (node === null || node === undefined || node === false) {
|
|
242
|
-
return
|
|
253
|
+
return createTextNodeWithHydration('', ownerDocument)
|
|
243
254
|
}
|
|
244
255
|
|
|
245
256
|
// Reactive getter function - resolve to actual node
|
|
246
257
|
if (isReactive(node)) {
|
|
247
258
|
const resolved = (node as () => FictNode)()
|
|
248
259
|
if (resolved === node) {
|
|
249
|
-
return
|
|
260
|
+
return createTextNodeWithHydration('', ownerDocument)
|
|
250
261
|
}
|
|
251
262
|
return createElementWithContext(resolved, namespace, ownerDocument)
|
|
252
263
|
}
|
|
@@ -254,7 +265,7 @@ function createElementWithContext(
|
|
|
254
265
|
// Non-reactive function values are not valid DOM nodes.
|
|
255
266
|
// Keep callback values inert instead of stringifying function source.
|
|
256
267
|
if (typeof node === 'function') {
|
|
257
|
-
return
|
|
268
|
+
return createTextNodeWithHydration('', ownerDocument)
|
|
258
269
|
}
|
|
259
270
|
|
|
260
271
|
if (typeof node === 'object' && node !== null && !(node instanceof Node)) {
|
|
@@ -290,11 +301,11 @@ function createElementWithContext(
|
|
|
290
301
|
|
|
291
302
|
// Primitive values - text node
|
|
292
303
|
if (typeof node === 'string' || typeof node === 'number') {
|
|
293
|
-
return
|
|
304
|
+
return createTextNodeWithHydration(String(node), ownerDocument)
|
|
294
305
|
}
|
|
295
306
|
|
|
296
307
|
if (typeof node === 'boolean') {
|
|
297
|
-
return
|
|
308
|
+
return createTextNodeWithHydration('', ownerDocument)
|
|
298
309
|
}
|
|
299
310
|
|
|
300
311
|
// VNode
|
|
@@ -615,7 +626,7 @@ function appendChildNode(
|
|
|
615
626
|
// Cast to Node for remaining logic
|
|
616
627
|
let domNode: Node
|
|
617
628
|
if (typeof child !== 'object' || child === null) {
|
|
618
|
-
domNode =
|
|
629
|
+
domNode = createTextNodeWithHydration(String(child ?? ''), parentOwnerDocument)
|
|
619
630
|
} else {
|
|
620
631
|
domNode = createElementWithContext(child as any, namespace, parentOwnerDocument) as Node
|
|
621
632
|
}
|
|
@@ -675,43 +686,14 @@ function appendChildren(
|
|
|
675
686
|
* Both types are automatically cleaned up on unmount.
|
|
676
687
|
*/
|
|
677
688
|
function applyRef(el: Element, value: unknown): void {
|
|
678
|
-
if (
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const root = getCurrentRoot()
|
|
685
|
-
if (root) {
|
|
686
|
-
registerRootCleanup(() => {
|
|
687
|
-
refFn(null)
|
|
688
|
-
})
|
|
689
|
-
} else if (isDev) {
|
|
690
|
-
console.warn(
|
|
691
|
-
'[fict] Ref applied outside of a root context. ' +
|
|
692
|
-
'The ref cleanup (setting to null) will not run automatically. ' +
|
|
693
|
-
'Consider using createRoot() or ensure the element is created within a component.',
|
|
694
|
-
)
|
|
695
|
-
}
|
|
696
|
-
} else if (value && typeof value === 'object' && 'current' in value) {
|
|
697
|
-
// Object ref
|
|
698
|
-
const refObj = value as RefObject<Element>
|
|
699
|
-
refObj.current = el
|
|
700
|
-
|
|
701
|
-
// Auto-cleanup on unmount
|
|
702
|
-
const root = getCurrentRoot()
|
|
703
|
-
if (root) {
|
|
704
|
-
registerRootCleanup(() => {
|
|
705
|
-
refObj.current = null
|
|
706
|
-
})
|
|
707
|
-
} else if (isDev) {
|
|
708
|
-
console.warn(
|
|
709
|
-
'[fict] Ref applied outside of a root context. ' +
|
|
710
|
-
'The ref cleanup (setting to null) will not run automatically. ' +
|
|
711
|
-
'Consider using createRoot() or ensure the element is created within a component.',
|
|
712
|
-
)
|
|
713
|
-
}
|
|
689
|
+
if (!getCurrentRoot() && isDev) {
|
|
690
|
+
console.warn(
|
|
691
|
+
'[fict] Ref applied outside of a root context. ' +
|
|
692
|
+
'The ref cleanup (setting to null) will not run automatically. ' +
|
|
693
|
+
'Consider using createRoot() or ensure the element is created within a component.',
|
|
694
|
+
)
|
|
714
695
|
}
|
|
696
|
+
bindRef(el, value)
|
|
715
697
|
}
|
|
716
698
|
|
|
717
699
|
// ============================================================================
|
package/src/effect.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function createEffect(fn: Effect, options?: EffectOptions): () => void {
|
|
|
21
21
|
|
|
22
22
|
// Cleanup runner - called by runEffect BEFORE signal values are committed
|
|
23
23
|
const doCleanup = () => {
|
|
24
|
-
runCleanupList(cleanups)
|
|
24
|
+
runCleanupList(cleanups, rootForError)
|
|
25
25
|
cleanups = []
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -49,7 +49,7 @@ export function createEffect(fn: Effect, options?: EffectOptions): () => void {
|
|
|
49
49
|
|
|
50
50
|
const disposeEffect = effectWithCleanup(run, doCleanup, rootForError, options)
|
|
51
51
|
const teardown = () => {
|
|
52
|
-
runCleanupList(cleanups)
|
|
52
|
+
runCleanupList(cleanups, rootForError)
|
|
53
53
|
disposeEffect()
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -61,15 +61,13 @@ export function createEffect(fn: Effect, options?: EffectOptions): () => void {
|
|
|
61
61
|
export const $effect = createEffect
|
|
62
62
|
|
|
63
63
|
export function createRenderEffect(fn: Effect, options?: EffectOptions): () => void {
|
|
64
|
-
let
|
|
64
|
+
let cleanups: Cleanup[] = []
|
|
65
65
|
const rootForError = getCurrentRoot()
|
|
66
66
|
|
|
67
67
|
// Cleanup runner - called by runEffect BEFORE signal values are committed
|
|
68
68
|
const doCleanup = () => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
cleanup = undefined
|
|
72
|
-
}
|
|
69
|
+
runCleanupList(cleanups, rootForError)
|
|
70
|
+
cleanups = []
|
|
73
71
|
}
|
|
74
72
|
|
|
75
73
|
const run = () => {
|
|
@@ -77,7 +75,9 @@ export function createRenderEffect(fn: Effect, options?: EffectOptions): () => v
|
|
|
77
75
|
try {
|
|
78
76
|
const maybeCleanup = fn()
|
|
79
77
|
if (typeof maybeCleanup === 'function') {
|
|
80
|
-
|
|
78
|
+
cleanups = [maybeCleanup]
|
|
79
|
+
} else {
|
|
80
|
+
cleanups = []
|
|
81
81
|
}
|
|
82
82
|
} catch (err) {
|
|
83
83
|
if (handleSuspend(err as any, rootForError)) {
|
|
@@ -93,10 +93,7 @@ export function createRenderEffect(fn: Effect, options?: EffectOptions): () => v
|
|
|
93
93
|
|
|
94
94
|
const disposeEffect = effectWithCleanup(run, doCleanup, rootForError, options)
|
|
95
95
|
const teardown = () => {
|
|
96
|
-
|
|
97
|
-
cleanup()
|
|
98
|
-
cleanup = undefined
|
|
99
|
-
}
|
|
96
|
+
runCleanupList(cleanups, rootForError)
|
|
100
97
|
disposeEffect()
|
|
101
98
|
}
|
|
102
99
|
|
package/src/error-boundary.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
pushRoot,
|
|
9
9
|
popRoot,
|
|
10
10
|
registerErrorHandler,
|
|
11
|
+
registerRootCleanup,
|
|
11
12
|
} from './lifecycle'
|
|
12
13
|
import { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'
|
|
13
14
|
import type { BaseProps, FictNode } from './types'
|
|
@@ -105,6 +106,13 @@ export function ErrorBoundary(props: ErrorBoundaryProps): FictNode {
|
|
|
105
106
|
|
|
106
107
|
renderValue(props.children ?? null)
|
|
107
108
|
|
|
109
|
+
registerRootCleanup(() => {
|
|
110
|
+
if (cleanup) {
|
|
111
|
+
cleanup()
|
|
112
|
+
cleanup = undefined
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
108
116
|
registerErrorHandler(err => {
|
|
109
117
|
renderValue(toView(err))
|
|
110
118
|
props.onError?.(err)
|
package/src/hydration.ts
CHANGED
|
@@ -6,7 +6,7 @@ interface HydrationContext {
|
|
|
6
6
|
|
|
7
7
|
const hydrationStack: HydrationContext[] = []
|
|
8
8
|
|
|
9
|
-
export function withHydration(root: ParentNode & Node, fn: () =>
|
|
9
|
+
export function withHydration<T>(root: ParentNode & Node, fn: () => T): T {
|
|
10
10
|
const owner = root.ownerDocument ?? document
|
|
11
11
|
hydrationStack.push({
|
|
12
12
|
cursor: root.firstChild,
|
|
@@ -14,25 +14,25 @@ export function withHydration(root: ParentNode & Node, fn: () => void): void {
|
|
|
14
14
|
owner,
|
|
15
15
|
})
|
|
16
16
|
try {
|
|
17
|
-
fn()
|
|
17
|
+
return fn()
|
|
18
18
|
} finally {
|
|
19
19
|
hydrationStack.pop()
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export function withHydrationRange(
|
|
23
|
+
export function withHydrationRange<T>(
|
|
24
24
|
start: Node | null,
|
|
25
25
|
end: Node | null,
|
|
26
26
|
owner: Document,
|
|
27
|
-
fn: () =>
|
|
28
|
-
):
|
|
27
|
+
fn: () => T,
|
|
28
|
+
): T {
|
|
29
29
|
hydrationStack.push({
|
|
30
30
|
cursor: start,
|
|
31
31
|
boundary: end,
|
|
32
32
|
owner,
|
|
33
33
|
})
|
|
34
34
|
try {
|
|
35
|
-
fn()
|
|
35
|
+
return fn()
|
|
36
36
|
} finally {
|
|
37
37
|
hydrationStack.pop()
|
|
38
38
|
}
|
|
@@ -70,6 +70,25 @@ export function claimNodes(templateRoot: Node, fallback: () => Node): Node {
|
|
|
70
70
|
return frag
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export function claimText(value: string, fallback: () => Text): Text {
|
|
74
|
+
const ctx = hydrationStack[hydrationStack.length - 1]
|
|
75
|
+
if (
|
|
76
|
+
!ctx ||
|
|
77
|
+
!ctx.cursor ||
|
|
78
|
+
ctx.cursor === ctx.boundary ||
|
|
79
|
+
ctx.cursor.nodeType !== Node.TEXT_NODE
|
|
80
|
+
) {
|
|
81
|
+
return fallback()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const text = ctx.cursor as Text
|
|
85
|
+
ctx.cursor = text.nextSibling
|
|
86
|
+
if (text.data !== value) {
|
|
87
|
+
text.data = value
|
|
88
|
+
}
|
|
89
|
+
return text
|
|
90
|
+
}
|
|
91
|
+
|
|
73
92
|
export function isHydratingActive(): boolean {
|
|
74
93
|
return hydrationStack.length > 0
|
|
75
94
|
}
|
package/src/jsx.ts
CHANGED
|
@@ -207,59 +207,105 @@ interface HTMLAttributes<T> {
|
|
|
207
207
|
|
|
208
208
|
// Event handlers
|
|
209
209
|
onClick?: (e: MouseEvent) => void
|
|
210
|
+
onClick$?: (e: MouseEvent) => void
|
|
210
211
|
onDblClick?: (e: MouseEvent) => void
|
|
212
|
+
onDblClick$?: (e: MouseEvent) => void
|
|
211
213
|
onMouseDown?: (e: MouseEvent) => void
|
|
214
|
+
onMouseDown$?: (e: MouseEvent) => void
|
|
212
215
|
onMouseUp?: (e: MouseEvent) => void
|
|
216
|
+
onMouseUp$?: (e: MouseEvent) => void
|
|
213
217
|
onMouseMove?: (e: MouseEvent) => void
|
|
218
|
+
onMouseMove$?: (e: MouseEvent) => void
|
|
214
219
|
onMouseEnter?: (e: MouseEvent) => void
|
|
220
|
+
onMouseEnter$?: (e: MouseEvent) => void
|
|
215
221
|
onMouseLeave?: (e: MouseEvent) => void
|
|
222
|
+
onMouseLeave$?: (e: MouseEvent) => void
|
|
216
223
|
onMouseOver?: (e: MouseEvent) => void
|
|
224
|
+
onMouseOver$?: (e: MouseEvent) => void
|
|
217
225
|
onMouseOut?: (e: MouseEvent) => void
|
|
226
|
+
onMouseOut$?: (e: MouseEvent) => void
|
|
218
227
|
onContextMenu?: (e: MouseEvent) => void
|
|
228
|
+
onContextMenu$?: (e: MouseEvent) => void
|
|
219
229
|
onInput?: (e: InputEvent) => void
|
|
230
|
+
onInput$?: (e: InputEvent) => void
|
|
220
231
|
onChange?: (e: Event) => void
|
|
232
|
+
onChange$?: (e: Event) => void
|
|
221
233
|
onSubmit?: (e: SubmitEvent) => void
|
|
234
|
+
onSubmit$?: (e: SubmitEvent) => void
|
|
222
235
|
onReset?: (e: Event) => void
|
|
236
|
+
onReset$?: (e: Event) => void
|
|
223
237
|
onKeyDown?: (e: KeyboardEvent) => void
|
|
238
|
+
onKeyDown$?: (e: KeyboardEvent) => void
|
|
224
239
|
onKeyUp?: (e: KeyboardEvent) => void
|
|
240
|
+
onKeyUp$?: (e: KeyboardEvent) => void
|
|
225
241
|
onKeyPress?: (e: KeyboardEvent) => void
|
|
242
|
+
onKeyPress$?: (e: KeyboardEvent) => void
|
|
226
243
|
onFocus?: (e: FocusEvent) => void
|
|
244
|
+
onFocus$?: (e: FocusEvent) => void
|
|
227
245
|
onBlur?: (e: FocusEvent) => void
|
|
246
|
+
onBlur$?: (e: FocusEvent) => void
|
|
228
247
|
onScroll?: (e: Event) => void
|
|
248
|
+
onScroll$?: (e: Event) => void
|
|
229
249
|
onWheel?: (e: WheelEvent) => void
|
|
250
|
+
onWheel$?: (e: WheelEvent) => void
|
|
230
251
|
onLoad?: (e: Event) => void
|
|
252
|
+
onLoad$?: (e: Event) => void
|
|
231
253
|
onError?: (e: Event) => void
|
|
254
|
+
onError$?: (e: Event) => void
|
|
232
255
|
|
|
233
256
|
// Drag events
|
|
234
257
|
onDrag?: (e: DragEvent) => void
|
|
258
|
+
onDrag$?: (e: DragEvent) => void
|
|
235
259
|
onDragStart?: (e: DragEvent) => void
|
|
260
|
+
onDragStart$?: (e: DragEvent) => void
|
|
236
261
|
onDragEnd?: (e: DragEvent) => void
|
|
262
|
+
onDragEnd$?: (e: DragEvent) => void
|
|
237
263
|
onDragEnter?: (e: DragEvent) => void
|
|
264
|
+
onDragEnter$?: (e: DragEvent) => void
|
|
238
265
|
onDragLeave?: (e: DragEvent) => void
|
|
266
|
+
onDragLeave$?: (e: DragEvent) => void
|
|
239
267
|
onDragOver?: (e: DragEvent) => void
|
|
268
|
+
onDragOver$?: (e: DragEvent) => void
|
|
240
269
|
onDrop?: (e: DragEvent) => void
|
|
270
|
+
onDrop$?: (e: DragEvent) => void
|
|
241
271
|
|
|
242
272
|
// Touch events
|
|
243
273
|
onTouchStart?: (e: TouchEvent) => void
|
|
274
|
+
onTouchStart$?: (e: TouchEvent) => void
|
|
244
275
|
onTouchMove?: (e: TouchEvent) => void
|
|
276
|
+
onTouchMove$?: (e: TouchEvent) => void
|
|
245
277
|
onTouchEnd?: (e: TouchEvent) => void
|
|
278
|
+
onTouchEnd$?: (e: TouchEvent) => void
|
|
246
279
|
onTouchCancel?: (e: TouchEvent) => void
|
|
280
|
+
onTouchCancel$?: (e: TouchEvent) => void
|
|
247
281
|
|
|
248
282
|
// Animation events
|
|
249
283
|
onAnimationStart?: (e: AnimationEvent) => void
|
|
284
|
+
onAnimationStart$?: (e: AnimationEvent) => void
|
|
250
285
|
onAnimationEnd?: (e: AnimationEvent) => void
|
|
286
|
+
onAnimationEnd$?: (e: AnimationEvent) => void
|
|
251
287
|
onAnimationIteration?: (e: AnimationEvent) => void
|
|
288
|
+
onAnimationIteration$?: (e: AnimationEvent) => void
|
|
252
289
|
onTransitionEnd?: (e: TransitionEvent) => void
|
|
290
|
+
onTransitionEnd$?: (e: TransitionEvent) => void
|
|
253
291
|
|
|
254
292
|
// Pointer events
|
|
255
293
|
onPointerDown?: (e: PointerEvent) => void
|
|
294
|
+
onPointerDown$?: (e: PointerEvent) => void
|
|
256
295
|
onPointerUp?: (e: PointerEvent) => void
|
|
296
|
+
onPointerUp$?: (e: PointerEvent) => void
|
|
257
297
|
onPointerMove?: (e: PointerEvent) => void
|
|
298
|
+
onPointerMove$?: (e: PointerEvent) => void
|
|
258
299
|
onPointerEnter?: (e: PointerEvent) => void
|
|
300
|
+
onPointerEnter$?: (e: PointerEvent) => void
|
|
259
301
|
onPointerLeave?: (e: PointerEvent) => void
|
|
302
|
+
onPointerLeave$?: (e: PointerEvent) => void
|
|
260
303
|
onPointerOver?: (e: PointerEvent) => void
|
|
304
|
+
onPointerOver$?: (e: PointerEvent) => void
|
|
261
305
|
onPointerOut?: (e: PointerEvent) => void
|
|
306
|
+
onPointerOut$?: (e: PointerEvent) => void
|
|
262
307
|
onPointerCancel?: (e: PointerEvent) => void
|
|
308
|
+
onPointerCancel$?: (e: PointerEvent) => void
|
|
263
309
|
|
|
264
310
|
// Ref
|
|
265
311
|
ref?: ((el: T | null) => void) | { current: T | null }
|
package/src/lifecycle.ts
CHANGED
|
@@ -29,8 +29,6 @@ type SuspenseHandler = (token: SuspenseToken | PromiseLike<unknown>) => boolean
|
|
|
29
29
|
|
|
30
30
|
let currentRoot: RootContext | undefined
|
|
31
31
|
let currentEffectCleanups: Cleanup[] | undefined
|
|
32
|
-
const globalErrorHandlers = new WeakMap<RootContext, ErrorHandler[]>()
|
|
33
|
-
const globalSuspenseHandlers = new WeakMap<RootContext, SuspenseHandler[]>()
|
|
34
32
|
const rootDevtoolsIds = new WeakMap<RootContext, number>()
|
|
35
33
|
let nextRootDevtoolsId = 0
|
|
36
34
|
|
|
@@ -115,20 +113,28 @@ export function onCleanup(fn: Cleanup): void {
|
|
|
115
113
|
export function flushOnMount(root: RootContext): void {
|
|
116
114
|
const cbs = root.onMountCallbacks
|
|
117
115
|
if (!cbs || cbs.length === 0) return
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
try {
|
|
117
|
+
withRootContext(root, () => {
|
|
118
|
+
for (let i = 0; i < cbs.length; i++) {
|
|
119
|
+
const cleanup = cbs[i]!()
|
|
120
|
+
if (typeof cleanup === 'function') {
|
|
121
|
+
root.cleanups.push(cleanup)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
} finally {
|
|
126
|
+
cbs.length = 0
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function withRootContext<T>(root: RootContext | undefined, fn: () => T): T {
|
|
131
|
+
if (!root) return fn()
|
|
120
132
|
const prevRoot = currentRoot
|
|
121
133
|
currentRoot = root
|
|
122
134
|
try {
|
|
123
|
-
|
|
124
|
-
const cleanup = cbs[i]!()
|
|
125
|
-
if (typeof cleanup === 'function') {
|
|
126
|
-
root.cleanups.push(cleanup)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
135
|
+
return fn()
|
|
129
136
|
} finally {
|
|
130
137
|
currentRoot = prevRoot
|
|
131
|
-
cbs.length = 0
|
|
132
138
|
}
|
|
133
139
|
}
|
|
134
140
|
|
|
@@ -139,7 +145,7 @@ export function registerRootCleanup(fn: Cleanup): void {
|
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
export function clearRoot(root: RootContext): void {
|
|
142
|
-
runCleanupList(root.cleanups)
|
|
148
|
+
runCleanupList(root.cleanups, root)
|
|
143
149
|
if (root.onMountCallbacks) {
|
|
144
150
|
root.onMountCallbacks.length = 0
|
|
145
151
|
}
|
|
@@ -147,19 +153,13 @@ export function clearRoot(root: RootContext): void {
|
|
|
147
153
|
|
|
148
154
|
export function destroyRoot(root: RootContext): void {
|
|
149
155
|
clearRoot(root)
|
|
150
|
-
runCleanupList(root.destroyCallbacks)
|
|
156
|
+
runCleanupList(root.destroyCallbacks, root)
|
|
151
157
|
if (root.errorHandlers) {
|
|
152
158
|
root.errorHandlers.length = 0
|
|
153
159
|
}
|
|
154
|
-
if (globalErrorHandlers.has(root)) {
|
|
155
|
-
globalErrorHandlers.delete(root)
|
|
156
|
-
}
|
|
157
160
|
if (root.suspenseHandlers) {
|
|
158
161
|
root.suspenseHandlers.length = 0
|
|
159
162
|
}
|
|
160
|
-
if (globalSuspenseHandlers.has(root)) {
|
|
161
|
-
globalSuspenseHandlers.delete(root)
|
|
162
|
-
}
|
|
163
163
|
disposeRootDevtools(root)
|
|
164
164
|
}
|
|
165
165
|
|
|
@@ -201,21 +201,23 @@ export function registerEffectCleanup(fn: Cleanup): void {
|
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
export function runCleanupList(list: Cleanup[]): void {
|
|
204
|
+
export function runCleanupList(list: Cleanup[], root?: RootContext): void {
|
|
205
205
|
let error: unknown
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
error
|
|
206
|
+
withRootContext(root, () => {
|
|
207
|
+
for (let i = list.length - 1; i >= 0; i--) {
|
|
208
|
+
try {
|
|
209
|
+
const cleanup = list[i]
|
|
210
|
+
if (cleanup) cleanup()
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (error === undefined) {
|
|
213
|
+
error = err
|
|
214
|
+
}
|
|
213
215
|
}
|
|
214
216
|
}
|
|
215
|
-
}
|
|
217
|
+
})
|
|
216
218
|
list.length = 0
|
|
217
219
|
if (error !== undefined) {
|
|
218
|
-
if (!handleError(error, { source: 'cleanup' })) {
|
|
220
|
+
if (!handleError(error, { source: 'cleanup' }, root)) {
|
|
219
221
|
throw error
|
|
220
222
|
}
|
|
221
223
|
}
|
|
@@ -239,12 +241,6 @@ export function registerErrorHandler(fn: ErrorHandler): void {
|
|
|
239
241
|
currentRoot.errorHandlers = []
|
|
240
242
|
}
|
|
241
243
|
currentRoot.errorHandlers.push(fn)
|
|
242
|
-
const existing = globalErrorHandlers.get(currentRoot)
|
|
243
|
-
if (existing) {
|
|
244
|
-
existing.push(fn)
|
|
245
|
-
} else {
|
|
246
|
-
globalErrorHandlers.set(currentRoot, [fn])
|
|
247
|
-
}
|
|
248
244
|
}
|
|
249
245
|
|
|
250
246
|
export function registerSuspenseHandler(fn: SuspenseHandler): void {
|
|
@@ -258,12 +254,6 @@ export function registerSuspenseHandler(fn: SuspenseHandler): void {
|
|
|
258
254
|
currentRoot.suspenseHandlers = []
|
|
259
255
|
}
|
|
260
256
|
currentRoot.suspenseHandlers.push(fn)
|
|
261
|
-
const existing = globalSuspenseHandlers.get(currentRoot)
|
|
262
|
-
if (existing) {
|
|
263
|
-
existing.push(fn)
|
|
264
|
-
} else {
|
|
265
|
-
globalSuspenseHandlers.set(currentRoot, [fn])
|
|
266
|
-
}
|
|
267
257
|
}
|
|
268
258
|
|
|
269
259
|
export function handleError(err: unknown, info?: ErrorInfo, startRoot?: RootContext): boolean {
|
|
@@ -286,24 +276,6 @@ export function handleError(err: unknown, info?: ErrorInfo, startRoot?: RootCont
|
|
|
286
276
|
}
|
|
287
277
|
root = root.parent
|
|
288
278
|
}
|
|
289
|
-
const globalForRoot = startRoot
|
|
290
|
-
? globalErrorHandlers.get(startRoot)
|
|
291
|
-
: currentRoot
|
|
292
|
-
? globalErrorHandlers.get(currentRoot)
|
|
293
|
-
: undefined
|
|
294
|
-
if (globalForRoot && globalForRoot.length) {
|
|
295
|
-
for (let i = globalForRoot.length - 1; i >= 0; i--) {
|
|
296
|
-
const handler = globalForRoot[i]!
|
|
297
|
-
try {
|
|
298
|
-
const handled = handler(error, info)
|
|
299
|
-
if (handled !== false) {
|
|
300
|
-
return true
|
|
301
|
-
}
|
|
302
|
-
} catch (nextErr) {
|
|
303
|
-
error = nextErr
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
279
|
// The caller (e.g., runCleanupList) can decide whether to rethrow.
|
|
308
280
|
// This makes the API consistent: handleError always returns a boolean
|
|
309
281
|
// indicating whether the error was handled.
|
|
@@ -334,25 +306,5 @@ export function handleSuspend(
|
|
|
334
306
|
}
|
|
335
307
|
root = root.parent
|
|
336
308
|
}
|
|
337
|
-
const globalForRoot =
|
|
338
|
-
startRoot && globalSuspenseHandlers.get(startRoot)
|
|
339
|
-
? globalSuspenseHandlers.get(startRoot)
|
|
340
|
-
: currentRoot
|
|
341
|
-
? globalSuspenseHandlers.get(currentRoot)
|
|
342
|
-
: undefined
|
|
343
|
-
if (globalForRoot && globalForRoot.length) {
|
|
344
|
-
for (let i = globalForRoot.length - 1; i >= 0; i--) {
|
|
345
|
-
const handler = globalForRoot[i]!
|
|
346
|
-
const handled = handler(token)
|
|
347
|
-
if (handled !== false) {
|
|
348
|
-
// Only set suspended = true when a handler actually handles the token
|
|
349
|
-
if (originRoot) {
|
|
350
|
-
originRoot.suspended = true
|
|
351
|
-
setRootSuspendDevtools(originRoot, true)
|
|
352
|
-
}
|
|
353
|
-
return true
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
309
|
return false
|
|
358
310
|
}
|