import type { App } from '@vue/runtime-dom'
import type { VaporComponent, VaporComponentInstance } from '../src/component'
import type { RawProps } from '../src/componentProps'
+import { compileScript, parse } from '@vue/compiler-sfc'
+import * as runtimeVapor from '../src'
+import * as runtimeDom from '@vue/runtime-dom'
+import * as VueServerRenderer from '@vue/server-renderer'
export interface RenderContext {
component: VaporComponent
return define
}
+
+export { runtimeDom, runtimeVapor, VueServerRenderer }
+export function compile(
+ sfc: string,
+ data: runtimeDom.Ref<any>,
+ components: Record<string, any> = {},
+ {
+ vapor = true,
+ ssr = false,
+ }: {
+ vapor?: boolean | undefined
+ ssr?: boolean | undefined
+ } = {},
+): any {
+ if (!sfc.includes(`<script`)) {
+ sfc =
+ `<script vapor>const data = _data; const components = _components;</script>` +
+ sfc
+ }
+ const descriptor = parse(sfc).descriptor
+
+ const script = compileScript(descriptor, {
+ id: 'x',
+ isProd: true,
+ inlineTemplate: true,
+ genDefaultAs: '__sfc__',
+ vapor,
+ templateOptions: {
+ ssr,
+ },
+ })
+
+ const code =
+ script.content
+ .replace(/\bimport {/g, 'const {')
+ .replace(/ as _/g, ': _')
+ .replace(/} from ['"]vue['"]/g, `} = Vue`)
+ .replace(/} from "vue\/server-renderer"/g, '} = VueServerRenderer') +
+ '\nreturn __sfc__'
+
+ return new Function('Vue', 'VueServerRenderer', '_data', '_components', code)(
+ { ...runtimeDom, ...runtimeVapor },
+ VueServerRenderer,
+ data,
+ components,
+ )
+}
--- /dev/null
+import { nextTick } from 'vue'
+import { compile, runtimeDom, runtimeVapor } from '../_utils'
+
+describe.todo('VaporSuspense', () => {})
+
+describe('vapor / vdom interop', () => {
+ async function testSuspense(
+ code: string,
+ components: Record<string, { code: string; vapor: boolean }> = {},
+ data: any = {},
+ { vapor = false } = {},
+ ) {
+ const clientComponents: any = {}
+ for (const key in components) {
+ const comp = components[key]
+ let code = comp.code
+ const isVaporComp = !!comp.vapor
+ clientComponents[key] = compile(code, data, clientComponents, {
+ vapor: isVaporComp,
+ })
+ }
+
+ const clientComp = compile(code, data, clientComponents, {
+ vapor,
+ })
+
+ const app = (vapor ? runtimeVapor.createVaporApp : runtimeDom.createApp)(
+ clientComp,
+ )
+ app.use(runtimeVapor.vaporInteropPlugin)
+
+ const container = document.createElement('div')
+ document.body.appendChild(container)
+ app.mount(container)
+ return { container }
+ }
+
+ function asyncWrapper(code: string) {
+ return {
+ code: `
+ <script vapor>
+ const data = _data;
+ const p = new Promise(r => setTimeout(r, 5))
+ data.deps.push(p.then(() => Promise.resolve()))
+ await p
+ </script>
+ ${code}
+ `,
+ vapor: true,
+ }
+ }
+
+ test('vdom suspense: render vapor components', async () => {
+ const data = { deps: [] }
+ const { container } = await testSuspense(
+ `<script setup>
+ const components = _components;
+ </script>
+ <template>
+ <Suspense>
+ <components.VaporChild/>
+ <template #fallback>
+ <span>loading</span>
+ </template>
+ </Suspense>
+ </template>`,
+ {
+ VaporChild: asyncWrapper(`<template><div>hi</div></template>`),
+ },
+ data,
+ )
+
+ expect(container.innerHTML).toBe(`<span>loading</span>`)
+ expect(data.deps.length).toBe(1)
+ await Promise.all(data.deps)
+ await nextTick()
+ expect(container.innerHTML).toBe(`<div>hi</div>`)
+ })
+})