Configuration
Customize Coda generation with coda.config.mjs
Coda works out of the box with sensible defaults, but you can customize its behavior with a coda.config.mjs
file.
Initialize Configuration
Create a configuration file using the init
command:
coda init
This creates a coda.config.mjs
file with helpful comments and examples.
Configuration Options
idlPath
- Type:
string | string[]
- Default:
"./target/idl/program.json"
- Description: Path to your Anchor IDL file(s). Can be a single path or array for multiple IDLs.
outputDir
- Type:
string
- Default:
"./src/generated"
- Description: Directory where generated TypeScript files will be placed
docs
- Type:
{ path?: string, npmPackageName?: string }
- Default:
{}
- Description: Documentation-specific options
path
: Directory where generated markdown documentation will be placed (default:"./docs"
)npmPackageName
: NPM package name to display badge and link in documentation
visitors
- Type:
Visitor[] | (context) => Visitor[]
- Default:
[]
- Description: Codama visitors to transform the AST. Can be an array or function that receives the IDL context.
Basic Configuration
Simple configuration with custom paths:
import { defineConfig } from "@macalinao/coda";
export default defineConfig({
idlPath: "./idls/my_program.json",
outputDir: "./src/client",
});
Multiple IDLs
Generate clients from multiple programs in one project:
import { defineConfig } from "@macalinao/coda";
export default defineConfig({
idlPath: [
"./idls/program_a.json",
"./idls/program_b.json",
"./idls/program_c.json",
],
outputDir: "./src/generated",
});
Important: Handling Naming Conflicts
When generating clients from multiple IDLs, be aware of potential naming conflicts:
- Naming conflicts will NOT throw errors during code generation
- Conflicting names will be silently overwritten by the last processed IDL
- This can lead to unexpected behavior and missing types/instructions
To avoid conflicts, use the renameVisitor
from @macalinao/codama-rename-visitor
:
import { renameVisitor } from "@macalinao/codama-rename-visitor";
export default defineConfig({
visitors: [
renameVisitor({
// Rename conflicting instructions in specific programs
programA: {
instructions: {
transfer: "transferA", // Rename transfer to transferA
deposit: "depositA", // Rename deposit to depositA
},
definedTypes: {
account: "accountA", // Rename account type to accountA
},
},
}),
],
});
See the Quarry configuration for a real-world example where instructions like claimRewards
, initMiner
, and withdrawTokens
were renamed to avoid conflicts between the quarryMine
and quarryMergeMine
programs.
Custom PDAs
Define program-derived addresses with custom seeds:
import { defineConfig } from "@macalinao/coda";
import {
addPdasVisitor,
constantPdaSeedNodeFromString,
publicKeyTypeNode,
variablePdaSeedNode,
} from "codama";
const addCustomPDAsVisitor = addPdasVisitor({
tokenMetadata: [
{
name: "metadata",
seeds: [
constantPdaSeedNodeFromString("utf8", "metadata"),
variablePdaSeedNode(
"programId",
publicKeyTypeNode(),
"The address of the program",
),
variablePdaSeedNode(
"mint",
publicKeyTypeNode(),
"The address of the mint account",
),
],
},
],
});
export default defineConfig({
idlPath: "./idls/token_metadata.json",
outputDir: "./src/generated",
visitors: [addCustomPDAsVisitor],
});
Advanced Example: Quarry Configuration
Real-world example with multiple IDLs, PDAs, and rename visitors:
import {
addPdasVisitor,
constantPdaSeedNodeFromString,
defineConfig,
publicKeyTypeNode,
variablePdaSeedNode,
} from "@macalinao/coda";
import { renameVisitor } from "@macalinao/codama-rename-visitor";
export default defineConfig({
// Multiple IDLs from the same project
idlPath: [
"./idls/quarry_mine.json",
"./idls/quarry_mint_wrapper.json",
"./idls/quarry_operator.json",
"./idls/quarry_merge_mine.json",
"./idls/quarry_redeemer.json",
"./idls/quarry_registry.json",
],
outputDir: "./src/generated",
visitors: [
// Add PDAs for each program
addPdasVisitor({
quarryMine: [
{
name: "rewarder",
seeds: [
constantPdaSeedNodeFromString("utf8", "Rewarder"),
variablePdaSeedNode("base", publicKeyTypeNode()),
],
},
{
name: "quarry",
seeds: [
constantPdaSeedNodeFromString("utf8", "Quarry"),
variablePdaSeedNode("rewarder", publicKeyTypeNode()),
variablePdaSeedNode("tokenMint", publicKeyTypeNode()),
],
},
{
name: "miner",
seeds: [
constantPdaSeedNodeFromString("utf8", "Miner"),
variablePdaSeedNode("quarry", publicKeyTypeNode()),
variablePdaSeedNode("authority", publicKeyTypeNode()),
],
},
],
quarryMergeMine: [
{
name: "mergePool",
seeds: [
constantPdaSeedNodeFromString("utf8", "MergePool"),
variablePdaSeedNode("primaryMint", publicKeyTypeNode()),
],
},
],
}),
// Rename instructions to avoid conflicts between programs
renameVisitor({
quarryMergeMine: {
instructions: {
claimRewards: "claimRewardsMM",
initMiner: "initMinerMM",
withdrawTokens: "withdrawTokensMM",
},
definedTypes: {
claimEvent: "claimEventMM",
},
},
}),
],
});
Using Visitor Functions
Access the IDL context in your visitors:
import { defineConfig } from "@macalinao/coda";
import { someCustomVisitor } from "./visitors/custom.js";
export default defineConfig({
visitors: ({ idl, idls }) => {
// Access parsed IDL(s) to customize visitors
console.log(`Generating for ${idl.name}`);
return [
someCustomVisitor(idl),
// Add more visitors based on IDL content
];
},
});
Command Line Arguments
Configuration file settings can be overridden via command line:
Generate Command
# Override IDL path
coda generate --idl ./other/idl.json
coda generate -i ./other/idl.json
# Override output directory
coda generate --output ./other/output
coda generate -o ./other/output
# Use different config file
coda generate --config ./custom.config.mjs
coda generate -c ./custom.config.mjs
Docs Command
# Override IDL path
coda docs --idl ./other/idl.json
coda docs -i ./other/idl.json
# Use different config file
coda docs --config ./custom.config.mjs
coda docs -c ./custom.config.mjs
Priority Order
Settings are applied in this priority order (highest to lowest):
- Command line arguments
- Configuration file (
coda.config.mjs
) - Default values
Integration with Build Tools
Add to your package.json scripts:
{
"scripts": {
"codegen": "coda generate",
"prebuild": "bun run codegen",
"dev": "bun run codegen && next dev",
"clean": "rm -rf ./src/generated"
}
}
Troubleshooting
Config not loading
- Ensure file is named
coda.config.mjs
with.mjs
extension - Check that you're using ES module syntax (
export default
) - Verify the config file is in your project root or specified via
--config
Multiple IDL conflicts
- Use
renameVisitor
to rename conflicting instructions/types - Generate to separate output directories
- Consider splitting into multiple config files
Visitor errors
- Ensure visitors are compatible with your Codama version
- Check that visitor functions return valid visitor objects
- Use the context parameter to access IDL information