Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 3x 3x 3x 3x 3x 3x 13x 13x 13x 13x 13x 13x 13x 16x 1x 15x 15x 15x 2x 13x 13x 13x 13x 1x 12x 12x 12x 11x 10x 8x | // Copyright 2026 ForgeKit Contributors
// SPDX-License-Identifier: Apache-2.0
// https://github.com/SubhanshuMG/ForgeKit
import * as path from 'path';
import * as fs from 'fs-extra';
import Handlebars from 'handlebars';
import { Template, ScaffoldOptions } from '../types';
import { validatePathContainment } from './security';
import { getTemplateDir } from './template-resolver';
export async function writeTemplateFiles(
template: Template,
options: ScaffoldOptions
): Promise<string[]> {
const templateDir = getTemplateDir(template.id);
const outputRoot = path.resolve(options.outputDir, options.projectName);
const filesCreated: string[] = [];
if (!options.dryRun) {
await fs.ensureDir(outputRoot);
}
const context = {
name: options.projectName,
...options.variables,
};
for (const file of template.files) {
// Skip conditional files where condition variable is falsy
if (file.condition && !context[file.condition as keyof typeof context]) {
continue;
}
// Render destination path (supports {{name}} tokens)
const destTemplate = Handlebars.compile(file.dest);
const destRelative = destTemplate(context);
// Security: validate path stays within output root
if (!validatePathContainment(outputRoot, destRelative)) {
throw new Error(`Security: Template file "${file.dest}" would escape the output directory. Aborting.`);
}
Iif (options.dryRun) {
filesCreated.push(destRelative);
continue;
}
const srcPath = path.join(templateDir, file.src);
const destPath = path.join(outputRoot, destRelative);
if (!await fs.pathExists(srcPath)) {
throw new Error(`Template source file not found: ${srcPath}`);
}
const rawContent = await fs.readFile(srcPath, 'utf-8');
const rendered = Handlebars.compile(rawContent)(context);
await fs.ensureDir(path.dirname(destPath));
// Output path is validated above via validatePathContainment and is
// user-specified (not an OS temp file) — CodeQL js/insecure-temporary-file
// does not apply here.
await fs.writeFile(destPath, rendered, 'utf-8'); // lgtm[js/insecure-temporary-file]
filesCreated.push(destRelative);
}
return filesCreated;
}
|