@wular/pnext 0.1.6 → 0.1.8
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-pycbtjt1.js +46 -0
- package/dist/server/_chunks/analyze-4d9e00g9.js +449 -0
- package/dist/server/_chunks/boot-bhemypxz.js +38 -0
- package/dist/server/_chunks/build-c991dv65.js +86 -0
- package/dist/server/_chunks/build-kh5e3x48.js +3341 -0
- package/dist/server/_chunks/build-tier-0a96kmx3.js +2456 -0
- package/dist/server/_chunks/compile-040r79gj.js +22 -0
- package/dist/server/_chunks/config-0tvz03a6.js +24 -0
- package/dist/server/_chunks/config-27rj7830.js +36 -0
- package/dist/server/_chunks/create-9rvwshsp.js +135 -0
- package/dist/server/_chunks/dev-ex9exynt.js +220 -0
- package/dist/server/_chunks/esbuild-yyktfrtt.js +15 -0
- package/dist/server/_chunks/generate-myw9hef7.js +22 -0
- package/dist/server/_chunks/index-0r5eqfc7.js +279 -0
- package/dist/server/_chunks/index-0yjv5v47.js +105 -0
- package/dist/server/_chunks/index-11xpbfej.js +17 -0
- package/dist/server/_chunks/index-141vcwk6.js +109 -0
- package/dist/server/_chunks/index-1pzm1hdy.js +4130 -0
- package/dist/server/_chunks/index-29pq0kax.js +590 -0
- package/dist/server/_chunks/index-2vz0wzdn.js +59 -0
- package/dist/server/_chunks/index-2wfv4kvx.js +182 -0
- package/dist/server/_chunks/index-38xpdxwd.js +194 -0
- package/dist/server/_chunks/index-3e075kny.js +128 -0
- package/dist/server/_chunks/index-3ycf5ckv.js +472 -0
- package/dist/server/_chunks/index-4399bvd2.js +18 -0
- package/dist/server/_chunks/index-4da76t62.js +297 -0
- package/dist/server/_chunks/index-4t92hdxd.js +172 -0
- package/dist/server/_chunks/index-4zzebszd.js +10 -0
- package/dist/server/_chunks/index-52k54ykj.js +95 -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-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-71885zaq.js +284 -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-8ct7rqy3.js +198 -0
- package/dist/server/_chunks/index-8kcxdfya.js +206 -0
- package/dist/server/_chunks/index-9ch50vtq.js +18 -0
- package/dist/server/_chunks/index-9f49w76j.js +213 -0
- package/dist/server/_chunks/index-9hz3s0m8.js +851 -0
- package/dist/server/_chunks/index-akkrvjn6.js +535 -0
- package/dist/server/_chunks/index-b6wtkdp4.js +24 -0
- package/dist/server/_chunks/index-b7j671dj.js +207 -0
- package/dist/server/_chunks/index-bcws5eh2.js +35 -0
- package/dist/server/_chunks/index-bzd2ef7t.js +10 -0
- package/dist/server/_chunks/index-cjgqs13g.js +65 -0
- package/dist/server/_chunks/index-ckkdpht5.js +41 -0
- package/dist/server/_chunks/index-cn86z041.js +296 -0
- package/dist/server/_chunks/index-cvr4z22m.js +92 -0
- package/dist/server/_chunks/index-cyfednwb.js +87 -0
- package/dist/server/_chunks/index-d158y67j.js +152 -0
- package/dist/server/_chunks/index-d1jsmhhk.js +955 -0
- package/dist/server/_chunks/index-dg0f05zs.js +199 -0
- package/dist/server/_chunks/index-dgr390q7.js +9438 -0
- package/dist/server/_chunks/index-drwv61cc.js +185 -0
- package/dist/server/_chunks/index-e3a6yx84.js +18 -0
- package/dist/server/_chunks/index-e88tyv35.js +45 -0
- package/dist/server/_chunks/index-en7n9f7m.js +22 -0
- package/dist/server/_chunks/index-entm8bmz.js +28 -0
- package/dist/server/_chunks/index-evhd413j.js +57 -0
- package/dist/server/_chunks/index-ezgj0tr1.js +88 -0
- package/dist/server/_chunks/index-fjv96re1.js +5295 -0
- package/dist/server/_chunks/index-fn1chkkd.js +214 -0
- package/dist/server/_chunks/index-fp8r0sqs.js +64 -0
- package/dist/server/_chunks/index-gcmj16xy.js +92 -0
- package/dist/server/_chunks/index-gkndxkxf.js +736 -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-hdbwtdv2.js +352 -0
- package/dist/server/_chunks/index-he6n2bvp.js +1270 -0
- package/dist/server/_chunks/index-j0m1jxqa.js +1253 -0
- package/dist/server/_chunks/index-j2zrcp1k.js +66 -0
- package/dist/server/_chunks/index-j681c48a.js +2530 -0
- package/dist/server/_chunks/index-jbj7ehef.js +50 -0
- package/dist/server/_chunks/index-jcsjq3d1.js +20 -0
- package/dist/server/_chunks/index-jks0x0a1.js +14 -0
- package/dist/server/_chunks/index-jn9rbhrx.js +1599 -0
- package/dist/server/_chunks/index-jt8e1pje.js +12 -0
- package/dist/server/_chunks/index-kdhxpwyq.js +483 -0
- package/dist/server/_chunks/index-knp0s912.js +7 -0
- package/dist/server/_chunks/index-kqyy1d4f.js +50 -0
- package/dist/server/_chunks/index-kresp2n8.js +445 -0
- package/dist/server/_chunks/index-m63czgq5.js +590 -0
- package/dist/server/_chunks/index-ms9v9tnx.js +31 -0
- package/dist/server/_chunks/index-n4hxpng2.js +11 -0
- package/dist/server/_chunks/index-n629dy38.js +1071 -0
- package/dist/server/_chunks/index-nf3df4jy.js +266 -0
- package/dist/server/_chunks/index-nqh3xhdc.js +57 -0
- package/dist/server/_chunks/index-pym1cezq.js +39 -0
- package/dist/server/_chunks/index-q3r9z8mq.js +213 -0
- package/dist/server/_chunks/index-q738kkhj.js +100 -0
- package/dist/server/_chunks/index-qcrvt6er.js +112 -0
- package/dist/server/_chunks/index-qjj6gddn.js +54 -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-rgj5mx84.js +101 -0
- package/dist/server/_chunks/index-rjegyfx4.js +1435 -0
- package/dist/server/_chunks/index-rt9qq1va.js +4 -0
- package/dist/server/_chunks/index-rthrsed5.js +356 -0
- package/dist/server/_chunks/index-sq3kvz3g.js +200 -0
- package/dist/server/_chunks/index-stbk3jhn.js +31 -0
- package/dist/server/_chunks/index-tf4j19ge.js +130 -0
- package/dist/server/_chunks/index-tgcakdxc.js +125 -0
- package/dist/server/_chunks/index-tgsfjmep.js +74 -0
- package/dist/server/_chunks/index-tq1f96v7.js +51 -0
- package/dist/server/_chunks/index-tq3b39a5.js +107 -0
- package/dist/server/_chunks/index-typcbcsv.js +819 -0
- package/dist/server/_chunks/index-v1ags1na.js +132 -0
- package/dist/server/_chunks/index-vmr35jjw.js +170 -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-wm36rj4v.js +311 -0
- package/dist/server/_chunks/index-x0y005nh.js +283 -0
- package/dist/server/_chunks/index-x2m17v9c.js +6166 -0
- package/dist/server/_chunks/index-x3yn4ywm.js +22 -0
- package/dist/server/_chunks/index-x9c55pzy.js +871 -0
- package/dist/server/_chunks/index-xh1nbk46.js +1299 -0
- package/dist/server/_chunks/index-xvj80c3a.js +30 -0
- package/dist/server/_chunks/index-xywjg2vc.js +100 -0
- package/dist/server/_chunks/index-y4d02znn.js +66 -0
- package/dist/server/_chunks/index-y7mzvq2q.js +110 -0
- package/dist/server/_chunks/index-ye9hmsvf.js +47 -0
- package/dist/server/_chunks/index-yhxkxghe.js +56 -0
- package/dist/server/_chunks/index-yrjkqtfj.js +86 -0
- package/dist/server/_chunks/index-z88qv16t.js +11 -0
- package/dist/server/_chunks/index-zfrwq472.js +154 -0
- package/dist/server/_chunks/index-zqpqyg8p.js +666 -0
- package/dist/server/_chunks/loader-5en7wpt5.js +80 -0
- package/dist/server/_chunks/modules-t92favpj.js +76 -0
- package/dist/server/_chunks/pipeline-4t9q43ht.js +836 -0
- package/dist/server/_chunks/production-385bw8df.js +55 -0
- package/dist/server/_chunks/proxy-config-60gprphe.js +23 -0
- package/dist/server/_chunks/proxy-gfhmktbk.js +35 -0
- package/dist/server/_chunks/router-xnrva921.js +501 -0
- package/dist/server/_chunks/routes-ht868pdc.js +66 -0
- package/dist/server/_chunks/run-d40d63ag.js +697 -0
- package/dist/server/_chunks/server-2p8fgqq2.js +1839 -0
- package/dist/server/_chunks/tsconfig-defaults-bcn34eqb.js +21 -0
- package/dist/server/_chunks/typegen-dvtcjtaw.js +37 -0
- package/dist/server/api/client-cache.js +14 -0
- package/dist/server/cli/index.js +375 -0
- package/dist/server/cli/start.js +158 -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 +34 -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 +21 -0
- package/dist/server/compat/next/error.js +112 -0
- package/dist/server/compat/next/font/cache.js +16 -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 +24 -0
- package/dist/server/compat/next/font/runtime-client.js +33 -0
- package/dist/server/compat/next/font/runtime.js +43 -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 +15 -0
- package/dist/server/compat/next/link.js +14 -0
- package/dist/server/compat/next/navigation.js +191 -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 +32 -0
- package/dist/server/compat/next/resource-hints.js +30 -0
- package/dist/server/compat/next/rewrites.js +43 -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/reference/compat.md +1 -1
- package/reference/config.md +2 -2
- package/reference/dev.md +1 -1
- package/reference/getting-started.md +1 -1
- package/reference/rendering.md +4 -4
- package/src/api/dynamic.tsx +86 -4
- package/src/api/server.ts +24 -6
- package/src/cli/adapters/vercel-warm.ts +327 -62
- package/src/cli/adapters/vercel.ts +164 -1054
- package/src/cli/analyze.ts +3 -2
- package/src/cli/build/cache.ts +6 -6
- package/src/cli/build.ts +78 -45
- package/src/cli/index.ts +21 -3
- package/src/cli/serve/immutable.ts +31 -0
- package/src/cli/serve/pipeline.ts +97 -93
- package/src/cli/serve/standalone-worker.ts +8 -0
- package/src/cli/serve/standalone.ts +703 -0
- package/src/cli/start.ts +31 -8
- package/src/client/build.ts +1 -1
- package/src/client/chunk-fold.ts +19 -6
- package/src/client/react-tier.ts +3 -2
- package/src/client/reference-stub.ts +23 -36
- package/src/compat/actions/client-plugin.ts +4 -2
- package/src/compat/actions/config.ts +6 -5
- package/src/compat/actions/extensions.ts +5 -5
- 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/bundler/require-context.ts +2 -1
- package/src/compat/bundler/webpack-loaders.ts +3 -2
- package/src/compat/bundler/worker.ts +2 -2
- package/src/compat/cache-control.ts +16 -8
- package/src/compat/css/inline-css.ts +1 -1
- package/src/compat/css/modules.ts +18 -5
- package/src/compat/export/build-complete.ts +1 -1
- package/src/compat/export/emit.ts +4 -4
- package/src/compat/export/standalone.ts +30 -65
- package/src/compat/image-optimizer/optimize.ts +1 -1
- package/src/compat/lifecycle/error-funnel.ts +1 -1
- package/src/compat/lifecycle/instrumentation-client.ts +4 -3
- package/src/compat/lifecycle/instrumentation.ts +117 -24
- package/src/compat/lifecycle/testmode.ts +3 -2
- package/src/compat/metadata-artifacts.ts +1 -1
- package/src/compat/next/config-loader.ts +140 -13
- package/src/compat/next/dynamic.tsx +10 -23
- package/src/compat/next/font/cache.ts +2 -1
- package/src/compat/next/font/runtime.ts +23 -3
- 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/image.ts +1 -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/typecheck/check.ts +6 -4
- package/src/compat/validation/validate.ts +10 -7
- package/src/config.ts +76 -9
- package/src/css/assets.ts +213 -0
- package/src/css/build.ts +190 -247
- package/src/dev/client-chunk-store.ts +2 -1
- package/src/dev/restart/client-key.ts +2 -1
- package/src/dev/restart/global-css.ts +2 -1
- package/src/dev/restart/route-bundle-key.ts +2 -1
- package/src/dev/restart/route-facts.ts +2 -1
- package/src/dev/server.ts +34 -14
- package/src/extensions.ts +4 -0
- package/src/out-paths.ts +10 -0
- package/src/render/hooks.ts +13 -0
- package/src/render/renderer.ts +53 -33
- package/src/request/context.ts +9 -0
- package/src/resolve/dynamic.ts +7 -1
- package/src/resolve/engine.ts +2 -12
- package/src/resolve/imports.ts +12 -52
- package/src/resolve/package-json.ts +56 -0
- package/src/resolve/scan-facts.ts +250 -1
- package/src/routing/href.ts +10 -3
- package/src/routing/match.ts +287 -0
- package/src/routing/metadata-files.ts +20 -6
- 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 +218 -44
- package/src/runtime/module-cache.ts +15 -1
- package/src/runtime/modules.ts +405 -82
- package/src/runtime/prebundle.ts +101 -0
- package/src/runtime/production.ts +456 -0
- package/src/runtime/release-graph.ts +342 -0
- package/src/runtime/vendor-build.ts +750 -17
- package/src/runtime/vendor.ts +16 -3
- package/src/types.ts +4 -3
- package/src/utils/directive.ts +40 -0
- package/src/utils/fs.ts +95 -4
- package/src/utils/resource-query.ts +12 -0
- package/src/cli/serve/entry.ts +0 -45
package/src/routing/proxy.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
// Lazy: the oxc-parser native binding costs ~12.6 MB RSS; load it only when a parse happens.
|
|
4
|
-
const parseSync: typeof import('oxc-parser').parseSync = (...args) =>
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
6
|
-
loadNative(() => require('oxc-parser') as typeof import('oxc-parser')).parseSync(...args)
|
|
7
|
-
import { loadNative } from '../utils/native-require'
|
|
8
|
-
import { registerServerRuntime } from '../runtime/loader'
|
|
9
3
|
import { pathToFileHref, type ResolvedConfig } from '../config'
|
|
10
|
-
import { devServerModuleHref } from '../runtime/
|
|
4
|
+
import { devServerModuleHref, registerServerRuntime } from '../runtime/load'
|
|
11
5
|
import { runWithRequest } from '../request/context'
|
|
12
6
|
import {
|
|
13
7
|
copyMiddlewareHeaders,
|
|
@@ -140,14 +134,20 @@ export interface ProxyRunnerOptions {
|
|
|
140
134
|
* load the build-time bundle that has aliases baked in.
|
|
141
135
|
*/
|
|
142
136
|
compiledModuleHref?: string
|
|
137
|
+
/** A production release's proxy: its source and literal matcher config, as the build found them. */
|
|
138
|
+
released?: { file?: string; config?: ProxyConfig }
|
|
143
139
|
}
|
|
144
140
|
|
|
145
141
|
export function createProxyRunner(
|
|
146
142
|
config: ResolvedConfig,
|
|
147
143
|
runnerOptions: ProxyRunnerOptions = {},
|
|
148
144
|
): ProxyRunner {
|
|
149
|
-
const
|
|
150
|
-
|
|
145
|
+
const { released } = runnerOptions
|
|
146
|
+
const file = released ? released.file : findProxyFile(config)
|
|
147
|
+
const releasedMatchers = released?.config ? proxyMatchers(released.config) : undefined
|
|
148
|
+
// Awaited before the proxy module loads; a failure surfaces there.
|
|
149
|
+
const runtimeReady = registerServerRuntime(config, file ? [file] : [])
|
|
150
|
+
runtimeReady.catch(() => undefined)
|
|
151
151
|
let moduleKey: string | undefined
|
|
152
152
|
let modulePromise: Promise<ProxyModule> | undefined
|
|
153
153
|
let matchers: CompiledMatcher[] | undefined
|
|
@@ -155,12 +155,14 @@ export function createProxyRunner(
|
|
|
155
155
|
/** The module for `options`, started at most once per import version. */
|
|
156
156
|
function proxyModule(options: ProxyRunOptions) {
|
|
157
157
|
const key = proxyModuleKey(options)
|
|
158
|
+
const load = () =>
|
|
159
|
+
runtimeReady.then(() => importProxyModule(config, file!, options, runnerOptions))
|
|
158
160
|
if (key !== moduleKey) {
|
|
159
161
|
moduleKey = key
|
|
160
|
-
modulePromise =
|
|
162
|
+
modulePromise = load()
|
|
161
163
|
matchers = undefined
|
|
162
164
|
}
|
|
163
|
-
return (modulePromise ??=
|
|
165
|
+
return (modulePromise ??= load())
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
const runner: ProxyRunner = async (request, options = {}) => {
|
|
@@ -176,7 +178,7 @@ export function createProxyRunner(
|
|
|
176
178
|
// Matcher first, straight off the source: a request no matcher selects must not wait on the
|
|
177
179
|
// proxy's compile+import, which is otherwise the first serial link of a cold page.
|
|
178
180
|
if (!matchers && proxyMatcherGateEnabled()) {
|
|
179
|
-
const staticMatchers = staticProxyMatchers(file)
|
|
181
|
+
const staticMatchers = released ? releasedMatchers : await staticProxyMatchers(file)
|
|
180
182
|
if (staticMatchers && !matchesProxy(staticMatchers, url, request)) return undefined
|
|
181
183
|
}
|
|
182
184
|
|
|
@@ -397,7 +399,7 @@ export function proxyRoutePatterns(config: ProxyConfig | undefined) {
|
|
|
397
399
|
const matcher = config?.matcher
|
|
398
400
|
if (!matcher) return ['^/(.*)$']
|
|
399
401
|
const matchers = Array.isArray(matcher) ? matcher : [matcher]
|
|
400
|
-
return matchers.map(item =>
|
|
402
|
+
return matchers.map(item => anchoredMatcherPattern(matcherSource(item)))
|
|
401
403
|
}
|
|
402
404
|
|
|
403
405
|
async function importProxyModule(
|
|
@@ -440,7 +442,7 @@ const staticMatcherCache = new Map<string, { key: string; matchers?: CompiledMat
|
|
|
440
442
|
* middleware-manifest.json at build time); anything computed, any parse error, and any file without a
|
|
441
443
|
* literal `config` yields undefined, and the caller falls back to the imported module's config.
|
|
442
444
|
*/
|
|
443
|
-
function staticProxyMatchers(file: string): CompiledMatcher[] | undefined {
|
|
445
|
+
async function staticProxyMatchers(file: string): Promise<CompiledMatcher[] | undefined> {
|
|
444
446
|
let key: string
|
|
445
447
|
try {
|
|
446
448
|
const stats = statSync(file)
|
|
@@ -452,6 +454,7 @@ function staticProxyMatchers(file: string): CompiledMatcher[] | undefined {
|
|
|
452
454
|
if (cached?.key === key) return cached.matchers
|
|
453
455
|
let matchers: CompiledMatcher[] | undefined
|
|
454
456
|
try {
|
|
457
|
+
const { staticProxyConfig } = await import('./proxy-config')
|
|
455
458
|
const config = staticProxyConfig(file, readFileSync(file, 'utf8'))
|
|
456
459
|
if (config) matchers = proxyMatchers(config)
|
|
457
460
|
} catch {
|
|
@@ -461,121 +464,6 @@ function staticProxyMatchers(file: string): CompiledMatcher[] | undefined {
|
|
|
461
464
|
return matchers
|
|
462
465
|
}
|
|
463
466
|
|
|
464
|
-
function staticProxyConfig(file: string, source: string): ProxyConfig | undefined {
|
|
465
|
-
const result = parseSync(file, source, { lang: parserLang(file) })
|
|
466
|
-
// A recovered parse may have dropped the very declaration we are reading.
|
|
467
|
-
if (result.errors.length > 0) return undefined
|
|
468
|
-
for (const statement of result.program.body as StaticNode[]) {
|
|
469
|
-
if (statement.type !== 'ExportNamedDeclaration') continue
|
|
470
|
-
const declaration = statement.declaration
|
|
471
|
-
if (declaration?.type !== 'VariableDeclaration') continue
|
|
472
|
-
for (const declarator of declaration.declarations ?? []) {
|
|
473
|
-
if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'config') continue
|
|
474
|
-
const value = staticValue(declarator.init)
|
|
475
|
-
return isProxyConfigShape(value) ? (value as ProxyConfig) : undefined
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return undefined
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
function parserLang(file: string) {
|
|
482
|
-
const ext = path.extname(file)
|
|
483
|
-
if (ext === '.tsx') return 'tsx'
|
|
484
|
-
if (ext === '.jsx') return 'jsx'
|
|
485
|
-
return ext === '.js' || ext === '.mjs' ? 'js' : 'ts'
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/** Structural view of the oxc AST nodes this file reads — no full type import. */
|
|
489
|
-
interface StaticNode {
|
|
490
|
-
type: string
|
|
491
|
-
name?: string
|
|
492
|
-
value?: unknown
|
|
493
|
-
computed?: boolean
|
|
494
|
-
shorthand?: boolean
|
|
495
|
-
key?: StaticNode
|
|
496
|
-
init?: StaticNode
|
|
497
|
-
id?: StaticNode
|
|
498
|
-
declaration?: StaticNode
|
|
499
|
-
declarations?: StaticNode[]
|
|
500
|
-
expression?: StaticNode
|
|
501
|
-
properties?: StaticNode[]
|
|
502
|
-
elements?: (StaticNode | null)[]
|
|
503
|
-
expressions?: StaticNode[]
|
|
504
|
-
quasis?: { value?: { cooked?: string } }[]
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
/** Literal-only evaluation; `undefined` marks "not statically known". */
|
|
508
|
-
function staticValue(node: StaticNode | undefined | null): unknown {
|
|
509
|
-
if (!node) return undefined
|
|
510
|
-
switch (node.type) {
|
|
511
|
-
// `… as const` / `… satisfies ProxyConfig` wrap the literal, they never change it.
|
|
512
|
-
case 'TSAsExpression':
|
|
513
|
-
case 'TSSatisfiesExpression':
|
|
514
|
-
case 'TSNonNullExpression':
|
|
515
|
-
return staticValue(node.expression)
|
|
516
|
-
case 'Literal':
|
|
517
|
-
case 'StringLiteral':
|
|
518
|
-
case 'NumericLiteral':
|
|
519
|
-
case 'BooleanLiteral':
|
|
520
|
-
// A regex literal reports `value: null` here; it is not a matcher shape.
|
|
521
|
-
return node.value === null ? undefined : node.value
|
|
522
|
-
case 'TemplateLiteral':
|
|
523
|
-
return node.expressions?.length === 0
|
|
524
|
-
? (node.quasis?.[0]?.value?.cooked ?? undefined)
|
|
525
|
-
: undefined
|
|
526
|
-
case 'ArrayExpression': {
|
|
527
|
-
const items: unknown[] = []
|
|
528
|
-
for (const element of node.elements ?? []) {
|
|
529
|
-
const value = staticValue(element)
|
|
530
|
-
if (value === undefined) return undefined
|
|
531
|
-
items.push(value)
|
|
532
|
-
}
|
|
533
|
-
return items
|
|
534
|
-
}
|
|
535
|
-
case 'ObjectExpression': {
|
|
536
|
-
const object: Record<string, unknown> = {}
|
|
537
|
-
for (const property of node.properties ?? []) {
|
|
538
|
-
if (property.type !== 'Property' && property.type !== 'ObjectProperty') return undefined
|
|
539
|
-
if (property.computed) return undefined
|
|
540
|
-
const key =
|
|
541
|
-
property.key?.type === 'Identifier' ? property.key.name : (property.key?.value as string)
|
|
542
|
-
if (typeof key !== 'string') return undefined
|
|
543
|
-
const value = staticValue(property.value as StaticNode | undefined)
|
|
544
|
-
if (value === undefined) return undefined
|
|
545
|
-
object[key] = value
|
|
546
|
-
}
|
|
547
|
-
return object
|
|
548
|
-
}
|
|
549
|
-
default:
|
|
550
|
-
return undefined
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/** Only a config whose `matcher` is exactly the documented shape may gate. */
|
|
555
|
-
function isProxyConfigShape(value: unknown): boolean {
|
|
556
|
-
if (typeof value !== 'object' || value === null) return false
|
|
557
|
-
const matcher = (value as { matcher?: unknown }).matcher
|
|
558
|
-
if (matcher === undefined) return true
|
|
559
|
-
const items = Array.isArray(matcher) ? matcher : [matcher]
|
|
560
|
-
return items.every(item => {
|
|
561
|
-
if (typeof item === 'string') return true
|
|
562
|
-
if (typeof item !== 'object' || item === null) return false
|
|
563
|
-
const { source, has, missing } = item as Record<string, unknown>
|
|
564
|
-
if (typeof source !== 'string') return false
|
|
565
|
-
return [has, missing].every(
|
|
566
|
-
list => list === undefined || (Array.isArray(list) && list.every(isConditionShape)),
|
|
567
|
-
)
|
|
568
|
-
})
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
function isConditionShape(value: unknown): boolean {
|
|
572
|
-
if (typeof value !== 'object' || value === null) return false
|
|
573
|
-
const { type, key, value: expected } = value as Record<string, unknown>
|
|
574
|
-
if (type !== 'header' && type !== 'query' && type !== 'cookie' && type !== 'host') return false
|
|
575
|
-
if (key !== undefined && typeof key !== 'string') return false
|
|
576
|
-
return expected === undefined || typeof expected === 'string'
|
|
577
|
-
}
|
|
578
|
-
|
|
579
467
|
function proxyMatchers(config: ProxyConfig | undefined): CompiledMatcher[] {
|
|
580
468
|
const matcher = config?.matcher
|
|
581
469
|
if (!matcher) return [{ regex: /.*/ }]
|
|
@@ -588,10 +476,11 @@ function proxyMatchers(config: ProxyConfig | undefined): CompiledMatcher[] {
|
|
|
588
476
|
}
|
|
589
477
|
|
|
590
478
|
function matchesProxy(matchers: CompiledMatcher[], url: URL, request: Request) {
|
|
591
|
-
const
|
|
479
|
+
const { pathname } = url
|
|
480
|
+
const decoded = pathname.includes('%') ? decodedPathnames(pathname) : []
|
|
592
481
|
return matchers.some(
|
|
593
482
|
matcher =>
|
|
594
|
-
(matcher.regex.test(
|
|
483
|
+
(matcher.regex.test(pathname) || decoded.some(item => matcher.regex.test(item))) &&
|
|
595
484
|
matchesConditions(matcher, url, request),
|
|
596
485
|
)
|
|
597
486
|
}
|
|
@@ -641,8 +530,15 @@ function conditionValue(condition: ProxyRouteCondition, url: URL, request: Reque
|
|
|
641
530
|
}
|
|
642
531
|
}
|
|
643
532
|
|
|
644
|
-
|
|
645
|
-
|
|
533
|
+
// Next also matches `decodeURIComponent(pathname)`, so encoded paths can't skip the proxy.
|
|
534
|
+
function decodedPathnames(pathname: string) {
|
|
535
|
+
const decoded = [pathname.replace(/%2f/gi, '/')]
|
|
536
|
+
try {
|
|
537
|
+
decoded.push(decodeURIComponent(pathname))
|
|
538
|
+
} catch {
|
|
539
|
+
// Malformed escapes match on the raw forms only.
|
|
540
|
+
}
|
|
541
|
+
return decoded
|
|
646
542
|
}
|
|
647
543
|
|
|
648
544
|
function matcherSource(matcher: ProxyMatcher) {
|
|
@@ -650,7 +546,12 @@ function matcherSource(matcher: ProxyMatcher) {
|
|
|
650
546
|
}
|
|
651
547
|
|
|
652
548
|
function matcherRegex(matcher: string) {
|
|
653
|
-
return new RegExp(
|
|
549
|
+
return new RegExp(anchoredMatcherPattern(matcher))
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Next's path-to-regexp (strict: false) also matches a trailing slash; routing strips any run of them.
|
|
553
|
+
function anchoredMatcherPattern(matcher: string) {
|
|
554
|
+
return `^(?:${matcherPattern(matcher)})/*$`
|
|
654
555
|
}
|
|
655
556
|
|
|
656
557
|
function matcherPattern(matcher: string) {
|
package/src/routing/routes.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { existsSync } from 'node:fs'
|
|
3
|
-
import { listFiles, listFilesSync,
|
|
4
|
-
import { safeDecode } from '../utils/decode'
|
|
3
|
+
import { listFiles, listFilesSync, toPosixPath, withDirCache } from '../utils/fs'
|
|
5
4
|
import {
|
|
6
5
|
dynamicCallsFromSource,
|
|
7
6
|
pnextDynamicImportNames,
|
|
@@ -11,6 +10,13 @@ import { resolveImport, resolveModuleAlias, resolvePackageSpecifier } from '../r
|
|
|
11
10
|
import { dynamicCallFacts, scanFacts, type DynamicCallFact } from '../resolve/scan-facts'
|
|
12
11
|
import { readSourceSync } from '../resolve/source-text'
|
|
13
12
|
import { globalCssSourcesForPaths } from '../css/build'
|
|
13
|
+
import {
|
|
14
|
+
conventionFileName,
|
|
15
|
+
findConventionFiles,
|
|
16
|
+
findLayouts,
|
|
17
|
+
pageExtensions,
|
|
18
|
+
serverActionsUnsupportedMessage,
|
|
19
|
+
} from './match'
|
|
14
20
|
import {
|
|
15
21
|
childrenDefaultFile,
|
|
16
22
|
interceptionMarkerLevels,
|
|
@@ -24,11 +30,9 @@ import type {
|
|
|
24
30
|
ClientEntryReason,
|
|
25
31
|
MetadataRouteEntry,
|
|
26
32
|
MetadataRouteKind,
|
|
27
|
-
NavState,
|
|
28
33
|
RouteInterception,
|
|
29
34
|
RouteManifestEntry,
|
|
30
35
|
RouteMode,
|
|
31
|
-
RouteParamValue,
|
|
32
36
|
RouteSegmentConfig,
|
|
33
37
|
} from '../types'
|
|
34
38
|
import {
|
|
@@ -48,20 +52,28 @@ import {
|
|
|
48
52
|
sourceUsesRegisteredRequestApi,
|
|
49
53
|
} from '../extensions'
|
|
50
54
|
|
|
55
|
+
export {
|
|
56
|
+
findConventionFiles,
|
|
57
|
+
findGlobalError,
|
|
58
|
+
findLayouts,
|
|
59
|
+
matchInterception,
|
|
60
|
+
matchRoute,
|
|
61
|
+
normalizePathname,
|
|
62
|
+
parseNavState,
|
|
63
|
+
selectRouteForRequest,
|
|
64
|
+
serverActionsUnsupportedMessage,
|
|
65
|
+
type InterceptionRouteMatch,
|
|
66
|
+
type RouteRenderSelection,
|
|
67
|
+
} from './match'
|
|
68
|
+
|
|
51
69
|
// Single source of truth for the page/convention extension list. `.tsx`/`.ts` stay first so an all-.tsx app
|
|
52
70
|
// resolves on the first candidate and produces the identical file as before; .js/.jsx/.mjs are additive
|
|
53
71
|
// alternatives, and compat appends `mdx`/`md` via registerPageExtensions. EVERYTHING below derives from
|
|
54
72
|
// pageExtensions()/routeHandlerExtensions() LAZILY, computed on first use and memoized, so the compat
|
|
55
73
|
// registration that runs at bootstrap - before scanRoutes - is honored; a module-level const regex is baked
|
|
56
74
|
// at import time, which is too early.
|
|
57
|
-
const BASE_PAGE_EXTENSIONS = ['tsx', 'ts', 'jsx', 'js', 'mjs'] as const
|
|
58
75
|
const BASE_ROUTE_HANDLER_EXTENSIONS = ['ts', 'tsx', 'js', 'mjs'] as const
|
|
59
76
|
|
|
60
|
-
/** Base + compat-registered page extensions, de-duplicated in order. */
|
|
61
|
-
function pageExtensions(): string[] {
|
|
62
|
-
return [...new Set([...BASE_PAGE_EXTENSIONS, ...extraPageExtensions()])]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
77
|
/** Route-handler extensions (route.*); compat page extensions do not apply. */
|
|
66
78
|
function routeHandlerExtensions(): readonly string[] {
|
|
67
79
|
return BASE_ROUTE_HANDLER_EXTENSIONS
|
|
@@ -79,8 +91,6 @@ function boundaryConventions(): string[] {
|
|
|
79
91
|
return [...componentConventions, ...extraBoundaryConventionNames()]
|
|
80
92
|
}
|
|
81
93
|
|
|
82
|
-
const globalErrorBase = 'global-error'
|
|
83
|
-
|
|
84
94
|
// The extension list is only known after compat bootstrap runs (which may
|
|
85
95
|
// registerPageExtensions), and scanRoutes always runs after bootstrap. Each
|
|
86
96
|
// pattern is computed on first use and memoized so the compat additions land
|
|
@@ -153,15 +163,6 @@ function extensionRegex(base: string, extensions: readonly string[]) {
|
|
|
153
163
|
return new RegExp(`(^|/)${escapeRegex(base)}\\.(${extensions.join('|')})$`)
|
|
154
164
|
}
|
|
155
165
|
|
|
156
|
-
/** Nearest existing file named `<base>.<ext>` for the convention extensions. */
|
|
157
|
-
function conventionFileName(dir: string, base: string) {
|
|
158
|
-
const { files } = readDirListing(dir)
|
|
159
|
-
for (const extension of pageExtensions()) {
|
|
160
|
-
if (files.has(`${base}.${extension}`)) return path.join(dir, `${base}.${extension}`)
|
|
161
|
-
}
|
|
162
|
-
return undefined
|
|
163
|
-
}
|
|
164
|
-
|
|
165
166
|
interface RouteParts {
|
|
166
167
|
route: string
|
|
167
168
|
pattern: string
|
|
@@ -1107,10 +1108,6 @@ function appendDefaultDerivedRoutes(
|
|
|
1107
1108
|
}
|
|
1108
1109
|
}
|
|
1109
1110
|
|
|
1110
|
-
export function findLayouts(appPath: string, routeFilePath: string) {
|
|
1111
|
-
return findConventionFiles(appPath, routeFilePath, 'layout')
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
1111
|
/**
|
|
1115
1112
|
* CSS imported by the app's ROOT `not-found.*` (and its module graph), in
|
|
1116
1113
|
* source order, excluding the root-layout global CSS (which is served
|
|
@@ -1147,236 +1144,6 @@ export async function collectFileCss(
|
|
|
1147
1144
|
return collectCssImports(context, existing)
|
|
1148
1145
|
}
|
|
1149
1146
|
|
|
1150
|
-
/**
|
|
1151
|
-
* Walk from the route's directory up to `appPath`, returning one convention file per segment ordered
|
|
1152
|
-
* root-to-leaf. `name` may be a bare base (`'layout'`) or carry an extension - a known extension is stripped
|
|
1153
|
-
* so the full extension list is tried. Each segment resolves to the first existing `<base>.<ext>`; when none
|
|
1154
|
-
* exists the `.tsx` candidate is returned so existing callers that filter by `existsSync` keep
|
|
1155
|
-
* byte-identical behavior.
|
|
1156
|
-
*/
|
|
1157
|
-
export function findConventionFiles(appPath: string, routeFilePath: string, name: string) {
|
|
1158
|
-
const base = conventionBase(name)
|
|
1159
|
-
const files: string[] = []
|
|
1160
|
-
let dir = path.dirname(routeFilePath)
|
|
1161
|
-
|
|
1162
|
-
while (dir.startsWith(appPath)) {
|
|
1163
|
-
files.push(conventionFileName(dir, base) ?? path.join(dir, `${base}.tsx`))
|
|
1164
|
-
if (dir === appPath) break
|
|
1165
|
-
dir = path.dirname(dir)
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
return files.reverse()
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
function conventionBase(name: string) {
|
|
1172
|
-
const match = new RegExp(`\\.(${pageExtensions().join('|')})$`).exec(name)
|
|
1173
|
-
return match ? name.slice(0, -match[0].length) : name
|
|
1174
|
-
}
|
|
1175
|
-
|
|
1176
|
-
/** App-root `global-error.*` file, if present (BuildManifest.globalErrorFile). */
|
|
1177
|
-
export function findGlobalError(appPath: string) {
|
|
1178
|
-
return conventionFileName(appPath, globalErrorBase)
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
export function matchRoute(routes: RouteManifestEntry[], pathname: string) {
|
|
1182
|
-
// Trailing-slash tolerance: '/route/' matches the same entry as '/route'
|
|
1183
|
-
// (Next redirects these away; static file serving already accepts both).
|
|
1184
|
-
const normalized = normalizePathname(pathname)
|
|
1185
|
-
for (const route of routes) {
|
|
1186
|
-
// Interception entries never match a plain (hard) request; they apply
|
|
1187
|
-
// only through matchInterception on soft navigations.
|
|
1188
|
-
if (route.interception) continue
|
|
1189
|
-
const match = routeRegex(route).exec(normalized)
|
|
1190
|
-
if (!match) continue
|
|
1191
|
-
return { route, params: routeMatchParams(route, match) }
|
|
1192
|
-
}
|
|
1193
|
-
return null
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
export function normalizePathname(pathname: string) {
|
|
1197
|
-
return pathname.length > 1 ? pathname.replace(/\/+$/, '') || '/' : pathname
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
function routeMatchParams(route: RouteManifestEntry, match: RegExpExecArray) {
|
|
1201
|
-
const params: Record<string, RouteParamValue> = {}
|
|
1202
|
-
route.params.forEach((name, index) => {
|
|
1203
|
-
const decoded = safeDecode(match[index + 1] ?? '')
|
|
1204
|
-
// A runtime request whose segment IS the dynamic placeholder for its own
|
|
1205
|
-
// param (`[slug]`, whether sent raw or as `%5Bslug%5D`) is Next's "params
|
|
1206
|
-
// placeholder": it must surface the ENCODED placeholder (`%5Bslug%5D`), not
|
|
1207
|
-
// a decoded `[slug]`. A decoded `[slug]` reads as a fallback param and would
|
|
1208
|
-
// trigger a fallback-shell render (failing without a parent Suspense
|
|
1209
|
-
// boundary); Next keeps it URL-encoded to render at runtime instead.
|
|
1210
|
-
// Fallback-shell generation injects its placeholder params through a
|
|
1211
|
-
// separate build path, so this only affects live requests.
|
|
1212
|
-
params[name] = decoded === `[${name}]` ? encodeURIComponent(decoded) : decoded
|
|
1213
|
-
})
|
|
1214
|
-
if (route.catchAll) {
|
|
1215
|
-
params[route.catchAll] = (match[route.params.length + 1] ?? '')
|
|
1216
|
-
.split('/')
|
|
1217
|
-
.filter(Boolean)
|
|
1218
|
-
.map(safeDecode)
|
|
1219
|
-
}
|
|
1220
|
-
return params
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
export interface InterceptionRouteMatch {
|
|
1224
|
-
route: RouteManifestEntry
|
|
1225
|
-
params: Record<string, RouteParamValue>
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
/**
|
|
1229
|
-
* Match a soft navigation against interception entries: the entry's target
|
|
1230
|
-
* pattern must match the destination and the navigation must originate at or
|
|
1231
|
-
* below the interceptor's base (the children path the current document
|
|
1232
|
-
* rendered from). The deepest base wins. A no-op when destination equals the
|
|
1233
|
-
* origin (a refresh re-renders whatever produced the current document).
|
|
1234
|
-
*/
|
|
1235
|
-
export function matchInterception(
|
|
1236
|
-
routes: RouteManifestEntry[],
|
|
1237
|
-
pathname: string,
|
|
1238
|
-
fromPath: string | undefined,
|
|
1239
|
-
): InterceptionRouteMatch | null {
|
|
1240
|
-
if (!fromPath) return null
|
|
1241
|
-
const target = normalizePathname(pathname)
|
|
1242
|
-
const from = normalizePathname(fromPath)
|
|
1243
|
-
if (target === from) return null
|
|
1244
|
-
let best: InterceptionRouteMatch | null = null
|
|
1245
|
-
let bestDepth = -1
|
|
1246
|
-
for (const route of routes) {
|
|
1247
|
-
const interception = route.interception
|
|
1248
|
-
if (!interception) continue
|
|
1249
|
-
const match = routeRegex(route).exec(target)
|
|
1250
|
-
if (!match) continue
|
|
1251
|
-
const basePattern = interception.basePattern ? `/${interception.basePattern}` : ''
|
|
1252
|
-
if (!new RegExp(`^${basePattern}(?:/.*)?$`).test(from)) continue
|
|
1253
|
-
const depth = interception.base.split('/').filter(Boolean).length
|
|
1254
|
-
if (depth <= bestDepth) continue
|
|
1255
|
-
bestDepth = depth
|
|
1256
|
-
best = { route, params: routeMatchParams(route, match) }
|
|
1257
|
-
}
|
|
1258
|
-
return best
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
export interface RouteRenderSelection {
|
|
1262
|
-
route: RouteManifestEntry
|
|
1263
|
-
params: Record<string, RouteParamValue>
|
|
1264
|
-
/** Pathname the children tree renders from (normalized). */
|
|
1265
|
-
childrenPath: string
|
|
1266
|
-
/** Internal pathname after rewrites, used to resolve interception slots. */
|
|
1267
|
-
targetPath: string
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
/**
|
|
1271
|
-
* Pick the entry a page request renders. Hard requests match plainly. Soft navigations first consult
|
|
1272
|
-
* interception entries; a slot interception (and a synthetic slot URL reached with known origin) renders the
|
|
1273
|
-
* ORIGIN's entry as host - the current page stays while the slot content changes - with the children tree
|
|
1274
|
-
* anchored at `childrenPath`.
|
|
1275
|
-
*/
|
|
1276
|
-
export function selectRouteForRequest(
|
|
1277
|
-
routes: RouteManifestEntry[],
|
|
1278
|
-
pathname: string,
|
|
1279
|
-
nav?: import('../types').NavState,
|
|
1280
|
-
): RouteRenderSelection | null {
|
|
1281
|
-
const target = normalizePathname(pathname)
|
|
1282
|
-
if (nav) {
|
|
1283
|
-
const intercepted = matchInterception(routes, target, nav.children)
|
|
1284
|
-
if (intercepted) {
|
|
1285
|
-
if (!intercepted.route.interception?.slotDir) {
|
|
1286
|
-
return {
|
|
1287
|
-
route: intercepted.route,
|
|
1288
|
-
params: intercepted.params,
|
|
1289
|
-
childrenPath: target,
|
|
1290
|
-
targetPath: target,
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
const host = hostMatch(routes, nav.children)
|
|
1294
|
-
if (host) return { ...host, targetPath: target }
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
const matched = matchRoute(routes, target)
|
|
1298
|
-
if (!matched) return null
|
|
1299
|
-
if (nav && matched.route.synthetic && nav.children) {
|
|
1300
|
-
const primary = matchRoute(
|
|
1301
|
-
routes.filter(route => !route.synthetic),
|
|
1302
|
-
target,
|
|
1303
|
-
)
|
|
1304
|
-
const syntheticOwner = matched.route.syntheticSlotDir
|
|
1305
|
-
? path.dirname(matched.route.syntheticSlotDir)
|
|
1306
|
-
: undefined
|
|
1307
|
-
const primaryFromOwner =
|
|
1308
|
-
primary && syntheticOwner ? path.relative(syntheticOwner, primary.route.file) : undefined
|
|
1309
|
-
if (
|
|
1310
|
-
primary &&
|
|
1311
|
-
primaryFromOwner !== undefined &&
|
|
1312
|
-
primaryFromOwner !== '..' &&
|
|
1313
|
-
!primaryFromOwner.startsWith(`..${path.sep}`)
|
|
1314
|
-
) {
|
|
1315
|
-
return {
|
|
1316
|
-
route: primary.route,
|
|
1317
|
-
params: primary.params,
|
|
1318
|
-
childrenPath: target,
|
|
1319
|
-
targetPath: target,
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
const host = hostMatch(routes, nav.children)
|
|
1323
|
-
if (
|
|
1324
|
-
host &&
|
|
1325
|
-
host.route !== matched.route &&
|
|
1326
|
-
matched.route.syntheticSlotDir &&
|
|
1327
|
-
host.route.slotDirs?.includes(matched.route.syntheticSlotDir)
|
|
1328
|
-
) {
|
|
1329
|
-
return { ...host, targetPath: target }
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
return {
|
|
1333
|
-
route: matched.route,
|
|
1334
|
-
params: matched.params,
|
|
1335
|
-
childrenPath: target,
|
|
1336
|
-
targetPath: target,
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
/**
|
|
1341
|
-
* Navigation headers from the client soft-nav runtime: `x-pnext-soft-nav`
|
|
1342
|
-
* marks the fetch and `x-pnext-nav-state` carries the current document's
|
|
1343
|
-
* parallel-route state (URI-encoded JSON).
|
|
1344
|
-
*/
|
|
1345
|
-
export function parseNavState(request: Request): NavState | undefined {
|
|
1346
|
-
if (!request.headers.get('x-pnext-soft-nav')) return undefined
|
|
1347
|
-
const raw = request.headers.get('x-pnext-nav-state')
|
|
1348
|
-
if (!raw) return {}
|
|
1349
|
-
try {
|
|
1350
|
-
const parsed = JSON.parse(decodeURIComponent(raw)) as unknown
|
|
1351
|
-
if (parsed && typeof parsed === 'object') return parsed
|
|
1352
|
-
} catch {
|
|
1353
|
-
// Malformed state degrades to a plain soft navigation.
|
|
1354
|
-
}
|
|
1355
|
-
return {}
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
function hostMatch(
|
|
1359
|
-
routes: RouteManifestEntry[],
|
|
1360
|
-
fromPath: string | undefined,
|
|
1361
|
-
): RouteRenderSelection | null {
|
|
1362
|
-
if (!fromPath) return null
|
|
1363
|
-
const from = normalizePathname(fromPath)
|
|
1364
|
-
const host = matchRoute(routes, from)
|
|
1365
|
-
if (host?.route.kind !== 'page') return null
|
|
1366
|
-
return { route: host.route, params: host.params, childrenPath: from, targetPath: from }
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
const routeRegexCache = new WeakMap<RouteManifestEntry, RegExp>()
|
|
1370
|
-
|
|
1371
|
-
function routeRegex(route: RouteManifestEntry) {
|
|
1372
|
-
let regex = routeRegexCache.get(route)
|
|
1373
|
-
if (!regex) {
|
|
1374
|
-
regex = new RegExp(`^${route.pattern}$`)
|
|
1375
|
-
routeRegexCache.set(route, regex)
|
|
1376
|
-
}
|
|
1377
|
-
return regex
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
1147
|
interface RouteSegmentToken {
|
|
1381
1148
|
route: string
|
|
1382
1149
|
/** Regex piece; absent for the optional catch-all (assembled specially). */
|
|
@@ -1784,32 +1551,6 @@ function serverActionFileFact(context: ScanContext, sourceFiles: string[]) {
|
|
|
1784
1551
|
* Read once, act once: the file, what pnext will not do, and both remedies. No
|
|
1785
1552
|
* stack - the CLI prints `error.message` alone (cli/index.ts).
|
|
1786
1553
|
*/
|
|
1787
|
-
export function serverActionsUnsupportedMessage(offender: {
|
|
1788
|
-
file: string
|
|
1789
|
-
route?: string
|
|
1790
|
-
root?: string
|
|
1791
|
-
}) {
|
|
1792
|
-
const file = offender.root
|
|
1793
|
-
? toPosixPath(path.relative(offender.root, offender.file))
|
|
1794
|
-
: offender.file
|
|
1795
|
-
const reach = offender.route ? `, reachable from the route ${offender.route}` : ''
|
|
1796
|
-
return [
|
|
1797
|
-
`Server actions need next compat, and this app is pure core.`,
|
|
1798
|
-
``,
|
|
1799
|
-
` ${file} has a 'use server' directive${reach}.`,
|
|
1800
|
-
``,
|
|
1801
|
-
`pnext will not build this app: core has no server-action registry and no`,
|
|
1802
|
-
`dispatch endpoint, so the action could only render as a form that submits`,
|
|
1803
|
-
`nowhere.`,
|
|
1804
|
-
``,
|
|
1805
|
-
`Fix it one of two ways:`,
|
|
1806
|
-
` 1. Enable compat in pnext.config.ts:`,
|
|
1807
|
-
` export default { compat: { next: true } }`,
|
|
1808
|
-
` 2. Or delete the 'use server' directive from ${file}, if those exports are`,
|
|
1809
|
-
` only ever called on the server (in core the directive does nothing).`,
|
|
1810
|
-
].join('\n')
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
1554
|
/**
|
|
1814
1555
|
* Fail the build / the dev request rather than render a form whose action goes
|
|
1815
1556
|
* nowhere. Only page routes carry the fact (see serverActionFileFact), so a
|
|
@@ -7,6 +7,12 @@ import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, type Dir
|
|
|
7
7
|
import path from 'node:path'
|
|
8
8
|
import { fileURLToPath } from 'node:url'
|
|
9
9
|
|
|
10
|
+
declare const PNEXT_PREBUNDLE: { fingerprint: string } | undefined
|
|
11
|
+
|
|
12
|
+
// Read off the prebundle's define, not imported: compat compiles this module into app graphs.
|
|
13
|
+
const prebundleFingerprint =
|
|
14
|
+
typeof PNEXT_PREBUNDLE === 'object' ? PNEXT_PREBUNDLE.fingerprint : undefined
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* Exactly what the package publishes and the vercel adapter ships: source extensions only, never
|
|
12
18
|
* declarations (both drop them) and never machine-local files like `.DS_Store`. The same tree must
|
|
@@ -101,6 +107,9 @@ function writeRecord(dir: string, record: Record) {
|
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
let fingerprint: string | undefined
|
|
110
|
+
let fingerprintStats: string | undefined
|
|
111
|
+
// Set once this process has hashed the tree's content: only that answer may serve another app.
|
|
112
|
+
let scanned = false
|
|
104
113
|
const recorded = new Set<string>()
|
|
105
114
|
|
|
106
115
|
/**
|
|
@@ -109,24 +118,30 @@ const recorded = new Set<string>()
|
|
|
109
118
|
*
|
|
110
119
|
* `recordDir` (a cache root) buys the O(1) stat path across restarts; without it the tree is read.
|
|
111
120
|
* `PNEXT_FRAMEWORK_FINGERPRINT` pins the value outright — the bisect seam, and how a deployment can
|
|
112
|
-
* replay its build's generation without walking the tree at all.
|
|
121
|
+
* replay its build's generation without walking the tree at all. The prebundle pins its publish-time value.
|
|
113
122
|
*/
|
|
114
123
|
export function frameworkFingerprint(recordDir?: string) {
|
|
115
124
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
116
|
-
const pinned = process.env.PNEXT_FRAMEWORK_FINGERPRINT
|
|
125
|
+
const pinned = process.env.PNEXT_FRAMEWORK_FINGERPRINT ?? prebundleFingerprint
|
|
117
126
|
if (pinned) return pinned
|
|
118
127
|
// Memoized, but a second app in the same process still gets its own record - without one its next
|
|
119
128
|
// boot pays the full read for a fingerprint this process already knows.
|
|
120
129
|
if (fingerprint !== undefined && (!recordDir || recorded.has(recordDir))) return fingerprint
|
|
121
|
-
const files = sourceFiles(frameworkSrc)
|
|
122
|
-
const stats = statSignature(files, frameworkSrc)
|
|
123
130
|
const previous = recordDir ? readRecord(recordDir) : undefined
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
// One content scan per process; after it a later record dir only needs its record written. A
|
|
132
|
+
// stat-only answer from an app's record serves that app, never another app's fresh cache.
|
|
133
|
+
if (!scanned || fingerprint === undefined || fingerprintStats === undefined) {
|
|
134
|
+
const files = sourceFiles(frameworkSrc)
|
|
135
|
+
fingerprintStats = statSignature(files, frameworkSrc)
|
|
136
|
+
const recordedFingerprint =
|
|
137
|
+
previous?.stats === fingerprintStats ? previous.fingerprint : undefined
|
|
138
|
+
scanned = recordedFingerprint === undefined
|
|
139
|
+
fingerprint = recordedFingerprint ?? contentFingerprint(files, frameworkSrc)
|
|
140
|
+
}
|
|
126
141
|
if (recordDir) {
|
|
127
142
|
recorded.add(recordDir)
|
|
128
|
-
if (previous?.stats !==
|
|
129
|
-
writeRecord(recordDir, { version: RECORD_VERSION, stats, fingerprint })
|
|
143
|
+
if (previous?.stats !== fingerprintStats)
|
|
144
|
+
writeRecord(recordDir, { version: RECORD_VERSION, stats: fingerprintStats, fingerprint })
|
|
130
145
|
}
|
|
131
146
|
return fingerprint
|
|
132
147
|
}
|