-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: image preprocessor #10788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
feat: image preprocessor #10788
Changes from 53 commits
Commits
Show all changes
117 commits
Select commit
Hold shift + click to select a range
2010480
feat: static image preprocessor
benmccann e806b97
rename package
benmccann 538215a
merge master
benmccann ffeaf49
doc updates
benmccann 263bf73
merge master, minor api updates, and docs
benmccann c539561
minor doc updates
benmccann 5bc7d93
fix
benmccann af02edf
type fix
benmccann 7d979cd
document directives
benmccann a33aac9
start to implement sizes
benmccann b526c61
doc updates
benmccann fc9c343
upgrade vite-imagetools and get image width
benmccann 8e6e902
remove accidental import
benmccann d33d221
fix missing alt tags
benmccann 10162b5
fix passing info from preprocessor to plugin
benmccann 7e03972
start using most calculated widths
benmccann f45593d
sizes cleanup
benmccann 627b331
fix join character
benmccann f79812d
partial revert of earlier commit
benmccann 0e8d7e6
caching docs
benmccann 31e82ad
pixel density descriptors
benmccann 805089b
merge master
benmccann 3d58fa1
changeset
benmccann 95aa3fa
reduce code
benmccann c9ccd24
tweak
benmccann e41aa95
reduce code some more
benmccann 89a49b5
lint
benmccann bc7adda
docs update
benmccann b5c7a94
redirect
benmccann 7fabe7b
imagetools 6
benmccann 08aa49c
remove options
benmccann 1187cce
update test output
benmccann 882335a
fix typescript issue
benmccann 46341ce
docs syntax
benmccann 5e1bf3b
docs
benmccann 6046b59
merge master
benmccann 61c4cc1
merge master
benmccann 39964dc
fix lockfile
benmccann 1121742
remove ignore flag
benmccann 816d289
delete test
benmccann 86fe774
update opt-in method
benmccann 4277033
rename
benmccann 9da0de9
missed a spot
benmccann 6e41c10
merge master
benmccann 1d33f6e
update test
benmccann 8b6d9b7
typo
benmccann 4f55f35
enhance hero image
benmccann e4fe826
require enhanced:img
benmccann 3389cfb
fix link
benmccann 503182c
docs cleanup and minor improvements
benmccann e9c36fd
fix
benmccann 15c08cf
cleanup
benmccann 04c22d1
disable in webcontainers
benmccann 32f69d5
Update .changeset/eighty-timers-exist.md
benmccann 3d697b0
Update sites/kit.svelte.dev/src/routes/home/Showcase.svelte
benmccann e0873d8
Update packages/enhanced-img/src/index.js
benmccann caaa0d8
Update documentation/docs/30-advanced/60-images.md
benmccann 50fc12a
Update documentation/docs/30-advanced/60-images.md
benmccann 148bfc9
Update documentation/docs/30-advanced/60-images.md
benmccann cc4e12f
Update documentation/docs/30-advanced/60-images.md
benmccann 4c89110
Update documentation/docs/30-advanced/60-images.md
benmccann 2ea4ab5
Update documentation/docs/30-advanced/60-images.md
benmccann 94a8528
Update documentation/docs/30-advanced/60-images.md
benmccann 432f01d
Update documentation/docs/30-advanced/60-images.md
benmccann e2413de
Update documentation/docs/30-advanced/60-images.md
benmccann 7430b06
Update documentation/docs/30-advanced/60-images.md
benmccann 35bd155
Update documentation/docs/30-advanced/60-images.md
benmccann e047856
Update documentation/docs/30-advanced/60-images.md
benmccann 3eab86c
Update documentation/docs/30-advanced/60-images.md
benmccann 0c2f414
Update documentation/docs/30-advanced/60-images.md
benmccann 9bcfff6
Update documentation/docs/30-advanced/60-images.md
benmccann 72e2589
Update packages/enhanced-img/src/preprocessor.js
benmccann 20346be
Update packages/enhanced-img/types/index.d.ts
benmccann 6051ffc
Update packages/enhanced-img/src/preprocessor.js
benmccann 24db03a
Update packages/enhanced-img/src/preprocessor.js
benmccann ec882d5
Update packages/enhanced-img/src/preprocessor.js
benmccann 4070aa4
Update documentation/docs/30-advanced/60-images.md
benmccann 8ddd73a
Update documentation/docs/30-advanced/60-images.md
benmccann 0c06bfa
Update documentation/docs/30-advanced/60-images.md
benmccann 20006d6
Update documentation/docs/30-advanced/60-images.md
benmccann dd83a92
Update documentation/docs/30-advanced/60-images.md
benmccann d0278dc
Update documentation/docs/30-advanced/60-images.md
benmccann 1a71b21
Update documentation/docs/30-advanced/60-images.md
benmccann 8ab8b48
Update documentation/docs/30-advanced/60-images.md
benmccann 982319e
Update documentation/docs/30-advanced/60-images.md
benmccann c1395cf
Update documentation/docs/30-advanced/60-images.md
benmccann b96c444
Update documentation/docs/30-advanced/60-images.md
benmccann 3de72b4
Update documentation/docs/30-advanced/60-images.md
benmccann 3dbfbf8
Update packages/enhanced-img/src/preprocessor.js
benmccann 44426b0
Update packages/enhanced-img/src/preprocessor.js
benmccann 9058a74
merge master
benmccann 5050172
hoistable const
benmccann 85e74e2
unroll loop
benmccann 3089fb3
fix tests
benmccann 4b067b5
update comment
benmccann ac1d442
better error message
benmccann 9d9fe34
fix
benmccann ea63ee3
missed a spot
benmccann 85a593c
update sizes docs
benmccann 107bc76
fix unused style warning
benmccann 8971796
update Hero
benmccann 7982f58
Merge branch 'master' into image
benmccann 0afd5b9
format
benmccann ac37762
bump imagetools
benmccann c0c7f69
bump imagetools
benmccann 05f4372
automatic sizes
benmccann a6ff9b8
Revert "automatic sizes"
benmccann ca840f3
fix test output
benmccann 5f51b2a
simplify choosing widths
benmccann 986eba2
fix a todo
benmccann 15bd759
update test
benmccann e810ee8
move image types into its package
dummdidumm 609e6d0
oops
dummdidumm c63ef28
fix test
benmccann c509a2b
Merge branch 'master' into image
benmccann 6448889
update lockfile
benmccann a56c459
fix link
benmccann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@sveltejs/static-img': patch | ||
| --- | ||
|
|
||
| feat: add experimental `@sveltejs/static-img` package | ||
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| --- | ||
| title: Images | ||
| --- | ||
|
|
||
| ## Caching and inlining | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| [Vite will automatically process imported assets](https://vitejs.dev/guide/assets.html) for improved performance. This includes assets referenced via the CSS `url()` function. Hashes will be added to the filenames so that they can be cached and assets smaller than `assetsInlineLimit` will be inlined. Vite's asset handling is most often used for images, but is also useful for video, audio, etc. | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```svelte | ||
| <script> | ||
| import logo from '$lib/assets/logo.png'; | ||
| </script> | ||
|
|
||
| <img alt="The project logo" src={logo} /> | ||
| ``` | ||
|
|
||
| To reference assets directly in the markup, you can use a preprocessor such as [`@sveltejs/enhanced-img`](#sveltejs-enhanced-img), which is discussed below and additionally transforms your images. | ||
|
|
||
| ## Transforming background | ||
|
|
||
| You may wish to transform your images to output compressed image formats such as `.webp` or `.avif` or responsive images with different sizes for different devices. There are two approaches to transforming images, which will be discussed below. With either approach, the transformed images may be served via a CDN. CDNs reduce latency by distributing copies of static assets globally. | ||
|
|
||
| The `@sveltejs/enhanced-img` package handles images that are located in your project and can be referred to with a static string. It can automatically set the intrinsic `width` and `height` for you, which can't be done with a dynamic approach. It is generated with a hash in the filename so that it can be maximally cached. It generates images at build time, so building may take longer the more images you transform. | ||
|
|
||
| Alternatively, using a CDN to do the image transformation provides more flexibility with regards to sizes and you can pass image sources not known at build time, but it comes with potentially a bit of setup overhead in configuring the image CDN and possible usage costs. Some images served from a CDN may require a request to the server to verify that the image has not changed. This will block the browser from using its cache until a [304 response](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/304) is received from the server. Building HTML to target CDNs may result in slightly smaller and simpler HTML because they can serve the appropriate file format for an `img` tag based on the `User-Agent` header whereas build-time optimizations must produce `picture` tags. Finally some CDNs may generate images lazily, which could have a negative performance impact for sites with low traffic and frequently changing images. We do not currently offer any tools for dynamic image transforms, but we may offer such utilities in the future. | ||
|
|
||
| You can mix and match both solutions in one project. For example, you may display images on your homepage with `@sveltejs/enhanced-img` and display user-submitted content with a dynamic approach. | ||
|
|
||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ## @sveltejs/enhanced-img | ||
|
|
||
| > **WARNING**: The `@sveltejs/enhanced-img` package is experimental. It uses pre-1.0 versioning and may introduce breaking changes with every minor version release. | ||
|
|
||
| `@sveltejs/enhanced-img` offers plug and play image processing that serves smaller file formats like `avif` or `webp`, automatically sets the intrinsic width and height of the image to avoid layout shift, creates images of multiple sizes for various devices, and strips EXIF data for privacy. It will work in any Vite-based project including, but not limited to, SvelteKit projects. | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Setup | ||
|
|
||
| Install: | ||
|
|
||
| ```bash | ||
| npm install --save-dev @sveltejs/enhanced-img | ||
| ``` | ||
|
|
||
| Adjust `vite.config.js`: | ||
|
|
||
| ```diff | ||
| import { sveltekit } from '@sveltejs/kit/vite'; | ||
| +import { enhancedImages } from '@sveltejs/enhanced-img'; | ||
| import { defineConfig } from 'vite'; | ||
|
|
||
| export default defineConfig({ | ||
| plugins: [ | ||
| + enhancedImages(), | ||
| sveltekit() | ||
| ] | ||
| }); | ||
| ``` | ||
|
|
||
| ### Basic usage | ||
|
|
||
| Use in your `.svelte` components by using `enhanced:img` rather than `img` and referencing the image file with a [Vite asset import](https://vitejs.dev/guide/assets.html#static-asset-handling) path: | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```svelte | ||
| <enhanced:img src="./path/to/your/image.jpg" alt="An alt text" /> | ||
| ``` | ||
|
|
||
benmccann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| At build time, your `img` tag will be replaced with a `picture` providing multiple image types and sizes. It's only possible to downscale images without losing quality, which means that you should provide the highest resolution image that you need — smaller versions will be generated for the various device types that may request an image. If you're not using the [`sizes` attribute](#sveltejs-enhanced-img-srcset-and-sizes) you should provide your image at 2x resolution for HiDPI displays (a.k.a. retina displays). | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Since the `enhanced:img` tag will be replaced with a `picture` tag, you should refer to it as such within your `style` tag. Generally we recommend styling the `img` child of the `picture` tag rather than the `picture` tag itself to make it easier to switch between various solutions such as a dynamic CDN-based solution should you ever need to do so. | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Dynamically choosing an image | ||
|
|
||
| You can also manually import an image and then pass it to an `enhanced:img` tag. This is useful when you have a collection of static images and would like to dynamically choose one. You can create a collection of images manually [as we do on the homepage showcase](https://github.com/sveltejs/kit/blob/master/sites/kit.svelte.dev/src/routes/home/Showcase.svelte). In this case you will need to update both the `import` statement and `img` tag as shown below to indicate you'd like process them. | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```svelte | ||
| <script> | ||
| import { MyImage } from './path/to/your/image.jpg?enhancedImg'; | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| </script> | ||
|
|
||
| <enhanced:img src={MyImage} alt="An alt text" /> | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| You can also use [Vite's `import.meta.glob`](https://vitejs.dev/guide/features.html#glob-import). Note that you will have to specify `enhancedImg` via a [custom query](https://vitejs.dev/guide/features.html#custom-queries): | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```js | ||
| const pictures = import.meta.glob( | ||
| '/path/to/assets/*.{avif,gif,heif,jpeg,jpg,png,tiff,webp}', | ||
| { | ||
| query: { | ||
| enhancedImg: true | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| ); | ||
| ``` | ||
|
|
||
| ### Intrinsic Dimensions | ||
|
|
||
| `width` and `height` are optional as they can be inferred from the source image and will be automatically added when the `<enhanced:img>` tag is preprocessed. These attributes are added because the browser can reserve the correct amount of space when it knows image dimensions which prevents layout shift. If you'd like to use a different `width` and `height` you can style the image with CSS. Because the preprocessor adds a `width` and `height` for you, if you'd like one of the dimensions to be automatically calculated then you will need to specify that: | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ```svelte | ||
| <style> | ||
| .hero-image img { | ||
| width: var(--size); | ||
| height: auto; | ||
| } | ||
| </style> | ||
| ``` | ||
|
|
||
| ### `srcset` and `sizes` | ||
|
|
||
| You may be wondering how to create something that looks like: | ||
|
|
||
| ```html | ||
| <img | ||
| srcset="image-small.png 160w, image-medium.png 400w, image-large.png 600w" | ||
| sizes="(min-width: 60rem) 80vw, (min-width: 40rem) 90vw, 100vw" | ||
| /> | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| In this example, it would be tedious to have to manually create three versions of your image. Instead, you can specify the widths as a query parameter and we'll generate the `srcset` for you. | ||
|
|
||
| ```svelte | ||
| <enhanced:img | ||
| src="./image.png?w=160,400,600" | ||
| sizes="(min-width: 60rem) 80vw, (min-width: 40rem) 90vw, 100vw" | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /> | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ``` | ||
|
|
||
| If `sizes` is specified directly as a string on the `img` tag then the plugin will automatically generate different width images: | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - If `sizes` is not provided, then a HiDPI/Retina image and a standard resolution image will be generated. The image you provide should be 2x the resolution you wish to display so that the browser can display that image on devices with a high [device pixel ratio](https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio). | ||
| - If `sizes` is provided, then a large `srcset` is generated. If some of the `sizes` have been specified as a percentage of the viewport width using the `vw` unit then the `srcset` will filter out any values which are too small to ever be requested by the browser. | ||
|
|
||
| > Text expressions like `sizes={computedSizes}` or `sizes={['(min-width: 60rem) 80vw', '(min-width: 40rem) 90vw', '100vw'].join(', ')}` will not be evaluated for the purposes of automatic image generation and will be skipped. | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### Per-image transforms | ||
|
|
||
| By default, enhanced images will be transformed to more efficient formats. However, you may wish to apply other transforms such as a blur, quality, flatten, or rotate operation. You can run per-image transforms by appending a query string: | ||
|
|
||
| ```svelte | ||
| <enhanced:img src="./path/to/your/image.jpg?blur=15" alt="An alt text" /> | ||
| ``` | ||
|
|
||
| [See the imagetools repo for the full list of directives](https://github.com/JonasKruckenberg/imagetools/blob/main/docs/directives.md). | ||
|
|
||
benmccann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ## Best practices | ||
|
|
||
| - Always provide a good `alt` text | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| - Choose one image per page which is the most important/largest one and give it `priority` so it loads faster. This gives you better web vitals scores (largest contentful paint in particular). | ||
| - Your original images should have a good quality/resolution. Image processing can size images down to save bandwidth when serving smaller screens, but cannot invent pixels to size images up any better than browsers can. | ||
| - Give the image a container or styling so that it is constrained and does not jump around. `width` and `height` help the browser reserving space while the image is still loading. `@sveltejs/enhanced-img` will add a `width` and `height` for you. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # `@sveltejs/enhanced-img` | ||
|
|
||
| A Vite plugin which runs a Svelte preprocessor to locate images and then transform them at build-time. | ||
|
|
||
| **WARNING**: This package is experimental. It uses pre-1.0 versioning and may introduce breaking changes with every minor version release. | ||
|
|
||
| ## Docs | ||
|
|
||
| [Docs](https://kit.svelte.dev/docs/assets) | ||
|
|
||
| ## Changelog | ||
|
|
||
| [The Changelog for this package is available on GitHub](https://github.com/sveltejs/kit/blob/master/packages/enhanced-img/CHANGELOG.md). | ||
|
|
||
| ## Acknowledgements | ||
|
|
||
| We'd like to thank the author of `svelte-preprocess-import-assets`, which this code is partially based off of. We'd also like to thank the authors of `vite-imagetools` which is used in `@sveltejs/enhanced-img`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| { | ||
| "name": "@sveltejs/enhanced-img", | ||
| "version": "0.1.0", | ||
| "description": "Image optimization for your Svelte apps", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/sveltejs/kit", | ||
| "directory": "packages/image" | ||
| }, | ||
| "license": "MIT", | ||
| "homepage": "https://kit.svelte.dev", | ||
| "type": "module", | ||
| "scripts": { | ||
| "lint": "prettier --check . --config ../../.prettierrc --ignore-path .gitignore", | ||
| "check": "tsc", | ||
| "format": "prettier --write . --config ../../.prettierrc --ignore-path .gitignore", | ||
| "test": "vitest" | ||
| }, | ||
| "files": [ | ||
| "src", | ||
| "types" | ||
| ], | ||
| "exports": { | ||
| "types": "./types/index.d.ts", | ||
| "import": "./src/index.js" | ||
| }, | ||
| "types": "types/index.d.ts", | ||
| "dependencies": { | ||
| "magic-string": "^0.30.0", | ||
| "svelte-parse-markup": "^0.1.1", | ||
| "vite-imagetools": "^6.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/estree": "^1.0.2", | ||
| "@types/node": "^16.18.6", | ||
| "svelte": "^4.0.5", | ||
| "typescript": "^4.9.4", | ||
| "vite": "^4.4.2", | ||
| "vitest": "^0.34.0" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import path from 'node:path'; | ||
| import { image } from './preprocessor.js'; | ||
|
|
||
| /** | ||
| * @returns {Promise<import('vite').Plugin[]>} | ||
| */ | ||
| export async function enhancedImages() { | ||
| const imagetools_plugin = await imagetools(); | ||
| if (!imagetools_plugin) { | ||
| console.error( | ||
| '@sveltejs/enhanced-img: vite-imagetools is not installed. Skipping build-time optimizations' | ||
| ); | ||
| } | ||
| return imagetools_plugin && !process.versions.webcontainer | ||
| ? [image_plugin(), imagetools_plugin] | ||
| : []; | ||
| } | ||
|
|
||
| /** | ||
| * Creates the Svelte image plugin which provides the preprocessor. | ||
| * @returns {import('vite').Plugin} | ||
| */ | ||
| function image_plugin() { | ||
| const preprocessor = image(); | ||
|
|
||
| return { | ||
| name: 'vite-plugin-enhanced-img', | ||
benmccann marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| api: { | ||
| sveltePreprocess: preprocessor | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| /** @type {Record<string,string>} */ | ||
| const fallback = { | ||
| '.avif': 'png', | ||
| '.gif': 'gif', | ||
| '.heif': 'jpg', | ||
| '.jpeg': 'jpg', | ||
| '.jpg': 'jpg', | ||
| '.png': 'png', | ||
| '.tiff': 'jpg', | ||
| '.webp': 'png' | ||
| }; | ||
|
|
||
| async function imagetools() { | ||
| /** @type {typeof import('vite-imagetools').imagetools} */ | ||
| let imagetools; | ||
| try { | ||
| ({ imagetools } = await import('vite-imagetools')); | ||
| } catch (err) { | ||
| return; | ||
| } | ||
|
|
||
| /** @type {Partial<import('vite-imagetools').VitePluginOptions>} */ | ||
| const imagetools_opts = { | ||
| defaultDirectives: async ({ pathname, searchParams: qs }, metadata) => { | ||
| const { imgSizes, imgWidth } = Object.fromEntries(qs); | ||
| if (!qs.has('enhancedImg')) return new URLSearchParams(); | ||
benmccann marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| const { widths, kind } = getWidths(imgWidth ?? (await metadata()).width, imgSizes); | ||
| return new URLSearchParams({ | ||
| as: 'picture', | ||
| format: `avif;webp;${fallback[path.extname(pathname)] ?? 'png'}`, | ||
| w: widths.join(';'), | ||
| ...(kind === 'x' && !qs.has('w') && { basePixels: widths[0].toString() }) | ||
| }); | ||
| } | ||
| }; | ||
|
|
||
| // TODO: should we make formats or sizes configurable besides just letting people override defaultDirectives? | ||
| // TODO: generate img rather than picture if only a single format is provided | ||
| // by resolving the directives for the URL in the preprocessor | ||
| return imagetools(imagetools_opts); | ||
| } | ||
|
|
||
| /** | ||
| * Derived from | ||
| * https://github.com/vercel/next.js/blob/3f25a2e747fc27da6c2166e45d54fc95e96d7895/packages/next/src/shared/lib/get-img-props.ts#L132 | ||
| * under the MIT license. Copyright (c) Vercel, Inc. | ||
| * @param {number | string | undefined} width | ||
| * @param {string | null | undefined} sizes | ||
| * @param {number[]} [deviceSizes] | ||
| * @param {number[]} [imageSizes] | ||
| * @returns {{ widths: number[]; kind: 'w' | 'x' }} | ||
| */ | ||
| function getWidths(width, sizes, deviceSizes, imageSizes) { | ||
| width = typeof width === 'string' ? parseInt(width) : width; | ||
| const chosen_device_sizes = deviceSizes || [640, 750, 828, 1080, 1200, 1920, 2048, 3840]; | ||
| const all_sizes = (imageSizes || [16, 32, 48, 64, 96, 128, 256, 384]).concat(chosen_device_sizes); | ||
|
|
||
| if (sizes) { | ||
| // Find all the "vw" percent sizes used in the sizes prop | ||
| const viewport_width_re = /(^|\s)(1?\d?\d)vw/g; | ||
| const percent_sizes = []; | ||
| for (let match; (match = viewport_width_re.exec(sizes)); match) { | ||
| percent_sizes.push(parseInt(match[2])); | ||
| } | ||
| if (percent_sizes.length) { | ||
| const smallest_ratio = Math.min(...percent_sizes) * 0.01; | ||
| return { | ||
| widths: all_sizes.filter((s) => s >= chosen_device_sizes[0] * smallest_ratio), | ||
| kind: 'w' | ||
| }; | ||
| } | ||
| return { widths: all_sizes, kind: 'w' }; | ||
| } | ||
| if (typeof width !== 'number') { | ||
| return { widths: chosen_device_sizes, kind: 'w' }; | ||
| } | ||
|
|
||
| // Don't need more than 2x resolution. | ||
| // Most OLED screens that say they are 3x resolution, | ||
| // are actually 3x in the green color, but only 1.5x in the red and | ||
| // blue colors. Showing a 3x resolution image in the app vs a 2x | ||
| // resolution image will be visually the same, though the 3x image | ||
| // takes significantly more data. Even true 3x resolution screens are | ||
| // wasteful as the human eye cannot see that level of detail without | ||
| // something like a magnifying glass. | ||
| // https://blog.twitter.com/engineering/en_us/topics/infrastructure/2019/capping-image-fidelity-on-ultra-high-resolution-devices.html | ||
|
|
||
| // We diverge from the Next.js logic here | ||
| // You can't really scale up an image, so you can't 2x the width | ||
| // Instead the user should provide the high-res image and we'll downscale | ||
| // Also, Vercel builds specific image sizes and picks the closest from those, | ||
| // but we can just build the ones we want exactly. | ||
| return { widths: [Math.round(width / 2), width], kind: 'x' }; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.