Skip to content

Commit

Permalink
feat: build.assetsInlineLimit callback (#15366)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudBarre committed Jan 12, 2024
1 parent b9b0816 commit 4d1342e
Show file tree
Hide file tree
Showing 9 changed files with 52 additions and 24 deletions.
4 changes: 3 additions & 1 deletion docs/config/build-options.md
Expand Up @@ -82,11 +82,13 @@ Specify the directory to nest generated assets under (relative to `build.outDir`
## build.assetsInlineLimit
- **Type:** `number`
- **Type:** `number` | `((filePath: string, content: Buffer) => boolean | undefined)`
- **Default:** `4096` (4 KiB)
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.
If a callback is passed, a boolean can be returned to opt-in or opt-out. If nothing is returned the default logic applies.
Git LFS placeholders are automatically excluded from inlining because they do not contain the content of the file they represent.
::: tip Note
Expand Down
12 changes: 9 additions & 3 deletions packages/vite/src/node/build.ts
Expand Up @@ -45,7 +45,11 @@ import { initDepsOptimizer } from './optimizer'
import { loadFallbackPlugin } from './plugins/loadFallback'
import { findNearestPackageData } from './packages'
import type { PackageCache } from './packages'
import { ESBUILD_MODULES_TARGET, VERSION } from './constants'
import {
DEFAULT_ASSETS_INLINE_LIMIT,
ESBUILD_MODULES_TARGET,
VERSION,
} from './constants'
import { resolveChokidarOptions } from './watch'
import { completeSystemWrapPlugin } from './plugins/completeSystemWrap'
import { mergeConfig } from './publicUtils'
Expand Down Expand Up @@ -101,7 +105,9 @@ export interface BuildOptions {
* base64 strings. Default limit is `4096` (4 KiB). Set to `0` to disable.
* @default 4096
*/
assetsInlineLimit?: number
assetsInlineLimit?:
| number
| ((filePath: string, content: Buffer) => boolean | undefined)
/**
* 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 @@ -325,7 +331,7 @@ export function resolveBuildOptions(
const defaultBuildOptions: BuildOptions = {
outDir: 'dist',
assetsDir: 'assets',
assetsInlineLimit: 4096,
assetsInlineLimit: DEFAULT_ASSETS_INLINE_LIMIT,
cssCodeSplit: !raw?.lib,
sourcemap: false,
rollupOptions: {},
Expand Down
2 changes: 2 additions & 0 deletions packages/vite/src/node/constants.ts
Expand Up @@ -156,4 +156,6 @@ export const DEFAULT_DEV_PORT = 5173

export const DEFAULT_PREVIEW_PORT = 4173

export const DEFAULT_ASSETS_INLINE_LIMIT = 4096

export const METADATA_FILENAME = '_metadata.json'
43 changes: 28 additions & 15 deletions packages/vite/src/node/plugins/asset.ts
Expand Up @@ -26,7 +26,7 @@ import {
removeLeadingSlash,
withTrailingSlash,
} from '../utils'
import { FS_PREFIX } from '../constants'
import { DEFAULT_ASSETS_INLINE_LIMIT, FS_PREFIX } from '../constants'
import type { ModuleGraph } from '../server/moduleGraph'

// referenceId is base64url but replaces - with $
Expand Down Expand Up @@ -325,7 +325,7 @@ async function fileToBuiltUrl(
config: ResolvedConfig,
pluginContext: PluginContext,
skipPublicCheck = false,
shouldInline?: boolean,
forceInline?: boolean,
): Promise<string> {
if (!skipPublicCheck && checkPublicFile(id, config)) {
return publicFileToBuiltUrl(id, config)
Expand All @@ -340,18 +340,8 @@ async function fileToBuiltUrl(
const file = cleanUrl(id)
const content = await fsp.readFile(file)

if (shouldInline == null) {
shouldInline =
!!config.build.lib ||
// 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))
}

let url: string
if (shouldInline) {
if (shouldInline(config, file, id, content, forceInline)) {
if (config.build.lib && isGitLfsPlaceholder(content)) {
config.logger.warn(
colors.yellow(`Inlined file ${id} was not downloaded via Git LFS`),
Expand Down Expand Up @@ -392,7 +382,7 @@ export async function urlToBuiltUrl(
importer: string,
config: ResolvedConfig,
pluginContext: PluginContext,
shouldInline?: boolean,
forceInline?: boolean,
): Promise<string> {
if (checkPublicFile(url, config)) {
return publicFileToBuiltUrl(url, config)
Expand All @@ -407,10 +397,33 @@ export async function urlToBuiltUrl(
pluginContext,
// skip public check since we just did it above
true,
shouldInline,
forceInline,
)
}

const shouldInline = (
config: ResolvedConfig,
file: string,
id: string,
content: Buffer,
forceInline: boolean | undefined,
): boolean => {
if (config.build.lib) return true
if (forceInline !== undefined) return forceInline
let limit: number
if (typeof config.build.assetsInlineLimit === 'function') {
const userShouldInline = config.build.assetsInlineLimit(file, content)
if (userShouldInline != null) return userShouldInline
limit = DEFAULT_ASSETS_INLINE_LIMIT
} else {
limit = Number(config.build.assetsInlineLimit)
}
if (file.endsWith('.html')) return false
// Don't inline SVG with fragments, as they are meant to be reused
if (file.endsWith('.svg') && id.includes('#')) return false
return content.length < limit && !isGitLfsPlaceholder(content)
}

const nestedQuotesRE = /"[^"']*'[^"]*"|'[^'"]*"[^']*'/

// Inspired by https://github.com/iconify/iconify/blob/main/packages/utils/src/svg/url.ts
Expand Down
3 changes: 2 additions & 1 deletion playground/worker/vite.config-es.js
Expand Up @@ -21,7 +21,8 @@ export default defineConfig({
},
build: {
outDir: 'dist/es',
assetsInlineLimit: 100, // keep SVG as assets URL
assetsInlineLimit: (filePath) =>
filePath.endsWith('.svg') ? false : undefined,
rollupOptions: {
output: {
assetFileNames: 'assets/[name].[ext]',
Expand Down
3 changes: 2 additions & 1 deletion playground/worker/vite.config-iife.js
Expand Up @@ -22,7 +22,8 @@ export default defineConfig({
},
build: {
outDir: 'dist/iife',
assetsInlineLimit: 100, // keep SVG as assets URL
assetsInlineLimit: (filePath) =>
filePath.endsWith('.svg') ? false : undefined,
manifest: true,
rollupOptions: {
output: {
Expand Down
3 changes: 2 additions & 1 deletion playground/worker/vite.config-relative-base-iife.js
Expand Up @@ -21,7 +21,8 @@ export default defineConfig(({ isPreview }) => ({
},
build: {
outDir: 'dist/relative-base-iife',
assetsInlineLimit: 100, // keep SVG as assets URL
assetsInlineLimit: (filePath) =>
filePath.endsWith('.svg') ? false : undefined,
rollupOptions: {
output: {
assetFileNames: 'other-assets/[name]-[hash].[ext]',
Expand Down
3 changes: 2 additions & 1 deletion playground/worker/vite.config-relative-base.js
Expand Up @@ -21,7 +21,8 @@ export default defineConfig(({ isPreview }) => ({
},
build: {
outDir: 'dist/relative-base',
assetsInlineLimit: 100, // keep SVG as assets URL
assetsInlineLimit: (filePath) =>
filePath.endsWith('.svg') ? false : undefined,
rollupOptions: {
output: {
assetFileNames: 'other-assets/[name]-[hash].[ext]',
Expand Down
3 changes: 2 additions & 1 deletion playground/worker/worker-sourcemap-config.js
Expand Up @@ -35,7 +35,8 @@ export default (sourcemap) => {
},
build: {
outDir: `dist/iife-${typeName}/`,
assetsInlineLimit: 100, // keep SVG as assets URL
assetsInlineLimit: (filePath) =>
filePath.endsWith('.svg') ? false : undefined,
sourcemap: sourcemap,
rollupOptions: {
output: {
Expand Down

0 comments on commit 4d1342e

Please sign in to comment.