Penpotの深淵へ:TypeScriptで構築する「最強の自動化プラグイン」開発の極意
多くのデザイナーやエンジニアが「PenpotをFigmaの代替品」と捉えているなら、それはあまりにも勿体ない。Penpotの真髄は、そのオープンソースとしての透明性と、DOMに限りなく近い柔軟なAPI設計にある。
本稿では、汎用的なプラグイン開発の手引きではなく、「開発効率を極限まで引き上げ、デザインパイプラインをコードで支配する」ための、上級者向けアーキテクチャ設計を解説する。
—
1. 建築学的なアプローチ:Plugin Architectureの解剖
Penpotのプラグインは、サンドボックス化された `iframe` 内で動作するWebアプリケーションだ。しかし、ここで陥りやすい罠がある。「ただのUIモジュールとして作る」ことだ。
真のプロフェッショナルは、プラグインを「デザインシステムとコードベースを繋ぐブリッジ」として定義する。
必須のツールスタック
- TypeScript (Strict Mode): 型定義はPenpotのAPIと密に同期させる。
- Vite: Webpackの重厚長大な設定は捨てろ。HMR(Hot Module Replacement)の速度こそが開発体験(DX)を左右する。
- Tailwind CSS: デザインシステムをプラグインUIにそのまま流し込むための必須レイヤー。
—
2. 環境構築:ボイラープレートを捨て、自動化パイプラインを組む
`npx` で生成される雛形はあくまで「教育用」だ。現場では、`package.json` にCI/CD連携用のスクリプトを直書きし、デプロイまでを自動化する。
// package.json の scripts セクション
“scripts”: {
“dev”: “vite”,
“build”: “tsc && vite build”,
“deploy:local”: “cp -r dist/ ~/.config/penpot/plugins/my-custom-plugin”,
“ci:validate”: “eslint src/ –fix && tsc –noEmit”
}
ハック: ローカル開発時は、Penpotのプラグイン読み込みディレクトリへ直接シンボリックリンクを貼るスクリプトを組め。これにより、ビルドした瞬間にPenpot上で即時反映される「ホットリロード環境」が完成する。
—
3. API操作の真髄:DOM操作の最適化
PenpotのAPIを叩く際、最も避けるべきは「ループ内でのAPI呼び出し」だ。APIは非同期であり、高頻度なコールはブラウザのイベントループをブロックし、UIのフリーズを招く。
最適化されたバッチ処理のパターン
// 非推奨: for文内で個別に update を呼ぶ
// 推奨: 変更対象をキャッシュし、最後に一括適用する
async function updateNodesBatch(nodes: any[]) {
// メモリ消費を抑えるため、プロパティをバッチで取得
const nodeData = await Promise.all(nodes.map(n => n.getProperties()));
// 計算処理を分離し、最後に一括でDOM反映を行う
const updates = nodeData.map(data => ({
…data,
x: data.x + 10 // 例: グリッド調整
}));
await penpot.ui.handleUpdates(updates);
}
—
4. 内部アーキテクチャの極み:状態管理とメモリ管理
プラグインが複雑化すると、Penpotのメインプロセスとプラグイン間のメッセージパッシング(`penpot.ui.sendMessage`)がボトルネックになる。
- 状態の正規化: UI側の状態と、Penpotのオブジェクトモデル(`penpot.selection`)を同期させるためのステートマシンを導入せよ。`Zustand` を推奨する。軽量で、Reactの再レンダリングを制御しやすい。
- メモリリークの排除: 観測不能なイベントリスナーは必ず `cleanup` 関数で破棄する。特に `penpot.on(‘selectionchange’, …)` は、プラグインのライフサイクルと必ず同期させること。
—
5. 現場で震える「隠れたハック」
APIの限界を突破するCLI連携
PenpotはWebベースだが、ローカルのNode.jsプロセスと通信することで、デザインデータとバックエンドのDBを直接同期できる。
1. WebSocketサーバー: ローカルに小さなWebSocketサーバーを立てる。
2. プラグインからの送信: プラグインUIから `fetch` でローカルサーバーへJSONを投げる。
3. 自動コード生成: ローカル側で受け取ったJSONを元に、デザインシステムのトークンを `TypeScript` の型定義ファイルとして書き出す。
結果: デザイナーがPenpotで色を変えた瞬間、エンジニアのIDEで自動的に型定義が更新される。これが「デザイナーとエンジニアのシームレスな同期」の正体だ。
—
結びに:君が作るべきもの
Penpotのプラグイン開発は、単なる機能追加ではない。それは「デザインの自動化」という名の、組織のワークフローを再設計する行為である。
メモリ効率を考え、型を厳格に管理し、CI/CDを回す。この泥臭いエンジニアリングの積み重ねこそが、洗練されたプロダクトを生む。
さあ、エディタを開け。君の組織が必要としている「真の自動化」は、君のコードの中にしかない。