Codegen
Type your commands, emojis, and plugins by running seedcord codegen. Covers why a separate step exists, what it writes, how it loads your bot, catching a stale file in CI, and the commands it skips or rejects as duplicates.
Here's a command that declares one option.
import {
BuilderComponent,
RegisterCommand
} from '@seedcord/gateway';
@RegisterCommand('global')
export class SearchCommand extends BuilderComponent<'command'> {
constructor() {
super('command');
this.instance
.setName('search')
.setDescription('Look something up')
.addStringOption((o) =>
o
.setName('query')
.setDescription('The term')
.setRequired(true)
);
}
}An option's name and its required flag are arguments to method calls. Those calls run when something constructs the class. TypeScript reads the source of that chain without running it, so the name query only exists once the chain has run.
seedcord codegen records those names for you. It imports every .ts and .js file under your commands folder, constructs each decorated class it finds, reads the JSON that class's builder produced, and writes what it found to seedcord-gen.d.ts.
pnpm run codegenRun it after you add a command, change its options, attach a plugin, or edit your emoji block.
Your handler then reads its options through this.options. getString('query') comes back as string, because you marked that option required. A typo in the name stops the build. Command options covers what each kind returns.
You rerun it whenever a command changes, and the file sits stale until you do. seedcord dev offers to run codegen whenever it re-registers your commands, so most of the time you answer y and never think about it.
Note
Codegen writes the file into the root folder set in your seedcord.config.ts. A scaffolded project sets root: './src', which puts it at src/seedcord-gen.d.ts.
What it writes
Every run rewrites the whole file from scratch.
| what it types | filled from |
|---|---|
SlashRegistry | every command class in your commands folder |
UserContextMenuRegistry | the same folder, for right-click commands on a user |
MessageContextMenuRegistry | the same folder, for right-click commands on a message |
EmojiMap | the bot.emojis block on your config, which Components shows in full |
Core | the key you gave each attached plugin |
Core only appears once you attach a plugin. It types this.core.db, for example.
Codegen records only the keys you attached. TypeScript resolves each plugin's type from the bot you built, since your attach() call already names the plugin class. Your handler still reads this.core.db with every method that plugin declares.
Gateway and http differ
Codegen constructs your bot
To read your commands folder and your emoji block, codegen imports the module your instance setting points at. Constructing your bot runs every attach() call and every plugin constructor.
Even so, your bot never starts. Nothing calls start(), so no plugin connects, no task runs, and nothing you've written reaches Discord.
A command constructor that throws fails the whole run, since codegen has to construct the class to read its options. CliCodegenCommandConstructorThrew names the class, the file, and what your constructor threw. Keep anything that can fail out of a command constructor.
Catching a stale file in CI
Some drift breaks the build for you. If you add a command without rerunning codegen, the handler naming its route fails to compile. Your editor flags it immediately.
Other drift compiles perfectly. If you delete a command, its entry stays in SlashRegistry, still typing a route your bot stopped answering. Passing --check renders the file in memory and compares it to the copy on disk, which catches the kind that still compiles.
pnpm exec seedcord codegen --checkWhen the two match, the command prints nothing and exits 0. A difference prints the path it compared and exits 1.
Augmentations are out of date. Run `seedcord codegen` and commit /home/you/my-bot/src/seedcord-gen.d.ts.--check only reports, so you fix the drift by running plain seedcord codegen. The scaffold does not wire a script for it, so put it wherever your pipeline runs its checks.
If codegen can't read your commands folder, it throws CliCodegenCommandsDirUnreadable, because a run that found no commands would pass --check against a file that's already stale.
Commands codegen passes over
Codegen records a command class once it carries @RegisterCommand. If a class has no decorator, codegen skips that export entirely, so your route never appears in SlashRegistry. You get the same compile failure as a missed codegen run.
Your commands folder can hold helpers and constants too. Codegen imports each of those files as well, keeps the exports that are decorated command classes, and ignores everything else.
Two commands, one name
Discord scopes command names per kind. seedcord enforces the same rule. Two commands resolving to one slash route throw CliCodegenDuplicateRoute. Two context menus of one kind sharing a name throw CliCodegenDuplicateContextMenu. Both errors print the two file paths, so you know which one to rename.