Advanced Features

This guide covers the advanced features of @pixpilot/scaffoldfy that enable powerful and flexible configuration automation.

Table of Contents


Configuration-Level Enabled

Control whether an entire configuration should be executed at the root level. This is useful for creating conditional configurations that only run under specific circumstances.

Overview

The root-level enabled property allows you to enable or disable an entire configuration, including all of its prompts, variables, and tasks. If the configuration is disabled, execution stops immediately without any user interaction or file operations.

Basic Usage

Simple Boolean

{
  "$schema": "https://unpkg.com/@pixpilot/scaffoldfy/schema",
  "name": "my-config",
  "enabled": true,
  "tasks": []
}

Conditional Format

{
  "$schema": "https://unpkg.com/@pixpilot/scaffoldfy/schema",
  "name": "conditional-config",
  "enabled": {
    "type": "condition",
    "value": "projectType === 'monorepo'"
  },
  "tasks": []
}

Executable Enabled

Determine if a configuration should run by executing a shell command:

{
  "$schema": "https://unpkg.com/@pixpilot/scaffoldfy/schema",
  "name": "git-config",
  "enabled": {
    "type": "exec",
    "value": "git rev-parse --is-inside-work-tree"
  },
  "tasks": []
}

The command output is parsed as a boolean:

Use Cases

1. Environment-Based Configurations

Only run certain configurations in specific environments:

{
  "name": "development-tools",
  "enabled": {
    "type": "exec",
    "value": "test \"$NODE_ENV\" = \"development\" && echo true || echo false"
  },
  "tasks": [
    {
      "id": "install-dev-tools",
      "name": "Install Development Tools",
      "type": "exec",
      "config": {
        "command": "npm install --save-dev prettier eslint"
      }
    }
  ]
}

2. Dependency-Based Configurations

Enable configurations only when certain dependencies exist:

{
  "name": "react-setup",
  "description": "Setup React-specific configurations",
  "enabled": {
    "type": "exec",
    "value": "test -f node_modules/react/package.json && echo true || echo false"
  },
  "tasks": []
}

3. Conditional Based on Other Configurations

Enable configurations based on variables from dependency configurations:

{
  "name": "typescript-config",
  "description": "TypeScript configuration",
  "dependencies": ["project-info"],
  "enabled": {
    "type": "condition",
    "value": "useTypeScript === true"
  },
  "tasks": [
    {
      "id": "create-tsconfig",
      "name": "Create tsconfig.json",
      "type": "write",
      "config": {
        "file": "tsconfig.json",
        "template": "{ \"compilerOptions\": { \"strict\": true } }"
      }
    }
  ]
}

In this example, if the project-info configuration has a prompt useTypeScript, this configuration will only run if the user answered true.

4. Git Repository Check

Only run Git-related configurations in Git repositories:

{
  "name": "git-hooks",
  "enabled": {
    "type": "exec",
    "value": "git rev-parse --git-dir"
  },
  "tasks": [
    {
      "id": "setup-hooks",
      "name": "Setup Git Hooks",
      "type": "exec",
      "config": {
        "command": "npx husky install"
      }
    }
  ]
}

Evaluation Timing

The configuration-level enabled property evaluation depends on the context:

For Main Configurations:

  1. Evaluated first - Before variable resolution
  2. Evaluated first - Before prompt collection
  3. Evaluated first - Before task execution
  4. Evaluated first - Before validation

If the main configuration is disabled, nothing from that configuration will be processed or executed.

For Extended Configurations (via extends):

Extended configurations use lazy evaluation, meaning their enabled condition is evaluated sequentially during execution with access to previous values:

  1. Prompts: Each prompt from an extended configuration is evaluated before asking - has access to all previous prompt answers
  2. Variables: Each variable from an extended configuration is evaluated before resolving - has access to all prompts + previous variables
  3. Tasks: Each task from an extended configuration is evaluated before execution - has access to all prompts and variables

Example: Conditional Extended Configuration

{
  "name": "pixpilot-info",
  "dependencies": ["project-info"],
  "variables": [
    {
      "id": "pixpilot_project",
      "value": {
        "type": "conditional",
        "condition": "repoOwner === 'pixpilots' || orgName === 'pixpilots'",
        "ifTrue": true,
        "ifFalse": false
      }
    }
  ]
}
{
  "name": "pixpilot-copilot-instructions",
  "dependencies": ["pixpilot-info"],
  "enabled": {
    "condition": "pixpilot_project == true"
  },
  "tasks": [
    {
      "id": "create-copilot-instructions",
      "name": "Create Copilot Instructions",
      "type": "write",
      "config": {
        "file": ".github/copilot-instructions.md",
        "templateFile": "copilot-instructions.md"
      }
    }
  ]
}

