-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
renderer.ts
61 lines (57 loc) · 2.71 KB
/
renderer.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
import type { DynamicRenderContext, DynamicRendererGenerator, SupportedComponentRenderer, StaticRendererGenerator } from '../../@types/renderer';
import { childrenToH } from './utils';
/** Initialize Astro Component renderer for Static and Dynamic components */
export function createRenderer(renderer: SupportedComponentRenderer) {
const _static: StaticRendererGenerator = (Component) => renderer.renderStatic(Component);
const _imports = (context: DynamicRenderContext) => {
const values = Object.values(renderer.imports ?? {})
.reduce((acc, v) => {
return [...acc, `{ ${v.join(', ')} }`];
}, [])
.join(', ');
const libs = Object.keys(renderer.imports ?? {})
.reduce((acc: string[], lib: string) => {
return [...acc, `import("${context.frameworkUrls[lib as any]}")`];
}, [])
.join(',');
return `const [{${context.componentExport}: Component}, ${values}] = await Promise.all([import("${context.componentUrl}")${renderer.imports ? ', ' + libs : ''}]);`;
};
const serializeProps = ({ children: _, ...props }: Record<string, any>) => JSON.stringify(props);
const createContext = () => {
const astroId = `${Math.floor(Math.random() * 1e16)}`;
return { ['data-astro-id']: astroId, root: `document.querySelector('[data-astro-id="${astroId}"]')`, Component: 'Component' };
};
const createDynamicRender: DynamicRendererGenerator = (wrapperStart, wrapperEnd) => (Component, renderContext) => {
const innerContext = createContext();
return async (props, ...children) => {
let value: string;
try {
value = await _static(Component)(props, ...children);
} catch (e) {
value = '';
}
value = `<div data-astro-id="${innerContext['data-astro-id']}">${value}</div>`;
const script = `
${typeof wrapperStart === 'function' ? wrapperStart(innerContext) : wrapperStart}
${_imports(renderContext)}
${renderer.render({
...innerContext,
props: serializeProps(props),
children: `[${childrenToH(renderer, children) ?? ''}]`,
childrenAsString: `\`${children}\``,
})}
${typeof wrapperEnd === 'function' ? wrapperEnd(innerContext) : wrapperEnd}
`;
return [value, `<script type="module">${script.trim()}</script>`].join('\n');
};
};
return {
static: _static,
load: createDynamicRender('(async () => {', '})()'),
idle: createDynamicRender('requestIdleCallback(async () => {', '})'),
visible: createDynamicRender(
'const o = new IntersectionObserver(async ([entry]) => { if (!entry.isIntersecting) { return; } o.disconnect();',
({ root }) => `}); Array.from(${root}.item(0).children).forEach(child => o.observe(child))`
),
};
}