@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
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request-time routing: matching a pathname against the built route table and finding an app
|
|
3
|
+
* directory's convention files. The route scan that builds the table lives in `./routes`.
|
|
4
|
+
*/
|
|
5
|
+
import path from 'node:path'
|
|
6
|
+
import { readDirListing, toPosixPath } from '../utils/fs'
|
|
7
|
+
import { safeDecode } from '../utils/decode'
|
|
8
|
+
import { extraPageExtensions } from '../extensions'
|
|
9
|
+
import type { NavState, RouteManifestEntry, RouteParamValue } from '../types'
|
|
10
|
+
|
|
11
|
+
const BASE_PAGE_EXTENSIONS = ['tsx', 'ts', 'jsx', 'js', 'mjs'] as const
|
|
12
|
+
|
|
13
|
+
/** Base + compat-registered page extensions, de-duplicated in order. */
|
|
14
|
+
export function pageExtensions(): string[] {
|
|
15
|
+
return [...new Set([...BASE_PAGE_EXTENSIONS, ...extraPageExtensions()])]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const globalErrorBase = 'global-error'
|
|
19
|
+
|
|
20
|
+
/** Nearest existing file named `<base>.<ext>` for the convention extensions. */
|
|
21
|
+
export function conventionFileName(dir: string, base: string) {
|
|
22
|
+
const { files } = readDirListing(dir)
|
|
23
|
+
for (const extension of pageExtensions()) {
|
|
24
|
+
if (files.has(`${base}.${extension}`)) return path.join(dir, `${base}.${extension}`)
|
|
25
|
+
}
|
|
26
|
+
return undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function findLayouts(appPath: string, routeFilePath: string) {
|
|
30
|
+
return findConventionFiles(appPath, routeFilePath, 'layout')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Walk from the route's directory up to `appPath`, returning one convention file per segment ordered
|
|
35
|
+
* root-to-leaf. `name` may be a bare base (`'layout'`) or carry an extension - a known extension is stripped
|
|
36
|
+
* so the full extension list is tried. Each segment resolves to the first existing `<base>.<ext>`; when none
|
|
37
|
+
* exists the `.tsx` candidate is returned so existing callers that filter by `existsSync` keep
|
|
38
|
+
* byte-identical behavior.
|
|
39
|
+
*/
|
|
40
|
+
export function findConventionFiles(appPath: string, routeFilePath: string, name: string) {
|
|
41
|
+
const base = conventionBase(name)
|
|
42
|
+
const files: string[] = []
|
|
43
|
+
let dir = path.dirname(routeFilePath)
|
|
44
|
+
|
|
45
|
+
while (dir.startsWith(appPath)) {
|
|
46
|
+
files.push(conventionFileName(dir, base) ?? path.join(dir, `${base}.tsx`))
|
|
47
|
+
if (dir === appPath) break
|
|
48
|
+
dir = path.dirname(dir)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return files.reverse()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function conventionBase(name: string) {
|
|
55
|
+
const match = new RegExp(`\\.(${pageExtensions().join('|')})$`).exec(name)
|
|
56
|
+
return match ? name.slice(0, -match[0].length) : name
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** App-root `global-error.*` file, if present (BuildManifest.globalErrorFile). */
|
|
60
|
+
export function findGlobalError(appPath: string) {
|
|
61
|
+
return conventionFileName(appPath, globalErrorBase)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function matchRoute(routes: RouteManifestEntry[], pathname: string) {
|
|
65
|
+
// Trailing-slash tolerance: '/route/' matches the same entry as '/route'
|
|
66
|
+
// (Next redirects these away; static file serving already accepts both).
|
|
67
|
+
const normalized = normalizePathname(pathname)
|
|
68
|
+
for (const route of routes) {
|
|
69
|
+
// Interception entries never match a plain (hard) request; they apply
|
|
70
|
+
// only through matchInterception on soft navigations.
|
|
71
|
+
if (route.interception) continue
|
|
72
|
+
const match = routeRegex(route).exec(normalized)
|
|
73
|
+
if (!match) continue
|
|
74
|
+
return { route, params: routeMatchParams(route, match) }
|
|
75
|
+
}
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function normalizePathname(pathname: string) {
|
|
80
|
+
return pathname.length > 1 ? pathname.replace(/\/+$/, '') || '/' : pathname
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function routeMatchParams(route: RouteManifestEntry, match: RegExpExecArray) {
|
|
84
|
+
const params: Record<string, RouteParamValue> = {}
|
|
85
|
+
route.params.forEach((name, index) => {
|
|
86
|
+
const decoded = safeDecode(match[index + 1] ?? '')
|
|
87
|
+
// A runtime request whose segment IS the dynamic placeholder for its own
|
|
88
|
+
// param (`[slug]`, whether sent raw or as `%5Bslug%5D`) is Next's "params
|
|
89
|
+
// placeholder": it must surface the ENCODED placeholder (`%5Bslug%5D`), not
|
|
90
|
+
// a decoded `[slug]`. A decoded `[slug]` reads as a fallback param and would
|
|
91
|
+
// trigger a fallback-shell render (failing without a parent Suspense
|
|
92
|
+
// boundary); Next keeps it URL-encoded to render at runtime instead.
|
|
93
|
+
// Fallback-shell generation injects its placeholder params through a
|
|
94
|
+
// separate build path, so this only affects live requests.
|
|
95
|
+
params[name] = decoded === `[${name}]` ? encodeURIComponent(decoded) : decoded
|
|
96
|
+
})
|
|
97
|
+
if (route.catchAll) {
|
|
98
|
+
params[route.catchAll] = (match[route.params.length + 1] ?? '')
|
|
99
|
+
.split('/')
|
|
100
|
+
.filter(Boolean)
|
|
101
|
+
.map(safeDecode)
|
|
102
|
+
}
|
|
103
|
+
return params
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface InterceptionRouteMatch {
|
|
107
|
+
route: RouteManifestEntry
|
|
108
|
+
params: Record<string, RouteParamValue>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Match a soft navigation against interception entries: the entry's target
|
|
113
|
+
* pattern must match the destination and the navigation must originate at or
|
|
114
|
+
* below the interceptor's base (the children path the current document
|
|
115
|
+
* rendered from). The deepest base wins. A no-op when destination equals the
|
|
116
|
+
* origin (a refresh re-renders whatever produced the current document).
|
|
117
|
+
*/
|
|
118
|
+
export function matchInterception(
|
|
119
|
+
routes: RouteManifestEntry[],
|
|
120
|
+
pathname: string,
|
|
121
|
+
fromPath: string | undefined,
|
|
122
|
+
): InterceptionRouteMatch | null {
|
|
123
|
+
if (!fromPath) return null
|
|
124
|
+
const target = normalizePathname(pathname)
|
|
125
|
+
const from = normalizePathname(fromPath)
|
|
126
|
+
if (target === from) return null
|
|
127
|
+
let best: InterceptionRouteMatch | null = null
|
|
128
|
+
let bestDepth = -1
|
|
129
|
+
for (const route of routes) {
|
|
130
|
+
const interception = route.interception
|
|
131
|
+
if (!interception) continue
|
|
132
|
+
const match = routeRegex(route).exec(target)
|
|
133
|
+
if (!match) continue
|
|
134
|
+
const basePattern = interception.basePattern ? `/${interception.basePattern}` : ''
|
|
135
|
+
if (!new RegExp(`^${basePattern}(?:/.*)?$`).test(from)) continue
|
|
136
|
+
const depth = interception.base.split('/').filter(Boolean).length
|
|
137
|
+
if (depth <= bestDepth) continue
|
|
138
|
+
bestDepth = depth
|
|
139
|
+
best = { route, params: routeMatchParams(route, match) }
|
|
140
|
+
}
|
|
141
|
+
return best
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface RouteRenderSelection {
|
|
145
|
+
route: RouteManifestEntry
|
|
146
|
+
params: Record<string, RouteParamValue>
|
|
147
|
+
/** Pathname the children tree renders from (normalized). */
|
|
148
|
+
childrenPath: string
|
|
149
|
+
/** Internal pathname after rewrites, used to resolve interception slots. */
|
|
150
|
+
targetPath: string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Pick the entry a page request renders. Hard requests match plainly. Soft navigations first consult
|
|
155
|
+
* interception entries; a slot interception (and a synthetic slot URL reached with known origin) renders the
|
|
156
|
+
* ORIGIN's entry as host - the current page stays while the slot content changes - with the children tree
|
|
157
|
+
* anchored at `childrenPath`.
|
|
158
|
+
*/
|
|
159
|
+
export function selectRouteForRequest(
|
|
160
|
+
routes: RouteManifestEntry[],
|
|
161
|
+
pathname: string,
|
|
162
|
+
nav?: import('../types').NavState,
|
|
163
|
+
): RouteRenderSelection | null {
|
|
164
|
+
const target = normalizePathname(pathname)
|
|
165
|
+
if (nav) {
|
|
166
|
+
const intercepted = matchInterception(routes, target, nav.children)
|
|
167
|
+
if (intercepted) {
|
|
168
|
+
if (!intercepted.route.interception?.slotDir) {
|
|
169
|
+
return {
|
|
170
|
+
route: intercepted.route,
|
|
171
|
+
params: intercepted.params,
|
|
172
|
+
childrenPath: target,
|
|
173
|
+
targetPath: target,
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const host = hostMatch(routes, nav.children)
|
|
177
|
+
if (host) return { ...host, targetPath: target }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const matched = matchRoute(routes, target)
|
|
181
|
+
if (!matched) return null
|
|
182
|
+
if (nav && matched.route.synthetic && nav.children) {
|
|
183
|
+
const primary = matchRoute(
|
|
184
|
+
routes.filter(route => !route.synthetic),
|
|
185
|
+
target,
|
|
186
|
+
)
|
|
187
|
+
const syntheticOwner = matched.route.syntheticSlotDir
|
|
188
|
+
? path.dirname(matched.route.syntheticSlotDir)
|
|
189
|
+
: undefined
|
|
190
|
+
const primaryFromOwner =
|
|
191
|
+
primary && syntheticOwner ? path.relative(syntheticOwner, primary.route.file) : undefined
|
|
192
|
+
if (
|
|
193
|
+
primary &&
|
|
194
|
+
primaryFromOwner !== undefined &&
|
|
195
|
+
primaryFromOwner !== '..' &&
|
|
196
|
+
!primaryFromOwner.startsWith(`..${path.sep}`)
|
|
197
|
+
) {
|
|
198
|
+
return {
|
|
199
|
+
route: primary.route,
|
|
200
|
+
params: primary.params,
|
|
201
|
+
childrenPath: target,
|
|
202
|
+
targetPath: target,
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const host = hostMatch(routes, nav.children)
|
|
206
|
+
if (
|
|
207
|
+
host &&
|
|
208
|
+
host.route !== matched.route &&
|
|
209
|
+
matched.route.syntheticSlotDir &&
|
|
210
|
+
host.route.slotDirs?.includes(matched.route.syntheticSlotDir)
|
|
211
|
+
) {
|
|
212
|
+
return { ...host, targetPath: target }
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
route: matched.route,
|
|
217
|
+
params: matched.params,
|
|
218
|
+
childrenPath: target,
|
|
219
|
+
targetPath: target,
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Navigation headers from the client soft-nav runtime: `x-pnext-soft-nav`
|
|
225
|
+
* marks the fetch and `x-pnext-nav-state` carries the current document's
|
|
226
|
+
* parallel-route state (URI-encoded JSON).
|
|
227
|
+
*/
|
|
228
|
+
export function parseNavState(request: Request): NavState | undefined {
|
|
229
|
+
if (!request.headers.get('x-pnext-soft-nav')) return undefined
|
|
230
|
+
const raw = request.headers.get('x-pnext-nav-state')
|
|
231
|
+
if (!raw) return {}
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(decodeURIComponent(raw)) as unknown
|
|
234
|
+
if (parsed && typeof parsed === 'object') return parsed
|
|
235
|
+
} catch {
|
|
236
|
+
// Malformed state degrades to a plain soft navigation.
|
|
237
|
+
}
|
|
238
|
+
return {}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function hostMatch(
|
|
242
|
+
routes: RouteManifestEntry[],
|
|
243
|
+
fromPath: string | undefined,
|
|
244
|
+
): RouteRenderSelection | null {
|
|
245
|
+
if (!fromPath) return null
|
|
246
|
+
const from = normalizePathname(fromPath)
|
|
247
|
+
const host = matchRoute(routes, from)
|
|
248
|
+
if (host?.route.kind !== 'page') return null
|
|
249
|
+
return { route: host.route, params: host.params, childrenPath: from, targetPath: from }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const routeRegexCache = new WeakMap<RouteManifestEntry, RegExp>()
|
|
253
|
+
|
|
254
|
+
function routeRegex(route: RouteManifestEntry) {
|
|
255
|
+
let regex = routeRegexCache.get(route)
|
|
256
|
+
if (!regex) {
|
|
257
|
+
regex = new RegExp(`^${route.pattern}$`)
|
|
258
|
+
routeRegexCache.set(route, regex)
|
|
259
|
+
}
|
|
260
|
+
return regex
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function serverActionsUnsupportedMessage(offender: {
|
|
264
|
+
file: string
|
|
265
|
+
route?: string
|
|
266
|
+
root?: string
|
|
267
|
+
}) {
|
|
268
|
+
const file = offender.root
|
|
269
|
+
? toPosixPath(path.relative(offender.root, offender.file))
|
|
270
|
+
: offender.file
|
|
271
|
+
const reach = offender.route ? `, reachable from the route ${offender.route}` : ''
|
|
272
|
+
return [
|
|
273
|
+
`Server actions need next compat, and this app is pure core.`,
|
|
274
|
+
``,
|
|
275
|
+
` ${file} has a 'use server' directive${reach}.`,
|
|
276
|
+
``,
|
|
277
|
+
`pnext will not build this app: core has no server-action registry and no`,
|
|
278
|
+
`dispatch endpoint, so the action could only render as a form that submits`,
|
|
279
|
+
`nowhere.`,
|
|
280
|
+
``,
|
|
281
|
+
`Fix it one of two ways:`,
|
|
282
|
+
` 1. Enable compat in pnext.config.ts:`,
|
|
283
|
+
` export default { compat: { next: true } }`,
|
|
284
|
+
` 2. Or delete the 'use server' directive from ${file}, if those exports are`,
|
|
285
|
+
` only ever called on the server (in core the directive does nothing).`,
|
|
286
|
+
].join('\n')
|
|
287
|
+
}
|
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
} from '../types'
|
|
14
14
|
import { escapeHtml, stringifyHtmlValue } from '../utils/html'
|
|
15
15
|
import { toPosixPath } from '../utils/fs'
|
|
16
|
+
import { servedAppFiles, servedRelease } from '../runtime/production'
|
|
16
17
|
|
|
17
18
|
export type StaticMetadataKind =
|
|
18
19
|
| 'favicon'
|
|
@@ -65,6 +66,8 @@ export function clearMetadataFileCaches() {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export function discoverStaticMetadataFiles(appPath: string): StaticMetadataFile[] {
|
|
69
|
+
const served = servedRelease()
|
|
70
|
+
if (served) return served.staticMetadataFiles ?? []
|
|
68
71
|
const cached = staticMetadataFiles.get(appPath)
|
|
69
72
|
if (cached) return cached
|
|
70
73
|
const files = existsSync(appPath)
|
|
@@ -343,15 +346,26 @@ function metadataAppliesToRoute(file: StaticMetadataFile, routeDir: string) {
|
|
|
343
346
|
return routeDir === dir || routeDir.startsWith(`${dir}/`)
|
|
344
347
|
}
|
|
345
348
|
|
|
349
|
+
// A release's tree is immutable: its inventory is derived once per release.
|
|
350
|
+
const releasedDynamicMetadataFiles = new WeakMap<object, DynamicMetadataFile[]>()
|
|
351
|
+
|
|
346
352
|
function discoverDynamicMetadataFiles(appPath: string): DynamicMetadataFile[] {
|
|
347
|
-
const
|
|
353
|
+
const served = servedRelease()?.conventions
|
|
354
|
+
const cached = served
|
|
355
|
+
? releasedDynamicMetadataFiles.get(served)
|
|
356
|
+
: dynamicMetadataFiles.get(appPath)
|
|
348
357
|
if (cached) return cached
|
|
349
|
-
const files =
|
|
350
|
-
?
|
|
358
|
+
const files = served
|
|
359
|
+
? servedAppFiles(served)
|
|
351
360
|
.flatMap(file => dynamicMetadataFile(appPath, file) ?? [])
|
|
352
361
|
.sort((a, b) => a.relative.localeCompare(b.relative))
|
|
353
|
-
:
|
|
354
|
-
|
|
362
|
+
: existsSync(appPath)
|
|
363
|
+
? listFilesSync(appPath)
|
|
364
|
+
.flatMap(file => dynamicMetadataFile(appPath, file) ?? [])
|
|
365
|
+
.sort((a, b) => a.relative.localeCompare(b.relative))
|
|
366
|
+
: []
|
|
367
|
+
if (served) releasedDynamicMetadataFiles.set(served, files)
|
|
368
|
+
else dynamicMetadataFiles.set(appPath, files)
|
|
355
369
|
return files
|
|
356
370
|
}
|
|
357
371
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** The proxy's literal `config` read off its source: build and dev only, a release carries it. */
|
|
2
|
+
import { readFileSync } from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import type { ResolvedConfig } from '../config'
|
|
5
|
+
import { loadNative } from '../utils/native-require'
|
|
6
|
+
import { findProxyFile, type ProxyConfig } from './proxy'
|
|
7
|
+
|
|
8
|
+
// Lazy: the oxc-parser native binding costs ~12.6 MB RSS; load it only when a parse happens.
|
|
9
|
+
const parseSync: typeof import('oxc-parser').parseSync = (...args) =>
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
11
|
+
loadNative(() => require('oxc-parser') as typeof import('oxc-parser')).parseSync(...args)
|
|
12
|
+
|
|
13
|
+
/** The proxy a production release serves: its source and, when wholly literal, its matcher config. */
|
|
14
|
+
export function releasedProxy(config: ResolvedConfig) {
|
|
15
|
+
const file = findProxyFile(config)
|
|
16
|
+
if (!file) return undefined
|
|
17
|
+
try {
|
|
18
|
+
return { file, config: staticProxyConfig(file, readFileSync(file, 'utf8')) }
|
|
19
|
+
} catch {
|
|
20
|
+
return { file }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function staticProxyConfig(file: string, source: string): ProxyConfig | undefined {
|
|
25
|
+
const result = parseSync(file, source, { lang: parserLang(file) })
|
|
26
|
+
// A recovered parse may have dropped the very declaration we are reading.
|
|
27
|
+
if (result.errors.length > 0) return undefined
|
|
28
|
+
for (const statement of result.program.body as StaticNode[]) {
|
|
29
|
+
if (statement.type !== 'ExportNamedDeclaration') continue
|
|
30
|
+
const declaration = statement.declaration
|
|
31
|
+
if (declaration?.type !== 'VariableDeclaration') continue
|
|
32
|
+
for (const declarator of declaration.declarations ?? []) {
|
|
33
|
+
if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'config') continue
|
|
34
|
+
const value = staticValue(declarator.init)
|
|
35
|
+
return isProxyConfigShape(value) ? (value as ProxyConfig) : undefined
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return undefined
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parserLang(file: string) {
|
|
42
|
+
const ext = path.extname(file)
|
|
43
|
+
if (ext === '.tsx') return 'tsx'
|
|
44
|
+
if (ext === '.jsx') return 'jsx'
|
|
45
|
+
return ext === '.js' || ext === '.mjs' ? 'js' : 'ts'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Structural view of the oxc AST nodes this file reads — no full type import. */
|
|
49
|
+
interface StaticNode {
|
|
50
|
+
type: string
|
|
51
|
+
name?: string
|
|
52
|
+
value?: unknown
|
|
53
|
+
computed?: boolean
|
|
54
|
+
shorthand?: boolean
|
|
55
|
+
key?: StaticNode
|
|
56
|
+
init?: StaticNode
|
|
57
|
+
id?: StaticNode
|
|
58
|
+
declaration?: StaticNode
|
|
59
|
+
declarations?: StaticNode[]
|
|
60
|
+
expression?: StaticNode
|
|
61
|
+
properties?: StaticNode[]
|
|
62
|
+
elements?: (StaticNode | null)[]
|
|
63
|
+
expressions?: StaticNode[]
|
|
64
|
+
quasis?: { value?: { cooked?: string } }[]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Literal-only evaluation; `undefined` marks "not statically known". */
|
|
68
|
+
function staticValue(node: StaticNode | undefined | null): unknown {
|
|
69
|
+
if (!node) return undefined
|
|
70
|
+
switch (node.type) {
|
|
71
|
+
// `… as const` / `… satisfies ProxyConfig` wrap the literal, they never change it.
|
|
72
|
+
case 'TSAsExpression':
|
|
73
|
+
case 'TSSatisfiesExpression':
|
|
74
|
+
case 'TSNonNullExpression':
|
|
75
|
+
return staticValue(node.expression)
|
|
76
|
+
case 'Literal':
|
|
77
|
+
case 'StringLiteral':
|
|
78
|
+
case 'NumericLiteral':
|
|
79
|
+
case 'BooleanLiteral':
|
|
80
|
+
// A regex literal reports `value: null` here; it is not a matcher shape.
|
|
81
|
+
return node.value === null ? undefined : node.value
|
|
82
|
+
case 'TemplateLiteral':
|
|
83
|
+
return node.expressions?.length === 0
|
|
84
|
+
? (node.quasis?.[0]?.value?.cooked ?? undefined)
|
|
85
|
+
: undefined
|
|
86
|
+
case 'ArrayExpression': {
|
|
87
|
+
const items: unknown[] = []
|
|
88
|
+
for (const element of node.elements ?? []) {
|
|
89
|
+
const value = staticValue(element)
|
|
90
|
+
if (value === undefined) return undefined
|
|
91
|
+
items.push(value)
|
|
92
|
+
}
|
|
93
|
+
return items
|
|
94
|
+
}
|
|
95
|
+
case 'ObjectExpression': {
|
|
96
|
+
const object: Record<string, unknown> = {}
|
|
97
|
+
for (const property of node.properties ?? []) {
|
|
98
|
+
if (property.type !== 'Property' && property.type !== 'ObjectProperty') return undefined
|
|
99
|
+
if (property.computed) return undefined
|
|
100
|
+
const key =
|
|
101
|
+
property.key?.type === 'Identifier' ? property.key.name : (property.key?.value as string)
|
|
102
|
+
if (typeof key !== 'string') return undefined
|
|
103
|
+
const value = staticValue(property.value as StaticNode | undefined)
|
|
104
|
+
if (value === undefined) return undefined
|
|
105
|
+
object[key] = value
|
|
106
|
+
}
|
|
107
|
+
return object
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
return undefined
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Only a config whose `matcher` is exactly the documented shape may gate. */
|
|
115
|
+
function isProxyConfigShape(value: unknown): boolean {
|
|
116
|
+
if (typeof value !== 'object' || value === null) return false
|
|
117
|
+
const matcher = (value as { matcher?: unknown }).matcher
|
|
118
|
+
if (matcher === undefined) return true
|
|
119
|
+
const items = Array.isArray(matcher) ? matcher : [matcher]
|
|
120
|
+
return items.every(item => {
|
|
121
|
+
if (typeof item === 'string') return true
|
|
122
|
+
if (typeof item !== 'object' || item === null) return false
|
|
123
|
+
const { source, has, missing } = item as Record<string, unknown>
|
|
124
|
+
if (typeof source !== 'string') return false
|
|
125
|
+
return [has, missing].every(
|
|
126
|
+
list => list === undefined || (Array.isArray(list) && list.every(isConditionShape)),
|
|
127
|
+
)
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function isConditionShape(value: unknown): boolean {
|
|
132
|
+
if (typeof value !== 'object' || value === null) return false
|
|
133
|
+
const { type, key, value: expected } = value as Record<string, unknown>
|
|
134
|
+
if (type !== 'header' && type !== 'query' && type !== 'cookie' && type !== 'host') return false
|
|
135
|
+
if (key !== undefined && typeof key !== 'string') return false
|
|
136
|
+
return expected === undefined || typeof expected === 'string'
|
|
137
|
+
}
|