Skip to content

fix(nuxt): make the Nuxt integration work on a stock Nuxt 4 app - #985

Open
philmillman wants to merge 14 commits into
mainfrom
pr-982-fixes
Open

fix(nuxt): make the Nuxt integration work on a stock Nuxt 4 app#985
philmillman wants to merge 14 commits into
mainfrom
pr-982-fixes

Conversation

@philmillman

@philmillman philmillman commented Aug 5, 2026

Copy link
Copy Markdown
Member

Builds on #982 (from a fork, so this targets main and carries its 8 commits). The last four commits are the new work. Intended to land in place of #982, or to be cherry-picked onto it.

Testing the Nuxt module against a real Nuxt 4 app turned up two defects that made it non-functional on a stock scaffold, plus a third found while fixing the second.

1. Env loaded from the wrong directory

Nuxt points vite's root at the srcDir (app/ by default in Nuxt 4), but .env.schema lives at the project root. The vite plugin reloaded varlock from root, varlock load succeeded there with an empty graph, and every build-time ENV.* replacement was dropped without a warning:

varlock:vite-integration loading config - count = 3 (cwd: .../nuxt-app/app)
varlock:vite-integration static replacements {}

nuxt dev returned 500 on every route. Production builds shipped a client bundle that threw ENV.PUBLIC_GREETING does not exist during hydration and rendered the Nuxt error page.

The plugin gains a rootDir option; the module passes nuxt.options.rootDir. The plugin now also tracks which directory the config came from and compares against that instead of process.cwd(), which fixes two other reloads that fell back to cwd (the telemetry-triggered one and the dev-restart one) and drops a redundant varlock load per build.

2. Init never reached the nitro bundle

Nitro rebuilds the server with its own rollup pass, so the init module injected into vite's SSR entry never reached .output/server — and server/api/** routes are never in vite's module graph at all.

grep -r "initVarlockEnv\|patchGlobalConsole" .output/server/chunks/   # no matches

So ssrInjectMode: 'auto-load' was a no-op (the mode the docs recommended for Node hosting), server-side log redaction never ran, and a route returning a sensitive value served it in plaintext with a 200. The module now registers the same init as a nitro plugin via addTemplate + addServerPlugin. buildVarlockSsrInitCode is extracted from the plugin closure and exported so both paths share one implementation; the extraction is verbatim.

3. auto-load tree-shaken out of the nitro bundle

Nitro's rollup treats external modules as side-effect free and dropped the bare import 'varlock/auto-load'. Emitted as a namespace binding under a new preserveSideEffectImports option.

Also covered: nuxt build --cwd <dir> does not chdir, and the nitro init template is generated before vite's config hook, so resolved-env baked an empty env and the server 500'd. The template is given rootDir too.

Verified

Against a real Nuxt 4 app on the default app/ layout:

before after
nuxt dev 500 on every route pages + server routes serve ENV
client bundle not inlined, throws on hydration inlined, no secret leak
auto-load standalone no-op, server 500s works
resolved-env (incl. --cwd) empty env baked, 500s works
response leak prevention secret served 200 in plaintext blocked, DETECTED LEAKED SENSITIVE CONFIG
log redaction absent server-side secret-log-test: su▒▒▒▒▒

New framework tests cover all of it: 16 passed, 5 skipped. Since the shared vite plugin changed, I also ran the suites that exercise it hardest — SvelteKit 19 passed, Astro v7 50 passed. Lint, typecheck:all, workspace test:ci, and the docs build all pass.

The --cwd scenario was negative-controlled: with the fix reverted it fails on Output files should contain "PUBLIC_VAR", and passes with it restored.

Packaging

  • @nuxt/kit moved to dependencies. It was being bundled from devDependencies, putting a second copy of kit in the artifact; dist went 575 KB → 67 KB.
  • Added src/globals.d.ts for the build-time constants, matching the other integrations, so the vite source no longer needs inline declare consts (that commit and its bump file are reverted).
  • Added a vitest config resolving workspace deps through ts-src, so test:ci is plain vitest --run again rather than building another package first.
  • Registered cascadeFrom: ["@varlock/vite-integration"] so a vite plugin release republishes the bundled copy, and wired the package into the pack helper and changed-integration detection.

