@ccheever/exact-renderer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +118 -0
- package/src/__tests__/adapter-window-state.test.tsx +190 -0
- package/src/__tests__/attrs.test.ts +157 -0
- package/src/__tests__/classname.test.ts +332 -0
- package/src/__tests__/color.test.ts +169 -0
- package/src/__tests__/dom-mirror.test.ts +682 -0
- package/src/__tests__/dom-shim.test.ts +274 -0
- package/src/__tests__/fixtures/SvelteCounter.svelte +7 -0
- package/src/__tests__/fixtures/SvelteInput.svelte +8 -0
- package/src/__tests__/host-config.test.ts +51 -0
- package/src/__tests__/host-ops.test.ts +2234 -0
- package/src/__tests__/image-source.test.ts +135 -0
- package/src/__tests__/liquid-glass.test.ts +72 -0
- package/src/__tests__/multi-root.test.ts +118 -0
- package/src/__tests__/native-view-events.test.ts +102 -0
- package/src/__tests__/nodes.test.ts +399 -0
- package/src/__tests__/normalize.test.ts +576 -0
- package/src/__tests__/paragraph-lowering.test.tsx +144 -0
- package/src/__tests__/props.test.ts +518 -0
- package/src/__tests__/protocol-encoder.test.ts +732 -0
- package/src/__tests__/protocol-fixture-bytes.test.ts +41 -0
- package/src/__tests__/reconciler.test.tsx +241 -0
- package/src/__tests__/svelte-adapter.test.ts +166 -0
- package/src/__tests__/svg-source.test.ts +71 -0
- package/src/__tests__/tags.test.ts +354 -0
- package/src/__tests__/toggle.test.ts +441 -0
- package/src/__tests__/transitions.test.ts +106 -0
- package/src/__tests__/web-primitives.test.tsx +454 -0
- package/src/__tests__/window-hooks.test.tsx +447 -0
- package/src/adapter-contract.ts +68 -0
- package/src/attrs.ts +596 -0
- package/src/classname-contract.ts +87 -0
- package/src/classname-resolve.ts +553 -0
- package/src/classname-runtime.ts +29 -0
- package/src/components.ts +214 -0
- package/src/css-variable-context.ts +83 -0
- package/src/dom-hydration.ts +160 -0
- package/src/dom-mirror.ts +1459 -0
- package/src/dom-shim.ts +1736 -0
- package/src/group-context.ts +69 -0
- package/src/host-config.ts +431 -0
- package/src/host-ops.ts +3167 -0
- package/src/image-source.native.ts +703 -0
- package/src/image-source.ts +554 -0
- package/src/index.ts +278 -0
- package/src/inspector-runtime.ts +244 -0
- package/src/inspector.ts +3570 -0
- package/src/jsx-augmentations.ts +54 -0
- package/src/keyboard-avoidance.ts +217 -0
- package/src/native-primitives.ts +43 -0
- package/src/native-view-events.ts +322 -0
- package/src/native-view.ts +60 -0
- package/src/nodes/index.ts +41 -0
- package/src/nodes/node.ts +531 -0
- package/src/peer-context.ts +100 -0
- package/src/primitives.native.ts +8 -0
- package/src/primitives.ts +8 -0
- package/src/props/index.ts +14 -0
- package/src/props/normalize.ts +816 -0
- package/src/protocol/encoder.ts +940 -0
- package/src/protocol/index.ts +33 -0
- package/src/reconciler.ts +581 -0
- package/src/runtime.ts +11 -0
- package/src/safe-area.ts +543 -0
- package/src/solid.ts +490 -0
- package/src/style/color.js +1 -0
- package/src/style/color.ts +15 -0
- package/src/style/index.js +1 -0
- package/src/style/index.ts +22 -0
- package/src/style/normalize.js +1 -0
- package/src/style/normalize.ts +1426 -0
- package/src/svelte.ts +349 -0
- package/src/svg-source.ts +222 -0
- package/src/tags/index.ts +21 -0
- package/src/tags/tag-map.ts +289 -0
- package/src/text/paragraph-lowering.ts +310 -0
- package/src/types.ts +1175 -0
- package/src/vue.ts +535 -0
- package/src/web-host.ts +19 -0
- package/src/web-primitives.ts +1654 -0
package/src/vue.ts
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact Vue Renderer
|
|
3
|
+
*
|
|
4
|
+
* This module provides the Vue adapter for the Exact runtime.
|
|
5
|
+
* It uses @vue/runtime-core's createRenderer to map Vue's rendering
|
|
6
|
+
* operations to Exact's host operations.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { createApp } from './renderer/vue';
|
|
11
|
+
* import App from './vue-app';
|
|
12
|
+
*
|
|
13
|
+
* createApp(App).mount();
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Note: Vue components should use render functions (h) rather than
|
|
17
|
+
* template syntax for universal rendering.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { createRenderer, type RendererOptions, nextTick } from '@vue/runtime-core';
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
__setActiveWindowRenderer,
|
|
24
|
+
subscribeToRootWindowState,
|
|
25
|
+
} from '@exact/core';
|
|
26
|
+
import {
|
|
27
|
+
// Types
|
|
28
|
+
type ElementNode,
|
|
29
|
+
type TextNode,
|
|
30
|
+
type RootNode,
|
|
31
|
+
|
|
32
|
+
// Instance creation
|
|
33
|
+
createInstance,
|
|
34
|
+
createTextInstance,
|
|
35
|
+
createRoot,
|
|
36
|
+
|
|
37
|
+
// Tree operations
|
|
38
|
+
appendChild,
|
|
39
|
+
insertBefore,
|
|
40
|
+
removeChild,
|
|
41
|
+
|
|
42
|
+
// Updates
|
|
43
|
+
updateInstanceProps,
|
|
44
|
+
updateTextContent,
|
|
45
|
+
commitBatch,
|
|
46
|
+
destroyRoot,
|
|
47
|
+
syncRootWindowState,
|
|
48
|
+
|
|
49
|
+
// Node operations
|
|
50
|
+
NodeKind,
|
|
51
|
+
|
|
52
|
+
// Handler registry
|
|
53
|
+
_clearHandlers,
|
|
54
|
+
} from './host-ops.js';
|
|
55
|
+
|
|
56
|
+
// =============================================================================
|
|
57
|
+
// Types
|
|
58
|
+
// =============================================================================
|
|
59
|
+
|
|
60
|
+
type ExactNode = ElementNode | TextNode;
|
|
61
|
+
type ExactParentNode = ElementNode | RootNode;
|
|
62
|
+
type OptionalWindowRendererKind = 'solid' | 'vue';
|
|
63
|
+
|
|
64
|
+
interface WindowRendererRoot {
|
|
65
|
+
render(content: unknown): void;
|
|
66
|
+
unmount(): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface RootState {
|
|
70
|
+
container: RootNode;
|
|
71
|
+
hasRenderedOnce: boolean;
|
|
72
|
+
commitScheduled: boolean;
|
|
73
|
+
claimed: boolean;
|
|
74
|
+
unsubscribeWindowState: (() => void) | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const rootStates = new Map<number, RootState>();
|
|
78
|
+
|
|
79
|
+
function registerOptionalWindowRenderer(
|
|
80
|
+
renderer: OptionalWindowRendererKind,
|
|
81
|
+
createRoot: (rootId: number) => WindowRendererRoot,
|
|
82
|
+
): void {
|
|
83
|
+
const globalScope = globalThis as typeof globalThis & {
|
|
84
|
+
__exactOptionalWindowRenderers?:
|
|
85
|
+
Partial<Record<OptionalWindowRendererKind, (rootId: number) => WindowRendererRoot>>;
|
|
86
|
+
};
|
|
87
|
+
const registry = globalScope.__exactOptionalWindowRenderers ??= {};
|
|
88
|
+
registry[renderer] = createRoot;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getRootContainerForNode(node: ExactNode | RootNode | null | undefined): RootNode | null {
|
|
92
|
+
let current = node;
|
|
93
|
+
while (current) {
|
|
94
|
+
if (current.kind === NodeKind.Root) {
|
|
95
|
+
return current;
|
|
96
|
+
}
|
|
97
|
+
current = current.parent as ExactNode | RootNode | null;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function getRootStateForNode(node: ExactNode | RootNode | null | undefined): RootState | null {
|
|
103
|
+
const container = getRootContainerForNode(node);
|
|
104
|
+
return container
|
|
105
|
+
? rootStates.get(container.rootId) ?? null
|
|
106
|
+
: null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function getOrCreateRootState(rootId: number): RootState {
|
|
110
|
+
let rootState = rootStates.get(rootId);
|
|
111
|
+
if (!rootState) {
|
|
112
|
+
rootState = {
|
|
113
|
+
container: createRoot(rootId, 'vue'),
|
|
114
|
+
hasRenderedOnce: false,
|
|
115
|
+
commitScheduled: false,
|
|
116
|
+
claimed: false,
|
|
117
|
+
unsubscribeWindowState:
|
|
118
|
+
typeof subscribeToRootWindowState === 'function'
|
|
119
|
+
? subscribeToRootWindowState(rootId, () => {
|
|
120
|
+
if (rootState) {
|
|
121
|
+
syncRootWindowState(rootState.container);
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
: null,
|
|
125
|
+
};
|
|
126
|
+
rootStates.set(rootId, rootState);
|
|
127
|
+
}
|
|
128
|
+
return rootState;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function scheduleCommitForRoot(rootState: RootState | null): void {
|
|
132
|
+
if (!rootState || rootState.commitScheduled) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
rootState.commitScheduled = true;
|
|
137
|
+
queueMicrotask(() => {
|
|
138
|
+
rootState.commitScheduled = false;
|
|
139
|
+
commitBatch(rootState.container);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function scheduleCommitForNode(node: ExactNode | RootNode | null | undefined): void {
|
|
144
|
+
scheduleCommitForRoot(getRootStateForNode(node));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// =============================================================================
|
|
148
|
+
// Vue Custom Renderer Options
|
|
149
|
+
// =============================================================================
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Vue renderer options that map Vue's rendering operations
|
|
153
|
+
* to Exact's host operations.
|
|
154
|
+
*/
|
|
155
|
+
const rendererOptions: RendererOptions<ExactNode, ExactParentNode> = {
|
|
156
|
+
/**
|
|
157
|
+
* Create a new element node.
|
|
158
|
+
*/
|
|
159
|
+
createElement(type: string): ElementNode {
|
|
160
|
+
return createInstance(type, {});
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create a new text node.
|
|
165
|
+
*/
|
|
166
|
+
createText(text: string): TextNode {
|
|
167
|
+
return createTextInstance(text);
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Create a comment node (not used in Exact, return empty text).
|
|
172
|
+
*/
|
|
173
|
+
createComment(text: string): TextNode {
|
|
174
|
+
// Comments aren't rendered in Exact, but Vue requires this
|
|
175
|
+
return createTextInstance('');
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Set the text content of a text node.
|
|
180
|
+
*/
|
|
181
|
+
setText(node: TextNode, text: string): void {
|
|
182
|
+
if (node.kind === NodeKind.Text) {
|
|
183
|
+
updateTextContent(node, text);
|
|
184
|
+
scheduleCommitForNode(node);
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Set text content of an element.
|
|
190
|
+
* Vue calls this when an element has a single text child (e.g., h('text', {}, 'Hello')).
|
|
191
|
+
* We create/update a text node child to hold the content.
|
|
192
|
+
*/
|
|
193
|
+
setElementText(node: ElementNode, text: string): void {
|
|
194
|
+
|
|
195
|
+
if (node.kind !== NodeKind.Element) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Check if we already have a text node child we can update
|
|
200
|
+
const existingTextChild = node.children.find(
|
|
201
|
+
(child): child is TextNode => child.kind === NodeKind.Text
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
if (existingTextChild) {
|
|
205
|
+
// Update existing text node
|
|
206
|
+
updateTextContent(existingTextChild, text);
|
|
207
|
+
} else {
|
|
208
|
+
// Clear any existing children and add a text node
|
|
209
|
+
// Note: We need to properly remove existing children
|
|
210
|
+
while (node.children.length > 0) {
|
|
211
|
+
const child = node.children[0];
|
|
212
|
+
removeChild(node, child);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Create and append new text node
|
|
216
|
+
const textNode = createTextInstance(text);
|
|
217
|
+
appendChild(node, textNode);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Schedule commit
|
|
221
|
+
scheduleCommitForNode(node);
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Insert a child into a parent.
|
|
226
|
+
*/
|
|
227
|
+
insert(child: ExactNode, parent: ExactParentNode, anchor?: ExactNode | null): void {
|
|
228
|
+
if (anchor) {
|
|
229
|
+
insertBefore(parent, child, anchor);
|
|
230
|
+
} else {
|
|
231
|
+
appendChild(parent, child);
|
|
232
|
+
}
|
|
233
|
+
scheduleCommitForNode(parent);
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Remove a child from its parent.
|
|
238
|
+
*/
|
|
239
|
+
remove(child: ExactNode): void {
|
|
240
|
+
const parent = child.parent;
|
|
241
|
+
if (parent && (parent.kind === NodeKind.Element || parent.kind === NodeKind.Root)) {
|
|
242
|
+
const parentNode = parent as ExactParentNode;
|
|
243
|
+
removeChild(parentNode, child);
|
|
244
|
+
scheduleCommitForNode(parentNode);
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get the parent of a node.
|
|
250
|
+
*/
|
|
251
|
+
parentNode(node: ExactNode): ExactParentNode | null {
|
|
252
|
+
return (node.parent as ExactParentNode) ?? null;
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get the next sibling of a node.
|
|
257
|
+
*/
|
|
258
|
+
nextSibling(node: ExactNode): ExactNode | null {
|
|
259
|
+
const parent = node.parent;
|
|
260
|
+
if (!parent || (parent.kind !== NodeKind.Element && parent.kind !== NodeKind.Root)) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const parentElement = parent as ExactParentNode;
|
|
265
|
+
const index = parentElement.children.indexOf(node as ElementNode);
|
|
266
|
+
if (index >= 0 && index < parentElement.children.length - 1) {
|
|
267
|
+
return parentElement.children[index + 1] as ExactNode;
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Patch element props.
|
|
274
|
+
*/
|
|
275
|
+
patchProp(
|
|
276
|
+
el: ElementNode,
|
|
277
|
+
key: string,
|
|
278
|
+
prevValue: unknown,
|
|
279
|
+
nextValue: unknown
|
|
280
|
+
): void {
|
|
281
|
+
if (el.kind !== NodeKind.Element) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Build new props object with the updated property
|
|
286
|
+
const newProps = { ...el.originalProps, [key]: nextValue };
|
|
287
|
+
|
|
288
|
+
// Use the shared updateInstanceProps
|
|
289
|
+
updateInstanceProps(el, el.originalProps || {}, newProps);
|
|
290
|
+
|
|
291
|
+
scheduleCommitForNode(el);
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Clone a node (required but not commonly used).
|
|
296
|
+
*/
|
|
297
|
+
cloneNode(node: ElementNode): ElementNode {
|
|
298
|
+
// Create a new instance with the same type and props
|
|
299
|
+
return createInstance(node.originalTag, node.originalProps || {});
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Insert static content (for v-html, not used in Exact).
|
|
304
|
+
*/
|
|
305
|
+
insertStaticContent(): [ExactNode, ExactNode] {
|
|
306
|
+
// Return dummy nodes - static content not supported
|
|
307
|
+
const node = createTextInstance('');
|
|
308
|
+
return [node, node];
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// =============================================================================
|
|
313
|
+
// Create the Vue Renderer
|
|
314
|
+
// =============================================================================
|
|
315
|
+
|
|
316
|
+
const { render: vueRender, createApp: vueCreateApp } = createRenderer(rendererOptions);
|
|
317
|
+
|
|
318
|
+
// =============================================================================
|
|
319
|
+
// Public API
|
|
320
|
+
// =============================================================================
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Create a Vue application instance for Exact.
|
|
324
|
+
*
|
|
325
|
+
* @param rootComponent - The root Vue component
|
|
326
|
+
* @param rootProps - Optional props for the root component
|
|
327
|
+
* @returns A Vue app instance with a custom mount method
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```ts
|
|
331
|
+
* import { createApp, ref, h } from './renderer/vue';
|
|
332
|
+
*
|
|
333
|
+
* const App = {
|
|
334
|
+
* setup() {
|
|
335
|
+
* const count = ref(0);
|
|
336
|
+
* return () => h('div', { style: { flex: 1 } }, [
|
|
337
|
+
* h('text', {}, `Count: ${count.value}`)
|
|
338
|
+
* ]);
|
|
339
|
+
* }
|
|
340
|
+
* };
|
|
341
|
+
*
|
|
342
|
+
* createApp(App).mount();
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
export function createApp(rootComponent: any, rootProps?: Record<string, unknown>) {
|
|
346
|
+
__setActiveWindowRenderer?.('vue');
|
|
347
|
+
const rootState = getOrCreateRootState(0);
|
|
348
|
+
|
|
349
|
+
const app = vueCreateApp(rootComponent, rootProps);
|
|
350
|
+
|
|
351
|
+
// Override mount to use our root container
|
|
352
|
+
const originalMount = app.mount;
|
|
353
|
+
app.mount = () => {
|
|
354
|
+
const proxy = originalMount(rootState.container as unknown as any);
|
|
355
|
+
|
|
356
|
+
// Commit the initial batch to native
|
|
357
|
+
commitBatch(rootState.container);
|
|
358
|
+
rootState.hasRenderedOnce = true;
|
|
359
|
+
|
|
360
|
+
return proxy;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
return app;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Render a Vue vnode directly to the Exact runtime.
|
|
368
|
+
*
|
|
369
|
+
* Note: Vue's component-level reactivity (watchEffect, component render effects)
|
|
370
|
+
* doesn't work properly with custom renderers. Use this with a raw `effect` from
|
|
371
|
+
* @vue/reactivity to trigger re-renders when reactive state changes.
|
|
372
|
+
*
|
|
373
|
+
* @param vnode - The Vue vnode to render
|
|
374
|
+
* @returns A cleanup function
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* ```ts
|
|
378
|
+
* import { effect } from '@vue/reactivity';
|
|
379
|
+
* import { render, ref, h } from './renderer/vue';
|
|
380
|
+
*
|
|
381
|
+
* const count = ref(0);
|
|
382
|
+
* effect(() => {
|
|
383
|
+
* render(h('text', {}, `Count: ${count.value}`));
|
|
384
|
+
* });
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
export function render(vnode: any): () => void {
|
|
388
|
+
__setActiveWindowRenderer?.('vue');
|
|
389
|
+
const rootState = getOrCreateRootState(0);
|
|
390
|
+
|
|
391
|
+
vueRender(vnode, rootState.container as unknown as any);
|
|
392
|
+
|
|
393
|
+
// First render: commit immediately to bind events.
|
|
394
|
+
// Subsequent renders: scheduleCommit in patchProp/setElementText handles commits.
|
|
395
|
+
if (!rootState.hasRenderedOnce) {
|
|
396
|
+
commitBatch(rootState.container);
|
|
397
|
+
rootState.hasRenderedOnce = true;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return () => {
|
|
401
|
+
vueRender(null, rootState.container as unknown as any);
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export interface ExactRoot {
|
|
406
|
+
readonly rootId: number;
|
|
407
|
+
render(vnode: any): void;
|
|
408
|
+
unmount(): void;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export function createExactRoot(rootId: number): ExactRoot {
|
|
412
|
+
if (rootId === 0) {
|
|
413
|
+
throw new Error('createExactRoot: rootId must be > 0. Use render() for the default root.');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const rootState = getOrCreateRootState(rootId);
|
|
417
|
+
if (rootState.claimed) {
|
|
418
|
+
throw new Error(`createExactRoot: rootId ${rootId} is already in use.`);
|
|
419
|
+
}
|
|
420
|
+
rootState.claimed = true;
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
rootId,
|
|
424
|
+
|
|
425
|
+
render(vnode: any): void {
|
|
426
|
+
__setActiveWindowRenderer?.('vue');
|
|
427
|
+
vueRender(vnode, rootState.container as unknown as any);
|
|
428
|
+
if (!rootState.hasRenderedOnce) {
|
|
429
|
+
commitBatch(rootState.container);
|
|
430
|
+
rootState.hasRenderedOnce = true;
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
unmount(): void {
|
|
435
|
+
vueRender(null, rootState.container as unknown as any);
|
|
436
|
+
rootStates.delete(rootId);
|
|
437
|
+
rootState.unsubscribeWindowState?.();
|
|
438
|
+
rootState.unsubscribeWindowState = null;
|
|
439
|
+
destroyRoot(rootId);
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
registerOptionalWindowRenderer(
|
|
445
|
+
'vue',
|
|
446
|
+
createExactRoot as (rootId: number) => WindowRendererRoot,
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Fully reset the renderer state.
|
|
451
|
+
*/
|
|
452
|
+
export function reset(): void {
|
|
453
|
+
for (const [rootId, rootState] of rootStates) {
|
|
454
|
+
vueRender(null, rootState.container as unknown as any);
|
|
455
|
+
rootState.unsubscribeWindowState?.();
|
|
456
|
+
rootState.unsubscribeWindowState = null;
|
|
457
|
+
destroyRoot(rootId);
|
|
458
|
+
}
|
|
459
|
+
rootStates.clear();
|
|
460
|
+
_clearHandlers();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// =============================================================================
|
|
464
|
+
// Re-exports from Vue
|
|
465
|
+
// =============================================================================
|
|
466
|
+
|
|
467
|
+
// Composition API
|
|
468
|
+
export {
|
|
469
|
+
ref,
|
|
470
|
+
reactive,
|
|
471
|
+
readonly,
|
|
472
|
+
computed,
|
|
473
|
+
watch,
|
|
474
|
+
watchEffect,
|
|
475
|
+
watchPostEffect,
|
|
476
|
+
watchSyncEffect,
|
|
477
|
+
|
|
478
|
+
// Lifecycle
|
|
479
|
+
onMounted,
|
|
480
|
+
onUpdated,
|
|
481
|
+
onUnmounted,
|
|
482
|
+
onBeforeMount,
|
|
483
|
+
onBeforeUpdate,
|
|
484
|
+
onBeforeUnmount,
|
|
485
|
+
|
|
486
|
+
// Dependency Injection
|
|
487
|
+
provide,
|
|
488
|
+
inject,
|
|
489
|
+
|
|
490
|
+
// Utilities
|
|
491
|
+
unref,
|
|
492
|
+
toRef,
|
|
493
|
+
toRefs,
|
|
494
|
+
isRef,
|
|
495
|
+
isReactive,
|
|
496
|
+
isReadonly,
|
|
497
|
+
isProxy,
|
|
498
|
+
shallowRef,
|
|
499
|
+
triggerRef,
|
|
500
|
+
shallowReactive,
|
|
501
|
+
shallowReadonly,
|
|
502
|
+
toRaw,
|
|
503
|
+
markRaw,
|
|
504
|
+
effectScope,
|
|
505
|
+
getCurrentScope,
|
|
506
|
+
onScopeDispose,
|
|
507
|
+
nextTick,
|
|
508
|
+
|
|
509
|
+
// Render helpers
|
|
510
|
+
h,
|
|
511
|
+
createVNode,
|
|
512
|
+
mergeProps,
|
|
513
|
+
cloneVNode,
|
|
514
|
+
isVNode,
|
|
515
|
+
resolveComponent,
|
|
516
|
+
resolveDirective,
|
|
517
|
+
withDirectives,
|
|
518
|
+
|
|
519
|
+
// Async
|
|
520
|
+
defineAsyncComponent,
|
|
521
|
+
|
|
522
|
+
// Special components
|
|
523
|
+
Fragment,
|
|
524
|
+
Teleport,
|
|
525
|
+
Suspense,
|
|
526
|
+
KeepAlive,
|
|
527
|
+
|
|
528
|
+
// Types
|
|
529
|
+
type Ref,
|
|
530
|
+
type ComputedRef,
|
|
531
|
+
type ShallowRef,
|
|
532
|
+
type UnwrapRef,
|
|
533
|
+
type Component,
|
|
534
|
+
type VNode,
|
|
535
|
+
} from '@vue/runtime-core';
|
package/src/web-host.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @system @ref LLP 0201 W1 / LLP 0202 H1 — production Contract web host CSR.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
installContractWebHost as installContractWebHostBase,
|
|
5
|
+
type ContractWebHostHandle,
|
|
6
|
+
type ContractWebHostOptions,
|
|
7
|
+
} from './dom-mirror.js';
|
|
8
|
+
import { installDomHydrationAdopterFactory } from './dom-hydration.js';
|
|
9
|
+
|
|
10
|
+
export type { ContractWebHostHandle, ContractWebHostOptions };
|
|
11
|
+
|
|
12
|
+
export function installContractWebHost(
|
|
13
|
+
options: ContractWebHostOptions = {},
|
|
14
|
+
): ContractWebHostHandle | null {
|
|
15
|
+
if (options.hydrate) {
|
|
16
|
+
installDomHydrationAdopterFactory();
|
|
17
|
+
}
|
|
18
|
+
return installContractWebHostBase(options);
|
|
19
|
+
}
|