In this example:

  1. project-info configuration asks for repoOwner and orgName
  2. pixpilot-info configuration computes pixpilot_project variable based on those answers
  3. pixpilot-copilot-instructions configuration is only enabled if pixpilot_project is true
  4. The tasks, prompts, and variables from disabled configurations are automatically skipped

Configuration Variables in Enabled

You can use variables in the enabled expression, but be aware that variables are resolved during execution. For exec-type enabled, you can use configuration interpolation:

{
  "name": "conditional-config",
  "variables": [
    {
      "id": "targetEnv",
      "value": {
        "type": "exec",
        "value": "echo $NODE_ENV"
      }
    }
  ],
  "enabled": { "type": "condition", "value": "targetEnv === 'production'" },
  "tasks": []
}

However, for early evaluation, prefer using exec-type enabled directly:

{
  "name": "conditional-config",
  "enabled": {
    "type": "exec",
    "value": "test \"$NODE_ENV\" = \"production\" && echo true || echo false"
  },
  "tasks": []
}

Best Practices

  1. Use exec for external checks - When checking file existence, environment variables, or system state
  2. Use conditions for prompt-based logic - When the decision depends on user input from dependency configurations
  3. Keep conditions simple - Complex logic should be in scripts, not conditions
  4. Document dependencies - If your configuration depends on variables from other configurations, list them in dependencies

Conditional Execution

Execute tasks conditionally based on user input, configuration values, or any JavaScript expression.

Overview

Every task type supports an optional condition field that determines whether the task should be executed. If the condition evaluates to false, the task is skipped.

Basic Usage

{
  "type": "update-json",
  "config": {
    "file": "package.json",
    "updates": {
      "private": true
    },
    "condition": "makePrivate === true"
  }
}

Condition Syntax

Conditions are JavaScript expressions that have access to all configuration variables:

Supported Operators:

Available Variables:

Using with Prompts

Combine conditional execution with interactive prompts for dynamic behavior:

{
  "prompts": [
    {
      "id": "useTypeScript",
      "type": "confirm",
      "message": "Use TypeScript?",
      "default": true
    }
  ],
  "tasks": [
    {
      "id": "setup-typescript",
      "type": "write",
      "config": {
        "file": "tsconfig.json",
        "templateFile": ".templates/tsconfig.hbs",
        "condition": "useTypeScript === true"
      }
    }
  ]
}

Complex Conditions

Create sophisticated conditional logic:

{
  "type": "delete",
  "config": {
    "paths": ["examples", "test-fixtures", "docs/api"],
    "condition": "!keepExamples && environment !== 'development'"
  }
}

Numeric Comparisons

{
  "prompts": [
    {
      "id": "nodeVersion",
      "type": "number",
      "message": "Minimum Node.js version?",
      "default": 18
    }
  ],
  "type": "exec",
  "config": {
    "command": "npm install --save-dev typescript@latest",
    "condition": "nodeVersion >= 16"
  }
}

Multiple Conditions

Combine multiple conditions with logical operators:

{
  "type": "write",
  "config": {
    "file": ".github/workflows/ci.yml",
    "templateFile": ".templates/ci-advanced.hbs",
    "condition": "enableCI === true && (useTypeScript === true || useLinting === true)"
  }
}

Dry Run with Conditions

When using --dry-run, you’ll see which tasks would be skipped:

✓ Task: Update Package Info
✗ Task: Setup TypeScript (condition not met: useTypeScript === false)
✓ Task: Create README

Best Practices

  1. Keep conditions simple - Complex logic is harder to debug
  2. Use root-level prompts - All prompts are available to all tasks
  3. Document conditions - Add clear descriptions explaining why tasks are conditional
  4. Test both paths - Run with conditions true and false to verify behavior
  5. Use meaningful prompt IDs - Make conditions self-documenting (e.g., enableFeatureX)

Examples

Conditional cleanup:

{
  "prompts": [
    {
      "id": "cleanupTemplate",
      "type": "confirm",
      "message": "Remove template files after initialization?",
      "default": true
    }
  ],
  "tasks": [
    {
      "id": "cleanup-template-files",
      "type": "delete",
      "config": {
        "paths": [".templates", "template-tasks.json"],
        "condition": "cleanupTemplate === true"
      }
    }
  ]
}

Environment-specific configuration:

