diff --git a/packages/nuxt/package.json b/packages/nuxt/package.json index 5f90d2fad4e..6ff6052ef81 100644 --- a/packages/nuxt/package.json +++ b/packages/nuxt/package.json @@ -70,7 +70,6 @@ "scule": "^1.0.0", "strip-literal": "^1.0.1", "ufo": "^1.0.1", - "ultrahtml": "^1.2.0", "unctx": "^2.1.1", "unenv": "^1.1.0", "unhead": "^1.0.21", diff --git a/packages/nuxt/src/components/module.ts b/packages/nuxt/src/components/module.ts index a340646d7d5..41d7a5a834f 100644 --- a/packages/nuxt/src/components/module.ts +++ b/packages/nuxt/src/components/module.ts @@ -192,35 +192,35 @@ export default defineNuxtModule({ const mode = isClient ? 'client' : 'server' config.plugins = config.plugins || [] - config.plugins.push(loaderPlugin.vite({ - sourcemap: nuxt.options.sourcemap[mode], - getComponents, - mode, - experimentalComponentIslands: nuxt.options.experimental.componentIslands - })) if (nuxt.options.experimental.treeshakeClientOnly && isServer) { config.plugins.push(TreeShakeTemplatePlugin.vite({ sourcemap: nuxt.options.sourcemap[mode], getComponents })) } + config.plugins.push(loaderPlugin.vite({ + sourcemap: nuxt.options.sourcemap[mode], + getComponents, + mode, + experimentalComponentIslands: nuxt.options.experimental.componentIslands + })) }) nuxt.hook('webpack:config', (configs) => { configs.forEach((config) => { const mode = config.name === 'client' ? 'client' : 'server' config.plugins = config.plugins || [] - config.plugins.push(loaderPlugin.webpack({ - sourcemap: nuxt.options.sourcemap[mode], - getComponents, - mode, - experimentalComponentIslands: nuxt.options.experimental.componentIslands - })) if (nuxt.options.experimental.treeshakeClientOnly && mode === 'server') { config.plugins.push(TreeShakeTemplatePlugin.webpack({ sourcemap: nuxt.options.sourcemap[mode], getComponents })) } + config.plugins.push(loaderPlugin.webpack({ + sourcemap: nuxt.options.sourcemap[mode], + getComponents, + mode, + experimentalComponentIslands: nuxt.options.experimental.componentIslands + })) }) }) } diff --git a/packages/nuxt/src/components/tree-shake.ts b/packages/nuxt/src/components/tree-shake.ts index 7e1b49d5271..874003d87d8 100644 --- a/packages/nuxt/src/components/tree-shake.ts +++ b/packages/nuxt/src/components/tree-shake.ts @@ -1,9 +1,10 @@ import { pathToFileURL } from 'node:url' import { parseURL } from 'ufo' import MagicString from 'magic-string' -import type { Node } from 'ultrahtml' -import { parse, walk, ELEMENT_NODE } from 'ultrahtml' +import { walk } from 'estree-walker' +import type { CallExpression, Property, Identifier, ImportDeclaration, MemberExpression, Literal, ReturnStatement, VariableDeclaration, ObjectExpression, Node } from 'estree' import { createUnplugin } from 'unplugin' +import escapeStringRegexp from 'escape-string-regexp' import type { Component } from '@nuxt/schema' import { resolve } from 'pathe' import { distDir } from '../dirs' @@ -13,57 +14,115 @@ interface TreeShakeTemplatePluginOptions { getComponents (): Component[] } -const PLACEHOLDER_RE = /^(v-slot|#)(fallback|placeholder)/ +type AcornNode = N & { start: number, end: number } + +const SSR_RENDER_RE = /ssrRenderComponent/ +const PLACEHOLDER_EXACT_RE = /^(fallback|placeholder)$/ export const TreeShakeTemplatePlugin = createUnplugin((options: TreeShakeTemplatePluginOptions) => { - const regexpMap = new WeakMap() + const regexpMap = new WeakMap() return { name: 'nuxt:tree-shake-template', - enforce: 'pre', + enforce: 'post', transformInclude (id) { const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href)) return pathname.endsWith('.vue') }, - async transform (code, id) { - const template = code.match(/