Skip to content

Commit c5e97cc

Browse files
committed
perf: hoist vite asset declarations to module block
1 parent 45cb8c5 commit c5e97cc

File tree

3 files changed

+61
-20
lines changed

3 files changed

+61
-20
lines changed

.changeset/friendly-wasps-return.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+
perf: hoist vite asset declarations to module block

packages/enhanced-img/src/preprocessor.js

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import MagicString from 'magic-string';
55
import { asyncWalk } from 'estree-walker';
66
import { parse } from 'svelte-parse-markup';
77

8-
const ASSET_PREFIX = '___ASSET___';
9-
108
// TODO: expose this in vite-imagetools rather than duplicating it
119
const OPTIMIZABLE = /^[^?]+\.(avif|heif|gif|jpeg|jpg|png|tiff|webp)(\?.*)?$/;
1210

@@ -35,11 +33,20 @@ export function image(opts) {
3533
const s = new MagicString(content);
3634
const ast = parse(content, { filename });
3735

38-
// Import path to import name
39-
// e.g. ./foo.png => ___ASSET___0
40-
/** @type {Map<string, string>} */
36+
/**
37+
* Import path to import name
38+
* e.g. ./foo.png => __IMPORTED_ASSET_0__
39+
* @type {Map<string, string>}
40+
*/
4141
const imports = new Map();
4242

43+
/**
44+
* Vite name to declaration name
45+
* e.g. __VITE_ASSET_0__ => __DECLARED_ASSET_0__
46+
* @type {Map<string, string>}
47+
*/
48+
const consts = new Map();
49+
4350
/**
4451
* @param {import('svelte/types/compiler/interfaces').TemplateNode} node
4552
* @param {{ type: string, start: number, end: number, raw: string }} src_attribute
@@ -94,10 +101,10 @@ export function image(opts) {
94101
image = await process(resolved_id, opts);
95102
images.set(resolved_id, image);
96103
}
97-
s.update(node.start, node.end, img_to_picture(content, node, image));
104+
s.update(node.start, node.end, img_to_picture(consts, content, node, image));
98105
} else {
99-
// e.g. <img src="./foo.svg" /> => <img src={___ASSET___0} />
100-
const name = ASSET_PREFIX + imports.size;
106+
// e.g. <img src="./foo.svg" /> => <img src={__IMPORTED_ASSET_0__} />
107+
const name = '__IMPORTED_ASSET_' + imports.size + '__';
101108
const { start, end } = src_attribute;
102109
// update src with reference to imported asset
103110
s.update(
@@ -131,15 +138,28 @@ export function image(opts) {
131138

132139
// add imports
133140
if (imports.size) {
134-
let import_text = '';
141+
let text = '';
135142
for (const [path, import_name] of imports.entries()) {
136-
import_text += `import ${import_name} from "${path}";`;
143+
text += `import ${import_name} from "${path}";`;
137144
}
138145
if (ast.instance) {
139146
// @ts-ignore
140-
s.appendLeft(ast.instance.content.start, import_text);
147+
s.appendLeft(ast.instance.content.start, text);
148+
} else {
149+
s.prepend(`<script>${text}</script>`);
150+
}
151+
}
152+
153+
if (consts.size) {
154+
let text = '';
155+
for (const [vite_name, declaration_name] of consts.entries()) {
156+
text += `\tconst ${declaration_name} = "${vite_name}";\n`;
157+
}
158+
if (ast.module) {
159+
// @ts-ignore
160+
s.appendLeft(ast.module.content.start, text);
141161
} else {
142-
s.append(`<script>${import_text}</script>`);
162+
s.prepend(`<script context="module">\n${text}</script>\n`);
143163
}
144164
}
145165

@@ -264,11 +284,12 @@ function stringToNumber(param) {
264284
}
265285

266286
/**
287+
* @param {Map<string,string>} consts
267288
* @param {string} content
268289
* @param {import('svelte/types/compiler/interfaces').TemplateNode} node
269290
* @param {import('vite-imagetools').Picture} image
270291
*/
271-
function img_to_picture(content, node, image) {
292+
function img_to_picture(consts, content, node, image) {
272293
/** @type {Array<import('svelte/types/compiler/interfaces').BaseDirective | import('svelte/types/compiler/interfaces').Attribute | import('svelte/types/compiler/interfaces').SpreadAttribute>} attributes */
273294
const attributes = node.attributes;
274295
const index = attributes.findIndex((attribute) => attribute.name === 'sizes');
@@ -281,11 +302,11 @@ function img_to_picture(content, node, image) {
281302
let res = '<picture>';
282303

283304
for (const [format, srcset] of Object.entries(image.sources)) {
284-
res += `<source srcset=${to_value(srcset)}${sizes_string} type="image/${format}" />`;
305+
res += `<source srcset=${to_value(consts, srcset)}${sizes_string} type="image/${format}" />`;
285306
}
286307

287308
res += `<img ${serialize_img_attributes(content, attributes, {
288-
src: to_value(image.img.src),
309+
src: to_value(consts, image.img.src),
289310
width: image.img.w,
290311
height: image.img.h
291312
})} />`;
@@ -294,10 +315,19 @@ function img_to_picture(content, node, image) {
294315
}
295316

296317
/**
318+
* @param {Map<string, string>} consts
297319
* @param {string} src
298320
*/
299-
function to_value(src) {
300-
return src.startsWith('__VITE_ASSET__') ? `{"${src}"}` : `"${src}"`;
321+
function to_value(consts, src) {
322+
if (src.startsWith('__VITE_ASSET__')) {
323+
let var_name = consts.get(src);
324+
if (!var_name) {
325+
var_name = '__DECLARED_ASSET_' + consts.size + '__';
326+
consts.set(src, var_name);
327+
}
328+
return `{"${var_name}"}`;
329+
}
330+
return `"${src}"`;
301331
}
302332

303333
/**

packages/enhanced-img/test/Output.svelte

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
<script context="module">
2+
const __DECLARED_ASSET_0__ = "__VITE_ASSET__2AM7_y_a__ 1440w, __VITE_ASSET__2AM7_y_b__ 960w";
3+
const __DECLARED_ASSET_1__ = "__VITE_ASSET__2AM7_y_c__ 1440w, __VITE_ASSET__2AM7_y_d__ 960w";
4+
const __DECLARED_ASSET_2__ = "__VITE_ASSET__2AM7_y_e__ 1440w, __VITE_ASSET__2AM7_y_f__ 960w";
5+
const __DECLARED_ASSET_3__ = "__VITE_ASSET__2AM7_y_g__";
6+
</script>
17
<script lang="ts">
2-
import ___ASSET___0 from "./foo.svg";
8+
import __IMPORTED_ASSET_0__ from "./foo.svg";
39
410
import manual_image1 from './no.png';
511
@@ -17,7 +23,7 @@
1723

1824
<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="dev test" width=1440 height=1440 /></picture>
1925

20-
<picture><source srcset={"__VITE_ASSET__2AM7_y_a__ 1440w, __VITE_ASSET__2AM7_y_b__ 960w"} type="image/avif" /><source srcset={"__VITE_ASSET__2AM7_y_c__ 1440w, __VITE_ASSET__2AM7_y_d__ 960w"} type="image/webp" /><source srcset={"__VITE_ASSET__2AM7_y_e__ 1440w, __VITE_ASSET__2AM7_y_f__ 960w"} type="image/png" /><img src={"__VITE_ASSET__2AM7_y_g__"} alt="production test" width=1440 height=1440 /></picture>
26+
<picture><source srcset={"__DECLARED_ASSET_0__"} type="image/avif" /><source srcset={"__DECLARED_ASSET_1__"} type="image/webp" /><source srcset={"__DECLARED_ASSET_2__"} type="image/png" /><img src={"__DECLARED_ASSET_3__"} alt="production test" width=1440 height=1440 /></picture>
2127

2228
<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" width="5" height="10" alt="dimensions test" width=1440 height=1440 /></picture>
2329

@@ -44,7 +50,7 @@
4450
</picture>
4551
{/if}
4652

47-
<img src={___ASSET___0} alt="svg test" />
53+
<img src={__IMPORTED_ASSET_0__} alt="svg test" />
4854

4955
{#each images as image}
5056
{#if typeof image === 'string'}

0 commit comments

Comments
 (0)