The Codemods API provides powerful utilities to programmatically modify AdonisJS application source files. This is primarily used in Ace commands for scaffolding, package installation, and code generation.
Overview
The Codemods class enables you to:
- Generate files from stub templates
- Register middleware, policies, and plugins
- Update configuration files (adonisrc.ts, vite.config.ts, etc.)
- Install npm packages
- Define environment variables
- Modify TypeScript source files using AST transformations
The Codemods API requires @adonisjs/assembler to be installed as a dependency. If not available, operations will fail gracefully with warnings.
Getting Started
Access the Codemods instance in Ace commands:
import { BaseCommand } from '@adonisjs/core/ace'
import { CommandOptions } from '@adonisjs/core/types/ace'
export default class MakeController extends BaseCommand {
static commandName = 'make:controller'
async run() {
const codemods = await this.createCodemods()
// Use codemods API
await codemods.makeUsingStub(stubsRoot, 'controller.stub', {
filename: 'UserController'
})
}
}
Generating Files from Stubs
Create files using stub templates with dynamic content:
const result = await codemods.makeUsingStub(
'./stubs',
'controller.stub',
{
filename: 'UserController',
entity: { name: 'User', modelName: 'User' },
resourceful: true
}
)
if (result.status === 'created') {
this.logger.success(`Created ${result.relativeFileName}`)
}
Overwriting Files
Control file overwrite behavior:
codemods.overwriteExisting = true
await codemods.makeUsingStub(stubsRoot, 'controller.stub', {
filename: 'UserController'
})
Using File Contents
Replace stub output with contents from an existing file:
await codemods.makeUsingStub(
stubsRoot,
'migration.stub',
{ filename: 'create_users_table' },
{ contentsFromFile: 'templates/base_migration.ts' }
)
Environment Variables
Define environment variables in .env and .env.example:
await codemods.defineEnvVariables({
DB_CONNECTION: 'mysql',
DB_HOST: 'localhost',
DB_PORT: 3306,
DB_USER: 'root',
SECRET_KEY: 'abc123'
}, {
omitFromExample: ['SECRET_KEY'] // Exclude secrets from .env.example
})
Environment Validation
Add validation schema to start/env.ts:
await codemods.defineEnvValidations({
NODE_ENV: 'Env.schema.enum(["development", "production", "test"] as const)',
PORT: 'Env.schema.number()',
HOST: 'Env.schema.string({ format: "host" })',
APP_KEY: 'Env.schema.string()'
})
Middleware Registration
Register middleware in start/kernel.ts:
// Server middleware
await codemods.registerMiddleware('server', [
{
name: 'cors',
path: '@adonisjs/cors/cors_middleware'
}
])
// Named middleware
await codemods.registerMiddleware('named', [
{
name: 'auth',
path: '#middleware/auth_middleware'
}
])
// Router middleware
await codemods.registerMiddleware('router', [
{
name: 'bodyParser',
path: '@adonisjs/core/bodyparser_middleware'
}
])
Bouncer Policies
Register policies in app/policies/main.ts:
await codemods.registerPolicies([
{
name: 'UserPolicy',
path: '#policies/user_policy'
},
{
name: 'PostPolicy',
path: '#policies/post_policy'
}
])
Package Installation
Install npm packages programmatically:
const success = await codemods.installPackages([
{ name: '@adonisjs/lucid', isDevDependency: false },
{ name: '@adonisjs/auth', isDevDependency: false },
{ name: '@types/node', isDevDependency: true }
])
if (success) {
this.logger.success('Packages installed successfully')
}
Package Manager Detection
Automatic detection of npm, yarn, or pnpm:
// Force specific package manager
await codemods.installPackages(packages, 'pnpm')
Verbose Output
Show installation output:
codemods.verboseInstallOutput = true
await codemods.installPackages(packages)
Manual Installation
Display installation instructions instead:
await codemods.listPackagesToInstall([
{ name: '@adonisjs/lucid', isDevDependency: false },
{ name: '@types/node', isDevDependency: true }
])
// Output:
// Please install following packages
// npm i -D @types/node
// npm i @adonisjs/lucid
Updating Config Files
AdonisRC File
Update adonisrc.ts configuration:
await codemods.updateRcFile((rcFile) => {
rcFile.addCommand('make:custom')
rcFile.addPreloadFile('#app/events/main')
rcFile.addProvider('@adonisjs/lucid/database_provider')
})
Vite Configuration
Register Vite plugins in vite.config.ts:
await codemods.registerVitePlugin({
name: 'vue',
import: 'import vue from "@vitejs/plugin-vue"',
options: '()'
})
await codemods.registerVitePlugin({
name: 'react',
import: 'import react from "@vitejs/plugin-react"',
options: '({ fastRefresh: true })'
})
Japa Configuration
Register Japa plugins in tests/bootstrap.ts:
await codemods.registerJapaPlugin({
name: 'expect',
import: 'import { expect } from "@japa/expect"'
})
await codemods.registerJapaPlugin({
name: 'apiClient',
import: 'import { apiClient } from "@japa/api-client"'
})
Advanced AST Operations
Adding Validators
Create validator files:
await codemods.addValidator({
validatorFileName: 'create_user',
exportName: 'createUserValidator',
contents: `export const createUserValidator = vine.compile(
vine.object({
email: vine.string().email(),
password: vine.string().minLength(8)
})
)`
})
Adding Rate Limiters
Create rate limiter definitions:
await codemods.addLimiter({
limiterFileName: 'api_throttle',
exportName: 'apiThrottleLimiter',
contents: `export const apiThrottleLimiter = limiter.define('api', {
requests: 100,
duration: '1 min'
})`
})
Model Mixins
Add mixins to models:
await codemods.addModelMixins('user', [
{
name: 'SoftDeletes',
importPath: '@adonisjs/lucid/mixins/soft_deletes',
importType: 'named'
}
])
Controller Methods
Add methods to existing controllers:
await codemods.addControllerMethod({
controllerFileName: 'users_controller',
className: 'UsersController',
name: 'destroy',
contents: `async destroy({ params, response }: HttpContext) {
const user = await User.findOrFail(params.id)
await user.delete()
return response.noContent()
}`,
imports: [
{ isType: false, isNamed: true, name: 'HttpContext', path: '@adonisjs/core/http' }
]
})
TsMorph Integration
Access the TsMorph project for advanced AST manipulation:
const project = await codemods.getTsMorphProject()
if (project) {
const sourceFile = project.getSourceFile('app/models/user.ts')
if (sourceFile) {
// Add import
sourceFile.addImportDeclaration({
moduleSpecifier: '@adonisjs/lucid/orm',
namedImports: ['column']
})
// Save changes
await sourceFile.save()
}
}
Event Handling
Listen to codemod errors:
codemods.on('error', (error) => {
this.logger.error(`Codemod failed: ${error.message}`)
})
await codemods.registerMiddleware('server', middleware)
API Reference
Constructor
const codemods = new Codemods(app, logger)
AdonisJS application instance
logger
UIPrimitives['logger']
required
CLI logger instance for output
Properties
Whether to overwrite existing files when generating from stubs
Display verbose output during package installation
Best Practices
Always check if the assembler is available before performing complex operations. Most methods handle missing assembler gracefully but display warnings.
When installing packages, consider providing a manual installation fallback using listPackagesToInstall() for better user experience.
const success = await codemods.installPackages(packages)
if (!success) {
this.logger.warning('Automatic installation failed')
await codemods.listPackagesToInstall(packages)
}