UNPKG

expo-asset

Version:

An Expo universal module to download assets and pass them into other APIs

95 lines (81 loc) 3.57 kB
import type { PackagerAsset } from '@react-native/assets-registry/registry'; import { Platform } from 'expo-modules-core'; import { PixelRatio, NativeModules } from 'react-native'; import AssetSourceResolver from './AssetSourceResolver'; import { getManifest2, manifestBaseUrl } from './PlatformUtils'; // @docsMissing export type AssetMetadata = Pick< PackagerAsset, 'httpServerLocation' | 'name' | 'hash' | 'type' | 'scales' | 'width' | 'height' > & { uri?: string; fileHashes?: string[]; fileUris?: string[]; }; export type AssetSource = { uri: string; hash: string; }; /** * Selects the best file for the given asset (ex: choosing the best scale for images) and returns * a { uri, hash } pair for the specific asset file. * * If the asset isn't an image with multiple scales, the first file is selected. */ export function selectAssetSource(meta: AssetMetadata): AssetSource { // This logic is based on that of AssetSourceResolver, with additional support for file hashes and // explicitly provided URIs const scale = AssetSourceResolver.pickScale(meta.scales, PixelRatio.get()); const index = meta.scales.findIndex((s) => s === scale); const hash = meta.fileHashes ? (meta.fileHashes[index] ?? meta.fileHashes[0]) : meta.hash; // Allow asset processors to directly provide the URL to load const uri = meta.fileUris ? (meta.fileUris[index] ?? meta.fileUris[0]) : meta.uri; if (uri) { return { uri: resolveUri(uri), hash }; } const fileScale = scale === 1 ? '' : `@${scale}x`; const fileExtension = meta.type ? `.${encodeURIComponent(meta.type)}` : ''; const suffix = `/${encodeURIComponent(meta.name)}${fileScale}${fileExtension}`; const params = new URLSearchParams({ platform: Platform.OS, hash: meta.hash, }); // For assets with a specified absolute URL, we use the existing origin instead of prepending the // development server or production CDN URL origin if (/^https?:\/\//.test(meta.httpServerLocation)) { const uri = meta.httpServerLocation + suffix + '?' + params; return { uri, hash }; } // For assets during development using manifest2, we use the development server's URL origin const manifest2 = getManifest2(); const devServerUrl = manifest2?.extra?.expoGo?.developer ? 'http://' + manifest2.extra.expoGo.debuggerHost : null; if (devServerUrl) { const baseUrl = new URL(meta.httpServerLocation + suffix, devServerUrl); baseUrl.searchParams.set('platform', Platform.OS); baseUrl.searchParams.set('hash', meta.hash); return { uri: baseUrl.href, hash, }; } // Temporary fallback for loading assets in Expo Go home if (NativeModules['ExponentKernel']) { return { uri: `https://classic-assets.eascdn.net/~assets/${encodeURIComponent(hash)}`, hash }; } // In correctly configured apps, we arrive here if the asset is locally available on disk due to // being managed by expo-updates, and `getLocalAssetUri(hash)` must return a local URI for this // hash. Since the asset is local, we don't have a remote URL and specify an invalid URL (an empty // string) as a placeholder. return { uri: '', hash }; } /** * Resolves the given URI to an absolute URI. If the given URI is already an absolute URI, it is * simply returned. Otherwise, if it is a relative URI, it is resolved relative to the manifest's * base URI. */ export function resolveUri(uri: string): string { // `manifestBaseUrl` is always an absolute URL or `null`. return manifestBaseUrl ? new URL(uri, manifestBaseUrl).href : uri; }