Scaffolder (scaffolder.ts):
- Interactive prompts: product name/ID/tagline/domain, port, platforms, features
- --from product.json flag to skip prompts (non-interactive)
- --dry-run preview mode
- Generates backend (always) + web (Next.js) + mobile (Expo) based on selection
- Template engine with {{VARIABLE}} and {{#IF FEATURE}} conditional blocks
- Backend scaffold: Fastify 5, Zod config, JWT auth, datastore, server.ts
- Web scaffold: Next.js 16 App Router, layout, page, product-config
- Mobile scaffold: Expo with app.json, index screen
- Root files: product.json, .gitignore, .env.example, README.md
Tests: 26 passing (11 template-engine + 15 scaffolder)
Tested with ActionTrail product.json dry-run — correct output
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
/**
|
|
* Simple template engine for scaffolding.
|
|
* Supports {{VARIABLE}} replacement and {{#IF FEATURE}}...{{/IF FEATURE}} conditional blocks.
|
|
*/
|
|
|
|
export type TemplateVars = Record<string, string | boolean | number>;
|
|
|
|
/**
|
|
* Replace {{VARIABLE}} placeholders and process {{#IF FEATURE}}...{{/IF FEATURE}} blocks.
|
|
*/
|
|
export function renderTemplate(template: string, vars: TemplateVars): string {
|
|
let result = template;
|
|
|
|
// Process conditional blocks: {{#IF KEY}}...{{/IF KEY}}
|
|
const ifRegex = /\{\{#IF (\w+)\}\}([\s\S]*?)\{\{\/IF \1\}\}/g;
|
|
result = result.replace(ifRegex, (_, key: string, content: string) => {
|
|
return vars[key] ? content : '';
|
|
});
|
|
|
|
// Process negative conditional blocks: {{#UNLESS KEY}}...{{/UNLESS KEY}}
|
|
const unlessRegex = /\{\{#UNLESS (\w+)\}\}([\s\S]*?)\{\{\/UNLESS \1\}\}/g;
|
|
result = result.replace(unlessRegex, (_, key: string, content: string) => {
|
|
return !vars[key] ? content : '';
|
|
});
|
|
|
|
// Replace {{VARIABLE}} placeholders
|
|
result = result.replace(/\{\{(\w+)\}\}/g, (match, key: string) => {
|
|
const val = vars[key];
|
|
if (val === undefined) return match;
|
|
return String(val);
|
|
});
|
|
|
|
return result;
|
|
}
|