kist
Version:
Package Pipeline Processor
125 lines (107 loc) • 4.25 kB
text/typescript
// ============================================================================
// Imports
// ============================================================================
import { mkdir, readFile, writeFile } from "fs/promises";
import { glob } from "glob";
import nunjucks from "nunjucks";
import path from "path";
import { Action } from "../../core/pipeline/Action";
import { ActionOptionsType } from "../../types/ActionOptionsType";
import nunjucksConfig from "./nunjucks.config.js";
// ============================================================================
// Classes
// ============================================================================
export class TemplateRenderAction extends Action {
async execute(options: ActionOptionsType): Promise<void> {
const {
templatesDir = "./templates",
outputDir = "./dist",
templates = [],
context = {},
contextFiles = [],
renderAllFromDir = false,
customConfig = {},
} = options;
const config = { ...nunjucksConfig, ...customConfig };
nunjucks.configure(templatesDir, config);
// Load and merge all JSON context files
const mergedContext = await this.mergeContextFiles(
context,
contextFiles,
);
try {
if (renderAllFromDir) {
this.logInfo(
`Auto-rendering all templates from ${templatesDir}...`,
);
const templateFiles = await glob("**/*.jinja", {
cwd: templatesDir,
});
for (const templateRelPath of templateFiles) {
const outputFile = path.join(
outputDir,
templateRelPath.replace(/\.jinja$/, ""),
);
await this.renderTemplate(
templateRelPath,
outputFile,
mergedContext,
templatesDir,
);
}
} else {
if (!Array.isArray(templates) || templates.length === 0) {
throw new Error(
"Option 'templates' must be provided if 'renderAllFromDir' is false.",
);
}
for (const { template, outputFile } of templates) {
await this.renderTemplate(
template,
outputFile,
mergedContext,
templatesDir,
);
}
}
this.logInfo("✓ All templates rendered successfully.");
} catch (error) {
this.logError("Error rendering templates.", error);
throw error;
}
}
private async renderTemplate(
template: string,
outputFile: string,
context: Record<string, any>,
templatesDir: string,
): Promise<void> {
this.logInfo(`Rendering: ${template} → ${outputFile}`);
const content = nunjucks.render(template, context);
const dir = path.dirname(outputFile);
await mkdir(dir, { recursive: true });
await writeFile(outputFile, content, "utf-8");
this.logInfo(`✓ Rendered: ${outputFile}`);
}
private async mergeContextFiles(
baseContext: Record<string, any>,
files: string[],
): Promise<Record<string, any>> {
let merged = { ...baseContext };
for (const file of files) {
try {
const content = await readFile(file, "utf-8");
const parsed = JSON.parse(content);
merged = { ...merged, ...parsed };
} catch (error) {
this.logWarn(`Skipping context file due to error: ${file}`);
this.logError("Context file parsing failed", error);
}
}
return merged;
}
describe(): string {
return "Renders one or many Nunjucks templates using a shared context or context files. Supports folder-wide auto-rendering.";
}
}
export default TemplateRenderAction;