* 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: stub node:async_hooks in client builds via virtual module
Several shims (headers, cache, navigation-state, etc.) import
AsyncLocalStorage from node:async_hooks. Since resolve.alias applies
globally, these shims resolve in every environment including client.
Vite externalizes node:async_hooks to __vite-browser-external in browser
builds — an empty stub with no named exports — causing Rollup errors.
Add a vinext:async-hooks-stub plugin that intercepts node:async_hooks
in the client environment and provides a no-op AsyncLocalStorage via a
virtual module. This is semantically correct: shims already guard with
`_als.getStore() ?? fallback` patterns, so undefined store returns
produce correct client-side behavior.
Unlike the external file approach in #293, this uses a virtual module
inline in the plugin — no additional files, no build artifacts.
Closes#293
* fix: address async-hooks-stub code review feedback
- Convert load hook to object form with handler() for consistency
- Match bare `async_hooks` specifier in addition to `node:async_hooks`
- Add intentionally-minimal comment explaining stub scope
- Add regression tests for resolveId and load hook behavior
* refactor: extract async-hooks-stub plugin to plugins/ and test against real implementation
Move the vinext:async-hooks-stub plugin object from an inline definition in
index.ts to packages/vinext/src/plugins/async-hooks-stub.ts, exporting it as
asyncHooksStubPlugin. Import and use it directly in index.ts.
Update the regression test to import the real plugin via _asyncHooksStubPlugin
and invoke its resolveId/load handlers with a mock context, eliminating the
duplicated logic that the test previously used as a workaround for the plugin
being unexported and inline.
* fix: add filter to load hook, evaluate stub at runtime in test
- Add filter to load hook object form for consistency with resolveId
and to opt into Vite/Rolldown hook optimization path
- Replace overlapping string-assertion test with one that evaluates
the generated source via new Function, testing actual runtime
behavior (getStore returns undefined, run/exit pass through return values)
* fix: remove control-char regex from load filter to fix lint
* fix: suppress no-control-regex for load filter via eslint-disable comment
* refactor: derive load filter regex from ASYNC_HOOKS_STUB_ID constant
* Update build-optimization.test.ts
Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
* Update index.ts
Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
* fix: widen run() type signature to accept rest args and typed callback
---------
Co-authored-by: James <james@eli.cx>
Co-authored-by: ask-bonk[bot] <249159057+ask-bonk[bot]@users.noreply.github.com>
* fix: strip getServerSideProps/getStaticProps from client bundles
Pages Router page modules export getServerSideProps, getStaticProps,
and getStaticPaths for server-side use. These functions often import
server-only modules (database drivers, fs, etc.) that would break or
bloat the client bundle.
Added a new Vite transform plugin (vinext:strip-server-exports) that
replaces these exports with no-op stubs when the module is being
processed for the client environment. The plugin only applies to
files under the pages/ directory and skips API routes, _app,
_document, and _error files.
The transform handles multiple export patterns:
- export (async) function declarations
- export const arrow functions
- export const simple references
- TypeScript type annotations in parameters
Extracted the core logic into a standalone stripServerExports()
function for testability. Added 11 unit tests covering all patterns
including edge cases with nested braces, string literals, TypeScript
types, and multiple server exports in one file.
* Rewrite stripServerExports to use AST instead of regex
Replace the regex-based approach with parseAst (Rollup/acorn) and
MagicString. The AST approach correctly handles all export patterns:
- Function declarations (export function / export async function)
- Variable declarations with any initializer (arrow functions,
function expressions, async named function expressions, simple refs)
- Re-export syntax (export { getServerSideProps })
- Mixed re-exports (export { getServerSideProps, config })
The old regex approach had bugs with function expressions, arrow
functions with TS return type annotations, re-exports, and regex
literals in function bodies. Since this transform runs after Vite's
JSX/TS compilation, parseAst always receives plain JavaScript.
Updated tests to use post-compiled JS (no JSX/TS) matching what the
transform actually receives. Added 6 new test cases for the patterns
that broke the regex approach.
* fix: set NODE_ENV
* address review feedback: readability, test coverage, and nextConfig.env guard
- Replace nested ternary with if/else for NODE_ENV resolution
- Add comment explaining why beforeEach/afterEach is file-scoped
- Add test cases: serve (development), build without mode, user override
- Skip NODE_ENV key in nextConfig.env loop to prevent silent override
- Extract setupTmpProject helper to reduce test boilerplate
---------
Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
* fix: exclude vinext from optimizeDeps to prevent virtual module resolution errors
When vinext is installed from npm (not symlinked), esbuild's dependency
optimization scans vinext/dist/ and hits virtual:vinext-* imports that
only exist at Vite plugin resolution time. This causes build failures
in all three environments (client, rsc, ssr).
Adding optimizeDeps.exclude: ['vinext'] at the top level and in each
environment prevents esbuild from scanning vinext's dist files entirely.
Fixes#1
* test: verify optimizeDeps.exclude contains vinext for all environments