-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add dynamic image optimization
part of #241 closes #9787 This adds image optimization through a new $app/images import. It's deliberately low level: The only export is getImage which you pass an image src and it returns an object containing src and srcset (possibly more?) values which you spread on an img tag. In order to use this you need to define a path to a loader in kit.config.images. The loader takes the original img src and a width and returns a URL pointing to the optimized image. You can also modify the number of sizes and trusted domains.
- Loading branch information
1 parent
a7bffde
commit a4d5eca
Showing
11 changed files
with
194 additions
and
1 deletion.
There are no files selected for viewing
This file contains 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,4 @@ | ||
/** | ||
* https://vercel.com/docs/concepts/image-optimization | ||
*/ | ||
export default function loader(src: string, width: number, options?: { quality?: number }): string; |
This file contains 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,49 @@ | ||
// https://vercel.com/docs/concepts/image-optimization | ||
|
||
/** | ||
* @param {string} src | ||
* @param {number} width | ||
* @param {{ quality?: number }} [options] | ||
*/ | ||
export default function loader(src, width, options) { | ||
const url = new URL(src, 'http://n'); // If the base is a relative URL, we need to add a dummy host to the URL | ||
if (url.pathname === '/_vercel/image') { | ||
set_param(url, 'w', width); | ||
set_param(url, 'q', options?.quality ?? 75, false); | ||
} else { | ||
url.pathname = `/_vercel/image`; | ||
set_param(url, 'url', src); | ||
set_param(url, 'w', width); | ||
set_param(url, 'q', options?.quality ?? 75); | ||
} | ||
return src === url.href ? url.href : relative_url(url); | ||
} | ||
|
||
/** | ||
* @param {URL} url | ||
*/ | ||
function relative_url(url) { | ||
const { pathname, search } = url; | ||
return `${pathname}${search}`; | ||
} | ||
/** | ||
* @param {URL} url | ||
* @param {string} param | ||
* @param {any} value | ||
* @param {boolean} [override] | ||
*/ | ||
function set_param(url, param, value, override = true) { | ||
if (value === undefined) { | ||
return; | ||
} | ||
|
||
if (value === null) { | ||
if (override || url.searchParams.has(param)) { | ||
url.searchParams.delete(param); | ||
} | ||
} else { | ||
if (override || !url.searchParams.has(param)) { | ||
url.searchParams.set(param, value); | ||
} | ||
} | ||
} |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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,47 @@ | ||
import { DEV } from 'esm-env'; | ||
import { sizes, loader, domains } from '__sveltekit/images'; | ||
|
||
/** | ||
* @param {string} src | ||
* @param {any} [options] | ||
* @returns {{ src: string, srcset?: string }} | ||
*/ | ||
export function getImage(src, options) { | ||
if (DEV) { | ||
if (!matches_domain(src)) { | ||
console.warn( | ||
`$app/images: Image src '${src}' does not match any of the allowed domains and will therefore not be optimized.` | ||
); | ||
} | ||
return { srcset: src, src }; | ||
} | ||
|
||
if (!matches_domain(src)) { | ||
return { src }; | ||
} | ||
|
||
const srcset = sizes | ||
.map((size) => { | ||
const url = loader(src, size, options); | ||
const w = size + 'w'; | ||
return `${url} ${w}`; | ||
}) | ||
.join(', '); | ||
const _src = loader(src, sizes[sizes.length - 1], options); | ||
|
||
// Order of attributes is important here as they are set in this order | ||
// and having src before srcset would result in a flicker | ||
return { srcset, src: _src }; | ||
} | ||
|
||
/** | ||
* @param {string} src | ||
*/ | ||
function matches_domain(src) { | ||
const url = new URL(src, 'http://n'); // if src is protocol relative, use dummy domain | ||
if (url.href === src) { | ||
return domains.some((domain) => url.hostname === domain); | ||
} else { | ||
return true; // relative urls are always ok | ||
} | ||
} |
This file contains 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