Skip to content

Repository files navigation

TinyCreate

A flexible CLI framework for scaffolding projects with templates.

Overview

TinyCreate is a powerful templating engine and CLI framework designed for creating project generators. It combines interactive prompts, Handlebars templates, and smart post-processing to generate complete projects from templates with conditional logic, automatic imports, and code formatting.

Features

  • Interactive Prompts - Built-in support for text, select, and confirm questions
  • Smart Templates - Handlebars with custom helpers for common patterns
  • Automatic Import Management - Colocate imports with conditional logic
  • Intelligent Transpilation - Convert TypeScript to JavaScript on demand
  • Code Formatting - Built-in Prettier support with import organization
  • File Inclusion System - Compose templates from reusable components
  • Non-Interactive Mode - Full CLI argument support for CI/CD
  • Package Manager Detection - Automatically uses npm, yarn, pnpm, or bun

Installation

npm install tinycreate

Quick Start

import{createCLI}from'tinycreate';import{dirname,join}from'path';import{fileURLToPath}from'url';const__dirname=dirname(fileURLToPath(import.meta.url));constconfig={welcomeMessage: '🎉 Welcome to My Generator!\n',questions: [{type: 'text',name: 'projectName',message: 'Project name:',initial: 'my-app',},{type: 'select',name: 'language',message: 'Language:',choices: [{title: 'TypeScript',value: 'typescript'},{title: 'JavaScript',value: 'javascript'},],},],createContext: (answers)=>({projectName: answers.projectName,isTypescript: answers.language==='typescript',ext: answers.language==='typescript' ? 'ts' : 'js',}),getFiles: (context)=>[{template: 'templates/App.tsx.hbs',output: `src/App.${context.ext}`,prettier: true,transpile: context.isTypescript===false,},],templateRoot: __dirname,onSuccess: (projectName)=>{console.log(`✅ Created ${projectName}!`);console.log(`\nNext steps:`);console.log(` cd ${projectName}`);console.log(` npm install`);console.log(` npm run dev`);},};awaitcreateCLI(config);

Custom Handlebars Helpers

TinyCreate extends Handlebars with powerful custom helpers designed for code generation:

{{addImport}}

Automatically manage imports at the top of files, keeping them colocated with conditional logic:

