askill
typescript-vscode-extension

typescript-vscode-extensionSafety 88Repository

VS Code extension development patterns in TypeScript. Use when creating extensions, registering commands, implementing providers, building webviews, or testing VS Code extensions.

0 stars
1.2k downloads
Updated 2/7/2026

Package Files

Loading files...
SKILL.md

TypeScript for VS Code Extensions

Production-oriented patterns and constraints for VS Code extensions written in TypeScript.

When to Use This Skill

  • Creating a VS Code extension or scaffolding extension code.
  • Registering commands or menus in package.json.
  • Implementing providers (TreeDataProvider, CompletionItemProvider, etc.).
  • Building webviews and messaging between extension and webview.
  • Implementing configuration, settings, or workspace behaviors.
  • Writing extension tests with @vscode/test-cli or @vscode/test-electron.

Critical Rules (MANDATORY)

  1. Manage all disposables via context.subscriptions.
  2. Use async APIs; avoid sync file I/O in activation and handlers.
  3. Webviews MUST enforce CSP and use a nonce for scripts.
  4. Never hardcode file system paths. Use Uri.joinPath() and context.extensionUri.
  5. Keep activation fast. Defer heavy work and use async activation.
  6. Use strict typing and explicit return types on public APIs.

Extension Lifecycle Pattern

Activation

  • Minimal synchronous work.
  • Register commands and providers immediately.
  • Defer heavy initialization.

GOOD:

export async function activate(context: vscode.ExtensionContext): Promise { await initializeIfNeeded();

const cmd = vscode.commands.registerCommand('ext.hello', () => {
    vscode.window.showInformationMessage('Hello');
});

context.subscriptions.push(cmd);

}

BAD:

export function activate(context: vscode.ExtensionContext): void { const data = fs.readFileSync('large.json', 'utf8'); heavyParse(data); }

Deactivation

  • Only cleanup work.
  • Return a promise if async cleanup is required.

export function deactivate(): void | Thenable { return undefined; }

Disposable Management

All resources that implement Disposable must be pushed to context.subscriptions.

GOOD:

context.subscriptions.push( vscode.commands.registerCommand('ext.run', handler), vscode.workspace.onDidChangeConfiguration(onConfigChange) );

BAD:

vscode.workspace.onDidChangeConfiguration(onConfigChange); // leaked

Custom Disposables

class MyResource implements vscode.Disposable { private disposed = false;

dispose(): void {
    if (this.disposed) return;
    this.disposed = true;
    // cleanup
}

}

Command Registration Patterns

package.json contribution

"contributes": { "commands": [ { "command": "ext.hello", "title": "Hello", "category": "My Extension" } ] }

TypeScript registration

const cmd = vscode.commands.registerCommand('ext.hello', () => { vscode.window.showInformationMessage('Hello'); }); context.subscriptions.push(cmd);

Text Editor Command

const fmt = vscode.commands.registerTextEditorCommand( 'ext.format', (editor, edit) => { edit.insert(editor.selection.start, 'Hello'); } ); context.subscriptions.push(fmt);

Event Handling Patterns

  • onDid* fires after an action.
  • onWill* fires before an action and can modify behavior.

vscode.workspace.onWillSaveTextDocument(event => { const edit = vscode.TextEdit.insert(new vscode.Position(0, 0), '// header\n'); event.waitUntil(Promise.resolve([edit])); });

Always dispose event subscriptions via context.subscriptions.

Configuration Pattern

package.json

"contributes": { "configuration": { "title": "My Extension", "properties": { "ext.enableFeature": { "type": "boolean", "default": true, "description": "Enable feature" } } } }

Reading configuration

const config = vscode.workspace.getConfiguration('ext'); const enabled = config.get('enableFeature', true);

vscode.workspace.onDidChangeConfiguration(e => { if (e.affectsConfiguration('ext.enableFeature')) { reload(); } });

TreeDataProvider Pattern

