From 6ae4f6535761017b2f24de0ae9f40f221c714343 Mon Sep 17 00:00:00 2001 From: Bao Nguyen Date: Mon, 24 Aug 2026 17:38:47 +0700 Subject: [PATCH 1/2] fix: resolve absolute stack frame paths in the webpack compiler The dev server resolves symbolicated stack frames to absolute paths before handing them to the compiler. The Rspack compiler guards this with path.isAbsolute, but the webpack one joined them onto the project root a second time, so the file was never found and the redbox lost its code frame. Apply the same guard to the webpack compiler. --- .../webpack-symbolicate-absolute-paths.md | 5 +++ .../repack/src/commands/webpack/Compiler.ts | 4 +- .../webpack/__tests__/Compiler.test.ts | 42 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 .changeset/webpack-symbolicate-absolute-paths.md diff --git a/.changeset/webpack-symbolicate-absolute-paths.md b/.changeset/webpack-symbolicate-absolute-paths.md new file mode 100644 index 000000000..4ac623244 --- /dev/null +++ b/.changeset/webpack-symbolicate-absolute-paths.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack": patch +--- + +Fix missing source code frames on the React Native redbox when using webpack. The dev server resolves symbolicated stack frames to absolute paths, but the webpack compiler joined them onto the project root a second time, so the file was never found and every frame was rendered without its source. diff --git a/packages/repack/src/commands/webpack/Compiler.ts b/packages/repack/src/commands/webpack/Compiler.ts index 76d27b086..5cede2b7e 100644 --- a/packages/repack/src/commands/webpack/Compiler.ts +++ b/packages/repack/src/commands/webpack/Compiler.ts @@ -276,7 +276,9 @@ export class Compiler implements CompilerInterface { } try { - const filePath = path.join(this.rootDir, filename); + const filePath = path.isAbsolute(filename) + ? filename + : path.join(this.rootDir, filename); const source = await fs.promises.readFile(filePath, 'utf8'); return source; } catch { diff --git a/packages/repack/src/commands/webpack/__tests__/Compiler.test.ts b/packages/repack/src/commands/webpack/__tests__/Compiler.test.ts index b89debb99..bf7327915 100644 --- a/packages/repack/src/commands/webpack/__tests__/Compiler.test.ts +++ b/packages/repack/src/commands/webpack/__tests__/Compiler.test.ts @@ -1,8 +1,11 @@ import type { EventEmitter } from 'node:events'; import { Worker } from 'node:worker_threads'; +import { fs, vol } from 'memfs'; import type { Reporter } from '../../../logging/types.js'; import { Compiler } from '../Compiler.js'; +jest.mock('node:fs', () => jest.requireActual('memfs').fs); + jest.mock('node:worker_threads', () => { const { EventEmitter } = jest.requireActual('node:events'); @@ -70,3 +73,42 @@ test('terminates active workers when closed', async () => { expect(worker.terminate).toHaveBeenCalledTimes(1); }); + +describe('getSource', () => { + const reporter: Reporter = { + process: jest.fn(), + flush: jest.fn(), + stop: jest.fn(), + }; + + const createCompiler = () => + new Compiler(['ios'], { host: '' }, reporter, '/project', '/react-native'); + + beforeEach(() => { + vol.reset(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('reads an absolute filename as-is', async () => { + vol.fromJSON({ '/outside/project/file.js': 'absolute source' }); + const readFile = jest.spyOn(fs.promises, 'readFile'); + + await expect( + createCompiler().getSource('/outside/project/file.js', 'ios') + ).resolves.toBe('absolute source'); + expect(readFile).toHaveBeenCalledWith('/outside/project/file.js', 'utf8'); + }); + + test('resolves a relative filename against the project root', async () => { + vol.fromJSON({ '/project/src/index.js': 'source under the project root' }); + const readFile = jest.spyOn(fs.promises, 'readFile'); + + await expect( + createCompiler().getSource('src/index.js', 'ios') + ).resolves.toBe('source under the project root'); + expect(readFile).toHaveBeenCalledWith('/project/src/index.js', 'utf8'); + }); +}); From fac96c1a425155a835f3e6fc8354ed1a31636528 Mon Sep 17 00:00:00 2001 From: Daniel Williams Date: Sun, 13 Sep 2026 16:35:57 +0100 Subject: [PATCH 2/2] chore: narrow changeset wording to the getSource fallback path --- .changeset/webpack-symbolicate-absolute-paths.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/webpack-symbolicate-absolute-paths.md b/.changeset/webpack-symbolicate-absolute-paths.md index 4ac623244..2d0d15d27 100644 --- a/.changeset/webpack-symbolicate-absolute-paths.md +++ b/.changeset/webpack-symbolicate-absolute-paths.md @@ -2,4 +2,4 @@ "@callstack/repack": patch --- -Fix missing source code frames on the React Native redbox when using webpack. The dev server resolves symbolicated stack frames to absolute paths, but the webpack compiler joined them onto the project root a second time, so the file was never found and every frame was rendered without its source. +Fix the webpack compiler double-joining absolute paths in `getSource`. The dev server resolves symbolicated stack frames to absolute paths before asking the compiler for their source, but the webpack compiler joined them onto the project root a second time, so the lookup failed. This only affected the fallback used when a frame's source is not embedded in the source map, for example with `nosources-*` devtools, and now matches the Rspack compiler.