Skip to content

Commit 2ee0f76

Browse files
committed
fix: warn rather than crash when non-enhanced image dynamically passed to enhanced:img
1 parent 9425c38 commit 2ee0f76

File tree

5 files changed

+83
-38
lines changed

5 files changed

+83
-38
lines changed

.changeset/poor-heads-fix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/enhanced-img': patch
3+
---
4+
5+
fix: warn rather than crash when non-enhanced image dynamically passed to `enhanced:img`

documentation/docs/40-best-practices/07-images.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ You can also use [Vite's `import.meta.glob`](https://vitejs.dev/guide/features.h
8484
```svelte
8585
<script>
8686
const imageModules = import.meta.glob(
87-
'/path/to/assets/*.{avif,gif,heif,jpeg,jpg,png,tiff,webp,svg}',
87+
'/path/to/assets/*.{avif,gif,heif,jpeg,jpg,png,tiff,webp}',
8888
{
8989
eager: true,
9090
query: {
@@ -99,6 +99,8 @@ You can also use [Vite's `import.meta.glob`](https://vitejs.dev/guide/features.h
9999
{/each}
100100
```
101101

102+
> [!NOTE] svg images are currently only supported statically
103+
102104
### Intrinsic Dimensions
103105

104106
`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. With these attributes, the browser can reserve the correct amount of space, preventing [layout shift](https://web.dev/articles/cls). 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:

packages/enhanced-img/src/vite-plugin.js

Lines changed: 51 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,13 @@ export function image_plugin(imagetools_plugin) {
128128
// this must come after the await so that we don't hand off processing between getting
129129
// the imports.size and incrementing the imports.size
130130
const name = imports.get(original_url) || '__IMPORTED_ASSET_' + imports.size + '__';
131+
if (!metadata.width || !metadata.height) {
132+
console.warn(`Could not determine intrinsic dimensions for ${resolved_id}`);
133+
}
131134
const new_markup = `<img ${serialize_img_attributes(content, node.attributes, {
132135
src: `{${name}}`,
133-
width: metadata.width || 0,
134-
height: metadata.height || 0
136+
width: metadata.width,
137+
height: metadata.height
135138
})} />`;
136139
s.update(node.start, node.end, new_markup);
137140
imports.set(original_url, name);
@@ -258,8 +261,8 @@ function get_attr_value(node, attr) {
258261
* @param {import('../types/internal.js').Attribute[]} attributes
259262
* @param {{
260263
* src: string,
261-
* width: string | number,
262-
* height: string | number
264+
* width?: string | number,
265+
* height?: string | number
263266
* }} details
264267
*/
265268
function serialize_img_attributes(content, attributes, details) {
@@ -283,21 +286,23 @@ function serialize_img_attributes(content, attributes, details) {
283286
}
284287
}
285288
}
286-
if (!user_width && !user_height) {
287-
attribute_strings.push(`width=${details.width}`);
288-
attribute_strings.push(`height=${details.height}`);
289-
} else if (!user_width && user_height) {
290-
attribute_strings.push(
291-
`width=${Math.round(
292-
(stringToNumber(details.width) * user_height) / stringToNumber(details.height)
293-
)}`
294-
);
295-
} else if (!user_height && user_width) {
296-
attribute_strings.push(
297-
`height=${Math.round(
298-
(stringToNumber(details.height) * user_width) / stringToNumber(details.width)
299-
)}`
300-
);
289+
if (details.width && details.height) {
290+
if (!user_width && !user_height) {
291+
attribute_strings.push(`width=${details.width}`);
292+
attribute_strings.push(`height=${details.height}`);
293+
} else if (!user_width && user_height) {
294+
attribute_strings.push(
295+
`width=${Math.round(
296+
(stringToNumber(details.width) * user_height) / stringToNumber(details.height)
297+
)}`
298+
);
299+
} else if (!user_height && user_width) {
300+
attribute_strings.push(
301+
`height=${Math.round(
302+
(stringToNumber(details.height) * user_width) / stringToNumber(details.width)
303+
)}`
304+
);
305+
}
301306
}
302307

303308
return attribute_strings.join(' ');
@@ -358,29 +363,42 @@ function to_value(src) {
358363
*/
359364
function dynamic_img_to_picture(content, node, src_var_name) {
360365
const attributes = node.attributes;
361-
const index = attributes.findIndex(
362-
(attribute) => 'name' in attribute && attribute.name === 'sizes'
363-
);
366+
/**
367+
* @param attribute_name {string}
368+
*/
369+
function index(attribute_name) {
370+
return attributes.findIndex(
371+
(attribute) => 'name' in attribute && attribute.name === attribute_name
372+
);
373+
}
374+
const size_index = index('sizes');
375+
const width_index = index('width');
376+
const height_index = index('height');
364377
let sizes_string = '';
365-
if (index >= 0) {
366-
sizes_string = ' ' + content.substring(attributes[index].start, attributes[index].end);
367-
attributes.splice(index, 1);
378+
if (size_index >= 0) {
379+
sizes_string =
380+
' ' + content.substring(attributes[size_index].start, attributes[size_index].end);
381+
attributes.splice(size_index, 1);
368382
}
369383

370-
const details = {
371-
src: `{${src_var_name}.img.src}`,
372-
width: `{${src_var_name}.img.w}`,
373-
height: `{${src_var_name}.img.h}`
374-
};
375-
376384
return `{#if typeof ${src_var_name} === 'string'}
377-
<img ${serialize_img_attributes(content, attributes, details)} />
385+
{#if import.meta.DEV && ${!width_index && !height_index}}
386+
${src_var_name} was not enhanced. Cannot determine dimensions.
387+
{:else}
388+
<img ${serialize_img_attributes(content, attributes, {
389+
src: `{${src_var_name}}`
390+
})} />
391+
{/if}
378392
{:else}
379393
<picture>
380394
{#each Object.entries(${src_var_name}.sources) as [format, srcset]}
381395
<source {srcset}${sizes_string} type={'image/' + format} />
382396
{/each}
383-
<img ${serialize_img_attributes(content, attributes, details)} />
397+
<img ${serialize_img_attributes(content, attributes, {
398+
src: `{${src_var_name}.img.src}`,
399+
width: `{${src_var_name}.img.w}`,
400+
height: `{${src_var_name}.img.h}`
401+
})} />
384402
</picture>
385403
{/if}`;
386404
}

packages/enhanced-img/test/Output.svelte

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@
3838
<picture><source srcset="/1 1440w, /2 960w" type="image/avif" /><source srcset="/3 1440w, /4 960w" type="image/webp" /><source srcset="5 1440w, /6 960w" type="image/png" /><img src="/7" alt="absolute path test" width=1440 height=1440 /></picture>
3939

4040
{#if typeof src === 'string'}
41-
<img src={src.img.src} alt="attribute shorthand test" width={src.img.w} height={src.img.h} />
41+
{#if
42+
import.meta.DEV && false}
43+
src was not enhanced. Cannot determine dimensions.
44+
{:else}
45+
<img src={src} alt="attribute shorthand test" />
46+
{/if}
4247
{:else}
4348
<picture>
4449
{#each Object.entries(src.sources) as [format, srcset]}
@@ -50,7 +55,12 @@
5055

5156
{#each images as image}
5257
{#if typeof image === 'string'}
53-
<img src={image.img.src} alt="opt-in test" width={image.img.w} height={image.img.h} />
58+
{#if
59+
import.meta.DEV && false}
60+
image was not enhanced. Cannot determine dimensions.
61+
{:else}
62+
<img src={image} alt="opt-in test" />
63+
{/if}
5464
{:else}
5565
<picture>
5666
{#each Object.entries(image.sources) as [format, srcset]}
@@ -63,7 +73,12 @@
6373

6474
{#each images as _, i}
6575
{#if typeof get_image(i) === 'string'}
66-
<img src={get_image(i).img.src} alt="opt-in test" width={get_image(i).img.w} height={get_image(i).img.h} />
76+
{#if
77+
import.meta.DEV && false}
78+
get_image(i) was not enhanced. Cannot determine dimensions.
79+
{:else}
80+
<img src={get_image(i)} alt="opt-in test" />
81+
{/if}
6782
{:else}
6883
<picture>
6984
{#each Object.entries(get_image(i).sources) as [format, srcset]}

packages/enhanced-img/test/apps/basics/src/routes/+page.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
import logo from './logo.png?enhanced';
33
</script>
44

5-
<enhanced:img id="playwright" src="./playwright-logo.svg" alt="Playwright logo" />
5+
<!-- standard image -->
66
<enhanced:img id="birds" src="./birds.jpg" alt="birds" />
7+
8+
<!-- svg over inline size -->
9+
<enhanced:img id="playwright" src="./playwright-logo.svg" alt="Playwright logo" />
10+
11+
<!-- dynamic image -->
712
<enhanced:img id="logo" src={logo} alt="Svelte logo" />

0 commit comments

Comments
 (0)