vinext 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -30
- package/dist/build/inject-pregenerated-paths.d.ts +4 -0
- package/dist/build/inject-pregenerated-paths.js +18 -0
- package/dist/build/layout-classification.js +1 -1
- package/dist/build/nitro-route-rules.js +1 -1
- package/dist/build/pages-client-assets-module.d.ts +11 -0
- package/dist/build/pages-client-assets-module.js +27 -0
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +13 -6
- package/dist/build/report.d.ts +13 -2
- package/dist/build/report.js +99 -2
- package/dist/build/run-prerender.d.ts +7 -0
- package/dist/build/run-prerender.js +10 -1
- package/dist/build/standalone.js +2 -0
- package/dist/cache/cache-adapters-virtual.js +3 -3
- package/dist/check.d.ts +18 -0
- package/dist/check.js +77 -19
- package/dist/cli-args.d.ts +1 -2
- package/dist/cli-args.js +1 -1
- package/dist/cli-dev-config.d.ts +12 -0
- package/dist/cli-dev-config.js +23 -0
- package/dist/cli.js +113 -97
- package/dist/{server → client}/dev-error-overlay-store.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay-store.js +1 -1
- package/dist/{server → client}/dev-error-overlay.d.ts +1 -1
- package/dist/{server → client}/dev-error-overlay.js +2 -2
- package/dist/client/navigation-runtime.d.ts +2 -0
- package/dist/client/navigation-runtime.js +3 -1
- package/dist/client/window-next.d.ts +2 -1
- package/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.js +2 -2
- package/dist/config/config-matchers.js +14 -10
- package/dist/config/next-config.d.ts +6 -3
- package/dist/config/next-config.js +47 -1
- package/dist/config/prerender.d.ts +34 -0
- package/dist/config/prerender.js +55 -0
- package/dist/config/server-external-packages.d.ts +4 -0
- package/dist/config/server-external-packages.js +91 -0
- package/dist/entries/app-rsc-entry.d.ts +2 -1
- package/dist/entries/app-rsc-entry.js +89 -15
- package/dist/entries/app-rsc-manifest.js +10 -0
- package/dist/entries/pages-client-entry.d.ts +3 -0
- package/dist/entries/pages-client-entry.js +16 -1
- package/dist/entries/pages-server-entry.js +6 -2
- package/dist/image/image-adapters-virtual.d.ts +59 -0
- package/dist/image/image-adapters-virtual.js +59 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +211 -118
- package/dist/init-cloudflare.d.ts +43 -0
- package/dist/init-cloudflare.js +839 -0
- package/dist/init-platform.d.ts +40 -0
- package/dist/init-platform.js +195 -0
- package/dist/init.d.ts +16 -38
- package/dist/init.js +208 -97
- package/dist/node_modules/.pnpm/am-i-vibing@0.5.0/node_modules/am-i-vibing/dist/detector-1yx2Hoe0.js +294 -0
- package/dist/node_modules/.pnpm/process-ancestry@0.1.0/node_modules/process-ancestry/dist/index.js +94 -0
- package/dist/{cloudflare → packages/cloudflare}/src/cache/cdn-adapter.runtime.js +1 -1
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.d.ts +2 -2
- package/dist/{cloudflare → packages/cloudflare}/src/cache/kv-data-adapter.runtime.js +1 -1
- package/dist/packages/cloudflare/src/deploy-config.js +150 -0
- package/dist/packages/cloudflare/src/deploy-help.js +55 -0
- package/dist/packages/cloudflare/src/deploy.js +276 -0
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.d.ts +4 -41
- package/dist/{cloudflare → packages/cloudflare/src}/tpr.js +18 -15
- package/dist/plugins/ast-scope.d.ts +16 -0
- package/dist/plugins/ast-scope.js +62 -0
- package/dist/plugins/ast-utils.d.ts +27 -1
- package/dist/plugins/ast-utils.js +32 -1
- package/dist/plugins/css-module-imports.d.ts +14 -0
- package/dist/plugins/css-module-imports.js +59 -0
- package/dist/plugins/extensionless-dynamic-import.js +2 -2
- package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
- package/dist/plugins/ignore-dynamic-requests.js +530 -0
- package/dist/plugins/middleware-server-only.d.ts +8 -6
- package/dist/plugins/middleware-server-only.js +8 -7
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/typeof-window.d.ts +1 -1
- package/dist/plugins/typeof-window.js +28 -56
- package/dist/routing/app-route-graph.d.ts +29 -4
- package/dist/routing/app-route-graph.js +182 -40
- package/dist/routing/app-router.d.ts +5 -0
- package/dist/routing/app-router.js +5 -0
- package/dist/routing/file-matcher.d.ts +8 -0
- package/dist/routing/file-matcher.js +10 -1
- package/dist/routing/pages-router.js +2 -2
- package/dist/server/api-handler.js +9 -14
- package/dist/server/app-browser-action-result.d.ts +4 -1
- package/dist/server/app-browser-action-result.js +8 -1
- package/dist/server/app-browser-entry.js +194 -190
- package/dist/server/app-browser-history-controller.d.ts +2 -1
- package/dist/server/app-browser-history-controller.js +6 -2
- package/dist/server/app-browser-interception-context.d.ts +1 -0
- package/dist/server/app-browser-interception-context.js +4 -2
- package/dist/server/app-browser-navigation-controller.d.ts +2 -0
- package/dist/server/app-browser-navigation-controller.js +60 -31
- package/dist/server/app-browser-server-action-client.d.ts +32 -0
- package/dist/server/app-browser-server-action-client.js +127 -0
- package/dist/server/app-browser-state.d.ts +11 -1
- package/dist/server/app-browser-state.js +25 -8
- package/dist/server/app-browser-visible-commit.d.ts +6 -1
- package/dist/server/app-browser-visible-commit.js +43 -25
- package/dist/server/app-elements-wire.d.ts +9 -2
- package/dist/server/app-elements-wire.js +8 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +4 -3
- package/dist/server/app-fallback-renderer.d.ts +5 -2
- package/dist/server/app-fallback-renderer.js +15 -7
- package/dist/server/app-hydration-cache-publication.d.ts +11 -0
- package/dist/server/app-hydration-cache-publication.js +43 -0
- package/dist/server/app-middleware.d.ts +4 -3
- package/dist/server/app-middleware.js +8 -3
- package/dist/server/app-optimistic-routing.js +11 -4
- package/dist/server/app-page-boundary-render.d.ts +3 -0
- package/dist/server/app-page-boundary-render.js +14 -3
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +10 -3
- package/dist/server/app-page-cache-render.d.ts +1 -0
- package/dist/server/app-page-cache-render.js +8 -4
- package/dist/server/app-page-cache.d.ts +1 -0
- package/dist/server/app-page-cache.js +5 -2
- package/dist/server/app-page-dispatch.d.ts +33 -4
- package/dist/server/app-page-dispatch.js +63 -90
- package/dist/server/app-page-element-builder.d.ts +9 -2
- package/dist/server/app-page-element-builder.js +114 -23
- package/dist/server/app-page-head.d.ts +16 -1
- package/dist/server/app-page-head.js +44 -22
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +8 -1
- package/dist/server/app-page-ppr-runtime.d.ts +7 -0
- package/dist/server/app-page-ppr-runtime.js +70 -0
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +6 -1
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +1 -1
- package/dist/server/app-page-render.d.ts +4 -1
- package/dist/server/app-page-render.js +26 -3
- package/dist/server/app-page-request.d.ts +22 -1
- package/dist/server/app-page-request.js +92 -15
- package/dist/server/app-page-route-wiring.d.ts +14 -2
- package/dist/server/app-page-route-wiring.js +61 -25
- package/dist/server/app-page-search-params-observation.d.ts +4 -2
- package/dist/server/app-page-search-params-observation.js +11 -7
- package/dist/server/app-page-segment-state.d.ts +2 -1
- package/dist/server/app-page-segment-state.js +3 -1
- package/dist/server/app-page-stream.d.ts +5 -0
- package/dist/server/app-page-stream.js +3 -1
- package/dist/server/app-ppr-fallback-shell-render.d.ts +4 -3
- package/dist/server/app-route-handler-dispatch.js +1 -0
- package/dist/server/app-route-handler-execution.js +7 -2
- package/dist/server/app-route-handler-response.js +1 -0
- package/dist/server/app-route-handler-runtime.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +2 -0
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-router-entry.js +16 -8
- package/dist/server/app-rsc-errors.js +7 -1
- package/dist/server/app-rsc-handler.d.ts +3 -1
- package/dist/server/app-rsc-handler.js +67 -39
- package/dist/server/app-rsc-request-normalization.d.ts +3 -2
- package/dist/server/app-rsc-request-normalization.js +6 -3
- package/dist/server/app-rsc-route-matching.d.ts +7 -0
- package/dist/server/app-rsc-route-matching.js +36 -3
- package/dist/server/app-segment-config.d.ts +12 -0
- package/dist/server/app-segment-config.js +91 -5
- package/dist/server/app-server-action-execution.d.ts +5 -0
- package/dist/server/app-server-action-execution.js +94 -33
- package/dist/server/app-ssr-entry.d.ts +3 -0
- package/dist/server/app-ssr-entry.js +19 -8
- package/dist/server/app-ssr-stream.d.ts +7 -3
- package/dist/server/app-ssr-stream.js +10 -6
- package/dist/server/app-static-generation.d.ts +1 -0
- package/dist/server/app-static-generation.js +1 -0
- package/dist/server/app-visited-response-cache.d.ts +6 -0
- package/dist/server/app-visited-response-cache.js +3 -1
- package/dist/server/client-trace-metadata.js +26 -0
- package/dist/server/default-global-not-found-module.d.ts +14 -0
- package/dist/server/default-global-not-found-module.js +14 -0
- package/dist/server/dev-route-files.js +4 -0
- package/dist/server/dev-server.js +19 -18
- package/dist/server/dev-stack-sourcemap.d.ts +1 -1
- package/dist/server/dev-stack-sourcemap.js +1 -1
- package/dist/server/fetch-handler.d.ts +2 -0
- package/dist/server/fetch-handler.js +18 -0
- package/dist/server/headers.d.ts +5 -15
- package/dist/server/headers.js +4 -15
- package/dist/server/image-optimization.d.ts +51 -1
- package/dist/server/image-optimization.js +52 -2
- package/dist/server/implicit-tags.js +1 -1
- package/dist/server/isr-cache.d.ts +1 -1
- package/dist/server/isr-cache.js +2 -2
- package/dist/server/metadata-routes.d.ts +3 -1
- package/dist/server/metadata-routes.js +12 -2
- package/dist/server/middleware-runtime.js +6 -1
- package/dist/server/navigation-planner.d.ts +1 -0
- package/dist/server/navigation-planner.js +14 -3
- package/dist/server/pages-api-route.js +2 -2
- package/dist/server/pages-asset-tags.d.ts +4 -6
- package/dist/server/pages-asset-tags.js +12 -12
- package/dist/server/pages-client-assets.d.ts +12 -0
- package/dist/server/pages-client-assets.js +10 -0
- package/dist/server/pages-node-compat.d.ts +8 -1
- package/dist/server/pages-node-compat.js +32 -4
- package/dist/server/pages-page-data.d.ts +24 -1
- package/dist/server/pages-page-data.js +45 -24
- package/dist/server/pages-page-handler.d.ts +2 -1
- package/dist/server/pages-page-handler.js +19 -12
- package/dist/server/pages-page-response.d.ts +7 -0
- package/dist/server/pages-page-response.js +4 -1
- package/dist/server/pages-request-pipeline.d.ts +2 -0
- package/dist/server/pages-request-pipeline.js +25 -1
- package/dist/server/pages-router-entry.d.ts +30 -0
- package/dist/server/pages-router-entry.js +108 -0
- package/dist/server/prerender-manifest.d.ts +3 -1
- package/dist/server/prerender-route-params.js +1 -1
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.js +48 -26
- package/dist/server/request-pipeline.js +1 -0
- package/dist/server/seed-cache.js +4 -4
- package/dist/server/worker-utils.d.ts +2 -1
- package/dist/server/worker-utils.js +7 -1
- package/dist/shims/app-router-scroll-state.d.ts +1 -0
- package/dist/shims/app-router-scroll-state.js +1 -0
- package/dist/shims/app-router-scroll.js +2 -1
- package/dist/shims/cache.js +19 -15
- package/dist/shims/cdn-cache.js +1 -1
- package/dist/shims/dynamic-preload-chunks.js +2 -1
- package/dist/shims/error-boundary.d.ts +19 -1
- package/dist/shims/error-boundary.js +11 -1
- package/dist/shims/form.d.ts +3 -1
- package/dist/shims/form.js +37 -43
- package/dist/shims/headers.d.ts +9 -1
- package/dist/shims/headers.js +31 -6
- package/dist/shims/image.js +75 -14
- package/dist/shims/internal/app-route-detection.d.ts +2 -17
- package/dist/shims/internal/app-route-detection.js +4 -17
- package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +23 -0
- package/dist/shims/internal/hybrid-client-route-owner-direct.js +51 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -15
- package/dist/shims/internal/hybrid-client-route-owner.js +9 -60
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +9 -5
- package/dist/shims/internal/pages-data-fetch-dedup.js +73 -14
- package/dist/shims/internal/pages-data-target.d.ts +10 -9
- package/dist/shims/internal/pages-data-target.js +91 -12
- package/dist/shims/internal/pages-router-components.d.ts +7 -0
- package/dist/shims/internal/pages-router-components.js +13 -0
- package/dist/shims/layout-segment-context.d.ts +4 -1
- package/dist/shims/layout-segment-context.js +18 -4
- package/dist/shims/link.js +70 -37
- package/dist/shims/metadata.d.ts +3 -2
- package/dist/shims/metadata.js +8 -4
- package/dist/shims/navigation.d.ts +5 -1
- package/dist/shims/navigation.js +45 -10
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/root-params.d.ts +15 -1
- package/dist/shims/root-params.js +21 -1
- package/dist/shims/router.d.ts +5 -7
- package/dist/shims/router.js +374 -94
- package/dist/shims/server.js +3 -2
- package/dist/shims/slot.js +3 -6
- package/dist/typegen.js +6 -5
- package/dist/utils/client-runtime-metadata.d.ts +2 -18
- package/dist/utils/client-runtime-metadata.js +31 -22
- package/dist/utils/dev-stack-sourcemap-endpoint.d.ts +4 -0
- package/dist/{server → utils}/dev-stack-sourcemap-endpoint.js +1 -1
- package/dist/utils/domain-locale.d.ts +6 -3
- package/dist/{server → utils}/middleware-request-headers.d.ts +1 -1
- package/dist/{server → utils}/middleware-request-headers.js +2 -2
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +1 -1
- package/dist/utils/project.d.ts +52 -2
- package/dist/utils/project.js +230 -5
- package/dist/utils/protocol-headers.d.ts +17 -0
- package/dist/utils/protocol-headers.js +17 -0
- package/dist/utils/react-version.d.ts +4 -0
- package/dist/utils/react-version.js +44 -0
- package/package.json +53 -4
- package/dist/deploy.d.ts +0 -190
- package/dist/deploy.js +0 -1033
- package/dist/server/dev-stack-sourcemap-endpoint.d.ts +0 -4
- /package/dist/{cloudflare → packages/cloudflare}/src/utils/cache-control-metadata.js +0 -0
package/README.md
CHANGED
|
@@ -49,30 +49,37 @@ Replace `next` with `vinext` in your scripts:
|
|
|
49
49
|
```bash
|
|
50
50
|
vinext dev # Development server with HMR
|
|
51
51
|
vinext build # Production build
|
|
52
|
-
vinext deploy
|
|
52
|
+
npx @vinext/cloudflare deploy # Build and deploy to Cloudflare Workers
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
With Vite+, use `vpx @vinext/cloudflare deploy`, or
|
|
56
|
+
`vp exec vinext-cloudflare deploy` when running the locally installed bin.
|
|
57
|
+
|
|
55
58
|
vinext auto-detects your `app/` or `pages/` directory, loads `next.config.js`, and configures Vite automatically. No `vite.config.ts` required for basic usage.
|
|
56
59
|
|
|
57
60
|
Your existing `pages/`, `app/`, `next.config.js`, and `public/` directories work as-is. Run `vinext check` first to scan for known compatibility issues, or use `vinext init` to [automate the full migration](#migrating-an-existing-nextjs-project).
|
|
58
61
|
|
|
59
62
|
### CLI reference
|
|
60
63
|
|
|
61
|
-
| Command
|
|
62
|
-
|
|
|
63
|
-
| `vinext dev`
|
|
64
|
-
| `vinext build`
|
|
65
|
-
| `vinext start`
|
|
66
|
-
| `vinext deploy`
|
|
67
|
-
| `vinext
|
|
68
|
-
| `vinext
|
|
69
|
-
| `vinext
|
|
64
|
+
| Command | Description |
|
|
65
|
+
| ---------------------------------- | ----------------------------------------------------------------------- |
|
|
66
|
+
| `vinext dev` | Start dev server with HMR |
|
|
67
|
+
| `vinext build` | Production build (multi-environment for App Router: RSC + SSR + client) |
|
|
68
|
+
| `vinext start` | Start local production server for testing |
|
|
69
|
+
| `npx @vinext/cloudflare deploy` | Build and deploy to Cloudflare Workers |
|
|
70
|
+
| `vp exec vinext-cloudflare deploy` | Build and deploy to Cloudflare Workers with Vite+ |
|
|
71
|
+
| `vinext init` | Migrate a Next.js project to run under vinext |
|
|
72
|
+
| `vinext check` | Scan your Next.js app for compatibility issues before migrating |
|
|
73
|
+
| `vinext lint` | Delegate to eslint or oxlint |
|
|
70
74
|
|
|
71
75
|
Options: `-p / --port <port>`, `-H / --hostname <host>`, `--turbopack` (accepted, no-op).
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
`@vinext/cloudflare deploy` options: `--preview`, `--env <name>`, `--name <name>`, `--skip-build`, `--dry-run`, `--experimental-tpr`.
|
|
78
|
+
|
|
79
|
+
`vinext init` prompts for a deployment target, defaulting to Cloudflare. Agents must ask the
|
|
80
|
+
user which target they want, then pass `--platform=cloudflare` or `--platform=node`.
|
|
74
81
|
|
|
75
|
-
|
|
82
|
+
Other options: `--port <port>` (default: 3001), `--skip-check`, `--force`.
|
|
76
83
|
|
|
77
84
|
If your `next.config.*` sets `output: "standalone"`, `vinext build` emits a self-hosting bundle at `dist/standalone/`. Start it with:
|
|
78
85
|
|
|
@@ -105,7 +112,9 @@ This will:
|
|
|
105
112
|
3. Rename CJS config files (e.g. `postcss.config.js` -> `.cjs`) to avoid ESM conflicts
|
|
106
113
|
4. Add `"type": "module"` to `package.json`
|
|
107
114
|
5. Add `dev:vinext`, `build:vinext`, and `start:vinext` scripts to `package.json`
|
|
108
|
-
6.
|
|
115
|
+
6. Prompt for a deployment platform (Cloudflare by default, or Node)
|
|
116
|
+
7. Generate the matching `vite.config.ts`
|
|
117
|
+
8. For Cloudflare, generate `wrangler.jsonc`
|
|
109
118
|
|
|
110
119
|
The migration is non-destructive -- your existing Next.js setup continues to work alongside vinext. It does not modify `next.config`, `tsconfig.json`, or any source files, and it does not remove Next.js dependencies.
|
|
111
120
|
|
|
@@ -118,7 +127,10 @@ npm run start:vinext # Start vinext production server
|
|
|
118
127
|
npm run dev # Still runs Next.js as before
|
|
119
128
|
```
|
|
120
129
|
|
|
121
|
-
Use `--
|
|
130
|
+
Use `--platform=cloudflare` or `--platform=node` to skip the platform prompt. Cloudflare init
|
|
131
|
+
updates an existing JavaScript or TypeScript Vite config using its AST, preserving unrelated
|
|
132
|
+
settings. Use `--force` to replace an existing Node-target Vite config, or `--skip-check` to skip
|
|
133
|
+
the compatibility report.
|
|
122
134
|
|
|
123
135
|
## Why
|
|
124
136
|
|
|
@@ -126,7 +138,7 @@ Vite has become the default build tool for modern web frameworks — fast HMR, a
|
|
|
126
138
|
|
|
127
139
|
vinext is an experiment: can we reimplement the Next.js API surface on Vite, so that existing Next.js applications can run on a completely different toolchain? The answer, so far, is mostly yes.
|
|
128
140
|
|
|
129
|
-
vinext works everywhere. It natively supports Cloudflare Workers (with `vinext deploy`, bindings, KV caching), and can be deployed to Vercel, Netlify, AWS, Deno Deploy, and more via the [Nitro](https://v3.nitro.build/) Vite plugin. Native support for additional platforms is [planned](https://github.com/cloudflare/vinext/issues/80).
|
|
141
|
+
vinext works everywhere. It natively supports Cloudflare Workers (with `npx @vinext/cloudflare deploy` or `vp exec vinext-cloudflare deploy`, bindings, KV caching), and can be deployed to Vercel, Netlify, AWS, Deno Deploy, and more via the [Nitro](https://v3.nitro.build/) Vite plugin. Native support for additional platforms is [planned](https://github.com/cloudflare/vinext/issues/80).
|
|
130
142
|
|
|
131
143
|
**Alternatives worth knowing about:**
|
|
132
144
|
|
|
@@ -173,7 +185,7 @@ Both. File-system routing, SSR, client hydration, and deployment to Cloudflare W
|
|
|
173
185
|
Next.js 16.x. No support for deprecated APIs from older versions.
|
|
174
186
|
|
|
175
187
|
**Can I deploy to AWS/Netlify/other platforms?**
|
|
176
|
-
Yes. Add the [Nitro](https://v3.nitro.build/) Vite plugin alongside vinext, and you can deploy to Vercel, Netlify, AWS Amplify, Deno Deploy, Azure, and [many more](https://v3.nitro.build/deploy). See [Other platforms (via Nitro)](#other-platforms-via-nitro) for setup. For Cloudflare Workers, the native integration (`vinext deploy`) gives you the smoothest experience. Native adapters for more platforms are [planned](https://github.com/cloudflare/vinext/issues/80).
|
|
188
|
+
Yes. Add the [Nitro](https://v3.nitro.build/) Vite plugin alongside vinext, and you can deploy to Vercel, Netlify, AWS Amplify, Deno Deploy, Azure, and [many more](https://v3.nitro.build/deploy). See [Other platforms (via Nitro)](#other-platforms-via-nitro) for setup. For Cloudflare Workers, the native integration (`npx @vinext/cloudflare deploy` or `vp exec vinext-cloudflare deploy`) gives you the smoothest experience. Native adapters for more platforms are [planned](https://github.com/cloudflare/vinext/issues/80).
|
|
177
189
|
|
|
178
190
|
**What happens when Next.js releases a new feature?**
|
|
179
191
|
We track the public Next.js API surface and add support for new stable features. Experimental or unstable Next.js features are lower priority. The plan is to add commit-level tracking of the Next.js repo so we can stay current as new versions are released.
|
|
@@ -182,16 +194,16 @@ We track the public Next.js API surface and add support for new stable features.
|
|
|
182
194
|
|
|
183
195
|
### Cloudflare Workers
|
|
184
196
|
|
|
185
|
-
vinext has native integration with Cloudflare Workers through `@cloudflare/vite-plugin`, including bindings access via `cloudflare:workers`, KV caching, image optimization, and the
|
|
197
|
+
vinext has native integration with Cloudflare Workers through `@cloudflare/vite-plugin`, including bindings access via `cloudflare:workers`, KV caching, image optimization, and the `@vinext/cloudflare deploy` one-command workflow.
|
|
186
198
|
|
|
187
199
|
#### Prerequisites
|
|
188
200
|
|
|
189
|
-
Before running `vinext deploy` for the first time you need to authenticate with Cloudflare and tell wrangler which account to deploy to.
|
|
201
|
+
Before running `npx @vinext/cloudflare deploy` for the first time you need to authenticate with Cloudflare and tell wrangler which account to deploy to.
|
|
190
202
|
|
|
191
203
|
**Authentication — pick one:**
|
|
192
204
|
|
|
193
205
|
- **`wrangler login`** (recommended for local development) — opens a browser window to authenticate. Run it once and wrangler caches the token.
|
|
194
|
-
- **`CLOUDFLARE_API_TOKEN` env var** (CI / non-interactive) — create a token at [dash.cloudflare.com/profile/api-tokens](https://dash.cloudflare.com/profile/api-tokens) using the **Edit Cloudflare Workers** template. That template grants all the permissions
|
|
206
|
+
- **`CLOUDFLARE_API_TOKEN` env var** (CI / non-interactive) — create a token at [dash.cloudflare.com/profile/api-tokens](https://dash.cloudflare.com/profile/api-tokens) using the **Edit Cloudflare Workers** template. That template grants all the permissions `@vinext/cloudflare deploy` needs.
|
|
195
207
|
|
|
196
208
|
**Account ID:**
|
|
197
209
|
|
|
@@ -208,16 +220,24 @@ Find your account ID in the Cloudflare dashboard URL (`dash.cloudflare.com/<acco
|
|
|
208
220
|
|
|
209
221
|
Alternatively, set the `CLOUDFLARE_ACCOUNT_ID` environment variable instead of hardcoding it in the config file.
|
|
210
222
|
|
|
211
|
-
`vinext
|
|
223
|
+
Run `vinext init --platform=cloudflare` first to install dependencies and create or AST-update
|
|
224
|
+
`vite.config.*` and `wrangler.jsonc`. `@vinext/cloudflare deploy` then validates that setup, builds the
|
|
225
|
+
application, and deploys to Workers without rewriting project configuration.
|
|
226
|
+
|
|
227
|
+
Cloudflare init can also configure image optimization declaratively in the Vite config with
|
|
228
|
+
`imagesOptimizer()` and add the matching Wrangler Images binding. The built-in fetch handlers register
|
|
229
|
+
that optimizer at runtime; image optimization is not implemented or generated by `@vinext/cloudflare deploy`.
|
|
212
230
|
|
|
213
231
|
```bash
|
|
214
|
-
vinext deploy
|
|
215
|
-
vinext deploy
|
|
232
|
+
npx @vinext/cloudflare deploy
|
|
233
|
+
vp exec vinext-cloudflare deploy
|
|
234
|
+
npx @vinext/cloudflare deploy --env staging
|
|
235
|
+
vp exec vinext-cloudflare deploy --env staging
|
|
216
236
|
```
|
|
217
237
|
|
|
218
238
|
Use `--env <name>` to target `wrangler.jsonc` `env.<name>`. `--preview` is shorthand for `--env preview`.
|
|
219
239
|
|
|
220
|
-
The
|
|
240
|
+
The init command also auto-detects and fixes common migration issues:
|
|
221
241
|
|
|
222
242
|
- Adds `"type": "module"` to package.json if missing
|
|
223
243
|
- Resolves tsconfig.json path aliases automatically (via `vite-tsconfig-paths`)
|
|
@@ -263,10 +283,11 @@ For TypeScript types, generate them with `wrangler types` and the `env` import w
|
|
|
263
283
|
TPR queries Cloudflare zone analytics at deploy time to find which pages actually get traffic, pre-renders only those, and uploads them to KV cache. The result is SSG-level latency for popular pages without pre-rendering your entire site.
|
|
264
284
|
|
|
265
285
|
```bash
|
|
266
|
-
vinext deploy --experimental-tpr # Pre-render pages covering 90% of traffic
|
|
267
|
-
vinext deploy --experimental-tpr
|
|
268
|
-
vinext deploy --experimental-tpr --tpr-
|
|
269
|
-
vinext deploy --experimental-tpr --tpr-
|
|
286
|
+
npx @vinext/cloudflare deploy --experimental-tpr # Pre-render pages covering 90% of traffic
|
|
287
|
+
vp exec vinext-cloudflare deploy --experimental-tpr # Same, with Vite+
|
|
288
|
+
npx @vinext/cloudflare deploy --experimental-tpr --tpr-coverage 95 # More aggressive coverage
|
|
289
|
+
npx @vinext/cloudflare deploy --experimental-tpr --tpr-limit 500 # Cap at 500 pages
|
|
290
|
+
npx @vinext/cloudflare deploy --experimental-tpr --tpr-window 48 # Use 48h of analytics
|
|
270
291
|
```
|
|
271
292
|
|
|
272
293
|
Requires a custom domain (zone analytics are unavailable on `*.workers.dev`) and `CLOUDFLARE_API_TOKEN` with Zone.Analytics read permission.
|
|
@@ -326,7 +347,7 @@ NITRO_PRESET=netlify npx vite build
|
|
|
326
347
|
NITRO_PRESET=deno_deploy npx vite build
|
|
327
348
|
```
|
|
328
349
|
|
|
329
|
-
> **Deploying to Cloudflare?** You can use Nitro, but the native integration (`vinext deploy`
|
|
350
|
+
> **Deploying to Cloudflare?** You can use Nitro, but the native integration (`npx @vinext/cloudflare deploy`, `vp exec vinext-cloudflare deploy`, and `@cloudflare/vite-plugin`) is recommended. It provides the best developer experience with `cloudflare:workers` bindings, KV caching, image optimization, and one-command deploys.
|
|
330
351
|
|
|
331
352
|
<details>
|
|
332
353
|
<summary>Vercel</summary>
|
|
@@ -508,7 +529,7 @@ Load order matches Next.js (highest priority first):
|
|
|
508
529
|
Modes:
|
|
509
530
|
|
|
510
531
|
- `vinext dev` uses `development`
|
|
511
|
-
- `vinext build`, `vinext start`, and
|
|
532
|
+
- `vinext build`, `vinext start`, and `@vinext/cloudflare deploy` use `production`
|
|
512
533
|
|
|
513
534
|
Variable expansion (`$VAR` / `${VAR}`) is supported.
|
|
514
535
|
|
|
@@ -597,7 +618,7 @@ These are gaps we'd like to close — distinct from the [intentional exclusions]
|
|
|
597
618
|
- **Google Fonts are loaded from the CDN, not self-hosted.** No `size-adjust` fallback font metrics. Local fonts work but `@font-face` CSS is injected at runtime, not extracted at build time.
|
|
598
619
|
- **Route segment config** — `runtime` and `preferredRegion` are ignored (everything runs in the same environment).
|
|
599
620
|
- **Node.js production server (`vinext start`)** works for testing but is less complete than Workers deployment. Cloudflare Workers is the primary target.
|
|
600
|
-
- **Native Node modules (sharp, resvg, satori, lightningcss, @napi-rs/canvas)** crash Vite's RSC dev environment. Dynamic OG image/icon routes using these work in production builds but not in dev mode. These are auto-stubbed during
|
|
621
|
+
- **Native Node modules (sharp, resvg, satori, lightningcss, @napi-rs/canvas)** crash Vite's RSC dev environment. Dynamic OG image/icon routes using these work in production builds but not in dev mode. These are auto-stubbed during `@vinext/cloudflare deploy`.
|
|
601
622
|
- **`next.config.ts` `baseUrl` bare imports require Vite 8.** A `next.config.ts` that imports a bare specifier resolved through `tsconfig.json`'s `compilerOptions.baseUrl` (e.g. `import { bar } from "bar"` resolving to a local `bar.ts`) relies on Vite 8's native `resolve.tsconfigPaths` (Rolldown/oxc-resolver). On Vite 7 there is no native equivalent, so these imports are not resolved. `compilerOptions.paths` aliases (e.g. `@/foo`) work on both Vite 7 and 8. Note that if a bare import matches both a `baseUrl`-local file and an installed package of the same name, the installed package wins (vinext keeps packages externalized so CJS config plugins like `@next/mdx` keep working).
|
|
602
623
|
|
|
603
624
|
## Benchmarks
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { escapeRegExp } from "../utils/regex.js";
|
|
2
|
+
import { readPrerenderManifest } from "../server/prerender-manifest.js";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
//#region src/build/inject-pregenerated-paths.ts
|
|
6
|
+
const VINEXT_PREGEN_START = "/* __VINEXT_PREGENERATED_CONCRETE_PATHS_START__ */";
|
|
7
|
+
const VINEXT_PREGEN_END = "/* __VINEXT_PREGENERATED_CONCRETE_PATHS_END__ */";
|
|
8
|
+
const VINEXT_PREGEN_RE = new RegExp(`${escapeRegExp(VINEXT_PREGEN_START)}[\\s\\S]*?${escapeRegExp(VINEXT_PREGEN_END)}\\n?`, "g");
|
|
9
|
+
function injectPregeneratedConcretePaths(root) {
|
|
10
|
+
const workerEntry = path.resolve(root, "dist", "server", "index.js");
|
|
11
|
+
if (!fs.existsSync(workerEntry)) return;
|
|
12
|
+
let code = fs.readFileSync(workerEntry, "utf-8").replace(VINEXT_PREGEN_RE, "");
|
|
13
|
+
const table = readPrerenderManifest(path.join(root, "dist", "server", "vinext-prerender.json"))?.pregeneratedConcretePaths ?? [];
|
|
14
|
+
if (table.length > 0) code = `${VINEXT_PREGEN_START}\nglobalThis.__VINEXT_PREGENERATED_CONCRETE_PATHS = ${JSON.stringify(table)};\n${VINEXT_PREGEN_END}\n` + code;
|
|
15
|
+
fs.writeFileSync(workerEntry, code);
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { injectPregeneratedConcretePaths };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { classifyLayoutSegmentConfig } from "./report.js";
|
|
1
2
|
import { AppElementsWire } from "../server/app-elements-wire.js";
|
|
2
3
|
import "../server/app-elements.js";
|
|
3
|
-
import { classifyLayoutSegmentConfig } from "./report.js";
|
|
4
4
|
import { createAppPageTreePath } from "../server/app-page-route-wiring.js";
|
|
5
5
|
//#region src/build/layout-classification.ts
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { apiRouter, pagesRouter } from "../routing/pages-router.js";
|
|
2
|
-
import { appRouter } from "../routing/app-router.js";
|
|
3
2
|
import { buildReportRows } from "./report.js";
|
|
3
|
+
import { appRouter } from "../routing/app-router.js";
|
|
4
4
|
//#region src/build/nitro-route-rules.ts
|
|
5
5
|
/**
|
|
6
6
|
* Scans the filesystem for route files and generates Nitro `routeRules` for ISR routes.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PagesClientAssets } from "../server/pages-client-assets.js";
|
|
2
|
+
|
|
3
|
+
//#region src/build/pages-client-assets-module.d.ts
|
|
4
|
+
declare const PAGES_CLIENT_ASSETS_MODULE = "vinext-client-assets.js";
|
|
5
|
+
declare function buildPagesClientAssetsModule(assets: PagesClientAssets): string;
|
|
6
|
+
declare function writePagesClientAssetsModuleIfMissing(outputDir: string, moduleSource: string): void;
|
|
7
|
+
declare function setPagesClientAssetsBuildMetadata(buildSession: string, moduleSource: string): void;
|
|
8
|
+
declare function takePagesClientAssetsBuildMetadata(buildSession: string): string | null;
|
|
9
|
+
declare function clearPagesClientAssetsBuildMetadata(buildSession: string): void;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { PAGES_CLIENT_ASSETS_MODULE, buildPagesClientAssetsModule, clearPagesClientAssetsBuildMetadata, setPagesClientAssetsBuildMetadata, takePagesClientAssetsBuildMetadata, writePagesClientAssetsModuleIfMissing };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
//#region src/build/pages-client-assets-module.ts
|
|
4
|
+
const PAGES_CLIENT_ASSETS_MODULE = "vinext-client-assets.js";
|
|
5
|
+
const pagesClientAssetsByBuildSession = /* @__PURE__ */ new Map();
|
|
6
|
+
function buildPagesClientAssetsModule(assets) {
|
|
7
|
+
return `export default ${JSON.stringify(assets)};\n`;
|
|
8
|
+
}
|
|
9
|
+
function writePagesClientAssetsModuleIfMissing(outputDir, moduleSource) {
|
|
10
|
+
const outputPath = path.join(outputDir, PAGES_CLIENT_ASSETS_MODULE);
|
|
11
|
+
if (fs.existsSync(outputPath)) return;
|
|
12
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
13
|
+
fs.writeFileSync(outputPath, moduleSource);
|
|
14
|
+
}
|
|
15
|
+
function setPagesClientAssetsBuildMetadata(buildSession, moduleSource) {
|
|
16
|
+
pagesClientAssetsByBuildSession.set(buildSession, moduleSource);
|
|
17
|
+
}
|
|
18
|
+
function takePagesClientAssetsBuildMetadata(buildSession) {
|
|
19
|
+
const moduleSource = pagesClientAssetsByBuildSession.get(buildSession) ?? null;
|
|
20
|
+
pagesClientAssetsByBuildSession.delete(buildSession);
|
|
21
|
+
return moduleSource;
|
|
22
|
+
}
|
|
23
|
+
function clearPagesClientAssetsBuildMetadata(buildSession) {
|
|
24
|
+
pagesClientAssetsByBuildSession.delete(buildSession);
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { PAGES_CLIENT_ASSETS_MODULE, buildPagesClientAssetsModule, clearPagesClientAssetsBuildMetadata, setPagesClientAssetsBuildMetadata, takePagesClientAssetsBuildMetadata, writePagesClientAssetsModuleIfMissing };
|
|
@@ -22,7 +22,8 @@ type PrerenderRouteResult = {
|
|
|
22
22
|
* Omitted for non-dynamic routes where pattern === path.
|
|
23
23
|
*/
|
|
24
24
|
path?: string; /** Which router produced this route. Used by cache seeding. */
|
|
25
|
-
router: "app" | "pages"; /**
|
|
25
|
+
router: "app" | "pages"; /** Response headers that must be replayed with the prerendered artifact. */
|
|
26
|
+
headers?: Record<string, string>; /** Set to true when this is a PPR fallback shell. */
|
|
26
27
|
fallback?: boolean;
|
|
27
28
|
} | {
|
|
28
29
|
route: string;
|
package/dist/build/prerender.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
|
|
2
|
-
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../
|
|
3
|
-
import {
|
|
2
|
+
import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
|
|
3
|
+
import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
|
|
4
4
|
import { NoOpCacheHandler, getCacheHandler, setCacheHandler } from "../shims/cache-handler.js";
|
|
5
5
|
import { headersContextFromRequest, runWithHeadersContext } from "../shims/headers.js";
|
|
6
6
|
import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
|
|
7
|
-
import {
|
|
7
|
+
import { normalizeStaticPathsEntry } from "../routing/route-pattern.js";
|
|
8
8
|
import { BLOCKED_PAGES } from "../shims/constants.js";
|
|
9
|
+
import { buildPregeneratedConcretePathTable } from "../server/prerender-manifest.js";
|
|
9
10
|
import { concatUint8Arrays, decodeRscEmbeddedChunk } from "../server/app-rsc-embedded-chunks.js";
|
|
10
11
|
import { navigationRuntimeRscBootstrapExpression } from "../server/app-ssr-stream.js";
|
|
11
|
-
import { createAppPprFallbackShells, markAppPprDynamicFallbackShellHtml } from "../server/app-ppr-fallback-shell.js";
|
|
12
12
|
import { encodePrerenderRouteParams, serializePrerenderRouteParamsHeader } from "../server/prerender-route-params.js";
|
|
13
13
|
import { readPrerenderSecret } from "./server-manifest.js";
|
|
14
|
+
import { createAppPprFallbackShells, markAppPprDynamicFallbackShellHtml } from "../server/app-ppr-fallback-shell.js";
|
|
14
15
|
import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
|
|
15
16
|
import { startProdServer } from "../server/prod-server.js";
|
|
16
17
|
import fs from "node:fs";
|
|
@@ -80,7 +81,7 @@ function extractRscPayloadFromPrerenderedHtml(html) {
|
|
|
80
81
|
let match;
|
|
81
82
|
while ((match = scriptPattern.exec(html)) !== null) {
|
|
82
83
|
const script = (match[1] ?? "").trim().replace(/;$/, "");
|
|
83
|
-
if (script === RSC_RUNTIME_DONE_SCRIPT || script === RSC_LEGACY_DONE_SCRIPT) {
|
|
84
|
+
if (script === RSC_RUNTIME_DONE_SCRIPT || script === RSC_LEGACY_DONE_SCRIPT || script.endsWith(`;${RSC_RUNTIME_DONE_SCRIPT}`)) {
|
|
84
85
|
sawDone = true;
|
|
85
86
|
continue;
|
|
86
87
|
}
|
|
@@ -721,10 +722,12 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
721
722
|
const htmlRender = await runWithHeadersContext(headersContextFromRequest(htmlRequest), async () => {
|
|
722
723
|
const response = await rscHandler(htmlRequest);
|
|
723
724
|
const cacheControl = response.headers.get("cache-control") ?? "";
|
|
725
|
+
const linkHeader = response.headers.get("link");
|
|
724
726
|
if (!response.ok || isSpeculative && cacheControl.includes("no-store")) {
|
|
725
727
|
await response.body?.cancel();
|
|
726
728
|
return {
|
|
727
729
|
cacheControl,
|
|
730
|
+
linkHeader,
|
|
728
731
|
html: null,
|
|
729
732
|
ok: response.ok,
|
|
730
733
|
requestCacheLife: null,
|
|
@@ -733,6 +736,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
733
736
|
}
|
|
734
737
|
return {
|
|
735
738
|
cacheControl,
|
|
739
|
+
linkHeader,
|
|
736
740
|
html: await response.text(),
|
|
737
741
|
ok: true,
|
|
738
742
|
requestCacheLife: _consumeRequestScopedCacheLife(),
|
|
@@ -800,6 +804,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
|
|
|
800
804
|
revalidate: renderedRevalidate,
|
|
801
805
|
...typeof renderedRevalidate === "number" ? { expire: renderedCacheControl.expire } : {},
|
|
802
806
|
router: "app",
|
|
807
|
+
...htmlRender.linkHeader ? { headers: { link: htmlRender.linkHeader } } : {},
|
|
803
808
|
...urlPath !== routePattern ? { path: urlPath } : {},
|
|
804
809
|
...isFallback ? { fallback: true } : {}
|
|
805
810
|
};
|
|
@@ -907,6 +912,7 @@ function writePrerenderIndex(routes, outDir, options) {
|
|
|
907
912
|
revalidate: r.revalidate,
|
|
908
913
|
...typeof r.revalidate === "number" ? { expire: r.expire } : {},
|
|
909
914
|
router: r.router,
|
|
915
|
+
...r.headers ? { headers: r.headers } : {},
|
|
910
916
|
...r.path ? { path: r.path } : {},
|
|
911
917
|
...r.fallback ? { fallback: true } : {}
|
|
912
918
|
};
|
|
@@ -924,7 +930,8 @@ function writePrerenderIndex(routes, outDir, options) {
|
|
|
924
930
|
const index = {
|
|
925
931
|
...buildId ? { buildId } : {},
|
|
926
932
|
...typeof trailingSlash === "boolean" ? { trailingSlash } : {},
|
|
927
|
-
routes: indexRoutes
|
|
933
|
+
routes: indexRoutes,
|
|
934
|
+
pregeneratedConcretePaths: buildPregeneratedConcretePathTable({ routes: indexRoutes })
|
|
928
935
|
};
|
|
929
936
|
fs.writeFileSync(path.join(outDir, "vinext-prerender.json"), JSON.stringify(index, null, 2), "utf-8");
|
|
930
937
|
}
|
package/dist/build/report.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ type RouteRow = {
|
|
|
17
17
|
prerendered?: boolean;
|
|
18
18
|
};
|
|
19
19
|
type AppRouteRenderEntry = Pick<AppRoute, "pagePath" | "routePath" | "parallelSlots">;
|
|
20
|
+
type StaticMiddlewareMatcherObject = {
|
|
21
|
+
source: string;
|
|
22
|
+
locale?: false;
|
|
23
|
+
has?: Array<Record<string, string>>;
|
|
24
|
+
missing?: Array<Record<string, string>>;
|
|
25
|
+
};
|
|
26
|
+
type StaticMiddlewareMatcher = string | Array<string | StaticMiddlewareMatcherObject>;
|
|
20
27
|
declare function getAppRouteRenderEntryPath(route: AppRouteRenderEntry): string | null;
|
|
21
28
|
/**
|
|
22
29
|
* Returns true if the source code contains an export declaration with the given name.
|
|
@@ -28,12 +35,15 @@ declare function getAppRouteRenderEntryPath(route: AppRouteRenderEntry): string
|
|
|
28
35
|
* export { foo }
|
|
29
36
|
*/
|
|
30
37
|
declare function hasNamedExport(code: string, name: string): boolean;
|
|
38
|
+
/** Returns true when Next.js' analyzer recognizes the requested export name. */
|
|
39
|
+
declare function hasExportedName(code: string, name: string): boolean;
|
|
31
40
|
/**
|
|
32
41
|
* Extracts the string value of `export const <name> = "value"`.
|
|
33
42
|
* Handles TypeScript annotations/assertions and no-substitution template literals.
|
|
34
43
|
* Returns null if the export is absent or not a string literal.
|
|
35
44
|
*/
|
|
36
45
|
declare function extractExportConstString(code: string, name: string): string | null;
|
|
46
|
+
declare function extractMiddlewareMatcherConfig(filePath: string): StaticMiddlewareMatcher | undefined;
|
|
37
47
|
/**
|
|
38
48
|
* Extracts the numeric value of `export const <name> = <number|false>`.
|
|
39
49
|
* Supports integers, decimals, negative values, `Infinity`, and `false`.
|
|
@@ -121,7 +131,8 @@ declare function formatBuildReport(rows: RouteRow[], routerLabel?: string): stri
|
|
|
121
131
|
* Scans the project at `root`, classifies all routes, and prints the
|
|
122
132
|
* Next.js-style build report to stdout.
|
|
123
133
|
*
|
|
124
|
-
*
|
|
134
|
+
* `root` must be forward-slash — it is passed to `findDir`. The caller (the
|
|
135
|
+
* `vinext build` entry in cli.ts) normalizes it.
|
|
125
136
|
*/
|
|
126
137
|
declare function printBuildReport(options: {
|
|
127
138
|
root: string;
|
|
@@ -129,4 +140,4 @@ declare function printBuildReport(options: {
|
|
|
129
140
|
prerenderResult?: PrerenderResult;
|
|
130
141
|
}): Promise<void>;
|
|
131
142
|
//#endregion
|
|
132
|
-
export { RouteRow, RouteType, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, formatBuildReport, getAppRouteRenderEntryPath, hasNamedExport, printBuildReport };
|
|
143
|
+
export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
package/dist/build/report.js
CHANGED
|
@@ -22,6 +22,7 @@ import { parseSync } from "vite";
|
|
|
22
22
|
* `export const revalidate` are classified as "unknown" rather than "static"
|
|
23
23
|
* to avoid false confidence.
|
|
24
24
|
*/
|
|
25
|
+
const UNSUPPORTED_STATIC_VALUE = Symbol("unsupported static value");
|
|
25
26
|
function getAppRouteRenderEntryPath(route) {
|
|
26
27
|
if (route.pagePath) return route.pagePath;
|
|
27
28
|
if (route.routePath) return null;
|
|
@@ -72,6 +73,21 @@ function hasNamedExport(code, name) {
|
|
|
72
73
|
if (!program) return false;
|
|
73
74
|
return hasNamedExportInProgram(program, name);
|
|
74
75
|
}
|
|
76
|
+
/** Returns true when Next.js' analyzer recognizes the requested export name. */
|
|
77
|
+
function hasExportedName(code, name) {
|
|
78
|
+
const program = parseRouteModule(code);
|
|
79
|
+
if (!program) return false;
|
|
80
|
+
for (const node of program.body) {
|
|
81
|
+
if (node.type !== "ExportNamedDeclaration") continue;
|
|
82
|
+
if (node.exportKind === "type") continue;
|
|
83
|
+
if (declarationHasBindingName(node.declaration, name)) return true;
|
|
84
|
+
for (const specifier of node.specifiers) {
|
|
85
|
+
if (specifier.exportKind === "type") continue;
|
|
86
|
+
if (moduleExportNameValue(specifier.local) === name) return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
75
91
|
function hasNamedExportInProgram(program, name) {
|
|
76
92
|
for (const node of program.body) {
|
|
77
93
|
if (node.type !== "ExportNamedDeclaration") continue;
|
|
@@ -117,6 +133,86 @@ function extractStringFromConstInitializer(initializer) {
|
|
|
117
133
|
if (expression.type === "TemplateLiteral" && expression.expressions.length === 0) return expression.quasis[0]?.value.cooked ?? expression.quasis[0]?.value.raw ?? null;
|
|
118
134
|
return null;
|
|
119
135
|
}
|
|
136
|
+
function extractMiddlewareMatcherConfig(filePath) {
|
|
137
|
+
let code;
|
|
138
|
+
try {
|
|
139
|
+
code = fs.readFileSync(filePath, "utf8");
|
|
140
|
+
} catch {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const initializer = findExportedConstInitializer(code, "config");
|
|
144
|
+
if (!initializer) return void 0;
|
|
145
|
+
const config = unwrapStaticExpression(initializer);
|
|
146
|
+
if (config.type !== "ObjectExpression") return void 0;
|
|
147
|
+
const matcherExpression = objectPropertyValue(config, "matcher");
|
|
148
|
+
if (!matcherExpression) return void 0;
|
|
149
|
+
const value = extractStaticJsonValue(matcherExpression);
|
|
150
|
+
return isStaticMiddlewareMatcher(value) ? value : void 0;
|
|
151
|
+
}
|
|
152
|
+
function objectPropertyValue(object, key) {
|
|
153
|
+
for (const property of object.properties) {
|
|
154
|
+
if (property.type !== "Property" || property.computed) continue;
|
|
155
|
+
if (propertyKeyName(property.key) !== key) continue;
|
|
156
|
+
return property.value;
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
function propertyKeyName(key) {
|
|
161
|
+
if (key.type === "Identifier") return key.name;
|
|
162
|
+
if (key.type === "Literal" && typeof key.value === "string") return key.value;
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
function extractStaticJsonValue(expression) {
|
|
166
|
+
const value = unwrapStaticExpression(expression);
|
|
167
|
+
if (value.type === "Literal") {
|
|
168
|
+
if (typeof value.value === "string" || typeof value.value === "number" || typeof value.value === "boolean" || value.value === null) return value.value;
|
|
169
|
+
return UNSUPPORTED_STATIC_VALUE;
|
|
170
|
+
}
|
|
171
|
+
if (value.type === "TemplateLiteral" && value.expressions.length === 0) return value.quasis[0]?.value.cooked ?? value.quasis[0]?.value.raw ?? "";
|
|
172
|
+
if (value.type === "ArrayExpression") {
|
|
173
|
+
const items = [];
|
|
174
|
+
for (const element of value.elements) {
|
|
175
|
+
if (!element || element.type === "SpreadElement") return UNSUPPORTED_STATIC_VALUE;
|
|
176
|
+
const item = extractStaticJsonValue(element);
|
|
177
|
+
if (item === UNSUPPORTED_STATIC_VALUE) return UNSUPPORTED_STATIC_VALUE;
|
|
178
|
+
items.push(item);
|
|
179
|
+
}
|
|
180
|
+
return items;
|
|
181
|
+
}
|
|
182
|
+
if (value.type === "ObjectExpression") {
|
|
183
|
+
const object = {};
|
|
184
|
+
for (const property of value.properties) {
|
|
185
|
+
if (property.type !== "Property" || property.computed) return UNSUPPORTED_STATIC_VALUE;
|
|
186
|
+
const key = propertyKeyName(property.key);
|
|
187
|
+
if (!key) return UNSUPPORTED_STATIC_VALUE;
|
|
188
|
+
const propertyValue = extractStaticJsonValue(property.value);
|
|
189
|
+
if (propertyValue === UNSUPPORTED_STATIC_VALUE) return UNSUPPORTED_STATIC_VALUE;
|
|
190
|
+
object[key] = propertyValue;
|
|
191
|
+
}
|
|
192
|
+
return object;
|
|
193
|
+
}
|
|
194
|
+
return UNSUPPORTED_STATIC_VALUE;
|
|
195
|
+
}
|
|
196
|
+
function isStaticMiddlewareMatcher(value) {
|
|
197
|
+
if (typeof value === "string") return true;
|
|
198
|
+
if (!Array.isArray(value)) return false;
|
|
199
|
+
return value.every((item) => typeof item === "string" || isStaticMiddlewareMatcherObject(item));
|
|
200
|
+
}
|
|
201
|
+
function isStaticMiddlewareMatcherObject(value) {
|
|
202
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
203
|
+
const record = value;
|
|
204
|
+
if (typeof record.source !== "string") return false;
|
|
205
|
+
if (record.locale !== void 0 && record.locale !== false) return false;
|
|
206
|
+
return isStaticMatcherConditions(record.has) && isStaticMatcherConditions(record.missing);
|
|
207
|
+
}
|
|
208
|
+
function isStaticMatcherConditions(value) {
|
|
209
|
+
if (value === void 0) return true;
|
|
210
|
+
if (!Array.isArray(value)) return false;
|
|
211
|
+
return value.every((item) => {
|
|
212
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return false;
|
|
213
|
+
return Object.values(item).every((entry) => typeof entry === "string");
|
|
214
|
+
});
|
|
215
|
+
}
|
|
120
216
|
/**
|
|
121
217
|
* Extracts the numeric value of `export const <name> = <number|false>`.
|
|
122
218
|
* Supports integers, decimals, negative values, `Infinity`, and `false`.
|
|
@@ -474,7 +570,8 @@ function formatBuildReport(rows, routerLabel = "app") {
|
|
|
474
570
|
* Scans the project at `root`, classifies all routes, and prints the
|
|
475
571
|
* Next.js-style build report to stdout.
|
|
476
572
|
*
|
|
477
|
-
*
|
|
573
|
+
* `root` must be forward-slash — it is passed to `findDir`. The caller (the
|
|
574
|
+
* `vinext build` entry in cli.ts) normalizes it.
|
|
478
575
|
*/
|
|
479
576
|
async function printBuildReport(options) {
|
|
480
577
|
const { root } = options;
|
|
@@ -501,4 +598,4 @@ async function printBuildReport(options) {
|
|
|
501
598
|
}
|
|
502
599
|
}
|
|
503
600
|
//#endregion
|
|
504
|
-
export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, formatBuildReport, getAppRouteRenderEntryPath, hasNamedExport, printBuildReport };
|
|
601
|
+
export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
|
@@ -56,6 +56,13 @@ type RunPrerenderOptions = {
|
|
|
56
56
|
* `output: 'export'`. Exported for direct unit testing. Refs cloudflare/vinext#1982
|
|
57
57
|
*/
|
|
58
58
|
declare function assertNoFatalPrerenderRoutes(routes: readonly PrerenderRouteResult[]): void;
|
|
59
|
+
/**
|
|
60
|
+
* Statically generate routes and return the prerender result.
|
|
61
|
+
*
|
|
62
|
+
* `options.root` must be forward-slash — it is passed to `findDir` and flows
|
|
63
|
+
* into the route model. The caller (the `vinext build` entry in cli.ts)
|
|
64
|
+
* normalizes it.
|
|
65
|
+
*/
|
|
59
66
|
declare function runPrerender(options: RunPrerenderOptions): Promise<PrerenderResult | null>;
|
|
60
67
|
//#endregion
|
|
61
68
|
export { assertNoFatalPrerenderRoutes, runPrerender };
|
|
@@ -6,12 +6,13 @@ import { loadNextConfig, resolveNextConfig } from "../config/next-config.js";
|
|
|
6
6
|
import { readPrerenderSecret } from "./server-manifest.js";
|
|
7
7
|
import { startProdServer } from "../server/prod-server.js";
|
|
8
8
|
import { prerenderApp, prerenderPages, writePrerenderIndex } from "./prerender.js";
|
|
9
|
+
import { injectPregeneratedConcretePaths } from "./inject-pregenerated-paths.js";
|
|
9
10
|
import fs from "node:fs";
|
|
10
11
|
import path from "node:path";
|
|
11
12
|
//#region src/build/run-prerender.ts
|
|
12
13
|
/**
|
|
13
14
|
* Shared prerender runner used by both `vinext build` (cli.ts) and
|
|
14
|
-
* `vinext deploy --prerender-all` (deploy.ts).
|
|
15
|
+
* `vinext-cloudflare deploy --prerender-all` (deploy.ts).
|
|
15
16
|
*
|
|
16
17
|
* `runPrerender` handles route scanning, dynamic imports, progress reporting,
|
|
17
18
|
* and result summarisation.
|
|
@@ -95,6 +96,13 @@ function assertNoFatalPrerenderRoutes(routes) {
|
|
|
95
96
|
const fatalList = fatalRoutes.map((r) => ` ${r.route}: ${r.error}`).join("\n");
|
|
96
97
|
throw new Error(`Prerender failed: ${fatalRoutes.length} route${fatalRoutes.length !== 1 ? "s" : ""} errored during static generation.\n${fatalList}`);
|
|
97
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Statically generate routes and return the prerender result.
|
|
101
|
+
*
|
|
102
|
+
* `options.root` must be forward-slash — it is passed to `findDir` and flows
|
|
103
|
+
* into the route model. The caller (the `vinext build` entry in cli.ts)
|
|
104
|
+
* normalizes it.
|
|
105
|
+
*/
|
|
98
106
|
async function runPrerender(options) {
|
|
99
107
|
const { root } = options;
|
|
100
108
|
const appDir = findDir(root, "app", "src/app");
|
|
@@ -215,6 +223,7 @@ async function runPrerender(options) {
|
|
|
215
223
|
const errorRoutes = allRoutes.filter((r) => r.status === "error").map((r) => ` ${r.route}: ${r.error}`).join("\n");
|
|
216
224
|
throw new Error(`Static export failed: ${errors} route${errors !== 1 ? "s" : ""} cannot be statically exported.\n${errorRoutes}\n\nRemove server-side data fetching (getServerSideProps, force-dynamic, revalidate) from these routes, or remove \`output: "export"\` from next.config.js.`);
|
|
217
225
|
}
|
|
226
|
+
injectPregeneratedConcretePaths(root);
|
|
218
227
|
return {
|
|
219
228
|
routes: allRoutes,
|
|
220
229
|
...allOutputFiles.length > 0 ? { outputFiles: allOutputFiles } : {}
|
package/dist/build/standalone.js
CHANGED
|
@@ -167,6 +167,8 @@ function emitStandaloneOutput(options) {
|
|
|
167
167
|
dereference: true,
|
|
168
168
|
filter: (src) => !path.relative(serverDir, src).split(path.sep).includes("node_modules")
|
|
169
169
|
});
|
|
170
|
+
const clientAssetsSidecar = path.join(outDir, "vinext-client-assets.js");
|
|
171
|
+
if (fs.existsSync(clientAssetsSidecar)) fs.copyFileSync(clientAssetsSidecar, path.join(standaloneDistDir, "vinext-client-assets.js"));
|
|
170
172
|
const publicDir = path.join(root, "public");
|
|
171
173
|
if (fs.existsSync(publicDir)) fs.cpSync(publicDir, path.join(standaloneDir, "public"), {
|
|
172
174
|
recursive: true,
|
|
@@ -35,9 +35,9 @@ function generateCacheAdaptersModule(cache) {
|
|
|
35
35
|
lines.push(`import __vinextCdnAdapterFactory from ${JSON.stringify(cdn.adapter)};`);
|
|
36
36
|
lines.push(`import { setCdnCacheAdapter } from "vinext/shims/cdn-cache";`);
|
|
37
37
|
}
|
|
38
|
-
lines.push("", "// A factory that throws (e.g. a missing binding on an incompatible runtime)", "// is logged and skipped so the default handler stays in place.", "let __vinextCacheAdaptersRegistered = false;", "", "export function registerConfiguredCacheAdapters(env) {", " if (__vinextCacheAdaptersRegistered) return;", " __vinextCacheAdaptersRegistered = true;");
|
|
39
|
-
if (data?.adapter) lines.push(" try {", ` setDataCacheHandler(__vinextDataAdapterFactory({ env, options: ${inlineOptions(data.adapter, data.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured data cache adapter; using the default handler
|
|
40
|
-
if (cdn?.adapter) lines.push(" try {", ` setCdnCacheAdapter(__vinextCdnAdapterFactory({ env, options: ${inlineOptions(cdn.adapter, cdn.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter
|
|
38
|
+
lines.push("", "// A factory that throws (e.g. a missing binding on an incompatible runtime)", "// is logged and skipped so the default handler stays in place.", "function __vinextFormatAdapterError(error) {", " if (error instanceof Error && error.message) return error.message;", " try {", " return String(error);", " } catch {", " return '<unknown error>';", " }", "}", "", "let __vinextCacheAdaptersRegistered = false;", "", "export function registerConfiguredCacheAdapters(env) {", " if (__vinextCacheAdaptersRegistered) return;", " __vinextCacheAdaptersRegistered = true;");
|
|
39
|
+
if (data?.adapter) lines.push(" try {", ` setDataCacheHandler(__vinextDataAdapterFactory({ env, options: ${inlineOptions(data.adapter, data.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured data cache adapter; using the default handler.\\n\" + __vinextFormatAdapterError(error));", " }");
|
|
40
|
+
if (cdn?.adapter) lines.push(" try {", ` setCdnCacheAdapter(__vinextCdnAdapterFactory({ env, options: ${inlineOptions(cdn.adapter, cdn.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\\n\" + __vinextFormatAdapterError(error));", " }");
|
|
41
41
|
lines.push("}", "");
|
|
42
42
|
return lines.join("\n");
|
|
43
43
|
}
|