Plugin System
The plugin system allows you to extend fluent-gen-ts with custom functionality, validation, transformations, and custom methods. Create powerful, reusable plugins that transform how builders are generated.
Why Use Plugins?
Plugins solve the problem of repetitive manual builder customization. Instead of extending every generated builder individually, plugins apply transformations during generation automatically.
Without Plugins:
// Manual approach - repetitive for every builder
class UserBuilder extends GeneratedUserBuilder {
withValidatedEmail(email: string): this {
if (!isEmail(email)) throw new Error('Invalid email');
return this.withEmail(email);
}
}
With Plugins:
// Automatic, reusable, type-safe
const validationPlugin = createPlugin('validation', '1.0.0')
.transformPropertyMethods(builder =>
builder
.when(ctx => ctx.property.name === 'email')
.setValidator('if (!isEmail(value)) throw new Error("Invalid email")')
.done(),
)
.build();
// Applied to ALL builders automatically!
Quick Start
Create your first plugin in under a minute:
// my-plugin.ts
import { createPlugin } from 'fluent-gen-ts';
const plugin = createPlugin('my-awesome-plugin', '1.0.0')
.setDescription('Adds email validation')
.transformPropertyMethods(builder =>
builder
.when(ctx => ctx.property.name === 'email')
.setValidator(
`
if (value && !value.includes('@')) {
throw new Error('Invalid email format');
}
`,
)
.done(),
)
.build();
export default plugin;
Add to your configuration:
// fluentgen.config.js
export default {
plugins: ['./my-plugin.ts'],
targets: [{ file: 'src/types.ts', types: ['User'] }],
};
Run generation:
npx fluent-gen-ts batch
Now all email properties are automatically validated!
When to Use Plugins
✅ Perfect For:
- Validation - Email, URL, phone number validation across all builders
- Type Transformations - Accept
string | Date
for date fields, auto-convert IDs - Custom Methods - Add
.withFakeData()
,.asAdmin()
,.withTimestamps()
- Import Management - Automatically add required dependencies
- Build Hooks - Insert logic before/after object creation
- Testing Utilities - Add
.buildMany()
,.asTestDouble()
methods
❌ Not Needed For:
- One-off customization - Just extend the builder manually
- Simple property defaults - Use builder's initial values
- Runtime behavior - Plugins work at generation time only
Core Capabilities
1. Property Method Transformations
Modify how withProperty()
methods work:
.transformPropertyMethods(builder =>
builder
.when(ctx => ctx.property.name === 'age')
.setParameter('number | string') // Accept string too
.setExtractor('Number(value)') // Convert to number
.setValidator('if (value < 0) throw new Error("Age must be positive")')
.done()
)
2. Custom Methods
Add new methods to builders:
.addMethod(method =>
method
.name('withTimestamps')
.returns('this')
.implementation(`
return this
.withCreatedAt(new Date())
.withUpdatedAt(new Date());
`)
)
3. Build Hooks
Insert logic before/after build:
.transformBuildMethod(transform =>
transform.insertBefore('return this.buildWithDefaults', `
// Auto-generate ID if missing
if (!this.has('id')) {
this.set('id', generateUUID());
}
`)
)
4. Type Matching
Match types with powerful matchers:
import { primitive, object, array, union } from 'fluent-gen-ts';
.when(ctx => ctx.type.isPrimitive('string'))
.when(ctx => ctx.type.matches(object('User')))
.when(ctx => ctx.type.matches(array().of(primitive('string'))))
.when(ctx => ctx.type.matches(union().containing(primitive('null'))))
5. Deep Type Transformations
Transform types recursively at any depth:
.when(ctx => ctx.type.containsDeep(primitive('string')))
.setParameter(ctx =>
ctx.type
.transformDeep()
.replace(primitive('string'), 'string | TaggedValue<string>')
.toString()
)
Plugin Examples
Email Validation
const emailValidation = createPlugin('email-validation', '1.0.0')
.requireImports(imports => imports.addExternal('validator', ['isEmail']))
.transformPropertyMethods(builder =>
builder
.when(ctx => ctx.property.name === 'email')
.setValidator(
'if (value && !isEmail(value)) throw new Error("Invalid email")',
)
.done(),
)
.build();
Testing Helper
const testingPlugin = createPlugin('testing', '1.0.0')
.requireImports(imports => imports.addExternal('faker', ['faker']))
.addMethod(method =>
method.name('withFakeData').returns('this').implementation(`
if (this.has('email')) this.withEmail(faker.internet.email());
if (this.has('name')) this.withName(faker.person.fullName());
return this;
`),
)
.build();
Auto UUID
const autoUUID = createPlugin('auto-uuid', '1.0.0')
.requireImports(imports => imports.addExternal('uuid', ['v4 as uuidv4']))
.transformBuildMethod(transform =>
transform.insertBefore(
'return this.buildWithDefaults',
`
if (!this.has('id')) {
this.set('id', uuidv4());
}
`,
),
)
.build();
Configuration
Single Plugin
// fluentgen.config.js
export default {
plugins: ['./plugins/validation.ts'],
targets: [{ file: './src/types.ts', types: ['User'] }],
};
Multiple Plugins
export default {
plugins: [
'./plugins/validation.ts',
'./plugins/testing.ts',
'./plugins/database.ts',
],
targets: [{ file: './src/types.ts', types: ['User', 'Product'] }],
};
Note: Plugins execute in order. Order matters when plugins modify the same properties!
Learn More
🚀 Getting Started
Step-by-step tutorial - Create your first plugin with detailed explanations
📖 Plugin System Overview
Comprehensive guide - Architecture, capabilities, and when to use plugins
🔍 API Reference
Quick lookup - All plugin API methods, type matchers, and context objects
📚 Cookbook
20+ Ready-to-Use Plugins - Copy-paste solutions for validation, testing, database, API transforms
⚡ Best Practices
Critical patterns - Rule ordering, error handling, testing, and common pitfalls
Related Documentation
- Advanced Usage - Complex scenarios and patterns
- CLI Commands - Command-line plugin configuration
- Configuration - Config file setup
- Examples - Real-world usage examples
The plugin system is designed to be both powerful and approachable. Start with simple transformations and gradually build up to complex, feature-rich plugins that can be shared across teams and projects.