-
Notifications
You must be signed in to change notification settings - Fork 34
/
vue.ts
79 lines (67 loc) · 2.19 KB
/
vue.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { state } from './state';
import { RenderOptions, render as renderCommon } from './render';
import { Importer, bootstrap as bootstrapCommon } from './bootstrap';
import type {
App,
Component,
ComponentPublicInstance,
} from '@vue/runtime-core';
interface VueRenderOptions extends RenderOptions {
/** Props passed to the Vue component to render */
props?: Record<string, any>;
}
// For some reason if we import this from vue then `let app = <App />` in React code breaks. Fun!
type CreateAppFunction<HostElement> = (
rootComponent: Component,
rootProps?: Record<string, unknown> | null
) => App<HostElement>;
type Renderable = Component;
type RenderFn = (
element: Renderable,
options?: Record<string, any>
) => Promise<ComponentPublicInstance>;
let renderFn: RenderFn;
const errorMessage = `App is not bootstrapped, did you forget to call \`bootstrap({ /* ... */ })\`?`;
const assertAndRender: RenderFn = (element: Renderable, options) => {
if (!renderFn) throw new Error(errorMessage);
return renderFn(element, options);
};
const defaultRender = (app: Renderable) => app;
export async function render(
elementToRender:
| Renderable
| ((app: Renderable) => Renderable) = defaultRender,
options: VueRenderOptions = {}
) {
if (typeof elementToRender === 'function') {
const rendered = (elementToRender as any)(
state.browserState?.renderElement.value ?? ({} as any)
);
elementToRender = rendered;
}
return renderCommon(
{ __isRenderable: true, thing: elementToRender },
options,
async (e) => assertAndRender(e.thing, options.props)
);
}
type BootstrapElement<Element> = Parameters<CreateAppFunction<Element>>[0];
type BootstrapArgs<Element> = Importer & {
element: BootstrapElement<Element>;
container: Element | string;
render: RenderFn;
options?: VueRenderOptions;
};
export const bootstrap = async <Element>(
args: BootstrapArgs<Element>
): Promise<Component> => {
renderFn = args.render;
state.browserState = {
retryAttempt: 0,
renderElement: { __type: 'renderElement', value: args.element },
};
return bootstrapCommon({
...args,
defaultRender: () => assertAndRender(args.element, args.options?.props),
});
};