Creating Custom Processors
Processors in Ecopages are plugins that handle file transformations during the build process. This guide will show you how to create your own processor.
Basic Structure
A processor extends the Processor abstract class from @ecopages/core:
import { Processor } from '@ecopages/core/plugins/processor';
import type { BunPlugin } from 'bun';
class CustomProcessor extends Processor {
// Plugins used during build time
buildPlugins: BunPlugin[] = [];
// Plugins used during runtime
plugins: BunPlugin[] = [];
constructor(config: ProcessorConfig) {
super({
name: 'custom-processor',
watch: {
paths: ['src/custom'],
extensions: ['.custom'],
},
...config,
});
}
async setup(): Promise<void> {
// Initialize resources, create cache directories, etc.
}
async process(input: unknown): Promise<unknown> {
// Transform the input
return input;
}
async teardown(): Promise<void> {
// Cleanup resources
}
}Processor Lifecycle
- Setup: Called when the processor is initialized
- Process: Called for each file that matches the processor's configuration
- Teardown: Called when the processor is being shut down
Working with Cache
Processors have built-in caching capabilities:
class CustomProcessor extends Processor {
async process(input: string): Promise<string> {
const cacheKey = 'my-cache-key';
// Try to read from cache first
const cached = await this.readCache<string>(cacheKey);
if (cached) return cached;
// Process and cache result
const result = await someExpensiveOperation(input);
await this.writeCache(cacheKey, result);
return result;
}
}Adding Bun Plugins
You can add Bun plugins for both build time and runtime:
import { plugin } from 'bun';
class CustomProcessor extends Processor {
buildPlugins = [
plugin({
name: 'custom-build-plugin',
setup(build) {
// Build-time plugin configuration
},
}),
];
plugins = [
plugin({
name: 'custom-runtime-plugin',
setup(build) {
// Runtime plugin configuration
},
}),
];
}File Watching
Configure file watching for development mode:
class CustomProcessor extends Processor {
constructor() {
super({
name: 'custom-processor',
watch: {
paths: ['src/custom'],
extensions: ['.custom'],
onChange: async ({ path, bridge }) => {
// Handle file changes
// bridge.reload() or bridge.cssUpdate(path)
},
onDelete: async ({ path, bridge }) => {
// Handle file deletions
},
},
});
}
}Using the Processor
Register your processor in the Ecopages configuration:
import { ConfigBuilder } from '@ecopages/core';
const customProcessor = new CustomProcessor({
// processor options
});
const config = await new ConfigBuilder().addProcessor(customProcessor).build();Communicating with the Browser (ClientBridge)
Processors can broadcast events to connected browser clients for live updates (e.g., CSS hot reload) using the bridge provided in the onChange callback:
class StyleProcessor extends Processor {
constructor() {
super({
name: 'style-processor',
watch: {
paths: ['src/styles'],
extensions: ['.css'],
onChange: async ({ path, bridge }) => {
await this.processFile(path, bridge);
},
},
});
}
private async processFile(filePath: string, bridge: ClientBridge) {
if (!this.context) return;
// Process and write the file...
const processed = await this.process(await Bun.file(filePath).text());
await Bun.write(outputPath, processed);
// Broadcast CSS update to connected browsers
// This will trigger a hot reload of the stylesheet
bridge.cssUpdate(filePath);
// Or trigger a full reload if needed:
// bridge.reload();
// Or send a custom error:
// bridge.error("Processing failed");
}
}The ClientBridge provides several utility methods:
reload(): Triggers a full page reload.cssUpdate(path): Triggers a CSS hot update for the given path.update(path): Triggers a JS module update.error(message): Sends an error notification to the client.broadcast(event): Sends a rawClientBridgeEvent.
Best Practices
- Use meaningful names for your processors
- Implement proper error handling
- Cache expensive operations
- Clean up resources in teardown
- Follow TypeScript best practices
- Document your processor's API
- Use
bridgefor live updates instead of full reloads when possible