🤩

VSCode 拡張機能の翻訳ができる「extension-translate-jp」を公開しました!!

に公開

VSCode拡張機能の開発から公開まで完全ガイド

はじめに

この記事では、VSCode拡張機能を一から開発し、Visual Studio Marketplaceに公開するまでの全手順を解説します。実際に私が開発したextension-translate-jpという拡張機能を例に、具体的なコードとともに説明していきます。

extension-translate-jpは、VSCodeの拡張機能の概要を自動で日本語に翻訳してくれる便利な拡張機能です。


目次

  1. 開発環境のセットアップ
  2. プロジェクトの初期化
  3. 拡張機能の開発
  4. テストとデバッグ
  5. パッケージング
  6. Marketplaceへの公開
  7. 継続的な更新とメンテナンス

1. 開発環境のセットアップ

必要なツール

# Node.jsのインストール(推奨:LTS版)
# https://nodejs.org/

# Gitのインストール
# https://git-scm.com/

# Visual Studio Codeのインストール
# https://code.visualstudio.com/

必要なnpmパッケージ

# Yeomanとジェネレータのインストール
npm install -g yo generator-code

# vsce(Visual Studio Code Extensions)のインストール
npm install -g @vscode/vsce

2. プロジェクトの初期化

Yeomanで雛形を生成

yo code

対話形式で以下の項目を選択・入力します:

? What type of extension do you want to create? New Extension (TypeScript)
? What's the name of your extension? translate-description
? What's the identifier of your extension? translate-description
? What's the description of your extension? Translate selected text
? Initialize a git repository? Yes
? Which bundler to use? webpack
? Which package manager to use? npm

プロジェクト構造

生成されたプロジェクトの主要なファイル:

translate-description/
├── src/
│   ├── extension.ts          # メインロジック
│   └── test/                  # テストコード
├── package.json               # 拡張機能の設定
├── tsconfig.json              # TypeScript設定
├── webpack.config.js          # webpack設定
└── README.md                  # ドキュメント

3. 拡張機能の開発

package.jsonの設定

package.jsonは拡張機能の心臓部です。重要な項目を設定します:

{
  "name": "translate-description",
  "displayName": "Translate Description",
  "description": "選択したテキストを翻訳する拡張機能",
  "version": "0.0.1",
  "publisher": "sanpi-cule",
  "engines": {
    "vscode": "^1.80.0"
  },
  "categories": [
    "Other"
  ],
  "keywords": [
    "translate",
    "translation",
    "i18n"
  ],
  "activationEvents": [],
  "main": "./dist/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "translate-description.translate",
        "title": "Translate Selected Text"
      }
    ],
    "keybindings": [
      {
        "command": "translate-description.translate",
        "key": "ctrl+shift+t",
        "mac": "cmd+shift+t",
        "when": "editorTextFocus"
      }
    ],
    "configuration": {
      "title": "Translate Description",
      "properties": {
        "translate-description.targetLanguage": {
          "type": "string",
          "default": "ja",
          "description": "翻訳先の言語コード"
        }
      }
    }
  },
  "scripts": {
    "vscode:prepublish": "npm run package",
    "compile": "webpack",
    "watch": "webpack --watch",
    "package": "webpack --mode production --devtool hidden-source-map"
  },
  "devDependencies": {
    "@types/node": "^18.0.0",
    "@types/vscode": "^1.80.0",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "eslint": "^8.0.0",
    "ts-loader": "^9.4.0",
    "typescript": "^5.0.0",
    "webpack": "^5.88.0",
    "webpack-cli": "^5.1.0"
  }
}

extension.tsの実装

メインロジックを実装します:

