diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index 9a91234f9378f6..1710dcdf9d23bc 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -325,6 +325,12 @@ function isGitLfsPlaceholder(content: Buffer): boolean { // Check whether the content begins with the characteristic string of Git LFS placeholders return GIT_LFS_PREFIX.compare(content, 0, GIT_LFS_PREFIX.length) === 0 } +// spaces in not supported in srcset +const svgDataURISupportedHexCode: Record = { + '%3D': '=', + '%3A': ':', + '%2F': '/', +} /** * Register an asset to be emitted as part of the bundle (if necessary) @@ -352,7 +358,8 @@ async function fileToBuiltUrl( let url: string if ( config.build.lib || - (!file.endsWith('.svg') && + // Don't inline SVG with fragments, as they are meant to be reused + (!(file.endsWith('.svg') && id.includes('#')) && !file.endsWith('.html') && content.length < Number(config.build.assetsInlineLimit) && !isGitLfsPlaceholder(content)) @@ -363,9 +370,27 @@ async function fileToBuiltUrl( ) } - const mimeType = mrmime.lookup(file) ?? 'application/octet-stream' - // base64 inlined as a string - url = `data:${mimeType};base64,${content.toString('base64')}` + if (file.endsWith('.svg')) { + let dataUri = content.toString() + // If the SVG contains some text any transformation is unsafe, and given that double quotes would then + // need to be escaped, the gain to use a data URI would be ridiculous if not negative + if (dataUri.includes(' svgDataURISupportedHexCode[match] || match.toLowerCase(), + ) + url = 'data:image/svg+xml,' + dataUri + } + } else { + const mimeType = mrmime.lookup(file) ?? 'application/octet-stream' + // base64 inlined as a string + url = `data:${mimeType};base64,${content.toString('base64')}` + } } else { // emit as asset const { search, hash } = parseUrl(id) diff --git a/playground/assets/__tests__/assets.spec.ts b/playground/assets/__tests__/assets.spec.ts index ab482bcdef3d2b..a4a0663d4ec0d1 100644 --- a/playground/assets/__tests__/assets.spec.ts +++ b/playground/assets/__tests__/assets.spec.ts @@ -285,7 +285,12 @@ describe('svg fragments', () => { test('from js import', async () => { const img = await page.$('.svg-frag-import') - expect(await img.getAttribute('src')).toMatch(/svg#icon-heart-view$/) + expect(await img.getAttribute('src')).toMatch( + isBuild + ? // Assert trimmed (data URI starts with < and ends with >) + /^data:image\/svg\+xml,%3c.*%3e#icon-heart-view$/ + : /svg#icon-heart-view$/, + ) }) }) diff --git a/playground/legacy/vite.config.js b/playground/legacy/vite.config.js index 4f319e250eb729..d07d295a0a27d8 100644 --- a/playground/legacy/vite.config.js +++ b/playground/legacy/vite.config.js @@ -16,6 +16,7 @@ export default defineConfig({ cssCodeSplit: false, manifest: true, sourcemap: true, + assetsInlineLimit: 100, // keep SVG as assets URL rollupOptions: { input: { index: path.resolve(__dirname, 'index.html'), diff --git a/playground/worker/vite.config-es.js b/playground/worker/vite.config-es.js index bd82434b99ab89..0049c5357fa0af 100644 --- a/playground/worker/vite.config-es.js +++ b/playground/worker/vite.config-es.js @@ -21,6 +21,7 @@ export default defineConfig({ }, build: { outDir: 'dist/es', + assetsInlineLimit: 100, // keep SVG as assets URL rollupOptions: { output: { assetFileNames: 'assets/[name].[ext]', diff --git a/playground/worker/vite.config-iife.js b/playground/worker/vite.config-iife.js index c98f7433383ef4..98757b8c00d2fd 100644 --- a/playground/worker/vite.config-iife.js +++ b/playground/worker/vite.config-iife.js @@ -38,6 +38,7 @@ export default defineConfig({ }, build: { outDir: 'dist/iife', + assetsInlineLimit: 100, // keep SVG as assets URL manifest: true, rollupOptions: { output: { diff --git a/playground/worker/vite.config-relative-base-iife.js b/playground/worker/vite.config-relative-base-iife.js index 77712979781705..6b3c7c6895f46a 100644 --- a/playground/worker/vite.config-relative-base-iife.js +++ b/playground/worker/vite.config-relative-base-iife.js @@ -21,6 +21,7 @@ export default defineConfig({ }, build: { outDir: 'dist/relative-base-iife', + assetsInlineLimit: 100, // keep SVG as assets URL rollupOptions: { output: { assetFileNames: 'other-assets/[name]-[hash].[ext]', diff --git a/playground/worker/vite.config-relative-base.js b/playground/worker/vite.config-relative-base.js index dd5af51bd8e263..a09aafa3b841aa 100644 --- a/playground/worker/vite.config-relative-base.js +++ b/playground/worker/vite.config-relative-base.js @@ -21,6 +21,7 @@ export default defineConfig({ }, build: { outDir: 'dist/relative-base', + assetsInlineLimit: 100, // keep SVG as assets URL rollupOptions: { output: { assetFileNames: 'other-assets/[name]-[hash].[ext]', diff --git a/playground/worker/worker-sourcemap-config.js b/playground/worker/worker-sourcemap-config.js index 89178e952ef03b..4ea0162f705119 100644 --- a/playground/worker/worker-sourcemap-config.js +++ b/playground/worker/worker-sourcemap-config.js @@ -35,6 +35,7 @@ export default (sourcemap) => { }, build: { outDir: `dist/iife-${typeName}/`, + assetsInlineLimit: 100, // keep SVG as assets URL sourcemap: sourcemap, rollupOptions: { output: {