🌌

Cursor/VSCode拡張機能開発完全ガイド - 第2部:SVG to PNG Converter拡張機能の実装

に公開

1. プロジェクトの概要

こちらからSVG to PNGはインストール可能です👇
https://marketplace.visualstudio.com/items?itemName=fumifumi0831.svg-to-png-converter

1.1 目的と背景

SVGファイルをPNGに変換する拡張機能の開発を通じて、以下の課題を解決します:

1.2 主な機能要件

interface ConverterFeatures {
    // 基本機能
    convertSingleFile: (file: vscode.Uri) => Promise<void>;
    convertMultipleFiles: (files: vscode.Uri[]) => Promise<void>;
    
    // 設定オプション
    quality: number;  // 0.1-1.0
    outputPath: string;
    
    // ユーザーインターフェース
    showProgress: boolean;
    showNotifications: boolean;
}

1.3 技術選定

  • sharpモジュールの採用理由
    • 高性能な画像処理
    • クロスプラットフォーム対応
    • メモリ効率の良さ
    • アクティブなメンテナンス

2. 基本機能の実装

2.1 SVGファイルの検出

// SVGファイルの検出と処理
function isSvgFile(file: vscode.Uri): boolean {
    return file.fsPath.toLowerCase().endsWith('.svg');
}

// コンテキストメニューの表示条件
{
    "when": "resourceExtname == .svg",
    "command": "svg-to-png-converter.convertToPng",
    "group": "navigation"
}

2.2 コンテキストメニューの追加

// コンテキストメニューの登録
context.subscriptions.push(
    vscode.commands.registerCommand('svg-to-png-converter.convertToPng', async (uri: vscode.Uri) => {
        // 変換処理の実行
    })
);

2.3 変換処理の実装

async function convertSvgToPng(inputPath: string, outputPath: string, quality: number) {
    try {
        await sharp(inputPath)
            .png({ quality: Math.round(quality * 100) })
            .toFile(outputPath);
        return true;
    } catch (error) {
        vscode.window.showErrorMessage(`変換エラー: ${error.message}`);
        return false;
    }
}

2.4 設定オプションの実装

{
    "configuration": {
        "title": "SVG to PNG Converter",
        "properties": {
            "svgToPngConverter.quality": {
                "type": "number",
                "default": 1,
                "description": "Quality of the PNG output (0.1 to 1.0)"
            },
            "svgToPngConverter.defaultOutputPath": {
                "type": "string",
                "default": "",
                "description": "Default output path for converted PNG files"
            }
        }
    }
}

3. ユーザーインターフェース

3.1 コマンドパレットの統合

// コマンドの登録
context.subscriptions.push(
    vscode.commands.registerCommand('svg-to-png-converter.convertToPng', async () => {
        const editor = vscode.window.activeTextEditor;
        if (editor) {
            // アクティブなエディタのファイルを変換
        }
    })
);

3.2 プログレス表示

async function convertWithProgress(files: vscode.Uri[]) {
    return vscode.window.withProgress({
        location: vscode.ProgressLocation.Notification,
        title: "SVG to PNG 変換中",
        cancellable: true
    }, async (progress, token) => {
        for (let i = 0; i < files.length; i++) {
            if (token.isCancellationRequested) {
                break;
            }
            progress.report({
                message: `ファイル ${i + 1}/${files.length} を変換中...`,
                increment: (100 / files.length)
            });
            // 変換処理
        }
    });
}

3.3 エラーハンドリング

try {
    // 変換処理
} catch (error) {
    if (error instanceof Error) {
        vscode.window.showErrorMessage(`変換エラー: ${error.message}`);
    } else {
        vscode.window.showErrorMessage('予期せぬエラーが発生しました');
    }
    console.error('変換エラー:', error);
}

3.4 設定画面の実装

// 設定の取得
const config = vscode.workspace.getConfiguration('svgToPngConverter');
const quality = config.get<number>('quality', 1);
const outputPath = config.get<string>('defaultOutputPath', '');

4. パフォーマンスと最適化

4.1 非同期処理の実装

// 並列処理による最適化
async function convertMultipleFiles(files: vscode.Uri[]) {
    const promises = files.map(file => convertSvgToPng(file.fsPath, getOutputPath(file)));
    await Promise.all(promises);
}

4.2 メモリ使用量の最適化

// ストリーミング処理
async function convertWithStreaming(inputPath: string, outputPath: string) {
    const readStream = fs.createReadStream(inputPath);
    const writeStream = fs.createWriteStream(outputPath);
    
    await new Promise((resolve, reject) => {
        readStream
            .pipe(sharp().png())
            .pipe(writeStream)
            .on('finish', resolve)
            .on('error', reject);
    });
}

4.3 バッチ処理の実装

// バッチ処理の実装
async function batchConvert(files: vscode.Uri[]) {
    const batchSize = 5; // 同時処理数
    for (let i = 0; i < files.length; i += batchSize) {
        const batch = files.slice(i, i + batchSize);
        await Promise.all(batch.map(file => convertSvgToPng(file.fsPath, getOutputPath(file))));
    }
}

5. 実装のポイントと注意点

5.1 エラーハンドリングの重要性

// 包括的なエラーハンドリング
async function safeConvert(file: vscode.Uri) {
    try {
        // 入力ファイルの検証
        if (!await fs.promises.access(file.fsPath)) {
            throw new Error('ファイルが見つかりません');
        }
        
        // 出力パスの検証
        const outputPath = getOutputPath(file);
        await ensureOutputDirectory(outputPath);
        
        // 変換処理
        await convertSvgToPng(file.fsPath, outputPath);
        
        return true;
    } catch (error) {
        handleError(error);
        return false;
    }
}

5.2 ユーザー体験の最適化

// プログレス表示とキャンセル機能
async function convertWithProgressAndCancel(files: vscode.Uri[]) {
    return vscode.window.withProgress({
        location: vscode.ProgressLocation.Notification,
        title: "SVG to PNG 変換中",
        cancellable: true
    }, async (progress, token) => {
        for (const file of files) {
            if (token.isCancellationRequested) {
                vscode.window.showInformationMessage('変換をキャンセルしました');
                break;
            }
            // 変換処理
        }
    });
}

この第2部では、実際のSVG to PNG Converter拡張機能の実装について、コード例を含めて詳しく説明しました。次の第3部では、トラブルシューティングと応用について解説していきます。

Discussion