From ea45310ee423098423e04351c7891d8069a514c1 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Tue, 8 Aug 2023 11:01:42 -0700 Subject: [PATCH 1/7] Refactor saveAssets code to allow out of tree overrides --- packages/metro-service/package.json | 1 + packages/metro-service/src/asset/android.ts | 17 +++++ packages/metro-service/src/asset/default.ts | 41 ++++++++++ packages/metro-service/src/asset/filter.ts | 7 +- packages/metro-service/src/asset/ios.ts | 61 +++++++++++---- packages/metro-service/src/asset/write.ts | 84 +++++++++------------ packages/metro-service/src/bundle.ts | 37 ++++++++- 7 files changed, 180 insertions(+), 68 deletions(-) create mode 100644 packages/metro-service/src/asset/default.ts diff --git a/packages/metro-service/package.json b/packages/metro-service/package.json index 3734146e3..9bfb75e59 100644 --- a/packages/metro-service/package.json +++ b/packages/metro-service/package.json @@ -64,6 +64,7 @@ }, "depcheck": { "ignoreMatches": [ + "@office-iss/react-native-win32", "metro-babel-transformer" ] }, diff --git a/packages/metro-service/src/asset/android.ts b/packages/metro-service/src/asset/android.ts index d928d27ca..db4919fbb 100644 --- a/packages/metro-service/src/asset/android.ts +++ b/packages/metro-service/src/asset/android.ts @@ -3,6 +3,7 @@ import * as path from "path"; import { getResourceIdentifier } from "./assetPathUtils"; import type { PackagerAsset } from "./types"; +import type {AssetData} from 'metro'; export function getAndroidAssetSuffix(scale: number): string { const tolerance = 0.01; @@ -52,3 +53,19 @@ export function getAssetDestPathAndroid( const fileName = getResourceIdentifier(asset); return path.join(androidFolder, `${fileName}.${asset.type}`); } + +export function saveAssetsAndroid( + assets: ReadonlyArray, + _platform: string, + _assetsDest: string | undefined, + _assetCatalogDest: string | undefined, + addAssetToCopy: ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string, + ) => void, +) { + assets.forEach((asset) => + addAssetToCopy(asset, undefined, getAssetDestPathAndroid), + ); +} \ No newline at end of file diff --git a/packages/metro-service/src/asset/default.ts b/packages/metro-service/src/asset/default.ts new file mode 100644 index 000000000..a09a7a139 --- /dev/null +++ b/packages/metro-service/src/asset/default.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +import path from "path"; +import type {AssetData} from 'metro'; +import type { PackagerAsset } from "./types"; + +export function getAssetDestPath( + asset: PackagerAsset, + scale: number +): string { + const suffix = scale === 1 ? "" : `@${scale}x`; + const fileName = `${asset.name + suffix}.${asset.type}`; + return path.join( + // Assets can have relative paths outside of the project root. + // Replace `../` with `_` to make sure they don't end up outside of + // the expected assets directory. + asset.httpServerLocation.substr(1).replace(/\.\.\//g, "_"), + fileName + ); +} + +export function saveAssetsDefault( + assets: ReadonlyArray, + _platform: string, + _assetsDest: string | undefined, + _assetCatalogDest: string | undefined, + addAssetToCopy: ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string, + ) => void, +) { + assets.forEach((asset) => addAssetToCopy(asset, undefined, getAssetDestPath)); +} + diff --git a/packages/metro-service/src/asset/filter.ts b/packages/metro-service/src/asset/filter.ts index fa55a7441..029b949f5 100644 --- a/packages/metro-service/src/asset/filter.ts +++ b/packages/metro-service/src/asset/filter.ts @@ -1,14 +1,9 @@ // https://github.com/react-native-community/cli/blob/716555851b442a83a1bf5e0db27b6226318c9a69/packages/cli-plugin-metro/src/commands/bundle/filterPlatformAssetScales.ts -const ALLOWED_SCALES: { [key: string]: number[] } = { - ios: [1, 2, 3], -}; - export function filterPlatformAssetScales( - platform: string, + allowlist: ReadonlyArray | undefined, scales: readonly number[] ): readonly number[] { - const allowlist: number[] = ALLOWED_SCALES[platform]; if (!allowlist) { return scales; } diff --git a/packages/metro-service/src/asset/ios.ts b/packages/metro-service/src/asset/ios.ts index 80594ba06..b7bde368e 100644 --- a/packages/metro-service/src/asset/ios.ts +++ b/packages/metro-service/src/asset/ios.ts @@ -3,9 +3,11 @@ import fs from "fs"; import type { AssetData } from "metro"; +import { error, info } from "@rnx-kit/console"; import path from "path"; import { getResourceIdentifier } from "./assetPathUtils"; -import type { PackagerAsset } from "./types"; +import { filterPlatformAssetScales } from './filter'; +import { getAssetDestPath } from './default'; type ImageSet = { basePath: string; @@ -68,17 +70,48 @@ export function writeImageSet(imageSet: ImageSet): void { ); } -export function getAssetDestPathIOS( - asset: PackagerAsset, - scale: number -): string { - const suffix = scale === 1 ? "" : `@${scale}x`; - const fileName = `${asset.name + suffix}.${asset.type}`; - return path.join( - // Assets can have relative paths outside of the project root. - // Replace `../` with `_` to make sure they don't end up outside of - // the expected assets directory. - asset.httpServerLocation.substring(1).replace(/\.\.\//g, "_"), - fileName - ); +const ALLOWED_SCALES = [1, 2, 3]; + +export function saveAssetsIOS( + assets: ReadonlyArray, + _platform: string, + _assetsDest: string | undefined, + assetCatalogDest: string | undefined, + addAssetToCopy: ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string, + ) => void, +) { + if (assetCatalogDest != null) { + // Use iOS Asset Catalog for images. This will allow Apple app thinning to + // remove unused scales from the optimized bundle. + const catalogDir = path.join(assetCatalogDest, 'RNAssets.xcassets'); + if (!fs.existsSync(catalogDir)) { + error( + `Could not find asset catalog 'RNAssets.xcassets' in ${assetCatalogDest}. Make sure to create it if it does not exist.`, + ); + return; + } + + info('Adding images to asset catalog', catalogDir); + cleanAssetCatalog(catalogDir); + for (const asset of assets) { + if (isCatalogAsset(asset)) { + const imageSet = getImageSet( + catalogDir, + asset, + filterPlatformAssetScales(ALLOWED_SCALES, asset.scales), + ); + writeImageSet(imageSet); + } else { + addAssetToCopy(asset, ALLOWED_SCALES, getAssetDestPath); + } + } + info('Done adding images to asset catalog'); + } else { + assets.forEach((asset) => + addAssetToCopy(asset, ALLOWED_SCALES, getAssetDestPath), + ); + } } diff --git a/packages/metro-service/src/asset/write.ts b/packages/metro-service/src/asset/write.ts index ab61381f3..ecd023b9e 100644 --- a/packages/metro-service/src/asset/write.ts +++ b/packages/metro-service/src/asset/write.ts @@ -1,18 +1,10 @@ // https://github.com/react-native-community/cli/blob/716555851b442a83a1bf5e0db27b6226318c9a69/packages/cli-plugin-metro/src/commands/bundle/saveAssets.ts -import { error, info, warn } from "@rnx-kit/console"; +import { info, warn } from "@rnx-kit/console"; import * as fs from "fs"; import type { AssetData } from "metro"; import * as path from "path"; -import { getAssetDestPathAndroid } from "./android"; import { filterPlatformAssetScales } from "./filter"; -import { - cleanAssetCatalog, - getAssetDestPathIOS, - getImageSet, - isCatalogAsset, - writeImageSet, -} from "./ios"; function copy( src: string, @@ -58,11 +50,22 @@ function copyAll(filesToCopy: Record) { }); } -export function saveAssets( +export async function saveAssets( assets: ReadonlyArray, platform: string, assetsDest: string | undefined, - assetCatalogDest: string | undefined + assetCatalogDest: string | undefined, + saveAssetsPlugin: ( + assets: ReadonlyArray, + platform: string, + assetsDest: string | undefined, + assetCatalogDest: string | undefined, + addAssetToCopy: ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string, + ) => void, + ) => void, ): Promise { if (!assetsDest) { warn("Assets destination folder is not set, skipping..."); @@ -71,13 +74,23 @@ export function saveAssets( const filesToCopy: Record = Object.create(null); // Map src -> dest - const getAssetDestPath = - platform === "android" ? getAssetDestPathAndroid : getAssetDestPathIOS; + const addAssetToCopy = ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string, + ) => { + const validScales = new Set( + filterPlatformAssetScales(allowedScales, asset.scales), + ); - const addAssetToCopy = (asset: AssetData) => { - const validScales = new Set( - filterPlatformAssetScales(platform, asset.scales) - ); + asset.scales.forEach((scale: number, idx: number) => { + if (!validScales.has(scale)) { + return; + } + const src = asset.files[idx]; + const dest = path.join(assetsDest, getAssetDestPath(asset, scale)); + filesToCopy[src] = dest; + }); asset.scales.forEach((scale, idx) => { if (!validScales.has(scale)) { @@ -89,35 +102,12 @@ export function saveAssets( }); }; - if (platform === "ios" && assetCatalogDest != null) { - // Use iOS Asset Catalog for images. This will allow Apple app thinning to - // remove unused scales from the optimized bundle. - const catalogDir = path.join(assetCatalogDest, "RNAssets.xcassets"); - if (!fs.existsSync(catalogDir)) { - error( - `Could not find asset catalog 'RNAssets.xcassets' in ${assetCatalogDest}. Make sure to create it if it does not exist.` - ); - return Promise.reject(); - } - - info("Adding images to asset catalog", catalogDir); - cleanAssetCatalog(catalogDir); - for (const asset of assets) { - if (isCatalogAsset(asset)) { - const imageSet = getImageSet( - catalogDir, - asset, - filterPlatformAssetScales(platform, asset.scales) - ); - writeImageSet(imageSet); - } else { - addAssetToCopy(asset); - } - } - info("Done adding images to asset catalog"); - } else { - assets.forEach(addAssetToCopy); - } - + saveAssetsPlugin( + assets, + platform, + assetsDest, + assetCatalogDest, + addAssetToCopy, + ); return copyAll(filesToCopy); } diff --git a/packages/metro-service/src/bundle.ts b/packages/metro-service/src/bundle.ts index dc814b834..d45674f19 100644 --- a/packages/metro-service/src/bundle.ts +++ b/packages/metro-service/src/bundle.ts @@ -5,10 +5,14 @@ import chalk from "chalk"; import fs from "fs"; import type { ConfigT } from "metro-config"; import type { BundleOptions, OutputOptions } from "metro/shared/types"; +import type {AssetData} from 'metro'; import Server from "metro/src/Server"; import Bundle from "metro/src/shared/output/bundle"; import path from "path"; import { saveAssets } from "./asset"; +import { saveAssetsAndroid } from './asset/android'; +import { saveAssetsDefault } from './asset/default'; +import { saveAssetsIOS } from './asset/ios'; import { ensureBabelConfig } from "./babel"; export type BundleArgs = { @@ -41,6 +45,34 @@ type RequestOptions = { unstable_transformProfile?: BundleOptions["unstable_transformProfile"]; }; +type SaveAssetsPlugin = ( + assets: ReadonlyArray, + platform: string, + assetsDest: string | undefined, + assetCatalogDest: string | undefined, + addAssetToCopy: ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string, + ) => void, +) => void; + + // Eventually this will be part of the rn config, but we require it on older rn versions for win32 and the cli doesn't allow extra config properties. + // See https://github.com/react-native-community/cli/pull/2002 +function getSaveAssetsPlugin(platform: string, projectRoot: string): SaveAssetsPlugin { + try { + if (platform === 'win32') { + const saveAssetsPlugin = require.resolve('@office-iss/react-native-win32/saveAssetPlugin', {paths: [projectRoot]}); + return require(saveAssetsPlugin); + } + } catch (_) { /* empty */ } + return platform === 'ios' + ? saveAssetsIOS + : platform === 'android' + ? saveAssetsAndroid + : saveAssetsDefault; +} + export async function bundle( args: BundleArgs, config: ConfigT, @@ -49,6 +81,8 @@ export async function bundle( // ensure Metro can find Babel config ensureBabelConfig(config); + const saveAssetsPlugin = getSaveAssetsPlugin(args.platform, config.projectRoot); + if (config.resolver.platforms.indexOf(args.platform) === -1) { error( `Invalid platform ${ @@ -107,7 +141,8 @@ export async function bundle( outputAssets, args.platform, args.assetsDest, - args.assetCatalogDest + args.assetCatalogDest, + saveAssetsPlugin ); } finally { server.end(); From 76b85a279639c295204f5c1f7832932f267ecdc9 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Tue, 8 Aug 2023 11:02:06 -0700 Subject: [PATCH 2/7] docs(changeset): Refactor saveAssets code to allow out of tree overrides --- .changeset/metal-ladybugs-wave.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/metal-ladybugs-wave.md diff --git a/.changeset/metal-ladybugs-wave.md b/.changeset/metal-ladybugs-wave.md new file mode 100644 index 000000000..5ba6d046d --- /dev/null +++ b/.changeset/metal-ladybugs-wave.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/metro-service": minor +--- + +Refactor saveAssets code to allow out of tree overrides From 94552649039f3925ed2b9561f3769f2e6f9d048c Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Tue, 8 Aug 2023 11:56:15 -0700 Subject: [PATCH 3/7] update tests --- .../test/asset/{ios.test.ts => default.test.ts} | 12 ++++++------ packages/metro-service/test/asset/filter.test.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) rename packages/metro-service/test/asset/{ios.test.ts => default.test.ts} (72%) diff --git a/packages/metro-service/test/asset/ios.test.ts b/packages/metro-service/test/asset/default.test.ts similarity index 72% rename from packages/metro-service/test/asset/ios.test.ts rename to packages/metro-service/test/asset/default.test.ts index 79da1a0a9..4b9a58803 100644 --- a/packages/metro-service/test/asset/ios.test.ts +++ b/packages/metro-service/test/asset/default.test.ts @@ -1,7 +1,7 @@ import * as path from "path"; -import { getAssetDestPathIOS } from "../../src/asset/ios"; +import { getAssetDestPath } from "../../src/asset/default"; -describe("getAssetDestPathIOS", () => { +describe("getAssetDestPath", () => { test("should build correct path", () => { const asset = { name: "icon", @@ -9,7 +9,7 @@ describe("getAssetDestPathIOS", () => { httpServerLocation: "/assets/test", }; - expect(getAssetDestPathIOS(asset, 1)).toBe( + expect(getAssetDestPath(asset, 1)).toBe( path.normalize("assets/test/icon.png") ); }); @@ -21,10 +21,10 @@ describe("getAssetDestPathIOS", () => { httpServerLocation: "/assets/test", }; - expect(getAssetDestPathIOS(asset, 2)).toBe( + expect(getAssetDestPath(asset, 2)).toBe( path.normalize("assets/test/icon@2x.png") ); - expect(getAssetDestPathIOS(asset, 3)).toBe( + expect(getAssetDestPath(asset, 3)).toBe( path.normalize("assets/test/icon@3x.png") ); }); @@ -36,7 +36,7 @@ describe("getAssetDestPathIOS", () => { httpServerLocation: "/assets/../../test", }; - expect(getAssetDestPathIOS(asset, 1)).toBe( + expect(getAssetDestPath(asset, 1)).toBe( path.normalize("assets/__test/icon.png") ); }); diff --git a/packages/metro-service/test/asset/filter.test.ts b/packages/metro-service/test/asset/filter.test.ts index 9e182b1d9..88e546208 100644 --- a/packages/metro-service/test/asset/filter.test.ts +++ b/packages/metro-service/test/asset/filter.test.ts @@ -2,21 +2,21 @@ import { filterPlatformAssetScales } from "../../src/asset/filter"; describe("filterPlatformAssetScales", () => { test("removes everything but 2x and 3x for iOS", () => { - expect(filterPlatformAssetScales("ios", [1, 1.5, 2, 3, 4])).toEqual([ + expect(filterPlatformAssetScales([1, 2, 3], [1, 1.5, 2, 3, 4])).toEqual([ 1, 2, 3, ]); - expect(filterPlatformAssetScales("ios", [3, 4])).toEqual([3]); + expect(filterPlatformAssetScales([1, 2, 3], [3, 4])).toEqual([3]); }); test("keeps closest largest one if nothing matches", () => { - expect(filterPlatformAssetScales("ios", [0.5, 4, 100])).toEqual([4]); - expect(filterPlatformAssetScales("ios", [0.5, 100])).toEqual([100]); - expect(filterPlatformAssetScales("ios", [0.5])).toEqual([0.5]); - expect(filterPlatformAssetScales("ios", [])).toEqual([]); + expect(filterPlatformAssetScales([1, 2, 3], [0.5, 4, 100])).toEqual([4]); + expect(filterPlatformAssetScales([1, 2, 3], [0.5, 100])).toEqual([100]); + expect(filterPlatformAssetScales([1, 2, 3], [0.5])).toEqual([0.5]); + expect(filterPlatformAssetScales([1, 2, 3], [])).toEqual([]); }); test("keeps all scales for unknown platform", () => { - expect(filterPlatformAssetScales("freebsd", [1, 1.5, 2, 3.7])).toEqual([ + expect(filterPlatformAssetScales(undefined, [1, 1.5, 2, 3.7])).toEqual([ 1, 1.5, 2, 3.7, ]); }); From 9046448c65dc264143fbe05625330a7683cb4429 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Tue, 8 Aug 2023 12:12:35 -0700 Subject: [PATCH 4/7] format --- packages/metro-service/src/asset/android.ts | 10 ++--- packages/metro-service/src/asset/default.ts | 12 ++---- packages/metro-service/src/asset/ios.ts | 20 ++++----- packages/metro-service/src/asset/write.ts | 40 +++++++++--------- packages/metro-service/src/bundle.ts | 47 +++++++++++++-------- 5 files changed, 68 insertions(+), 61 deletions(-) diff --git a/packages/metro-service/src/asset/android.ts b/packages/metro-service/src/asset/android.ts index db4919fbb..c3f7cfc51 100644 --- a/packages/metro-service/src/asset/android.ts +++ b/packages/metro-service/src/asset/android.ts @@ -3,7 +3,7 @@ import * as path from "path"; import { getResourceIdentifier } from "./assetPathUtils"; import type { PackagerAsset } from "./types"; -import type {AssetData} from 'metro'; +import type { AssetData } from "metro"; export function getAndroidAssetSuffix(scale: number): string { const tolerance = 0.01; @@ -62,10 +62,10 @@ export function saveAssetsAndroid( addAssetToCopy: ( asset: AssetData, allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string, - ) => void, + getAssetDestPath: (asset: AssetData, scale: number) => string + ) => void ) { assets.forEach((asset) => - addAssetToCopy(asset, undefined, getAssetDestPathAndroid), + addAssetToCopy(asset, undefined, getAssetDestPathAndroid) ); -} \ No newline at end of file +} diff --git a/packages/metro-service/src/asset/default.ts b/packages/metro-service/src/asset/default.ts index a09a7a139..d46349245 100644 --- a/packages/metro-service/src/asset/default.ts +++ b/packages/metro-service/src/asset/default.ts @@ -7,13 +7,10 @@ */ import path from "path"; -import type {AssetData} from 'metro'; +import type { AssetData } from "metro"; import type { PackagerAsset } from "./types"; -export function getAssetDestPath( - asset: PackagerAsset, - scale: number -): string { +export function getAssetDestPath(asset: PackagerAsset, scale: number): string { const suffix = scale === 1 ? "" : `@${scale}x`; const fileName = `${asset.name + suffix}.${asset.type}`; return path.join( @@ -33,9 +30,8 @@ export function saveAssetsDefault( addAssetToCopy: ( asset: AssetData, allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string, - ) => void, + getAssetDestPath: (asset: AssetData, scale: number) => string + ) => void ) { assets.forEach((asset) => addAssetToCopy(asset, undefined, getAssetDestPath)); } - diff --git a/packages/metro-service/src/asset/ios.ts b/packages/metro-service/src/asset/ios.ts index b7bde368e..5316db31d 100644 --- a/packages/metro-service/src/asset/ios.ts +++ b/packages/metro-service/src/asset/ios.ts @@ -6,8 +6,8 @@ import type { AssetData } from "metro"; import { error, info } from "@rnx-kit/console"; import path from "path"; import { getResourceIdentifier } from "./assetPathUtils"; -import { filterPlatformAssetScales } from './filter'; -import { getAssetDestPath } from './default'; +import { filterPlatformAssetScales } from "./filter"; +import { getAssetDestPath } from "./default"; type ImageSet = { basePath: string; @@ -80,38 +80,38 @@ export function saveAssetsIOS( addAssetToCopy: ( asset: AssetData, allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string, - ) => void, + getAssetDestPath: (asset: AssetData, scale: number) => string + ) => void ) { if (assetCatalogDest != null) { // Use iOS Asset Catalog for images. This will allow Apple app thinning to // remove unused scales from the optimized bundle. - const catalogDir = path.join(assetCatalogDest, 'RNAssets.xcassets'); + const catalogDir = path.join(assetCatalogDest, "RNAssets.xcassets"); if (!fs.existsSync(catalogDir)) { error( - `Could not find asset catalog 'RNAssets.xcassets' in ${assetCatalogDest}. Make sure to create it if it does not exist.`, + `Could not find asset catalog 'RNAssets.xcassets' in ${assetCatalogDest}. Make sure to create it if it does not exist.` ); return; } - info('Adding images to asset catalog', catalogDir); + info("Adding images to asset catalog", catalogDir); cleanAssetCatalog(catalogDir); for (const asset of assets) { if (isCatalogAsset(asset)) { const imageSet = getImageSet( catalogDir, asset, - filterPlatformAssetScales(ALLOWED_SCALES, asset.scales), + filterPlatformAssetScales(ALLOWED_SCALES, asset.scales) ); writeImageSet(imageSet); } else { addAssetToCopy(asset, ALLOWED_SCALES, getAssetDestPath); } } - info('Done adding images to asset catalog'); + info("Done adding images to asset catalog"); } else { assets.forEach((asset) => - addAssetToCopy(asset, ALLOWED_SCALES, getAssetDestPath), + addAssetToCopy(asset, ALLOWED_SCALES, getAssetDestPath) ); } } diff --git a/packages/metro-service/src/asset/write.ts b/packages/metro-service/src/asset/write.ts index ecd023b9e..f8f15f68a 100644 --- a/packages/metro-service/src/asset/write.ts +++ b/packages/metro-service/src/asset/write.ts @@ -63,9 +63,9 @@ export async function saveAssets( addAssetToCopy: ( asset: AssetData, allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string, - ) => void, - ) => void, + getAssetDestPath: (asset: AssetData, scale: number) => string + ) => void + ) => void ): Promise { if (!assetsDest) { warn("Assets destination folder is not set, skipping..."); @@ -74,23 +74,23 @@ export async function saveAssets( const filesToCopy: Record = Object.create(null); // Map src -> dest - const addAssetToCopy = ( - asset: AssetData, - allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string, - ) => { - const validScales = new Set( - filterPlatformAssetScales(allowedScales, asset.scales), - ); + const addAssetToCopy = ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string + ) => { + const validScales = new Set( + filterPlatformAssetScales(allowedScales, asset.scales) + ); - asset.scales.forEach((scale: number, idx: number) => { - if (!validScales.has(scale)) { - return; - } - const src = asset.files[idx]; - const dest = path.join(assetsDest, getAssetDestPath(asset, scale)); - filesToCopy[src] = dest; - }); + asset.scales.forEach((scale: number, idx: number) => { + if (!validScales.has(scale)) { + return; + } + const src = asset.files[idx]; + const dest = path.join(assetsDest, getAssetDestPath(asset, scale)); + filesToCopy[src] = dest; + }); asset.scales.forEach((scale, idx) => { if (!validScales.has(scale)) { @@ -107,7 +107,7 @@ export async function saveAssets( platform, assetsDest, assetCatalogDest, - addAssetToCopy, + addAssetToCopy ); return copyAll(filesToCopy); } diff --git a/packages/metro-service/src/bundle.ts b/packages/metro-service/src/bundle.ts index d45674f19..0bd88d35f 100644 --- a/packages/metro-service/src/bundle.ts +++ b/packages/metro-service/src/bundle.ts @@ -5,14 +5,14 @@ import chalk from "chalk"; import fs from "fs"; import type { ConfigT } from "metro-config"; import type { BundleOptions, OutputOptions } from "metro/shared/types"; -import type {AssetData} from 'metro'; +import type { AssetData } from "metro"; import Server from "metro/src/Server"; import Bundle from "metro/src/shared/output/bundle"; import path from "path"; import { saveAssets } from "./asset"; -import { saveAssetsAndroid } from './asset/android'; -import { saveAssetsDefault } from './asset/default'; -import { saveAssetsIOS } from './asset/ios'; +import { saveAssetsAndroid } from "./asset/android"; +import { saveAssetsDefault } from "./asset/default"; +import { saveAssetsIOS } from "./asset/ios"; import { ensureBabelConfig } from "./babel"; export type BundleArgs = { @@ -53,24 +53,32 @@ type SaveAssetsPlugin = ( addAssetToCopy: ( asset: AssetData, allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string, - ) => void, + getAssetDestPath: (asset: AssetData, scale: number) => string + ) => void ) => void; - // Eventually this will be part of the rn config, but we require it on older rn versions for win32 and the cli doesn't allow extra config properties. - // See https://github.com/react-native-community/cli/pull/2002 -function getSaveAssetsPlugin(platform: string, projectRoot: string): SaveAssetsPlugin { +// Eventually this will be part of the rn config, but we require it on older rn versions for win32 and the cli doesn't allow extra config properties. +// See https://github.com/react-native-community/cli/pull/2002 +function getSaveAssetsPlugin( + platform: string, + projectRoot: string +): SaveAssetsPlugin { try { - if (platform === 'win32') { - const saveAssetsPlugin = require.resolve('@office-iss/react-native-win32/saveAssetPlugin', {paths: [projectRoot]}); + if (platform === "win32") { + const saveAssetsPlugin = require.resolve( + "@office-iss/react-native-win32/saveAssetPlugin", + { paths: [projectRoot] } + ); return require(saveAssetsPlugin); } - } catch (_) { /* empty */ } - return platform === 'ios' - ? saveAssetsIOS - : platform === 'android' - ? saveAssetsAndroid - : saveAssetsDefault; + } catch (_) { + /* empty */ + } + return platform === "ios" + ? saveAssetsIOS + : platform === "android" + ? saveAssetsAndroid + : saveAssetsDefault; } export async function bundle( @@ -81,7 +89,10 @@ export async function bundle( // ensure Metro can find Babel config ensureBabelConfig(config); - const saveAssetsPlugin = getSaveAssetsPlugin(args.platform, config.projectRoot); + const saveAssetsPlugin = getSaveAssetsPlugin( + args.platform, + config.projectRoot + ); if (config.resolver.platforms.indexOf(args.platform) === -1) { error( From dbccff1c4b7a04dc16e6c3190ef713b82c113354 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Wed, 9 Aug 2023 09:23:30 -0700 Subject: [PATCH 5/7] Update packages/metro-service/src/bundle.ts Co-authored-by: Tommy Nguyen <4123478+tido64@users.noreply.github.com> --- packages/metro-service/src/bundle.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/metro-service/src/bundle.ts b/packages/metro-service/src/bundle.ts index 0bd88d35f..c7ca2a535 100644 --- a/packages/metro-service/src/bundle.ts +++ b/packages/metro-service/src/bundle.ts @@ -63,16 +63,16 @@ function getSaveAssetsPlugin( platform: string, projectRoot: string ): SaveAssetsPlugin { - try { - if (platform === "win32") { + if (platform === "win32") { + try { const saveAssetsPlugin = require.resolve( "@office-iss/react-native-win32/saveAssetPlugin", { paths: [projectRoot] } ); return require(saveAssetsPlugin); + } catch (_) { + /* empty */ } - } catch (_) { - /* empty */ } return platform === "ios" ? saveAssetsIOS From 7d60f40fbd1496c515336426b6bb8fc966f2b17a Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Wed, 9 Aug 2023 09:33:38 -0700 Subject: [PATCH 6/7] review feedback --- packages/metro-service/src/asset/android.ts | 23 +++++++---------- packages/metro-service/src/asset/default.ts | 23 +++++++---------- packages/metro-service/src/asset/ios.ts | 21 +++++++--------- packages/metro-service/src/asset/types.ts | 14 +++++++++++ packages/metro-service/src/asset/write.ts | 15 +++-------- packages/metro-service/src/bundle.ts | 28 ++++++++------------- 6 files changed, 54 insertions(+), 70 deletions(-) diff --git a/packages/metro-service/src/asset/android.ts b/packages/metro-service/src/asset/android.ts index c3f7cfc51..daea6ba18 100644 --- a/packages/metro-service/src/asset/android.ts +++ b/packages/metro-service/src/asset/android.ts @@ -2,8 +2,7 @@ import * as path from "path"; import { getResourceIdentifier } from "./assetPathUtils"; -import type { PackagerAsset } from "./types"; -import type { AssetData } from "metro"; +import type { PackagerAsset, SaveAssetsPlugin } from "./types"; export function getAndroidAssetSuffix(scale: number): string { const tolerance = 0.01; @@ -54,18 +53,14 @@ export function getAssetDestPathAndroid( return path.join(androidFolder, `${fileName}.${asset.type}`); } -export function saveAssetsAndroid( - assets: ReadonlyArray, - _platform: string, - _assetsDest: string | undefined, - _assetCatalogDest: string | undefined, - addAssetToCopy: ( - asset: AssetData, - allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string - ) => void -) { +export const saveAssetsAndroid: SaveAssetsPlugin = ( + assets, + _platform, + _assetsDest, + _assetCatalogDest, + addAssetToCopy +) => { assets.forEach((asset) => addAssetToCopy(asset, undefined, getAssetDestPathAndroid) ); -} +}; diff --git a/packages/metro-service/src/asset/default.ts b/packages/metro-service/src/asset/default.ts index d46349245..1e3984873 100644 --- a/packages/metro-service/src/asset/default.ts +++ b/packages/metro-service/src/asset/default.ts @@ -7,8 +7,7 @@ */ import path from "path"; -import type { AssetData } from "metro"; -import type { PackagerAsset } from "./types"; +import type { PackagerAsset, SaveAssetsPlugin } from "./types"; export function getAssetDestPath(asset: PackagerAsset, scale: number): string { const suffix = scale === 1 ? "" : `@${scale}x`; @@ -22,16 +21,12 @@ export function getAssetDestPath(asset: PackagerAsset, scale: number): string { ); } -export function saveAssetsDefault( - assets: ReadonlyArray, - _platform: string, - _assetsDest: string | undefined, - _assetCatalogDest: string | undefined, - addAssetToCopy: ( - asset: AssetData, - allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string - ) => void -) { +export const saveAssetsDefault: SaveAssetsPlugin = ( + assets, + _platform, + _assetsDest, + _assetCatalogDest, + addAssetToCopy +) => { assets.forEach((asset) => addAssetToCopy(asset, undefined, getAssetDestPath)); -} +}; diff --git a/packages/metro-service/src/asset/ios.ts b/packages/metro-service/src/asset/ios.ts index 5316db31d..720cb62bf 100644 --- a/packages/metro-service/src/asset/ios.ts +++ b/packages/metro-service/src/asset/ios.ts @@ -8,6 +8,7 @@ import path from "path"; import { getResourceIdentifier } from "./assetPathUtils"; import { filterPlatformAssetScales } from "./filter"; import { getAssetDestPath } from "./default"; +import type { SaveAssetsPlugin } from "./types"; type ImageSet = { basePath: string; @@ -72,17 +73,13 @@ export function writeImageSet(imageSet: ImageSet): void { const ALLOWED_SCALES = [1, 2, 3]; -export function saveAssetsIOS( - assets: ReadonlyArray, - _platform: string, - _assetsDest: string | undefined, - assetCatalogDest: string | undefined, - addAssetToCopy: ( - asset: AssetData, - allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string - ) => void -) { +export const saveAssetsIOS: SaveAssetsPlugin = ( + assets, + _platform, + _assetsDest, + assetCatalogDest, + addAssetToCopy +) => { if (assetCatalogDest != null) { // Use iOS Asset Catalog for images. This will allow Apple app thinning to // remove unused scales from the optimized bundle. @@ -114,4 +111,4 @@ export function saveAssetsIOS( addAssetToCopy(asset, ALLOWED_SCALES, getAssetDestPath) ); } -} +}; diff --git a/packages/metro-service/src/asset/types.ts b/packages/metro-service/src/asset/types.ts index 043f159ba..57ee89e3f 100644 --- a/packages/metro-service/src/asset/types.ts +++ b/packages/metro-service/src/asset/types.ts @@ -1,5 +1,19 @@ +import type { AssetData } from "metro"; + export type PackagerAsset = { httpServerLocation: string; name: string; type: string; }; + +export type SaveAssetsPlugin = ( + assets: ReadonlyArray, + platform: string, + assetsDest: string | undefined, + assetCatalogDest: string | undefined, + addAssetToCopy: ( + asset: AssetData, + allowedScales: number[] | undefined, + getAssetDestPath: (asset: AssetData, scale: number) => string + ) => void +) => void; diff --git a/packages/metro-service/src/asset/write.ts b/packages/metro-service/src/asset/write.ts index f8f15f68a..6d40cf401 100644 --- a/packages/metro-service/src/asset/write.ts +++ b/packages/metro-service/src/asset/write.ts @@ -5,6 +5,7 @@ import * as fs from "fs"; import type { AssetData } from "metro"; import * as path from "path"; import { filterPlatformAssetScales } from "./filter"; +import type { SaveAssetsPlugin } from "./types"; function copy( src: string, @@ -50,22 +51,12 @@ function copyAll(filesToCopy: Record) { }); } -export async function saveAssets( +export function saveAssets( assets: ReadonlyArray, platform: string, assetsDest: string | undefined, assetCatalogDest: string | undefined, - saveAssetsPlugin: ( - assets: ReadonlyArray, - platform: string, - assetsDest: string | undefined, - assetCatalogDest: string | undefined, - addAssetToCopy: ( - asset: AssetData, - allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string - ) => void - ) => void + saveAssetsPlugin: SaveAssetsPlugin ): Promise { if (!assetsDest) { warn("Assets destination folder is not set, skipping..."); diff --git a/packages/metro-service/src/bundle.ts b/packages/metro-service/src/bundle.ts index c7ca2a535..07b13129e 100644 --- a/packages/metro-service/src/bundle.ts +++ b/packages/metro-service/src/bundle.ts @@ -5,11 +5,11 @@ import chalk from "chalk"; import fs from "fs"; import type { ConfigT } from "metro-config"; import type { BundleOptions, OutputOptions } from "metro/shared/types"; -import type { AssetData } from "metro"; import Server from "metro/src/Server"; import Bundle from "metro/src/shared/output/bundle"; import path from "path"; import { saveAssets } from "./asset"; +import type { SaveAssetsPlugin } from "./asset/types"; import { saveAssetsAndroid } from "./asset/android"; import { saveAssetsDefault } from "./asset/default"; import { saveAssetsIOS } from "./asset/ios"; @@ -45,18 +45,6 @@ type RequestOptions = { unstable_transformProfile?: BundleOptions["unstable_transformProfile"]; }; -type SaveAssetsPlugin = ( - assets: ReadonlyArray, - platform: string, - assetsDest: string | undefined, - assetCatalogDest: string | undefined, - addAssetToCopy: ( - asset: AssetData, - allowedScales: number[] | undefined, - getAssetDestPath: (asset: AssetData, scale: number) => string - ) => void -) => void; - // Eventually this will be part of the rn config, but we require it on older rn versions for win32 and the cli doesn't allow extra config properties. // See https://github.com/react-native-community/cli/pull/2002 function getSaveAssetsPlugin( @@ -74,11 +62,15 @@ function getSaveAssetsPlugin( /* empty */ } } - return platform === "ios" - ? saveAssetsIOS - : platform === "android" - ? saveAssetsAndroid - : saveAssetsDefault; + + switch (platform) { + case "ios": + return saveAssetsIOS; + case "android": + return saveAssetsAndroid; + default: + return saveAssetsDefault; + } } export async function bundle( From f3c1ba40564f917291fbbda51648f947476bf127 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Wed, 9 Aug 2023 10:03:19 -0700 Subject: [PATCH 7/7] fix --- packages/metro-service/src/asset/default.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/metro-service/src/asset/default.ts b/packages/metro-service/src/asset/default.ts index 1e3984873..d2a317f58 100644 --- a/packages/metro-service/src/asset/default.ts +++ b/packages/metro-service/src/asset/default.ts @@ -1,11 +1,3 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - import path from "path"; import type { PackagerAsset, SaveAssetsPlugin } from "./types";