@llui/dom 0.0.19 → 0.0.20
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/README.md +9 -7
- package/dist/hmr.d.ts +1 -0
- package/dist/hmr.d.ts.map +1 -1
- package/dist/hmr.js +69 -29
- package/dist/hmr.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mount.d.ts +32 -0
- package/dist/mount.d.ts.map +1 -1
- package/dist/mount.js +223 -0
- package/dist/mount.js.map +1 -1
- package/dist/primitives/slice.d.ts.map +1 -1
- package/dist/primitives/slice.js +6 -0
- package/dist/primitives/slice.js.map +1 -1
- package/dist/primitives/unsafe-html.d.ts +28 -0
- package/dist/primitives/unsafe-html.d.ts.map +1 -0
- package/dist/primitives/unsafe-html.js +82 -0
- package/dist/primitives/unsafe-html.js.map +1 -0
- package/dist/view-helpers.d.ts +6 -0
- package/dist/view-helpers.d.ts.map +1 -1
- package/dist/view-helpers.js +2 -0
- package/dist/view-helpers.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,13 +57,15 @@ Element helpers (`div`, `button`, `span`, etc.) stay as imports — they're stat
|
|
|
57
57
|
|
|
58
58
|
### Core
|
|
59
59
|
|
|
60
|
-
| Export
|
|
61
|
-
|
|
|
62
|
-
| `component(def)`
|
|
63
|
-
| `mountApp(el, def)`
|
|
64
|
-
| `hydrateApp(el, def)`
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
60
|
+
| Export | Purpose |
|
|
61
|
+
| ------------------------------ | --------------------------------------------------------- |
|
|
62
|
+
| `component(def)` | Create a component definition |
|
|
63
|
+
| `mountApp(el, def)` | Mount a component to a DOM element |
|
|
64
|
+
| `hydrateApp(el, def)` | Hydrate server-rendered HTML |
|
|
65
|
+
| `mountAtAnchor(anchor, def)` | Mount a component relative to a comment anchor |
|
|
66
|
+
| `hydrateAtAnchor(anchor, def)` | Hydrate server-rendered HTML relative to a comment anchor |
|
|
67
|
+
| `flush()` | Synchronously flush all pending updates |
|
|
68
|
+
| `createView(send)` | Create a full View bundle (for tests/dynamic use) |
|
|
67
69
|
|
|
68
70
|
### View Primitives
|
|
69
71
|
|
package/dist/hmr.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { ComponentDef, AppHandle } from './types.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare function enableHmr(): void;
|
|
7
7
|
export declare function registerForHmr(name: string, inst: object, container: HTMLElement): void;
|
|
8
|
+
export declare function registerForAnchor(name: string, inst: object, anchor: Comment, endSentinel: Comment): void;
|
|
8
9
|
export declare function unregisterForHmr(name: string, inst: object): void;
|
|
9
10
|
/**
|
|
10
11
|
* Hot-swap a component definition on all live instances.
|
package/dist/hmr.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAUzD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAUzD;;;GAGG;AACH,wBAAgB,SAAS,IAAI,IAAI,CAQhC;AAmBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,CAIvF;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EACf,WAAW,EAAE,OAAO,GACnB,IAAI,CAIN;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAMjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACtC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC5B,SAAS,GAAG,IAAI,CAyElB"}
|
package/dist/hmr.js
CHANGED
|
@@ -10,12 +10,23 @@ import { createView } from './view-helpers.js';
|
|
|
10
10
|
* Importing this module registers it with mountApp for hot-swapping.
|
|
11
11
|
*/
|
|
12
12
|
export function enableHmr() {
|
|
13
|
-
_setHmrModule({
|
|
13
|
+
_setHmrModule({
|
|
14
|
+
enableHmr,
|
|
15
|
+
registerForHmr,
|
|
16
|
+
registerForAnchor,
|
|
17
|
+
unregisterForHmr,
|
|
18
|
+
replaceComponent,
|
|
19
|
+
});
|
|
14
20
|
}
|
|
15
21
|
const hmrRegistry = new Map();
|
|
16
22
|
export function registerForHmr(name, inst, container) {
|
|
17
23
|
const entries = hmrRegistry.get(name) ?? [];
|
|
18
|
-
entries.push({ inst: inst, container });
|
|
24
|
+
entries.push({ kind: 'container', inst: inst, container });
|
|
25
|
+
hmrRegistry.set(name, entries);
|
|
26
|
+
}
|
|
27
|
+
export function registerForAnchor(name, inst, anchor, endSentinel) {
|
|
28
|
+
const entries = hmrRegistry.get(name) ?? [];
|
|
29
|
+
entries.push({ kind: 'anchor', inst: inst, anchor, endSentinel });
|
|
19
30
|
hmrRegistry.set(name, entries);
|
|
20
31
|
}
|
|
21
32
|
export function unregisterForHmr(name, inst) {
|
|
@@ -43,9 +54,8 @@ export function replaceComponent(name, newDef) {
|
|
|
43
54
|
if (!entries || entries.length === 0)
|
|
44
55
|
return null;
|
|
45
56
|
let handle = null;
|
|
46
|
-
for (const
|
|
47
|
-
const typedInst = inst;
|
|
48
|
-
// Replace functions on the live definition
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
const typedInst = entry.inst;
|
|
49
59
|
typedInst.def = {
|
|
50
60
|
...typedInst.def,
|
|
51
61
|
update: newDef.update,
|
|
@@ -55,52 +65,82 @@ export function replaceComponent(name, newDef) {
|
|
|
55
65
|
__update: newDef.__update,
|
|
56
66
|
__handlers: newDef.__handlers,
|
|
57
67
|
};
|
|
58
|
-
// Dispose old scope tree — removes all old DOM nodes and bindings
|
|
59
68
|
disposeScope(typedInst.rootScope);
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
// Clear the owned region per-kind.
|
|
70
|
+
if (entry.kind === 'container') {
|
|
71
|
+
entry.container.textContent = '';
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
// anchor kind — wipe siblings between anchor and endSentinel, keep the
|
|
75
|
+
// anchor AND the end sentinel (they bracket the fresh render).
|
|
76
|
+
let sib = entry.anchor.nextSibling;
|
|
77
|
+
while (sib !== null && sib !== entry.endSentinel) {
|
|
78
|
+
const next = sib.nextSibling;
|
|
79
|
+
sib.parentNode.removeChild(sib);
|
|
80
|
+
sib = next;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
62
83
|
typedInst.rootScope = createScope(null);
|
|
63
84
|
typedInst.rootScope._kind = 'root';
|
|
64
85
|
typedInst.allBindings = [];
|
|
65
86
|
typedInst.structuralBlocks = [];
|
|
66
|
-
// Re-run view with current state
|
|
67
87
|
setFlatBindings(typedInst.allBindings);
|
|
68
88
|
setRenderContext({
|
|
69
89
|
rootScope: typedInst.rootScope,
|
|
70
90
|
state: typedInst.state,
|
|
71
91
|
allBindings: typedInst.allBindings,
|
|
72
92
|
structuralBlocks: typedInst.structuralBlocks,
|
|
73
|
-
container,
|
|
93
|
+
container: entry.kind === 'container' ? entry.container : (entry.anchor.parentElement ?? undefined),
|
|
74
94
|
send: typedInst.send,
|
|
75
95
|
instance: typedInst,
|
|
76
96
|
});
|
|
77
97
|
const nodes = typedInst.def.view(createView(typedInst.send));
|
|
78
98
|
clearRenderContext();
|
|
79
99
|
setFlatBindings(null);
|
|
80
|
-
|
|
81
|
-
|
|
100
|
+
if (entry.kind === 'container') {
|
|
101
|
+
for (const node of nodes) {
|
|
102
|
+
entry.container.appendChild(node);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
for (const node of nodes) {
|
|
107
|
+
entry.anchor.parentNode.insertBefore(node, entry.endSentinel);
|
|
108
|
+
}
|
|
82
109
|
}
|
|
83
|
-
// Return AppHandle for the first instance
|
|
84
110
|
if (!handle) {
|
|
85
|
-
handle =
|
|
86
|
-
dispose() {
|
|
87
|
-
unregisterForHmr(name, inst);
|
|
88
|
-
inst.abortController.abort();
|
|
89
|
-
unregisterInstance(inst);
|
|
90
|
-
disposeScope(typedInst.rootScope);
|
|
91
|
-
container.textContent = '';
|
|
92
|
-
},
|
|
93
|
-
flush() {
|
|
94
|
-
flushInstance(inst);
|
|
95
|
-
},
|
|
96
|
-
send(msg) {
|
|
97
|
-
;
|
|
98
|
-
typedInst.send(msg);
|
|
99
|
-
},
|
|
100
|
-
};
|
|
111
|
+
handle = makeReplacementHandle(name, entry, typedInst);
|
|
101
112
|
}
|
|
102
113
|
}
|
|
103
114
|
console.log(`[LLui HMR] ${name} updated — state preserved`);
|
|
104
115
|
return handle;
|
|
105
116
|
}
|
|
117
|
+
function makeReplacementHandle(name, entry, typedInst) {
|
|
118
|
+
return {
|
|
119
|
+
dispose() {
|
|
120
|
+
unregisterForHmr(name, entry.inst);
|
|
121
|
+
entry.inst.abortController.abort();
|
|
122
|
+
unregisterInstance(entry.inst);
|
|
123
|
+
disposeScope(typedInst.rootScope);
|
|
124
|
+
if (entry.kind === 'container') {
|
|
125
|
+
entry.container.textContent = '';
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
let sib = entry.anchor.nextSibling;
|
|
129
|
+
while (sib !== null && sib !== entry.endSentinel) {
|
|
130
|
+
const next = sib.nextSibling;
|
|
131
|
+
sib.parentNode.removeChild(sib);
|
|
132
|
+
sib = next;
|
|
133
|
+
}
|
|
134
|
+
entry.endSentinel.parentNode?.removeChild(entry.endSentinel);
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
flush() {
|
|
138
|
+
flushInstance(entry.inst);
|
|
139
|
+
},
|
|
140
|
+
send(msg) {
|
|
141
|
+
;
|
|
142
|
+
typedInst.send(msg);
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
106
146
|
//# sourceMappingURL=hmr.js.map
|
package/dist/hmr.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hmr.js","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle } from './types.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport { flushInstance } from './update-loop.js'\nimport { createScope, disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { unregisterInstance } from './runtime.js'\nimport { _setHmrModule } from './mount.js'\nimport { createView } from './view-helpers.js'\n\n/**\n * Enable HMR state preservation. Called by compiler-generated dev code.\n * Importing this module registers it with mountApp for hot-swapping.\n */\nexport function enableHmr(): void {\n _setHmrModule({ enableHmr, registerForHmr, unregisterForHmr, replaceComponent })\n}\n\n// ── HMR Registry ─────────────────────────────────────────────────\n\ninterface HmrEntry {\n inst: ComponentInstance\n container: HTMLElement\n}\n\nconst hmrRegistry = new Map<string, HmrEntry[]>()\n\nexport function registerForHmr(name: string, inst: object, container: HTMLElement): void {\n const entries = hmrRegistry.get(name) ?? []\n entries.push({ inst: inst as ComponentInstance, container })\n hmrRegistry.set(name, entries)\n}\n\nexport function unregisterForHmr(name: string, inst: object): void {\n const entries = hmrRegistry.get(name)\n if (!entries) return\n const idx = entries.findIndex((e) => e.inst === inst)\n if (idx !== -1) entries.splice(idx, 1)\n if (entries.length === 0) hmrRegistry.delete(name)\n}\n\n/**\n * Hot-swap a component definition on all live instances.\n *\n * Preserves the current state. Replaces update, view, onEffect, and __dirty.\n * Disposes the old scope tree (removing old DOM and bindings),\n * re-runs view(currentState, send) to rebuild fresh DOM.\n *\n * Returns an AppHandle for the first instance (for mountApp compatibility),\n * or null if no instances are registered (first mount).\n */\nexport function replaceComponent<S, M, E>(\n name: string,\n newDef: ComponentDef<S, M, E>,\n): AppHandle | null {\n const entries = hmrRegistry.get(name)\n if (!entries || entries.length === 0) return null\n\n let handle: AppHandle | null = null\n\n for (const { inst, container } of entries) {\n const typedInst = inst as ComponentInstance<S, M, E>\n\n // Replace functions on the live definition\n typedInst.def = {\n ...typedInst.def,\n update: newDef.update,\n view: newDef.view,\n onEffect: newDef.onEffect,\n __dirty: newDef.__dirty,\n __update: newDef.__update,\n __handlers: newDef.__handlers,\n }\n\n // Dispose old scope tree — removes all old DOM nodes and bindings\n disposeScope(typedInst.rootScope)\n container.textContent = ''\n\n // Create fresh scope tree\n typedInst.rootScope = createScope(null)\n typedInst.rootScope._kind = 'root'\n typedInst.allBindings = []\n typedInst.structuralBlocks = []\n\n // Re-run view with current state\n setFlatBindings(typedInst.allBindings)\n setRenderContext({\n rootScope: typedInst.rootScope,\n state: typedInst.state,\n allBindings: typedInst.allBindings,\n structuralBlocks: typedInst.structuralBlocks,\n container,\n send: typedInst.send as (msg: unknown) => void,\n instance: typedInst as ComponentInstance,\n })\n const nodes = typedInst.def.view(createView<S, M>(typedInst.send))\n clearRenderContext()\n setFlatBindings(null)\n\n for (const node of nodes) {\n container.appendChild(node)\n }\n\n // Return AppHandle for the first instance\n if (!handle) {\n handle = {\n dispose() {\n unregisterForHmr(name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n disposeScope(typedInst.rootScope)\n container.textContent = ''\n },\n flush() {\n flushInstance(inst)\n },\n send(msg: unknown) {\n ;(typedInst.send as (m: unknown) => void)(msg)\n },\n }\n }\n }\n\n console.log(`[LLui HMR] ${name} updated — state preserved`)\n\n return handle\n}\n"]}
|
|
1
|
+
{"version":3,"file":"hmr.js","sourceRoot":"","sources":["../src/hmr.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle } from './types.js'\nimport type { ComponentInstance } from './update-loop.js'\nimport { flushInstance } from './update-loop.js'\nimport { createScope, disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { unregisterInstance } from './runtime.js'\nimport { _setHmrModule } from './mount.js'\nimport { createView } from './view-helpers.js'\n\n/**\n * Enable HMR state preservation. Called by compiler-generated dev code.\n * Importing this module registers it with mountApp for hot-swapping.\n */\nexport function enableHmr(): void {\n _setHmrModule({\n enableHmr,\n registerForHmr,\n registerForAnchor,\n unregisterForHmr,\n replaceComponent,\n })\n}\n\n// ── HMR Registry ─────────────────────────────────────────────────\n\ntype HmrEntry =\n | {\n kind: 'container'\n inst: ComponentInstance\n container: HTMLElement\n }\n | {\n kind: 'anchor'\n inst: ComponentInstance\n anchor: Comment\n endSentinel: Comment\n }\n\nconst hmrRegistry = new Map<string, HmrEntry[]>()\n\nexport function registerForHmr(name: string, inst: object, container: HTMLElement): void {\n const entries = hmrRegistry.get(name) ?? []\n entries.push({ kind: 'container', inst: inst as ComponentInstance, container })\n hmrRegistry.set(name, entries)\n}\n\nexport function registerForAnchor(\n name: string,\n inst: object,\n anchor: Comment,\n endSentinel: Comment,\n): void {\n const entries = hmrRegistry.get(name) ?? []\n entries.push({ kind: 'anchor', inst: inst as ComponentInstance, anchor, endSentinel })\n hmrRegistry.set(name, entries)\n}\n\nexport function unregisterForHmr(name: string, inst: object): void {\n const entries = hmrRegistry.get(name)\n if (!entries) return\n const idx = entries.findIndex((e) => e.inst === inst)\n if (idx !== -1) entries.splice(idx, 1)\n if (entries.length === 0) hmrRegistry.delete(name)\n}\n\n/**\n * Hot-swap a component definition on all live instances.\n *\n * Preserves the current state. Replaces update, view, onEffect, and __dirty.\n * Disposes the old scope tree (removing old DOM and bindings),\n * re-runs view(currentState, send) to rebuild fresh DOM.\n *\n * Returns an AppHandle for the first instance (for mountApp compatibility),\n * or null if no instances are registered (first mount).\n */\nexport function replaceComponent<S, M, E>(\n name: string,\n newDef: ComponentDef<S, M, E>,\n): AppHandle | null {\n const entries = hmrRegistry.get(name)\n if (!entries || entries.length === 0) return null\n\n let handle: AppHandle | null = null\n\n for (const entry of entries) {\n const typedInst = entry.inst as ComponentInstance<S, M, E>\n\n typedInst.def = {\n ...typedInst.def,\n update: newDef.update,\n view: newDef.view,\n onEffect: newDef.onEffect,\n __dirty: newDef.__dirty,\n __update: newDef.__update,\n __handlers: newDef.__handlers,\n }\n\n disposeScope(typedInst.rootScope)\n\n // Clear the owned region per-kind.\n if (entry.kind === 'container') {\n entry.container.textContent = ''\n } else {\n // anchor kind — wipe siblings between anchor and endSentinel, keep the\n // anchor AND the end sentinel (they bracket the fresh render).\n let sib = entry.anchor.nextSibling\n while (sib !== null && sib !== entry.endSentinel) {\n const next = sib.nextSibling\n sib.parentNode!.removeChild(sib)\n sib = next\n }\n }\n\n typedInst.rootScope = createScope(null)\n typedInst.rootScope._kind = 'root'\n typedInst.allBindings = []\n typedInst.structuralBlocks = []\n\n setFlatBindings(typedInst.allBindings)\n setRenderContext({\n rootScope: typedInst.rootScope,\n state: typedInst.state,\n allBindings: typedInst.allBindings,\n structuralBlocks: typedInst.structuralBlocks,\n container:\n entry.kind === 'container' ? entry.container : (entry.anchor.parentElement ?? undefined),\n send: typedInst.send as (msg: unknown) => void,\n instance: typedInst as ComponentInstance,\n })\n const nodes = typedInst.def.view(createView<S, M>(typedInst.send))\n clearRenderContext()\n setFlatBindings(null)\n\n if (entry.kind === 'container') {\n for (const node of nodes) {\n entry.container.appendChild(node)\n }\n } else {\n for (const node of nodes) {\n entry.anchor.parentNode!.insertBefore(node, entry.endSentinel)\n }\n }\n\n if (!handle) {\n handle = makeReplacementHandle(name, entry, typedInst)\n }\n }\n\n console.log(`[LLui HMR] ${name} updated — state preserved`)\n\n return handle\n}\n\nfunction makeReplacementHandle<S, M, E>(\n name: string,\n entry: HmrEntry,\n typedInst: ComponentInstance<S, M, E>,\n): AppHandle {\n return {\n dispose() {\n unregisterForHmr(name, entry.inst)\n entry.inst.abortController.abort()\n unregisterInstance(entry.inst)\n disposeScope(typedInst.rootScope)\n if (entry.kind === 'container') {\n entry.container.textContent = ''\n } else {\n let sib = entry.anchor.nextSibling\n while (sib !== null && sib !== entry.endSentinel) {\n const next = sib.nextSibling\n sib.parentNode!.removeChild(sib)\n sib = next\n }\n entry.endSentinel.parentNode?.removeChild(entry.endSentinel)\n }\n },\n flush() {\n flushInstance(entry.inst)\n },\n send(msg: unknown) {\n ;(typedInst.send as (m: unknown) => void)(msg)\n },\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { ComponentDef, Send, Props, AppHandle, Scope, Binding, BindingKind, TransitionOptions, BranchOptions, ShowOptions, EachOptions, ItemAccessor, PortalOptions, ForeignOptions, ChildOptions, } from './types.js';
|
|
2
2
|
export { component } from './component.js';
|
|
3
3
|
export { createView, type View } from './view-helpers.js';
|
|
4
|
-
export { mountApp, hydrateApp, type MountOptions } from './mount.js';
|
|
4
|
+
export { mountApp, hydrateApp, mountAtAnchor, hydrateAtAnchor, type MountOptions } from './mount.js';
|
|
5
5
|
export type { LluiDebugAPI, ElementReport, MessageRecord, StateDiff } from './devtools.js';
|
|
6
6
|
export type { CoverageSnapshot } from './tracking/coverage.js';
|
|
7
7
|
export type { EachDiff } from './tracking/each-diff.js';
|
|
@@ -16,6 +16,7 @@ export { createContext, provide, provideValue, useContext, useContextValue, type
|
|
|
16
16
|
export { sliceHandler } from './slice-handler.js';
|
|
17
17
|
export { childHandlers, type ChildState, type ChildMsg, type ModuleState, type ModuleMsg, } from './compose.js';
|
|
18
18
|
export { text } from './primitives/text.js';
|
|
19
|
+
export { unsafeHtml } from './primitives/unsafe-html.js';
|
|
19
20
|
export { branch } from './primitives/branch.js';
|
|
20
21
|
export { each } from './primitives/each.js';
|
|
21
22
|
export { virtualEach, type VirtualEachOptions } from './primitives/virtual-each.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,YAAY,EACZ,IAAI,EACJ,KAAK,EACL,SAAS,EACT,KAAK,EACL,OAAO,EACP,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,GACb,MAAM,YAAY,CAAA;AAInB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAA;AAIzD,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
export { component } from './component.js';
|
|
4
4
|
export { createView } from './view-helpers.js';
|
|
5
5
|
// ── Mount ─────────────────────────────────────────────────────────
|
|
6
|
-
export { mountApp, hydrateApp } from './mount.js';
|
|
6
|
+
export { mountApp, hydrateApp, mountAtAnchor, hydrateAtAnchor } from './mount.js';
|
|
7
7
|
// ── Runtime ───────────────────────────────────────────────────────
|
|
8
8
|
export { flush } from './runtime.js';
|
|
9
9
|
export { addressOf } from './addressed.js';
|
|
@@ -14,6 +14,7 @@ export { sliceHandler } from './slice-handler.js';
|
|
|
14
14
|
export { childHandlers, } from './compose.js';
|
|
15
15
|
// ── View Primitives ───────────────────────────────────────────────
|
|
16
16
|
export { text } from './primitives/text.js';
|
|
17
|
+
export { unsafeHtml } from './primitives/unsafe-html.js';
|
|
17
18
|
export { branch } from './primitives/branch.js';
|
|
18
19
|
export { each } from './primitives/each.js';
|
|
19
20
|
export { virtualEach } from './primitives/virtual-each.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AAoBrE,qEAAqE;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAa,MAAM,mBAAmB,CAAA;AAEzD,qEAAqE;AAErE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAqB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// ── Types ─────────────────────────────────────────────────────────\n\nexport type {\n ComponentDef,\n Send,\n Props,\n AppHandle,\n Scope,\n Binding,\n BindingKind,\n TransitionOptions,\n BranchOptions,\n ShowOptions,\n EachOptions,\n ItemAccessor,\n PortalOptions,\n ForeignOptions,\n ChildOptions,\n} from './types.js'\n\n// ── Component ─────────────────────────────────────────────────────\n\nexport { component } from './component.js'\nexport { createView, type View } from './view-helpers.js'\n\n// ── Mount ─────────────────────────────────────────────────────────\n\nexport { mountApp, hydrateApp, mountAtAnchor, hydrateAtAnchor, type MountOptions } from './mount.js'\n// installDevTools is NOT re-exported here to keep it out of production bundles.\n// Import directly: import { installDevTools } from '@llui/dom/devtools'\nexport type { LluiDebugAPI, ElementReport, MessageRecord, StateDiff } from './devtools.js'\nexport type { CoverageSnapshot } from './tracking/coverage.js'\nexport type { EachDiff } from './tracking/each-diff.js'\nexport type { DisposerEvent } from './tracking/disposer-log.js'\nexport type { PendingEffect, EffectTimelineEntry, EffectMatch } from './tracking/effect-timeline.js'\nexport type { ScopeNode } from './types.js'\n\n// ── Runtime ───────────────────────────────────────────────────────\n\nexport { flush } from './runtime.js'\nexport { addressOf } from './addressed.js'\nexport { renderToString, renderNodes, serializeNodes } from './ssr.js'\nexport { mergeHandlers } from './merge-handlers.js'\nexport {\n createContext,\n provide,\n provideValue,\n useContext,\n useContextValue,\n type Context,\n} from './primitives/context.js'\nexport { sliceHandler } from './slice-handler.js'\nexport {\n childHandlers,\n type ChildState,\n type ChildMsg,\n type ModuleState,\n type ModuleMsg,\n} from './compose.js'\n\n// ── View Primitives ───────────────────────────────────────────────\n\nexport { text } from './primitives/text.js'\nexport { unsafeHtml } from './primitives/unsafe-html.js'\nexport { branch } from './primitives/branch.js'\nexport { each } from './primitives/each.js'\nexport { virtualEach, type VirtualEachOptions } from './primitives/virtual-each.js'\nexport { show } from './primitives/show.js'\nexport { slice } from './primitives/slice.js'\nexport { portal } from './primitives/portal.js'\nexport { foreign } from './primitives/foreign.js'\nexport { child } from './primitives/child.js'\nexport { lazy, type LazyOptions } from './primitives/lazy.js'\nexport type { LazyDef, AnyComponentDef } from './types.js'\nexport { memo } from './primitives/memo.js'\nexport { selector } from './primitives/selector.js'\nexport { onMount } from './primitives/on-mount.js'\nexport { errorBoundary } from './primitives/error-boundary.js'\n\n// ── Element Helpers ───────────────────────────────────────────────\n\nexport {\n a,\n abbr,\n article,\n aside,\n b,\n blockquote,\n br,\n button,\n canvas,\n code,\n dd,\n details,\n dialog,\n div,\n dl,\n dt,\n em,\n fieldset,\n figcaption,\n figure,\n footer,\n form,\n h1,\n h2,\n h3,\n h4,\n h5,\n h6,\n header,\n hr,\n i,\n iframe,\n img,\n input,\n label,\n legend,\n li,\n main,\n mark,\n nav,\n ol,\n optgroup,\n option,\n output,\n p,\n pre,\n progress,\n section,\n select,\n small,\n span,\n strong,\n sub,\n summary,\n sup,\n table,\n tbody,\n td,\n textarea,\n tfoot,\n th,\n thead,\n time,\n tr,\n ul,\n video,\n} from './elements.js'\n\n// ── SVG Elements ─────────────────────────────────────────────────\n\nexport {\n svg,\n g,\n defs,\n symbol,\n use,\n circle,\n ellipse,\n line,\n path,\n polygon,\n polyline,\n rect,\n text as svgText,\n tspan,\n textPath,\n clipPath,\n linearGradient,\n radialGradient,\n stop,\n mask,\n pattern,\n marker,\n filter,\n feBlend,\n feColorMatrix,\n feComponentTransfer,\n feComposite,\n feConvolveMatrix,\n feDiffuseLighting,\n feDisplacementMap,\n feDropShadow,\n feFlood,\n feGaussianBlur,\n feImage,\n feMerge,\n feMergeNode,\n feMorphology,\n feOffset,\n feSpecularLighting,\n feTile,\n feTurbulence,\n fePointLight,\n feSpotLight,\n feDistantLight,\n feFuncR,\n feFuncG,\n feFuncB,\n feFuncA,\n image,\n foreignObject,\n animate,\n animateMotion,\n animateTransform,\n set,\n mpath,\n desc,\n title as svgTitle,\n metadata,\n} from './svg-elements.js'\n\n// ── MathML Elements ──────────────────────────────────────────────\n\nexport {\n math,\n mi,\n mn,\n mo,\n ms,\n mtext,\n mrow,\n mfrac,\n msqrt,\n mroot,\n msup,\n msub,\n msubsup,\n munder,\n mover,\n munderover,\n mmultiscripts,\n mprescripts,\n mnone,\n mtable,\n mtr,\n mtd,\n mspace,\n mpadded,\n mphantom,\n menclose,\n merror,\n maction,\n semantics,\n annotation,\n annotationXml,\n} from './mathml-elements.js'\n\n// ── Form Utilities ────────────────────────────────────────────────\n\nexport { applyField, type FieldMsg } from './form.js'\n\n// ── Compiler Target ───────────────────────────────────────────────\n\nexport { elSplit } from './el-split.js'\nexport { elTemplate } from './el-template.js'\nexport { _runPhase2 as __runPhase2, _handleMsg as __handleMsg } from './update-loop.js'\n"]}
|
package/dist/mount.d.ts
CHANGED
|
@@ -23,5 +23,37 @@ export interface MountOptions {
|
|
|
23
23
|
parentScope?: Scope;
|
|
24
24
|
}
|
|
25
25
|
export declare function mountApp<S, M, E>(container: HTMLElement, def: ComponentDef<S, M, E>, data?: unknown, options?: MountOptions): AppHandle;
|
|
26
|
+
/**
|
|
27
|
+
* Mount a component relative to a comment anchor rather than inside a
|
|
28
|
+
* container element. Inserts a synthesized end sentinel (`<!-- llui-mount-end -->`)
|
|
29
|
+
* immediately after the anchor and places the component's nodes between
|
|
30
|
+
* the pair. The anchor must already be attached to a live DOM tree.
|
|
31
|
+
*
|
|
32
|
+
* Unlike `mountApp`, the caller's anchor node is preserved across the
|
|
33
|
+
* handle's lifetime — only the content between the pair (and the end
|
|
34
|
+
* sentinel itself) is disposed. Used by `@llui/vike` persistent layouts
|
|
35
|
+
* to mount chain layers without a wrapper element.
|
|
36
|
+
*
|
|
37
|
+
* If a pre-existing `<!-- llui-mount-end -->` is found after the anchor
|
|
38
|
+
* (e.g. stale from an undisposed prior mount), the content between the
|
|
39
|
+
* anchor and that sentinel is swept and the sentinel is reused. Dev mode
|
|
40
|
+
* warns in that case.
|
|
41
|
+
*/
|
|
42
|
+
export declare function mountAtAnchor<S, M, E>(anchor: Comment, def: ComponentDef<S, M, E>, data?: unknown, options?: MountOptions): AppHandle;
|
|
43
|
+
/**
|
|
44
|
+
* Hydrate a component relative to a comment anchor rather than inside a
|
|
45
|
+
* container element. Analogous to `hydrateApp` — uses `serverState` as
|
|
46
|
+
* the initial state (not `init()`'s output) while preserving `init()`'s
|
|
47
|
+
* effects for post-mount dispatch.
|
|
48
|
+
*
|
|
49
|
+
* The DOM-handling path is identical to `mountAtAnchor`: reuses a
|
|
50
|
+
* pre-existing end sentinel when present, synthesizes one otherwise.
|
|
51
|
+
* Atomic-swaps the owned region whether or not server content is there
|
|
52
|
+
* to replace. No error for a missing end sentinel — the vike chain's
|
|
53
|
+
* outer `hydrateApp`'s `replaceChildren` wipes inner layers' sentinels,
|
|
54
|
+
* so inner-layer `hydrateAtAnchor` calls routinely find nothing to
|
|
55
|
+
* reuse, and that's normal.
|
|
56
|
+
*/
|
|
57
|
+
export declare function hydrateAtAnchor<S, M, E>(anchor: Comment, def: ComponentDef<S, M, E>, serverState: S, options?: MountOptions): AppHandle;
|
|
26
58
|
export declare function hydrateApp<S, M, E>(container: HTMLElement, def: ComponentDef<S, M, E>, serverState: S, options?: MountOptions): AppHandle;
|
|
27
59
|
//# sourceMappingURL=mount.d.ts.map
|
package/dist/mount.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"mount.d.ts","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AA2ChE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,UAAU;QAClB,GAAG,CAAC,EAAE;YAAE,GAAG,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KACxB;CACF;AAsBD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,KAAK,CAAA;CACpB;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC9B,SAAS,EAAE,WAAW,EACtB,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CA6FX;AAmDD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACnC,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CA6FX;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACrC,MAAM,EAAE,OAAO,EACf,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,WAAW,EAAE,CAAC,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CA+EX;AAYD,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAChC,SAAS,EAAE,WAAW,EACtB,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,WAAW,EAAE,CAAC,EACd,OAAO,CAAC,EAAE,YAAY,GACrB,SAAS,CAqEX"}
|
package/dist/mount.js
CHANGED
|
@@ -5,6 +5,37 @@ import { setFlatBindings } from './binding.js';
|
|
|
5
5
|
import { registerInstance, unregisterInstance } from './runtime.js';
|
|
6
6
|
import { createView } from './view-helpers.js';
|
|
7
7
|
import { pushMountQueue, popMountQueue, flushMountQueue } from './primitives/on-mount.js';
|
|
8
|
+
// ── Sentinel-region helpers (used by anchor-based mount primitives) ─────
|
|
9
|
+
/**
|
|
10
|
+
* Remove every sibling from `anchor.nextSibling` up to but not including
|
|
11
|
+
* `stopBefore`. Used by anchor-based mount primitives and their HMR
|
|
12
|
+
* swap path to clear the owned DOM region between the pair.
|
|
13
|
+
*/
|
|
14
|
+
function _removeBetween(anchor, stopBefore) {
|
|
15
|
+
const parent = anchor.parentNode;
|
|
16
|
+
if (parent === null)
|
|
17
|
+
return;
|
|
18
|
+
while (anchor.nextSibling !== null && anchor.nextSibling !== stopBefore) {
|
|
19
|
+
parent.removeChild(anchor.nextSibling);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Walk forward from `anchor.nextSibling` looking for an existing
|
|
24
|
+
* `<!-- llui-mount-end -->` sentinel. Used by mount/hydrate at anchor
|
|
25
|
+
* to reuse a server-emitted (or stale) sentinel rather than synthesizing
|
|
26
|
+
* a duplicate. Returns null if no matching comment is found before the
|
|
27
|
+
* end of the parent's children.
|
|
28
|
+
*/
|
|
29
|
+
function _findEndSentinel(anchor) {
|
|
30
|
+
let node = anchor.nextSibling;
|
|
31
|
+
while (node !== null) {
|
|
32
|
+
if (node.nodeType === 8 && node.nodeValue === 'llui-mount-end') {
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
node = node.nextSibling;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
8
39
|
// ── HMR (dev only) ──────────────────────────────────────────────
|
|
9
40
|
// Set by enableHmr() from '@llui/dom/hmr' — never imported in production.
|
|
10
41
|
let hmrModule = null;
|
|
@@ -161,6 +192,198 @@ function findNonSerializable(v, path = 'state', depth = 0, seen = new WeakSet())
|
|
|
161
192
|
}
|
|
162
193
|
return null;
|
|
163
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Mount a component relative to a comment anchor rather than inside a
|
|
197
|
+
* container element. Inserts a synthesized end sentinel (`<!-- llui-mount-end -->`)
|
|
198
|
+
* immediately after the anchor and places the component's nodes between
|
|
199
|
+
* the pair. The anchor must already be attached to a live DOM tree.
|
|
200
|
+
*
|
|
201
|
+
* Unlike `mountApp`, the caller's anchor node is preserved across the
|
|
202
|
+
* handle's lifetime — only the content between the pair (and the end
|
|
203
|
+
* sentinel itself) is disposed. Used by `@llui/vike` persistent layouts
|
|
204
|
+
* to mount chain layers without a wrapper element.
|
|
205
|
+
*
|
|
206
|
+
* If a pre-existing `<!-- llui-mount-end -->` is found after the anchor
|
|
207
|
+
* (e.g. stale from an undisposed prior mount), the content between the
|
|
208
|
+
* anchor and that sentinel is swept and the sentinel is reused. Dev mode
|
|
209
|
+
* warns in that case.
|
|
210
|
+
*/
|
|
211
|
+
export function mountAtAnchor(anchor, def, data, options) {
|
|
212
|
+
if (anchor.parentNode === null) {
|
|
213
|
+
throw new Error(`[LLui] mountAtAnchor: anchor comment must be attached to a live DOM tree before mount`);
|
|
214
|
+
}
|
|
215
|
+
// Locate or synthesize the end sentinel.
|
|
216
|
+
const existingEnd = _findEndSentinel(anchor);
|
|
217
|
+
let endSentinel;
|
|
218
|
+
if (existingEnd !== null) {
|
|
219
|
+
if (import.meta.env?.DEV) {
|
|
220
|
+
console.warn(`[LLui] mountAtAnchor: anchor has a pre-existing end sentinel. ` +
|
|
221
|
+
`A prior mount was not disposed — sweeping stale siblings and reusing the sentinel.`);
|
|
222
|
+
}
|
|
223
|
+
_removeBetween(anchor, existingEnd);
|
|
224
|
+
endSentinel = existingEnd;
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
endSentinel = document.createComment('llui-mount-end');
|
|
228
|
+
anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling);
|
|
229
|
+
}
|
|
230
|
+
const inst = createComponentInstance(def, data, options?.parentScope ?? null);
|
|
231
|
+
if (devToolsInstall)
|
|
232
|
+
devToolsInstall(inst);
|
|
233
|
+
if (import.meta.env?.DEV) {
|
|
234
|
+
const offender = findNonSerializable(inst.state);
|
|
235
|
+
if (offender) {
|
|
236
|
+
console.warn(`[LLui] <${def.name}> initial state contains a non-serializable value at "${offender.path}":`, offender.value, '\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +
|
|
237
|
+
'\nThis will break SSR hydration, state replay, and devtools snapshots.' +
|
|
238
|
+
'\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue();
|
|
242
|
+
setFlatBindings(inst.allBindings);
|
|
243
|
+
setRenderContext({
|
|
244
|
+
...inst,
|
|
245
|
+
container: anchor.parentElement ?? undefined,
|
|
246
|
+
send: inst.send,
|
|
247
|
+
instance: inst,
|
|
248
|
+
});
|
|
249
|
+
const nodes = def.view(createView(inst.send));
|
|
250
|
+
clearRenderContext();
|
|
251
|
+
setFlatBindings(null);
|
|
252
|
+
popMountQueue(prevMountQueue);
|
|
253
|
+
// Batch-insert via DocumentFragment — one layout pass instead of N.
|
|
254
|
+
if (nodes.length > 1) {
|
|
255
|
+
const frag = document.createDocumentFragment();
|
|
256
|
+
for (const node of nodes)
|
|
257
|
+
frag.appendChild(node);
|
|
258
|
+
anchor.parentNode.insertBefore(frag, endSentinel);
|
|
259
|
+
}
|
|
260
|
+
else if (nodes.length === 1) {
|
|
261
|
+
anchor.parentNode.insertBefore(nodes[0], endSentinel);
|
|
262
|
+
}
|
|
263
|
+
flushMountQueue(onMountQueue);
|
|
264
|
+
registerInstance(inst);
|
|
265
|
+
if (hmrModule && def.name) {
|
|
266
|
+
hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel);
|
|
267
|
+
}
|
|
268
|
+
dispatchInitialEffects(inst);
|
|
269
|
+
let disposed = false;
|
|
270
|
+
return {
|
|
271
|
+
dispose() {
|
|
272
|
+
if (disposed)
|
|
273
|
+
return;
|
|
274
|
+
disposed = true;
|
|
275
|
+
if (hmrModule && def.name)
|
|
276
|
+
hmrModule.unregisterForHmr(def.name, inst);
|
|
277
|
+
inst.abortController.abort();
|
|
278
|
+
unregisterInstance(inst);
|
|
279
|
+
inst.rootScope.disposalCause = 'app-unmount';
|
|
280
|
+
disposeScope(inst.rootScope);
|
|
281
|
+
_removeBetween(anchor, endSentinel);
|
|
282
|
+
endSentinel.parentNode?.removeChild(endSentinel);
|
|
283
|
+
},
|
|
284
|
+
flush() {
|
|
285
|
+
if (disposed)
|
|
286
|
+
return;
|
|
287
|
+
flushInstance(inst);
|
|
288
|
+
},
|
|
289
|
+
send(msg) {
|
|
290
|
+
if (disposed)
|
|
291
|
+
return;
|
|
292
|
+
inst.send(msg);
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Hydrate a component relative to a comment anchor rather than inside a
|
|
298
|
+
* container element. Analogous to `hydrateApp` — uses `serverState` as
|
|
299
|
+
* the initial state (not `init()`'s output) while preserving `init()`'s
|
|
300
|
+
* effects for post-mount dispatch.
|
|
301
|
+
*
|
|
302
|
+
* The DOM-handling path is identical to `mountAtAnchor`: reuses a
|
|
303
|
+
* pre-existing end sentinel when present, synthesizes one otherwise.
|
|
304
|
+
* Atomic-swaps the owned region whether or not server content is there
|
|
305
|
+
* to replace. No error for a missing end sentinel — the vike chain's
|
|
306
|
+
* outer `hydrateApp`'s `replaceChildren` wipes inner layers' sentinels,
|
|
307
|
+
* so inner-layer `hydrateAtAnchor` calls routinely find nothing to
|
|
308
|
+
* reuse, and that's normal.
|
|
309
|
+
*/
|
|
310
|
+
export function hydrateAtAnchor(anchor, def, serverState, options) {
|
|
311
|
+
if (anchor.parentNode === null) {
|
|
312
|
+
throw new Error(`[LLui] hydrateAtAnchor: anchor comment must be attached to a live DOM tree before hydrate`);
|
|
313
|
+
}
|
|
314
|
+
const existingEnd = _findEndSentinel(anchor);
|
|
315
|
+
let endSentinel;
|
|
316
|
+
if (existingEnd !== null) {
|
|
317
|
+
_removeBetween(anchor, existingEnd);
|
|
318
|
+
endSentinel = existingEnd;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
endSentinel = document.createComment('llui-mount-end');
|
|
322
|
+
anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling);
|
|
323
|
+
}
|
|
324
|
+
// Run original init() to capture effects, then override state with server's.
|
|
325
|
+
const [, originalEffects] = def.init(undefined);
|
|
326
|
+
const hydrateDef = {
|
|
327
|
+
...def,
|
|
328
|
+
init: () => [serverState, originalEffects],
|
|
329
|
+
};
|
|
330
|
+
const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null);
|
|
331
|
+
if (devToolsInstall)
|
|
332
|
+
devToolsInstall(inst);
|
|
333
|
+
const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue();
|
|
334
|
+
setFlatBindings(inst.allBindings);
|
|
335
|
+
setRenderContext({
|
|
336
|
+
...inst,
|
|
337
|
+
container: anchor.parentElement ?? undefined,
|
|
338
|
+
send: inst.send,
|
|
339
|
+
instance: inst,
|
|
340
|
+
});
|
|
341
|
+
const nodes = hydrateDef.view(createView(inst.send));
|
|
342
|
+
clearRenderContext();
|
|
343
|
+
setFlatBindings(null);
|
|
344
|
+
popMountQueue(prevMountQueue);
|
|
345
|
+
if (nodes.length > 1) {
|
|
346
|
+
const frag = document.createDocumentFragment();
|
|
347
|
+
for (const node of nodes)
|
|
348
|
+
frag.appendChild(node);
|
|
349
|
+
anchor.parentNode.insertBefore(frag, endSentinel);
|
|
350
|
+
}
|
|
351
|
+
else if (nodes.length === 1) {
|
|
352
|
+
anchor.parentNode.insertBefore(nodes[0], endSentinel);
|
|
353
|
+
}
|
|
354
|
+
flushMountQueue(onMountQueue);
|
|
355
|
+
registerInstance(inst);
|
|
356
|
+
if (hmrModule && def.name) {
|
|
357
|
+
hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel);
|
|
358
|
+
}
|
|
359
|
+
dispatchInitialEffects(inst);
|
|
360
|
+
let disposed = false;
|
|
361
|
+
return {
|
|
362
|
+
dispose() {
|
|
363
|
+
if (disposed)
|
|
364
|
+
return;
|
|
365
|
+
disposed = true;
|
|
366
|
+
if (hmrModule && def.name)
|
|
367
|
+
hmrModule.unregisterForHmr(def.name, inst);
|
|
368
|
+
inst.abortController.abort();
|
|
369
|
+
unregisterInstance(inst);
|
|
370
|
+
inst.rootScope.disposalCause = 'app-unmount';
|
|
371
|
+
disposeScope(inst.rootScope);
|
|
372
|
+
_removeBetween(anchor, endSentinel);
|
|
373
|
+
endSentinel.parentNode?.removeChild(endSentinel);
|
|
374
|
+
},
|
|
375
|
+
flush() {
|
|
376
|
+
if (disposed)
|
|
377
|
+
return;
|
|
378
|
+
flushInstance(inst);
|
|
379
|
+
},
|
|
380
|
+
send(msg) {
|
|
381
|
+
if (disposed)
|
|
382
|
+
return;
|
|
383
|
+
inst.send(msg);
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
164
387
|
function dispatchInitialEffects(inst) {
|
|
165
388
|
if (inst.initialEffects.length === 0 || !inst.def.onEffect)
|
|
166
389
|
return;
|
package/dist/mount.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle, Scope } from './types.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from './update-loop.js'\nimport { disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { registerInstance, unregisterInstance } from './runtime.js'\nimport { createView } from './view-helpers.js'\nimport { pushMountQueue, popMountQueue, flushMountQueue } from './primitives/on-mount.js'\n\n// Vite injects import.meta.env.DEV — declare the shape for TypeScript\ndeclare global {\n interface ImportMeta {\n env?: { DEV?: boolean }\n }\n}\n\n// ── HMR (dev only) ──────────────────────────────────────────────\n// Set by enableHmr() from '@llui/dom/hmr' — never imported in production.\n\nlet hmrModule: typeof import('./hmr') | null = null\n\n/** @internal Called by enableHmr in the hmr module */\nexport function _setHmrModule(m: typeof import('./hmr')): void {\n hmrModule = m\n}\n\n// ── DevTools auto-install (dev only) ────────────────────────────\n// Set by enableDevTools() from '@llui/dom/devtools' — never imported in production.\n\nlet devToolsInstall: ((inst: object) => void) | null = null\n\n/** @internal Called by enableDevTools in the devtools module */\nexport function _setDevToolsInstall(fn: ((inst: object) => void) | null): void {\n devToolsInstall = fn\n}\n\nexport interface MountOptions {\n devTools?: boolean\n /**\n * Parent scope for the mounted component's rootScope. When provided,\n * the rootScope is created as a child of this scope — context lookups\n * from within the component walk up through the parent's scope tree,\n * and disposing the parent scope cascades into this instance's scope.\n * Used by `@llui/vike`'s persistent-layout machinery to mount a page\n * as a true scope-tree child of its enclosing layout, so layout-\n * provided contexts flow naturally into pages via `useContext`.\n *\n * When omitted (the default), the rootScope is detached — same as\n * every `mountApp` call before persistent layouts existed.\n */\n parentScope?: Scope\n}\n\nexport function mountApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n data?: unknown,\n options?: MountOptions,\n): AppHandle {\n // HMR: if this component is already mounted (module re-execution\n // during hot update), swap the definition instead of creating a new instance.\n // HMR swap bypasses parentScope — HMR re-mounts the outermost app handle,\n // which in a layout setup means the layout re-mounts at the root and the\n // rest of the chain is re-established via the normal mount path.\n if (hmrModule && def.name && !options?.parentScope) {\n const swapped = hmrModule.replaceComponent(def.name, def)\n if (swapped) return swapped\n }\n\n const inst = createComponentInstance(def, data, options?.parentScope ?? null)\n\n // Dev-only: auto-install devtools if enabled via '@llui/dom/devtools' import\n if (devToolsInstall) devToolsInstall(inst)\n\n // Dev-only: warn if initial state contains non-serializable values.\n // Silent bug-bomb: Date/Map/Set/class instances break SSR, hydration, replay tools.\n if (import.meta.env?.DEV) {\n const offender = findNonSerializable(inst.state)\n if (offender) {\n console.warn(\n `[LLui] <${def.name}> initial state contains a non-serializable value at \"${offender.path}\":`,\n offender.value,\n '\\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +\n '\\nThis will break SSR hydration, state replay, and devtools snapshots.' +\n '\\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).',\n )\n }\n }\n\n // Run view() within a render context so primitives can register bindings.\n // Also collect onMount callbacks in a queue we'll flush synchronously\n // after node insertion — prevents the race where a user event fires\n // between mount and the queueMicrotask callback running.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Batch-insert via DocumentFragment — one layout-invalidating operation\n // instead of N individual appendChild calls on a live container element.\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n container.appendChild(frag)\n } else if (nodes.length === 1) {\n container.appendChild(nodes[0]!)\n }\n\n // Flush onMount callbacks SYNCHRONOUSLY now that the DOM is in place.\n // Any listeners they attach are ready before this function returns,\n // so a synchronous dispatchEvent in the caller's next line fires\n // against a fully-wired tree.\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForHmr(def.name, inst, container)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\n// Walks an object graph looking for non-JSON-serializable values. Returns the\n// first offender found (depth-first), or null if everything is fine. Stops at\n// depth 6 to bound runtime cost for large states.\nfunction findNonSerializable(\n v: unknown,\n path = 'state',\n depth = 0,\n seen = new WeakSet<object>(),\n): { path: string; value: unknown } | null {\n if (depth > 6) return null\n if (v === null || v === undefined) return null\n const t = typeof v\n if (t === 'string' || t === 'number' || t === 'boolean') return null\n if (t === 'function') return { path, value: v }\n if (t === 'symbol' || t === 'bigint') return { path, value: v }\n if (t !== 'object') return null\n const obj = v as object\n if (seen.has(obj)) return null\n seen.add(obj)\n if (obj instanceof Date) return { path: `${path} (Date)`, value: v }\n if (obj instanceof Map) return { path: `${path} (Map)`, value: v }\n if (obj instanceof Set) return { path: `${path} (Set)`, value: v }\n if (obj instanceof RegExp) return { path: `${path} (RegExp)`, value: v }\n if (obj instanceof Promise) return { path: `${path} (Promise)`, value: v }\n // Plain objects/arrays have Object.prototype / Array.prototype. Class instances\n // have a different prototype.\n const proto = Object.getPrototypeOf(obj)\n if (proto !== null && proto !== Object.prototype && proto !== Array.prototype) {\n return { path: `${path} (${proto?.constructor?.name ?? 'class instance'})`, value: v }\n }\n if (Array.isArray(v)) {\n for (let i = 0; i < v.length; i++) {\n const r = findNonSerializable(v[i], `${path}[${i}]`, depth + 1, seen)\n if (r) return r\n }\n return null\n }\n for (const k of Object.keys(obj)) {\n const r = findNonSerializable(\n (obj as Record<string, unknown>)[k],\n `${path}.${k}`,\n depth + 1,\n seen,\n )\n if (r) return r\n }\n return null\n}\n\nfunction dispatchInitialEffects<S, M, E>(\n inst: ReturnType<typeof createComponentInstance<S, M, E>>,\n): void {\n if (inst.initialEffects.length === 0 || !inst.def.onEffect) return\n for (const effect of inst.initialEffects) {\n inst.def.onEffect({ effect, send: inst.send, signal: inst.signal })\n }\n inst.initialEffects = []\n}\n\nexport function hydrateApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n serverState: S,\n options?: MountOptions,\n): AppHandle {\n // Run the original init once to capture its effects. The state it\n // returns is discarded — we use `serverState` (what the server\n // rendered with) instead. The effects are preserved and dispatched\n // after the DOM is in place, so components that rely on \"load data\n // or wire subscriptions on mount\" behave consistently between fresh\n // mount and SSR+hydrate. If the original init has already-loaded\n // data for the hydration case, gate the effect emission inside init\n // itself (e.g. based on a `loaded` flag in state).\n const [, originalEffects] = (def.init as (data: unknown) => [S, E[]])(undefined)\n\n const hydrateDef: ComponentDef<S, M, E> = {\n ...def,\n init: () => [serverState, originalEffects],\n }\n\n const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null)\n\n // Build the component DOM and swap atomically with server HTML.\n // Server HTML remains visible until JS finishes — no flash.\n // onMount callbacks are collected in a queue and flushed synchronously\n // after the swap, matching mountApp's ordering.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = hydrateDef.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Atomic swap — replaces server HTML with client DOM in one operation\n container.replaceChildren(...nodes)\n\n // Flush onMount callbacks synchronously now that the DOM is in place.\n flushMountQueue(onMountQueue)\n\n // Fire the original init's effects post-swap, matching mountApp's\n // lifecycle. Previously these were silently dropped.\n dispatchInitialEffects(inst)\n\n registerInstance(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"mount.js","sourceRoot":"","sources":["../src/mount.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ComponentDef, AppHandle, Scope } from './types.js'\nimport { createComponentInstance, flushInstance, type ComponentInstance } from './update-loop.js'\nimport { disposeScope } from './scope.js'\nimport { setRenderContext, clearRenderContext } from './render-context.js'\nimport { setFlatBindings } from './binding.js'\nimport { registerInstance, unregisterInstance } from './runtime.js'\nimport { createView } from './view-helpers.js'\nimport { pushMountQueue, popMountQueue, flushMountQueue } from './primitives/on-mount.js'\n\n// ── Sentinel-region helpers (used by anchor-based mount primitives) ─────\n\n/**\n * Remove every sibling from `anchor.nextSibling` up to but not including\n * `stopBefore`. Used by anchor-based mount primitives and their HMR\n * swap path to clear the owned DOM region between the pair.\n */\nfunction _removeBetween(anchor: Comment, stopBefore: Comment): void {\n const parent = anchor.parentNode\n if (parent === null) return\n while (anchor.nextSibling !== null && anchor.nextSibling !== stopBefore) {\n parent.removeChild(anchor.nextSibling)\n }\n}\n\n/**\n * Walk forward from `anchor.nextSibling` looking for an existing\n * `<!-- llui-mount-end -->` sentinel. Used by mount/hydrate at anchor\n * to reuse a server-emitted (or stale) sentinel rather than synthesizing\n * a duplicate. Returns null if no matching comment is found before the\n * end of the parent's children.\n */\nfunction _findEndSentinel(anchor: Comment): Comment | null {\n let node: Node | null = anchor.nextSibling\n while (node !== null) {\n if (node.nodeType === 8 && (node as Comment).nodeValue === 'llui-mount-end') {\n return node as Comment\n }\n node = node.nextSibling\n }\n return null\n}\n\n// Vite injects import.meta.env.DEV — declare the shape for TypeScript\ndeclare global {\n interface ImportMeta {\n env?: { DEV?: boolean }\n }\n}\n\n// ── HMR (dev only) ──────────────────────────────────────────────\n// Set by enableHmr() from '@llui/dom/hmr' — never imported in production.\n\nlet hmrModule: typeof import('./hmr') | null = null\n\n/** @internal Called by enableHmr in the hmr module */\nexport function _setHmrModule(m: typeof import('./hmr')): void {\n hmrModule = m\n}\n\n// ── DevTools auto-install (dev only) ────────────────────────────\n// Set by enableDevTools() from '@llui/dom/devtools' — never imported in production.\n\nlet devToolsInstall: ((inst: object) => void) | null = null\n\n/** @internal Called by enableDevTools in the devtools module */\nexport function _setDevToolsInstall(fn: ((inst: object) => void) | null): void {\n devToolsInstall = fn\n}\n\nexport interface MountOptions {\n devTools?: boolean\n /**\n * Parent scope for the mounted component's rootScope. When provided,\n * the rootScope is created as a child of this scope — context lookups\n * from within the component walk up through the parent's scope tree,\n * and disposing the parent scope cascades into this instance's scope.\n * Used by `@llui/vike`'s persistent-layout machinery to mount a page\n * as a true scope-tree child of its enclosing layout, so layout-\n * provided contexts flow naturally into pages via `useContext`.\n *\n * When omitted (the default), the rootScope is detached — same as\n * every `mountApp` call before persistent layouts existed.\n */\n parentScope?: Scope\n}\n\nexport function mountApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n data?: unknown,\n options?: MountOptions,\n): AppHandle {\n // HMR: if this component is already mounted (module re-execution\n // during hot update), swap the definition instead of creating a new instance.\n // HMR swap bypasses parentScope — HMR re-mounts the outermost app handle,\n // which in a layout setup means the layout re-mounts at the root and the\n // rest of the chain is re-established via the normal mount path.\n if (hmrModule && def.name && !options?.parentScope) {\n const swapped = hmrModule.replaceComponent(def.name, def)\n if (swapped) return swapped\n }\n\n const inst = createComponentInstance(def, data, options?.parentScope ?? null)\n\n // Dev-only: auto-install devtools if enabled via '@llui/dom/devtools' import\n if (devToolsInstall) devToolsInstall(inst)\n\n // Dev-only: warn if initial state contains non-serializable values.\n // Silent bug-bomb: Date/Map/Set/class instances break SSR, hydration, replay tools.\n if (import.meta.env?.DEV) {\n const offender = findNonSerializable(inst.state)\n if (offender) {\n console.warn(\n `[LLui] <${def.name}> initial state contains a non-serializable value at \"${offender.path}\":`,\n offender.value,\n '\\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +\n '\\nThis will break SSR hydration, state replay, and devtools snapshots.' +\n '\\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).',\n )\n }\n }\n\n // Run view() within a render context so primitives can register bindings.\n // Also collect onMount callbacks in a queue we'll flush synchronously\n // after node insertion — prevents the race where a user event fires\n // between mount and the queueMicrotask callback running.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Batch-insert via DocumentFragment — one layout-invalidating operation\n // instead of N individual appendChild calls on a live container element.\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n container.appendChild(frag)\n } else if (nodes.length === 1) {\n container.appendChild(nodes[0]!)\n }\n\n // Flush onMount callbacks SYNCHRONOUSLY now that the DOM is in place.\n // Any listeners they attach are ready before this function returns,\n // so a synchronous dispatchEvent in the caller's next line fires\n // against a fully-wired tree.\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForHmr(def.name, inst, container)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\n// Walks an object graph looking for non-JSON-serializable values. Returns the\n// first offender found (depth-first), or null if everything is fine. Stops at\n// depth 6 to bound runtime cost for large states.\nfunction findNonSerializable(\n v: unknown,\n path = 'state',\n depth = 0,\n seen = new WeakSet<object>(),\n): { path: string; value: unknown } | null {\n if (depth > 6) return null\n if (v === null || v === undefined) return null\n const t = typeof v\n if (t === 'string' || t === 'number' || t === 'boolean') return null\n if (t === 'function') return { path, value: v }\n if (t === 'symbol' || t === 'bigint') return { path, value: v }\n if (t !== 'object') return null\n const obj = v as object\n if (seen.has(obj)) return null\n seen.add(obj)\n if (obj instanceof Date) return { path: `${path} (Date)`, value: v }\n if (obj instanceof Map) return { path: `${path} (Map)`, value: v }\n if (obj instanceof Set) return { path: `${path} (Set)`, value: v }\n if (obj instanceof RegExp) return { path: `${path} (RegExp)`, value: v }\n if (obj instanceof Promise) return { path: `${path} (Promise)`, value: v }\n // Plain objects/arrays have Object.prototype / Array.prototype. Class instances\n // have a different prototype.\n const proto = Object.getPrototypeOf(obj)\n if (proto !== null && proto !== Object.prototype && proto !== Array.prototype) {\n return { path: `${path} (${proto?.constructor?.name ?? 'class instance'})`, value: v }\n }\n if (Array.isArray(v)) {\n for (let i = 0; i < v.length; i++) {\n const r = findNonSerializable(v[i], `${path}[${i}]`, depth + 1, seen)\n if (r) return r\n }\n return null\n }\n for (const k of Object.keys(obj)) {\n const r = findNonSerializable(\n (obj as Record<string, unknown>)[k],\n `${path}.${k}`,\n depth + 1,\n seen,\n )\n if (r) return r\n }\n return null\n}\n\n/**\n * Mount a component relative to a comment anchor rather than inside a\n * container element. Inserts a synthesized end sentinel (`<!-- llui-mount-end -->`)\n * immediately after the anchor and places the component's nodes between\n * the pair. The anchor must already be attached to a live DOM tree.\n *\n * Unlike `mountApp`, the caller's anchor node is preserved across the\n * handle's lifetime — only the content between the pair (and the end\n * sentinel itself) is disposed. Used by `@llui/vike` persistent layouts\n * to mount chain layers without a wrapper element.\n *\n * If a pre-existing `<!-- llui-mount-end -->` is found after the anchor\n * (e.g. stale from an undisposed prior mount), the content between the\n * anchor and that sentinel is swept and the sentinel is reused. Dev mode\n * warns in that case.\n */\nexport function mountAtAnchor<S, M, E>(\n anchor: Comment,\n def: ComponentDef<S, M, E>,\n data?: unknown,\n options?: MountOptions,\n): AppHandle {\n if (anchor.parentNode === null) {\n throw new Error(\n `[LLui] mountAtAnchor: anchor comment must be attached to a live DOM tree before mount`,\n )\n }\n\n // Locate or synthesize the end sentinel.\n const existingEnd = _findEndSentinel(anchor)\n let endSentinel: Comment\n if (existingEnd !== null) {\n if (import.meta.env?.DEV) {\n console.warn(\n `[LLui] mountAtAnchor: anchor has a pre-existing end sentinel. ` +\n `A prior mount was not disposed — sweeping stale siblings and reusing the sentinel.`,\n )\n }\n _removeBetween(anchor, existingEnd)\n endSentinel = existingEnd\n } else {\n endSentinel = document.createComment('llui-mount-end')\n anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling)\n }\n\n const inst = createComponentInstance(def, data, options?.parentScope ?? null)\n\n if (devToolsInstall) devToolsInstall(inst)\n\n if (import.meta.env?.DEV) {\n const offender = findNonSerializable(inst.state)\n if (offender) {\n console.warn(\n `[LLui] <${def.name}> initial state contains a non-serializable value at \"${offender.path}\":`,\n offender.value,\n '\\nState must be plain JSON (no Date/Map/Set/class instances/functions).' +\n '\\nThis will break SSR hydration, state replay, and devtools snapshots.' +\n '\\nhint: Convert to a serializable representation (e.g., Date → ISO string, Map → Record).',\n )\n }\n }\n\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container: anchor.parentElement ?? undefined,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = def.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Batch-insert via DocumentFragment — one layout pass instead of N.\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n anchor.parentNode.insertBefore(frag, endSentinel)\n } else if (nodes.length === 1) {\n anchor.parentNode.insertBefore(nodes[0]!, endSentinel)\n }\n\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n _removeBetween(anchor, endSentinel)\n endSentinel.parentNode?.removeChild(endSentinel)\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\n/**\n * Hydrate a component relative to a comment anchor rather than inside a\n * container element. Analogous to `hydrateApp` — uses `serverState` as\n * the initial state (not `init()`'s output) while preserving `init()`'s\n * effects for post-mount dispatch.\n *\n * The DOM-handling path is identical to `mountAtAnchor`: reuses a\n * pre-existing end sentinel when present, synthesizes one otherwise.\n * Atomic-swaps the owned region whether or not server content is there\n * to replace. No error for a missing end sentinel — the vike chain's\n * outer `hydrateApp`'s `replaceChildren` wipes inner layers' sentinels,\n * so inner-layer `hydrateAtAnchor` calls routinely find nothing to\n * reuse, and that's normal.\n */\nexport function hydrateAtAnchor<S, M, E>(\n anchor: Comment,\n def: ComponentDef<S, M, E>,\n serverState: S,\n options?: MountOptions,\n): AppHandle {\n if (anchor.parentNode === null) {\n throw new Error(\n `[LLui] hydrateAtAnchor: anchor comment must be attached to a live DOM tree before hydrate`,\n )\n }\n\n const existingEnd = _findEndSentinel(anchor)\n let endSentinel: Comment\n if (existingEnd !== null) {\n _removeBetween(anchor, existingEnd)\n endSentinel = existingEnd\n } else {\n endSentinel = document.createComment('llui-mount-end')\n anchor.parentNode.insertBefore(endSentinel, anchor.nextSibling)\n }\n\n // Run original init() to capture effects, then override state with server's.\n const [, originalEffects] = (def.init as (data: unknown) => [S, E[]])(undefined)\n const hydrateDef: ComponentDef<S, M, E> = {\n ...def,\n init: () => [serverState, originalEffects],\n }\n\n const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null)\n\n if (devToolsInstall) devToolsInstall(inst)\n\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container: anchor.parentElement ?? undefined,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = hydrateDef.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n if (nodes.length > 1) {\n const frag = document.createDocumentFragment()\n for (const node of nodes) frag.appendChild(node)\n anchor.parentNode.insertBefore(frag, endSentinel)\n } else if (nodes.length === 1) {\n anchor.parentNode.insertBefore(nodes[0]!, endSentinel)\n }\n\n flushMountQueue(onMountQueue)\n\n registerInstance(inst)\n if (hmrModule && def.name) {\n hmrModule.registerForAnchor(def.name, inst, anchor, endSentinel)\n }\n dispatchInitialEffects(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n if (hmrModule && def.name) hmrModule.unregisterForHmr(def.name, inst)\n inst.abortController.abort()\n unregisterInstance(inst)\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n _removeBetween(anchor, endSentinel)\n endSentinel.parentNode?.removeChild(endSentinel)\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n\nfunction dispatchInitialEffects<S, M, E>(\n inst: ReturnType<typeof createComponentInstance<S, M, E>>,\n): void {\n if (inst.initialEffects.length === 0 || !inst.def.onEffect) return\n for (const effect of inst.initialEffects) {\n inst.def.onEffect({ effect, send: inst.send, signal: inst.signal })\n }\n inst.initialEffects = []\n}\n\nexport function hydrateApp<S, M, E>(\n container: HTMLElement,\n def: ComponentDef<S, M, E>,\n serverState: S,\n options?: MountOptions,\n): AppHandle {\n // Run the original init once to capture its effects. The state it\n // returns is discarded — we use `serverState` (what the server\n // rendered with) instead. The effects are preserved and dispatched\n // after the DOM is in place, so components that rely on \"load data\n // or wire subscriptions on mount\" behave consistently between fresh\n // mount and SSR+hydrate. If the original init has already-loaded\n // data for the hydration case, gate the effect emission inside init\n // itself (e.g. based on a `loaded` flag in state).\n const [, originalEffects] = (def.init as (data: unknown) => [S, E[]])(undefined)\n\n const hydrateDef: ComponentDef<S, M, E> = {\n ...def,\n init: () => [serverState, originalEffects],\n }\n\n const inst = createComponentInstance(hydrateDef, undefined, options?.parentScope ?? null)\n\n // Build the component DOM and swap atomically with server HTML.\n // Server HTML remains visible until JS finishes — no flash.\n // onMount callbacks are collected in a queue and flushed synchronously\n // after the swap, matching mountApp's ordering.\n const { queue: onMountQueue, prev: prevMountQueue } = pushMountQueue()\n setFlatBindings(inst.allBindings)\n setRenderContext({\n ...inst,\n container,\n send: inst.send as (msg: unknown) => void,\n instance: inst as ComponentInstance,\n })\n const nodes = hydrateDef.view(createView<S, M>(inst.send))\n clearRenderContext()\n setFlatBindings(null)\n popMountQueue(prevMountQueue)\n\n // Atomic swap — replaces server HTML with client DOM in one operation\n container.replaceChildren(...nodes)\n\n // Flush onMount callbacks synchronously now that the DOM is in place.\n flushMountQueue(onMountQueue)\n\n // Fire the original init's effects post-swap, matching mountApp's\n // lifecycle. Previously these were silently dropped.\n dispatchInitialEffects(inst)\n\n registerInstance(inst)\n let disposed = false\n\n return {\n dispose() {\n if (disposed) return\n disposed = true\n inst.abortController.abort()\n unregisterInstance(inst)\n // Tag the root scope so the disposer log reports app-level\n // teardown distinct from in-tree component-unmount events.\n inst.rootScope.disposalCause = 'app-unmount'\n disposeScope(inst.rootScope)\n container.textContent = ''\n },\n flush() {\n if (disposed) return\n flushInstance(inst)\n },\n send(msg: unknown) {\n if (disposed) return\n ;(inst.send as (m: unknown) => void)(msg)\n },\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAA2C,MAAM,aAAa,CAAA;AAChF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAA2C,MAAM,aAAa,CAAA;AAChF,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAU9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAChC,CAAC,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,EACpC,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,GACrB,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CA2Dd"}
|
package/dist/primitives/slice.js
CHANGED
|
@@ -2,6 +2,7 @@ import { show as _show } from './show.js';
|
|
|
2
2
|
import { branch as _branch } from './branch.js';
|
|
3
3
|
import { each as _each } from './each.js';
|
|
4
4
|
import { text as _text } from './text.js';
|
|
5
|
+
import { unsafeHtml as _unsafeHtml } from './unsafe-html.js';
|
|
5
6
|
import { memo as _memo } from './memo.js';
|
|
6
7
|
import { selector as _selector } from './selector.js';
|
|
7
8
|
import { useContext } from './context.js';
|
|
@@ -57,6 +58,11 @@ export function slice(h, lift) {
|
|
|
57
58
|
return _text(accessor);
|
|
58
59
|
return _text((r) => accessor(lift(r)), mask);
|
|
59
60
|
},
|
|
61
|
+
unsafeHtml: (accessor, mask) => {
|
|
62
|
+
if (typeof accessor === 'string')
|
|
63
|
+
return _unsafeHtml(accessor);
|
|
64
|
+
return _unsafeHtml((r) => accessor(lift(r)), mask);
|
|
65
|
+
},
|
|
60
66
|
memo: (accessor) => {
|
|
61
67
|
const m = _memo((r) => accessor(lift(r)));
|
|
62
68
|
return (s) => m(s);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slice.js","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Send, EachOptions, ShowOptions, BranchOptions } from '../types.js'\nimport type { View } from '../view-helpers.js'\nimport { show as _show } from './show.js'\nimport { branch as _branch } from './branch.js'\nimport { each as _each } from './each.js'\nimport { text as _text } from './text.js'\nimport { memo as _memo } from './memo.js'\nimport { selector as _selector } from './selector.js'\nimport { useContext, type Context } from './context.js'\n\n/**\n * Build a `View<Sub, M>` that composes a selector into every state-bound\n * accessor. Used to write view-functions over a sub-slice of parent state:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n *\n * view: (h) => {\n * const formView = slice(h, (s) => s.form)\n * return [...formView.show({ when: f => f.valid, render: (h) => [...] })]\n * }\n * ```\n *\n * Kept as a standalone function rather than a method on the View bundle so\n * apps that don't use it don't pay for its bundle cost — tree-shaken when\n * unused.\n */\nexport function slice<Root, Sub, M>(\n h: View<Root, M> | { send: Send<M> },\n lift: (r: Root) => Sub,\n): View<Sub, M> {\n const send = h.send\n\n // Wrap a Sub-typed case callback to work with the Root-typed primitive.\n // The inner callback receives a View<Root, M> from the primitive, and we\n // narrow it to View<Sub, M> via a recursive slice() call.\n const wrapCase =\n (fn: (h: View<Sub, M>) => Node[]) =>\n (rootH: View<Root, M>): Node[] =>\n fn(slice(rootH, lift))\n\n const wrapCases = (\n cases: Record<string | number, (h: View<Sub, M>) => Node[]>,\n ): Record<string | number, (h: View<Root, M>) => Node[]> => {\n const out: Record<string | number, (h: View<Root, M>) => Node[]> = {}\n for (const key of Object.keys(cases)) {\n out[key] = wrapCase(cases[key]!)\n }\n return out\n }\n\n return {\n send,\n show: (opts: ShowOptions<Sub, M>) =>\n _show<Root, M>({\n ...opts,\n when: (r) => opts.when(lift(r)),\n render: wrapCase(opts.render),\n fallback: opts.fallback ? wrapCase(opts.fallback) : undefined,\n }),\n branch: (opts: BranchOptions<Sub, M>) =>\n _branch<Root, M>({\n ...opts,\n on: (r) => opts.on(lift(r)),\n cases: wrapCases(opts.cases),\n }),\n each: <T>(opts: EachOptions<Sub, T, M>) =>\n _each<Root, T, M>({\n ...opts,\n items: (r) => opts.items(lift(r)),\n }),\n text: (accessor, mask) => {\n if (typeof accessor === 'string') return _text(accessor)\n return _text<Root>((r) => accessor(lift(r)), mask)\n },\n memo: <T>(accessor: (s: Sub) => T) => {\n const m = _memo<Root, T>((r) => accessor(lift(r)))\n return (s: Sub) => m(s as unknown as Root)\n },\n selector: <V>(field: (s: Sub) => V) => _selector<Root, V>((r) => field(lift(r))),\n ctx: <T>(c: Context<T>) => {\n const root = useContext<Root, T>(c)\n return (s: Sub) => root(s as unknown as Root)\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"slice.js","sourceRoot":"","sources":["../../src/primitives/slice.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,UAAU,IAAI,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC5D,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAA;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,KAAK,CACnB,CAAoC,EACpC,IAAsB;IAEtB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAA;IAEnB,wEAAwE;IACxE,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,QAAQ,GACZ,CAAC,EAA+B,EAAE,EAAE,CACpC,CAAC,KAAoB,EAAU,EAAE,CAC/B,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;IAE1B,MAAM,SAAS,GAAG,CAChB,KAA2D,EACJ,EAAE;QACzD,MAAM,GAAG,GAA0D,EAAE,CAAA;QACrE,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACrC,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAE,CAAC,CAAA;QAClC,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC,CAAA;IAED,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,CAAC,IAAyB,EAAE,EAAE,CAClC,KAAK,CAAU;YACb,GAAG,IAAI;YACP,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YAC7B,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS;SAC9D,CAAC;QACJ,MAAM,EAAE,CAAC,IAA2B,EAAE,EAAE,CACtC,OAAO,CAAU;YACf,GAAG,IAAI;YACP,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC3B,KAAK,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;SAC7B,CAAC;QACJ,IAAI,EAAE,CAAI,IAA4B,EAAE,EAAE,CACxC,KAAK,CAAa;YAChB,GAAG,IAAI;YACP,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAClC,CAAC;QACJ,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE;YACvB,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAAE,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAA;YACxD,OAAO,KAAK,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;QACpD,CAAC;QACD,UAAU,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE;YAC7B,IAAI,OAAO,QAAQ,KAAK,QAAQ;gBAAE,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAA;YAC9D,OAAO,WAAW,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;QAC1D,CAAC;QACD,IAAI,EAAE,CAAI,QAAuB,EAAE,EAAE;YACnC,MAAM,CAAC,GAAG,KAAK,CAAU,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YAClD,OAAO,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAoB,CAAC,CAAA;QAC5C,CAAC;QACD,QAAQ,EAAE,CAAI,KAAoB,EAAE,EAAE,CAAC,SAAS,CAAU,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAChF,GAAG,EAAE,CAAI,CAAa,EAAE,EAAE;YACxB,MAAM,IAAI,GAAG,UAAU,CAAU,CAAC,CAAC,CAAA;YACnC,OAAO,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,CAAoB,CAAC,CAAA;QAC/C,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import type { Send, EachOptions, ShowOptions, BranchOptions } from '../types.js'\nimport type { View } from '../view-helpers.js'\nimport { show as _show } from './show.js'\nimport { branch as _branch } from './branch.js'\nimport { each as _each } from './each.js'\nimport { text as _text } from './text.js'\nimport { unsafeHtml as _unsafeHtml } from './unsafe-html.js'\nimport { memo as _memo } from './memo.js'\nimport { selector as _selector } from './selector.js'\nimport { useContext, type Context } from './context.js'\n\n/**\n * Build a `View<Sub, M>` that composes a selector into every state-bound\n * accessor. Used to write view-functions over a sub-slice of parent state:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n *\n * view: (h) => {\n * const formView = slice(h, (s) => s.form)\n * return [...formView.show({ when: f => f.valid, render: (h) => [...] })]\n * }\n * ```\n *\n * Kept as a standalone function rather than a method on the View bundle so\n * apps that don't use it don't pay for its bundle cost — tree-shaken when\n * unused.\n */\nexport function slice<Root, Sub, M>(\n h: View<Root, M> | { send: Send<M> },\n lift: (r: Root) => Sub,\n): View<Sub, M> {\n const send = h.send\n\n // Wrap a Sub-typed case callback to work with the Root-typed primitive.\n // The inner callback receives a View<Root, M> from the primitive, and we\n // narrow it to View<Sub, M> via a recursive slice() call.\n const wrapCase =\n (fn: (h: View<Sub, M>) => Node[]) =>\n (rootH: View<Root, M>): Node[] =>\n fn(slice(rootH, lift))\n\n const wrapCases = (\n cases: Record<string | number, (h: View<Sub, M>) => Node[]>,\n ): Record<string | number, (h: View<Root, M>) => Node[]> => {\n const out: Record<string | number, (h: View<Root, M>) => Node[]> = {}\n for (const key of Object.keys(cases)) {\n out[key] = wrapCase(cases[key]!)\n }\n return out\n }\n\n return {\n send,\n show: (opts: ShowOptions<Sub, M>) =>\n _show<Root, M>({\n ...opts,\n when: (r) => opts.when(lift(r)),\n render: wrapCase(opts.render),\n fallback: opts.fallback ? wrapCase(opts.fallback) : undefined,\n }),\n branch: (opts: BranchOptions<Sub, M>) =>\n _branch<Root, M>({\n ...opts,\n on: (r) => opts.on(lift(r)),\n cases: wrapCases(opts.cases),\n }),\n each: <T>(opts: EachOptions<Sub, T, M>) =>\n _each<Root, T, M>({\n ...opts,\n items: (r) => opts.items(lift(r)),\n }),\n text: (accessor, mask) => {\n if (typeof accessor === 'string') return _text(accessor)\n return _text<Root>((r) => accessor(lift(r)), mask)\n },\n unsafeHtml: (accessor, mask) => {\n if (typeof accessor === 'string') return _unsafeHtml(accessor)\n return _unsafeHtml<Root>((r) => accessor(lift(r)), mask)\n },\n memo: <T>(accessor: (s: Sub) => T) => {\n const m = _memo<Root, T>((r) => accessor(lift(r)))\n return (s: Sub) => m(s as unknown as Root)\n },\n selector: <V>(field: (s: Sub) => V) => _selector<Root, V>((r) => field(lift(r))),\n ctx: <T>(c: Context<T>) => {\n const root = useContext<Root, T>(c)\n return (s: Sub) => root(s as unknown as Root)\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insert raw HTML into the DOM tree.
|
|
3
|
+
*
|
|
4
|
+
* **Security.** The caller is responsible for sanitizing `html`. Never
|
|
5
|
+
* interpolate unsanitized user input — this is the XSS escape hatch and
|
|
6
|
+
* the name carries the warning deliberately.
|
|
7
|
+
*
|
|
8
|
+
* **Opaque to the framework.** The parsed subtree is treated as a black
|
|
9
|
+
* box by LLui — no bindings, events, `each`, `show`, or `child` inside
|
|
10
|
+
* it will be discovered or driven by the framework. Use element helpers
|
|
11
|
+
* for anything that needs to react to state or dispatch messages.
|
|
12
|
+
*
|
|
13
|
+
* **Static form.** `unsafeHtml('<b>hi</b>')` parses once at view time
|
|
14
|
+
* and returns the parsed nodes. Zero reactive overhead.
|
|
15
|
+
*
|
|
16
|
+
* **Reactive form.** `unsafeHtml((s) => s.markdownHtml)` registers a
|
|
17
|
+
* structural block that re-parses when the returned string differs
|
|
18
|
+
* (strict `===`) from the previous value. Unchanged strings short-
|
|
19
|
+
* circuit — existing DOM identity (focus, selection, listeners
|
|
20
|
+
* attached outside LLui) is preserved.
|
|
21
|
+
*
|
|
22
|
+
* **Mask hint.** The second parameter mirrors `text()`: the compiler
|
|
23
|
+
* passes a precise mask derived from which state fields the accessor
|
|
24
|
+
* reads. Phase 2 / Phase 1 skip the reconcile when no interesting bit
|
|
25
|
+
* is set. Defaults to FULL_MASK for hand-written components.
|
|
26
|
+
*/
|
|
27
|
+
export declare function unsafeHtml<S>(accessor: ((s: S) => string) | string, mask?: number): Node[];
|
|
28
|
+
//# sourceMappingURL=unsafe-html.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsafe-html.d.ts","sourceRoot":"","sources":["../../src/primitives/unsafe-html.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,CA8C1F"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getRenderContext } from '../render-context.js';
|
|
2
|
+
import { FULL_MASK } from '../update-loop.js';
|
|
3
|
+
/**
|
|
4
|
+
* Insert raw HTML into the DOM tree.
|
|
5
|
+
*
|
|
6
|
+
* **Security.** The caller is responsible for sanitizing `html`. Never
|
|
7
|
+
* interpolate unsanitized user input — this is the XSS escape hatch and
|
|
8
|
+
* the name carries the warning deliberately.
|
|
9
|
+
*
|
|
10
|
+
* **Opaque to the framework.** The parsed subtree is treated as a black
|
|
11
|
+
* box by LLui — no bindings, events, `each`, `show`, or `child` inside
|
|
12
|
+
* it will be discovered or driven by the framework. Use element helpers
|
|
13
|
+
* for anything that needs to react to state or dispatch messages.
|
|
14
|
+
*
|
|
15
|
+
* **Static form.** `unsafeHtml('<b>hi</b>')` parses once at view time
|
|
16
|
+
* and returns the parsed nodes. Zero reactive overhead.
|
|
17
|
+
*
|
|
18
|
+
* **Reactive form.** `unsafeHtml((s) => s.markdownHtml)` registers a
|
|
19
|
+
* structural block that re-parses when the returned string differs
|
|
20
|
+
* (strict `===`) from the previous value. Unchanged strings short-
|
|
21
|
+
* circuit — existing DOM identity (focus, selection, listeners
|
|
22
|
+
* attached outside LLui) is preserved.
|
|
23
|
+
*
|
|
24
|
+
* **Mask hint.** The second parameter mirrors `text()`: the compiler
|
|
25
|
+
* passes a precise mask derived from which state fields the accessor
|
|
26
|
+
* reads. Phase 2 / Phase 1 skip the reconcile when no interesting bit
|
|
27
|
+
* is set. Defaults to FULL_MASK for hand-written components.
|
|
28
|
+
*/
|
|
29
|
+
export function unsafeHtml(accessor, mask) {
|
|
30
|
+
if (typeof accessor === 'string') {
|
|
31
|
+
return parseHtmlToNodes(accessor);
|
|
32
|
+
}
|
|
33
|
+
const ctx = getRenderContext('unsafeHtml');
|
|
34
|
+
const blocks = ctx.structuralBlocks;
|
|
35
|
+
const blockMask = mask ?? FULL_MASK;
|
|
36
|
+
const anchor = document.createComment('unsafeHtml');
|
|
37
|
+
let currentHtml = accessor(ctx.state);
|
|
38
|
+
let currentNodes = parseHtmlToNodes(currentHtml);
|
|
39
|
+
const block = {
|
|
40
|
+
mask: blockMask,
|
|
41
|
+
reconcile(state) {
|
|
42
|
+
const newHtml = accessor(state);
|
|
43
|
+
// Identity short-circuit — don't rebuild the subtree when the
|
|
44
|
+
// author handed us the same string. Preserves focus, selection,
|
|
45
|
+
// and any listeners outside LLui's view of the world.
|
|
46
|
+
if (newHtml === currentHtml)
|
|
47
|
+
return;
|
|
48
|
+
const parent = anchor.parentNode;
|
|
49
|
+
if (!parent)
|
|
50
|
+
return;
|
|
51
|
+
const oldNodes = currentNodes;
|
|
52
|
+
currentNodes = parseHtmlToNodes(newHtml);
|
|
53
|
+
currentHtml = newHtml;
|
|
54
|
+
// The anchor sits immediately before the current node range, so
|
|
55
|
+
// `anchor.nextSibling` is the first old node (or whatever follows
|
|
56
|
+
// when the old list is empty). Insert new nodes there, then
|
|
57
|
+
// remove the old ones — matches `branch()`'s enter/leave order so
|
|
58
|
+
// transitions can inspect both sets.
|
|
59
|
+
const ref = anchor.nextSibling;
|
|
60
|
+
for (const node of currentNodes) {
|
|
61
|
+
parent.insertBefore(node, ref);
|
|
62
|
+
}
|
|
63
|
+
for (const node of oldNodes) {
|
|
64
|
+
if (node.parentNode)
|
|
65
|
+
node.parentNode.removeChild(node);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
blocks.push(block);
|
|
70
|
+
return [anchor, ...currentNodes];
|
|
71
|
+
}
|
|
72
|
+
function parseHtmlToNodes(html) {
|
|
73
|
+
// `<template>` parses into an inert DocumentFragment without running
|
|
74
|
+
// scripts, resolving images, or firing connection callbacks. The
|
|
75
|
+
// childNodes live reference is stable until we move the nodes out.
|
|
76
|
+
const template = document.createElement('template');
|
|
77
|
+
template.innerHTML = html;
|
|
78
|
+
// Snapshot — moving nodes to a parent drains template.content, but
|
|
79
|
+
// callers hold this array and we iterate it on reconcile.
|
|
80
|
+
return Array.from(template.content.childNodes);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=unsafe-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unsafe-html.js","sourceRoot":"","sources":["../../src/primitives/unsafe-html.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,UAAU,CAAI,QAAqC,EAAE,IAAa;IAChF,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAA;IACnC,CAAC;IAED,MAAM,GAAG,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAA;IACnC,MAAM,SAAS,GAAG,IAAI,IAAI,SAAS,CAAA;IAEnC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,YAAY,CAAC,CAAA;IACnD,IAAI,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAU,CAAC,CAAA;IAC1C,IAAI,YAAY,GAAW,gBAAgB,CAAC,WAAW,CAAC,CAAA;IAExD,MAAM,KAAK,GAAoB;QAC7B,IAAI,EAAE,SAAS;QACf,SAAS,CAAC,KAAc;YACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAU,CAAC,CAAA;YACpC,8DAA8D;YAC9D,gEAAgE;YAChE,sDAAsD;YACtD,IAAI,OAAO,KAAK,WAAW;gBAAE,OAAM;YAEnC,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAA;YAChC,IAAI,CAAC,MAAM;gBAAE,OAAM;YAEnB,MAAM,QAAQ,GAAG,YAAY,CAAA;YAC7B,YAAY,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;YACxC,WAAW,GAAG,OAAO,CAAA;YAErB,gEAAgE;YAChE,kEAAkE;YAClE,4DAA4D;YAC5D,kEAAkE;YAClE,qCAAqC;YACrC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAA;YAC9B,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;gBAChC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;YAChC,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,IAAI,IAAI,CAAC,UAAU;oBAAE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;YACxD,CAAC;QACH,CAAC;KACF,CAAA;IACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAElB,OAAO,CAAC,MAAM,EAAE,GAAG,YAAY,CAAC,CAAA;AAClC,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,qEAAqE;IACrE,iEAAiE;IACjE,mEAAmE;IACnE,MAAM,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAA;IACnD,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAA;IACzB,mEAAmE;IACnE,0DAA0D;IAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAChD,CAAC","sourcesContent":["import type { StructuralBlock } from '../structural.js'\nimport { getRenderContext } from '../render-context.js'\nimport { FULL_MASK } from '../update-loop.js'\n\n/**\n * Insert raw HTML into the DOM tree.\n *\n * **Security.** The caller is responsible for sanitizing `html`. Never\n * interpolate unsanitized user input — this is the XSS escape hatch and\n * the name carries the warning deliberately.\n *\n * **Opaque to the framework.** The parsed subtree is treated as a black\n * box by LLui — no bindings, events, `each`, `show`, or `child` inside\n * it will be discovered or driven by the framework. Use element helpers\n * for anything that needs to react to state or dispatch messages.\n *\n * **Static form.** `unsafeHtml('<b>hi</b>')` parses once at view time\n * and returns the parsed nodes. Zero reactive overhead.\n *\n * **Reactive form.** `unsafeHtml((s) => s.markdownHtml)` registers a\n * structural block that re-parses when the returned string differs\n * (strict `===`) from the previous value. Unchanged strings short-\n * circuit — existing DOM identity (focus, selection, listeners\n * attached outside LLui) is preserved.\n *\n * **Mask hint.** The second parameter mirrors `text()`: the compiler\n * passes a precise mask derived from which state fields the accessor\n * reads. Phase 2 / Phase 1 skip the reconcile when no interesting bit\n * is set. Defaults to FULL_MASK for hand-written components.\n */\nexport function unsafeHtml<S>(accessor: ((s: S) => string) | string, mask?: number): Node[] {\n if (typeof accessor === 'string') {\n return parseHtmlToNodes(accessor)\n }\n\n const ctx = getRenderContext('unsafeHtml')\n const blocks = ctx.structuralBlocks\n const blockMask = mask ?? FULL_MASK\n\n const anchor = document.createComment('unsafeHtml')\n let currentHtml = accessor(ctx.state as S)\n let currentNodes: Node[] = parseHtmlToNodes(currentHtml)\n\n const block: StructuralBlock = {\n mask: blockMask,\n reconcile(state: unknown) {\n const newHtml = accessor(state as S)\n // Identity short-circuit — don't rebuild the subtree when the\n // author handed us the same string. Preserves focus, selection,\n // and any listeners outside LLui's view of the world.\n if (newHtml === currentHtml) return\n\n const parent = anchor.parentNode\n if (!parent) return\n\n const oldNodes = currentNodes\n currentNodes = parseHtmlToNodes(newHtml)\n currentHtml = newHtml\n\n // The anchor sits immediately before the current node range, so\n // `anchor.nextSibling` is the first old node (or whatever follows\n // when the old list is empty). Insert new nodes there, then\n // remove the old ones — matches `branch()`'s enter/leave order so\n // transitions can inspect both sets.\n const ref = anchor.nextSibling\n for (const node of currentNodes) {\n parent.insertBefore(node, ref)\n }\n for (const node of oldNodes) {\n if (node.parentNode) node.parentNode.removeChild(node)\n }\n },\n }\n blocks.push(block)\n\n return [anchor, ...currentNodes]\n}\n\nfunction parseHtmlToNodes(html: string): Node[] {\n // `<template>` parses into an inert DocumentFragment without running\n // scripts, resolving images, or firing connection callbacks. The\n // childNodes live reference is stable until we move the nodes out.\n const template = document.createElement('template')\n template.innerHTML = html\n // Snapshot — moving nodes to a parent drains template.content, but\n // callers hold this array and we iterate it on reconcile.\n return Array.from(template.content.childNodes)\n}\n"]}
|
package/dist/view-helpers.d.ts
CHANGED
|
@@ -32,6 +32,12 @@ export interface View<S, M> {
|
|
|
32
32
|
branch(opts: BranchOptions<S, M>): Node[];
|
|
33
33
|
each<T>(opts: EachOptions<S, T, M>): Node[];
|
|
34
34
|
text(accessor: ((s: S) => string) | string, mask?: number): Text;
|
|
35
|
+
/**
|
|
36
|
+
* Insert raw HTML into the tree. Caller is responsible for sanitizing.
|
|
37
|
+
* The parsed subtree is opaque to LLui — no nested bindings, events,
|
|
38
|
+
* or primitives inside it will be tracked. See `unsafeHtml` for details.
|
|
39
|
+
*/
|
|
40
|
+
unsafeHtml(accessor: ((s: S) => string) | string, mask?: number): Node[];
|
|
35
41
|
memo<T>(accessor: (s: S) => T): (s: S) => T;
|
|
36
42
|
selector<V>(field: (s: S) => V): SelectorInstance<V>;
|
|
37
43
|
ctx<T>(c: Context<T>): (s: S) => T;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-helpers.d.ts","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"view-helpers.d.ts","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAO/E,OAAO,EAAyB,KAAK,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAElE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC;IACxB,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;IACb,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IACrC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IACzC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IAC3C,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChE;;;;OAIG;IACH,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC,GAAG,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,CAAA;IACxE,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;IAC3C,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;IACpD,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;CACnC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAc1D"}
|
package/dist/view-helpers.js
CHANGED
|
@@ -2,6 +2,7 @@ import { show as _show } from './primitives/show.js';
|
|
|
2
2
|
import { branch as _branch } from './primitives/branch.js';
|
|
3
3
|
import { each as _each } from './primitives/each.js';
|
|
4
4
|
import { text as _text } from './primitives/text.js';
|
|
5
|
+
import { unsafeHtml as _unsafeHtml } from './primitives/unsafe-html.js';
|
|
5
6
|
import { memo as _memo } from './primitives/memo.js';
|
|
6
7
|
import { selector as _selector } from './primitives/selector.js';
|
|
7
8
|
import { useContext } from './primitives/context.js';
|
|
@@ -16,6 +17,7 @@ export function createView(send) {
|
|
|
16
17
|
branch: (opts) => _branch(opts),
|
|
17
18
|
each: (opts) => _each(opts),
|
|
18
19
|
text: (accessor, mask) => typeof accessor === 'string' ? _text(accessor) : _text(accessor, mask),
|
|
20
|
+
unsafeHtml: (accessor, mask) => typeof accessor === 'string' ? _unsafeHtml(accessor) : _unsafeHtml(accessor, mask),
|
|
19
21
|
memo: (accessor) => _memo(accessor),
|
|
20
22
|
selector: (field) => _selector(field),
|
|
21
23
|
ctx: (c) => useContext(c),
|
package/dist/view-helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-helpers.js","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAyB,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAE,UAAU,EAAgB,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"view-helpers.js","sourceRoot":"","sources":["../src/view-helpers.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,UAAU,IAAI,WAAW,EAAE,MAAM,6BAA6B,CAAA;AACvE,OAAO,EAAE,IAAI,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,QAAQ,IAAI,SAAS,EAAyB,MAAM,0BAA0B,CAAA;AACvF,OAAO,EAAE,UAAU,EAAgB,MAAM,yBAAyB,CAAA;AA4ClE;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAO,IAAa;IAC5C,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAO,IAAI,CAAC;QACjC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAO,IAAI,CAAC;QACrC,IAAI,EAAE,CAAI,IAA0B,EAAE,EAAE,CAAC,KAAK,CAAU,IAAI,CAAC;QAC7D,IAAI,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CACvB,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAI,QAAQ,EAAE,IAAI,CAAC;QAC3E,UAAU,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAC7B,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAI,QAAQ,EAAE,IAAI,CAAC;QACvF,IAAI,EAAE,CAAI,QAAqB,EAAE,EAAE,CAAC,KAAK,CAAO,QAAQ,CAAC;QACzD,QAAQ,EAAE,CAAI,KAAkB,EAAE,EAAE,CAAC,SAAS,CAAO,KAAK,CAAC;QAC3D,GAAG,EAAE,CAAI,CAAa,EAAE,EAAE,CAAC,UAAU,CAAO,CAAC,CAAC;KAC/C,CAAA;AACH,CAAC","sourcesContent":["import type { Send, ShowOptions, BranchOptions, EachOptions } from './types.js'\nimport { show as _show } from './primitives/show.js'\nimport { branch as _branch } from './primitives/branch.js'\nimport { each as _each } from './primitives/each.js'\nimport { text as _text } from './primitives/text.js'\nimport { unsafeHtml as _unsafeHtml } from './primitives/unsafe-html.js'\nimport { memo as _memo } from './primitives/memo.js'\nimport { selector as _selector, type SelectorInstance } from './primitives/selector.js'\nimport { useContext, type Context } from './primitives/context.js'\n\n/**\n * Typed view helpers bound to a component's `State` / `Msg`. The sole\n * argument to `view`, so every state-bound primitive infers `State` from\n * the component definition — no per-call `show<State>(...)` annotation.\n *\n * ```ts\n * view: ({ send, show, text }) => [\n * ...show({ when: s => s.count > 0, render: () => [...] }),\n * text(s => String(s.count)),\n * ]\n * ```\n *\n * Tip: to view-function over a sub-slice of parent state, import `slice`\n * as a standalone helper:\n *\n * ```ts\n * import { slice } from '@llui/dom'\n * const form = slice(h, s => s.form) // returns View<FormState, Msg>\n * ```\n *\n * The Vite plugin's mask-injection pass recognizes all three call forms\n * equivalently: `h.text(...)` (member expression), `text(...)` (destructured\n * alias), and `text(...)` (bare import from `@llui/dom`). No per-binding\n * gating is lost when calling through `h`.\n */\nexport interface View<S, M> {\n send: Send<M>\n show(opts: ShowOptions<S, M>): Node[]\n branch(opts: BranchOptions<S, M>): Node[]\n each<T>(opts: EachOptions<S, T, M>): Node[]\n text(accessor: ((s: S) => string) | string, mask?: number): Text\n /**\n * Insert raw HTML into the tree. Caller is responsible for sanitizing.\n * The parsed subtree is opaque to LLui — no nested bindings, events,\n * or primitives inside it will be tracked. See `unsafeHtml` for details.\n */\n unsafeHtml(accessor: ((s: S) => string) | string, mask?: number): Node[]\n memo<T>(accessor: (s: S) => T): (s: S) => T\n selector<V>(field: (s: S) => V): SelectorInstance<V>\n ctx<T>(c: Context<T>): (s: S) => T\n}\n\n/**\n * Create a `View<S, M>` bundle for a component's `view` callback.\n * Delegates straight to the underlying primitives — zero per-call overhead.\n */\nexport function createView<S, M>(send: Send<M>): View<S, M> {\n return {\n send,\n show: (opts) => _show<S, M>(opts),\n branch: (opts) => _branch<S, M>(opts),\n each: <T>(opts: EachOptions<S, T, M>) => _each<S, T, M>(opts),\n text: (accessor, mask) =>\n typeof accessor === 'string' ? _text(accessor) : _text<S>(accessor, mask),\n unsafeHtml: (accessor, mask) =>\n typeof accessor === 'string' ? _unsafeHtml(accessor) : _unsafeHtml<S>(accessor, mask),\n memo: <T>(accessor: (s: S) => T) => _memo<S, T>(accessor),\n selector: <V>(field: (s: S) => V) => _selector<S, V>(field),\n ctx: <T>(c: Context<T>) => useContext<S, T>(c),\n }\n}\n"]}
|