Claude Media
Claude CodeでShopifyのアプリブロックを作る — theme app extensionの検証

Claude CodeでShopifyのアプリブロックを作る — theme app extensionの検証

Claude CodeでShopifyのtheme app extensionを作り、blocks/*.liquidのschemaとassetsの置き方を整え、shopify app buildで直す手順を権限と公開の分け方まで示します。

Claude CodeでShopifyのアプリブロックを作る — theme app extensionの検証

Shopifyアプリの機能をストアのテーマに差し込むには、theme app extensionを使います。Claude Codeに任せるなら、書く場所は extensions/<名前>/blocks/*.liquid と assets/ に絞り、検証は shopify app build に寄せるのが要です。このコマンドはTheme Checkを拡張機能に対して走らせます。

テーマのファイルを直接書き換える方式とは、触る場所も守る基準も違います。テーマ直接編集の流れはClaude CodeでShopifyテーマ開発を進める手順に書きました。ここでは、アプリ側の拡張機能として作るときのschema、assets、権限、公開の分け方を順に扱います。

theme app extensionは何を差し込む仕組みか

theme app extensionは、アプリブロック、アプリ埋め込みブロック、assets、snippetsの束です。マーチャントはテーマのLiquidやコードに触れずに、レビュー表示や価格、評価のような動的な要素をテーマへ足せます。対象はOnline Store 2.0のテーマです。

テーマコードを書き換えないので、テーマを壊すリスクが下がり、アプリの更新を全ストアへ同時に配れます。テーマエディターにも自動で現れるため、位置の調整や設定の画面をアプリ側で作り直す必要がありません。

ブロックには2種類あります。違いは「どこに出すか」と「初期状態」です。

くらべる

アプリブロックとアプリ埋め込みブロック

target: content

アプリブロック

ページ内の好きなセクションに差し込む型です。星評価やレビュー一覧のように、位置をマーチャントが動かしたい要素に向きます。インストール直後のテーマには入っておらず、エディターのAppsから追加します。

target: head / compliance_head / body

アプリ埋め込みブロック

</head> や </body> の前に出力される型です。チャットバブル、タグ、分析のように画面を持たない、または浮かぶ要素に向きます。インストール後は無効で、Theme SettingsのApp embedsで有効にします。

ScriptTag に頼らない理由もここにあります。アプリ埋め込みブロックは、スクリプトを特定のページだけで読み込めます。ScriptTag ではそれができないと説明されています。必要なページだけに限れば、アプリがストアの表示速度に与える影響も抑えられます。

ファイル構成とschemaの決まり

shopify app generate extension でTheme app extensionを選ぶと、次の構成が生成されます。Claudeに触らせる範囲は、この下に限ります。

extensions/
  my-theme-app-extension/
    assets/                  # CSS・JS・画像
    blocks/                  # アプリブロック・埋め込みブロックのLiquid
    snippets/                # ブロックから共有するLiquid部品
    locales/                 # 翻訳JSON
    package.json
    shopify.extension.toml   # name と type = "theme"

shopify.extension.toml は最小で済みます。ブロックの挙動はconfigではなく、各Liquidファイルの末尾にある {% schema %} が決めます。

name = "my-theme-app-extension"
type = "theme"

公式の例を基に、商品ページ用のアプリブロックを書くと次の形になります。例示としての形で、app.css と app.js は assets/ に置く前提です。

<div class="my-badge" style="color: {{ block.settings.color }}">
  {{ block.settings.label }}
</div>
 
{% schema %}
{
  "name": "Badge",
  "target": "content",
  "enabled_on": { "templates": ["product"] },
  "stylesheet": "app.css",
  "javascript": "app.js",
  "settings": [
    { "label": "Color", "id": "color", "type": "color", "default": "#000000" },
    { "label": "Label", "id": "label", "type": "text", "default": "New" }
  ]
}
{% endschema %}

schemaで間違えやすい点を、Claudeに渡す前提として表にします。

項目決まり
name決まり必須。25文字未満に収め、アプリ名を含めない
target決まり必須。アプリブロックは content、埋め込みは head / compliance_head / body
stylesheet / javascript決まりassets/ 内のファイルを指す。同じファイルを複数ブロックが指しても1回だけ読み込まれる
enabled_on と disabled_on決まりどちらか一方だけ使える。チェックアウトの各ステップには描画できない
available_if決まり値はapp-dataメタフィールドの参照。boolean型のメタフィールドが前提

target の旧値 section は今も動き、content と同じ挙動です。新しく作るブロックは content を使います。

制限と、できないこと

ここを知らないと、Claudeが「動くはず」のコードを書いてデプロイで落ちます。公式が挙げる強制の上限は次のとおりです。

数字

デプロイで検証される上限

  • 拡張機能の全ファイル

    10 MB

    強制

  • ブロック数

    30

    強制

  • Liquid全体のサイズ

    100 KB

    強制

  • locale ファイル

    15 KB / 本

    強制。100本まで

超えると検証に失敗し、更新されません

schemaから直接参照するCSSは圧縮後100KB、JSは圧縮後10KBが推奨値です。こちらは強制ではありません。

そのほかの制約は次のとおりです。

  • チェックアウトのページ群(連絡先、配送方法、支払い方法、注文状況)には出せません
  • content_for_header、content_for_index、content_for_layout のLiquidオブジェクトは使えません
  • 親セクションのプロパティで使えるのは id だけです
  • JSONにコメントや末尾カンマを書けません。テーマのファイルとは違う点です

テーマ側にも条件があります。アプリブロックが動くには、JSONテンプレートと、@app 型のブロックを受けて描画するセクションが必要です。Dawnの商品メインセクションが実装例として挙げられています。古いVintageテーマでは、アプリブロックでなく埋め込みブロックを選ぶことになります。

CLAUDE.mdに書く内容

CLAUDE.mdは公式の目安で1ファイル200行未満です。拡張機能の作業に必要な範囲に絞った例を示します。

# Shopify theme app extension
 
## 作業範囲
- 編集は extensions/<拡張名>/ の下だけ。テーマのファイルは触らない
- ブロックは blocks/*.liquid、共有部品は snippets/、CSS・JSは assets/
 
## schema
- アプリブロックは "target": "content"。"section" は使わない
- name はアプリ名を含めず25文字未満
- enabled_on と disabled_on は同時に書かない
- stylesheet / javascript は assets/ に実在するファイル名を指す
 
## 検証(編集のたびに実行)
- `shopify app build` が成功するまで直す
- 失敗したら出力のチェック名と行を読み、原因を報告してから直す
 
## 禁止
- `shopify app deploy` は実行しない(公開は人間が行う)
- ScriptTag APIでスクリプトを注入しない

「検証が通るまで直す」を完了条件にする点は、テーマ直接編集のときと同じです。違うのは、検証の入口が shopify theme check でなく shopify app build になること。公式の手順では、このコマンドがTheme Checkを拡張機能に対して実行し、上の制限値を超えていないかも確かめます。

buildを回して直させる反復

最初の1回は、生成直後の拡張機能で shopify app build が通ることを確かめます。ここが基準になります。

shopify app generate extension   # Theme app extension を選ぶ
shopify app build

そのうえでClaudeに変更を頼みます。たとえば「商品ページにだけ出すバッジのブロックを追加して。ラベルと色を設定にして」と、目的だけを書きます。Claudeはblockを追加し、schemaを書き、shopify app build を実行して、出力を読んで直します。

生成された拡張機能のサンプルには前提があります。商品の評価を表示する例なので、メタフィールド定義(Products、namespace demo、key avg_rating、Integer)を作って値を入れておかないと、画面に何も出ません。ビルドが通っても表示が空という状況は、検証コマンドでは見つかりません。表示の確認は、次のプレビューで人間が行います。

切り分け

表示されないときの切り分け

  1. Qアプリブロックをテーマエディターの Apps から追加したか?
    答えがいいえのとき

    アプリブロックは、インストールしただけではテーマに入りません。エディターで追加して保存します。

    答えがはい
  2. Q追加したテンプレートは schema の enabled_on に含まれるか?
    答えがいいえのとき

    enabled_on か disabled_on を見直します。外れたテンプレートでは選択肢に出ません。

    答えがはい
  3. Q埋め込みブロックを App embeds で有効にしたか?
    答えがいいえのとき

    インストール後は無効です。Theme SettingsのApp embedsで有効にします。

    メタフィールドの値や、セクションが @app ブロックに対応しているかを確かめます。

プレビューは shopify app dev で行います。ホットリロードに対応し、Google Chromeでのみ使えます。テーマを指定しなければ、開発ストアにDawnがアップロードされます。--theme で既存のテーマを指定すると、そのテーマの上で確かめられます。

PostToolUse hookでbuildを固定する

CLAUDE.mdの指示は、守られない回があります。毎回確実に走らせたいなら、PostToolUse hookで固定します。終了コード2で終わると、標準エラーがClaudeに返り、直すきっかけになります。

#!/bin/bash
# .claude/hooks/app-build.sh
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
 
case "$FILE" in
  */extensions/*) ;;
  *) exit 0 ;;
esac
 
OUT=$(shopify app build 2>&1)
if [ $? -ne 0 ]; then
  echo "$OUT" >&2
  exit 2
fi
exit 0
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/app-build.sh"
          }
        ]
      }
    ]
  }
}

extensions/ 配下を編集したときだけ走るので、アプリ本体のコードを直すたびにビルドが走ることはありません。ビルドが重いと感じたら、編集ごとでなく作業の終わりに1回走らせる運用に切り替えます。

権限 — buildは許可、デプロイは確認を挟む

shopify app build はローカルの検証なので、許可に入れて承認の手間を省けます。一方で、deploy は扱いが違います。

{
  "permissions": {
    "allow": [
      "Bash(shopify app build *)"
    ],
    "ask": [
      "Bash(shopify app deploy *)",
      "Bash(shopify app release *)",
      "Bash(shopify app dev *)"
    ]
  }
}

shopify app deploy はアプリバージョンを作って、既定ではそのまま公開します。アプリバージョンは、アプリの設定とすべての拡張機能のスナップショットです。公開すると、いま配信されているバージョンを置き換えます。アプリをインストール済みのストアに行き渡るまで、数分かかることがあります。

公開を人間の判断に残したいときは、--no-release を付けてバージョンだけを作る手があります。作ったバージョンは shopify app release かDev Dashboardから公開できます。CLAUDE.mdには「deploy は --no-release を付ける」と書き、権限は ask に置く形が、事故を避けやすい構成です。

公開後に残る作業

コードが通っても、マーチャントが使える状態とは限りません。

  • 導入の案内: アプリブロックは自動で入らないため、追加の手順をアプリ内で示す必要があります。ディープリンクを使えば、テーマエディターを開いたところでブロックを足せます。UXガイドラインも、導入手順の用意を求めています
  • ディープリンク: アプリブロックは addAppBlockId={api_key}/{handle}、埋め込みブロックは activateAppId={api_key}/{handle} で指定します。handle はブロックのLiquidのファイル名(拡張子なし)です
  • ブロックの検出: アプリ側で、追加済みかどうかを app.extensions() で確かめられます

ディープリンクのURLは打ち間違いやすく、Claudeに書かせたあとは実機で一度開くのが確実です。テンプレートやセクションが見つからないとマーチャントにエラーが表示されます。

まとめ

theme app extensionをClaude Codeに任せるときの要点は3つです。編集範囲を extensions/ の下に限ること、shopify app build が通ることを完了条件にしてhookで固定すること、deploy を ask に置いて公開は人間が決めることです。ビルドが通っても、アプリブロックの追加やメタフィールドの値といった表示側の条件は検証されないため、プレビューで画面を確かめる工程は残ります。

Shopifyでの割引や配送のロジックを書く場合は、Shopify Functionsの検証手順が参考になります。Admin APIを叩く処理はGraphQLレート制限の守り方にまとめています。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. shopify.dev/docs/apps/build/online-store/theme-app-extensions
  2. shopify.dev/docs/apps/build/online-store/theme-app-extensions/configur…
  3. shopify.dev/docs/apps/build/online-store/theme-app-extensions/build
  4. shopify.dev/docs/apps/build/online-store/theme-app-extensions/ux
  5. code.claude.com/docs/en/hooks-guide
  6. code.claude.com/docs/en/memory
  7. code.claude.com/docs/en/permissions
この記事を共有:XはてブLinkedIn