or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bundling.mdcli-commands.mdconfiguration.mddevelopment-server.mdindex.md
tile.json

development-server.mddocs/

Development Server

Development server functionality providing hot module reloading, WebSocket connections, middleware support, and on-demand bundling for fast development cycles. Metro's development server enables rapid iteration with sub-second reload times.

Capabilities

Run Development Server

Starts an HTTP/HTTPS development server with Metro middleware for serving bundles and assets.

/**
 * Start HTTP/HTTPS development server with Metro middleware
 * @param config - Metro configuration object
 * @param options - Server options and callbacks
 * @returns Promise resolving to server instance
 */
function runServer(
  config: ConfigT,
  options?: RunServerOptions
): Promise<RunServerResult>;

interface RunServerOptions {
  /** Indicate reduced performance mode */
  hasReducedPerformance?: boolean;
  /** Host to bind server to (default: localhost) */
  host?: string;
  /** Error callback for server errors */
  onError?: (error: Error & {code?: string}) => void;
  /** Callback when server is ready */
  onReady?: (server: HttpServer | HttpsServer) => void;
  /** Callback when server closes */
  onClose?: () => void;
  /** HTTPS server options */
  secureServerOptions?: Record<string, unknown>;
  /** Enable HTTPS (deprecated, use secureServerOptions) */
  secure?: boolean;
  /** HTTPS certificate path (deprecated) */
  secureCert?: string;
  /** HTTPS private key path (deprecated) */
  secureKey?: string;
  /** Additional middleware handlers */
  unstable_extraMiddleware?: ReadonlyArray<HandleFunction>;
  /** Wait for bundler initialization */
  waitForBundler?: boolean;
  /** Enable file watching */
  watch?: boolean;
  /** Custom WebSocket endpoints */
  websocketEndpoints?: Record<string, WebsocketServer>;
}

interface RunServerResult {
  /** HTTP or HTTPS server instance */
  httpServer: HttpServer | HttpsServer;
}

Usage Example:

import { runServer, loadConfig } from "metro";
import https from "https";
import fs from "fs";

const config = await loadConfig({
  projectRoot: process.cwd(),
});

// Start HTTPS development server
const { httpServer } = await runServer(config, {
  host: "0.0.0.0",
  secureServerOptions: {
    key: fs.readFileSync("./certs/key.pem"),
    cert: fs.readFileSync("./certs/cert.pem"),
  },
  onReady: (server) => {
    const address = server.address();
    console.log(`Metro server running on https://${address.address}:${address.port}`);
  },
  onError: (error) => {
    console.error("Server error:", error.message);
  },
  waitForBundler: true,
  watch: true,
});

// Handle graceful shutdown
process.on("SIGTERM", () => {
  httpServer.close();
});

Create Connect Middleware

Creates Connect-compatible middleware for integration with existing Express or Connect applications.

/**
 * Create Connect-compatible middleware for custom servers
 * @param config - Metro configuration object
 * @param options - Middleware creation options
 * @returns Promise resolving to Metro middleware object
 */
function createConnectMiddleware(
  config: ConfigT,
  options?: RunMetroOptions
): Promise<MetroMiddleWare>;

interface MetroMiddleWare {
  /** Attach HMR server to existing HTTP server */
  attachHmrServer: (httpServer: HttpServer | HttpsServer) => void;
  /** Cleanup middleware and server */
  end: () => Promise<void>;
  /** Metro server instance */
  metroServer: MetroServer;
  /** Connect middleware function */
  middleware: Middleware;
}

interface RunMetroOptions extends ServerOptions {
  /** Wait for bundler initialization before resolving */
  waitForBundler?: boolean;
}

Usage Example:

import express from "express";
import { createConnectMiddleware, loadConfig } from "metro";

const config = await loadConfig({
  projectRoot: process.cwd(),
});

// Create Express app with Metro middleware
const app = express();
const server = http.createServer(app);

// Add Metro middleware
const {
  middleware,
  attachHmrServer,
  end: endMiddleware,
} = await createConnectMiddleware(config, {
  waitForBundler: true,
});

app.use("/api", (req, res) => {
  res.json({ status: "ok" });
});

// Add Metro middleware after custom routes
app.use(middleware);

// Attach HMR server for hot reloading
attachHmrServer(server);

server.listen(3000, () => {
  console.log("Server with Metro running on http://localhost:3000");
});

// Handle shutdown
process.on("SIGTERM", async () => {
  await endMiddleware();
  server.close();
});

Hot Module Reloading

Metro provides built-in hot module reloading through WebSocket connections for rapid development feedback.

/**
 * HMR Server for managing hot module reloading connections
 */
class HmrServer {
  constructor(
    bundler: IncrementalBundler,
    createModuleId: (path: string) => number,
    config: ConfigT
  );

  /** Handle client connection for HMR */
  onClientConnect(
    requestUrl: string,
    sendFn: (data: string) => void
  ): Promise<Client>;

  /** Handle client disconnection */
  onClientDisconnect(client: Client): void;

  /** Handle client messages */
  onClientMessage(client: Client, message: string): void;
}

interface WebsocketServer extends EventEmitter {
  handleUpgrade<T = WebsocketServer>(
    request: IncomingMessage,
    socket: Duplex,
    upgradeHead: Buffer,
    callback: (client: T, request: IncomingMessage) => void
  ): void;
  emit(event: 'connection', ws: T, request: IncomingMessage): boolean;
}

interface Client {
  id: string;
  send: (data: string) => void;
  close: () => void;
}

Usage Example:

// HMR is automatically enabled when using runServer or createConnectMiddleware
// Client-side code connects to WebSocket endpoint at /hot

// Custom WebSocket endpoints can be added:
const { httpServer } = await runServer(config, {
  websocketEndpoints: {
    "/custom": customWebSocketServer,
  },
});

Development Utilities

Reporting and Progress

Metro provides built-in reporting for development feedback and build progress.

/**
 * Terminal-based reporter for development console output
 */
class TerminalReporter {
  constructor(terminal: Terminal);
  
  /** Update reporter with build events */
  update(event: TerminalReportableEvent): void;
}

/**
 * JSON-based reporter for structured logging
 */
class JsonReporter {
  constructor(stream: Writable);
  
  /** Update reporter with build events */
  update(event: ReportableEvent): void;
}

/**
 * Terminal utility for reporter output
 */
class Terminal {
  constructor(stream: Writable);
}

Server Configuration

interface ServerConfig {
  /** Server port (default: 8081) */
  port: number;
  /** Enable HTTPS */
  useGlobalHotkey: boolean;
  /** Middleware enhancement function */
  enhanceMiddleware?: (
    middleware: Middleware,
    server: MetroServer
  ) => Middleware;
  /** Verify requests before processing */
  verifyConnections: boolean;
}

interface ServerOptions {
  /** Indicate reduced performance mode */
  hasReducedPerformance?: boolean;
  /** Enable file watching */
  watch?: boolean;
}

type Middleware = (
  req: IncomingMessage,
  res: ServerResponse,
  next: (error?: Error) => void
) => void;

type HandleFunction = (
  req: IncomingMessage,
  res: ServerResponse,
  next: (error?: Error) => void
) => void;

Error Handling

Development server includes comprehensive error handling for common development scenarios:

// Server startup errors
const { httpServer } = await runServer(config, {
  onError: (error) => {
    if (error.code === "EADDRINUSE") {
      console.error("Port already in use");
    } else if (error.code === "EACCES") {
      console.error("Permission denied");
    } else {
      console.error("Server error:", error.message);
    }
  },
});

// Middleware errors are handled automatically and reported through the reporter system