Skip to content

fix(config): pass correct Next.js phase to function-form configs + instrumentation test coverage#273

Merged
james-elicx merged 8 commits intocloudflare:mainfrom
NathanDrake2406:fix/config-phase-instrumentation-tests
Mar 6, 2026
Merged

fix(config): pass correct Next.js phase to function-form configs + instrumentation test coverage#273
james-elicx merged 8 commits intocloudflare:mainfrom
NathanDrake2406:fix/config-phase-instrumentation-tests

Conversation

@NathanDrake2406
Copy link
Contributor

@NathanDrake2406 NathanDrake2406 commented Mar 5, 2026

Summary

  • fix(config): unwrapConfig() hardcoded "phase-development-server" when calling function-form Next.js configs, even during production builds. Apps that branch on phase in next.config.ts (e.g. (phase) => phase === "phase-production-build" ? prodConfig : devConfig) received the wrong config during vinext build. Now maps Vite's env.command to the correct Next.js phase ("build""phase-production-build", "serve""phase-development-server").

  • test(instrumentation): Added 11 tests for server/instrumentation.ts which previously had zero coverage. Covers findInstrumentationFile (file discovery priority, src/ fallback, null when absent), runInstrumentation (register lifecycle, onRequestError storage, error isolation), and reportRequestError (handler forwarding, no-op without handler, handler error isolation).

  • fix(test): Fixed 6 pre-existing failures in static-export.test.ts App Router section. The test used a local startFixtureServer that loaded the fixture's vite.config.ts via configFile, which caused RSC plugin timing issues on Node 25. Switched to the shared startFixtureServerHelper from helpers.ts (uses configFile: false with explicit plugins), matching the pattern used by app-router.test.ts.

Test plan

  • npx vitest run tests/next-config.test.ts — 3 tests verifying phase propagation (production build phase, dev default, object-form ignores phase)
  • npx vitest run tests/instrumentation.test.ts — 11 tests covering file discovery, register/onRequestError lifecycle, error isolation
  • npx vitest run tests/static-export.test.ts — 14 tests now all pass (was 6 failures)
  • npx vitest run — full suite passes (2182 passed, 0 failed)
  • pnpm run typecheck — clean

unwrapConfig() hardcoded "phase-development-server" regardless of
whether vinext was running in dev or build mode. Apps that branch on
the phase argument in next.config.ts received wrong config during
production builds.

Map Vite's env.command to the correct Next.js phase constant:
- "serve" → "phase-development-server"
- "build" → "phase-production-build"
The instrumentation module had zero tests. Add 11 tests covering:

- findInstrumentationFile: file discovery across root and src/,
  priority ordering, null when absent
- runInstrumentation: register() lifecycle, onRequestError handler
  storage, graceful handling of empty modules, error isolation
  when ssrLoadModule fails
- reportRequestError: handler invocation with correct args,
  no-op without handler, error isolation when handler throws
The static-export test had its own startFixtureServer that loaded the
fixture's vite.config.ts via configFile. This caused RSC plugin timing
issues on Node 25 / macOS — the App Router dev server returned 404 for
all pages. The Pages Router section (same approach) worked because it
doesn't use the RSC plugin.

Switch the App Router section to use the shared startFixtureServerHelper
from helpers.ts, which uses configFile: false with explicit plugin
registration. This matches how app-router.test.ts (193 passing tests)
creates its dev server and avoids the RSC timing issue.
@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 5, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@273

commit: 74b80a8

