@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
|
@@ -4,18 +4,18 @@ import {
|
|
|
4
4
|
getWorkUnit,
|
|
5
5
|
requireRequest,
|
|
6
6
|
requestApiPromise,
|
|
7
|
-
} from '../../request/context'
|
|
8
|
-
import type { NextRequestCookies } from '../../types'
|
|
7
|
+
} from '../../request/context'
|
|
8
|
+
import type { NextRequestCookies } from '../../types'
|
|
9
9
|
import {
|
|
10
10
|
assertDraftModeToggleAllowedInAfter,
|
|
11
11
|
assertRequestApiAllowedInAfter,
|
|
12
12
|
lifecycleError,
|
|
13
|
-
} from '../lifecycle/after-scope'
|
|
14
|
-
import { assertNoDynamicApiInsideCache, currentRenderCacheMeta } from '../cache/revalidate'
|
|
13
|
+
} from '../lifecycle/after-scope'
|
|
14
|
+
import { assertNoDynamicApiInsideCache, currentRenderCacheMeta } from '../cache/revalidate'
|
|
15
15
|
|
|
16
16
|
const COOKIE_MUTATION_ERROR =
|
|
17
17
|
'Cookies can only be modified in a Server Action or Route Handler. ' +
|
|
18
|
-
'Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options'
|
|
18
|
+
'Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options'
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Cookie writes are only legal while the request is executing a Server Action or a Route Handler. Every
|
|
@@ -27,15 +27,15 @@ const COOKIE_MUTATION_ERROR =
|
|
|
27
27
|
* instead; an action sets phase 'action' explicitly. Outside any work unit mutation is left alone.
|
|
28
28
|
*/
|
|
29
29
|
function assertCookieMutationAllowed(): void {
|
|
30
|
-
const unit = getWorkUnit()
|
|
31
|
-
if (!unit) return
|
|
32
|
-
if (unit.phase === 'action') return
|
|
33
|
-
if (unit.phase !== 'after' && unit.routeKind === 'route-handler') return
|
|
34
|
-
throw new Error(COOKIE_MUTATION_ERROR)
|
|
30
|
+
const unit = getWorkUnit()
|
|
31
|
+
if (!unit) return
|
|
32
|
+
if (unit.phase === 'action') return
|
|
33
|
+
if (unit.phase !== 'after' && unit.routeKind === 'route-handler') return
|
|
34
|
+
throw new Error(COOKIE_MUTATION_ERROR)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const HEADERS_MUTATION_ERROR =
|
|
38
|
-
'Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers'
|
|
38
|
+
'Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers'
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* headers() exposes the REQUEST headers, which Next makes read-only: append(), set() and delete() throw
|
|
@@ -47,35 +47,37 @@ function readonlyHeaders(source: Headers): Headers {
|
|
|
47
47
|
get(target, prop) {
|
|
48
48
|
if (prop === 'append' || prop === 'set' || prop === 'delete') {
|
|
49
49
|
return () => {
|
|
50
|
-
throw new Error(HEADERS_MUTATION_ERROR)
|
|
51
|
-
}
|
|
50
|
+
throw new Error(HEADERS_MUTATION_ERROR)
|
|
51
|
+
}
|
|
52
52
|
}
|
|
53
|
-
if (prop === 'getSetCookie') return () => []
|
|
54
|
-
const value: unknown = Reflect.get(target, prop, target)
|
|
55
|
-
return typeof value === 'function'
|
|
53
|
+
if (prop === 'getSetCookie') return () => []
|
|
54
|
+
const value: unknown = Reflect.get(target, prop, target)
|
|
55
|
+
return typeof value === 'function'
|
|
56
|
+
? (value as (...args: unknown[]) => unknown).bind(target)
|
|
57
|
+
: value
|
|
56
58
|
},
|
|
57
|
-
})
|
|
59
|
+
})
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export function headers() {
|
|
61
|
-
assertRequestApiAllowedInAfter('headers()')
|
|
62
|
-
assertDynamicErrorRequestApi('headers()')
|
|
63
|
-
assertNoDynamicApiInsideCache('headers()')
|
|
63
|
+
assertRequestApiAllowedInAfter('headers()')
|
|
64
|
+
assertDynamicErrorRequestApi('headers()')
|
|
65
|
+
assertNoDynamicApiInsideCache('headers()')
|
|
64
66
|
return requestApiPromise('headers()', () =>
|
|
65
67
|
readonlyHeaders(new Headers(requireRequest('headers()').headers)),
|
|
66
|
-
)
|
|
68
|
+
)
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
export interface CookieSetOptions {
|
|
70
|
-
name?: string
|
|
71
|
-
value?: string
|
|
72
|
-
path?: string
|
|
73
|
-
maxAge?: number
|
|
74
|
-
expires?: Date | number
|
|
75
|
-
httpOnly?: boolean
|
|
76
|
-
secure?: boolean
|
|
77
|
-
sameSite?: 'strict' | 'lax' | 'none' | boolean
|
|
78
|
-
domain?: string
|
|
72
|
+
name?: string
|
|
73
|
+
value?: string
|
|
74
|
+
path?: string
|
|
75
|
+
maxAge?: number
|
|
76
|
+
expires?: Date | number
|
|
77
|
+
httpOnly?: boolean
|
|
78
|
+
secure?: boolean
|
|
79
|
+
sameSite?: 'strict' | 'lax' | 'none' | boolean
|
|
80
|
+
domain?: string
|
|
79
81
|
}
|
|
80
82
|
|
|
81
83
|
/**
|
|
@@ -86,75 +88,81 @@ export interface CookieSetOptions {
|
|
|
86
88
|
* set({ name, value, ...options }).
|
|
87
89
|
*/
|
|
88
90
|
export function cookies() {
|
|
89
|
-
assertRequestApiAllowedInAfter('cookies()')
|
|
90
|
-
assertDynamicErrorRequestApi('cookies()')
|
|
91
|
-
assertNoDynamicApiInsideCache('cookies()')
|
|
92
|
-
return requestApiPromise('cookies()', () => wrapCookieJar(requireRequest('cookies()').cookies))
|
|
91
|
+
assertRequestApiAllowedInAfter('cookies()')
|
|
92
|
+
assertDynamicErrorRequestApi('cookies()')
|
|
93
|
+
assertNoDynamicApiInsideCache('cookies()')
|
|
94
|
+
return requestApiPromise('cookies()', () => wrapCookieJar(requireRequest('cookies()').cookies))
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
export function assertDynamicErrorRequestApi(
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
export function assertDynamicErrorRequestApi(
|
|
98
|
+
api: 'headers()' | 'cookies()' | 'connection()',
|
|
99
|
+
): void {
|
|
100
|
+
const meta = currentRenderCacheMeta()
|
|
101
|
+
if (!meta?.dynamicError) return
|
|
98
102
|
throw lifecycleError(
|
|
99
103
|
`Route ${meta.route ?? ''} with \`dynamic = "error"\` couldn't be rendered statically because it used \`${api}\`.`,
|
|
100
|
-
)
|
|
104
|
+
)
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
function wrapCookieJar(jar: NextRequestCookies) {
|
|
104
108
|
return {
|
|
105
109
|
get: (name: CookieNameArg) => jar.get(cookieName(name)),
|
|
106
|
-
getAll: (name?: CookieNameArg) =>
|
|
110
|
+
getAll: (name?: CookieNameArg) =>
|
|
111
|
+
name === undefined ? jar.getAll() : jar.getAll(cookieName(name)),
|
|
107
112
|
has: (name: CookieNameArg) => jar.has(cookieName(name)),
|
|
108
113
|
get size() {
|
|
109
|
-
return jar.getAll().length
|
|
114
|
+
return jar.getAll().length
|
|
110
115
|
},
|
|
111
116
|
[Symbol.iterator]() {
|
|
112
|
-
return jar
|
|
117
|
+
return jar
|
|
118
|
+
.getAll()
|
|
119
|
+
.map(cookie => [cookie.name, cookie] as const)
|
|
120
|
+
[Symbol.iterator]()
|
|
113
121
|
},
|
|
114
122
|
set(nameOrOptions: string | CookieSetOptions, value?: unknown, options?: CookieSetOptions) {
|
|
115
|
-
assertCookieMutationAllowed()
|
|
116
|
-
const parsed = normalizeSetArgs(nameOrOptions, value, options)
|
|
117
|
-
jar.set(parsed.name, parsed.value)
|
|
118
|
-
currentResponseHeaders()?.append('set-cookie', serializeSetCookie(parsed))
|
|
119
|
-
return this
|
|
123
|
+
assertCookieMutationAllowed()
|
|
124
|
+
const parsed = normalizeSetArgs(nameOrOptions, value, options)
|
|
125
|
+
jar.set(parsed.name, parsed.value)
|
|
126
|
+
currentResponseHeaders()?.append('set-cookie', serializeSetCookie(parsed))
|
|
127
|
+
return this
|
|
120
128
|
},
|
|
121
129
|
delete(name: CookieNameArg) {
|
|
122
|
-
assertCookieMutationAllowed()
|
|
123
|
-
const resolved = cookieName(name)
|
|
124
|
-
const existed = jar.delete(resolved)
|
|
130
|
+
assertCookieMutationAllowed()
|
|
131
|
+
const resolved = cookieName(name)
|
|
132
|
+
const existed = jar.delete(resolved)
|
|
125
133
|
currentResponseHeaders()?.append(
|
|
126
134
|
'set-cookie',
|
|
127
135
|
serializeSetCookie({ name: resolved, value: '', path: '/', maxAge: 0 }),
|
|
128
|
-
)
|
|
129
|
-
return existed
|
|
136
|
+
)
|
|
137
|
+
return existed
|
|
130
138
|
},
|
|
131
139
|
clear() {
|
|
132
|
-
for (const cookie of jar.getAll()) this.delete(cookie.name)
|
|
133
|
-
return this
|
|
140
|
+
for (const cookie of jar.getAll()) this.delete(cookie.name)
|
|
141
|
+
return this
|
|
134
142
|
},
|
|
135
143
|
toString() {
|
|
136
144
|
return jar
|
|
137
145
|
.getAll()
|
|
138
146
|
.map(cookie => `${cookie.name}=${encodeURIComponent(cookie.value)}`)
|
|
139
|
-
.join('; ')
|
|
147
|
+
.join('; ')
|
|
140
148
|
},
|
|
141
|
-
}
|
|
149
|
+
}
|
|
142
150
|
}
|
|
143
151
|
|
|
144
152
|
interface NormalizedCookie extends CookieSetOptions {
|
|
145
|
-
name: string
|
|
146
|
-
value: string
|
|
153
|
+
name: string
|
|
154
|
+
value: string
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
/**
|
|
150
158
|
* Next's RequestCookies accessors take a name OR a cookie-shaped object, so
|
|
151
159
|
* `cookies().get({ name: 'x', value: undefined })` reads the same entry as `cookies().get('x')`.
|
|
152
160
|
*/
|
|
153
|
-
type CookieNameArg = string | { name?: string }
|
|
161
|
+
type CookieNameArg = string | { name?: string }
|
|
154
162
|
|
|
155
163
|
function cookieName(arg: CookieNameArg): string {
|
|
156
|
-
if (typeof arg === 'string') return arg
|
|
157
|
-
return typeof arg?.name === 'string' ? arg.name : ''
|
|
164
|
+
if (typeof arg === 'string') return arg
|
|
165
|
+
return typeof arg?.name === 'string' ? arg.name : ''
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
function normalizeSetArgs(
|
|
@@ -163,85 +171,85 @@ function normalizeSetArgs(
|
|
|
163
171
|
options?: CookieSetOptions,
|
|
164
172
|
): NormalizedCookie {
|
|
165
173
|
if (typeof nameOrOptions === 'string') {
|
|
166
|
-
return { ...(options ?? {}), name: nameOrOptions, value: stringifyCookieValue(value) }
|
|
174
|
+
return { ...(options ?? {}), name: nameOrOptions, value: stringifyCookieValue(value) }
|
|
167
175
|
}
|
|
168
176
|
return {
|
|
169
177
|
...nameOrOptions,
|
|
170
178
|
name: String(nameOrOptions.name ?? ''),
|
|
171
179
|
value: String(nameOrOptions.value ?? ''),
|
|
172
|
-
}
|
|
180
|
+
}
|
|
173
181
|
}
|
|
174
182
|
|
|
175
183
|
function stringifyCookieValue(raw: unknown): string {
|
|
176
|
-
if (raw === undefined || raw === null) return ''
|
|
177
|
-
if (typeof raw === 'string') return raw
|
|
184
|
+
if (raw === undefined || raw === null) return ''
|
|
185
|
+
if (typeof raw === 'string') return raw
|
|
178
186
|
if (typeof raw === 'number' || typeof raw === 'boolean' || typeof raw === 'bigint') {
|
|
179
|
-
return String(raw)
|
|
187
|
+
return String(raw)
|
|
180
188
|
}
|
|
181
|
-
return JSON.stringify(raw) ?? ''
|
|
189
|
+
return JSON.stringify(raw) ?? ''
|
|
182
190
|
}
|
|
183
191
|
|
|
184
192
|
function serializeSetCookie(cookie: NormalizedCookie): string {
|
|
185
|
-
const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`]
|
|
186
|
-
parts.push(`Path=${cookie.path ?? '/'}`)
|
|
187
|
-
if (cookie.maxAge !== undefined) parts.push(`Max-Age=${cookie.maxAge}`)
|
|
193
|
+
const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`]
|
|
194
|
+
parts.push(`Path=${cookie.path ?? '/'}`)
|
|
195
|
+
if (cookie.maxAge !== undefined) parts.push(`Max-Age=${cookie.maxAge}`)
|
|
188
196
|
if (cookie.expires !== undefined) {
|
|
189
|
-
const date = cookie.expires instanceof Date ? cookie.expires : new Date(cookie.expires)
|
|
190
|
-
parts.push(`Expires=${date.toUTCString()}`)
|
|
197
|
+
const date = cookie.expires instanceof Date ? cookie.expires : new Date(cookie.expires)
|
|
198
|
+
parts.push(`Expires=${date.toUTCString()}`)
|
|
191
199
|
}
|
|
192
|
-
if (cookie.domain) parts.push(`Domain=${cookie.domain}`)
|
|
193
|
-
if (cookie.httpOnly) parts.push('HttpOnly')
|
|
194
|
-
if (cookie.secure) parts.push('Secure')
|
|
200
|
+
if (cookie.domain) parts.push(`Domain=${cookie.domain}`)
|
|
201
|
+
if (cookie.httpOnly) parts.push('HttpOnly')
|
|
202
|
+
if (cookie.secure) parts.push('Secure')
|
|
195
203
|
if (cookie.sameSite) {
|
|
196
|
-
const value = cookie.sameSite === true ? 'Strict' : capitalize(cookie.sameSite)
|
|
197
|
-
parts.push(`SameSite=${value}`)
|
|
204
|
+
const value = cookie.sameSite === true ? 'Strict' : capitalize(cookie.sameSite)
|
|
205
|
+
parts.push(`SameSite=${value}`)
|
|
198
206
|
}
|
|
199
|
-
return parts.join('; ')
|
|
207
|
+
return parts.join('; ')
|
|
200
208
|
}
|
|
201
209
|
|
|
202
210
|
function capitalize(value: string) {
|
|
203
|
-
return value.charAt(0).toUpperCase() + value.slice(1)
|
|
211
|
+
return value.charAt(0).toUpperCase() + value.slice(1)
|
|
204
212
|
}
|
|
205
213
|
|
|
206
|
-
const draftCookieName = '__prerender_bypass'
|
|
214
|
+
const draftCookieName = '__prerender_bypass'
|
|
207
215
|
|
|
208
216
|
export interface DraftMode {
|
|
209
|
-
readonly isEnabled: boolean
|
|
210
|
-
enable(): void
|
|
211
|
-
disable(): void
|
|
217
|
+
readonly isEnabled: boolean
|
|
218
|
+
enable(): void
|
|
219
|
+
disable(): void
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
export function draftMode(): Promise<DraftMode> {
|
|
215
223
|
// Unlike cookies()/headers(), draftMode() never opts a page into dynamic
|
|
216
224
|
// rendering: during build/partial prerender there is no (real) request and
|
|
217
225
|
// draft is simply off, so reading isEnabled must not postpone or throw.
|
|
218
|
-
const request = currentRequest()
|
|
219
|
-
const requestCookies = request?.cookies
|
|
226
|
+
const request = currentRequest()
|
|
227
|
+
const requestCookies = request?.cookies
|
|
220
228
|
return Promise.resolve({
|
|
221
229
|
get isEnabled() {
|
|
222
|
-
return requestCookies?.has(draftCookieName) ?? false
|
|
230
|
+
return requestCookies?.has(draftCookieName) ?? false
|
|
223
231
|
},
|
|
224
232
|
enable() {
|
|
225
|
-
assertDraftModeToggleAllowedInAfter('enable')
|
|
226
|
-
const cookies = requireRequest('draftMode().enable()').cookies
|
|
227
|
-
const value = cookies.get(draftCookieName)?.value || bypassToken()
|
|
228
|
-
cookies.set(draftCookieName, value)
|
|
233
|
+
assertDraftModeToggleAllowedInAfter('enable')
|
|
234
|
+
const cookies = requireRequest('draftMode().enable()').cookies
|
|
235
|
+
const value = cookies.get(draftCookieName)?.value || bypassToken()
|
|
236
|
+
cookies.set(draftCookieName, value)
|
|
229
237
|
currentResponseHeaders()?.append(
|
|
230
238
|
'set-cookie',
|
|
231
239
|
`${draftCookieName}=${value}; Path=/; HttpOnly; SameSite=None; Secure`,
|
|
232
|
-
)
|
|
240
|
+
)
|
|
233
241
|
},
|
|
234
242
|
disable() {
|
|
235
|
-
assertDraftModeToggleAllowedInAfter('disable')
|
|
236
|
-
requireRequest('draftMode().disable()').cookies.delete(draftCookieName)
|
|
243
|
+
assertDraftModeToggleAllowedInAfter('disable')
|
|
244
|
+
requireRequest('draftMode().disable()').cookies.delete(draftCookieName)
|
|
237
245
|
currentResponseHeaders()?.append(
|
|
238
246
|
'set-cookie',
|
|
239
247
|
`${draftCookieName}=; Path=/; Max-Age=0; HttpOnly; SameSite=None; Secure`,
|
|
240
|
-
)
|
|
248
|
+
)
|
|
241
249
|
},
|
|
242
|
-
})
|
|
250
|
+
})
|
|
243
251
|
}
|
|
244
252
|
|
|
245
253
|
function bypassToken() {
|
|
246
|
-
return globalThis.crypto?.randomUUID?.().replace(/-/g, '') ?? Math.random().toString(36).slice(2)
|
|
254
|
+
return globalThis.crypto?.randomUUID?.().replace(/-/g, '') ?? Math.random().toString(36).slice(2)
|
|
247
255
|
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from 'preact/compat'
|
|
11
|
+
import type { Ref } from 'preact/compat'
|
|
12
|
+
import type { JSX } from 'preact'
|
|
13
|
+
import { getImagesConfig, resolveImageProps } from './props'
|
|
14
|
+
import type { ImageProps, ResolvedImagesConfig } from './props'
|
|
15
|
+
|
|
16
|
+
const useSettleEffect =
|
|
17
|
+
!process.browser && typeof window === 'undefined' ? useEffect : useLayoutEffect
|
|
18
|
+
|
|
19
|
+
// The initial hydration pass, before the entry flips __NEXT_HYDRATED (a
|
|
20
|
+
// microtask after the island mounts). Layout effects run inside it, so an image
|
|
21
|
+
// seen here came from the server document — one rendered by a later client
|
|
22
|
+
// update (a click) did not.
|
|
23
|
+
function hydrating(): boolean {
|
|
24
|
+
return (
|
|
25
|
+
(process.browser || typeof window !== 'undefined') &&
|
|
26
|
+
!(window as { __NEXT_HYDRATED?: boolean }).__NEXT_HYDRATED
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type OnLoad = NonNullable<ImageProps['onLoad']>
|
|
31
|
+
type OnLoadingComplete = NonNullable<ImageProps['onLoadingComplete']>
|
|
32
|
+
type ImageClientProps = ImageProps & { __pnextConfig?: ResolvedImagesConfig }
|
|
33
|
+
|
|
34
|
+
function handleLoading(
|
|
35
|
+
img: HTMLImageElement | null,
|
|
36
|
+
placeholder: string,
|
|
37
|
+
onLoadRef: { current: OnLoad | undefined },
|
|
38
|
+
onLoadingCompleteRef: { current: OnLoadingComplete | undefined },
|
|
39
|
+
setBlurComplete: (b: boolean) => void,
|
|
40
|
+
): void {
|
|
41
|
+
if (!img) return
|
|
42
|
+
const src = img.src
|
|
43
|
+
const marker = img as HTMLImageElement & { 'data-loaded-src'?: string }
|
|
44
|
+
if (marker['data-loaded-src'] === src) return
|
|
45
|
+
marker['data-loaded-src'] = src
|
|
46
|
+
const p = 'decode' in img ? img.decode() : Promise.resolve()
|
|
47
|
+
void p
|
|
48
|
+
.catch(() => undefined)
|
|
49
|
+
.then(() => {
|
|
50
|
+
if (!img.parentElement || !img.isConnected) return
|
|
51
|
+
if (placeholder !== 'empty') setBlurComplete(true)
|
|
52
|
+
if (onLoadRef.current) {
|
|
53
|
+
const event = new Event('load')
|
|
54
|
+
Object.defineProperty(event, 'target', { writable: false, value: img })
|
|
55
|
+
let prevented = false
|
|
56
|
+
let stopped = false
|
|
57
|
+
onLoadRef.current({
|
|
58
|
+
...(event as unknown as Record<string, unknown>),
|
|
59
|
+
nativeEvent: event,
|
|
60
|
+
currentTarget: img,
|
|
61
|
+
target: img,
|
|
62
|
+
isDefaultPrevented: () => prevented,
|
|
63
|
+
isPropagationStopped: () => stopped,
|
|
64
|
+
persist: () => undefined,
|
|
65
|
+
preventDefault: () => {
|
|
66
|
+
prevented = true
|
|
67
|
+
event.preventDefault()
|
|
68
|
+
},
|
|
69
|
+
stopPropagation: () => {
|
|
70
|
+
stopped = true
|
|
71
|
+
event.stopPropagation()
|
|
72
|
+
},
|
|
73
|
+
} as unknown as JSX.TargetedEvent<HTMLImageElement, Event>)
|
|
74
|
+
}
|
|
75
|
+
if (onLoadingCompleteRef.current) onLoadingCompleteRef.current(img)
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function useMergedRef<T>(
|
|
80
|
+
refA: Ref<T> | undefined,
|
|
81
|
+
refB: Ref<T> | undefined,
|
|
82
|
+
): (el: T | null) => void {
|
|
83
|
+
const cleanupA = useRef<(() => void) | null>(null)
|
|
84
|
+
const cleanupB = useRef<(() => void) | null>(null)
|
|
85
|
+
return useCallback(
|
|
86
|
+
(current: T | null) => {
|
|
87
|
+
if (current === null) {
|
|
88
|
+
cleanupA.current?.()
|
|
89
|
+
cleanupA.current = null
|
|
90
|
+
cleanupB.current?.()
|
|
91
|
+
cleanupB.current = null
|
|
92
|
+
} else {
|
|
93
|
+
if (refA) cleanupA.current = applyRef(refA, current)
|
|
94
|
+
if (refB) cleanupB.current = applyRef(refB, current)
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
[refA, refB],
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function applyRef<T>(ref: NonNullable<Ref<T>>, current: T): () => void {
|
|
102
|
+
if (typeof ref === 'function') {
|
|
103
|
+
const cleanup = ref(current)
|
|
104
|
+
return typeof cleanup === 'function' ? cleanup : () => (ref as (v: T | null) => void)(null)
|
|
105
|
+
}
|
|
106
|
+
;(ref as { current: T | null }).current = current
|
|
107
|
+
return () => {
|
|
108
|
+
;(ref as { current: T | null }).current = null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const Image = forwardRef<HTMLImageElement, ImageClientProps>(function Image(
|
|
113
|
+
{ __pnextConfig, ...imageProps },
|
|
114
|
+
forwardedRef,
|
|
115
|
+
) {
|
|
116
|
+
const config = __pnextConfig ?? getImagesConfig()
|
|
117
|
+
const [blurComplete, setBlurComplete] = useState(false)
|
|
118
|
+
const [showAltText, setShowAltText] = useState(false)
|
|
119
|
+
const { props, meta } = resolveImageProps(imageProps, config, blurComplete, showAltText)
|
|
120
|
+
|
|
121
|
+
const { onLoad, onError, onLoadingComplete } = imageProps
|
|
122
|
+
const onLoadRef = useRef(onLoad)
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
onLoadRef.current = onLoad
|
|
125
|
+
}, [onLoad])
|
|
126
|
+
const onLoadingCompleteRef = useRef(onLoadingComplete)
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
onLoadingCompleteRef.current = onLoadingComplete
|
|
129
|
+
}, [onLoadingComplete])
|
|
130
|
+
|
|
131
|
+
const placeholder = meta.placeholder
|
|
132
|
+
const renderedSrc = typeof props.src === 'string' ? props.src : ''
|
|
133
|
+
const insertedImgRef = useRef<HTMLImageElement | null>(null)
|
|
134
|
+
const settledSrcRef = useRef<string | undefined>(undefined)
|
|
135
|
+
// True while an error the app must not see is outstanding: the server-rendered
|
|
136
|
+
// request that was already in flight when this component mounted. See below.
|
|
137
|
+
const swallowErrorRef = useRef(false)
|
|
138
|
+
useSettleEffect(() => {
|
|
139
|
+
const img = insertedImgRef.current
|
|
140
|
+
if (img === null || settledSrcRef.current === renderedSrc) return
|
|
141
|
+
settledSrcRef.current = renderedSrc
|
|
142
|
+
// An error on a server-rendered image happens before onError is attached,
|
|
143
|
+
// so it is lost — Next re-issues the request once at mount to surface it.
|
|
144
|
+
if (onError) {
|
|
145
|
+
if (img.complete) {
|
|
146
|
+
const currentSrc = img.src
|
|
147
|
+
img.src = currentSrc
|
|
148
|
+
} else if (hydrating()) {
|
|
149
|
+
// Same situation, mid-flight: the request started with the document,
|
|
150
|
+
// so its outcome still predates this mount. Swallow it and re-issue
|
|
151
|
+
// once it lands, so onError sees an error raised against this mount.
|
|
152
|
+
swallowErrorRef.current = true
|
|
153
|
+
img.addEventListener(
|
|
154
|
+
'error',
|
|
155
|
+
() => {
|
|
156
|
+
swallowErrorRef.current = false
|
|
157
|
+
const failedSrc = img.src
|
|
158
|
+
img.src = failedSrc
|
|
159
|
+
},
|
|
160
|
+
{ once: true },
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const settle = () =>
|
|
165
|
+
handleLoading(img, placeholder, onLoadRef, onLoadingCompleteRef, setBlurComplete)
|
|
166
|
+
if (img.complete) settle()
|
|
167
|
+
}, [renderedSrc, placeholder, onError])
|
|
168
|
+
|
|
169
|
+
const ref = useMergedRef<HTMLImageElement>(forwardedRef, insertedImgRef)
|
|
170
|
+
const extra = imageProps as unknown as {
|
|
171
|
+
crossOrigin?: string
|
|
172
|
+
referrerPolicy?: string
|
|
173
|
+
fetchPriority?: string
|
|
174
|
+
}
|
|
175
|
+
const preloadLink = meta.preload ? (
|
|
176
|
+
<link
|
|
177
|
+
rel="preload"
|
|
178
|
+
as="image"
|
|
179
|
+
href={meta.imgAttributes.srcSet ? undefined : meta.imgAttributes.src}
|
|
180
|
+
{...({
|
|
181
|
+
imagesrcset: meta.imgAttributes.srcSet,
|
|
182
|
+
imagesizes: meta.imgAttributes.sizes,
|
|
183
|
+
crossOrigin: extra.crossOrigin,
|
|
184
|
+
referrerPolicy: extra.referrerPolicy,
|
|
185
|
+
fetchpriority: extra.fetchPriority,
|
|
186
|
+
} as Record<string, unknown>)}
|
|
187
|
+
/>
|
|
188
|
+
) : null
|
|
189
|
+
|
|
190
|
+
return (
|
|
191
|
+
<>
|
|
192
|
+
{preloadLink}
|
|
193
|
+
<img
|
|
194
|
+
{...props}
|
|
195
|
+
data-nimg={meta.fill ? 'fill' : '1'}
|
|
196
|
+
ref={ref}
|
|
197
|
+
onLoad={event => {
|
|
198
|
+
handleLoading(
|
|
199
|
+
event.currentTarget,
|
|
200
|
+
placeholder,
|
|
201
|
+
onLoadRef,
|
|
202
|
+
onLoadingCompleteRef,
|
|
203
|
+
setBlurComplete,
|
|
204
|
+
)
|
|
205
|
+
}}
|
|
206
|
+
onError={event => {
|
|
207
|
+
setShowAltText(true)
|
|
208
|
+
if (placeholder !== 'empty') setBlurComplete(true)
|
|
209
|
+
// The pre-mount failure is re-issued by the settle effect; the app
|
|
210
|
+
// hears about the retry, not the request that predated its handler.
|
|
211
|
+
if (swallowErrorRef.current) return
|
|
212
|
+
onError?.(event)
|
|
213
|
+
}}
|
|
214
|
+
/>
|
|
215
|
+
</>
|
|
216
|
+
)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
export default Image
|
|
220
|
+
export { Image }
|