{{#ifisReact}}{{addImport"import React from 'react';"}}{{/if}}{{#ifisTypescript}}{{addImport"import type {FC} from 'react';"}}{{/if}}
export const Component = () => { return
<div>Hello</div>; };

Output (when both conditions are true):

importReactfrom'react';importtype{FC}from'react';exportconstComponent=()=>{return<div>Hello</div>;};

All imports added with {{addImport}} are automatically:

  • Deduplicated
  • Moved to the top of the file
  • Preserved in their original order

{{#list}}...{{/list}}

Generate comma-separated lists with automatic formatting:

const dependencies = {
{{#list}}
"react": "^18.0.0"
{{#ifincludeRouter}}
"react-router": "^6.0.0"
{{/if}}
"lodash": "^4.17.21"
{{/list}}
};

Output (when includeRouter is true):

constdependencies={react: '^18.0.0','react-router': '^6.0.0',lodash: '^4.17.21',};

The {{#list}} helper:

  • Automatically adds commas between items
  • Removes trailing comma from last item
  • Handles conditional items gracefully
  • Filters out empty lines and comments

{{eq}}

Compare values in conditional blocks:

{{#if (eqframework'react')}}
import React from 'react';
{{/if}}

{{includeFile}}

Compose templates from other templates and track file dependencies:

{{includeFiletemplate='components/Button.tsx.hbs'output='src/Button.tsx'prettier=true}}
import {Button} from './Button'; export const App = () => (
<Button>Click me</Button>
);

This directive:

  • Signals that Button.tsx should be generated
  • Allows the parent file to import it
  • Processes the included template with the same context
  • Supports prettier and transpile options per file

API Reference

createCLI(config, options)

Main entry point for creating a CLI generator.

Config Options:

interfaceProjectConfig{// Optional welcome message shown before promptswelcomeMessage?: string;// Array of question objects (text, select, or confirm)questions: Question[];// Transform user answers into template contextcreateContext: (answers: Record<string,unknown>)=>TemplateContext;// Return array of files to generategetFiles: (context: TemplateContext)=>FileConfig[]|Promise<FileConfig[]>;// Optional: Process included files before renderingprocessIncludedFile?: (file: FileConfig,context: TemplateContext,)=>FileConfig;// Root directory containing templatestemplateRoot: string;// Optional: Create custom directoriescreateDirectories?: (targetDir: string,context: TemplateContext,)=>Promise<void>;// Optional: Custom install command (default: auto-detected package manager)installCommand?: string;// Optional: Custom dev commanddevCommand?: string;// Optional: Success callback after project creationonSuccess?: (projectName: string,context: TemplateContext,)=>void|Promise<void>;}

CLI Options:

interfaceCLIOptions{// Enable non-interactive mode for CI/CDnonInteractive?: boolean;// Custom CLI arguments (defaults to process.argv.slice(2))args?: string[];}

Question Types

interfaceQuestion{// Question type (or function returning type based on previous answers)type:
|'text'|'select'|'confirm'|((prev: unknown,answers: Record<string,unknown>,)=>'text'|'select'|'confirm'|null);// Answer key namename: string;// Question promptmessage: string;// Default valueinitial?: string|number|boolean;// Choices for 'select' typechoices?: Array<{title: string;value: string|boolean}>;// Validation function for 'text' typevalidate?: (value: string)=>boolean|string;}

FileConfig

interfaceFileConfig{// Path to Handlebars template (relative to templateRoot)template: string;// Output path (supports template variables like {{projectName}})output: string;// Enable Prettier formatting (default: false)prettier?: boolean;// Enable TypeScript to JavaScript transpilation (default: false)transpile?: boolean;// Internal: processed content (set by engine)processedContent?: string;}

Post-Processing

import{postProcessFile,postProcessProject}from'tinycreate';// Process a single fileconst{filePath, content}=awaitpostProcessFile('src/App.tsx',fileContent,{prettier: true,transpileToJS: true,});// Process multiple filesconstprocessedFiles=awaitpostProcessProject('/path/to/project',filesMap,{prettier: true,transpileToJS: false,});

Non-Interactive Mode

TinyCreate supports non-interactive mode for use in CI/CD or automated scripts:

node cli.js --non-interactive \
--projectName my-app \
--language typescript \
--framework react

Any question without a provided argument will use its initial value.

Advanced Patterns

Conditional Questions

Questions can be conditional based on previous answers:

{type: (prev,answers)=>answers.language==='typescript' ? 'confirm' : null,name: 'strictMode',message: 'Enable strict mode?',initial: true,}

If the function returns null, the question is skipped.

Dynamic File Generation

Generate files based on context:

getFiles: (context)=>{constfiles=[{template: 'App.tsx.hbs',output: 'src/App.tsx'}];if(context.includeTests){files.push({template: 'App.test.tsx.hbs',output: 'src/App.test.tsx',});}returnfiles;};

Custom Directory Structure

Create custom directories before file generation:

createDirectories: async(targetDir,context)=>{const{mkdir}=awaitimport('fs/promises');awaitmkdir(join(targetDir,'src','components'),{recursive: true});awaitmkdir(join(targetDir,'public'),{recursive: true});if(context.includeServer){awaitmkdir(join(targetDir,'server'),{recursive: true});}};

Processing Included Files

Modify included files before rendering:

processIncludedFile: (file,context)=>{// Force all component files to be formattedif(file.output.includes('components/')){return{...file,prettier: true};}returnfile;};

Examples

See the create-tinybase project for a complete real-world example that uses TinyCreate to generate TinyBase applications with multiple frameworks, languages, and configurations.

Testing

npm test

TinyCreate includes comprehensive tests covering:

  • Template engine functionality
  • Import management
  • List formatting
  • File inclusion
  • Post-processing
  • TypeScript transpilation

Requirements

  • Node.js >= 18.0.0

Dependencies

  • handlebars - Template rendering
  • prompts - Interactive CLI prompts
  • esbuild - Fast TypeScript transpilation
  • prettier - Code formatting

License

MIT License - see LICENSE file for details.

About

A flexible CLI framework for scaffolding projects with templates.

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages