Commit Graph

19 Commits

Author SHA1 Message Date
James Anderson 9ec793244e fix(app-router): send deployment id on RSC responses (#2452)
* fix(app-router): send deployment id on RSC responses

* test(app-router): isolate deployment id integration coverage
2026-07-01 11:27:22 +01:00
James Anderson 0a48f0f7fb feat(cloudflare): move deploy command to cloudflare package (#2405)
* feat(cloudflare): move deploy command to cloudflare package

* fix(cloudflare): expose deploy cli bin
2026-06-29 16:47:28 +00:00
James Anderson b2ea860284 fix(proxy): match Next.js file conventions (#2155)
* fix(proxy): match Next.js file conventions

* test(proxy): keep convention fixture out of workspace

* test(proxy): provide middleware path in handler fixture
2026-06-18 23:54:39 +01:00
Nathan Nguyen b324cbe04c refactor(utils): dedupe shared helpers and lint redefinitions (#1793)
* refactor(utils): dedupe shared helpers and lint redefinitions

Shared helper logic was being reimplemented across generated entries, runtime modules, and shims. That made small utility semantics easy to drift and gave agents no mechanical feedback when they hand-rolled copies.

Add a local Oxlint rule that discovers exported shared helpers and reports local redefinitions, including helpers embedded in generated source strings. Centralize the existing low-risk duplicates through shared utility modules.

* fix(lint): harden shared utility rule validation

* test(lint): avoid matching oxlint success summary

* fix(lint): make shared utility rule cwd independent

* fix(lint): address bonk review comments

- Drop redundant "default" export conditions in package.json; the
  generated worker template resolves these subpaths via "import" like
  its siblings (request-pipeline, pages-i18n, config-matchers).
- Mask backtick template literals in prefer-shared-utils so an
  apostrophe or comment sequence inside generated template source can
  no longer open string/comment masking and swallow a subsequent
  helper definition (false negative). Template bodies stay scannable.

---------

Co-authored-by: James <james@eli.cx>
2026-06-06 22:56:55 +01:00
Nathan Nguyen e372ff336a test: rebalance app router integration shards (#1765)
Vitest integration CI shards by test file, so the 340-test App Router integration file becomes one oversized scheduling unit. That makes the slowest shard depend on one monolithic file even after adding native shards.

Split the App Router integration coverage into focused files and increase the integration matrix to four native Vitest shards. This keeps fileParallelism disabled for shared-fixture safety while giving CI smaller scheduling units to distribute.

Validation: vp check tests/app-router-*.test.ts vite.config.ts .github/workflows/ci.yml; vp test run --project integration tests/app-router-client-preloading.test.ts tests/app-router-dev-server.test.ts tests/app-router-external-rewrite.test.ts tests/app-router-font-google-prod.test.ts tests/app-router-isr-codegen.test.ts tests/app-router-malformed-url.test.ts tests/app-router-metadata-routes.test.ts tests/app-router-middleware-next-request.test.ts tests/app-router-next-config-codegen.test.ts tests/app-router-next-config-dev.test.ts tests/app-router-origin-check.test.ts tests/app-router-production-build.test.ts tests/app-router-production-server.test.ts tests/app-router-rsc-flight-hint.test.ts tests/app-router-rsc-plugin.test.ts tests/app-router-static-export.test.ts tests/app-router-worker-entry.test.ts
2026-06-05 15:48:54 +01:00
James Anderson 8d00797ada feat(cache): extract Cloudflare cache adapters into @vinext/cloudflare (#1748)
* refactor(cache): extract Cloudflare cache adapters into @vinext/cloudflare

Move the Cloudflare KV data cache and edge CDN cache adapters out of
vinext into a new publishable @vinext/cloudflare package:

  - cache/kv-data-adapter(.runtime).ts  (KVCacheHandler, kvDataAdapter)
  - cache/cdn-adapter(.runtime).ts       (CloudflareCdnCacheAdapter, cdnAdapter)

tpr.ts stays in vinext. vinext now depends on @vinext/cloudflare
(workspace:*) and the package declares vinext as a peer dep; both build
from source via tsconfig paths so there is no build-order cycle. The
vinext/cloudflare barrel still re-exports KVCacheHandler for back-compat.

Wires up tsconfig paths, a vitest source alias, root build/postinstall,
and the preview/publish workflows for the new package. Updates internal
consumers (apps/web, examples/workers-cache), docs, and tests.

* ci(create-next-app): install @vinext/cloudflare from local tarball

vinext now depends on @vinext/cloudflare, which isn't published to npm
yet. The create-next-app smoke test packs vinext locally and resolves
its deps from the registry, so the install (and dev server) failed with
ERR_PNPM_FETCH_404 for @vinext/cloudflare.

Pack @vinext/cloudflare alongside vinext and add a pnpm override in the
scaffolded project pointing at the local tarball so the dependency
resolves offline.

* refactor(cloudflare): address review feedback

- Remove the root barrel export from @vinext/cloudflare; expose only the
  ./cache/* subpaths via a wildcard export (no root main/types).
- vinext/cloudflare re-exports KVCacheHandler from the full subpath.
- Drop the redundant .npmignore (the package.json "files" allowlist
  already restricts the publish to dist).
- Remove the unsupported imperative setCacheHandler/KVCacheHandler usage
  from both READMEs; the cache plugin config is the supported approach.
- Simplify test wiring: drop the now-unused @vinext/cloudflare tsconfig
  path and dedupe the vitest source alias into a shared constant.

* chore(cloudflare): drop unused vite devDependency

The @vinext/cloudflare config uses vite-plus and nothing imports vite, so
the vite devDependency was unused. build/check/knip stay green without it.

* Apply suggestion from @james-elicx
2026-06-05 14:57:20 +01:00
James Anderson 61d5fe00a6 feat(release): commit-driven auto-generated changesets (#1753)
* feat(release): commit-driven changesets with auto-generated changesets

Adopt stock Changesets for versioning/changelog/publishing, with the only
bespoke surface being a script that creates changesets automatically from
Conventional Commits. Auto-generated changeset files never live on `main` —
they are written to the CI working tree, consumed by changesets/action into a
rolling Version PR, and discarded. Manually authored changesets still work.

- scripts/create-changeset.mjs: derive per-package bumps from Conventional
  Commits (paths -> package, type -> bump). Includes the version-vs-tag guard
  so a merged Version PR publishes instead of re-opening a PR.
- scripts/version.mjs: `changeset version` + append a `## Contributors` list to
  each bumped package's CHANGELOG.md (idempotent, pure rewrite unit-tested).
- .github/workflows/release-pr.yml: version-only changesets/action (no publish).
- .github/workflows/publish.yml: guarded OIDC publish (preserves
  `vp pm publish --provenance`); version now comes from `changeset version`.
- Reconcile packages/vinext version (0.0.5 -> 0.0.55) so the guard is coherent.

Unit tests for both scripts pass (35). The release orchestration (Version PR
creation, OIDC publish, gh contributor resolution) can only be validated in a
live CI run.

* refactor(release): collapse to a single changesets workflow

Let changesets/action own as much as possible. Delete the separate publish.yml
(guard job, manual bump, manual OIDC/tag/release/notify steps) and the
release-pr.yml split. One workflow now:

- create-changeset.mjs writes auto changesets to the working tree (its
  version-vs-tag guard yields nothing right after a Version PR merges).
- changesets/action maintains the Version PR and, when no changesets remain,
  publishes via `changeset publish` with OIDC trusted publishing + provenance,
  and creates the git tag + GitHub Release.

Removes the unused release:version script. Contributors list still handled by
scripts/version.mjs as the action's version command.

* refactor(release): convert release scripts to .mts, drop .d.mts files

Replace the .mjs + hand-written .d.mts declaration pairs with real TypeScript
(.mts) source. Node >=24 (the setup default) runs .mts directly via native type
stripping; .mts is unambiguously ESM so it needs no "type": "module" and emits
no MODULE_TYPELESS warning. The cross-import uses an explicit .mts specifier
(Node requires it), permitted in tsc via allowImportingTsExtensions (safe: the
project is noEmit).

- scripts/create-changeset.{mjs,d.mts} -> scripts/create-changeset.mts
- scripts/version.{mjs,d.mts}          -> scripts/version.mts
- tsconfig: allowImportingTsExtensions
- release.yml: run node scripts/*.mts

vp check (format + lint + types) clean; 35/35 unit tests pass.

* refactor(release): trim release scripts (~690→~470 source lines)

Cut comment bloat, remove dead code, tighten without dropping behavior:
- Condense verbose JSDoc/@param blocks to one-line purpose comments; keep the
  load-bearing "why" (correctness-rule header, insertContributors idempotency).
- Delete unused `newestChangelogVersion` (+ its tests) — exported/tested but
  never called.
- Tighten run()/insertContributors/latestTagVersion without behavior change.

Multi-package machinery and the bottom `## Contributors` list are retained per
requirements. vp check clean; 33 unit tests pass.

* feat(release): grouped conventional changelog + filter bot contributors

The default changesets changelog groups by bump level (### Minor Changes) and
renders our changeset summary as a nested bullet dump. Replace it with a real
conventional-commits changelog: version.mts now regroups each release's commits
into ### Features / ### Bug Fixes / ### Performance sections (scope bolded, type
prefix dropped) and rewrites the newest CHANGELOG section, then appends the
## Contributors list with [bot] accounts filtered out.

- create-changeset.mts: extract conventionalParts + collectReleaseCommits +
  releaseRangeStart and export the commit-walk so version.mts reuses it (no
  duplicate git logic).
- version.mts: groupedChangelogBody + rewriteReleaseSection (idempotent: only
  `## <digit>` is a section boundary); dedupeSortLogins drops `[bot]` logins.
- Tests updated for the new pure builders.

vp check clean; 33 unit tests pass. Verified end-to-end with a local dry run.

* refactor(release): simplify per PR review

- discoverPublishablePackages: scan only packages/* (publishable packages don't
  live elsewhere), drop the apps/examples/benchmarks scan.
- Delete isReleaseCommit: the "chore: version packages" release commit is a
  `chore`, already excluded by parseBumpFromSubject returning null. No need to
  hardcode release-message formats.

vp check clean; 31 unit tests pass.

* docs(release): clarify why the version-vs-tag guard exists

Per PR review: the guard was confusing. Document inline that it only suppresses
the post-merge/pre-publish window (version bumped, tag not yet created), which
would otherwise re-open a Version PR instead of publishing. Keeping the
never-on-main design per review decision.

* feat(release): sub-group changelog areas and humanize area names

Within each type section (### Features / ### Bug Fixes / …), areas (commit
scopes) with more than 3 items now get their own `#### <Area>` sub-group with
the scope prefix dropped; smaller areas and scopeless commits fall under
`#### Other`. When no area qualifies the list stays flat. Area names are
humanized (app-router → "App Router", css → "CSS", ppr → "PPR", i18n → "i18n")
in headings and in the bold prefixes.

Also: groupedChangelogBody now renders only known release types (GROUPS) — it
no longer leaks non-release types into an "Other Changes" bucket.

vp check clean; 32 unit tests pass. Verified with a local dry run.

* feat(release): rename changelog Other sub-group to Misc

* feat(release): sub-group areas with 3+ items (was >3)

* fix(release): address PR review (stale refs, contributor API, doc)

- Fix stale references to deleted files: .mjs → .mts and release-pr.yml →
  release.yml in .changeset/README.md and the release.yml header comment.
- resolveContributors: one paginated `gh api compare` call instead of one
  `gh api commits/<sha>` per commit (N round-trips → 1). This also removes
  version.mts's local git() helper, so no more `fatal:` probe noise from it.

No behavior change to the changelog/contributor output (verified via dry run:
same 4 contributors resolved). vp check clean; 32 unit tests pass.

The bot's "no git tags" bootstrap concern does not apply: tags v0.0.10..v0.0.55
exist and CI checks out with fetch-depth: 0, so the guard resolves 0.0.55.

* fix(release): run pinned changeset CLI; drop dead affectedPackages branch

Per second bonk review:
- Invoke the pinned, installed @changesets/cli via `vp exec changeset` instead
  of `vp dlx @changesets/cli` (which fetches a floating latest at run time) — in
  release.yml publish and version.mts. Removes the reproducibility gap vs the
  lockfile-pinned 2.31.0.
- affectedPackages: remove the unreachable `dir === "."` / empty-prefix branch
  now that discovery only returns `packages/*` keys.

vp check clean; 32 unit tests pass.

* fix(release): only emit valid GitHub logins in Contributors; doc fix

Per third bonk review:
- resolveContributors: `.author.login // empty` (was `// .commit.author.name`),
  so commits with an unlinked email contribute no entry instead of a raw git
  display name. dedupeSortLogins now keeps only `[a-zA-Z0-9-]+` shapes, which
  also subsumes the previous `[bot]` filter — no more broken `- @Full Name`
  mentions.
- .changeset/README.md: suggest `vp exec changeset` instead of the floating
  `vp dlx @changesets/cli` for manual changeset authoring.

vp check clean; 32 unit tests pass; dry run resolves the same valid handles.
2026-06-05 13:34:34 +00:00
Divanshu Chauhan (divkix) 8b4210f38f fix(app-router): dev-only favicon.ico short-circuit to avoid expensive 404 render (#1612) 2026-05-27 19:56:38 +01:00
James Anderson d03c5aa045 test(msw): introduce MSW infrastructure and migrate font-google-build (#1444)
* test(msw): introduce MSW infrastructure and migrate font-google-build

Replaces ad-hoc `globalThis.fetch` hijacking in tests with Mock Service
Worker. MSW intercepts both `globalThis.fetch` and `node:http`/`https`,
so it covers fetches issued by in-process Vite servers and fixture pages
without each test having to manage its own stub.

The setup file enables `onUnhandledRequest: 'error'` so any future test
that issues an unmocked external request fails loudly instead of
silently hitting the network. Loopback hostnames (localhost, 127.0.0.1,
::1) are bypassed because the integration project deliberately hits
in-process test servers via fetch.

What this PR contains:
- `tests/_msw/{handlers,server,setup}.ts` — shared infra
- `setupFiles` wired into both vitest projects (unit + integration)
- `msw` + `@mswjs/interceptors` added to the workspace catalog
- `msw` postinstall skipped via `allowBuilds` (it only copies a browser
  service worker; we exclusively use `msw/node`)
- `tests/font-google-build.test.ts` migrated as the proof of pattern

Temporary handlers for `httpbin.org/uuid` and
`example.com/not-cacheable` keep the existing fixture pages
(`revalidate-tag-test`, `dynamic-error-fetch`) working under the new
guard. These are placeholders flagged with `TODO(msw-migration)` — a
follow-up will replace those live URLs with test-local endpoints.

This is PR 1 of 3:
- PR 2 will migrate `tests/fetch-cache.test.ts`
- PR 3 will migrate `tests/font-google.test.ts` and the font block in
  `tests/app-router.test.ts`

* test(msw): restrict MSW to the unit project + use passthrough for loopback

Two fixes to the initial MSW wiring that surfaced when CI ran the full
suite:

1. The integration project is now excluded from MSW. Integration tests
   spin up in-process HTTP servers and fixture dev servers and exercise
   them via `fetch("http://127.0.0.1:<port>/...")`. Even with a loopback
   `passthrough()` handler installed, the @mswjs/interceptors layer
   interferes with that traffic in subtle ways:

   - `tests/prerender.test.ts > errors without writing .rsc when the
     middleware short-circuit fallback RSC request fails` timed out at
     30s — the 5xx response body never reached the caller.
   - `tests/ecosystem.test.ts > nuqs` hung in `beforeAll` waiting for
     its fixture process to come up, because the readiness fetch
     stalled inside the interceptor.

   Integration tests already talk to real local servers, not the
   internet, so the unhandled-request guard buys little there. The
   value of MSW for this repo is mocking external HTTP for unit tests
   of fetch wrappers (font-google, font-google-build, fetch-cache),
   which is preserved.

2. The `onUnhandledRequest` callback in `setup.ts` that tried to
   silently let loopback through wasn't sufficient — MSW's interceptor
   still ran for those requests and could stall them. Replaced with a
   default `http.all(LOOPBACK_URL_PATTERN, () => passthrough())`
   handler in `handlers.ts` and a plain `onUnhandledRequest: "error"`
   in `setup.ts`. Unit tests like the middleware-rewrite proxy in
   `tests/shims.test.ts` and the JSX-in-JS suite need this to keep
   their in-process HTTP servers reachable.

* test(msw): address BigBonk review notes

- Update PR description in the commit body to drop the stale mention of
  TODO(msw-migration) handlers — they were removed when the integration
  project was excluded from MSW; the fixture pages run there.
- Tighten the loopback IPv4 octet regex from `\d+` to `\d{1,3}` so it
  doesn't match absurdly long octet strings. `URL.hostname` normalises
  real addresses, so this is purely a readability fix.

* test(msw): address remaining BigBonk notes from the initial review

- Add `0.0.0.0` to the loopback passthrough pattern so test servers that
  bind to the unspecified address keep working without per-test mocks.
- Add a `TODO(msw-3)` comment in `server.ts` flagging the
  `SetupServerApi` deprecation in MSW 2.14.6 — the `FetchInterceptor`-only
  construction will need to migrate to `defineNetwork`'s interceptor
  config when we bump to MSW 3.

Both raised by BigBonk on the first review of this PR (the second review
focused on the stale description and the loose `\d+` octet bound, which
were addressed in the previous commit).

* test(msw): scope server.use() inside the try block in font-google-build

Minor structural fix from BigBonk's review: moves the `server.use()`
handler registration inside the `try` block so all setup and the
matching `fs.unlink` cleanup share the same try/finally scope.

No behaviour change — `server.use()` is synchronous and infallible, so
this is purely a structural tidy-up.

* test(msw): correct comment in font-google-build to match interceptor config

BigBonk caught that the comment described the default `setupServer()`
behaviour (which uses both `FetchInterceptor` and `ClientRequestInterceptor`),
but this project's `server.ts` deliberately constructs `SetupServerApi`
with only `FetchInterceptor`. Update the comment to reflect that — it
intercepts `globalThis.fetch`, not `node:http`/`node:https`.

No code change, comment only.
2026-05-22 13:03:08 +00:00
Nathan Nguyen 3adf2da4c4 chore(lint): enable stricter promise and error rules (#1148)
* chore(lint): enable stricter promise and error rules

The lint config did not enforce several promise, exhaustive switch, template expression, self-import, and error-construction checks on source files. That let void callback promise boundaries and empty Error fixtures slip through review.

Enable the rules for vinext source files, apply the useful test subset, and make the existing async callback boundaries explicit. Also remove the redundant unknown union and close response bodies in the affected Pages Router tests so server teardown remains deterministic.

* chore(lint): require exhaustive test switches

Test files already enforce strict async callback and error construction checks, but exhaustive switch checking was source-only.

Enable switch exhaustiveness for tests and make the Pages Router response decoder handle the undefined encoding case explicitly. Tighten the allowedDevOrigins suite cleanup so the temporary dev server closes deterministically.
2026-05-11 08:13:00 +01:00
James Anderson c1c81886d2 test: add coverage summaries for integration tests in ci (#1024) 2026-05-02 21:17:27 +00:00
Divanshu Chauhan (divkix) 2c852d13fc fix: replace relative shim imports with bare specifier vinext/shims/X (#1001) (#1006)
* fix: replace relative shim imports with bare specifier vinext/shims/X (#1001)

Internally, server modules imported their own 'use client' shims via
relative paths (e.g. from "../shims/slot.js"). @vitejs/plugin-rsc's
packageSources tracking only fires for bare specifier imports, so
relative shim imports took a broken fallback path generating absolute
filesystem paths that fail against the package's exports field.

Changes:
- 67 relative shim imports across 34 source files rewritten to
  "vinext/shims/<name>"
- Added .tsconfig paths mapping so TypeScript resolves shim types to
  source (avoids type mismatch between dist and source module graphs)
- Added vitest project-level resolve aliases for vinext/shims -> source
  (prevents separate module graph instances in unit tests)
- Added package.json exports for ./dist/shims/*.js as a safety net
- Added regression test (tests/shim-imports.test.ts) that asserts no
  relative shim imports exist in source

* fix: address review feedback for relative shim imports (#1006)

- Convert missed side-effect import in dev-server.ts to vinext/shims/router-state
- Extend regression test regex to catch bare side-effect import statements
- Remove redundant ./dist/shims/*.js exports from package.json

* replace test with oxlint plugin

---------

Co-authored-by: James <james@eli.cx>
2026-05-02 16:32:53 +00:00
James Anderson a83be594f9 chore: turn on lint rule to prefer type over interface (#716)
* chore: turn on lint rule to prefer `type` over `interface`

* fix vite-hmr.d.ts
2026-03-29 22:09:39 +01:00
James Anderson 2245429c38 chore: turn on no-explicit-any lint rule (#715)
* chore: turn on no-explicit-any lint rule

* Apply suggestions from code review

Co-authored-by: James Anderson <james@eli.cx>
2026-03-29 21:47:32 +01:00
James Anderson 1ff166eb92 chore: turn on more lint rules (#714)
* chore: turn on more lint rules

* fmt

* add todos
2026-03-29 20:40:57 +01:00
Steve Faulkner 47fec0c871 chore: upgrade vitest to v4 and enable agent reporter (#612)
* chore: bump pnpm to 10.32.1 and enable vitest agent reporter

* chore: upgrade GitHub Actions to Node 24 compatible versions

* perf: re-land unit/integration split and add CI sharding

- Restore unit/integration vitest project split (lost in vp migration)
- vitest 4 supports fileParallelism per-project (was global-only in v3),
  so unit tests now run in parallel (~11s) while integration stays serial
- Add 3-shard matrix for integration tests in CI, cutting wall time from
  ~4.5min to ~1.5min
- Move kv-cache-handler to integration to avoid flakiness under parallelism

* chore: remove unused deploy-preview slash command workflow

* ci: add sentinel job for stable branch protection rule
2026-03-20 16:20:42 -05:00
Stephen Zhou 25f6e0fc87 chore: enable typeAware and typeCheck, use vp check (#551)
* chore: enable typeAware and typeCheck, use vp check

* Build

* Try cache false

* Revert "Try cache false"

This reverts commit 5f76ed02f1.

* Update

* workaround for vp check

* Try no workaround

* Fix check

* Update to 0.1.12

* denyWarnings
2026-03-16 11:00:47 -05:00
Stephen Zhou 62910e80e9 chore: move to vp pack, merge vitest config (#550)
* chore: migrate to vp pack

* No copy

* Move test

* Update tsconfig
2026-03-15 11:58:42 +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