From 88a78a4e5cad78f503d6e6d326daed35d1eed211 Mon Sep 17 00:00:00 2001 From: Jakub Romanczyk Date: Tue, 4 Apr 2023 15:27:49 +0200 Subject: [PATCH] feat(repack): resolve scale automatically for inlined assets --- .changeset/honest-glasses-yell.md | 5 ++++ .../__tests__/assetsLoader.test.ts | 27 ++++++++++++------- .../loaders/assetsLoader/inlineAssets.ts | 23 ++++++++++++---- 3 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 .changeset/honest-glasses-yell.md diff --git a/.changeset/honest-glasses-yell.md b/.changeset/honest-glasses-yell.md new file mode 100644 index 000000000..faf2f432d --- /dev/null +++ b/.changeset/honest-glasses-yell.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack": minor +--- + +Auto device scale resolution for inlined assets diff --git a/packages/repack/src/webpack/loaders/assetsLoader/__tests__/assetsLoader.test.ts b/packages/repack/src/webpack/loaders/assetsLoader/__tests__/assetsLoader.test.ts index fc7a44c66..3993ad2a6 100644 --- a/packages/repack/src/webpack/loaders/assetsLoader/__tests__/assetsLoader.test.ts +++ b/packages/repack/src/webpack/loaders/assetsLoader/__tests__/assetsLoader.test.ts @@ -45,9 +45,13 @@ async function compileBundle( platform, }), new VirtualModulesPlugin({ - ...virtualModules, 'node_modules/react-native/Libraries/Image/AssetRegistry.js': 'module.exports = { registerAsset: (spec) => spec };', + 'node_modules/react-native/Libraries/Utilities/PixelRatio.js': + 'module.exports = { get: () => 1 };', + 'node_modules/react-native/Libraries/Image/AssetSourceResolver.js': + 'module.exports = { pickScale: (scales, pixelRatio) => scales[pixelRatio - 1] };', + ...virtualModules, }), ], }); @@ -205,10 +209,15 @@ describe('assetLoader', () => { }); }); - it('with scales', async () => { + 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 @@ -235,14 +244,12 @@ describe('assetLoader', () => { ).toString('base64'), ]); - expect(context.Export?.default).toEqual( - logos.map((logo, index) => ({ - uri: `data:image/png;base64,${logo}`, - scale: index + 1, - height: 272, - width: 286, - })) - ); + expect(context.Export?.default).toEqual({ + uri: `data:image/png;base64,${logos[prefferedScale - 1]}`, + width: 286, + height: 272, + scale: prefferedScale, + }); }); }); }); diff --git a/packages/repack/src/webpack/loaders/assetsLoader/inlineAssets.ts b/packages/repack/src/webpack/loaders/assetsLoader/inlineAssets.ts index d8ee8f9ee..bcdc5c5eb 100644 --- a/packages/repack/src/webpack/loaders/assetsLoader/inlineAssets.ts +++ b/packages/repack/src/webpack/loaders/assetsLoader/inlineAssets.ts @@ -1,3 +1,4 @@ +import dedent from 'dedent'; import mimeTypes from 'mime-types'; import type { Asset, ImageSize, URISource } from './types'; import { getImageSize } from './utils'; @@ -18,15 +19,27 @@ export function inlineAssets({ if (!mimeType) { throw new Error( - `Cannot inline asset for request ${resourcePath} - unable to detect mime type` + `Cannot inline asset for request ${resourcePath} - unable to detect MIME type` ); } - const sourceSet = assets.map((asset) => encodeAsset(asset, mimeType, size)); + // keys are always converted to strings + const sourceSet = assets.reduce((sources, asset) => { + sources[asset.scale] = encodeAsset(asset, mimeType, size); + return sources; + }, {} as Record); - return `module.exports = ${JSON.stringify( - sourceSet.length === 1 ? sourceSet[0] : sourceSet - )}`; + const scales = JSON.stringify(Object.keys(sourceSet).map(Number)); + + // we need to import PixelRatio to remain compatible + // with older versions of React-Native + return dedent` + var PixelRatio = require('react-native/Libraries/Utilities/PixelRatio'); + var AssetSourceResolver = require('react-native/Libraries/Image/AssetSourceResolver'); + var prefferedScale = AssetSourceResolver.pickScale(${scales}, PixelRatio.get()); + + module.exports = ${JSON.stringify(sourceSet)}[prefferedScale]; + `; } function encodeAsset(