Skip to content

feat: support bundled dev mode in ssr - #21626

Open
sheremet-va wants to merge 90 commits into
vitejs:mainfrom
sheremet-va:fix/support-full-bundle-mode-in-ssr
Open

sheremet-va wants to merge 90 commits into
vitejs:mainfrom
sheremet-va:fix/support-full-bundle-mode-in-ssr

Conversation

@sheremet-va

@sheremet-va sheremet-va commented Feb 12, 2026 •

Copy link
Copy Markdown
Member

TODO

I am exploring how full bundle mode can be supported in SSR. At the moment the goal is to support environments.ssr.runner.import and environment.fetchModule. Module graph, transformRequest, pluginContainer are out of scope of this PR, but they will be revisited later.

Known quirks (to discuss)

  • url in runner.import(url) is not resolved with resolveId like it is done usually. Instead it accepts:
    • Absolute URL to the bundled entry point specified in rolldownOptions.input
    • Relative URL (relative to root) to the bundled entry point
    • The chunk name
    • Relative URL to the bundled entry point starting with / (/entry-point.js vs ./entry-point.js)
    • The url must be an actual filename or a URL with the extension at the end
  • import.meta.url, import.meta.filename and import.meta.dirname reference "would be" (or "virtual") files, these are the URLs that would exist if vite kept the bundle on the file system. Doing something like readFileSync(import.meta.filename) will throw an error because the file does not exist on disk
    • For example for the build configuration like { ourDir: './dist' }, the filename would be something like <root>/dist/asset/chunk-1.js

To enable full bundle mode in SSR, add this to the config:

export default defineConfig({
  environments: {
    ssr: {
      isBundled: true,
    },
  },
})

@sheremet-va sheremet-va changed the title feat: support ful bundle mode in ssr feat: support full bundle mode in ssr Feb 12, 2026
@jamesopstad

Copy link
Copy Markdown
Contributor

Very excited to see the beginnings of this!

Comment thread packages/vite/src/node/server/environments/fullBundleEnvironment.ts Outdated
Comment thread packages/vite/src/module-runner/createImportMeta.ts Outdated
Comment thread packages/vite/src/module-runner/runner.ts
Comment thread packages/vite/src/module-runner/runner.ts Outdated
Comment thread packages/vite/src/module-runner/runner.ts Outdated
Comment thread packages/vite/src/node/config.ts Outdated
Comment thread packages/vite/src/node/ssr/fetchModule.ts Outdated
graphite-app Bot pushed a commit to rolldown/rolldown that referenced this pull request Aug 26, 2026
graphite-app Bot pushed a commit to rolldown/rolldown that referenced this pull request Aug 26, 2026
graphite-app Bot pushed a commit to rolldown/rolldown that referenced this pull request Aug 26, 2026
RomanHotsiy added a commit to redocly-contrib/vite that referenced this pull request Sep 30, 2026
Temporary fork of vite 8.3.1 with full bundle mode in SSR (vitejs#21626)
plus the fixes it needed, until that lands upstream. Consumers alias it:
`"vite": "npm:@redocly/vite@8.3.1"`. The version stays the upstream version
(no prerelease tag) so `vite: ^8` peer ranges keep matching; fork-only bumps
use patch numbers upstream will not reach (8.3.1001, …).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@RomanHotsiy

Copy link
Copy Markdown

Hi @sheremet-va 🙌 , thanks again fo this work!

I finally tried the branch on a real SSR app (Hono + React streaming SSR through environments.ssr.runner:

  • ~1.6k modules-
  • noExternal: true
  • module runners in child processes

and it is mainly working!
Page edits went from a 1.1 s graph re-evaluation to a 22 ms patch.

There are a few issues found by Claude which we verified and fixed on our side to make it work.

  • fetchBundledModule re-runs ssrTransform on the whole chunk for every runner.import() (the runner always invokes fetchModule; cached is ignored) — ~450 ms per request on a 10 MB chunk.
  • A build error is sent as an error payload through the server runner transport, which throws and exits the process; full-reload is rejected the same way.
  • reloadPending is never re-armed for the module runner after a program reload, so later pushes are dropped.
  • Dynamic imports return the stale chunk namespace after a patch; regionId is only set for config entries.
    runner.import() of a module's own virtual import.meta.url throws "Entrypoint … was not defined".
  • On 8.3.1, rolldown patches call rolldown_runtime.payloadDelivered, which the runner's runtime lacks, so every patch fell back to a program reload.

# Conflicts:
#	packages/vite/src/client/client.ts
#	packages/vite/src/module-runner/runner.ts
#	packages/vite/src/node/server/bundledDev.ts
#	packages/vite/src/node/ssr/fetchModule.ts
#	packages/vite/src/node/ssr/runtime/__tests__/server-hmr.spec.ts
#	packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts
#	packages/vite/src/node/ssr/runtime/__tests__/utils.ts
@sapphi-red sapphi-red added this to the 8.4 milestone Oct 19, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants