From 1d72d6701ad13c458610c8c8c51401ff684e6d70 Mon Sep 17 00:00:00 2001 From: filip131311 Date: Wed, 16 Oct 2024 21:51:44 +0200 Subject: [PATCH 01/12] init --- packages/vscode-extension/lib/runtime.js | 2 +- .../src/builders/buildAndroid.ts | 2 +- .../src/debugging/DebugAdapter.ts | 42 ++++++++++++++----- .../src/utilities/reactNative.ts | 2 +- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/packages/vscode-extension/lib/runtime.js b/packages/vscode-extension/lib/runtime.js index cfee23dd5..d749f1719 100644 --- a/packages/vscode-extension/lib/runtime.js +++ b/packages/vscode-extension/lib/runtime.js @@ -26,7 +26,7 @@ function wrapConsole(consoleFunc) { const expoLogIndex = stack.findIndex((frame) => frame.methodName === "__expoConsoleLog"); const location = expoLogIndex > 0 ? stack[expoLogIndex + 1] : stack[1]; const lineOffset = global.__EXPO_ENV_PRELUDE_LINES__ || 0; - args.push(location.file, location.lineNumber - lineOffset, location.column); + args.push(lineOffset, location.file, location.lineNumber, location.column); return consoleFunc.apply(console, args); }; } diff --git a/packages/vscode-extension/src/builders/buildAndroid.ts b/packages/vscode-extension/src/builders/buildAndroid.ts index 4a0af62f2..6f8e49b86 100644 --- a/packages/vscode-extension/src/builders/buildAndroid.ts +++ b/packages/vscode-extension/src/builders/buildAndroid.ts @@ -143,7 +143,7 @@ export async function buildAndroid( ), ]; // configureReactNativeOverrides init script is only necessary for RN versions older then 0.74.0 see comments in configureReactNativeOverrides.gradle for more details - if (semver.lt(await getReactNativeVersion(), "0.74.0")) { + if (semver.lt(getReactNativeVersion(), "0.74.0")) { gradleArgs.push( "--init-script", // configureReactNativeOverrides init script is used to patch React Android project, see comments in configureReactNativeOverrides.gradle for more details path.join( diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index abbc2838b..3820764f2 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -14,6 +14,8 @@ import { Source, StackFrame, } from "@vscode/debugadapter"; +import { getReactNativeVersion } from "../utilities/reactNative"; +import semver from "semver"; import { DebugProtocol } from "@vscode/debugprotocol"; import WebSocket from "ws"; import { NullablePosition, SourceMapConsumer } from "source-map"; @@ -83,7 +85,8 @@ export class DebugAdapter extends DebugSession { private absoluteProjectPath: string; private projectPathAlias?: string; private threads: Array = []; - private sourceMaps: Array<[string, string, SourceMapConsumer]> = []; + private sourceMaps: Array<[string, string, SourceMapConsumer, number]> = []; + private lineOffset: number = 0; private linesStartAt1 = true; private columnsStartAt1 = true; @@ -150,7 +153,21 @@ export class DebugAdapter extends DebugSession { const decodedData = Buffer.from(base64Data, "base64").toString("utf-8"); const sourceMap = JSON.parse(decodedData); const consumer = await new SourceMapConsumer(sourceMap); - this.sourceMaps.push([message.params.url, message.params.scriptId, consumer]); + + // This line is here because of a problem with sourcemaps for expo projects that was addressed in + // this PR https://github.com/expo/expo/pull/29463, unfortunately it still requires changes to + // metro that were attempted here https://github.com/facebook/metro/pull/1284 we should monitor + // the situation in upcoming versions and if the changes are still not added bump the version below. + // more over offset should only be applied to the main bundle sourcemap as other files (generated during reload) + // do not contain the prelude causing the issue + const shouldApplyOffset = + semver.lte(getReactNativeVersion(), "0.76.0") && this.sourceMaps.length === 0; + this.sourceMaps.push([ + message.params.url, + message.params.scriptId, + consumer, + shouldApplyOffset ? this.lineOffset : 0, + ]); this.updateBreakpointsInSource(message.params.url, consumer); } @@ -186,9 +203,12 @@ export class DebugAdapter extends DebugSession { if (argsLen > 3 && message.params.args[argsLen - 1].type === "number") { // Since console.log stack is extracted from Error, unlike other messages sent over CDP // the line and column numbers are 1-based - const [scriptURL, generatedLineNumber1Based, generatedColumn1Based] = message.params.args - .slice(-3) - .map((v: any) => v.value); + const [lineOffset, scriptURL, generatedLineNumber1Based, generatedColumn1Based] = + message.params.args.slice(-4).map((v: any) => v.value); + + if (this.lineOffset !== lineOffset) { + this.lineOffset = lineOffset; + } const { lineNumber1Based, columnNumber0Based, sourceURL } = this.findOriginalPosition( scriptURL, @@ -196,10 +216,10 @@ export class DebugAdapter extends DebugSession { generatedColumn1Based - 1 ); - const variablesRefDapID = this.createVariableForOutputEvent(message.params.args.slice(0, -3)); + const variablesRefDapID = this.createVariableForOutputEvent(message.params.args.slice(0, -4)); output = new OutputEvent( - (await formatMessage(message.params.args.slice(0, -3))) + "\n", + (await formatMessage(message.params.args.slice(0, -4))) + "\n", typeToCategory(message.params.type) ); output.body = { @@ -264,7 +284,7 @@ export class DebugAdapter extends DebugSession { let sourceLine1Based = lineNumber1Based; let sourceColumn0Based = columnNumber0Based; - this.sourceMaps.forEach(([url, id, consumer]) => { + this.sourceMaps.forEach(([url, id, consumer, lineOffset]) => { // when we identify script by its URL we need to deal with a situation when the URL is sent with a different // hostname and port than the one we have registered in the source maps. The reason for that is that the request // that populates the source map (scriptParsed) is sent by metro, while the requests from breakpoints or logs @@ -273,7 +293,7 @@ export class DebugAdapter extends DebugSession { if (id === scriptIdOrURL || compareIgnoringHost(url, scriptIdOrURL)) { scriptURL = url; const pos = consumer.originalPositionFor({ - line: lineNumber1Based, + line: lineNumber1Based - lineOffset, column: columnNumber0Based, }); if (pos.source != null) { @@ -440,7 +460,7 @@ export class DebugAdapter extends DebugSession { } let position: NullablePosition = { line: null, column: null, lastColumn: null }; let originalSourceURL: string = ""; - this.sourceMaps.forEach(([sourceURL, scriptId, consumer]) => { + this.sourceMaps.forEach(([sourceURL, scriptId, consumer, lineOffset]) => { const sources = []; consumer.eachMapping((mapping) => { sources.push(mapping.source); @@ -453,7 +473,7 @@ export class DebugAdapter extends DebugSession { }); if (pos.line != null) { originalSourceURL = sourceURL; - position = pos; + position = { ...pos, line: pos.line + lineOffset }; } }); if (position.line === null) { diff --git a/packages/vscode-extension/src/utilities/reactNative.ts b/packages/vscode-extension/src/utilities/reactNative.ts index 5e4e75d80..c2c64cf80 100644 --- a/packages/vscode-extension/src/utilities/reactNative.ts +++ b/packages/vscode-extension/src/utilities/reactNative.ts @@ -1,7 +1,7 @@ import path from "path"; import { getAppRootFolder } from "./extensionContext"; -export async function getReactNativeVersion() { +export function getReactNativeVersion() { const workspacePath = getAppRootFolder(); const reactNativeRoot = path.dirname(require.resolve("react-native", { paths: [workspacePath] })); const packageJsonPath = path.join(reactNativeRoot, "package.json"); From c64d42b278459ddddd37aee38fd89381bcc84a3b Mon Sep 17 00:00:00 2001 From: filip131311 Date: Wed, 16 Oct 2024 22:13:30 +0200 Subject: [PATCH 02/12] init --- .../vscode-extension/src/debugging/DebugAdapter.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index 3820764f2..e25a029cf 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -154,12 +154,13 @@ export class DebugAdapter extends DebugSession { const sourceMap = JSON.parse(decodedData); const consumer = await new SourceMapConsumer(sourceMap); - // This line is here because of a problem with sourcemaps for expo projects that was addressed in - // this PR https://github.com/expo/expo/pull/29463, unfortunately it still requires changes to - // metro that were attempted here https://github.com/facebook/metro/pull/1284 we should monitor - // the situation in upcoming versions and if the changes are still not added bump the version below. - // more over offset should only be applied to the main bundle sourcemap as other files (generated during reload) - // do not contain the prelude causing the issue + // This line is here because of a problem with sourcemaps for expo projects, + // that was addressed in this PR https://github.com/expo/expo/pull/29463, + // unfortunately it still requires changes to metro that were attempted here + // https://github.com/facebook/metro/pull/1284 we should monitor the situation + // in upcoming versions and if the changes are still not added bump the version below. + // more over offset should only be applied to the main bundle sourcemap as other files + // (generated during reload) do not contain the prelude causing the issue const shouldApplyOffset = semver.lte(getReactNativeVersion(), "0.76.0") && this.sourceMaps.length === 0; this.sourceMaps.push([ From e2cbc1ddd93e1395670e8071b0a389d8d8b6610c Mon Sep 17 00:00:00 2001 From: filip131311 Date: Thu, 17 Oct 2024 12:18:05 +0200 Subject: [PATCH 03/12] changes after CR --- packages/vscode-extension/lib/metro_helpers.js | 14 ++++++-------- packages/vscode-extension/lib/runtime.js | 3 +-- .../src/debugging/DebugAdapter.ts | 16 +++++++--------- .../src/debugging/DebugSession.ts | 1 + packages/vscode-extension/src/project/metro.ts | 15 +++++++++++++++ 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/packages/vscode-extension/lib/metro_helpers.js b/packages/vscode-extension/lib/metro_helpers.js index ca3ef42d6..63f0b6afd 100644 --- a/packages/vscode-extension/lib/metro_helpers.js +++ b/packages/vscode-extension/lib/metro_helpers.js @@ -41,16 +41,14 @@ function adaptMetroConfig(config) { module.output[0].data.code = `${preludeCode};var __REACT_DEVTOOLS_PORT__=${process.env.RCT_DEVTOOLS_PORT};`; } } else if (module.path === "__env__") { - // this handles @expo/env plugin, which is used to inject environment variables - // the code below instantiates a global variable __EXPO_ENV_PRELUDE_LINES__ that stores - // the number of lines in the prelude. This is used to calculate the line number offset - // when reporting line numbers from the JS runtime. The reason why this is needed, is that + // this handles @expo/env plugin, which is used to expose the number of lines in the prelude. + // This is used to calculate the line number offset when reporting line numbers for + // console.logs, breakpoints and uncaught exceptions. The reason why this is needed, is that // metro doesn't include __env__ prelude in the source map resulting in the source map // transformation getting shifted by the number of lines in the prelude. - const expoEnvCode = module.output[0].data.code; - if (!expoEnvCode.includes("__EXPO_ENV_PRELUDE_LINES__")) { - module.output[0].data.code = `${expoEnvCode};var __EXPO_ENV_PRELUDE_LINES__=${module.output[0].data.lineCount};`; - } + const expoEnvPreludeLines = module.output[0].data.lineCount; + process.stdout.write(JSON.stringify({ type: "RNIDE_Expo_Env_Prelude_Lines", expoEnvPreludeLines })); + process.stdout.write("\n"); } return origProcessModuleFilter(module); }; diff --git a/packages/vscode-extension/lib/runtime.js b/packages/vscode-extension/lib/runtime.js index d749f1719..b2f410df4 100644 --- a/packages/vscode-extension/lib/runtime.js +++ b/packages/vscode-extension/lib/runtime.js @@ -25,8 +25,7 @@ function wrapConsole(consoleFunc) { const stack = parseErrorStack(new Error().stack); const expoLogIndex = stack.findIndex((frame) => frame.methodName === "__expoConsoleLog"); const location = expoLogIndex > 0 ? stack[expoLogIndex + 1] : stack[1]; - const lineOffset = global.__EXPO_ENV_PRELUDE_LINES__ || 0; - args.push(lineOffset, location.file, location.lineNumber, location.column); + args.push(location.file, location.lineNumber, location.column); return consoleFunc.apply(console, args); }; } diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index e25a029cf..8cbcad2fa 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -86,7 +86,7 @@ export class DebugAdapter extends DebugSession { private projectPathAlias?: string; private threads: Array = []; private sourceMaps: Array<[string, string, SourceMapConsumer, number]> = []; - private lineOffset: number = 0; + private lineOffset: number; private linesStartAt1 = true; private columnsStartAt1 = true; @@ -99,6 +99,7 @@ export class DebugAdapter extends DebugSession { this.absoluteProjectPath = configuration.absoluteProjectPath; this.projectPathAlias = configuration.projectPathAlias; this.connection = new WebSocket(configuration.websocketAddress); + this.lineOffset = configuration.lineOffset; this.connection.on("open", () => { // the below catch handler is used to ignore errors coming from non critical CDP messages we @@ -204,12 +205,9 @@ export class DebugAdapter extends DebugSession { if (argsLen > 3 && message.params.args[argsLen - 1].type === "number") { // Since console.log stack is extracted from Error, unlike other messages sent over CDP // the line and column numbers are 1-based - const [lineOffset, scriptURL, generatedLineNumber1Based, generatedColumn1Based] = - message.params.args.slice(-4).map((v: any) => v.value); - - if (this.lineOffset !== lineOffset) { - this.lineOffset = lineOffset; - } + const [scriptURL, generatedLineNumber1Based, generatedColumn1Based] = message.params.args + .slice(-3) + .map((v: any) => v.value); const { lineNumber1Based, columnNumber0Based, sourceURL } = this.findOriginalPosition( scriptURL, @@ -217,10 +215,10 @@ export class DebugAdapter extends DebugSession { generatedColumn1Based - 1 ); - const variablesRefDapID = this.createVariableForOutputEvent(message.params.args.slice(0, -4)); + const variablesRefDapID = this.createVariableForOutputEvent(message.params.args.slice(0, -3)); output = new OutputEvent( - (await formatMessage(message.params.args.slice(0, -4))) + "\n", + (await formatMessage(message.params.args.slice(0, -3))) + "\n", typeToCategory(message.params.type) ); output.body = { diff --git a/packages/vscode-extension/src/debugging/DebugSession.ts b/packages/vscode-extension/src/debugging/DebugSession.ts index 93a03d531..21750bef2 100644 --- a/packages/vscode-extension/src/debugging/DebugSession.ts +++ b/packages/vscode-extension/src/debugging/DebugSession.ts @@ -56,6 +56,7 @@ export class DebugSession implements Disposable { websocketAddress: websocketAddress, absoluteProjectPath: getAppRootFolder(), projectPathAlias: this.metro.isUsingNewDebugger ? "/[metro-project]" : undefined, + lineOffset: this.metro.initialBundleLineOffset, }, { suppressDebugStatusbar: true, diff --git a/packages/vscode-extension/src/project/metro.ts b/packages/vscode-extension/src/project/metro.ts index 109af4c8b..3949a980d 100644 --- a/packages/vscode-extension/src/project/metro.ts +++ b/packages/vscode-extension/src/project/metro.ts @@ -46,6 +46,10 @@ type MetroEvent = transformedFileCount: number; totalFileCount: number; } + | { + type: "RNIDE_Expo_Env_Prelude_Lines"; + expoEnvPreludeLines: number; + } | { type: "RNIDE_initialize_done"; port: number; @@ -64,6 +68,7 @@ type MetroEvent = export class Metro implements Disposable { private subprocess?: ChildProcess; private _port = 0; + private _initialBundleLineOffset = 0; private startPromise: Promise | undefined; private usesNewDebugger?: Boolean; @@ -80,6 +85,10 @@ export class Metro implements Disposable { return this._port; } + public get initialBundleLineOffset() { + return this._initialBundleLineOffset; + } + public dispose() { this.subprocess?.kill(9); } @@ -207,6 +216,12 @@ export class Metro implements Disposable { } switch (event.type) { + case "RNIDE_Expo_Env_Prelude_Lines": + this._initialBundleLineOffset = event.expoEnvPreludeLines; + Logger.debug( + `Metro initial bundle offset is set to ${this._initialBundleLineOffset}` + ); + break; case "RNIDE_initialize_done": this._port = event.port; Logger.info(`Metro started on port ${this._port}`); From 900205f6c95a242eedd09cc397c96bbb55637451 Mon Sep 17 00:00:00 2001 From: filip131311 Date: Thu, 17 Oct 2024 22:34:00 +0200 Subject: [PATCH 04/12] changes after CR --- packages/vscode-extension/lib/metro_helpers.js | 15 +++++++++------ .../src/debugging/DebugAdapter.ts | 11 +++++++---- .../src/debugging/DebugSession.ts | 1 - packages/vscode-extension/src/project/metro.ts | 15 --------------- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/packages/vscode-extension/lib/metro_helpers.js b/packages/vscode-extension/lib/metro_helpers.js index 63f0b6afd..d3489ec22 100644 --- a/packages/vscode-extension/lib/metro_helpers.js +++ b/packages/vscode-extension/lib/metro_helpers.js @@ -41,14 +41,17 @@ function adaptMetroConfig(config) { module.output[0].data.code = `${preludeCode};var __REACT_DEVTOOLS_PORT__=${process.env.RCT_DEVTOOLS_PORT};`; } } else if (module.path === "__env__") { - // this handles @expo/env plugin, which is used to expose the number of lines in the prelude. - // This is used to calculate the line number offset when reporting line numbers for - // console.logs, breakpoints and uncaught exceptions. The reason why this is needed, is that + // this handles @expo/env plugin, which is used to inject environment variables + // the code below instantiates a global variable __EXPO_ENV_PRELUDE_LINES__ that stores + // the number of lines in the prelude. This is used to calculate the line number offset + // when reporting line numbers from the JS runtime, breakpoints + // and uncaught exceptions. The reason why this is needed, is that // metro doesn't include __env__ prelude in the source map resulting in the source map // transformation getting shifted by the number of lines in the prelude. - const expoEnvPreludeLines = module.output[0].data.lineCount; - process.stdout.write(JSON.stringify({ type: "RNIDE_Expo_Env_Prelude_Lines", expoEnvPreludeLines })); - process.stdout.write("\n"); + const expoEnvCode = module.output[0].data.code; + if (!expoEnvCode.includes("__EXPO_ENV_PRELUDE_LINES__")) { + module.output[0].data.code = `${expoEnvCode};var __EXPO_ENV_PRELUDE_LINES__=${module.output[0].data.lineCount};`; + } } return origProcessModuleFilter(module); }; diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index 8cbcad2fa..d3431afc6 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -86,8 +86,6 @@ export class DebugAdapter extends DebugSession { private projectPathAlias?: string; private threads: Array = []; private sourceMaps: Array<[string, string, SourceMapConsumer, number]> = []; - private lineOffset: number; - private linesStartAt1 = true; private columnsStartAt1 = true; @@ -99,7 +97,6 @@ export class DebugAdapter extends DebugSession { this.absoluteProjectPath = configuration.absoluteProjectPath; this.projectPathAlias = configuration.projectPathAlias; this.connection = new WebSocket(configuration.websocketAddress); - this.lineOffset = configuration.lineOffset; this.connection.on("open", () => { // the below catch handler is used to ignore errors coming from non critical CDP messages we @@ -155,6 +152,10 @@ export class DebugAdapter extends DebugSession { const sourceMap = JSON.parse(decodedData); const consumer = await new SourceMapConsumer(sourceMap); + const sourceFile = await (await fetch(message.params.url)).text(); + const lineOffsetRegex = /__EXPO_ENV_PRELUDE_LINES__=(\d+);/; + const match = sourceFile.match(lineOffsetRegex); + const lineOffset = Number(match ? match[1] : null); // This line is here because of a problem with sourcemaps for expo projects, // that was addressed in this PR https://github.com/expo/expo/pull/29463, // unfortunately it still requires changes to metro that were attempted here @@ -164,11 +165,13 @@ export class DebugAdapter extends DebugSession { // (generated during reload) do not contain the prelude causing the issue const shouldApplyOffset = semver.lte(getReactNativeVersion(), "0.76.0") && this.sourceMaps.length === 0; + Logger.debug("Expo prelude lines were detected and an offset was set to:", lineOffset); + this.sourceMaps.push([ message.params.url, message.params.scriptId, consumer, - shouldApplyOffset ? this.lineOffset : 0, + shouldApplyOffset ? lineOffset : 0, ]); this.updateBreakpointsInSource(message.params.url, consumer); } diff --git a/packages/vscode-extension/src/debugging/DebugSession.ts b/packages/vscode-extension/src/debugging/DebugSession.ts index 21750bef2..93a03d531 100644 --- a/packages/vscode-extension/src/debugging/DebugSession.ts +++ b/packages/vscode-extension/src/debugging/DebugSession.ts @@ -56,7 +56,6 @@ export class DebugSession implements Disposable { websocketAddress: websocketAddress, absoluteProjectPath: getAppRootFolder(), projectPathAlias: this.metro.isUsingNewDebugger ? "/[metro-project]" : undefined, - lineOffset: this.metro.initialBundleLineOffset, }, { suppressDebugStatusbar: true, diff --git a/packages/vscode-extension/src/project/metro.ts b/packages/vscode-extension/src/project/metro.ts index 3949a980d..109af4c8b 100644 --- a/packages/vscode-extension/src/project/metro.ts +++ b/packages/vscode-extension/src/project/metro.ts @@ -46,10 +46,6 @@ type MetroEvent = transformedFileCount: number; totalFileCount: number; } - | { - type: "RNIDE_Expo_Env_Prelude_Lines"; - expoEnvPreludeLines: number; - } | { type: "RNIDE_initialize_done"; port: number; @@ -68,7 +64,6 @@ type MetroEvent = export class Metro implements Disposable { private subprocess?: ChildProcess; private _port = 0; - private _initialBundleLineOffset = 0; private startPromise: Promise | undefined; private usesNewDebugger?: Boolean; @@ -85,10 +80,6 @@ export class Metro implements Disposable { return this._port; } - public get initialBundleLineOffset() { - return this._initialBundleLineOffset; - } - public dispose() { this.subprocess?.kill(9); } @@ -216,12 +207,6 @@ export class Metro implements Disposable { } switch (event.type) { - case "RNIDE_Expo_Env_Prelude_Lines": - this._initialBundleLineOffset = event.expoEnvPreludeLines; - Logger.debug( - `Metro initial bundle offset is set to ${this._initialBundleLineOffset}` - ); - break; case "RNIDE_initialize_done": this._port = event.port; Logger.info(`Metro started on port ${this._port}`); From 1912a1e5892355ba1e342a31e381009eda2cdd17 Mon Sep 17 00:00:00 2001 From: filip131311 Date: Thu, 17 Oct 2024 22:37:45 +0200 Subject: [PATCH 05/12] changes after CR --- packages/vscode-extension/src/debugging/DebugAdapter.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index d3431afc6..9e13408a6 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -163,8 +163,7 @@ export class DebugAdapter extends DebugSession { // in upcoming versions and if the changes are still not added bump the version below. // more over offset should only be applied to the main bundle sourcemap as other files // (generated during reload) do not contain the prelude causing the issue - const shouldApplyOffset = - semver.lte(getReactNativeVersion(), "0.76.0") && this.sourceMaps.length === 0; + const shouldApplyOffset = semver.lte(getReactNativeVersion(), "0.76.0"); Logger.debug("Expo prelude lines were detected and an offset was set to:", lineOffset); this.sourceMaps.push([ From 8c309a1c8d4aba041ad8f9d7956e2f8d592a48d1 Mon Sep 17 00:00:00 2001 From: filip131311 Date: Thu, 17 Oct 2024 22:38:48 +0200 Subject: [PATCH 06/12] changes after CR --- packages/vscode-extension/src/debugging/DebugAdapter.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index 9e13408a6..75396c866 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -164,7 +164,12 @@ export class DebugAdapter extends DebugSession { // more over offset should only be applied to the main bundle sourcemap as other files // (generated during reload) do not contain the prelude causing the issue const shouldApplyOffset = semver.lte(getReactNativeVersion(), "0.76.0"); - Logger.debug("Expo prelude lines were detected and an offset was set to:", lineOffset); + if (lineOffset && shouldApplyOffset) { + Logger.debug( + "Expo prelude lines were detected and an offset was set to:", + lineOffset + ); + } this.sourceMaps.push([ message.params.url, From 4f37c39f2d2be024b86104ba388ac5c6a9a7927b Mon Sep 17 00:00:00 2001 From: filip131311 Date: Thu, 17 Oct 2024 22:44:54 +0200 Subject: [PATCH 07/12] changes after CR --- packages/vscode-extension/src/debugging/DebugAdapter.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index 75396c866..f8c46350a 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -161,8 +161,6 @@ export class DebugAdapter extends DebugSession { // unfortunately it still requires changes to metro that were attempted here // https://github.com/facebook/metro/pull/1284 we should monitor the situation // in upcoming versions and if the changes are still not added bump the version below. - // more over offset should only be applied to the main bundle sourcemap as other files - // (generated during reload) do not contain the prelude causing the issue const shouldApplyOffset = semver.lte(getReactNativeVersion(), "0.76.0"); if (lineOffset && shouldApplyOffset) { Logger.debug( From e590d624a26858327e162004573d5753e6a4c51d Mon Sep 17 00:00:00 2001 From: filip131311 Date: Fri, 18 Oct 2024 13:16:29 +0200 Subject: [PATCH 08/12] move to passing informatoion througsh metro --- .../vscode-extension/lib/metro_helpers.js | 12 +++++------- .../src/debugging/DebugAdapter.ts | 19 +++++++++++-------- .../src/debugging/DebugSession.ts | 1 + .../vscode-extension/src/project/metro.ts | 10 ++++++++++ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/vscode-extension/lib/metro_helpers.js b/packages/vscode-extension/lib/metro_helpers.js index d3489ec22..42c68f08d 100644 --- a/packages/vscode-extension/lib/metro_helpers.js +++ b/packages/vscode-extension/lib/metro_helpers.js @@ -42,16 +42,14 @@ function adaptMetroConfig(config) { } } else if (module.path === "__env__") { // this handles @expo/env plugin, which is used to inject environment variables - // the code below instantiates a global variable __EXPO_ENV_PRELUDE_LINES__ that stores - // the number of lines in the prelude. This is used to calculate the line number offset + // the code below exposes the number of lines in the prelude. + // This is used to calculate the line number offset // when reporting line numbers from the JS runtime, breakpoints // and uncaught exceptions. The reason why this is needed, is that // metro doesn't include __env__ prelude in the source map resulting in the source map - // transformation getting shifted by the number of lines in the prelude. - const expoEnvCode = module.output[0].data.code; - if (!expoEnvCode.includes("__EXPO_ENV_PRELUDE_LINES__")) { - module.output[0].data.code = `${expoEnvCode};var __EXPO_ENV_PRELUDE_LINES__=${module.output[0].data.lineCount};`; - } + // transformation getting shifted by the number of lines in the expo generated prelude. + process.stdout.write(JSON.stringify({ type: "RNIDE_expo_env_prelude_lines", lineOffset: module.output[0].data.lineCount })); + process.stdout.write("\n"); } return origProcessModuleFilter(module); }; diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index f8c46350a..7977ecb73 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -86,6 +86,7 @@ export class DebugAdapter extends DebugSession { private projectPathAlias?: string; private threads: Array = []; private sourceMaps: Array<[string, string, SourceMapConsumer, number]> = []; + private lineOffset: number; private linesStartAt1 = true; private columnsStartAt1 = true; @@ -97,6 +98,7 @@ export class DebugAdapter extends DebugSession { this.absoluteProjectPath = configuration.absoluteProjectPath; this.projectPathAlias = configuration.projectPathAlias; this.connection = new WebSocket(configuration.websocketAddress); + this.lineOffset = configuration.lineOffset; this.connection.on("open", () => { // the below catch handler is used to ignore errors coming from non critical CDP messages we @@ -152,20 +154,21 @@ export class DebugAdapter extends DebugSession { const sourceMap = JSON.parse(decodedData); const consumer = await new SourceMapConsumer(sourceMap); - const sourceFile = await (await fetch(message.params.url)).text(); - const lineOffsetRegex = /__EXPO_ENV_PRELUDE_LINES__=(\d+);/; - const match = sourceFile.match(lineOffsetRegex); - const lineOffset = Number(match ? match[1] : null); + // This is a heuristic that checks if the source map should contain __env__ + // module that is added by expo, but not reported in the source map + const isFileWithOffset = sourceMap.sources.includes("__prelude__"); + // This line is here because of a problem with sourcemaps for expo projects, // that was addressed in this PR https://github.com/expo/expo/pull/29463, // unfortunately it still requires changes to metro that were attempted here // https://github.com/facebook/metro/pull/1284 we should monitor the situation // in upcoming versions and if the changes are still not added bump the version below. - const shouldApplyOffset = semver.lte(getReactNativeVersion(), "0.76.0"); - if (lineOffset && shouldApplyOffset) { + const shouldApplyOffset = + semver.lte(getReactNativeVersion(), "0.76.0") && isFileWithOffset; + if (this.lineOffset !== 0 && shouldApplyOffset) { Logger.debug( "Expo prelude lines were detected and an offset was set to:", - lineOffset + this.lineOffset ); } @@ -173,7 +176,7 @@ export class DebugAdapter extends DebugSession { message.params.url, message.params.scriptId, consumer, - shouldApplyOffset ? lineOffset : 0, + shouldApplyOffset ? this.lineOffset : 0, ]); this.updateBreakpointsInSource(message.params.url, consumer); } diff --git a/packages/vscode-extension/src/debugging/DebugSession.ts b/packages/vscode-extension/src/debugging/DebugSession.ts index 93a03d531..0a8a2c83a 100644 --- a/packages/vscode-extension/src/debugging/DebugSession.ts +++ b/packages/vscode-extension/src/debugging/DebugSession.ts @@ -56,6 +56,7 @@ export class DebugSession implements Disposable { websocketAddress: websocketAddress, absoluteProjectPath: getAppRootFolder(), projectPathAlias: this.metro.isUsingNewDebugger ? "/[metro-project]" : undefined, + lineOffset: this.metro.expoPreludeLineOffset, }, { suppressDebugStatusbar: true, diff --git a/packages/vscode-extension/src/project/metro.ts b/packages/vscode-extension/src/project/metro.ts index 109af4c8b..f6e2428a3 100644 --- a/packages/vscode-extension/src/project/metro.ts +++ b/packages/vscode-extension/src/project/metro.ts @@ -46,6 +46,7 @@ type MetroEvent = transformedFileCount: number; totalFileCount: number; } + | { type: "RNIDE_expo_env_prelude_lines"; lineOffset: number } | { type: "RNIDE_initialize_done"; port: number; @@ -66,6 +67,7 @@ export class Metro implements Disposable { private _port = 0; private startPromise: Promise | undefined; private usesNewDebugger?: Boolean; + private _expoPreludeLineOffset: number = 0; constructor(private readonly devtools: Devtools, private readonly delegate: MetroDelegate) {} @@ -80,6 +82,10 @@ export class Metro implements Disposable { return this._port; } + public get expoPreludeLineOffset() { + return this._expoPreludeLineOffset; + } + public dispose() { this.subprocess?.kill(9); } @@ -207,6 +213,10 @@ export class Metro implements Disposable { } switch (event.type) { + case "RNIDE_expo_env_prelude_lines": + this._expoPreludeLineOffset = event.lineOffset; + Logger.debug("Expo prelude line offset was set to: ", this._expoPreludeLineOffset); + break; case "RNIDE_initialize_done": this._port = event.port; Logger.info(`Metro started on port ${this._port}`); From d5b61ea3adab62a91a7886740a9f8c48b4c693af Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Tue, 22 Oct 2024 11:19:11 +0200 Subject: [PATCH 09/12] Update packages/vscode-extension/src/debugging/DebugAdapter.ts --- .../vscode-extension/src/debugging/DebugAdapter.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index 7977ecb73..dfa2d885b 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -158,11 +158,11 @@ export class DebugAdapter extends DebugSession { // module that is added by expo, but not reported in the source map const isFileWithOffset = sourceMap.sources.includes("__prelude__"); - // This line is here because of a problem with sourcemaps for expo projects, - // that was addressed in this PR https://github.com/expo/expo/pull/29463, - // unfortunately it still requires changes to metro that were attempted here - // https://github.com/facebook/metro/pull/1284 we should monitor the situation - // in upcoming versions and if the changes are still not added bump the version below. + // When using expo <${PUT_VERSION_HERE}, source maps skip the prelude module which is + // included in the main bundle at the start. As a result, all lines in the main bundle are shifted by + // the amount of lines the prelude file adds. To offset this, we use the lineOffset parameter that we pass + // to the source maps list that is used later on to correct line numbers when translating from generated + // to the original positions. const shouldApplyOffset = semver.lte(getReactNativeVersion(), "0.76.0") && isFileWithOffset; if (this.lineOffset !== 0 && shouldApplyOffset) { From fb61f3cedf07e02404796da1b2af4850cab6db9b Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Tue, 22 Oct 2024 11:19:23 +0200 Subject: [PATCH 10/12] Update packages/vscode-extension/src/debugging/DebugAdapter.ts --- packages/vscode-extension/src/debugging/DebugAdapter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index dfa2d885b..1af6dd0d9 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -154,8 +154,8 @@ export class DebugAdapter extends DebugSession { const sourceMap = JSON.parse(decodedData); const consumer = await new SourceMapConsumer(sourceMap); - // This is a heuristic that checks if the source map should contain __env__ - // module that is added by expo, but not reported in the source map + // We detect when a source map for the entire bundle is loaded by checking + // if __env__ module is present in the sources. const isFileWithOffset = sourceMap.sources.includes("__prelude__"); // When using expo <${PUT_VERSION_HERE}, source maps skip the prelude module which is From d28b7963ae1ca8fe2b797513c4e6a57f7e5700c7 Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Tue, 22 Oct 2024 11:29:32 +0200 Subject: [PATCH 11/12] Small adjustments of the code and conditions --- .../vscode-extension/lib/metro_helpers.js | 7 ++++- .../src/debugging/DebugAdapter.ts | 31 +++++++++---------- .../src/debugging/DebugSession.ts | 2 +- .../vscode-extension/src/project/metro.ts | 12 +++---- 4 files changed, 28 insertions(+), 24 deletions(-) diff --git a/packages/vscode-extension/lib/metro_helpers.js b/packages/vscode-extension/lib/metro_helpers.js index 42c68f08d..9dcc19c23 100644 --- a/packages/vscode-extension/lib/metro_helpers.js +++ b/packages/vscode-extension/lib/metro_helpers.js @@ -48,7 +48,12 @@ function adaptMetroConfig(config) { // and uncaught exceptions. The reason why this is needed, is that // metro doesn't include __env__ prelude in the source map resulting in the source map // transformation getting shifted by the number of lines in the expo generated prelude. - process.stdout.write(JSON.stringify({ type: "RNIDE_expo_env_prelude_lines", lineOffset: module.output[0].data.lineCount })); + process.stdout.write( + JSON.stringify({ + type: "RNIDE_expo_env_prelude_lines", + lineCount: module.output[0].data.lineCount, + }) + ); process.stdout.write("\n"); } return origProcessModuleFilter(module); diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index 1af6dd0d9..33335929a 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -86,7 +86,7 @@ export class DebugAdapter extends DebugSession { private projectPathAlias?: string; private threads: Array = []; private sourceMaps: Array<[string, string, SourceMapConsumer, number]> = []; - private lineOffset: number; + private expoPreludeLineCount: number; private linesStartAt1 = true; private columnsStartAt1 = true; @@ -98,7 +98,7 @@ export class DebugAdapter extends DebugSession { this.absoluteProjectPath = configuration.absoluteProjectPath; this.projectPathAlias = configuration.projectPathAlias; this.connection = new WebSocket(configuration.websocketAddress); - this.lineOffset = configuration.lineOffset; + this.expoPreludeLineCount = configuration.expoPreludeLineCount; this.connection.on("open", () => { // the below catch handler is used to ignore errors coming from non critical CDP messages we @@ -154,29 +154,28 @@ export class DebugAdapter extends DebugSession { const sourceMap = JSON.parse(decodedData); const consumer = await new SourceMapConsumer(sourceMap); - // We detect when a source map for the entire bundle is loaded by checking - // if __env__ module is present in the sources. - const isFileWithOffset = sourceMap.sources.includes("__prelude__"); - - // When using expo <${PUT_VERSION_HERE}, source maps skip the prelude module which is - // included in the main bundle at the start. As a result, all lines in the main bundle are shifted by - // the amount of lines the prelude file adds. To offset this, we use the lineOffset parameter that we pass - // to the source maps list that is used later on to correct line numbers when translating from generated - // to the original positions. - const shouldApplyOffset = - semver.lte(getReactNativeVersion(), "0.76.0") && isFileWithOffset; - if (this.lineOffset !== 0 && shouldApplyOffset) { + // We detect when a source map for the entire bundle is loaded by checking if __prelude__ module is present in the sources. + const isMainBundle = sourceMap.sources.includes("__prelude__"); + + // Expo env plugin has a bug that causes the bundle to include so-called expo prelude module named __env__ + // which is not present in the source map. As a result, the line numbers are shifted by the amount of lines + // the __env__ module adds. If we detect that main bundle is loaded, but __env__ is not there, we use the provided + // expoPreludeLineCount which reflects the number of lines in __env__ module to offset the line numbers in the source map. + const bundleContainsExpoPrelude = sourceMap.sources.includes("__env__"); + let lineOffset = 0; + if (isMainBundle && !bundleContainsExpoPrelude) { Logger.debug( "Expo prelude lines were detected and an offset was set to:", - this.lineOffset + this.expoPreludeLineCount ); + lineOffset = this.expoPreludeLineCount; } this.sourceMaps.push([ message.params.url, message.params.scriptId, consumer, - shouldApplyOffset ? this.lineOffset : 0, + lineOffset, ]); this.updateBreakpointsInSource(message.params.url, consumer); } diff --git a/packages/vscode-extension/src/debugging/DebugSession.ts b/packages/vscode-extension/src/debugging/DebugSession.ts index 0a8a2c83a..fa473003f 100644 --- a/packages/vscode-extension/src/debugging/DebugSession.ts +++ b/packages/vscode-extension/src/debugging/DebugSession.ts @@ -56,7 +56,7 @@ export class DebugSession implements Disposable { websocketAddress: websocketAddress, absoluteProjectPath: getAppRootFolder(), projectPathAlias: this.metro.isUsingNewDebugger ? "/[metro-project]" : undefined, - lineOffset: this.metro.expoPreludeLineOffset, + expoPreludeLineCount: this.metro.expoPreludeLineCount, }, { suppressDebugStatusbar: true, diff --git a/packages/vscode-extension/src/project/metro.ts b/packages/vscode-extension/src/project/metro.ts index f6e2428a3..9cc49e508 100644 --- a/packages/vscode-extension/src/project/metro.ts +++ b/packages/vscode-extension/src/project/metro.ts @@ -46,7 +46,7 @@ type MetroEvent = transformedFileCount: number; totalFileCount: number; } - | { type: "RNIDE_expo_env_prelude_lines"; lineOffset: number } + | { type: "RNIDE_expo_env_prelude_lines"; lineCount: number } | { type: "RNIDE_initialize_done"; port: number; @@ -67,7 +67,7 @@ export class Metro implements Disposable { private _port = 0; private startPromise: Promise | undefined; private usesNewDebugger?: Boolean; - private _expoPreludeLineOffset: number = 0; + private _expoPreludeLineCount = 0; constructor(private readonly devtools: Devtools, private readonly delegate: MetroDelegate) {} @@ -82,8 +82,8 @@ export class Metro implements Disposable { return this._port; } - public get expoPreludeLineOffset() { - return this._expoPreludeLineOffset; + public get expoPreludeLineCount() { + return this._expoPreludeLineCount; } public dispose() { @@ -214,8 +214,8 @@ export class Metro implements Disposable { switch (event.type) { case "RNIDE_expo_env_prelude_lines": - this._expoPreludeLineOffset = event.lineOffset; - Logger.debug("Expo prelude line offset was set to: ", this._expoPreludeLineOffset); + this._expoPreludeLineCount = event.lineCount; + Logger.debug("Expo prelude line offset was set to: ", this._expoPreludeLineCount); break; case "RNIDE_initialize_done": this._port = event.port; From 7ecfcb56074ac37b623f9777dcdc587a27cd9d5f Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Tue, 22 Oct 2024 11:50:52 +0200 Subject: [PATCH 12/12] Make sure we log expo prelude message when there's some offset we apply --- packages/vscode-extension/src/debugging/DebugAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vscode-extension/src/debugging/DebugAdapter.ts b/packages/vscode-extension/src/debugging/DebugAdapter.ts index 33335929a..50e786a90 100644 --- a/packages/vscode-extension/src/debugging/DebugAdapter.ts +++ b/packages/vscode-extension/src/debugging/DebugAdapter.ts @@ -163,7 +163,7 @@ export class DebugAdapter extends DebugSession { // expoPreludeLineCount which reflects the number of lines in __env__ module to offset the line numbers in the source map. const bundleContainsExpoPrelude = sourceMap.sources.includes("__env__"); let lineOffset = 0; - if (isMainBundle && !bundleContainsExpoPrelude) { + if (isMainBundle && !bundleContainsExpoPrelude && this.expoPreludeLineCount > 0) { Logger.debug( "Expo prelude lines were detected and an offset was set to:", this.expoPreludeLineCount