Skip to content

Commit 95867ce

Browse files
robhoganfacebook-github-bot
authored andcommitted
Breaking: Return an object containing httpServer from runServer API
Summary: Currently the `runServer` API simply returns the Node.js `HttpServer` (or `HttpsServer`) instance it creates. Because that's not a type we control, we can't extend this API - for example, it can't expose an imperative `end()`, or anything about the Metro server. We've come up against this limitation with this specific API in the past. Since we're about to release a major version with other breaking changes, this takes the opportunity to change the return type to `Promise<{httpServer: HttpServer | HttpsServer}>`, thereby making it extensible in subsequent non-breaking changes. Changelog: ``` - **[Breaking]**: Metro.runServer - return an object containing `httpServer` rather than the server instance directly Reviewed By: huntie Differential Revision: D78099715 fbshipit-source-id: 1aae46cc73a4b9901ef1b9cbffdab66b6d04fd72
1 parent ae6f423 commit 95867ce

File tree

6 files changed

+80
-70
lines changed

6 files changed

+80
-70
lines changed

docs/GettingStarted.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ app.listen(8081);
7676

7777
### Method `runServer(config, options)`
7878

79-
Starts a development server based on the given configuration and options. Returns the server.
79+
Starts a development server based on the given configuration and options. Returns an object with `httpServer`, the Node.js HTTP(S) server.
8080
We recommend using `runMetro` instead of `runServer`, `runMetro` calls this function.
8181

