npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

lokus-plugin-sdk

v1.0.3

Published

Official Plugin Development Kit (PDK) for Lokus - Build powerful plugins with TypeScript-first development experience

Downloads

362

Readme

@lokus/plugin-sdk

npm version TypeScript License: MIT

Official Plugin Development Kit (PDK) for Lokus - Build powerful plugins with TypeScript-first development experience.

🚀 Quick Start

Installation

npm install lokus-plugin-sdk

Tip: The easiest way to start is using the CLI, which sets up the SDK for you:

npx lokus-plugin-cli create my-plugin

Create Your First Plugin

import { BasePlugin, PluginContext } from '@lokus/plugin-sdk'

export default class MyPlugin extends BasePlugin {
  async activate(context: PluginContext): Promise<void> {
    await this.initialize(context)
    
    // Register a command
    this.registerCommand('my-plugin.hello', () => {
      this.showNotification('Hello from my plugin! 👋')
    }, {
      title: 'Say Hello',
      category: 'My Plugin'
    })
    
    this.getLogger().info('Plugin activated successfully!')
  }
}

📦 What's Included

🔧 Development Tools

  • Base Classes: BasePlugin, EnhancedBasePlugin with common functionality
  • Utilities: Logger, Config Manager, Event Bus, Disposable Store
  • Validators: Manifest validation, dependency checking
  • Hot Reload: Development server with live reloading

🎨 Plugin Templates

  • Basic Plugin: Simple starter template
  • UI Extension: Custom panels, webviews, status bars
  • Language Support: Syntax highlighting, completions, diagnostics
  • Task Provider: Custom build tasks and runners
  • Debug Adapter: Debugging support for languages
  • Theme Plugin: Custom editor themes
  • Command Plugin: Command palette extensions

🧪 Testing Framework

  • Mocks: Complete API mocking for unit tests
  • Fixtures: Pre-built test data and scenarios
  • Assertions: Plugin-specific test assertions
  • Test Utils: Helper functions for plugin testing

📚 TypeScript Definitions

  • Complete API Coverage: All Lokus APIs with full type safety
  • Extension Points: Commands, UI, Editor, Workspace, etc.
  • Event System: Type-safe event handling
  • Configuration: Strongly-typed plugin configuration

🏗️ Project Templates

Create from Template

# Using npm
npx @lokus/plugin-sdk create my-awesome-plugin

# Using the SDK directly
import { PluginSDK } from '@lokus/plugin-sdk'

await PluginSDK.createFromTemplate('ui-extension', {
  name: 'My Awesome Plugin',
  id: 'my-awesome-plugin',
  description: 'An awesome plugin for Lokus',
  author: 'Your Name',
  outputDir: './my-awesome-plugin',
  typescript: true,
  includeTests: true
})

Available Templates

| Template | Description | Complexity | Use Cases | |----------|-------------|------------|-----------| | basic | Simple plugin structure | Beginner | Learning, simple automation | | ui-extension | Custom UI components | Intermediate | Dashboards, tools, panels | | language-support | Language features | Advanced | Syntax highlighting, LSP | | task-provider | Build system integration | Intermediate | Custom tasks, runners | | debug-adapter | Debugging support | Advanced | Language debugging | | theme | Editor themes | Beginner | Visual customization | | command | Command palette | Beginner | Shortcuts, automation |

📖 API Reference

Core APIs

import type { 
  LokusAPI,
  CommandAPI,
  EditorAPI, 
  UIAPI,
  WorkspaceAPI 
} from '@lokus/plugin-sdk'

// Access APIs through plugin context
export default class MyPlugin extends BasePlugin {
  async activate(context: PluginContext) {
    const api = context.api
    
    // Commands
    await api.commands.execute('workbench.action.quickOpen')
    
    // Editor
    const content = await api.editor.getContent()
    await api.editor.insertContent('Hello World!')
    
    // UI
    await api.ui.showNotification('Plugin loaded!', 'success')
    const result = await api.ui.showDialog({
      title: 'Confirm Action',
      message: 'Do you want to continue?',
      buttons: [
        { id: 'yes', label: 'Yes', primary: true },
        { id: 'no', label: 'No' }
      ]
    })
    
    // Workspace  
    const folders = await api.workspace.getFolders()
    const files = await api.workspace.findFiles('**/*.js')
  }
}

Base Plugin Features

