@vmz/core 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/client-nav.d.ts +5 -55
- package/dist/browser/client-nav.js +5 -769
- package/dist/browser/diagnostics/precision-trace.d.ts +47 -0
- package/dist/browser/diagnostics/precision-trace.js +86 -0
- package/dist/browser/dom/attributes.d.ts +5 -0
- package/dist/browser/dom/attributes.js +102 -0
- package/dist/browser/dom/component.d.ts +11 -0
- package/dist/browser/dom/component.js +255 -0
- package/dist/browser/dom/direct-api.d.ts +66 -0
- package/dist/browser/dom/direct-api.js +446 -0
- package/dist/browser/dom/lifecycle.d.ts +9 -0
- package/dist/browser/dom/lifecycle.js +161 -0
- package/dist/browser/dom/registry.d.ts +5 -0
- package/dist/browser/dom/registry.js +16 -0
- package/dist/browser/dom-core.d.ts +51 -249
- package/dist/browser/dom-core.js +51 -2521
- package/dist/browser/navigation/client-navigation.d.ts +44 -0
- package/dist/browser/navigation/client-navigation.js +277 -0
- package/dist/browser/navigation/extract-html.d.ts +1 -0
- package/dist/browser/navigation/extract-html.js +5 -0
- package/dist/browser/navigation/layout-retain.d.ts +3 -0
- package/dist/browser/navigation/layout-retain.js +34 -0
- package/dist/browser/navigation/locale-transition.d.ts +24 -0
- package/dist/browser/navigation/locale-transition.js +323 -0
- package/dist/browser/navigation/scroll-focus.d.ts +14 -0
- package/dist/browser/navigation/scroll-focus.js +87 -0
- package/dist/browser/reactivity/patch-scheduler.d.ts +12 -0
- package/dist/browser/reactivity/patch-scheduler.js +720 -0
- package/dist/browser/reactivity/write-barrier.d.ts +16 -0
- package/dist/browser/reactivity/write-barrier.js +546 -0
- package/dist/browser/resume/adopt.d.ts +1 -0
- package/dist/browser/resume/adopt.js +168 -0
- package/dist/browser/resume/hydrate.d.ts +4 -0
- package/dist/browser/resume/hydrate.js +107 -0
- package/dist/browser/resume/resume.d.ts +3 -0
- package/dist/browser/resume/resume.js +102 -0
- package/dist/faces/dom.browser.d.ts +2 -1
- package/dist/faces/dom.browser.js +2 -1
- package/dist/host/{deployment-registry.d.ts → deployment/deployment-registry.d.ts} +1 -1
- package/dist/host/{deployment-registry.js → deployment/deployment-registry.js} +1 -1
- package/dist/host/list-client-components.d.ts +2 -2
- package/dist/host/list-client-components.js +4 -4
- package/dist/host/{render-host.d.ts → render/render-host.d.ts} +2 -2
- package/dist/host/{render-host.js → render/render-host.js} +1 -1
- package/dist/host/{route-layout-chain.js → render/route-layout-chain.js} +1 -1
- package/dist/host/serve/bootstrap.d.ts +1 -0
- package/dist/host/serve/bootstrap.js +25 -0
- package/dist/host/serve/constants.d.ts +6 -0
- package/dist/host/serve/constants.js +6 -0
- package/dist/host/serve/hmr-dev.d.ts +34 -0
- package/dist/host/serve/hmr-dev.js +294 -0
- package/dist/host/serve/http.d.ts +1 -0
- package/dist/host/serve/http.js +124 -0
- package/dist/host/serve/locale-theme.d.ts +19 -0
- package/dist/host/serve/locale-theme.js +169 -0
- package/dist/host/serve/page-catalog.d.ts +14 -0
- package/dist/host/serve/page-catalog.js +105 -0
- package/dist/host/serve/page-render.d.ts +17 -0
- package/dist/host/serve/page-render.js +419 -0
- package/dist/host/serve/paths.d.ts +3 -0
- package/dist/host/serve/paths.js +74 -0
- package/dist/host/serve/route-catalog.d.ts +6 -0
- package/dist/host/serve/route-catalog.js +108 -0
- package/dist/host/serve/state.d.ts +30 -0
- package/dist/host/serve/state.js +36 -0
- package/dist/host/serve-host.d.ts +0 -17
- package/dist/host/serve-host.mjs +4 -1420
- package/dist/ssr/document.d.ts +2 -0
- package/dist/ssr/document.js +71 -0
- package/dist/ssr/dom-ssr.d.ts +10 -47
- package/dist/ssr/dom-ssr.js +10 -1284
- package/dist/ssr/render.d.ts +2 -0
- package/dist/ssr/render.js +78 -0
- package/dist/ssr/serialize.d.ts +3 -0
- package/dist/ssr/serialize.js +678 -0
- package/package.json +11 -11
- /package/dist/host/{route-layout-chain.d.ts → render/route-layout-chain.d.ts} +0 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { directHostBoxStyleAttr } from '../browser/direct-host-box.js';
|
|
2
|
+
import { BOOLEAN_HTML_ATTRS, createInstance, eventPropHandlerName, getRegisteredComponent, isEventEntryStrategy, mergeClassParts, mergeStyleParts, snapshotInstanceState, stripFns, } from '../browser/dom-core.js';
|
|
3
|
+
import { serializeUnknownComponentNode } from '../browser/unknown-component.js';
|
|
4
|
+
import { _ssrDocumentLastError, ensureSsrDocument } from './document.js';
|
|
5
|
+
function runDirectSerializeTree(Component, inst) {
|
|
6
|
+
serializeApi._inst = inst;
|
|
7
|
+
try {
|
|
8
|
+
return Component.__vmzCreate.call(inst, serializeApi);
|
|
9
|
+
}
|
|
10
|
+
finally {
|
|
11
|
+
serializeApi._inst = null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export async function runDirectSerializeTreeWithMounts(Component, inst) {
|
|
15
|
+
let preMounted = [];
|
|
16
|
+
let tree = null;
|
|
17
|
+
for (let round = 0; round < 32; round++) {
|
|
18
|
+
serializeApi._ssrPreMounted = preMounted;
|
|
19
|
+
serializeApi._ssrPreIdx = 0;
|
|
20
|
+
serializeApi._ssrCollected = [];
|
|
21
|
+
tree = runDirectSerializeTree(Component, inst);
|
|
22
|
+
if (serializeApi._ssrPreIdx !== preMounted.length) {
|
|
23
|
+
throw new Error(`vmz:dom SSR child mount queue desync (used ${serializeApi._ssrPreIdx}, had ${preMounted.length})`);
|
|
24
|
+
}
|
|
25
|
+
const collected = serializeApi._ssrCollected;
|
|
26
|
+
serializeApi._ssrPreMounted = null;
|
|
27
|
+
serializeApi._ssrCollected = null;
|
|
28
|
+
if (!collected.length)
|
|
29
|
+
return tree;
|
|
30
|
+
for (const child of collected) {
|
|
31
|
+
if (typeof child.onMount === 'function') {
|
|
32
|
+
await child.onMount();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
preMounted = preMounted.concat(collected);
|
|
36
|
+
}
|
|
37
|
+
throw new Error('vmz:dom SSR child onMount expansion exceeded 32 rounds');
|
|
38
|
+
}
|
|
39
|
+
function serializeOpenTag(node) {
|
|
40
|
+
const tag = node.tag || 'div';
|
|
41
|
+
let attrs = '';
|
|
42
|
+
for (const [k, v] of Object.entries(node.attrs || {})) {
|
|
43
|
+
if (v == null || v === false)
|
|
44
|
+
continue;
|
|
45
|
+
if (k === 'className')
|
|
46
|
+
attrs += ` class="${escapeHtml(v)}"`;
|
|
47
|
+
else
|
|
48
|
+
attrs += ` ${k}="${escapeHtml(v)}"`;
|
|
49
|
+
}
|
|
50
|
+
return { tag, open: `<${tag}${attrs}>` };
|
|
51
|
+
}
|
|
52
|
+
export function flattenSerializeNode(node) {
|
|
53
|
+
if (node == null || node === false)
|
|
54
|
+
return '';
|
|
55
|
+
if (typeof node === 'string' || typeof node === 'number')
|
|
56
|
+
return escapeHtml(node);
|
|
57
|
+
if (node.__kind === 'text')
|
|
58
|
+
return escapeHtml(node.value);
|
|
59
|
+
if (node.__kind === 'comment')
|
|
60
|
+
return '';
|
|
61
|
+
if (node.__kind === 'frag') {
|
|
62
|
+
if (node.__rawHtml != null)
|
|
63
|
+
return String(node.__rawHtml);
|
|
64
|
+
return (node.children || []).map(flattenSerializeNode).join('');
|
|
65
|
+
}
|
|
66
|
+
if (node.__kind === 'el') {
|
|
67
|
+
const tag = node.tag || 'div';
|
|
68
|
+
if (tag === 'slot') {
|
|
69
|
+
if (node.__rawHtml != null)
|
|
70
|
+
return String(node.__rawHtml);
|
|
71
|
+
return (node.children || []).map(flattenSerializeNode).join('');
|
|
72
|
+
}
|
|
73
|
+
// rowKernel SSR: full outerHTML payload (do not re-wrap).
|
|
74
|
+
if (node.__rawOuter && node.__rawHtml != null)
|
|
75
|
+
return String(node.__rawHtml);
|
|
76
|
+
const { open } = serializeOpenTag(node);
|
|
77
|
+
if (node.__rawHtml != null) {
|
|
78
|
+
return `${open}${String(node.__rawHtml)}</${tag}>`;
|
|
79
|
+
}
|
|
80
|
+
const inner = (node.children || []).map(flattenSerializeNode).join('');
|
|
81
|
+
return `${open}${inner}</${tag}>`;
|
|
82
|
+
}
|
|
83
|
+
return '';
|
|
84
|
+
}
|
|
85
|
+
export function* streamSerializeChunks(node) {
|
|
86
|
+
if (node == null || node === false)
|
|
87
|
+
return;
|
|
88
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
89
|
+
yield escapeHtml(node);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (node.__kind === 'text') {
|
|
93
|
+
yield escapeHtml(node.value);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (node.__kind === 'comment')
|
|
97
|
+
return;
|
|
98
|
+
if (node.__kind === 'frag') {
|
|
99
|
+
if (node.__rawHtml != null) {
|
|
100
|
+
yield String(node.__rawHtml);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
for (const c of node.children || [])
|
|
104
|
+
yield* streamSerializeChunks(c);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (node.__kind === 'el') {
|
|
108
|
+
const tag = node.tag || 'div';
|
|
109
|
+
if (tag === 'slot') {
|
|
110
|
+
if (node.__rawHtml != null) {
|
|
111
|
+
yield String(node.__rawHtml);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
for (const c of node.children || [])
|
|
115
|
+
yield* streamSerializeChunks(c);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (node.__rawOuter && node.__rawHtml != null) {
|
|
119
|
+
yield String(node.__rawHtml);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const { open } = serializeOpenTag(node);
|
|
123
|
+
yield open;
|
|
124
|
+
if (node.__rawHtml != null) {
|
|
125
|
+
yield String(node.__rawHtml);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
for (const c of node.children || [])
|
|
129
|
+
yield* streamSerializeChunks(c);
|
|
130
|
+
}
|
|
131
|
+
yield `</${tag}>`;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function fillRowKernelHtml(rk, item, key) {
|
|
135
|
+
const slots = rk.textSlots;
|
|
136
|
+
if (!slots || typeof slots !== 'object')
|
|
137
|
+
return null;
|
|
138
|
+
const fields = Object.entries(slots)
|
|
139
|
+
.filter(([, i]) => typeof i === 'number' && Number.isFinite(i))
|
|
140
|
+
.sort((a, b) => Number(a[1]) - Number(b[1]))
|
|
141
|
+
.map(([f]) => f);
|
|
142
|
+
if (!fields.length)
|
|
143
|
+
return null;
|
|
144
|
+
let slotI = 0;
|
|
145
|
+
// Generator emits one space per text interp as a dedicated text node (`> <`).
|
|
146
|
+
const filled = String(rk.html).replace(/>([^<]*)</g, (m, text) => {
|
|
147
|
+
if (text === ' ' && slotI < fields.length) {
|
|
148
|
+
const f = fields[slotI++];
|
|
149
|
+
const v = item == null ? '' : item[f];
|
|
150
|
+
return `>${escapeHtml(v == null ? '' : String(v))}<`;
|
|
151
|
+
}
|
|
152
|
+
return m;
|
|
153
|
+
});
|
|
154
|
+
if (slotI !== fields.length)
|
|
155
|
+
return null;
|
|
156
|
+
if (key == null)
|
|
157
|
+
return filled;
|
|
158
|
+
// Inject data-vmz-key on the root opening tag (same attr hydrate would set).
|
|
159
|
+
return filled.replace(/^<([A-Za-z][\w:-]*)/, `<$1 data-vmz-key="${escapeHtml(String(key))}"`);
|
|
160
|
+
}
|
|
161
|
+
function serializeRowFromKernel(inst, rk, box, key) {
|
|
162
|
+
const item = box.item;
|
|
163
|
+
const hostFields = Array.isArray(rk.hostFields) ? rk.hostFields : [];
|
|
164
|
+
// Text-only kernels: no document. Host class ternaries still need hydrate/DOM.
|
|
165
|
+
if (hostFields.length === 0) {
|
|
166
|
+
const raw = fillRowKernelHtml(rk, item, key);
|
|
167
|
+
if (raw != null) {
|
|
168
|
+
return {
|
|
169
|
+
__kind: 'el',
|
|
170
|
+
tag: 'div',
|
|
171
|
+
attrs: Object.create(null),
|
|
172
|
+
children: [],
|
|
173
|
+
__rawOuter: true,
|
|
174
|
+
__rawHtml: raw,
|
|
175
|
+
appendChild() { },
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!ensureSsrDocument()) {
|
|
180
|
+
// Degrade: still ship text fill if possible (class may be wrong until client hydrate).
|
|
181
|
+
const raw = fillRowKernelHtml(rk, item, key);
|
|
182
|
+
if (raw != null) {
|
|
183
|
+
return {
|
|
184
|
+
__kind: 'el',
|
|
185
|
+
tag: 'div',
|
|
186
|
+
attrs: Object.create(null),
|
|
187
|
+
children: [],
|
|
188
|
+
__rawOuter: true,
|
|
189
|
+
__rawHtml: raw,
|
|
190
|
+
appendChild() { },
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const cause = _ssrDocumentLastError;
|
|
194
|
+
const detail = cause && cause.message ? cause.message : 'unavailable';
|
|
195
|
+
throw new Error(`vmz:dom SSR rowKernel requires a document (createItem omitted): ${detail}`, cause ? { cause } : undefined);
|
|
196
|
+
}
|
|
197
|
+
const tpl = document.createElement('template');
|
|
198
|
+
tpl.innerHTML = rk.html;
|
|
199
|
+
const root = tpl.content.firstElementChild;
|
|
200
|
+
if (!root || root.nodeType !== 1) {
|
|
201
|
+
throw new Error('vmz:dom SSR rowKernel html produced no element');
|
|
202
|
+
}
|
|
203
|
+
if (typeof rk.hydrate === 'function') {
|
|
204
|
+
rk.hydrate.call(inst, root, item);
|
|
205
|
+
}
|
|
206
|
+
if (key != null)
|
|
207
|
+
root.setAttribute('data-vmz-key', String(key));
|
|
208
|
+
return {
|
|
209
|
+
__kind: 'el',
|
|
210
|
+
tag: root.tagName.toLowerCase(),
|
|
211
|
+
attrs: Object.create(null),
|
|
212
|
+
children: [],
|
|
213
|
+
__rawOuter: true,
|
|
214
|
+
__rawHtml: root.outerHTML,
|
|
215
|
+
appendChild() { },
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function virtualLink(parent, child) {
|
|
219
|
+
if (child != null && typeof child === 'object')
|
|
220
|
+
child.parentNode = parent;
|
|
221
|
+
}
|
|
222
|
+
function virtualAppend(parent, child) {
|
|
223
|
+
if (parent == null || child == null)
|
|
224
|
+
return;
|
|
225
|
+
if (!Array.isArray(parent.children))
|
|
226
|
+
parent.children = [];
|
|
227
|
+
parent.children.push(child);
|
|
228
|
+
virtualLink(parent, child);
|
|
229
|
+
}
|
|
230
|
+
function virtualInsertBefore(parent, node, ref) {
|
|
231
|
+
if (parent == null || node == null)
|
|
232
|
+
return;
|
|
233
|
+
if (!Array.isArray(parent.children))
|
|
234
|
+
parent.children = [];
|
|
235
|
+
if (ref == null) {
|
|
236
|
+
virtualAppend(parent, node);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const idx = parent.children.indexOf(ref);
|
|
240
|
+
if (idx < 0)
|
|
241
|
+
virtualAppend(parent, node);
|
|
242
|
+
else {
|
|
243
|
+
parent.children.splice(idx, 0, node);
|
|
244
|
+
virtualLink(parent, node);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function virtualRemove(node) {
|
|
248
|
+
const p = node && node.parentNode;
|
|
249
|
+
if (!p || !Array.isArray(p.children))
|
|
250
|
+
return;
|
|
251
|
+
const idx = p.children.indexOf(node);
|
|
252
|
+
if (idx >= 0)
|
|
253
|
+
p.children.splice(idx, 1);
|
|
254
|
+
node.parentNode = null;
|
|
255
|
+
}
|
|
256
|
+
function makeVirtualEl(tag) {
|
|
257
|
+
const node = {
|
|
258
|
+
__kind: 'el',
|
|
259
|
+
tag: tag || 'div',
|
|
260
|
+
attrs: {},
|
|
261
|
+
children: [],
|
|
262
|
+
style: {},
|
|
263
|
+
__rawHtml: null,
|
|
264
|
+
parentNode: null,
|
|
265
|
+
setAttribute(name, value) {
|
|
266
|
+
applySerializeAttr(this, name, value);
|
|
267
|
+
},
|
|
268
|
+
appendChild(c) {
|
|
269
|
+
virtualAppend(this, c);
|
|
270
|
+
},
|
|
271
|
+
insertBefore(node, ref) {
|
|
272
|
+
virtualInsertBefore(this, node, ref);
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
// Direct emit writes `el.innerHTML` for v-html. Mirror that DOM property
|
|
276
|
+
// on the SSR virtual element so the serializer receives the same payload.
|
|
277
|
+
Object.defineProperty(node, 'innerHTML', {
|
|
278
|
+
configurable: true,
|
|
279
|
+
get() {
|
|
280
|
+
return this.__rawHtml ?? '';
|
|
281
|
+
},
|
|
282
|
+
set(value) {
|
|
283
|
+
this.__rawHtml = value == null ? '' : String(value);
|
|
284
|
+
this.children = [];
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
return node;
|
|
288
|
+
}
|
|
289
|
+
const serializeApi = {
|
|
290
|
+
_inst: null,
|
|
291
|
+
_branchBinds: null,
|
|
292
|
+
_itemPatches: null,
|
|
293
|
+
_eachCtx: null,
|
|
294
|
+
_ssrPreMounted: null,
|
|
295
|
+
_ssrPreIdx: 0,
|
|
296
|
+
_ssrCollected: null,
|
|
297
|
+
_ssrChildInstance(Ctor, resolved) {
|
|
298
|
+
const pre = serializeApi._ssrPreMounted;
|
|
299
|
+
if (pre && serializeApi._ssrPreIdx < pre.length) {
|
|
300
|
+
return pre[serializeApi._ssrPreIdx++];
|
|
301
|
+
}
|
|
302
|
+
const child = createInstance(Ctor, resolved);
|
|
303
|
+
if (serializeApi._ssrCollected)
|
|
304
|
+
serializeApi._ssrCollected.push(child);
|
|
305
|
+
return child;
|
|
306
|
+
},
|
|
307
|
+
el(tag) {
|
|
308
|
+
const node = makeVirtualEl(tag);
|
|
309
|
+
if (String(tag || '').toLowerCase() === 'svg') {
|
|
310
|
+
node.namespaceURI = 'http://www.w3.org/2000/svg';
|
|
311
|
+
}
|
|
312
|
+
return node;
|
|
313
|
+
},
|
|
314
|
+
elNS(ns, tag) {
|
|
315
|
+
const node = makeVirtualEl(tag);
|
|
316
|
+
node.namespaceURI = ns || 'http://www.w3.org/2000/svg';
|
|
317
|
+
return node;
|
|
318
|
+
},
|
|
319
|
+
text(value) {
|
|
320
|
+
const node = { __kind: 'text', value: value == null ? '' : String(value), parentNode: null };
|
|
321
|
+
// Generated Direct patches use DOM `textContent`; virtual text uses `value`.
|
|
322
|
+
Object.defineProperty(node, 'textContent', {
|
|
323
|
+
configurable: true,
|
|
324
|
+
enumerable: false,
|
|
325
|
+
get() {
|
|
326
|
+
return node.value;
|
|
327
|
+
},
|
|
328
|
+
set(v) {
|
|
329
|
+
node.value = v == null ? '' : String(v);
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
return node;
|
|
333
|
+
},
|
|
334
|
+
comment(value) {
|
|
335
|
+
return { __kind: 'comment', value: value == null ? '' : String(value), parentNode: null };
|
|
336
|
+
},
|
|
337
|
+
frag() {
|
|
338
|
+
return {
|
|
339
|
+
__kind: 'frag',
|
|
340
|
+
children: [],
|
|
341
|
+
parentNode: null,
|
|
342
|
+
appendChild(c) {
|
|
343
|
+
virtualAppend(this, c);
|
|
344
|
+
},
|
|
345
|
+
insertBefore(node, ref) {
|
|
346
|
+
virtualInsertBefore(this, node, ref);
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
},
|
|
350
|
+
insertBefore(parent, node, ref) {
|
|
351
|
+
virtualInsertBefore(parent, node, ref);
|
|
352
|
+
},
|
|
353
|
+
removeNode(node) {
|
|
354
|
+
virtualRemove(node);
|
|
355
|
+
},
|
|
356
|
+
trackPatch(inst, deps, patch, bindingId = null) {
|
|
357
|
+
if (typeof patch !== 'function')
|
|
358
|
+
return;
|
|
359
|
+
if (serializeApi._branchBinds) {
|
|
360
|
+
serializeApi._branchBinds.push({ deps: deps || [], fn: patch, bindingId });
|
|
361
|
+
try {
|
|
362
|
+
patch.call(inst);
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
console.error('vmz:ssr trackPatch branch', err);
|
|
366
|
+
}
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (serializeApi._itemPatches) {
|
|
370
|
+
patch.__vmzItemLocal = true;
|
|
371
|
+
serializeApi._itemPatches.push(patch);
|
|
372
|
+
if (serializeApi._eachCtx) {
|
|
373
|
+
serializeApi._eachCtx.noteItemBind(bindingId, deps || [], patch);
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
patch.call(inst);
|
|
377
|
+
}
|
|
378
|
+
catch (err) {
|
|
379
|
+
console.error('vmz:ssr trackPatch item', err);
|
|
380
|
+
}
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
try {
|
|
384
|
+
patch.call(inst);
|
|
385
|
+
}
|
|
386
|
+
catch (err) {
|
|
387
|
+
console.error('vmz:ssr trackPatch', err);
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
untrackPatch() {
|
|
391
|
+
/* SSR one-shot schedule ??no reactive unregister */
|
|
392
|
+
},
|
|
393
|
+
disposeTree() {
|
|
394
|
+
/* SSR one-shot schedule */
|
|
395
|
+
},
|
|
396
|
+
attr(el, name, value) {
|
|
397
|
+
if (!el || el.__kind !== 'el')
|
|
398
|
+
return;
|
|
399
|
+
applySerializeAttr(el, name, value);
|
|
400
|
+
},
|
|
401
|
+
mergeClass(...parts) {
|
|
402
|
+
return mergeClassParts(...parts);
|
|
403
|
+
},
|
|
404
|
+
mergeStyle(...parts) {
|
|
405
|
+
return mergeStyleParts(...parts);
|
|
406
|
+
},
|
|
407
|
+
on() {
|
|
408
|
+
/* events are no-ops during SSR */
|
|
409
|
+
},
|
|
410
|
+
onMethod() {
|
|
411
|
+
/* named method events are also attached only during client resume */
|
|
412
|
+
},
|
|
413
|
+
adoptEnter() {
|
|
414
|
+
return false;
|
|
415
|
+
},
|
|
416
|
+
adoptLeave() {
|
|
417
|
+
/* resume scope only */
|
|
418
|
+
},
|
|
419
|
+
onComponentEvent() {
|
|
420
|
+
/* component events attach only on the client */
|
|
421
|
+
},
|
|
422
|
+
specFieldText(inst, _bindingId, fieldName, textNode) {
|
|
423
|
+
let raw;
|
|
424
|
+
try {
|
|
425
|
+
raw = inst[fieldName];
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
raw = '';
|
|
429
|
+
}
|
|
430
|
+
textNode.value = String(raw ?? '');
|
|
431
|
+
},
|
|
432
|
+
specFieldAttr(inst, _bindingId, fieldName, el, name) {
|
|
433
|
+
let raw;
|
|
434
|
+
try {
|
|
435
|
+
raw = inst[fieldName];
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
raw = null;
|
|
439
|
+
}
|
|
440
|
+
if (name === 'class' || name === 'className') {
|
|
441
|
+
applySerializeAttr(el, 'class', mergeClassParts(raw));
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (name === 'style') {
|
|
445
|
+
applySerializeAttr(el, 'style', mergeStyleParts(raw));
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
applySerializeAttr(el, name, raw);
|
|
449
|
+
},
|
|
450
|
+
bindComponentProp() {
|
|
451
|
+
/* SSR: props already resolved into the child instance at create */
|
|
452
|
+
},
|
|
453
|
+
projectDefaultSlot(hostEl, node) {
|
|
454
|
+
if (!hostEl || node == null)
|
|
455
|
+
return;
|
|
456
|
+
// serializeApi.component returns a serialize el tree (or island shell).
|
|
457
|
+
const root = hostEl.__kind === 'el' ? hostEl : null;
|
|
458
|
+
const findOwnedSlot = (n) => {
|
|
459
|
+
if (!n || typeof n !== 'object')
|
|
460
|
+
return null;
|
|
461
|
+
if (n.__kind === 'frag') {
|
|
462
|
+
for (const c of n.children || []) {
|
|
463
|
+
const hit = findOwnedSlot(c);
|
|
464
|
+
if (hit)
|
|
465
|
+
return hit;
|
|
466
|
+
}
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
if (n.__kind !== 'el')
|
|
470
|
+
return null;
|
|
471
|
+
if (n.tag === 'slot' && !(n.attrs && n.attrs.name))
|
|
472
|
+
return n;
|
|
473
|
+
for (const c of n.children || []) {
|
|
474
|
+
if (c && c.__kind === 'el' && c.attrs && c.attrs['data-vmz'] != null)
|
|
475
|
+
continue;
|
|
476
|
+
const hit = findOwnedSlot(c);
|
|
477
|
+
if (hit)
|
|
478
|
+
return hit;
|
|
479
|
+
}
|
|
480
|
+
return null;
|
|
481
|
+
};
|
|
482
|
+
const findOwnedSlotTarget = (n) => {
|
|
483
|
+
const slot = findOwnedSlot(n);
|
|
484
|
+
if (slot)
|
|
485
|
+
return slot;
|
|
486
|
+
if (!n || n.__kind !== 'el')
|
|
487
|
+
return null;
|
|
488
|
+
const tag = String(n.tag || '').toLowerCase();
|
|
489
|
+
if ((tag === 'button' || tag === 'a') && n.attrs && n.attrs['data-vmz-ui'] === 'button')
|
|
490
|
+
return n;
|
|
491
|
+
if (n.attrs && n.attrs['data-vmz'] != null)
|
|
492
|
+
return null;
|
|
493
|
+
for (const c of n.children || []) {
|
|
494
|
+
if (c && c.__kind === 'el' && c.attrs && c.attrs['data-vmz'] != null)
|
|
495
|
+
continue;
|
|
496
|
+
const hit = findOwnedSlotTarget(c);
|
|
497
|
+
if (hit)
|
|
498
|
+
return hit;
|
|
499
|
+
}
|
|
500
|
+
return null;
|
|
501
|
+
};
|
|
502
|
+
// Prefer searching the component body (first child of host wrapper).
|
|
503
|
+
let target = null;
|
|
504
|
+
if (root) {
|
|
505
|
+
for (const c of root.children || []) {
|
|
506
|
+
if (c && c.__kind === 'el' && c.attrs && c.attrs['data-vmz'] != null)
|
|
507
|
+
continue;
|
|
508
|
+
target = findOwnedSlotTarget(c);
|
|
509
|
+
if (target)
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
if (!target)
|
|
513
|
+
target = findOwnedSlotTarget(root);
|
|
514
|
+
}
|
|
515
|
+
if (target) {
|
|
516
|
+
if (target.tag === 'slot') {
|
|
517
|
+
target.__rawHtml = null;
|
|
518
|
+
if (!Array.isArray(target.children))
|
|
519
|
+
target.children = [];
|
|
520
|
+
// Append ??multiple projectDefaultSlot calls must accumulate (SSR).
|
|
521
|
+
// Client path replaces the live <slot> then appends siblings; serialize must push.
|
|
522
|
+
target.children.push(node);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (!Array.isArray(target.children))
|
|
526
|
+
target.children = [];
|
|
527
|
+
target.children.push(node);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (root)
|
|
531
|
+
root.appendChild(node);
|
|
532
|
+
},
|
|
533
|
+
setHtml(el, value) {
|
|
534
|
+
if (!el || el.__kind !== 'el')
|
|
535
|
+
return;
|
|
536
|
+
el.__rawHtml = value == null ? '' : String(value);
|
|
537
|
+
el.children = [];
|
|
538
|
+
},
|
|
539
|
+
bindHtml(inst, bindingId, deps, get, el) {
|
|
540
|
+
serializeApi.trackPatch(inst, deps || [], function bindHtmlPatch() {
|
|
541
|
+
let raw;
|
|
542
|
+
try {
|
|
543
|
+
raw = get.call(inst);
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
raw = '';
|
|
547
|
+
}
|
|
548
|
+
serializeApi.setHtml(el, raw);
|
|
549
|
+
}, bindingId);
|
|
550
|
+
},
|
|
551
|
+
component(hostInst, nameOrCtor, props, client) {
|
|
552
|
+
const name = typeof nameOrCtor === 'function' ? nameOrCtor.__vmzTag || nameOrCtor.name || 'Component' : nameOrCtor;
|
|
553
|
+
const Ctor = typeof nameOrCtor === 'function' ? nameOrCtor : getRegisteredComponent(name);
|
|
554
|
+
if (!Ctor) {
|
|
555
|
+
return serializeUnknownComponentNode(name);
|
|
556
|
+
}
|
|
557
|
+
const resolved = {};
|
|
558
|
+
for (const [k, v] of Object.entries(props || {})) {
|
|
559
|
+
const onKey = typeof v === 'function' ? eventPropHandlerName(k) : null;
|
|
560
|
+
if (onKey)
|
|
561
|
+
continue;
|
|
562
|
+
else if (typeof v === 'function')
|
|
563
|
+
resolved[k] = v.call(hostInst);
|
|
564
|
+
else
|
|
565
|
+
resolved[k] = v;
|
|
566
|
+
}
|
|
567
|
+
if (client) {
|
|
568
|
+
// resume: Island SSR includes body + ResumeEntry slice (same Direct schedule).
|
|
569
|
+
const child = serializeApi._ssrChildInstance(Ctor, resolved);
|
|
570
|
+
let body = null;
|
|
571
|
+
if (Ctor.__vmzDirect && typeof Ctor.__vmzCreate === 'function') {
|
|
572
|
+
const prev = serializeApi._inst;
|
|
573
|
+
serializeApi._inst = child;
|
|
574
|
+
try {
|
|
575
|
+
body = Ctor.__vmzCreate.call(child, serializeApi);
|
|
576
|
+
}
|
|
577
|
+
finally {
|
|
578
|
+
serializeApi._inst = prev;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
const state = snapshotInstanceState(child) || {};
|
|
582
|
+
const plan = Ctor.__vmzPlan || null;
|
|
583
|
+
const resume = {
|
|
584
|
+
schema: 'vmz.resume.v0',
|
|
585
|
+
component: name,
|
|
586
|
+
strategy: String(client),
|
|
587
|
+
props: stripFns(resolved),
|
|
588
|
+
state,
|
|
589
|
+
planSchema: plan?.schema || null,
|
|
590
|
+
planRootIds: plan?.root_ids || [],
|
|
591
|
+
};
|
|
592
|
+
const attrs = {
|
|
593
|
+
'data-vmz': name,
|
|
594
|
+
'data-vmz-island': name,
|
|
595
|
+
'data-vmz-client': String(client),
|
|
596
|
+
'data-vmz-props': JSON.stringify(stripFns(resolved)),
|
|
597
|
+
'data-vmz-resume': JSON.stringify(resume),
|
|
598
|
+
};
|
|
599
|
+
const boxStyle = directHostBoxStyleAttr(name, Ctor);
|
|
600
|
+
if (boxStyle)
|
|
601
|
+
attrs.style = boxStyle;
|
|
602
|
+
if (isEventEntryStrategy(String(client))) {
|
|
603
|
+
attrs['data-vmz-entry'] = 'event';
|
|
604
|
+
}
|
|
605
|
+
return {
|
|
606
|
+
__kind: 'el',
|
|
607
|
+
tag: 'div',
|
|
608
|
+
attrs,
|
|
609
|
+
children: body ? [body] : [],
|
|
610
|
+
appendChild(c) {
|
|
611
|
+
if (c != null)
|
|
612
|
+
this.children.push(c);
|
|
613
|
+
},
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
const child = serializeApi._ssrChildInstance(Ctor, resolved);
|
|
617
|
+
if (Ctor.__vmzDirect && typeof Ctor.__vmzCreate === 'function') {
|
|
618
|
+
const prev = serializeApi._inst;
|
|
619
|
+
serializeApi._inst = child;
|
|
620
|
+
try {
|
|
621
|
+
const node = Ctor.__vmzCreate.call(child, serializeApi);
|
|
622
|
+
const attrs = { 'data-vmz': name };
|
|
623
|
+
const boxStyle = directHostBoxStyleAttr(name, Ctor);
|
|
624
|
+
if (boxStyle)
|
|
625
|
+
attrs.style = boxStyle;
|
|
626
|
+
return {
|
|
627
|
+
__kind: 'el',
|
|
628
|
+
tag: 'div',
|
|
629
|
+
attrs,
|
|
630
|
+
children: node ? [node] : [],
|
|
631
|
+
appendChild(c) {
|
|
632
|
+
if (c != null)
|
|
633
|
+
this.children.push(c);
|
|
634
|
+
},
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
finally {
|
|
638
|
+
serializeApi._inst = prev;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
throw new Error(`vmz:dom serialize component <${name}> requires __vmzCreate (rebuild child with Direct)`);
|
|
642
|
+
},
|
|
643
|
+
};
|
|
644
|
+
function applySerializeAttr(el, name, value) {
|
|
645
|
+
if (!el || el.__kind !== 'el')
|
|
646
|
+
return;
|
|
647
|
+
const key = name === 'className' ? 'class' : name;
|
|
648
|
+
if (key === 'class') {
|
|
649
|
+
const s = mergeClassParts(value);
|
|
650
|
+
if (s)
|
|
651
|
+
el.attrs[key] = s;
|
|
652
|
+
else
|
|
653
|
+
delete el.attrs[key];
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (key === 'style') {
|
|
657
|
+
const s = mergeStyleParts(value);
|
|
658
|
+
if (s)
|
|
659
|
+
el.attrs[key] = s;
|
|
660
|
+
else
|
|
661
|
+
delete el.attrs[key];
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (BOOLEAN_HTML_ATTRS.has(String(key).toLowerCase())) {
|
|
665
|
+
if (value === false || value == null || value === '')
|
|
666
|
+
delete el.attrs[key];
|
|
667
|
+
else
|
|
668
|
+
el.attrs[key] = value === true ? '' : String(value);
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (value == null || value === false)
|
|
672
|
+
delete el.attrs[key];
|
|
673
|
+
else
|
|
674
|
+
el.attrs[key] = value === true ? '' : String(value);
|
|
675
|
+
}
|
|
676
|
+
function escapeHtml(s) {
|
|
677
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
678
|
+
}
|