アイコンライブラリのリゾルバーが非同期に対応

shoelace-style/webawesome

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.tsgetIconSource() メソッドが非同期化されました。同期呼び出しだった library.resolver(...)await で待機するように変更し、メソッドシグネチャも async getIconSource(): Promise<IconSource> へと更新されています。リゾルバーが例外を投げた場合は try/catch で捕捉し、urlundefined として処理を継続する設計になっています。

変更前:

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 のチェック)に乗せることで、障害時の挙動を既存コードと統一しています。

まとめ

今回の変更は型定義の拡張を起点に、実装・テストまで一貫して非同期対応を組み込んだ小さくも実用的な拡張です。既存の同期リゾルバーへの影響を最小化しつつ、より柔軟なアイコン解決戦略の実装を可能にしています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
de7a2409

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)、背景、技術的な変更、設計判断、まとめ(結論)という「総論→各論→結論」の構成が明確で、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト、GitHubのIssue/PRへのリンク記法ともに正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Promiseやasync/awaitなどの知識を前提としており、専門知識を持つエンジニアという対象読者に適した内容です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が徹底されています。非常に読みやすいです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコードは、提供されたDiffの内容と完全に一致しており、正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「リゾルバー」「Union型」「後方互換性」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

型定義の変更が起点となり、関連するメソッドが非同期化されたという一連の変更の流れが、技術的に正確かつ論理的に説明されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのタイトル、説明、Diff内のコード、または参照されているIssueの内容によって裏付けられており、ハルシネーションは見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2152)とIssue番号(#2144)が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「アイコンライブラリのリゾルバーが非同期に対応」は、PRの主題「Async resolver for register icon library」を的確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

PR情報に含まれないバージョンサポート状況やリリース日程などの外部知識の記載はなく、提供された情報源に忠実です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「これまで〜だった」「今回の変更で〜」といった時間表現が、PRの内容と矛盾なく正確に使用されています。