export default class MyPlugin extends BasePlugin {
  async activate(context: PluginContext) {
    await this.initialize(context)
    
    // Automatic cleanup with disposables
    this.registerCommand('my.command', this.handleCommand.bind(this))
    
    // Configuration management
    this.watchConfig('mySettings.enabled', (newValue, oldValue) => {
      this.getLogger().info(`Setting changed: ${oldValue} -> ${newValue}`)
    })
    
    // Safe error handling
    await this.safeExecute(async () => {
      await this.performRiskyOperation()
    }, 'Failed to perform operation')
    
    // Performance measurement
    await this.measurePerformance(async () => {
      await this.heavyOperation()
    }, 'Heavy operation')
  }
  
  private async handleCommand() {
    if (!this.validateState()) return
    
    const config = this.getConfig()
    const enabled = config.get('enabled', true)
    
    if (enabled) {
      this.showNotification('Command executed!', 'success')
    }
  }
}

🧪 Testing Your Plugin

Unit Testing

import { createMockContext, MockLokusAPI } from '@lokus/plugin-sdk/testing'
import MyPlugin from '../src/MyPlugin'

describe('MyPlugin', () => {
  let plugin: MyPlugin
  let mockContext: PluginContext
  
  beforeEach(() => {
    mockContext = createMockContext('my-plugin', {
      id: 'my-plugin',
      name: 'My Plugin',
      version: '1.0.0'
    })
    plugin = new MyPlugin()
  })
  
  test('should activate successfully', async () => {
    await plugin.activate(mockContext)
    expect(plugin.isActive()).toBe(true)
  })
  
  test('should register commands', async () => {
    await plugin.activate(mockContext)
    
    const mockAPI = mockContext.api as MockLokusAPI
    const commands = await mockAPI.commands.getAll()
    
    expect(commands).toHaveLength(1)
    expect(commands[0].id).toBe('my-plugin.hello')
  })
})

Integration Testing

import { TestUtils } from '@lokus/plugin-sdk/testing'

describe('MyPlugin Integration', () => {
  test('should work with real API', async () => {
    const result = await TestUtils.withRealAPI(async (api) => {
      await api.commands.execute('my-plugin.hello')
      return api.ui.getNotifications()
    })
    
    expect(result).toContainEqual(
      expect.objectContaining({
        message: expect.stringContaining('Hello')
      })
    )
  })
})

🔧 Development Workflow

Development Mode

import { DevMode } from '@lokus/plugin-sdk'

export default class MyPlugin extends BasePlugin {
  async activate(context: PluginContext) {
    await this.initialize(context)
    
    if (DevMode.isEnabled()) {
      // Enable hot reload
      DevMode.enableHotReload(this)
      
      // Enhanced development logging
      const devLogger = DevMode.createLogger(this.pluginId)
      devLogger?.devLog('Plugin loaded in development mode')
    }
  }
}

Configuration

Create a plugin.json manifest:

{
  "id": "my-awesome-plugin",
  "version": "1.0.0",
  "name": "My Awesome Plugin",
  "description": "Does awesome things",
  "author": "Your Name",
  "main": "dist/index.js",
  "activationEvents": ["*"],
  "permissions": [
    "commands:register",
    "ui:notifications",
    "editor:read",
    "editor:write"
  ],
  "contributes": {
    "commands": [
      {
        "command": "my-awesome-plugin.hello",
        "title": "Say Hello",
        "category": "My Awesome Plugin"
      }
    ]
  }
}

Build Configuration

TypeScript (tsconfig.json)

