@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,851 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
scanRootParamTypes
|
|
4
|
+
} from "./index-v1ags1na.js";
|
|
5
|
+
import {
|
|
6
|
+
getNextConfig
|
|
7
|
+
} from "./index-6e4fqv30.js";
|
|
8
|
+
import {
|
|
9
|
+
ensureDir,
|
|
10
|
+
toPosixPath
|
|
11
|
+
} from "./index-20ppawv7.js";
|
|
12
|
+
|
|
13
|
+
// src/compat/typed-routes/generate.ts
|
|
14
|
+
import { existsSync as existsSync2 } from "fs";
|
|
15
|
+
import { readFile, writeFile } from "fs/promises";
|
|
16
|
+
import path2 from "path";
|
|
17
|
+
|
|
18
|
+
// src/compat/typed-routes/manifest.ts
|
|
19
|
+
import { existsSync } from "fs";
|
|
20
|
+
import { readdir } from "fs/promises";
|
|
21
|
+
import path from "path";
|
|
22
|
+
var PAGE_EXTENSIONS = ["tsx", "ts", "jsx", "js", "mdx", "md"];
|
|
23
|
+
var LAYOUT_NAMES = new Set(PAGE_EXTENSIONS.map((ext) => `layout.${ext}`));
|
|
24
|
+
function isGroupSegment(name) {
|
|
25
|
+
return name.startsWith("(") && name.endsWith(")");
|
|
26
|
+
}
|
|
27
|
+
function isSlotSegment(name) {
|
|
28
|
+
return name.startsWith("@");
|
|
29
|
+
}
|
|
30
|
+
function decodeLiteral(segment) {
|
|
31
|
+
return segment.replace(/%5[Ff]/g, "_");
|
|
32
|
+
}
|
|
33
|
+
function groupsFromBracketRoute(route) {
|
|
34
|
+
const groups = {};
|
|
35
|
+
let pos = 1;
|
|
36
|
+
for (const segment of route.split("/").filter(Boolean)) {
|
|
37
|
+
const optionalCatchAll = /^\[\[\.\.\.(.+)\]\]$/.exec(segment);
|
|
38
|
+
if (optionalCatchAll?.[1]) {
|
|
39
|
+
groups[optionalCatchAll[1]] = { pos: pos++, repeat: true, optional: true };
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const catchAll = /^\[\.\.\.(.+)\]$/.exec(segment);
|
|
43
|
+
if (catchAll?.[1]) {
|
|
44
|
+
groups[catchAll[1]] = { pos: pos++, repeat: true, optional: false };
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const param = /^\[(.+)\]$/.exec(segment);
|
|
48
|
+
if (param?.[1]) {
|
|
49
|
+
groups[param[1]] = { pos: pos++, repeat: false, optional: false };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return groups;
|
|
53
|
+
}
|
|
54
|
+
function bracketRouteFromEntry(entry) {
|
|
55
|
+
const segments = entry.route.split("/").filter(Boolean).map((segment) => {
|
|
56
|
+
const catchAll = /^:(.+)\*$/.exec(segment);
|
|
57
|
+
if (catchAll?.[1]) {
|
|
58
|
+
const name = catchAll[1];
|
|
59
|
+
return name === entry.catchAll && entry.catchAllOptional ? `[[...${name}]]` : `[...${name}]`;
|
|
60
|
+
}
|
|
61
|
+
const param = /^:(.+)$/.exec(segment);
|
|
62
|
+
if (param?.[1])
|
|
63
|
+
return `[${param[1]}]`;
|
|
64
|
+
return segment;
|
|
65
|
+
});
|
|
66
|
+
return `/${segments.join("/")}`.replace(/\/$/, "") || "/";
|
|
67
|
+
}
|
|
68
|
+
function bracketRouteFromDir(relativeDir) {
|
|
69
|
+
const segments = [];
|
|
70
|
+
for (const segment of relativeDir.split("/").filter(Boolean)) {
|
|
71
|
+
if (isGroupSegment(segment) || isSlotSegment(segment))
|
|
72
|
+
continue;
|
|
73
|
+
if (/^\[\[?\.\.\..+\]\]?$/.test(segment) || /^\[.+\]$/.test(segment)) {
|
|
74
|
+
segments.push(segment);
|
|
75
|
+
} else {
|
|
76
|
+
segments.push(decodeLiteral(segment));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return `/${segments.join("/")}`.replace(/\/$/, "") || "/";
|
|
80
|
+
}
|
|
81
|
+
function pagesRouteFromRelative(relative) {
|
|
82
|
+
const withoutExt = relative.replace(/\.[^.]+$/, "");
|
|
83
|
+
const trimmed = withoutExt.replace(/\/index$/, "").replace(/^index$/, "");
|
|
84
|
+
const route = `/${trimmed}`.replace(/\/$/, "") || "/";
|
|
85
|
+
return route;
|
|
86
|
+
}
|
|
87
|
+
async function walkFiles(dir) {
|
|
88
|
+
if (!existsSync(dir))
|
|
89
|
+
return [];
|
|
90
|
+
const out = [];
|
|
91
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
const full = path.join(dir, entry.name);
|
|
94
|
+
if (entry.isDirectory())
|
|
95
|
+
out.push(...await walkFiles(full));
|
|
96
|
+
else
|
|
97
|
+
out.push(full);
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function isPrivate(relativeDir) {
|
|
102
|
+
return relativeDir.split("/").some((seg) => seg.startsWith("_") && !isGroupSegment(seg) && !isSlotSegment(seg));
|
|
103
|
+
}
|
|
104
|
+
function convertCustomRouteSource(source) {
|
|
105
|
+
const normalized = source.startsWith("/") ? source : `/${source}`;
|
|
106
|
+
const converted = normalized.split("/").map((segment) => {
|
|
107
|
+
const optional = /^:(.+)\*$/.exec(segment);
|
|
108
|
+
if (optional?.[1])
|
|
109
|
+
return `[[...${optional[1]}]]`;
|
|
110
|
+
const catchAll = /^:(.+)\+$/.exec(segment);
|
|
111
|
+
if (catchAll?.[1])
|
|
112
|
+
return `[...${catchAll[1]}]`;
|
|
113
|
+
const param = /^:([A-Za-z0-9_]+)$/.exec(segment);
|
|
114
|
+
if (param?.[1])
|
|
115
|
+
return `[${param[1]}]`;
|
|
116
|
+
return segment;
|
|
117
|
+
}).join("/");
|
|
118
|
+
return [converted.replace(/\/$/, "") || "/"];
|
|
119
|
+
}
|
|
120
|
+
async function buildRouteTypesManifest({
|
|
121
|
+
config,
|
|
122
|
+
routes,
|
|
123
|
+
nextConfig
|
|
124
|
+
}) {
|
|
125
|
+
const relApp = (file) => toPosixPath(path.relative(config.root, file));
|
|
126
|
+
const isHybrid = config.appPath.includes("pnext-pages-compat");
|
|
127
|
+
const sourceAppPath = (entry) => {
|
|
128
|
+
if (!isHybrid)
|
|
129
|
+
return entry.file;
|
|
130
|
+
const basename = entry.kind === "page" ? /^page\.(?:tsx?|jsx?|mdx?)$/ : /^route\.(?:tsx?|jsx?)$/;
|
|
131
|
+
const source = entry.sourceFiles.find((file) => {
|
|
132
|
+
const normalized = toPosixPath(file);
|
|
133
|
+
return normalized.includes("/source-app/") && basename.test(path.basename(normalized));
|
|
134
|
+
});
|
|
135
|
+
if (!source)
|
|
136
|
+
return;
|
|
137
|
+
const relative = toPosixPath(source).split("/source-app/")[1];
|
|
138
|
+
return relative ? path.join(config.root, "app", relative) : undefined;
|
|
139
|
+
};
|
|
140
|
+
const manifest = {
|
|
141
|
+
appRoutes: {},
|
|
142
|
+
pageRoutes: {},
|
|
143
|
+
layoutRoutes: {},
|
|
144
|
+
appRouteHandlerRoutes: {},
|
|
145
|
+
redirectRoutes: {},
|
|
146
|
+
rewriteRoutes: {},
|
|
147
|
+
appPagePaths: new Set,
|
|
148
|
+
pagesRouterPagePaths: new Set,
|
|
149
|
+
layoutPaths: new Set,
|
|
150
|
+
appRouteHandlers: new Set,
|
|
151
|
+
pageApiRoutes: new Set,
|
|
152
|
+
filePathToRoute: new Map
|
|
153
|
+
};
|
|
154
|
+
const hybridFilteredRoutes = isHybrid ? routes.filter((entry) => {
|
|
155
|
+
const file = entry.file;
|
|
156
|
+
if (!file.includes("pnext-pages-compat"))
|
|
157
|
+
return true;
|
|
158
|
+
const sourceFiles = entry.sourceFiles ?? [];
|
|
159
|
+
const isPagesWrapper = sourceFiles.some((sf) => sf.includes("source-pages"));
|
|
160
|
+
if (isPagesWrapper)
|
|
161
|
+
return false;
|
|
162
|
+
return true;
|
|
163
|
+
}) : routes;
|
|
164
|
+
for (const entry of hybridFilteredRoutes) {
|
|
165
|
+
if (entry.synthetic || entry.interception || entry.metadataRoute)
|
|
166
|
+
continue;
|
|
167
|
+
const source = sourceAppPath(entry);
|
|
168
|
+
if (!source)
|
|
169
|
+
continue;
|
|
170
|
+
const bracket = bracketRouteFromEntry(entry);
|
|
171
|
+
const rel = relApp(source);
|
|
172
|
+
const info = { path: rel, groups: groupsFromBracketRoute(bracket) };
|
|
173
|
+
if (entry.kind === "page") {
|
|
174
|
+
if (!manifest.appRoutes[bracket])
|
|
175
|
+
manifest.appRoutes[bracket] = info;
|
|
176
|
+
} else {
|
|
177
|
+
if (!manifest.appRouteHandlerRoutes[bracket])
|
|
178
|
+
manifest.appRouteHandlerRoutes[bracket] = info;
|
|
179
|
+
manifest.appRouteHandlers.add(rel);
|
|
180
|
+
manifest.filePathToRoute.set(rel, bracket);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const appFiles = await walkFiles(isHybrid ? path.join(config.root, "app") : config.appPath);
|
|
184
|
+
const appPath = isHybrid ? path.join(config.root, "app") : config.appPath;
|
|
185
|
+
for (const file of appFiles) {
|
|
186
|
+
const relative = toPosixPath(path.relative(appPath, file));
|
|
187
|
+
const base = path.basename(relative);
|
|
188
|
+
if (!LAYOUT_NAMES.has(base))
|
|
189
|
+
continue;
|
|
190
|
+
const dir = path.dirname(relative) === "." ? "" : path.dirname(relative);
|
|
191
|
+
if (isPrivate(dir))
|
|
192
|
+
continue;
|
|
193
|
+
const bracket = bracketRouteFromDir(dir);
|
|
194
|
+
const rel = relApp(file);
|
|
195
|
+
if (!manifest.layoutRoutes[bracket]) {
|
|
196
|
+
manifest.layoutRoutes[bracket] = {
|
|
197
|
+
path: rel,
|
|
198
|
+
groups: groupsFromBracketRoute(bracket),
|
|
199
|
+
slots: []
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
manifest.layoutPaths.add(rel);
|
|
203
|
+
manifest.filePathToRoute.set(rel, bracket);
|
|
204
|
+
}
|
|
205
|
+
for (const file of appFiles) {
|
|
206
|
+
const relative = toPosixPath(path.relative(appPath, file));
|
|
207
|
+
const segments = relative.split("/");
|
|
208
|
+
let slotIndex = -1;
|
|
209
|
+
for (let i = segments.length - 1;i >= 0; i--) {
|
|
210
|
+
if (isSlotSegment(segments[i])) {
|
|
211
|
+
slotIndex = i;
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (slotIndex === -1)
|
|
216
|
+
continue;
|
|
217
|
+
const slotName = segments[slotIndex].slice(1);
|
|
218
|
+
const ownerDir = segments.slice(0, slotIndex).join("/");
|
|
219
|
+
if (isPrivate(ownerDir))
|
|
220
|
+
continue;
|
|
221
|
+
const ownerRoute = bracketRouteFromDir(ownerDir);
|
|
222
|
+
const layout = manifest.layoutRoutes[ownerRoute];
|
|
223
|
+
if (layout && !layout.slots.includes(slotName))
|
|
224
|
+
layout.slots.push(slotName);
|
|
225
|
+
}
|
|
226
|
+
const PAGE_BASENAMES = new Set(PAGE_EXTENSIONS.map((ext) => `page.${ext}`));
|
|
227
|
+
for (const file of appFiles) {
|
|
228
|
+
const relative = toPosixPath(path.relative(appPath, file));
|
|
229
|
+
if (!PAGE_BASENAMES.has(path.basename(relative)))
|
|
230
|
+
continue;
|
|
231
|
+
const dir = path.dirname(relative) === "." ? "" : path.dirname(relative);
|
|
232
|
+
if (isPrivate(dir))
|
|
233
|
+
continue;
|
|
234
|
+
const route = bracketRouteFromDir(dir);
|
|
235
|
+
if (!manifest.appRoutes[route])
|
|
236
|
+
continue;
|
|
237
|
+
const rel = relApp(file);
|
|
238
|
+
manifest.appPagePaths.add(rel);
|
|
239
|
+
manifest.filePathToRoute.set(rel, route);
|
|
240
|
+
}
|
|
241
|
+
const pagesDir = path.join(config.root, "pages");
|
|
242
|
+
for (const file of await walkFiles(pagesDir)) {
|
|
243
|
+
const relative = toPosixPath(path.relative(pagesDir, file));
|
|
244
|
+
if (!PAGE_EXTENSIONS.some((ext) => relative.endsWith(`.${ext}`)))
|
|
245
|
+
continue;
|
|
246
|
+
const base = path.basename(relative);
|
|
247
|
+
if (base.startsWith("_app.") || base.startsWith("_document.") || base.startsWith("_error.")) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
const rel = relApp(file);
|
|
251
|
+
const isApi = relative === "api" || relative.startsWith("api/");
|
|
252
|
+
const route = pagesRouteFromRelative(relative);
|
|
253
|
+
if (isApi) {
|
|
254
|
+
manifest.pageApiRoutes.add(rel);
|
|
255
|
+
manifest.filePathToRoute.set(rel, route);
|
|
256
|
+
} else {
|
|
257
|
+
manifest.pageRoutes[route] = { path: rel, groups: groupsFromBracketRoute(route) };
|
|
258
|
+
manifest.pagesRouterPagePaths.add(rel);
|
|
259
|
+
manifest.filePathToRoute.set(rel, route);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const redirects = nextConfig.redirects;
|
|
263
|
+
if (typeof redirects === "function") {
|
|
264
|
+
const list = await redirects();
|
|
265
|
+
for (const item of list ?? []) {
|
|
266
|
+
for (const route of convertCustomRouteSource(item.source)) {
|
|
267
|
+
manifest.redirectRoutes[route] = { path: route, groups: groupsFromBracketRoute(route) };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
const rewrites = nextConfig.rewrites;
|
|
272
|
+
if (typeof rewrites === "function") {
|
|
273
|
+
const result = await rewrites();
|
|
274
|
+
const sources = Array.isArray(result) ? result : [...result?.beforeFiles ?? [], ...result?.afterFiles ?? [], ...result?.fallback ?? []];
|
|
275
|
+
for (const item of sources) {
|
|
276
|
+
for (const route of convertCustomRouteSource(item.source)) {
|
|
277
|
+
manifest.rewriteRoutes[route] = { path: route, groups: groupsFromBracketRoute(route) };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return manifest;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/compat/typed-routes/typegen.ts
|
|
285
|
+
function isDynamicRoute(route) {
|
|
286
|
+
return /\[.+\]/.test(route);
|
|
287
|
+
}
|
|
288
|
+
function generateRouteTypes(manifest) {
|
|
289
|
+
const appRoutes = Object.keys(manifest.appRoutes).sort();
|
|
290
|
+
const pageRoutes = Object.keys(manifest.pageRoutes).sort();
|
|
291
|
+
const layoutRoutes = Object.keys(manifest.layoutRoutes).sort();
|
|
292
|
+
const redirectRoutes = Object.keys(manifest.redirectRoutes).sort();
|
|
293
|
+
const rewriteRoutes = Object.keys(manifest.rewriteRoutes).sort();
|
|
294
|
+
const appRouteHandlerRoutes = Object.keys(manifest.appRouteHandlerRoutes).sort();
|
|
295
|
+
const hasAppRouteHandlers = appRouteHandlerRoutes.length > 0;
|
|
296
|
+
const union = (list) => list.map((route) => JSON.stringify(route)).join(" | ");
|
|
297
|
+
let result = "";
|
|
298
|
+
result += appRoutes.length > 0 ? `type AppRoutes = ${union(appRoutes)}
|
|
299
|
+
` : `type AppRoutes = never
|
|
300
|
+
`;
|
|
301
|
+
if (hasAppRouteHandlers) {
|
|
302
|
+
result += `type AppRouteHandlerRoutes = ${union(appRouteHandlerRoutes)}
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
result += pageRoutes.length > 0 ? `type PageRoutes = ${union(pageRoutes)}
|
|
306
|
+
` : `type PageRoutes = never
|
|
307
|
+
`;
|
|
308
|
+
result += layoutRoutes.length > 0 ? `type LayoutRoutes = ${union(layoutRoutes)}
|
|
309
|
+
` : `type LayoutRoutes = never
|
|
310
|
+
`;
|
|
311
|
+
result += redirectRoutes.length > 0 ? `type RedirectRoutes = ${union(redirectRoutes)}
|
|
312
|
+
` : `type RedirectRoutes = never
|
|
313
|
+
`;
|
|
314
|
+
result += rewriteRoutes.length > 0 ? `type RewriteRoutes = ${union(rewriteRoutes)}
|
|
315
|
+
` : `type RewriteRoutes = never
|
|
316
|
+
`;
|
|
317
|
+
const routeUnionParts = [
|
|
318
|
+
"AppRoutes",
|
|
319
|
+
"PageRoutes",
|
|
320
|
+
"LayoutRoutes",
|
|
321
|
+
"RedirectRoutes",
|
|
322
|
+
"RewriteRoutes"
|
|
323
|
+
];
|
|
324
|
+
if (hasAppRouteHandlers)
|
|
325
|
+
routeUnionParts.push("AppRouteHandlerRoutes");
|
|
326
|
+
result += `type Routes = ${routeUnionParts.join(" | ")}
|
|
327
|
+
`;
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
function generateParamTypes(manifest) {
|
|
331
|
+
const allRoutes = {
|
|
332
|
+
...manifest.appRoutes,
|
|
333
|
+
...manifest.appRouteHandlerRoutes,
|
|
334
|
+
...manifest.pageRoutes,
|
|
335
|
+
...manifest.layoutRoutes,
|
|
336
|
+
...manifest.redirectRoutes,
|
|
337
|
+
...manifest.rewriteRoutes
|
|
338
|
+
};
|
|
339
|
+
let paramTypes = `interface ParamMap {
|
|
340
|
+
`;
|
|
341
|
+
const sortedRoutes = Object.entries(allRoutes).sort(([a], [b]) => a.localeCompare(b));
|
|
342
|
+
for (const [route, routeInfo] of sortedRoutes) {
|
|
343
|
+
const groups = routeInfo.groups;
|
|
344
|
+
if (!isDynamicRoute(route) || Object.keys(groups ?? {}).length === 0) {
|
|
345
|
+
paramTypes += ` ${JSON.stringify(route)}: {}
|
|
346
|
+
`;
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
let paramType = "{";
|
|
350
|
+
for (const [key, group] of Object.entries(groups)) {
|
|
351
|
+
const escapedKey = JSON.stringify(key);
|
|
352
|
+
if (group.repeat) {
|
|
353
|
+
paramType += group.optional ? ` ${escapedKey}?: string[];` : ` ${escapedKey}: string[];`;
|
|
354
|
+
} else {
|
|
355
|
+
paramType += group.optional ? ` ${escapedKey}?: string;` : ` ${escapedKey}: string;`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
paramType += " }";
|
|
359
|
+
paramTypes += ` ${JSON.stringify(route)}: ${paramType}
|
|
360
|
+
`;
|
|
361
|
+
}
|
|
362
|
+
paramTypes += `}
|
|
363
|
+
`;
|
|
364
|
+
return paramTypes;
|
|
365
|
+
}
|
|
366
|
+
function generateLayoutSlotMap(manifest) {
|
|
367
|
+
let slotMap = `interface LayoutSlotMap {
|
|
368
|
+
`;
|
|
369
|
+
const sorted = Object.entries(manifest.layoutRoutes).sort(([a], [b]) => a.localeCompare(b));
|
|
370
|
+
for (const [route, routeInfo] of sorted) {
|
|
371
|
+
const slots = [...routeInfo.slots].sort();
|
|
372
|
+
slotMap += slots.length > 0 ? ` ${JSON.stringify(route)}: ${slots.map((slot) => JSON.stringify(slot)).join(" | ")}
|
|
373
|
+
` : ` ${JSON.stringify(route)}: never
|
|
374
|
+
`;
|
|
375
|
+
}
|
|
376
|
+
slotMap += `}
|
|
377
|
+
`;
|
|
378
|
+
return slotMap;
|
|
379
|
+
}
|
|
380
|
+
function formatRouteToRouteType(route) {
|
|
381
|
+
const isDynamic = isDynamicRoute(route);
|
|
382
|
+
let routeType = route;
|
|
383
|
+
if (isDynamic) {
|
|
384
|
+
routeType = route.split("/").map((part) => {
|
|
385
|
+
if (part.startsWith("[") && part.endsWith("]")) {
|
|
386
|
+
if (part.startsWith("[[...") && part.endsWith("]]"))
|
|
387
|
+
return "${OptionalCatchAllSlug<T>}";
|
|
388
|
+
if (part.startsWith("[..."))
|
|
389
|
+
return "${CatchAllSlug<T>}";
|
|
390
|
+
return "${SafeSlug<T>}";
|
|
391
|
+
}
|
|
392
|
+
return part;
|
|
393
|
+
}).join("/");
|
|
394
|
+
}
|
|
395
|
+
return { isDynamic, routeType };
|
|
396
|
+
}
|
|
397
|
+
function serializeRouteTypes(routeTypes) {
|
|
398
|
+
routeTypes.sort(([a], [b]) => a.localeCompare(b));
|
|
399
|
+
let union = "";
|
|
400
|
+
for (const [route, cause] of routeTypes) {
|
|
401
|
+
union += `
|
|
402
|
+
| \`${route}\` // ${cause}`;
|
|
403
|
+
}
|
|
404
|
+
return union;
|
|
405
|
+
}
|
|
406
|
+
function generateRouteTypesFile(manifest) {
|
|
407
|
+
const routeTypes = generateRouteTypes(manifest);
|
|
408
|
+
const paramTypes = generateParamTypes(manifest);
|
|
409
|
+
const layoutSlotMap = generateLayoutSlotMap(manifest);
|
|
410
|
+
const hasAppRouteHandlers = Object.keys(manifest.appRouteHandlerRoutes).length > 0;
|
|
411
|
+
const routeExports = [
|
|
412
|
+
"AppRoutes",
|
|
413
|
+
"PageRoutes",
|
|
414
|
+
"LayoutRoutes",
|
|
415
|
+
"RedirectRoutes",
|
|
416
|
+
"RewriteRoutes",
|
|
417
|
+
"ParamMap"
|
|
418
|
+
];
|
|
419
|
+
if (hasAppRouteHandlers)
|
|
420
|
+
routeExports.push("AppRouteHandlerRoutes");
|
|
421
|
+
const exportStatement = `export type { ${routeExports.join(", ")} }`;
|
|
422
|
+
const routeContextInterface = hasAppRouteHandlers ? `
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Context for Next.js App Router route handlers
|
|
426
|
+
* @example
|
|
427
|
+
* \`\`\`tsx
|
|
428
|
+
* export async function GET(request: NextRequest, context: RouteContext<'/api/users/[id]'>) {
|
|
429
|
+
* const { id } = await context.params
|
|
430
|
+
* return Response.json({ id })
|
|
431
|
+
* }
|
|
432
|
+
* \`\`\`
|
|
433
|
+
*/
|
|
434
|
+
interface RouteContext<AppRouteHandlerRoute extends AppRouteHandlerRoutes> {
|
|
435
|
+
params: Promise<ParamMap[AppRouteHandlerRoute]>
|
|
436
|
+
}` : "";
|
|
437
|
+
return `// This file is generated automatically by Next.js
|
|
438
|
+
// Do not edit this file manually
|
|
439
|
+
|
|
440
|
+
${routeTypes}
|
|
441
|
+
|
|
442
|
+
${paramTypes}
|
|
443
|
+
|
|
444
|
+
export type ParamsOf<Route extends Routes> = ParamMap[Route]
|
|
445
|
+
|
|
446
|
+
${layoutSlotMap}
|
|
447
|
+
|
|
448
|
+
${exportStatement}
|
|
449
|
+
|
|
450
|
+
declare global {
|
|
451
|
+
/**
|
|
452
|
+
* Props for Next.js App Router page components
|
|
453
|
+
* @example
|
|
454
|
+
* \`\`\`tsx
|
|
455
|
+
* export default function Page(props: PageProps<'/blog/[slug]'>) {
|
|
456
|
+
* const { slug } = await props.params
|
|
457
|
+
* return <div>Blog post: {slug}</div>
|
|
458
|
+
* }
|
|
459
|
+
* \`\`\`
|
|
460
|
+
*/
|
|
461
|
+
interface PageProps<AppRoute extends AppRoutes> {
|
|
462
|
+
params: Promise<ParamMap[AppRoute]>
|
|
463
|
+
searchParams: Promise<Record<string, string | string[] | undefined>>
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Props for Next.js App Router layout components
|
|
468
|
+
* @example
|
|
469
|
+
* \`\`\`tsx
|
|
470
|
+
* export default function Layout(props: LayoutProps<'/dashboard'>) {
|
|
471
|
+
* return <div>{props.children}</div>
|
|
472
|
+
* }
|
|
473
|
+
* \`\`\`
|
|
474
|
+
*/
|
|
475
|
+
type LayoutProps<LayoutRoute extends LayoutRoutes> = {
|
|
476
|
+
params: Promise<ParamMap[LayoutRoute]>
|
|
477
|
+
searchParams: Promise<Record<string, string | string[] | undefined>>
|
|
478
|
+
children: React.ReactNode
|
|
479
|
+
} & {
|
|
480
|
+
[K in LayoutSlotMap[LayoutRoute]]: React.ReactNode
|
|
481
|
+
}${routeContextInterface}
|
|
482
|
+
}
|
|
483
|
+
`;
|
|
484
|
+
}
|
|
485
|
+
function generateLinkTypesFile(manifest) {
|
|
486
|
+
const visited = new Set;
|
|
487
|
+
const staticRouteTypes = [];
|
|
488
|
+
const dynamicRouteTypes = [];
|
|
489
|
+
for (const routeMap of [
|
|
490
|
+
manifest.appRoutes,
|
|
491
|
+
manifest.pageRoutes,
|
|
492
|
+
manifest.redirectRoutes,
|
|
493
|
+
manifest.rewriteRoutes,
|
|
494
|
+
manifest.appRouteHandlerRoutes
|
|
495
|
+
]) {
|
|
496
|
+
for (const route in routeMap) {
|
|
497
|
+
if (visited.has(route))
|
|
498
|
+
continue;
|
|
499
|
+
visited.add(route);
|
|
500
|
+
const { isDynamic, routeType } = formatRouteToRouteType(route);
|
|
501
|
+
const cause = routeMap[route].path;
|
|
502
|
+
(isDynamic ? dynamicRouteTypes : staticRouteTypes).push([routeType, cause]);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
for (const filePath of manifest.pageApiRoutes) {
|
|
506
|
+
if (visited.has(filePath))
|
|
507
|
+
continue;
|
|
508
|
+
visited.add(filePath);
|
|
509
|
+
const { isDynamic, routeType } = formatRouteToRouteType(filePath);
|
|
510
|
+
(isDynamic ? dynamicRouteTypes : staticRouteTypes).push([routeType, filePath]);
|
|
511
|
+
}
|
|
512
|
+
const serializedStaticRouteTypes = serializeRouteTypes(staticRouteTypes);
|
|
513
|
+
const serializedDynamicRouteTypes = serializeRouteTypes(dynamicRouteTypes);
|
|
514
|
+
const routeTypesFallback = !serializedStaticRouteTypes && !serializedDynamicRouteTypes ? "string & {}" : "";
|
|
515
|
+
return `// This file is generated automatically by Next.js
|
|
516
|
+
// Do not edit this file manually
|
|
517
|
+
|
|
518
|
+
// Type definitions for Next.js routes
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Internal types used by the Next.js router and Link component.
|
|
522
|
+
* These types are not meant to be used directly.
|
|
523
|
+
* @internal
|
|
524
|
+
*/
|
|
525
|
+
declare namespace __next_route_internal_types__ {
|
|
526
|
+
type SearchOrHash = \`?\${string}\` | \`#\${string}\`
|
|
527
|
+
type WithProtocol = \`\${string}:\${string}\`
|
|
528
|
+
|
|
529
|
+
type Suffix = '' | SearchOrHash
|
|
530
|
+
|
|
531
|
+
type SafeSlug<S extends string> = S extends \`\${string}/\${string}\`
|
|
532
|
+
? never
|
|
533
|
+
: S extends \`\${string}\${SearchOrHash}\`
|
|
534
|
+
? never
|
|
535
|
+
: S extends ''
|
|
536
|
+
? never
|
|
537
|
+
: S
|
|
538
|
+
|
|
539
|
+
type CatchAllSlug<S extends string> = S extends \`\${string}\${SearchOrHash}\`
|
|
540
|
+
? never
|
|
541
|
+
: S extends ''
|
|
542
|
+
? never
|
|
543
|
+
: S
|
|
544
|
+
|
|
545
|
+
type OptionalCatchAllSlug<S extends string> =
|
|
546
|
+
S extends \`\${string}\${SearchOrHash}\` ? never : S
|
|
547
|
+
|
|
548
|
+
type StaticRoutes = ${serializedStaticRouteTypes || "never"}
|
|
549
|
+
type DynamicRoutes<T extends string = string> = ${serializedDynamicRouteTypes || "never"}
|
|
550
|
+
|
|
551
|
+
type RouteImpl<T> = ${routeTypesFallback || `
|
|
552
|
+
${"| StaticRoutes"}
|
|
553
|
+
| SearchOrHash
|
|
554
|
+
| WithProtocol
|
|
555
|
+
| \`\${StaticRoutes}\${SearchOrHash}\`
|
|
556
|
+
| (T extends \`\${DynamicRoutes<infer _>}\${Suffix}\` ? T : never)
|
|
557
|
+
`}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
declare module 'next' {
|
|
561
|
+
export { default } from 'next/types.js'
|
|
562
|
+
export * from 'next/types.js'
|
|
563
|
+
|
|
564
|
+
export type Route<T extends string = string> =
|
|
565
|
+
__next_route_internal_types__.RouteImpl<T>
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
declare module 'next/link' {
|
|
569
|
+
export { useLinkStatus } from 'next/dist/client/link.js'
|
|
570
|
+
|
|
571
|
+
import type { LinkProps as OriginalLinkProps } from 'next/dist/client/link.js'
|
|
572
|
+
import type { AnchorHTMLAttributes, DetailedHTMLProps } from 'react'
|
|
573
|
+
import type { UrlObject } from 'url'
|
|
574
|
+
|
|
575
|
+
type LinkRestProps = Omit<
|
|
576
|
+
Omit<
|
|
577
|
+
DetailedHTMLProps<
|
|
578
|
+
AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
579
|
+
HTMLAnchorElement
|
|
580
|
+
>,
|
|
581
|
+
keyof OriginalLinkProps
|
|
582
|
+
> &
|
|
583
|
+
OriginalLinkProps,
|
|
584
|
+
'href'
|
|
585
|
+
>
|
|
586
|
+
|
|
587
|
+
export type LinkProps<RouteInferType> = LinkRestProps & {
|
|
588
|
+
/**
|
|
589
|
+
* The path or URL to navigate to. This is the only required prop. It can also be an object.
|
|
590
|
+
* @see https://nextjs.org/docs/api-reference/next/link
|
|
591
|
+
*/
|
|
592
|
+
href: __next_route_internal_types__.RouteImpl<RouteInferType> | UrlObject
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export default function Link<RouteType>(props: LinkProps<RouteType>): JSX.Element
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
declare module 'next/navigation' {
|
|
599
|
+
export * from 'next/dist/client/components/navigation.js'
|
|
600
|
+
|
|
601
|
+
import type { NavigateOptions, PrefetchOptions, AppRouterInstance as OriginalAppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime.js'
|
|
602
|
+
import type { RedirectType } from 'next/dist/client/components/redirect-error.js'
|
|
603
|
+
|
|
604
|
+
interface AppRouterInstance extends OriginalAppRouterInstance {
|
|
605
|
+
/**
|
|
606
|
+
* Navigate to the provided href.
|
|
607
|
+
* Pushes a new history entry.
|
|
608
|
+
*/
|
|
609
|
+
push<RouteType>(href: __next_route_internal_types__.RouteImpl<RouteType>, options?: NavigateOptions): void
|
|
610
|
+
/**
|
|
611
|
+
* Navigate to the provided href.
|
|
612
|
+
* Replaces the current history entry.
|
|
613
|
+
*/
|
|
614
|
+
replace<RouteType>(href: __next_route_internal_types__.RouteImpl<RouteType>, options?: NavigateOptions): void
|
|
615
|
+
/**
|
|
616
|
+
* Prefetch the provided href.
|
|
617
|
+
*/
|
|
618
|
+
prefetch<RouteType>(href: __next_route_internal_types__.RouteImpl<RouteType>, options?: PrefetchOptions): void
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function useRouter(): AppRouterInstance;
|
|
622
|
+
|
|
623
|
+
export function redirect<RouteType>(
|
|
624
|
+
url: __next_route_internal_types__.RouteImpl<RouteType>,
|
|
625
|
+
type?: RedirectType
|
|
626
|
+
): never;
|
|
627
|
+
|
|
628
|
+
export function permanentRedirect<RouteType>(
|
|
629
|
+
url: __next_route_internal_types__.RouteImpl<RouteType>,
|
|
630
|
+
type?: RedirectType
|
|
631
|
+
): never;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
declare module 'next/form' {
|
|
635
|
+
import type { FormProps as OriginalFormProps } from 'next/dist/client/form.js'
|
|
636
|
+
|
|
637
|
+
type FormRestProps = Omit<OriginalFormProps, 'action'>
|
|
638
|
+
|
|
639
|
+
export type FormProps<RouteInferType> = {
|
|
640
|
+
action: __next_route_internal_types__.RouteImpl<RouteInferType> | ((formData: FormData) => void)
|
|
641
|
+
} & FormRestProps
|
|
642
|
+
|
|
643
|
+
export default function Form<RouteType>(props: FormProps<RouteType>): JSX.Element
|
|
644
|
+
}
|
|
645
|
+
`;
|
|
646
|
+
}
|
|
647
|
+
function generateValidatorFile(manifest, toImportSpecifier) {
|
|
648
|
+
const generateValidations = (paths, type, pathToRouteMap) => paths.sort().filter((filePath) => filePath.endsWith(".ts") || filePath.endsWith(".tsx")).filter((filePath) => type !== "AppPageConfig" || filePath.endsWith("page.ts") || filePath.endsWith("page.tsx")).map((filePath) => {
|
|
649
|
+
const importPath = toImportSpecifier(filePath);
|
|
650
|
+
const route = pathToRouteMap?.get(filePath);
|
|
651
|
+
const typeWithRoute = route && (type === "AppPageConfig" || type === "LayoutConfig" || type === "RouteHandlerConfig") ? `${type}<${JSON.stringify(route)}>` : type;
|
|
652
|
+
return `// Validate ${filePath}
|
|
653
|
+
{
|
|
654
|
+
type __IsExpected<Specific extends ${typeWithRoute}> = Specific
|
|
655
|
+
const handler = {} as typeof import(${JSON.stringify(importPath.replace(/\.tsx?$/, ".js"))})
|
|
656
|
+
type __Check = __IsExpected<typeof handler>
|
|
657
|
+
// @ts-ignore
|
|
658
|
+
type __Unused = __Check
|
|
659
|
+
}`;
|
|
660
|
+
}).join(`
|
|
661
|
+
|
|
662
|
+
`);
|
|
663
|
+
const appPageValidations = generateValidations([...manifest.appPagePaths].sort(), "AppPageConfig", manifest.filePathToRoute);
|
|
664
|
+
const appRouteHandlerValidations = generateValidations([...manifest.appRouteHandlers].sort(), "RouteHandlerConfig", manifest.filePathToRoute);
|
|
665
|
+
const pagesRouterPageValidations = generateValidations([...manifest.pagesRouterPagePaths].sort(), "PagesPageConfig");
|
|
666
|
+
const pagesApiRouteValidations = generateValidations([...manifest.pageApiRoutes].sort(), "ApiRouteConfig");
|
|
667
|
+
const layoutValidations = generateValidations([...manifest.layoutPaths].sort(), "LayoutConfig", manifest.filePathToRoute);
|
|
668
|
+
const hasAppRouteHandlers = Object.keys(manifest.appRouteHandlerRoutes).length > 0;
|
|
669
|
+
let typeDefinitions = "";
|
|
670
|
+
if (appPageValidations) {
|
|
671
|
+
typeDefinitions += `type AppPageConfig<Route extends AppRoutes = AppRoutes> = {
|
|
672
|
+
default: React.JSXElementConstructor<PageProps<Route>> | ((props: { params: Promise<ParamMap[Route]>, searchParams: Promise<any> }) => React.ReactNode | Promise<React.ReactNode> | never | void | Promise<void>)
|
|
673
|
+
generateStaticParams?: (props: { params: ParamMap[Route] }) => Promise<any[]> | any[]
|
|
674
|
+
generateMetadata?: (
|
|
675
|
+
props: { params: Promise<ParamMap[Route]> } & any,
|
|
676
|
+
parent: ResolvingMetadata
|
|
677
|
+
) => Promise<any> | any
|
|
678
|
+
generateViewport?: (
|
|
679
|
+
props: { params: Promise<ParamMap[Route]> } & any,
|
|
680
|
+
parent: ResolvingViewport
|
|
681
|
+
) => Promise<any> | any
|
|
682
|
+
metadata?: any
|
|
683
|
+
viewport?: any
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
`;
|
|
687
|
+
}
|
|
688
|
+
if (pagesRouterPageValidations) {
|
|
689
|
+
typeDefinitions += `type PagesPageConfig = {
|
|
690
|
+
default: React.ComponentType<any> | ((props: any) => React.ReactNode | Promise<React.ReactNode> | never | void)
|
|
691
|
+
getStaticProps?: (context: any) => Promise<any> | any
|
|
692
|
+
getStaticPaths?: (context: any) => Promise<any> | any
|
|
693
|
+
getServerSideProps?: (context: any) => Promise<any> | any
|
|
694
|
+
getInitialProps?: (context: any) => Promise<any> | any
|
|
695
|
+
/**
|
|
696
|
+
* Segment configuration for legacy Pages Router pages.
|
|
697
|
+
* Validated at build-time by parsePagesSegmentConfig.
|
|
698
|
+
*/
|
|
699
|
+
config?: {
|
|
700
|
+
maxDuration?: number
|
|
701
|
+
runtime?: 'edge' | 'experimental-edge' | 'nodejs' | string // necessary unless config is exported as const
|
|
702
|
+
regions?: string[]
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
`;
|
|
707
|
+
}
|
|
708
|
+
if (layoutValidations) {
|
|
709
|
+
typeDefinitions += `type LayoutConfig<Route extends LayoutRoutes = LayoutRoutes> = {
|
|
710
|
+
default: React.ComponentType<LayoutProps<Route>> | ((props: LayoutProps<Route>) => React.ReactNode | Promise<React.ReactNode> | never | void | Promise<void>)
|
|
711
|
+
generateStaticParams?: (props: { params: ParamMap[Route] }) => Promise<any[]> | any[]
|
|
712
|
+
generateMetadata?: (
|
|
713
|
+
props: { params: Promise<ParamMap[Route]> } & any,
|
|
714
|
+
parent: ResolvingMetadata
|
|
715
|
+
) => Promise<any> | any
|
|
716
|
+
generateViewport?: (
|
|
717
|
+
props: { params: Promise<ParamMap[Route]> } & any,
|
|
718
|
+
parent: ResolvingViewport
|
|
719
|
+
) => Promise<any> | any
|
|
720
|
+
metadata?: any
|
|
721
|
+
viewport?: any
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
`;
|
|
725
|
+
}
|
|
726
|
+
if (appRouteHandlerValidations) {
|
|
727
|
+
typeDefinitions += `type RouteHandlerConfig<Route extends AppRouteHandlerRoutes = AppRouteHandlerRoutes> = {
|
|
728
|
+
GET?: (request: NextRequest, context: { params: Promise<ParamMap[Route]> }) => Promise<Response | void> | Response | void
|
|
729
|
+
POST?: (request: NextRequest, context: { params: Promise<ParamMap[Route]> }) => Promise<Response | void> | Response | void
|
|
730
|
+
PUT?: (request: NextRequest, context: { params: Promise<ParamMap[Route]> }) => Promise<Response | void> | Response | void
|
|
731
|
+
PATCH?: (request: NextRequest, context: { params: Promise<ParamMap[Route]> }) => Promise<Response | void> | Response | void
|
|
732
|
+
DELETE?: (request: NextRequest, context: { params: Promise<ParamMap[Route]> }) => Promise<Response | void> | Response | void
|
|
733
|
+
HEAD?: (request: NextRequest, context: { params: Promise<ParamMap[Route]> }) => Promise<Response | void> | Response | void
|
|
734
|
+
OPTIONS?: (request: NextRequest, context: { params: Promise<ParamMap[Route]> }) => Promise<Response | void> | Response | void
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
`;
|
|
738
|
+
}
|
|
739
|
+
if (pagesApiRouteValidations) {
|
|
740
|
+
typeDefinitions += `type ApiRouteConfig = {
|
|
741
|
+
default: (req: any, res: any) => ReturnType<NextApiHandler>
|
|
742
|
+
config?: {
|
|
743
|
+
api?: {
|
|
744
|
+
bodyParser?: boolean | { sizeLimit?: number | string }
|
|
745
|
+
responseLimit?: string | number | boolean
|
|
746
|
+
externalResolver?: boolean
|
|
747
|
+
}
|
|
748
|
+
runtime?: 'edge' | 'experimental-edge' | 'nodejs' | string // necessary unless config is exported as const
|
|
749
|
+
maxDuration?: number
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
`;
|
|
754
|
+
}
|
|
755
|
+
const routeImports = [];
|
|
756
|
+
if (appPageValidations)
|
|
757
|
+
routeImports.push("AppRoutes");
|
|
758
|
+
if (layoutValidations)
|
|
759
|
+
routeImports.push("LayoutRoutes");
|
|
760
|
+
if (appPageValidations || layoutValidations || appRouteHandlerValidations)
|
|
761
|
+
routeImports.push("ParamMap");
|
|
762
|
+
if (hasAppRouteHandlers)
|
|
763
|
+
routeImports.push("AppRouteHandlerRoutes");
|
|
764
|
+
const routeImportStatement = routeImports.length > 0 ? `import type { ${routeImports.join(", ")} } from "./routes.js"` : "";
|
|
765
|
+
const nextRequestImport = hasAppRouteHandlers ? `import type { NextRequest } from 'next/server.js'
|
|
766
|
+
` : "";
|
|
767
|
+
const nextTypes = [];
|
|
768
|
+
if (pagesApiRouteValidations)
|
|
769
|
+
nextTypes.push("NextApiHandler");
|
|
770
|
+
if (appPageValidations || layoutValidations)
|
|
771
|
+
nextTypes.push("ResolvingMetadata", "ResolvingViewport");
|
|
772
|
+
const nextTypesImport = nextTypes.length > 0 ? `import type { ${nextTypes.join(", ")} } from "next/types.js"
|
|
773
|
+
` : "";
|
|
774
|
+
return `// This file is generated automatically by Next.js
|
|
775
|
+
// Do not edit this file manually
|
|
776
|
+
// This file validates that all pages and layouts export the correct types
|
|
777
|
+
|
|
778
|
+
${routeImportStatement}
|
|
779
|
+
${nextTypesImport}${nextRequestImport}
|
|
780
|
+
${typeDefinitions}
|
|
781
|
+
${appPageValidations}
|
|
782
|
+
|
|
783
|
+
${appRouteHandlerValidations}
|
|
784
|
+
|
|
785
|
+
${pagesRouterPageValidations}
|
|
786
|
+
|
|
787
|
+
${pagesApiRouteValidations}
|
|
788
|
+
|
|
789
|
+
${layoutValidations}
|
|
790
|
+
`;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// src/compat/typed-routes/generate.ts
|
|
794
|
+
async function writeIfChanged(file, source) {
|
|
795
|
+
if (existsSync2(file) && await readFile(file, "utf8") === source)
|
|
796
|
+
return;
|
|
797
|
+
await ensureDir(path2.dirname(file));
|
|
798
|
+
await writeFile(file, source);
|
|
799
|
+
}
|
|
800
|
+
async function generateTypedRoutes(config, routes) {
|
|
801
|
+
const nextConfig = getNextConfig();
|
|
802
|
+
const typedRoutes = nextConfig.typedRoutes === true;
|
|
803
|
+
const manifest = await buildRouteTypesManifest({ config, routes, nextConfig });
|
|
804
|
+
const typesDir = path2.join(config.root, ".next", "types");
|
|
805
|
+
await writeIfChanged(path2.join(typesDir, "routes.d.ts"), generateRouteTypesFile(manifest));
|
|
806
|
+
const toImportSpecifier = (rootRelativePath) => {
|
|
807
|
+
const abs = path2.join(config.root, rootRelativePath);
|
|
808
|
+
const rel = toPosixPath(path2.relative(typesDir, abs));
|
|
809
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
810
|
+
};
|
|
811
|
+
await writeIfChanged(path2.join(typesDir, "validator.ts"), generateValidatorFile(manifest, toImportSpecifier));
|
|
812
|
+
if (typedRoutes) {
|
|
813
|
+
await writeIfChanged(path2.join(typesDir, "link.d.ts"), generateLinkTypesFile(manifest));
|
|
814
|
+
}
|
|
815
|
+
await writeIfChanged(path2.join(typesDir, "root-params.d.ts"), rootParamsTypesFile(scanRootParamTypes(path2.join(config.root, "app"))));
|
|
816
|
+
await writeNextEnv(config.root, typedRoutes);
|
|
817
|
+
}
|
|
818
|
+
function rootParamsTypesFile(rootParams) {
|
|
819
|
+
if (rootParams.size === 0) {
|
|
820
|
+
return `// Type definitions for Next.js root params (next/root-params)
|
|
821
|
+
// No root params detected.
|
|
822
|
+
export {}
|
|
823
|
+
`;
|
|
824
|
+
}
|
|
825
|
+
const order = ["string", "string[]", "undefined"];
|
|
826
|
+
const getters = [...rootParams.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([name, types]) => {
|
|
827
|
+
const union = order.filter((type) => types.has(type)).join(" | ");
|
|
828
|
+
return ` export function ${name}(): Promise<${union}>`;
|
|
829
|
+
});
|
|
830
|
+
return `// Type definitions for Next.js root params (next/root-params)
|
|
831
|
+
|
|
832
|
+
declare module 'next/root-params' {
|
|
833
|
+
${getters.join(`
|
|
834
|
+
`)}
|
|
835
|
+
}
|
|
836
|
+
`;
|
|
837
|
+
}
|
|
838
|
+
async function writeNextEnv(root, typedRoutes) {
|
|
839
|
+
const lines = [
|
|
840
|
+
'/// <reference types="next" />',
|
|
841
|
+
'import "./.next/types/routes.d.ts";',
|
|
842
|
+
'import "./.next/types/root-params.d.ts";'
|
|
843
|
+
];
|
|
844
|
+
if (typedRoutes)
|
|
845
|
+
lines.push('import "./.next/types/link.d.ts";');
|
|
846
|
+
lines.push("", "// NOTE: This file should not be edited", "");
|
|
847
|
+
await writeIfChanged(path2.join(root, "next-env.d.ts"), lines.join(`
|
|
848
|
+
`));
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
export { generateTypedRoutes };
|