@wular/pnext 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -0
- package/bin/pnext +67 -0
- package/config/lint/base.js +48 -0
- package/config/ts/base.json +26 -0
- package/config/ts/react.json +13 -0
- package/package.json +70 -0
- package/reference/compat.md +63 -0
- package/reference/config.md +120 -0
- package/reference/css.md +58 -0
- package/reference/dev.md +69 -0
- package/reference/env.md +40 -0
- package/reference/metadata.md +86 -0
- package/reference/navigation.md +149 -0
- package/reference/overview.md +35 -0
- package/reference/performance.md +97 -0
- package/reference/rendering.md +127 -0
- package/reference/routing.md +167 -0
- package/reference/typegen.md +64 -0
- package/src/api/cache.ts +80 -0
- package/src/api/client-cache.ts +9 -0
- package/src/api/client-navigation.ts +279 -0
- package/src/api/dynamic.tsx +102 -0
- package/src/api/link.tsx +119 -0
- package/src/api/navigation.ts +198 -0
- package/src/api/router/events.ts +53 -0
- package/src/api/router/history.ts +70 -0
- package/src/api/router/hub.ts +194 -0
- package/src/api/router/policies.ts +107 -0
- package/src/api/router/runtime.ts +5238 -0
- package/src/api/router/types.ts +299 -0
- package/src/api/router.ts +167 -0
- package/src/api/server.ts +323 -0
- package/src/api/suspense.ts +16 -0
- package/src/cache/context.ts +61 -0
- package/src/cli/adapters/vercel-warm.ts +375 -0
- package/src/cli/adapters/vercel.ts +1310 -0
- package/src/cli/analyze-print.ts +181 -0
- package/src/cli/analyze.ts +328 -0
- package/src/cli/boot-trace.ts +29 -0
- package/src/cli/build.ts +3114 -0
- package/src/cli/dev.ts +276 -0
- package/src/cli/index.ts +196 -0
- package/src/cli/named-bin.ts +119 -0
- package/src/cli/serve-ui.ts +160 -0
- package/src/cli/start.ts +1425 -0
- package/src/client/build.ts +2136 -0
- package/src/client/chunk-fold.ts +526 -0
- package/src/client/entry.ts +1525 -0
- package/src/client/paths.ts +22 -0
- package/src/client/prebuilt.ts +621 -0
- package/src/client/profile.ts +75 -0
- package/src/client/react-compiler.ts +94 -0
- package/src/client/reference-stub.ts +145 -0
- package/src/client/reference.ts +47 -0
- package/src/compat/actions/action-client.ts +531 -0
- package/src/compat/actions/action-dispatch.ts +676 -0
- package/src/compat/actions/action-router.ts +40 -0
- package/src/compat/actions/action-shared.ts +95 -0
- package/src/compat/actions/client-plugin.ts +135 -0
- package/src/compat/actions/client-stub.ts +65 -0
- package/src/compat/actions/config.ts +164 -0
- package/src/compat/actions/detect.ts +208 -0
- package/src/compat/actions/discovery.ts +244 -0
- package/src/compat/actions/early-submit.ts +40 -0
- package/src/compat/actions/endpoint.ts +602 -0
- package/src/compat/actions/flight.ts +52 -0
- package/src/compat/actions/form-state.ts +73 -0
- package/src/compat/actions/hoist.ts +485 -0
- package/src/compat/actions/ids.ts +39 -0
- package/src/compat/actions/index.ts +41 -0
- package/src/compat/actions/instances.ts +144 -0
- package/src/compat/actions/origin.ts +109 -0
- package/src/compat/actions/protocol.ts +125 -0
- package/src/compat/actions/registry.ts +74 -0
- package/src/compat/actions/rewrite.ts +282 -0
- package/src/compat/actions/serve.ts +414 -0
- package/src/compat/actions/server-tag.ts +21 -0
- package/src/compat/actions/unrecognized-error.ts +30 -0
- package/src/compat/adapter/build-complete.ts +257 -0
- package/src/compat/bundler/bun-externals.ts +53 -0
- package/src/compat/bundler/cjs-exports.ts +542 -0
- package/src/compat/bundler/config.ts +363 -0
- package/src/compat/bundler/externals.ts +34 -0
- package/src/compat/bundler/import-meta-url.ts +60 -0
- package/src/compat/bundler/modularize-imports.ts +119 -0
- package/src/compat/bundler/new-url-asset.ts +87 -0
- package/src/compat/bundler/optimize-package-imports.ts +273 -0
- package/src/compat/bundler/polyfill.ts +88 -0
- package/src/compat/bundler/react-compiler.ts +61 -0
- package/src/compat/bundler/react-profiler.tsx +25 -0
- package/src/compat/bundler/relay-transform.ts +116 -0
- package/src/compat/bundler/require-context.ts +281 -0
- package/src/compat/bundler/resolve-extensions.ts +75 -0
- package/src/compat/bundler/source-cache.ts +61 -0
- package/src/compat/bundler/static-imports.ts +25 -0
- package/src/compat/bundler/symlink-imports.ts +119 -0
- package/src/compat/bundler/tsconfig-paths.ts +50 -0
- package/src/compat/bundler/wasm.ts +153 -0
- package/src/compat/bundler/webpack-loaders.ts +685 -0
- package/src/compat/bundler/worker.ts +278 -0
- package/src/compat/cache/build-flags.ts +81 -0
- package/src/compat/cache/build-prerender-errors.ts +163 -0
- package/src/compat/cache/custom-handler.ts +159 -0
- package/src/compat/cache/fetch-patch.ts +745 -0
- package/src/compat/cache/handler.ts +100 -0
- package/src/compat/cache/modern-handler.ts +275 -0
- package/src/compat/cache/resume-data-cache.ts +143 -0
- package/src/compat/cache/revalidate.ts +759 -0
- package/src/compat/cache/runtime-error.ts +124 -0
- package/src/compat/cache/use-cache-transform.ts +961 -0
- package/src/compat/cache/use-cache.ts +1695 -0
- package/src/compat/cache-control.ts +269 -0
- package/src/compat/client/base-path.ts +64 -0
- package/src/compat/client/css-order.ts +36 -0
- package/src/compat/client/errors/control-flow.ts +92 -0
- package/src/compat/client/errors/error-boundary.ts +222 -0
- package/src/compat/client/errors/global-error.ts +238 -0
- package/src/compat/client/errors/install.ts +217 -0
- package/src/compat/client/errors/lazy.ts +53 -0
- package/src/compat/client/errors/primitive-throw.ts +126 -0
- package/src/compat/client/errors/soft-refresh.ts +14 -0
- package/src/compat/client/link-status.ts +86 -0
- package/src/compat/client/nav-compat-runtime.ts +57 -0
- package/src/compat/client/nav-compat.ts +42 -0
- package/src/compat/client/navigation-scroll.ts +154 -0
- package/src/compat/client/optimistic-routing.ts +206 -0
- package/src/compat/client/prefetch-cache.ts +111 -0
- package/src/compat/client/route-announcer.ts +72 -0
- package/src/compat/client/segment-cache-policy.ts +159 -0
- package/src/compat/client/segment-cache.ts +1077 -0
- package/src/compat/client/segment-prefetch.ts +375 -0
- package/src/compat/client/trailing-slash.ts +24 -0
- package/src/compat/css/chunking.ts +254 -0
- package/src/compat/css/inline-css.ts +73 -0
- package/src/compat/css/lightningcss.ts +90 -0
- package/src/compat/css/modules.ts +373 -0
- package/src/compat/css/nonce.ts +30 -0
- package/src/compat/css/sass-plugin.ts +65 -0
- package/src/compat/css/sass.ts +392 -0
- package/src/compat/css/styled-jsx-runtime.ts +80 -0
- package/src/compat/css/styled-jsx.ts +49 -0
- package/src/compat/edge-runtime.ts +71 -0
- package/src/compat/export/client.ts +112 -0
- package/src/compat/export/index.ts +272 -0
- package/src/compat/export/standalone.ts +207 -0
- package/src/compat/image-optimizer/cache.ts +119 -0
- package/src/compat/image-optimizer/detect.ts +143 -0
- package/src/compat/image-optimizer/index.ts +601 -0
- package/src/compat/image-optimizer/source.ts +243 -0
- package/src/compat/index.ts +458 -0
- package/src/compat/lifecycle/after-scope.ts +86 -0
- package/src/compat/lifecycle/after.ts +173 -0
- package/src/compat/lifecycle/error-funnel.ts +306 -0
- package/src/compat/lifecycle/error-serialize.ts +87 -0
- package/src/compat/lifecycle/error-ui.ts +167 -0
- package/src/compat/lifecycle/instrumentation-client.ts +138 -0
- package/src/compat/lifecycle/instrumentation.ts +277 -0
- package/src/compat/lifecycle/node-console.ts +19 -0
- package/src/compat/lifecycle/testmode.ts +263 -0
- package/src/compat/mdx/compile.ts +219 -0
- package/src/compat/mdx/next-mdx-stub.ts +46 -0
- package/src/compat/mdx/plugin.ts +37 -0
- package/src/compat/metadata-route-artifacts.ts +458 -0
- package/src/compat/metadata.ts +295 -0
- package/src/compat/middleware/manifest.ts +210 -0
- package/src/compat/misc/action-return.ts +173 -0
- package/src/compat/next/cache.ts +211 -0
- package/src/compat/next/canonical-url.ts +35 -0
- package/src/compat/next/client-cache.ts +57 -0
- package/src/compat/next/client-navigation.ts +313 -0
- package/src/compat/next/client-only.ts +3 -0
- package/src/compat/next/client-script.tsx +215 -0
- package/src/compat/next/client-server.ts +39 -0
- package/src/compat/next/config-loader.ts +569 -0
- package/src/compat/next/config.ts +29 -0
- package/src/compat/next/constants.cjs +6 -0
- package/src/compat/next/constants.ts +6 -0
- package/src/compat/next/custom-server.ts +236 -0
- package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
- package/src/compat/next/dynamic.tsx +46 -0
- package/src/compat/next/error.tsx +148 -0
- package/src/compat/next/font/cache.ts +171 -0
- package/src/compat/next/font/google.ts +2 -0
- package/src/compat/next/font/index.ts +8 -0
- package/src/compat/next/font/local.ts +5 -0
- package/src/compat/next/font/runtime-client.ts +71 -0
- package/src/compat/next/font/runtime.ts +974 -0
- package/src/compat/next/font/shared.ts +281 -0
- package/src/compat/next/form.tsx +156 -0
- package/src/compat/next/head.tsx +10 -0
- package/src/compat/next/headers.ts +247 -0
- package/src/compat/next/image/config.ts +196 -0
- package/src/compat/next/image/optimizer.ts +96 -0
- package/src/compat/next/image/patterns.ts +103 -0
- package/src/compat/next/image/shared.ts +141 -0
- package/src/compat/next/image/static-metadata.ts +283 -0
- package/src/compat/next/image/validate.ts +269 -0
- package/src/compat/next/image-client.tsx +215 -0
- package/src/compat/next/image-props.ts +575 -0
- package/src/compat/next/image-usage.ts +102 -0
- package/src/compat/next/image.tsx +56 -0
- package/src/compat/next/index.ts +1 -0
- package/src/compat/next/legacy-image.tsx +97 -0
- package/src/compat/next/link-usage.ts +29 -0
- package/src/compat/next/link-validation-transform.ts +200 -0
- package/src/compat/next/link.tsx +466 -0
- package/src/compat/next/navigation.cjs +21 -0
- package/src/compat/next/navigation.ts +188 -0
- package/src/compat/next/offline.ts +51 -0
- package/src/compat/next/og.ts +324 -0
- package/src/compat/next/optimistic-route-state.ts +188 -0
- package/src/compat/next/preferred-region.ts +39 -0
- package/src/compat/next/redirects.ts +131 -0
- package/src/compat/next/resource-hints.ts +136 -0
- package/src/compat/next/rewrites.ts +350 -0
- package/src/compat/next/root-params.ts +142 -0
- package/src/compat/next/router.cjs +49 -0
- package/src/compat/next/router.ts +143 -0
- package/src/compat/next/script.tsx +355 -0
- package/src/compat/next/server-only.ts +3 -0
- package/src/compat/next/server.ts +28 -0
- package/src/compat/next/svgr.ts +58 -0
- package/src/compat/next/telemetry.ts +77 -0
- package/src/compat/next/user-agent.ts +100 -0
- package/src/compat/next/web-vitals.ts +56 -0
- package/src/compat/otel/api.ts +95 -0
- package/src/compat/otel/client-trace-metadata.ts +71 -0
- package/src/compat/otel/fetch-span.ts +77 -0
- package/src/compat/otel/tracer.ts +944 -0
- package/src/compat/pages/client-plugin.ts +108 -0
- package/src/compat/pages/index.ts +527 -0
- package/src/compat/pages/router-state.ts +94 -0
- package/src/compat/ppr/io.ts +38 -0
- package/src/compat/ppr/missing-root-params.ts +105 -0
- package/src/compat/ppr/root-params-scan.ts +164 -0
- package/src/compat/ppr/root-params-transform.ts +75 -0
- package/src/compat/ppr/root-params.ts +129 -0
- package/src/compat/ppr/segment-config-incompat.ts +34 -0
- package/src/compat/protocol.ts +202 -0
- package/src/compat/react/client.ts +59 -0
- package/src/compat/react/compiler-runtime.ts +60 -0
- package/src/compat/react/dom-client.ts +115 -0
- package/src/compat/react/dom-react-server.ts +20 -0
- package/src/compat/react/dom-server.ts +40 -0
- package/src/compat/react/dom.ts +154 -0
- package/src/compat/react/preact.ts +522 -0
- package/src/compat/react/react-server.ts +84 -0
- package/src/compat/react/router-shim.ts +26 -0
- package/src/compat/react/server-component-use.ts +48 -0
- package/src/compat/react/server-inserted-html.ts +87 -0
- package/src/compat/react/server.ts +156 -0
- package/src/compat/react/view-transition.ts +60 -0
- package/src/compat/register/actions.ts +875 -0
- package/src/compat/register/boot.ts +141 -0
- package/src/compat/register/build-tier.ts +11 -0
- package/src/compat/register/build.ts +182 -0
- package/src/compat/register/bundler.ts +587 -0
- package/src/compat/register/cache.ts +85 -0
- package/src/compat/register/client-errors.ts +18 -0
- package/src/compat/register/config.ts +17 -0
- package/src/compat/register/css-extras.ts +120 -0
- package/src/compat/register/edge-runtime.ts +6 -0
- package/src/compat/register/errors.ts +46 -0
- package/src/compat/register/export.ts +23 -0
- package/src/compat/register/font.ts +36 -0
- package/src/compat/register/hooks.ts +34 -0
- package/src/compat/register/image.ts +133 -0
- package/src/compat/register/index.ts +111 -0
- package/src/compat/register/instrumentation-client.ts +35 -0
- package/src/compat/register/lifecycle.ts +86 -0
- package/src/compat/register/mdx.ts +48 -0
- package/src/compat/register/middleware.ts +36 -0
- package/src/compat/register/misc.ts +44 -0
- package/src/compat/register/otel.ts +288 -0
- package/src/compat/register/pages-api.ts +473 -0
- package/src/compat/register/ppr.ts +56 -0
- package/src/compat/register/protocol.ts +57 -0
- package/src/compat/register/proxy.ts +127 -0
- package/src/compat/register/render.ts +268 -0
- package/src/compat/register/routing.ts +410 -0
- package/src/compat/register/segment.ts +1903 -0
- package/src/compat/register/static-image.ts +21 -0
- package/src/compat/register/typed-routes.ts +35 -0
- package/src/compat/register/usecache.ts +131 -0
- package/src/compat/register/validation.ts +56 -0
- package/src/compat/segment/loading-boundary.ts +113 -0
- package/src/compat/segment/page-slot.ts +200 -0
- package/src/compat/segment/tree.ts +481 -0
- package/src/compat/segment/vary-key.ts +102 -0
- package/src/compat/segment/vary-params.ts +551 -0
- package/src/compat/static-params.ts +33 -0
- package/src/compat/tsconfig-defaults.ts +301 -0
- package/src/compat/typecheck/index.ts +1481 -0
- package/src/compat/typecheck/worker.ts +26 -0
- package/src/compat/typed-routes/index.ts +92 -0
- package/src/compat/typed-routes/manifest.ts +356 -0
- package/src/compat/typed-routes/typegen.ts +566 -0
- package/src/compat/validation/errors.ts +159 -0
- package/src/compat/validation/index.ts +1770 -0
- package/src/compat/validation/prerender-diagnostics.ts +1508 -0
- package/src/compat-bootstrap.ts +67 -0
- package/src/config.ts +218 -0
- package/src/css/build.ts +697 -0
- package/src/css/index.ts +2 -0
- package/src/css/postcss.ts +236 -0
- package/src/css/worker.ts +34 -0
- package/src/dev/client-actions.ts +35 -0
- package/src/dev/client-chunk-store.ts +92 -0
- package/src/dev/client-key-cache.ts +178 -0
- package/src/dev/global-css-cache.ts +212 -0
- package/src/dev/imports.ts +2430 -0
- package/src/dev/module-cache.ts +721 -0
- package/src/dev/module-generations.ts +38 -0
- package/src/dev/module-transform.ts +188 -0
- package/src/dev/node-module-bundle-cache.ts +63 -0
- package/src/dev/restart-cache.ts +10 -0
- package/src/dev/route-bundle-key-cache.ts +154 -0
- package/src/dev/route-facts-cache.ts +223 -0
- package/src/dev/server.ts +1710 -0
- package/src/dynamic/source.ts +307 -0
- package/src/dynamic/tree-shake.ts +262 -0
- package/src/env.ts +92 -0
- package/src/extensions.ts +1898 -0
- package/src/index.ts +34 -0
- package/src/internal.ts +43 -0
- package/src/islands/boundary-error.ts +8 -0
- package/src/islands/static-children.ts +37 -0
- package/src/islands/static-slots.ts +106 -0
- package/src/ppr-postpone.ts +24 -0
- package/src/ppr.ts +784 -0
- package/src/proxy.ts +752 -0
- package/src/render/hooks.ts +384 -0
- package/src/render/index.ts +1 -0
- package/src/render/island-context.ts +47 -0
- package/src/render/metadata.ts +857 -0
- package/src/render/renderer.ts +7391 -0
- package/src/render/resource-hints.ts +44 -0
- package/src/render/slots.tsx +679 -0
- package/src/request/context.ts +396 -0
- package/src/resolve/engine.ts +219 -0
- package/src/resolve/imports.ts +1104 -0
- package/src/resolve/scan-facts.ts +474 -0
- package/src/resolve/source-text.ts +86 -0
- package/src/routing/forwarded.ts +41 -0
- package/src/routing/handler.ts +271 -0
- package/src/routing/href.ts +203 -0
- package/src/routing/metadata.ts +1018 -0
- package/src/routing/request-runtime.ts +43 -0
- package/src/routing/routes.ts +2560 -0
- package/src/routing/slots.ts +432 -0
- package/src/runtime/server.ts +3453 -0
- package/src/runtime/vendor.ts +1160 -0
- package/src/style-modules.d.ts +9 -0
- package/src/typegen.ts +151 -0
- package/src/types.ts +725 -0
- package/src/utils/ansi.ts +9 -0
- package/src/utils/content-type.ts +31 -0
- package/src/utils/decode.ts +7 -0
- package/src/utils/dev-profile.ts +31 -0
- package/src/utils/error-log.ts +29 -0
- package/src/utils/fs-cache.ts +31 -0
- package/src/utils/fs.ts +119 -0
- package/src/utils/html.ts +46 -0
- package/src/utils/serialize.ts +378 -0
- package/src/utils/source.ts +35 -0
- package/src/utils/verbose.ts +39 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { Writable } from 'node:stream';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import type { ResolvedConfig } from '../../config';
|
|
5
|
+
import { nextCompatEnabled } from '../../compat';
|
|
6
|
+
import { devServerModuleHref } from '../../dev/imports';
|
|
7
|
+
import { pagesApiBundleTargetForRuntime } from '../../runtime/server';
|
|
8
|
+
import {
|
|
9
|
+
getRequestExtensions,
|
|
10
|
+
registerRequestInterceptors,
|
|
11
|
+
registerRevalidateBypassToken,
|
|
12
|
+
type RequestInterceptor,
|
|
13
|
+
} from '../../extensions';
|
|
14
|
+
import { getRequestRuntime } from '../../routing/request-runtime';
|
|
15
|
+
import { selectRouteForRequest } from '../../routing/routes';
|
|
16
|
+
import { serverBundleTargetForRuntime } from '../../runtime/server';
|
|
17
|
+
import { setWorkUnitRoute } from '../../request/context';
|
|
18
|
+
import { canonicalUrlHref } from '../next/canonical-url';
|
|
19
|
+
import { markPathRevalidated, normalizeRevalidatePath } from '../cache/revalidate';
|
|
20
|
+
import { withPagesApiHandlerSpan } from '../otel/tracer';
|
|
21
|
+
import { toNextRequest } from '../../api/server';
|
|
22
|
+
|
|
23
|
+
type PagesApiHandler = (req: unknown, res: PagesApiResponse) => Promise<unknown>;
|
|
24
|
+
|
|
25
|
+
interface PagesApiModule {
|
|
26
|
+
default?: PagesApiHandler;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface PagesApiResponse {
|
|
30
|
+
statusCode: number;
|
|
31
|
+
on(event: 'close' | 'error', listener: (...args: unknown[]) => void): PagesApiResponse;
|
|
32
|
+
write(
|
|
33
|
+
chunk: unknown,
|
|
34
|
+
encoding?: BufferEncoding,
|
|
35
|
+
callback?: () => void,
|
|
36
|
+
): boolean;
|
|
37
|
+
setHeader(name: string, value: string | number | readonly string[]): PagesApiResponse;
|
|
38
|
+
getHeader(name: string): string | null;
|
|
39
|
+
status(code: number): PagesApiResponse;
|
|
40
|
+
json(value: unknown): PagesApiResponse;
|
|
41
|
+
send(value?: unknown): PagesApiResponse;
|
|
42
|
+
end(value?: unknown): PagesApiResponse;
|
|
43
|
+
revalidate(path: string): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const pageExtensions = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'mts'];
|
|
47
|
+
|
|
48
|
+
export function registerPagesApiExtensions(config: ResolvedConfig): void {
|
|
49
|
+
if (!nextCompatEnabled(config)) return;
|
|
50
|
+
// Registered after the action/rewrite interceptors (register.ts order), so a
|
|
51
|
+
// data request rewritten by middleware or next.config lands here with its
|
|
52
|
+
// destination URL already resolved.
|
|
53
|
+
registerRequestInterceptors(pagesApiInterceptor, pagesDataInterceptor);
|
|
54
|
+
// The preview-mode id written into prerender-manifest.json: requests carrying
|
|
55
|
+
// it in x-prerender-revalidate are on-demand revalidations (middleware skip +
|
|
56
|
+
// x-nextjs-cache: REVALIDATED — see cli/start.ts).
|
|
57
|
+
registerRevalidateBypassToken(() => PREVIEW_MODE_ID);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const PREVIEW_MODE_ID = 'pnext-preview-id';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Pages-router data protocol (`/_next/data/<id>/<page>.json`, normalized to the page path by cli/start.ts
|
|
64
|
+
* with the `x-nextjs-data` marker): serve the page's data-fetch result as Next's data JSON -
|
|
65
|
+
* `{ pageProps }` from getServerSideProps/getStaticProps of the materialized pages route - instead of
|
|
66
|
+
* rendering HTML. When the request was rewritten, `x-nextjs-matched-path` reports the destination the
|
|
67
|
+
* data was served from, which the pages client router uses to resolve the rewrite.
|
|
68
|
+
*/
|
|
69
|
+
const pagesDataInterceptor: RequestInterceptor = async request => {
|
|
70
|
+
if (request.headers.get('x-nextjs-data') !== '1') return undefined;
|
|
71
|
+
const method = request.method.toUpperCase();
|
|
72
|
+
if (method !== 'GET' && method !== 'HEAD') return undefined;
|
|
73
|
+
const runtime = getRequestRuntime();
|
|
74
|
+
if (!runtime) return undefined;
|
|
75
|
+
const url = new URL(request.url);
|
|
76
|
+
// Un-normalized data URLs (skipMiddlewareUrlNormalize without a middleware
|
|
77
|
+
// rewrite) never match a route here; header-only marker requests for
|
|
78
|
+
// document paths fall through to the HTML render.
|
|
79
|
+
if (url.pathname.startsWith('/_next/')) return undefined;
|
|
80
|
+
|
|
81
|
+
const selection = selectRouteForRequest(runtime.routes, url.pathname);
|
|
82
|
+
if (selection?.route.kind !== 'page') {
|
|
83
|
+
return dataResponse({ notFound: true }, 404, url);
|
|
84
|
+
}
|
|
85
|
+
const route = selection.route;
|
|
86
|
+
if (!isMaterializedPagesRoute(route.file)) return undefined;
|
|
87
|
+
|
|
88
|
+
const module = (await import(
|
|
89
|
+
await devServerModuleHref(
|
|
90
|
+
runtime.config,
|
|
91
|
+
route.file,
|
|
92
|
+
runtime.dev ? (runtime.devImportVersion ?? 'dev') : 'build',
|
|
93
|
+
{
|
|
94
|
+
conditionTarget: serverBundleTargetForRuntime(route.segmentConfig?.runtime),
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
)) as PagesDataModule;
|
|
98
|
+
|
|
99
|
+
setWorkUnitRoute('data', 'dynamic');
|
|
100
|
+
const params = selection.params ?? {};
|
|
101
|
+
const query = { ...Object.fromEntries(url.searchParams), ...params };
|
|
102
|
+
if (typeof module.getServerSideProps === 'function') {
|
|
103
|
+
const result = await module.getServerSideProps({
|
|
104
|
+
params,
|
|
105
|
+
query,
|
|
106
|
+
req: pagesDataRequestShim(request, url),
|
|
107
|
+
res: pagesDataResponseShim(),
|
|
108
|
+
resolvedUrl: `${url.pathname}${url.search}`,
|
|
109
|
+
preview: false,
|
|
110
|
+
previewData: undefined,
|
|
111
|
+
});
|
|
112
|
+
return pagesDataResult(result, url);
|
|
113
|
+
}
|
|
114
|
+
if (typeof module.getStaticProps === 'function') {
|
|
115
|
+
const result = await module.getStaticProps({ params });
|
|
116
|
+
return pagesDataResult(result, url, true);
|
|
117
|
+
}
|
|
118
|
+
return dataResponse({ pageProps: {} }, 200, url);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
interface PagesDataFnResult {
|
|
122
|
+
props?: Record<string, unknown>;
|
|
123
|
+
notFound?: boolean;
|
|
124
|
+
redirect?: { destination: string; permanent?: boolean; statusCode?: number };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface PagesDataModule {
|
|
128
|
+
getServerSideProps?: (context: Record<string, unknown>) => Promise<PagesDataFnResult> | PagesDataFnResult;
|
|
129
|
+
getStaticProps?: (context: Record<string, unknown>) => Promise<PagesDataFnResult> | PagesDataFnResult;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function pagesDataResult(result: PagesDataFnResult | undefined, url: URL, ssg = false): Response {
|
|
133
|
+
if (result?.notFound) return dataResponse({ notFound: true }, 404, url);
|
|
134
|
+
if (result?.redirect) {
|
|
135
|
+
const status =
|
|
136
|
+
result.redirect.statusCode ?? (result.redirect.permanent ? 308 : 307);
|
|
137
|
+
return dataResponse(
|
|
138
|
+
{
|
|
139
|
+
pageProps: {
|
|
140
|
+
__N_REDIRECT: result.redirect.destination,
|
|
141
|
+
__N_REDIRECT_STATUS: status,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
200,
|
|
145
|
+
url,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
return dataResponse(
|
|
149
|
+
{ pageProps: result?.props ?? {}, ...(ssg ? { __N_SSG: true } : { __N_SSP: true }) },
|
|
150
|
+
200,
|
|
151
|
+
url,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function dataResponse(body: Record<string, unknown>, status: number, url: URL): Response {
|
|
156
|
+
const headers = new Headers({ 'content-type': 'application/json; charset=utf-8' });
|
|
157
|
+
// A rewrite happened when the canonical (as-requested, post-normalization)
|
|
158
|
+
// pathname differs from the URL the route was matched on.
|
|
159
|
+
const canonical = canonicalUrlHref();
|
|
160
|
+
if (canonical && new URL(canonical).pathname !== url.pathname) {
|
|
161
|
+
headers.set('x-nextjs-matched-path', url.pathname);
|
|
162
|
+
}
|
|
163
|
+
return new Response(JSON.stringify(body), { status, headers });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// The materialized hybrid app hosts BOTH routers; only pages wrappers
|
|
167
|
+
// re-export from source-pages/, so sniff the (tiny, generated) wrapper body —
|
|
168
|
+
// app routes must never serve _next/data.
|
|
169
|
+
function isMaterializedPagesRoute(file: string): boolean {
|
|
170
|
+
if (!file.split(path.sep).join('/').includes('pnext-pages-compat/')) return false;
|
|
171
|
+
try {
|
|
172
|
+
return readFileSync(file, 'utf8').includes('source-pages/');
|
|
173
|
+
} catch {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function pagesDataRequestShim(request: Request, url: URL) {
|
|
179
|
+
return {
|
|
180
|
+
method: request.method,
|
|
181
|
+
url: `${url.pathname}${url.search}`,
|
|
182
|
+
headers: Object.fromEntries(request.headers),
|
|
183
|
+
cookies: cookiesFromHeader(request.headers.get('cookie')),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function pagesDataResponseShim() {
|
|
188
|
+
const headers = new Headers();
|
|
189
|
+
let statusCode = 200;
|
|
190
|
+
return {
|
|
191
|
+
get statusCode() {
|
|
192
|
+
return statusCode;
|
|
193
|
+
},
|
|
194
|
+
set statusCode(value: number) {
|
|
195
|
+
statusCode = value;
|
|
196
|
+
},
|
|
197
|
+
setHeader(name: string, value: string | number | readonly string[]) {
|
|
198
|
+
headers.set(name, Array.isArray(value) ? value.join(', ') : String(value));
|
|
199
|
+
return this;
|
|
200
|
+
},
|
|
201
|
+
getHeader(name: string) {
|
|
202
|
+
return headers.get(name);
|
|
203
|
+
},
|
|
204
|
+
removeHeader(name: string) {
|
|
205
|
+
headers.delete(name);
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const pagesApiInterceptor: RequestInterceptor = async (request, { config }) => {
|
|
211
|
+
const url = new URL(request.url);
|
|
212
|
+
const file = pagesApiFile(config.root, url.pathname);
|
|
213
|
+
if (!file) return undefined;
|
|
214
|
+
const imported = (await import(
|
|
215
|
+
await devServerModuleHref(config, file, 'build', {
|
|
216
|
+
conditionTarget: pagesApiBundleTargetForRuntime(apiRuntime(file)),
|
|
217
|
+
}),
|
|
218
|
+
)) as PagesApiModule;
|
|
219
|
+
if (typeof imported.default !== 'function') return undefined;
|
|
220
|
+
|
|
221
|
+
const responder = createPagesApiResponse(request);
|
|
222
|
+
// Edge-runtime pages API handlers receive the real NextRequest (signal,
|
|
223
|
+
// nextUrl, text() — Request prototype getters a spread would lose); node
|
|
224
|
+
// handlers get the IncomingMessage-shaped shim.
|
|
225
|
+
const handlerRequest =
|
|
226
|
+
apiRuntime(file) === 'edge' ? toNextRequest(request) : createPagesApiRequest(request, url);
|
|
227
|
+
const finished = withPagesApiHandlerSpan(url.pathname, () =>
|
|
228
|
+
Promise.resolve(imported.default!(handlerRequest, responder.res)),
|
|
229
|
+
).catch(error => responder.fail(error));
|
|
230
|
+
// A handler that starts writing (res.write / pipeline) streams: the response returns at the first write
|
|
231
|
+
// and the body flows through until end or disconnect - a client disconnect destroys `res`, surfacing as
|
|
232
|
+
// its 'close' event, which is Node pages-API cancellation semantics. A handler that only buffers
|
|
233
|
+
// resolves on completion exactly as before.
|
|
234
|
+
const outcome = await Promise.race([
|
|
235
|
+
responder.started.then(() => STREAM_STARTED),
|
|
236
|
+
finished,
|
|
237
|
+
]);
|
|
238
|
+
if (outcome !== STREAM_STARTED) {
|
|
239
|
+
if (outcome instanceof Response) return outcome;
|
|
240
|
+
return responder.response(outcome);
|
|
241
|
+
}
|
|
242
|
+
return responder.streamResponse();
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const STREAM_STARTED = Symbol('pnext.pagesApiStreamStarted');
|
|
246
|
+
|
|
247
|
+
function pagesApiFile(root: string, pathname: string): string | undefined {
|
|
248
|
+
if (pathname !== '/api' && !pathname.startsWith('/api/')) return undefined;
|
|
249
|
+
let relative: string;
|
|
250
|
+
try {
|
|
251
|
+
relative = decodeURIComponent(pathname === '/api' ? 'index' : pathname.slice('/api/'.length));
|
|
252
|
+
} catch {
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
const baseDir = path.join(root, 'pages', 'api');
|
|
256
|
+
const base = path.resolve(baseDir, relative);
|
|
257
|
+
if (base !== baseDir && !base.startsWith(`${baseDir}${path.sep}`)) return undefined;
|
|
258
|
+
for (const ext of pageExtensions) {
|
|
259
|
+
const file = `${base}.${ext}`;
|
|
260
|
+
if (existsSync(file)) return file;
|
|
261
|
+
const index = path.join(base, `index.${ext}`);
|
|
262
|
+
if (existsSync(index)) return index;
|
|
263
|
+
}
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function apiRuntime(file: string): string | undefined {
|
|
268
|
+
return /\bruntime\b\s*[:=]\s*(['"`])(?:edge|experimental-edge)\1/.test(
|
|
269
|
+
readFileSync(file, 'utf8'),
|
|
270
|
+
)
|
|
271
|
+
? 'edge'
|
|
272
|
+
: undefined;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function createPagesApiRequest(request: Request, url: URL) {
|
|
276
|
+
const nextRequest = toNextRequest(request);
|
|
277
|
+
return {
|
|
278
|
+
...nextRequest,
|
|
279
|
+
method: request.method,
|
|
280
|
+
url: `${url.pathname}${url.search}`,
|
|
281
|
+
headers: Object.fromEntries(request.headers),
|
|
282
|
+
query: Object.fromEntries(url.searchParams),
|
|
283
|
+
cookies: cookiesFromHeader(request.headers.get('cookie')),
|
|
284
|
+
nextUrl: nextRequest.nextUrl,
|
|
285
|
+
text() {
|
|
286
|
+
return request.text();
|
|
287
|
+
},
|
|
288
|
+
json() {
|
|
289
|
+
return request.json();
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function createPagesApiResponse(request: Request) {
|
|
295
|
+
const headers = new Headers();
|
|
296
|
+
let statusCode = 200;
|
|
297
|
+
let sent = false;
|
|
298
|
+
let body: BodyInit | null = null;
|
|
299
|
+
let started = false;
|
|
300
|
+
let notifyStarted: () => void = () => undefined;
|
|
301
|
+
const startedPromise = new Promise<void>(resolve => {
|
|
302
|
+
notifyStarted = resolve;
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
let controller: ReadableStreamDefaultController<Uint8Array> | undefined;
|
|
306
|
+
const streamBody = new ReadableStream<Uint8Array>({
|
|
307
|
+
start(c) {
|
|
308
|
+
controller = c;
|
|
309
|
+
},
|
|
310
|
+
// The client disconnected mid-stream: destroy the res so the handler
|
|
311
|
+
// observes it as the response closing (Node pages-API semantics —
|
|
312
|
+
// `res.on('close', ...)` aborts its inner stream).
|
|
313
|
+
cancel() {
|
|
314
|
+
writable.destroy();
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
const start = () => {
|
|
318
|
+
if (!started) {
|
|
319
|
+
started = true;
|
|
320
|
+
notifyStarted();
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
const enqueue = (chunk: unknown) => {
|
|
324
|
+
const value = normalizeWriteChunk(chunk);
|
|
325
|
+
if (value === '') return;
|
|
326
|
+
try {
|
|
327
|
+
controller?.enqueue(typeof value === 'string' ? new TextEncoder().encode(value) : new Uint8Array(value));
|
|
328
|
+
} catch {
|
|
329
|
+
// Stream already closed/cancelled — drop the late chunk.
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
// A REAL Writable: `stream.pipeline(source, res)` and event listeners need a
|
|
333
|
+
// genuine EventEmitter/stream destination, not a duck-typed object.
|
|
334
|
+
const writable = new Writable({
|
|
335
|
+
write(chunk, _encoding, callback) {
|
|
336
|
+
start();
|
|
337
|
+
enqueue(chunk);
|
|
338
|
+
callback();
|
|
339
|
+
},
|
|
340
|
+
final(callback) {
|
|
341
|
+
// Only a stream that actually wrote flags the streaming path: a plain
|
|
342
|
+
// res.end(value) is a BUFFERED response (its body rides `body`, not the
|
|
343
|
+
// stream) and must resolve through the handler-completion race.
|
|
344
|
+
try {
|
|
345
|
+
controller?.close();
|
|
346
|
+
} catch {
|
|
347
|
+
// Already closed.
|
|
348
|
+
}
|
|
349
|
+
callback();
|
|
350
|
+
},
|
|
351
|
+
destroy(error, callback) {
|
|
352
|
+
try {
|
|
353
|
+
controller?.close();
|
|
354
|
+
} catch {
|
|
355
|
+
// Already closed.
|
|
356
|
+
}
|
|
357
|
+
callback(error ?? null);
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
request.signal?.addEventListener?.('abort', () => writable.destroy(), { once: true });
|
|
361
|
+
|
|
362
|
+
const api = {
|
|
363
|
+
setHeader(name: string, value: string | number | readonly string[]) {
|
|
364
|
+
headers.set(name, Array.isArray(value) ? value.join(', ') : String(value));
|
|
365
|
+
return res;
|
|
366
|
+
},
|
|
367
|
+
getHeader(name: string) {
|
|
368
|
+
return headers.get(name);
|
|
369
|
+
},
|
|
370
|
+
removeHeader(name: string) {
|
|
371
|
+
headers.delete(name);
|
|
372
|
+
},
|
|
373
|
+
status(code: number) {
|
|
374
|
+
statusCode = code;
|
|
375
|
+
return res;
|
|
376
|
+
},
|
|
377
|
+
json(value: unknown) {
|
|
378
|
+
if (!headers.has('content-type')) headers.set('content-type', 'application/json; charset=utf-8');
|
|
379
|
+
body = JSON.stringify(value);
|
|
380
|
+
sent = true;
|
|
381
|
+
return res;
|
|
382
|
+
},
|
|
383
|
+
send(value?: unknown) {
|
|
384
|
+
body = bodyFromValue(value, headers);
|
|
385
|
+
sent = true;
|
|
386
|
+
return res;
|
|
387
|
+
},
|
|
388
|
+
end(value?: unknown) {
|
|
389
|
+
if (!sent) {
|
|
390
|
+
body = bodyFromValue(value, headers);
|
|
391
|
+
sent = true;
|
|
392
|
+
}
|
|
393
|
+
if (value !== undefined && value !== null && started) {
|
|
394
|
+
// Mid-stream end(chunk): flush the tail through the stream.
|
|
395
|
+
enqueue(value);
|
|
396
|
+
}
|
|
397
|
+
(Writable.prototype.end as (this: Writable) => Writable).call(writable);
|
|
398
|
+
return res;
|
|
399
|
+
},
|
|
400
|
+
async revalidate(pathname: string) {
|
|
401
|
+
markPathRevalidated(pathname);
|
|
402
|
+
await getRequestExtensions().onDemandRevalidatePath(normalizeRevalidatePath(pathname));
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
const res = Object.assign(writable, api) as unknown as PagesApiResponse;
|
|
406
|
+
Object.defineProperty(res, 'statusCode', {
|
|
407
|
+
get: () => statusCode,
|
|
408
|
+
set: (value: number) => {
|
|
409
|
+
statusCode = value;
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
res,
|
|
415
|
+
started: startedPromise,
|
|
416
|
+
fail(error: unknown): never | undefined {
|
|
417
|
+
if (!started) throw error;
|
|
418
|
+
try {
|
|
419
|
+
controller?.error(error);
|
|
420
|
+
} catch {
|
|
421
|
+
// Stream already settled.
|
|
422
|
+
}
|
|
423
|
+
return undefined;
|
|
424
|
+
},
|
|
425
|
+
streamResponse() {
|
|
426
|
+
// Null-body statuses (204/304) must not carry a body stream.
|
|
427
|
+
if (statusCode === 204 || statusCode === 304) {
|
|
428
|
+
return new Response(null, { status: statusCode, headers });
|
|
429
|
+
}
|
|
430
|
+
return new Response(streamBody, { status: statusCode, headers });
|
|
431
|
+
},
|
|
432
|
+
response(result: unknown) {
|
|
433
|
+
if (result !== undefined && !sent) body = bodyFromValue(result, headers);
|
|
434
|
+
return new Response(statusCode === 204 || statusCode === 304 ? null : body, {
|
|
435
|
+
status: statusCode,
|
|
436
|
+
headers,
|
|
437
|
+
});
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function normalizeWriteChunk(
|
|
443
|
+
chunk: unknown,
|
|
444
|
+
) {
|
|
445
|
+
if (chunk === null || chunk === undefined) return '';
|
|
446
|
+
if (typeof chunk === 'string') return chunk;
|
|
447
|
+
if (chunk instanceof ArrayBuffer) return Buffer.from(chunk);
|
|
448
|
+
if (ArrayBuffer.isView(chunk)) return Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
449
|
+
if (typeof chunk === 'number' || typeof chunk === 'boolean' || typeof chunk === 'bigint') {
|
|
450
|
+
return String(chunk);
|
|
451
|
+
}
|
|
452
|
+
return JSON.stringify(chunk) ?? '';
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function bodyFromValue(value: unknown, headers: Headers): BodyInit | null {
|
|
456
|
+
if (value === undefined || value === null) return null;
|
|
457
|
+
if (typeof value === 'string' || value instanceof Blob || value instanceof ArrayBuffer) return value;
|
|
458
|
+
if (value instanceof Uint8Array && value.buffer instanceof ArrayBuffer) {
|
|
459
|
+
return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
|
|
460
|
+
}
|
|
461
|
+
if (!headers.has('content-type')) headers.set('content-type', 'application/json; charset=utf-8');
|
|
462
|
+
return JSON.stringify(value);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function cookiesFromHeader(header: string | null): Record<string, string> {
|
|
466
|
+
if (!header) return {};
|
|
467
|
+
return Object.fromEntries(
|
|
468
|
+
header
|
|
469
|
+
.split(';')
|
|
470
|
+
.map(part => part.trim().split('='))
|
|
471
|
+
.filter((parts): parts is [string, string] => parts.length === 2 && parts[0] !== ''),
|
|
472
|
+
);
|
|
473
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// PPR / cacheComponents / root-params registration (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// Wires the PPR-stack compat behavior into core: the core cacheComponents flag is set per config load,
|
|
4
|
+
// and routingExtensions.usageDetection learns that io() usage marks a route dynamic.
|
|
5
|
+
//
|
|
6
|
+
// The global `cacheComponents` flag turns the existing shell+resume path into the default for every
|
|
7
|
+
// route, and switches request APIs (cookies/headers/connection/params/searchParams/io) to hanging
|
|
8
|
+
// promises that postpone at the first await inside a boundary. Idempotent.
|
|
9
|
+
|
|
10
|
+
import type { ResolvedConfig } from '../../config';
|
|
11
|
+
import { nextCompatEnabled } from '../../compat';
|
|
12
|
+
import { onExtensionHostReset, registerBuildSteps, registerUsageDetectors } from '../../extensions';
|
|
13
|
+
import type { RouteManifestEntry } from '../../types';
|
|
14
|
+
import { cacheComponents } from '../../ppr';
|
|
15
|
+
import { validateRootParamsProvided } from '../ppr/missing-root-params';
|
|
16
|
+
|
|
17
|
+
let registered = false;
|
|
18
|
+
// Host-scoped guard: a fresh host must be registered into again.
|
|
19
|
+
onExtensionHostReset(() => (registered = false));
|
|
20
|
+
|
|
21
|
+
export function registerPprExtensions(config: ResolvedConfig): void {
|
|
22
|
+
if (registered) return;
|
|
23
|
+
if (!nextCompatEnabled(config)) return;
|
|
24
|
+
registered = true;
|
|
25
|
+
|
|
26
|
+
// The core cacheComponents flag is NOT set here: registration is a process
|
|
27
|
+
// one-shot, so it is derived per config load in next/config-loader's
|
|
28
|
+
// setNextConfig() instead.
|
|
29
|
+
|
|
30
|
+
// next/root-params named imports -> default-Proxy member reads: run by the
|
|
31
|
+
// next/font pre-transform (compat/index.ts), which is registered for the same
|
|
32
|
+
// `compat.next` apps and fires ahead of the static-import alias rewrite. A
|
|
33
|
+
// second registration here would only ever re-scan an already-rewritten
|
|
34
|
+
// source, so this file registers none.
|
|
35
|
+
|
|
36
|
+
// `io()` (from next/server) makes a route request-dependent under
|
|
37
|
+
// cacheComponents, so a route awaiting it is a PPR candidate rather than a
|
|
38
|
+
// pure static build. Reuses the generic usage-detection seam.
|
|
39
|
+
registerUsageDetectors(
|
|
40
|
+
source =>
|
|
41
|
+
cacheComponents() &&
|
|
42
|
+
/\bio\s*\(\s*\)/.test(source) &&
|
|
43
|
+
(source.includes('next/server') || source.includes('next/cache')),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// cacheComponents build guard: a root param (a dynamic segment at/above a
|
|
47
|
+
// root layout) with no generateStaticParams value is a hard build error. The
|
|
48
|
+
// registry is process-global, so re-check enablement per build (a later
|
|
49
|
+
// pure-core buildProject in the same process must not run this).
|
|
50
|
+
registerBuildSteps(async ctx => {
|
|
51
|
+
if (!cacheComponents()) return;
|
|
52
|
+
await ctx.log.step('validate root params', () =>
|
|
53
|
+
validateRootParamsProvided(ctx.config, ctx.routes as RouteManifestEntry[]),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// HTTP protocol finalizer registration (COMPAT - may import core freely). registerProtocolExtensions()
|
|
2
|
+
// appends the compat responseFinalizers (Vary merge, x-nextjs-rewritten-path/query) into the core
|
|
3
|
+
// registry. No-op when compat.next is off.
|
|
4
|
+
|
|
5
|
+
import type { ResolvedConfig } from '../../config';
|
|
6
|
+
import {
|
|
7
|
+
registerRequestInterceptors,
|
|
8
|
+
registerResponseFinalizers,
|
|
9
|
+
setRouterProtocolExtensions,
|
|
10
|
+
} from '../../extensions';
|
|
11
|
+
import { cacheControlFinalizers } from '../cache-control';
|
|
12
|
+
import { protocolFinalizers, protocolInterceptors } from '../protocol';
|
|
13
|
+
import { registerResourceHintFinalizer } from '../next/resource-hints';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The router-state-tree validation interceptor, registered on its own so the
|
|
17
|
+
* orchestrator can put it AHEAD of every short-circuiting interceptor (segment
|
|
18
|
+
* prefetch, action dispatch): a malformed tree must fail the request before any
|
|
19
|
+
* of them answers it.
|
|
20
|
+
*/
|
|
21
|
+
export function registerProtocolRequestValidation(config: ResolvedConfig): void {
|
|
22
|
+
registerRequestInterceptors(...protocolInterceptors(config));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function registerProtocolExtensions(config: ResolvedConfig): void {
|
|
26
|
+
setRouterProtocolExtensions({
|
|
27
|
+
prefetchRequestHeaders: () => ['next-router-prefetch'],
|
|
28
|
+
// Next's partial prefetch: a default (`next-router-prefetch: 1`) router prefetch never runs dynamic
|
|
29
|
+
// data - it gets the static shell with the loading/Suspense fallbacks in place. A route opting into
|
|
30
|
+
// runtime prefetching serves its runtime-prefetchable content on prefetch instead, and a full prefetch
|
|
31
|
+
// (`prefetch={true}`, which Next issues WITHOUT the prefetch header) is a complete fetch - neither is
|
|
32
|
+
// shell-only.
|
|
33
|
+
shellOnlyRequest: (request, route) => {
|
|
34
|
+
if (request.method.toUpperCase() !== 'GET') return false;
|
|
35
|
+
if (request.headers.get('rsc') !== '1') return false;
|
|
36
|
+
if (request.headers.get('next-router-prefetch') !== '1') return false;
|
|
37
|
+
const segmentConfig = route.segmentConfig as
|
|
38
|
+
| { prefetch?: unknown; dynamic?: unknown }
|
|
39
|
+
| undefined;
|
|
40
|
+
if (
|
|
41
|
+
segmentConfig?.prefetch === 'allow-runtime' ||
|
|
42
|
+
segmentConfig?.prefetch === 'unstable_eager'
|
|
43
|
+
) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// Static-capable content belongs in a prefetch (Next inlines static
|
|
47
|
+
// data — and lazy-static persistence must never cache a truncated
|
|
48
|
+
// body). Only a truly dynamic render, or a PPR shell with holes, is cut
|
|
49
|
+
// at the shell.
|
|
50
|
+
if (route.mode !== 'dynamic' && !route.ppr) return false;
|
|
51
|
+
if (segmentConfig?.dynamic === 'force-static') return false;
|
|
52
|
+
return true;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
registerResponseFinalizers(...protocolFinalizers(config), ...cacheControlFinalizers(config));
|
|
56
|
+
registerResourceHintFinalizer();
|
|
57
|
+
}
|