| name | Modify Generated Code |
| description | Workflow for modifying generated code in Baseplate, including template extraction, generator updates, and project synchronization. |
Modify Generated Code Skill
Use this skill when modifying code that is generated by Baseplate generators, adding new templates, or updating generator behavior.
Overview
Baseplate follows a code-first development approach:
- Make changes directly in working codebases (example projects)
- Extract those changes into reusable templates
- Update generators to use the new templates
- Validate and sync the generated code
Example projects:
examples/blog-with-auth- Blog application with authenticationexamples/todo-with-auth0- Todo application with Auth0 integration
Documentation References
For 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',
});
Generator Architecture
Important: Never manually edit files in
generated/directories. These are auto-generated bypnpm generate:templates. Only modify:
- Template source files in
templates/directoriesextractor.jsonconfiguration- The main
*.generator.tsfile
Generator Packages
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 |
Generator Structure
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());
},
};
},
}),
}),
});
Key Providers
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 |
Shared Providers and Scopes
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,
}
Template Rendering with Renderers
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',
},
}),
);
},
};
},
}),
});
Creating New Generators
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/fastify
This creates:
<name>.generator.ts- Main generator with boilerplateindex.ts- Barrel exportgenerated/index.ts- Placeholder for generated exportsextractor.json- Template extractor configuration
After 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
Step-by-Step Process
1. Code Development
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
pnpm build && pnpm lint
2. Review Changes with User
Before proceeding with template extraction, pause and allow the user to review the code changes. This ensures:
- The fixes/features look correct before extracting to templates
- Any issues can be caught early before they propagate to generators
- The user understands what will be extracted
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.
3. Template Metadata Management (Optional)
For most cases: Skip this step! Template metadata is automatically preserved when modifying existing template files.
Only manage metadata when:
- Creating brand new template files
- Changing fundamental template properties (name, generator, type)
- Removing template files completely
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',
});
4. Template Extraction
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.
5. Generator Updates
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
6. Diff Validation
Check for differences between written and generated code:
mcp__baseplate_dev_server__diff_project({
project: 'blog-with-auth',
packages: ['backend'], // or ['web'] for frontend
});
7. Handle Differences
Analyze the diff results:
- Acceptable differences (import aliases, minor formatting): Can be incrementally fixed using
sync-fileor ignored - Significant differences: Update generators to match written code
- Intentional differences: Use snapshot system
Incremental 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:
- Unit tests specific to the example project
- Demo data or sample content
- Example-specific configuration
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,
});
8. Code Synchronization
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:
- Ensure your diff looks correct (step 6)
- Consider committing your current changes first, so you can revert if needed
9. Final Validation
// 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.
10. Sync All Projects
Sync all example projects to apply your generator changes:
mcp__baseplate_dev_server__sync_all_projects({
overwrite: true,
});
11. Create Changeset
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 changes
Package names should match the package that was modified (e.g., @baseplate-dev/plugin-auth, @baseplate-dev/fastify-generators, @baseplate-dev/react-generators).
Advanced: Snapshot Management
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,
});
Advanced: Template Management
// 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',
});
Important: When to Stop and Ask for Help
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.
Troubleshooting
Type Errors After Template Extraction
- Check that all template variables are properly defined in generators
- Ensure import providers are correctly configured
- Run
pnpm typecheckto identify specific issues
Diff Conflicts
- Use
snapshot_addto add resolved files to snapshot - For manual conflicts, edit files and re-add to snapshot
- Consider if differences indicate generator bugs vs. intentional customizations
Import Alias Differences
- Differences in relative paths vs. import aliases are acceptable as long as they resolve to the same file (e.g.,
../components/button.tsvs@src/components/button.ts) - Use snapshots if these differences are intentional and should be preserved
- Update generators if aliases should be standardized
Best Practices
- Start with working code - Always develop features in a concrete codebase first
- Use snapshots judiciously - Only for intentional differences, not generator bugs
- Validate frequently - Run diff commands often during development
- Test generated code - Ensure
pnpm build && pnpm lintpasses on synced code - Keep commits focused - Separate generator changes from template changes when possible
- Document template variables - Use clear, descriptive names for template variables
- Sync all projects when done - Always run
sync_all_projectsto ensure all examples are updated