fix(nuxt): don't treeshake client-only fallback templates (#7659)

This commit is contained in:
Daniel Roe 2022-09-20 07:24:45 +01:00 committed by GitHub
parent 42cf48e45d
commit f8a23564c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 16 additions and 4 deletions

View File

@ -6,7 +6,7 @@ import type { Component } from '@nuxt/schema'
interface TreeShakeTemplatePluginOptions { interface TreeShakeTemplatePluginOptions {
sourcemap?: boolean sourcemap?: boolean
getComponents(): Component[] getComponents (): Component[]
} }
export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplatePluginOptions) => { export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplatePluginOptions) => {
@ -26,7 +26,7 @@ export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplat
.filter(c => c.mode === 'client' && !components.some(other => other.mode !== 'client' && other.pascalName === c.pascalName)) .filter(c => c.mode === 'client' && !components.some(other => other.mode !== 'client' && other.pascalName === c.pascalName))
.map(c => `${c.pascalName}|${c.kebabName}`) .map(c => `${c.pascalName}|${c.kebabName}`)
.concat('ClientOnly|client-only') .concat('ClientOnly|client-only')
.map(component => `<(${component})[^>]*>[\\s\\S]*?<\\/(${component})>`) .map(component => `<(${component})(| [^>]*)>[\\s\\S]*?<\\/(${component})>`)
regexpMap.set(components, new RegExp(`(${clientOnlyComponents.join('|')})`, 'g')) regexpMap.set(components, new RegExp(`(${clientOnlyComponents.join('|')})`, 'g'))
} }
@ -34,8 +34,12 @@ export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplat
const COMPONENTS_RE = regexpMap.get(components)! const COMPONENTS_RE = regexpMap.get(components)!
const s = new MagicString(code) const s = new MagicString(code)
// Do not render client-only slots on SSR, but preserve attributes // Do not render client-only slots on SSR, but preserve attributes and fallback/placeholder slots
s.replace(COMPONENTS_RE, r => r.replace(/<([^>]*[^/])\/?>[\s\S]*$/, '<$1 />')) s.replace(COMPONENTS_RE, r => r.replace(/<([^>]*[^/])\/?>[\s\S]*$/, (chunk: string, el: string) => {
const fallback = chunk.match(/<template[^>]*(#|v-slot:)(fallback|placeholder)[^>]*>[\s\S]*?<\/template>/)?.[0] || ''
const tag = el.split(' ').shift()
return `<${el}>${fallback}</${tag}>`
}))
if (s.hasChanged()) { if (s.hasChanged()) {
return { return {

View File

@ -137,6 +137,8 @@ describe('pages', () => {
const html = await $fetch('/client-only-components') const html = await $fetch('/client-only-components')
expect(html).toContain('<div class="client-only-script" foo="bar">') expect(html).toContain('<div class="client-only-script" foo="bar">')
expect(html).toContain('<div class="client-only-script-setup" foo="hello">') expect(html).toContain('<div class="client-only-script-setup" foo="hello">')
expect(html).toContain('<div>Fallback</div>')
expect(html).not.toContain('Should not be server rendered')
await expectNoClientErrors('/client-only-components') await expectNoClientErrors('/client-only-components')
}) })

View File

@ -8,5 +8,11 @@
</div> </div>
</template> </template>
</ClientOnlySetupScript> </ClientOnlySetupScript>
<ClientOnly>
Should not be server rendered.
<template #fallback>
<div>Fallback</div>
</template>
</ClientOnly>
</div> </div>
</template> </template>