Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-max-inline-size-option.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@callstack/repack": minor
---

Add `maxInlineSize` option to assets loader for size-based asset inlining. Use it together with `inline: true` — assets whose largest variant is within the threshold are inlined as base64 URIs; larger assets are extracted as separate files.
5 changes: 4 additions & 1 deletion apps/tester-federation-v2/configs/rspack.host-app.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export default Repack.defineRspackConfig((env) => {
},
type: 'javascript/auto',
},
...Repack.getAssetTransformRules(),
...Repack.getAssetTransformRules({
inline: true,
maxInlineSize: 90 * 1024,
}),
],
},
plugins: [
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions apps/tester-federation-v2/src/host/navigation/MainNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { StyleSheet } from 'react-native';

import DetailScreen from '../screens/DetailScreen';
import HomeScreen from '../screens/HomeScreen';
import HostAssetsScreen from '../screens/HostAssetsScreen';
import MiniAppScreen from '../screens/MiniAppScreen';

export type MainStackParamList = {
Home: undefined;
Detail: undefined;
HostAssets: undefined;
MiniApp: undefined;
};

Expand All @@ -34,6 +36,19 @@ const MainNavigator = () => {
>
<Main.Screen name="Home" component={HomeScreen} />
<Main.Screen name="Detail" component={DetailScreen} />
<Main.Screen
name="HostAssets"
component={HostAssetsScreen}
options={{
title: 'Host Assets',
headerTitle: 'Host Assets',
headerLargeTitleEnabled: true,
headerStyle: styles.header,
headerLargeStyle: styles.hostAssetsLargeHeader,
headerTitleStyle: styles.headerTitle,
headerLargeTitleStyle: styles.hostAssetsLargeTitle,
}}
/>
<Main.Screen
name="MiniApp"
component={MiniAppScreen}
Expand All @@ -50,6 +65,12 @@ const styles = StyleSheet.create({
headerTitle: {
color: '#FFFFFF', // White text
},
hostAssetsLargeHeader: {
backgroundColor: 'transparent',
},
hostAssetsLargeTitle: {
color: '#2C3E50',
},
});

export default MainNavigator;
17 changes: 17 additions & 0 deletions apps/tester-federation-v2/src/host/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ const HomeScreen = () => {
>
<Text style={styles.buttonText}>See details</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.secondaryButton]}
onPress={() => navigation.navigate('HostAssets')}
>
<Text style={styles.secondaryButtonText}>Host assets</Text>
</TouchableOpacity>
</View>
<View style={styles.miniAppSection}>
<Text style={styles.sectionTitle}>Mini Apps</Text>
Expand Down Expand Up @@ -84,11 +90,22 @@ const styles = StyleSheet.create({
borderRadius: 10,
alignItems: 'center',
},
secondaryButton: {
marginTop: 12,
backgroundColor: '#FFFFFF',
borderColor: '#3498DB',
borderWidth: 1,
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
secondaryButtonText: {
color: '#3498DB',
fontSize: 16,
fontWeight: '600',
},
miniAppSection: {
backgroundColor: 'white',
padding: 20,
Expand Down
168 changes: 168 additions & 0 deletions apps/tester-federation-v2/src/host/screens/HostAssetsScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import {
FlatList,
Image,
type ImageRequireSource,
StyleSheet,
Text,
View,
} from 'react-native';

const assets = [
{
source: require('../assets/pic_1.jpg'),
status: 'Inlined',
size: '89,400 bytes',
},
{
source: require('../assets/pic_2.jpg'),
status: 'Emitted',
size: '121,569 bytes',
},
{
source: require('../assets/pic_3.jpg'),
status: 'Inlined',
size: '59,862 bytes',
},
];

const data = assets.map((asset, index) => ({
title: `Host asset ${index + 1}`,
...asset,
}));

const AssetRow = ({
title,
source,
status,
size,
}: {
title: string;
source: ImageRequireSource;
status: string;
size: string;
}) => (
<View style={styles.row}>
<Image source={source} style={styles.image} />
<View style={styles.titleContainer}>
<View style={styles.titleRow}>
<Text style={styles.title}>{title}</Text>
<View
style={[
styles.badge,
status === 'Inlined' ? styles.inlineBadge : styles.emittedBadge,
]}
>
<Text
style={[
styles.badgeText,
status === 'Inlined'
? styles.inlineBadgeText
: styles.emittedBadgeText,
]}
>
{status}
</Text>
</View>
</View>
<Text style={styles.size}>{size}</Text>
<Text style={styles.subtitle}>
Loaded from the host app bundle to test inline asset size limits
</Text>
</View>
</View>
);

const HostAssetsScreen = () => {
return (
<FlatList
contentInsetAdjustmentBehavior="automatic"
contentContainerStyle={styles.contentContainer}
data={data}
keyExtractor={(item) => item.title}
renderItem={({ item }) => (
<AssetRow
title={item.title}
source={item.source}
status={item.status}
size={item.size}
/>
)}
style={styles.container}
/>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F9FF',
},
contentContainer: {
paddingVertical: 20,
},
row: {
marginBottom: 20,
marginHorizontal: 15,
backgroundColor: 'white',
borderRadius: 10,
overflow: 'hidden',
shadowColor: '#2C3E50',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
image: {
width: '100%',
height: 250,
resizeMode: 'cover',
},
titleContainer: {
padding: 15,
},
titleRow: {
alignItems: 'center',
flexDirection: 'row',
gap: 10,
marginBottom: 5,
},
title: {
flex: 1,
fontSize: 18,
fontWeight: '600',
color: '#2C3E50',
},
badge: {
borderRadius: 4,
paddingHorizontal: 8,
paddingVertical: 4,
},
inlineBadge: {
backgroundColor: '#DFF5E7',
},
emittedBadge: {
backgroundColor: '#FFF1D6',
},
badgeText: {
fontSize: 12,
fontWeight: '700',
},
inlineBadgeText: {
color: '#1E7F45',
},
emittedBadgeText: {
color: '#9A5A00',
},
size: {
color: '#7F8C8D',
fontSize: 13,
marginBottom: 5,
},
subtitle: {
fontSize: 14,
color: '#3498DB',
fontWeight: '400',
},
});

export default HostAssetsScreen;
9 changes: 8 additions & 1 deletion packages/repack/src/loaders/assetsLoader/assetsLoader.ts
Comment thread
bartekkrok marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,15 @@ export default async function repackAssetsLoader(
})
);

const largestVariantSize = Math.max(
...assets.map((asset) => asset.data.length)
);
const shouldInlineAsset =
Comment thread
bartekkrok marked this conversation as resolved.
!!options.inline &&
(!options.maxInlineSize || largestVariantSize <= options.maxInlineSize);

let result: string;
if (options.inline) {
if (shouldInlineAsset) {
logger.debug(`Inlining assets for request ${resourcePath}`);
result = inlineAssets({ assets, resourcePath });
} else {
Expand Down
2 changes: 2 additions & 0 deletions packages/repack/src/loaders/assetsLoader/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface AssetLoaderOptions {
scalableAssetExtensions?: string[];
scalableAssetResolutions?: string[];
inline?: boolean;
maxInlineSize?: number;
publicPath?: string;
remote?: AssetLoaderRemoteOptions;
}
Expand All @@ -39,6 +40,7 @@ export const optionsSchema: Schema = {
type: 'array',
},
inline: { type: 'boolean' },
maxInlineSize: { type: 'number' },
publicPath: { type: 'string' },
remote: {
type: 'object',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { type CodeSigningPluginConfig, validateConfig } from './config.js';

export class CodeSigningPlugin {
private chunkFilenames: Set<string>;

/**
* Constructs new `RepackPlugin`.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ exports[`getAssetTransformRules should add SVGR rule when svg="svgr" 1`] = `
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": undefined,
"maxInlineSize": undefined,
"remote": undefined,
},
},
Expand All @@ -32,6 +33,7 @@ exports[`getAssetTransformRules should add URI rule when svg="uri" 1`] = `
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": undefined,
"maxInlineSize": undefined,
"remote": undefined,
},
},
Expand All @@ -51,6 +53,7 @@ exports[`getAssetTransformRules should add XML rule when svg="xml" 1`] = `
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": undefined,
"maxInlineSize": undefined,
"remote": undefined,
},
},
Expand All @@ -70,6 +73,7 @@ exports[`getAssetTransformRules should include additional options for SVGR 1`] =
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": undefined,
"maxInlineSize": undefined,
"remote": undefined,
},
},
Expand All @@ -95,6 +99,7 @@ exports[`getAssetTransformRules should return default asset transform rules when
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": undefined,
"maxInlineSize": undefined,
"remote": undefined,
},
},
Expand All @@ -110,6 +115,23 @@ exports[`getAssetTransformRules should return rules with inline option when prov
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": true,
"maxInlineSize": undefined,
"remote": undefined,
},
},
},
]
`;

exports[`getAssetTransformRules should return rules with maxInlineSize option when provided 1`] = `
[
{
"test": /\\\\\\.\\(bmp\\|gif\\|jpg\\|jpeg\\|png\\|psd\\|svg\\|webp\\|tiff\\|m4v\\|mov\\|mp4\\|mpeg\\|mpg\\|webm\\|aac\\|aiff\\|caf\\|m4a\\|mp3\\|wav\\|html\\|pdf\\|yaml\\|yml\\|otf\\|ttf\\|zip\\|obj\\)\\$/,
"use": {
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": undefined,
"maxInlineSize": 1024,
"remote": undefined,
},
},
Expand All @@ -125,6 +147,7 @@ exports[`getAssetTransformRules should return rules with remote options when pro
"loader": "@callstack/repack/assets-loader",
"options": {
"inline": undefined,
"maxInlineSize": undefined,
"remote": {
"enabled": true,
"publicPath": "https://example.com/assets",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ describe('getAssetTransformRules', () => {
expect(rules).toMatchSnapshot();
});

it('should return rules with maxInlineSize option when provided', () => {
const rules = getAssetTransformRules({ maxInlineSize: 1024 });

// @ts-ignore
expect(rules[0]?.use?.options?.maxInlineSize).toEqual(1024);
expect(rules).toMatchSnapshot();
});

it('should return rules with remote options when provided', () => {
const remoteOptions = { publicPath: 'https://example.com/assets' };
const rules = getAssetTransformRules({ remote: remoteOptions });
Expand Down
Loading
Loading