Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(metro-service): consume @react-native/assets-registry #2689

Merged
merged 3 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`
1 change: 1 addition & 0 deletions packages/metro-service/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
src/assets-registry/
39 changes: 39 additions & 0 deletions packages/metro-service/copy-assets-registry.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// @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",
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
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;
94 changes: 94 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,94 @@
// THIS FILE WAS GENERATED BY 'copy-assets-registry.mjs'
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
/**
* 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;
29 changes: 29 additions & 0 deletions packages/metro-service/src/assets-registry/registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// THIS FILE WAS GENERATED BY 'copy-assets-registry.mjs'
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-nocheck
/**
* 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