Commands
Define commands with defineCommand, register them in defineConfig, and set a default command.
A command is the unit zodline dispatches on. defineCommand describes one; defineConfig registers it
under the name users type.
Defining a command
import { z } from 'zod';
import { defineCommand, defineOptions } from 'zodline';
export const build = defineCommand({
description: 'Build the project',
options: defineOptions(
z.object({
watch: z.boolean().default(false).describe('Rebuild on change'),
}),
{ w: 'watch' },
),
action: async (options) => {
console.log(options.watch ? 'Watching...' : 'Building once');
},
});
description?string
Shown next to the command in the help screen.
stringoptions?OptionsDefinition
The result of defineOptions(). Omit for a command with no flags.
OptionsDefinitionargs?z.ZodType
Schema for the positional arguments array. Omit to receive them unvalidated.
z.ZodTypeaction(options, args) => void | Promise<void>
What the command does. Never called by zodline itself.
(options, args) => void | Promise<void>defineCommand is a typing helper — it returns its input unchanged. Its job is to carry the Zod generics so
options and args inside action are inferred instead of any.
Registering commands
The keys of commands are the names users type. They can contain any characters a shell will pass through,
which makes namespaced names practical:
import { defineConfig } from 'zodline';
export const config = defineConfig({
meta: {
name: 'my-cli',
version: '1.0.0',
description: 'Does useful things',
},
commands: {
build,
'apps:bundles:create': createBundle,
},
});
Commands without options
options is optional. Leave it out and action receives an empty object:
const version = defineCommand({
description: 'Print the resolved toolchain version',
action: async () => {
console.log(process.version);
},
});
Default command
defaultCommand runs when the user passes no command name at all:
export const config = defineConfig({
meta: { name: 'my-cli', version: '1.0.0' },
commands: { build, serve },
defaultCommand: build,
});
my-cli --watch # runs build
my-cli build # also runs build
my-cli nope # throws: Unknown command: nope
Two behaviours to keep in mind:
--helpand--versionare handled before the default command, somy-cli --helpstill prints the command list rather than runningbuild.- The default command is only reached when there are no positional arguments, so it always receives an empty
argsarray. A command that needs positional arguments has to be invoked by name.
Without a defaultCommand, running the CLI bare prints the help screen and throws
ZodlineError: No command specified.
Next: Options
Schemas, aliases, kebab-case, arrays and defaults.