Actions guide
Actions transform your files after the repo is scaffolded. Each is a function
you import from the package and add to your config’s actions array — so
every action and its options are fully typed.
import { defineConfig, prompt, render, execute,} from '@reuters-graphics/bluprint';
export default defineConfig({ name: 'My bluprint', files: ['**/*'], ignores: [], actions: [ prompt({ name: 'name', type: 'text', message: 'Project name?' }), // runs first render({ files: ['README.md'] }), // then this execute('pnpm install'), // then this ],});The run context
Section titled “The run context”Actions share a context object that flows through the run. It starts with
some defaults and grows as actions (like prompt) contribute to it:
| Key | Value |
|---|---|
year / month / day |
Date parts at run time ("2026", "07", "02") |
dirname |
Basename of the directory being scaffolded |
bluprintPart |
The selected part, if any |
A prompt action adds its answer under its name, and later actions can read
it — in templates ({{ name }}) or via the when gate below. To make those
custom values type-checked (instead of unknown) in when / run / editor
callbacks, declare them with defineConfig<Context> — see
Typing the run context.
Gating actions with when
Section titled “Gating actions with when”Every action takes an optional when predicate. Return false and the action
is skipped. It receives the current context:
prompt({ name: 'useTypeScript', type: 'confirm', message: 'Use TypeScript?' }),render({ files: ['tsconfig.json'], when: (ctx) => ctx.useTypeScript === true,}),Gate on the selected part with ctx.bluprintPart:
execute('pnpm install', { when: (ctx) => ctx.bluprintPart === 'app' }),Handling errors with failOnError
Section titled “Handling errors with failOnError”If an action throws, it’s logged and skipped so the rest of the run continues.
Set failOnError: true to abort the whole run instead:
execute('pnpm build', { failOnError: true }),Templating
Section titled “Templating”render, log, json values, and destination paths for copy/move are
rendered with mustache against the context, with
these string helpers available: camelize, capitalize, dasherize,
humanize, latinise, slugify, titleCase, underscore.
The helpers are mustache lambda sections, so wrap the value in a section
(note the #): {{# helper }}…{{ /helper }}.
# {{# titleCase }}{{ name }}{{ /titleCase }}Created {{ year }} in {{ dirname }}.For example, a templated copy destination: src/{{# slugify }}{{ name }}{{ /slugify }}.ts.
render also supports EJS via engine: 'ejs'.
The actions
Section titled “The actions”prompt
Section titled “prompt”Ask the user a question; the answer is stored in the context under name. The
spec is a discriminated union on type — text, confirm, select,
multiselect, or datetime:
prompt({ name: 'name', type: 'text', message: 'Project name?' }),prompt({ name: 'analytics', type: 'confirm', message: 'Add analytics?' }),prompt({ name: 'kind', type: 'select', message: 'What kind of project?', options: [ { value: 'blog', label: 'Blog' }, { value: 'scraper', label: 'Data scraper' }, ],}),render
Section titled “render”Render template files in place against the context (plus any extra
context you pass). Defaults to the mustache engine.
render({ files: ['README.md', 'package.json'] }),render({ files: ['index.html'], engine: 'ejs', context: { analytics: true } }),copy / move
Section titled “copy / move”Copy or move files and directories. Take a single [from, to] pair or an array
of them; the destination is rendered as a mustache template.
copy(['templates/component.tsx', 'src/{{ name }}.tsx']),move([ ['gitignore', '.gitignore'], ['README.template.md', 'README.md'],]),remove
Section titled “remove”Delete files and directories matching one or more globs.
remove(['**/*.test.ts', 'internal', 'TODO.md']),regexreplace
Section titled “regexreplace”Replace content in files with regular expressions. Each replacement is
[pattern, replacement, flags?] (flags default to gm); the replacement is
mustache-rendered.
regexreplace({ files: ['README.md'], replace: [ ['^# .*$', '# {{ name }}'], ['\\d{4}', '{{ year }}', 'g'], ],}),execute
Section titled “execute”Run a shell command in the project. Pass a string (run via the shell) or a
[command, ...args] array (run without one). By default the command’s output
streams to the terminal; with silent, output is hidden and a spinner is shown
instead.
execute('pnpm install', { silent: true }),execute(['git', 'init']),Edit a JSON file. The editor receives the parsed data and the context and must return the data to write (mutate then return, or return a new value):
json('package.json', (pkg, ctx) => { pkg.name = ctx.name; pkg.private = true; return pkg;}),Pass a type argument for typed editing: json<PackageJson>('package.json', …).
The json analog for YAML files (e.g. CI workflows). Same must-return editor.
yaml('.github/workflows/ci.yml', (workflow, ctx) => { workflow.name = ctx.name; return workflow;}),append / prepend
Section titled “append / prepend”Add (templated) content to a file, creating it if missing:
append('.gitignore', 'dist\n.env\n'),prepend('src/index.ts', '// @generated for {{ name }}\n'),Print a message. Rendered with mustache, then styled with chalk-template color tags:
log('Scaffolded {green {{ name }}}! Run {yellow pnpm dev} to start.'),The escape hatch: run any function. It receives the context and may return a partial context to merge in for later actions. Async is supported.
run(async (ctx) => { const res = await fetch(`https://example.com/${ctx.name}`); return { headline: (await res.json()).title }; // available to later actions}),Under a typed config, ctx and
the returned object are typed: declare the keys run contributes on your
Context, and later actions read them type-safely.
interface Context { name: string; headline: string; // contributed by the run action}
// inside defineConfig<Context>({ actions: [ … ] })run(async (ctx): Promise<Partial<Context>> => { const res = await fetch(`https://example.com/${ctx.name}`); return { headline: (await res.json()).title };}),Next steps
Section titled “Next steps”- Actions reference — every signature and option
- Parts — split a bluprint into reusable segments
- Previewing — test your actions locally