@fictjs/runtime 0.0.11 → 0.0.13
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 +2373 -3048
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -141
- package/dist/index.d.ts +11 -141
- package/dist/index.dev.js +2558 -2653
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +2374 -3042
- package/dist/index.js.map +1 -1
- package/package.json +1 -6
- package/src/binding.ts +28 -423
- package/src/constants.ts +368 -344
- package/src/cycle-guard.ts +124 -97
- package/src/dev.d.ts +5 -0
- package/src/dom.ts +39 -27
- package/src/effect.ts +4 -0
- package/src/error-boundary.ts +11 -2
- package/src/hooks.ts +9 -1
- package/src/index.ts +1 -19
- package/src/lifecycle.ts +17 -3
- package/src/list-helpers.ts +248 -86
- package/src/props.ts +2 -4
- package/src/reconcile.ts +4 -0
- package/src/signal.ts +128 -63
- package/src/suspense.ts +24 -7
- package/src/transition.ts +4 -1
- package/dist/slim.cjs +0 -3668
- package/dist/slim.cjs.map +0 -1
- package/dist/slim.d.cts +0 -504
- package/dist/slim.d.ts +0 -504
- package/dist/slim.js +0 -3616
- package/dist/slim.js.map +0 -1
- package/src/slim.ts +0 -69
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fictjs/runtime",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "Fict reactive runtime",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -30,11 +30,6 @@
|
|
|
30
30
|
"import": "./dist/index.js",
|
|
31
31
|
"require": "./dist/index.cjs"
|
|
32
32
|
},
|
|
33
|
-
"./slim": {
|
|
34
|
-
"types": "./dist/slim.d.ts",
|
|
35
|
-
"import": "./dist/slim.js",
|
|
36
|
-
"require": "./dist/slim.cjs"
|
|
37
|
-
},
|
|
38
33
|
"./jsx-runtime": {
|
|
39
34
|
"types": "./dist/jsx-runtime.d.ts",
|
|
40
35
|
"import": "./dist/jsx-runtime.js",
|
package/src/binding.ts
CHANGED
|
@@ -19,12 +19,10 @@ import {
|
|
|
19
19
|
ChildProperties,
|
|
20
20
|
getPropAlias,
|
|
21
21
|
SVGNamespace,
|
|
22
|
-
Aliases,
|
|
23
22
|
} from './constants'
|
|
24
23
|
import { createRenderEffect } from './effect'
|
|
25
24
|
import { Fragment } from './jsx'
|
|
26
25
|
import {
|
|
27
|
-
clearRoot,
|
|
28
26
|
createRootContext,
|
|
29
27
|
destroyRoot,
|
|
30
28
|
flushOnMount,
|
|
@@ -36,12 +34,16 @@ import {
|
|
|
36
34
|
registerRootCleanup,
|
|
37
35
|
type RootContext,
|
|
38
36
|
} from './lifecycle'
|
|
39
|
-
import { createVersionedSignalAccessor } from './list-helpers'
|
|
40
37
|
import { toNodeArray, removeNodes, insertNodesBefore } from './node-ops'
|
|
41
38
|
import { batch } from './scheduler'
|
|
42
|
-
import { computed,
|
|
39
|
+
import { computed, untrack } from './signal'
|
|
43
40
|
import type { Cleanup, FictNode } from './types'
|
|
44
41
|
|
|
42
|
+
const isDev =
|
|
43
|
+
typeof __DEV__ !== 'undefined'
|
|
44
|
+
? __DEV__
|
|
45
|
+
: typeof process === 'undefined' || process.env?.NODE_ENV !== 'production'
|
|
46
|
+
|
|
45
47
|
// ============================================================================
|
|
46
48
|
// Type Definitions
|
|
47
49
|
// ============================================================================
|
|
@@ -63,16 +65,6 @@ export interface BindingHandle {
|
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
/** Managed child node with its dispose function */
|
|
66
|
-
interface ManagedBlock<T = unknown> {
|
|
67
|
-
nodes: Node[]
|
|
68
|
-
root: RootContext
|
|
69
|
-
value: Signal<T>
|
|
70
|
-
index: Signal<number>
|
|
71
|
-
start: Comment
|
|
72
|
-
end: Comment
|
|
73
|
-
renderCurrent: () => FictNode
|
|
74
|
-
}
|
|
75
|
-
|
|
76
68
|
// ============================================================================
|
|
77
69
|
// Utility Functions
|
|
78
70
|
// ============================================================================
|
|
@@ -130,151 +122,6 @@ export function callEventHandler(
|
|
|
130
122
|
invoke(handler)
|
|
131
123
|
}
|
|
132
124
|
|
|
133
|
-
export const PRIMITIVE_PROXY = Symbol('fict:primitive-proxy')
|
|
134
|
-
const PRIMITIVE_PROXY_RAW_VALUE = Symbol('fict:primitive-proxy:raw-value')
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Unwrap a primitive proxy value to get the raw primitive value.
|
|
138
|
-
* This is primarily useful for advanced scenarios where you need the actual
|
|
139
|
-
* primitive type (e.g., for typeof checks or strict equality comparisons).
|
|
140
|
-
*
|
|
141
|
-
* @param value - A potentially proxied primitive value
|
|
142
|
-
* @returns The raw primitive value
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* ```ts
|
|
146
|
-
* createList(
|
|
147
|
-
* () => [1, 2, 3],
|
|
148
|
-
* (item) => {
|
|
149
|
-
* const raw = unwrapPrimitive(item)
|
|
150
|
-
* typeof raw === 'number' // true
|
|
151
|
-
* raw === 1 // true (for first item)
|
|
152
|
-
* },
|
|
153
|
-
* item => item
|
|
154
|
-
* )
|
|
155
|
-
* ```
|
|
156
|
-
*/
|
|
157
|
-
export function unwrapPrimitive<T>(value: T): T {
|
|
158
|
-
if (value && typeof value === 'object' && PRIMITIVE_PROXY in value) {
|
|
159
|
-
// Use the internal raw value getter
|
|
160
|
-
const getRawValue = (value as Record<PropertyKey, unknown>)[PRIMITIVE_PROXY_RAW_VALUE]
|
|
161
|
-
if (typeof getRawValue === 'function') {
|
|
162
|
-
return (getRawValue as () => T)()
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return value
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function _createValueProxy<T>(read: () => T): T {
|
|
169
|
-
const getPrimitivePrototype = (value: unknown): Record<PropertyKey, unknown> | undefined => {
|
|
170
|
-
switch (typeof value) {
|
|
171
|
-
case 'string':
|
|
172
|
-
return String.prototype as unknown as Record<PropertyKey, unknown>
|
|
173
|
-
case 'number':
|
|
174
|
-
return Number.prototype as unknown as Record<PropertyKey, unknown>
|
|
175
|
-
case 'boolean':
|
|
176
|
-
return Boolean.prototype as unknown as Record<PropertyKey, unknown>
|
|
177
|
-
case 'bigint':
|
|
178
|
-
return BigInt.prototype as unknown as Record<PropertyKey, unknown>
|
|
179
|
-
case 'symbol':
|
|
180
|
-
return Symbol.prototype as unknown as Record<PropertyKey, unknown>
|
|
181
|
-
default:
|
|
182
|
-
return undefined
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const target: Record<PropertyKey, unknown> = {}
|
|
187
|
-
const handler: ProxyHandler<Record<PropertyKey, unknown>> = {
|
|
188
|
-
get(_target, prop, receiver) {
|
|
189
|
-
if (prop === PRIMITIVE_PROXY) {
|
|
190
|
-
return true
|
|
191
|
-
}
|
|
192
|
-
if (prop === PRIMITIVE_PROXY_RAW_VALUE) {
|
|
193
|
-
return () => read()
|
|
194
|
-
}
|
|
195
|
-
if (prop === Symbol.toPrimitive) {
|
|
196
|
-
return (hint: 'string' | 'number' | 'default') => {
|
|
197
|
-
const value = read() as unknown
|
|
198
|
-
if (value != null && (typeof value === 'object' || typeof value === 'function')) {
|
|
199
|
-
const toPrimitive = (value as { [Symbol.toPrimitive]?: (hint: string) => unknown })[
|
|
200
|
-
Symbol.toPrimitive
|
|
201
|
-
]
|
|
202
|
-
if (typeof toPrimitive === 'function') {
|
|
203
|
-
return toPrimitive.call(value, hint)
|
|
204
|
-
}
|
|
205
|
-
if (hint === 'string') return value.toString?.() ?? '[object Object]'
|
|
206
|
-
if (hint === 'number') return value.valueOf?.() ?? value
|
|
207
|
-
return value.valueOf?.() ?? value
|
|
208
|
-
}
|
|
209
|
-
return value
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (prop === 'valueOf') {
|
|
213
|
-
return () => {
|
|
214
|
-
const value = read() as unknown
|
|
215
|
-
if (value != null && (typeof value === 'object' || typeof value === 'function')) {
|
|
216
|
-
return typeof (value as { valueOf?: () => unknown }).valueOf === 'function'
|
|
217
|
-
? (value as { valueOf: () => unknown }).valueOf()
|
|
218
|
-
: value
|
|
219
|
-
}
|
|
220
|
-
return value
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
if (prop === 'toString') {
|
|
224
|
-
return () => String(read())
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
const value = read() as unknown
|
|
228
|
-
if (value != null && (typeof value === 'object' || typeof value === 'function')) {
|
|
229
|
-
return Reflect.get(value as object, prop, receiver === _target ? value : receiver)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const proto = getPrimitivePrototype(value)
|
|
233
|
-
if (proto && prop in proto) {
|
|
234
|
-
const descriptor = Reflect.get(proto, prop, value)
|
|
235
|
-
return typeof descriptor === 'function' ? descriptor.bind(value) : descriptor
|
|
236
|
-
}
|
|
237
|
-
return undefined
|
|
238
|
-
},
|
|
239
|
-
set(_target, prop, newValue, receiver) {
|
|
240
|
-
const value = read() as unknown
|
|
241
|
-
if (value != null && (typeof value === 'object' || typeof value === 'function')) {
|
|
242
|
-
return Reflect.set(value as object, prop, newValue, receiver === _target ? value : receiver)
|
|
243
|
-
}
|
|
244
|
-
return false
|
|
245
|
-
},
|
|
246
|
-
has(_target, prop) {
|
|
247
|
-
if (prop === PRIMITIVE_PROXY || prop === PRIMITIVE_PROXY_RAW_VALUE) {
|
|
248
|
-
return true
|
|
249
|
-
}
|
|
250
|
-
const value = read() as unknown
|
|
251
|
-
if (value != null && (typeof value === 'object' || typeof value === 'function')) {
|
|
252
|
-
return prop in (value as object)
|
|
253
|
-
}
|
|
254
|
-
const proto = getPrimitivePrototype(value)
|
|
255
|
-
return proto ? prop in proto : false
|
|
256
|
-
},
|
|
257
|
-
ownKeys() {
|
|
258
|
-
const value = read() as unknown
|
|
259
|
-
if (value != null && (typeof value === 'object' || typeof value === 'function')) {
|
|
260
|
-
return Reflect.ownKeys(value as object)
|
|
261
|
-
}
|
|
262
|
-
const proto = getPrimitivePrototype(value)
|
|
263
|
-
return proto ? Reflect.ownKeys(proto) : []
|
|
264
|
-
},
|
|
265
|
-
getOwnPropertyDescriptor(_target, prop) {
|
|
266
|
-
const value = read() as unknown
|
|
267
|
-
if (value != null && (typeof value === 'object' || typeof value === 'function')) {
|
|
268
|
-
return Object.getOwnPropertyDescriptor(value as object, prop)
|
|
269
|
-
}
|
|
270
|
-
const proto = getPrimitivePrototype(value)
|
|
271
|
-
return proto ? Object.getOwnPropertyDescriptor(proto, prop) || undefined : undefined
|
|
272
|
-
},
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return new Proxy(target, handler) as T
|
|
276
|
-
}
|
|
277
|
-
|
|
278
125
|
// ============================================================================
|
|
279
126
|
// Text Binding
|
|
280
127
|
// ============================================================================
|
|
@@ -519,9 +366,9 @@ function applyStyle(
|
|
|
519
366
|
}
|
|
520
367
|
}
|
|
521
368
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
369
|
+
const isUnitlessStyleProperty = isDev
|
|
370
|
+
? (prop: string): boolean => UnitlessStyles.has(prop)
|
|
371
|
+
: (prop: string): boolean => prop === 'opacity' || prop === 'zIndex'
|
|
525
372
|
|
|
526
373
|
// ============================================================================
|
|
527
374
|
// Class Binding
|
|
@@ -1107,7 +954,7 @@ export function bindEvent(
|
|
|
1107
954
|
// Optimization: Global Event Delegation
|
|
1108
955
|
// If the event is delegatable and no special options (capture, passive) are used,
|
|
1109
956
|
// we attach the handler to the element property and rely on the global listener.
|
|
1110
|
-
if (DelegatedEvents.has(eventName) && !options) {
|
|
957
|
+
if (isDev && DelegatedEvents.has(eventName) && !options) {
|
|
1111
958
|
const key = `$$${eventName}`
|
|
1112
959
|
|
|
1113
960
|
// Ensure global delegation is active for this event
|
|
@@ -1124,7 +971,9 @@ export function bindEvent(
|
|
|
1124
971
|
const fn = resolveHandler()
|
|
1125
972
|
callEventHandler(fn as EventListenerOrEventListenerObject, args[0] as Event, el)
|
|
1126
973
|
} catch (err) {
|
|
1127
|
-
handleError(err, { source: 'event', eventName }, rootRef)
|
|
974
|
+
if (!handleError(err, { source: 'event', eventName }, rootRef)) {
|
|
975
|
+
throw err
|
|
976
|
+
}
|
|
1128
977
|
}
|
|
1129
978
|
}
|
|
1130
979
|
|
|
@@ -1386,7 +1235,7 @@ function assignProp(
|
|
|
1386
1235
|
// Standard event handling: onClick, onInput, etc.
|
|
1387
1236
|
if (prop.slice(0, 2) === 'on') {
|
|
1388
1237
|
const eventName = prop.slice(2).toLowerCase()
|
|
1389
|
-
const shouldDelegate = DelegatedEvents.has(eventName)
|
|
1238
|
+
const shouldDelegate = isDev && DelegatedEvents.has(eventName)
|
|
1390
1239
|
if (!shouldDelegate && prev) {
|
|
1391
1240
|
const handler = Array.isArray(prev) ? prev[0] : prev
|
|
1392
1241
|
node.removeEventListener(eventName, handler as EventListener)
|
|
@@ -1430,13 +1279,20 @@ function assignProp(
|
|
|
1430
1279
|
|
|
1431
1280
|
// Property handling (for non-SVG elements)
|
|
1432
1281
|
if (!isSVG) {
|
|
1433
|
-
const propAlias = getPropAlias(prop, node.tagName)
|
|
1434
|
-
const isProperty =
|
|
1435
|
-
|
|
1282
|
+
const propAlias = isDev ? getPropAlias(prop, node.tagName) : undefined
|
|
1283
|
+
const isProperty = isDev
|
|
1284
|
+
? Properties.has(prop)
|
|
1285
|
+
: prop in (node as unknown as Record<string, unknown>)
|
|
1286
|
+
const isChildProp = isDev
|
|
1287
|
+
? ChildProperties.has(prop)
|
|
1288
|
+
: prop === 'innerHTML' ||
|
|
1289
|
+
prop === 'textContent' ||
|
|
1290
|
+
prop === 'innerText' ||
|
|
1291
|
+
prop === 'children'
|
|
1436
1292
|
|
|
1437
1293
|
if (propAlias || isProperty || isChildProp || isCE) {
|
|
1438
1294
|
const propName = propAlias || prop
|
|
1439
|
-
if (isCE && !isProperty && !isChildProp) {
|
|
1295
|
+
if (isCE && !isProperty && !isChildProp && !propAlias) {
|
|
1440
1296
|
;(node as unknown as Record<string, unknown>)[toPropertyName(propName)] = value
|
|
1441
1297
|
} else {
|
|
1442
1298
|
;(node as unknown as Record<string, unknown>)[propName] = value
|
|
@@ -1457,7 +1313,7 @@ function assignProp(
|
|
|
1457
1313
|
}
|
|
1458
1314
|
|
|
1459
1315
|
// Default: set as attribute
|
|
1460
|
-
const attrName =
|
|
1316
|
+
const attrName = prop === 'htmlFor' ? 'for' : prop
|
|
1461
1317
|
if (value == null) node.removeAttribute(attrName)
|
|
1462
1318
|
else node.setAttribute(attrName, String(value))
|
|
1463
1319
|
return value
|
|
@@ -1605,116 +1461,6 @@ export function createConditional(
|
|
|
1605
1461
|
}
|
|
1606
1462
|
}
|
|
1607
1463
|
|
|
1608
|
-
// ============================================================================
|
|
1609
|
-
// List Rendering
|
|
1610
|
-
// ============================================================================
|
|
1611
|
-
|
|
1612
|
-
/** Key extractor function type */
|
|
1613
|
-
export type KeyFn<T> = (item: T, index: number) => string | number
|
|
1614
|
-
|
|
1615
|
-
/**
|
|
1616
|
-
* Create a reactive list rendering binding with optional keying.
|
|
1617
|
-
* The render callback receives signal accessors for the item and index.
|
|
1618
|
-
*/
|
|
1619
|
-
export function createList<T>(
|
|
1620
|
-
items: () => T[],
|
|
1621
|
-
renderItem: (item: Signal<T>, index: Signal<number>) => FictNode,
|
|
1622
|
-
createElementFn: CreateElementFn,
|
|
1623
|
-
getKey?: KeyFn<T>,
|
|
1624
|
-
): BindingHandle {
|
|
1625
|
-
const startMarker = document.createComment('fict:list:start')
|
|
1626
|
-
const endMarker = document.createComment('fict:list:end')
|
|
1627
|
-
const fragment = document.createDocumentFragment()
|
|
1628
|
-
fragment.append(startMarker, endMarker)
|
|
1629
|
-
const hostRoot = getCurrentRoot()
|
|
1630
|
-
|
|
1631
|
-
const nodeMap = new Map<string | number, ManagedBlock<T>>()
|
|
1632
|
-
let pendingItems: T[] | null = null
|
|
1633
|
-
|
|
1634
|
-
const runListUpdate = () => {
|
|
1635
|
-
const arr = items()
|
|
1636
|
-
const parent = startMarker.parentNode as (ParentNode & Node) | null
|
|
1637
|
-
if (!parent) {
|
|
1638
|
-
pendingItems = arr
|
|
1639
|
-
return
|
|
1640
|
-
}
|
|
1641
|
-
pendingItems = null
|
|
1642
|
-
|
|
1643
|
-
const newNodeMap = new Map<string | number, ManagedBlock<T>>()
|
|
1644
|
-
const blocks: ManagedBlock<T>[] = []
|
|
1645
|
-
|
|
1646
|
-
for (let i = 0; i < arr.length; i++) {
|
|
1647
|
-
const item = arr[i]! as T
|
|
1648
|
-
const key = getKey ? getKey(item, i) : i
|
|
1649
|
-
const existing = nodeMap.get(key)
|
|
1650
|
-
|
|
1651
|
-
let block: ManagedBlock<T>
|
|
1652
|
-
if (existing) {
|
|
1653
|
-
const previousValue = existing.value()
|
|
1654
|
-
if (!getKey && previousValue !== item) {
|
|
1655
|
-
destroyRoot(existing.root)
|
|
1656
|
-
removeBlockNodes(existing)
|
|
1657
|
-
block = mountBlock(item, i, renderItem, parent, endMarker, createElementFn, hostRoot)
|
|
1658
|
-
} else {
|
|
1659
|
-
const previousIndex = existing.index()
|
|
1660
|
-
existing.value(item)
|
|
1661
|
-
existing.index(i)
|
|
1662
|
-
|
|
1663
|
-
const needsRerender = getKey ? true : previousValue !== item || previousIndex !== i
|
|
1664
|
-
block = needsRerender ? rerenderBlock(existing, createElementFn) : existing
|
|
1665
|
-
}
|
|
1666
|
-
} else {
|
|
1667
|
-
block = mountBlock(item, i, renderItem, parent, endMarker, createElementFn, hostRoot)
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
newNodeMap.set(key, block)
|
|
1671
|
-
blocks.push(block)
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
for (const [key, managed] of nodeMap) {
|
|
1675
|
-
if (!newNodeMap.has(key)) {
|
|
1676
|
-
destroyRoot(managed.root)
|
|
1677
|
-
removeBlockNodes(managed)
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
let anchor: Node = endMarker
|
|
1682
|
-
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
1683
|
-
const block = blocks[i]!
|
|
1684
|
-
insertNodesBefore(parent, block.nodes, anchor)
|
|
1685
|
-
if (block.nodes.length > 0) {
|
|
1686
|
-
anchor = block.nodes[0]!
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
nodeMap.clear()
|
|
1691
|
-
for (const [k, v] of newNodeMap) {
|
|
1692
|
-
nodeMap.set(k, v)
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
|
|
1696
|
-
const dispose = createRenderEffect(runListUpdate)
|
|
1697
|
-
|
|
1698
|
-
return {
|
|
1699
|
-
marker: fragment,
|
|
1700
|
-
flush: () => {
|
|
1701
|
-
if (pendingItems !== null) {
|
|
1702
|
-
runListUpdate()
|
|
1703
|
-
}
|
|
1704
|
-
},
|
|
1705
|
-
dispose: () => {
|
|
1706
|
-
dispose()
|
|
1707
|
-
for (const [, managed] of nodeMap) {
|
|
1708
|
-
destroyRoot(managed.root)
|
|
1709
|
-
removeBlockNodes(managed)
|
|
1710
|
-
}
|
|
1711
|
-
nodeMap.clear()
|
|
1712
|
-
startMarker.parentNode?.removeChild(startMarker)
|
|
1713
|
-
endMarker.parentNode?.removeChild(endMarker)
|
|
1714
|
-
},
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
1464
|
// ============================================================================
|
|
1719
1465
|
// Show/Hide Helper
|
|
1720
1466
|
// ==========================================================================
|
|
@@ -1846,131 +1592,6 @@ export function createPortal(
|
|
|
1846
1592
|
}
|
|
1847
1593
|
}
|
|
1848
1594
|
|
|
1849
|
-
// ============================================================================
|
|
1850
|
-
// Internal helpers
|
|
1851
|
-
// ============================================================================
|
|
1852
|
-
|
|
1853
|
-
function mountBlock<T>(
|
|
1854
|
-
initialValue: T,
|
|
1855
|
-
initialIndex: number,
|
|
1856
|
-
renderItem: (item: Signal<T>, index: Signal<number>) => FictNode,
|
|
1857
|
-
parent: ParentNode & Node,
|
|
1858
|
-
anchor: Node,
|
|
1859
|
-
createElementFn: CreateElementFn,
|
|
1860
|
-
hostRoot?: RootContext | undefined,
|
|
1861
|
-
): ManagedBlock<T> {
|
|
1862
|
-
const start = document.createComment('fict:block:start')
|
|
1863
|
-
const end = document.createComment('fict:block:end')
|
|
1864
|
-
const valueSig = createVersionedSignalAccessor<T>(initialValue)
|
|
1865
|
-
const indexSig = createSignal<number>(initialIndex)
|
|
1866
|
-
const renderCurrent = () => renderItem(valueSig, indexSig)
|
|
1867
|
-
const root = createRootContext(hostRoot)
|
|
1868
|
-
const prev = pushRoot(root)
|
|
1869
|
-
const nodes: Node[] = [start]
|
|
1870
|
-
let handledError = false
|
|
1871
|
-
try {
|
|
1872
|
-
const output = renderCurrent()
|
|
1873
|
-
if (output != null && output !== false) {
|
|
1874
|
-
const el = createElementFn(output)
|
|
1875
|
-
const rendered = toNodeArray(el)
|
|
1876
|
-
nodes.push(...rendered)
|
|
1877
|
-
}
|
|
1878
|
-
nodes.push(end)
|
|
1879
|
-
insertNodesBefore(parent, nodes, anchor)
|
|
1880
|
-
} catch (err) {
|
|
1881
|
-
if (handleSuspend(err as any, root)) {
|
|
1882
|
-
handledError = true
|
|
1883
|
-
nodes.push(end)
|
|
1884
|
-
insertNodesBefore(parent, nodes, anchor)
|
|
1885
|
-
} else if (handleError(err, { source: 'renderChild' }, root)) {
|
|
1886
|
-
handledError = true
|
|
1887
|
-
nodes.push(end)
|
|
1888
|
-
insertNodesBefore(parent, nodes, anchor)
|
|
1889
|
-
} else {
|
|
1890
|
-
throw err
|
|
1891
|
-
}
|
|
1892
|
-
} finally {
|
|
1893
|
-
popRoot(prev)
|
|
1894
|
-
if (!handledError) {
|
|
1895
|
-
flushOnMount(root)
|
|
1896
|
-
} else {
|
|
1897
|
-
destroyRoot(root)
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
return {
|
|
1901
|
-
nodes,
|
|
1902
|
-
root,
|
|
1903
|
-
value: valueSig,
|
|
1904
|
-
index: indexSig,
|
|
1905
|
-
start,
|
|
1906
|
-
end,
|
|
1907
|
-
renderCurrent,
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
function rerenderBlock<T>(
|
|
1912
|
-
block: ManagedBlock<T>,
|
|
1913
|
-
createElementFn: CreateElementFn,
|
|
1914
|
-
): ManagedBlock<T> {
|
|
1915
|
-
const currentContent = block.nodes.slice(1, Math.max(1, block.nodes.length - 1))
|
|
1916
|
-
const currentNode = currentContent.length === 1 ? currentContent[0] : null
|
|
1917
|
-
|
|
1918
|
-
clearRoot(block.root)
|
|
1919
|
-
|
|
1920
|
-
const prev = pushRoot(block.root)
|
|
1921
|
-
let nextOutput: FictNode
|
|
1922
|
-
let handledError = false
|
|
1923
|
-
try {
|
|
1924
|
-
nextOutput = block.renderCurrent()
|
|
1925
|
-
} catch (err) {
|
|
1926
|
-
if (handleSuspend(err as any, block.root)) {
|
|
1927
|
-
handledError = true
|
|
1928
|
-
popRoot(prev)
|
|
1929
|
-
destroyRoot(block.root)
|
|
1930
|
-
block.nodes = [block.start, block.end]
|
|
1931
|
-
return block
|
|
1932
|
-
}
|
|
1933
|
-
if (handleError(err, { source: 'renderChild' }, block.root)) {
|
|
1934
|
-
handledError = true
|
|
1935
|
-
popRoot(prev)
|
|
1936
|
-
destroyRoot(block.root)
|
|
1937
|
-
block.nodes = [block.start, block.end]
|
|
1938
|
-
return block
|
|
1939
|
-
}
|
|
1940
|
-
throw err
|
|
1941
|
-
} finally {
|
|
1942
|
-
if (!handledError) {
|
|
1943
|
-
popRoot(prev)
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
|
|
1947
|
-
if (isFragmentVNode(nextOutput) && currentContent.length > 0) {
|
|
1948
|
-
const patched = patchFragmentChildren(currentContent, nextOutput.props?.children)
|
|
1949
|
-
if (patched) {
|
|
1950
|
-
block.nodes = [block.start, ...currentContent, block.end]
|
|
1951
|
-
return block
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
if (currentNode && patchNode(currentNode, nextOutput)) {
|
|
1956
|
-
block.nodes = [block.start, currentNode, block.end]
|
|
1957
|
-
return block
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
clearContent(block)
|
|
1961
|
-
|
|
1962
|
-
if (nextOutput != null && nextOutput !== false) {
|
|
1963
|
-
const newNodes = toNodeArray(
|
|
1964
|
-
nextOutput instanceof Node ? nextOutput : (createElementFn(nextOutput) as Node),
|
|
1965
|
-
)
|
|
1966
|
-
insertNodesBefore(block.start.parentNode as ParentNode & Node, newNodes, block.end)
|
|
1967
|
-
block.nodes = [block.start, ...newNodes, block.end]
|
|
1968
|
-
} else {
|
|
1969
|
-
block.nodes = [block.start, block.end]
|
|
1970
|
-
}
|
|
1971
|
-
return block
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
1595
|
function patchElement(el: Element, output: FictNode): boolean {
|
|
1975
1596
|
if (
|
|
1976
1597
|
output === null ||
|
|
@@ -2102,7 +1723,7 @@ function patchNode(currentNode: Node | null, nextOutput: FictNode): boolean {
|
|
|
2102
1723
|
return false
|
|
2103
1724
|
}
|
|
2104
1725
|
|
|
2105
|
-
function
|
|
1726
|
+
function _isFragmentVNode(
|
|
2106
1727
|
value: unknown,
|
|
2107
1728
|
): value is { type: typeof Fragment; props?: { children?: FictNode | FictNode[] } } {
|
|
2108
1729
|
return (
|
|
@@ -2133,7 +1754,7 @@ function normalizeChildren(
|
|
|
2133
1754
|
return result
|
|
2134
1755
|
}
|
|
2135
1756
|
|
|
2136
|
-
function
|
|
1757
|
+
function _patchFragmentChildren(
|
|
2137
1758
|
nodes: Node[],
|
|
2138
1759
|
children: FictNode | FictNode[] | undefined,
|
|
2139
1760
|
): boolean {
|
|
@@ -2149,20 +1770,4 @@ function patchFragmentChildren(
|
|
|
2149
1770
|
return true
|
|
2150
1771
|
}
|
|
2151
1772
|
|
|
2152
|
-
function clearContent<T>(block: ManagedBlock<T>): void {
|
|
2153
|
-
const nodes = block.nodes.slice(1, Math.max(1, block.nodes.length - 1))
|
|
2154
|
-
removeNodes(nodes)
|
|
2155
|
-
}
|
|
2156
|
-
|
|
2157
|
-
function removeBlockNodes<T>(block: ManagedBlock<T>): void {
|
|
2158
|
-
let cursor: Node | null = block.start
|
|
2159
|
-
const end = block.end
|
|
2160
|
-
while (cursor) {
|
|
2161
|
-
const next: Node | null = cursor.nextSibling
|
|
2162
|
-
cursor.parentNode?.removeChild(cursor)
|
|
2163
|
-
if (cursor === end) break
|
|
2164
|
-
cursor = next
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
1773
|
// DOM utility functions are imported from './node-ops' to avoid duplication
|