{
  "extends": "@lokus/plugin-sdk/tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Package Scripts

{
  "scripts": {
    "build": "tsc",
    "build:watch": "tsc --watch",
    "dev": "lokus-plugin-dev --watch",
    "test": "jest",
    "test:watch": "jest --watch",
    "package": "lokus-plugin-package",
    "publish": "lokus-plugin-publish"
  }
}

🎯 Best Practices

1. Use TypeScript

  • Leverage full type safety
  • Better IntelliSense and error detection
  • Easier refactoring and maintenance

2. Extend Base Classes

// ✅ Good
export default class MyPlugin extends BasePlugin {
  // Automatic resource management and utilities
}

// ❌ Avoid
export default class MyPlugin implements Plugin {
  // Manual resource management required
}

3. Handle Errors Gracefully

// ✅ Good
await this.safeExecute(async () => {
  await riskyOperation()
}, 'Operation failed')

// ❌ Avoid
try {
  await riskyOperation()
} catch (error) {
  console.error(error) // Poor error handling
}

4. Use Configuration

// ✅ Good
const enabled = this.getConfig().get('feature.enabled', true)

// ❌ Avoid
const enabled = true // Hardcoded values

5. Test Thoroughly

// ✅ Good - Use mocks for fast unit tests
const mockContext = createMockContext('my-plugin', manifest)

// ✅ Good - Integration tests for critical paths
await TestUtils.withRealAPI(async (api) => {
  // Test real integration
})

📚 Examples

Basic Command Plugin

import { BasePlugin, PluginContext } from '@lokus/plugin-sdk'

export default class HelloWorldPlugin extends BasePlugin {
  async activate(context: PluginContext) {
    await this.initialize(context)
    
    this.registerCommand('hello-world.greet', async () => {
      const name = await this.getAPI().ui.showInputBox({
        placeholder: 'Enter your name',
        prompt: 'What is your name?'
      })
      
      if (name) {
        this.showNotification(`Hello, ${name}! 👋`, 'success')
      }
    }, {
      title: 'Say Hello',
      category: 'Hello World'
    })
  }
}

UI Extension Plugin

import { EnhancedBasePlugin, PluginContext } from '@lokus/plugin-sdk'

export default class DashboardPlugin extends EnhancedBasePlugin {
  async activate(context: PluginContext) {
    await this.initialize(context)
    
    // Create status bar item
    this.createStatusBarItem('dashboard.status', '$(dashboard) Dashboard', {
      tooltip: 'Open Dashboard',
      command: 'dashboard.open'
    })
    
    // Register command with palette
    this.registerCommandWithPalette({
      id: 'dashboard.open',
      title: 'Open Dashboard',
      category: 'Dashboard',
      handler: () => this.openDashboard()
    })
  }
  
  private async openDashboard() {
    const panel = this.getAPI().ui.registerWebviewPanel({
      id: 'dashboard.panel',
      title: 'Dashboard',
      type: 'webview',
      location: 'editor',
      html: this.getDashboardHTML(),
      options: { enableScripts: true }
    })
    
    panel.webview.onDidReceiveMessage((message) => {
      this.handleDashboardMessage(message)
    })
  }
  
  private getDashboardHTML(): string {
    return `
      <!DOCTYPE html>
      <html>
        <head>
          <title>Dashboard</title>
          <style>
            body { font-family: system-ui; padding: 20px; }
            .card { border: 1px solid #ddd; padding: 16px; margin: 8px 0; border-radius: 4px; }
          </style>
        </head>
        <body>
          <h1>My Dashboard</h1>
          <div class="card">
            <h3>Project Stats</h3>
            <p>Files: <span id="fileCount">Loading...</span></p>
            <button onclick="refreshStats()">Refresh</button>
          </div>
          
          <script>
            const vscode = acquireVsCodeApi()
            
            function refreshStats() {
              vscode.postMessage({ command: 'refresh-stats' })
            }
            
            window.addEventListener('message', event => {
              const message = event.data
              if (message.command === 'update-stats') {
                document.getElementById('fileCount').textContent = message.fileCount
              }
            })
            
            refreshStats() // Initial load
          </script>
        </body>
      </html>
    `
  }
  
  private async handleDashboardMessage(message: any) {
    if (message.command === 'refresh-stats') {
      const files = await this.getAPI().workspace.findFiles('**/*')
      this.sendToDashboard('update-stats', { fileCount: files.length })
    }
  }
}

🔍 Debugging

Debug Configuration

{
  "type": "node",
  "request": "launch",
  "name": "Debug Plugin",
  "program": "${workspaceFolder}/node_modules/@lokus/plugin-sdk/bin/debug.js",
  "args": ["--plugin", "${workspaceFolder}"],
  "env": {
    "NODE_ENV": "development",
    "LOKUS_DEV": "true"
  }
}

Logging

// Different log levels
this.getLogger().trace('Detailed debug information')
this.getLogger().debug('Debug information')
this.getLogger().info('General information')
this.getLogger().warn('Warning message')
this.getLogger().error('Error occurred', error)

// Performance logging
const perfLogger = new PerformanceLogger(this.getLogger())
await perfLogger.measure('operation-name', async () => {
  await performOperation()
})

🚀 Publishing

Package Your Plugin

npm run package
# Creates my-plugin-1.0.0.vsix

Publish to Marketplace

npm run publish
# Publishes to Lokus Plugin Marketplace

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

📄 License

MIT © Lokus Team

🔗 Links


Happy Plugin Development! 🎉