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