Skip to content

Commit 841c099

Browse files
committed
feat: configurable Metro port
1 parent 5cc5afc commit 841c099

File tree

21 files changed

+104
-57
lines changed

21 files changed

+104
-57
lines changed

packages/bundler-metro/src/constants.ts

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/bundler-metro/src/factory.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import connect from 'connect';
66
import nocache from 'nocache';
77
import { isPortAvailable, getMetroPackage } from './utils.js';
88
import { MetroPortUnavailableError } from './errors.js';
9-
import { METRO_PORT } from './constants.js';
109
import type { MetroInstance, MetroOptions } from './types.js';
1110
import {
1211
type Reporter,
@@ -41,18 +40,19 @@ export const getMetroInstance = async (
4140
abortSignal: AbortSignal
4241
): Promise<MetroInstance> => {
4342
const { projectRoot, harnessConfig } = options;
44-
const isDefaultPortAvailable = await isPortAvailable(METRO_PORT);
43+
const metroPort = harnessConfig.metroPort;
44+
const isMetroPortAvailable = await isPortAvailable(metroPort);
4545

46-
if (!isDefaultPortAvailable) {
47-
throw new MetroPortUnavailableError(METRO_PORT);
46+
if (!isMetroPortAvailable) {
47+
throw new MetroPortUnavailableError(metroPort);
4848
}
4949

5050
const Metro = getMetroPackage(projectRoot);
5151

5252
process.env.RN_HARNESS = 'true';
5353

5454
const projectMetroConfig = await Metro.loadConfig({
55-
port: METRO_PORT,
55+
port: metroPort,
5656
projectRoot,
5757
});
5858
const config = await withRnHarness(projectMetroConfig, true)();
@@ -62,7 +62,7 @@ export const getMetroInstance = async (
6262

6363
const middleware = connect()
6464
.use(nocache())
65-
.use('/', getExpoMiddleware(projectRoot, harnessConfig.entryPoint))
65+
.use('/', getExpoMiddleware(projectRoot, harnessConfig.entryPoint, metroPort))
6666
.use('/status', getStatusMiddleware(projectRoot));
6767

6868
const ready = waitForBundler(reporter, abortSignal);

packages/bundler-metro/src/middlewares/expo-middleware.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import crypto from 'node:crypto';
44
import { getResolvedEntryPointWithoutExtension } from '../entry-point-utils.js';
55

66
export const getExpoMiddleware =
7-
(projectRoot: string, entryPoint: string) =>
7+
(projectRoot: string, entryPoint: string, metroPort: number) =>
88
(req: IncomingMessage, res: ServerResponse, next: NextFunction) => {
99
if (req.url !== '/') {
1010
next();
@@ -24,7 +24,7 @@ export const getExpoMiddleware =
2424
launchAsset: {
2525
key: 'bundle',
2626
contentType: 'application/javascript',
27-
url: `http://localhost:8081/${resolvedEntryPoint}.bundle?platform=${platform}&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=1&transform.routerRoot=app&transform.reactCompiler=true&unstable_transformProfile=hermes-stable`,
27+
url: `http://localhost:${metroPort}/${resolvedEntryPoint}.bundle?platform=${platform}&dev=true&hot=false&lazy=true&transform.engine=hermes&transform.bytecode=1&transform.routerRoot=app&transform.reactCompiler=true&unstable_transformProfile=hermes-stable`,
2828
},
2929
assets: [],
3030
metadata: {},
@@ -35,7 +35,7 @@ export const getExpoMiddleware =
3535
version: '1.0.0',
3636
},
3737
expoGo: {
38-
debuggerHost: 'localhost:8081',
38+
debuggerHost: `localhost:${metroPort}`,
3939
developer: {
4040
tool: 'expo-cli',
4141
projectRoot,

packages/bundler-metro/src/prewarm.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { METRO_PORT } from './constants.js';
21
import { getResolvedEntryPointWithoutExtension } from './entry-point-utils.js';
32

43
type PrewarmOptions = {
@@ -8,6 +7,7 @@ type PrewarmOptions = {
87
dev: boolean;
98
minify: boolean;
109
signal: AbortSignal;
10+
metroPort: number;
1111
};
1212

1313
export const prewarmMetroBundle = async (
@@ -23,7 +23,7 @@ export const prewarmMetroBundle = async (
2323
dev: String(dev),
2424
minify: String(minify),
2525
});
26-
const url = `http://localhost:${METRO_PORT}/${resolvedEntryPoint}.bundle?${searchParams.toString()}`;
26+
const url = `http://localhost:${options.metroPort}/${resolvedEntryPoint}.bundle?${searchParams.toString()}`;
2727

2828
const response = await fetch(url, { signal });
2929

packages/config/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ export const ConfigSchema = z
2222
runners: z.array(RunnerSchema).min(1, 'At least one runner is required'),
2323
defaultRunner: z.string().optional(),
2424
host: z.string().min(1, 'Host is required').optional(),
25+
metroPort: z
26+
.number()
27+
.min(1, 'Metro port must be at least 1')
28+
.max(65535, 'Metro port must be at most 65535')
29+
.optional()
30+
.default(8081),
2531
webSocketPort: z.number().optional().default(3001),
2632
bridgeTimeout: z
2733
.number()

packages/jest/src/cli-args.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { hideBin } from 'yargs/helpers';
33

44
export type HarnessCliArgs = {
55
harnessRunner?: string;
6+
metroPort?: number;
67
};
78

89
export const getAdditionalCliArgs = (): HarnessCliArgs => {
@@ -11,6 +12,10 @@ export const getAdditionalCliArgs = (): HarnessCliArgs => {
1112
type: 'string',
1213
description: 'Specify which Harness runner to use',
1314
})
15+
.option('metroPort', {
16+
type: 'number',
17+
description: 'Override the Metro bundler port',
18+
})
1419
.strict(false)
1520
.help(false)
1621
.version(false)
@@ -19,5 +24,6 @@ export const getAdditionalCliArgs = (): HarnessCliArgs => {
1924

2025
return {
2126
harnessRunner: argv.harnessRunner,
27+
metroPort: argv.metroPort,
2228
};
2329
};

packages/jest/src/harness.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ const getHarnessInternal = async (
182182
dev: true,
183183
minify: false,
184184
signal,
185+
metroPort: config.metroPort,
185186
});
186187
logMetroPrewarmCompleted(platform);
187188
await appMonitor.start();

packages/jest/src/setup.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ export const setup = async (globalConfig: JestConfig.GlobalConfig) => {
5757
});
5858

5959
const cliArgs = getAdditionalCliArgs();
60+
61+
if (cliArgs.metroPort != null) {
62+
harnessConfig.metroPort = cliArgs.metroPort;
63+
}
64+
6065
const selectedRunner = getHarnessRunner(harnessConfig, cliArgs);
6166

6267
if (globalConfig.collectCoverage) {

packages/platform-android/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"dependencies": {
1919
"@react-native-harness/platforms": "workspace:*",
2020
"@react-native-harness/tools": "workspace:*",
21-
"@react-native-harness/config": "workspace:*",
2221
"zod": "^3.25.67",
2322
"tslib": "^2.3.0"
2423
},

packages/platform-android/src/adb.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,3 +265,19 @@ export const getConnectedDevices = async (): Promise<AdbDevice[]> => {
265265

266266
return devices;
267267
};
268+
269+
export const setDebugHttpHost = async (
270+
adbId: string,
271+
bundleId: string,
272+
host: string
273+
): Promise<void> => {
274+
const prefsPath = `shared_prefs/${bundleId}_preferences.xml`;
275+
const prefsContent = `<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?><map><string name=\\"debug_http_host\\">${host}</string></map>`;
276+
await spawn('adb', [
277+
'-s',
278+
adbId,
279+
'shell',
280+
`run-as ${bundleId} sh -c 'mkdir -p shared_prefs && printf "${prefsContent}" > ${prefsPath}'`,
281+
]);
282+
};
283+

0 commit comments

Comments
 (0)