@wular/pnext 0.0.4 → 0.0.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/README.md +22 -16
- package/bin/pnext +6 -1
- package/config/lint/base.js +7 -7
- package/config/ts/base.json +2 -4
- package/config/ts/react.json +2 -6
- package/package.json +23 -2
- package/reference/compat.md +87 -22
- package/reference/config.md +40 -93
- package/reference/css.md +42 -33
- package/reference/dev.md +26 -33
- package/reference/env.md +18 -20
- package/reference/getting-started.md +132 -0
- package/reference/metadata.md +31 -46
- package/reference/navigation.md +65 -105
- package/reference/performance.md +21 -142
- package/reference/rendering.md +78 -75
- package/reference/routing.md +97 -107
- package/reference/typegen.md +27 -29
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +121 -114
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +52 -53
- package/src/api/navigation.ts +59 -58
- package/src/api/server.ts +152 -144
- package/src/api/suspense.ts +4 -4
- package/src/cli/adapters/vercel-warm.ts +126 -121
- package/src/cli/adapters/vercel.ts +437 -443
- package/src/cli/analyze.ts +333 -144
- package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
- package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
- package/src/cli/build.ts +976 -978
- package/src/cli/create.ts +72 -64
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +94 -85
- package/src/cli/migrate/package-json.ts +73 -71
- package/src/cli/migrate/report.ts +32 -33
- package/src/cli/migrate/{index.ts → run.ts} +48 -49
- package/src/cli/migrate/scan.ts +46 -46
- package/src/cli/migrate/spinner.ts +9 -9
- package/src/cli/migrate/tsconfig.ts +35 -35
- package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
- package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
- package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
- package/src/cli/start.ts +65 -66
- package/src/{typegen.ts → cli/typegen.ts} +59 -59
- package/src/client/build.ts +802 -768
- package/src/client/chunk-fold.ts +245 -240
- package/src/client/{paths.ts → chunk-name.ts} +6 -6
- package/src/client/entry.ts +162 -147
- package/src/client/prebuilt.ts +231 -223
- package/src/client/profile.ts +29 -29
- package/src/client/react-compiler.ts +20 -15
- package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
- package/src/client/reference-stub.ts +51 -51
- package/src/client/reference.ts +19 -19
- package/src/{api → client}/router/events.ts +32 -17
- package/src/{api → client}/router/history.ts +16 -16
- package/src/{api → client}/router/hub.ts +52 -53
- package/src/{api/router.ts → client/router/index.ts} +61 -60
- package/src/{api → client}/router/policies.ts +24 -24
- package/src/{api → client}/router/runtime.ts +2022 -1995
- package/src/{api → client}/router/types.ts +98 -98
- package/src/compat/actions/client-plugin.ts +42 -43
- package/src/compat/actions/client-stub.ts +14 -14
- package/src/compat/actions/{action-client.ts → client.ts} +194 -193
- package/src/compat/actions/config.ts +63 -64
- package/src/compat/actions/detect.ts +68 -68
- package/src/compat/actions/discovery.ts +105 -105
- package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
- package/src/compat/actions/early-submit.ts +1 -1
- package/src/compat/actions/endpoint.ts +198 -198
- package/src/compat/actions/extensions.ts +811 -0
- package/src/compat/actions/flight.ts +23 -23
- package/src/compat/actions/form-state.ts +34 -34
- package/src/compat/actions/hoist.ts +382 -236
- package/src/compat/actions/ids.ts +7 -12
- package/src/compat/actions/index.ts +8 -8
- package/src/compat/actions/instances.ts +46 -46
- package/src/compat/actions/origin.ts +47 -48
- package/src/compat/actions/protocol.ts +22 -22
- package/src/compat/actions/registry.ts +19 -19
- package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
- package/src/compat/actions/rewrite.ts +125 -125
- package/src/compat/actions/{action-router.ts → router.ts} +10 -10
- package/src/compat/actions/serve.ts +132 -136
- package/src/compat/actions/server-tag.ts +4 -4
- package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
- package/src/compat/actions/unrecognized-error.ts +4 -4
- package/src/compat/{index.ts → aliases.ts} +112 -109
- package/src/compat/bundler/bun-externals.ts +18 -18
- package/src/compat/bundler/cjs-exports.ts +271 -223
- package/src/compat/bundler/config.ts +116 -111
- package/src/compat/bundler/externals.ts +12 -12
- package/src/compat/bundler/import-meta-url.ts +19 -19
- package/src/compat/bundler/modularize-imports.ts +36 -33
- package/src/compat/bundler/new-url-asset.ts +22 -24
- package/src/compat/bundler/optimize-package-imports.ts +111 -107
- package/src/compat/bundler/polyfill.ts +28 -28
- package/src/compat/bundler/react-compiler.ts +35 -29
- package/src/compat/bundler/react-profiler.tsx +11 -11
- package/src/compat/bundler/relay-transform.ts +48 -47
- package/src/compat/bundler/require-context.ts +119 -113
- package/src/compat/bundler/resolve-extensions.ts +19 -19
- package/src/compat/bundler/source-cache.ts +25 -25
- package/src/compat/bundler/static-imports.ts +7 -7
- package/src/compat/bundler/symlink-imports.ts +46 -46
- package/src/compat/bundler/tsconfig-paths.ts +13 -15
- package/src/compat/bundler/wasm.ts +58 -60
- package/src/compat/bundler/webpack-loaders.ts +254 -241
- package/src/compat/bundler/worker.ts +101 -104
- package/src/compat/cache/build-flags.ts +29 -29
- package/src/compat/cache/build-prerender-errors.ts +40 -44
- package/src/compat/cache/custom-handler.ts +60 -53
- package/src/compat/cache/fetch-patch.ts +240 -240
- package/src/compat/cache/handler.ts +27 -31
- package/src/compat/cache/modern-handler.ts +148 -126
- package/src/compat/cache/resume-data-cache.ts +47 -45
- package/src/compat/cache/revalidate.ts +233 -232
- package/src/compat/cache/runtime-error.ts +35 -35
- package/src/compat/cache/use-cache-transform.ts +442 -417
- package/src/compat/cache/use-cache.ts +650 -614
- package/src/compat/cache-control.ts +140 -142
- package/src/compat/client/base-path.ts +21 -20
- package/src/compat/client/css-order.ts +18 -18
- package/src/compat/client/errors/bare-boundary.ts +11 -11
- package/src/compat/client/errors/control-flow.ts +23 -23
- package/src/compat/client/errors/error-boundary.ts +61 -61
- package/src/compat/client/errors/global-error.ts +101 -91
- package/src/compat/client/errors/install.ts +71 -72
- package/src/compat/client/errors/lazy.ts +23 -23
- package/src/compat/client/errors/primitive-throw.ts +47 -45
- package/src/compat/client/errors/soft-refresh.ts +4 -4
- package/src/compat/client/link-status.ts +33 -33
- package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
- package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
- package/src/compat/client/navigation-scroll.ts +63 -63
- package/src/compat/client/optimistic-routing.ts +93 -88
- package/src/compat/client/prefetch-cache.ts +23 -24
- package/src/compat/client/route-announcer.ts +35 -35
- package/src/compat/client/segment-cache-policy.ts +20 -20
- package/src/compat/client/segment-cache.ts +309 -315
- package/src/compat/client/segment-prefetch.ts +127 -132
- package/src/compat/client/trailing-slash.ts +5 -4
- package/src/compat/css/chunking.ts +113 -116
- package/src/compat/css/inline-css.ts +21 -21
- package/src/compat/css/lightningcss.ts +37 -38
- package/src/compat/css/modules.ts +161 -175
- package/src/compat/css/nonce.ts +7 -7
- package/src/compat/css/sass-plugin.ts +18 -21
- package/src/compat/css/sass.ts +150 -152
- package/src/compat/css/styled-jsx-runtime.ts +27 -27
- package/src/compat/css/styled-jsx.ts +21 -21
- package/src/compat/edge-runtime.ts +27 -27
- package/src/compat/{adapter → export}/build-complete.ts +72 -75
- package/src/compat/export/client.ts +29 -31
- package/src/compat/export/{index.ts → emit.ts} +111 -110
- package/src/compat/export/standalone.ts +62 -54
- package/src/compat/image-optimizer/cache.ts +51 -49
- package/src/compat/image-optimizer/detect.ts +52 -52
- package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
- package/src/compat/image-optimizer/source.ts +77 -80
- package/src/compat/lifecycle/after-scope.ts +26 -26
- package/src/compat/lifecycle/after.ts +48 -45
- package/src/compat/lifecycle/error-funnel.ts +98 -102
- package/src/compat/lifecycle/error-serialize.ts +30 -26
- package/src/compat/lifecycle/error-ui.ts +67 -32
- package/src/compat/lifecycle/instrumentation-client.ts +36 -38
- package/src/compat/lifecycle/instrumentation.ts +85 -85
- package/src/compat/lifecycle/node-console.ts +11 -11
- package/src/compat/lifecycle/testmode.ts +160 -132
- package/src/compat/mdx/compile.ts +60 -57
- package/src/compat/mdx/plugin.ts +11 -11
- package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
- package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
- package/src/compat/metadata.ts +75 -81
- package/src/compat/next/cache.ts +68 -69
- package/src/compat/next/canonical-url.ts +9 -9
- package/src/compat/next/client-cache.ts +19 -19
- package/src/compat/next/client-navigation.ts +112 -116
- package/src/compat/next/client-only.ts +1 -1
- package/src/compat/next/client-script.tsx +99 -93
- package/src/compat/next/client-server.ts +10 -10
- package/src/compat/next/config-loader.ts +176 -173
- package/src/compat/next/config.ts +7 -7
- package/src/compat/next/constants.cjs +6 -6
- package/src/compat/next/constants.ts +6 -6
- package/src/compat/next/custom-server.ts +26 -24
- package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
- package/src/compat/next/dynamic.tsx +21 -18
- package/src/compat/next/error.tsx +52 -52
- package/src/compat/next/font/cache.ts +74 -74
- package/src/compat/next/font/google.ts +2 -2
- package/src/compat/next/font/index.ts +1 -1
- package/src/compat/next/font/local.ts +3 -3
- package/src/compat/next/font/runtime-client.ts +17 -15
- package/src/compat/next/font/runtime.ts +443 -408
- package/src/compat/next/font/shared.ts +120 -108
- package/src/compat/next/form.tsx +63 -63
- package/src/compat/next/head.tsx +2 -2
- package/src/compat/next/headers.ts +103 -95
- package/src/compat/next/image/client.tsx +220 -0
- package/src/compat/next/image/config.ts +56 -58
- package/src/compat/next/image/optimizer.ts +40 -36
- package/src/compat/next/image/patterns.ts +37 -40
- package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
- package/src/compat/next/image/shared.ts +65 -57
- package/src/compat/next/image/static-metadata.ts +98 -107
- package/src/compat/next/image/validate.ts +79 -88
- package/src/compat/next/image.tsx +19 -23
- package/src/compat/next/index.ts +1 -1
- package/src/compat/next/legacy-image.tsx +59 -60
- package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
- package/src/compat/next/link.tsx +158 -158
- package/src/compat/next/navigation.cjs +12 -3
- package/src/compat/next/navigation.ts +48 -50
- package/src/compat/next/offline.ts +27 -27
- package/src/compat/next/og.ts +121 -124
- package/src/compat/next/preferred-region.ts +13 -14
- package/src/compat/next/redirects.ts +58 -56
- package/src/compat/next/resource-hints.ts +73 -76
- package/src/compat/next/rewrites.ts +130 -133
- package/src/compat/next/root-params.ts +45 -45
- package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
- package/src/compat/next/router.cjs +4 -2
- package/src/compat/next/router.ts +58 -61
- package/src/compat/next/script.tsx +108 -108
- package/src/compat/next/server-only.ts +1 -1
- package/src/compat/next/server.ts +19 -19
- package/src/compat/next/svgr.ts +18 -17
- package/src/compat/next/telemetry.ts +24 -24
- package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
- package/src/compat/next/user-agent.ts +53 -49
- package/src/compat/next/web-vitals.ts +22 -24
- package/src/compat/otel/api.ts +41 -41
- package/src/compat/otel/client-trace-metadata.ts +25 -27
- package/src/compat/otel/fetch-span.ts +29 -29
- package/src/compat/otel/tracer.ts +331 -331
- package/src/compat/pages/api.ts +456 -0
- package/src/compat/pages/client-plugin.ts +36 -36
- package/src/compat/pages/router-state.ts +34 -34
- package/src/compat/pages/{index.ts → router.ts} +130 -135
- package/src/compat/ppr/io.ts +12 -12
- package/src/compat/ppr/missing-root-params.ts +34 -38
- package/src/compat/ppr/root-params-scan.ts +66 -66
- package/src/compat/ppr/root-params-transform.ts +24 -26
- package/src/compat/ppr/root-params.ts +30 -30
- package/src/compat/ppr/segment-config-incompat.ts +6 -7
- package/src/compat/protocol.ts +71 -70
- package/src/compat/react/action-state.ts +47 -48
- package/src/compat/react/client-lite.ts +15 -15
- package/src/compat/react/client.ts +4 -4
- package/src/compat/react/compiler-runtime.ts +11 -11
- package/src/compat/react/dom-client.ts +44 -44
- package/src/compat/react/dom-react-server.ts +10 -16
- package/src/compat/react/dom-server.ts +10 -10
- package/src/compat/react/dom.ts +52 -52
- package/src/compat/react/hooks-extra.ts +34 -35
- package/src/compat/react/parity.ts +64 -61
- package/src/compat/react/preact.ts +81 -82
- package/src/compat/react/react-server.ts +28 -28
- package/src/compat/react/router-shim.ts +1 -1
- package/src/compat/react/server-component-use.ts +8 -8
- package/src/compat/react/server-inserted-html.ts +30 -31
- package/src/compat/react/server.ts +53 -55
- package/src/compat/react/use.ts +32 -32
- package/src/compat/react/view-transition.ts +20 -20
- package/src/compat/register/actions.ts +35 -824
- package/src/compat/register/boot.ts +41 -41
- package/src/compat/register/build-tier.ts +5 -5
- package/src/compat/register/build.ts +74 -70
- package/src/compat/register/bundler.ts +161 -156
- package/src/compat/register/cache.ts +20 -20
- package/src/compat/register/client-errors.ts +3 -3
- package/src/compat/register/config.ts +6 -6
- package/src/compat/register/css-extras.ts +30 -34
- package/src/compat/register/edge-runtime.ts +3 -3
- package/src/compat/register/errors.ts +10 -12
- package/src/compat/register/export.ts +16 -16
- package/src/compat/register/font.ts +11 -11
- package/src/compat/register/hooks.ts +2 -2
- package/src/compat/register/image.ts +40 -40
- package/src/compat/register/index.ts +59 -62
- package/src/compat/register/instrumentation-client.ts +10 -10
- package/src/compat/register/lifecycle.ts +25 -28
- package/src/compat/register/mdx.ts +10 -10
- package/src/compat/register/middleware.ts +226 -16
- package/src/compat/register/otel.ts +80 -88
- package/src/compat/register/pages-api.ts +10 -463
- package/src/compat/register/ppr.ts +16 -16
- package/src/compat/register/protocol.ts +17 -18
- package/src/compat/register/proxy.ts +49 -51
- package/src/compat/register/render.ts +79 -76
- package/src/compat/register/routing.ts +162 -159
- package/src/compat/register/segment.ts +24 -1897
- package/src/compat/register/static-image.ts +3 -3
- package/src/compat/register/{misc.ts → taint.ts} +9 -9
- package/src/compat/register/typed-routes.ts +14 -14
- package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
- package/src/compat/register/validation.ts +18 -18
- package/src/compat/segment/loading-boundary.ts +43 -45
- package/src/compat/segment/page-slot.ts +69 -69
- package/src/compat/segment/serve.ts +1884 -0
- package/src/compat/segment/tree.ts +113 -112
- package/src/compat/segment/vary-key.ts +38 -38
- package/src/compat/segment/vary-params.ts +138 -142
- package/src/compat/static-params.ts +14 -12
- package/src/compat/tsconfig-defaults.ts +87 -91
- package/src/compat/typecheck/{index.ts → check.ts} +234 -212
- package/src/compat/typecheck/worker.ts +15 -12
- package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
- package/src/compat/typed-routes/manifest.ts +174 -170
- package/src/compat/typed-routes/typegen.ts +127 -110
- package/src/compat/validation/errors.ts +16 -19
- package/src/compat/validation/prerender-diagnostics.ts +521 -504
- package/src/compat/validation/{index.ts → validate.ts} +647 -648
- package/src/compat-bootstrap.ts +16 -16
- package/src/config.ts +86 -69
- package/src/css/build.ts +230 -227
- package/src/css/postcss.ts +79 -80
- package/src/css/worker.ts +14 -15
- package/src/dev/client-actions.ts +10 -10
- package/src/dev/client-chunk-store.ts +27 -27
- package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
- package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
- package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
- package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
- package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
- package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
- package/src/dev/server.ts +804 -820
- package/src/env.ts +46 -43
- package/src/extensions.ts +491 -478
- package/src/index.ts +8 -8
- package/src/internal.ts +20 -23
- package/src/{islands → render}/boundary-error.ts +3 -3
- package/src/render/hooks.ts +71 -71
- package/src/render/island-context.ts +14 -14
- package/src/render/metadata.ts +310 -310
- package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
- package/src/{ppr.ts → render/ppr.ts} +244 -245
- package/src/render/renderer.ts +2087 -2082
- package/src/render/resource-hints.ts +16 -17
- package/src/render/slots.tsx +224 -235
- package/src/{islands → render}/static-children.ts +9 -12
- package/src/{islands → render}/static-slots.ts +37 -37
- package/src/{cache/context.ts → request/cache.ts} +20 -20
- package/src/request/context.ts +107 -107
- package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +318 -296
- package/src/resolve/source-text.ts +37 -37
- package/src/{dynamic → resolve}/tree-shake.ts +132 -128
- package/src/routing/forwarded.ts +19 -19
- package/src/routing/handler.ts +84 -91
- package/src/routing/href.ts +87 -70
- package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
- package/src/{proxy.ts → routing/proxy.ts} +306 -312
- package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
- package/src/routing/routes.ts +827 -815
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +954 -0
- package/src/{dev → runtime}/module-cache.ts +309 -287
- package/src/{dev → runtime}/module-generations.ts +9 -9
- package/src/{dev → runtime}/module-transform.ts +81 -72
- package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
- package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
- package/src/runtime/vendor.ts +428 -406
- package/src/styles.d.ts +9 -0
- package/src/types.ts +324 -335
- package/src/utils/ansi.ts +5 -5
- package/src/utils/{source.ts → code.ts} +15 -12
- package/src/utils/content-type.ts +3 -3
- package/src/utils/decode.ts +2 -2
- package/src/utils/dev-profile.ts +13 -13
- package/src/utils/error-log.ts +6 -6
- package/src/utils/esbuild.ts +18 -18
- package/src/utils/fs-cache.ts +13 -13
- package/src/utils/fs.ts +57 -49
- package/src/utils/html.ts +20 -24
- package/src/utils/native-require.ts +8 -8
- package/src/utils/serialize.ts +139 -146
- package/src/utils/verbose.ts +18 -18
- package/reference/overview.md +0 -35
- package/src/cli/analyze-print.ts +0 -181
- package/src/compat/middleware/manifest.ts +0 -210
- package/src/compat/next/image-client.tsx +0 -215
- package/src/compat/next/link-usage.ts +0 -29
- package/src/css/index.ts +0 -2
- package/src/render/index.ts +0 -1
- package/src/style-modules.d.ts +0 -9
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// here is read by the router runtime and by nothing else, so it loads with the
|
|
3
3
|
// runtime chunk — the installers finish before the facade hands the runtime out,
|
|
4
4
|
// so the first fetch already sees Next's windows and caches.
|
|
5
|
-
import { setSoftNavBoundary } from '../../
|
|
6
|
-
import { setNavigationScrollAction } from '../../
|
|
5
|
+
import { setSoftNavBoundary } from '../../client/router'
|
|
6
|
+
import { setNavigationScrollAction } from '../../client/router/events'
|
|
7
7
|
import {
|
|
8
8
|
setLoadingShellPredictionPolicy,
|
|
9
9
|
setPrefetchStaleTimePolicy,
|
|
@@ -11,44 +11,44 @@ import {
|
|
|
11
11
|
setSegmentCachePolicy,
|
|
12
12
|
setShellStaleTimePolicy,
|
|
13
13
|
setStylesheetReconciler,
|
|
14
|
-
} from '../../
|
|
15
|
-
import { installSegmentCachePolicy } from './segment-cache-policy'
|
|
14
|
+
} from '../../client/router/policies'
|
|
15
|
+
import { installSegmentCachePolicy } from './segment-cache-policy'
|
|
16
16
|
// `output: 'export'` document fetching. Imported for its module-scope
|
|
17
17
|
// registration, which has to beat the router's first prefetches — this module
|
|
18
18
|
// resolving before the runtime is handed out is exactly that guarantee.
|
|
19
|
-
import '../export/client'
|
|
20
|
-
import { reconcileNextStylesheets } from './css-order'
|
|
21
|
-
import { staleTimeForEntryMs, staticStaleTimeMs } from './prefetch-cache'
|
|
22
|
-
import { isOutsideBasePath } from './base-path'
|
|
23
|
-
import { predictNextLoadingShell } from './optimistic-routing'
|
|
24
|
-
import { applyNextNavigationScroll } from './navigation-scroll'
|
|
25
|
-
import { installRouteAnnouncer } from './route-announcer'
|
|
19
|
+
import '../export/client'
|
|
20
|
+
import { reconcileNextStylesheets } from './css-order'
|
|
21
|
+
import { staleTimeForEntryMs, staticStaleTimeMs } from './prefetch-cache'
|
|
22
|
+
import { isOutsideBasePath } from './base-path'
|
|
23
|
+
import { predictNextLoadingShell } from './optimistic-routing'
|
|
24
|
+
import { applyNextNavigationScroll } from './navigation-scroll'
|
|
25
|
+
import { installRouteAnnouncer } from './route-announcer'
|
|
26
26
|
|
|
27
27
|
export function installNavPolicies() {
|
|
28
28
|
// Route announcer: mount the empty `<next-route-announcer>` and update it on
|
|
29
29
|
// every soft-nav commit (a11y — announce client route changes to AT). Next
|
|
30
30
|
// mounts it at hydration; it announces nothing until the first navigation, and
|
|
31
31
|
// this tier lands on idle, long before one can happen.
|
|
32
|
-
installRouteAnnouncer()
|
|
32
|
+
installRouteAnnouncer()
|
|
33
33
|
// Prefetch staleTime policy: static 300s / dynamic 0, with the
|
|
34
34
|
// experimental.staleTimes override injected as window.__PNEXT_STALE_TIMES__,
|
|
35
35
|
// and a response `x-nextjs-stale-time` header overriding both when present.
|
|
36
|
-
setPrefetchStaleTimePolicy(staleTimeForEntryMs)
|
|
37
|
-
setRevalidationPrefetchDelay(300)
|
|
36
|
+
setPrefetchStaleTimePolicy(staleTimeForEntryMs)
|
|
37
|
+
setRevalidationPrefetchDelay(300)
|
|
38
38
|
// Loading-shell window: a route's cached loading boundary (and the prefetch
|
|
39
39
|
// entry carrying it) reuses for the STATIC window even when its dynamic data
|
|
40
40
|
// is immediately stale — Next's split staleness model.
|
|
41
|
-
setShellStaleTimePolicy(staticStaleTimeMs)
|
|
42
|
-
setLoadingShellPredictionPolicy(predictNextLoadingShell)
|
|
41
|
+
setShellStaleTimePolicy(staticStaleTimeMs)
|
|
42
|
+
setLoadingShellPredictionPolicy(predictNextLoadingShell)
|
|
43
43
|
// Per-segment (vary-params) client cache — Segment-M2 deliverable 3. Entries
|
|
44
44
|
// are keyed on the params a segment provably reads, so one entry serves every
|
|
45
45
|
// param value it does not depend on.
|
|
46
|
-
setSegmentCachePolicy(installSegmentCachePolicy())
|
|
47
|
-
setNavigationScrollAction(applyNextNavigationScroll)
|
|
48
|
-
setStylesheetReconciler(reconcileNextStylesheets)
|
|
46
|
+
setSegmentCachePolicy(installSegmentCachePolicy())
|
|
47
|
+
setNavigationScrollAction(applyNextNavigationScroll)
|
|
48
|
+
setStylesheetReconciler(reconcileNextStylesheets)
|
|
49
49
|
// basePath boundary: a same-origin URL outside the configured basePath must
|
|
50
50
|
// hard-navigate (a `basePath:false` target), not soft-swap.
|
|
51
|
-
setSoftNavBoundary(isOutsideBasePath)
|
|
51
|
+
setSoftNavBoundary(isOutsideBasePath)
|
|
52
52
|
// NOTE: the standalone segment-prefetch wire runtime (segment-prefetch.ts) is
|
|
53
53
|
// deliberately NOT installed: its intent-time tree/body fetches fed caches
|
|
54
54
|
// nothing consumed and broke Next's request-count contracts (a hover inside a
|
|
@@ -10,33 +10,32 @@ import {
|
|
|
10
10
|
registerDeferredInstall,
|
|
11
11
|
setBlockedJavascriptUrlReporter,
|
|
12
12
|
softNavigate,
|
|
13
|
-
} from '../../
|
|
13
|
+
} from '../../client/router'
|
|
14
14
|
|
|
15
|
-
const BLOCKED_JS_URL_MESSAGE =
|
|
16
|
-
'Next.js has blocked a javascript: URL as a security precaution.';
|
|
15
|
+
const BLOCKED_JS_URL_MESSAGE = 'Next.js has blocked a javascript: URL as a security precaution.'
|
|
17
16
|
|
|
18
17
|
export function installNavCompat() {
|
|
19
|
-
if (!process.browser && typeof window === 'undefined') return
|
|
20
|
-
setBlockedJavascriptUrlReporter(() => console.error(BLOCKED_JS_URL_MESSAGE))
|
|
21
|
-
const windowWithReporter = window as typeof window & { __PNEXT_JS_URL_REPORTER__?: boolean }
|
|
18
|
+
if (!process.browser && typeof window === 'undefined') return
|
|
19
|
+
setBlockedJavascriptUrlReporter(() => console.error(BLOCKED_JS_URL_MESSAGE))
|
|
20
|
+
const windowWithReporter = window as typeof window & { __PNEXT_JS_URL_REPORTER__?: boolean }
|
|
22
21
|
if (!windowWithReporter.__PNEXT_JS_URL_REPORTER__) {
|
|
23
|
-
windowWithReporter.__PNEXT_JS_URL_REPORTER__ = true
|
|
22
|
+
windowWithReporter.__PNEXT_JS_URL_REPORTER__ = true
|
|
24
23
|
window.addEventListener('pnext:blocked-javascript-url', () => {
|
|
25
|
-
console.error(BLOCKED_JS_URL_MESSAGE)
|
|
26
|
-
})
|
|
24
|
+
console.error(BLOCKED_JS_URL_MESSAGE)
|
|
25
|
+
})
|
|
27
26
|
}
|
|
28
|
-
installNextRouterGlobal()
|
|
29
|
-
registerDeferredInstall(() => import('./nav-
|
|
27
|
+
installNextRouterGlobal()
|
|
28
|
+
registerDeferredInstall(() => import('./nav-runtime').then(m => m.installNavPolicies()))
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
// `window.next.router` — Next's own global handle. Pure-core apps have no such
|
|
33
32
|
// namespace, so it installs from compat rather than from the router hub.
|
|
34
33
|
function installNextRouterGlobal() {
|
|
35
|
-
window.next ??= {}
|
|
34
|
+
window.next ??= {}
|
|
36
35
|
window.next.router = {
|
|
37
36
|
...window.next.router,
|
|
38
37
|
push: href => softNavigate(String(href)),
|
|
39
38
|
replace: href => softNavigate(String(href), { replace: true }),
|
|
40
39
|
prefetch: href => prefetchRoute(String(href), { strict: true }),
|
|
41
|
-
}
|
|
40
|
+
}
|
|
42
41
|
}
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
import type { NavigationScrollAction, NavigationScrollOptions } from '../../
|
|
1
|
+
import type { NavigationScrollAction, NavigationScrollOptions } from '../../client/router'
|
|
2
2
|
|
|
3
|
-
const RECT_PROPERTIES = ['bottom', 'height', 'left', 'right', 'top', 'width', 'x', 'y'] as const
|
|
3
|
+
const RECT_PROPERTIES = ['bottom', 'height', 'left', 'right', 'top', 'width', 'x', 'y'] as const
|
|
4
4
|
|
|
5
|
-
export type NextScrollPlan = 'hash' | 'preserve' | 'restore' | 'top' | 'top-then-target'
|
|
5
|
+
export type NextScrollPlan = 'hash' | 'preserve' | 'restore' | 'top' | 'top-then-target'
|
|
6
6
|
|
|
7
7
|
export function nextScrollPlan(
|
|
8
8
|
url: URL,
|
|
9
9
|
options: NavigationScrollOptions,
|
|
10
10
|
targetInViewport: boolean | undefined,
|
|
11
11
|
): NextScrollPlan {
|
|
12
|
-
if (options.pop) return 'restore'
|
|
13
|
-
if (options.scroll === false) return 'preserve'
|
|
14
|
-
if (url.hash) return 'hash'
|
|
15
|
-
if (targetInViewport === undefined) return 'top'
|
|
16
|
-
return targetInViewport ? 'preserve' : 'top-then-target'
|
|
12
|
+
if (options.pop) return 'restore'
|
|
13
|
+
if (options.scroll === false) return 'preserve'
|
|
14
|
+
if (url.hash) return 'hash'
|
|
15
|
+
if (targetInViewport === undefined) return 'top'
|
|
16
|
+
return targetInViewport ? 'preserve' : 'top-then-target'
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function firstScrollCandidate<T>(
|
|
20
20
|
candidates: Iterable<T>,
|
|
21
21
|
shouldSkip: (candidate: T) => boolean,
|
|
22
22
|
): T | undefined {
|
|
23
|
-
for (const candidate of candidates) if (!shouldSkip(candidate)) return candidate
|
|
24
|
-
return undefined
|
|
23
|
+
for (const candidate of candidates) if (!shouldSkip(candidate)) return candidate
|
|
24
|
+
return undefined
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function deepestSingleChild<T>(node: T | undefined, onlyChild: (node: T) => T | undefined) {
|
|
28
28
|
while (node) {
|
|
29
|
-
const child = onlyChild(node)
|
|
30
|
-
if (!child) return node
|
|
31
|
-
node = child
|
|
29
|
+
const child = onlyChild(node)
|
|
30
|
+
if (!child) return node
|
|
31
|
+
node = child
|
|
32
32
|
}
|
|
33
|
-
return undefined
|
|
33
|
+
return undefined
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export function topOnlyScrollPosition(scrollX: number): [number, number] {
|
|
37
|
-
return [scrollX, 0]
|
|
37
|
+
return [scrollX, 0]
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export const applyNextNavigationScroll: NavigationScrollAction = (url, options) => {
|
|
41
|
-
const target = scrollFocusTarget()
|
|
41
|
+
const target = scrollFocusTarget()
|
|
42
42
|
const plan = nextScrollPlan(
|
|
43
43
|
url,
|
|
44
44
|
options,
|
|
45
45
|
target ? targetTopInViewport(target, document.documentElement.clientHeight) : undefined,
|
|
46
|
-
)
|
|
46
|
+
)
|
|
47
47
|
if (plan === 'restore') {
|
|
48
|
-
const state = history.state as { __pnextScroll?: [number, number] } | null
|
|
49
|
-
const [x, y] = state?.__pnextScroll ?? [0, 0]
|
|
50
|
-
window.scrollTo(x, y)
|
|
51
|
-
return
|
|
48
|
+
const state = history.state as { __pnextScroll?: [number, number] } | null
|
|
49
|
+
const [x, y] = state?.__pnextScroll ?? [0, 0]
|
|
50
|
+
window.scrollTo(x, y)
|
|
51
|
+
return
|
|
52
52
|
}
|
|
53
53
|
// React hoists a rendered <style precedence>/<link rel=stylesheet>/metadata element out of the segment
|
|
54
54
|
// and into <head>. Next resolves its scroll target with findDOMNode() on the changed segment, so when
|
|
@@ -56,43 +56,43 @@ export const applyNextNavigationScroll: NavigationScrollAction = (url, options)
|
|
|
56
56
|
// zero-rect sibling there and runs out - the navigation scrolls nowhere and focuses nothing. pnext
|
|
57
57
|
// renders those elements in place, so recognize the leading hoisted element and reproduce the same
|
|
58
58
|
// no-op. Placed after `restore` so history traversal still restores its saved position.
|
|
59
|
-
if (segmentLeadsWithHoistedElement()) return
|
|
59
|
+
if (segmentLeadsWithHoistedElement()) return
|
|
60
60
|
if (plan === 'hash') {
|
|
61
|
-
scrollToHash(url.hash)
|
|
62
|
-
return
|
|
61
|
+
scrollToHash(url.hash)
|
|
62
|
+
return
|
|
63
63
|
}
|
|
64
64
|
if (plan === 'preserve') {
|
|
65
|
-
if (target && options.scroll !== false) focusSegment(target)
|
|
66
|
-
return
|
|
65
|
+
if (target && options.scroll !== false) focusSegment(target)
|
|
66
|
+
return
|
|
67
67
|
}
|
|
68
68
|
if (plan === 'top') {
|
|
69
|
-
window.scrollTo(...topOnlyScrollPosition(window.scrollX))
|
|
70
|
-
return
|
|
69
|
+
window.scrollTo(...topOnlyScrollPosition(window.scrollX))
|
|
70
|
+
return
|
|
71
71
|
}
|
|
72
|
-
if (!target) return
|
|
73
|
-
window.scrollTo(...topOnlyScrollPosition(window.scrollX))
|
|
74
|
-
if (!targetTopInViewport(target, document.documentElement.clientHeight)) target.scrollIntoView()
|
|
75
|
-
focusSegment(target)
|
|
76
|
-
}
|
|
72
|
+
if (!target) return
|
|
73
|
+
window.scrollTo(...topOnlyScrollPosition(window.scrollX))
|
|
74
|
+
if (!targetTopInViewport(target, document.documentElement.clientHeight)) target.scrollIntoView()
|
|
75
|
+
focusSegment(target)
|
|
76
|
+
}
|
|
77
77
|
|
|
78
78
|
function shouldSkipScrollElement(element: HTMLElement) {
|
|
79
|
-
const position = getComputedStyle(element).position
|
|
80
|
-
if (position === 'sticky' || position === 'fixed') return true
|
|
81
|
-
const rect = element.getBoundingClientRect()
|
|
82
|
-
return RECT_PROPERTIES.every(item => rect[item] === 0)
|
|
79
|
+
const position = getComputedStyle(element).position
|
|
80
|
+
if (position === 'sticky' || position === 'fixed') return true
|
|
81
|
+
const rect = element.getBoundingClientRect()
|
|
82
|
+
return RECT_PROPERTIES.every(item => rect[item] === 0)
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
function targetTopInViewport(element: HTMLElement, viewportHeight: number) {
|
|
86
|
-
const rects = element.getClientRects()
|
|
87
|
-
if (rects.length === 0) return false
|
|
88
|
-
let top = Number.POSITIVE_INFINITY
|
|
89
|
-
for (const rect of rects) top = Math.min(top, rect.top)
|
|
90
|
-
return top >= 0 && top <= viewportHeight
|
|
86
|
+
const rects = element.getClientRects()
|
|
87
|
+
if (rects.length === 0) return false
|
|
88
|
+
let top = Number.POSITIVE_INFINITY
|
|
89
|
+
for (const rect of rects) top = Math.min(top, rect.top)
|
|
90
|
+
return top >= 0 && top <= viewportHeight
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function scrollFocusTarget(): HTMLElement | null {
|
|
94
|
-
const segment = changedSegmentRoot()
|
|
95
|
-
if (!segment) return null
|
|
94
|
+
const segment = changedSegmentRoot()
|
|
95
|
+
if (!segment) return null
|
|
96
96
|
// Next resolves the target with findDOMNode() on the changed segment: the
|
|
97
97
|
// FIRST host element the segment rendered, never a descendant of it. The page
|
|
98
98
|
// container IS that segment, so its first element child is the target as-is.
|
|
@@ -103,51 +103,51 @@ function scrollFocusTarget(): HTMLElement | null {
|
|
|
103
103
|
? (segment.firstElementChild ?? undefined)
|
|
104
104
|
: deepestSingleChild(segment.firstElementChild ?? undefined, node =>
|
|
105
105
|
node.nextElementSibling === null ? (node.firstElementChild ?? undefined) : undefined,
|
|
106
|
-
)
|
|
107
|
-
const nodes: HTMLElement[] = []
|
|
106
|
+
)
|
|
107
|
+
const nodes: HTMLElement[] = []
|
|
108
108
|
for (let node = first; node; node = node.nextElementSibling ?? undefined) {
|
|
109
|
-
if (node instanceof HTMLElement) nodes.push(node)
|
|
109
|
+
if (node instanceof HTMLElement) nodes.push(node)
|
|
110
110
|
}
|
|
111
|
-
return firstScrollCandidate(nodes, shouldSkipScrollElement) ?? null
|
|
111
|
+
return firstScrollCandidate(nodes, shouldSkipScrollElement) ?? null
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
/** The renderer's page-slot wrapper — the segment a navigation replaced. */
|
|
115
|
-
const PAGE_CONTAINER_ID = 'pnext-page'
|
|
115
|
+
const PAGE_CONTAINER_ID = 'pnext-page'
|
|
116
116
|
|
|
117
117
|
// Elements React relocates into <head> when a component renders them.
|
|
118
|
-
const HOISTED_TAGS = new Set(['style', 'link', 'title', 'meta', 'base'])
|
|
118
|
+
const HOISTED_TAGS = new Set(['style', 'link', 'title', 'meta', 'base'])
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
121
|
* Whether the changed segment's first rendered element is one React would have hoisted into <head> -
|
|
122
122
|
* the state in which Next's scroll walk never reaches the page content (see the call site).
|
|
123
123
|
*/
|
|
124
124
|
function segmentLeadsWithHoistedElement(): boolean {
|
|
125
|
-
const segment = changedSegmentRoot()
|
|
126
|
-
const first = segment?.firstElementChild
|
|
127
|
-
return first !== null && first !== undefined && HOISTED_TAGS.has(first.localName)
|
|
125
|
+
const segment = changedSegmentRoot()
|
|
126
|
+
const first = segment?.firstElementChild
|
|
127
|
+
return first !== null && first !== undefined && HOISTED_TAGS.has(first.localName)
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
function changedSegmentRoot(): HTMLElement | null {
|
|
131
|
-
const page = document.getElementById(PAGE_CONTAINER_ID)
|
|
132
|
-
if (page instanceof HTMLElement) return page
|
|
133
|
-
const root = document.querySelector('[data-pnext-root]')
|
|
134
|
-
return root instanceof HTMLElement ? root : document.body
|
|
131
|
+
const page = document.getElementById(PAGE_CONTAINER_ID)
|
|
132
|
+
if (page instanceof HTMLElement) return page
|
|
133
|
+
const root = document.querySelector('[data-pnext-root]')
|
|
134
|
+
return root instanceof HTMLElement ? root : document.body
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
function scrollToHash(hash: string) {
|
|
138
|
-
const id = decodeURIComponent(hash.slice(1))
|
|
139
|
-
const target = id === 'top' ? document.body : (document.getElementById(id) ?? namedTarget(id))
|
|
140
|
-
target?.scrollIntoView()
|
|
138
|
+
const id = decodeURIComponent(hash.slice(1))
|
|
139
|
+
const target = id === 'top' ? document.body : (document.getElementById(id) ?? namedTarget(id))
|
|
140
|
+
target?.scrollIntoView()
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
function namedTarget(name: string): HTMLElement | null {
|
|
144
|
-
const named = document.getElementsByName(name)[0]
|
|
145
|
-
return named instanceof HTMLElement ? named : null
|
|
144
|
+
const named = document.getElementsByName(name)[0]
|
|
145
|
+
return named instanceof HTMLElement ? named : null
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
function focusSegment(target: HTMLElement) {
|
|
149
149
|
try {
|
|
150
|
-
target.focus({ preventScroll: true })
|
|
150
|
+
target.focus({ preventScroll: true })
|
|
151
151
|
} catch {
|
|
152
152
|
// A swap can detach the target before focus runs.
|
|
153
153
|
}
|
|
@@ -12,26 +12,26 @@
|
|
|
12
12
|
// produced by a rewrite. Such a response must never seed predictions - the rewrite may map a
|
|
13
13
|
// sibling URL somewhere else entirely, or vary on search params.
|
|
14
14
|
|
|
15
|
-
import type { RouteParamValue } from '../../types'
|
|
15
|
+
import type { RouteParamValue } from '../../types'
|
|
16
16
|
|
|
17
17
|
interface RouteState {
|
|
18
18
|
// Bracket-free colon form: `:id` for a dynamic segment, `:slug*` for a
|
|
19
19
|
// catch-all; `catchAllOptional` distinguishes `[[...slug]]` (matches zero
|
|
20
20
|
// segments) from `[...slug]` (requires at least one).
|
|
21
|
-
route: string
|
|
22
|
-
params: Record<string, RouteParamValue
|
|
23
|
-
catchAllOptional?: boolean
|
|
24
|
-
staticChildren?: string[]
|
|
25
|
-
staticChildrenBySegment?: Record<string, string[]
|
|
26
|
-
prefetchKind?: 'shell' | 'eager'
|
|
21
|
+
route: string
|
|
22
|
+
params: Record<string, RouteParamValue>
|
|
23
|
+
catchAllOptional?: boolean
|
|
24
|
+
staticChildren?: string[]
|
|
25
|
+
staticChildrenBySegment?: Record<string, string[]>
|
|
26
|
+
prefetchKind?: 'shell' | 'eager'
|
|
27
27
|
/** `prefetch = 'allow-runtime'`: the shell is request-sampled PER URL. */
|
|
28
|
-
runtimePrefetch?: boolean
|
|
28
|
+
runtimePrefetch?: boolean
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
interface PredictedShell {
|
|
32
|
-
html: string
|
|
33
|
-
route: RouteState
|
|
34
|
-
prefetch?: 'shell' | 'eager'
|
|
32
|
+
html: string
|
|
33
|
+
route: RouteState
|
|
34
|
+
prefetch?: 'shell' | 'eager'
|
|
35
35
|
/**
|
|
36
36
|
* True when the predicted shell belongs to an `allow-runtime` route. Its content is derived from the
|
|
37
37
|
* sampled request's params, so a cached shell of ONE param value says nothing about another - the
|
|
@@ -41,13 +41,13 @@ interface PredictedShell {
|
|
|
41
41
|
* with its `params` HANGING, so the cached shell IS the route's shared App Shell and says the same
|
|
42
42
|
* thing for every param. Revealing another link of the route then has nothing to fetch.
|
|
43
43
|
*/
|
|
44
|
-
runtimePrefetch?: boolean
|
|
44
|
+
runtimePrefetch?: boolean
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/** `experimental.appShells`, stamped into the document by register-render. */
|
|
48
48
|
function appShellsEnabled(): boolean {
|
|
49
|
-
if (!process.browser && typeof window === 'undefined') return false
|
|
50
|
-
return (window as { __PNEXT_APP_SHELLS__?: boolean }).__PNEXT_APP_SHELLS__ === true
|
|
49
|
+
if (!process.browser && typeof window === 'undefined') return false
|
|
50
|
+
return (window as { __PNEXT_APP_SHELLS__?: boolean }).__PNEXT_APP_SHELLS__ === true
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function predictNextLoadingShell(
|
|
@@ -56,13 +56,13 @@ export function predictNextLoadingShell(
|
|
|
56
56
|
): PredictedShell | undefined {
|
|
57
57
|
// Most recently stored shells first (Map preserves insertion order).
|
|
58
58
|
for (const [pathname, html] of [...shells.entries()].reverse()) {
|
|
59
|
-
const state = routeStateFromShell(html)
|
|
60
|
-
if (!state) continue
|
|
59
|
+
const state = routeStateFromShell(html)
|
|
60
|
+
if (!state) continue
|
|
61
61
|
// A shell's own URL must fit its route pattern before it can teach the
|
|
62
62
|
// route trie. This catches rewrites, including a dynamic response served
|
|
63
63
|
// for a known static sibling.
|
|
64
|
-
if (!matchRoute(state, pathname)) continue
|
|
65
|
-
const params = matchRoute(state, url.pathname)
|
|
64
|
+
if (!matchRoute(state, pathname)) continue
|
|
65
|
+
const params = matchRoute(state, url.pathname)
|
|
66
66
|
if (params) {
|
|
67
67
|
return {
|
|
68
68
|
html: reifyShellParams(html, state.params, params),
|
|
@@ -73,65 +73,69 @@ export function predictNextLoadingShell(
|
|
|
73
73
|
},
|
|
74
74
|
...(state.prefetchKind ? { prefetch: state.prefetchKind } : {}),
|
|
75
75
|
...(state.runtimePrefetch && !appShellsEnabled() ? { runtimePrefetch: true } : {}),
|
|
76
|
-
}
|
|
76
|
+
}
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
return undefined
|
|
79
|
+
return undefined
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function routeStateFromShell(html: string): RouteState | undefined {
|
|
83
|
-
const raw = /window\.__PNEXT_ROUTE__=(\{.*?\});<\/script>/.exec(html)?.[1]
|
|
84
|
-
if (!raw) return undefined
|
|
83
|
+
const raw = /window\.__PNEXT_ROUTE__=(\{.*?\});<\/script>/.exec(html)?.[1]
|
|
84
|
+
if (!raw) return undefined
|
|
85
85
|
try {
|
|
86
|
-
const state = JSON.parse(raw) as RouteState
|
|
86
|
+
const state = JSON.parse(raw) as RouteState
|
|
87
87
|
return typeof state.route === 'string' && state.params && typeof state.params === 'object'
|
|
88
88
|
? state
|
|
89
|
-
: undefined
|
|
89
|
+
: undefined
|
|
90
90
|
} catch {
|
|
91
|
-
return undefined
|
|
91
|
+
return undefined
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
function matchRoute(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
function matchRoute(
|
|
96
|
+
state: RouteState,
|
|
97
|
+
pathname: string,
|
|
98
|
+
): Record<string, RouteParamValue> | undefined {
|
|
99
|
+
const pattern = state.route.split('/').filter(Boolean)
|
|
100
|
+
const target = pathname.split('/').filter(Boolean).map(decodeSegment)
|
|
101
|
+
const params: Record<string, RouteParamValue> = {}
|
|
102
|
+
let cursor = 0
|
|
100
103
|
for (let index = 0; index < pattern.length; index++) {
|
|
101
|
-
const segment = pattern[index]
|
|
104
|
+
const segment = pattern[index]!
|
|
102
105
|
// Colon form: `:slug*` is a catch-all, `:id` a single dynamic segment.
|
|
103
|
-
const catchAll = /^:([\w$]+)\*$/.exec(segment)
|
|
104
|
-
const dynamic = catchAll ? null : /^:([\w$]+)$/.exec(segment)
|
|
105
|
-
const siblingKey = String(index)
|
|
106
|
-
const siblings =
|
|
107
|
-
|
|
106
|
+
const catchAll = /^:([\w$]+)\*$/.exec(segment)
|
|
107
|
+
const dynamic = catchAll ? null : /^:([\w$]+)$/.exec(segment)
|
|
108
|
+
const siblingKey = String(index)
|
|
109
|
+
const siblings =
|
|
110
|
+
state.staticChildrenBySegment?.[siblingKey] ??
|
|
111
|
+
(index === pattern.length - 1 ? state.staticChildren : undefined)
|
|
108
112
|
// A dynamic trie node is usable only after the server has said its static
|
|
109
113
|
// siblings are complete. This is the production-only null-vs-empty rule.
|
|
110
|
-
if ((dynamic || catchAll) && siblings === undefined) return undefined
|
|
111
|
-
if (siblings?.includes(target[cursor] ?? '')) return undefined
|
|
114
|
+
if ((dynamic || catchAll) && siblings === undefined) return undefined
|
|
115
|
+
if (siblings?.includes(target[cursor] ?? '')) return undefined
|
|
112
116
|
if (catchAll) {
|
|
113
|
-
const name = catchAll[1]
|
|
117
|
+
const name = catchAll[1]!
|
|
114
118
|
if (state.catchAllOptional) {
|
|
115
119
|
// `[[...slug]]`: matches the base path plus any nested tail.
|
|
116
|
-
const rest = target.slice(cursor)
|
|
117
|
-
if (rest.length > 0) params[name] = rest
|
|
118
|
-
return params
|
|
120
|
+
const rest = target.slice(cursor)
|
|
121
|
+
if (rest.length > 0) params[name] = rest
|
|
122
|
+
return params
|
|
119
123
|
}
|
|
120
124
|
// `[...slug]`: a catch-all learned from a parallel slot must not predict a
|
|
121
125
|
// sibling URL for the primary route. Only reuse it when the source shell
|
|
122
126
|
// itself was rendered through that catch-all, and require ≥1 segment.
|
|
123
|
-
if (!Array.isArray(state.params[name])) return undefined
|
|
124
|
-
const rest = target.slice(cursor)
|
|
125
|
-
if (rest.length === 0) return undefined
|
|
126
|
-
params[name] = rest
|
|
127
|
-
return params
|
|
127
|
+
if (!Array.isArray(state.params[name])) return undefined
|
|
128
|
+
const rest = target.slice(cursor)
|
|
129
|
+
if (rest.length === 0) return undefined
|
|
130
|
+
params[name] = rest
|
|
131
|
+
return params
|
|
128
132
|
}
|
|
129
|
-
const value = target[cursor++]
|
|
130
|
-
if (value === undefined) return undefined
|
|
131
|
-
if (dynamic) params[dynamic[1]!] = value
|
|
132
|
-
else if (segment !== value) return undefined
|
|
133
|
+
const value = target[cursor++]
|
|
134
|
+
if (value === undefined) return undefined
|
|
135
|
+
if (dynamic) params[dynamic[1]!] = value
|
|
136
|
+
else if (segment !== value) return undefined
|
|
133
137
|
}
|
|
134
|
-
return cursor === target.length ? params : undefined
|
|
138
|
+
return cursor === target.length ? params : undefined
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
function reifyShellParams(
|
|
@@ -139,45 +143,46 @@ function reifyShellParams(
|
|
|
139
143
|
previous: Record<string, RouteParamValue>,
|
|
140
144
|
params: Record<string, RouteParamValue>,
|
|
141
145
|
): string {
|
|
142
|
-
if (typeof DOMParser === 'undefined') return html
|
|
143
|
-
const doc = new DOMParser().parseFromString(html, 'text/html')
|
|
144
|
-
materializeClientMarkers(doc)
|
|
146
|
+
if (typeof DOMParser === 'undefined') return html
|
|
147
|
+
const doc = new DOMParser().parseFromString(html, 'text/html')
|
|
148
|
+
materializeClientMarkers(doc)
|
|
145
149
|
for (const root of doc.querySelectorAll<HTMLElement>('[data-pnext-params]')) {
|
|
146
|
-
const raw = root.getAttribute('data-pnext-params')
|
|
147
|
-
if (!raw) continue
|
|
150
|
+
const raw = root.getAttribute('data-pnext-params')
|
|
151
|
+
if (!raw) continue
|
|
148
152
|
try {
|
|
149
|
-
const scoped = JSON.parse(raw) as Record<string, RouteParamValue
|
|
150
|
-
if (sameParams(scoped, previous))
|
|
153
|
+
const scoped = JSON.parse(raw) as Record<string, RouteParamValue>
|
|
154
|
+
if (sameParams(scoped, previous))
|
|
155
|
+
root.setAttribute('data-pnext-params', JSON.stringify(params))
|
|
151
156
|
} catch {
|
|
152
157
|
// A malformed island scope cannot safely be reified.
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
|
-
return `<!doctype html>${doc.documentElement.outerHTML}
|
|
160
|
+
return `<!doctype html>${doc.documentElement.outerHTML}`
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
function materializeClientMarkers(doc: Document): void {
|
|
159
|
-
const comments: Comment[] = []
|
|
160
|
-
const walker = doc.createTreeWalker(doc, NodeFilter.SHOW_COMMENT)
|
|
161
|
-
while (walker.nextNode()) comments.push(walker.currentNode as Comment)
|
|
164
|
+
const comments: Comment[] = []
|
|
165
|
+
const walker = doc.createTreeWalker(doc, NodeFilter.SHOW_COMMENT)
|
|
166
|
+
while (walker.nextNode()) comments.push(walker.currentNode as Comment)
|
|
162
167
|
for (const start of comments.reverse()) {
|
|
163
|
-
const match = /^(pnext-client|pnext-page):([^>]*)$/.exec(start.data)
|
|
164
|
-
const kind = match?.[1]
|
|
165
|
-
const encoded = match?.[2]
|
|
166
|
-
if (!kind || !encoded || !start.parentNode) continue
|
|
167
|
-
const container = doc.createElement('div')
|
|
168
|
-
const tag = kind === 'pnext-page' ? 'div' : 'pnext-client'
|
|
169
|
-
container.innerHTML = `<${tag} ${encoded}></${tag}
|
|
170
|
-
const island = container.firstElementChild
|
|
171
|
-
if (!island) continue
|
|
172
|
-
let node = start.nextSibling
|
|
168
|
+
const match = /^(pnext-client|pnext-page):([^>]*)$/.exec(start.data)
|
|
169
|
+
const kind = match?.[1]
|
|
170
|
+
const encoded = match?.[2]
|
|
171
|
+
if (!kind || !encoded || !start.parentNode) continue
|
|
172
|
+
const container = doc.createElement('div')
|
|
173
|
+
const tag = kind === 'pnext-page' ? 'div' : 'pnext-client'
|
|
174
|
+
container.innerHTML = `<${tag} ${encoded}></${tag}>`
|
|
175
|
+
const island = container.firstElementChild
|
|
176
|
+
if (!island) continue
|
|
177
|
+
let node = start.nextSibling
|
|
173
178
|
while (node && !(node.nodeType === Node.COMMENT_NODE && node.nodeValue === `/${kind}`)) {
|
|
174
|
-
const next = node.nextSibling
|
|
175
|
-
island.append(node)
|
|
176
|
-
node = next
|
|
179
|
+
const next = node.nextSibling
|
|
180
|
+
island.append(node)
|
|
181
|
+
node = next
|
|
177
182
|
}
|
|
178
|
-
if (!node) continue
|
|
179
|
-
start.replaceWith(island)
|
|
180
|
-
node.remove()
|
|
183
|
+
if (!node) continue
|
|
184
|
+
start.replaceWith(island)
|
|
185
|
+
node.remove()
|
|
181
186
|
}
|
|
182
187
|
}
|
|
183
188
|
|
|
@@ -185,22 +190,22 @@ function sameParams(
|
|
|
185
190
|
left: Record<string, RouteParamValue>,
|
|
186
191
|
right: Record<string, RouteParamValue>,
|
|
187
192
|
): boolean {
|
|
188
|
-
const leftKeys = Object.keys(left)
|
|
189
|
-
const rightKeys = Object.keys(right)
|
|
190
|
-
if (leftKeys.length !== rightKeys.length) return false
|
|
193
|
+
const leftKeys = Object.keys(left)
|
|
194
|
+
const rightKeys = Object.keys(right)
|
|
195
|
+
if (leftKeys.length !== rightKeys.length) return false
|
|
191
196
|
return leftKeys.every(key => {
|
|
192
|
-
const a = left[key]
|
|
193
|
-
const b = right[key]
|
|
197
|
+
const a = left[key]
|
|
198
|
+
const b = right[key]
|
|
194
199
|
return Array.isArray(a)
|
|
195
200
|
? Array.isArray(b) && a.length === b.length && a.every((value, index) => value === b[index])
|
|
196
|
-
: a === b
|
|
197
|
-
})
|
|
201
|
+
: a === b
|
|
202
|
+
})
|
|
198
203
|
}
|
|
199
204
|
|
|
200
205
|
function decodeSegment(segment: string) {
|
|
201
206
|
try {
|
|
202
|
-
return decodeURIComponent(segment)
|
|
207
|
+
return decodeURIComponent(segment)
|
|
203
208
|
} catch {
|
|
204
|
-
return segment
|
|
209
|
+
return segment
|
|
205
210
|
}
|
|
206
211
|
}
|