プラグインのアーキテクチャ
プラグインは、Obsidian 向けの小さなパッケージです。プラグインの組み立ては src/main.ts だけで行い、Riebeckite のモノレポとは別にビルドします。workspace: による Riebeckite への依存はありません。
ファイルの責務
src/main.tsは唯一の組み立て起点です。Obsidian SDK を読み込み、設定を読み込んで設定タブとコマンドを登録し、無効化時にリソースを破棄します。src/settings/settings.tsは保存する設定のモデルと検証を担当します。Obsidian は読み込みません。src/settings/settings-tab.tsは設定画面を担当し、保存はプラグインへ委譲します。src/lifecycle/resource-registry.tsは手動で登録したリソースを、登録と逆順に1回だけ破棄します。scripts/はビルド、マニフェストとリリースファイルの検証、バージョン同期を担当します。test/は設定、破棄、リリースファイルを対象にした Node のテストです。
Obsidian SDK を読み込むのは組み立て起点と設定タブだけです。設定の検証は Obsidian に依存しないため、Obsidian なしでテストできます。
読み込みの流れ
Diagram source
sequenceDiagram
participant O as Obsidian
participant P as RiebeckitePlugin
participant S as parseSettings
O->>P: onload()
P->>O: loadData()
O-->>P: 保存済みデータ
P->>S: parseSettings(data)
S-->>P: 設定または既定値
P->>O: addSettingTab()
P->>O: addCommand(open-settings)
O->>P: onunload()
P->>P: resources.dispose()
onload の途中で例外が起きると、catch がエラーを記録して通知を表示します。プラグインは読み込まれた状態のままですが、失敗した箇所より後の登録処理は実行されません。
コマンドと設定画面
Riebeckite: Open settings コマンドは、app.setting.open() で Obsidian の設定画面を開き、app.setting.openTabById(this.manifest.id) でプラグインのタブを選択します。設定画面の API は公開型定義に含まれていないため、この呼び出しでは this.app を拡張した型へキャストしています。将来の Obsidian で API が変わった場合は、minAppVersion の引き上げが必要です。
設定画面には、見出し、ローカル開発サーバーとの連携が未接続である旨、Local server URL の入力欄を表示します。入力が変わるたびに updateSettings を呼びます。
設定の検証
parseSettings(value: unknown) は、保存済みデータを信頼しない入力として扱い、次の場合は既定値を返します。
- 値がオブジェクトでない、または
null serverUrlが文字列でない- 文字列が URL として解釈できない
- プロトコルが
http:でもhttps:でもない
有効な URL は URL.toString() で正規化し、末尾のスラッシュを1つ除きます。updateSettings は変更を現在の設定へマージし、検証してから saveData で保存します。
検証はエラーではなく既定値を返すため、updateSettings に無効な値が届くと、保存されるのは既定値です。読み込み時に無効なデータが見つかった場合、メモリ上の設定は既定値になり、ファイルは次に保存するまで書き換えません。
リソースの破棄
addSettingTab と addCommand で登録したリソースは Obsidian が管理します。ResourceRegistry は、Obsidian が管理しないリソース(タイマー、ソケット、購読など)のために用意しています。
onunload が dispose を呼ぶと、一覧を空にして、登録と逆順に破棄します。一覧が空になるため、2回呼んでも安全です。現在の実装では登録されるリソースがないため、dispose は実行時に何もしません。
ビルドとリリース
esbuild は src/main.ts を CommonJS の main.js へまとめます。ターゲットは ES2021 です。obsidian、Electron、CodeMirror、Lezer、Node の組み込みモジュールは、実行時に Obsidian が提供するため外部のままにします。開発ビルドはインラインのソースマップを付け、入口を監視します。本番ビルドは圧縮し、ソースマップを出力しません。
pnpm check:manifest は manifest.json、package.json、versions.json のバージョンが揃っていることを検証します。pnpm build:release はビルド、マニフェスト検証、リリース対象ファイルの検証を順に実行します。v<version> タグを push すると、リリースワークフローがタグを検証し、main.js と manifest.json を GitHub Release へ添付します。
テスト
pnpm test は test/*.test.ts を Node のテストランナーで実行します。
| テスト | 検証する契約 |
|---|---|
settings.test.ts |
無効なデータは既定値になり、有効な URL は正規化される |
resource-registry.test.ts |
破棄は逆順に1回だけ実行される |
release-files.test.ts |
マニフェストのバージョンが揃い、リリースファイルを受け付ける |
テストは、プラグインのリポジトリにある Obsidian での手動確認を代替しません。Obsidian 上で、設定タブが開くことと URL が保存されることを確認します。プラグインを再度有効化したときに、コマンドが1つだけ登録されることも確認します。
接続機能を追加するときの境界
保存した URL は現時点では設定値にすぎません。接続機能を追加するときは、最小限のアダプターから始め、次の点を守ります。
- レスポンスを検証する。
- 接続状態を設定画面に表示する。
- 生成したリソースを
ResourceRegistryへ登録し、onunloadで破棄する。
サーバー呼び出しが存在しない機能を、利用可能であるかのように表示してはいけません。