1import { Platform } from 'expo-modules-core'; 2import { PixelRatio } from 'react-native'; 3 4import { PackagerAsset } from './ReactNativeCompatibleAssetsRegistry'; 5 6export type ResolvedAssetSource = { 7 __packager_asset: boolean; 8 width?: number; 9 height?: number; 10 uri: string; 11 scale: number; 12}; 13 14// Returns the Metro dev server-specific asset location. 15function getScaledAssetPath(asset): string { 16 const scale = AssetSourceResolver.pickScale(asset.scales, PixelRatio.get()); 17 const scaleSuffix = scale === 1 ? '' : '@' + scale + 'x'; 18 const type = !asset.type ? '' : `.${asset.type}`; 19 return asset.httpServerLocation + '/' + asset.name + scaleSuffix + type; 20} 21 22export default class AssetSourceResolver { 23 serverUrl: string; 24 // where the jsbundle is being run from 25 // NOTE(EvanBacon): Never defined on web. 26 jsbundleUrl?: string | null; 27 // the asset to resolve 28 asset: PackagerAsset; 29 30 constructor( 31 serverUrl: string | undefined | null, 32 jsbundleUrl: string | undefined | null, 33 asset: PackagerAsset 34 ) { 35 if (!serverUrl) { 36 throw new Error('Web assets require a server URL'); 37 } 38 39 this.serverUrl = serverUrl; 40 this.jsbundleUrl = null; 41 this.asset = asset; 42 } 43 44 // Always true for web runtimes 45 isLoadedFromServer(): boolean { 46 return true; 47 } 48 49 // Always false for web runtimes 50 isLoadedFromFileSystem(): boolean { 51 return false; 52 } 53 54 defaultAsset(): ResolvedAssetSource { 55 return this.assetServerURL(); 56 } 57 58 /** 59 * @returns absolute remote URL for the hosted asset. 60 */ 61 assetServerURL(): ResolvedAssetSource { 62 const fromUrl = new URL(getScaledAssetPath(this.asset), this.serverUrl); 63 fromUrl.searchParams.set('platform', Platform.OS); 64 fromUrl.searchParams.set('hash', this.asset.hash); 65 return this.fromSource(fromUrl.toString()); 66 } 67 68 fromSource(source: string): ResolvedAssetSource { 69 return { 70 __packager_asset: true, 71 width: this.asset.width ?? undefined, 72 height: this.asset.height ?? undefined, 73 uri: source, 74 scale: AssetSourceResolver.pickScale(this.asset.scales, PixelRatio.get()), 75 }; 76 } 77 78 static pickScale(scales: number[], deviceScale: number): number { 79 for (let i = 0; i < scales.length; i++) { 80 if (scales[i] >= deviceScale) { 81 return scales[i]; 82 } 83 } 84 return scales[scales.length - 1] || 1; 85 } 86} 87