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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | 2x 2x 2x 2x 2x 2x 2x 18x 18x 18x 2x 16x 16x 16x 16x 2x 14x 4x 4x 3x 1x 1x 2x 2x 2x 1x 14x 14x 14x 14x 14x 14x 11x 11x 1x 1x 1x 1x 1x 14x | // Copyright 2026 ForgeKit Contributors
// SPDX-License-Identifier: Apache-2.0
// https://github.com/SubhanshuMG/ForgeKit
import { spawnSync } from 'child_process';
import * as path from 'path';
import { ScaffoldOptions, ScaffoldResult, Template } from '../types';
import { getTemplate } from './template-resolver';
import { writeTemplateFiles } from './file-writer';
import { validateHookCommand } from './security';
import { trackEvent } from './telemetry';
export async function scaffold(options: ScaffoldOptions): Promise<ScaffoldResult> {
const errors: string[] = [];
let template: Template;
try {
template = await getTemplate(options.templateId);
} catch (err) {
return {
success: false,
projectPath: '',
filesCreated: [],
errors: [(err as Error).message],
nextSteps: [],
};
}
const projectPath = path.resolve(options.outputDir, options.projectName);
let filesCreated: string[] = [];
try {
filesCreated = await writeTemplateFiles(template, options);
} catch (err) {
return {
success: false,
projectPath,
filesCreated,
errors: [(err as Error).message],
nextSteps: [],
};
}
// Run post-scaffold hooks (e.g. npm install)
if (!options.skipInstall && !options.dryRun) {
for (const hook of template.hooks) {
if (hook.type !== 'post-scaffold') continue;
if (!validateHookCommand(hook.command)) {
errors.push(`Skipped unsafe hook command: ${hook.command}`);
continue;
}
try {
const result = spawnSync(hook.command, hook.args, { cwd: projectPath, stdio: 'inherit', shell: false });
if (result.status !== 0) throw new Error(`exited with code ${result.status}`);
} catch (err) {
errors.push(`Hook "${hook.command} ${hook.args.join(' ')}" failed: ${(err as Error).message}`);
}
}
}
const nextSteps = buildNextSteps(template, options.projectName);
const success = errors.length === 0;
trackEvent('scaffold', { template: options.templateId, success });
return {
success,
projectPath,
filesCreated,
errors,
nextSteps,
};
}
function buildNextSteps(template: Template, projectName: string): string[] {
const steps: string[] = [`cd ${projectName}`];
switch (template.id) {
case 'web-app':
steps.push('npm run dev', 'Open http://localhost:3000');
break;
case 'api-service':
steps.push('pip install -r requirements.txt', 'uvicorn main:app --reload', 'Open http://localhost:8000/docs');
break;
case 'ml-pipeline':
steps.push('pip install -r requirements.txt', 'jupyter lab', 'Open notebooks/01_explore.ipynb');
break;
default:
steps.push('Follow the README in your new project');
}
return steps;
}
|