Workflow for modifying generated code in Baseplate, including template extraction, generator updates, and project synchronization.
Use this skill when modifying code that is generated by Baseplate generators, adding new templates, or updating generator behavior.
Baseplate follows a code-first development approach:
Example projects:
examples/blog-with-auth - Blog application with authenticationexamples/todo-with-better-auth - Todo application with Better Auth integrationFor detailed documentation on generators, consult these guides via the baseplate-docs MCP:
| Guide | Document ID | Description |
|---|---|---|
| Generator Development Guide | df804c4d-72f1-4b30-bb4a-cdafac7608d2 |
Comprehensive guide covering core providers, template rendering, and generator patterns |
| React Generators Guide | 8687772b-b258-4241-9cbe-e73e9d13a203 |
React-specific providers, Vite configuration, component generation |
| Fastify Generators Guide | 862dcef2-7ff8-4de1-9840-fc73d89f32eb |
Backend providers, API generation, configuration management |
| Core Providers Reference | 200cfab4-56ea-4f60-880c-fa6b8a18048c |
Detailed reference for nodeProvider, typescriptFileProvider, etc. |
To fetch a guide:
mcp__baseplate_docs__get_document_by_id({
documentId: 'df804c4d-72f1-4b30-bb4a-cdafac7608d2',
});
Important: Never manually edit files in
generated/directories. These are auto-generated by thegenerate-templatescommand. Only modify:
- Template source files in
templates/directoriesextractor.jsonconfiguration- The main
*.generator.tsfile
Generators are organized into three main packages:
| Package | Location | Purpose |
|---|---|---|
@baseplate-dev/core-generators |
packages/core-generators/src/generators/ |
Node.js, TypeScript, ESLint, Prettier, Vitest |
@baseplate-dev/react-generators |
packages/react-generators/src/generators/ |
React, Vite, Apollo, admin UI |
@baseplate-dev/fastify-generators |
packages/fastify-generators/src/generators/ |
Fastify, Prisma, Pothos, auth, email |
Every generator follows this pattern:
import { createGenerator, createGeneratorTask } from '@baseplate-dev/sync';
import { z } from 'zod';
export const myGenerator = createGenerator({
name: 'category/my-generator',
generatorFileUrl: import.meta.url,
descriptorSchema: z.object({
// Configuration options
}),
buildTasks: (descriptor) => ({
// Add auto-generated tasks for templates
paths: MY_GENERATOR_GENERATED.paths.task,
imports: MY_GENERATOR_GENERATED.imports.task,
renderers: MY_GENERATOR_GENERATED.renderers.task,
main: createGeneratorTask({
dependencies: {
renderers: MY_GENERATOR_GENERATED.renderers.provider,
},
run({ renderers }) {
return {
build: async (builder) => {
await builder.apply(renderers.myTemplate.render());
},
};
},
}),
}),
});
Core Providers (from @baseplate-dev/core-generators):
| Provider | Purpose |
|---|---|
nodeProvider |
Package.json, dependencies, scripts |
typescriptFileProvider |
TypeScript file generation with imports |
packageInfoProvider |
Package name, root path, src path |
eslintConfigProvider |
ESLint configuration |
nodeGitIgnoreProvider |
.gitignore management |
React Providers (from @baseplate-dev/react-generators):
| Provider | Purpose |
|---|---|
reactBaseConfigProvider |
Vite plugins, server options, app configuration |
reactPathsProvider |
React-specific paths (components folder, etc.) |
Fastify Providers (from @baseplate-dev/fastify-generators):
| Provider | Purpose |
|---|---|
fastifyProvider |
Node flags, dev output formatter |
fastifyOutputProvider |
Runtime config (node commands, flags) |
configServiceProvider |
Environment variables with Zod validation |
Providers communicate between tasks using scopes:
import { packageScope } from '@baseplate-dev/core-generators';
// Export a provider to package scope (available to entire package)
exports: {
myProvider: myProviderType.export(packageScope),
}
// Consume a provider from dependencies
dependencies: {
someProvider: someProviderType,
}
The modern pattern uses auto-generated renderers:
// Renderers are generated from templates in ./templates/ directory
import { MY_GENERATOR_GENERATED } from './generated/index.js';
// Use in tasks
buildTasks: () => ({
paths: MY_GENERATOR_GENERATED.paths.task,
imports: MY_GENERATOR_GENERATED.imports.task,
renderers: MY_GENERATOR_GENERATED.renderers.task,
main: createGeneratorTask({
dependencies: {
renderers: MY_GENERATOR_GENERATED.renderers.provider,
},
run({ renderers }) {
return {
build: async (builder) => {
// Simple render
await builder.apply(renderers.myTemplate.render());
// Render with variables
await builder.apply(
renderers.service.render({
variables: {
TPL_SERVICE_NAME: 'UserService',
},
}),
);
},
};
},
}),
});
Use the MCP action to scaffold new generators:
mcp__baseplate_dev_server__create_generator({
name: 'category/my-generator',
directory: 'packages/fastify-generators/src/generators',
includeTemplates: true,
});
Example directories:
packages/core-generators/src/generatorspackages/fastify-generators/src/generatorspackages/react-generators/src/generatorsplugins/plugin-storage/src/generators/fastifyThis creates:
<name>.generator.ts - Main generator with boilerplateindex.ts - Barrel exportgenerated/index.ts - Placeholder for generated exportsextractor.json - Template extractor configurationAfter creating, add templates to the templates/ directory and regenerate the typed template helpers:
mcp__baseplate_dev_server__generate_templates({});
Or via command line:
pnpm generate:templates
Make changes in the appropriate example project (e.g., examples/blog-with-auth).
Note: You only need to modify one project at a time.
# Validate changes work in the example project
pnpm run:example blog-with-auth -- pnpm build
pnpm run:example blog-with-auth -- pnpm typecheck
Before proceeding with template extraction, pause and allow the user to review the code changes. This ensures:
IMPORTANT: At this stage, you should ONLY have modified files in the example project (e.g.,
examples/blog-with-auth). Do NOT modify any generator code (*.generator.ts), template files intemplates/directories, or plugin code until AFTER the user has reviewed and approved the example project changes. Generator modifications happen in Step 5 after template extraction.
For most cases: Skip this step! Template metadata is automatically preserved when modifying existing template files.
Only manage metadata when:
Check existing metadata:
mcp__baseplate_dev_server__show_template_metadata({
filePath: 'src/components/my-component.tsx',
project: 'blog-with-auth',
});
Configure NEW templates only when needed:
// For NEW TypeScript templates
mcp__baseplate_dev_server__configure_ts_template({
filePath: 'src/components/brand-new-component.tsx',
generator: '@baseplate-dev/react-generators#core/react',
templateName: 'brand-new-component',
project: 'blog-with-auth',
});
// For NEW text templates with variables
mcp__baseplate_dev_server__configure_text_template({
filePath: 'src/config/new-config.json',
generator: '@baseplate-dev/core-generators',
templateName: 'new-config',
variables: { appName: { value: 'MyApp' } },
project: 'blog-with-auth',
});
// For NEW raw/binary templates
mcp__baseplate_dev_server__configure_raw_template({
filePath: 'public/new-icon.ico',
generator: '@baseplate-dev/core-generators',
templateName: 'new-icon',
project: 'blog-with-auth',
});
Delete template completely:
mcp__baseplate_dev_server__delete_template({
filePath: 'src/components/old-component.tsx',
project: 'blog-with-auth',
});
Extract templates from your working codebase:
mcp__baseplate_dev_server__extract_templates({
project: 'blog-with-auth',
app: 'backend', // or 'web' for frontend
});
This updates the local generator templates based on your code changes.
Back in the repository root:
# Fix any type errors from added/removed variables and templates
pnpm typecheck
# Update generator configurations, schemas, and UI as needed:
# - Wire up new variables in compilers
# - Update schemas in project-builder-web or plugins
# - Add/remove generator logic as needed
# Validate changes
pnpm build && pnpm lint
Check for differences between written and generated code:
mcp__baseplate_dev_server__diff_project({
project: 'blog-with-auth',
packages: ['backend'], // or ['web'] for frontend
});
Analyze the diff results:
sync-file or ignoredIncremental fixes with sync-file:
For acceptable differences that you want to eliminate before the final sync, use the sync-file command to apply individual files:
// Apply specific files from generated output
mcp__baseplate_dev_server__sync_file({
project: 'blog-with-auth',
app: 'backend',
files: ['src/routes/users.ts', 'src/models/*.ts'],
});
This writes the generated version directly to both the working directory and baseplate/generated/ folder without performing a full sync. Use this to incrementally clear diffs before running the final overwrite sync in step 8.
Intentional differences are changes that should exist in the example project but NOT be extracted into generators. Common examples:
For files with intentional differences:
// Add files to snapshot
mcp__baseplate_dev_server__snapshot_add({
project: 'blog-with-auth',
app: 'backend',
files: ['src/specific-file.ts'],
});
// For removed files that should stay removed
mcp__baseplate_dev_server__snapshot_add({
project: 'blog-with-auth',
app: 'backend',
files: ['src/removed-file.ts'],
deleted: true,
});
Once diffs are acceptable, synchronize the working codebase:
mcp__baseplate_dev_server__sync_project({
project: 'blog-with-auth',
overwrite: true,
});
Warning: This will overwrite all files in the project. Before running with overwrite: true:
// Run final diff to ensure no unexpected changes
mcp__baseplate_dev_server__diff_project({
project: 'blog-with-auth',
packages: ['backend'],
});
The diff should show no changes or only expected/snapshotted differences.
Also verify the example project still builds and typechecks after the sync:
pnpm run:example blog-with-auth -- pnpm install
pnpm run:example blog-with-auth -- pnpm typecheck
pnpm run:example blog-with-auth -- pnpm build
Sync all example projects to apply your generator changes:
mcp__baseplate_dev_server__sync_all_projects({
overwrite: true,
});
Add a changeset to document the changes for the changelog:
# Create a new changeset file in .changeset/ directory
Example changeset format:
---
'@baseplate-dev/plugin-auth': patch
---
Brief description of the change
- Bullet point details of what changed
- Another detail if needed
Changeset types:
patch: Bug fixes, minor changes (most common)minor: New features, non-breaking changesmajor: Breaking changesPackage names should match the package that was modified (e.g., @baseplate-dev/plugin-auth, @baseplate-dev/fastify-generators, @baseplate-dev/react-generators).
For complex scenarios with intentional differences:
// View current snapshot status
mcp__baseplate_dev_server__snapshot_show({
project: 'blog-with-auth',
app: 'backend',
});
// Remove files from snapshot (let them be generated normally)
mcp__baseplate_dev_server__snapshot_remove({
project: 'blog-with-auth',
app: 'backend',
files: ['src/file.ts'],
});
// Save complete snapshot (overwrites existing)
mcp__baseplate_dev_server__snapshot_save({
project: 'blog-with-auth',
app: 'backend',
force: true,
});
// Create a new generator
mcp__baseplate_dev_server__create_generator({
name: 'email/sendgrid',
directory: 'packages/fastify-generators/src/generators',
includeTemplates: true,
});
// List current templates
mcp__baseplate_dev_server__list_templates({
project: 'blog-with-auth',
});
// Generate template files after manual extractor.json changes
mcp__baseplate_dev_server__generate_templates({
project: 'blog-with-auth',
});
// Remove outdated templates
mcp__baseplate_dev_server__delete_template({
filePath: 'src/outdated-file.ts',
project: 'blog-with-auth',
});
If you encounter issues with core generator logic (e.g., providers not wiring correctly, template rendering issues, or unexpected generation behavior), stop and highlight the issue for the user rather than trying to hack around it. These issues often indicate deeper problems that need proper resolution.
pnpm typecheck to identify specific issuessnapshot_add to add resolved files to snapshot../components/button.ts vs @src/components/button.ts)pnpm run:example <name> -- pnpm typecheck)sync_all_projects to ensure all examples are updated