NathanDrake2406 added a commit to NathanDrake2406/vinext that referenced this pull request Mar 5, 2026
loadNextConfig() hardcoded "phase-development-server" regardless of
context. Add optional phase parameter (matching cloudflare#273) and pass the
correct phase from each call site:

- vinext build: "phase-production-build"
- runStaticExport: "phase-export"

This ensures function-form next.config files that branch on the
phase argument receive the correct value during builds and exports.
@southpolesteve
Copy link
Collaborator

/bigbonk review please

Copy link
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Solid PR — the config phase fix is a real bug fix, the instrumentation tests are well-structured, and the static-export test fix is a reasonable approach. A few things to consider:

Config phase fix: Correct and well-tested. One improvement: import the existing PHASE_* constants from shims/constants.ts instead of hardcoding string literals in two more places.

Instrumentation tests: Good coverage of the public API. The vi.resetModules() pattern is the right approach for testing module-level state. One issue: temp directories are created but never cleaned up.

Static export test fix: The switch to startFixtureServerHelper makes sense and aligns with app-router.test.ts. Note that the old local startFixtureServer function is now only used by the Pages Router section — consider switching that too for consistency (and removing the local function), or adding a comment explaining why it's different.

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 5, 2026

The review was submitted successfully. Here's a summary of the feedback I posted:

Review posted on PR #273 with 4 inline comments:

  1. index.ts:1756 — Use the existing PHASE_PRODUCTION_BUILD and PHASE_DEVELOPMENT_SERVER constants from shims/constants.ts instead of hardcoding the phase strings. Includes a code suggestion.

  2. next-config.ts:180 — Same constant-import suggestion for unwrapConfig's default parameter.

  3. next-config.ts:200 — Same constant-import suggestion for loadNextConfig's default parameter.

  4. instrumentation.test.ts:16 — Temp directories created in beforeEach are never cleaned up (unlike next-config.test.ts which does clean up via afterEach). Suggested adding an afterEach cleanup block.

The review summary also noted that the now-local startFixtureServer in static-export.test.ts is only used by the Pages Router section and could be migrated to the shared helper too.

github run

- Import PHASE_PRODUCTION_BUILD and PHASE_DEVELOPMENT_SERVER constants
  from shims/constants.ts instead of hardcoding string literals in
  index.ts and config/next-config.ts
- Add afterEach cleanup for temp directories in instrumentation tests
…etup

- Replace hardcoded phase strings in next-config.test.ts with
  PHASE_PRODUCTION_BUILD and PHASE_DEVELOPMENT_SERVER constants
- Remove unnecessary vi.resetModules() from findInstrumentationFile
  tests — it's a pure function with no module-level state to reset
Remove the local startFixtureServer function — the shared helper from
tests/helpers.ts does the same thing. The Pages Router fixture's
vite.config.ts is just `vinext()` with no special options, so the
shared helper (which uses configFile: false + vinext plugin) works
identically.
@NathanDrake2406
Copy link
Contributor Author

@southpolesteve pls have a look :)

@southpolesteve
Copy link
Collaborator

/bigbonk re-review please

@southpolesteve
Copy link
Collaborator

@NathanDrake2406 thanks for keeping on working on this!

Copy link
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

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

Clean PR — the config phase fix is correct and well-tested, the instrumentation test coverage is solid, and the static-export test fix is the right approach. Previous review feedback (use PHASE_* constants, temp directory cleanup) has all been addressed.

One minor nit inline, otherwise LGTM.

};

// Should not throw
await runInstrumentation(server, "/fake/instrumentation.ts");
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: The runInstrumentation tests verify register() is called and onRequestError is stored, but there's no test for the case where register() itself throws. The implementation catches the outer ssrLoadModule failure (tested at line 106), but if register() throws, the error propagates uncaught from runInstrumentation since only the ssrLoadModule call is inside the try/catch — actually, looking again at the source, the await mod.register() is also inside the try/catch block at instrumentation.ts:92-109, so it would be caught. A test confirming that would be a nice addition, but not blocking.

@ask-bonk
Copy link
Contributor

ask-bonk bot commented Mar 6, 2026

Review submitted. Here's a summary of my findings:

Approved — this is a solid PR with three well-scoped changes:

  1. Config phase fix (index.ts, next-config.ts): Correctly maps env.command === "build" to PHASE_PRODUCTION_BUILD and defaults to PHASE_DEVELOPMENT_SERVER. The phase is properly threaded from the Vite config() hook through loadNextConfigunwrapConfig. Uses the existing constants from shims/constants.ts (previous review feedback addressed). Only one call site for loadNextConfig in the runtime, so the fix is complete.

  2. Instrumentation tests (instrumentation.test.ts): 11 tests with good coverage of findInstrumentationFile (file discovery priority, src/ fallback, null case), runInstrumentation (register lifecycle, onRequestError storage, error handling), and reportRequestError (handler forwarding, no-op, error isolation). The vi.resetModules() pattern correctly isolates module-level _onRequestError state between test runs. Temp directory cleanup is in place.

  3. Static export test fix (static-export.test.ts): Replaces the local startFixtureServer with the shared helper from helpers.ts (which uses configFile: false with explicit plugins), fixing RSC plugin timing issues. Both Pages Router and App Router sections now use the shared helper consistently.

One minor nit left as a non-blocking comment about adding a test for register() throwing.

github run

@NathanDrake2406
Copy link
Contributor Author

@NathanDrake2406 thanks for keeping on working on this!

Np haha, I had a bunch of tokens left before it reset, just putting it to good use

Keep both imports: PHASE_DEVELOPMENT_SERVER (from this PR) and
normalizePageExtensions (from upstream).
@james-elicx james-elicx merged commit d056e50 into cloudflare:main Mar 6, 2026
14 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.

3 participants