Complete CLI implementation for build, serve, and other commands. The CLI provides the primary interface for interacting with Broccoli from the command line.
Main CLI function that processes command line arguments and executes appropriate actions.
/**
* Main CLI function for processing command line arguments
* @param args - Array of command line arguments
* @param ui - Optional UI instance for output (defaults to console-ui)
* @returns Promise that resolves when command is complete
*/
function cli(args: string[], ui?: UI): Promise<any>;Usage Examples:
const { cli } = require("broccoli");
const { UI } = require("console-ui");
// Process command line arguments
const ui = new UI();
// Build command
await cli(['build', 'dist', '--environment=production'], ui);
// Serve command
await cli(['serve', '--port=4200', '--host=localhost'], ui);
// Custom arguments
await cli(['build', 'output', '--prod', '--brocfile=./config/Brocfile.js'], ui);The CLI supports several command types with different option sets.
Build the project once and output to a directory.
interface BuildOptions {
/** Path to Brocfile (optional, auto-discovered if not specified) */
brocfilePath?: string;
/** Output directory path */
outputPath?: string;
/** Current working directory */
cwd?: string;
/** Enable file watching for continuous builds */
watch?: boolean;
/** File watcher implementation to use */
watcher?: string;
/** Build environment (development/production/test) */
environment: string;
/** Shorthand for --environment=production */
prod?: boolean;
/** Shorthand for --environment=development */
dev?: boolean;
}CLI Usage:
# Basic build
broccoli build dist
# Production build
broccoli build dist --prod
broccoli build dist --environment=production
# Development build
broccoli build dist --dev
broccoli build dist --environment=development
# Build with file watching
broccoli build dist --watch
# Custom Brocfile
broccoli build dist --brocfile=./config/Brocfile.js
# Custom working directory
broccoli build dist --cwd=/path/to/projectStart a development server with file watching.
interface ServeOptions {
/** Host address to bind to */
host: string;
/** Port number to listen on */
port: string;
/** Enable HTTPS */
ssl: boolean;
/** Path to SSL private key file */
sslKey: string;
/** Path to SSL certificate file */
sslCert: string;
/** Path to Brocfile (optional) */
brocfilePath?: string;
/** Output directory path (optional) */
outputPath?: string;
/** Current working directory */
cwd?: string;
/** Disable file watching */
noWatch?: boolean;
/** File watcher implementation to use */
watcher?: string;
/** Build environment */
environment: string;
/** Shorthand for --environment=production */
prod?: boolean;
/** Shorthand for --environment=development */
dev?: boolean;
/** Enable file watching (opposite of noWatch) */
watch: boolean;
}CLI Usage:
# Basic development server
broccoli serve
# Custom host and port
broccoli serve --host=0.0.0.0 --port=3000
# HTTPS server
broccoli serve --ssl --ssl-key=./certs/key.pem --ssl-cert=./certs/cert.pem
# Production server (not recommended for production use)
broccoli serve --prod
# Disable file watching
broccoli serve --no-watch
# Custom watcher
broccoli serve --watcher=pollingInternal functions used by the CLI for processing options and creating instances.
/**
* Build Brocfile options from CLI arguments
* @param options - CLI options containing environment
* @returns BrocfileOptions object
*/
function buildBrocfileOptions(options: { environment: string }): BrocfileOptions;
/**
* Get Builder instance from CLI options
* @param options - CLI options containing environment
* @returns Builder instance
*/
function getBuilder(options: { environment: string }): Builder;
/**
* Get appropriate Watcher class based on CLI options
* @param options - CLI options containing watch flag
* @returns Watcher class (regular Watcher or DummyWatcher)
*/
function getWatcher(options: { watch?: boolean }): typeof Watcher;
/**
* Build watcher options from CLI arguments
* @param options - CLI options containing watcher preference
* @param ui - UI instance for logging
* @returns Watcher options object
*/
function buildWatcherOptions(options: { watcher?: string }, ui: UI): any;
/**
* Validate and guard output directory
* @param outputDir - Output directory path to validate
* @throws Error if output directory is invalid
*/
function guardOutputDir(outputDir: string): void;Usage Examples:
const {
buildBrocfileOptions,
getBuilder,
getWatcher,
buildWatcherOptions,
guardOutputDir
} = require("broccoli");
// Build Brocfile options
const brocfileOptions = buildBrocfileOptions({ environment: 'production' });
// Result: { env: 'production' }
// Get Builder
const builder = getBuilder({ environment: 'development' });
// Get Watcher class
const WatcherClass = getWatcher({ watch: true });
const DummyWatcherClass = getWatcher({ watch: false });
// Build watcher options
const watcherOptions = buildWatcherOptions({ watcher: 'polling' }, ui);
// Validate output directory
try {
guardOutputDir('./dist');
} catch (error) {
console.error('Invalid output directory:', error.message);
}Common CLI usage patterns for different scenarios.
Development Workflow:
# Start development server with live reloading
broccoli serve --host=localhost --port=4200
# Build with file watching for testing
broccoli build tmp/dev --watch --dev
# Build for testing
broccoli build tmp/test --environment=testProduction Workflow:
# Production build
broccoli build dist --prod
# Production build with custom Brocfile
broccoli build dist --prod --brocfile=./config/production.js
# Verify production build
broccoli build tmp/prod-verify --prod --no-watchCI/CD Integration:
# Build for different environments
broccoli build dist --environment=${NODE_ENV}
# Test build (no output)
broccoli build tmp/ci-test --environment=test
# Production build with validation
broccoli build dist --prod && test -d dist/assetsThe CLI provides comprehensive error handling and user-friendly messages.
const { cli } = require("broccoli");
try {
await cli(['build', 'dist', '--prod']);
} catch (error) {
if (error instanceof CliError) {
console.error('CLI Error:', error.message);
process.exit(1);
} else if (error.isBuildError) {
console.error('Build failed:', error.message);
process.exit(2);
} else {
console.error('Unexpected error:', error);
process.exit(3);
}
}Common Error Scenarios:
# Missing output directory
broccoli build
# Error: Output directory is required
# Invalid environment
broccoli build dist --environment=invalid
# Error: Invalid environment: invalid
# Missing Brocfile
broccoli build dist --brocfile=nonexistent.js
# Error: Brocfile not found: nonexistent.js
# Port already in use
broccoli serve --port=3000
# Error: Port 3000 is already in use
# Permission denied
broccoli build /root/dist
# Error: Permission denied: /root/distThe CLI respects several environment variables for configuration.
# Set default environment
export BROCCOLI_ENV=production
broccoli build dist # Uses production environment
# Custom temporary directory
export TMPDIR=/custom/tmp
broccoli build dist
# Node environment fallback
export NODE_ENV=production
broccoli build dist # Uses production if BROCCOLI_ENV not setExamples of integrating Broccoli CLI with other build tools.
package.json Scripts:
{
"scripts": {
"build": "broccoli build dist --prod",
"build:dev": "broccoli build dist --dev",
"serve": "broccoli serve --port=4200",
"serve:prod": "broccoli serve --prod --port=3000",
"watch": "broccoli build tmp --watch --dev",
"clean": "rm -rf dist tmp"
}
}Makefile Integration:
.PHONY: build serve clean
build:
broccoli build dist --prod
serve:
broccoli serve --host=localhost --port=4200
clean:
rm -rf dist tmp
watch:
broccoli build tmp --watch --devDocker Integration:
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["broccoli", "serve", "--host=0.0.0.0", "--port=3000", "--prod"]Advanced patterns and customizations for complex scenarios.
Custom UI Implementation:
const { cli } = require("broccoli");
class CustomUI {
write(message: string, level?: string): void {
// Custom logging implementation
if (level === 'error') {
console.error(`❌ ${message}`);
} else if (level === 'warn') {
console.warn(`⚠️ ${message}`);
} else {
console.log(`ℹ️ ${message}`);
}
}
writeLine(message: string, level?: string): void {
this.write(message + '\n', level);
}
writeError(error: Error): void {
console.error(`💥 ${error.message}`);
if (error.stack) {
console.error(error.stack);
}
}
}
// Use custom UI
await cli(['build', 'dist'], new CustomUI());Programmatic CLI Usage:
const { cli } = require("broccoli");
// Build programmatically
async function buildProject(env: string, outputDir: string): Promise<void> {
const args = ['build', outputDir, `--environment=${env}`];
await cli(args);
}
// Serve programmatically
async function serveProject(port: number, host: string = 'localhost'): Promise<void> {
const args = ['serve', `--port=${port}`, `--host=${host}`];
await cli(args);
}
// Usage
await buildProject('production', 'dist');
await serveProject(4200, '0.0.0.0');