5.7 KiB
5.7 KiB
Plugin CLI Example
Complete example of an oclif CLI with plugin support.
Overview
This example shows:
- Main CLI with core commands
- Plugin system for extensibility
- Plugin installation and management
- Shared hooks between CLI and plugins
Main CLI Structure
mycli/
├── package.json
├── src/
│ ├── commands/
│ │ └── core.ts
│ └── hooks/
│ └── init.ts
└── plugins/
└── plugin-deploy/
├── package.json
└── src/
└── commands/
└── deploy.ts
Step 1: Create Main CLI
Main CLI package.json
{
"name": "mycli",
"version": "1.0.0",
"oclif": {
"bin": "mycli",
"commands": "./lib/commands",
"plugins": [
"@oclif/plugin-help",
"@oclif/plugin-plugins"
],
"hooks": {
"init": "./lib/hooks/init"
}
},
"dependencies": {
"@oclif/core": "^3.0.0",
"@oclif/plugin-help": "^6.0.0",
"@oclif/plugin-plugins": "^4.0.0"
}
}
Core Command
File: src/commands/core.ts
import { Command, Flags } from '@oclif/core'
export default class Core extends Command {
static description = 'Core CLI functionality'
static flags = {
version: Flags.boolean({
char: 'v',
description: 'Show CLI version',
}),
}
async run(): Promise<void> {
const { flags } = await this.parse(Core)
if (flags.version) {
this.log(`Version: ${this.config.version}`)
return
}
this.log('Core CLI is running')
// List installed plugins
const plugins = this.config.plugins
this.log(`\nInstalled plugins: ${plugins.length}`)
plugins.forEach(p => {
this.log(` - ${p.name} (${p.version})`)
})
}
}
Init Hook
File: src/hooks/init.ts
import { Hook } from '@oclif/core'
const hook: Hook<'init'> = async function (opts) {
// Initialize CLI
this.debug('Initializing mycli...')
// Check for updates, load config, etc.
}
export default hook
Step 2: Create Plugin
Plugin package.json
{
"name": "@mycli/plugin-deploy",
"version": "1.0.0",
"description": "Deployment plugin for mycli",
"oclif": {
"bin": "mycli",
"commands": "./lib/commands",
"topics": {
"deploy": {
"description": "Deployment commands"
}
}
},
"dependencies": {
"@oclif/core": "^3.0.0"
}
}
Deploy Command
File: plugins/plugin-deploy/src/commands/deploy.ts
import { Command, Flags } from '@oclif/core'
export default class Deploy extends Command {
static description = 'Deploy application'
static examples = [
'<%= config.bin %> deploy --env production',
]
static flags = {
env: Flags.string({
char: 'e',
description: 'Environment to deploy to',
options: ['development', 'staging', 'production'],
required: true,
}),
force: Flags.boolean({
char: 'f',
description: 'Force deployment',
default: false,
}),
}
async run(): Promise<void> {
const { flags } = await this.parse(Deploy)
this.log(`Deploying to ${flags.env}...`)
if (flags.force) {
this.log('Force deployment enabled')
}
// Deployment logic here
this.log('✓ Deployment successful')
}
}
Step 3: Build and Link Plugin
# Build main CLI
cd mycli
npm run build
# Build plugin
cd plugins/plugin-deploy
npm run build
# Link plugin to main CLI
cd ../../
mycli plugins:link ./plugins/plugin-deploy
Step 4: Use Plugin Commands
# List plugins
mycli plugins
# Use plugin command
mycli deploy --env production
# Get help for plugin command
mycli deploy --help
Step 5: Install Plugin from npm
Publish Plugin
cd plugins/plugin-deploy
npm publish
Install Plugin
mycli plugins:install @mycli/plugin-deploy
Plugin Management Commands
# List installed plugins
mycli plugins
# Install plugin
mycli plugins:install @mycli/plugin-name
# Update plugin
mycli plugins:update @mycli/plugin-name
# Uninstall plugin
mycli plugins:uninstall @mycli/plugin-name
# Link local plugin (development)
mycli plugins:link /path/to/plugin
Advanced: Plugin with Hooks
File: plugins/plugin-deploy/src/hooks/prerun.ts
import { Hook } from '@oclif/core'
const hook: Hook<'prerun'> = async function (opts) {
// Check deployment prerequisites
if (opts.Command.id === 'deploy') {
this.log('Checking deployment prerequisites...')
// Check environment, credentials, etc.
}
}
export default hook
Register in plugin package.json:
{
"oclif": {
"hooks": {
"prerun": "./lib/hooks/prerun"
}
}
}
Key Concepts Demonstrated
- Plugin System: @oclif/plugin-plugins integration
- Plugin Discovery: Automatic command loading from plugins
- Plugin Management: Install, update, uninstall commands
- Local Development: plugins:link for local plugin development
- Hooks: Shared hooks between main CLI and plugins
- Topic Commands: Organized plugin commands (deploy:*)
- Plugin Metadata: Package.json oclif configuration
- Plugin Distribution: Publishing to npm
Testing Plugins
File: plugins/plugin-deploy/test/commands/deploy.test.ts
import { expect, test } from '@oclif/test'
describe('deploy', () => {
test
.stdout()
.command(['deploy', '--env', 'production'])
.it('deploys to production', ctx => {
expect(ctx.stdout).to.contain('Deploying to production')
expect(ctx.stdout).to.contain('successful')
})
test
.command(['deploy'])
.catch(error => {
expect(error.message).to.contain('Missing required flag')
})
.it('requires env flag')
})