Skip to content

Commit

Permalink
feat: Adding callback to assetsInlineLimit. Passes the `filePath: s…
Browse files Browse the repository at this point in the history
…tring`, `contentSize: number` and currently accrued bundled size `totalBundledSize: number`
  • Loading branch information
seivan committed Jun 24, 2022
1 parent 4f9097b commit 17ebf9a
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 54 deletions.
6 changes: 4 additions & 2 deletions docs/config/build-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ Specify the directory to nest generated assets under (relative to `build.outDir`

## build.assetsInlineLimit

- **Type:** `number`
- **Default:** `4096` (4kb)
- **Type:** `number` | `((filePath: string, size: number, totalBundledSize: number) => boolean)`
- **Default:** `6144` (6kb)

Imported or referenced assets that are smaller than this threshold will be inlined as base64 URLs to avoid extra http requests. Set to `0` to disable inlining altogether.
Can also implement a function that return boolean if it should bundled.
Passes the `filePath: string`, `contentSize: number` and currently accrued bundled size `totalBundledSize: number`

::: tip Note
If you specify `build.lib`, `build.assetsInlineLimit` will be ignored and assets will always be inlined, regardless of file size.
Expand Down
10 changes: 7 additions & 3 deletions packages/vite/src/node/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,14 @@ export interface BuildOptions {
assetsDir?: string
/**
* Static asset files smaller than this number (in bytes) will be inlined as
* base64 strings. Default limit is `4096` (4kb). Set to `0` to disable.
* base64 strings. Default limit is `6144` (6kb). Set to `0` to disable.
* Can also implement a function that return boolean if it should bundled.
* Passes the `filePath`, `contentSize` and currently accrued bundled size `totalBundledSize`
* @default 4096
*/
assetsInlineLimit?: number
assetsInlineLimit?:
| number
| ((filePath: string, size: number, totalBundledSize: number) => boolean)
/**
* Whether to code-split CSS. When enabled, CSS in async chunks will be
* inlined as strings in the chunk and inserted via dynamically created
Expand Down Expand Up @@ -239,7 +243,7 @@ export function resolveBuildOptions(
polyfillModulePreload: true,
outDir: 'dist',
assetsDir: 'assets',
assetsInlineLimit: 4096,
assetsInlineLimit: 6144,
cssCodeSplit: !raw?.lib,
cssTarget: false,
sourcemap: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ cli
)
.option(
'--assetsInlineLimit <number>',
`[number] static asset base64 inline threshold in bytes (default: 4096)`
`[number] static asset base64 inline threshold in bytes (default: 6144)`
)
.option(
'--ssr [entry]',
Expand Down
135 changes: 89 additions & 46 deletions packages/vite/src/node/plugins/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ export const assetUrlRE = /__VITE_ASSET__([a-z\d]{8})__(?:\$_(.*?)__)?/g
const rawRE = /(\?|&)raw(?:&|$)/
const urlRE = /(\?|&)url(?:&|$)/

const assetCache = new WeakMap<ResolvedConfig, Map<string, string>>()
const assetCache = new WeakMap<
ResolvedConfig,
Map<string, { url: string; size: number }>
>()

const assetHashToFilenameMap = new WeakMap<
ResolvedConfig,
Expand Down Expand Up @@ -356,6 +359,11 @@ export function publicFileToBuiltUrl(
return `__VITE_PUBLIC_ASSET__${hash}__`
}

const byteSizeOf = (function () {
const encoder = new TextEncoder()
const encode = encoder.encode.bind(encoder)
return (input: string) => encode(input).length
})()
/**
* Register an asset to be emitted as part of the bundle (if necessary)
* and returns the resolved public URL
Expand All @@ -373,62 +381,97 @@ async function fileToBuiltUrl(
const cache = assetCache.get(config)!
const cached = cache.get(id)
if (cached) {
return cached
return cached.url
}

const file = cleanUrl(id)
const content = await fsp.readFile(file)

let url: string
if (
config.build.lib ||
(!file.endsWith('.svg') &&
content.length < Number(config.build.assetsInlineLimit))
) {
const mimeType = mrmime.lookup(file) ?? 'application/octet-stream'
// base64 inlined as a string
url = `data:${mimeType};base64,${content.toString('base64')}`
} else {
// emit as asset
// rollup supports `import.meta.ROLLUP_FILE_URL_*`, but it generates code
// that uses runtime url sniffing and it can be verbose when targeting
// non-module format. It also fails to cascade the asset content change
// into the chunk's hash, so we have to do our own content hashing here.
// https://bundlers.tooling.report/hashing/asset-cascade/
// https://github.com/rollup/rollup/issues/3415
const map = assetHashToFilenameMap.get(config)!
const contentHash = getHash(content)
const { search, hash } = parseUrl(id)
const postfix = (search || '') + (hash || '')

const fileName = assetFileNamesToFileName(
resolveAssetFileNames(config),
file,
contentHash,
content
)
if (!map.has(contentHash)) {
map.set(contentHash, fileName)
}
const emittedSet = emittedHashMap.get(config)!
if (!emittedSet.has(contentHash)) {
const name = normalizePath(path.relative(config.root, file))
pluginContext.emitFile({
name,
fileName,
type: 'asset',
source: content
})
emittedSet.add(contentHash)
let size: number

/*
lib should always inlined
svg should never be inlined (unless lib)
*/
if (config.build.lib) {
url = fileToInlinedAsset(file, content)
size = 0
} else if (file.endsWith('.svg') === false) {
const inlinedURL = fileToInlinedAsset(file, content)
const inlinedSize: number = byteSizeOf(inlinedURL)

const assetInlineLimit = config.build.assetsInlineLimit ?? 0

const shouldInline =
typeof assetInlineLimit === 'number'
? inlinedSize < Number(assetInlineLimit)
: assetInlineLimit(
file,
inlinedSize,
[...cache.values()].reduce((memo, { size }) => memo + size, 0)
)

if (shouldInline) {
size = inlinedSize
url = inlinedURL
}

url = `__VITE_ASSET__${contentHash}__${postfix ? `$_${postfix}__` : ``}`
}

cache.set(id, url)
url ??= fileToLinkedAsset(id, config, pluginContext, file, content)
size ||= 0

cache.set(id, { url, size })
return url
}

function fileToInlinedAsset(file: string, content: Buffer): string {
const mimeType = mrmime.lookup(file) ?? 'application/octet-stream'
return `data:${mimeType};base64,${content.toString('base64')}`
}

function fileToLinkedAsset(
id: string,
config: ResolvedConfig,
pluginContext: PluginContext,
file: string,
content: Buffer
): string {
// emit as asset
// rollup supports `import.meta.ROLLUP_FILE_URL_*`, but it generates code
// that uses runtime url sniffing and it can be verbose when targeting
// non-module format. It also fails to cascade the asset content change
// into the chunk's hash, so we have to do our own content hashing here.
// https://bundlers.tooling.report/hashing/asset-cascade/
// https://github.com/rollup/rollup/issues/3415
const map = assetHashToFilenameMap.get(config)!
const contentHash = getHash(content)
const { search, hash } = parseUrl(id)
const postfix = (search || '') + (hash || '')

const fileName = assetFileNamesToFileName(
resolveAssetFileNames(config),
file,
contentHash,
content
)
if (!map.has(contentHash)) {
map.set(contentHash, fileName)
}
const emittedSet = emittedHashMap.get(config)!
if (!emittedSet.has(contentHash)) {
const name = normalizePath(path.relative(config.root, file))
pluginContext.emitFile({
name,
fileName,
type: 'asset',
source: content
})
emittedSet.add(contentHash)
}

return `__VITE_ASSET__${contentHash}__${postfix ? `$_${postfix}__` : ``}`
}

export async function urlToBuiltUrl(
url: string,
importer: string,
Expand Down
6 changes: 5 additions & 1 deletion playground/css/vite.config-relative-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* @type {import('vite').UserConfig}
*/

let totalSize = 0
const baseConfig = require('./vite.config.js')
module.exports = {
...baseConfig,
Expand All @@ -11,7 +12,10 @@ module.exports = {
outDir: 'dist/relative-base',
watch: false,
minify: false,
assetsInlineLimit: 0,
assetsInlineLimit: (_file, fileSize, combinedSize) => {
totalSize += fileSize
return true && totalSize === combinedSize + fileSize
},
rollupOptions: {
output: {
entryFileNames: 'entries/[name].js',
Expand Down
4 changes: 3 additions & 1 deletion playground/wasm/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export default defineConfig({
build: {
// make can no emit light.wasm
// and emit add.wasm
assetsInlineLimit: 80
assetsInlineLimit: (_file: string, _size: number, _totalSize: number) => {
return true
}
}
})

0 comments on commit 17ebf9a

Please sign in to comment.