import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
    console.log('translate-description is now active');

    // コマンドの登録
    let disposable = vscode.commands.registerCommand(
        'translate-description.translate',
        async () => {
            const editor = vscode.window.activeTextEditor;
            
            if (!editor) {
                vscode.window.showErrorMessage('エディタが開かれていません');
                return;
            }

            const selection = editor.selection;
            const text = editor.document.getText(selection);

            if (!text) {
                vscode.window.showWarningMessage('テキストが選択されていません');
                return;
            }

            // 設定から翻訳先言語を取得
            const config = vscode.workspace.getConfiguration('translate-description');
            const targetLang = config.get<string>('targetLanguage', 'ja');

            try {
                // 翻訳処理を実行
                const translatedText = await translateText(text, targetLang);
                
                // 翻訳結果を表示
                vscode.window.showInformationMessage(
                    `翻訳結果: ${translatedText}`
                );

                // クリップボードにコピー
                await vscode.env.clipboard.writeText(translatedText);
                vscode.window.showInformationMessage(
                    'クリップボードにコピーしました'
                );

            } catch (error) {
                vscode.window.showErrorMessage(
                    `翻訳エラー: ${error instanceof Error ? error.message : '不明なエラー'}`
                );
            }
        }
    );

    context.subscriptions.push(disposable);
}

// 翻訳関数(実際にはAPIを使用)
async function translateText(text: string, targetLang: string): Promise<string> {
    // ここに翻訳APIの実装
    // 例:Google Translate API、DeepL API等
    
    // ダミー実装
    return `[${targetLang}] ${text}`;
}

export function deactivate() {}

より実践的な実装例

翻訳APIを使った実装例:

import * as vscode from 'vscode';
import * as https from 'https';

async function translateText(text: string, targetLang: string): Promise<string> {
    // Google Translate APIを使用する例
    const apiKey = vscode.workspace
        .getConfiguration('translate-description')
        .get<string>('apiKey');

    if (!apiKey) {
        throw new Error('APIキーが設定されていません');
    }

    return new Promise((resolve, reject) => {
        const params = new URLSearchParams({
            q: text,
            target: targetLang,
            key: apiKey
        });

        const options = {
            hostname: 'translation.googleapis.com',
            path: `/language/translate/v2?${params}`,
            method: 'POST'
        };

        const req = https.request(options, (res) => {
            let data = '';

            res.on('data', (chunk) => {
                data += chunk;
            });

            res.on('end', () => {
                try {
                    const json = JSON.parse(data);
                    const translated = json.data.translations[0].translatedText;
                    resolve(translated);
                } catch (error) {
                    reject(error);
                }
            });
        });

        req.on('error', reject);
        req.end();
    });
}

4. テストとデバッグ

デバッグ実行

  1. F5キーを押して拡張機能開発ホストを起動
  2. 新しいVSCodeウィンドウが開きます
  3. コマンドパレット(Ctrl+Shift+P)からTranslate Selected Textを実行してテスト

ユニットテストの作成

src/test/suite/extension.test.tsにテストを追加:

import * as assert from 'assert';
import * as vscode from 'vscode';

suite('Extension Test Suite', () => {
    vscode.window.showInformationMessage('Start all tests.');

    test('Extension should be present', () => {
        assert.ok(vscode.extensions.getExtension('sanpi-cule.translate-description'));
    });

    test('Command should be registered', async () => {
        const commands = await vscode.commands.getCommands();
        assert.ok(commands.includes('translate-description.translate'));
    });
});

テストの実行:

npm run test

5. パッケージング

READMEの作成

README.mdに詳細な説明を記載:

# Translate Description

選択したテキストを簡単に翻訳できるVSCode拡張機能です。

## 機能

- 選択したテキストをワンクリックで翻訳
- キーボードショートカット対応(Ctrl+Shift+T)
- 翻訳結果を自動的にクリップボードにコピー
- 翻訳先言語のカスタマイズが可能

## 使い方

1. 翻訳したいテキストを選択
2. コマンドパレット(Ctrl+Shift+P)から「Translate Selected Text」を実行
3. または、キーボードショートカット(Ctrl+Shift+T)を使用

## 設定

- `translate-description.targetLanguage`: 翻訳先の言語コード(デフォルト: "ja")
- `translate-description.apiKey`: 翻訳APIキー

## ライセンス

MIT License

LICENSEファイルの作成

MIT Licenseを使用する場合:

MIT License

Copyright (c) 2024 [Your Name]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

アイコンの追加

128x128pxのPNG画像を用意し、icon.pngとして保存。package.jsonに追加:

{
  "icon": "icon.png"
}

VSIXファイルの生成

# パッケージのビルド
npm run vscode:prepublish

# VSIXファイルの生成
vsce package

成功するとtranslate-description-0.0.1.vsixファイルが生成されます。


6. Marketplaceへの公開

