UNPKG

@callstack/repack-dev-server

Version:

A bundler-agnostic development server for React Native applications as part of @callstack/repack.

143 lines (142 loc) 5.13 kB
import { Writable } from 'node:stream'; import middie from '@fastify/middie'; import fastifySensible from '@fastify/sensible'; import { createDevMiddleware } from '@react-native/dev-middleware'; import Fastify from 'fastify'; import apiPlugin from './plugins/api/apiPlugin.js'; import compilerPlugin from './plugins/compiler/compilerPlugin.js'; import devtoolsPlugin from './plugins/devtools/devtoolsPlugin.js'; import faviconPlugin from './plugins/favicon/faviconPlugin.js'; import multipartPlugin from './plugins/multipart/multipartPlugin.js'; import symbolicatePlugin from './plugins/symbolicate/sybmolicatePlugin.js'; import wssPlugin from './plugins/wss/wssPlugin.js'; import { Internal } from './types.js'; import { normalizeOptions } from './utils/normalizeOptions.js'; /** * Create instance of development server, powered by Fastify. * * @param config Server configuration. * @returns `start` and `stop` functions as well as an underlying Fastify `instance`. */ export async function createServer(config) { // biome-ignore lint/style/useConst: needed in fastify constructor let delegate; const options = normalizeOptions(config.options); /** Fastify instance powering the development server. */ const instance = Fastify({ disableRequestLogging: options.disableRequestLogging, logger: { level: 'trace', stream: new Writable({ write: (chunk, _encoding, callback) => { const log = JSON.parse(chunk.toString()); delegate?.logger.onMessage(log); instance.wss?.apiServer.send(log); callback(); }, }), }, ...(options.https ? { https: options.https } : {}), }); delegate = config.delegate({ options, log: instance.log, notifyBuildStart: (platform) => { instance.wss.apiServer.send({ event: Internal.EventTypes.BuildStart, platform, }); }, notifyBuildEnd: (platform) => { instance.wss.apiServer.send({ event: Internal.EventTypes.BuildEnd, platform, }); }, broadcastToHmrClients: (event) => { instance.wss.hmrServer.send(event); }, broadcastToMessageClients: ({ method, params }) => { instance.wss.messageServer.broadcast(method, params); }, }); let handledDevMiddlewareNotice = false; const devMiddleware = createDevMiddleware({ projectRoot: options.rootDir, serverBaseUrl: options.url, logger: { error: instance.log.error, warn: instance.log.warn, info: (...message) => { if (!handledDevMiddlewareNotice) { if (message.join().includes('JavaScript logs have moved!')) { handledDevMiddlewareNotice = true; return; } } else { instance.log.info(message); return; } }, }, unstable_experiments: { // @ts-expect-error removed in 0.76, keep this for backkwards compatibility enableNewDebugger: true, }, }); // Register plugins await instance.register(fastifySensible); await instance.register(middie); await instance.register(wssPlugin, { delegate, endpoints: devMiddleware.websocketEndpoints, }); await instance.register(multipartPlugin); await instance.register(apiPlugin, { delegate, prefix: '/api', }); await instance.register(compilerPlugin, { delegate, }); await instance.register(devtoolsPlugin, { rootDir: options.rootDir, }); await instance.register(symbolicatePlugin, { delegate, }); // below is to prevent showing `GET 400 /favicon.ico` // errors in console when requesting the bundle via browser await instance.register(faviconPlugin); instance.addHook('onSend', async (request, reply, payload) => { reply.header('X-Content-Type-Options', 'nosniff'); reply.header('X-React-Native-Project-Root', options.rootDir); const [pathname] = request.url.split('?'); if (pathname.endsWith('.map')) { reply.header('Access-Control-Allow-Origin', 'devtools://devtools'); } return payload; }); // Register dev middleware instance.use(devMiddleware.middleware); // Register routes instance.get('/', async () => delegate.messages.getHello()); instance.get('/status', async () => delegate.messages.getStatus()); /** Start the development server. */ async function start() { await instance.listen({ port: options.port, host: options.host, }); } /** Stop the development server. */ async function stop() { await instance.close(); } return { start, stop, instance, }; }