From 996348fc68972f5db7f6fb8a1ea5e46ace586b39 Mon Sep 17 00:00:00 2001 From: Tommy Nguyen <4123478+tido64@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:18:31 +0100 Subject: [PATCH] fix(cli): automatically compose source maps for Hermes bundle --- .changeset/afraid-suns-live.md | 5 +++++ .changeset/red-turkeys-trade.md | 5 +++++ packages/cli/src/bundle/hermes.ts | 26 ++++++++++++++++-------- packages/tools-react-native/package.json | 1 + packages/tools-react-native/src/metro.ts | 11 ++++++++-- yarn.lock | 3 ++- 6 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 .changeset/afraid-suns-live.md create mode 100644 .changeset/red-turkeys-trade.md diff --git a/.changeset/afraid-suns-live.md b/.changeset/afraid-suns-live.md new file mode 100644 index 000000000..49ac25cf5 --- /dev/null +++ b/.changeset/afraid-suns-live.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/tools-react-native": patch +--- + +Allow importing `metro-source-map` via `metro` diff --git a/.changeset/red-turkeys-trade.md b/.changeset/red-turkeys-trade.md new file mode 100644 index 000000000..d489e731c --- /dev/null +++ b/.changeset/red-turkeys-trade.md @@ -0,0 +1,5 @@ +--- +"@rnx-kit/cli": patch +--- + +Automatically compose source maps of the JS and Hermes bytecode bundles diff --git a/packages/cli/src/bundle/hermes.ts b/packages/cli/src/bundle/hermes.ts index 923bc8d2a..09c095e89 100644 --- a/packages/cli/src/bundle/hermes.ts +++ b/packages/cli/src/bundle/hermes.ts @@ -1,6 +1,7 @@ import type { HermesOptions } from "@rnx-kit/config"; import { error, info } from "@rnx-kit/console"; import { findPackageDependencyDir } from "@rnx-kit/tools-node/package"; +import { requireModuleFromMetro } from "@rnx-kit/tools-react-native/metro"; import { spawnSync } from "child_process"; import * as fs from "fs"; import * as os from "os"; @@ -57,10 +58,6 @@ function getOutput(args: string[]): string | null { return null; } -function isSourceMapFlag(flag: string): boolean { - return flag === "-source-map" || flag.startsWith("-source-map="); -} - export function emitBytecode( input: string, sourcemap: string | undefined, @@ -90,10 +87,6 @@ export function emitBytecode( args.push("-out", output); } - if (sourcemap && !args.some(isSourceMapFlag)) { - args.push("-source-map", sourcemap); - } - args.push(input); info("Emitting bytecode to:", output); @@ -101,4 +94,21 @@ export function emitBytecode( if (result.status !== 0) { throw result.error; } + + if (sourcemap && args.includes("-output-source-map")) { + const outputMap = output + ".map"; + info(`Combining source maps: ${sourcemap} + ${outputMap}`); + + const options = { encoding: "utf-8" } as const; + const packagerSourcemap = JSON.parse(fs.readFileSync(sourcemap, options)); + const compilerSourcemap = JSON.parse(fs.readFileSync(outputMap, options)); + + // `composeSourceMaps` was introduced in 0.56 — see + // https://github.com/facebook/metro/commit/6017085bdad96ca5cec39d50038eb5622ce1097b + // @ts-expect-error Property 'composeSourceMaps' does not exist + const { composeSourceMaps } = requireModuleFromMetro("metro-source-map"); + + const composed = composeSourceMaps([packagerSourcemap, compilerSourcemap]); + fs.writeFileSync(outputMap, JSON.stringify(composed)); + } } diff --git a/packages/tools-react-native/package.json b/packages/tools-react-native/package.json index 0dd5763cd..ca32b4420 100644 --- a/packages/tools-react-native/package.json +++ b/packages/tools-react-native/package.json @@ -59,6 +59,7 @@ "metro-config": "^0.76.5", "metro-core": "^0.76.5", "metro-resolver": "^0.76.5", + "metro-source-map": "^0.76.5", "prettier": "^3.0.0", "typescript": "^5.0.0" }, diff --git a/packages/tools-react-native/src/metro.ts b/packages/tools-react-native/src/metro.ts index b12ae885d..c466f8911 100644 --- a/packages/tools-react-native/src/metro.ts +++ b/packages/tools-react-native/src/metro.ts @@ -10,7 +10,8 @@ type MetroImport = | typeof import("metro/src/shared/output/bundle") | typeof import("metro-config") | typeof import("metro-core") - | typeof import("metro-resolver"); + | typeof import("metro-resolver") + | typeof import("metro-source-map"); type MetroModule = | "metro" @@ -19,7 +20,8 @@ type MetroModule = | "metro/src/shared/output/bundle" | "metro-config" | "metro-core" - | "metro-resolver"; + | "metro-resolver" + | "metro-source-map"; function resolveFrom(name: string, startDir: string): string | undefined { return findPackageDependencyDir(name, { @@ -110,6 +112,11 @@ export function requireModuleFromMetro( fromDir?: string ): typeof import("metro-resolver"); +export function requireModuleFromMetro( + moduleName: "metro-source-map", + fromDir?: string +): typeof import("metro-source-map"); + /** * Imports specified module starting from the installation directory of the * currently used `metro` version. diff --git a/yarn.lock b/yarn.lock index 868e30916..7a6540a9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4386,6 +4386,7 @@ __metadata: metro-config: ^0.76.5 metro-core: ^0.76.5 metro-resolver: ^0.76.5 + metro-source-map: ^0.76.5 prettier: ^3.0.0 typescript: ^5.0.0 languageName: unknown @@ -10638,7 +10639,7 @@ __metadata: languageName: node linkType: hard -"metro-source-map@npm:0.76.8, metro-source-map@npm:^0.76.8": +"metro-source-map@npm:0.76.8, metro-source-map@npm:^0.76.5, metro-source-map@npm:^0.76.8": version: 0.76.8 resolution: "metro-source-map@npm:0.76.8" dependencies: