or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

build-system.mdcli.mdconfiguration.mddev-server.mderror-handling.mdfile-watching.mdindex.md
tile.json

dev-server.mddocs/

Development Server

HTTP server for serving built assets during development with live reloading support. The server system provides both standalone server functionality and Express/Connect middleware.

Capabilities

serve Function

High-level function to start a development server with file watching.

/**
 * Start a development server with file watching
 * @param watcher - Watcher instance for rebuilds
 * @param host - Host address to bind to
 * @param port - Port number to listen on
 * @param connect - Connect instance (optional, uses require('connect') by default)
 * @param process - Process object (optional, uses global process by default)
 * @param ui - UI instance for logging
 * @param ssl - Enable HTTPS
 * @param sslKey - Path to SSL private key file
 * @param sslCert - Path to SSL certificate file
 * @returns Promise that resolves when server is running
 */
function serve(
  watcher: Watcher,
  host: string,
  port: string,
  connect?: any,
  process?: NodeJS.Process,
  ui?: UI,
  ssl?: boolean,
  sslKey?: string,
  sslCert?: string
): Promise<void>;

Usage Examples:

const { serve, Watcher, Builder, loadBrocfile } = require("broccoli");
const { UI } = require("console-ui");

// Basic development server
const buildFn = loadBrocfile();
const tree = buildFn({ env: 'development' });
const builder = new Builder(tree);
const watcher = new Watcher(builder, builder.watchedSourceNodeWrappers);
const ui = new UI();

// Start HTTP server
await serve(watcher, 'localhost', '4200', undefined, process, ui, false, '', '');
console.log('Development server running at http://localhost:4200');

// Start HTTPS server
await serve(
  watcher, 
  'localhost', 
  '4443', 
  undefined, 
  process, 
  ui, 
  true,
  './ssl/server.key',
  './ssl/server.crt'
);
console.log('Development server running at https://localhost:4443');

Server Class

Lower-level server class for more control over server lifecycle.

/**
 * HTTP server class for development
 */
class Server extends EventEmitter {
  /**
   * Creates a new Server instance
   * @param watcher - Watcher instance for rebuilds
   * @param host - Host address to bind to
   * @param port - Port number to listen on
   * @param connect - Connect instance
   * @param ui - UI instance for logging
   * @param ssl - Enable HTTPS
   * @param sslKey - Path to SSL private key file
   * @param sslCert - Path to SSL certificate file
   */
  constructor(
    watcher: Watcher,
    host: string,
    port: string,
    connect: any,
    ui: UI,
    ssl: boolean,
    sslKey: string,
    sslCert: string
  );
  
  /** Connect application instance */
  app: any | null;
  
  /** HTTP/HTTPS server instance */
  instance: any | null;
  
  /**
   * Start the server
   * @returns Promise that resolves when server is listening
   */
  start(): Promise<void>;
  
  /**
   * Stop the server
   * @returns Promise that resolves when server is closed
   */
  stop(): Promise<void>;
}

Usage Examples:

const { Server } = require("broccoli");
const connect = require("connect");
const { UI } = require("console-ui");

// Create server instance
const server = new Server(
  watcher,
  'localhost',
  '3000',
  connect(),
  new UI(),
  false,  // HTTP
  '',
  ''
);

// Start server
await server.start();
console.log('Server started');

// Stop server when needed
process.on('SIGINT', async () => {
  await server.stop();
  console.log('Server stopped');
  process.exit(0);
});

getMiddleware Function

Express/Connect middleware for integrating Broccoli into existing applications.

/**
 * Create Express/Connect middleware for serving built assets
 * @param watcher - Watcher instance for rebuilds
 * @param options - Middleware configuration options
 * @returns Middleware function for Express/Connect
 */
function getMiddleware(
  watcher: Watcher,
  options?: MiddlewareOptions
): (req: any, res: any, next: any) => Promise<any>;

interface MiddlewareOptions {
  /** Enable automatic directory index listings (default: true) */
  autoIndex?: boolean;
  
