Skip to content

Commit df1cd01

Browse files
authored
Handle metro bundle configuration for direct debugging (#2221)
1 parent 7ee7914 commit df1cd01

File tree

8 files changed

+127
-1
lines changed

8 files changed

+127
-1
lines changed

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,12 @@
465465
"title": "Enable & Disable Expo Hermes",
466466
"category": "React Native",
467467
"enablement": "!config.security.workspace.trust.enabled || isWorkspaceTrusted"
468+
},
469+
{
470+
"command": "reactNative.updateMetro",
471+
"title": "Update metro bundler configure(from 0.76) -- Experimental",
472+
"category": "React Native",
473+
"enablement": "!config.security.workspace.trust.enabled || isWorkspaceTrusted"
468474
}
469475
],
470476
"menus": {

src/common/error/errorStrings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,4 +421,5 @@ export const ERROR_STRINGS = {
421421
),
422422
[InternalErrorCode.FailedToEnableHermes]: "Failed to make changes to Hermes",
423423
[InternalErrorCode.FailedToEnableExpoHermes]: "Failed to make changes to Expo Hermes",
424+
[InternalErrorCode.FailedToUpdateMetro]: "Failed to update metro bundle configration",
424425
};

src/common/error/internalErrorCode.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export enum InternalErrorCode {
3434
FailedToReopenQRCode = 129,
3535
FailedToEnableHermes = 130,
3636
FailedToEnableExpoHermes = 131,
37+
FailedToUpdateMetro = 132,
38+
3739
// Device Deployer errors
3840
IOSDeployNotFound = 201,
3941

src/common/packager.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { FileSystem } from "./node/fileSystem";
2727
import { PromiseUtil } from "./node/promise";
2828
import { CONTEXT_VARIABLES_NAMES } from "./contextVariablesNames";
2929
import { getNodeVersion } from "./nodeHelper";
30-
import { getTSVersion } from "./utils";
30+
import { getTSVersion, switchBundleOptions } from "./utils";
3131

3232
nls.config({
3333
messageFormat: nls.MessageFormat.bundle,
@@ -62,6 +62,7 @@ export class Packager {
6262
private static NODE_AVAIABLE = "18.0.0";
6363
private static RN_VERSION_WITH_PACKER_ISSUE = "0.73.0";
6464
private static TS_VERSION_SUPPORTED = "0.70.0";
65+
private static RNVersion_Direct_Debug = "0.76.0";
6566
private static JS_INJECTOR_DIRPATH =
6667
findFileInFolderHierarchy(__dirname, "js-patched") || __dirname;
6768
private static NODE_MODULES_FODLER_NAME = "node_modules";
@@ -225,6 +226,9 @@ export class Packager {
225226

226227
const versions = await ProjectVersionHelper.getReactNativeVersions(this.projectPath);
227228
rnVersion = versions.reactNativeVersion;
229+
if (semver.gte(rnVersion, Packager.RNVersion_Direct_Debug)) {
230+
await switchBundleOptions(this.projectPath, true);
231+
}
228232
await this.monkeyPatchOpnForRNPackager(rnVersion);
229233

230234
const args = await this.getPackagerArgs(this.projectPath, rnVersion, resetCache);

src/common/utils.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for details.
33
import * as path from "path";
44
import * as net from "net";
5+
import * as fs from "fs";
56
import stripJsonComments = require("strip-json-comments");
67
import { logger } from "@vscode/debugadapter";
78
import { Address4, Address6 } from "ip-address";
89
import { ChildProcess } from "./node/childProcess";
910
import { HostPlatform } from "./hostPlatform";
11+
import { FileSystem } from "./node/fileSystem";
1012
import customRequire from "./customRequire";
1113

1214
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -124,3 +126,57 @@ export function ipToBuffer(ip: string): Buffer {
124126
}
125127
throw new Error("Invalid IP address format.");
126128
}
129+
130+
export async function switchBundleOptions(projectRootPath: string, flag: boolean) {
131+
const splitBundleOptionsPath = path.resolve(
132+
projectRootPath,
133+
"node_modules",
134+
"metro",
135+
"src",
136+
"lib",
137+
"splitBundleOptions.js",
138+
);
139+
const splitBundleOptionsContent = fs.readFileSync(splitBundleOptionsPath, "utf-8");
140+
let modifiedData;
141+
if (flag) {
142+
modifiedData = splitBundleOptionsContent.replace(
143+
/excludeSource:\s*options\.excludeSource/,
144+
"excludeSource: false",
145+
);
146+
147+
modifiedData = modifiedData.replace(
148+
/sourcePaths:\s*options\.sourcePaths/,
149+
'sourcePaths: "absolute"',
150+
);
151+
} else {
152+
modifiedData = splitBundleOptionsContent.replace(
153+
/excludeSource:\s*false/,
154+
"excludeSource: options.excludeSource",
155+
);
156+
157+
modifiedData = modifiedData.replace(
158+
/sourcePaths:\s*"absolute"/,
159+
"sourcePaths: options.sourcePaths",
160+
);
161+
}
162+
const nodeFileSystem = new FileSystem();
163+
await nodeFileSystem.writeFile(splitBundleOptionsPath, modifiedData);
164+
}
165+
166+
export function checkBundleOptions(projectRootPath: string): boolean {
167+
const splitBundleOptionsPath = path.resolve(
168+
projectRootPath,
169+
"node_modules",
170+
"metro",
171+
"src",
172+
"lib",
173+
"splitBundleOptions.js",
174+
);
175+
176+
const splitBundleOptionsContent = fs.readFileSync(splitBundleOptionsPath, "utf-8");
177+
178+
const excludeSourceRegex = /excludeSource\s*:\s*false/.test(splitBundleOptionsContent);
179+
const sourcePathsRegex = /sourcePaths\s*:\s*"absolute"/.test(splitBundleOptionsContent);
180+
181+
return excludeSourceRegex && sourcePathsRegex;
182+
}

src/debugger/direct/directDebugSession.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as vscode from "vscode";
55
import { logger } from "@vscode/debugadapter";
66
import { DebugProtocol } from "vscode-debugprotocol";
77
import * as nls from "vscode-nls";
8+
import * as semver from "semver";
89
import { ProjectVersionHelper } from "../../common/projectVersionHelper";
910
import { TelemetryHelper } from "../../common/telemetryHelper";
1011
import { HermesCDPMessageHandler } from "../../cdp-proxy/CDPMessageHandlers/hermesCDPMessageHandler";
@@ -26,6 +27,7 @@ import { RNSession } from "../debugSessionWrapper";
2627
import { SettingsHelper } from "../../extension/settingsHelper";
2728
import { ReactNativeProjectHelper } from "../../common/reactNativeProjectHelper";
2829
import { IWDPHelper } from "./IWDPHelper";
30+
import { checkBundleOptions } from "../../common/utils";
2931

3032
nls.config({
3133
messageFormat: nls.MessageFormat.bundle,
@@ -40,6 +42,7 @@ export class DirectDebugSession extends DebugSessionBase {
4042
private appTargetConnectionClosedHandlerDescriptor?: vscode.Disposable;
4143
private attachSession: vscode.DebugSession | null;
4244
private iOSWKDebugProxyHelper: IWDPHelper;
45+
private static RNVersion_Direct_Debug = "0.76.0";
4346

4447
constructor(rnSession: RNSession) {
4548
super(rnSession);
@@ -175,6 +178,17 @@ export class DirectDebugSession extends DebugSessionBase {
175178
this.projectRootPath,
176179
ProjectVersionHelper.generateAdditionalPackagesToCheckByPlatform(attachArgs),
177180
);
181+
182+
if (
183+
semver.gte(versions.reactNativeVersion, DirectDebugSession.RNVersion_Direct_Debug)
184+
) {
185+
if (!checkBundleOptions(this.projectRootPath)) {
186+
logger.warn(
187+
"You are currently on react native >= 0.76.0, please use command React Native: Update metro bundler configure(from 0.76) -- Experimental -> Using react-native-tools debugger and then rebuild your application before Attach",
188+
);
189+
}
190+
}
191+
178192
extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
179193
attachArgs,
180194
versions,

src/extension/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ export * from "./prebuildClean";
3333
export * from "./reopenQRCode";
3434
export * from "./enableHermes";
3535
export * from "./enableExpoHemes";
36+
export * from "./updateMetro";
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for details.
3+
4+
import * as vscode from "vscode";
5+
import * as assert from "assert";
6+
import * as semver from "semver";
7+
import { ErrorHelper } from "../../common/error/errorHelper";
8+
import { InternalErrorCode } from "../../common/error/internalErrorCode";
9+
import { Command } from "./util/command";
10+
import { ProjectVersionHelper } from "../../common/projectVersionHelper";
11+
import { switchBundleOptions } from "../../common/utils";
12+
import { OutputChannelLogger } from "../log/OutputChannelLogger";
13+
14+
const logger = OutputChannelLogger.getMainChannel();
15+
16+
export class updateMetro extends Command {
17+
codeName = "updateMetro";
18+
label = "Update metro bundler configure(from 0.76) -- Experimental";
19+
error = ErrorHelper.getInternalError(InternalErrorCode.FailedToUpdateMetro);
20+
private static RNVersion_Direct_Debug = "0.76.0";
21+
22+
async baseFn(): Promise<void> {
23+
assert(this.project);
24+
25+
const type = await vscode.window.showQuickPick([
26+
"Using react-native-tools debugger",
27+
"Default",
28+
]);
29+
const projectRootPath = this.project.getPackager().getProjectPath();
30+
const versions = await ProjectVersionHelper.getReactNativeVersions(projectRootPath);
31+
if (semver.lt(versions.reactNativeVersion, updateMetro.RNVersion_Direct_Debug)) return;
32+
if (!type) return;
33+
if (type === "Using react-native-tools debugger") {
34+
logger.info("update metro bundle configuration to react-native-tools debugger");
35+
await switchBundleOptions(projectRootPath, true);
36+
}
37+
if (type === "Default") {
38+
logger.info("update metro bundle configuration to default");
39+
await switchBundleOptions(projectRootPath, false);
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)