Skip to content

fix: keep vite out of the $app/env runtime validator#15953

Merged
Rich-Harris merged 4 commits into
sveltejs:mainfrom
hjaber:fix/explicit-env-runtime-no-vite
Jun 5, 2026
Merged

fix: keep vite out of the $app/env runtime validator#15953
Rich-Harris merged 4 commits into
sveltejs:mainfrom
hjaber:fix/explicit-env-runtime-no-vite

Conversation

@hjaber
Copy link
Copy Markdown
Contributor

@hjaber hjaber commented Jun 4, 2026

Problem

Enabling explicit environment variables (experimental.explicitEnvironmentVariables) pulls Vite into the production server runtime, which breaks any deployment that compiles or bundles the server into a self-contained artifact without node_modules.

The runtime env validator @sveltejs/kit/internal/env runs at server startupServer.init()set_env()validate() / handle_issues(). It imports stackless from exports/vite/utils.js, whose top-level imports include import { loadEnv } from 'vite'. So importing the validator drags all of Vite into the server bundle.

Concretely, compiling the SvelteKit server to a standalone binary with bun build --compile:

  • the build fails to resolve Vite's lazy import("esbuild") (esbuild is an optional peer of Vite 8), and
  • once that's worked around, the binary crashes at startup — Vite reads its own version via new URL('../../package.json', import.meta.url), which resolves to /package.json inside the compiled binary's virtual filesystem → ENOENT.

This very likely also affects pruned production runtimes (e.g. adapter-node deployed with npm ci --omit=dev), where Vite — a build-time/dev dependency — isn't installed but the runtime tries to import it.

Fix

stackless is a tiny, dependency-free helper (it just creates an Error with no stack trace). Move it to src/utils/stackless.js and import it from there in internal/env.js. exports/vite/utils.js re-exports it, so the build-time caller (exports/vite/index.js) is unchanged. This removes Vite from the runtime env validator's import graph entirely.

Test

Adds packages/kit/src/exports/internal/env.spec.js, which statically walks the import graph of internal/env.js (via acorn) and asserts it reaches neither vite nor any module under exports/vite/. It fails on main (internal/env.js → ../vite/utils.js → vite) and passes with this change.


  • This message body clearly illustrates the problem this PR solves.
  • Includes a test that fails without this PR and passes with it (verified the graph-walk both ways: fails on main, passes here).
  • Changeset: none — explicit env vars are unreleased.
  • Please ensure that 'Allow edits from maintainers' is checked.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 4, 2026

🦋 Changeset detected

Latest commit: e32358b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@sveltejs/kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@hjaber hjaber force-pushed the fix/explicit-env-runtime-no-vite branch from 691d402 to 978edfb Compare June 4, 2026 08:30
`@sveltejs/kit/internal/env` runs at server startup but imported `stackless`
from `exports/vite/utils.js`, which imports `vite` — pulling Vite into the
production server bundle and breaking deploys that compile/bundle the server
without `node_modules` (e.g. `bun build --compile`). Move `stackless` to a
dependency-free `utils/stackless.js` (re-exported from the vite utils so
build-time callers are unchanged) and add a regression test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hjaber hjaber force-pushed the fix/explicit-env-runtime-no-vite branch from 978edfb to 2814641 Compare June 4, 2026 08:49
Comment thread packages/kit/src/exports/vite/utils.js Outdated
import { negotiate } from '../../utils/http.js';
import { filter_env } from '../../utils/env.js';
import { escape_html } from '../../utils/escape.js';
import { stackless } from '../../utils/stackless.js';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we avoid the re-export and instead change the files to import directly?

Comment thread packages/kit/src/utils/stackless.js Outdated
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it possible to rename this to error.js if there isn’t one already?

Copy link
Copy Markdown
Member

@teemingc teemingc left a comment

Choose a reason for hiding this comment

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

We might need a changeset now. Explicit env vars was just released

hjaber and others added 2 commits June 4, 2026 08:58
Addresses review feedback: drop the re-export from exports/vite/utils.js and
import `stackless` directly from utils/stackless.js in exports/vite/index.js.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- merge stackless into the existing utils/error.js (per review) and import it
  directly from there; delete the standalone utils/stackless.js
- harden the regression test to follow @sveltejs/kit/* self-imports, so a Vite
  pull reaching internal/env *through* @sveltejs/kit/internal can't slip past
- add the patch changeset (explicit env vars is now released)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hjaber
Copy link
Copy Markdown
Contributor Author

hjaber commented Jun 4, 2026

Thanks @teemingc, all addressed in the latest commits:

  • Dropped the re-export. internal/env, vite/index, and vite/utils now import stackless directly.
  • Moved it into utils/error.js and deleted the standalone file. That adds an @sveltejs/kit/internal edge to the validator's graph, but that's runtime-only (and already loaded in every server), confirmed Vite-free with no cycle back to env/error, so it doesn't reintroduce the issue.
  • Added a patch changeset.

One extra thing: since error.js reaches @sveltejs/kit/internal, I taught the regression test to follow @sveltejs/kit/* self-imports too (via the exports map), not just relative ones, so a Vite import that ever sneaks in through internal would still be caught, not only direct exports/vite/* imports.

@teemingc
Copy link
Copy Markdown
Member

teemingc commented Jun 4, 2026

Thank you so much! I’ll have another look once I can get to my laptop if another maintainer doesn’t beat me to it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'll let other maintainers weigh in on this but I wonder if this should just be a custom ESLint rule -- we typically shouldn't be importing from the vite folder in other places in the first place.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This hit me while working on another PR too. Might be worth adding one. We already have one for kit internals

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It'd probably be better than adding it as a test because it'd give us much broader coverage

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I agree. I tried to build this just now but I don't have the patience to coax ESLint into doing something useful. (I don't think I'd run pnpm eslint 'packages/kit/src/**/*.js' locally before just now; hooboy is it slow. We really should switch to Oxlint if we can.)

For now I'll remove this test so that we can fix the immediate issue but we should definitely follow this up

@Rich-Harris Rich-Harris merged commit f0ba6e0 into sveltejs:main Jun 5, 2026
25 of 26 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 5, 2026
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.

4 participants