Skip to content

Commit

Permalink
fix(metro-service): consume @react-native/assets-registry
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 committed Sep 19, 2023
1 parent 628ba00 commit 4294868
Show file tree
Hide file tree
Showing 14 changed files with 256 additions and 122 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-cheetahs-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rnx-kit/metro-service": patch
---

Reuse code from `@react-native/assets-registry`
40 changes: 40 additions & 0 deletions packages/metro-service/copy-assets-registry.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// @ts-check
import flowRemoveTypes from "flow-remove-types";
import * as fs from "node:fs";
import { createRequire } from "node:module";
import * as path from "node:path";

function main() {
const require = createRequire(import.meta.url);

const flowOptions = { all: true, pretty: true };
/** @type {{ encoding: "utf-8" }} */
const fsOptions = { encoding: "utf-8" };
/** @type {fs.NoParamCallback} */
const rethrow = (err) => {
if (err) {
throw err;
}
};

const assetsRegistry = path.dirname(
require.resolve("@react-native/assets-registry/package.json")
);

for (const filename of ["path-support.js", "registry.js"]) {
const p = path.join(assetsRegistry, filename);
fs.readFile(p, fsOptions, (err, source) => {
rethrow(err);
const t = [
`// THIS FILE WAS GENERATED BY '${path.basename(import.meta.url)}'`,
"/* eslint-disable @typescript-eslint/ban-ts-comment */",
"// @ts-nocheck",
"// prettier-ignore",
flowRemoveTypes(source, flowOptions).toString(),
].join("\n");
fs.writeFile(path.join("src", "assets-registry", filename), t, rethrow);
});
}
}

