Skip to content

Commit

Permalink
fix(repack): fixed asset inlining for newer React Native versions (#422)
Browse files Browse the repository at this point in the history
* fix(repack): fix inlining assets in react-native >= 0.72

* chore: add changeset

* fix: remove redundant .default

* test(repack): added tests for inlining assets in newer React Native versions
  • Loading branch information
jbroma authored Oct 9, 2023
1 parent 7deea25 commit 430d6be
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-panthers-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@callstack/repack": patch
---

Fix inlining assets in react-native versions >= 0.72
Original file line number Diff line number Diff line change
Expand Up @@ -207,73 +207,110 @@ describe('assetLoader', () => {
});

describe('should inline asset', () => {
it('without scales', async () => {
const { code } = await compileBundle(
'android',
{
'./index.js': "export { default } from './__fixtures__/logo.png';",
},
true
);
it.each([
{ reactNativeVersion: '<0.72', moduleExportSyntax: 'module.exports =' },
{ reactNativeVersion: '>=0.72', moduleExportSyntax: 'export default ' },
])(
'without scales - React Native $reactNativeVersion',
async ({ moduleExportSyntax }) => {
const { code } = await compileBundle(
'android',
{
'node_modules/react-native/Libraries/Utilities/PixelRatio.js': `${moduleExportSyntax} { get: () => 1 };`,
'./index.js': "export { default } from './__fixtures__/logo.png';",
},
true
);

const context: { Export?: { default: Record<string, any> } } = {};
vm.runInNewContext(code, context);
const context: { Export?: { default: Record<string, any> } } = {};
vm.runInNewContext(code, context);

const logo = (
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/logo.png')
)
).toString('base64');
expect(context.Export?.default).toEqual({
uri: `data:image/png;base64,${logo}`,
width: 2292,
height: 393,
scale: 1,
});
});
const logo = (
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/logo.png')
)
).toString('base64');
expect(context.Export?.default).toEqual({
uri: `data:image/png;base64,${logo}`,
width: 2292,
height: 393,
scale: 1,
});
}
);

it.each([
{ prefferedScale: 1 },
{ prefferedScale: 2 },
{ prefferedScale: 3 },
])('with scales', async ({ prefferedScale }) => {
const { code } = await compileBundle(
'android',
{
'node_modules/react-native/Libraries/Utilities/PixelRatio.js': `module.exports = { get: () => ${prefferedScale} };`,
'./index.js': "export { default } from './__fixtures__/star.png';",
},
true
);
{
prefferedScale: 1,
reactNativeVersion: '<0.72',
moduleExportSyntax: 'module.exports =',
},
{
prefferedScale: 2,
reactNativeVersion: '<0.72',
moduleExportSyntax: 'module.exports =',
},
{
prefferedScale: 3,
reactNativeVersion: '<0.72',
moduleExportSyntax: 'module.exports =',
},
{
prefferedScale: 1,
reactNativeVersion: '>=0.72',
moduleExportSyntax: 'export default ',
},
{
prefferedScale: 2,
reactNativeVersion: '>=0.72',
moduleExportSyntax: 'export default ',
},
{
prefferedScale: 3,
reactNativeVersion: '>=0.72',
moduleExportSyntax: 'export default ',
},
])(
'with scales ($prefferedScale) - React Native $reactNativeVersion',
async ({ prefferedScale, moduleExportSyntax }) => {
const { code } = await compileBundle(
'android',
{
'node_modules/react-native/Libraries/Utilities/PixelRatio.js': `${moduleExportSyntax} { get: () => ${prefferedScale} };`,
'./index.js': "export { default } from './__fixtures__/star.png';",
},
true
);

const context: { Export?: { default: Record<string, any> } } = {};
vm.runInNewContext(code, context);
const context: { Export?: { default: Record<string, any> } } = {};
vm.runInNewContext(code, context);

const logos = await Promise.all([
(
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/[email protected]')
)
).toString('base64'),
(
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/[email protected]')
)
).toString('base64'),
(
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/[email protected]')
)
).toString('base64'),
]);
const logos = await Promise.all([
(
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/[email protected]')
)
).toString('base64'),
(
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/[email protected]')
)
).toString('base64'),
(
await fs.promises.readFile(
path.join(__dirname, './__fixtures__/[email protected]')
)
).toString('base64'),
]);

expect(context.Export?.default).toEqual({
uri: `data:image/png;base64,${logos[prefferedScale - 1]}`,
width: 286,
height: 272,
scale: prefferedScale,
});
});
expect(context.Export?.default).toEqual({
uri: `data:image/png;base64,${logos[prefferedScale - 1]}`,
width: 286,
height: 272,
scale: prefferedScale,
});
}
);
});

describe('should convert to remote-asset', () => {
Expand Down Expand Up @@ -306,7 +343,7 @@ describe('assetLoader', () => {
{ prefferedScale: 1 },
{ prefferedScale: 2 },
{ prefferedScale: 3 },
])('with scales', async ({ prefferedScale }) => {
])('with scales $prefferedScale', async ({ prefferedScale }) => {
const { code } = await compileBundle(
'ios', // platform doesn't matter for remote-assets
{
Expand Down
10 changes: 8 additions & 2 deletions packages/repack/src/webpack/loaders/assetsLoader/inlineAssets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ export function inlineAssets({

const scales = JSON.stringify(Object.keys(sourceSet).map(Number));

// we need to import PixelRatio to remain compatible
// with older versions of React-Native
/**
* To enable scale resolution in runtime we need to import PixelRatio & AssetSourceResolver
* Although we could use AssetSourceResolver as it is, we need to import PixelRatio to remain
* compatible with older versions of React-Native. Newer versions of React-Native use
* ESM for PixelRatio, so we need to check if PixelRatio is an ESM module and if so, adjust the import.
*/
return dedent`
var PixelRatio = require('react-native/Libraries/Utilities/PixelRatio');
var AssetSourceResolver = require('react-native/Libraries/Image/AssetSourceResolver');
if (PixelRatio.__esModule) PixelRatio = PixelRatio.default;
var prefferedScale = AssetSourceResolver.pickScale(${scales}, PixelRatio.get());
module.exports = ${JSON.stringify(sourceSet)}[prefferedScale];
Expand Down

0 comments on commit 430d6be

Please sign in to comment.