Commit Graph

10 Commits

Author SHA1 Message Date
Divanshu Chauhan (divkix) 6a806ecee1 feat(instrumentation): support nextConfig.instrumentationClientInject (#1416)
* feat(instrumentation): support nextConfig.instrumentationClientInject (#1326)

Add support for the Next.js instrumentationClientInject config option that
lets users inject client bootstrap modules for side effects and an optional
onRouterTransitionStart hook ahead of the user's instrumentation-client file.

Changes:
- New generateInstrumentationClientInjectModule() generates a virtual ESM
  module with side-effect imports in array order + composed hook fan-out.
- Accept instrumentationClientInject in NextConfig / ResolvedNextConfig.
- New vinext:instrumentation-client-inject Vite plugin with resolveId/load
  hooks; virtual module string is precomputed once in config() for efficiency.
- Unit tests cover empty injects, single/multi specifiers, hook ordering,
  and empty-module fallback when no user file exists.

* test(instrumentation): fix oxfmt formatting and assertion indentation

The test expected 2-space indent for the generated code, but the
implementation produces 4-space indent within if blocks. Also fixes
oxfmt quote style and line wrapping.

* fix(ci): lint warnings and missing field in test helper

- Replace unnecessary escaped quotes inside template literals to fix
  eslint(no-useless-escape) warnings in instrumentation-client-inject.ts
- Add missing instrumentationClientInject field to makeResolved() test
  helper in next-config.test.ts to fix TS2322 type errors

* Address review feedback for instrumentationClientInject.

Make clientInjectModule self-clearing on config re-eval, filter non-string inject specifiers, document dual resolution paths, and add escaping coverage.

Signed-off-by: Divanshu Chauhan <divkix@divkix.me>

* test(instrumentation): add plugin pipeline integration tests and fix inject resolution

Add client-environment tests for instrumentationClientInject virtual module
wiring, omit the alias when injects are active so resolveId/load run, reload
next.config when root changes, and pre-resolve relative inject paths. Share
empty-module path via INSTRUMENTATION_CLIENT_EMPTY_MODULE and simplify tests.

* fix(instrumentation): resolve empty-module.js in production builds

CI E2E builds failed because INSTRUMENTATION_CLIENT_EMPTY_MODULE pointed
at empty-module.ts, which is not shipped in dist. Prefer .js when present.

* test(instrumentation): address PR review comments for client inject

- Revert nextConfigRoot guard changes in config loading to avoid buildId divergence risk
- Add cache-busting to dynamic ESM module imports in tests and document the reason
- Add integration test case for empty-inject and missing user-file fallback path

---------

Signed-off-by: Divanshu Chauhan <divkix@divkix.me>
2026-05-27 11:13:53 +01:00
Nathan Nguyen 2b958ad90e refactor: extract instrumentation lazy init to server/instrumentation-runtime.ts (#1015)
The 25-line __ensureInstrumentation block in entries/app-rsc-entry.ts was
module-level state plus idempotent init logic embedded in a generated entry
template. This is the canonical 'imperative shell with mutable bookkeeping'
pattern — it never belongs in codegen.

Lift it to a new server/instrumentation-runtime.ts module exporting
ensureInstrumentationRegistered(instrumentationModule). The generated RSC
entry now only:

1. Imports the helper (conditional on instrumentationPath).
2. Calls await ensureInstrumentationRegistered(_instrumentation) in the
   request handler.

All bookkeeping (initialized flag, shared promise, prerender skip) lives in
the typed helper, making it unit-testable and keeping the entry template thin.

New tests cover idempotency, concurrent racing, prerender skipping, and
graceful handling of modules without register/onRequestError.

Refs:
- Next.js instrumentation docs: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
- Next.js source: https://github.com/vercel/next.js/blob/canary/packages/next/src/server/dev/next-dev-server.ts (instrumentation boot)
- Next.js source: https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/nextjs-require-cache-hot-relayer.ts (onRequestError global wiring)
2026-05-02 20:09:03 +01:00
Stephen Zhou 207588d164 feat: add instrumentation-client support (#679)
* feat: add instrumentation-client support

* trigger ci

* fix deps

* tweaks

* tweaks

* tweaks

* tweaks

* rebuild lockfile

* fix: add app-with-src to CI E2E matrix

---------

Co-authored-by: James <james@eli.cx>
2026-03-28 21:21:36 +00:00
James Anderson 2ef52ad66b fix: use page extensions for middleware files (#595)
* fix: use page extensions for middleware files

* same thing for instrumentation
2026-03-20 08:45:26 +00:00
Stephen Zhou c17d6941be chore: migrate to vite plus (#535)
* chore: migrate to vite plus

* Disable typeAware and typeCheck

* Update CI

* Fix CI

* Fix test

* Clean

* Run test with vp

* Try revert

* react: false In test

* Fix test

* Revert "Try revert"

This reverts commit 009da10473.

* Update

* Update

* Try revert ci changes

* revert

* Run vp migrate

* Disable typeAware and typeCheck for now

* Better resolve for test

* Use vp dev instead of vite

* Update expect

* Fix NormalizeManifestModuleId

* Try increase timeout

* Update to use vp

* Try new check

* Bring back npx vp

* Migrate CI

* Make next-intl resolvable

* Update

* Update

* Update
2026-03-15 10:50:13 +00:00
Nathan Nguyen e9063411b8 fix: register reportRequestError with ctx.waitUntil on Workers (#493)
On Cloudflare Workers the isolate can terminate after the Response is
sent, killing in-flight error reports (e.g. Sentry HTTP requests).

Fix by having reportRequestError() self-register with
ctx.waitUntil() via getRequestExecutionContext(). This is the same
pattern used by KVCacheHandler, ISR regeneration, and fetch cache
revalidation. On Node.js (dev or vinext start) the context is null
so it's a no-op — fire-and-forget is fine because the process stays
alive.

Since reportRequestError never rejects (internal try/catch), the
redundant .catch() at all 5 call sites is removed.
2026-03-12 08:57:24 +00:00
James Anderson 764a496ce7 add oxfmt formatter (#380)
* add oxfmt formatter: config, scripts, CI, editor setup, docs

* rebuild lockfile

* fix: add Format to required checks list, remove dead ignore pattern

* run fmt

* add format to agents.md again
2026-03-09 14:56:14 +00:00
James Anderson 4e0294fc4f fix: middleware crash in dev mode with cloudflare plugin (#344)
* test: middleware with cloudflare plugin and routing bypass

* fix: use module runner for pages middleware

* add pages router dev test
2026-03-08 14:04:11 +00:00
James Anderson 80acf27933 fix: register instrumentation in rsc entry and put error handler in global scope (#330)
* test: instrumentation

* store onRequestError on globalThis

* add instrumentation registration app-dev-server

* guard against double calling runinstrumentation for appdir
2026-03-07 22:40:27 +00:00
Nathan Nguyen d056e50467 fix(config): pass correct Next.js phase to function-form configs + instrumentation test coverage (#273)
* fix(config): pass correct Next.js phase to function-form configs

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"

* test: add instrumentation module test coverage

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

* fix(test): use shared helper for App Router static export test

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.

* fix: address PR review feedback

- 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

* refactor: use phase constants in tests, simplify pure function test setup

- 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

* refactor: use shared startFixtureServer helper in static export tests

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.
2026-03-06 16:31:16 +00:00