@fs-eire/wgsl-template
Version:
A powerful template system for generating WGSL (WebGPU Shading Language) code with support for parameters, conditionals, and multiple output formats including C++ code generation.
153 lines ⢠5.8 kB
JavaScript
import * as fs from "fs/promises";
import * as fsSync from "fs";
import * as path from "path";
import { build } from "../src/index.js";
import { Debouncer } from "./debouncer.js";
/**
* File system watcher for WGSL template files
*/
export class TemplateWatcher {
options;
debouncer;
watchers = [];
isBuilding = false;
constructor(options) {
this.options = options;
this.debouncer = new Debouncer(options.debounce);
}
/**
* Start watching the source directories for changes
*/
async start() {
// Verify all source directories exist and get their absolute paths
const absoluteSourceDirs = [];
for (const dir of this.options.sourceDirs) {
const dirPath = typeof dir === "string" ? dir : dir.path;
const resolvedPath = path.resolve(dirPath);
try {
await fs.access(resolvedPath);
absoluteSourceDirs.push(resolvedPath);
}
catch {
throw new Error(`Source directory "${resolvedPath}" does not exist`);
}
}
console.log(`š Starting watch mode...`);
console.log(` Sources: ${absoluteSourceDirs.join(", ")}`);
console.log(` Output: ${path.resolve(this.options.outDir)}`);
console.log(` Template extension: ${this.options.templateExt}`);
console.log(` Debounce delay: ${this.options.debounce}ms`);
// Perform initial build
console.log(`\nā” Running initial build...`);
await this.runBuild();
// Start watching all source directories
for (const srcPath of absoluteSourceDirs) {
await this.setupWatchers(srcPath);
}
console.log(`\nš Watching for changes... (Press Ctrl+C to stop)`);
}
/**
* Stop watching and clean up resources
*/
async stop() {
this.debouncer.cancel();
for (const watcher of this.watchers) {
watcher.close();
}
this.watchers.length = 0;
console.log(`\nā Watch mode stopped.`);
}
/**
* Set up file system watchers recursively
*/
async setupWatchers(dirPath) {
try {
const entries = await fs.readdir(dirPath, { withFileTypes: true }); // Watch the current directory
const watcher = fsSync.watch(dirPath, (eventType, filename) => {
if (filename) {
this.handleFileChange(eventType, path.join(dirPath, filename));
}
});
this.watchers.push(watcher);
// Recursively watch subdirectories
for (const entry of entries) {
if (entry.isDirectory()) {
const subDirPath = path.join(dirPath, entry.name);
await this.setupWatchers(subDirPath);
}
}
}
catch (error) {
console.error(`ā ļø Failed to setup watcher for ${dirPath}: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle file system changes
*/
handleFileChange(eventType, filePath) {
// Only watch template files
if (!filePath.endsWith(this.options.templateExt)) {
return;
}
// Find which source directory this file belongs to
let relativePath = filePath;
for (const dir of this.options.sourceDirs) {
const dirPath = typeof dir === "string" ? dir : dir.path;
const resolvedDirPath = path.resolve(dirPath);
if (filePath.startsWith(resolvedDirPath)) {
relativePath = path.relative(resolvedDirPath, filePath);
if (typeof dir !== "string" && dir.alias) {
relativePath = `${dir.alias}/${relativePath}`;
}
break;
}
}
if (this.options.verbose) {
const timestamp = new Date().toLocaleTimeString();
console.log(`[${timestamp}] š ${eventType}: ${relativePath}`);
}
// Debounce the rebuild to batch rapid changes
this.debouncer.debounce(() => this.runBuild());
}
/**
* Run the build process
*/
async runBuild() {
if (this.isBuilding) {
return; // Prevent concurrent builds
}
this.isBuilding = true;
const timestamp = new Date().toLocaleTimeString();
try {
console.log(`\n[${timestamp}] š Building...`);
const startTime = Date.now();
const result = await build({
sourceDirs: this.options.sourceDirs,
outDir: this.options.outDir,
templateExt: this.options.templateExt,
generator: this.options.generator,
includePathPrefix: this.options.includePathPrefix,
preserveCodeReference: this.options.preserveCodeReference,
});
const duration = Date.now() - startTime;
if (result.status === "success") {
const fileCount = result.result?.files?.templates.size || 0;
console.log(`[${timestamp}] ā
Build completed successfully! (${fileCount} files in ${duration}ms)`);
}
else {
console.log(`[${timestamp}] ā Build failed:`);
if (result.error) {
console.error(` ${result.error.message}`);
}
}
}
catch (error) {
console.log(`[${timestamp}] ā Build failed with error:`);
console.error(` ${error instanceof Error ? error.message : String(error)}`);
}
finally {
this.isBuilding = false;
}
}
}
//# sourceMappingURL=watcher.js.map