コンテンツにスキップ

テーマとプラグインを連携する

テーマとプラグインは互いを import しません。プラグインが capability を公開し、render payload が有効な capability をテーマへ伝え、テーマは ctx.hasCapability() で確認します。

ステップ 1: 公開 contract を決める

Section titled “ステップ 1: 公開 contract を決める”

特定の実装名ではなく機能を表す、安定した namespace 付き capability を選びます。

適切な例:

  • reviews.summary
  • commerce.products
  • search.facets

acme-plugin-v2-installed のように、特定のインストールやバージョンに結び付いた名前は避けてください。

次に、公開データをどこから受け取るかを決めます。

  • 保存済みページドキュメントの一部であるデータには block props を使用します。
  • リクエストごとに動的に生成する必要があるデータには、platform render payload のフィールドを使用します。この方法には Z-CMS core の contract 変更が必要です。
  • テーマからプラグインや CMS の private endpoint を呼び出さないでください。

ステップ 2: プラグインから capability を公開する

Section titled “ステップ 2: プラグインから capability を公開する”

プラグインの plugin.json に capability を追加します。

{
"id": "com.acme.plugin.reviews",
"capabilities": ["reviews.summary"]
}

サイトにインストールされ、かつ有効なプラグインだけが render payload に capability を追加します。プラグインを無効化すると、その capability も削除されます。

ステップ 3: テーマで任意の capability として宣言する

Section titled “ステップ 3: テーマで任意の capability として宣言する”

theme.json に capability を追加します。

{
"id": "com.acme.theme.storefront",
"optionalCapabilities": ["reviews.summary"]
}

これは任意の機能です。プラグインが未インストール、無効、または別のプラグインに置き換えられた場合でも、テーマは表示できなければなりません。

ステップ 4: テンプレートで機能を検出する

Section titled “ステップ 4: テンプレートで機能を検出する”

ctx.hasCapability() を使用し、必ずフォールバック表示を用意します。

function ProductPage({ ctx, content }: PageTemplateProps<Settings>) {
const reviewsEnabled = ctx.hasCapability("reviews.summary");
return (
<article>
<h1>{content.title}</h1>
{ctx.renderBlocks(content.blocks)}
{!reviewsEnabled ? <p>レビュー機能は現在利用できません。</p> : null}
</article>
);
}

capability の検出で分かるのは「provider が有効かどうか」だけです。レビューなどのプラグインレコードは返されません。

ステップ 5: プラグイン関連ブロックを表示する

Section titled “ステップ 5: プラグイン関連ブロックを表示する”

ページドキュメントに namespace 付きブロックがあり、公開データが props に保存されている場合は、テーマにその表示 component を登録します。

content.blocks に保存されるブロックの例:

{
"id": "review-summary-1",
"type": "acme/review-summary",
"props": {
"average": 4.8,
"count": 124
}
}

テーマの component:

interface ReviewSummaryProps {
average?: unknown;
count?: unknown;
}
function ReviewSummary({
ctx,
props,
}: BlockProps<ReviewSummaryProps, Settings>) {
if (!ctx.hasCapability("reviews.summary")) return null;
const average = typeof props.average === "number" ? props.average : 0;
const count = typeof props.count === "number" ? props.count : 0;
return (
<section aria-label="レビュー">
<strong>{average.toFixed(1)} / 5</strong>
<span>{count} 件のレビュー</span>
</section>
);
}
const blocks = {
"core/hero": Hero,
"core/richtext": RichText,
"acme/review-summary": ReviewSummary,
};

プラグイン関連ブロックの props も core block と同様に検証してください。JSON 値なので、欠けている場合や古いプラグインバージョンの形式である場合があります。

ステップ 6: プラグインがない場合や変更された場合に対応する

Section titled “ステップ 6: プラグインがない場合や変更された場合に対応する”

次の状態をすべてテストしてください。

  1. プラグインがインストールされていない。
  2. プラグインはインストール済みだが無効である。
  3. プラグインが有効で、想定したブロックデータがある。
  4. capability はあるが、block props が空、または古い schema である。
  5. ページにブロックが残っているが、プラグインが capability を提供しなくなった。

どの状態でもページの主要機能を利用できるようにしてください。ナビゲーション、ページタイトル、主要コンテンツを任意の capability だけに依存させないでください。

Theme SDK には、plugin と間接的に連携するための共通 contract がすでに あります。ctx.hasCapability() は機能を検出し、ctx.getIntegration() は core が allow-list した公開 projection を読み取り、ctx.renderSlot() は runtime が所有する対話 UI の配置場所を決めます。theme が plugin を import したり、plugin の credential を受け取ったりすることはありません。

現在の contract でも、theme は次のことを行えません。

  • 任意の plugin handler を直接呼び出すこと(ctx.callPlugin()ctx.callCapability() はありません)。
  • プラグイン設定や plugin storage を読み取る。
  • プラグイン所有の database table を query する。
  • ctx.hasCapability() から任意のプラグインデータを取得する。
  • package だけで新しい動的 payload 形式を登録する。

リクエスト時のデータや action が必要な場合、core が対応する public projection または integration action を定義し、allow-list する必要があります。 たとえば現在の ai.assistantgetIntegration() で表示用データを公開し、 renderSlot("floating") で chat UI を配置し、gateway では chat action だけを 許可します。これは theme が任意の plugin を呼び出せる汎用 proxy では ありません。theme に private API の使用を案内することは、今もサポート外で 安全でない theme を作ることになります。

テンプレートのデータフロー全体については、ページとブログ記事を表示するを参照してください。