Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vapor): vapor teleport #13082

Open
wants to merge 29 commits into
base: vapor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9be697b
wip: save
edison1105 Mar 20, 2025
2571388
wip: save
edison1105 Mar 21, 2025
c0cd7fc
wip: add tests
edison1105 Mar 21, 2025
b945079
wip: update tests
edison1105 Mar 21, 2025
dd18528
wip: save
edison1105 Mar 21, 2025
5c8f7ed
wip: save
edison1105 Mar 21, 2025
17317c5
wip: port tests
edison1105 Mar 24, 2025
b232e45
wip: remove unnecessary tests
edison1105 Mar 24, 2025
97e6174
wip: handle vapor teleport
edison1105 Mar 24, 2025
33830a0
wip: save
edison1105 Mar 24, 2025
098f50d
wip: handing teleport hmr updating
edison1105 Mar 24, 2025
51ca617
test: add e2e tests for vdom interop
edison1105 Mar 24, 2025
90c2e20
wip: hmr updating
edison1105 Mar 25, 2025
ec76aec
wip: test hmr updating
edison1105 Mar 25, 2025
f315423
test: remove vapor mark
edison1105 Mar 25, 2025
16b30d8
test: port more tests
edison1105 Mar 26, 2025
b646856
wip: save
edison1105 Mar 26, 2025
b474ce0
wip: fix teleport root component hmr reload
edison1105 Mar 26, 2025
c1547b5
wip: save
edison1105 Mar 26, 2025
5b933f5
wip: extract VaporFragment into a separate file
edison1105 Mar 26, 2025
ba6577f
wip: add more hmr tests + refactor
edison1105 Mar 27, 2025
7ab1a30
wip: refactor
edison1105 Mar 27, 2025
fd6f163
wip: save
edison1105 Mar 27, 2025
4de819c
wip: refactor
edison1105 Mar 27, 2025
177c6a6
wip: save
edison1105 Mar 27, 2025
5574fbf
wip: refactor
edison1105 Mar 27, 2025
ba9db34
test: add e2e tests for vdom teleport vapor interop
edison1105 Mar 27, 2025
43d2799
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 27, 2025
1033d2b
Merge branch 'vapor' into edison/feat/vaporTeleport
edison1105 Apr 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions packages-private/vapor-e2e-test/__tests__/teleport.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import path from 'node:path'
import {
E2E_TIMEOUT,
setupPuppeteer,
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
import connect from 'connect'
import sirv from 'sirv'
import { nextTick } from 'vue'
import { ports } from '../utils'
const { page, click, html } = setupPuppeteer()

describe('vapor teleport', () => {
let server: any
const port = ports.teleport
beforeAll(() => {
server = connect()
.use(sirv(path.resolve(import.meta.dirname, '../dist')))
.listen(port)
process.on('SIGTERM', () => server && server.close())
})

afterAll(() => {
server.close()
})

beforeEach(async () => {
const baseUrl = `http://localhost:${port}/teleport/`
await page().goto(baseUrl)
await page().waitForSelector('#app')
})

test(
'render vdom component',
async () => {
const targetSelector = '.target'
const testSelector = '.interop-render-vdom-comp'
const containerSelector = `${testSelector} > div`
const btnSelector = `${testSelector} > button`

const tt = await html('#app')
console.log(tt)

// teleport is disabled
expect(await html(containerSelector)).toBe('<h1>vdom comp</h1>')
expect(await html(targetSelector)).toBe('')

// enable teleport
await click(btnSelector)
await nextTick()

expect(await html(containerSelector)).toBe('')
expect(await html(targetSelector)).toBe('<h1>vdom comp</h1>')

// disable teleport
await click(btnSelector)
await nextTick()
expect(await html(containerSelector)).toBe('<h1>vdom comp</h1>')
expect(await html(targetSelector)).toBe('')
},
E2E_TIMEOUT,
)
})
3 changes: 2 additions & 1 deletion packages-private/vapor-e2e-test/__tests__/todomvc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
import connect from 'connect'
import sirv from 'sirv'
import { ports } from '../utils'

describe('e2e: todomvc', () => {
const {
Expand All @@ -23,7 +24,7 @@ describe('e2e: todomvc', () => {
} = setupPuppeteer()

let server: any
const port = '8194'
const port = ports.todomvc
beforeAll(() => {
server = connect()
.use(sirv(path.resolve(import.meta.dirname, '../dist')))
Expand Down
45 changes: 39 additions & 6 deletions packages-private/vapor-e2e-test/__tests__/vdomInterop.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,33 @@ import {
} from '../../../packages/vue/__tests__/e2e/e2eUtils'
import connect from 'connect'
import sirv from 'sirv'
import { ports } from '../utils'
import { nextTick } from 'vue'
const { page, click, text, enterValue, html } = setupPuppeteer()

describe('vdom / vapor interop', () => {
const { page, click, text, enterValue } = setupPuppeteer()

let server: any
const port = '8193'
const port = ports.vdomInterop
beforeAll(() => {
server = connect()
.use(sirv(path.resolve(import.meta.dirname, '../dist')))
.listen(port)
process.on('SIGTERM', () => server && server.close())
})

beforeEach(async () => {
const baseUrl = `http://localhost:${port}/interop/`
await page().goto(baseUrl)
await page().waitForSelector('#app')
})

afterAll(() => {
server.close()
})

test(
'should work',
async () => {
const baseUrl = `http://localhost:${port}/interop/`
await page().goto(baseUrl)

expect(await text('.vapor > h2')).toContain('Vapor component in VDOM')

expect(await text('.vapor-prop')).toContain('hello')
Expand Down Expand Up @@ -81,4 +85,33 @@ describe('vdom / vapor interop', () => {
},
E2E_TIMEOUT,
)

describe('teleport', () => {
const testSelector = '.teleport'
test('render vapor component', async () => {
const targetSelector = `${testSelector} .teleport-target`
const containerSelector = `${testSelector} .render-vapor-comp`
const buttonSelector = `${containerSelector} button`

// teleport is disabled by default
expect(await html(containerSelector)).toBe(
`<button>toggle</button><div>vapor comp</div>`,
)
expect(await html(targetSelector)).toBe('')

// disabled -> enabled
await click(buttonSelector)
await nextTick()
expect(await html(containerSelector)).toBe(`<button>toggle</button>`)
expect(await html(targetSelector)).toBe('<div>vapor comp</div>')

// enabled -> disabled
await click(buttonSelector)
await nextTick()
expect(await html(containerSelector)).toBe(
`<button>toggle</button><div>vapor comp</div>`,
)
expect(await html(targetSelector)).toBe('')
})
})
})
1 change: 1 addition & 0 deletions packages-private/vapor-e2e-test/index.html
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
<a href="/interop/">VDOM / Vapor interop</a>
<a href="/todomvc/">Vapor TodoMVC</a>
<a href="/teleport/">Vapor Teleport</a>
16 changes: 15 additions & 1 deletion packages-private/vapor-e2e-test/interop/App.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script setup lang="ts">
import { ref } from 'vue'
import VaporComp from './VaporComp.vue'
import VaporComp from './components/VaporComp.vue'
import SimpleVaporComp from './components/SimpleVaporComp.vue'

const msg = ref('hello')
const passSlot = ref(true)
const disabled = ref(true)
</script>

<template>
Expand All @@ -19,4 +21,16 @@ const passSlot = ref(true)

<template #test v-if="passSlot">A test slot</template>
</VaporComp>

<!-- teleport -->
<div class="teleport">
<div class="teleport-target"></div>
<div class="render-vapor-comp">
<button @click="disabled = !disabled">toggle</button>
<Teleport to=".teleport-target" defer :disabled="disabled">
<SimpleVaporComp />
</Teleport>
</div>
</div>
<!-- teleport end-->
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<script setup vapor lang="ts">
const msg = 'vapor comp'
</script>
<template>
<div>{{ msg }}</div>
</template>
17 changes: 17 additions & 0 deletions packages-private/vapor-e2e-test/teleport/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script setup vapor>
import { ref, Teleport } from 'vue'
import VdomComp from './components/VdomComp.vue'
const disabled = ref(true)
</script>

<template>
<div class="target"></div>
<div class="interop-render-vdom-comp">
<button @click="disabled = !disabled">toggle</button>
<div>
<Teleport to=".target" defer :disabled>
<VdomComp />
</Teleport>
</div>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script setup lang="ts">
const msg = 'vdom comp'
</script>

<template>
<h1>{{ msg }}</h1>
</template>
2 changes: 2 additions & 0 deletions packages-private/vapor-e2e-test/teleport/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<script type="module" src="./main.ts"></script>
<div id="app"></div>
5 changes: 5 additions & 0 deletions packages-private/vapor-e2e-test/teleport/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createVaporApp, vaporInteropPlugin } from 'vue'
import App from './App.vue'
import 'todomvc-app-css/index.css'

createVaporApp(App).use(vaporInteropPlugin).mount('#app')
6 changes: 6 additions & 0 deletions packages-private/vapor-e2e-test/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// make sure these ports are unique
export const ports = {
vdomInterop: 8193,
todomvc: 8194,
teleport: 8195,
}
1 change: 1 addition & 0 deletions packages-private/vapor-e2e-test/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default defineConfig({
input: {
interop: resolve(import.meta.dirname, 'interop/index.html'),
todomvc: resolve(import.meta.dirname, 'todomvc/index.html'),
teleport: resolve(import.meta.dirname, 'teleport/index.html'),
},
},
},
Expand Down
10 changes: 9 additions & 1 deletion packages/compiler-vapor/src/generators/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { genEventHandler } from './event'
import { genDirectiveModifiers, genDirectivesForElement } from './directive'
import { genBlock } from './block'
import { genModelHandler } from './vModel'
import { isBuiltInComponent } from '../utils'

export function genCreateComponent(
operation: CreateComponentIRNode,
Expand Down Expand Up @@ -92,8 +93,15 @@ export function genCreateComponent(
} else if (operation.asset) {
return toValidAssetId(operation.tag, 'component')
} else {
const { tag } = operation
const builtInTag = isBuiltInComponent(tag)
if (builtInTag) {
// @ts-expect-error
helper(builtInTag)
return `_${builtInTag}`
}
return genExpression(
extend(createSimpleExpression(operation.tag, false), { ast: null }),
extend(createSimpleExpression(tag, false), { ast: null }),
context,
)
}
Expand Down
8 changes: 7 additions & 1 deletion packages/compiler-vapor/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
type VaporDirectiveNode,
} from '../ir'
import { EMPTY_EXPRESSION } from './utils'
import { findProp } from '../utils'
import { findProp, isBuiltInComponent } from '../utils'

export const isReservedProp: (key: string) => boolean = /*#__PURE__*/ makeMap(
// the leading comma is intentional so empty string "" is also included
Expand Down Expand Up @@ -109,6 +109,12 @@ function transformComponentElement(
asset = false
}

const builtInTag = isBuiltInComponent(tag)
if (builtInTag) {
tag = builtInTag
asset = false
}

const dotIndex = tag.indexOf('.')
if (dotIndex > 0) {
const ns = resolveSetupReference(tag.slice(0, dotIndex), context)
Expand Down
11 changes: 11 additions & 0 deletions packages/compiler-vapor/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,14 @@ export function getLiteralExpressionValue(
}
return exp.isStatic ? exp.content : null
}

export function isTeleportTag(tag: string): boolean {
tag = tag.toLowerCase()
return tag === 'teleport' || tag === 'vaporteleport'
}

export function isBuiltInComponent(tag: string): string | undefined {
if (isTeleportTag(tag)) {
return 'VaporTeleport'
}
}
6 changes: 3 additions & 3 deletions packages/runtime-core/src/components/Teleport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export const TeleportEndKey: unique symbol = Symbol('_vte')

export const isTeleport = (type: any): boolean => type.__isTeleport

const isTeleportDisabled = (props: VNode['props']): boolean =>
export const isTeleportDisabled = (props: VNode['props']): boolean =>
props && (props.disabled || props.disabled === '')

const isTeleportDeferred = (props: VNode['props']): boolean =>
export const isTeleportDeferred = (props: VNode['props']): boolean =>
props && (props.defer || props.defer === '')

const isTargetSVG = (target: RendererElement): boolean =>
Expand All @@ -39,7 +39,7 @@ const isTargetSVG = (target: RendererElement): boolean =>
const isTargetMathML = (target: RendererElement): boolean =>
typeof MathMLElement === 'function' && target instanceof MathMLElement

const resolveTarget = <T = RendererElement>(
export const resolveTarget = <T = RendererElement>(
props: TeleportProps | null,
select: RendererOptions['querySelector'],
): T | null => {
Expand Down
2 changes: 1 addition & 1 deletion packages/runtime-core/src/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ function reload(id: string, newComp: HMRComponent): void {
// create a snapshot which avoids the set being mutated during updates
const instances = [...record.instances]

if (newComp.vapor) {
if (newComp.__vapor) {
for (const instance of instances) {
instance.hmrReload!(newComp)
}
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,11 @@ export { startMeasure, endMeasure } from './profiling'
* @internal
*/
export { initFeatureFlags } from './featureFlags'
/**
* @internal
*/
export {
resolveTarget as resolveTeleportTarget,
isTeleportDisabled,
isTeleportDeferred,
} from './components/Teleport'
9 changes: 2 additions & 7 deletions packages/runtime-vapor/__tests__/block.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import {
VaporFragment,
insert,
normalizeBlock,
prepend,
remove,
} from '../src/block'
import { insert, normalizeBlock, prepend, remove } from '../src/block'
import { VaporFragment } from '../src/fragment'

const node1 = document.createTextNode('node1')
const node2 = document.createTextNode('node2')
Expand Down
Loading