@wular/pnext 0.1.5 → 0.1.7
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/bin/pnext +9 -2
- package/dist/server/_chunks/aliases-j47a10ce.js +44 -0
- package/dist/server/_chunks/analyze-arswqe6s.js +445 -0
- package/dist/server/_chunks/boot-af0mv3dy.js +36 -0
- package/dist/server/_chunks/build-spy3dxp4.js +84 -0
- package/dist/server/_chunks/build-t9h0hec3.js +3550 -0
- package/dist/server/_chunks/build-tier-m7yvqz61.js +2472 -0
- package/dist/server/_chunks/compile-53j02j27.js +22 -0
- package/dist/server/_chunks/config-1yzwexq6.js +27 -0
- package/dist/server/_chunks/config-n2z7yxps.js +22 -0
- package/dist/server/_chunks/create-meec9hs6.js +134 -0
- package/dist/server/_chunks/dev-fjmqg6en.js +220 -0
- package/dist/server/_chunks/esbuild-yyktfrtt.js +15 -0
- package/dist/server/_chunks/generate-n9sh6hzt.js +21 -0
- package/dist/server/_chunks/index-0374j2b9.js +66 -0
- package/dist/server/_chunks/index-06nyqntx.js +213 -0
- package/dist/server/_chunks/index-1011cscr.js +5263 -0
- package/dist/server/_chunks/index-11sfk4hb.js +170 -0
- package/dist/server/_chunks/index-20ppawv7.js +297 -0
- package/dist/server/_chunks/index-2dv4y4wd.js +1065 -0
- package/dist/server/_chunks/index-2vz0wzdn.js +59 -0
- package/dist/server/_chunks/index-2wfv4kvx.js +182 -0
- package/dist/server/_chunks/index-357na4a8.js +109 -0
- package/dist/server/_chunks/index-3b3at80h.js +109 -0
- package/dist/server/_chunks/index-3tf1xjsp.js +6166 -0
- package/dist/server/_chunks/index-3xbr4eyv.js +105 -0
- package/dist/server/_chunks/index-4399bvd2.js +18 -0
- package/dist/server/_chunks/index-4hgjrqh7.js +851 -0
- package/dist/server/_chunks/index-4q2gbfwk.js +2530 -0
- package/dist/server/_chunks/index-4t92hdxd.js +172 -0
- package/dist/server/_chunks/index-4zzebszd.js +10 -0
- package/dist/server/_chunks/index-5gyr4emz.js +107 -0
- package/dist/server/_chunks/index-5hr4062s.js +70 -0
- package/dist/server/_chunks/index-68m3mwbd.js +84 -0
- package/dist/server/_chunks/index-6b009mt0.js +56 -0
- package/dist/server/_chunks/index-6e4fqv30.js +588 -0
- package/dist/server/_chunks/index-6gex3pfa.js +15 -0
- package/dist/server/_chunks/index-6kp9r0d9.js +11 -0
- package/dist/server/_chunks/index-6q7zvrap.js +134 -0
- package/dist/server/_chunks/index-73z4esab.js +38 -0
- package/dist/server/_chunks/index-79ngvam8.js +60 -0
- package/dist/server/_chunks/index-7wc9n8be.js +25 -0
- package/dist/server/_chunks/index-8k7pfejn.js +1599 -0
- package/dist/server/_chunks/index-8kcxdfya.js +206 -0
- package/dist/server/_chunks/index-91x12ykq.js +274 -0
- package/dist/server/_chunks/index-9ch50vtq.js +18 -0
- package/dist/server/_chunks/index-9hx2641q.js +28 -0
- package/dist/server/_chunks/index-9n2914ww.js +535 -0
- package/dist/server/_chunks/index-9ych9ma8.js +207 -0
- package/dist/server/_chunks/index-b6wtkdp4.js +24 -0
- package/dist/server/_chunks/index-bcws5eh2.js +35 -0
- package/dist/server/_chunks/index-c0g2wf3z.js +92 -0
- package/dist/server/_chunks/index-cf4rvxv6.js +95 -0
- package/dist/server/_chunks/index-cjgqs13g.js +65 -0
- package/dist/server/_chunks/index-ckkdpht5.js +41 -0
- package/dist/server/_chunks/index-cyfednwb.js +87 -0
- package/dist/server/_chunks/index-danncfhy.js +311 -0
- package/dist/server/_chunks/index-dg0f05zs.js +199 -0
- package/dist/server/_chunks/index-e3a6yx84.js +18 -0
- package/dist/server/_chunks/index-e88tyv35.js +45 -0
- package/dist/server/_chunks/index-eajaspjk.js +1253 -0
- package/dist/server/_chunks/index-eeaanp2e.js +483 -0
- package/dist/server/_chunks/index-egykcqg3.js +101 -0
- package/dist/server/_chunks/index-en7n9f7m.js +22 -0
- package/dist/server/_chunks/index-ezgj0tr1.js +88 -0
- package/dist/server/_chunks/index-fkp000p1.js +590 -0
- package/dist/server/_chunks/index-fn1chkkd.js +214 -0
- package/dist/server/_chunks/index-fp8r0sqs.js +64 -0
- package/dist/server/_chunks/index-ggk2j965.js +9432 -0
- package/dist/server/_chunks/index-grta8ap3.js +238 -0
- package/dist/server/_chunks/index-h2a70tg4.js +7 -0
- package/dist/server/_chunks/index-h4xy648y.js +51 -0
- package/dist/server/_chunks/index-hkdfj58q.js +92 -0
- package/dist/server/_chunks/index-j2zrcp1k.js +66 -0
- package/dist/server/_chunks/index-jcsjq3d1.js +20 -0
- package/dist/server/_chunks/index-jepjvcrf.js +198 -0
- package/dist/server/_chunks/index-jks0x0a1.js +14 -0
- package/dist/server/_chunks/index-jx7bqvw1.js +57 -0
- package/dist/server/_chunks/index-k1g3epw0.js +1299 -0
- package/dist/server/_chunks/index-knp0s912.js +7 -0
- package/dist/server/_chunks/index-kq8kpzye.js +472 -0
- package/dist/server/_chunks/index-kqyy1d4f.js +50 -0
- package/dist/server/_chunks/index-kresp2n8.js +445 -0
- package/dist/server/_chunks/index-ms9v9tnx.js +31 -0
- package/dist/server/_chunks/index-n4hxpng2.js +11 -0
- package/dist/server/_chunks/index-ncnes8c6.js +17 -0
- package/dist/server/_chunks/index-nqh3xhdc.js +57 -0
- package/dist/server/_chunks/index-nyrba2kr.js +4130 -0
- package/dist/server/_chunks/index-p77b4kcx.js +296 -0
- package/dist/server/_chunks/index-pym1cezq.js +39 -0
- package/dist/server/_chunks/index-q738kkhj.js +100 -0
- package/dist/server/_chunks/index-qjj6gddn.js +54 -0
- package/dist/server/_chunks/index-qm6kmhqs.js +110 -0
- package/dist/server/_chunks/index-qn4z5s3c.js +819 -0
- package/dist/server/_chunks/index-qpwsgt3a.js +158 -0
- package/dist/server/_chunks/index-r97ptz1q.js +97 -0
- package/dist/server/_chunks/index-ras0sdab.js +26 -0
- package/dist/server/_chunks/index-rcs322t1.js +19 -0
- package/dist/server/_chunks/index-rn8dyr1f.js +126 -0
- package/dist/server/_chunks/index-rt9qq1va.js +4 -0
- package/dist/server/_chunks/index-rthrsed5.js +356 -0
- package/dist/server/_chunks/index-sctcgyvv.js +185 -0
- package/dist/server/_chunks/index-sjpckxqn.js +213 -0
- package/dist/server/_chunks/index-sq3kvz3g.js +200 -0
- package/dist/server/_chunks/index-stbk3jhn.js +31 -0
- package/dist/server/_chunks/index-sv8m6789.js +266 -0
- package/dist/server/_chunks/index-tgcakdxc.js +125 -0
- package/dist/server/_chunks/index-tgsfjmep.js +74 -0
- package/dist/server/_chunks/index-tq0pc7eg.js +154 -0
- package/dist/server/_chunks/index-tq1f96v7.js +51 -0
- package/dist/server/_chunks/index-tqn2qnnn.js +241 -0
- package/dist/server/_chunks/index-tvqkq6e6.js +128 -0
- package/dist/server/_chunks/index-v1ags1na.js +132 -0
- package/dist/server/_chunks/index-vvmmknm1.js +1431 -0
- package/dist/server/_chunks/index-vvq7rqtn.js +329 -0
- package/dist/server/_chunks/index-vz0km5qk.js +33 -0
- package/dist/server/_chunks/index-w6h8m6vp.js +138 -0
- package/dist/server/_chunks/index-web361b8.js +138 -0
- package/dist/server/_chunks/index-wpfhfbs4.js +194 -0
- package/dist/server/_chunks/index-x0y005nh.js +283 -0
- package/dist/server/_chunks/index-x3yn4ywm.js +22 -0
- package/dist/server/_chunks/index-x93hv0sc.js +271 -0
- package/dist/server/_chunks/index-xvj80c3a.js +30 -0
- package/dist/server/_chunks/index-xywjg2vc.js +100 -0
- package/dist/server/_chunks/index-y2jr2n72.js +152 -0
- package/dist/server/_chunks/index-y36a0ybw.js +57 -0
- package/dist/server/_chunks/index-ye9hmsvf.js +47 -0
- package/dist/server/_chunks/index-yhxkxghe.js +56 -0
- package/dist/server/_chunks/index-ym67zqxp.js +130 -0
- package/dist/server/_chunks/index-ytjvpbm2.js +736 -0
- package/dist/server/_chunks/index-yz54pxrh.js +960 -0
- package/dist/server/_chunks/index-z4z9zv3b.js +733 -0
- package/dist/server/_chunks/index-z84ab1w0.js +1270 -0
- package/dist/server/_chunks/index-z88qv16t.js +11 -0
- package/dist/server/_chunks/index-zqpqyg8p.js +666 -0
- package/dist/server/_chunks/loader-7yw0j98z.js +78 -0
- package/dist/server/_chunks/modules-jn8bmbn8.js +74 -0
- package/dist/server/_chunks/pipeline-yy9jhydf.js +832 -0
- package/dist/server/_chunks/production-jar9ztm3.js +44 -0
- package/dist/server/_chunks/proxy-config-j84dgksn.js +21 -0
- package/dist/server/_chunks/proxy-w1d0wmsn.js +33 -0
- package/dist/server/_chunks/router-k1reahfn.js +498 -0
- package/dist/server/_chunks/routes-2s277fc6.js +64 -0
- package/dist/server/_chunks/run-pvd8bs1t.js +696 -0
- package/dist/server/_chunks/server-mt4a994c.js +1835 -0
- package/dist/server/_chunks/tsconfig-defaults-1nb3308g.js +20 -0
- package/dist/server/_chunks/typegen-0qr0459b.js +35 -0
- package/dist/server/api/client-cache.js +14 -0
- package/dist/server/cli/index.js +378 -0
- package/dist/server/cli/start.js +137 -0
- package/dist/server/client/reference.js +16 -0
- package/dist/server/compat/next/cache.js +56 -0
- package/dist/server/compat/next/canonical-url.js +13 -0
- package/dist/server/compat/next/client-cache.js +57 -0
- package/dist/server/compat/next/client-navigation.js +232 -0
- package/dist/server/compat/next/client-only.js +1 -0
- package/dist/server/compat/next/client-script.js +178 -0
- package/dist/server/compat/next/client-server.js +34 -0
- package/dist/server/compat/next/config-headers.js +32 -0
- package/dist/server/compat/next/config-loader.js +25 -0
- package/dist/server/compat/next/config.js +10 -0
- package/dist/server/compat/next/constants.js +18 -0
- package/dist/server/compat/next/custom-server.js +10 -0
- package/dist/server/compat/next/dist/client/components/app-router-headers.js +48 -0
- package/dist/server/compat/next/dist/server/web/spec-extension/revalidate.js +27 -0
- package/dist/server/compat/next/dist/server/web/spec-extension/unstable-cache.js +25 -0
- package/dist/server/compat/next/dist/server/web/spec-extension/unstable-no-store.js +25 -0
- package/dist/server/compat/next/dynamic.js +32 -0
- package/dist/server/compat/next/error.js +112 -0
- package/dist/server/compat/next/font/cache.js +14 -0
- package/dist/server/compat/next/font/google.js +3 -0
- package/dist/server/compat/next/font/index.js +1 -0
- package/dist/server/compat/next/font/local.js +22 -0
- package/dist/server/compat/next/font/runtime-client.js +33 -0
- package/dist/server/compat/next/font/runtime.js +41 -0
- package/dist/server/compat/next/font/shared.js +22 -0
- package/dist/server/compat/next/form.js +129 -0
- package/dist/server/compat/next/head.js +10 -0
- package/dist/server/compat/next/headers.js +30 -0
- package/dist/server/compat/next/image/client.js +11 -0
- package/dist/server/compat/next/image/config.js +18 -0
- package/dist/server/compat/next/image/optimizer.js +25 -0
- package/dist/server/compat/next/image/patterns.js +12 -0
- package/dist/server/compat/next/image/props.js +16 -0
- package/dist/server/compat/next/image/shared.js +86 -0
- package/dist/server/compat/next/image/static-metadata.js +32 -0
- package/dist/server/compat/next/image/validate.js +8 -0
- package/dist/server/compat/next/image.js +59 -0
- package/dist/server/compat/next/index.js +1 -0
- package/dist/server/compat/next/legacy-image.js +78 -0
- package/dist/server/compat/next/legacy-request-apis.js +24 -0
- package/dist/server/compat/next/link-transform.js +14 -0
- package/dist/server/compat/next/link.js +14 -0
- package/dist/server/compat/next/navigation.js +190 -0
- package/dist/server/compat/next/offline.js +45 -0
- package/dist/server/compat/next/og.js +8 -0
- package/dist/server/compat/next/preferred-region.js +8 -0
- package/dist/server/compat/next/redirects.js +30 -0
- package/dist/server/compat/next/resource-hints.js +30 -0
- package/dist/server/compat/next/rewrites.js +41 -0
- package/dist/server/compat/next/root-params.js +121 -0
- package/dist/server/compat/next/route-state.js +23 -0
- package/dist/server/compat/next/router.js +103 -0
- package/dist/server/compat/next/script.js +37 -0
- package/dist/server/compat/next/server-only.js +1 -0
- package/dist/server/compat/next/server.js +131 -0
- package/dist/server/compat/next/svgr.js +14 -0
- package/dist/server/compat/next/telemetry.js +10 -0
- package/dist/server/compat/next/usage.js +13 -0
- package/dist/server/compat/next/user-agent.js +12 -0
- package/dist/server/compat/next/web-vitals.js +35 -0
- package/dist/server/compat/react/action-state.js +11 -0
- package/dist/server/compat/react/client-lite.js +83 -0
- package/dist/server/compat/react/client.js +131 -0
- package/dist/server/compat/react/compiler-runtime.js +42 -0
- package/dist/server/compat/react/dom-client.js +106 -0
- package/dist/server/compat/react/dom-react-server.js +37 -0
- package/dist/server/compat/react/dom-server.js +40 -0
- package/dist/server/compat/react/dom.js +52 -0
- package/dist/server/compat/react/hooks-extra.js +12 -0
- package/dist/server/compat/react/internals.js +14 -0
- package/dist/server/compat/react/parity.js +11 -0
- package/dist/server/compat/react/preact.js +113 -0
- package/dist/server/compat/react/react-server.js +133 -0
- package/dist/server/compat/react/router-shim.js +8 -0
- package/dist/server/compat/react/server-component-use.js +14 -0
- package/dist/server/compat/react/server-inserted-html.js +14 -0
- package/dist/server/compat/react/server.js +194 -0
- package/dist/server/compat/react/style-values.js +10 -0
- package/dist/server/compat/react/use.js +10 -0
- package/dist/server/compat/react/view-transition.js +12 -0
- package/dist/server/generation.json +1 -0
- package/package.json +4 -1
- package/src/api/dynamic.tsx +2 -0
- package/src/api/server.ts +24 -6
- package/src/cli/adapters/vercel-warm.ts +315 -61
- package/src/cli/adapters/vercel.ts +348 -71
- package/src/cli/build.ts +58 -33
- package/src/cli/index.ts +23 -10
- package/src/cli/serve/entry.ts +21 -159
- package/src/cli/serve/immutable.ts +31 -0
- package/src/cli/serve/pipeline.ts +82 -84
- package/src/cli/start.ts +3 -1
- package/src/client/chunk-fold.ts +19 -6
- package/src/client/react-tier.ts +3 -2
- package/src/client/reference-stub.ts +21 -2
- package/src/compat/actions/client-plugin.ts +4 -2
- package/src/compat/actions/config.ts +6 -5
- package/src/compat/actions/extensions.ts +2 -3
- package/src/compat/aliases.ts +8 -1
- package/src/compat/bundler/config.ts +88 -0
- package/src/compat/bundler/optimize-package-imports.ts +86 -26
- package/src/compat/bundler/relay-transform.ts +15 -3
- package/src/compat/cache-control.ts +16 -8
- package/src/compat/css/modules.ts +18 -5
- package/src/compat/lifecycle/error-funnel.ts +1 -1
- package/src/compat/lifecycle/instrumentation.ts +117 -24
- package/src/compat/lifecycle/testmode.ts +3 -2
- package/src/compat/next/config-loader.ts +137 -13
- package/src/compat/next/font/runtime.ts +22 -2
- package/src/compat/next/og.ts +3 -3
- package/src/compat/next/usage.ts +3 -2
- package/src/compat/pages/api.ts +83 -14
- package/src/compat/pages/router.ts +30 -14
- package/src/compat/register/bundler.ts +3 -1
- package/src/compat/register/middleware.ts +1 -1
- package/src/compat/register/otel.ts +5 -9
- package/src/compat/register/render.ts +3 -1
- package/src/compat/register/use-cache.ts +3 -1
- package/src/compat/release.ts +46 -0
- package/src/compat/segment/serve.ts +1 -1
- package/src/compat/validation/validate.ts +10 -7
- package/src/config.ts +51 -7
- package/src/css/assets.ts +213 -0
- package/src/css/build.ts +174 -238
- package/src/dev/server.ts +27 -8
- package/src/extensions.ts +4 -0
- package/src/render/hooks.ts +13 -0
- package/src/render/renderer.ts +52 -32
- package/src/request/context.ts +9 -0
- package/src/resolve/dynamic.ts +3 -1
- package/src/resolve/imports.ts +12 -52
- package/src/resolve/package-json.ts +56 -0
- package/src/resolve/scan-facts.ts +65 -0
- package/src/routing/href.ts +10 -3
- package/src/routing/match.ts +287 -0
- package/src/routing/metadata-files.ts +19 -5
- package/src/routing/proxy-config.ts +137 -0
- package/src/routing/proxy.ts +35 -134
- package/src/routing/routes.ts +22 -281
- package/src/runtime/fingerprint.ts +23 -8
- package/src/runtime/load.ts +170 -0
- package/src/runtime/loader.ts +215 -43
- package/src/runtime/module-cache.ts +13 -0
- package/src/runtime/modules.ts +370 -81
- package/src/runtime/prebundle.ts +81 -0
- package/src/runtime/production.ts +391 -0
- package/src/runtime/release-graph.ts +334 -0
- package/src/runtime/vendor-build.ts +724 -13
- package/src/runtime/vendor.ts +16 -3
- package/src/types.ts +2 -0
- package/src/utils/fs.ts +89 -3
|
@@ -13,7 +13,15 @@
|
|
|
13
13
|
// export / top-level await / dynamic import() and CJS+ESM authoring styles.
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
existsSync,
|
|
18
|
+
mkdirSync,
|
|
19
|
+
readFileSync,
|
|
20
|
+
readdirSync,
|
|
21
|
+
renameSync,
|
|
22
|
+
rmSync,
|
|
23
|
+
writeFileSync,
|
|
24
|
+
} from 'node:fs'
|
|
17
25
|
import { createRequire } from 'node:module'
|
|
18
26
|
import path from 'node:path'
|
|
19
27
|
import { pathToFileURL } from 'node:url'
|
|
@@ -43,6 +51,12 @@ const CONFIG_BASENAMES = [
|
|
|
43
51
|
]
|
|
44
52
|
|
|
45
53
|
let store: NextConfigObject = {}
|
|
54
|
+
let storeReleased = false
|
|
55
|
+
|
|
56
|
+
/** Whether the stored config came from a production release, which no consumer may re-read from source. */
|
|
57
|
+
export function nextConfigReleased(): boolean {
|
|
58
|
+
return storeReleased
|
|
59
|
+
}
|
|
46
60
|
|
|
47
61
|
/** The full loaded next.config object; empty until loadNextConfig runs. */
|
|
48
62
|
export function getNextConfig(): NextConfigObject {
|
|
@@ -188,19 +202,104 @@ async function importConfigModule(
|
|
|
188
202
|
return importConfigBundle(outfile, cjs)
|
|
189
203
|
}
|
|
190
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Compile the app's next.config into `outDir`, plus the cache handlers it names, so a production server
|
|
207
|
+
* imports neither from source. Names carry a content hash: a rebuilt release never reuses the previous
|
|
208
|
+
* generation's module instance. Paths are relative to the bundle, so the release can move.
|
|
209
|
+
*/
|
|
210
|
+
export async function emitNextConfigBundle(root: string, outDir: string) {
|
|
211
|
+
const configPath = findConfigPath(root)
|
|
212
|
+
if (!configPath) return undefined
|
|
213
|
+
const cjs = cjsConfigFormat(configPath, root)
|
|
214
|
+
const [file, ...handlerFiles] = await Promise.all([
|
|
215
|
+
hashedOutput(outDir, 'next.config', cjs ? '.cjs' : '.mjs', outfile =>
|
|
216
|
+
bundleConfig({ configPath, root, outDir, outfile, cjs, relocatable: true }),
|
|
217
|
+
),
|
|
218
|
+
...cacheHandlerFiles(store).map(([name, source]) =>
|
|
219
|
+
hashedOutput(outDir, name, '.cjs', outfile =>
|
|
220
|
+
build({
|
|
221
|
+
entryPoints: [source],
|
|
222
|
+
outfile,
|
|
223
|
+
bundle: true,
|
|
224
|
+
platform: 'node',
|
|
225
|
+
format: 'cjs',
|
|
226
|
+
target: 'esnext',
|
|
227
|
+
packages: 'external',
|
|
228
|
+
logLevel: 'silent',
|
|
229
|
+
}),
|
|
230
|
+
).then(output => [name, output] as const),
|
|
231
|
+
),
|
|
232
|
+
])
|
|
233
|
+
return { file, handlers: Object.fromEntries(handlerFiles as [string, string][]) }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Write through `emit` to a temporary name, then publish as `<name>.<content hash><ext>`. */
|
|
237
|
+
async function hashedOutput(
|
|
238
|
+
outDir: string,
|
|
239
|
+
name: string,
|
|
240
|
+
ext: string,
|
|
241
|
+
emit: (outfile: string) => Promise<unknown>,
|
|
242
|
+
) {
|
|
243
|
+
const temporary = path.join(outDir, `${name}.${process.pid}.tmp${ext}`)
|
|
244
|
+
await emit(temporary)
|
|
245
|
+
const hash = bun.hash(readFileSync(temporary)).toString(36)
|
|
246
|
+
const outfile = path.join(outDir, `${name}.${hash}${ext}`)
|
|
247
|
+
renameSync(temporary, outfile)
|
|
248
|
+
return outfile
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** The configured cache handler sources, by the name their release bundle takes. */
|
|
252
|
+
function cacheHandlerFiles(config: NextConfigObject): [string, string][] {
|
|
253
|
+
const handlers = config.cacheHandlers as Record<string, unknown> | undefined
|
|
254
|
+
return [
|
|
255
|
+
['cache-handler', config.cacheHandler],
|
|
256
|
+
[
|
|
257
|
+
'cache-handler-default',
|
|
258
|
+
handlers && typeof handlers === 'object' ? handlers.default : undefined,
|
|
259
|
+
],
|
|
260
|
+
].filter(
|
|
261
|
+
(entry): entry is [string, string] =>
|
|
262
|
+
typeof entry[1] === 'string' && path.isAbsolute(entry[1]) && existsSync(entry[1]),
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Point a released config's cache handlers at the bundles its build emitted. */
|
|
267
|
+
function releasedCacheHandlers(
|
|
268
|
+
config: NextConfigObject,
|
|
269
|
+
handlers: Record<string, string>,
|
|
270
|
+
): NextConfigObject {
|
|
271
|
+
const handler = handlers['cache-handler']
|
|
272
|
+
const fallback = handlers['cache-handler-default']
|
|
273
|
+
return {
|
|
274
|
+
...config,
|
|
275
|
+
...(handler ? { cacheHandler: handler } : {}),
|
|
276
|
+
...(fallback
|
|
277
|
+
? { cacheHandlers: { ...(config.cacheHandlers as object), default: fallback } }
|
|
278
|
+
: {}),
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const toPosix = (file: string) => file.split(path.sep).join('/')
|
|
283
|
+
|
|
191
284
|
function bundleConfig({
|
|
192
285
|
configPath,
|
|
193
286
|
root,
|
|
194
287
|
outDir,
|
|
195
288
|
outfile,
|
|
196
289
|
cjs,
|
|
290
|
+
relocatable = false,
|
|
197
291
|
}: {
|
|
198
292
|
configPath: string
|
|
199
293
|
root: string
|
|
200
294
|
outDir: string
|
|
201
295
|
outfile: string
|
|
202
296
|
cjs: boolean
|
|
297
|
+
/** A release bundle: locate the config from the bundle itself, never from the build machine's paths. */
|
|
298
|
+
relocatable?: boolean
|
|
203
299
|
}) {
|
|
300
|
+
const configUrl = relocatable
|
|
301
|
+
? `new URL(${JSON.stringify(toPosix(path.relative(outDir, configPath)))}, ${cjs ? '__pnextNodeRequire("node:url").pathToFileURL(__filename)' : 'import.meta.url'}).href`
|
|
302
|
+
: JSON.stringify(pathToFileURL(configPath).href)
|
|
204
303
|
return build({
|
|
205
304
|
entryPoints: [configPath],
|
|
206
305
|
outfile,
|
|
@@ -220,12 +319,20 @@ function bundleConfig({
|
|
|
220
319
|
? {
|
|
221
320
|
js:
|
|
222
321
|
'var __pnextNodeRequire = module.require.bind(module);\n' +
|
|
223
|
-
`var
|
|
322
|
+
`var __pnextConfigUrl = ${configUrl};\n` +
|
|
323
|
+
'var __pnextConfigFile = __pnextNodeRequire("node:url").fileURLToPath(__pnextConfigUrl);\n' +
|
|
324
|
+
'var __pnextConfigDir = __pnextNodeRequire("node:path").dirname(__pnextConfigFile);\n' +
|
|
325
|
+
'var require = __pnextNodeRequire("node:module").createRequire(__pnextConfigUrl);',
|
|
224
326
|
}
|
|
225
327
|
: {
|
|
226
328
|
js:
|
|
227
329
|
'import { createRequire as __pnextCreateRequire } from "node:module";\n' +
|
|
228
|
-
|
|
330
|
+
'import { fileURLToPath as __pnextFileURLToPath } from "node:url";\n' +
|
|
331
|
+
'import { dirname as __pnextDirname } from "node:path";\n' +
|
|
332
|
+
`const __pnextConfigUrl = ${configUrl};\n` +
|
|
333
|
+
'const __pnextConfigFile = __pnextFileURLToPath(__pnextConfigUrl);\n' +
|
|
334
|
+
'const __pnextConfigDir = __pnextDirname(__pnextConfigFile);\n' +
|
|
335
|
+
'const require = __pnextCreateRequire(__pnextConfigUrl);',
|
|
229
336
|
},
|
|
230
337
|
// Relative/absolute imports (incl. .ts/.cts/.mts/.cjs/.mjs and tsconfig
|
|
231
338
|
// `paths` aliases) are bundled in. esbuild reads `paths`/`extends` and
|
|
@@ -235,9 +342,9 @@ function bundleConfig({
|
|
|
235
342
|
// import.meta.url in ESM configs (node-api-esm) must point at the config's
|
|
236
343
|
// original location, not the emitted file under .pnext/config.
|
|
237
344
|
define: {
|
|
238
|
-
__dirname:
|
|
239
|
-
__filename:
|
|
240
|
-
'import.meta.url':
|
|
345
|
+
__dirname: '__pnextConfigDir',
|
|
346
|
+
__filename: '__pnextConfigFile',
|
|
347
|
+
'import.meta.url': '__pnextConfigUrl',
|
|
241
348
|
},
|
|
242
349
|
logLevel: 'silent',
|
|
243
350
|
sourcemap: false,
|
|
@@ -647,20 +754,36 @@ function validateDevIndicatorPosition(config: NextConfigObject): void {
|
|
|
647
754
|
*/
|
|
648
755
|
export async function loadNextConfig(
|
|
649
756
|
root: string,
|
|
650
|
-
options: {
|
|
757
|
+
options: {
|
|
758
|
+
dev?: boolean
|
|
759
|
+
serve?: boolean
|
|
760
|
+
warnings?: boolean
|
|
761
|
+
bundle?: string | null
|
|
762
|
+
handlers?: Record<string, string>
|
|
763
|
+
} = {},
|
|
651
764
|
): Promise<LoadNextConfigResult> {
|
|
652
|
-
|
|
765
|
+
// A production server names its compiled config, or null when the app had none.
|
|
766
|
+
storeReleased = options.bundle !== undefined
|
|
767
|
+
const configPath = options.bundle === undefined ? findConfigPath(root) : options.bundle
|
|
653
768
|
if (!configPath) {
|
|
654
769
|
setNextConfig({})
|
|
655
770
|
emitSessionStartedTelemetry({})
|
|
656
771
|
return { config: {}, overrides: {} }
|
|
657
772
|
}
|
|
773
|
+
if (options.bundle && !existsSync(options.bundle)) {
|
|
774
|
+
// The release names this file; serving defaults in its place would drop headers, redirects and more.
|
|
775
|
+
throw new Error(
|
|
776
|
+
`pnext: the build's compiled next.config (${options.bundle}) is missing. Run 'pnext build' again.`,
|
|
777
|
+
)
|
|
778
|
+
}
|
|
658
779
|
let exported: NextConfigExport
|
|
659
780
|
try {
|
|
660
|
-
exported =
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
781
|
+
exported = options.bundle
|
|
782
|
+
? await importConfigBundle(options.bundle, options.bundle.endsWith('.cjs'))
|
|
783
|
+
: await importConfigModule(configPath, root, {
|
|
784
|
+
dev: Boolean(options.dev),
|
|
785
|
+
serve: Boolean(options.serve),
|
|
786
|
+
})
|
|
664
787
|
} catch (error) {
|
|
665
788
|
// A next.config that pulls in an optional wrapper dependency not installed
|
|
666
789
|
// in this app (e.g. @next/mdx when deps are declared but not installed)
|
|
@@ -677,10 +800,11 @@ export async function loadNextConfig(
|
|
|
677
800
|
}
|
|
678
801
|
throw error
|
|
679
802
|
}
|
|
680
|
-
const
|
|
803
|
+
const resolved = normalizeCacheHandlerPath(
|
|
681
804
|
await resolveConfigExport(exported, Boolean(options.dev)),
|
|
682
805
|
root,
|
|
683
806
|
)
|
|
807
|
+
const config = options.bundle ? releasedCacheHandlers(resolved, options.handlers ?? {}) : resolved
|
|
684
808
|
// Internal build workers need the resolved config but must not surface a
|
|
685
809
|
// second copy of CLI warnings already emitted by the owning build process.
|
|
686
810
|
validateConfig(config, configPath, options.warnings !== false)
|
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url'
|
|
|
8
8
|
import type { ResolvedConfig } from '../../../config'
|
|
9
9
|
import { onExtensionHostReset, registerResponseFinalizers } from '../../../extensions'
|
|
10
10
|
import { getWorkUnit } from '../../../request/context'
|
|
11
|
+
import { servedRelease } from '../../../runtime/production'
|
|
11
12
|
import type { MetadataLink } from '../../../types'
|
|
12
13
|
import { persistFont, persistedFont } from './cache'
|
|
13
14
|
import { escapeRegex } from '../../../utils/code'
|
|
@@ -383,7 +384,23 @@ function registerFont(definition: FontDefinition) {
|
|
|
383
384
|
fontStorage.getStore()?.fonts.set(definition.key, definition)
|
|
384
385
|
}
|
|
385
386
|
|
|
386
|
-
|
|
387
|
+
/** Every declared font, resolved for a release (its files emitted under the out dir). */
|
|
388
|
+
export async function releaseFonts(config: ResolvedConfig): Promise<Record<string, ResolvedFont>> {
|
|
389
|
+
const fonts: Record<string, ResolvedFont> = {}
|
|
390
|
+
for (const font of declaredFonts.values()) {
|
|
391
|
+
const { sources: _sources, ...resolved } = await resolveFont(font, { config })
|
|
392
|
+
fonts[font.key] = resolved
|
|
393
|
+
}
|
|
394
|
+
return fonts
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async function resolveFont(
|
|
398
|
+
definition: FontDefinition,
|
|
399
|
+
context: FontResolveContext,
|
|
400
|
+
): Promise<ResolvedFont> {
|
|
401
|
+
// A production release serves the build's resolution: font sources are not read at request time.
|
|
402
|
+
const released = servedRelease()?.fonts?.[definition.key]
|
|
403
|
+
if (released) return released
|
|
387
404
|
const key = `${context.config.outPath}:${context.dev ? 'dev' : 'build'}:${definition.key}`
|
|
388
405
|
let resolved = resolvedFontCache.get(key)
|
|
389
406
|
if (!resolved) {
|
|
@@ -506,7 +523,10 @@ async function resolveLocalFont(
|
|
|
506
523
|
? false
|
|
507
524
|
: true
|
|
508
525
|
const preload = request.options.preload !== false
|
|
509
|
-
const sources = normalizeLocalSources(
|
|
526
|
+
const sources = normalizeLocalSources(
|
|
527
|
+
request.options,
|
|
528
|
+
request.callerFile && path.resolve(context.config.root, request.callerFile),
|
|
529
|
+
)
|
|
510
530
|
const emitted = await Promise.all(
|
|
511
531
|
sources.map(async source => ({
|
|
512
532
|
...source,
|
package/src/compat/next/og.ts
CHANGED
|
@@ -134,13 +134,13 @@ async function renderWithSatori(
|
|
|
134
134
|
element: unknown,
|
|
135
135
|
options: ImageResponseOptions,
|
|
136
136
|
): Promise<Uint8Array | undefined> {
|
|
137
|
+
// Fonts first: without one satori cannot lay out text, so loading it would be wasted work.
|
|
138
|
+
const fonts = normalizeFonts(options.fonts)
|
|
139
|
+
if (fonts.length === 0) return undefined
|
|
137
140
|
const satori = await loadOptional<SatoriModule>('satori')
|
|
138
141
|
const resvg = await loadOptional<ResvgModule>('@resvg/resvg-js')
|
|
139
142
|
if (!satori || !resvg) return undefined
|
|
140
143
|
|
|
141
|
-
const fonts = normalizeFonts(options.fonts)
|
|
142
|
-
if (fonts.length === 0) return undefined
|
|
143
|
-
|
|
144
144
|
const width = dimension(options.width, DEFAULT_WIDTH, 'width')
|
|
145
145
|
const height = dimension(options.height, DEFAULT_HEIGHT, 'height')
|
|
146
146
|
|
package/src/compat/next/usage.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, readdirSync,
|
|
1
|
+
import { existsSync, readdirSync, type Dirent } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { readSourceSync } from '../../resolve/source-text'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Build-time app facts that let compat drop code an app cannot reach. The fact here: whether any
|
|
@@ -95,7 +96,7 @@ function scan(dir: string, match: (source: string) => boolean): boolean {
|
|
|
95
96
|
if (!sourceFile.test(entry.name)) continue
|
|
96
97
|
let source: string
|
|
97
98
|
try {
|
|
98
|
-
source =
|
|
99
|
+
source = readSourceSync(file)
|
|
99
100
|
} catch {
|
|
100
101
|
continue
|
|
101
102
|
}
|
package/src/compat/pages/api.ts
CHANGED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
// shims plus the API-route and data-fetch (`/_next/data`) dispatch. Registered by
|
|
3
3
|
// ../register/pages-api.ts, which wires the exports below into the extension registries.
|
|
4
4
|
|
|
5
|
-
import { existsSync, readFileSync } from 'node:fs'
|
|
5
|
+
import { type Dirent, existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
6
6
|
import { Writable } from 'node:stream'
|
|
7
7
|
import path from 'node:path'
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
devServerModuleHref,
|
|
10
|
+
pagesApiBundleTargetForRuntime,
|
|
11
|
+
serverBundleTargetForRuntime,
|
|
12
|
+
} from '../../runtime/load'
|
|
13
|
+
import { productionRelease } from '../../runtime/production'
|
|
14
|
+
import type { ResolvedConfig } from '../../config'
|
|
10
15
|
import { getRequestExtensions, type RequestInterceptor } from '../../extensions'
|
|
11
16
|
import { getRequestRuntime } from '../../routing/request-environment'
|
|
12
|
-
import { selectRouteForRequest } from '../../routing/
|
|
13
|
-
import { serverBundleTargetForRuntime } from '../../runtime/loader'
|
|
17
|
+
import { selectRouteForRequest } from '../../routing/match'
|
|
14
18
|
import { setWorkUnitRoute } from '../../request/context'
|
|
15
19
|
import { canonicalUrlHref } from '../next/canonical-url'
|
|
16
20
|
import { markPathRevalidated, normalizeRevalidatePath } from '../cache/revalidate'
|
|
@@ -65,7 +69,7 @@ export const pagesDataInterceptor: RequestInterceptor = async request => {
|
|
|
65
69
|
return dataResponse({ notFound: true }, 404, url)
|
|
66
70
|
}
|
|
67
71
|
const route = selection.route
|
|
68
|
-
if (!isMaterializedPagesRoute(route.file)) return undefined
|
|
72
|
+
if (!isMaterializedPagesRoute(runtime.config, route.file)) return undefined
|
|
69
73
|
|
|
70
74
|
const module = (await import(
|
|
71
75
|
await devServerModuleHref(
|
|
@@ -150,8 +154,19 @@ function dataResponse(body: Record<string, unknown>, status: number, url: URL):
|
|
|
150
154
|
|
|
151
155
|
// The materialized hybrid app hosts BOTH routers; only pages wrappers
|
|
152
156
|
// re-export from source-pages/, so sniff the (tiny, generated) wrapper body —
|
|
153
|
-
// app routes must never serve _next/data.
|
|
154
|
-
|
|
157
|
+
// app routes must never serve _next/data. A release carries the answer.
|
|
158
|
+
const releasedPagesRoutes = new WeakMap<object, Set<string>>()
|
|
159
|
+
|
|
160
|
+
export function isMaterializedPagesRoute(config: ResolvedConfig, file: string): boolean {
|
|
161
|
+
const released = productionRelease(config)?.compat?.pagesRoutes
|
|
162
|
+
if (released) {
|
|
163
|
+
let routes = releasedPagesRoutes.get(released)
|
|
164
|
+
if (!routes) {
|
|
165
|
+
routes = new Set(released.map(route => path.join(config.root, route)))
|
|
166
|
+
releasedPagesRoutes.set(released, routes)
|
|
167
|
+
}
|
|
168
|
+
return routes.has(file)
|
|
169
|
+
}
|
|
155
170
|
if (!file.split(path.sep).join('/').includes('pnext-pages-compat/')) return false
|
|
156
171
|
try {
|
|
157
172
|
return readFileSync(file, 'utf8').includes('source-pages/')
|
|
@@ -160,6 +175,29 @@ function isMaterializedPagesRoute(file: string): boolean {
|
|
|
160
175
|
}
|
|
161
176
|
}
|
|
162
177
|
|
|
178
|
+
/** The materialized pages-router wrappers of a pages-compat app, root-relative, for its release. */
|
|
179
|
+
export function pagesRouteFacts(config: ResolvedConfig): string[] {
|
|
180
|
+
if (!config.appPath.includes('pnext-pages-compat')) return []
|
|
181
|
+
const routes: string[] = []
|
|
182
|
+
const visit = (dir: string) => {
|
|
183
|
+
let entries: Dirent[]
|
|
184
|
+
try {
|
|
185
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
186
|
+
} catch {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
const file = path.join(dir, entry.name)
|
|
191
|
+
if (entry.isDirectory()) visit(file)
|
|
192
|
+
else if (isMaterializedPagesRoute(config, file)) {
|
|
193
|
+
routes.push(path.relative(config.root, file).split(path.sep).join('/'))
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
visit(config.appPath)
|
|
198
|
+
return routes
|
|
199
|
+
}
|
|
200
|
+
|
|
163
201
|
function pagesDataRequestShim(request: Request, url: URL) {
|
|
164
202
|
return {
|
|
165
203
|
method: request.method,
|
|
@@ -194,11 +232,16 @@ function pagesDataResponseShim() {
|
|
|
194
232
|
|
|
195
233
|
export const pagesApiInterceptor: RequestInterceptor = async (request, { config }) => {
|
|
196
234
|
const url = new URL(request.url)
|
|
197
|
-
const
|
|
235
|
+
const released = productionRelease(config)?.compat?.pagesApi
|
|
236
|
+
const exists = released
|
|
237
|
+
? (candidate: string) => path.relative(config.root, candidate) in released
|
|
238
|
+
: existsSync
|
|
239
|
+
const file = pagesApiFile(config.root, url.pathname, exists)
|
|
198
240
|
if (!file) return undefined
|
|
241
|
+
const runtime = released ? released[path.relative(config.root, file)] : apiRuntime(file)
|
|
199
242
|
const imported = (await import(
|
|
200
243
|
await devServerModuleHref(config, file, 'build', {
|
|
201
|
-
conditionTarget: pagesApiBundleTargetForRuntime(
|
|
244
|
+
conditionTarget: pagesApiBundleTargetForRuntime(runtime),
|
|
202
245
|
})
|
|
203
246
|
)) as PagesApiModule
|
|
204
247
|
if (typeof imported.default !== 'function') return undefined
|
|
@@ -208,7 +251,7 @@ export const pagesApiInterceptor: RequestInterceptor = async (request, { config
|
|
|
208
251
|
// nextUrl, text() — Request prototype getters a spread would lose); node
|
|
209
252
|
// handlers get the IncomingMessage-shaped shim.
|
|
210
253
|
const handlerRequest =
|
|
211
|
-
|
|
254
|
+
runtime === 'edge' ? toNextRequest(request) : createPagesApiRequest(request, url)
|
|
212
255
|
const finished = withPagesApiHandlerSpan(url.pathname, () =>
|
|
213
256
|
Promise.resolve(imported.default!(handlerRequest, responder.res)),
|
|
214
257
|
).catch(error => responder.fail(error))
|
|
@@ -226,7 +269,33 @@ export const pagesApiInterceptor: RequestInterceptor = async (request, { config
|
|
|
226
269
|
|
|
227
270
|
const STREAM_STARTED = Symbol('pnext.pagesApiStreamStarted')
|
|
228
271
|
|
|
229
|
-
|
|
272
|
+
/** Every pages API source with its runtime (`nodejs` unless declared), root-relative: what a release serves. */
|
|
273
|
+
export function pagesApiFacts(root: string): Record<string, string> {
|
|
274
|
+
const facts: Record<string, string> = {}
|
|
275
|
+
const visit = (dir: string) => {
|
|
276
|
+
let entries: Dirent[]
|
|
277
|
+
try {
|
|
278
|
+
entries = readdirSync(dir, { withFileTypes: true })
|
|
279
|
+
} catch {
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
for (const entry of entries) {
|
|
283
|
+
const file = path.join(dir, entry.name)
|
|
284
|
+
if (entry.isDirectory()) visit(file)
|
|
285
|
+
else if (pageExtensions.some(ext => entry.name.endsWith(`.${ext}`))) {
|
|
286
|
+
facts[path.relative(root, file)] = apiRuntime(file) ?? 'nodejs'
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
visit(path.join(root, 'pages', 'api'))
|
|
291
|
+
return facts
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function pagesApiFile(
|
|
295
|
+
root: string,
|
|
296
|
+
pathname: string,
|
|
297
|
+
exists: (file: string) => boolean,
|
|
298
|
+
): string | undefined {
|
|
230
299
|
if (pathname !== '/api' && !pathname.startsWith('/api/')) return undefined
|
|
231
300
|
let relative: string
|
|
232
301
|
try {
|
|
@@ -239,9 +308,9 @@ function pagesApiFile(root: string, pathname: string): string | undefined {
|
|
|
239
308
|
if (base !== baseDir && !base.startsWith(`${baseDir}${path.sep}`)) return undefined
|
|
240
309
|
for (const ext of pageExtensions) {
|
|
241
310
|
const file = `${base}.${ext}`
|
|
242
|
-
if (
|
|
311
|
+
if (exists(file)) return file
|
|
243
312
|
const index = path.join(base, `index.${ext}`)
|
|
244
|
-
if (
|
|
313
|
+
if (exists(index)) return index
|
|
245
314
|
}
|
|
246
315
|
return undefined
|
|
247
316
|
}
|
|
@@ -57,7 +57,10 @@ function isAppConvention(name: string): boolean {
|
|
|
57
57
|
* outside the source app preserves native App routes and avoids hybrid-route
|
|
58
58
|
* conflict validation; linked sources and node_modules keep normal resolution.
|
|
59
59
|
*/
|
|
60
|
-
export async function materializePagesApp(
|
|
60
|
+
export async function materializePagesApp(
|
|
61
|
+
root: string,
|
|
62
|
+
base = path.join(tmpdir(), 'pnext-pages-compat'),
|
|
63
|
+
): Promise<string | undefined> {
|
|
61
64
|
const pagesPath = path.join(root, 'pages')
|
|
62
65
|
if (!existsSync(pagesPath)) return undefined
|
|
63
66
|
|
|
@@ -71,11 +74,7 @@ export async function materializePagesApp(root: string): Promise<string | undefi
|
|
|
71
74
|
// one shared directory let one process scan a half-materialized tree ("page.js doesn't have a root
|
|
72
75
|
// layout" on a build that passes on the next run). Naming the directory after what goes in it makes
|
|
73
76
|
// the second writer a no-op, and staging + rename means a reader sees the whole tree or nothing.
|
|
74
|
-
const generatedRoot = path.join(
|
|
75
|
-
tmpdir(),
|
|
76
|
-
'pnext-pages-compat',
|
|
77
|
-
materializationKey(root, nativeApp, pages, apiRoutes),
|
|
78
|
-
)
|
|
77
|
+
const generatedRoot = path.join(base, materializationKey(root, nativeApp, pages, apiRoutes))
|
|
79
78
|
const appPath = path.join(generatedRoot, 'app')
|
|
80
79
|
if (existsSync(appPath)) return appPath
|
|
81
80
|
|
|
@@ -88,10 +87,20 @@ export async function materializePagesApp(root: string): Promise<string | undefi
|
|
|
88
87
|
await rm(staging, { recursive: true, force: true })
|
|
89
88
|
if (!existsSync(appPath)) throw new Error(`pnext: could not materialize ${root}/pages`)
|
|
90
89
|
}
|
|
91
|
-
void sweepStaleMaterializations(
|
|
90
|
+
void sweepStaleMaterializations(
|
|
91
|
+
base,
|
|
92
|
+
isInsideApp(root, generatedRoot) ? path.basename(generatedRoot) : undefined,
|
|
93
|
+
)
|
|
92
94
|
return appPath
|
|
93
95
|
}
|
|
94
96
|
|
|
97
|
+
const isInsideApp = (root: string, dir: string) => !path.relative(root, dir).startsWith('..')
|
|
98
|
+
|
|
99
|
+
/** A materialized tree's link to app source: relative when the tree lives inside the app. */
|
|
100
|
+
function materializedLinkTarget(root: string, generatedRoot: string, target: string) {
|
|
101
|
+
return isInsideApp(root, generatedRoot) ? path.relative(generatedRoot, target) : target
|
|
102
|
+
}
|
|
103
|
+
|
|
95
104
|
/**
|
|
96
105
|
* Everything the materialized tree is derived from: the pages facts (already parsed out of the
|
|
97
106
|
* sources), the native app tree's shape, and this module — whose `wrapperSource` writes the files.
|
|
@@ -134,10 +143,12 @@ function materializationKey(
|
|
|
134
143
|
// a dev server holds its materialized tree open for as long as it runs.
|
|
135
144
|
const STALE_MATERIALIZATION_MS = 7 * 24 * 60 * 60_000
|
|
136
145
|
|
|
137
|
-
|
|
146
|
+
// A build's own output keeps only its `current` tree: nothing else serves from it.
|
|
147
|
+
async function sweepStaleMaterializations(base: string, current?: string) {
|
|
138
148
|
try {
|
|
139
|
-
const cutoff = Date.now() - STALE_MATERIALIZATION_MS
|
|
149
|
+
const cutoff = current ? Infinity : Date.now() - STALE_MATERIALIZATION_MS
|
|
140
150
|
for (const entry of await readdir(base)) {
|
|
151
|
+
if (current && (entry === current || entry.endsWith('.staging'))) continue
|
|
141
152
|
const dir = path.join(base, entry)
|
|
142
153
|
const info = statSync(dir, { throwIfNoEntry: false })
|
|
143
154
|
if (info && info.mtimeMs < cutoff) await rm(dir, { recursive: true, force: true })
|
|
@@ -158,23 +169,28 @@ async function buildMaterializedApp(
|
|
|
158
169
|
const appPath = path.join(generatedRoot, 'app')
|
|
159
170
|
const sourcePages = path.join(generatedRoot, 'source-pages')
|
|
160
171
|
const sourceApp = path.join(generatedRoot, 'source-app')
|
|
172
|
+
// Inside a build's output the links stay relative, so a moved build names no build-machine path.
|
|
173
|
+
const link = (target: string, at: string) =>
|
|
174
|
+
symlink(materializedLinkTarget(root, generatedRoot, target), at, 'dir')
|
|
161
175
|
|
|
162
176
|
await rm(generatedRoot, { recursive: true, force: true })
|
|
163
177
|
await mkdir(appPath, { recursive: true })
|
|
164
|
-
await
|
|
178
|
+
await link(pagesPath, sourcePages)
|
|
179
|
+
// Inside the app, packages resolve up to the app's own node_modules under their real paths.
|
|
165
180
|
const nodeModules = path.join(root, 'node_modules')
|
|
166
|
-
if (existsSync(nodeModules))
|
|
167
|
-
await
|
|
181
|
+
if (existsSync(nodeModules) && !isInsideApp(root, generatedRoot)) {
|
|
182
|
+
await link(nodeModules, path.join(generatedRoot, 'node_modules'))
|
|
183
|
+
}
|
|
168
184
|
|
|
169
185
|
// Static assets referenced from materialized routes (e.g. an app route's
|
|
170
186
|
// `new URL('../../public/vercel.png', import.meta.url)` or a `public/*` asset
|
|
171
187
|
// import) resolve relative to the generated root; without the symlink the
|
|
172
188
|
// bundler can't find them and the route 500s.
|
|
173
189
|
const publicDir = path.join(root, 'public')
|
|
174
|
-
if (existsSync(publicDir)) await
|
|
190
|
+
if (existsSync(publicDir)) await link(publicDir, path.join(generatedRoot, 'public'))
|
|
175
191
|
|
|
176
192
|
if (existsSync(nativeApp)) {
|
|
177
|
-
await
|
|
193
|
+
await link(nativeApp, sourceApp)
|
|
178
194
|
await materializeNativeApp(sourceApp, appPath)
|
|
179
195
|
}
|
|
180
196
|
if (!existsSync(path.join(appPath, 'layout.js'))) {
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
setResolveExtensions,
|
|
47
47
|
setTsConfigPath,
|
|
48
48
|
} from '../../resolve/imports'
|
|
49
|
-
import { setServerBundleConditions } from '../../runtime/
|
|
49
|
+
import { setServerBundleConditions } from '../../runtime/load'
|
|
50
50
|
import { escapeRegex, specifierSniffTokens } from '../../utils/code'
|
|
51
51
|
import {
|
|
52
52
|
cacheComponentsEnabled,
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
relayCompilerConfig,
|
|
60
60
|
removeConsoleTargets,
|
|
61
61
|
resolveExtensions,
|
|
62
|
+
builtInServerExternalPackages,
|
|
62
63
|
serverExternalPackages,
|
|
63
64
|
swcEnvUsageMode,
|
|
64
65
|
tsconfigPath,
|
|
@@ -160,6 +161,7 @@ export function registerBundlerExtensions(config: ResolvedConfig): void {
|
|
|
160
161
|
// Linked workspace packages are deliberately absent: esbuild resolves their
|
|
161
162
|
// symlinks to real paths, so they never reach a filter as node_modules.
|
|
162
163
|
transpiled: () => [...transpile].filter(name => !externals.has(name)),
|
|
164
|
+
releaseExternal: name => builtInServerExternalPackages().has(name) && !transpile.has(name),
|
|
163
165
|
esmExternals,
|
|
164
166
|
})
|
|
165
167
|
setExternalLoadResolver(({ root, fromFile, specifier, target }) => {
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type ProxyConfig,
|
|
15
15
|
type ProxyModule,
|
|
16
16
|
} from '../../routing/proxy'
|
|
17
|
-
import { devServerModuleHref } from '../../runtime/
|
|
17
|
+
import { devServerModuleHref } from '../../runtime/load'
|
|
18
18
|
import { pathToFileHref, type ResolvedConfig } from '../../config'
|
|
19
19
|
import { toPosixPath } from '../../utils/fs'
|
|
20
20
|
import type { RouteManifestEntry } from '../../types'
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
// opentelemetry suite. Everything is inert unless next compat is on AND @opentelemetry/api resolves from
|
|
15
15
|
// the app.
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
import path from 'node:path'
|
|
17
|
+
import { isMaterializedPagesRoute } from '../pages/api'
|
|
19
18
|
import type { ResolvedConfig } from '../../config'
|
|
20
19
|
import type { RouteManifestEntry } from '../../types'
|
|
21
20
|
import {
|
|
@@ -30,7 +29,7 @@ import {
|
|
|
30
29
|
} from '../../extensions'
|
|
31
30
|
import { nextCompatEnabled } from '../aliases'
|
|
32
31
|
import { getRequestRuntime } from '../../routing/request-environment'
|
|
33
|
-
import { selectRouteForRequest } from '../../routing/
|
|
32
|
+
import { selectRouteForRequest } from '../../routing/match'
|
|
34
33
|
import { queueAfterTask } from '../../request/context'
|
|
35
34
|
import {
|
|
36
35
|
getDocumentScriptExtensions,
|
|
@@ -247,14 +246,11 @@ function routeEntryFor(routePattern: string): RouteManifestEntry | undefined {
|
|
|
247
246
|
// stable generator-owned marker; cache per file (routes don't change at start).
|
|
248
247
|
const pagesCompatFileCache = new Map<string, boolean>()
|
|
249
248
|
function isPagesCompatEntry(entry: RouteManifestEntry | undefined): boolean {
|
|
250
|
-
|
|
249
|
+
const config = getRequestRuntime()?.config
|
|
250
|
+
if (!entry || !config) return false
|
|
251
251
|
let cached = pagesCompatFileCache.get(entry.file)
|
|
252
252
|
if (cached === undefined) {
|
|
253
|
-
|
|
254
|
-
cached = readFileSync(entry.file, 'utf8').includes('source-pages/')
|
|
255
|
-
} catch {
|
|
256
|
-
cached = false
|
|
257
|
-
}
|
|
253
|
+
cached = isMaterializedPagesRoute(config, entry.file)
|
|
258
254
|
pagesCompatFileCache.set(entry.file, cached)
|
|
259
255
|
}
|
|
260
256
|
return cached
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
setStreamRouteExtensions,
|
|
33
33
|
setSuspenseExtensions,
|
|
34
34
|
} from '../../render/hooks'
|
|
35
|
-
import { withAssetPrefix } from '../../css/
|
|
35
|
+
import { withAssetPrefix } from '../../css/assets'
|
|
36
36
|
import { polyfillsChunkPath } from '../bundler/polyfill'
|
|
37
37
|
import { nextRouterShimScript } from '../react/router-shim'
|
|
38
38
|
import { renderPartytownHeadScripts } from '../next/script'
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
fontPreloadDomScript,
|
|
44
44
|
fontSizeAdjustMeta,
|
|
45
45
|
prewarmFonts,
|
|
46
|
+
releaseFonts,
|
|
46
47
|
runWithFontScope,
|
|
47
48
|
} from '../next/font/runtime'
|
|
48
49
|
import {
|
|
@@ -117,6 +118,7 @@ export function registerRenderExtensions(config: ResolvedConfig): void {
|
|
|
117
118
|
runWithFontScope,
|
|
118
119
|
collectFontAssets: fontAssets,
|
|
119
120
|
prewarmFontAssets: prewarmFonts,
|
|
121
|
+
releaseFonts,
|
|
120
122
|
})
|
|
121
123
|
setDocumentScriptExtensions({
|
|
122
124
|
documentBodyScripts: cfg =>
|