class MyTreeProvider implements vscode.TreeDataProvider { private readonly _onDidChangeTreeData = new vscode.EventEmitter<MyItem | undefined>(); readonly onDidChangeTreeData = this._onDidChangeTreeData.event;

getTreeItem(element: MyItem): vscode.TreeItem {
    const item = new vscode.TreeItem(element.label);
    item.id = element.id;
    item.contextValue = 'myItem';
    return item;
}

getChildren(element?: MyItem): MyItem[] {
    return element ? element.children ?? [] : this.rootItems();
}

refresh(): void {
    this._onDidChangeTreeData.fire(undefined);
}

}

const provider = new MyTreeProvider(); const treeView = vscode.window.createTreeView('myView', { treeDataProvider: provider }); context.subscriptions.push(treeView);

Webview Pattern (Core)

Must include CSP with nonce

function getWebviewHtml(webview: vscode.Webview, extensionUri: vscode.Uri): string { const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'main.js')); const nonce = createNonce();

return `<!DOCTYPE html>

Webview Messaging

panel.webview.onDidReceiveMessage(message => { if (message.command === 'alert') { vscode.window.showInformationMessage(message.text); } }, undefined, context.subscriptions);

Language Feature Providers (Minimal Examples)

Completion Provider

const completionProvider = vscode.languages.registerCompletionItemProvider( 'typescript', { provideCompletionItems(): vscode.CompletionItem[] { const item = new vscode.CompletionItem('hello'); item.detail = 'Example completion'; return [item]; } }, '.' ); context.subscriptions.push(completionProvider);

Diagnostics

const diagnostics = vscode.languages.createDiagnosticCollection('ext'); context.subscriptions.push(diagnostics);

function updateDiagnostics(doc: vscode.TextDocument): void { const range = new vscode.Range(0, 0, 0, 5); const diagnostic = new vscode.Diagnostic(range, 'Example issue', vscode.DiagnosticSeverity.Warning); diagnostics.set(doc.uri, [diagnostic]); }

Async and Cancellation

When performing expensive operations, respect CancellationToken.

async function provideItems(token: vscode.CancellationToken): Promise<MyItem[]> { if (token.isCancellationRequested) return []; const items = await loadItems(); if (token.isCancellationRequested) return []; return items; }

Testing with @vscode/test-cli

.vscode-test.mjs

import { defineConfig } from '@vscode/test-cli';

export default defineConfig({ files: 'out/test/**/*.test.js', version: 'stable', mocha: { ui: 'tdd', timeout: 20000 } });

Basic test

suite('Extension Test Suite', () => { test('Extension activates', async () => { const ext = vscode.extensions.getExtension('publisher.extension-name'); if (!ext) throw new Error('Extension not found'); await ext.activate(); }); });

Project Structure (Typical)

my-extension/

  • src/
    • extension.ts
    • commands/
    • providers/
    • views/
    • test/
  • media/
  • resources/
  • package.json
  • tsconfig.json
  • .vscode-test.mjs

Common Pitfalls

  • Not disposing resources => memory leaks.
  • Missing CSP in webviews => security risks.
  • Slow activation => poor user experience.
  • Hardcoded paths => cross-platform bugs.
  • Using fs.*Sync in activation => UI freeze.

References

  • VS Code API reference
  • VS Code Extension guides

Install

Download ZIP
Requires askill CLI v1.0+

AI Quality Score

83/100Analyzed 2/19/2026

High-quality technical reference skill for VS Code extension development in TypeScript. Comprehensive coverage of lifecycle patterns, disposables, webviews, providers, and testing with clear GOOD/BAD examples. Has explicit "When to Use" section and structured code patterns. Located in .github/skills suggests project-specific origin but content is broadly applicable. Minor deduction for somewhat generic tags.

88
90
72
85
90

Metadata

Licenseunknown
Version-
Updated2/7/2026
PublisherMameMame777

Tags

apisecuritytesting