@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.
93 lines • 3.75 kB
JavaScript
import * as fs from "fs/promises";
import * as path from "path";
import { resolveCodeGenerator } from "./code-generator-impl.js";
import { generator } from "./generator-impl.js";
import { loader } from "./loader-impl.js";
import { parser } from "./parser-impl.js";
import { WgslTemplateBuildError } from "./errors.js";
// Export error types
export * from "./errors.js";
// Export loader functions
export { loader } from "./loader-impl.js";
export const build = async (options) => {
let pass0;
let pass1;
let pass2;
let files;
try {
// Validate that sourceDirs is provided
if (!options.sourceDirs || options.sourceDirs.length === 0) {
throw new WgslTemplateBuildError("sourceDirs must be provided and cannot be empty", "invalid-options", {});
}
// Load templates using the multi-directory loader
pass0 = await loader.loadFromDirectories(options.sourceDirs, { ext: options.templateExt });
pass1 = parser.parse(pass0);
const codeGenerator = resolveCodeGenerator(options.generator);
pass2 = generator.generateDirectory(pass1, codeGenerator, {
preserveCodeReference: options.preserveCodeReference,
});
const files = codeGenerator.build(pass2, {
templateExt: options.templateExt,
includePathPrefix: options.includePathPrefix,
});
// Write files to the output directory
const basePath = path.resolve(options.outDir);
for (const [filePath, result] of files.templates) {
try {
const fullPath = path.resolve(basePath, filePath);
// Security check: ensure the resolved path is within the output directory
if (!fullPath.startsWith(basePath + path.sep) && fullPath !== basePath) {
throw new WgslTemplateBuildError(`Security violation: attempted to write file outside output directory: ${filePath}`, "path-security-violation", { filePath });
}
const dirName = path.dirname(fullPath);
// Create the directory if it doesn't exist
await fs.mkdir(dirName, { recursive: true });
// Only write the file if content has changed
let shouldWrite = true;
try {
const existingContent = await fs.readFile(fullPath, "utf8");
shouldWrite = existingContent !== result;
}
catch {
// File doesn't exist or can't be read, so we should write it
shouldWrite = true;
}
if (shouldWrite) {
await fs.writeFile(fullPath, result, "utf8");
}
// Record successful file write
await fs.stat(fullPath);
}
catch (error) {
return {
status: "file-write-error",
error: error instanceof Error ? error : new Error(String(error)),
result: {
pass0,
pass1,
pass2,
files,
},
};
}
}
return {
status: "success",
result: { pass0, pass1, pass2, files },
};
}
catch (error) {
const errorObj = error instanceof Error ? error : new Error(String(error));
return {
status: "build-error",
error: errorObj,
result: {
pass0,
pass1,
pass2,
files,
},
};
}
};
//# sourceMappingURL=index.js.map