mirror of
https://github.com/nuxt/nuxt.git
synced 2025-02-16 13:48:13 +00:00
feat(nuxt): tree-shake client and server-only composables (#5749)
This commit is contained in:
parent
5c323ca195
commit
4d607080f5
@ -59,6 +59,7 @@
|
|||||||
"pathe": "^0.3.2",
|
"pathe": "^0.3.2",
|
||||||
"perfect-debounce": "^0.1.3",
|
"perfect-debounce": "^0.1.3",
|
||||||
"scule": "^0.2.1",
|
"scule": "^0.2.1",
|
||||||
|
"strip-literal": "^0.4.0",
|
||||||
"ufo": "^0.8.4",
|
"ufo": "^0.8.4",
|
||||||
"unctx": "^1.1.4",
|
"unctx": "^1.1.4",
|
||||||
"unenv": "^0.5.2",
|
"unenv": "^0.5.2",
|
||||||
|
@ -14,6 +14,7 @@ import { distDir, pkgDir } from '../dirs'
|
|||||||
import { version } from '../../package.json'
|
import { version } from '../../package.json'
|
||||||
import { ImportProtectionPlugin, vueAppPatterns } from './plugins/import-protection'
|
import { ImportProtectionPlugin, vueAppPatterns } from './plugins/import-protection'
|
||||||
import { UnctxTransformPlugin } from './plugins/unctx'
|
import { UnctxTransformPlugin } from './plugins/unctx'
|
||||||
|
import { TreeShakePlugin } from './plugins/tree-shake'
|
||||||
import { addModuleTranspiles } from './modules'
|
import { addModuleTranspiles } from './modules'
|
||||||
import { initNitro } from './nitro'
|
import { initNitro } from './nitro'
|
||||||
|
|
||||||
@ -67,6 +68,17 @@ async function initNuxt (nuxt: Nuxt) {
|
|||||||
addVitePlugin(UnctxTransformPlugin(nuxt).vite({ sourcemap: nuxt.options.sourcemap }))
|
addVitePlugin(UnctxTransformPlugin(nuxt).vite({ sourcemap: nuxt.options.sourcemap }))
|
||||||
addWebpackPlugin(UnctxTransformPlugin(nuxt).webpack({ 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
|
// Transpile layers within node_modules
|
||||||
nuxt.options.build.transpile.push(
|
nuxt.options.build.transpile.push(
|
||||||
...nuxt.options._layers.filter(i => i.cwd && i.cwd.includes('node_modules')).map(i => i.cwd)
|
...nuxt.options._layers.filter(i => i.cwd && i.cwd.includes('node_modules')).map(i => i.cwd)
|
||||||
|
49
packages/nuxt/src/core/plugins/tree-shake.ts
Normal file
49
packages/nuxt/src/core/plugins/tree-shake.ts
Normal 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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
@ -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('extends support', () => {
|
||||||
describe('layouts & pages', () => {
|
describe('layouts & pages', () => {
|
||||||
it('extends foo/layouts/default & foo/pages/index', async () => {
|
it('extends foo/layouts/default & foo/pages/index', async () => {
|
||||||
|
6
test/fixtures/basic/components/BreaksServer.ts
vendored
Normal file
6
test/fixtures/basic/components/BreaksServer.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
window.test = true
|
||||||
|
|
||||||
|
export default () => ({
|
||||||
|
render: () => 'hi'
|
||||||
|
})
|
16
test/fixtures/basic/pages/client.vue
vendored
Normal file
16
test/fixtures/basic/pages/client.vue
vendored
Normal 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>
|
Loading…
Reference in New Issue
Block a user