22 Commits

Author SHA1 Message Date
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
Marcus Reinhardt 9f5550579e fix vite-rsc link (#20) 2026-02-24 22:35:25 -06:00
Steve Faulkner 197f42106a docs: add LICENSE, sync package README, add bug reporting guidance (#26)
* docs: update README — add blog link, lead with agent skill, fix examples, remove stale benchmarks

* docs: add LICENSE, sync package README from root, add bug reporting guidance

- Add MIT LICENSE to repo root
- Copy root README into package at prepack time instead of maintaining two copies
- Add 'Reporting bugs' section encouraging AI-assisted diagnosis

* fix: update LICENSE copyright to Cloudflare, Inc.
2026-02-24 15:40:12 -06:00
Steve Faulkner 2c732add55 docs: update README — add blog link, lead with agent skill, fix examples, remove stale benchmarks (#19) 2026-02-24 20:11:37 +00:00
Matt Silverlock 009ace26bf add migrate-to-vinext agent skill (#6)
* add migrate-to-vinext agent skill

* Apply suggestion from @elithrar

* remove .claude/skills in favor of .agents/skills
2026-02-24 16:53:38 +00:00
Steve Faulkner 12fea722b6 Initial public release of vinext 2026-02-24 09:29:39 -06:00