Skip to content

Commit

Permalink
feat(build)!: inline SVGs (#14643)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudBarre authored Oct 19, 2023
1 parent e5ee420 commit 5acda5e
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 5 deletions.
38 changes: 34 additions & 4 deletions packages/vite/src/node/plugins/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,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))
Expand All @@ -382,9 +383,13 @@ 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')) {
url = svgToDataURL(content)
} 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)
Expand Down Expand Up @@ -428,3 +433,28 @@ export async function urlToBuiltUrl(
true,
)
}

// Inspired by https://github.com/iconify/iconify/blob/main/packages/utils/src/svg/url.ts
function svgToDataURL(content: Buffer): string {
const stringContent = 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 (stringContent.includes('<text')) {
return `data:image/svg+xml;base64,${content.toString('base64')}`
} else {
return (
'data:image/svg+xml,' +
stringContent
.trim()
.replaceAll('"', "'")
.replaceAll('%', '%25')
.replaceAll('#', '%23')
.replaceAll('<', '%3c')
.replaceAll('>', '%3e')
// Spaces are not valid in srcset it has some use cases
// it can make the uncompressed URI slightly higher than base64, but will compress way better
// https://github.com/vitejs/vite/pull/14643#issuecomment-1766288673
.replaceAll(/\s+/g, '%20')
)
}
}
7 changes: 6 additions & 1 deletion playground/assets/__tests__/assets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,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$/,
)
})
})

Expand Down
1 change: 1 addition & 0 deletions playground/legacy/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
1 change: 1 addition & 0 deletions playground/worker/vite.config-es.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default defineConfig({
},
build: {
outDir: 'dist/es',
assetsInlineLimit: 100, // keep SVG as assets URL
rollupOptions: {
output: {
assetFileNames: 'assets/[name].[ext]',
Expand Down
1 change: 1 addition & 0 deletions playground/worker/vite.config-iife.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default defineConfig({
},
build: {
outDir: 'dist/iife',
assetsInlineLimit: 100, // keep SVG as assets URL
manifest: true,
rollupOptions: {
output: {
Expand Down
1 change: 1 addition & 0 deletions playground/worker/vite.config-relative-base-iife.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]',
Expand Down
1 change: 1 addition & 0 deletions playground/worker/vite.config-relative-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]',
Expand Down
1 change: 1 addition & 0 deletions playground/worker/worker-sourcemap-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default (sourcemap) => {
},
build: {
outDir: `dist/iife-${typeName}/`,
assetsInlineLimit: 100, // keep SVG as assets URL
sourcemap: sourcemap,
rollupOptions: {
output: {
Expand Down

0 comments on commit 5acda5e

Please sign in to comment.