アイコンライブラリのリゾルバーが非同期に対応
registerIconLibrary のリゾルバー関数が Promise を返せるようになり、外部APIへの問い合わせや非同期処理を経てアイコンのURLを解決できるようになりました。
背景
これまで IconLibraryResolver の型は同期関数 () => string に限定されており、URLを即座に返す実装しか許容されていませんでした。#2144 で議論されたように、アイコンURLの解決に非同期処理が必要なユースケース(例:トークン検証を経てCDNの署名付きURLを取得するケースなど)には対応できていませんでした。この制約が今回の変更で解消されています。
技術的な変更
変更の起点は library.ts における型定義の拡張です。リゾルバーの戻り値型を string から string | Promise<string> に広げることで、同期・非同期どちらの実装も受け入れられるようになりました。
変更前:
export type IconLibraryResolver = (name: string, family: string, variant: string, autoWidth: boolean) => string;
変更後:
export type IconLibraryResolver = (
name: string,
family: string,
variant: string,
autoWidth: boolean,
) => string | Promise<string>;
型の変更に合わせて、icon.ts の getIconSource() メソッドが非同期化されました。同期呼び出しだった library.resolver(...) を await で待機するように変更し、メソッドシグネチャも async getIconSource(): Promise<IconSource> へと更新されています。リゾルバーが例外を投げた場合は try/catch で捕捉し、url を undefined として処理を継続する設計になっています。
変更前:
private getIconSource(): IconSource {
// ...
if (this.name && library) {
return {
url: library.resolver(this.name, family, this.variant, this.autoWidth),
fromLibrary: true,
};
}
// ...
}
変更後:
private async getIconSource(): Promise<IconSource> {
// ...
if (this.name && library) {
let url: string | undefined;
try {
url = await library.resolver(this.name, family, this.variant, this.autoWidth);
} catch {
url = undefined;
}
return { url, fromLibrary: true };
}
// ...
}
もう一点、setIcon() 内の競合状態対策にも変更が入っています。アイコンのフェッチ完了後に現在のURLと比較してリクエストを破棄する処理がありますが、ここでも getIconSource() の呼び出しが await を伴うように修正されています。
変更前:
if (url !== this.getIconSource().url) {
return;
}
変更後:
const sourceAfterFetch = await this.getIconSource();
if (url !== sourceAfterFetch.url) {
return;
}
テストでは nextFrame() を挟んだ非同期リゾルバーを registerIconLibrary で登録し、wa-load イベントの発火と svg 要素のレンダリングを検証するケースが追加されています。
設計判断
後方互換性を維持したまま非同期対応を実現する ことが優先されています。string | Promise<string> というUnion型を採用することで、既存の同期リゾルバーはそのまま動作します。await はPromiseでない値にも適用できるため、呼び出し側のコードを統一したまま両方のケースを透過的に扱えます。
リゾルバーが例外を投げた場合に url = undefined として処理を継続する設計も注目点です。エラーを上位に伝播させるのではなく、URLなしの状態として既存のエラーハンドリングフロー(!url のチェック)に乗せることで、障害時の挙動を既存コードと統一しています。
まとめ
今回の変更は型定義の拡張を起点に、実装・テストまで一貫して非同期対応を組み込んだ小さくも実用的な拡張です。既存の同期リゾルバーへの影響を最小化しつつ、より柔軟なアイコン解決戦略の実装を可能にしています。