"use strict";
const { execFileSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const os = require("os");
function findBinary() {
if (process.env.WF_BIN) return process.env.WF_BIN;
try {
const cmd = process.platform === "win32" ? "where" : "which";
return execFileSync(cmd, ["wf"], { encoding: "utf-8" }).trim();
} catch {}
const candidates = [
path.join(os.homedir(), ".cargo", "bin", "wf"),
"/usr/local/bin/wf",
"/usr/bin/wf",
];
if (process.platform === "win32") {
candidates.push(path.join(os.homedir(), ".cargo", "bin", "wf.exe"));
}
for (const p of candidates) {
if (fs.existsSync(p)) return p;
}
throw new Error(
"WebFluent CLI (wf) not found. Install it with: cargo install webfluent\n" +
"Or set the WF_BIN environment variable to the binary path."
);
}
let _bin = null;
function bin() {
if (!_bin) _bin = findBinary();
return _bin;
}
function tmpFile(content, ext) {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "wf-"));
const file = path.join(dir, `data${ext}`);
fs.writeFileSync(file, content);
return file;
}
function cleanup(filepath) {
try {
fs.unlinkSync(filepath);
fs.rmdirSync(path.dirname(filepath));
} catch {}
}
class Template {
constructor(source, options = {}) {
this._source = source;
this._theme = options.theme || "default";
this._tokens = options.tokens || {};
this._templateFile = null;
}
static fromString(source, options) {
return new Template(source, options);
}
static fromFile(filePath, options) {
const tpl = new Template("", options);
tpl._templateFile = path.resolve(filePath);
return tpl;
}
withTheme(theme) {
this._theme = theme;
return this;
}
withTokens(tokens) {
this._tokens = { ...this._tokens, ...tokens };
return this;
}
renderHtml(data) {
return this._render(data, "html");
}
renderHtmlFragment(data) {
return this._render(data, "fragment");
}
renderPdf(data) {
const outFile = tmpFile("", ".pdf");
try {
this._render(data, "pdf", outFile);
return fs.readFileSync(outFile);
} finally {
cleanup(outFile);
}
}
_render(data, format, outputFile) {
const tplFile = this._getTemplateFile();
const dataFile = tmpFile(JSON.stringify(data), ".json");
try {
const args = [
"render", tplFile,
"--data", dataFile,
"--format", format,
"--theme", this._theme,
];
if (outputFile) {
args.push("-o", outputFile);
}
const result = execFileSync(bin(), args, {
encoding: format === "pdf" && !outputFile ? "buffer" : "utf-8",
maxBuffer: 50 * 1024 * 1024, stdio: ["pipe", "pipe", "pipe"],
});
return outputFile ? undefined : result;
} finally {
cleanup(dataFile);
if (!this._templateFile) {
cleanup(tplFile);
}
}
}
_getTemplateFile() {
if (this._templateFile) return this._templateFile;
const f = tmpFile(this._source, ".wf");
return f;
}
}
module.exports = { Template };