* 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>
* 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
* 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
* fix(init): detect bun and invoking package manager reliably
* refactor: address review feedback on detectPackageManagerName
- Combine bun.lock/bun.lockb checks into block form for consistency
with the npm lockfile check
- Accept optional env parameter (defaults to process.env) so the
function can be unit-tested without mutating global state
---------
Co-authored-by: root <root@localhost.localdomain>
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
* feat: add /dist/ to .gitignore during vinext init
vinext build outputs to /dist/ which should not be committed.
The init command now ensures /dist/ is in .gitignore, creating
the file if needed and skipping if the entry already exists.
* fix: broaden .gitignore duplicate check to cover dist/ and dist variants
Address review feedback: detect dist/ (no leading slash) and bare dist
as existing coverage for the dist directory to avoid adding a redundant
/dist/ entry. Rename entry -> exactEntry for clarity.
* fix: install react-server-dom-webpack during init and deploy for App Router
vinext init was missing react-server-dom-webpack from its dependency list,
causing 'Could not resolve react-server-dom-webpack/server.edge' errors on
fresh create-next-app projects. Additionally, react-server-dom-webpack@19.2.4
requires react@^19.2.4, but create-next-app ships react@19.2.3 -- the version
mismatch caused npm to nest rsdw inside vinext/node_modules where Vite can't
resolve it.
Fix: add react-server-dom-webpack to getInitDeps for App Router, detect when
the installed React version is too old, and upgrade react/react-dom as regular
dependencies before installing dev deps (avoiding ERESOLVE peer-dep conflicts
and keeping react in 'dependencies' rather than 'devDependencies').
Also applies the same fix to vinext deploy for parity, and adds a CI job that
scaffolds a real create-next-app project, runs vinext init, and verifies the
dev server returns HTTP 200.
Closes#22
* refactor: use createRequire for package resolution instead of direct node_modules reads
Address review feedback (PR #18 pattern): replace fs.existsSync(node_modules/...)
with createRequire-based resolution, which handles hoisting, pnpm symlinks,
monorepos, and Yarn PnP correctly.
- deploy.ts: extract isPackageResolvable() helper using createRequire, use it
in getMissingDeps for react-server-dom-webpack detection
- init.ts: refactor getReactUpgradeDeps() to use createRequire + findPackageVersion
walk-up pattern instead of reading node_modules/react/package.json directly
- tests: add isPackageResolvable unit tests, use dependency injection for
getMissingDeps rsdw test (Vitest's createRequire intercepts resolution)
* ci: add Windows to create-next-app smoke test
Run the create-next-app compatibility test on both ubuntu-latest and
windows-latest. Replace bash-specific server health check (curl, kill,
backgrounding) with a cross-platform Node script using child_process
and http. Use runner.temp instead of /tmp for portable paths.
* fix: use pathToFileURL for dynamic import() calls on Windows
On Windows, Node's ESM loader requires file:// URLs for absolute paths.
Bare paths like D:\... are rejected with ERR_UNSUPPORTED_ESM_URL_SCHEME.
Wrap all dynamic import() calls that receive absolute paths from
require.resolve() or path.join() with pathToFileURL().href:
- index.ts: RSC plugin import, RSC transforms import, PostCSS config
loading, PostCSS plugin resolution (5 call sites)
- cli.ts: Vite module import from project root
* fix: pathToFileURL for cache-runtime codegen and test imports
Additional Windows fixes found via PR #43 review:
- index.ts: use pathToFileURL for runtimeModulePath in 'use cache'
transform code generation (2 locations) — these embed absolute paths
into generated import() statements via JSON.stringify
- pages-router.test.ts: wrap 9 import(serverEntryPath) calls with
pathToFileURL so tests work on Windows
- Install vinext itself as a devDependency during init (#11)
- Remove explicit rsc() from generated configs since vinext auto-registers it (#12)
- Widen react/react-dom peer dep to >=19.2.0 for create-next-app compat (#13)
Co-authored-by: Fred K. Schott <fschott@cloudflare.com>