main();
3 changes: 3 additions & 0 deletions packages/metro-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"directory": "packages/metro-service"
},
"scripts": {
"prepare": "node copy-assets-registry.mjs",
"build": "rnx-kit-scripts build",
"format": "rnx-kit-scripts format",
"lint": "rnx-kit-scripts lint",
Expand Down Expand Up @@ -53,10 +54,12 @@
},
"devDependencies": {
"@react-native-community/cli-types": "^11.0.0",
"@react-native/assets-registry": "^0.73.0",
"@rnx-kit/scripts": "*",
"@types/node": "^18.0.0",
"@types/node-fetch": "^2.6.5",
"eslint": "^8.0.0",
"flow-remove-types": "~2.206.0",
"jest": "^29.2.1",
"metro": "^0.76.5",
"metro-config": "^0.76.5",
Expand Down
47 changes: 5 additions & 42 deletions packages/metro-service/src/asset/android.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,18 @@
// https://github.com/react-native-community/cli/blob/716555851b442a83a1bf5e0db27b6226318c9a69/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathAndroid.ts

import * as path from "path";
import { getResourceIdentifier } from "./assetPathUtils";
import {
getAndroidResourceFolderName,
getAndroidResourceIdentifier,
} from "../assets-registry/path-support";
import type { PackagerAsset, SaveAssetsPlugin } from "./types";

export function getAndroidAssetSuffix(scale: number): string {
const tolerance = 0.01;
const scaleApprox = scale + tolerance;
if (scaleApprox >= 4) {
return "xxxhdpi";
} else if (scaleApprox >= 3) {
return "xxhdpi";
} else if (scaleApprox >= 2) {
return "xhdpi";
} else if (scaleApprox >= 1.5) {
return "hdpi";
} else if (scaleApprox >= 1) {
return "mdpi";
} else {
return "ldpi";
}
}

// See https://developer.android.com/guide/topics/resources/drawable-resource.html
const drawableFileTypes = new Set<string>([
"gif",
"jpeg",
"jpg",
"png",
"webp",
"xml",
]);

function getAndroidResourceFolderName(
asset: PackagerAsset,
scale: number
): string {
if (!drawableFileTypes.has(asset.type)) {
return "raw";
}
const suffix = getAndroidAssetSuffix(scale);
const androidFolder = `drawable-${suffix}`;
return androidFolder;
}

export function getAssetDestPathAndroid(
asset: PackagerAsset,
scale: number
): string {
const androidFolder = getAndroidResourceFolderName(asset, scale);
const fileName = getResourceIdentifier(asset);
const fileName = getAndroidResourceIdentifier(asset);
return path.join(androidFolder, `${fileName}.${asset.type}`);
}

Expand Down
20 changes: 0 additions & 20 deletions packages/metro-service/src/asset/assetPathUtils.ts

This file was deleted.

7 changes: 3 additions & 4 deletions packages/metro-service/src/asset/ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@

import { error, info } from "@rnx-kit/console";
import fs from "fs";
import type { AssetData } from "metro";
import path from "path";
import { getResourceIdentifier } from "./assetPathUtils";
import { getAndroidResourceIdentifier } from "../assets-registry/path-support";
import { getAssetDestPath } from "./default";
import { filterPlatformAssetScales } from "./filter";
import type { SaveAssetsPlugin } from "./types";
import type { AssetData, SaveAssetsPlugin } from "./types";

type ImageSet = {
basePath: string;
Expand All @@ -29,7 +28,7 @@ export function getImageSet(
asset: AssetData,
scales: readonly number[]
): ImageSet {
const fileName = getResourceIdentifier(asset);
const fileName = getAndroidResourceIdentifier(asset);
return {
basePath: path.join(catalogDir, `${fileName}.imageset`),
files: scales.map((scale, idx) => {
Expand Down
11 changes: 4 additions & 7 deletions packages/metro-service/src/asset/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import type { AssetData } from "metro";

export type PackagerAsset = {
httpServerLocation: string;
name: string;
type: string;
};
import type { PackagerAsset } from "../assets-registry/registry";

export type SaveAssetsPlugin = (
assets: ReadonlyArray<AssetData>,
assets: readonly AssetData[],
platform: string,
assetsDest: string | undefined,
assetCatalogDest: string | undefined,
Expand All @@ -17,3 +12,5 @@ export type SaveAssetsPlugin = (
getAssetDestPath: (asset: AssetData, scale: number) => string
) => void
) => void;

export { AssetData, PackagerAsset };
5 changes: 2 additions & 3 deletions packages/metro-service/src/asset/write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

import { info, warn } from "@rnx-kit/console";
import * as fs from "fs";
import type { AssetData } from "metro";
import * as path from "path";
import { filterPlatformAssetScales } from "./filter";
import type { SaveAssetsPlugin } from "./types";
import type { AssetData, SaveAssetsPlugin } from "./types";

function copy(
src: string,
Expand Down Expand Up @@ -52,7 +51,7 @@ function copyAll(filesToCopy: Record<string, string>) {
}

export function saveAssets(
assets: ReadonlyArray<AssetData>,
assets: readonly AssetData[],
platform: string,
assetsDest: string | undefined,
assetCatalogDest: string | undefined,
Expand Down
9 changes: 9 additions & 0 deletions packages/metro-service/src/assets-registry/path-support.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { PackagerAsset } from "./registry";
export declare function getAndroidResourceFolderName(
asset: PackagerAsset,
scale: number
): string | "raw";
export declare function getAndroidResourceIdentifier(
asset: PackagerAsset
): string;
export declare function getBasePath(asset: PackagerAsset): string;
95 changes: 95 additions & 0 deletions packages/metro-service/src/assets-registry/path-support.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// THIS FILE WAS GENERATED BY 'copy-assets-registry.mjs'
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
// prettier-ignore
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* strict
*/

'use strict';


const androidScaleSuffix = {
'0.75': 'ldpi',
'1': 'mdpi',
'1.5': 'hdpi',
'2': 'xhdpi',
'3': 'xxhdpi',
'4': 'xxxhdpi',
};

const ANDROID_BASE_DENSITY = 160;

/**
* FIXME: using number to represent discrete scale numbers is fragile in essence because of
* floating point numbers imprecision.
*/
function getAndroidAssetSuffix(scale) {
if (scale.toString() in androidScaleSuffix) {
return androidScaleSuffix[scale.toString()];
}
// NOTE: Android Gradle Plugin does not fully support the nnndpi format.
// See https://issuetracker.google.com/issues/72884435
if (Number.isFinite(scale) && scale > 0) {
return Math.round(scale * ANDROID_BASE_DENSITY) + 'dpi';
}
throw new Error('no such scale ' + scale.toString());
}

// See https://developer.android.com/guide/topics/resources/drawable-resource.html
const drawableFileTypes = new Set([
'gif',
'jpeg',
'jpg',
'ktx',
'png',
'svg',
'webp',
'xml',
]);

function getAndroidResourceFolderName(
asset,
scale,
) {
if (!drawableFileTypes.has(asset.type)) {
return 'raw';
}
const suffix = getAndroidAssetSuffix(scale);
if (!suffix) {
throw new Error(
"Don't know which android drawable suffix to use for scale: " +
scale +
'\nAsset: ' +
JSON.stringify(asset, null, '\t') +
'\nPossible scales are:' +
JSON.stringify(androidScaleSuffix, null, '\t'),
);
}
return 'drawable-' + suffix;
}

function getAndroidResourceIdentifier(asset) {
return (getBasePath(asset) + '/' + asset.name)
.toLowerCase()
.replace(/\//g, '_') // Encode folder structure in file name
.replace(/([^a-z0-9_])/g, '') // Remove illegal chars
.replace(/^assets_/, ''); // Remove "assets_" prefix
}

function getBasePath(asset) {
const basePath = asset.httpServerLocation;
return basePath.startsWith('/') ? basePath.substr(1) : basePath;
}

module.exports = {
getAndroidResourceFolderName,
getAndroidResourceIdentifier,
getBasePath,
};
13 changes: 13 additions & 0 deletions packages/metro-service/src/assets-registry/registry.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type PackagerAsset = {
readonly __packager_asset: boolean;
readonly fileSystemLocation: string;
readonly httpServerLocation: string;
readonly width?: null | undefined | number;
readonly height?: null | undefined | number;
readonly scales: number[];
readonly hash: string;
readonly name: string;
readonly type: string;
};
export declare function registerAsset(asset: PackagerAsset): number;
export declare function getAssetByID(assetId: number): PackagerAsset;
30 changes: 30 additions & 0 deletions packages/metro-service/src/assets-registry/registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// THIS FILE WAS GENERATED BY 'copy-assets-registry.mjs'
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
// prettier-ignore
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* strict
* @format
*/

'use strict';


const assets = [];

function registerAsset(asset) {
// `push` returns new array length, so the first asset will
// get id 1 (not 0) to make the value truthy
return assets.push(asset);
}

function getAssetByID(assetId) {
return assets[assetId - 1];
}

module.exports = {registerAsset, getAssetByID};
Loading

0 comments on commit 4294868

Please sign in to comment.