  /** Path to live reload script (enables live reloading when specified) */
  liveReloadPath?: string;
}

Usage Examples:

const express = require("express");
const { getMiddleware, Watcher, Builder, loadBrocfile } = require("broccoli");

// Express integration
const app = express();
const buildFn = loadBrocfile();
const tree = buildFn({ env: 'development' });
const builder = new Builder(tree);
const watcher = new Watcher(builder, builder.watchedSourceNodeWrappers);

// Add Broccoli middleware
const broccoliMiddleware = getMiddleware(watcher, {
  autoIndex: true,
  liveReloadPath: '/livereload.js'
});

app.use('/assets', broccoliMiddleware);

// Start Express server
app.listen(3000, () => {
  console.log('Express server with Broccoli middleware running on port 3000');
});

// Connect integration
const connect = require("connect");

const connectApp = connect();
connectApp.use('/build', getMiddleware(watcher, { autoIndex: false }));

connectApp.listen(4000, () => {
  console.log('Connect server with Broccoli middleware running on port 4000');
});

Live Reloading

The development server supports live reloading for automatic browser refresh.

Enable Live Reloading:

// With middleware
const middleware = getMiddleware(watcher, {
  liveReloadPath: '/livereload.js'
});

// Include in HTML
// <script src="/livereload.js"></script>

Custom Live Reload Setup:

const express = require("express");
const { getMiddleware } = require("broccoli");

const app = express();

// Serve live reload script
app.get('/livereload.js', (req, res) => {
  res.type('application/javascript');
  res.send(`
    // Live reload client code
    const eventSource = new EventSource('/events');
    eventSource.onmessage = function(event) {
      if (event.data === 'reload') {
        window.location.reload();
      }
    };
  `);
});

// Server-sent events for reload notifications
app.get('/events', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });
  
  // Send reload event when build completes
  watcher.on('buildSuccess', () => {
    res.write('data: reload\n\n');
  });
});

app.use('/assets', getMiddleware(watcher));

Server Configuration

Common server configuration patterns for different development needs.

Basic Development Server:

await serve(
  watcher,
  'localhost',
  '4200',
  undefined,
  process,
  ui,
  false,  // HTTP
  '',
  ''
);

HTTPS Development Server:

await serve(
  watcher,
  'localhost',
  '4443',
  undefined,
  process,
  ui,
  true,   // HTTPS
  './certificates/server.key',
  './certificates/server.crt'
);

Network Accessible Server:

await serve(
  watcher,
  '0.0.0.0',  // Bind to all interfaces
  '4200',
  undefined,
  process,
  ui,
  false,
  '',
  ''
);

Error Handling

Proper error handling for server operations.

const { serve, Server } = require("broccoli");

try {
  await serve(watcher, 'localhost', '4200', undefined, process, ui, false, '', '');
} catch (error) {
  if (error.code === 'EADDRINUSE') {
    console.error('Port 4200 is already in use');
    // Try alternative port
    await serve(watcher, 'localhost', '4201', undefined, process, ui, false, '', '');
  } else if (error.code === 'EACCES') {
    console.error('Permission denied - try a port above 1024');
  } else {
    console.error('Server error:', error.message);
  }
}

// Server class error handling
const server = new Server(watcher, 'localhost', '3000', connect(), ui, false, '', '');

server.on('error', (error) => {
  console.error('Server error:', error);
});

try {
  await server.start();
} catch (error) {
  console.error('Failed to start server:', error);
}

Custom Connect App

Integration with custom Connect applications.

const connect = require("connect");
const { getMiddleware } = require("broccoli");
const serveStatic = require("serve-static");
const compression = require("compression");

const app = connect();

// Add middleware stack
app.use(compression());
app.use('/public', serveStatic('./public'));
app.use('/assets', getMiddleware(watcher, { autoIndex: true }));

// Custom request logging
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

// Start server
const server = require('http').createServer(app);
server.listen(3000, 'localhost', () => {
  console.log('Custom Connect server running on http://localhost:3000');
});