Conditional Execution
Run plugin logic based on configuration
Conditional Execution
Run plugin logic based on configuration from the template. This allows templates to control which post-processing steps run.
Using Config
The config property passed to plugins contains configuration defined in the template:
import { StartPluginWithLambda } from '@atomicloud/cyan-sdk';import { $ } from 'bun';StartPluginWithLambda(async (input) => {const { directory, config } = input;// Define your config typeconst cfg = config as {git?: boolean;installDeps?: boolean;packageManager?: 'npm' | 'bun' | 'yarn' | 'pnpm';formatter?: 'prettier' | 'biome' | 'none';runBuild?: boolean;};// Git initializationif (cfg.git) {await $`git -C ${directory} init`.quiet();await $`git -C ${directory} add .`.quiet();await $`git -C ${directory} commit -m "Initial commit"`.quiet();}// Install dependenciesif (cfg.installDeps) {const pm = cfg.packageManager || 'npm';await $`cd ${directory} && ${pm} install`.quiet();}// Run formatterif (cfg.formatter === 'prettier') {await $`cd ${directory} && npx prettier --write .`.quiet();} else if (cfg.formatter === 'biome') {await $`cd ${directory} && npx biome format --write .`.quiet();}// Run buildif (cfg.runBuild) {await $`cd ${directory} && npm run build --if-present`.quiet();}return { directory };});
from cyan_sdk import start_plugin_with_fnimport subprocessimport osdef run_command(cmd, cwd=None):subprocess.run(cmd, shell=True, cwd=cwd, check=True, capture_output=True)@start_plugin_with_fndef main(input_data):directory = input_data["directory"]config = input_data.get("config", {})# Git initializationif config.get("git"):run_command("git init", cwd=directory)run_command("git add .", cwd=directory)run_command('git commit -m "Initial commit"', cwd=directory)# Install dependenciesif config.get("install_deps"):pm = config.get("package_manager", "npm")run_command(f"{pm} install", cwd=directory)# Run formatterformatter = config.get("formatter")if formatter == "prettier":run_command("npx prettier --write .", cwd=directory)elif formatter == "biome":run_command("npx biome format --write .", cwd=directory)# Run buildif config.get("run_build"):run_command("npm run build --if-present", cwd=directory)return {"directory": directory}
using CyanSdk;using System.Diagnostics;[PluginMain]public static class Plugin{public static async Task<PluginOutput> Main(PluginInput input){var directory = input.Directory;var cfg = input.Config;// Git initializationif (cfg.TryGetValue("git", out var git) && (bool)git){RunCommand($"git -C {directory} init");RunCommand($"git -C {directory} add .");RunCommand($"git -C {directory} commit -m \"Initial commit\"");}// Install dependenciesif (cfg.TryGetValue("installDeps", out var installDeps) && (bool)installDeps){var pm = cfg.TryGetValue("packageManager", out var pmValue) ? pmValue.ToString() : "npm";RunCommand($"cd {directory} && {pm} install");}// Run formatterif (cfg.TryGetValue("formatter", out var formatter)){if (formatter.ToString() == "prettier")RunCommand($"cd {directory} && npx prettier --write .");else if (formatter.ToString() == "biome")RunCommand($"cd {directory} && npx biome format --write .");}// Run buildif (cfg.TryGetValue("runBuild", out var runBuild) && (bool)runBuild){RunCommand($"cd {directory} && npm run build --if-present");}return new PluginOutput { Directory = directory };}private static void RunCommand(string command){var parts = command.Split(' ');var process = Process.Start(new ProcessStartInfo{FileName = parts[0],Arguments = string.Join(" ", parts.Skip(1)),RedirectStandardOutput = true,RedirectStandardError = true});process.WaitForExit();}}
Template Configuration
Configure the plugin in your template's index.ts file. The plugin configuration is defined programmatically in the return statement of your template's main function:
import type { Cyan } from '@atomicloud/cyan-sdk';import type { IInquirer, IDeterminism } from '@atomicloud/cyan-sdk';// In your template's index.ts, obtain answers from inquirer promptsexport default async function (i: IInquirer, d: IDeterminism): Promise<Cyan> {// ... prompt users for answers using inquirer ...return {processors: [// ... processors],plugins: [{name: 'myorg/setup-plugin',config: {git: true,installDeps: true, // or use answers.installDeps from promptspackageManager: 'bun', // or use answers.packageManager from promptsformatter: 'biome',runBuild: false}}]};}
from cyan_sdk import Cyan, PluginConfig# In your template's index.pydef main(inquirer, determinism):# ... prompt users for answers using inquirer ...return Cyan(processors=[# ... processors],plugins=[PluginConfig(name='myorg/setup-plugin',config={'git': True,'install_deps': True, # or use answers['install_deps']'package_manager': 'bun', # or use answers['package_manager']'formatter': 'biome','run_build': False})])
using CyanSdk;public static class Template{public static async Task<Cyan> Main(IInquirer inquirer, IDeterminism determinism){// ... prompt users for answers using inquirer ...return new Cyan{Processors = new List<Processor>{// ... processors},Plugins = new List<PluginConfig>{new PluginConfig{Name = "myorg/setup-plugin",Config = new Dictionary<string, object>{["git"] = true,["installDeps"] = true, // or use answers.InstallDeps["packageManager"] = "bun", // or use answers.PackageManager["formatter"] = "biome",["runBuild"] = false}}}};}}
Advanced Patterns
The code examples below use Bun's shell template syntax (await $command``) which requires the Bun runtime. Import it with import { $ } from 'bun';. When using fs and path operations, import them with import fs from 'node:fs'; and import path from 'node:path';.
Feature Flags
import fs from 'node:fs';import path from 'node:path';import { StartPluginWithLambda } from '@atomicloud/cyan-sdk';import { $ } from 'bun';StartPluginWithLambda(async (input) => {const { directory, config } = input;const features = config as {features?: {eslint?: boolean;prettier?: boolean;testing?: boolean;ci?: boolean;};};const enabledFeatures = features.features || {};if (enabledFeatures.eslint) {await $`cd ${directory} && npm install -D eslint`.quiet();// Create .eslintrc.jsonawait fs.writeFile(path.join(directory, '.eslintrc.json'),JSON.stringify({ extends: ['eslint:recommended'] }, null, 2));}if (enabledFeatures.prettier) {await $`cd ${directory} && npm install -D prettier`.quiet();}if (enabledFeatures.testing) {await $`cd ${directory} && npm install -D vitest`.quiet();}if (enabledFeatures.ci) {// Create GitHub Actions workflowconst workflowsDir = path.join(directory, '.github', 'workflows');await fs.mkdir(workflowsDir, { recursive: true });await fs.writeFile(path.join(workflowsDir, 'ci.yml'),`name: CI\non: [push]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - run: npm ci\n - run: npm test\n`);}return { directory };});
from cyan_sdk import start_plugin_with_fnimport subprocessimport osimport jsondef run_command(cmd, cwd=None):subprocess.run(cmd, shell=True, cwd=cwd, check=True, capture_output=True)@start_plugin_with_fndef main(input_data):directory = input_data["directory"]config = input_data.get("config", {})features = config.get("features", {})if features.get("eslint"):run_command("cd {} && npm install -D eslint".format(directory))# Create .eslintrc.jsonwith open(os.path.join(directory, ".eslintrc.json"), "w") as f:json.dump({"extends": ["eslint:recommended"]}, f, indent=2)if features.get("prettier"):run_command("cd {} && npm install -D prettier".format(directory))if features.get("testing"):run_command("cd {} && npm install -D vitest".format(directory))if features.get("ci"):# Create GitHub Actions workflowworkflows_dir = os.path.join(directory, ".github", "workflows")os.makedirs(workflows_dir, exist_ok=True)with open(os.path.join(workflows_dir, "ci.yml"), "w") as f:f.write("name: CI\non: [push]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - run: npm ci\n - run: npm test\n")return {"directory": directory}
using CyanSdk;using System.Diagnostics;using System.IO;using System.Text.Json;[PluginMain]public static class Plugin{public static async Task<PluginOutput> Main(PluginInput input){var directory = input.Directory;var features = input.Config.TryGetValue("features", out var f)? f as Dictionary<string, object>: new Dictionary<string, object>();if (features.TryGetValue("eslint", out var eslint) && (bool)eslint){RunCommand($"cd {directory} && npm install -D eslint");var eslintConfig = new { extends = new[] { "eslint:recommended" } };File.WriteAllText(Path.Combine(directory, ".eslintrc.json"),JsonSerializer.Serialize(eslintConfig, new JsonSerializerOptions { WriteIndented = true }));}if (features.TryGetValue("prettier", out var prettier) && (bool)prettier){RunCommand($"cd {directory} && npm install -D prettier");}if (features.TryGetValue("testing", out var testing) && (bool)testing){RunCommand($"cd {directory} && npm install -D vitest");}if (features.TryGetValue("ci", out var ci) && (bool)ci){var workflowsDir = Path.Combine(directory, ".github", "workflows");Directory.CreateDirectory(workflowsDir);File.WriteAllText(Path.Combine(workflowsDir, "ci.yml"),"name: CI\non: [push]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - run: npm ci\n - run: npm test\n");}return new PluginOutput { Directory = directory };}private static void RunCommand(string command){var parts = command.Split(' ');var process = Process.Start(new ProcessStartInfo{FileName = parts[0],Arguments = string.Join(" ", parts.Skip(1)),RedirectStandardOutput = true,RedirectStandardError = true});process.WaitForExit();}}
Conditional Based on Project Type
import { StartPluginWithLambda } from '@atomicloud/cyan-sdk';import { $ } from 'bun';StartPluginWithLambda(async (input) => {const { directory, config } = input;const cfg = config as {projectType?: 'nextjs' | 'react' | 'node' | 'cli';};switch (cfg.projectType) {case 'nextjs':await $`cd ${directory} && npm install next react react-dom`.quiet();break;case 'react':await $`cd ${directory} && npm install react react-dom`.quiet();break;case 'node':// Minimal setup for nodebreak;case 'cli':await $`cd ${directory} && npm install commander`.quiet();break;}return { directory };});
from cyan_sdk import start_plugin_with_fnimport subprocessdef run_command(cmd, cwd=None):subprocess.run(cmd, shell=True, cwd=cwd, check=True, capture_output=True)@start_plugin_with_fndef main(input_data):directory = input_data["directory"]config = input_data.get("config", {})project_type = config.get("project_type")if project_type == "nextjs":run_command("cd {} && npm install next react react-dom".format(directory))elif project_type == "react":run_command("cd {} && npm install react react-dom".format(directory))elif project_type == "node":# Minimal setup for nodepasselif project_type == "cli":run_command("cd {} && npm install commander".format(directory))return {"directory": directory}
using CyanSdk;using System.Diagnostics;[PluginMain]public static class Plugin{public static async Task<PluginOutput> Main(PluginInput input){var directory = input.Directory;var projectType = input.Config.TryGetValue("projectType", out var pt)? pt.ToString(): null;switch (projectType){case "nextjs":RunCommand($"cd {directory} && npm install next react react-dom");break;case "react":RunCommand($"cd {directory} && npm install react react-dom");break;case "node":// Minimal setup for nodebreak;case "cli":RunCommand($"cd {directory} && npm install commander");break;}return new PluginOutput { Directory = directory };}private static void RunCommand(string command){var parts = command.Split(' ');var process = Process.Start(new ProcessStartInfo{FileName = parts[0],Arguments = string.Join(" ", parts.Skip(1)),RedirectStandardOutput = true,RedirectStandardError = true});process.WaitForExit();}}
Using Defaults
import { StartPluginWithLambda } from '@atomicloud/cyan-sdk';import { $ } from 'bun';StartPluginWithLambda(async (input) => {const { directory, config } = input;// Provide defaults for all config optionsconst cfg = {git: true,installDeps: true,packageManager: 'npm' as const,formatter: 'none' as const,...(config as Record<string, unknown>)};if (cfg.git) {await $`git -C ${directory} init`.quiet();}return { directory };});
from cyan_sdk import start_plugin_with_fnimport subprocessdef run_command(cmd, cwd=None):subprocess.run(cmd, shell=True, cwd=cwd, check=True, capture_output=True)@start_plugin_with_fndef main(input_data):directory = input_data["directory"]config = input_data.get("config", {})# Provide defaults for all config optionsdefaults = {"git": True,"install_deps": True,"package_manager": "npm","formatter": "none",}cfg = {**defaults, **config}if cfg["git"]:run_command("git -C {} init".format(directory))return {"directory": directory}
using CyanSdk;using System.Diagnostics;[PluginMain]public static class Plugin{public static async Task<PluginOutput> Main(PluginInput input){var directory = input.Directory;var config = input.Config;// Provide defaults for all config optionsvar git = config.TryGetValue("git", out var g) ? (bool)g : true;var installDeps = config.TryGetValue("installDeps", out var i) ? (bool)i : true;var packageManager = config.TryGetValue("packageManager", out var pm) ? pm.ToString() : "npm";var formatter = config.TryGetValue("formatter", out var f) ? f.ToString() : "none";if (git){RunCommand($"git -C {directory} init");}return new PluginOutput { Directory = directory };}private static void RunCommand(string command){var parts = command.Split(' ');var process = Process.Start(new ProcessStartInfo{FileName = parts[0],Arguments = string.Join(" ", parts.Skip(1)),RedirectStandardOutput = true,RedirectStandardError = true});process.WaitForExit();}}
Always provide sensible defaults for optional configuration. Templates should work without specifying every option.