Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/bitter-sheep-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@callstack/repack-dev-server": patch
"@callstack/repack": patch
---

Fix opening stack frame source file from LogBox
5 changes: 5 additions & 0 deletions .changeset/chatty-taxes-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@callstack/repack": minor
---

Handle displaying relative paths to sourcefiles in DevTools similarly to Metro
5 changes: 5 additions & 0 deletions .changeset/evil-geckos-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@callstack/repack": patch
---

Don't include 3rd party lib sourcemaps by default into the final sourcemaps
4 changes: 2 additions & 2 deletions apps/tester-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android --appId com.testerapp",
"android": "react-native run-android --appId com.testerapp --no-packager",
"android:release": "node ./scripts/release.js android",
"ios": "react-native run-ios",
"ios": "react-native run-ios --no-packager",
"ios:release": "node ./scripts/release.js ios",
"pods": "(cd ios && bundle install && (bundle exec pod install || bundle exec pod update))",
"pods:update": "(cd ios && bundle install && bundle exec pod update)",
Expand Down
4 changes: 2 additions & 2 deletions apps/tester-federation-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android --appId com.tester.federationV2",
"ios": "react-native run-ios",
"android": "react-native run-android --appId com.tester.federationV2 --no-packager",
"ios": "react-native run-ios --no-packager",
"pods": "(cd ios && bundle install && (bundle exec pod install || bundle exec pod update))",
"pods:update": "(cd ios && bundle install && bundle exec pod update)",
"start:hostapp": "react-native webpack-start --config config.host-app.mts",
Expand Down
4 changes: 2 additions & 2 deletions apps/tester-federation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"version": "0.0.1",
"private": true,
"scripts": {
"android": "react-native run-android --appId com.tester.federation",
"ios": "react-native run-ios",
"android": "react-native run-android --appId com.tester.federation --no-packager",
"ios": "react-native run-ios --no-packager",
"pods": "(cd ios && bundle install && (bundle exec pod install || bundle exec pod update))",
"pods:update": "(cd ios && bundle install && bundle exec pod update)",
"start:hostapp": "react-native webpack-start --config config.host-app.mts",
Expand Down
2 changes: 1 addition & 1 deletion packages/dev-server/src/createServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export async function createServer(config: Server.Config) {
delegate,
});
await instance.register(devtoolsPlugin, {
rootDir: options.rootDir,
delegate,
});
await instance.register(symbolicatePlugin, {
delegate,
Expand Down
8 changes: 4 additions & 4 deletions packages/dev-server/src/plugins/compiler/compilerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ async function compilerPlugin(
},
},
handler: async (request, reply) => {
const filename = (request.params as { '*'?: string })['*'];
const filepath = (request.params as { '*'?: string })['*'];
let { platform } = request.query as { platform?: string };

if (!filename) {
if (!filepath) {
// This technically should never happen - this route should not be called if file is missing.
request.log.debug('File was not provided');
return reply.notFound('File was not provided');
Expand All @@ -49,12 +49,12 @@ async function compilerPlugin(

try {
const asset = await delegate.compiler.getAsset(
filename,
filepath,
platform,
sendProgress
);
const mimeType = delegate.compiler.getMimeType(
filename,
filepath,
platform,
asset
);
Expand Down
10 changes: 7 additions & 3 deletions packages/dev-server/src/plugins/devtools/devtoolsPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { FastifyInstance } from 'fastify';
import fastifyPlugin from 'fastify-plugin';
import launchEditor from 'launch-editor';
import open from 'open';
import type { Server } from '../../types.js';

interface OpenURLRequestBody {
url: string;
Expand All @@ -18,7 +19,10 @@ function parseRequestBody<T>(body: unknown): T {
throw new Error(`Unsupported body type: ${typeof body}`);
}

async function devtoolsPlugin(instance: FastifyInstance) {
async function devtoolsPlugin(
instance: FastifyInstance,
{ delegate }: { delegate: Server.Delegate }
) {
// reference implementation in `@react-native-community/cli-server-api`:
// https://github.com/react-native-community/cli/blob/46436a12478464752999d34ed86adf3212348007/packages/cli-server-api/src/openURLMiddleware.ts
instance.route({
Expand All @@ -40,8 +44,8 @@ async function devtoolsPlugin(instance: FastifyInstance) {
const { file, lineNumber } = parseRequestBody<OpenStackFrameRequestBody>(
request.body
);
// TODO fix rewriting of `webpack://` to rootDir of the project
launchEditor(`${file}:${lineNumber}`, process.env.REACT_EDITOR);
const filepath = delegate.devTools?.resolveProjectPath(file) ?? file;
launchEditor(`${filepath}:${lineNumber}`, process.env.REACT_EDITOR);
reply.send('OK');
},
});
Expand Down
16 changes: 11 additions & 5 deletions packages/dev-server/src/plugins/symbolicate/Symbolicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,23 @@ export class Symbolicator {
};
}

const lookup = consumer.originalPositionFor({
let lookup = consumer.originalPositionFor({
line: frame.lineNumber,
column: frame.column,
bias: SourceMapConsumer.LEAST_UPPER_BOUND,
});

// If lookup fails, we get the same shape object, but with
// all values set to null
if (!lookup.source) {
// It is better to gracefully return the original frame
// than to throw an exception
// fallback to GREATEST_LOWER_BOUND
lookup = consumer.originalPositionFor({
line: frame.lineNumber,
column: frame.column,
bias: SourceMapConsumer.GREATEST_LOWER_BOUND,
});
}

// return the original frame when both lookups fail
if (!lookup.source) {
return {
...frame,
collapse: false,
Expand Down
16 changes: 16 additions & 0 deletions packages/dev-server/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ export namespace Server {
/** A compiler delegate. */
compiler: CompilerDelegate;

/** A DevTools delegate. */
devTools?: DevToolsDelegate;

/** A symbolicator delegate. */
symbolicator: SymbolicatorDelegate;

Expand Down Expand Up @@ -139,6 +142,19 @@ export namespace Server {
onMessage: (log: any) => void;
}

/**
* Delegate with implementation for dev tools functions.
*/
export interface DevToolsDelegate {
/**
* Resolve the project filepath with [projectRoot] prefix.
*
* @param filepath The filepath to resolve.
* @returns The resolved project path.
*/
resolveProjectPath: (filepath: string) => string;
}

/**
* Delegate with implementation for messages used in route handlers.
*/
Expand Down
4 changes: 4 additions & 0 deletions packages/repack/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ module.exports = {
include: ['./src/**/implementation'],
comments: false,
},
{
include: ['./src/**/implementation', './src/modules'],
sourceMaps: false,
},
{
exclude: ['./src/**/implementation', './src/modules'],
presets: [
Expand Down
2 changes: 1 addition & 1 deletion packages/repack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"access": "public"
},
"scripts": {
"build:js": "babel src --out-dir dist --extensions \".js,.ts\" --source-maps --ignore \"**/__tests__/**\" --delete-dir-on-start",
"build:js": "babel src --out-dir dist --extensions \".js,.ts\" --ignore \"**/__tests__/**\" --delete-dir-on-start",
"build:ts": "tsc -p tsconfig.build.json --emitDeclarationOnly",
"build": "pnpm run \"/^build:.*/\"",
"test": "jest",
Expand Down
101 changes: 101 additions & 0 deletions packages/repack/src/commands/common/__tests__/parseUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { parseUrl } from '../parseUrl.js';

describe('parseUrl', () => {
const expectParsed = (
url: string,
expected: { resourcePath: string; platform?: string },
platformList = ['ios', 'android', 'web']
) => {
expect(parseUrl(url, platformList)).toEqual(expected);
};

it('should parse URLs with platform from query parameters', () => {
expectParsed('src/index.js?platform=ios', {
resourcePath: 'src/index.js',
platform: 'ios',
});
expectParsed('components/Button.tsx?platform=android', {
resourcePath: 'components/Button.tsx',
platform: 'android',
});
expectParsed('/absolute/path/file.js?platform=web', {
resourcePath: 'absolute/path/file.js',
platform: 'web',
});
});

it('should parse URLs with platform from pathname', () => {
expectParsed('/ios/src/index.js', {
resourcePath: 'src/index.js',
platform: 'ios',
});
expectParsed('/android/components/Button.tsx', {
resourcePath: 'components/Button.tsx',
platform: 'android',
});
expectParsed('/web/utils/helper.js', {
resourcePath: 'utils/helper.js',
platform: 'web',
});
});

it('should parse URLs with platform from file extension', () => {
expectParsed('src/index.ios.js', {
resourcePath: 'src/index.ios.js',
platform: 'ios',
});
expectParsed('components/Button.android.tsx', {
resourcePath: 'components/Button.android.tsx',
platform: 'android',
});
expectParsed('styles/theme.web.css', {
resourcePath: 'styles/theme.web.css',
platform: 'web',
});
});

it('should handle URLs without platform detection', () => {
expectParsed('src/index.js', {
resourcePath: 'src/index.js',
platform: undefined,
});
expectParsed('/components/Button.tsx', {
resourcePath: 'components/Button.tsx',
platform: undefined,
});
expectParsed('utils/helper.unknown.js', {
resourcePath: 'utils/helper.unknown.js',
platform: undefined,
});
});

it('should prioritize query parameter over pathname and extension', () => {
expectParsed('/android/src/index.ios.js?platform=web', {
resourcePath: 'android/src/index.ios.js',
platform: 'web',
});
expectParsed('/ios/components/Button.android.tsx?platform=web', {
resourcePath: 'ios/components/Button.android.tsx',
platform: 'web',
});
});

it('should work with different platform lists', () => {
expectParsed(
'/react-native/src/index.js',
{
resourcePath: 'src/index.js',
platform: 'react-native',
},
['react-native', 'macos']
);
expectParsed(
'app.macos.js',
{
resourcePath: 'app.macos.js',
platform: 'macos',
},
['react-native', 'macos']
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { resolveProjectPath } from '../resolveProjectPath.js';

describe('resolveProjectPath', () => {
const expectResolved = (
input: string,
expected: string,
root = '/project/root'
) => {
expect(resolveProjectPath(input, root)).toBe(expected);
};

it('should resolve [projectRoot] prefix correctly', () => {
expectResolved('[projectRoot]/src/index.js', '/project/root/src/index.js');
expectResolved(
'[projectRoot]/build/output.js',
'/apps/my-app/build/output.js',
'/apps/my-app'
);
expectResolved(
'[projectRoot]/special-file@2x.png',
'/project/root/special-file@2x.png'
);
expectResolved(
'[projectRoot]/file with spaces.txt',
'/project/root/file with spaces.txt'
);
});

it('should resolve [projectRoot^N] prefix with up-level navigation', () => {
expectResolved('[projectRoot^1]/src/index.js', '/project/src/index.js');
expectResolved('[projectRoot^2]/shared/utils.js', '/shared/utils.js');
expectResolved('[projectRoot^3]/global/config.json', '/global/config.json');
expectResolved(
'[projectRoot^2]/utils/helper.js',
'/deep/nested/utils/helper.js',
'/deep/nested/project/folder'
);
expectResolved(
'[projectRoot^5]/very/deep/file.js',
'/a/very/deep/file.js',
'/a/b/c/d/e/f'
);
});
});
3 changes: 2 additions & 1 deletion packages/repack/src/commands/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export * from './adaptFilenameToPlatform.js';
export * from './getMimeType.js';
export * from './resolveProjectPath.js';
export * from './runAdbReverse.js';
export * from './parseFileUrl.js';
export * from './parseUrl.js';
export * from './resetPersistentCache.js';
export * from './setupInteractions.js';
export * from './setupStatsWriter.js';
Expand Down
29 changes: 0 additions & 29 deletions packages/repack/src/commands/common/parseFileUrl.ts

This file was deleted.

Loading