Support deep merging chart configs (e.g. with functions)
が関数を含む Chart.js 設定を安全にマージできるようになり、DataCloneError が解消されました。これにより、ユーザーは既存の Chart.js API をそのまま利用しつつ、Web Awesome コンポーネント側での設定処理を簡潔に保てます。
背景
このセクションでは、関数付き Chart.js 設定が引き起こすエラーとその影響を概観します。 Chart.js の tooltip.callbacks.label などのコールバック関数を含む設定オブジェクトを <wa-chart> に渡すと、内部で structuredClone が呼び出され DataCloneError がスローされました。エラーメッセージは “Failed to execute 'structuredClone' on 'Window'” と示され、コンポーネントの描画が中断されます。
再現手順は単純で、関数を含む設定オブジェクトを new Chart(...) に渡すだけです。 当該設定は公式ドキュメントでも例示されており、実務での利用頻度が高いため、エラーは実装上の重大な障壁となっていました。Issue #2444 で報告されたこのバグは、Web Awesome の主要コンポーネントの信頼性に直結していました。
結果として、関数を回避するためにカスタムプラグインを導入するという回避策が提示されましたが、実装負荷が大きく不自然でした。 そこで根本的な解決策として、設定オブジェクトのクローン処理を見直すことが決定されました。
技術的な変更
本セクションでは、clone.ts に導入された deepClone と、コンポーネント側でのマージロジック変更を詳述します。 packages/webawesome/src/utilities/clone.ts に新規追加された deepClone は、プリミティブ・関数・クラスインスタンス・DOM ノードなどを参照渡しし、プレーンオブジェクトと配列のみを再帰的にコピーします。これにより、関数を含む構造体でも structuredClone のように例外を投げません。
export function deepClone<T>(value: T, seen = new WeakMap<object, unknown>()): T {
if (value === null || typeof value !== 'object') {
return value; // primitives and functions are returned as‑is
}
if (seen.has(value as object)) {
return seen.get(value as object) as T; // circular reference guard
}
if (Array.isArray(value)) {
const result: unknown[] = [];
seen.set(value as object, result);
for (const item of value) {
result.push(deepClone(item, seen));
}
return result as T;
}
if (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null) {
const result: Record<string, unknown> = {};
seen.set(value as object, result);
for (const key of Object.keys(value as Record<string, unknown>)) {
result[key] = deepClone((value as Record<string, unknown>)[key], seen);
}
return result as T;
}
return value;
}
deepClone が導入されたことにより、<wa-chart> の内部で設定オブジェクトをマージする際に安全なコピーが取得できるようになりました。 具体的には、applyDefaultConfig で受け取ったユーザー設定とデフォルト設定を deepClone 後に再帰的にマージし、関数はそのまま保持されます。結果として、Chart.js のコールバック関数が正しく動作し、エラーが発生しなくなります。
設計上の選択肢としては、structuredClone の代替として既存のロジックを置き換えるか、外部ライブラリを導入するかが考えられました。 本実装は軽量で依存性を増やさず、Circular reference にも対応している点が評価され、採用が決定されました。
設計判断
このセクションでは、カスタムクローン実装を選んだ背景とトレードオフを整理します。 structuredClone は標準 API で安全性は高いものの、関数や DOM ノードといった非シリアライズ可能な値を受け付けず、Chart.js 設定のようなユースケースでは不適切です。代替案としては、lodash.cloneDeep のような汎用ライブラリを使用する方法がありますが、バンドルサイズ増大や余計なプロトタイプ除去のリスクがあります。
deepClone は「プレーンオブジェクトと配列だけをクローンし、その他は参照で渡す」シンプルな方針を取っています。 これにより、関数やクラスインスタンスが意図せずコピーされて失われることが防げ、かつ実装が数十行に収まります。Circular reference のハンドリングも WeakMap により安全に処理しています。
結果として、Web Awesome のコンポーネントは既存の API 互換性を保ちつつ、関数付き設定をサポートできるようになりました。 今後、同様の構造体を扱う他コンポーネントでも deepClone を再利用でき、コードベース全体の保守性が向上します。
まとめ
本 PR で導入された deepClone により、<wa-chart> が関数を含む Chart.js 設定を安全にマージでき、DataCloneError が解消されました。 変更は内部ユーティリティの追加のみで、外部 API への影響はなく、既存プロジェクトへの導入障壁はほぼゼロです。今後は同様のパターンでカスタム設定を扱うコンポーネントでも同様の利便性が提供されることが期待されます。