Files
gh-vanman2024-cli-builder-p…/skills/gluegun-patterns/examples/plugin-system
2025-11-30 09:04:14 +08:00
..
2025-11-30 09:04:14 +08:00

Gluegun Plugin System Example

Demonstrates how to build an extensible CLI with plugin architecture.

Structure

plugin-system/
├── src/
│   ├── cli.ts              # CLI with plugin loading
│   ├── commands/
│   │   └── plugin.ts       # Plugin management command
│   └── plugins/
│       ├── custom-plugin.ts    # Example plugin
│       └── validator-plugin.ts # Validation plugin
├── package.json
└── README.md

Features

  • Plugin discovery and loading
  • Custom toolbox extensions via plugins
  • Plugin-provided commands
  • Plugin configuration management

Installation

npm install
npm run build

Usage

Load Plugins

# CLI automatically loads plugins from:
# - ./plugins/
# - ./node_modules/mycli-*

Use Plugin Commands

# Commands added by plugins
./bin/cli validate
./bin/cli custom-action

Use Plugin Extensions

// In any command
const { myPlugin } = toolbox
myPlugin.doSomething()

Creating a Plugin

Basic Plugin Structure

module.exports = (toolbox) => {
  const { print } = toolbox

  // Add to toolbox
  toolbox.myPlugin = {
    version: '1.0.0',
    doSomething: () => {
      print.info('Plugin action executed')
    }
  }
}

Plugin with Commands

module.exports = (toolbox) => {
  const { runtime } = toolbox

  runtime.addPlugin({
    name: 'my-plugin',
    commands: [
      {
        name: 'my-command',
        run: async (toolbox) => {
          // Command implementation
        }
      }
    ]
  })
}

Plugin Discovery

The CLI looks for plugins in:

  1. Local plugins directory: ./plugins/*.js
  2. Node modules: ./node_modules/mycli-*
  3. Scoped packages: @scope/mycli-*

Plugin Naming Convention

  • Local plugins: Any .js or .ts file in plugins/
  • NPM plugins: Must match pattern mycli-*
  • Example: mycli-validator, @myorg/mycli-helper

Key Patterns

1. Load Plugins from Directory

cli.plugins('./plugins', { matching: '*.js' })

2. Load NPM Plugins

cli.plugins('./node_modules', {
  matching: 'mycli-*',
  hidden: true
})

3. Add Toolbox Extension

toolbox.validator = {
  validate: (data) => { /* ... */ }
}

4. Register Commands

runtime.addPlugin({
  name: 'my-plugin',
  commands: [/* commands */]
})

Example Plugins

custom-plugin.ts

Adds custom utilities to toolbox.

toolbox.custom = {
  formatDate: (date) => { /* ... */ },
  parseConfig: (file) => { /* ... */ }
}

validator-plugin.ts

Adds validation command and utilities.

toolbox.validator = {
  validateFile: (path) => { /* ... */ },
  validateSchema: (data) => { /* ... */ }
}

Publishing Plugins

1. Create NPM Package

{
  "name": "mycli-myplugin",
  "main": "dist/index.js",
  "keywords": ["mycli", "plugin"]
}

2. Export Plugin

module.exports = (toolbox) => {
  // Plugin implementation
}

3. Publish

npm publish

4. Install and Use

npm install mycli-myplugin
# Automatically loaded by CLI

Advanced Patterns

Conditional Plugin Loading

if (config.enablePlugin) {
  cli.plugins('./plugins/optional')
}

Plugin Configuration

toolbox.myPlugin = {
  config: await filesystem.read('.mypluginrc', 'json'),
  // Plugin methods
}

Plugin Dependencies

module.exports = (toolbox) => {
  // Check for required plugins
  if (!toolbox.otherPlugin) {
    throw new Error('Requires other-plugin')
  }
}

Best Practices

  1. Namespace Extensions: Use unique names for toolbox extensions
  2. Document APIs: Provide clear documentation for plugin methods
  3. Handle Errors: Validate inputs and handle failures gracefully
  4. Version Plugins: Use semantic versioning
  5. Test Plugins: Write tests for plugin functionality

Testing Plugins

import { build } from 'gluegun'

test('plugin loads correctly', async () => {
  const cli = build().src(__dirname).plugins('./plugins').create()
  const toolbox = await cli.run()

  expect(toolbox.myPlugin).toBeDefined()
})