Skip to content

Project Structure

AI Scaffolding is organized into several key components that work together to create customized project files and folders.

Understanding this structure is essential for extending the tool with new packages and features.

Directory Structure

The codebase follows a modular organization that separates concerns and promotes extensibility:

src/
├── packages/             # Package templates and configuration
│   ├── root/             # Files copied to project root
│   ├── hardhat/          # Hardhat package files
│   ├── vite/             # Vite package files
│   ├── nextjs/           # Next.js package files
│   ├── nestjs/           # NestJS package files
│   └── index.ts          # Package exports and prompt configuration
├── builders/             # Code generation logic
│   ├── MainBuilder.ts    # Orchestrates the entire build process
│   └── PackageBuilder.ts # Base class for package builders
├── main.ts               # Main application logic
├── run.ts                # CLI entry point
├── types.ts              # Type definitions
├── prompts.ts            # Main project prompts
└── handlebars.ts         # Handlebars template engine setup

Core Components

Entry Point

The CLI tool's entry point is run.ts, which sets up the command-line interface using Commander.js. This file defines the available commands and routes them to the appropriate handlers:

// Main command to scaffold a new project
program
  .name('ai-scaffolding-v2')
  .description('Scaffold a new Web(3) (d)App project')
  .version(packageJson.version)
  .argument('[path]', 'Path for the project (e.g., my-app or ../my-app)')
  .option('--skipInstall', 'Skip the installation of dependencies', false)
  .action(main);
 
// Additional commands
program.command('cursor')
  .description('Update Cursor rules')
  // ...options
 
program.command('config')
  .description('Show the current configuration')
  // ...subcommands

Workflow Phases

The tool operates in two distinct phases:

  1. Prompt Phase:

Collects user input through interactive prompts to configure the project.

The prompts are organized by:

  • Main project configuration (project name, location, package manager)
  • Package selection (which packages to include)
  • Package-specific configuration (options for each selected package)
  1. Execution Phase:

Generates the project based on the collected configuration:

  • Creates the directory structure
  • Applies templates using Handlebars
  • Copies static files
  • Sets up git repository
  • Installs dependencies

Type System

The type system in types.ts defines the structure of configuration objects and ensures type safety throughout the application:

// Main configuration interfaces
export interface ProjectConfigV2 {
  projectName: string;
  packageManager: PackageManager;
  projectPath: string;
  targetDir: string;
  packages: Record<'hardhat', PackageConfig<HardhatConfig>>
    & Record<'vite', PackageConfig<ViteConfig>>
    & Record<'nextjs', PackageConfig<NextjsConfig>>
    & Record<'nestjs', PackageConfig<NestJsConfig>>;
  global: GlobalConfig;
  skipInstall: boolean;
}
 
// Context passed to prompt functions
export interface PromptContext {
  projectName: string
  projectPath: string
  targetDir: string
  packageManager: PackageManager
  shouldAddHardhat: boolean
  shouldAddVite: boolean
  shouldAddNextjs: boolean
  skipInstall: boolean
}
 
// Package prompt function type
export type PackagePrompt<T extends Record<string, unknown>> = 
  (ctx: PromptContext) => Promise<PackageConfig<T>>

Configuration Object

The central piece connecting the prompt and execution phases is the ProjectConfig object defined in main.ts. This object stores all user selections and is passed to the builders for project generation:

const config: ProjectConfig = {
  projectName,
  packageManager,
  packages,
  projectPath,
  targetDir,
  skipInstall,
  global: {
    useSilentData,
    shouldAddHardhat,
    shouldAddVite,
    shouldAddNextjs,
    shouldAddNestjs,
    networks,
    isPnpm: packageManager === PackageManager.PNPM,
    isYarn: packageManager === PackageManager.YARN,
    isNpm: packageManager === PackageManager.NPM,
    defaultNetwork
  }
};

This configuration becomes available as variables in Handlebars templates, allowing conditional file generation.

Template System

AI Scaffolding uses Handlebars as its templating engine. The engine is configured in handlebars.ts with custom helpers:

// Custom Handlebars helpers
Handlebars.registerHelper('uppercase', (str) => str.toUpperCase());
Handlebars.registerHelper('lowercase', (str) => str.toLowerCase());
Handlebars.registerHelper('ifeq', (val1, val2, options) => val1 === val2 ? options.fn(val1) : undefined);
Handlebars.registerHelper('ifneq', (val1, val2, options) => val1 !== val2 ? options.fn(val1) : undefined);

Package files can include Handlebars directives for dynamic content generation:

// Example from hardhat.config.ts
/*#{{#if packages.hardhat.useIgnition}}*/
import '@nomicfoundation/hardhat-ignition'
/*#{{/if}}*/
 
// Network configuration with dynamic values
const networks = {
/*#{{#each packages.hardhat.networks}}*/
  /*#{{#ifneq this 'hardhat'}}*/
  ['{{this}}']: {
    url: process.env.{{uppercase this}}_RPC_URL || '',
    accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [],
  },
  /*#{{/ifneq}}*/
/*#{{/each}}*/
}

Package System

AI Scaffolding is built around a modular package system that allows for easy extension with new features and frameworks. Each package is a self-contained module that can be selected during project creation.

For detailed information on how packages work and how to create new ones, see the Development Guide.

Build Process

The MainBuilder class orchestrates the entire build process through a series of well-defined tasks:

public async build() {
  // Orchestrate tasks
  this._createProjectStructure();
  this._buildPackages();
  this._createCursorrules();
  this._installDependencies();
  this._formatAll();
  this._initGitRepository();
 
  // Run tasks
  await this.tasks.run();
}

Each task is implemented as a private method that adds one or more tasks to the task list:

  1. _createProjectStructure(): Sets up the basic project structure
  2. _buildPackages(): Builds each selected package using its builder
  3. _createCursorrules(): Adds Cursor AI assistant rules to the project
  4. _installDependencies(): Installs all dependencies
  5. _formatAll(): Formats all generated code
  6. _initGitRepository(): Initializes a Git repository

Each package builder extends the PackageBuilder base class and implements its own build logic.

Next Steps

Now that you understand the structure, proceed to the Development Guide to learn how to extend AI Scaffolding with new packages and features.