Azure DevOpsの設定

  1. Azure DevOpsにアクセス
  2. Microsoft アカウントでサインイン
  3. 新しい組織を作成(既にある場合はスキップ)

Personal Access Token(PAT)の作成

  1. Azure DevOpsの右上のユーザーアイコンをクリック
  2. 「Personal access tokens」を選択
  3. 「New Token」をクリック
  4. 以下の設定で作成:
    • Name: vscode-marketplace
    • Organization: All accessible organizations
    • Expiration: Custom defined(適切な期間を設定)
    • Scopes: MarketplaceManageにチェック
  5. 「Create」をクリックし、表示されたトークンを安全に保存

Publisherの作成

vsce create-publisher <publisher-name>

対話形式で以下を入力:

Personal Access Token: [先ほど作成したPATを貼り付け]
Publisher human-friendly name: [表示名]
Publisher email: [メールアドレス]

または、Marketplace Publisher Managementから直接作成することもできます。

ログイン

vsce login <publisher-name>

公開

vsce publish

初回公開時は自動的にバージョン番号がインクリメントされます。

特定のバージョンで公開する場合:

vsce publish 1.0.0

マイナーアップデート:

vsce publish minor  # 0.0.1 → 0.1.0

パッチアップデート:

vsce publish patch  # 0.1.0 → 0.1.1

メジャーアップデート:

vsce publish major  # 0.1.1 → 1.0.0

7. 継続的な更新とメンテナンス

GitHubでのバージョン管理

# 初期化(まだの場合)
git init
git add .
git commit -m "Initial commit"

# GitHubリポジトリの作成後
git remote add origin https://github.com/yourusername/translate-vscode-extensions.git
git push -u origin main

更新の流れ

  1. 機能追加・バグ修正

    git checkout -b feature/new-feature
    # コードの変更
    git commit -m "Add new feature"
    
  2. テスト

    npm run test
    # デバッグ実行で動作確認
    
  3. バージョン更新

    // package.jsonのversionを更新
    "version": "0.1.0"
    
  4. CHANGELOG.mdの更新

    ## [0.1.0] - 2024-01-15
    ### Added
    - 新機能の追加
    
    ### Fixed
    - バグの修正
    
  5. 公開

    git merge feature/new-feature
    git tag v0.1.0
    git push origin main --tags
    vsce publish
    

GitHub Actionsでの自動公開

.github/workflows/publish.ymlを作成:

name: Publish Extension

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run vscode:prepublish
      
      - name: Publish to Marketplace
        run: npx vsce publish -p ${{ secrets.VSCE_PAT }}

GitHubのSettings → Secrets and variablesにVSCE_PATを追加。


トラブルシューティング

よくある問題と解決策

1. vsceコマンドが見つからない

npm install -g @vscode/vsce

2. 公開時の認証エラー

  • PATの有効期限を確認
  • Scopeに「Marketplace (Manage)」が含まれているか確認

3. package.jsonのエラー

  • publisherフィールドが正しく設定されているか確認
  • repositoryフィールドが正しいURLになっているか確認

4. 拡張機能が動作しない

  • activationEventsが適切に設定されているか確認
  • コマンドIDがpackage.jsonextension.tsで一致しているか確認

まとめ

この記事では、VSCode拡張機能の開発から公開までの全工程を解説しました。主なポイント:

  1. 開発環境: Node.js、npm、vsce、Yeomanを使用
  2. 開発: TypeScriptでロジックを実装、package.jsonで設定
  3. テスト: F5キーでデバッグ、ユニットテストで品質保証
  4. パッケージング: READMEとLICENSEを整備、VSIXファイルを生成
  5. 公開: Azure DevOpsでPATを取得、vsceコマンドで公開
  6. 運用: GitHubでバージョン管理、GitHub Actionsで自動化

私が開発したtranslate-descriptionも、この手順で公開しました。ぜひ試してみてください!

最後に(OSS活動をやりませんか?)

また最後に、こちらの拡張機能は、OSSとして公開しており、Contributorとして開発を手伝ってくださる方を募集しています。今後の機能拡張や、UI/UX改善に取り組んでくださるかたお集まりください!!

参考リンク


この記事が、VSCode拡張機能開発の一助となれば幸いです。質問やフィードバックがあれば、GitHubのIssuesでお待ちしています!

Discussion