Core build orchestration functionality for executing transformation pipelines and managing build state. The Builder class is the heart of Broccoli's build system.
The main class responsible for orchestrating the build process, managing temporary directories, and tracking build state.
/**
* Core build orchestration class that manages node graph construction and execution
*/
class Builder {
/**
* Creates a new Builder instance
* @param outputNode - The final output node in the build graph
* @param options - Optional builder configuration
*/
constructor(outputNode: Node, options?: BuilderOptions);
/** Path to the build output directory */
readonly outputPath: string;
/** Unique identifier for the current build */
readonly buildId: number;
/** Array of file paths that are being watched for changes */
readonly watchedPaths: string[];
/** Array of file paths that are not being watched */
readonly unwatchedPaths: string[];
/** Temporary directory path used during builds */
readonly tmpdir: string | null;
/** The output node being built */
readonly outputNode: Node;
/** Get node wrappers for watched source nodes */
readonly watchedSourceNodeWrappers: SourceNodeWrapper[];
/** Get all node wrappers in the build graph */
readonly nodeWrappers: NodeWrapper[];
/** Get broccoli node info features */
readonly features: any;
/**
* Execute the build process
* @returns Promise that resolves when build is complete
*/
build(): Promise<void>;
/**
* Cancel the current build operation
* @returns Promise that resolves when cancellation is complete
*/
cancel(): Promise<void>;
/**
* Clean up temporary files and resources
* @returns Promise that resolves when cleanup is complete
*/
cleanup(): Promise<void>;
/**
* Create a node wrapper for a build graph node
* @param node - The node to wrap
* @param stack - Optional stack trace information
* @returns Appropriate node wrapper instance
*/
makeNodeWrapper(node: Node, stack?: any): NodeWrapper;
/**
* Validate that all input paths exist
* @throws Error if required input paths are missing
*/
checkInputPathsExist(): void;
}
interface BuilderOptions {
/** Custom temporary directory path (optional) */
tmpdir?: string;
}Usage Examples:
const { Builder, loadBrocfile } = require("broccoli");
// Basic build
const buildFn = loadBrocfile({ brocfilePath: './Brocfile.js' });
const tree = buildFn({ env: 'production' });
const builder = new Builder(tree);
try {
await builder.build();
console.log('Build completed successfully');
console.log('Output path:', builder.outputPath);
console.log('Build ID:', builder.buildId);
} catch (error) {
console.error('Build failed:', error);
} finally {
await builder.cleanup();
}
// Build with custom temporary directory
const builderWithCustomTmp = new Builder(tree, {
tmpdir: '/custom/tmp/path'
});
// Access build information
console.log('Watched paths:', builder.watchedPaths);
console.log('Unwatched paths:', builder.unwatchedPaths);
console.log('Node wrappers:', builder.nodeWrappers.length);Wrapper classes that encapsulate different types of nodes in the build graph.
/**
* Base class for all node wrappers in the build system
*/
abstract class NodeWrapper {
/** Unique identifier for this node */
readonly id: number;
/** Human-readable label for this node */
readonly label: string;
/** Path to the node's cache directory */
readonly cachePath: string;
/** Path to the node's output directory */
readonly outputPath: string;
/** Node information metadata */
readonly nodeInfo: NodeInfo;
/** Array of input node wrappers */
readonly inputNodeWrappers: NodeWrapper[];
/** Build state tracking */
readonly buildState: {
selfTime?: number;
totalTime?: number;
built?: boolean;
};
/** Current revision number for change detection */
readonly revision: number;
/**
* Trigger a revision update
*/
revise(): void;
/**
* Convert node to JSON representation
*/
toJSON(): any;
/**
* Format instantiation stack for terminal display
*/
formatInstantiationStackForTerminal(): string;
/**
* Convert node info to JSON
*/
nodeInfoToJSON(): any;
}
/**
* Wrapper for source nodes (file system inputs)
*/
class SourceNodeWrapper extends NodeWrapper {
readonly nodeInfo: SourceNodeInfo;
/**
* Set up the source node
*/
setup(): void;
/**
* Build the source node (usually no-op)
*/
build(): void;
/**
* String representation of the source node
*/
toString(): string;
}
/**
* Wrapper for transform nodes (processing steps)
*/
class TransformNodeWrapper extends NodeWrapper {
readonly nodeInfo: TransformNodeInfo;
readonly inputPaths: string[];
readonly callbackObject: CallbackObject;
/**
* Set up the transform node with features
* @param features - Available broccoli features
*/
setup(features: any): void;
/**
* Determine if this node needs to be rebuilt
* @returns true if rebuild is needed
*/
shouldBuild(): boolean;
/**
* Execute the transform operation
*/
build(): Promise<void>;
/**
* String representation of the transform node
*/
toString(): string;
}The Builder class exposes error classes and node wrapper classes as static properties.
class Builder {
/** BuilderError class for general builder errors */
static BuilderError: typeof BuilderError;
/** InvalidNodeError class for invalid node errors */
static InvalidNodeError: typeof Error;
/** NodeSetupError class for node setup failures */
static NodeSetupError: typeof NodeSetupError;
/** BuildError class for build failures */
static BuildError: typeof BuildError;
/** NodeWrapper base class */
static NodeWrapper: typeof NodeWrapper;
/** TransformNodeWrapper class */
static TransformNodeWrapper: typeof TransformNodeWrapper;
/** SourceNodeWrapper class */
static SourceNodeWrapper: typeof SourceNodeWrapper;
}Usage Examples:
const { Builder } = require("broccoli");
// Access error classes
try {
await builder.build();
} catch (error) {
if (Builder.BuilderError.isBuilderError(error)) {
console.log('Builder error occurred:', error.message);
}
if (error instanceof Builder.BuildError) {
console.log('Build failed with payload:', error.broccoliPayload);
}
}
// Work with node wrappers
const nodeWrappers = builder.nodeWrappers;
for (const wrapper of nodeWrappers) {
console.log(`Node ${wrapper.id}: ${wrapper.label}`);
console.log(`Output: ${wrapper.outputPath}`);
console.log(`Revision: ${wrapper.revision}`);
if (wrapper instanceof Builder.SourceNodeWrapper) {
console.log('Source node:', wrapper.toString());
} else if (wrapper instanceof Builder.TransformNodeWrapper) {
console.log('Transform node:', wrapper.toString());
console.log('Should rebuild:', wrapper.shouldBuild());
}
}The build system supports cancellation of long-running builds.
/**
* Cancellation request handler for interrupting builds
*/
class CancelationRequest {
/**
* Create a new cancellation request
* @param pendingWork - Promise representing work that can be cancelled
*/
constructor(pendingWork: Promise<void>);
/** Check if cancellation has been requested */
readonly isCanceled: boolean;
/**
* Throw cancellation error if cancellation was requested
*/
throwIfRequested(): void;
/**
* Cancel the pending work
*/
cancel(): Promise<void>;
/**
* Promise interface support
*/
then(...args: any[]): any;
}Usage Examples:
const { Builder, CancelationRequest } = require("broccoli");
const builder = new Builder(tree);
// Build with cancellation support
const buildPromise = builder.build();
const cancelRequest = new CancelationRequest(buildPromise);
// Cancel after 5 seconds
setTimeout(async () => {
if (!cancelRequest.isCanceled) {
console.log('Cancelling build...');
await cancelRequest.cancel();
}
}, 5000);
try {
await buildPromise;
console.log('Build completed');
} catch (error) {
if (error.isCancellation) {
console.log('Build was cancelled');
}
}