For the reviewer

  • The dev-server framework scenario is skipped. nuxt dev emits nothing when spawned by this harness (detached + shell) so the ready pattern never matches, while the identical spawn — same command, cwd, env, pnpm version, --no-fork, stdin from /dev/null — streams normally outside vitest. It looks like harness plumbing rather than an integration bug; dev behavior was verified by hand. A TODO next to it records what was tried. Worth a look from someone who knows that harness better.
  • Docs: ssrInjectMode now states the default and what each mode is for, and notes that the module does not disable Nuxt's own .env loading, so process.env.X and ENV.X can diverge when a plain .env is present.
  • The package is named @varlock/nuxt-integration, matching every other integration in the repo. feat: add @varlock/nuxt package and docs #982 had renamed it to @varlock/nuxt; that rename is reverted here.

atinux and others added 12 commits August 5, 2026 11:01
- Add packages/integrations/nuxt with Nuxt module wrapping the Vite plugin
- Add docs page at integrations/nuxt
- Add Nuxt to sidebar, works-with tiles, and root README

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rename package to @varlock/nuxt across package metadata and docs
- Use addVitePlugin from @nuxt/kit in the Nuxt module
- Target Nuxt 4 in devDependencies while keeping compatibility >=3.0.0
- Add bumpy changeset for @varlock/nuxt

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add inline declarations for __VARLOCK_INTEGRATION_NAME__ and __VARLOCK_INTEGRATION_VERSION__
- Keeps ts-src consumers type-safe during workspace typecheck

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add a vitest for @varlock/nuxt module setup
- Verify addVitePlugin registration and option forwarding
- Add test and test:ci scripts to the Nuxt package

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove claim that ENV is available during nuxt.config evaluation
- Document varlock run + process.env pattern for config-time usage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Build @varlock/vite-integration before running Nuxt package tests
- Ensures clean workspace runs do not depend on prebuilt dist artifacts

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The plugin reloaded varlock from vite's `root`, which is not the project
root for every framework. Nuxt points `root` at the srcDir (`app/` in Nuxt
4), so `varlock load` ran where there is no `.env.schema`, succeeded with an
empty graph, and silently dropped every build-time `ENV.*` replacement.

- add a `rootDir` option so an integration can name the real project root
- track the directory the config was loaded from, and compare against that
  rather than `process.cwd()`. This also fixes the telemetry-triggered
  reload and the dev-restart reload, which both fell back to cwd and would
  undo a corrected root, and removes one redundant `varlock load` per build
- warn when a reload from a new directory empties a working config, instead
  of failing silently at runtime

Also export `buildVarlockSsrInitCode`, extracted verbatim from the plugin
closure, so integrations can inject the same init sequence into build
pipelines vite does not own. It takes `rootDir` for callers that run before
the `config` hook, and `preserveSideEffectImports` for bundlers that treat
external modules as side-effect free.

The `declare const` block added for ts-src consumers is reverted; the
consuming package gets its own `globals.d.ts`, matching every other
integration.
…ro build

Two defects made the module non-functional on a stock Nuxt 4 app:

1. Nuxt sets vite's `root` to the srcDir, so varlock loaded no schema and
   `ENV.*` was never inlined. `nuxt dev` returned 500 on every route, and
   production builds shipped a client bundle that threw
   "`ENV.X` does not exist" during hydration. The module now passes
   `nuxt.options.rootDir` to the vite plugin.

2. Nitro rebuilds the server with its own rollup pass, so the init module
   injected into vite's SSR entry never reached `.output/server` — and
   `server/api/**` routes are never in vite's module graph at all. The
   result: `ssrInjectMode: 'auto-load'` was a no-op, log redaction never
   ran, and a route returning a sensitive value served it in plaintext. The
   module now registers the same init as a nitro plugin.

Fixing (2) surfaced a third issue: nitro's rollup treats externals as
side-effect free and tree-shook the bare `import 'varlock/auto-load'`, so
the init code is emitted with `preserveSideEffectImports`.

`nuxt build --cwd <dir>` does not chdir, so the nitro init template — which
is generated before vite's `config` hook — is given `rootDir` too. Without
it a `resolved-env` build baked an empty env and every route 500'd.

Packaging:

- `@nuxt/kit` moves to `dependencies`. It was bundled from devDependencies,
  which put a second copy of kit in the artifact and took dist from 575 KB
  to 67 KB once externalized.
- add `src/globals.d.ts` for the build-time constants, as the other
  integrations do, so the vite source does not need the declarations
- add a vitest config resolving workspace deps through `ts-src`, so
  `test:ci` no longer has to build another package first