{
  "prompts": [
    {
      "id": "environment",
      "type": "select",
      "message": "Deployment environment?",
      "choices": ["development", "staging", "production"]
    }
  ],
  "tasks": [
    {
      "id": "create-env",
      "type": "write",
      "config": {
        "file": ".env",
        "templateFile": ".templates/env-production.hbs",
        "condition": "environment === 'production'"
      }
    }
  ]
}

Conditional Variables

Create dynamic variables that evaluate expressions and return different values based on conditions. This enables intelligent automation that adapts to user input or system state.

Overview

Conditional variables evaluate JavaScript expressions and return different values based on whether the condition is true or false. They have access to:

Basic Syntax

{
  "id": "variableName",
  "value": {
    "type": "conditional",
    "condition": "expression === 'value'",
    "ifTrue": "value when true",
    "ifFalse": "value when false"
  }
}

Properties

Property Type Required Description
type string Yes Must be "conditional"
condition string Yes JavaScript expression to evaluate
ifTrue any Yes Value returned when condition is true
ifFalse any Yes Value returned when condition is false

Evaluation Timing

Conditional variables are resolved in two passes:

  1. First pass: Before prompts - resolves non-conditional variables
  2. Second pass: After prompts - resolves conditional variables with full context

This allows conditional variables to reference prompt values.

Common Use Cases

1. Derive Boolean Flags

{
  "variables": [
    {
      "id": "pixpilot_project",
      "value": {
        "type": "conditional",
        "condition": "repoOwner === 'pixpilot' || orgName === 'pixpilot'",
        "ifTrue": true,
        "ifFalse": false
      }
    }
  ]
}

2. Choose Configuration Files

{
  "variables": [
    {
      "id": "configFile",
      "value": {
        "type": "conditional",
        "condition": "language === 'typescript'",
        "ifTrue": "tsconfig.json",
        "ifFalse": "jsconfig.json"
      }
    }
  ]
}

3. Set Different Values Based on Environment

{
  "variables": [
    {
      "id": "apiEndpoint",
      "value": {
        "type": "conditional",
        "condition": "environment === 'production'",
        "ifTrue": "https://api.prod.example.com",
        "ifFalse": "https://api.dev.example.com"
      }
    }
  ]
}

Dynamic Configuration Enabling

Combine conditional variables with configuration-level enabled to create configurations that automatically activate based on project context:

Config: pixpilot-info (derives the flag)

{
  "name": "pixpilot-info",
  "dependencies": ["project-info"],
  "variables": [
    {
      "id": "pixpilot_project",
      "value": {
        "type": "conditional",
        "condition": "repoOwner === 'pixpilot' || orgName === 'pixpilot'",
        "ifTrue": true,
        "ifFalse": false
      }
    }
  ]
}

Config: pixpilot-copilot-instructions (uses the flag)

{
  "name": "pixpilot-copilot-instructions",
  "dependencies": ["project-info", "pixpilot-info"],
  "enabled": {
    "condition": "pixpilot_project == true"
  },
  "tasks": [
    {
      "id": "create-copilot-instructions",
      "name": "Create Copilot Instructions",
      "type": "write",
      "config": {
        "file": ".github/copilot-instructions.md",
        "templateFile": "copilot-instructions.md"
      }
    }
  ]
}

Result: The pixpilot-copilot-instructions configuration only runs when the repository owner or organization is “pixpilot”.

Complex Conditions

Conditional variables support complex JavaScript expressions:

{
  "variables": [
    {
      "id": "shouldUseYarn",
      "value": {
        "type": "conditional",
        "condition": "packageManager === 'yarn' && nodeVersion >= 14",
        "ifTrue": true,
        "ifFalse": false
      }
    },
    {
      "id": "installCommand",
      "value": {
        "type": "conditional",
        "condition": "shouldUseYarn",
        "ifTrue": "yarn install",
        "ifFalse": "npm install"
      }
    }
  ]
}

Best Practices

  1. Keep conditions simple: Use clear, readable expressions
  2. Document the logic: Add comments in description field
  3. Chain dependencies: Let conditional variables build on each other
  4. Fail gracefully: Provide sensible default values in ifFalse

Handlebars Templates

Use powerful Handlebars templating for advanced template generation with conditionals, loops, and helpers.

Overview

Scaffoldfy automatically uses Handlebars for template files ending in .hbs. This gives you access to advanced templating features like conditionals, loops, and built-in helpers.

Automatic Detection

No configuration needed - just use the .hbs extension:

