diff --git a/packages/repack/src/loaders/assetsLoader/assetsLoader.ts b/packages/repack/src/loaders/assetsLoader/assetsLoader.ts index 919ddf87e..6bf584a5e 100644 --- a/packages/repack/src/loaders/assetsLoader/assetsLoader.ts +++ b/packages/repack/src/loaders/assetsLoader/assetsLoader.ts @@ -222,8 +222,16 @@ export default async function repackAssetsLoader( }) ); + const largestVariantSize = Math.max( + ...assets.map((asset) => asset.data.length) + ); + const shouldInlineAsset = + options.inline === true || + (options.maxInlineSize !== undefined && + largestVariantSize <= options.maxInlineSize); + let result: string; - if (options.inline) { + if (shouldInlineAsset) { logger.debug(`Inlining assets for request ${resourcePath}`); result = inlineAssets({ assets, resourcePath }); } else { diff --git a/packages/repack/src/loaders/assetsLoader/options.ts b/packages/repack/src/loaders/assetsLoader/options.ts index 6f7632771..3b9f4b66a 100644 --- a/packages/repack/src/loaders/assetsLoader/options.ts +++ b/packages/repack/src/loaders/assetsLoader/options.ts @@ -18,6 +18,7 @@ export interface AssetLoaderOptions { scalableAssetExtensions?: string[]; scalableAssetResolutions?: string[]; inline?: boolean; + maxInlineSize?: number; publicPath?: string; remote?: AssetLoaderRemoteOptions; } @@ -39,6 +40,7 @@ export const optionsSchema: Schema = { type: 'array', }, inline: { type: 'boolean' }, + maxInlineSize: { type: 'number' }, publicPath: { type: 'string' }, remote: { type: 'object', diff --git a/packages/repack/src/utils/getAssetTransformRules.ts b/packages/repack/src/utils/getAssetTransformRules.ts index efed8ca03..8a05e5b5a 100644 --- a/packages/repack/src/utils/getAssetTransformRules.ts +++ b/packages/repack/src/utils/getAssetTransformRules.ts @@ -39,6 +39,13 @@ interface GetAssetTransformRulesOptions { */ inline?: boolean; + /** + * Maximum asset file size in bytes to inline as base64 URIs. + * Assets larger than this threshold will be extracted as separate files. + * Mutually exclusive with `inline`. + */ + maxInlineSize?: number; + /** * Configuration for remote asset loading. */ @@ -57,7 +64,8 @@ interface GetAssetTransformRulesOptions { * Creates `module.rules` configuration for handling assets in React Native applications. * * @param options Configuration options - * @param options.inline Whether to inline assets as base64 URIs (defaults to false) + * @param options.inline Whether to inline all assets as base64 URIs (defaults to false) + * @param options.maxInlineSize Maximum asset file size in bytes to inline as base64 URIs; larger assets are extracted as separate files * @param options.remote Configuration for remote asset loading with publicPath and optional assetPath function * @param options.svg Determines how SVG files should be processed ('svgr', 'xml', or 'uri') * @@ -65,6 +73,7 @@ interface GetAssetTransformRulesOptions { */ export function getAssetTransformRules({ inline, + maxInlineSize, remote, svg, }: GetAssetTransformRulesOptions = {}) { @@ -85,7 +94,7 @@ export function getAssetTransformRules({ test: getAssetExtensionsRegExp(extensions), use: { loader: '@callstack/repack/assets-loader', - options: { inline, remote: remoteOptions }, + options: { inline, maxInlineSize, remote: remoteOptions }, }, }); diff --git a/website/src/latest/docs/guides/inline-assets.md b/website/src/latest/docs/guides/inline-assets.md index 87f9b6286..fe9a96482 100644 --- a/website/src/latest/docs/guides/inline-assets.md +++ b/website/src/latest/docs/guides/inline-assets.md @@ -61,13 +61,55 @@ import image from './image.png'; The value of `image` in this example would be either an object with `uri`, `width`, `height` and `scale` or an array of such objects, in case there are multiple scales. -## Selective inlining +## Size-based inlining + +Instead of inlining all assets unconditionally, you can use `maxInlineSize` to set a file size threshold in bytes. Assets whose **largest scale variant** is smaller than or equal to the threshold will be inlined; larger assets will be extracted as separate files. + +```js title="rspack.config.cjs" +const Repack = require("@callstack/repack"); + +module.exports = { + module: { + rules: [ + { + test: Repack.getAssetExtensionsRegExp(), + use: { + loader: "@callstack/repack/assets-loader", + options: { maxInlineSize: 20 * 1024 }, // inline assets up to 20 KB + }, + }, + ], + }, +}; +``` + +Or via the helper: + +```js title="rspack.config.cjs" +const Repack = require("@callstack/repack"); + +module.exports = { + module: { + rules: [...Repack.getAssetTransformRules({ maxInlineSize: 20 * 1024 })], + }, +}; +``` + +:::info Scale variants and the size threshold + +The threshold is compared against the **largest scale variant** of the asset (e.g. `@3x`), not the `@1x` file. This is intentional — when an asset is inlined, all scale variants are embedded into the bundle, so the largest one is what determines the worst-case size impact. + +For example, a `@1x` PNG that is 10 KB may have a `@3x` variant of 80 KB. With `maxInlineSize: 20 * 1024` the asset would be extracted, not inlined, because the `@3x` variant exceeds the threshold. + +::: + +## Selective inlining by path You can provide multiple rules with Re.Pack's [Assets loader](/api/loaders/assets-loader) - one rule would extract the assets and another would inline them. There's no limit how many of these rules you could have. Make sure you configure those rules not to overlap, so that any single asset is only processed by one rule (by one [Assets loader](/api/loaders/assets-loader)). Use combination of `include`, `exclude` and `test` (for extensions matching) to configure each rule. -```js title="rspack.config.mjs" +```js title="rspack.config.cjs" const Repack = require("@callstack/repack"); module.exports = {