mcpField
P
plugin-heavy-dep

plugin-heavy-dep

github.com/oh-my-dsh開発305190 回インストール1 週間前に更新
リポジトリ

plugin-heavy-dep について

軽量な DSH Web プラグインに大きな依存ライブラリを組み込む開発スキルです。遅延読み込み、安全な配信と描画、フォールバック、リリース時の確認を扱います。

主な機能

  1. 01大きな依存ライブラリの遅延読み込み
  2. 02単一 ESM チャンクへのバンドル
  3. 03パス検証付きのプラグインファイル配信
  4. 04生成 SVG のサニタイズ
  5. 05モーダルでのズームとパン
  6. 06フォールバックとルートの安全性テスト

活用シーン

  1. 01オンデマンドの図表レンダラー追加
  2. 02大規模ライブラリを初期バンドルから除外
  3. 03ホストルート経由でプラグインチャンクを配信
  4. 04信頼できないマークアップの描画を保護

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/oh-my-dsh/dsh-plugin-upgrade-skill --skill plugin-heavy-dep

インストール後、Claude Code で/plugin-heavy-depを入力して読み込まれたか確認します。

同梱ファイルREADME.md

詳細

軽量な DSH Web プラグインに重い依存関係を追加する

軽量な Web プラグイン(クライアントバンドルが小さく、ビルドファームも不要)でも、数メガバイトのレンダラーを提供できます。重いコードを必要になるまで読み込まず、読み込めない場合は適切にフォールバックできればよいのです。このスキルは統合時のチェックリストです。以下の各項目は、実際の mermaid 統合から得られた知見です。

1. 遅延読み込みにするか、インライン化するかを決める

依存関係によってクライアントバンドルが数倍に膨らむ一方で、それを必要とする機能がひとつだけ(フェンスレンダラーや、要求に応じて開くエディターなど)なら、クライアントバンドルにインライン化してはいけません。機能が実際にレンダリングされるときにのみクライアントが動的に読み込む、別個のチャンクファイルに分割してください。

2. チャンクを単一ファイルとしてバンドルする

依存関係を、コード分割を無効にした自己完結型の単一 ESM ファイルにバンドルしてください。汎用バンドラーをデフォルト設定のまま使うと、ライブラリ内部の動的インポートが、コンテンツハッシュ付きの名前を持つ兄弟チャンクファイルに分割されます。その場合、ブラウザーはチャンクの URL を基準とした相対インポートとしてそれらを解決するため、すべての兄弟ファイルを配信し、正確な名前を付け、適切な MIME タイプを設定する必要があります。ファイルはひとつ、インポートもひとつにすれば、相対パス解決にまつわる種類のバグを避けられます。

3. 独自の lib に限定したホストルートから配信する

webServer にプレフィックスルートを登録し、プラグイン独自の lib ディレクトリにあるファイルだけを配信してください。

  • lib ディレクトリはホストバンドル自体の場所(import.meta.url)から解決してください。process.cwd() は使わないでください。
  • 拡張子をホワイトリストで制限してください(チャンクは .js/.mjs です。それ以外は配信しないでください)。
  • パスの包含チェックを行い、要求されたパスが lib ディレクトリ内にとどまることを検証してください。苦労して得られた重要なルールがふたつあります。
    • startsWith ではなく path.relative と比較してください。ファイルシステムの正規化結果がベース文字列と異なる場合、プレフィックス比較は正しく機能しません。
    • Windows のドライブ文字は大文字と小文字が変わることがあります。ベースが E:\… を示していても、realpathSync が e:\… を返す場合があります。大文字と小文字を区別した比較では、正しく包含されているパスをパス逸脱と誤判定し、403 を返すことになります。path.relative(および異なるドライブ間を判定する結果への isAbsolute チェック)は堅牢です。
  • JavaScript の MIME タイプ(application/javascript)で配信してください。MIME タイプが正しくないと、ブラウザーは動的インポートを拒否します。

ホストルートが有効になるのは、dsh の再起動後であることを忘れないでください。ハードリフレッシュだけでは、新しいホストコードは登録されません。追加したばかりのルートで 404 が発生した場合、ほとんどの場合は「まだ再起動していない」か「インストール済みのコピーにそのルートが含まれていない」ことが原因です。

4. 遅延インポートし、判定結果をキャッシュし、フォールバックする

  • 機能が初めてレンダリングされるときにチャンクの URL を import() し、成功したモジュールをキャッシュして、フェンスが繰り返しインポートされないようにしてください。
  • 失敗を同じ方法でキャッシュしないでください。一時的な失敗がページの表示中ずっと残ってしまいます。次回は再試行できるようにします。ただし、スロットリングは行ってください(インポートがループ内で失敗すると、それ自体がコンソールを埋め尽くします)。
  • インポートまたはレンダリングで例外が発生した場合は、必ずフォールバック(元のコードブロックなど)をレンダリングしてください。図を読み込めないことで、読み進められなくなることがあってはなりません。フォールバックには状態属性を付け、失敗の理由をコンソールに記録してください。「フォールバックした」だけで理由がなければ、デバッグできません。

5. 信頼できないマークアップ:innerHTML の前にサニタイズする

信頼できないテキスト(Markdown、モデル出力)を受け取るレンダラーが出力したマークアップは、dangerouslySetInnerHTML に渡す前に悪意あるものとして扱う必要があります。

  • レンダラーを厳格モードに設定し、HTML ラベルではなく実際の SVG テキストを出力させます(HTML ラベルは <foreignObject> 内に埋め込まれます。ここは、SVG 内に生の HTML を含められる唯一の経路です)。
  • ライブラリのグローバルなエラー副作用を抑制します(一部のレンダラーは、エラーを返す前に巨大なエラー SVG を document.body に出力します)。
  • そのうえで、出力された SVG を自分で再サニタイズします。依存関係ゼロの許可リスト方式で処理します。XML(image/svg+xml)として解析し、解析に失敗した場合は文字列全体を拒否します。ルート要素として <svg> のみを受け入れ、大文字と小文字を区別せずに foreignObject/script と HTML 系要素を除去し、on*/@* 属性を除去し、すべての href/xlink:href を除去します(静的な図ではリンクに利点がなく、悪意ある href によって GUI の移動が発生する可能性があります)。これは多層防御であり、代わりの防御ではありません。

6. モーダル表示中の操作の所有権

プラグインのパネル上に全画面のズーム/パン用モーダルを表示している間は、Ctrl+ホイールを含むすべてのホイールイベントをモーダルが所有する必要があります(背景のペインですでに Ctrl+ホイール(フォントサイズ変更)がバインドされている場合も同様です)。ペイン側のハンドラーにモーダルの表示状態チェックを追加してください。そうしないと、1回の操作で両方の動作が発生します。モーダルのズーム操作は、カーソル位置を基準にしたホイール操作、ドラッグによるパン、キーボードショートカットに対応し、Esc キーまたはオーバーレイのクリックで閉じられるようにします。

7. リリースする

  • ビルド済みチャンクをパッケージにコミットします(プラグインには lib/ が同梱されます)。公開されるパッケージの内容が限定されるよう、files に指定してください。
  • リリースノートに、再起動が必要なケース(ホストルート)と更新のみで済むケース(クライアント)を記載します。そうしないと、ユーザーに 404 が発生します。
  • 各落とし穴に対する回帰テストを用意します。フォールバック経路(チャンクのインポート失敗 → 元の描画)、サニタイザー(悪意ある経路を除去)、およびホストルートについては、大文字と小文字が異なるもののパス内に収まっているパスが拒否されずに配信されることを確認する包含テストを追加します。

コメント