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
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ beforeEach(async () => {
.use(
createDevServerMiddleware({
serverBaseUrl: new URL(baseUrl),
projectRoot: '/project',
watchFolders: ['/project'],
}),
)
Expand All @@ -46,8 +47,15 @@ afterEach(() => {
});

describe('createDevServerMiddleware', () => {
test('serves /status', async () => {
const resp = await fetch(baseUrl + '/status');

expect(resp.status).toBe(200);
expect(await resp.text()).toBe('packager-status:running');
});

test('refuses a cross-origin request before any route', async () => {
const resp = await fetch(baseUrl + '/open-url', {
const resp = await fetch(baseUrl + '/status', {
headers: {Origin: 'https://example.com'},
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import statusMiddleware from '../statusMiddleware';
import connect from 'connect';
import http from 'node:http';

jest.useRealTimers();

async function fetchStatus(projectRoot: string): Promise<Response> {
const app = connect().use('/status', statusMiddleware({projectRoot}));
const server = http.createServer(app);
await new Promise(resolve => server.listen(0, 'localhost', resolve));
try {
const address = server.address();
if (address == null || typeof address === 'string') {
throw new Error('Expected the server to listen on a TCP port');
}
return await fetch(`http://localhost:${address.port}/status`);
} finally {
server.closeAllConnections();
await new Promise(resolve => server.close(() => resolve()));
}
}

describe('/status', () => {
// React Native apps and `isDevServerRunning` depend on this exact response.
test('reports that the packager is running for the project root', async () => {
const resp = await fetchStatus('/mocked/path');

expect(resp.status).toBe(200);
expect(await resp.text()).toBe('packager-status:running');
expect(resp.headers.get('X-React-Native-Project-Root')).toBe(
'/mocked/path',
);
expect(resp.headers.get('Cache-Control')).toBe('no-store');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import type {Server} from 'connect';
import openStackFrameMiddleware from './openStackFrameMiddleware';
import openURLMiddleware from './openURLMiddleware';
import securityHeadersMiddleware from './securityHeadersMiddleware';
import statusMiddleware from './statusMiddleware';
import compression from 'compression';
import connect from 'connect';
import nocache from 'nocache';

type Options = Readonly<{
serverBaseUrl: URL,
projectRoot: string,
watchFolders: ReadonlyArray<string>,
}>;

Expand All @@ -28,12 +30,14 @@ type Options = Readonly<{
*/
export default function createDevServerMiddleware({
serverBaseUrl,
projectRoot,
watchFolders,
}: Options): Server {
return connect()
.use(securityHeadersMiddleware({serverBaseUrl}))
.use(compression())
.use(nocache())
.use('/open-stack-frame', openStackFrameMiddleware({watchFolders}))
.use('/open-url', openURLMiddleware());
.use('/open-url', openURLMiddleware())
.use('/status', statusMiddleware({projectRoot}));
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export default async function isDevServerRunning(
return 'not_running';
}

// FIXME: Depends on @react-native-community/cli-server-api
const statusResponse = await fetch(`${devServerUrl}/status`);
const body = await statusResponse.text();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ const unusedMiddlewareStub: Server = {};
// (@react-native-community/cli-server-api) and should be migrated into core.
// e.g. used by packages/react-native/Libraries/Core/Devtools/:
// - /symbolicate
// e.g. used by ./isDevServerRunning.js:
// - /status
const communityMiddlewareFallback: DevServerMiddlewareFactory = () => ({
middleware: unusedMiddlewareStub,
websocketEndpoints: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ async function runDevServer(
});
const pluginMiddleware = createDevServerMiddleware({
serverBaseUrl: new URL(devServerUrl),
projectRoot,
watchFolders: [unstable_serverRoot ?? projectRoot, ...watchFolders],
});

Expand Down
31 changes: 31 additions & 0 deletions packages/community-cli-plugin/src/dev-server/statusMiddleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/

import type {NextHandleFunction} from 'connect';

type Options = Readonly<{
projectRoot: string,
}>;

/**
* Report that this dev server is running for `projectRoot`, so that the app
* and `isDevServerRunning` can tell it from another process on the port.
*/
export default function statusMiddleware({
projectRoot,
}: Options): NextHandleFunction {
const projectRootHeader = new URL(`file:///${projectRoot}`).pathname.slice(1);

return (req, res) => {
res.setHeader('X-React-Native-Project-Root', projectRootHeader);
res.setHeader('Cache-Control', 'no-store');
res.end('packager-status:running');
};
}
Loading