Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/amanvarshney01/create-better-t-stack/llms.txt

Use this file to discover all available pages before exploring further.

The template generator is the core engine that powers project scaffolding. You can use it directly for advanced use cases.

Overview

The template generator takes a project configuration and embedded templates, then generates a complete project structure in-memory using a virtual filesystem.

API

generate()

Generate a virtual project file tree from templates and configuration.
function generate(
  options: GeneratorOptions
): Promise<Result<VirtualFileTree, GeneratorError>>
Source: ~/workspace/source/packages/template-generator/src/generator.ts:50

Parameters

options
GeneratorOptions
required
Generator configuration options.

Return Type

Promise<Result<VirtualFileTree, GeneratorError>>
Returns a Result with either:
  • ok: VirtualFileTree - Generated project tree
  • err: GeneratorError - Generation error

Example

import {
  generate,
  EMBEDDED_TEMPLATES,
  type ProjectConfig,
} from "create-better-t-stack";

const config: ProjectConfig = {
  projectName: "my-app",
  projectDir: "/virtual",
  relativePath: "./virtual",
  frontend: ["tanstack-router"],
  backend: "hono",
  runtime: "bun",
  database: "sqlite",
  orm: "drizzle",
  api: "trpc",
  auth: "none",
  payments: "none",
  addons: [],
  examples: [],
  packageManager: "bun",
  install: false,
  git: false,
  dbSetup: "none",
  webDeploy: "none",
  serverDeploy: "none",
};

const result = await generate({
  config,
  templates: EMBEDDED_TEMPLATES,
  version: "1.0.0",
});

result.match({
  ok: (tree) => console.log(`Generated ${tree.fileCount} files`),
  err: (error) => console.error(error.message),
});

EMBEDDED_TEMPLATES

Pre-bundled templates included with the CLI.
const EMBEDDED_TEMPLATES: Map<string, string>
Source: ~/workspace/source/packages/template-generator/src/templates.generated.ts This is a generated file containing all Handlebars templates embedded at build time.

Example

import { EMBEDDED_TEMPLATES, TEMPLATE_COUNT } from "create-better-t-stack";

console.log(`${TEMPLATE_COUNT} templates available`);
console.log(`Templates:`, Array.from(EMBEDDED_TEMPLATES.keys()));

TEMPLATE_COUNT

Number of embedded templates.
const TEMPLATE_COUNT: number
Source: ~/workspace/source/packages/template-generator/src/templates.generated.ts

Template Processing

The generator processes templates in phases:
  1. Base Template - Root project structure
  2. Frontend Templates - Frontend framework files
  3. Backend Templates - Backend server files
  4. Database Templates - Database configuration
  5. API Templates - API layer (tRPC, etc.)
  6. Config Package - Shared configuration
  7. Env Package - Environment variables
  8. Auth Templates - Authentication setup
  9. Payments Templates - Payment integration
  10. Addon Templates - Additional tools
  11. Example Templates - Example code
  12. Extras - Additional utilities
  13. Deploy Templates - Deployment configuration
  14. Post-processing - Dependencies, catalogs, README

Advanced Usage

Custom Templates

You can provide custom templates instead of using EMBEDDED_TEMPLATES:
import { generate } from "create-better-t-stack";

const customTemplates = new Map<string, string>([
  ["package.json.hbs", JSON.stringify({
    name: "{{projectName}}",
    version: "1.0.0",
    scripts: {
      dev: "{{#if (eq runtime 'bun')}}bun{{else}}node{{/if}} index.ts"
    }
  }, null, 2)],
  ["index.ts.hbs", `console.log('Hello {{projectName}}!');`],
]);

const result = await generate({
  config: myConfig,
  templates: customTemplates,
});

Template Helpers

Templates use Handlebars with custom helpers:

eq - Equality Check

