mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #474] feat: support assetPrefix in next.config #600
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#600
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/474
Author: @elydelva
Created: 3/11/2026
Status: 🔄 Open
Base:
main← Head:feat/asset-prefix📝 Commits (10+)
00c8711feat: add assetPrefix support in next.config8b5071afeat: support assetPrefix in next.configd99cc97test: update entry-template snapshots for assetPrefix04e09e1test: add font-local CDN preload and basePath+assetPrefix combination tests6949427test: add E2E test verifying CDN-prefixed asset URLs in rendered HTML290a2aastyle: fix oxfmt formatting (semicolons, line length)ef6d83afix: avoid double basePath prefix in collectAssetTags when assetPrefix inherits basePatheca4295chore: update pnpm lockfile for asset-prefix fixturedd8364estyle: run oxfmt on all modified files6150056fix: harden assetPrefix edge cases and config composition📊 Changes
23 files changed (+622 additions, -31 deletions)
View changed files
📝
packages/vinext/src/config/next-config.ts(+19 -4)📝
packages/vinext/src/entries/app-rsc-entry.ts(+11 -7)📝
packages/vinext/src/entries/pages-server-entry.ts(+17 -5)📝
packages/vinext/src/index.ts(+24 -0)📝
packages/vinext/src/server/prod-server.ts(+4 -0)📝
packages/vinext/src/shims/font-local.ts(+10 -2)📝
playwright.config.ts(+11 -0)📝
pnpm-lock.yaml(+16 -0)📝
tests/__snapshots__/entry-templates.test.ts.snap(+17 -6)➕
tests/asset-prefix.test.ts(+102 -0)➕
tests/e2e/asset-prefix/asset-prefix.spec.ts(+128 -0)➕
tests/fixtures/asset-prefix/next-shims.d.ts(+24 -0)➕
tests/fixtures/asset-prefix/next.config.mjs(+6 -0)➕
tests/fixtures/asset-prefix/package.json(+13 -0)➕
tests/fixtures/asset-prefix/pages/_app.tsx(+6 -0)➕
tests/fixtures/asset-prefix/pages/about.tsx(+10 -0)➕
tests/fixtures/asset-prefix/pages/index.tsx(+11 -0)➕
tests/fixtures/asset-prefix/pages/styles.css(+4 -0)➕
tests/fixtures/asset-prefix/tsconfig.json(+11 -0)➕
tests/fixtures/asset-prefix/vite.config.ts(+6 -0)...and 3 more files
📄 Description
Closes #472
Summary
assetPrefixfromnext.configis currently silently ignored in vinext. This PR implements full support conformant with the Next.jsassetPrefixspec.Motivation
When deploying to Cloudflare Workers behind a gateway Worker (routing pattern for auth, multi-tenant apps, etc.), every request — including static fonts, JS chunks, and CSS — goes through the gateway and incurs a Worker invocation. With
assetPrefix, static assets can be served from a dedicated subdomain (e.g.assets-app.example.com) backed by a Cloudflare Workers Assets-only deployment withrun_worker_first: false, resulting in zero Worker invocations for static assets.Exploration
Before writing code, the Next.js reference implementation and the vinext codebase were fully analysed:
Next.js (
vercel/next.js) —assetPrefixflows through:webpack output.publicPath→ all JS/CSS chunksnext-font-loaderoutputPath → font files baked at build time<script src>,<link href>) inapp-render/client/asset-prefix.ts)public/files or/_next/image(by design)assetPrefixis empty andbasePathis set →assetPrefixinheritsbasePathvinext —
assetPrefixrequires 8 changes across 5 files. Critical gap found:shims/font-local.tsline 329 gates<link rel="preload">emission onhref.startsWith("/"), which silently breaks whenassetPrefixis an absolute CDN URL.Changes
1.
src/config/next-config.ts— config types + resolutionassetPrefix?: stringtoNextConfiginterfaceassetPrefix: stringtoResolvedNextConfiginterface""in null-config fallbackbasePathinheritance rule: ifassetPrefixis empty andbasePathis set →assetPrefix = basePath2.
src/index.ts— Viteexperimental.renderBuiltUrlassetPrefixis set, add Vite'sexperimental.renderBuiltUrlhooktype === "asset"(fonts, images) andtype === "chunk"(JS) URLs withassetPrefixssr: trueso server-side bundles keep relative pathsbase(driven bybasePath, separate concern)3.
src/shims/font-local.ts— font preload hintscollectFontPreloads()line ~329: extend URL guard fromhref.startsWith("/")to also accepthttps://,http://, and//prefixes<link rel="preload">tags are silently dropped whenassetPrefixis an absolute URL4.
src/entries/pages-server-entry.ts— Pages Router HTML injectionassetPrefixin the serialisedvinextConfigJsonpassed to the generated entrycollectAssetTagsgenerated code, replace hardcoded/href prefix withvinextConfig.assetPrefix + "/"5.
src/entries/app-rsc-entry.ts— App Router RSC serialisationassetPrefixin App Router RSC config alongsidebasePath6.
src/server/prod-server.ts— lazy chunks dedup7.
src/index.ts(Cloudflare build plugin) — worker entryassetPrefixto__VINEXT_CLIENT_ENTRY__global for Cloudflare Worker injection8. Tests
tests/asset-prefix.test.tsCoverage matrix (after this PR)
renderBuiltUrlrenderBuiltUrlnext/font/local)renderBuiltUrl+font-local.tsfix<script>/<link>in SSR HTMLvinextConfig.assetPrefixincollectAssetTags<link rel="preload">font-local.tsguard fixpublic/folder files/_next/imageWhat
assetPrefixdoes NOT cover (per spec)This is intentional and matches the Next.js documentation.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.