Skip to content

fix(bundler-webpack): externalize vue subpaths in ssr bundle (close #1718) - #1725

Merged
Mister-Hope merged 1 commit into
vuepress:mainfrom
maoger:fix/bundler-webpack-ssr-externals
Sep 29, 2026
Merged

Mister-Hope merged 1 commit into
vuepress:mainfrom
maoger:fix/bundler-webpack-ssr-externals

Conversation

@maoger

@maoger maoger commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Before submitting the PR, please make sure you do the following

  • Read the Contributing Guidelines.
  • Provide a description in this PR that addresses what the PR is solving. If this PR is going to solve an existing issue, please reference the issue (e.g. close #123).

What is the purpose of this pull request?

  • Bug fix
  • New feature
  • Documentation update
  • Other

Description

close #1718

Root cause

As analyzed in #1718, the webpack server bundle only externalizes vue itself:

config.externals(['vue'])

The array form of webpack externals matches requests exactly, so vue/server-renderer, which SFCs compiled for SSR import from, is resolved to @vue/server-renderer and bundled together with its own copies of @vue/runtime-core and @vue/shared, while vue stays external. Components are created by the bundled runtime, so instance.refs is the bundled EMPTY_OBJ, but useTemplateRef comes from the external vue and compares it with its own EMPTY_OBJ. With the production build of vue the check never matches, so the ref is defined as a non-configurable property on the shared EMPTY_OBJ, and rendering another page that uses the same ref key throws Cannot redefine property. The error is logged and the build still succeeds, but the content of that page is empty in the generated HTML.

Fix

Externalize vue and all of its subpaths:

config.externals([/^vue($|\/)/])

@vue/* packages are not externalized as proposed in #1718, because they are not guaranteed to be resolvable from the temp directory with pnpm. Trying it on the e2e site failed with Cannot find module '@vue/devtools-api'. Only vue is guaranteed to be resolvable, as it is a peer dependency of the site.

Server bundle of the e2e site (vuepress build docs --debug with the webpack bundler):

before after
app.cjs size 1,973,588 B 550,577 B
bundled @vue/runtime-core yes no
bundled @vue/server-renderer yes no
externals vue vue, vue/server-renderer

Tests

Added two e2e pages that use useTemplateRef with the same key, and a test that checks both pages are rendered, including their SSR HTML in build mode (the same way as imports/conditional-exports.spec.ts). Without the fix, e2e:build:webpack fails on whichever page is rendered second:

[WebServer] TypeError: Cannot redefine property: el
  1) [chromium] › tests/imports/use-template-ref.spec.ts › should render use-template-ref-2 correctly
    Expected substring: ">use-template-ref-2</div>"

Verification

  • pnpm lint, pnpm check-types, pnpm test:unit (63 files, 666 tests): pass.
  • e2e, all specs: e2e:build:webpack 91 passed, e2e:dev:webpack 84 passed, e2e:build 90 passed, e2e:dev 82 passed and 1 failed (hmr.spec.ts › "should update content and frontmatter and title correctly after navigation", which is flaky locally, failing once and passing once on rerun; the vite dev server is not affected by this change).

Screenshots

N/A

…1718)

The server bundle only externalized `vue` itself. The array form of
webpack `externals` matches requests exactly, so `vue/server-renderer`,
which SFCs compiled for SSR import from, was resolved to
`@vue/server-renderer` and bundled together with its own copies of
`@vue/runtime-core` and `@vue/shared`, while `vue` stayed external.

With two vue runtimes in one server bundle, components are created by
the bundled runtime, and `instance.refs` is the bundled `EMPTY_OBJ`,
but `useTemplateRef` comes from the external `vue` and compares it with
its own `EMPTY_OBJ`. With the production build of vue, the check never
matches, so the ref is defined as a non-configurable property on the
shared `EMPTY_OBJ`, and rendering another page that uses the same ref
key throws `Cannot redefine property`, leaving the page content empty
in the generated HTML.

Externalize `vue` and all of its subpaths. `@vue/*` packages are not
externalized, because they are not guaranteed to be resolvable from the
temp directory with pnpm, e.g. `@vue/devtools-api`.
Copilot AI balanced review requested due to automatic review settings September 29, 2026 05:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Mister-Hope
Mister-Hope merged commit e748f7c into vuepress:main Sep 29, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bundler-webpack: SSR bundle inlines a second Vue runtime -> 'Cannot redefine property' when rendering multiple pages

3 participants