@liteforge/runtime 0.1.0

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.
@@ -0,0 +1,452 @@
1
+ /**
2
+ * LiteForge Control Flow Components
3
+ *
4
+ * Components for conditional rendering and list iteration.
5
+ * These handle DOM insertion/removal reactively based on signals.
6
+ */
7
+ import { effect } from '@liteforge/core';
8
+ import { isComponentFactory } from './component.js';
9
+ /**
10
+ * Discriminate a dynamic component value into a tagged union.
11
+ * This helps TypeScript narrow types that can't be narrowed with type guards alone.
12
+ */
13
+ function discriminateDynamicComponent(comp) {
14
+ if (comp === null) {
15
+ return { kind: 'null' };
16
+ }
17
+ if (isComponentFactory(comp)) {
18
+ // Safe cast: isComponentFactory checks for __liteforge_component
19
+ return { kind: 'factory', value: comp };
20
+ }
21
+ // If it's not null and not a ComponentFactory, it must be a RenderFunction
22
+ return { kind: 'render', value: comp };
23
+ }
24
+ /**
25
+ * Conditionally render children based on a condition.
26
+ * When the condition is truthy, the truthy value is passed to the children callback.
27
+ *
28
+ * @typeParam T - The type returned by the `when` getter
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * // Boolean condition
33
+ * Show({
34
+ * when: () => isLoggedIn(),
35
+ * fallback: () => document.createTextNode('Please log in'),
36
+ * children: () => createWelcomeMessage(),
37
+ * })
38
+ *
39
+ * // Value-based condition with type narrowing
40
+ * Show({
41
+ * when: () => currentUser(), // User | null
42
+ * children: (user) => ( // user is User, guaranteed non-null
43
+ * <h1>{user.name}</h1>
44
+ * ),
45
+ * })
46
+ * ```
47
+ */
48
+ // Debug counter for Show instances
49
+ let showInstanceId = 0;
50
+ export function Show(config) {
51
+ const { when, fallback, children } = config;
52
+ // Create a marker comment for positioning
53
+ const instanceId = ++showInstanceId;
54
+ const marker = document.createComment(`Show:${instanceId}`);
55
+ let currentNode = null;
56
+ let lastValue;
57
+ let hasRendered = false;
58
+ // Resolve the condition to a function
59
+ const getValue = () => {
60
+ if (typeof when === 'function') {
61
+ return when();
62
+ }
63
+ return when;
64
+ };
65
+ // Update content based on condition
66
+ function updateContent() {
67
+ const parentElement = marker.parentNode;
68
+ if (!parentElement)
69
+ return;
70
+ const value = getValue();
71
+ // Value unchanged (by reference) → nothing to do
72
+ // This handles: same primitive (boolean true/false/null), same object reference
73
+ // Prevents re-rendering when isAdmin() returns same boolean but effect re-runs
74
+ if (hasRendered && Object.is(value, lastValue)) {
75
+ return;
76
+ }
77
+ lastValue = value;
78
+ hasRendered = true;
79
+ // Remove old node if it exists
80
+ if (currentNode && currentNode.parentNode) {
81
+ currentNode.parentNode.removeChild(currentNode);
82
+ currentNode = null;
83
+ }
84
+ // Create and insert new node
85
+ const isTruthy = Boolean(value);
86
+ const newNode = isTruthy
87
+ ? children(value)
88
+ : fallback?.() ?? null;
89
+ if (newNode) {
90
+ parentElement.insertBefore(newNode, marker.nextSibling);
91
+ currentNode = newNode;
92
+ }
93
+ }
94
+ // Set up reactive effect for updates
95
+ // We use requestAnimationFrame to ensure the marker is in the DOM
96
+ // before the first update (replaces problematic MutationObserver)
97
+ effect(() => {
98
+ // Read the value to track dependencies
99
+ getValue();
100
+ // Only update if marker is in DOM
101
+ if (marker.parentNode) {
102
+ updateContent();
103
+ }
104
+ else {
105
+ // Marker not in DOM yet - schedule check for next frame
106
+ requestAnimationFrame(() => {
107
+ if (marker.parentNode) {
108
+ updateContent();
109
+ }
110
+ });
111
+ }
112
+ });
113
+ return marker;
114
+ }
115
+ /**
116
+ * Render a list of items with keyed reconciliation.
117
+ *
118
+ * @typeParam T - The item type, inferred from the `each` array
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * For({
123
+ * each: () => users(),
124
+ * key: 'id',
125
+ * children: (user, index) => {
126
+ * return document.createTextNode(`${index}: ${user.name}`);
127
+ * },
128
+ * fallback: () => document.createTextNode('No users'),
129
+ * })
130
+ * ```
131
+ */
132
+ export function For(config) {
133
+ const { each, key, children, fallback } = config;
134
+ // Create a marker comment for positioning
135
+ const marker = document.createComment('For');
136
+ let items = [];
137
+ let fallbackNode = null;
138
+ let isInitialized = false;
139
+ // Resolve the list to a function
140
+ const getList = () => {
141
+ if (typeof each === 'function') {
142
+ return each();
143
+ }
144
+ return each;
145
+ };
146
+ // Get key for an item
147
+ const getKey = (item, index) => {
148
+ if (key === undefined) {
149
+ // For primitives without explicit key, use the value itself to enable proper reconciliation
150
+ // This allows detecting when items at the same index have different values
151
+ if (typeof item === 'string' || typeof item === 'number') {
152
+ return `${index}:${item}`;
153
+ }
154
+ // For objects without key, fall back to index (no reconciliation benefit)
155
+ return index;
156
+ }
157
+ if (typeof key === 'function') {
158
+ return key(item, index);
159
+ }
160
+ return String(item[key]);
161
+ };
162
+ // Remove fallback node if present
163
+ function removeFallback() {
164
+ if (fallbackNode && fallbackNode.parentNode) {
165
+ fallbackNode.parentNode.removeChild(fallbackNode);
166
+ fallbackNode = null;
167
+ }
168
+ }
169
+ // Reconcile the list
170
+ function updateList() {
171
+ const parentElement = marker.parentNode;
172
+ if (!parentElement)
173
+ return;
174
+ const list = getList();
175
+ // Handle empty list with fallback
176
+ if (list.length === 0) {
177
+ // Remove all existing items
178
+ for (const oldItem of items) {
179
+ if (oldItem.node.parentNode) {
180
+ oldItem.node.parentNode.removeChild(oldItem.node);
181
+ }
182
+ }
183
+ items = [];
184
+ // Show fallback if provided
185
+ if (fallback && !fallbackNode) {
186
+ fallbackNode = fallback();
187
+ parentElement.insertBefore(fallbackNode, marker.nextSibling);
188
+ }
189
+ return;
190
+ }
191
+ // Remove fallback if list has items
192
+ removeFallback();
193
+ // Build a map of existing items by key
194
+ const existingByKey = new Map();
195
+ for (const item of items) {
196
+ existingByKey.set(item.key, item);
197
+ }
198
+ // Build new items list
199
+ const newItems = [];
200
+ const newKeys = new Set();
201
+ for (let i = 0; i < list.length; i++) {
202
+ const item = list[i];
203
+ const itemKey = getKey(item, i);
204
+ newKeys.add(itemKey);
205
+ const existing = existingByKey.get(itemKey);
206
+ if (existing) {
207
+ // Reuse existing item, update index
208
+ existing.item = item;
209
+ existing.index = i;
210
+ newItems.push(existing);
211
+ }
212
+ else {
213
+ // Create new item - pass plain index number
214
+ const node = children(item, i);
215
+ newItems.push({ item, key: itemKey, node, index: i });
216
+ }
217
+ }
218
+ // Remove items that are no longer in the list
219
+ for (const oldItem of items) {
220
+ if (!newKeys.has(oldItem.key)) {
221
+ if (oldItem.node.parentNode) {
222
+ oldItem.node.parentNode.removeChild(oldItem.node);
223
+ }
224
+ }
225
+ }
226
+ // Insert/reorder items in the DOM
227
+ // We need to maintain the correct order after the marker
228
+ let prevNode = marker;
229
+ for (const newItem of newItems) {
230
+ const { node } = newItem;
231
+ const expectedPosition = prevNode.nextSibling;
232
+ if (node !== expectedPosition) {
233
+ // Node is either not in DOM or in wrong position
234
+ if (node.parentNode) {
235
+ node.parentNode.removeChild(node);
236
+ }
237
+ parentElement.insertBefore(node, prevNode.nextSibling);
238
+ }
239
+ prevNode = node;
240
+ }
241
+ items = newItems;
242
+ }
243
+ // Use MutationObserver to detect when marker is added to DOM
244
+ const observer = new MutationObserver(() => {
245
+ if (marker.parentNode && !isInitialized) {
246
+ isInitialized = true;
247
+ observer.disconnect();
248
+ updateList();
249
+ }
250
+ });
251
+ observer.observe(document, { childList: true, subtree: true });
252
+ // Set up reactive effect for updates
253
+ effect(() => {
254
+ // Read the list to track dependencies
255
+ getList();
256
+ // Only update if marker is in DOM
257
+ if (marker.parentNode) {
258
+ updateList();
259
+ }
260
+ });
261
+ return marker;
262
+ }
263
+ /**
264
+ * Render the first matching case, or fallback.
265
+ *
266
+ * @example
267
+ * ```ts
268
+ * Switch({
269
+ * fallback: () => document.createTextNode('Unknown'),
270
+ * children: [
271
+ * Match({ when: () => status() === 'loading', children: () => Spinner() }),
272
+ * Match({ when: () => status() === 'error', children: () => ErrorView() }),
273
+ * Match({ when: () => status() === 'success', children: () => SuccessView() }),
274
+ * ],
275
+ * })
276
+ * ```
277
+ */
278
+ export function Switch(config) {
279
+ const { fallback, children } = config;
280
+ const marker = document.createComment('Switch');
281
+ let currentNode = null;
282
+ let currentIndex = -1;
283
+ let isInitialized = false;
284
+ function findMatchIndex() {
285
+ for (let i = 0; i < children.length; i++) {
286
+ const child = children[i];
287
+ if (!child)
288
+ continue;
289
+ const condition = typeof child.when === 'function' ? child.when() : child.when;
290
+ if (condition) {
291
+ return i;
292
+ }
293
+ }
294
+ return fallback ? -2 : -1; // -2 = fallback, -1 = no match
295
+ }
296
+ function updateSwitch() {
297
+ const parentElement = marker.parentNode;
298
+ if (!parentElement)
299
+ return;
300
+ const matchedIndex = findMatchIndex();
301
+ // Only update if the matched case changed
302
+ if (matchedIndex !== currentIndex) {
303
+ // Remove old node
304
+ if (currentNode && currentNode.parentNode) {
305
+ currentNode.parentNode.removeChild(currentNode);
306
+ currentNode = null;
307
+ }
308
+ // Insert new node
309
+ let render;
310
+ if (matchedIndex >= 0) {
311
+ render = children[matchedIndex]?.render;
312
+ }
313
+ else if (matchedIndex === -2) {
314
+ render = fallback;
315
+ }
316
+ if (render) {
317
+ currentNode = render();
318
+ parentElement.insertBefore(currentNode, marker.nextSibling);
319
+ }
320
+ currentIndex = matchedIndex;
321
+ }
322
+ }
323
+ // Use MutationObserver to detect when marker is added to DOM
324
+ const observer = new MutationObserver(() => {
325
+ if (marker.parentNode && !isInitialized) {
326
+ isInitialized = true;
327
+ observer.disconnect();
328
+ updateSwitch();
329
+ }
330
+ });
331
+ observer.observe(document, { childList: true, subtree: true });
332
+ // Set up reactive effect
333
+ effect(() => {
334
+ // Read all conditions to track dependencies
335
+ for (const child of children) {
336
+ if (typeof child.when === 'function') {
337
+ child.when();
338
+ }
339
+ }
340
+ if (marker.parentNode) {
341
+ updateSwitch();
342
+ }
343
+ });
344
+ return marker;
345
+ }
346
+ /**
347
+ * Helper to create a Match case for Switch.
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * Match({
352
+ * when: () => status() === 'active',
353
+ * children: () => <span>Active</span>,
354
+ * })
355
+ * ```
356
+ */
357
+ export function Match(config) {
358
+ return { when: config.when, render: config.children };
359
+ }
360
+ /**
361
+ * Dynamically render a component based on a signal.
362
+ *
363
+ * @example
364
+ * ```ts
365
+ * Dynamic({
366
+ * component: () => currentView(),
367
+ * props: () => ({ id: currentId() }),
368
+ * })
369
+ * ```
370
+ */
371
+ export function Dynamic(config) {
372
+ const { component, props } = config;
373
+ const marker = document.createComment('Dynamic');
374
+ let currentNode = null;
375
+ let currentComponent = null;
376
+ let currentInstance = null;
377
+ let isInitialized = false;
378
+ const getComponent = () => {
379
+ return component();
380
+ };
381
+ const getProps = () => {
382
+ if (typeof props === 'function') {
383
+ return props();
384
+ }
385
+ return (props ?? {});
386
+ };
387
+ function updateDynamic() {
388
+ const parentElement = marker.parentNode;
389
+ if (!parentElement)
390
+ return;
391
+ const comp = getComponent();
392
+ const currentProps = getProps();
393
+ // Check if component changed
394
+ if (comp !== currentComponent) {
395
+ // Unmount old component/remove old node
396
+ if (currentInstance) {
397
+ currentInstance.unmount();
398
+ currentInstance = null;
399
+ }
400
+ if (currentNode && currentNode.parentNode) {
401
+ currentNode.parentNode.removeChild(currentNode);
402
+ currentNode = null;
403
+ }
404
+ // Mount new component using discriminated union for proper type narrowing
405
+ const discriminated = discriminateDynamicComponent(comp);
406
+ if (discriminated.kind === 'factory') {
407
+ currentInstance = discriminated.value(currentProps);
408
+ // Create a temporary container to get the node
409
+ const tempContainer = document.createElement('div');
410
+ currentInstance.mount(tempContainer);
411
+ const node = tempContainer.firstChild;
412
+ if (node) {
413
+ tempContainer.removeChild(node);
414
+ parentElement.insertBefore(node, marker.nextSibling);
415
+ currentNode = node;
416
+ }
417
+ }
418
+ else if (discriminated.kind === 'render') {
419
+ currentNode = discriminated.value();
420
+ if (currentNode) {
421
+ parentElement.insertBefore(currentNode, marker.nextSibling);
422
+ }
423
+ }
424
+ // kind === 'null' means no component to mount
425
+ currentComponent = comp;
426
+ }
427
+ else if (currentInstance) {
428
+ // Same component, just update props
429
+ currentInstance.updateProps(currentProps);
430
+ }
431
+ }
432
+ // Use MutationObserver to detect when marker is added to DOM
433
+ const observer = new MutationObserver(() => {
434
+ if (marker.parentNode && !isInitialized) {
435
+ isInitialized = true;
436
+ observer.disconnect();
437
+ updateDynamic();
438
+ }
439
+ });
440
+ observer.observe(document, { childList: true, subtree: true });
441
+ // Set up reactive effect
442
+ effect(() => {
443
+ // Read to track dependencies
444
+ getComponent();
445
+ getProps();
446
+ if (marker.parentNode) {
447
+ updateDynamic();
448
+ }
449
+ });
450
+ return marker;
451
+ }
452
+ //# sourceMappingURL=control-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-flow.js","sourceRoot":"","sources":["../src/control-flow.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAepD;;;GAGG;AACH,SAAS,4BAA4B,CACnC,IAAiD;IAEjD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1B,CAAC;IACD,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,iEAAiE;QACjE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAA2B,EAAE,CAAC;IACjE,CAAC;IACD,2EAA2E;IAC3E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAsB,EAAE,CAAC;AAC3D,CAAC;AAgBD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,mCAAmC;AACnC,IAAI,cAAc,GAAG,CAAC,CAAC;AAEvB,MAAM,UAAU,IAAI,CAAI,MAAqB;IAC3C,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAE5C,0CAA0C;IAC1C,MAAM,UAAU,GAAG,EAAE,cAAc,CAAC;IACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,UAAU,EAAE,CAAC,CAAC;IAC5D,IAAI,WAAW,GAAgB,IAAI,CAAC;IACpC,IAAI,SAAwB,CAAC;IAC7B,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,sCAAsC;IACtC,MAAM,QAAQ,GAAG,GAAM,EAAE;QACvB,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAQ,IAAgB,EAAE,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,oCAAoC;IACpC,SAAS,aAAa;QACpB,MAAM,aAAa,GAAG,MAAM,CAAC,UAAqB,CAAC;QACnD,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;QAEzB,iDAAiD;QACjD,gFAAgF;QAChF,+EAA+E;QAC/E,IAAI,WAAW,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;YAC/C,OAAO;QACT,CAAC;QAED,SAAS,GAAG,KAAK,CAAC;QAClB,WAAW,GAAG,IAAI,CAAC;QAEnB,+BAA+B;QAC/B,IAAI,WAAW,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;YAC1C,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YAChD,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC;QAED,6BAA6B;QAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,OAAO,GAAG,QAAQ;YACtB,CAAC,CAAC,QAAQ,CAAC,KAAuB,CAAC;YACnC,CAAC,CAAC,QAAQ,EAAE,EAAE,IAAI,IAAI,CAAC;QAEzB,IAAI,OAAO,EAAE,CAAC;YACZ,aAAa,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YACxD,WAAW,GAAG,OAAO,CAAC;QACxB,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,kEAAkE;IAClE,kEAAkE;IAClE,MAAM,CAAC,GAAG,EAAE;QACV,uCAAuC;QACvC,QAAQ,EAAE,CAAC;QAEX,kCAAkC;QAClC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,aAAa,EAAE,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,wDAAwD;YACxD,qBAAqB,CAAC,GAAG,EAAE;gBACzB,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;oBACtB,aAAa,EAAE,CAAC;gBAClB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAwBD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,GAAG,CAAI,MAAoB;IACzC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAEjD,0CAA0C;IAC1C,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,KAAK,GAAiB,EAAE,CAAC;IAC7B,IAAI,YAAY,GAAgB,IAAI,CAAC;IACrC,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,iCAAiC;IACjC,MAAM,OAAO,GAAG,GAAqB,EAAE;QACrC,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,OAAQ,IAA+B,EAAE,CAAC;QAC5C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,sBAAsB;IACtB,MAAM,MAAM,GAAG,CAAC,IAAO,EAAE,KAAa,EAAmB,EAAE;QACzD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,4FAA4F;YAC5F,2EAA2E;YAC3E,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACzD,OAAO,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC;YAC5B,CAAC;YACD,0EAA0E;YAC1E,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE,CAAC;YAC9B,OAAO,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC;IAEF,kCAAkC;IAClC,SAAS,cAAc;QACrB,IAAI,YAAY,IAAI,YAAY,CAAC,UAAU,EAAE,CAAC;YAC5C,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;YAClD,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,SAAS,UAAU;QACjB,MAAM,aAAa,GAAG,MAAM,CAAC,UAAqB,CAAC;QACnD,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;QAEvB,kCAAkC;QAClC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,4BAA4B;YAC5B,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;gBAC5B,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;oBAC5B,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;YACD,KAAK,GAAG,EAAE,CAAC;YAEX,4BAA4B;YAC5B,IAAI,QAAQ,IAAI,CAAC,YAAY,EAAE,CAAC;gBAC9B,YAAY,GAAG,QAAQ,EAAE,CAAC;gBAC1B,aAAa,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO;QACT,CAAC;QAED,oCAAoC;QACpC,cAAc,EAAE,CAAC;QAEjB,uCAAuC;QACvC,MAAM,aAAa,GAAG,IAAI,GAAG,EAA+B,CAAC;QAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QAED,uBAAuB;QACvB,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmB,CAAC;QAE3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAM,CAAC;YAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAErB,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAE5C,IAAI,QAAQ,EAAE,CAAC;gBACb,oCAAoC;gBACpC,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;gBACrB,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC;gBACnB,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC1B,CAAC;iBAAM,CAAC;gBACN,4CAA4C;gBAC5C,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QAED,8CAA8C;QAC9C,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9B,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;oBAC5B,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACpD,CAAC;YACH,CAAC;QACH,CAAC;QAED,kCAAkC;QAClC,yDAAyD;QACzD,IAAI,QAAQ,GAAS,MAAM,CAAC;QAE5B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;YACzB,MAAM,gBAAgB,GAAG,QAAQ,CAAC,WAAW,CAAC;YAE9C,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBAC9B,iDAAiD;gBACjD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;oBACpB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;gBACpC,CAAC;gBACD,aAAa,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;YACzD,CAAC;YAED,QAAQ,GAAG,IAAI,CAAC;QAClB,CAAC;QAED,KAAK,GAAG,QAAQ,CAAC;IACnB,CAAC;IAED,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,GAAG,EAAE;QACzC,IAAI,MAAM,CAAC,UAAU,IAAI,CAAC,aAAa,EAAE,CAAC;YACxC,aAAa,GAAG,IAAI,CAAC;YACrB,QAAQ,CAAC,UAAU,EAAE,CAAC;YACtB,UAAU,EAAE,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/D,qCAAqC;IACrC,MAAM,CAAC,GAAG,EAAE;QACV,sCAAsC;QACtC,OAAO,EAAE,CAAC;QAEV,kCAAkC;QAClC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,UAAU,EAAE,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AA+BD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,MAAM,CAAC,MAAoB;IACzC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IAEtC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,WAAW,GAAgB,IAAI,CAAC;IACpC,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;IACtB,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,SAAS,cAAc;QACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;YAC/E,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,CAAC,CAAC;YACX,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,+BAA+B;IAC5D,CAAC;IAED,SAAS,YAAY;QACnB,MAAM,aAAa,GAAG,MAAM,CAAC,UAAqB,CAAC;QACnD,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,MAAM,YAAY,GAAG,cAAc,EAAE,CAAC;QAEtC,0CAA0C;QAC1C,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;YAClC,kBAAkB;YAClB,IAAI,WAAW,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;gBAC1C,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;gBAChD,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YAED,kBAAkB;YAClB,IAAI,MAAgC,CAAC;YACrC,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;gBACtB,MAAM,GAAG,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;YAC1C,CAAC;iBAAM,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;gBAC/B,MAAM,GAAG,QAAQ,CAAC;YACpB,CAAC;YAED,IAAI,MAAM,EAAE,CAAC;gBACX,WAAW,GAAG,MAAM,EAAE,CAAC;gBACvB,aAAa,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YAC9D,CAAC;YAED,YAAY,GAAG,YAAY,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,GAAG,EAAE;QACzC,IAAI,MAAM,CAAC,UAAU,IAAI,CAAC,aAAa,EAAE,CAAC;YACxC,aAAa,GAAG,IAAI,CAAC;YACrB,QAAQ,CAAC,UAAU,EAAE,CAAC;YACtB,YAAY,EAAE,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/D,yBAAyB;IACzB,MAAM,CAAC,GAAG,EAAE;QACV,4CAA4C;QAC5C,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBACrC,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,YAAY,EAAE,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CAAC,MAAmB;IACvC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;AACxD,CAAC;AAkBD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,OAAO,CAAoC,MAAwB;IACjF,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAEpC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,WAAW,GAAgB,IAAI,CAAC;IACpC,IAAI,gBAAgB,GAAgD,IAAI,CAAC;IACzE,IAAI,eAAe,GAA6B,IAAI,CAAC;IACrD,IAAI,aAAa,GAAG,KAAK,CAAC;IAE1B,MAAM,YAAY,GAAG,GAAgD,EAAE;QACrE,OAAO,SAAS,EAAE,CAAC;IACrB,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,GAAM,EAAE;QACvB,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;YAChC,OAAO,KAAK,EAAE,CAAC;QACjB,CAAC;QACD,OAAO,CAAC,KAAK,IAAI,EAAE,CAAM,CAAC;IAC5B,CAAC,CAAC;IAEF,SAAS,aAAa;QACpB,MAAM,aAAa,GAAG,MAAM,CAAC,UAAqB,CAAC;QACnD,IAAI,CAAC,aAAa;YAAE,OAAO;QAE3B,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,QAAQ,EAAE,CAAC;QAEhC,6BAA6B;QAC7B,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAC9B,wCAAwC;YACxC,IAAI,eAAe,EAAE,CAAC;gBACpB,eAAe,CAAC,OAAO,EAAE,CAAC;gBAC1B,eAAe,GAAG,IAAI,CAAC;YACzB,CAAC;YACD,IAAI,WAAW,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC;gBAC1C,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;gBAChD,WAAW,GAAG,IAAI,CAAC;YACrB,CAAC;YAED,0EAA0E;YAC1E,MAAM,aAAa,GAAG,4BAA4B,CAAC,IAAI,CAAC,CAAC;YAEzD,IAAI,aAAa,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBACrC,eAAe,GAAG,aAAa,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBACpD,+CAA+C;gBAC/C,MAAM,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;gBACpD,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;gBACrC,MAAM,IAAI,GAAG,aAAa,CAAC,UAAU,CAAC;gBACtC,IAAI,IAAI,EAAE,CAAC;oBACT,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;oBAChC,aAAa,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;oBACrD,WAAW,GAAG,IAAI,CAAC;gBACrB,CAAC;YACH,CAAC;iBAAM,IAAI,aAAa,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC3C,WAAW,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC;gBACpC,IAAI,WAAW,EAAE,CAAC;oBAChB,aAAa,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;YACD,8CAA8C;YAE9C,gBAAgB,GAAG,IAAI,CAAC;QAC1B,CAAC;aAAM,IAAI,eAAe,EAAE,CAAC;YAC3B,oCAAoC;YACpC,eAAe,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,GAAG,EAAE;QACzC,IAAI,MAAM,CAAC,UAAU,IAAI,CAAC,aAAa,EAAE,CAAC;YACxC,aAAa,GAAG,IAAI,CAAC;YACrB,QAAQ,CAAC,UAAU,EAAE,CAAC;YACtB,aAAa,EAAE,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAE/D,yBAAyB;IACzB,MAAM,CAAC,GAAG,EAAE;QACV,6BAA6B;QAC7B,YAAY,EAAE,CAAC;QACf,QAAQ,EAAE,CAAC;QAEX,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YACtB,aAAa,EAAE,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/dist/h.d.ts ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * LiteForge h() Function
3
+ *
4
+ * The hyperscript function that creates DOM elements from JSX.
5
+ * Handles both HTML elements and components, with support for
6
+ * reactive props and children via getter functions.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * // HTML element
11
+ * h('div', { class: 'container' }, 'Hello')
12
+ *
13
+ * // Reactive prop
14
+ * h('div', { class: () => theme() }, 'Text')
15
+ *
16
+ * // Component
17
+ * h(MyComponent, { name: 'John' })
18
+ *
19
+ * // Fragment
20
+ * h(Fragment, null, h('div', null, 'A'), h('div', null, 'B'))
21
+ * ```
22
+ */
23
+ import type { ComponentFactory, RenderFunction } from './types.js';
24
+ /**
25
+ * Fragment represents a list of children without a wrapper element.
26
+ * Use with h(Fragment, null, ...children) or JSX <>...</>
27
+ */
28
+ export declare const Fragment: unique symbol;
29
+ /** Props can be static values or getter functions for reactivity */
30
+ type PropValue = unknown | (() => unknown);
31
+ /** Props object passed to h() */
32
+ type Props = Record<string, PropValue> | null;
33
+ /** Child can be a node, string, number, getter, or component */
34
+ type HChild = Node | string | number | boolean | null | undefined | (() => HChild) | HChild[];
35
+ /**
36
+ * Function component type that accepts props.
37
+ * The bivariant hack uses a method signature which TypeScript checks bivariantly.
38
+ */
39
+ type FunctionComponent<P = Record<string, unknown>> = {
40
+ bivarianceHack(props: P): Node;
41
+ }['bivarianceHack'];
42
+ /** Tag can be HTML element name, Component, Fragment, or render function */
43
+ type Tag = string | typeof Fragment | ComponentFactory<Record<string, unknown>> | FunctionComponent | RenderFunction;
44
+ /**
45
+ * Create a DOM node from JSX-like arguments.
46
+ *
47
+ * @param tag - Element name, Component, or Fragment
48
+ * @param props - Props object or null
49
+ * @param children - Child nodes
50
+ */
51
+ export declare function h(tag: Tag, props: Props, ...children: HChild[]): Node;
52
+ export {};
53
+ //# sourceMappingURL=h.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"h.d.ts","sourceRoot":"","sources":["../src/h.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAOnE;;;GAGG;AACH,eAAO,MAAM,QAAQ,eAAmC,CAAC;AAMzD,oEAAoE;AACpE,KAAK,SAAS,GAAG,OAAO,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC;AAE3C,iCAAiC;AACjC,KAAK,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC;AAE9C,gEAAgE;AAChE,KAAK,MAAM,GACP,IAAI,GACJ,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,SAAS,GACT,CAAC,MAAM,MAAM,CAAC,GACd,MAAM,EAAE,CAAC;AAEb;;;GAGG;AACH,KAAK,iBAAiB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACpD,cAAc,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CAChC,CAAC,gBAAgB,CAAC,CAAC;AAEpB,4EAA4E;AAC5E,KAAK,GAAG,GACJ,MAAM,GACN,OAAO,QAAQ,GACf,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAGzC,iBAAiB,GACjB,cAAc,CAAC;AAMnB;;;;;;GAMG;AACH,wBAAgB,CAAC,CACf,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,KAAK,EACZ,GAAG,QAAQ,EAAE,MAAM,EAAE,GACpB,IAAI,CAaN"}