{{#if (eq database "postgres")}}
  // Postgres-specific code
{{/if}}

ne - Not Equal

{{#if (ne database "none")}}
  // Database is configured
{{/if}}

and - Logical AND

{{#if (and (eq database "postgres") (eq orm "drizzle"))}}
  // Postgres + Drizzle
{{/if}}

or - Logical OR

{{#if (or (eq database "postgres") (eq database "mysql"))}}
  // SQL database
{{/if}}

includes - Array Contains

{{#if (includes frontend "next")}}
  // Next.js is in frontend array
{{/if}}

Accessing Template Content

Read generated file content from the virtual tree:
import { generate, EMBEDDED_TEMPLATES } from "create-better-t-stack";

const result = await generate({
  config: myConfig,
  templates: EMBEDDED_TEMPLATES,
});

if (result.isOk()) {
  const tree = result.value;
  
  function findFile(node, path) {
    if (node.type === "file" && node.path === path) {
      return node;
    }
    if (node.type === "directory") {
      for (const child of node.children) {
        const found = findFile(child, path);
        if (found) return found;
      }
    }
    return null;
  }
  
  const packageJson = findFile(tree.root, "package.json");
  if (packageJson) {
    console.log(packageJson.content);
  }
}

Writing to Disk

Convert virtual tree to real files:
import { generate, EMBEDDED_TEMPLATES } from "create-better-t-stack";
import fs from "node:fs";
import path from "node:path";

const result = await generate({
  config: myConfig,
  templates: EMBEDDED_TEMPLATES,
});

if (result.isOk()) {
  const tree = result.value;
  const outputDir = "/path/to/output";
  
  function writeNode(node, parentPath) {
    const fullPath = path.join(parentPath, node.name);
    
    if (node.type === "directory") {
      fs.mkdirSync(fullPath, { recursive: true });
      for (const child of node.children) {
        writeNode(child, fullPath);
      }
    } else {
      fs.writeFileSync(fullPath, node.content, "utf-8");
    }
  }
  
  writeNode(tree.root, outputDir);
  console.log(`Wrote ${tree.fileCount} files to ${outputDir}`);
}

Template Data

All templates receive a TemplateData context:
type TemplateData = ProjectConfig & {
  // Additional computed fields available in templates
}
Templates have access to all ProjectConfig fields:
{
  "name": "{{projectName}}",
  "scripts": {
    "dev": "{{#if (eq runtime 'bun')}}bun{{else}}node{{/if}} dev"
  },
  "dependencies": {
    {{#if (eq database "postgres")}}
    "postgres": "^3.0.0",
    {{/if}}
  }
}

Examples

Generate and Analyze

import { generate, EMBEDDED_TEMPLATES } from "create-better-t-stack";

const result = await generate({
  config: {
    projectName: "my-app",
    // ... full config
  },
  templates: EMBEDDED_TEMPLATES,
  version: "1.0.0",
});

if (result.isOk()) {
  const { fileCount, directoryCount, root } = result.value;
  
  console.log(`Files: ${fileCount}`);
  console.log(`Directories: ${directoryCount}`);
  
  // Count file types
  const extensions = new Map();
  
  function countTypes(node) {
    if (node.type === "file") {
      const ext = node.extension || "no-ext";
      extensions.set(ext, (extensions.get(ext) || 0) + 1);
    } else {
      node.children.forEach(countTypes);
    }
  }
  
  countTypes(root);
  console.log("File types:", Object.fromEntries(extensions));
}

Compare Configurations

import { generate, EMBEDDED_TEMPLATES } from "create-better-t-stack";

async function compareConfigs(config1, config2) {
  const result1 = await generate({
    config: config1,
    templates: EMBEDDED_TEMPLATES,
  });
  
  const result2 = await generate({
    config: config2,
    templates: EMBEDDED_TEMPLATES,
  });
  
  if (result1.isOk() && result2.isOk()) {
    console.log(`Config 1: ${result1.value.fileCount} files`);
    console.log(`Config 2: ${result2.value.fileCount} files`);
    console.log(`Difference: ${result2.value.fileCount - result1.value.fileCount}`);
  }
}

Build docs developers (and LLMs) love