HTTP server for serving built assets during development with live reloading support. The server system provides both standalone server functionality and Express/Connect middleware.
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');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);
});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');
});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));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,
'',
''
);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);
}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');
});