* Copyright (c) 2024 Huawei Technologies Co., Ltd.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import type { AssetData } from 'metro';
import { getBasePath } from '@react-native/assets-registry/path-support';
import Dimensions from '../Utilities/Dimensions';
import Platform from '../Utilities/Platform';
const ALLOWED_SCALES: number[] = [1, 2, 3, 4];
function filterAssetScales(
scales: readonly number[],
): readonly number[] {
const result = scales.filter(scale => ALLOWED_SCALES.includes(scale));
if (result.length === 0 && scales.length > 0) {
const maxScale = ALLOWED_SCALES[ALLOWED_SCALES.length - 1];
for (const scale of scales) {
if (scale > maxScale) {
result.push(scale);
break;
}
}
if (result.length === 0) {
result.push(scales[scales.length - 1]);
}
}
return result;
}
function pickScale(scales: Array<number>, deviceScale?: number): number {
const validScales = filterAssetScales(scales);
if (deviceScale == null) {
deviceScale = Dimensions.get('window').scale;
}
for (let i = 0; i < validScales.length; i++) {
if (validScales[i] >= deviceScale) {
return validScales[i];
}
}
return validScales[validScales.length - 1] || 1;
}
function getScaledAssetPath(asset: AssetData): string {
const scale = pickScale(asset.scales);
const scaleSuffix = scale === 1 ? '' : '@' + scale + 'x';
const assetDir = getBasePath(asset);
return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type;
}
type ResolvedAssetSource = {
readonly __packager_asset: boolean;
readonly width?: number;
readonly height?: number;
readonly uri: string;
readonly scale: number;
};
class AssetSourceResolver {
constructor(
private serverUrl: string | undefined,
private jsbundleUrl: string | undefined,
private asset: AssetData
) {}
isLoadedFromServer(): boolean {
return !!this.serverUrl;
}
public defaultAsset(): ResolvedAssetSource {
if (this.isLoadedFromServer()) {
return this.assetServerURL();
}
const path = 'asset://';
return this.fromSource(
path +
getScaledAssetPath(this.asset)
.replace(/^assets\//, '')
.replace(/\.\.\//g, '_')
);
}
* Returns an absolute URL which can be used to fetch the asset
* from the devserver
*/
assetServerURL(): ResolvedAssetSource {
if (!this.serverUrl) {
throw new Error('need server to load from');
}
return this.fromSource(
this.serverUrl +
getScaledAssetPath(this.asset) +
'?platform=' +
Platform.OS +
'&hash=' +
this.asset.hash
);
}
fromSource(source: string): ResolvedAssetSource {
return {
__packager_asset: true,
width: this.asset.width,
height: this.asset.height,
uri: source,
scale: pickScale(this.asset.scales),
};
}
}
export default AssetSourceResolver;