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