Skip to content

Commit 252db0a

Browse files
committed
refactor(sfc): infer vapor multi-root metadata from template analysis
1 parent 7d59ef6 commit 252db0a

File tree

10 files changed

+440
-46
lines changed

10 files changed

+440
-46
lines changed

packages/compiler-core/src/codegen.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ export interface BaseCodegenResult {
111111
ast: unknown
112112
map?: RawSourceMap
113113
helpers?: Set<string> | Set<symbol>
114-
rootShape?: number
115114
}
116115

117116
export interface CodegenResult extends BaseCodegenResult {

packages/compiler-sfc/__tests__/compileScript.spec.ts

Lines changed: 219 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,10 +1831,225 @@ describe('vapor mode + ssr', () => {
18311831
})
18321832
})
18331833

1834-
describe('vapor mode', () => {
1835-
test.todo('inject root shape metadata into template-only vapor component')
1834+
describe('multiRoot metadata', () => {
1835+
test.todo('inject multiRoot metadata into template-only component')
18361836

1837-
test('inject root shape metadata into defineVaporComponent', () => {
1837+
test('marks a non-inline component as multi-root', () => {
1838+
const { content } = compile(
1839+
`
1840+
<script setup vapor>
1841+
const ok = true
1842+
</script>
1843+
<template>
1844+
<div />
1845+
<div />
1846+
</template>
1847+
`,
1848+
{
1849+
inlineTemplate: false,
1850+
vapor: true,
1851+
},
1852+
)
1853+
1854+
expect(content).toContain(`__multiRoot: true`)
1855+
})
1856+
1857+
test('treats root control flow as a single owner unit', () => {
1858+
const { content } = compile(
1859+
`
1860+
<script setup vapor>
1861+
const ok = true
1862+
</script>
1863+
<template>
1864+
<template v-if="ok">
1865+
<div />
1866+
<div />
1867+
</template>
1868+
</template>
1869+
`,
1870+
{
1871+
inlineTemplate: false,
1872+
vapor: true,
1873+
},
1874+
)
1875+
1876+
expect(content).toContain(`__multiRoot: false`)
1877+
})
1878+
1879+
test('treats preserved root comments as root units', () => {
1880+
const { content } = compile(
1881+
`
1882+
<script setup vapor>
1883+
const ok = true
1884+
</script>
1885+
<template>
1886+
<!-- root comment -->
1887+
<div />
1888+
</template>
1889+
`,
1890+
{
1891+
inlineTemplate: false,
1892+
vapor: true,
1893+
},
1894+
)
1895+
1896+
expect(content).toContain(`__multiRoot: true`)
1897+
})
1898+
1899+
test('respects template comments option when inferring multiRoot', () => {
1900+
const { content } = compile(
1901+
`
1902+
<script setup vapor>
1903+
const ok = true
1904+
</script>
1905+
<template>
1906+
<!-- root comment -->
1907+
<div />
1908+
</template>
1909+
`,
1910+
{
1911+
inlineTemplate: false,
1912+
vapor: true,
1913+
templateOptions: {
1914+
compilerOptions: {
1915+
comments: false,
1916+
},
1917+
},
1918+
},
1919+
)
1920+
1921+
expect(content).toContain(`__multiRoot: false`)
1922+
})
1923+
1924+
test('ignores comments between root if branches', () => {
1925+
const { content } = compile(
1926+
`
1927+
<script setup vapor>
1928+
const ok = true
1929+
</script>
1930+
<template>
1931+
<template v-if="ok">
1932+
<div />
1933+
</template>
1934+
<!-- branch separator -->
1935+
<template v-else>
1936+
<div />
1937+
</template>
1938+
</template>
1939+
`,
1940+
{
1941+
inlineTemplate: false,
1942+
vapor: true,
1943+
},
1944+
)
1945+
1946+
expect(content).toContain(`__multiRoot: false`)
1947+
})
1948+
1949+
test('treats root v-for as a single owner unit', () => {
1950+
const { content } = compile(
1951+
`
1952+
<script setup vapor>
1953+
const list = [1, 2]
1954+
</script>
1955+
<template>
1956+
<template v-for="item in list" :key="item">
1957+
<div>{{ item }}</div>
1958+
</template>
1959+
</template>
1960+
`,
1961+
{
1962+
inlineTemplate: false,
1963+
vapor: true,
1964+
},
1965+
)
1966+
1967+
expect(content).toContain(`__multiRoot: false`)
1968+
})
1969+
1970+
test('treats a root slot outlet as a single owner unit', () => {
1971+
const { content } = compile(
1972+
`
1973+
<script setup vapor>
1974+
const ok = true
1975+
</script>
1976+
<template>
1977+
<slot />
1978+
</template>
1979+
`,
1980+
{
1981+
inlineTemplate: false,
1982+
vapor: true,
1983+
},
1984+
)
1985+
1986+
expect(content).toContain(`__multiRoot: false`)
1987+
})
1988+
1989+
test('treats a root component as a single owner unit', () => {
1990+
const { content } = compile(
1991+
`
1992+
<script setup vapor>
1993+
const Foo = {}
1994+
</script>
1995+
<template>
1996+
<Foo />
1997+
</template>
1998+
`,
1999+
{
2000+
inlineTemplate: false,
2001+
vapor: true,
2002+
},
2003+
)
2004+
2005+
expect(content).toContain(`__multiRoot: false`)
2006+
})
2007+
2008+
test('treats a root component with a sibling as multi-root', () => {
2009+
const { content } = compile(
2010+
`
2011+
<script setup vapor>
2012+
import { KeepAlive } from 'vue'
2013+
</script>
2014+
<template>
2015+
<KeepAlive>
2016+
<div />
2017+
</KeepAlive>
2018+
<span />
2019+
</template>
2020+
`,
2021+
{
2022+
inlineTemplate: true,
2023+
vapor: true,
2024+
},
2025+
)
2026+
2027+
expect(content).toContain(`__multiRoot: true`)
2028+
})
2029+
2030+
test('treats a plain root template element as a single root', () => {
2031+
const { content } = compile(
2032+
`
2033+
<script setup vapor>
2034+
const ok = true
2035+
</script>
2036+
<template>
2037+
<template>
2038+
<div />
2039+
<div />
2040+
</template>
2041+
</template>
2042+
`,
2043+
{
2044+
inlineTemplate: false,
2045+
vapor: true,
2046+
},
2047+
)
2048+
2049+
expect(content).toContain(`__multiRoot: false`)
2050+
})
2051+
2052+
test('marks an inline component as multi-root', () => {
18382053
const { content } = compile(
18392054
`
18402055
<script setup vapor>
@@ -1851,6 +2066,6 @@ describe('vapor mode', () => {
18512066
},
18522067
)
18532068

1854-
expect(content).toContain(`__shape: 2`)
2069+
expect(content).toContain(`__multiRoot: true`)
18552070
})
18562071
})

packages/compiler-sfc/__tests__/compileTemplate.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,3 +531,36 @@ test('prefixing props edge case in inline mode', () => {
531531
expect(content).toMatchSnapshot()
532532
expect(content).toMatch(`__props["Foo"]).Bar`)
533533
})
534+
535+
test('returns multiRoot metadata for a multi-root template when vapor is enabled', () => {
536+
const result = compile({
537+
filename: 'example.vue',
538+
source: `<div /><div />`,
539+
vapor: true,
540+
})
541+
542+
expect(result.multiRoot).toBe(true)
543+
})
544+
545+
test('returns single-root metadata for root control flow when vapor is enabled', () => {
546+
const result = compile({
547+
filename: 'example.vue',
548+
source: `<template v-if="ok"><div /><div /></template>`,
549+
vapor: true,
550+
})
551+
552+
expect(result.multiRoot).toBe(false)
553+
})
554+
555+
test('respects comments option when returning multiRoot metadata', () => {
556+
const result = compile({
557+
filename: 'example.vue',
558+
source: `<!-- root comment --><div />`,
559+
vapor: true,
560+
compilerOptions: {
561+
comments: false,
562+
},
563+
})
564+
565+
expect(result.multiRoot).toBe(false)
566+
})

packages/compiler-sfc/src/compileScript.ts

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import {
7070
resolveTemplateVModelIdentifiers,
7171
} from './script/importUsageCheck'
7272
import { processAwait } from './script/topLevelAwait'
73+
import { isMultiRoot } from './template/templateUtils'
7374

7475
export interface SFCScriptCompileOptions {
7576
/**
@@ -949,7 +950,6 @@ export function compileScript(
949950
let templateMap
950951
// 9. generate return statement
951952
let returned
952-
let shape: number | undefined
953953
// ensure props bindings register before compile template in inline mode
954954
const propsDecl = genRuntimeProps(ctx)
955955
if (!inlineMode || (!sfc.template && ctx.hasDefaultExportRender)) {
@@ -996,26 +996,25 @@ export function compileScript(
996996
}
997997
// inline render function mode - we are going to compile the template and
998998
// inline it right here
999-
const { code, preamble, tips, errors, helpers, map, rootShape } =
1000-
compileTemplate({
1001-
filename,
1002-
ast: sfc.template.ast,
1003-
source: sfc.template.content,
1004-
inMap: sfc.template.map,
1005-
...options.templateOptions,
1006-
id: scopeId,
1007-
scoped: sfc.styles.some(s => s.scoped),
1008-
isProd: options.isProd,
1009-
ssrCssVars: sfc.cssVars,
1010-
vapor,
1011-
compilerOptions: {
1012-
...(options.templateOptions &&
1013-
options.templateOptions.compilerOptions),
1014-
inline: true,
1015-
isTS: ctx.isTS,
1016-
bindingMetadata: ctx.bindingMetadata,
1017-
},
1018-
})
999+
const { code, preamble, tips, errors, helpers, map } = compileTemplate({
1000+
filename,
1001+
ast: sfc.template.ast,
1002+
source: sfc.template.content,
1003+
inMap: sfc.template.map,
1004+
...options.templateOptions,
1005+
id: scopeId,
1006+
scoped: sfc.styles.some(s => s.scoped),
1007+
isProd: options.isProd,
1008+
ssrCssVars: sfc.cssVars,
1009+
vapor,
1010+
compilerOptions: {
1011+
...(options.templateOptions &&
1012+
options.templateOptions.compilerOptions),
1013+
inline: true,
1014+
isTS: ctx.isTS,
1015+
bindingMetadata: ctx.bindingMetadata,
1016+
},
1017+
})
10191018
templateMap = map
10201019
if (tips.length) {
10211020
tips.forEach(warnOnce)
@@ -1041,7 +1040,6 @@ export function compileScript(
10411040
if (preamble) {
10421041
ctx.s.prepend(preamble)
10431042
}
1044-
shape = rootShape
10451043
// avoid duplicated unref import
10461044
// as this may get injected by the render function preamble OR the
10471045
// css vars codegen
@@ -1097,8 +1095,13 @@ export function compileScript(
10971095

10981096
const emitsDecl = genRuntimeEmits(ctx)
10991097
if (emitsDecl) runtimeOptions += `\n emits: ${emitsDecl},`
1100-
if (vapor && shape != null) {
1101-
runtimeOptions += `\n __shape: ${shape},`
1098+
1099+
// add multiRoot marker for vapor component
1100+
if (vapor && !ssr && sfc.template && !sfc.template.src) {
1101+
runtimeOptions += `\n __multiRoot: ${isMultiRoot(
1102+
sfc.template.ast!,
1103+
options.templateOptions?.compilerOptions,
1104+
)},`
11021105
}
11031106

11041107
let definedOptions = ''

0 commit comments

Comments
 (0)