@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
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// markup matches Next byte-for-byte where the e2e suites snapshot it.
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
|
|
8
|
-
import type { ResolvedImagesConfig } from './config'
|
|
8
|
+
import type { ResolvedImagesConfig } from './config'
|
|
9
9
|
|
|
10
|
-
export const BLUR_IMG_SIZE = 8
|
|
11
|
-
export const BLUR_QUALITY = 70
|
|
10
|
+
export const BLUR_IMG_SIZE = 8
|
|
11
|
+
export const BLUR_QUALITY = 70
|
|
12
12
|
|
|
13
13
|
/** Default loader: build the /_next/image optimizer URL. */
|
|
14
14
|
export function defaultLoader(
|
|
@@ -17,8 +17,8 @@ export function defaultLoader(
|
|
|
17
17
|
width: number,
|
|
18
18
|
quality?: number,
|
|
19
19
|
): string {
|
|
20
|
-
const q = findClosestQuality(config, quality)
|
|
21
|
-
return `${config.path}?url=${encodeURIComponent(src)}&w=${width}&q=${q}
|
|
20
|
+
const q = findClosestQuality(config, quality)
|
|
21
|
+
return `${config.path}?url=${encodeURIComponent(src)}&w=${width}&q=${q}`
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -26,37 +26,37 @@ export function defaultLoader(
|
|
|
26
26
|
* out-of-list qualities to the nearest configured one; default 75).
|
|
27
27
|
*/
|
|
28
28
|
export function findClosestQuality(config: ResolvedImagesConfig, quality?: number): number {
|
|
29
|
-
const qualities = config.qualities
|
|
30
|
-
if (!qualities || qualities.length === 0) return quality ?? 75
|
|
29
|
+
const qualities = config.qualities
|
|
30
|
+
if (!qualities || qualities.length === 0) return quality ?? 75
|
|
31
31
|
if (quality === undefined) {
|
|
32
32
|
// Next's default when qualities is configured: 75 if present, else closest to 75.
|
|
33
|
-
return closest(qualities, 75)
|
|
33
|
+
return closest(qualities, 75)
|
|
34
34
|
}
|
|
35
|
-
return closest(qualities, quality)
|
|
35
|
+
return closest(qualities, quality)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function closest(values: number[], target: number): number {
|
|
39
|
-
let best = values[0]
|
|
40
|
-
let bestDelta = Math.abs(best - target)
|
|
39
|
+
let best = values[0]!
|
|
40
|
+
let bestDelta = Math.abs(best - target)
|
|
41
41
|
for (const value of values) {
|
|
42
|
-
const delta = Math.abs(value - target)
|
|
42
|
+
const delta = Math.abs(value - target)
|
|
43
43
|
if (delta < bestDelta) {
|
|
44
|
-
best = value
|
|
45
|
-
bestDelta = delta
|
|
44
|
+
best = value
|
|
45
|
+
bestDelta = delta
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
return best
|
|
48
|
+
return best
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export interface GenImgAttrsResult {
|
|
52
|
-
src: string
|
|
53
|
-
srcSet: string | undefined
|
|
54
|
-
sizes: string | undefined
|
|
52
|
+
src: string
|
|
53
|
+
srcSet: string | undefined
|
|
54
|
+
sizes: string | undefined
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
interface GetWidthsResult {
|
|
58
|
-
widths: number[]
|
|
59
|
-
kind: 'w' | 'x'
|
|
58
|
+
widths: number[]
|
|
59
|
+
kind: 'w' | 'x'
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function getWidths(
|
|
@@ -64,78 +64,86 @@ function getWidths(
|
|
|
64
64
|
width: number | undefined,
|
|
65
65
|
sizes: string | undefined,
|
|
66
66
|
): GetWidthsResult {
|
|
67
|
-
const { deviceSizes, allSizes } = config
|
|
67
|
+
const { deviceSizes, allSizes } = config
|
|
68
68
|
if (sizes) {
|
|
69
|
-
const viewportWidthRe = /(^|\s)(1?\d?\d)vw/g
|
|
70
|
-
const percentSizes: number[] = []
|
|
71
|
-
for (let match; (match = viewportWidthRe.exec(sizes));
|
|
72
|
-
percentSizes.push(parseInt(match[2]!, 10))
|
|
69
|
+
const viewportWidthRe = /(^|\s)(1?\d?\d)vw/g
|
|
70
|
+
const percentSizes: number[] = []
|
|
71
|
+
for (let match; (match = viewportWidthRe.exec(sizes));) {
|
|
72
|
+
percentSizes.push(parseInt(match[2]!, 10))
|
|
73
73
|
}
|
|
74
74
|
if (percentSizes.length) {
|
|
75
|
-
const smallestRatio = Math.min(...percentSizes) * 0.01
|
|
75
|
+
const smallestRatio = Math.min(...percentSizes) * 0.01
|
|
76
76
|
return {
|
|
77
77
|
widths: allSizes.filter(s => s >= deviceSizes[0]! * smallestRatio),
|
|
78
78
|
kind: 'w',
|
|
79
|
-
}
|
|
79
|
+
}
|
|
80
80
|
}
|
|
81
|
-
return { widths: allSizes, kind: 'w' }
|
|
81
|
+
return { widths: allSizes, kind: 'w' }
|
|
82
82
|
}
|
|
83
83
|
if (typeof width !== 'number') {
|
|
84
|
-
return { widths: deviceSizes, kind: 'w' }
|
|
84
|
+
return { widths: deviceSizes, kind: 'w' }
|
|
85
85
|
}
|
|
86
86
|
const widths = [
|
|
87
87
|
...new Set(
|
|
88
88
|
[width, width * 2].map(w => allSizes.find(p => p >= w) || allSizes[allSizes.length - 1]!),
|
|
89
89
|
),
|
|
90
|
-
]
|
|
91
|
-
return { widths, kind: 'x' }
|
|
90
|
+
]
|
|
91
|
+
return { widths, kind: 'x' }
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/** Build src/srcSet/sizes exactly as Next's generateImgAttrs. */
|
|
95
95
|
export function generateImgAttrs(params: {
|
|
96
|
-
config: ResolvedImagesConfig
|
|
97
|
-
src: string
|
|
98
|
-
unoptimized: boolean
|
|
99
|
-
width: number | undefined
|
|
100
|
-
quality: number | undefined
|
|
101
|
-
sizes: string | undefined
|
|
102
|
-
loader: (args: {
|
|
96
|
+
config: ResolvedImagesConfig
|
|
97
|
+
src: string
|
|
98
|
+
unoptimized: boolean
|
|
99
|
+
width: number | undefined
|
|
100
|
+
quality: number | undefined
|
|
101
|
+
sizes: string | undefined
|
|
102
|
+
loader: (args: {
|
|
103
|
+
config: ResolvedImagesConfig
|
|
104
|
+
src: string
|
|
105
|
+
width: number
|
|
106
|
+
quality?: number
|
|
107
|
+
}) => string
|
|
103
108
|
}): GenImgAttrsResult {
|
|
104
|
-
const { config, src, unoptimized, width, quality, sizes, loader } = params
|
|
109
|
+
const { config, src, unoptimized, width, quality, sizes, loader } = params
|
|
105
110
|
if (unoptimized) {
|
|
106
|
-
return { src, srcSet: undefined, sizes: undefined }
|
|
111
|
+
return { src, srcSet: undefined, sizes: undefined }
|
|
107
112
|
}
|
|
108
|
-
const { widths, kind } = getWidths(config, width, sizes)
|
|
109
|
-
const last = widths.length - 1
|
|
113
|
+
const { widths, kind } = getWidths(config, width, sizes)
|
|
114
|
+
const last = widths.length - 1
|
|
110
115
|
return {
|
|
111
116
|
sizes: !sizes && kind === 'w' ? '100vw' : sizes,
|
|
112
117
|
srcSet: widths
|
|
113
|
-
.map(
|
|
118
|
+
.map(
|
|
119
|
+
(w, i) =>
|
|
120
|
+
`${loader({ config, src, quality, width: w })} ${kind === 'w' ? w : i + 1}${kind}`,
|
|
121
|
+
)
|
|
114
122
|
.join(', '),
|
|
115
123
|
src: loader({ config, src, quality, width: widths[last]! }),
|
|
116
|
-
}
|
|
124
|
+
}
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
/** Next's getImageBlurSvg — the inline feGaussianBlur SVG placeholder. */
|
|
120
128
|
export function getImageBlurSvg(params: {
|
|
121
|
-
widthInt?: number
|
|
122
|
-
heightInt?: number
|
|
123
|
-
blurWidth?: number
|
|
124
|
-
blurHeight?: number
|
|
125
|
-
blurDataURL: string
|
|
126
|
-
objectFit?: string
|
|
129
|
+
widthInt?: number
|
|
130
|
+
heightInt?: number
|
|
131
|
+
blurWidth?: number
|
|
132
|
+
blurHeight?: number
|
|
133
|
+
blurDataURL: string
|
|
134
|
+
objectFit?: string
|
|
127
135
|
}): string {
|
|
128
|
-
const { widthInt, heightInt, blurWidth, blurHeight, blurDataURL, objectFit } = params
|
|
129
|
-
const std = 20
|
|
130
|
-
const svgWidth = blurWidth ? blurWidth * 40 : widthInt
|
|
131
|
-
const svgHeight = blurHeight ? blurHeight * 40 : heightInt
|
|
132
|
-
const viewBox = svgWidth && svgHeight ? `viewBox='0 0 ${svgWidth} ${svgHeight}'` : ''
|
|
136
|
+
const { widthInt, heightInt, blurWidth, blurHeight, blurDataURL, objectFit } = params
|
|
137
|
+
const std = 20
|
|
138
|
+
const svgWidth = blurWidth ? blurWidth * 40 : widthInt
|
|
139
|
+
const svgHeight = blurHeight ? blurHeight * 40 : heightInt
|
|
140
|
+
const viewBox = svgWidth && svgHeight ? `viewBox='0 0 ${svgWidth} ${svgHeight}'` : ''
|
|
133
141
|
const preserveAspectRatio = viewBox
|
|
134
142
|
? 'none'
|
|
135
143
|
: objectFit === 'contain'
|
|
136
144
|
? 'xMidYMid'
|
|
137
145
|
: objectFit === 'cover'
|
|
138
146
|
? 'xMidYMid slice'
|
|
139
|
-
: 'none'
|
|
140
|
-
return `%3Csvg xmlns='http://www.w3.org/2000/svg' ${viewBox}%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3CfeColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/%3E%3CfeFlood x='0' y='0' width='100%25' height='100%25'/%3E%3CfeComposite operator='out' in='s'/%3E%3CfeComposite in2='SourceGraphic'/%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3C/filter%3E%3Cimage width='100%25' height='100%25' x='0' y='0' preserveAspectRatio='${preserveAspectRatio}' style='filter: url(%23b);' href='${blurDataURL}'/%3E%3C/svg%3E
|
|
147
|
+
: 'none'
|
|
148
|
+
return `%3Csvg xmlns='http://www.w3.org/2000/svg' ${viewBox}%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3CfeColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/%3E%3CfeFlood x='0' y='0' width='100%25' height='100%25'/%3E%3CfeComposite operator='out' in='s'/%3E%3CfeComposite in2='SourceGraphic'/%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3C/filter%3E%3Cimage width='100%25' height='100%25' x='0' y='0' preserveAspectRatio='${preserveAspectRatio}' style='filter: url(%23b);' href='${blurDataURL}'/%3E%3C/svg%3E`
|
|
141
149
|
}
|
|
@@ -8,25 +8,25 @@
|
|
|
8
8
|
// Registered into core's `staticAssetModule` seam so the server module pipeline picks it up. A
|
|
9
9
|
// pure-core app keeps core's generic asset behavior instead.
|
|
10
10
|
|
|
11
|
-
import { createHash } from 'node:crypto'
|
|
12
|
-
import path from 'node:path'
|
|
13
|
-
import type { StaticAssetModuleContext } from '../../../extensions'
|
|
14
|
-
import { isSvgAsComponentEnabled, svgComponentModule } from '../svgr'
|
|
15
|
-
import { generateBlurDataURL } from './optimizer'
|
|
11
|
+
import { createHash } from 'node:crypto'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import type { StaticAssetModuleContext } from '../../../extensions'
|
|
14
|
+
import { isSvgAsComponentEnabled, svgComponentModule } from '../svgr'
|
|
15
|
+
import { generateBlurDataURL } from './optimizer'
|
|
16
16
|
|
|
17
17
|
export interface ImageDimensions {
|
|
18
|
-
width: number
|
|
19
|
-
height: number
|
|
18
|
+
width: number
|
|
19
|
+
height: number
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/** The `{src,width,height,blurDataURL,blurWidth,blurHeight}` shape next/image consumes. */
|
|
23
23
|
export interface StaticImageData {
|
|
24
|
-
src: string
|
|
25
|
-
width: number
|
|
26
|
-
height: number
|
|
27
|
-
blurDataURL: string
|
|
28
|
-
blurWidth: number
|
|
29
|
-
blurHeight: number
|
|
24
|
+
src: string
|
|
25
|
+
width: number
|
|
26
|
+
height: number
|
|
27
|
+
blurDataURL: string
|
|
28
|
+
blurWidth: number
|
|
29
|
+
blurHeight: number
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const STATIC_IMAGE_EXTENSIONS = new Set([
|
|
@@ -39,7 +39,7 @@ const STATIC_IMAGE_EXTENSIONS = new Set([
|
|
|
39
39
|
'.svg',
|
|
40
40
|
'.ico',
|
|
41
41
|
'.bmp',
|
|
42
|
-
])
|
|
42
|
+
])
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* Core `staticAssetModule` seam implementation for next/image. Given the source file and bytes plus
|
|
@@ -49,23 +49,20 @@ const STATIC_IMAGE_EXTENSIONS = new Set([
|
|
|
49
49
|
export async function staticImageModule(
|
|
50
50
|
context: StaticAssetModuleContext,
|
|
51
51
|
): Promise<string | undefined> {
|
|
52
|
-
const ext = path.extname(context.sourcePath).toLowerCase()
|
|
53
|
-
if (ext === '.svg' && isSvgAsComponentEnabled()) return svgComponentModule(context.bytes)
|
|
54
|
-
if (!STATIC_IMAGE_EXTENSIONS.has(ext)) return undefined
|
|
55
|
-
const src = context.emit(staticMediaRelative(context.sourcePath, context.bytes), context.bytes)
|
|
56
|
-
const data = await staticImageData(src, context.sourcePath, context.bytes)
|
|
57
|
-
return staticImageModuleText(data)
|
|
52
|
+
const ext = path.extname(context.sourcePath).toLowerCase()
|
|
53
|
+
if (ext === '.svg' && isSvgAsComponentEnabled()) return svgComponentModule(context.bytes)
|
|
54
|
+
if (!STATIC_IMAGE_EXTENSIONS.has(ext)) return undefined
|
|
55
|
+
const src = context.emit(staticMediaRelative(context.sourcePath, context.bytes), context.bytes)
|
|
56
|
+
const data = await staticImageData(src, context.sourcePath, context.bytes)
|
|
57
|
+
return staticImageModuleText(data)
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/** POSIX-relative `_next/static/media/<name>.<hash>.<ext>` path for an imported image. */
|
|
61
61
|
export function staticMediaRelative(sourcePath: string, bytes: Uint8Array): string {
|
|
62
|
-
const ext = path.extname(sourcePath).toLowerCase() || '.bin'
|
|
63
|
-
const hash = createHash('sha256')
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.slice(0, 8);
|
|
67
|
-
const base = path.basename(sourcePath, path.extname(sourcePath)).replace(/[^A-Za-z0-9_-]+/g, '-');
|
|
68
|
-
return path.posix.join('_next', 'static', 'media', `${base}.${hash}${ext}`);
|
|
62
|
+
const ext = path.extname(sourcePath).toLowerCase() || '.bin'
|
|
63
|
+
const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 8)
|
|
64
|
+
const base = path.basename(sourcePath, path.extname(sourcePath)).replace(/[^A-Za-z0-9_-]+/g, '-')
|
|
65
|
+
return path.posix.join('_next', 'static', 'media', `${base}.${hash}${ext}`)
|
|
69
66
|
}
|
|
70
67
|
|
|
71
68
|
/** Build the full static-image descriptor (real dimensions + blur placeholder). */
|
|
@@ -74,11 +71,11 @@ export async function staticImageData(
|
|
|
74
71
|
sourcePath: string,
|
|
75
72
|
bytes: Uint8Array,
|
|
76
73
|
): Promise<StaticImageData> {
|
|
77
|
-
const ext = path.extname(sourcePath).toLowerCase() || '.bin'
|
|
78
|
-
const dimensions = imageDimensions(bytes, ext)
|
|
79
|
-
const { blurWidth, blurHeight } = blurDimensions(dimensions)
|
|
74
|
+
const ext = path.extname(sourcePath).toLowerCase() || '.bin'
|
|
75
|
+
const dimensions = imageDimensions(bytes, ext)
|
|
76
|
+
const { blurWidth, blurHeight } = blurDimensions(dimensions)
|
|
80
77
|
const blurDataURL =
|
|
81
|
-
(await generateBlurDataURL(Buffer.from(bytes), ext.slice(1), blurWidth, blurHeight)) ?? ''
|
|
78
|
+
(await generateBlurDataURL(Buffer.from(bytes), ext.slice(1), blurWidth, blurHeight)) ?? ''
|
|
82
79
|
return {
|
|
83
80
|
src,
|
|
84
81
|
width: dimensions?.width ?? 0,
|
|
@@ -86,7 +83,7 @@ export async function staticImageData(
|
|
|
86
83
|
blurDataURL,
|
|
87
84
|
blurWidth: blurDataURL ? blurWidth : 0,
|
|
88
85
|
blurHeight: blurDataURL ? blurHeight : 0,
|
|
89
|
-
}
|
|
86
|
+
}
|
|
90
87
|
}
|
|
91
88
|
|
|
92
89
|
/** ESM module text for a static-image import (default + named field exports). */
|
|
@@ -99,125 +96,119 @@ export const height = image.height;
|
|
|
99
96
|
export const blurDataURL = image.blurDataURL;
|
|
100
97
|
export const blurWidth = image.blurWidth;
|
|
101
98
|
export const blurHeight = image.blurHeight;
|
|
102
|
-
|
|
99
|
+
`
|
|
103
100
|
}
|
|
104
101
|
|
|
105
|
-
const textDecoder = new TextDecoder()
|
|
102
|
+
const textDecoder = new TextDecoder()
|
|
106
103
|
|
|
107
104
|
/** Parse intrinsic pixel dimensions from an image file's header bytes. */
|
|
108
105
|
export function imageDimensions(bytes: Uint8Array, ext: string): ImageDimensions | undefined {
|
|
109
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
106
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
110
107
|
switch (ext) {
|
|
111
108
|
case '.png':
|
|
112
|
-
return pngDimensions(view)
|
|
109
|
+
return pngDimensions(view)
|
|
113
110
|
case '.jpg':
|
|
114
111
|
case '.jpeg':
|
|
115
|
-
return jpegDimensions(view)
|
|
112
|
+
return jpegDimensions(view)
|
|
116
113
|
case '.gif':
|
|
117
|
-
return gifDimensions(view)
|
|
114
|
+
return gifDimensions(view)
|
|
118
115
|
case '.webp':
|
|
119
|
-
return webpDimensions(view, bytes)
|
|
116
|
+
return webpDimensions(view, bytes)
|
|
120
117
|
case '.bmp':
|
|
121
|
-
return bmpDimensions(view)
|
|
118
|
+
return bmpDimensions(view)
|
|
122
119
|
case '.avif':
|
|
123
|
-
return avifDimensions(bytes)
|
|
120
|
+
return avifDimensions(bytes)
|
|
124
121
|
case '.svg':
|
|
125
|
-
return svgDimensions(bytes)
|
|
122
|
+
return svgDimensions(bytes)
|
|
126
123
|
case '.ico':
|
|
127
|
-
return icoDimensions(view)
|
|
124
|
+
return icoDimensions(view)
|
|
128
125
|
default:
|
|
129
|
-
return undefined
|
|
126
|
+
return undefined
|
|
130
127
|
}
|
|
131
128
|
}
|
|
132
129
|
|
|
133
130
|
function pngDimensions(view: DataView): ImageDimensions | undefined {
|
|
134
131
|
// PNG signature (8 bytes) + IHDR chunk: length(4) + 'IHDR'(4) + width(4) + height(4).
|
|
135
|
-
if (view.byteLength < 24) return undefined
|
|
136
|
-
if (view.getUint32(0) !== 0x89504e47) return undefined
|
|
137
|
-
return { width: view.getUint32(16), height: view.getUint32(20) }
|
|
132
|
+
if (view.byteLength < 24) return undefined
|
|
133
|
+
if (view.getUint32(0) !== 0x89504e47) return undefined
|
|
134
|
+
return { width: view.getUint32(16), height: view.getUint32(20) }
|
|
138
135
|
}
|
|
139
136
|
|
|
140
137
|
function jpegDimensions(view: DataView): ImageDimensions | undefined {
|
|
141
|
-
if (view.byteLength < 4 || view.getUint16(0) !== 0xffd8) return undefined
|
|
142
|
-
let offset = 2
|
|
138
|
+
if (view.byteLength < 4 || view.getUint16(0) !== 0xffd8) return undefined
|
|
139
|
+
let offset = 2
|
|
143
140
|
while (offset + 9 < view.byteLength) {
|
|
144
141
|
if (view.getUint8(offset) !== 0xff) {
|
|
145
|
-
offset += 1
|
|
146
|
-
continue
|
|
142
|
+
offset += 1
|
|
143
|
+
continue
|
|
147
144
|
}
|
|
148
|
-
const marker = view.getUint8(offset + 1)
|
|
145
|
+
const marker = view.getUint8(offset + 1)
|
|
149
146
|
// SOF0..SOF15 (baseline / progressive) carry the frame dimensions; skip the
|
|
150
147
|
// RST/standalone markers that have no length field.
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
marker <= 0xcf &&
|
|
154
|
-
marker !== 0xc4 &&
|
|
155
|
-
marker !== 0xc8 &&
|
|
156
|
-
marker !== 0xcc
|
|
157
|
-
) {
|
|
158
|
-
return { height: view.getUint16(offset + 5), width: view.getUint16(offset + 7) };
|
|
148
|
+
if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
|
|
149
|
+
return { height: view.getUint16(offset + 5), width: view.getUint16(offset + 7) }
|
|
159
150
|
}
|
|
160
|
-
const segmentLength = view.getUint16(offset + 2)
|
|
161
|
-
if (segmentLength < 2) return undefined
|
|
162
|
-
offset += 2 + segmentLength
|
|
151
|
+
const segmentLength = view.getUint16(offset + 2)
|
|
152
|
+
if (segmentLength < 2) return undefined
|
|
153
|
+
offset += 2 + segmentLength
|
|
163
154
|
}
|
|
164
|
-
return undefined
|
|
155
|
+
return undefined
|
|
165
156
|
}
|
|
166
157
|
|
|
167
158
|
function gifDimensions(view: DataView): ImageDimensions | undefined {
|
|
168
|
-
if (view.byteLength < 10) return undefined
|
|
159
|
+
if (view.byteLength < 10) return undefined
|
|
169
160
|
// 'GIF8' magic; logical screen width/height are little-endian at offset 6/8.
|
|
170
|
-
if (view.getUint32(0) !== 0x47494638) return undefined
|
|
171
|
-
return { width: view.getUint16(6, true), height: view.getUint16(8, true) }
|
|
161
|
+
if (view.getUint32(0) !== 0x47494638) return undefined
|
|
162
|
+
return { width: view.getUint16(6, true), height: view.getUint16(8, true) }
|
|
172
163
|
}
|
|
173
164
|
|
|
174
165
|
function webpDimensions(view: DataView, bytes: Uint8Array): ImageDimensions | undefined {
|
|
175
|
-
if (view.byteLength < 30) return undefined
|
|
166
|
+
if (view.byteLength < 30) return undefined
|
|
176
167
|
// 'RIFF' .... 'WEBP'
|
|
177
|
-
if (view.getUint32(0) !== 0x52494646 || view.getUint32(8) !== 0x57454250) return undefined
|
|
178
|
-
const format = textDecoder.decode(bytes.subarray(12, 16))
|
|
168
|
+
if (view.getUint32(0) !== 0x52494646 || view.getUint32(8) !== 0x57454250) return undefined
|
|
169
|
+
const format = textDecoder.decode(bytes.subarray(12, 16))
|
|
179
170
|
if (format === 'VP8 ') {
|
|
180
171
|
// Lossy: 16-bit width/height (14 low bits) at offset 26/28, little-endian.
|
|
181
172
|
return {
|
|
182
173
|
width: view.getUint16(26, true) & 0x3fff,
|
|
183
174
|
height: view.getUint16(28, true) & 0x3fff,
|
|
184
|
-
}
|
|
175
|
+
}
|
|
185
176
|
}
|
|
186
177
|
if (format === 'VP8L') {
|
|
187
178
|
// Lossless: 14-bit width-1 / height-1 packed after the 0x2f signature byte.
|
|
188
|
-
const bits = view.getUint32(21, true)
|
|
179
|
+
const bits = view.getUint32(21, true)
|
|
189
180
|
return {
|
|
190
181
|
width: (bits & 0x3fff) + 1,
|
|
191
182
|
height: ((bits >> 14) & 0x3fff) + 1,
|
|
192
|
-
}
|
|
183
|
+
}
|
|
193
184
|
}
|
|
194
185
|
if (format === 'VP8X') {
|
|
195
186
|
// Extended: 24-bit width-1 / height-1 at offset 24/27, little-endian.
|
|
196
|
-
const width = (bytes[24]! | (bytes[25]! << 8) | (bytes[26]! << 16)) + 1
|
|
197
|
-
const height = (bytes[27]! | (bytes[28]! << 8) | (bytes[29]! << 16)) + 1
|
|
198
|
-
return { width, height }
|
|
187
|
+
const width = (bytes[24]! | (bytes[25]! << 8) | (bytes[26]! << 16)) + 1
|
|
188
|
+
const height = (bytes[27]! | (bytes[28]! << 8) | (bytes[29]! << 16)) + 1
|
|
189
|
+
return { width, height }
|
|
199
190
|
}
|
|
200
|
-
return undefined
|
|
191
|
+
return undefined
|
|
201
192
|
}
|
|
202
193
|
|
|
203
194
|
function bmpDimensions(view: DataView): ImageDimensions | undefined {
|
|
204
|
-
if (view.byteLength < 26 || view.getUint16(0) !== 0x424d) return undefined
|
|
205
|
-
return { width: view.getInt32(18, true), height: Math.abs(view.getInt32(22, true)) }
|
|
195
|
+
if (view.byteLength < 26 || view.getUint16(0) !== 0x424d) return undefined
|
|
196
|
+
return { width: view.getInt32(18, true), height: Math.abs(view.getInt32(22, true)) }
|
|
206
197
|
}
|
|
207
198
|
|
|
208
199
|
function icoDimensions(view: DataView): ImageDimensions | undefined {
|
|
209
200
|
if (view.byteLength < 8 || view.getUint16(0, true) !== 0 || view.getUint16(2, true) !== 1) {
|
|
210
|
-
return undefined
|
|
201
|
+
return undefined
|
|
211
202
|
}
|
|
212
203
|
// First directory entry: width/height bytes (0 means 256).
|
|
213
|
-
const width = view.getUint8(6) || 256
|
|
214
|
-
const height = view.getUint8(7) || 256
|
|
215
|
-
return { width, height }
|
|
204
|
+
const width = view.getUint8(6) || 256
|
|
205
|
+
const height = view.getUint8(7) || 256
|
|
206
|
+
return { width, height }
|
|
216
207
|
}
|
|
217
208
|
|
|
218
209
|
function avifDimensions(bytes: Uint8Array): ImageDimensions | undefined {
|
|
219
210
|
// ISOBMFF: find the 'ispe' box (ImageSpatialExtents) → width(4)/height(4).
|
|
220
|
-
const marker = [0x69, 0x73, 0x70, 0x65]
|
|
211
|
+
const marker = [0x69, 0x73, 0x70, 0x65] // 'ispe'
|
|
221
212
|
for (let i = 0; i + 12 <= bytes.length; i += 1) {
|
|
222
213
|
if (
|
|
223
214
|
bytes[i] === marker[0] &&
|
|
@@ -225,36 +216,36 @@ function avifDimensions(bytes: Uint8Array): ImageDimensions | undefined {
|
|
|
225
216
|
bytes[i + 2] === marker[2] &&
|
|
226
217
|
bytes[i + 3] === marker[3]
|
|
227
218
|
) {
|
|
228
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
219
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength)
|
|
229
220
|
// 'ispe' + version/flags(4) then width/height.
|
|
230
|
-
return { width: view.getUint32(i + 8), height: view.getUint32(i + 12) }
|
|
221
|
+
return { width: view.getUint32(i + 8), height: view.getUint32(i + 12) }
|
|
231
222
|
}
|
|
232
223
|
}
|
|
233
|
-
return undefined
|
|
224
|
+
return undefined
|
|
234
225
|
}
|
|
235
226
|
|
|
236
227
|
function svgDimensions(bytes: Uint8Array): ImageDimensions | undefined {
|
|
237
|
-
const head = textDecoder.decode(bytes.subarray(0, Math.min(bytes.length, 4096)))
|
|
238
|
-
const svgTag = /<svg[^>]*>/i.exec(head)?.[0]
|
|
239
|
-
if (!svgTag) return undefined
|
|
240
|
-
const width = svgLength(svgTag, 'width')
|
|
241
|
-
const height = svgLength(svgTag, 'height')
|
|
242
|
-
if (width && height) return { width, height }
|
|
243
|
-
const viewBox = /viewBox\s*=\s*["']?\s*[\d.]+[ ,]+[\d.]+[ ,]+([\d.]+)[ ,]+([\d.]+)/i.exec(svgTag)
|
|
228
|
+
const head = textDecoder.decode(bytes.subarray(0, Math.min(bytes.length, 4096)))
|
|
229
|
+
const svgTag = /<svg[^>]*>/i.exec(head)?.[0]
|
|
230
|
+
if (!svgTag) return undefined
|
|
231
|
+
const width = svgLength(svgTag, 'width')
|
|
232
|
+
const height = svgLength(svgTag, 'height')
|
|
233
|
+
if (width && height) return { width, height }
|
|
234
|
+
const viewBox = /viewBox\s*=\s*["']?\s*[\d.]+[ ,]+[\d.]+[ ,]+([\d.]+)[ ,]+([\d.]+)/i.exec(svgTag)
|
|
244
235
|
if (viewBox?.[1] && viewBox[2]) {
|
|
245
|
-
return { width: Math.round(Number(viewBox[1])), height: Math.round(Number(viewBox[2])) }
|
|
236
|
+
return { width: Math.round(Number(viewBox[1])), height: Math.round(Number(viewBox[2])) }
|
|
246
237
|
}
|
|
247
|
-
return undefined
|
|
238
|
+
return undefined
|
|
248
239
|
}
|
|
249
240
|
|
|
250
241
|
function svgLength(tag: string, attr: string): number | undefined {
|
|
251
|
-
const match = new RegExp(`${attr}\\s*=\\s*["']?\\s*([\\d.]+)`, 'i').exec(tag)
|
|
252
|
-
if (!match?.[1]) return undefined
|
|
253
|
-
const value = Number(match[1])
|
|
254
|
-
return Number.isFinite(value) && value > 0 ? Math.round(value) : undefined
|
|
242
|
+
const match = new RegExp(`${attr}\\s*=\\s*["']?\\s*([\\d.]+)`, 'i').exec(tag)
|
|
243
|
+
if (!match?.[1]) return undefined
|
|
244
|
+
const value = Number(match[1])
|
|
245
|
+
return Number.isFinite(value) && value > 0 ? Math.round(value) : undefined
|
|
255
246
|
}
|
|
256
247
|
|
|
257
|
-
const BLUR_IMG_SIZE = 8
|
|
248
|
+
const BLUR_IMG_SIZE = 8
|
|
258
249
|
|
|
259
250
|
/**
|
|
260
251
|
* Blur thumbnail dimensions, matching Next's `getBlurImage`: shrink the
|
|
@@ -263,21 +254,21 @@ const BLUR_IMG_SIZE = 8;
|
|
|
263
254
|
* `blurWidth < 8` thumbnail instead of a squashed one.
|
|
264
255
|
*/
|
|
265
256
|
export function blurDimensions(dimensions: ImageDimensions | undefined): {
|
|
266
|
-
blurWidth: number
|
|
267
|
-
blurHeight: number
|
|
257
|
+
blurWidth: number
|
|
258
|
+
blurHeight: number
|
|
268
259
|
} {
|
|
269
260
|
if (!dimensions || dimensions.width <= 0 || dimensions.height <= 0) {
|
|
270
|
-
return { blurWidth: BLUR_IMG_SIZE, blurHeight: BLUR_IMG_SIZE }
|
|
261
|
+
return { blurWidth: BLUR_IMG_SIZE, blurHeight: BLUR_IMG_SIZE }
|
|
271
262
|
}
|
|
272
|
-
const { width, height } = dimensions
|
|
263
|
+
const { width, height } = dimensions
|
|
273
264
|
if (width >= height) {
|
|
274
265
|
return {
|
|
275
266
|
blurWidth: BLUR_IMG_SIZE,
|
|
276
267
|
blurHeight: Math.max(Math.round((height / width) * BLUR_IMG_SIZE), 1),
|
|
277
|
-
}
|
|
268
|
+
}
|
|
278
269
|
}
|
|
279
270
|
return {
|
|
280
271
|
blurWidth: Math.max(Math.round((width / height) * BLUR_IMG_SIZE), 1),
|
|
281
272
|
blurHeight: BLUR_IMG_SIZE,
|
|
282
|
-
}
|
|
273
|
+
}
|
|
283
274
|
}
|