PluginsExplanation
Execution Order
Understanding the order of operations in the CyanPrint pipeline
Execution Order
Understanding the execution order helps you decide where to place your logic - in a processor or a plugin.
Pipeline Overview
Processors execute in parallel (subject to parallelism limits), then their outputs are merged. Plugins execute sequentially in the order defined.
Detailed Sequence
Phase 1: Template Collection
- User invokes template
- Template asks questions via
IInquirerand receivesIDeterminismfor deterministic behavior - Template returns
Cyanwith processors and plugins
// Template index.ts - the template function signatureimport { IInquirer, IDeterminism } from '@atomicloud/cyan-sdk';export default async function(i: IInquirer, d: IDeterminism) {return {processors: [{name: 'my-processor',files: [{ /* CyanGlob configuration */ }],config: { /* processor-specific config */ }}],plugins: [{ name: 'my-plugin', config: { /* plugin config */ } }]};}
# Template main.py - the template function signaturefrom cyanprintsdk.main import start_template_with_fnfrom cyanprintsdk.protocol import IInquirer, IDeterminism, Cyanasync def template(i: IInquirer, d: IDeterminism) -> Cyan:return Cyan(processors=[CyanProcessor(name='my-processor',files=[/* CyanGlob configuration */],config={/* processor-specific config */})],plugins=[CyanPlugin(name='my-plugin', config={/* plugin config */})])template_main = start_template_with_fn(template)
// Template.cs - the template function signatureusing CyanPrintSDK;public static class Template{[TemplateMain]public static async Task<Cyan> Run(IInquirer i, IDeterminism d){return new Cyan{Processors = new[]{new CyanProcessor{Name = "my-processor",Files = new List<CyanGlob> { /* CyanGlob configuration */ },Config = new Dictionary<string, object> { /* config */ }}},Plugins = new[]{new CyanPlugin { Name = "my-plugin", Config = new Dictionary<string, object>() }}};}}
Phase 2: Processing (Processors)
For each processor (executed in parallel):
- Processor receives
ProcessorInputwith:readDir- Directory to read files fromwriteDir- Directory to write transformed files toglobs- File patterns to processconfig- Processor-specific configuration
- Processor reads files using
CyanFileHelpermethods:resolveAll()- Resolve all matching filesread()- Read file contentsget()- Get file metadatacopy()- Copy files directlyreadAsStream()- Read files as streams
- Processor transforms file content
- Processor writes files to its output directory
- After all processors complete, outputs are merged into final directory
Phase 3: Merge
After all processors complete, the Merger combines their outputs:
- Each processor's output directory is collected
- All outputs are merged into a single directory
- Conflicts are resolved according to merge rules
- Final merged directory is prepared for plugins
Phase 4: Post-Processing (Plugins)
For each plugin (executed sequentially):
- Plugin receives
PluginInputwith:directory- Path to the merged output directoryconfig- Plugin-specific configuration
- Plugin runs operations (commands, file modifications)
- Plugin returns
PluginOutputwith the directory path - Directory passed to next plugin
Plugins receive the merged output of all processors, not individual processor outputs. This means plugins work with the complete, combined result of all transformations.
Phase 5: Output
- Final directory delivered to user
- User can open and use the project
Example Pipeline
// Template configurationreturn {processors: [{name: 'cyan/default',files: [{ glob: '**/*', exclude: ['node_modules/**'] }],config: { }},{name: 'custom/formatter',files: [{ glob: '**/*.ts', exclude: [] }, { glob: '**/*.tsx', exclude: [] }],config: { formatter: 'prettier' }},],plugins: [{ name: 'my-org/git-init', config: {} },{ name: 'my-org/npm-install', config: {} },]};
# Template configurationreturn Cyan(processors=[CyanProcessor(name='cyan/default',files=[CyanGlob(glob='**/*', exclude=['node_modules/**'])],config={}),CyanProcessor(name='custom/formatter',files=[CyanGlob(glob='**/*.ts', exclude=[]),CyanGlob(glob='**/*.tsx', exclude=[])],config={'formatter': 'prettier'}),],plugins=[CyanPlugin(name='my-org/git-init', config={}),CyanPlugin(name='my-org/npm-install', config={}),])
// Template configurationreturn new Cyan{Processors = new[]{new CyanProcessor{Name = "cyan/default",Files = new List<CyanGlob>{new CyanGlob { Glob = "**/*", Exclude = new List<string> { "node_modules/**" } }},Config = new Dictionary<string, object>()},new CyanProcessor{Name = "custom/formatter",Files = new List<CyanGlob>{new CyanGlob { Glob = "**/*.ts", Exclude = new List<string>() },new CyanGlob { Glob = "**/*.tsx", Exclude = new List<string>() }},Config = new Dictionary<string, object> { ["formatter"] = "prettier" }}},Plugins = new[]{new CyanPlugin { Name = "my-org/git-init", Config = new Dictionary<string, object>() },new CyanPlugin { Name = "my-org/npm-install", Config = new Dictionary<string, object>() }}};
Timing Considerations
| Phase | Duration | Bottleneck |
|---|---|---|
| Processing (parallel) | Fast | File size, parallelism limit |
| Merging | Fast | File count |
| Post-Processing (sequential) | Variable | Network (npm install) |
Error Handling
During Processing
- Processors execute in parallel; if one fails, others continue running
- After parallel execution completes, the pipeline returns any errors and stops
- Individual processor outputs may be partially written before failure is detected
- The merge phase may not complete if errors occurred
During Post-Processing
- Plugins execute sequentially; if one fails, subsequent plugins do not run
- The merged processor output already exists at this point
- Partial plugin changes may have been applied
- User may need to clean up manually
Multiple Plugins
Plugins run in the order defined:
plugins: [{ name: 'plugin-a', config: {} }, // Runs first{ name: 'plugin-b', config: {} }, // Runs second{ name: 'plugin-c', config: {} }, // Runs third]
Dependency Order
Consider dependencies when ordering plugins:
plugins: [// Git must be initialized before hooks{ name: 'my-org/git-init', config: {} },// Hooks require git{ name: 'my-org/husky-setup', config: {} },// Dependencies must be installed before build{ name: 'my-org/npm-install', config: {} },// Build requires dependencies{ name: 'my-org/npm-build', config: {} },]
What Runs Where
| Operation | Phase | Component |
|---|---|---|
| Variable substitution | Processing | Processor |
| Syntax transformation | Processing | Processor |
| Code generation | Processing | Processor |
| File filtering | Processing | Processor |
git init | Post-processing | Plugin |
npm install | Post-processing | Plugin |
prettier --write | Post-processing | Plugin |
| File creation | Post-processing | Plugin |
| Symlink creation | Post-processing | Plugin |
Related
- Plugins vs Processors - Comparison
- What Are Plugins - Plugin concepts
- First Plugin - Tutorial