feat(c3): scaffold Next.js apps with vinext instead of OpenNext - #14896
feat(c3): scaffold Next.js apps with vinext instead of OpenNext#14896scottbuscemi wants to merge 2 commits into
Conversation
Point create-cloudflare --framework=next at create-vinext-app so the generated project matches the recommended Next.js-on-Workers path (vinext dev/build + vinext-cloudflare deploy) instead of the OpenNext remote template.
🦋 Changeset detectedLatest commit: d4a4cba The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Prompt for vinext (default) vs OpenNext when scaffolding Next.js. --variant=vinext|opennext and -y/--accept-defaults (vinext) cover the non-interactive paths so OpenNext stays one flag away.
Update: OpenNext kept as an opt-in variantPer review feedback, C3 no longer drops OpenNext entirely.
Non-interactive: npm create cloudflare@latest my-app -- --framework=next --variant=vinext
npm create cloudflare@latest my-app -- --framework=next --variant=opennextE2E covers both ( |
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
There was a problem hiding this comment.
🔴 OpenNext Next.js end-to-end test fails on every run
The OpenNext framework test leaves type verification enabled while pointing at a type file the OpenNext scaffold never creates (typesPath "worker-configuration.d.ts", verifyTypes left at its default), so the test throws while reading a missing file and fails every run (packages/create-cloudflare/e2e/tests/frameworks/test-config.ts:366-381).
Impact: The stable OpenNext Next.js e2e test fails deterministically instead of passing.
Why verifyTypes always throws for this config
verifyTypes in packages/create-cloudflare/e2e/helpers/framework-helpers.ts:337-341 only skips when workersTypes === "none" or verify === false; neither holds here, so it runs and calls readFile(join(projectPath, typesPath)) with typesPath = "./worker-configuration.d.ts". The OpenNext scaffold generates cloudflare-env.d.ts, not worker-configuration.d.ts, so readFile throws and the test's outer catch calls expect.fail.
The experimental OpenNext test explicitly sets verifyTypes: false (with a comment) for exactly this reason, but this stable config omits it. Previously the template's typesPath was ./cloudflare-env.d.ts, so the old test passed.
(Refers to lines 380-381)
Was this helpful? React with 👍 or 👎 to provide feedback.
| const typesPath = "./cloudflare-env.d.ts"; | ||
| // vinext generates wrangler types into worker-configuration.d.ts; OpenNext's | ||
| // remote template ships cloudflare-env.d.ts and its own cf-typegen script. | ||
| const typesPath = "./worker-configuration.d.ts"; |
There was a problem hiding this comment.
🟡 OpenNext Next.js projects hit a broken type-setup step
The location of the generated type file is hard-coded to a filename the OpenNext path never creates (typesPath = "./worker-configuration.d.ts" at packages/create-cloudflare/templates/next/c3.ts:122), so scaffolding an OpenNext project fails to update its TypeScript config and prints a failure warning.
Impact: Users who scaffold the OpenNext Next.js variant see a spurious "Failed to update tsconfig.json" warning and their project's TypeScript config may not reference the generated Cloudflare types.
Why the shared typesPath breaks OpenNext
The single typesPath value is used for both variants, but the two variants emit different files: vinext produces worker-configuration.d.ts while the OpenNext remote template ships/generates cloudflare-env.d.ts (see the comment at packages/create-cloudflare/templates/next/c3.ts:120-121).
During the non-experimental configure phase (packages/create-cloudflare/src/cli.ts:199-201), addTypes → updateTsConfig runs with ctx.template.typesPath. At packages/create-cloudflare/src/workers.ts:149-151 it does readFile("./worker-configuration.d.ts"), which throws for an OpenNext project (the file does not exist), and the surrounding try/catch at packages/create-cloudflare/src/workers.ts:184-186 swallows it as warn("Failed to update tsconfig.json."), skipping the tsconfig update entirely (including the node types addition for nodeCompat).
Before this PR, typesPath was ./cloudflare-env.d.ts, which matched the OpenNext output, so this step worked.
Prompt for agents
The Next.js template now serves two variants (vinext and opennext) that generate different type-declaration files: vinext produces worker-configuration.d.ts, while the OpenNext remote template ships/generates cloudflare-env.d.ts. However templates/next/c3.ts exports a single static typesPath = "./worker-configuration.d.ts" used by C3 core (updateTsConfig in src/workers.ts reads this file to update tsconfig types). For OpenNext projects this file does not exist, so readFile throws and updateTsConfig aborts with a 'Failed to update tsconfig.json' warning, skipping the tsconfig update. Consider making typesPath (and envInterfaceName) variant-aware, or guard the OpenNext path so C3 does not try to read/inject a worker-configuration.d.ts that OpenNext never produces (OpenNext already ships its own cf-typegen and tsconfig). Note the same typesPath is also consumed by the e2e verifyTypes helper, which reads join(projectPath, typesPath).
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Makes
create-cloudflare --framework=nextconfigure vinext, while keeping opennext available with a flag.Today C3's Next template only downloads the OpenNext remote template (
github:opennextjs/opennextjs-cloudflare/create-cloudflare/next). The docs PR at cloudflare/cloudflare-docs#31887 recommends vinext as the default. This PR makes C3 do the same, with OpenNext retained as an opt-in variant.Behavior
Interactive (
--framework=next) prompts:Non-interactive:
-y/--accept-defaultsselects vinext.Changes
templates/next/c3.ts(and experimental re-export):--variantforvinext|opennextcreate-vinext-appviarunFrameworkGeneratorwith--platform cloudflare --data-cache none --yes --skip-install --disable-git+ matching package-manager flagpreviewscript (build && start) so C3's sharedpreviewScript: "preview"works for both variants[email protected]insrc/frameworks/package.json(keepcreate-next-apppin as well)next(vinext) +next:opennextin both stable and experimental matrices;getFrameworkConfigtolerates non-platform labels likenext:opennextGenerated project shapes
vinext (default)
{ "scripts": { "dev": "vinext dev", "build": "vinext build", "start": "wrangler dev --config dist/server/wrangler.json", "preview": "<pm> run build && <pm> run start", "deploy": "vinext-cloudflare deploy --config dist/server/wrangler.json" } }opennext — same as today's template (
opennextjs-cloudflare build/deploy/preview, etc.)Notes / follow-ups for reviewers
--variant=opennextand the manual OpenNext adapter docs.--data-cache noneon the vinext path avoids a placeholder KV namespace id that would block deploy until the user provisions one.Note
This is a contribution from an AI agent: opencode, xai/grok-4.5. Opened as a draft for human review before any merge consideration.