From 8bb8d824c73a7b2e4fcc4f0b870e6e036318e6a6 Mon Sep 17 00:00:00 2001 From: Julien Huang Date: Sat, 9 Mar 2024 13:38:08 +0100 Subject: [PATCH] fix(nuxt): detect component usage within `ssrRender` (#26162) --- packages/nuxt/src/components/tree-shake.ts | 2 +- packages/nuxt/test/treeshake-client.test.ts | 31 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/nuxt/src/components/tree-shake.ts b/packages/nuxt/src/components/tree-shake.ts index 1bcdb4dfb2..e8b9d9d84d 100644 --- a/packages/nuxt/src/components/tree-shake.ts +++ b/packages/nuxt/src/components/tree-shake.ts @@ -197,7 +197,7 @@ function isComponentNotCalledInSetup (codeAst: Node, name: string): string | voi let found = false walk(codeAst, { enter (node) { - if ((node.type === 'Property' && node.key.type === 'Identifier' && node.value.type === 'FunctionExpression' && node.key.name === 'setup') || (node.type === 'FunctionDeclaration' && node.id?.name === '_sfc_ssrRender')) { + if ((node.type === 'Property' && node.key.type === 'Identifier' && node.value.type === 'FunctionExpression' && node.key.name === 'setup') || (node.type === 'FunctionDeclaration' && (node.id?.name === '_sfc_ssrRender' || node.id?.name === 'ssrRender'))) { // walk through the setup function node or the ssrRender function walk(node, { enter (node) { diff --git a/packages/nuxt/test/treeshake-client.test.ts b/packages/nuxt/test/treeshake-client.test.ts index ac36ba584f..e7cf75412d 100644 --- a/packages/nuxt/test/treeshake-client.test.ts +++ b/packages/nuxt/test/treeshake-client.test.ts @@ -185,4 +185,35 @@ describe('treeshake client only in ssr', () => { expect(treeshaken.replace(/data-v-[\d\w]{8}/g, 'data-v-one-hash').replace(/scoped=[\d\w]{8}/g, 'scoped=one-hash')).toMatchSnapshot() }) } + + it('should not treeshake reused component #26137', async () => { + const treeshaken = await treeshake(`import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode } from "vue" + import { ssrRenderComponent as _ssrRenderComponent, ssrRenderAttrs as _ssrRenderAttrs } from "vue/server-renderer" + + export function ssrRender(_ctx, _push, _parent, _attrs) { + const _component_AppIcon = _resolveComponent("AppIcon") + const _component_ClientOnly = _resolveComponent("ClientOnly") + + _push(\`\`) + _push(_ssrRenderComponent(_component_AppIcon, { name: "caret-left" }, null, _parent)) + _push(_ssrRenderComponent(_component_ClientOnly, null, { + default: _withCtx((_, _push, _parent, _scopeId) => { + if (_push) { + _push(\`TEST\`) + _push(_ssrRenderComponent(_component_AppIcon, { name: "caret-up" }, null, _parent, _scopeId)) + } else { + return [ + _createVNode("span", null, "TEST"), + _createVNode(_component_AppIcon, { name: "caret-up" }) + ] + } + }), + _: 1 /* STABLE */ + }, _parent)) + _push(\`\`) + }`) + + expect(treeshaken).toContain('resolveComponent("AppIcon")') + expect(treeshaken).not.toContain('caret-up') + }) })