- register `cascadeFrom: ["@varlock/vite-integration"]` so a vite plugin
  release republishes the bundled copy
Both defects fixed in the previous commit only appear in a real build, so
the integration needed the same end-to-end coverage every other one has.

Scenarios: build assertions against the default Nuxt 4 layout (the srcDir
regression guard), a `--cwd` build (guards the nitro init template's load
directory), and three production-server runs covering `init-only` under
`varlock run`, `auto-load` standalone, and response leak prevention.

Registers `@varlock/nuxt` in the pack helper and the changed-integration
detection so CI runs these when either package changes.

The dev-server scenario is skipped: `nuxt dev` emits nothing when spawned
by this harness (detached + shell) so the ready pattern never matches,
while the identical spawn streams normally outside vitest. Details are in
the TODO next to it; dev behavior was verified by hand.
- state the default mode (`init-only`) and what each mode is actually for,
  including the `varlock run` invocation the default requires. The previous
  text recommended `auto-load` without saying the default differs.
- note that the module does not disable Nuxt's own `.env` loading, so
  `process.env.X` and `ENV.X` can diverge when a plain `.env` is present
- the package README claimed it overrides Nuxt's env loading; it describes
  what the module does now that it covers the nitro build
- drop em dashes per the repo writing conventions
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

bumpy-frog

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

minor Minor releases

  • @varlock/astro-integration 1.3.0 → 1.4.0 (cascade)
  • @varlock/cloudflare-integration 1.4.0 → 1.5.0 (cascade)
  • @varlock/nuxt-integration 0.0.1 → 0.1.0
  • @varlock/vite-integration 1.4.0 → 1.5.0

Bump files in this PR

Click here if you want to add another bump file to this PR


This comment is maintained by bumpy.

@pkg-pr-new

pkg-pr-new Bot commented Aug 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/dmno-dev/varlock/@varlock/astro-integration@985
npm i https://pkg.pr.new/dmno-dev/varlock/@varlock/cloudflare-integration@985
npm i https://pkg.pr.new/dmno-dev/varlock/@varlock/nuxt-integration@985
npm i https://pkg.pr.new/dmno-dev/varlock/@varlock/vite-integration@985

commit: ff6f579

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
varlock-website 3591e9f Commit Preview URL

Branch Preview URL
Aug 05 2026, 06:02 PM

@pullfrog pullfrog Bot left a comment

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.

Important

The Nuxt package's new standalone test setup still requires a prebuilt workspace dependency.

Reviewed changes across the new Nuxt module, shared Vite initialization extraction, Nitro integration, package wiring, framework coverage, and documentation.

  • Nuxt module: Registers the Varlock Vite plugin with the Nuxt project root and injects the same initialization sequence into Nitro's server bundle.
  • Shared Vite behavior: Adds explicit load-root tracking and exports the SSR initialization-code builder while retaining existing SSR modes, encryption, and platform-specific handling.
  • Runtime coverage: Exercises stock Nuxt 4 builds, alternate working directories, Nitro routes, SSR injection modes, leak blocking, and log redaction; the live dev scenario remains skipped with its harness limitation documented.
  • Packaging and docs: Adds release cascading, framework-test packing, package metadata, installation guidance, navigation, and SSR-mode documentation.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using azure/gpt-5.6-sol𝕏

Comment thread packages/integrations/nuxt/vitest.config.ts Outdated
Matches the naming used by every other integration in the repo
(astro-integration, cloudflare-integration, expo-integration,
nextjs-integration, vite-integration), reverting the rename made earlier in
this branch.

Updates the package name, docs, root README table, bumpy config and
changeset, framework test fixtures and pack helper, changed-integration
detection, and the lockfile.
@pullfrog

pullfrog Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Addressed the review by explicitly aliasing @varlock/vite-integration to its TypeScript source in the Nuxt Vitest config. Pushed commit ff6f5795; the standalone Nuxt suite passes all 4 tests with the Vite integration dist output absent, repository lint passes, and the review thread is resolved.

Task list (4/4 completed)
  • Check out PR 985 and verify the review finding against the current code
  • Implement the minimal correction and run focused validation
  • Review, commit, and push the final diff
  • Reply to and resolve every addressed review thread

Pullfrog  | View workflow run | via Pullfrog | Using azure/gpt-5.6-sol𝕏

@philmillman
philmillman requested a review from theoephraim August 5, 2026 18:31
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.

2 participants