{
  "type": "write",
  "config": {
    "file": "README.md",
    "templateFile": ".templates/readme.hbs"
  }
}

Template Processing Rules

Template Type Extension Processing
Handlebars .hbs Full Handlebars templating
Simple Other extensions Simple `` replacement
Inline N/A Simple `` replacement

Basic Handlebars Template

Create .templates/readme.hbs:

#




## Installation ```sh npm install

License


Use it in your task:

```json
{
  "type": "write",
  "config": {
    "file": "README.md",
    "templateFile": ".templates/readme.hbs"
  }
}

Conditionals

Use and for conditional content:

# 





## Features


- Written in TypeScript
- Full type safety



- Comprehensive test coverage



## Installation

```sh
npm install 

### Loops

Use `` to iterate over arrays:

```handlebars
# Contributors


-  ()


# Features


- 

Pass arrays through prompts or config:

{
  "prompts": [
    {
      "id": "features",
      "type": "input",
      "message": "List features (comma-separated):"
    }
  ]
}

Nested Conditionals

Combine conditionals for complex logic:


  ## TypeScript Configuration This project uses TypeScript.

  
    Strict mode is enabled for maximum type safety.
  
    Standard TypeScript configuration.
  

Built-in Helpers

Handlebars provides useful built-in helpers:




  Content if true

  Content if false



  Content if false



  : 



  Parent: 
  Current: 

Complex Example

.templates/package-json.hbs:

{ "name": "", "version": "", "description": "",

  "private": true,

"scripts": {

  "build": "tsc", "dev": "tsc --watch",


  "test": "vitest",

"start": "node
dist/index.js" }, "devDependencies": {

  "typescript": "^5.0.0", "@types/node": "^20.0.0",


  "vitest": "^1.0.0"

} }

File Organization

Recommended structure:

project-root/
├── .templates/
│   ├── readme.hbs
│   ├── package-json.hbs
│   ├── tsconfig.hbs
│   └── ci-workflow.hbs
└── template-tasks.json

Complete Workflow Example

{
  "prompts": [
    {
      "id": "projectName",
      "type": "input",
      "message": "Project name?",
      "required": true
    },
    {
      "id": "description",
      "type": "input",
      "message": "Description?"
    },
    {
      "id": "useTypeScript",
      "type": "confirm",
      "message": "Use TypeScript?",
      "default": true
    },
    {
      "id": "enableTesting",
      "type": "confirm",
      "message": "Include testing?",
      "default": true
    }
  ],
  "tasks": [
    {
      "id": "collect-info",
      "name": "Collect Project Info",
      "type": "write",
      "config": {
        "file": "package.json",
        "templateFile": ".templates/package-json.hbs"
      }
    },
    {
      "id": "create-readme",
      "name": "Create README",
      "type": "write",
      "config": {
        "file": "README.md",
        "templateFile": ".templates/readme.hbs"
      }
    },
    {
      "id": "setup-typescript",
      "name": "Setup TypeScript",
      "type": "write",
      "config": {
        "file": "tsconfig.json",
        "templateFile": ".templates/tsconfig.hbs",
        "condition": "useTypeScript === true"
      }
    }
  ]
}

Simple vs Handlebars

When to use Handlebars (.hbs):

When to use simple templates:

Best Practices

  1. Use .hbs extension - Makes it clear which templates use Handlebars
  2. Organize templates - Keep all .hbs files in a .templates/ directory
  3. Test templates - Use --dry-run to preview generated files
  4. Keep logic simple - Complex logic belongs in prompts or conditions, not templates
  5. Document variables - Comment what variables your templates expect
  6. Validate JSON - When generating JSON files, ensure proper comma handling

Troubleshooting

Template not using Handlebars?

Syntax errors?

Missing variables?

Learn More

For complete Handlebars documentation:


Combining Features

The real power comes from combining these features:

{
  "prompts": [
    {
      "id": "framework",
      "type": "select",
      "message": "Choose framework:",
      "choices": ["react", "vue", "svelte"]
    },
    {
      "id": "useTypeScript",
      "type": "confirm",
      "message": "Use TypeScript?",
      "default": true
    }
  ],
  "tasks": [
    {
      "id": "setup",
      "type": "write",
      "config": {
        "file": "package.json",
        "templateFile": ".templates/package-.hbs"
      }
    },
    {
      "id": "setup-typescript",
      "type": "write",
      "config": {
        "file": "tsconfig.json",
        "templateFile": ".templates/tsconfig-.hbs",
        "condition": "useTypeScript === true"
      }
    }
  ]
}

This example:

Next Steps