8282
#### Options
@@ -88,7 +88,7 @@ We recommend using `runMetro` instead of `runServer`, `runMetro` calls this func
8888
```js
8989
const config = await Metro.loadConfig();
9090

91-
const metroHttpServer = await Metro.runServer(config, {
91+
await Metro.runServer(config, {
9292
onClose: () => {console.log('metro server and all associated processes are closed')}
9393
});
9494

packages/metro/src/commands/serve.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,10 @@ module.exports = (): {
107107
resetCache: _resetCache,
108108
...runServerOptions
109109
} = argv;
110-
server = await MetroApi.runServer(config, runServerOptions);
110+
({httpServer: server} = await MetroApi.runServer(
111+
config,
112+
runServerOptions,
113+
));
111114

112115
restarting = false;
113116
}

packages/metro/src/index.flow.js

Lines changed: 60 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ export type RunServerOptions = $ReadOnly<{
8585
}>,
8686
}>;
8787

88+
export type RunServerResult = {
89+
httpServer: HttpServer | HttpsServer,
90+
};
91+
8892
type BuildGraphOptions = {
8993
entries: $ReadOnlyArray<string>,
9094
customTransformOptions?: CustomTransformOptions,
@@ -270,7 +274,7 @@ exports.runServer = async (
270274
websocketEndpoints = {},
271275
watch,
272276
}: RunServerOptions = {},
273-
): Promise<HttpServer | HttpsServer> => {
277+
): Promise<RunServerResult> => {
274278
await earlyPortCheck(host, config.server.port);
275279

276280
if (secure != null || secureCert != null || secureKey != null) {
@@ -319,73 +323,68 @@ exports.runServer = async (
319323
} else {
320324
httpServer = http.createServer(serverApp);
321325
}
322-
return new Promise(
323-
(
324-
resolve: (result: HttpServer | HttpsServer) => void,
325-
reject: mixed => mixed,
326-
) => {
327-
httpServer.on('error', error => {
328-
endMiddleware().finally(() => {
329-
onError?.(error);
330-
reject(error);
331-
});
326+
return new Promise((resolve, reject) => {
327+
httpServer.on('error', error => {
328+
endMiddleware().finally(() => {
329+
onError?.(error);
330+
reject(error);
332331
});
332+
});
333333

334-
httpServer.listen(config.server.port, host, () => {
335-
const {address, port, family} = httpServer.address();
336-
config.reporter.update({
337-
type: 'server_listening',
338-
address,
339-
port, // Assigned port if configured with port 0
340-
family,
341-
});
342-
343-
websocketEndpoints = {
344-
...websocketEndpoints,
345-
'/hot': createWebsocketServer({
346-
websocketServer: new MetroHmrServer(
347-
metroServer.getBundler(),
348-
metroServer.getCreateModuleId(),
349-
config,
350-
),
351-
}),
352-
};
353-
354-
httpServer.on('upgrade', (request, socket, head) => {
355-
const {pathname} = parse(request.url);
356-
if (pathname != null && websocketEndpoints[pathname]) {
357-
websocketEndpoints[pathname].handleUpgrade(
358-
request,
359-
socket,
360-
head,
361-
ws => {
362-
websocketEndpoints[pathname].emit('connection', ws, request);
363-
},
364-
);
365-
} else {
366-
socket.destroy();
367-
}
368-
});
369-
370-
if (onReady) {
371-
onReady(httpServer);
372-
}
334+
httpServer.listen(config.server.port, host, () => {
335+
const {address, port, family} = httpServer.address();
336+
config.reporter.update({
337+
type: 'server_listening',
338+
address,
339+
port, // Assigned port if configured with port 0
340+
family,
341+
});
373342

374-
resolve(httpServer);
343+
websocketEndpoints = {
344+
...websocketEndpoints,
345+
'/hot': createWebsocketServer({
346+
websocketServer: new MetroHmrServer(
347+
metroServer.getBundler(),
348+
metroServer.getCreateModuleId(),
349+
config,
350+
),
351+
}),
352+
};
353+
354+
httpServer.on('upgrade', (request, socket, head) => {
355+
const {pathname} = parse(request.url);
356+
if (pathname != null && websocketEndpoints[pathname]) {
357+
websocketEndpoints[pathname].handleUpgrade(
358+
request,
359+
socket,
360+
head,
361+
ws => {
362+
websocketEndpoints[pathname].emit('connection', ws, request);
363+
},
364+
);
365+
} else {
366+
socket.destroy();
367+
}
375368
});
376369

377-
// Disable any kind of automatic timeout behavior for incoming
378-
// requests in case it takes the packager more than the default
379-
// timeout of 120 seconds to respond to a request.
380-
httpServer.timeout = 0;
370+
if (onReady) {
371+
onReady(httpServer);
372+
}
373+
374+
resolve({httpServer});
375+
});
376+
377+
// Disable any kind of automatic timeout behavior for incoming
378+
// requests in case it takes the packager more than the default
379+
// timeout of 120 seconds to respond to a request.
380+
httpServer.timeout = 0;
381381

382-
httpServer.on('close', () => {
383-
endMiddleware()?.finally(() => {
384-
onClose?.();
385-
});
382+
httpServer.on('close', () => {
383+
endMiddleware()?.finally(() => {
384+
onClose?.();
386385
});
387-
},
388-
);
386+
});
387+
});
389388
};
390389

391390
exports.runBuild = async (

packages/metro/src/integration_tests/__tests__/server-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ describe('Metro development server serves bundles via HTTP', () => {
6060

6161
let onCloseResolve;
6262
serverClosedPromise = new Promise(resolve => (onCloseResolve = resolve));
63-
httpServer = await Metro.runServer(config, {
63+
({httpServer} = await Metro.runServer(config, {
6464
reporter: {update() {}},
6565
onClose: () => {
6666
onCloseResolve();
6767
},
68-
});
68+
}));
6969
});
7070

7171
afterEach(async () => {

packages/metro/src/integration_tests/__tests__/server-torn-down-test.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @format
8+
* @flow strict-local
89
* @oncall react_native
910
*/
1011

1112
const Metro = require('../../..');
13+
// $FlowFixMe[cannot-resolve-module] - Untyped module
1214
const asyncHooks = require('async_hooks');
1315

1416
jest.unmock('cosmiconfig');
@@ -18,7 +20,10 @@ jest.useRealTimers();
1820
jest.setTimeout(10000);
1921

2022
describe('Server torn down test', () => {
21-
const active = new Map();
23+
const active = new Map<
24+
number,
25+
{type: string, callStack: string, resource: mixed},
26+
>();
2227
const hook = asyncHooks.createHook({
2328
init(asyncId, type, _triggerAsyncId, resource) {
2429
if (
@@ -76,8 +81,7 @@ describe('Server torn down test', () => {
7681
let onCloseResolve;
7782
const closePromise = new Promise(resolve => (onCloseResolve = resolve));
7883

79-
const httpServer = await Metro.runServer(config, {
80-
reporter: {update() {}},
84+
const {httpServer} = await Metro.runServer(config, {
8185
onClose: () => {
8286
onCloseResolve();
8387
},

packages/metro/types/index.d.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ export interface RunServerOptions {
8282
};
8383
}
8484

85+
export interface RunServerResult {
86+
httpServer: HttpServer | HttpsServer;
87+
}
88+
8589
export interface RunBuildOptions {
8690
entry: string;
8791
dev?: boolean;
@@ -139,7 +143,7 @@ export function createConnectMiddleware(
139143
export function runServer(
140144
config: ConfigT,
141145
options: RunServerOptions,
142-
): Promise<HttpServer | HttpsServer>;
146+
): Promise<RunServerResult>;
143147

144148
export function runBuild(
145149
config: ConfigT,

0 commit comments

Comments
 (0)