@fictjs/runtime 0.0.8 → 0.0.10
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/index.cjs +2542 -2457
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -26
- package/dist/index.d.ts +30 -26
- package/dist/index.dev.js +2583 -2495
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +2542 -2457
- package/dist/index.js.map +1 -1
- package/dist/slim.cjs +2123 -1988
- package/dist/slim.cjs.map +1 -1
- package/dist/slim.d.cts +13 -11
- package/dist/slim.d.ts +13 -11
- package/dist/slim.js +2123 -1988
- package/dist/slim.js.map +1 -1
- package/package.json +1 -1
- package/src/binding.ts +115 -92
- package/src/dom.ts +90 -38
- package/src/effect.ts +2 -1
- package/src/error-boundary.ts +3 -1
- package/src/hooks.ts +14 -1
- package/src/list-helpers.ts +141 -50
- package/src/ref.ts +1 -1
- package/src/signal.ts +16 -2
- package/src/store.ts +30 -2
- package/src/types.ts +3 -3
package/src/dom.ts
CHANGED
|
@@ -25,7 +25,14 @@ import {
|
|
|
25
25
|
type AttributeSetter,
|
|
26
26
|
type BindingHandle,
|
|
27
27
|
} from './binding'
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
Properties,
|
|
30
|
+
ChildProperties,
|
|
31
|
+
Aliases,
|
|
32
|
+
getPropAlias,
|
|
33
|
+
SVGElements,
|
|
34
|
+
SVGNamespace,
|
|
35
|
+
} from './constants'
|
|
29
36
|
import { __fictPushContext, __fictPopContext } from './hooks'
|
|
30
37
|
import { Fragment } from './jsx'
|
|
31
38
|
import {
|
|
@@ -43,6 +50,11 @@ import { createPropsProxy, unwrapProps } from './props'
|
|
|
43
50
|
import { untrack } from './scheduler'
|
|
44
51
|
import type { DOMElement, FictNode, FictVNode, RefObject } from './types'
|
|
45
52
|
|
|
53
|
+
type NamespaceContext = 'svg' | 'mathml' | null
|
|
54
|
+
|
|
55
|
+
const SVG_NS = 'http://www.w3.org/2000/svg'
|
|
56
|
+
const MATHML_NS = 'http://www.w3.org/1998/Math/MathML'
|
|
57
|
+
|
|
46
58
|
// ============================================================================
|
|
47
59
|
// Main Render Function
|
|
48
60
|
// ============================================================================
|
|
@@ -104,6 +116,19 @@ export function render(view: () => FictNode, container: HTMLElement): () => void
|
|
|
104
116
|
* - Reactive values (functions returning any of the above)
|
|
105
117
|
*/
|
|
106
118
|
export function createElement(node: FictNode): DOMElement {
|
|
119
|
+
return createElementWithContext(node, null)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function resolveNamespace(tagName: string, namespace: NamespaceContext): NamespaceContext {
|
|
123
|
+
if (tagName === 'svg') return 'svg'
|
|
124
|
+
if (tagName === 'math') return 'mathml'
|
|
125
|
+
if (namespace === 'mathml') return 'mathml'
|
|
126
|
+
if (namespace === 'svg') return 'svg'
|
|
127
|
+
if (SVGElements.has(tagName)) return 'svg'
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function createElementWithContext(node: FictNode, namespace: NamespaceContext): DOMElement {
|
|
107
132
|
// Already a DOM node - pass through
|
|
108
133
|
if (node instanceof Node) {
|
|
109
134
|
return node
|
|
@@ -118,7 +143,22 @@ export function createElement(node: FictNode): DOMElement {
|
|
|
118
143
|
if (typeof node === 'object' && node !== null && !(node instanceof Node)) {
|
|
119
144
|
// Handle BindingHandle (createList, createConditional, etc)
|
|
120
145
|
if ('marker' in node) {
|
|
121
|
-
|
|
146
|
+
const handle = node as { marker: unknown; dispose?: () => void; flush?: () => void }
|
|
147
|
+
// Register dispose cleanup if available
|
|
148
|
+
if (typeof handle.dispose === 'function') {
|
|
149
|
+
registerRootCleanup(handle.dispose)
|
|
150
|
+
}
|
|
151
|
+
if (typeof handle.flush === 'function') {
|
|
152
|
+
const runFlush = () => handle.flush && handle.flush()
|
|
153
|
+
if (typeof queueMicrotask === 'function') {
|
|
154
|
+
queueMicrotask(runFlush)
|
|
155
|
+
} else {
|
|
156
|
+
Promise.resolve()
|
|
157
|
+
.then(runFlush)
|
|
158
|
+
.catch(() => undefined)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return createElement(handle.marker as FictNode)
|
|
122
162
|
}
|
|
123
163
|
|
|
124
164
|
const nodeRecord = node as unknown as Record<PropertyKey, unknown>
|
|
@@ -134,7 +174,7 @@ export function createElement(node: FictNode): DOMElement {
|
|
|
134
174
|
if (Array.isArray(node)) {
|
|
135
175
|
const frag = document.createDocumentFragment()
|
|
136
176
|
for (const child of node) {
|
|
137
|
-
appendChildNode(frag, child)
|
|
177
|
+
appendChildNode(frag, child, namespace)
|
|
138
178
|
}
|
|
139
179
|
return frag
|
|
140
180
|
}
|
|
@@ -180,20 +220,20 @@ export function createElement(node: FictNode): DOMElement {
|
|
|
180
220
|
})
|
|
181
221
|
|
|
182
222
|
const props = createPropsProxy(baseProps)
|
|
223
|
+
// Create a fresh hook context for this component instance.
|
|
224
|
+
// This preserves slot state across re-renders driven by __fictRender.
|
|
225
|
+
__fictPushContext()
|
|
183
226
|
try {
|
|
184
|
-
// Create a fresh hook context for this component instance.
|
|
185
|
-
// This preserves slot state across re-renders driven by __fictRender.
|
|
186
|
-
__fictPushContext()
|
|
187
227
|
const rendered = vnode.type(props)
|
|
188
|
-
|
|
189
|
-
return createElement(rendered as FictNode)
|
|
228
|
+
return createElementWithContext(rendered as FictNode, namespace)
|
|
190
229
|
} catch (err) {
|
|
191
|
-
__fictPopContext()
|
|
192
230
|
if (handleSuspend(err as any)) {
|
|
193
231
|
return document.createComment('fict:suspend')
|
|
194
232
|
}
|
|
195
233
|
handleError(err, { source: 'render', componentName: vnode.type.name })
|
|
196
234
|
throw err
|
|
235
|
+
} finally {
|
|
236
|
+
__fictPopContext()
|
|
197
237
|
}
|
|
198
238
|
}
|
|
199
239
|
|
|
@@ -201,15 +241,26 @@ export function createElement(node: FictNode): DOMElement {
|
|
|
201
241
|
if (vnode.type === Fragment) {
|
|
202
242
|
const frag = document.createDocumentFragment()
|
|
203
243
|
const children = vnode.props?.children as FictNode | FictNode[] | undefined
|
|
204
|
-
appendChildren(frag, children)
|
|
244
|
+
appendChildren(frag, children, namespace)
|
|
205
245
|
return frag
|
|
206
246
|
}
|
|
207
247
|
|
|
208
248
|
// HTML Element
|
|
209
249
|
const tagName = typeof vnode.type === 'string' ? vnode.type : 'div'
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
250
|
+
const resolvedNamespace = resolveNamespace(tagName, namespace)
|
|
251
|
+
const el =
|
|
252
|
+
resolvedNamespace === 'svg'
|
|
253
|
+
? document.createElementNS(SVG_NS, tagName)
|
|
254
|
+
: resolvedNamespace === 'mathml'
|
|
255
|
+
? document.createElementNS(MATHML_NS, tagName)
|
|
256
|
+
: document.createElement(tagName)
|
|
257
|
+
applyProps(el, vnode.props ?? {}, resolvedNamespace === 'svg')
|
|
258
|
+
appendChildren(
|
|
259
|
+
el as unknown as ParentNode & Node,
|
|
260
|
+
vnode.props?.children as FictNode | FictNode[] | undefined,
|
|
261
|
+
tagName === 'foreignObject' ? null : resolvedNamespace,
|
|
262
|
+
)
|
|
263
|
+
return el as DOMElement
|
|
213
264
|
}
|
|
214
265
|
|
|
215
266
|
/**
|
|
@@ -231,7 +282,7 @@ export function template(
|
|
|
231
282
|
|
|
232
283
|
const create = (): Node => {
|
|
233
284
|
const t = isMathML
|
|
234
|
-
? document.createElementNS(
|
|
285
|
+
? document.createElementNS(MATHML_NS, 'template')
|
|
235
286
|
: document.createElement('template')
|
|
236
287
|
t.innerHTML = html
|
|
237
288
|
|
|
@@ -279,7 +330,11 @@ function isBindingHandle(node: unknown): node is BindingHandle {
|
|
|
279
330
|
/**
|
|
280
331
|
* Append a child node to a parent, handling all node types including reactive values.
|
|
281
332
|
*/
|
|
282
|
-
function appendChildNode(
|
|
333
|
+
function appendChildNode(
|
|
334
|
+
parent: ParentNode & Node,
|
|
335
|
+
child: FictNode,
|
|
336
|
+
namespace: NamespaceContext,
|
|
337
|
+
): void {
|
|
283
338
|
// Skip nullish values
|
|
284
339
|
if (child === null || child === undefined || child === false) {
|
|
285
340
|
return
|
|
@@ -287,7 +342,7 @@ function appendChildNode(parent: HTMLElement | DocumentFragment, child: FictNode
|
|
|
287
342
|
|
|
288
343
|
// Handle BindingHandle (recursive)
|
|
289
344
|
if (isBindingHandle(child)) {
|
|
290
|
-
appendChildNode(parent, child.marker)
|
|
345
|
+
appendChildNode(parent, child.marker, namespace)
|
|
291
346
|
// Flush pending nodes now that markers are in the DOM
|
|
292
347
|
child.flush?.()
|
|
293
348
|
return
|
|
@@ -296,14 +351,14 @@ function appendChildNode(parent: HTMLElement | DocumentFragment, child: FictNode
|
|
|
296
351
|
// Handle getter function (recursive)
|
|
297
352
|
if (typeof child === 'function' && (child as () => FictNode).length === 0) {
|
|
298
353
|
const childGetter = child as () => FictNode
|
|
299
|
-
createChildBinding(parent
|
|
354
|
+
createChildBinding(parent, childGetter, node => createElementWithContext(node, namespace))
|
|
300
355
|
return
|
|
301
356
|
}
|
|
302
357
|
|
|
303
358
|
// Static child - create element and append
|
|
304
359
|
if (Array.isArray(child)) {
|
|
305
360
|
for (const item of child) {
|
|
306
|
-
appendChildNode(parent, item)
|
|
361
|
+
appendChildNode(parent, item, namespace)
|
|
307
362
|
}
|
|
308
363
|
return
|
|
309
364
|
}
|
|
@@ -313,14 +368,14 @@ function appendChildNode(parent: HTMLElement | DocumentFragment, child: FictNode
|
|
|
313
368
|
if (typeof child !== 'object' || child === null) {
|
|
314
369
|
domNode = document.createTextNode(String(child ?? ''))
|
|
315
370
|
} else {
|
|
316
|
-
domNode =
|
|
371
|
+
domNode = createElementWithContext(child as any, namespace) as Node
|
|
317
372
|
}
|
|
318
373
|
|
|
319
374
|
// Handle DocumentFragment manually to avoid JSDOM issues
|
|
320
375
|
if (domNode.nodeType === 11) {
|
|
321
376
|
const children = Array.from(domNode.childNodes)
|
|
322
377
|
for (const node of children) {
|
|
323
|
-
appendChildNode(parent, node)
|
|
378
|
+
appendChildNode(parent, node as FictNode, namespace)
|
|
324
379
|
}
|
|
325
380
|
return
|
|
326
381
|
}
|
|
@@ -345,19 +400,20 @@ function appendChildNode(parent: HTMLElement | DocumentFragment, child: FictNode
|
|
|
345
400
|
* Append multiple children, handling arrays and nested structures.
|
|
346
401
|
*/
|
|
347
402
|
function appendChildren(
|
|
348
|
-
parent:
|
|
403
|
+
parent: ParentNode & Node,
|
|
349
404
|
children: FictNode | FictNode[] | undefined,
|
|
405
|
+
namespace: NamespaceContext,
|
|
350
406
|
): void {
|
|
351
407
|
if (children === undefined) return
|
|
352
408
|
|
|
353
409
|
if (Array.isArray(children)) {
|
|
354
410
|
for (const child of children) {
|
|
355
|
-
appendChildren(parent, child)
|
|
411
|
+
appendChildren(parent, child, namespace)
|
|
356
412
|
}
|
|
357
413
|
return
|
|
358
414
|
}
|
|
359
415
|
|
|
360
|
-
appendChildNode(parent, children)
|
|
416
|
+
appendChildNode(parent, children, namespace)
|
|
361
417
|
}
|
|
362
418
|
|
|
363
419
|
// ============================================================================
|
|
@@ -368,10 +424,10 @@ function appendChildren(
|
|
|
368
424
|
* Apply a ref to an element, supporting both callback and object refs.
|
|
369
425
|
* Both types are automatically cleaned up on unmount.
|
|
370
426
|
*/
|
|
371
|
-
function applyRef(el:
|
|
427
|
+
function applyRef(el: Element, value: unknown): void {
|
|
372
428
|
if (typeof value === 'function') {
|
|
373
429
|
// Callback ref
|
|
374
|
-
const refFn = value as (el:
|
|
430
|
+
const refFn = value as (el: Element | null) => void
|
|
375
431
|
refFn(el)
|
|
376
432
|
|
|
377
433
|
// Match React behavior: call ref(null) on unmount
|
|
@@ -382,7 +438,7 @@ function applyRef(el: HTMLElement, value: unknown): void {
|
|
|
382
438
|
}
|
|
383
439
|
} else if (value && typeof value === 'object' && 'current' in value) {
|
|
384
440
|
// Object ref
|
|
385
|
-
const refObj = value as RefObject<
|
|
441
|
+
const refObj = value as RefObject<Element>
|
|
386
442
|
refObj.current = el
|
|
387
443
|
|
|
388
444
|
// Auto-cleanup on unmount
|
|
@@ -402,7 +458,7 @@ function applyRef(el: HTMLElement, value: unknown): void {
|
|
|
402
458
|
* Apply props to an HTML element, setting up reactive bindings as needed.
|
|
403
459
|
* Uses comprehensive property constants for correct attribute/property handling.
|
|
404
460
|
*/
|
|
405
|
-
function applyProps(el:
|
|
461
|
+
function applyProps(el: Element, props: Record<string, unknown>, isSVG = false): void {
|
|
406
462
|
props = unwrapProps(props)
|
|
407
463
|
const tagName = el.tagName
|
|
408
464
|
|
|
@@ -545,10 +601,6 @@ function applyProps(el: HTMLElement, props: Record<string, unknown>, isSVG = fal
|
|
|
545
601
|
const attrName = Aliases[key] || key
|
|
546
602
|
createAttributeBinding(el, attrName, value as MaybeReactive<unknown>, setAttribute)
|
|
547
603
|
}
|
|
548
|
-
|
|
549
|
-
// Handle children
|
|
550
|
-
const children = props.children as FictNode | FictNode[] | undefined
|
|
551
|
-
appendChildren(el, children)
|
|
552
604
|
}
|
|
553
605
|
|
|
554
606
|
/**
|
|
@@ -565,7 +617,7 @@ function toPropertyName(name: string): string {
|
|
|
565
617
|
/**
|
|
566
618
|
* Set an attribute on an element, handling various value types.
|
|
567
619
|
*/
|
|
568
|
-
const setAttribute: AttributeSetter = (el:
|
|
620
|
+
const setAttribute: AttributeSetter = (el: Element, key: string, value: unknown): void => {
|
|
569
621
|
// Remove attribute for nullish/false values
|
|
570
622
|
if (value === undefined || value === null || value === false) {
|
|
571
623
|
el.removeAttribute(key)
|
|
@@ -598,7 +650,7 @@ const setAttribute: AttributeSetter = (el: HTMLElement, key: string, value: unkn
|
|
|
598
650
|
/**
|
|
599
651
|
* Set a property on an element, ensuring nullish values clear sensibly.
|
|
600
652
|
*/
|
|
601
|
-
const setProperty: AttributeSetter = (el:
|
|
653
|
+
const setProperty: AttributeSetter = (el: Element, key: string, value: unknown): void => {
|
|
602
654
|
if (value === undefined || value === null) {
|
|
603
655
|
const fallback = key === 'checked' || key === 'selected' ? false : ''
|
|
604
656
|
;(el as unknown as Record<string, unknown>)[key] = fallback
|
|
@@ -610,7 +662,7 @@ const setProperty: AttributeSetter = (el: HTMLElement, key: string, value: unkno
|
|
|
610
662
|
for (const k in value as Record<string, string>) {
|
|
611
663
|
const v = (value as Record<string, string>)[k]
|
|
612
664
|
if (v !== undefined) {
|
|
613
|
-
;(el.style as unknown as Record<string, string>)[k] = String(v)
|
|
665
|
+
;((el as HTMLElement).style as unknown as Record<string, string>)[k] = String(v)
|
|
614
666
|
}
|
|
615
667
|
}
|
|
616
668
|
return
|
|
@@ -622,14 +674,14 @@ const setProperty: AttributeSetter = (el: HTMLElement, key: string, value: unkno
|
|
|
622
674
|
/**
|
|
623
675
|
* Set innerHTML on an element (used for dangerouslySetInnerHTML)
|
|
624
676
|
*/
|
|
625
|
-
const setInnerHTML: AttributeSetter = (el:
|
|
626
|
-
el.innerHTML = value == null ? '' : String(value)
|
|
677
|
+
const setInnerHTML: AttributeSetter = (el: Element, _key: string, value: unknown): void => {
|
|
678
|
+
;(el as HTMLElement).innerHTML = value == null ? '' : String(value)
|
|
627
679
|
}
|
|
628
680
|
|
|
629
681
|
/**
|
|
630
682
|
* Set a boolean attribute on an element (empty string when true, removed when false)
|
|
631
683
|
*/
|
|
632
|
-
const setBoolAttribute: AttributeSetter = (el:
|
|
684
|
+
const setBoolAttribute: AttributeSetter = (el: Element, key: string, value: unknown): void => {
|
|
633
685
|
if (value) {
|
|
634
686
|
el.setAttribute(key, '')
|
|
635
687
|
} else {
|
|
@@ -640,7 +692,7 @@ const setBoolAttribute: AttributeSetter = (el: HTMLElement, key: string, value:
|
|
|
640
692
|
/**
|
|
641
693
|
* Set an attribute with a namespace (for SVG xlink:href, etc.)
|
|
642
694
|
*/
|
|
643
|
-
function setAttributeNS(el:
|
|
695
|
+
function setAttributeNS(el: Element, namespace: string, name: string, value: unknown): void {
|
|
644
696
|
if (value == null) {
|
|
645
697
|
el.removeAttributeNS(namespace, name)
|
|
646
698
|
} else {
|
package/src/effect.ts
CHANGED
|
@@ -61,7 +61,8 @@ export function createRenderEffect(fn: Effect): () => void {
|
|
|
61
61
|
cleanup = maybeCleanup
|
|
62
62
|
}
|
|
63
63
|
} catch (err) {
|
|
64
|
-
|
|
64
|
+
const handled = handleError(err, { source: 'effect' }, rootForError)
|
|
65
|
+
if (handled) {
|
|
65
66
|
return
|
|
66
67
|
}
|
|
67
68
|
throw err
|
package/src/error-boundary.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
createRootContext,
|
|
5
5
|
destroyRoot,
|
|
6
6
|
flushOnMount,
|
|
7
|
+
getCurrentRoot,
|
|
7
8
|
pushRoot,
|
|
8
9
|
popRoot,
|
|
9
10
|
registerErrorHandler,
|
|
@@ -24,6 +25,7 @@ export function ErrorBoundary(props: ErrorBoundaryProps): FictNode {
|
|
|
24
25
|
fragment.appendChild(marker)
|
|
25
26
|
|
|
26
27
|
const currentView = createSignal<FictNode | null>(props.children ?? null)
|
|
28
|
+
const hostRoot = getCurrentRoot()
|
|
27
29
|
|
|
28
30
|
let cleanup: (() => void) | undefined
|
|
29
31
|
let activeNodes: Node[] = []
|
|
@@ -52,7 +54,7 @@ export function ErrorBoundary(props: ErrorBoundaryProps): FictNode {
|
|
|
52
54
|
return
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
const root = createRootContext()
|
|
57
|
+
const root = createRootContext(hostRoot)
|
|
56
58
|
const prev = pushRoot(root)
|
|
57
59
|
let nodes: Node[] = []
|
|
58
60
|
try {
|
package/src/hooks.ts
CHANGED
|
@@ -5,18 +5,26 @@ import { createSignal, type SignalAccessor, type ComputedAccessor } from './sign
|
|
|
5
5
|
interface HookContext {
|
|
6
6
|
slots: unknown[]
|
|
7
7
|
cursor: number
|
|
8
|
+
rendering?: boolean
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const ctxStack: HookContext[] = []
|
|
11
12
|
|
|
13
|
+
function assertRenderContext(ctx: HookContext, hookName: string): void {
|
|
14
|
+
if (!ctx.rendering) {
|
|
15
|
+
throw new Error(`${hookName} can only be used during render execution`)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
export function __fictUseContext(): HookContext {
|
|
13
20
|
if (ctxStack.length === 0) {
|
|
14
|
-
const ctx: HookContext = { slots: [], cursor: 0 }
|
|
21
|
+
const ctx: HookContext = { slots: [], cursor: 0, rendering: true }
|
|
15
22
|
ctxStack.push(ctx)
|
|
16
23
|
return ctx
|
|
17
24
|
}
|
|
18
25
|
const ctx = ctxStack[ctxStack.length - 1]!
|
|
19
26
|
ctx.cursor = 0
|
|
27
|
+
ctx.rendering = true
|
|
20
28
|
return ctx
|
|
21
29
|
}
|
|
22
30
|
|
|
@@ -35,6 +43,7 @@ export function __fictResetContext(): void {
|
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
export function __fictUseSignal<T>(ctx: HookContext, initial: T, slot?: number): SignalAccessor<T> {
|
|
46
|
+
assertRenderContext(ctx, '__fictUseSignal')
|
|
38
47
|
const index = slot ?? ctx.cursor++
|
|
39
48
|
if (!ctx.slots[index]) {
|
|
40
49
|
ctx.slots[index] = createSignal(initial)
|
|
@@ -47,6 +56,7 @@ export function __fictUseMemo<T>(
|
|
|
47
56
|
fn: () => T,
|
|
48
57
|
slot?: number,
|
|
49
58
|
): ComputedAccessor<T> {
|
|
59
|
+
assertRenderContext(ctx, '__fictUseMemo')
|
|
50
60
|
const index = slot ?? ctx.cursor++
|
|
51
61
|
if (!ctx.slots[index]) {
|
|
52
62
|
ctx.slots[index] = createMemo(fn)
|
|
@@ -55,6 +65,7 @@ export function __fictUseMemo<T>(
|
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
export function __fictUseEffect(ctx: HookContext, fn: () => void, slot?: number): void {
|
|
68
|
+
assertRenderContext(ctx, '__fictUseEffect')
|
|
58
69
|
const index = slot ?? ctx.cursor++
|
|
59
70
|
if (!ctx.slots[index]) {
|
|
60
71
|
ctx.slots[index] = createEffect(fn)
|
|
@@ -64,9 +75,11 @@ export function __fictUseEffect(ctx: HookContext, fn: () => void, slot?: number)
|
|
|
64
75
|
export function __fictRender<T>(ctx: HookContext, fn: () => T): T {
|
|
65
76
|
ctxStack.push(ctx)
|
|
66
77
|
ctx.cursor = 0
|
|
78
|
+
ctx.rendering = true
|
|
67
79
|
try {
|
|
68
80
|
return fn()
|
|
69
81
|
} finally {
|
|
82
|
+
ctx.rendering = false
|
|
70
83
|
ctxStack.pop()
|
|
71
84
|
}
|
|
72
85
|
}
|