feat(nuxt): tree-shake client and server-only composables (#5749)

This commit is contained in:
Daniel Roe 2022-07-07 17:04:38 +01:00 committed by GitHub
parent 5c323ca195
commit 4d607080f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 0 deletions

View File

@ -59,6 +59,7 @@
"pathe": "^0.3.2",
"perfect-debounce": "^0.1.3",
"scule": "^0.2.1",
"strip-literal": "^0.4.0",
"ufo": "^0.8.4",
"unctx": "^1.1.4",
"unenv": "^0.5.2",

View File

@ -14,6 +14,7 @@ import { distDir, pkgDir } from '../dirs'
import { version } from '../../package.json'
import { ImportProtectionPlugin, vueAppPatterns } from './plugins/import-protection'
import { UnctxTransformPlugin } from './plugins/unctx'
import { TreeShakePlugin } from './plugins/tree-shake'
import { addModuleTranspiles } from './modules'
import { initNitro } from './nitro'
@ -67,6 +68,17 @@ async function initNuxt (nuxt: Nuxt) {
addVitePlugin(UnctxTransformPlugin(nuxt).vite({ sourcemap: nuxt.options.sourcemap }))
addWebpackPlugin(UnctxTransformPlugin(nuxt).webpack({ sourcemap: nuxt.options.sourcemap }))
if (!nuxt.options.dev) {
const removeFromServer = ['onBeforeMount', 'onMounted', 'onBeforeUpdate', 'onRenderTracked', 'onRenderTriggered', 'onActivated', 'onDeactivated', 'onBeforeUnmount']
const removeFromClient = ['onServerPrefetch', 'onRenderTracked', 'onRenderTriggered']
// Add tree-shaking optimisations for SSR - build time only
addVitePlugin(TreeShakePlugin.vite({ sourcemap: nuxt.options.sourcemap, treeShake: removeFromServer }), { client: false })
addVitePlugin(TreeShakePlugin.vite({ sourcemap: nuxt.options.sourcemap, treeShake: removeFromClient }), { server: false })
addWebpackPlugin(TreeShakePlugin.webpack({ sourcemap: nuxt.options.sourcemap, treeShake: removeFromServer }), { client: false })
addWebpackPlugin(TreeShakePlugin.webpack({ sourcemap: nuxt.options.sourcemap, treeShake: removeFromClient }), { server: false })
}
// Transpile layers within node_modules
nuxt.options.build.transpile.push(
...nuxt.options._layers.filter(i => i.cwd && i.cwd.includes('node_modules')).map(i => i.cwd)

View File

@ -0,0 +1,49 @@
import { pathToFileURL } from 'node:url'
import { stripLiteral } from 'strip-literal'
import { parseQuery, parseURL } from 'ufo'
import MagicString from 'magic-string'
import { createUnplugin } from 'unplugin'
interface TreeShakePluginOptions {
sourcemap?: boolean
treeShake: string[]
}
export const TreeShakePlugin = createUnplugin((options: TreeShakePluginOptions) => {
const COMPOSABLE_RE = new RegExp(`($|\\s*)(${options.treeShake.join('|')})(?=\\()`, 'g')
return {
name: 'nuxt:server-treeshake:transfrom',
enforce: 'post',
transformInclude (id) {
const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href))
const { type, macro } = parseQuery(search)
// vue files
if (pathname.endsWith('.vue') && (type === 'script' || macro || !search)) {
return true
}
// js files
if (pathname.match(/\.((c|m)?j|t)sx?$/g)) {
return true
}
},
transform (code, id) {
if (!code.match(COMPOSABLE_RE)) { return }
const s = new MagicString(code)
const strippedCode = stripLiteral(code)
for (const match of strippedCode.matchAll(COMPOSABLE_RE) || []) {
s.overwrite(match.index, match.index + match[0].length, `(() => {}) || /*#__PURE__*/ false && ${match[0]}`)
}
if (s.hasChanged()) {
return {
code: s.toString(),
map: options.sourcemap && s.generateMap({ source: id, includeContent: true })
}
}
}
}
})

View File

@ -244,6 +244,14 @@ describe('reactivity transform', () => {
})
})
describe('server tree shaking', () => {
it('should work', async () => {
const html = await $fetch('/client')
expect(html).toContain('This page should not crash when rendered')
})
})
describe('extends support', () => {
describe('layouts & pages', () => {
it('extends foo/layouts/default & foo/pages/index', async () => {

View File

@ -0,0 +1,6 @@
// @ts-ignore
window.test = true
export default () => ({
render: () => 'hi'
})

16
test/fixtures/basic/pages/client.vue vendored Normal file
View File

@ -0,0 +1,16 @@
<script setup lang="ts">
onMounted(() => import('~/components/BreaksServer'))
onBeforeMount(() => import('~/components/BreaksServer'))
onBeforeUpdate(() => import('~/components/BreaksServer'))
onRenderTracked(() => import('~/components/BreaksServer'))
onRenderTriggered(() => import('~/components/BreaksServer'))
onActivated(() => import('~/components/BreaksServer'))
onDeactivated(() => import('~/components/BreaksServer'))
onBeforeUnmount(() => import('~/components/BreaksServer'))
</script>
<template>
<div>
This page should not crash when rendered.
</div>
</template>

View File

@ -9900,6 +9900,7 @@ __metadata:
pathe: ^0.3.2
perfect-debounce: ^0.1.3
scule: ^0.2.1
strip-literal: ^0.4.0
ufo: ^0.8.4
unbuild: latest
unctx: ^1.1.4