Skip to content

perf: hoist vite asset declarations to module block #12627

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
merged 1 commit into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/friendly-wasps-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/enhanced-img': patch
---

perf: hoist vite asset declarations to module block
64 changes: 47 additions & 17 deletions packages/enhanced-img/src/preprocessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import MagicString from 'magic-string';
import { asyncWalk } from 'estree-walker';
import { parse } from 'svelte-parse-markup';

const ASSET_PREFIX = '___ASSET___';

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

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

// Import path to import name
// e.g. ./foo.png => ___ASSET___0
/** @type {Map<string, string>} */
/**
* Import path to import name
* e.g. ./foo.png => __IMPORTED_ASSET_0__
* @type {Map<string, string>}
*/
const imports = new Map();

/**
* Vite name to declaration name
* e.g. __VITE_ASSET_0__ => __DECLARED_ASSET_0__
* @type {Map<string, string>}
*/
const consts = new Map();

/**
* @param {import('svelte/types/compiler/interfaces').TemplateNode} node
* @param {{ type: string, start: number, end: number, raw: string }} src_attribute
Expand Down Expand Up @@ -94,10 +101,10 @@ export function image(opts) {
image = await process(resolved_id, opts);
images.set(resolved_id, image);
}
s.update(node.start, node.end, img_to_picture(content, node, image));
s.update(node.start, node.end, img_to_picture(consts, content, node, image));
} else {
// e.g. <img src="./foo.svg" /> => <img src={___ASSET___0} />
const name = ASSET_PREFIX + imports.size;
// e.g. <img src="./foo.svg" /> => <img src={__IMPORTED_ASSET_0__} />
const name = '__IMPORTED_ASSET_' + imports.size + '__';
const { start, end } = src_attribute;
// update src with reference to imported asset
s.update(
Expand Down Expand Up @@ -131,15 +138,28 @@ export function image(opts) {

// add imports
if (imports.size) {
let import_text = '';
let text = '';
for (const [path, import_name] of imports.entries()) {
import_text += `import ${import_name} from "${path}";`;
text += `import ${import_name} from "${path}";`;
}
if (ast.instance) {
// @ts-ignore
s.appendLeft(ast.instance.content.start, import_text);
s.appendLeft(ast.instance.content.start, text);
} else {
s.prepend(`<script>${text}</script>`);
}
}

if (consts.size) {
let text = '';
for (const [vite_name, declaration_name] of consts.entries()) {
text += `\tconst ${declaration_name} = "${vite_name}";\n`;
}
if (ast.module) {
// @ts-ignore
s.appendLeft(ast.module.content.start, text);
} else {
s.append(`<script>${import_text}</script>`);
s.prepend(`<script context="module">\n${text}</script>\n`);
}
}

Expand Down Expand Up @@ -264,11 +284,12 @@ function stringToNumber(param) {
}

/**
* @param {Map<string,string>} consts
* @param {string} content
* @param {import('svelte/types/compiler/interfaces').TemplateNode} node
* @param {import('vite-imagetools').Picture} image
*/
function img_to_picture(content, node, image) {
function img_to_picture(consts, content, node, image) {
/** @type {Array<import('svelte/types/compiler/interfaces').BaseDirective | import('svelte/types/compiler/interfaces').Attribute | import('svelte/types/compiler/interfaces').SpreadAttribute>} attributes */
const attributes = node.attributes;
const index = attributes.findIndex((attribute) => attribute.name === 'sizes');
Expand All @@ -281,11 +302,11 @@ function img_to_picture(content, node, image) {
let res = '<picture>';

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

res += `<img ${serialize_img_attributes(content, attributes, {
src: to_value(image.img.src),
src: to_value(consts, image.img.src),
width: image.img.w,
height: image.img.h
})} />`;
Expand All @@ -294,10 +315,19 @@ function img_to_picture(content, node, image) {
}

/**
* @param {Map<string, string>} consts
* @param {string} src
*/
function to_value(src) {
return src.startsWith('__VITE_ASSET__') ? `{"${src}"}` : `"${src}"`;
function to_value(consts, src) {
if (src.startsWith('__VITE_ASSET__')) {
let var_name = consts.get(src);
if (!var_name) {
var_name = '__DECLARED_ASSET_' + consts.size + '__';
consts.set(src, var_name);
}
return `{${var_name}}`;
}
return `"${src}"`;
}

/**
Expand Down
12 changes: 9 additions & 3 deletions packages/enhanced-img/test/Output.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<script context="module">
const __DECLARED_ASSET_0__ = "__VITE_ASSET__2AM7_y_a__ 1440w, __VITE_ASSET__2AM7_y_b__ 960w";
const __DECLARED_ASSET_1__ = "__VITE_ASSET__2AM7_y_c__ 1440w, __VITE_ASSET__2AM7_y_d__ 960w";
const __DECLARED_ASSET_2__ = "__VITE_ASSET__2AM7_y_e__ 1440w, __VITE_ASSET__2AM7_y_f__ 960w";
const __DECLARED_ASSET_3__ = "__VITE_ASSET__2AM7_y_g__";
</script>
<script lang="ts">
import ___ASSET___0 from "./foo.svg";
import __IMPORTED_ASSET_0__ from "./foo.svg";

import manual_image1 from './no.png';

Expand All @@ -17,7 +23,7 @@

<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>

<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>
<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>

<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>

Expand All @@ -44,7 +50,7 @@
</picture>
{/if}

<img src={___ASSET___0} alt="svg test" />
<img src={__IMPORTED_ASSET_0__} alt="svg test" />

{#each images as image}
{#if typeof image === 'string'}
Expand Down
Loading