@wular/pnext 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -0
- package/bin/pnext +67 -0
- package/config/lint/base.js +48 -0
- package/config/ts/base.json +26 -0
- package/config/ts/react.json +13 -0
- package/package.json +70 -0
- package/reference/compat.md +63 -0
- package/reference/config.md +120 -0
- package/reference/css.md +58 -0
- package/reference/dev.md +69 -0
- package/reference/env.md +40 -0
- package/reference/metadata.md +86 -0
- package/reference/navigation.md +149 -0
- package/reference/overview.md +35 -0
- package/reference/performance.md +97 -0
- package/reference/rendering.md +127 -0
- package/reference/routing.md +167 -0
- package/reference/typegen.md +64 -0
- package/src/api/cache.ts +80 -0
- package/src/api/client-cache.ts +9 -0
- package/src/api/client-navigation.ts +279 -0
- package/src/api/dynamic.tsx +102 -0
- package/src/api/link.tsx +119 -0
- package/src/api/navigation.ts +198 -0
- package/src/api/router/events.ts +53 -0
- package/src/api/router/history.ts +70 -0
- package/src/api/router/hub.ts +194 -0
- package/src/api/router/policies.ts +107 -0
- package/src/api/router/runtime.ts +5238 -0
- package/src/api/router/types.ts +299 -0
- package/src/api/router.ts +167 -0
- package/src/api/server.ts +323 -0
- package/src/api/suspense.ts +16 -0
- package/src/cache/context.ts +61 -0
- package/src/cli/adapters/vercel-warm.ts +375 -0
- package/src/cli/adapters/vercel.ts +1310 -0
- package/src/cli/analyze-print.ts +181 -0
- package/src/cli/analyze.ts +328 -0
- package/src/cli/boot-trace.ts +29 -0
- package/src/cli/build.ts +3114 -0
- package/src/cli/dev.ts +276 -0
- package/src/cli/index.ts +196 -0
- package/src/cli/named-bin.ts +119 -0
- package/src/cli/serve-ui.ts +160 -0
- package/src/cli/start.ts +1425 -0
- package/src/client/build.ts +2136 -0
- package/src/client/chunk-fold.ts +526 -0
- package/src/client/entry.ts +1525 -0
- package/src/client/paths.ts +22 -0
- package/src/client/prebuilt.ts +621 -0
- package/src/client/profile.ts +75 -0
- package/src/client/react-compiler.ts +94 -0
- package/src/client/reference-stub.ts +145 -0
- package/src/client/reference.ts +47 -0
- package/src/compat/actions/action-client.ts +531 -0
- package/src/compat/actions/action-dispatch.ts +676 -0
- package/src/compat/actions/action-router.ts +40 -0
- package/src/compat/actions/action-shared.ts +95 -0
- package/src/compat/actions/client-plugin.ts +135 -0
- package/src/compat/actions/client-stub.ts +65 -0
- package/src/compat/actions/config.ts +164 -0
- package/src/compat/actions/detect.ts +208 -0
- package/src/compat/actions/discovery.ts +244 -0
- package/src/compat/actions/early-submit.ts +40 -0
- package/src/compat/actions/endpoint.ts +602 -0
- package/src/compat/actions/flight.ts +52 -0
- package/src/compat/actions/form-state.ts +73 -0
- package/src/compat/actions/hoist.ts +485 -0
- package/src/compat/actions/ids.ts +39 -0
- package/src/compat/actions/index.ts +41 -0
- package/src/compat/actions/instances.ts +144 -0
- package/src/compat/actions/origin.ts +109 -0
- package/src/compat/actions/protocol.ts +125 -0
- package/src/compat/actions/registry.ts +74 -0
- package/src/compat/actions/rewrite.ts +282 -0
- package/src/compat/actions/serve.ts +414 -0
- package/src/compat/actions/server-tag.ts +21 -0
- package/src/compat/actions/unrecognized-error.ts +30 -0
- package/src/compat/adapter/build-complete.ts +257 -0
- package/src/compat/bundler/bun-externals.ts +53 -0
- package/src/compat/bundler/cjs-exports.ts +542 -0
- package/src/compat/bundler/config.ts +363 -0
- package/src/compat/bundler/externals.ts +34 -0
- package/src/compat/bundler/import-meta-url.ts +60 -0
- package/src/compat/bundler/modularize-imports.ts +119 -0
- package/src/compat/bundler/new-url-asset.ts +87 -0
- package/src/compat/bundler/optimize-package-imports.ts +273 -0
- package/src/compat/bundler/polyfill.ts +88 -0
- package/src/compat/bundler/react-compiler.ts +61 -0
- package/src/compat/bundler/react-profiler.tsx +25 -0
- package/src/compat/bundler/relay-transform.ts +116 -0
- package/src/compat/bundler/require-context.ts +281 -0
- package/src/compat/bundler/resolve-extensions.ts +75 -0
- package/src/compat/bundler/source-cache.ts +61 -0
- package/src/compat/bundler/static-imports.ts +25 -0
- package/src/compat/bundler/symlink-imports.ts +119 -0
- package/src/compat/bundler/tsconfig-paths.ts +50 -0
- package/src/compat/bundler/wasm.ts +153 -0
- package/src/compat/bundler/webpack-loaders.ts +685 -0
- package/src/compat/bundler/worker.ts +278 -0
- package/src/compat/cache/build-flags.ts +81 -0
- package/src/compat/cache/build-prerender-errors.ts +163 -0
- package/src/compat/cache/custom-handler.ts +159 -0
- package/src/compat/cache/fetch-patch.ts +745 -0
- package/src/compat/cache/handler.ts +100 -0
- package/src/compat/cache/modern-handler.ts +275 -0
- package/src/compat/cache/resume-data-cache.ts +143 -0
- package/src/compat/cache/revalidate.ts +759 -0
- package/src/compat/cache/runtime-error.ts +124 -0
- package/src/compat/cache/use-cache-transform.ts +961 -0
- package/src/compat/cache/use-cache.ts +1695 -0
- package/src/compat/cache-control.ts +269 -0
- package/src/compat/client/base-path.ts +64 -0
- package/src/compat/client/css-order.ts +36 -0
- package/src/compat/client/errors/control-flow.ts +92 -0
- package/src/compat/client/errors/error-boundary.ts +222 -0
- package/src/compat/client/errors/global-error.ts +238 -0
- package/src/compat/client/errors/install.ts +217 -0
- package/src/compat/client/errors/lazy.ts +53 -0
- package/src/compat/client/errors/primitive-throw.ts +126 -0
- package/src/compat/client/errors/soft-refresh.ts +14 -0
- package/src/compat/client/link-status.ts +86 -0
- package/src/compat/client/nav-compat-runtime.ts +57 -0
- package/src/compat/client/nav-compat.ts +42 -0
- package/src/compat/client/navigation-scroll.ts +154 -0
- package/src/compat/client/optimistic-routing.ts +206 -0
- package/src/compat/client/prefetch-cache.ts +111 -0
- package/src/compat/client/route-announcer.ts +72 -0
- package/src/compat/client/segment-cache-policy.ts +159 -0
- package/src/compat/client/segment-cache.ts +1077 -0
- package/src/compat/client/segment-prefetch.ts +375 -0
- package/src/compat/client/trailing-slash.ts +24 -0
- package/src/compat/css/chunking.ts +254 -0
- package/src/compat/css/inline-css.ts +73 -0
- package/src/compat/css/lightningcss.ts +90 -0
- package/src/compat/css/modules.ts +373 -0
- package/src/compat/css/nonce.ts +30 -0
- package/src/compat/css/sass-plugin.ts +65 -0
- package/src/compat/css/sass.ts +392 -0
- package/src/compat/css/styled-jsx-runtime.ts +80 -0
- package/src/compat/css/styled-jsx.ts +49 -0
- package/src/compat/edge-runtime.ts +71 -0
- package/src/compat/export/client.ts +112 -0
- package/src/compat/export/index.ts +272 -0
- package/src/compat/export/standalone.ts +207 -0
- package/src/compat/image-optimizer/cache.ts +119 -0
- package/src/compat/image-optimizer/detect.ts +143 -0
- package/src/compat/image-optimizer/index.ts +601 -0
- package/src/compat/image-optimizer/source.ts +243 -0
- package/src/compat/index.ts +458 -0
- package/src/compat/lifecycle/after-scope.ts +86 -0
- package/src/compat/lifecycle/after.ts +173 -0
- package/src/compat/lifecycle/error-funnel.ts +306 -0
- package/src/compat/lifecycle/error-serialize.ts +87 -0
- package/src/compat/lifecycle/error-ui.ts +167 -0
- package/src/compat/lifecycle/instrumentation-client.ts +138 -0
- package/src/compat/lifecycle/instrumentation.ts +277 -0
- package/src/compat/lifecycle/node-console.ts +19 -0
- package/src/compat/lifecycle/testmode.ts +263 -0
- package/src/compat/mdx/compile.ts +219 -0
- package/src/compat/mdx/next-mdx-stub.ts +46 -0
- package/src/compat/mdx/plugin.ts +37 -0
- package/src/compat/metadata-route-artifacts.ts +458 -0
- package/src/compat/metadata.ts +295 -0
- package/src/compat/middleware/manifest.ts +210 -0
- package/src/compat/misc/action-return.ts +173 -0
- package/src/compat/next/cache.ts +211 -0
- package/src/compat/next/canonical-url.ts +35 -0
- package/src/compat/next/client-cache.ts +57 -0
- package/src/compat/next/client-navigation.ts +313 -0
- package/src/compat/next/client-only.ts +3 -0
- package/src/compat/next/client-script.tsx +215 -0
- package/src/compat/next/client-server.ts +39 -0
- package/src/compat/next/config-loader.ts +569 -0
- package/src/compat/next/config.ts +29 -0
- package/src/compat/next/constants.cjs +6 -0
- package/src/compat/next/constants.ts +6 -0
- package/src/compat/next/custom-server.ts +236 -0
- package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
- package/src/compat/next/dynamic.tsx +46 -0
- package/src/compat/next/error.tsx +148 -0
- package/src/compat/next/font/cache.ts +171 -0
- package/src/compat/next/font/google.ts +2 -0
- package/src/compat/next/font/index.ts +8 -0
- package/src/compat/next/font/local.ts +5 -0
- package/src/compat/next/font/runtime-client.ts +71 -0
- package/src/compat/next/font/runtime.ts +974 -0
- package/src/compat/next/font/shared.ts +281 -0
- package/src/compat/next/form.tsx +156 -0
- package/src/compat/next/head.tsx +10 -0
- package/src/compat/next/headers.ts +247 -0
- package/src/compat/next/image/config.ts +196 -0
- package/src/compat/next/image/optimizer.ts +96 -0
- package/src/compat/next/image/patterns.ts +103 -0
- package/src/compat/next/image/shared.ts +141 -0
- package/src/compat/next/image/static-metadata.ts +283 -0
- package/src/compat/next/image/validate.ts +269 -0
- package/src/compat/next/image-client.tsx +215 -0
- package/src/compat/next/image-props.ts +575 -0
- package/src/compat/next/image-usage.ts +102 -0
- package/src/compat/next/image.tsx +56 -0
- package/src/compat/next/index.ts +1 -0
- package/src/compat/next/legacy-image.tsx +97 -0
- package/src/compat/next/link-usage.ts +29 -0
- package/src/compat/next/link-validation-transform.ts +200 -0
- package/src/compat/next/link.tsx +466 -0
- package/src/compat/next/navigation.cjs +21 -0
- package/src/compat/next/navigation.ts +188 -0
- package/src/compat/next/offline.ts +51 -0
- package/src/compat/next/og.ts +324 -0
- package/src/compat/next/optimistic-route-state.ts +188 -0
- package/src/compat/next/preferred-region.ts +39 -0
- package/src/compat/next/redirects.ts +131 -0
- package/src/compat/next/resource-hints.ts +136 -0
- package/src/compat/next/rewrites.ts +350 -0
- package/src/compat/next/root-params.ts +142 -0
- package/src/compat/next/router.cjs +49 -0
- package/src/compat/next/router.ts +143 -0
- package/src/compat/next/script.tsx +355 -0
- package/src/compat/next/server-only.ts +3 -0
- package/src/compat/next/server.ts +28 -0
- package/src/compat/next/svgr.ts +58 -0
- package/src/compat/next/telemetry.ts +77 -0
- package/src/compat/next/user-agent.ts +100 -0
- package/src/compat/next/web-vitals.ts +56 -0
- package/src/compat/otel/api.ts +95 -0
- package/src/compat/otel/client-trace-metadata.ts +71 -0
- package/src/compat/otel/fetch-span.ts +77 -0
- package/src/compat/otel/tracer.ts +944 -0
- package/src/compat/pages/client-plugin.ts +108 -0
- package/src/compat/pages/index.ts +527 -0
- package/src/compat/pages/router-state.ts +94 -0
- package/src/compat/ppr/io.ts +38 -0
- package/src/compat/ppr/missing-root-params.ts +105 -0
- package/src/compat/ppr/root-params-scan.ts +164 -0
- package/src/compat/ppr/root-params-transform.ts +75 -0
- package/src/compat/ppr/root-params.ts +129 -0
- package/src/compat/ppr/segment-config-incompat.ts +34 -0
- package/src/compat/protocol.ts +202 -0
- package/src/compat/react/client.ts +59 -0
- package/src/compat/react/compiler-runtime.ts +60 -0
- package/src/compat/react/dom-client.ts +115 -0
- package/src/compat/react/dom-react-server.ts +20 -0
- package/src/compat/react/dom-server.ts +40 -0
- package/src/compat/react/dom.ts +154 -0
- package/src/compat/react/preact.ts +522 -0
- package/src/compat/react/react-server.ts +84 -0
- package/src/compat/react/router-shim.ts +26 -0
- package/src/compat/react/server-component-use.ts +48 -0
- package/src/compat/react/server-inserted-html.ts +87 -0
- package/src/compat/react/server.ts +156 -0
- package/src/compat/react/view-transition.ts +60 -0
- package/src/compat/register/actions.ts +875 -0
- package/src/compat/register/boot.ts +141 -0
- package/src/compat/register/build-tier.ts +11 -0
- package/src/compat/register/build.ts +182 -0
- package/src/compat/register/bundler.ts +587 -0
- package/src/compat/register/cache.ts +85 -0
- package/src/compat/register/client-errors.ts +18 -0
- package/src/compat/register/config.ts +17 -0
- package/src/compat/register/css-extras.ts +120 -0
- package/src/compat/register/edge-runtime.ts +6 -0
- package/src/compat/register/errors.ts +46 -0
- package/src/compat/register/export.ts +23 -0
- package/src/compat/register/font.ts +36 -0
- package/src/compat/register/hooks.ts +34 -0
- package/src/compat/register/image.ts +133 -0
- package/src/compat/register/index.ts +111 -0
- package/src/compat/register/instrumentation-client.ts +35 -0
- package/src/compat/register/lifecycle.ts +86 -0
- package/src/compat/register/mdx.ts +48 -0
- package/src/compat/register/middleware.ts +36 -0
- package/src/compat/register/misc.ts +44 -0
- package/src/compat/register/otel.ts +288 -0
- package/src/compat/register/pages-api.ts +473 -0
- package/src/compat/register/ppr.ts +56 -0
- package/src/compat/register/protocol.ts +57 -0
- package/src/compat/register/proxy.ts +127 -0
- package/src/compat/register/render.ts +268 -0
- package/src/compat/register/routing.ts +410 -0
- package/src/compat/register/segment.ts +1903 -0
- package/src/compat/register/static-image.ts +21 -0
- package/src/compat/register/typed-routes.ts +35 -0
- package/src/compat/register/usecache.ts +131 -0
- package/src/compat/register/validation.ts +56 -0
- package/src/compat/segment/loading-boundary.ts +113 -0
- package/src/compat/segment/page-slot.ts +200 -0
- package/src/compat/segment/tree.ts +481 -0
- package/src/compat/segment/vary-key.ts +102 -0
- package/src/compat/segment/vary-params.ts +551 -0
- package/src/compat/static-params.ts +33 -0
- package/src/compat/tsconfig-defaults.ts +301 -0
- package/src/compat/typecheck/index.ts +1481 -0
- package/src/compat/typecheck/worker.ts +26 -0
- package/src/compat/typed-routes/index.ts +92 -0
- package/src/compat/typed-routes/manifest.ts +356 -0
- package/src/compat/typed-routes/typegen.ts +566 -0
- package/src/compat/validation/errors.ts +159 -0
- package/src/compat/validation/index.ts +1770 -0
- package/src/compat/validation/prerender-diagnostics.ts +1508 -0
- package/src/compat-bootstrap.ts +67 -0
- package/src/config.ts +218 -0
- package/src/css/build.ts +697 -0
- package/src/css/index.ts +2 -0
- package/src/css/postcss.ts +236 -0
- package/src/css/worker.ts +34 -0
- package/src/dev/client-actions.ts +35 -0
- package/src/dev/client-chunk-store.ts +92 -0
- package/src/dev/client-key-cache.ts +178 -0
- package/src/dev/global-css-cache.ts +212 -0
- package/src/dev/imports.ts +2430 -0
- package/src/dev/module-cache.ts +721 -0
- package/src/dev/module-generations.ts +38 -0
- package/src/dev/module-transform.ts +188 -0
- package/src/dev/node-module-bundle-cache.ts +63 -0
- package/src/dev/restart-cache.ts +10 -0
- package/src/dev/route-bundle-key-cache.ts +154 -0
- package/src/dev/route-facts-cache.ts +223 -0
- package/src/dev/server.ts +1710 -0
- package/src/dynamic/source.ts +307 -0
- package/src/dynamic/tree-shake.ts +262 -0
- package/src/env.ts +92 -0
- package/src/extensions.ts +1898 -0
- package/src/index.ts +34 -0
- package/src/internal.ts +43 -0
- package/src/islands/boundary-error.ts +8 -0
- package/src/islands/static-children.ts +37 -0
- package/src/islands/static-slots.ts +106 -0
- package/src/ppr-postpone.ts +24 -0
- package/src/ppr.ts +784 -0
- package/src/proxy.ts +752 -0
- package/src/render/hooks.ts +384 -0
- package/src/render/index.ts +1 -0
- package/src/render/island-context.ts +47 -0
- package/src/render/metadata.ts +857 -0
- package/src/render/renderer.ts +7391 -0
- package/src/render/resource-hints.ts +44 -0
- package/src/render/slots.tsx +679 -0
- package/src/request/context.ts +396 -0
- package/src/resolve/engine.ts +219 -0
- package/src/resolve/imports.ts +1104 -0
- package/src/resolve/scan-facts.ts +474 -0
- package/src/resolve/source-text.ts +86 -0
- package/src/routing/forwarded.ts +41 -0
- package/src/routing/handler.ts +271 -0
- package/src/routing/href.ts +203 -0
- package/src/routing/metadata.ts +1018 -0
- package/src/routing/request-runtime.ts +43 -0
- package/src/routing/routes.ts +2560 -0
- package/src/routing/slots.ts +432 -0
- package/src/runtime/server.ts +3453 -0
- package/src/runtime/vendor.ts +1160 -0
- package/src/style-modules.d.ts +9 -0
- package/src/typegen.ts +151 -0
- package/src/types.ts +725 -0
- package/src/utils/ansi.ts +9 -0
- package/src/utils/content-type.ts +31 -0
- package/src/utils/decode.ts +7 -0
- package/src/utils/dev-profile.ts +31 -0
- package/src/utils/error-log.ts +29 -0
- package/src/utils/fs-cache.ts +31 -0
- package/src/utils/fs.ts +119 -0
- package/src/utils/html.ts +46 -0
- package/src/utils/serialize.ts +378 -0
- package/src/utils/source.ts +35 -0
- package/src/utils/verbose.ts +39 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Resolved images config (COMPAT).
|
|
3
|
+
//
|
|
4
|
+
// Mirrors Next's imageConfigDefault, overlaid with the app's next.config
|
|
5
|
+
// `images` block (read through the compat config store). Both the optimizer
|
|
6
|
+
// endpoint and the next/image component read a single resolved config so the
|
|
7
|
+
// srcset the component emits stays in lock-step with the widths the endpoint
|
|
8
|
+
// accepts.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
import { getNextConfig } from '../config-loader';
|
|
12
|
+
|
|
13
|
+
export interface LocalPattern {
|
|
14
|
+
pathname?: string;
|
|
15
|
+
search?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RemotePattern {
|
|
19
|
+
protocol?: string;
|
|
20
|
+
hostname?: string;
|
|
21
|
+
port?: string;
|
|
22
|
+
pathname?: string;
|
|
23
|
+
search?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ResolvedImagesConfig {
|
|
27
|
+
deviceSizes: number[];
|
|
28
|
+
imageSizes: number[];
|
|
29
|
+
/** deviceSizes + imageSizes, sorted ascending (Next's `allSizes`). */
|
|
30
|
+
allSizes: number[];
|
|
31
|
+
qualities: number[] | undefined;
|
|
32
|
+
formats: string[];
|
|
33
|
+
path: string;
|
|
34
|
+
loader: string;
|
|
35
|
+
loaderFile: string;
|
|
36
|
+
domains: string[];
|
|
37
|
+
disableStaticImages: boolean;
|
|
38
|
+
minimumCacheTTL: number;
|
|
39
|
+
maximumRedirects: number;
|
|
40
|
+
maximumResponseBody: number;
|
|
41
|
+
dangerouslyAllowLocalIP: boolean;
|
|
42
|
+
dangerouslyAllowSVG: boolean;
|
|
43
|
+
contentSecurityPolicy: string;
|
|
44
|
+
contentDispositionType: string;
|
|
45
|
+
localPatterns: LocalPattern[] | undefined;
|
|
46
|
+
remotePatterns: RemotePattern[];
|
|
47
|
+
unoptimized: boolean;
|
|
48
|
+
customCacheHandler: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const DEFAULTS = {
|
|
52
|
+
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
53
|
+
imageSizes: [32, 48, 64, 96, 128, 256, 384],
|
|
54
|
+
path: '/_next/image',
|
|
55
|
+
loader: 'default',
|
|
56
|
+
loaderFile: '',
|
|
57
|
+
domains: [] as string[],
|
|
58
|
+
disableStaticImages: false,
|
|
59
|
+
minimumCacheTTL: 14400,
|
|
60
|
+
formats: ['image/webp'],
|
|
61
|
+
maximumRedirects: 3,
|
|
62
|
+
maximumResponseBody: 50_000_000,
|
|
63
|
+
dangerouslyAllowLocalIP: false,
|
|
64
|
+
dangerouslyAllowSVG: false,
|
|
65
|
+
contentSecurityPolicy: `script-src 'none'; frame-src 'none'; sandbox;`,
|
|
66
|
+
contentDispositionType: 'attachment',
|
|
67
|
+
localPatterns: undefined as LocalPattern[] | undefined,
|
|
68
|
+
remotePatterns: [] as RemotePattern[],
|
|
69
|
+
qualities: [75] as number[] | undefined,
|
|
70
|
+
unoptimized: false,
|
|
71
|
+
customCacheHandler: false,
|
|
72
|
+
} as const;
|
|
73
|
+
|
|
74
|
+
let cached: { key: unknown; value: ResolvedImagesConfig } | undefined;
|
|
75
|
+
|
|
76
|
+
function asNumberArray(value: unknown, fallback: number[]): number[] {
|
|
77
|
+
if (!Array.isArray(value)) return fallback;
|
|
78
|
+
const nums = value.filter((v): v is number => typeof v === 'number');
|
|
79
|
+
return nums.length > 0 ? nums : fallback;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The app's resolved images config, defaults overlaid with next.config.images. */
|
|
83
|
+
export function getImagesConfig(): ResolvedImagesConfig {
|
|
84
|
+
const raw = getNextConfig().images;
|
|
85
|
+
if (cached && cached.key === raw) return cached.value;
|
|
86
|
+
const images = (raw && typeof raw === 'object' ? raw : {}) as Record<string, unknown>;
|
|
87
|
+
|
|
88
|
+
const deviceSizes = asNumberArray(images.deviceSizes, [...DEFAULTS.deviceSizes]).sort(
|
|
89
|
+
(a, b) => a - b,
|
|
90
|
+
);
|
|
91
|
+
const imageSizes = asNumberArray(images.imageSizes, [...DEFAULTS.imageSizes]).sort(
|
|
92
|
+
(a, b) => a - b,
|
|
93
|
+
);
|
|
94
|
+
const qualities =
|
|
95
|
+
images.qualities === undefined
|
|
96
|
+
? [...(DEFAULTS.qualities ?? [])]
|
|
97
|
+
: asNumberArray(images.qualities, [...(DEFAULTS.qualities ?? [])]).sort((a, b) => a - b);
|
|
98
|
+
|
|
99
|
+
const value: ResolvedImagesConfig = {
|
|
100
|
+
deviceSizes,
|
|
101
|
+
imageSizes,
|
|
102
|
+
allSizes: [...deviceSizes, ...imageSizes].sort((a, b) => a - b),
|
|
103
|
+
qualities,
|
|
104
|
+
formats: Array.isArray(images.formats)
|
|
105
|
+
? (images.formats as string[])
|
|
106
|
+
: [...DEFAULTS.formats],
|
|
107
|
+
path: typeof images.path === 'string' ? images.path : DEFAULTS.path,
|
|
108
|
+
loader: typeof images.loader === 'string' ? images.loader : DEFAULTS.loader,
|
|
109
|
+
loaderFile: typeof images.loaderFile === 'string' ? images.loaderFile : DEFAULTS.loaderFile,
|
|
110
|
+
domains: Array.isArray(images.domains) ? (images.domains as string[]) : [...DEFAULTS.domains],
|
|
111
|
+
disableStaticImages:
|
|
112
|
+
typeof images.disableStaticImages === 'boolean'
|
|
113
|
+
? images.disableStaticImages
|
|
114
|
+
: DEFAULTS.disableStaticImages,
|
|
115
|
+
minimumCacheTTL:
|
|
116
|
+
typeof images.minimumCacheTTL === 'number'
|
|
117
|
+
? images.minimumCacheTTL
|
|
118
|
+
: DEFAULTS.minimumCacheTTL,
|
|
119
|
+
maximumRedirects:
|
|
120
|
+
typeof images.maximumRedirects === 'number'
|
|
121
|
+
? images.maximumRedirects
|
|
122
|
+
: DEFAULTS.maximumRedirects,
|
|
123
|
+
maximumResponseBody:
|
|
124
|
+
typeof images.maximumResponseBody === 'number'
|
|
125
|
+
? images.maximumResponseBody
|
|
126
|
+
: DEFAULTS.maximumResponseBody,
|
|
127
|
+
dangerouslyAllowLocalIP:
|
|
128
|
+
typeof images.dangerouslyAllowLocalIP === 'boolean'
|
|
129
|
+
? images.dangerouslyAllowLocalIP
|
|
130
|
+
: DEFAULTS.dangerouslyAllowLocalIP,
|
|
131
|
+
dangerouslyAllowSVG:
|
|
132
|
+
typeof images.dangerouslyAllowSVG === 'boolean'
|
|
133
|
+
? images.dangerouslyAllowSVG
|
|
134
|
+
: DEFAULTS.dangerouslyAllowSVG,
|
|
135
|
+
contentSecurityPolicy:
|
|
136
|
+
typeof images.contentSecurityPolicy === 'string'
|
|
137
|
+
? images.contentSecurityPolicy
|
|
138
|
+
: DEFAULTS.contentSecurityPolicy,
|
|
139
|
+
contentDispositionType:
|
|
140
|
+
typeof images.contentDispositionType === 'string'
|
|
141
|
+
? images.contentDispositionType
|
|
142
|
+
: DEFAULTS.contentDispositionType,
|
|
143
|
+
localPatterns: normalizeLocalPatterns(images.localPatterns),
|
|
144
|
+
remotePatterns: normalizeRemotePatterns(images.remotePatterns),
|
|
145
|
+
unoptimized:
|
|
146
|
+
typeof images.unoptimized === 'boolean' ? images.unoptimized : DEFAULTS.unoptimized,
|
|
147
|
+
customCacheHandler: Boolean(images.customCacheHandler),
|
|
148
|
+
};
|
|
149
|
+
cached = { key: raw, value };
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function normalizeLocalPatterns(value: unknown): LocalPattern[] | undefined {
|
|
154
|
+
if (!Array.isArray(value)) return undefined;
|
|
155
|
+
const patterns = value.map(entry => {
|
|
156
|
+
if (typeof entry === 'string') return { pathname: entry, search: '' };
|
|
157
|
+
const obj = (entry ?? {}) as Record<string, unknown>;
|
|
158
|
+
return {
|
|
159
|
+
pathname: typeof obj.pathname === 'string' ? obj.pathname : undefined,
|
|
160
|
+
search: typeof obj.search === 'string' ? obj.search : undefined,
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
// Next implicitly allows statically-imported media once localPatterns is
|
|
164
|
+
// configured, so build-time optimized static images keep loading. Appended to
|
|
165
|
+
// the normalized list so the runtime check and the images-manifest writer
|
|
166
|
+
// share one source of truth.
|
|
167
|
+
patterns.push(
|
|
168
|
+
{ pathname: '/_next/static/media/**', search: '' },
|
|
169
|
+
{ pathname: '/_next/static/immutable/media/**', search: '' },
|
|
170
|
+
);
|
|
171
|
+
return patterns;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function normalizeRemotePatterns(value: unknown): RemotePattern[] {
|
|
175
|
+
if (!Array.isArray(value)) return [];
|
|
176
|
+
return value.map(entry => {
|
|
177
|
+
// next.config may pass a URL instance (new URL('https://host/**')).
|
|
178
|
+
if (entry instanceof URL) {
|
|
179
|
+
return {
|
|
180
|
+
protocol: entry.protocol.replace(/:$/, ''),
|
|
181
|
+
hostname: entry.hostname,
|
|
182
|
+
...(entry.port ? { port: entry.port } : {}),
|
|
183
|
+
pathname: entry.pathname,
|
|
184
|
+
search: entry.search,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const obj = (entry ?? {}) as Record<string, unknown>;
|
|
188
|
+
return {
|
|
189
|
+
protocol: typeof obj.protocol === 'string' ? obj.protocol : undefined,
|
|
190
|
+
hostname: typeof obj.hostname === 'string' ? obj.hostname : undefined,
|
|
191
|
+
port: typeof obj.port === 'string' ? obj.port : undefined,
|
|
192
|
+
pathname: typeof obj.pathname === 'string' ? obj.pathname : undefined,
|
|
193
|
+
search: typeof obj.search === 'string' ? obj.search : undefined,
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// next/image optimizer glue (COMPAT).
|
|
3
|
+
//
|
|
4
|
+
// The full /_next/image endpoint lives in ../../image-optimizer/. This module
|
|
5
|
+
// keeps the pieces the rest of the image pipeline uses: the endpoint matcher
|
|
6
|
+
// and the build-time blur-placeholder generator for static imports.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
import type { ResolvedImagesConfig } from './config';
|
|
10
|
+
|
|
11
|
+
export { serveImageOptimizer } from '../../image-optimizer';
|
|
12
|
+
|
|
13
|
+
const BLUR_QUALITY = 70;
|
|
14
|
+
|
|
15
|
+
interface SharpPipeline {
|
|
16
|
+
resize(options: { width?: number; height?: number; withoutEnlargement?: boolean }): SharpPipeline;
|
|
17
|
+
webp(options: { quality: number }): SharpPipeline;
|
|
18
|
+
avif(options: { quality: number }): SharpPipeline;
|
|
19
|
+
png(options: { quality: number }): SharpPipeline;
|
|
20
|
+
jpeg(options: { quality: number; mozjpeg?: boolean }): SharpPipeline;
|
|
21
|
+
toBuffer(): Promise<Buffer>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type SharpModule = (input: Buffer | Uint8Array) => SharpPipeline;
|
|
25
|
+
|
|
26
|
+
let sharpPromise: Promise<SharpModule | undefined> | undefined;
|
|
27
|
+
function loadSharp(): Promise<SharpModule | undefined> {
|
|
28
|
+
if (!sharpPromise) {
|
|
29
|
+
sharpPromise = import('sharp')
|
|
30
|
+
.then(m => (m.default ?? m) as unknown as SharpModule)
|
|
31
|
+
.catch(() => undefined);
|
|
32
|
+
}
|
|
33
|
+
return sharpPromise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** True if this request is for the image optimizer endpoint. */
|
|
37
|
+
export function isImageOptimizerRequest(url: URL, config: ResolvedImagesConfig): boolean {
|
|
38
|
+
// `/_next/image/` (trailingSlash: true apps) is served the same as the bare
|
|
39
|
+
// path — the interceptor runs before core's trailing-slash normalization.
|
|
40
|
+
return url.pathname === config.path || url.pathname === `${config.path}/`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const VALID_BLUR_EXT = new Set(['jpeg', 'jpg', 'png', 'webp', 'avif']);
|
|
44
|
+
|
|
45
|
+
/** True for animated webp/gif/avif — Next skips blur-placeholder generation for these. */
|
|
46
|
+
function isAnimated(buffer: Buffer): boolean {
|
|
47
|
+
// WebP: an 'ANIM' chunk under a VP8X container.
|
|
48
|
+
if (buffer.length > 16 && buffer.toString('ascii', 0, 4) === 'RIFF' && buffer.toString('ascii', 8, 12) === 'WEBP') {
|
|
49
|
+
return buffer.includes(Buffer.from('ANIM', 'ascii'));
|
|
50
|
+
}
|
|
51
|
+
// GIF: more than one image descriptor (0x2C) block before the trailer (0x3B).
|
|
52
|
+
if (buffer.length > 6 && buffer.toString('ascii', 0, 3) === 'GIF') {
|
|
53
|
+
let frames = 0;
|
|
54
|
+
for (let i = 6; i < buffer.length; i++) {
|
|
55
|
+
if (buffer[i] === 0x2c) frames++;
|
|
56
|
+
if (frames > 1) return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// AVIF: 'avis' major/compatible brand in the ftyp box signals an image sequence.
|
|
61
|
+
if (buffer.length > 16 && buffer.toString('ascii', 4, 8) === 'ftyp') {
|
|
62
|
+
return buffer.toString('ascii', 8, 200).includes('avis');
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Generate a blur data URL from image bytes (build-time static import blur).
|
|
69
|
+
* Mirrors Next's `getBlurImage`: shrink to an 8px-max thumbnail (aspect-scaled
|
|
70
|
+
* height passed explicitly so sharp's rounding matches Next's), quality 70,
|
|
71
|
+
* mozjpeg for JPEG. Returns undefined for extensions/animated images Next
|
|
72
|
+
* does not generate a placeholder for.
|
|
73
|
+
*/
|
|
74
|
+
export async function generateBlurDataURL(
|
|
75
|
+
buffer: Buffer,
|
|
76
|
+
ext: string,
|
|
77
|
+
blurWidth: number,
|
|
78
|
+
blurHeight: number,
|
|
79
|
+
): Promise<string | undefined> {
|
|
80
|
+
const extension = ext === 'jpg' ? 'jpeg' : ext;
|
|
81
|
+
if (!VALID_BLUR_EXT.has(extension) || isAnimated(buffer)) return undefined;
|
|
82
|
+
const sharp = await loadSharp();
|
|
83
|
+
if (!sharp) return undefined;
|
|
84
|
+
try {
|
|
85
|
+
const pipeline = sharp(buffer).resize({ width: blurWidth, height: blurHeight });
|
|
86
|
+
let out: SharpPipeline;
|
|
87
|
+
if (extension === 'png') out = pipeline.png({ quality: BLUR_QUALITY });
|
|
88
|
+
else if (extension === 'webp') out = pipeline.webp({ quality: BLUR_QUALITY });
|
|
89
|
+
else if (extension === 'avif') out = pipeline.avif({ quality: BLUR_QUALITY });
|
|
90
|
+
else out = pipeline.jpeg({ quality: BLUR_QUALITY, mozjpeg: true });
|
|
91
|
+
const resized = await out.toBuffer();
|
|
92
|
+
return `data:image/${extension};base64,${resized.toString('base64')}`;
|
|
93
|
+
} catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// localPatterns / remotePatterns matching (COMPAT).
|
|
3
|
+
//
|
|
4
|
+
// Mirrors Next's hasLocalMatch / hasRemoteMatch used by the optimizer to decide
|
|
5
|
+
// whether a requested `url` is allowed (else 400).
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
import type { LocalPattern, RemotePattern } from './config';
|
|
9
|
+
|
|
10
|
+
function makePathnameRegex(pathname: string): RegExp {
|
|
11
|
+
// Convert a Next path-to-regexp style glob ('/assets/**') into a RegExp.
|
|
12
|
+
// '**' matches any depth; '*' matches a single segment.
|
|
13
|
+
let source = '';
|
|
14
|
+
const escaped = pathname.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
15
|
+
// Handle ** then * (double-star must be processed first).
|
|
16
|
+
source = escaped.replace(/\/\*\*/g, '(?:/.*)?').replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*');
|
|
17
|
+
return new RegExp(`^${source}$`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function hasLocalMatch(
|
|
21
|
+
localPatterns: LocalPattern[] | undefined,
|
|
22
|
+
url: URL,
|
|
23
|
+
): boolean {
|
|
24
|
+
// undefined localPatterns => allow all local images (Next default).
|
|
25
|
+
if (localPatterns === undefined) return true;
|
|
26
|
+
return localPatterns.some(pattern => {
|
|
27
|
+
if (pattern.pathname !== undefined && !makePathnameRegex(pattern.pathname).test(url.pathname)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (pattern.search !== undefined && pattern.search !== url.search) return false;
|
|
31
|
+
return true;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function hasRemoteMatch(
|
|
36
|
+
domains: string[],
|
|
37
|
+
remotePatterns: RemotePattern[],
|
|
38
|
+
url: URL,
|
|
39
|
+
): boolean {
|
|
40
|
+
if (domains.includes(url.hostname)) return true;
|
|
41
|
+
return remotePatterns.some(pattern => {
|
|
42
|
+
if (pattern.protocol !== undefined && pattern.protocol !== url.protocol.replace(/:$/, '')) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
if (pattern.hostname !== undefined && !matchHostname(pattern.hostname, url.hostname)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (pattern.port !== undefined && pattern.port !== '' && pattern.port !== url.port) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (
|
|
52
|
+
pattern.pathname !== undefined &&
|
|
53
|
+
!makePathnameRegex(pattern.pathname).test(url.pathname)
|
|
54
|
+
) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (pattern.search !== undefined && pattern.search !== '' && pattern.search !== url.search) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
return true;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function matchHostname(pattern: string, hostname: string): boolean {
|
|
65
|
+
if (pattern.startsWith('**.')) {
|
|
66
|
+
const suffix = pattern.slice(2); // ".example.com"
|
|
67
|
+
return hostname.endsWith(suffix) && hostname.length > suffix.length;
|
|
68
|
+
}
|
|
69
|
+
if (pattern.startsWith('*.')) {
|
|
70
|
+
const suffix = pattern.slice(1);
|
|
71
|
+
if (!hostname.endsWith(suffix)) return false;
|
|
72
|
+
const sub = hostname.slice(0, -suffix.length);
|
|
73
|
+
return sub.length > 0 && !sub.includes('.');
|
|
74
|
+
}
|
|
75
|
+
return pattern === hostname;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Next serializes localPatterns into images-manifest.json as pathname RegExp
|
|
80
|
+
* source strings (path-to-regexp compiled). We reproduce the exact strings the
|
|
81
|
+
* e2e suites assert.
|
|
82
|
+
*/
|
|
83
|
+
export function manifestLocalPatterns(
|
|
84
|
+
localPatterns: LocalPattern[] | undefined,
|
|
85
|
+
): { pathname: string; search: string }[] {
|
|
86
|
+
const patterns = localPatterns ?? [{ pathname: undefined, search: '' }];
|
|
87
|
+
const entries = patterns.map(pattern => ({
|
|
88
|
+
pathname: compileLocalPathname(pattern.pathname),
|
|
89
|
+
search: pattern.search ?? '',
|
|
90
|
+
}));
|
|
91
|
+
return entries;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// path-to-regexp compiled source strings Next emits for local patterns.
|
|
95
|
+
function compileLocalPathname(pathname: string | undefined): string {
|
|
96
|
+
if (pathname === undefined || pathname === '**') {
|
|
97
|
+
return '^(?:(?!(?:^|\\/)\\.{1,2}(?:\\/|$))(?:(?:(?!(?:^|\\/)\\.{1,2}(?:\\/|$)).)*?)\\/?)$';
|
|
98
|
+
}
|
|
99
|
+
// '/assets/**' style
|
|
100
|
+
const base = pathname.replace(/\/\*\*$/, '');
|
|
101
|
+
const escaped = base.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\//g, '\\/');
|
|
102
|
+
return `^(?:${escaped}(?:\\/(?!\\.{1,2}(?:\\/|$))(?:(?:(?!(?:^|\\/)\\.{1,2}(?:\\/|$)).)*?)|$))$`;
|
|
103
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shared next/image URL + srcset logic (COMPAT).
|
|
3
|
+
//
|
|
4
|
+
// Ports Next's get-img-props / image-loader / image-blur-svg so the rendered
|
|
5
|
+
// markup matches Next byte-for-byte where the e2e suites snapshot it.
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
import type { ResolvedImagesConfig } from './config';
|
|
9
|
+
|
|
10
|
+
export const BLUR_IMG_SIZE = 8;
|
|
11
|
+
export const BLUR_QUALITY = 70;
|
|
12
|
+
|
|
13
|
+
/** Default loader: build the /_next/image optimizer URL. */
|
|
14
|
+
export function defaultLoader(
|
|
15
|
+
config: ResolvedImagesConfig,
|
|
16
|
+
src: string,
|
|
17
|
+
width: number,
|
|
18
|
+
quality?: number,
|
|
19
|
+
): string {
|
|
20
|
+
const q = findClosestQuality(config, quality);
|
|
21
|
+
return `${config.path}?url=${encodeURIComponent(src)}&w=${width}&q=${q}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Coerce the requested quality to the closest allowed value (Next coerces
|
|
26
|
+
* out-of-list qualities to the nearest configured one; default 75).
|
|
27
|
+
*/
|
|
28
|
+
export function findClosestQuality(config: ResolvedImagesConfig, quality?: number): number {
|
|
29
|
+
const qualities = config.qualities;
|
|
30
|
+
if (!qualities || qualities.length === 0) return quality ?? 75;
|
|
31
|
+
if (quality === undefined) {
|
|
32
|
+
// Next's default when qualities is configured: 75 if present, else closest to 75.
|
|
33
|
+
return closest(qualities, 75);
|
|
34
|
+
}
|
|
35
|
+
return closest(qualities, quality);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function closest(values: number[], target: number): number {
|
|
39
|
+
let best = values[0]!;
|
|
40
|
+
let bestDelta = Math.abs(best - target);
|
|
41
|
+
for (const value of values) {
|
|
42
|
+
const delta = Math.abs(value - target);
|
|
43
|
+
if (delta < bestDelta) {
|
|
44
|
+
best = value;
|
|
45
|
+
bestDelta = delta;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return best;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface GenImgAttrsResult {
|
|
52
|
+
src: string;
|
|
53
|
+
srcSet: string | undefined;
|
|
54
|
+
sizes: string | undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface GetWidthsResult {
|
|
58
|
+
widths: number[];
|
|
59
|
+
kind: 'w' | 'x';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getWidths(
|
|
63
|
+
config: ResolvedImagesConfig,
|
|
64
|
+
width: number | undefined,
|
|
65
|
+
sizes: string | undefined,
|
|
66
|
+
): GetWidthsResult {
|
|
67
|
+
const { deviceSizes, allSizes } = config;
|
|
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));
|
|
73
|
+
}
|
|
74
|
+
if (percentSizes.length) {
|
|
75
|
+
const smallestRatio = Math.min(...percentSizes) * 0.01;
|
|
76
|
+
return {
|
|
77
|
+
widths: allSizes.filter(s => s >= deviceSizes[0]! * smallestRatio),
|
|
78
|
+
kind: 'w',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return { widths: allSizes, kind: 'w' };
|
|
82
|
+
}
|
|
83
|
+
if (typeof width !== 'number') {
|
|
84
|
+
return { widths: deviceSizes, kind: 'w' };
|
|
85
|
+
}
|
|
86
|
+
const widths = [
|
|
87
|
+
...new Set(
|
|
88
|
+
[width, width * 2].map(w => allSizes.find(p => p >= w) || allSizes[allSizes.length - 1]!),
|
|
89
|
+
),
|
|
90
|
+
];
|
|
91
|
+
return { widths, kind: 'x' };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Build src/srcSet/sizes exactly as Next's generateImgAttrs. */
|
|
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: { config: ResolvedImagesConfig; src: string; width: number; quality?: number }) => string;
|
|
103
|
+
}): GenImgAttrsResult {
|
|
104
|
+
const { config, src, unoptimized, width, quality, sizes, loader } = params;
|
|
105
|
+
if (unoptimized) {
|
|
106
|
+
return { src, srcSet: undefined, sizes: undefined };
|
|
107
|
+
}
|
|
108
|
+
const { widths, kind } = getWidths(config, width, sizes);
|
|
109
|
+
const last = widths.length - 1;
|
|
110
|
+
return {
|
|
111
|
+
sizes: !sizes && kind === 'w' ? '100vw' : sizes,
|
|
112
|
+
srcSet: widths
|
|
113
|
+
.map((w, i) => `${loader({ config, src, quality, width: w })} ${kind === 'w' ? w : i + 1}${kind}`)
|
|
114
|
+
.join(', '),
|
|
115
|
+
src: loader({ config, src, quality, width: widths[last]! }),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Next's getImageBlurSvg — the inline feGaussianBlur SVG placeholder. */
|
|
120
|
+
export function getImageBlurSvg(params: {
|
|
121
|
+
widthInt?: number;
|
|
122
|
+
heightInt?: number;
|
|
123
|
+
blurWidth?: number;
|
|
124
|
+
blurHeight?: number;
|
|
125
|
+
blurDataURL: string;
|
|
126
|
+
objectFit?: string;
|
|
127
|
+
}): 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}'` : '';
|
|
133
|
+
const preserveAspectRatio = viewBox
|
|
134
|
+
? 'none'
|
|
135
|
+
: objectFit === 'contain'
|
|
136
|
+
? 'xMidYMid'
|
|
137
|
+
: objectFit === 'cover'
|
|
138
|
+
? '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`;
|
|
141
|
+
}
|