Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/webpack-symbolicate-absolute-paths.md
Original file line number Diff line number Diff line change
@@ -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.
4 changes: 3 additions & 1 deletion packages/repack/src/commands/webpack/Compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
42 changes: 42 additions & 0 deletions packages/repack/src/commands/webpack/__tests__/Compiler.test.ts
Original file line number Diff line number Diff line change
@@ -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<typeof import('node:events')>('node:events');
Expand Down Expand Up @@ -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');
});
});