Commit Graph

55 Commits

Author SHA1 Message Date
James 347ba67d30 docs(cloudflare): describe generated cache entrypoint 2026-09-02 07:15:04 +01:00
James Anderson c8bea6bccf feat(cache): classify and warm static Route Handlers (#3113)
* feat(cache): classify and warm static Route Handlers

* fix(cache): identify Route Handler probe responses

* test(cache): assert Route Handler probe identity

* fix(cache): prove final Route Handler responses

* fix(cache): admit explicit Route Handler policies safely

* fix(cache): close Route Handler admission fallbacks

* fix(cache): honor explicit dynamic handler policies

* fix(cache): reject unsafe Route Handler probe responses

* fix(cache): preserve Route Handler CDN policy ownership
2026-09-01 21:25:26 +01:00
James Anderson 8956b70556 fix(cache): certify staged cache fills before promotion (#3094)
* fix(cache): certify staged cache fills before promotion

* test(cache): verify post-purge CDN admission

* test(cache): await post-purge CDN reuse

* fix(cloudflare): make cache fill certification opt-in

* fix(cloudflare): make cache certification authoritative

* style(cloudflare): format certification deploy
2026-09-01 21:25:25 +01:00
James Anderson c1a78a73d8 fix(build): support trailing-slash static exports (#3081)
* fix(build): support trailing-slash static exports

* fix(ci): initialize static export preview routes

* fix(build): emit trailing-slash 404 fallback

* docs(build): clarify static export page count
2026-08-26 23:01:09 +01:00
João Lucas 52c81f3c5f feat: experimental support for React Compiler via react: { compiler: true } (#3005)
* feat: support the React Compiler via react: { compiler: true }

@vitejs/plugin-react 6.1.0 added an experimental `compiler` option backed by
oxc-transform-react. vinext already forwards its `react` option to that plugin,
but enabling the compiler broke the build for apps with JSX in plain `.js`
files: `vite:react-compiler` and `vinext:jsx-in-js` both run at `enforce: "pre"`
and the React plugin was registered first, so the compiler parsed those files as
plain JS and failed with `Unexpected JSX expression`.

Register `vinext:jsx-in-js` before `@vitejs/plugin-react`, bump the plugin-react
catalog entry to ^6.1.0, add oxc-transform-react as a dev dependency for the
tests, and document the option.

* fix: fail fast when react.compiler is set on plugin-react 6.0

Addresses review feedback.

@vitejs/plugin-react 6.0 accepts an unknown `compiler` key and drops it, and it
still satisfies vinext's published peer range, so a project that upgrades vinext
without upgrading the plugin would silently get no React Compiler. Probe the
plugin list the factory returns and throw an actionable error when the compiler
plugin is missing.

Also drops the hand-authored changeset: AGENTS.md says changesets are generated
from Conventional Commits in CI.

* fix: keep the compiler version error out of the import failure wrapper

Addresses review feedback.

The trailing `.catch()` wrapped every rejection from the plugin-react promise
chain, including the compiler version check, so the actionable message and its
install command were demoted to a nested `cause` behind
`vinext: Failed to load @vitejs/plugin-react.`. Attach the wrapper to the
dynamic import itself so only genuine import failures are relabeled.

* fix: move only the compiler plugin instead of the whole React group

Addresses review feedback.

Reordering the entire @vitejs/plugin-react group put `vinext:jsx-in-js` ahead of
`vite:react-babel`, so on plugin-react 5.x, which is still in the supported peer
range and does expose a `babel` option, a user-configured Babel transform would
stop seeing JSX in plain `.js` files. Split `vite:react-compiler` out of the
group and register just that plugin after `vinext:jsx-in-js`. Every other plugin
keeps the position it had before this PR.

Also documents that components using `<style jsx>` are not memoized, since
`vinext:styled-jsx` lowers their JSX through the Next.js SWC transform first.
Running the compiler ahead of it is not an option: the styled-jsx plugin selects
modules by matching `<style` in the source, and lowered JSX no longer matches,
which would silently disable styled-jsx entirely.

* fix(ci): account for React Compiler test dependency

---------

Co-authored-by: James <james@eli.cx>
2026-08-20 14:07:54 +01:00
James Anderson 9a876322c5 fix(web): consolidate canonical host SEO signals (#2989) 2026-08-19 00:12:05 +01:00
Scott Buscemi 881ff398b5 fix(web): add search indexing metadata (#2988)
* fix(web): add search indexing metadata

* fix(web): refresh dashboard sitemap entries daily
2026-08-18 22:06:22 +01:00
Elias Shieh 7591622d93 docs: align README cache claims with the implementation (#2945) 2026-08-16 19:09:45 +01:00
Donny/강동윤 86620d5917 feat(client): expose host React to Module Federation remotes (#2828)
* fix(client): bridge React for Module Federation

* fix(ci): register generated React bootstrap entry

---------

Co-authored-by: James <james@eli.cx>
2026-08-06 14:43:11 +00:00
Jerry Zhao a0e5d1be86 chore(deps): upgrade vite-plus to 0.2.6 (#2694)
* chore(deps): upgrade vite-plus to 0.2.6

* fix(app-router): suppress benign AbortError from superseded navigations

A fast follow-up navigation aborts the in-flight navigation's RSC fetch
mid-stream. When the aborted stream still holds an un-consumed React Flight
chunk (e.g. streamed metadata the superseded route never rendered), React
reports the resulting AbortError globally as a window `error` event rather
than to a specific consumer, which trips the "no console errors" e2e
assertion (metadata-icons.spec.ts).

Install a page-lifetime window listener at bootstrap that preventDefault()s
these benign navigation AbortErrors, mirroring the existing redirect-error
bridge. Installed once (not in a component effect) so there is no listener
gap while the router tree re-renders mid-navigation.

The vite-plus 0.2.6 toolchain shifted navigation timing enough to expose
this latent race on CI.

* chore: adapt to vite-plus 0.2.6 toolchain

Follow-up to the vite-plus 0.2.6 bump; keeps CI green under the new
oxfmt/oxlint/rolldown toolchain. No published runtime change.

- Formatting: oxfmt 0.60.0 reformats README.md, apps/web/next.config.ts and
  tests/nextjs-compat/TRACKING.md (collapses empty-object-with-comment;
  unpads markdown tables). Applied `vp check --fix`.
- Lint: oxlint 1.75.0 now flags dynamic `import("node:path")` under the
  existing no-restricted-imports rule. Disabled inline in loadStaticPrerender
  with a reason -- the resolved path feeds a dynamic import(), so pathslash's
  forward-slash canonicalization buys nothing there.
- Test: rolldown code-splits the server build, so the font markers moved out
  of index.js into _next/static/* chunks. Scan the whole server output for
  the markers instead of index.js alone (verified they still exist).

* Revert "fix(app-router): suppress benign AbortError from superseded navigations"

Drop the runtime AbortError suppressor to keep this PR a pure toolchain bump.
The metadata-icons "rapid icon replacement" failure it addressed is a
pre-existing, timing-dependent race (not caused by the upgrade). Assessing
whether it triggers stably on CI / locally before deciding how and where to
fix it.

This reverts commit 84454d5fd of this branch.
2026-07-28 23:24:18 +01:00
Christoph Richter bd87926171 docs(cloudflare): drop duplicate rsc() from App Router config examples (#2652)
The Cloudflare App Router `vite.config.ts` examples in README.md and in the
migrate-to-vinext skill both register `@vitejs/plugin-rsc` explicitly. Copying
either verbatim fails the build immediately:

    [vinext] Duplicate @vitejs/plugin-rsc detected.
             vinext auto-registers @vitejs/plugin-rsc when app/ is detected.

vinext has auto-registered the plugin since the `app/` detection landed
(packages/vinext/src/index.ts), and every deployed App Router example
(app-router-cloudflare, app-router-playground, hackernews) omits `rsc()`.
The docs are the stale part.

Both examples now match the working examples, plus a note on the non-obvious
bit: `@vitejs/plugin-rsc` is an optional peer, so it must be installed but not
registered, and `rsc: false` is the escape hatch for owning the registration.

Co-authored-by: piffie <1213363+piffie@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 21:42:18 +01:00
James Anderson b859a03bbd feat(types): ship Next-compatible types without Next.js (#2612)
* feat(types): add vendored Next.js declarations

* feat(types): expose fallback types through vinext

* fix(types): support readonly Node environment declarations

* fix(ci): install vendored types before vinext tarballs

* test(types): avoid brittle packed consumer pins

* fix(ci): configure pnpm 11 local type overrides

* test(types): avoid registry metadata in packed install

* test(types): isolate packed declaration install

* feat(init): add standard script aliases for new apps

* fix(create-vinext-app): run typegen portably

* fix(typegen): report next env updates

* fix(create-vinext-app): use standard scripts only

* refactor(create-vinext-app): use exhaustive package manager return

* fix(create-vinext-app): fall back for unknown package managers
2026-07-14 13:27:20 +01:00
James Anderson fd1cc3d3dd docs: recommend vinext setup commands (#2536) 2026-07-04 17:21:22 +00:00
James Anderson c47c74a919 docs: update vinext project status (#2525) 2026-07-04 13:46:10 +00:00
James Anderson e4aa03ac5c feat(create): add create-vinext-app (#2483)
* feat(create): add create-vinext-app

* fix(ci): support create package checks

* fix(create): address creator review findings

* fix(create): document version flag

* fix(ci): keep create-next-app cloudflare coverage

* fix(create): update init option test for cdn cache

* fix(create): bundle creator helper deps

* fix(ci): allow local vinext dlx deploy test

* fix(ci): run create-vinext-app deploy test from local tarballs

* fix(create): keep creator package self-contained

* fix(ci): preview release create-vinext-app

* fix(init): install runtime deps as app dependencies

* fix(create-vinext-app): scope workers copy to cloudflare

* fix(create-vinext-app): use pnpm in template examples

* fix(init): start cloudflare apps with wrangler
2026-07-03 17:51:49 +01:00
James Anderson b5897d0595 feat(build): require Vite 8 (#2486)
* feat(build)!: require Vite 8

* fix(build): remove unused Vite version export

* test(link): harden prefetch timing assertions
2026-07-03 15:20:33 +01:00
James Anderson a3910e7aa9 feat(cloudflare): add unified worker entry (#2416)
* feat(cloudflare): add default pages router worker entry

* feat(cloudflare): add unified worker entry

* chore(web): use unified worker entry

* chore(examples): use built-in workers-cache entry

* chore(examples): keep hackernews on app router entry

* chore(cloudflare): rename unified entry to fetch handler

* chore(cloudflare): keep pages entry source helper test-local
2026-06-29 17:33:51 +00: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 25a3c26f7a feat(init): scaffold for cloudflare and node (#2279)
* feat(init): select deployment platform

* refactor(init): AST-update deployment config

* feat(init): configure Cloudflare cache and images

* fix(init): avoid backtracking in config updates

* fix(init): cover packaged Cloudflare setup

* ci: allow Cloudflare init smoke builds

* ci: fix packaged Cloudflare dependency install

* test(e2e): isolate browser fixture servers

* chore: allow Windows taskkill binary

* fix(init): configure Cloudflare image adapter

* fix(init): address platform setup review

* fix(init): address cache setup review

* fix(init): hide workers cache option

* fix(init): align generated config indentation

* fix(init): validate cloudflare setup before mutation

* fix(init): preserve additive config syntax

* fix(init): tidy interactive prompt spacing

* fix(images): rename Cloudflare optimizer builder

* refactor(images): remove draft name migration

* fix(init): install dependencies after setup

* fix(init): clarify setup recovery steps

* fix(init): ignore Wrangler state for Cloudflare

* fix lockfile
2026-06-26 17:09:19 +01:00
James Anderson b4843d75c1 feat(images): configure image optimization via vinext({ images }) adapter (#1873)
* feat(images): configure image optimization via vinext({ images }) adapter

Move server-side image optimization from a hand-wired custom worker entry to a
declarative `vinext({ images: { optimizer } })` option, mirroring the cache
adapter pattern. The default entries now handle `/_next/image` through a
registered optimizer, so no custom worker is required, and the same config
works across all targets — optimizing on Cloudflare, gracefully serving images
unoptimized on Node/dev where the binding is unavailable (like KV cache
degrading to in-memory).

- add an ImageOptimizer registry (set/getImageOptimizer +
  handleConfiguredImageOptimization) in server/image-optimization.ts
- generate `virtual:vinext-image-adapters` (registerConfiguredImageOptimizer)
  from the new `images` plugin option
- add @vinext/cloudflare/image/image-adapter: imageAdapter() builder + runtime
  factory reading the env.IMAGES binding
- handle /_next/image in the default app-router entry and the generated Pages
  worker via the registry; inline next.config `images` (allowed widths +
  security headers) into the RSC entry
- vinext deploy points App Router `main` at vinext/server/app-router-entry
  (no generated worker) and prints a hint to enable the optimizer

next.config `images` (remotePatterns, deviceSizes, dangerouslyAllowSVG, etc.)
continues to drive the standard Next.js options; the vite-config
`images.optimizer` only selects the runtime transform backend.

* fix(images): cover deploy image-hint helpers and preserve optimizer this-binding

The Check CI job failed because knip flagged viteConfigHasImageAdapter and
formatImageOptimizationHint as unused exports — they were only called inside
deploy.ts. Cover both with unit tests in tests/deploy.test.ts (mirroring the
existing viteConfigHasCacheAdapter / formatMissingCacheAdapterError suites),
which also closes the coverage gap for the new deploy hint path.

Also wrap the registered optimizer's transformImage in
handleConfiguredImageOptimization instead of detaching the method, so an
optimizer implemented as a class instance keeps its this binding.

* fix(images): honor configured deviceSizes/imageSizes on the App Router Node prod server

Review follow-up (ask-bonk):

- The App Router prod server (vinext start) validated /_next/image widths
  against the hardcoded Next.js defaults, rejecting valid optimizer URLs with
  400 when the app configures custom images.deviceSizes/imageSizes — while the
  Cloudflare worker entry and the Pages prod path already honored them. Read
  the __imageAllowedWidths constant inlined into the RSC entry (falling back to
  the defaults for older builds), matching how __assetPrefix/__basePath are read.
- Lock in the this-binding behavior of handleConfiguredImageOptimization with a
  class-instance optimizer test.

* fix(images): pass an explicit empty allowed-widths config through on vinext start

Review follow-up (ask-bonk, awareness note): the old-build fallback guard
conflated a missing __imageAllowedWidths export with an explicit empty
deviceSizes/imageSizes config, mapping the latter to the Next.js defaults on
the Node App Router path while the Cloudflare worker passes the empty array
straight through. Only fall back to the defaults when the export is absent.

* refactor(images): read App Router image config from the RSC entry, retire the JSON sidecar

Review follow-up (ask-bonk): the App Router had two parallel build-time sources
for next.config images security/header settings — the __imageConfig constant
inlined into the RSC entry (read by the Cloudflare worker entry) and the
image-config.json sidecar written by the vinext:image-config plugin (read by
vinext start). Unify on the RSC entry export: prod-server now reads
rscModule.__imageConfig, keeping image-config.json only as a read-side fallback
for dist outputs built by older vinext versions, and the sidecar writer plugin
is removed.

* fix(deploy): keep wrangler main on a user-authored worker entry for App Router

Review follow-up (ask-bonk): an App Router app with a custom worker/index.ts
but no wrangler.jsonc would have had its custom worker silently dropped —
generateWranglerConfig unconditionally pointed main at the default
vinext/server/app-router-entry. Respect hasWorkerEntry so a user-authored
worker keeps winning for both routers, with a regression test.

* fix(images): expose Cloudflare optimizer under images path

* fix(deploy): install Cloudflare image adapter package

* fix(examples): declare Cloudflare image adapter package

* test(images): update App Router image config codegen assertions

* fix(examples): configure image optimizer adapters
2026-06-23 20:20:36 +01:00
James Anderson e46e2dbb05 feat(web): add performance benchmark dashboard (#2131)
* feat(web): add performance benchmark dashboard

* fix(web): clean up benchmark dashboard exports

* fix(web): support benchmark runs without baselines

* fix(perf): support targeted main baseline runs

* fix(web): revalidate benchmark commit baselines

* feat(perf): improve vinext trace profiles

* fix(web): neutralize insignificant benchmark diffs

* fix(perf): replace existing benchmark runs

* fix(perf): expose vinext trace frames

* feat(perf): retain complete vinext trace detail

* feat(perf): filter flame graph categories

* fix(web): compact flame graph rows

* fix(perf): replace runs by commit identity

* fix(benchmarks): preserve complete performance traces

* fix(benchmarks): use commit time for performance runs

* fix(benchmarks): store performance profiles in R2

* fix(benchmarks): stream performance profiles to R2

* fix(benchmarks): decode raw performance profiles

* fix(benchmarks): show inclusive filtered profile time

* fix(benchmarks): resize filtered flame graphs

* fix(benchmarks): render narrow flame frames

* perf(benchmarks): bound flame graph detail

* ci(perf): split benchmark publication privileges

* fix(perf): harden benchmark publication

* fix(perf): initialize runner paths in job

* fix(perf): install through vite plus

* fix(perf): disable pnpm hooks during install

* fix(perf): select trusted harness ref

* fix(perf): resolve vite plus for isolated user

* fix(perf): grant isolated tool access

* fix(perf): share benchmark cache cleanup

* fix(perf): harden trace publication

* chore(perf): format migration snapshots

* feat(web): restore benchmarks navigation
2026-06-18 11:41:50 +01:00
James Anderson 206b016303 fix(cloudflare): update cache adapter jsdoc and examples (#1898)
* docs: remove cdn adapter from examples

* update adapter docs
2026-06-10 19:16:11 +00: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 f0f6aa72e0 feat(cache): configure cache adapters from vite plugin config (#1733)
* feat(cache): configure cache adapters from vite plugin config

Add a `cache` option to the vinext() plugin so CDN and data cache
adapters can be declared in vite.config instead of calling
setDataCacheHandler() / setCdnCacheAdapter() from a worker entry:

  vinext({
    cache: {
      cdn:  { adapter: require.resolve('vinext/cloudflare/cache/cdn-adapter') },
      data: { adapter: require.resolve('vinext/cloudflare/cache/kv-data-adapter') },
    },
  })

Each slot points at an adapter module whose default export is a factory
(DataCacheAdapterFactory / CdnCacheAdapterFactory). The plugin generates
a virtual:vinext-cache-adapters module that the App Router worker entry
calls per request (self-guarded, once per isolate), passing the host env
so binding-backed adapters (e.g. KV) can read their namespace.

Ships ready-made Cloudflare adapter entry points:
  - vinext/cloudflare/cache/kv-data-adapter  (KVCacheHandler)
  - vinext/cloudflare/cache/cdn-adapter      (CloudflareCdnCacheAdapter)

* feat(cache): add typed adapter builders (kvDataAdapter/cdnAdapter)

Instead of `{ adapter: require.resolve(...) }`, each adapter module now
also exports a config-time builder from the same path:

  import { cdnAdapter } from 'vinext/cloudflare/cache/cdn-adapter';
  import { kvDataAdapter } from 'vinext/cloudflare/cache/kv-data-adapter';

  vinext({ cache: { cdn: cdnAdapter(), data: kvDataAdapter({ binding: 'MY_KV' }) } })

A builder returns a plain, serializable { adapter, options } descriptor —
it never touches the Workers runtime, so nothing throws at config / build
/ dev time when bindings aren't available. Descriptor `options` (e.g. the
KV binding name) are inlined into the generated registration module and
forwarded to the factory's { env, options } context, where the binding is
resolved lazily on the first request.

- shims/cache-adapter: descriptors + options-aware factory/context types
- kv-data-adapter: kvDataAdapter() builder + configurable binding/appPrefix/ttl
- cdn-adapter: cdnAdapter() builder
- raw { adapter, options } path form still supported

* test(cache): verify absolute (require.resolve) local adapter path bundles

Real Cloudflare build pointing cache.data at a local adapter file by
absolute path (what require.resolve('./adapter') yields). Proves the
generated registration module resolves the absolute import, bundles the
local adapter into the worker, and does not need any Workers context at
build time.

* refactor(cache): builder require.resolve + register across all routers/runtimes

Addresses review feedback:

* Move adapters into their own runtime modules instead of re-exporting.
  Each adapter is now a builder module (kv-data-adapter.ts / cdn-adapter.ts)
  plus a sibling *.runtime.ts holding the default-export factory. Type
  definitions have a single home in shims/cache-adapter.ts (dropped the
  re-export shim; index.ts imports the config type from there).

* The exposed builder utility resolves the relative runtime path internally
  via import.meta.resolve (the ESM require.resolve), so the descriptor carries
  an absolute path to the runtime factory rather than a bare specifier — the
  example is just kvDataAdapter({ binding }), no require.resolve at the call site.

* Register configured cache handlers EVERYWHERE, not just the App Router worker:
  - App Router: the generated RSC entry passes registerConfiguredCacheAdapters
    into createAppRscHandler, which calls it per request — covering Workers,
    the Node server, and dev through the one shared handler.
  - Pages Router: the generated server entry registers in renderPage and
    handleApiRoute (Node/dev), and the generated worker registers with env
    (Workers, for KV bindings).
  Registration self-guards (first call with real env wins) and is now resilient:
  a factory that throws on an incompatible runtime is logged and skipped, so the
  default handler stays in place instead of failing every request.

Tests: generator-level assertions that every router/runtime entry wires
registration, plus the existing builder/codegen/factory and full-build coverage.
vp check clean; app-router (339) and pages-router (272) suites pass.

* refactor(cache): keep all Cloudflare adapter code under cloudflare/

The adapter factory contract lived in shims/cache-adapter.ts (outside
cloudflare/), and the Cloudflare adapters reached out to it. Move the
contract into cloudflare/cache/adapter.ts so every Cloudflare-specific
cache adapter file is self-contained under cloudflare/ — importing only
cloudflare-local modules and the core CacheHandler/CdnCacheAdapter
interfaces it implements.

The plugin's config schema (CacheAdapterDescriptor / VinextCacheConfig)
is genuinely framework-level (it's the vinext() `cache` option), so it
moves into the codegen module the plugin already owns; index.ts imports
it from there. Builders return a structural { adapter, options } so they
don't import the descriptor type either. Deletes shims/cache-adapter.ts.

* refactor(cache): merge KV/CDN classes into the runtime adapter files

All Cloudflare cache code now lives in one directory, cloudflare/cache/,
and each runtime file holds both the implementation class and its
config-driven factory (no separate class module to reach for):

  - kv-cache-handler.ts        -> cache/kv-data-adapter.runtime.ts
    (KVCacheHandler + ENTRY_PREFIX + createKvDataCacheAdapter default export)
  - cloudflare-cdn-cache.ts    -> cache/cdn-adapter.runtime.ts
    (CloudflareCdnCacheAdapter + createCloudflareCdnCacheAdapter default export)

Updated importers: cloudflare/index.ts re-exports the classes from the
runtime files, tpr.ts pulls ENTRY_PREFIX from there, shims/cdn-cache.ts
imports the edge adapter from there, and the tests follow the moved paths.
git mv preserves history.

vp check clean; cache/kv/cdn/app-route/tpr/shims suites pass (1300+ tests).

* chore(cache): trim low-value comments added in this branch

Remove narrating/redundant comments that just restated the code; keep
the non-obvious why (registration ordering/resilience, import.meta.resolve
rationale, edge cache-control semantics). No code changes.

* review: address PR #1733 feedback

- Make registerCacheAdapters a required field on the RSC handler options
  (the generated entry already passes it; test factory updated).
- Remove the separate cloudflare/cache/adapter.ts contract file; inline the
  factory param types directly into the two runtime adapters.
- Drop the CloudflareCdnCacheAdapter re-export from cloudflare/index.ts.
- Fold the virtual:vinext-cache-adapters declaration into global.d.ts and
  delete the standalone .d.ts.
- Remove the ./cloudflare/cache/* package.json export for now; README uses a
  local-adapter require.resolve example with a note that the built-in adapter
  export paths are pending.
- Rename the config-driven KV default binding to VINEXT_KV_CACHE (imperative
  deploy/tpr path keeps VINEXT_CACHE — flagged on the thread).

* refactor(cache): align KV binding name to VINEXT_KV_CACHE everywhere

Rename the KV cache binding from VINEXT_CACHE to VINEXT_KV_CACHE across the
whole codebase so the config-driven adapter, the imperative deploy-generated
worker, TPR's wrangler detection, and the apps/web example all agree. The
unrelated X-Vinext-Cache response-header constant (VINEXT_CACHE_HEADER) is
untouched.

* tidy

* .

* .

* .

* .

* .

* Move apps/web cache to plugin config

Co-authored-by: james-elicx <james-elicx@users.noreply.github.com>

---------

Co-authored-by: ask-bonk[bot] <ask-bonk[bot]@users.noreply.github.com>
Co-authored-by: james-elicx <james-elicx@users.noreply.github.com>
2026-06-04 23:30:33 +00:00
James Anderson 49f3905b7b fix(config): keep packages externalized when loading TypeScript next.config (#1710)
PR #1701 added `resolve.noExternal: true` while loading a TypeScript
next.config so a baseUrl-local file could shadow an installed package of the
same name. That de-externalizes every installed package and forces it through
Vite's module runner, which breaks CJS config plugins that call `require` /
`require.resolve` at runtime — e.g. `@next/mdx`, which failed the
app-router-playground production build with "require is not defined".

This was not caught pre-merge because deploy-examples is skipped for fork PRs.

Drop the blanket `noExternal: true`. Installed packages stay externalized (so
CJS config plugins keep working), and the native Vite 8 `resolve.tsconfigPaths`
resolver still resolves baseUrl-local bare imports that have no installed
package of the same name (the real Next.js tsconfig-extends fixture). The only
behavior we give up is shadowing an installed package with a baseUrl-local file
of the same name, which no Next.js fixture exercises.

- Update the shadowing test to assert the installed package wins.
- Add a regression test for an @next/mdx-style CJS plugin that calls
  `require.resolve` at runtime (red before this change).
- Document the package-precedence behavior in the README limitations entry.
2026-06-01 09:58:09 +00:00
Nathan Nguyen 5991406324 fix(config): resolve next.config baseUrl imports from tsconfig (#1701)
* fix(config): resolve next.config baseUrl imports from tsconfig

next.config.ts could resolve tsconfig paths aliases but not bare imports covered by compilerOptions.baseUrl. This broke configs that inherit baseUrl through tsconfig extends and import local modules such as "bar" from next.config.ts.

Carry the effective baseUrl out of the tsconfig reader and add a narrow Vite runner resolver for bare specifiers so local files resolve before normal package fallback. Add a regression ported from the Next.js tsconfig-extends deploy fixture.

* fix(config): preserve baseUrl priority over packages

next.config.ts imports that matched both a local tsconfig baseUrl target and an installed package resolved to the package first. That violated the TypeScript and Next lookup order where baseUrl local resolution wins before package fallback.

Vite's module runner externalized installed bare packages before resolveId hooks ran, so the baseUrl resolver only handled missing packages. Keep config imports inside the resolver pipeline, run the baseUrl resolver before package resolution, and scope it to project-owned importers so package dependencies are not rewritten through the app baseUrl.

Adds focused regression coverage for local-over-package shadowing, package fallback, node_modules importer isolation, and existing CJS/ESM package imports from next.config.ts.

* fix(config): keep baseUrl lookup scoped to TypeScript configs

next.config.mjs could inherit TypeScript baseUrl package shadowing because the config runner noExternal override and baseUrl resolver were enabled for every config file type. That leaked Next's TypeScript config transpilation semantics into non-TypeScript config loading.

Gate the baseUrl resolver and noExternal runner override behind TypeScript config extensions while keeping existing alias loading behavior unchanged. Add a regression for mjs package shadowing and update the tsconfig helper comment to name baseUrl explicitly.

* refactor(config): resolve next.config baseUrl via native Vite 8 tsconfigPaths

Replace the hand-rolled tsconfigBaseUrlResolverPlugin with Vite 8's native
resolve.tsconfigPaths (oxc-resolver tsconfig: 'auto'), which already mirrors
Next.js's SWC paths + baseUrl handling: follows extends, resolves baseUrl-local
bare imports before package fallback, and scopes the app baseUrl to
project-owned importers via per-importer tsconfig discovery.

Gate it behind the existing Vite-major check (matching index.ts). Vite 7 has no
native equivalent, so baseUrl bare imports in next.config.ts are a documented
Vite 7/8 capability gap; paths aliases still work on both via resolve.alias.

Extract getViteMajorVersion into utils/vite-version.ts so next-config.ts and
index.ts share one detection path without a circular import.

---------

Co-authored-by: James <james@eli.cx>
2026-06-01 10:34:00 +01:00
James Anderson 61cf2e78f5 fix(image): emit /_next/image URLs (#1562)
* fix(image): emit /_next/image URLs to match Next.js

Closes #1513

The default image loader and optimization endpoint switched from the
vinext-specific /_vinext/image path to Next.js's canonical /_next/image.
This unblocks the deploy suite tests that import Next.js's expected
URL shape (/_next/image?url=...&w=...&q=...).

* refactor(image): use IMAGE_OPTIMIZATION_PATH constant at remaining call sites

Replace hardcoded "/_next/image" strings in index.ts, app-rsc-handler.ts,
and the generated worker entry templates in deploy.ts with the
IMAGE_OPTIMIZATION_PATH constant from server/image-optimization, matching
the pattern already used in prod-server.ts. Prevents future drift if the
path ever changes again.

* feat(image): accept both /_next/image and /_vinext/image at the optimizer

Add a VINEXT_IMAGE_OPTIMIZATION_PATH constant and an
isImageOptimizationPath() helper, then route through every match site
(prod-server, dev server passthrough, app RSC handler, generated worker
templates, and shipped example workers). Apps that wire image URLs to
either prefix now hit the same handler; new URLs are still emitted via
IMAGE_OPTIMIZATION_PATH.
2026-05-26 10:28:22 +01:00
James Anderson c74faed38c chore(deps): bump react version (#1110) 2026-05-06 16:46:47 +00:00
James Anderson 74a05788dd chore: tweak readme (#1042) 2026-05-03 18:21:55 +00:00
Nathan Nguyen 3be41803bd remove a stale readme line regarding layout segment (#846) 2026-04-14 19:00:14 +01:00
Steve Faulkner f910e74045 fix(app-router): reject cyclic Flight payloads in server actions (#798)
* fix(app-router): reject cyclic Flight payloads in server actions

* test(app-router): update generated entry snapshots
2026-04-08 22:11:15 -05:00
Sankalp Mukim f6aedd3615 Add standalone self-host output and align init production scripts (#178)
* feat: add standalone self-host output for vinext build

Generate dist/standalone from output: 'standalone' with a runnable server entry and runtime deps, and align init scripts with vinext build/start for production self-host workflows.

* fmt

* Address bonk review: fix duplicate PHASE constant, complete lazy-chunks extraction, remove dead imports, add process.exit to standalone path

* Address bonk nits: document regex patterns, move PHASE re-exports to bottom, add assets field to BuildManifestChunk

* refactor(standalone): use Vite bundle graph for server externals instead of regex scan

Replace the fragile post-build regex scan of emitted JS files and the
package.json#dependencies over-seeding with a new
vinext:server-externals-manifest Vite plugin. The plugin collects
chunk.imports + chunk.dynamicImports from the SSR/RSC writeBundle hook
and writes dist/server/vinext-externals.json — the authoritative,
compiler-derived list of packages left external in the server bundle.

emitStandaloneOutput now reads this manifest as its sole seed for the
BFS node_modules copy, so standalone output only includes packages the
server actually imports at runtime. Removed collectServerExternalPackages,
walkFiles, the four regex constants, and the package.json deps seeding.

* fix(standalone): copy vinext runtime deps, add node_modules filter to cpSync, remove unused pendingWrites

* fix(standalone): address round-6 bonk nits — explicit root, manifest comment, pre-flight check, bare-specifier comment, export-from re-export

* fix(standalone): address round 7 bonk review comments

- harden outDir resolution in server-externals-manifest: replace fragile
  walk-up heuristic with direct basename check (ssr -> dirname, else dir),
  avoiding misfires when a user's project path contains a 'server' segment
- remove redundant 'chunk' alias in server-externals-manifest writeBundle;
  use 'item' directly after the type guard narrows it to OutputChunk
- add node_modules filter to vinext dist/ cpSync in standalone.ts, matching
  the same defensive filter used for app package copies
- rewrite standalone server.js as pure ESM using import.meta.dirname
  (Node >= 21.2, vinext requires >= 22); drop CJS require/__dirname and
  switch standalone package.json to 'type':'module'
- add explanatory comment for DEFAULT_PHASE constant in next-config.ts
- add comment in cli.ts noting pre-flight path and resolveVinextPackageRoot
  must stay in sync

* fix(standalone): address round 8 bonk review comments

- fix test import: use 'vite-plus/test' instead of 'vitest' in
  standalone-build.test.ts, matching the convention used by every other
  test file in the repo (documented in AGENTS.md)
- extract resolveVinextPackageRoot into utils/vinext-root.ts so cli.ts
  and standalone.ts share a single source of truth; removes the coupling
  comment added in round 7 and the duplicated path traversal logic
- add node_modules filter to dist/client and dist/server cpSync calls for
  defensive consistency with the vinext dist/ and package copies

* interface -> type

* fix(standalone): address round 9 bonk review comments

- add node_modules filter to public/ cpSync for defensive consistency
  with all other cpSync calls in emitStandaloneOutput
- warn on malformed vinext-externals.json instead of silently returning
  an empty list; includes the error string to aid debugging
- add doc comment on copyPackageAndRuntimeDeps clarifying that the return
  value is the full accumulated set (including pre-existing entries), not
  only packages copied by the current call
- document HOST (not HOSTNAME) env var in README standalone section, with
  a note explaining the difference from Next.js standalone behaviour

* fix(standalone): address round 10 bonk review comments

- add comment in writeStandaloneServerEntry explaining why the import of
  vinext/server/prod-server is intentional as a static import: it is a
  documented exports-map entry, always present in the copied dist tree,
  and a static import gives a clearer ERR_MODULE_NOT_FOUND at startup
- generalise outDir sub-directory detection in server-externals-manifest:
  replace basename === 'ssr' with basename !== 'server' so any future
  sub-directory environment (e.g. 'edge') is handled without code changes
- add JSDoc on runtimeDeps clarifying that it returns both dependencies
  and optionalDependencies

* fix(next-config): drop PHASE_DEVELOPMENT_SERVER from re-export

No external consumer imports it from next-config.ts — only
PHASE_PRODUCTION_BUILD is used externally (by cli.ts). Removing it
minimises the public surface area of the module.

---------

Co-authored-by: GPU VM <copilot-gpu-vm@example.com>
Co-authored-by: James <james@eli.cx>
2026-03-30 20:42:40 +01:00
Steve Faulkner a62e5c4901 chore(benchmarks): remove Vite 7 runner, update Next.js to 16.2.1 (#699)
* chore(benchmarks): remove Vite 7 runner, update Next.js to 16.2.1

The vinext-rolldown benchmark runner was identical to the vinext runner
(both resolved to the same Vite version via catalog), producing
duplicate results on the dashboard. Remove it entirely and simplify
to a two-runner comparison: Next.js (Turbopack) vs vinext (Vite 8).

Also bump the Next.js benchmark dependency from 16.1.7 to 16.2.1 to
pick up recent performance improvements.

* fix: remove vinext-rolldown from pnpm-workspace.yaml, fix migration comment
2026-03-27 19:23:50 -05:00
Stephen Zhou e683e0d84d feat: support inline next config (#649)
* feat: support inline next config

* Apply suggestion from @james-elicx

Co-authored-by: James Anderson <james@eli.cx>

* tweaks

---------

Co-authored-by: James Anderson <james@eli.cx>
2026-03-23 08:25:08 +00:00
Gentrit Biba fa03a06bea feat: optimize barrel imports for RSC-incompatible packages (#138)
* feat: add vinext:optimize-imports plugin for barrel import rewriting

Barrel imports like `import { Slot } from "radix-ui"` cause RSC crashes
because Vite eagerly evaluates all re-exported sub-packages, some of which
call React.createContext() — unavailable in the react-server condition.

This plugin rewrites barrel imports into direct sub-module imports on the
server, matching Next.js's optimizePackageImports behavior:

- Parses barrel entry files to build export maps (namespace, named,
  default re-exports, and import-then-export patterns)
- resolveId hook handles pnpm strict hoisting via Vite's own resolver
- Only runs on server environments; client uses Vite's dep optimizer
- Lazy-initialized Set<string> for O(1) package lookups
- Respects experimental.optimizePackageImports from next.config
- Includes Next.js default package list plus radix-ui

Closes cloudflare/vinext#100
Closes cloudflare/vinext#137

* fix: add missing packages to DEFAULT_OPTIMIZE_PACKAGES

Add the full effect ecosystem (17 packages) and all react-icons/*
sub-packages (32 packages) to match Next.js's built-in defaults.

* fix: address bonk review — entryPathCache, buildStart init, Windows path fix, trailing semicolons, fixture-based transform tests

* fix: correct bySource grouping for mixed namespace+named imports, clear barrelCaches on rebuild

- Key bySource map on `${source}::${isNamespace}` instead of just source
  so that a namespace re-export and a named re-export from the same sub-module
  produce two separate import statements rather than one corrupted one.
  Caught by the new 'produces separate statements for namespace and named
  imports from the same source' test.
- Clear barrelCaches.exportMapCache and barrelCaches.subpkgOrigin in buildStart
  alongside entryPathCache so stale barrel analysis doesn't survive rebuilds.
- Add regression test for the mixed namespace+named grouping bug.

* fix: resolve relative barrel re-export paths to absolute, emit default imports for default re-exports

Barrels like lodash-es and lucide-react use relative re-export paths
(e.g. `export { chunk } from './chunk.js'`). Writing those verbatim caused
Vite to resolve './chunk.js' against the importing user file rather than
the barrel package directory.

Fix: resolve any relative source path against the barrel entry's directory
before emitting the replacement import statement, producing an absolute
filesystem path that Vite resolves correctly regardless of where the
importing file lives.

Also fix: `export { default as X }` re-exports now produce `import X from`
(a default import) instead of `import { default as X }` (invalid syntax
that only works via ASI / bundler leniency).

Tests updated to assert absolute paths and added a dedicated test for
default re-export handling.

* refactor: extract barrel optimization to plugins/optimize-imports.ts, add Windows path fix and react-server TODO

- Move all barrel types, helpers, DEFAULT_OPTIMIZE_PACKAGES, and the
  vinext:optimize-imports IIFE from index.ts into a new
  packages/vinext/src/plugins/optimize-imports.ts file with a factory
  function createOptimizeImportsPlugin(getNextConfig, getRoot)
- Forward-slash normalize resolved absolute paths so emitted import
  statements use '/' separators on Windows (path.sep join)
- Add TODO comment in resolveExportsValue noting that the 'react-server'
  condition should be added in a future pass
- Add test for ImportDefaultSpecifier: import MyFoo from 'pkg' rewrites
  to import MyFoo from '<abs>/sub' (22 tests total, all passing)

* fix(optimize-imports): address sixth-pass review comments

- Fix astName() to throw on unexpected nodes instead of String(value)
- Normalize all resolvePackageEntry return values to forward slashes (Windows fix)
- Fix originalName falsy guard: && → !== undefined
- Pre-build quotedPackages in buildStart to avoid per-file template literal allocations
- Add first-wins comment on subpkgOrigin registration
- Add clarifying comment to lucide-react test fixture

* fix(optimize-imports): remove EnvironmentPluginContext cast, expand subpkgOrigin comment

Vite augments rolldown's MinimalPluginContext with `environment: Environment`
via declaration merging, so both resolveId and transform have `this.environment`
typed without any cast. Drop the EnvironmentPluginContext interface and the
`as unknown as EnvironmentPluginContext` cast in the transform handler, using
direct `this.environment` access consistently with resolveId.

Also expand the subpkgOrigin first-wins comment to make explicit why first-wins
is safe: the sub-package specifier resolves to the same path regardless of which
barrel's importer context is used.

* fix(optimize-imports): address seventh-pass review nits

- Fix test imports: vitest → vite-plus/test, Plugin from vite → vite-plus,
  following the convention used by 82 of 85 test files in this repo
- Add TODO comment for extensionless absolute paths produced when barrel
  sources lack file extensions (Vite resolves these correctly in practice)
- Expand subpkgOrigin first-wins comment to explicitly note the nested
  node_modules edge case and why it is out of scope for the default list

* fix(optimize-imports): address james-elicx review comments

- Support wildcard re-exports (export * from './sub') by recursively
  parsing sub-modules and merging their exports into the barrel map
- Add circular-reference guard to prevent infinite loops in recursive
  export map building
- Refactor buildBarrelExportMap into buildExportMapFromFile helper
  with shared recursive logic
- Use switch statements for spec.type handling instead of if-else chains
- Remove non-null assertions and type assertions; use optional chaining
  and typeof guards throughout
- Extract resolvePackageInfo helper to separate concerns in
  resolvePackageEntry
- Tests now import directly from optimize-imports.ts instead of via
  the full vinext() plugin array
- Remove _buildBarrelExportMap re-export from index.ts (no longer needed
  since the plugin has its own file)
- Fix type assertion for optimizePackageImports in next-config.ts using
  a proper type-guard filter
- Apply README nit: 'is always optimized' → 'are always optimized'
- Add tests for wildcard export resolution, no-overwrite semantics,
  and circular re-export safety

* fix(optimize-imports): address ask-bonk review comments

- Resolve relative sources to absolute paths inside buildExportMapFromFile
  so nested wildcard re-exports (e.g. antd-style components/) resolve against
  the correct sub-module directory instead of the barrel root
- Add react-server condition to resolveExportsValue (before node/import) for
  RSC-compatible entry points in packages like react and react-dom
- Add NOTE comment in resolvePackageEntry documenting that only exports["."]
  is checked, not subpath exports
- Eliminate double readFile call in buildBarrelExportMap by passing the
  already-read content to buildExportMapFromFile via new initialContent param
- Update subpkgOrigin guard to check for absolute paths (not startsWith ".")
  since relative sources are now stored as absolute paths in the export map
- Add test for nested subdirectory wildcard path resolution correctness
- Update existing unit tests to expect absolute paths in export map entries

* fix(optimize-imports): only prefer react-server condition in RSC environment

SSR renders with the full React runtime and must not resolve react-server
export condition entries. Thread preferReactServer (env.name === 'rsc')
through resolveExportsValue and resolvePackageEntry, and key entryPathCache
by environment prefix to keep RSC and SSR barrel entries separate.

* fix(optimize-imports): address review nits — TODO comment, type annotation, test names, wildcard dir test

- Add TODO comment on allResolved bail-out suggesting debug logging
- Remove redundant ': string[]' annotation on optimizePackageImports (inferred)
- Rename namespace re-export test to use generic X/barrel names instead of Slot/lucide-react
- Update syntax-error test to match new empty-map-on-parse-error behavior (not null)
- Add test for directory-style 'export * from "./components"' where components/index.js exists

* fix(optimize-imports): address review nits — wildcard extensions, double-read, readFile hoisting, subpkg registration guard

- Add /index.mjs, /index.tsx, and .tsx to wildcard candidate list for ESM-first and TypeScript-first packages
- Pass candidateContent to buildExportMapFromFile in wildcard loop to avoid reading each sub-module file twice
- Add comment clarifying the no-op resolveEntry callback pattern
- Hoist readFileSafe closure outside the import-declaration for-loop (one allocation per transform, not per barrel import)
- Gate subpkgOrigin registration loop with registeredBarrels Set so 50 files importing the same barrel don't each iterate the full export map

* fix(optimize-imports): remove redundant double-cache, extract PluginCtx type alias

- Remove the redundant exportMapCache.set() in buildBarrelExportMap — buildExportMapFromFile
  already stores the result under the same key; the extra set was a no-op but misleading
- Extract PluginCtx type alias for the environment cast so all three hook sites share one
  definition and the verbose inline cast doesn't repeat across resolveId and transform

* fix(optimize-imports): hoist readFileSafe to module level, add namespace import test

- Move readFileSafe from handler scope to module level since it captures
  nothing from the per-file closure; avoids a per-call function allocation
- Add test verifying import * as Pkg from 'barrel' is left unchanged
  (ImportNamespaceSpecifier sets allResolved = false — can't optimize a
  full namespace import to a sub-module)

* fix(optimize-imports): make barrel resolution async, add debug logging, update tests

* fix(optimize-imports): address review feedback — extensions, env-keyed subpkgOrigin, debug gate, graceful astName

* fix: scope registeredBarrels key to env to prevent RSC registration blocking SSR

When RSC and SSR resolve the same barrel entry path (common — most packages
have no react-server export condition), the bare barrelEntry key in
registeredBarrels caused RSC's registration to suppress SSR's inner loop
entirely. SSR's subpkgOrigin map was left empty, making resolveId depend
solely on the cross-env fallback — fragile coupling that could silently break.

Change the registeredBarrels key from barrelEntry to `${envKey}:barrelEntry`
so each environment runs the inner loop and populates its own subpkgOrigin
map independently. Matches the existing entryPathCache key pattern.

Add a regression test covering the shared-barrel-entry scenario.

* fix: handle inline export declarations in barrel sub-modules (date-fns)

date-fns barrels use `export * from "./formatDistanceToNow.js"` where the
sub-module directly declares `export function formatDistanceToNow(...) {}`.
buildExportMapFromFile only handled ExportNamedDeclaration with a source
(re-exports) or specifiers (local re-bindings) — it silently skipped inline
declarations (export function/class/const/let/var), leaving the name absent
from the export map and causing the plugin to log "skipping: could not
resolve specifier" and leave the barrel import unchanged.

Handle the declaration branch: FunctionDeclaration and ClassDeclaration
contribute their id.name; VariableDeclaration iterates all declarators.
The source for inline declaration entries is the sub-module file itself.

Also extend AstBodyNode.declaration typing from `unknown` to the minimal
shape covering all three declaration kinds.

Add three unit tests covering export function, export const (multiple
declarators), and export class; all 38 tests pass.

* refactor: remove DEBUG-gated logging from optimize-imports plugin

* refactor: always emit skip log without DEBUG env gating

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: James <james@eli.cx>
2026-03-21 15:18:54 +00:00
Steve Faulkner 9c2fd26990 docs: update Vite 8 guidance (#596) 2026-03-19 20:44:29 -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 21558f3092 feat: support @vitejs/plugin-react v6 as peer (#525)
* feat: support @vitejs/plugin-react v6 as peer

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update

* Update
2026-03-13 15:35:36 +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 23a5cb2ceb refactor: delete app-dev-server.ts, point all callers at entries/ directly (#253 batch 3) (#379)
* refactor: delete app-dev-server.ts and update all references to entries/ directly

The file was already a pure re-export shim with no logic of its own.
Update every import/dynamic-import site to point straight at the
individual entry modules:

  entries/app-rsc-entry.ts    ← generateRscEntry, AppRouterConfig
  entries/app-ssr-entry.ts    ← generateSsrEntry
  entries/app-browser-entry.ts ← generateBrowserEntry

Sites updated:
  packages/vinext/src/index.ts
  tests/entry-templates.test.ts
  tests/app-router.test.ts
  tests/shims.test.ts

Also update stale comments in:
  server/middleware-codegen.ts, server/request-pipeline.ts,
  server/instrumentation.ts, shims/metadata.tsx,
  tests/rsc-streaming.test.ts, tests/nextjs-compat/rsc-context-lazy-stream.test.ts,
  examples/app-router-cloudflare/instrumentation*.ts

* docs: update app-dev-server.ts references in markdown files to entries/
2026-03-09 14:11:07 +00:00
James Anderson 70b18536ab fix: warn when existing vite.config.ts is missing cloudflare plugin on deploy (#315)
* fix: warn when existing vite.config.ts is missing cloudflare plugin on deploy

Closes #312.

- Add `viteConfigHasCloudflarePlugin()` to deploy.ts: when `vinext deploy`
  finds an existing vite.config.ts that doesn't import @cloudflare/vite-plugin,
  it now prints an actionable warning with the exact config fix rather than
  silently proceeding to a broken build.
- Add README prerequisites section to the Cloudflare Workers deploy docs:
  covers wrangler login vs CLOUDFLARE_API_TOKEN (with required permissions)
  and how to supply account_id.
- Add 7 unit tests for `viteConfigHasCloudflarePlugin()`.

* fix: pages router warning shows cloudflare() not cloudflare(())
2026-03-07 14:50:58 +00:00
Abdel d21d541fbc fix typo in README.md (#248) 2026-03-04 17:23:03 +00:00
Steve Faulkner 719dd306e8 docs: document Cloudflare bindings and Nitro multi-platform deployment (#231)
Rework the README deployment section into two clear paths:
- Cloudflare Workers (native) with cloudflare:workers bindings docs
- Other platforms (via Nitro) with collapsible examples for Vercel,
  Netlify, AWS, Deno, Node.js

Frame vinext as 'works everywhere' with Cloudflare as the first native
target and more native adapters planned (#80). Update the agent skill
and config references with the same guidance.
2026-03-03 03:04:40 +00:00
Steve Faulkner 9a6c8a7ba6 Run safe CI for external contributors, add /deploy-preview slash command (#201)
* Skip deploy previews for fork PRs that lack Cloudflare secrets

Fork PRs don't have access to repository secrets (CLOUDFLARE_API_TOKEN,
CLOUDFLARE_ACCOUNT_ID), so deploy/smoke-test/comment steps always fail.
Add a fork detection condition to skip these steps gracefully. The build
steps still run, so example builds are still validated for fork PRs.

* Run safe CI for external contributors, add /deploy-preview slash command

Switch ci.yml from pull_request to pull_request_target so lint, typecheck,
vitest, and e2e run automatically for fork PRs without needing approval.
No secrets are used, so this is safe with untrusted code.

Add a /deploy-preview slash command workflow that lets maintainers trigger
deploy previews on fork PRs. Gated by author_association (org members,
collaborators, repo owners only).

Cloudflare employees who push branches to the main repo continue to get
automatic deploy previews via the existing deploy-examples.yml workflow.

* Revert ci.yml to pull_request, skip deploy-examples for fork PRs

Simpler approach: keep ci.yml on pull_request (first-time contributor
approval is fine, avoids cache poisoning concern with pull_request_target).

The actual fix: add a job-level if condition to deploy-examples.yml so the
entire workflow is skipped for fork PRs. This prevents the noisy
failed/skipped deploy checks on external contributor PRs.

Update docs in AGENTS.md and README.md to reflect the approach.
2026-02-28 16:04:03 -06:00
Steve Faulkner 9939063998 Clarify OpenNext language in README (#166) 2026-02-27 10:42:15 -06:00
Fred K. Schott a498012970 Update README with new project setup instructions
Added instructions for starting a new vinext project.
2026-02-26 23:57:30 -08:00
Steve Faulkner 8dac26c0e5 fix(dotenv): load .env files with Next.js precedence (#70)
* feat(cli): load .env files with Next.js precedence

* fix(dotenv): address review feedback on .env loading

- Document Vite double-loading interaction in loadDotenv JSDoc
- Fix escaped dollar sign bug ($100 was not unescaped when $ wasn't
  followed by a valid variable name)
- Add type cast for parseEnv return value
- Expand test coverage: loadedEnv assertions, production mode
  precedence, ${VAR} expansion, escaped dollars, circular refs,
  missing files, empty result
- Add getDotenvFiles unit tests for all three modes
- Add 'Prefer Node.js Built-in APIs' guideline to AGENTS.md

Co-authored-by: liuxiaopai-ai <liuxiaopai-ai@users.noreply.github.com>

* test(dotenv): add cross-file expansion and multi-line value tests

Address review suggestions: test that .env can reference vars from
higher-priority .env.development.local, and that parseEnv handles
quoted multi-line values correctly.

---------

Co-authored-by: root <root@localhost.localdomain>
Co-authored-by: liuxiaopai-ai <liuxiaopai-ai@users.noreply.github.com>
2026-02-25 14:21:48 -06:00
Skywalker512 cb15a16aaa fix: link to Vite RSC plugin in README (#39)
Updated the link for the Vite RSC plugin to point to the correct repository.
2026-02-25 10:55:21 -06:00
Steve Faulkner 05ef04788c feat(deploy): add --env support with security and parsing improvements (#49)
* feat(deploy): add --env support for wrangler deploy

Adds vinext deploy --env <name> and threads it through to wrangler deploy --env <name>. Keeps --preview working as shorthand for preview environment, adds unit tests for wrangler arg construction, and updates README/help examples. Refs #33.

* fix(deploy): use execFileSync and util.parseArgs for --env support

Build on #44 by liuxiaopai-ai — adds --env flag for wrangler deploy.

Security: switch from execSync(shell string) to execFileSync(binary, args)
to eliminate command injection via unsanitized --env/--name values.

Replace hand-rolled flag parsing (parseStringFlag, parseNumericFlag,
rawArgs.includes) with Node.js built-in util.parseArgs. This gives us
strict mode (throws on unknown flags), automatic --flag=value handling,
and proper error messages for missing values.

Also: buildWranglerDeployArgs now returns { args, env } directly instead
of re-parsing the args array for the log message.

---------

Co-authored-by: root <root@localhost.localdomain>
2026-02-25 06:57:44 -06:00