テンプレートの拡張性

テンプレートの拡張性は、PWA Kit v3 に導入された機能です。この機能の目的は、テンプレートをカスタマイズすることで、PWA Kit プロジェクトをより簡単に構築できるようにすることです。Retail React App などの既存のベーステンプレートをカスタマイズすることも、独自のテンプレートを作成することも可能です。PWA Kit コミュニティのメンバーがベーステンプレートを相互に共有することをお勧めします。

テンプレートの拡張性により、テンプレート内のすべてのファイルを複製することなく、選択したテンプレートを変更できます。

テンプレートの拡張性はオプションの機能です。ただし、2023 年 6 月 15 日以降に生成された新しいプロジェクトでは、テンプレートの拡張性を使用するように自動的に構成されます。

PWA Kit v3 では、プロジェクトにベーステンプレートと overrides ディレクトリを含めることができ、どちらもプロジェクトの package.json ファイルで構成できます。テンプレートの拡張性を使用するには、package.jsonccExtensibility.extends でベーステンプレートを宣言し、ccExtensibility.overridesDir で overrides ディレクトリを宣言する必要があります。(構成の詳細については、次のセクションで説明します。)

ベーステンプレートは、別の PWA Kit プロジェクトがそのファイルの一部を上書きできるように事前構成された、完全に機能する PWA Kit プロジェクトを含む npm モジュールです。これは、「拡張性アプリ」または「拡張可能アプリ」と呼ばれることもあります。"

overrides ディレクトリは、ベーステンプレート内の対応するファイルを上書きするファイルを保存できる、プロジェクト内のディレクトリです。

すでに上書きディレクトリを定義し、Retail React App をベーステンプレートとして定義していると仮定しましょう。Retail React App のホームページコンポーネントを上書きするとします。Retail React App では、ホームページコンポーネントのコードは @salesforce/retail-react-app パッケージ内の app/pages/home/index.jsx にあります。そのファイルを上書きするには、package.jsonccExtensibility.overridesDir で宣言されたディレクトリに同じファイルを再作成する必要があります。つまり、新しいファイルのパスは <ccExtensibility.overridesDir>/app/pages/home/index.jsx になります。

これで、index.jsx@salesforce/retail-react-app パッケージ内のいずれかのファイルにインポートされるたびに、パッケージ内のファイルではなく、overrides ディレクトリ内のファイルが読み込まれるようになりました。ファイル名のベースが同じであるだけで、上書きが行えます。ファイル拡張子は異なる場合があります。

アプリを構築するにつれて、ベーステンプレートから overrides ディレクトリにファイルを徐々に追加できます。

上書きするファイル数が増えるほど、ベーステンプレートの変更に対応するために必要な作業が増えることになります。

テンプレートの拡張性の操作を実際に確認するには、npx @salesforce/pwa-kit-create-app@latest --outputDir <path/to/new/local/project> を実行して、新しい PWA Kit v3 プロジェクトを生成してください。

生成されたプロジェクトは Retail React App のベーステンプレートを使用しており、https://pwa-kit.mobify-storefront.com/ とほぼ同じ外観のアプリです。

このセクションの手順では、テンプレートの拡張性の機能が有効になっていない状態で生成された既存のプロジェクトに対し、テンプレートの拡張性を有効にすることを前提としています。

ベーステンプレートを定義するには、プロジェクトの package.json ファイルに npm 依存関係として @salesforce/retail-react-app (または別のテンプレート) を追加します。

その後、以下のキーと値を使用して ccExtensibility キーを package.json に追加します。

Retail React App 以外のベーステンプレートを使用している場合は、@salesforce/retail-react-app を他のテンプレートのパッケージ識別子に置き換えることを忘れないでください。

overridesDir の値をカスタムディレクトリ名に置き換えることもできますが、プロジェクト (およびサンプルコード) 間の一貫性を保つために overrides を使用することをお勧めします。

同じ依存関係をメインプロジェクトとベーステンプレートの両方に含めることはできません。メインプロジェクトとベーステンプレートを区別するには、@salesforce/pwa-kit-dev の 2 つの PWA Kit アプリが異なっている必要があります。(メインプロジェクトはベーステンプレートを定義する必要があり、それ自体を継承することはできません。)

プロジェクトには、package.json ファイルにリストされているベーステンプレートの npm 依存関係とバージョン競合があってはなりません。基になるテンプレートで @chakra-ui が使用されている場合、プロジェクトにもそれが依存関係として含まれており、同じバージョンでなければなりません。この予防策により、同じパッケージのバージョンの競合によるバンドルの偶発的な肥大化 (機能の破損は言うまでもなく) を防げます。このルールには例外が 1 つあります。それは、特定の依存関係をインポートするベーステンプレート内のすべてのファイルに上書きを追加した場合です。この場合、上書きを介して依存関係をインポートするすべてのファイルが削除されているため、依存関係のベーステンプレートのバージョンはプロジェクトにインポートされません。

基になるテンプレートで使用されていない追加の npm 依存関係を追加しても問題ありません。 👌

Managed Runtime にデプロイされるすべての PWA Kit プロジェクトには、次のファイルが必要です。

  • <overridesDir>/app/main.jsx
  • <overridesDir>/app/ssr.js
  • <overridesDir>/app/routes.jsx
  • <overridesDir>/app/request-processor.js
  • config/default.js (例外: package.jsonmobify キーを使用してサイト構成データを保存している場合)

プロジェクト内の動作をカスタマイズするには、@salesforce/retail-react-app でこれらのファイルを上書きするのが一般的です。

  • app/pages/home/index.jsx: デフォルトの Salesforce マーケティングコンテンツをホームページから削除することは、ほとんどすべてのプロジェクトで最初のステップとなります。
  • app/static/*: これらのファイルは、サイトのブランドを決定するためにデスクトップおよびモバイルブラウザーで使用されるアイコンを提供するデフォルトです。自社のブランドに合わせて更新してください。
  • app/constants.js
  • app/assets/svg/brand-logo.svg: このファイルはジェネレータープロジェクトで上書きされます。この例に従い、プロジェクト内の他のアイコンを上書きします。

この「API としてのファイルシステム」設定の目的は、基になるファイルを見つけてコピーし、動作とロジックをカスタマイズできるようにすることです。コードを、多くのサブコンポーネントを調整する小さなファイルに分割すると、上書きする動作のみをターゲットにしやすくなります。

そうは言っても、ファイルの動作の一部だけを変更するために、ファイルを overrides ディレクトリにコピーしなければならない場合があります。テンプレートのバージョンを変更しても拡張プロジェクトでコードを手動で更新しなくてもすむように、可能な限り、基になるテンプレートのエクスポートをインポートして再エクスポートすることをお勧めします。

たとえば、生成されたプロジェクトroutes.jsx ファイルでは、デフォルトのルートが import {routes as _routes} from '@salesforce/retail-react-app/app/routes' 経由でインポートされていることに注意しましょう。_routes は、これらのデフォルトのルートが外部依存によって管理されていることを示す規則として意図されています。ファイル @salesforce/retail-react-app/app/routes.jsx と同様、routes.jsx のカスタム実装は、ルートをエクスポートします。つまり、これは / (ホーム) ルートを追加します。React Router は、routes 配列内の指定されたパス名の最初の項目を見つけるため、新しいルートはデフォルトのルートよりも優先されます。

相対的インポートは、テンプレートの拡張性を使用するほとんどのプロジェクトにとって最良のアプローチです。ベーステンプレート内のファイル (@salesforce/retail-react-app など) のインポート動作には、overrides ディレクトリ内のファイルを優先する特別なロジックがあります。このインポートロジックは、overrides ディレクトリ内のファイルには適用されません。したがって、ほとんどの実装では相対的インポートを使用することをお勧めします。

テンプレートの拡張性を使用する際に注意する問題の 1 つに、競合する 2 つの異なるファイルを誤ってインポートしてしまうというものがあります。PWA Kit プロジェクトは @salesforce/pwa-kit-dev によってビルドされ、内部で webpack を使用しています。@salesforce/pwa-kit-dev には、テンプレート拡張性の「上書き」機能を有効にするプラグインがあります。このプラグインは、すべての webpack ファイルのリクエストを検査し、次の 2 つの質問をします。

  1. このファイルのリクエストはベーステンプレートからのものですか?
  2. このファイルは overrides ディレクトリ内に存在しますか?

両方の質問に対する答えが「はい」の場合、ファイルリクエストはビルド時に overrides ディレクトリ内のファイルを指すように書き換えられます。

このロジックは、overrides ディレクトリから発生する webpack ファイルのリクエストには作用しません。したがって、overrides ディレクトリとベーステンプレートの両方から同じファイルをインポートすることが可能です。この状況が、たとえば @saleforce/retail-react-app/my-file<overrides directory>/my-file が同じバンドルにインポートされている場合に発生すると、webpack は、can’t find <export name> from <filename> のような紛らわしいエラーをスローします。webpack がこのエラーをスローするのは、どのインポートがバンドルに含める対象となるのか、バンドルが認識していないためです。

同等のファイルが上書きに存在する場合は、1 つの顕著な例外を除き、ベーステンプレートからファイルをインポートしないようにすることが重要です。特殊ファイルセクションのコード例で前に説明し、constants.js で示したように、基になるテンプレートをインポートし、そのすべてのエクスポートを再エクスポートし、必要な数のエクスポートを上書きまたは追加することをお勧めします。

app/components/_app/index.jsx コンポーネントを上書きする必要がある場合、HeaderFooterDrawerMenu などの多くのグローバルコンポーネントが存在します。_app/index.jsx 上書きが設定された後、ベーステンプレートがこのコンポーネントをインポートしようとすると、ファイルリクエストは <ccExtensibility.overridesDir>/app/components/_app/index.jsx に再ルーティングされます。しかし、ここに落とし穴があります。HeaderFooterapp/components/_app/index.jsx によってインポートされるため、_app/index.jsx でもインポートを更新する必要が出てくるのです。それ以外の場合、_app/index.jsx はベーステンプレートから HeaderFooter をインポートします。

ccExtensibility.extends からの ECMAScript インポートは、@salesforce/pwa-kit-dev がその名前のファイルを複数の場所でチェックし、ccExtensibility.overridesDir が優先されるという点で「マジカル」です。

一方で、<ccExtensibility.overridesDir>/* からのインポートには魔法のような動作はなく、目的のファイルをターゲットにする必要があるため、次のように <ccExtensibility.overridesDir>/app/components/header/index.jsx<ccExtensibility.overridesDir>/app/components/_app/index.jsx に明示的にインポートする必要があります。

このようにパッケージからインポートはしないでください。overrides ディレクトリ内のヘッダーがバイパスされるためです。

routes.jsx は特殊ファイルであることに注意してください。これは、アプリケーション全体の webpack 用語での「entryPoint」を形成します。routes.jsx で何かが正しく設定されていない場合、pwa-kit-dev のルートベースのチャンクが適切に解決されないため、コンパイル段階でアプリ全体が失敗します。このため、デフォルト @salesforce/retail-react-app/app/routes.jsx とローカル routes.jsx からのインポートを overridesDir に混合することで、ルートを適切に拡張する方法の例を含めます。

このインポートは「ツリーの最上部」にあるため、これらのファイルを上書きしてから footer.jsx を上書きする場合は、_app/index.jsx に戻って、相対的テンプレートインポートを指すようにそのインポートを変更する必要があります。

テンプレート拡張システムの既知の制限事項は、retail-react-app/constants を魔法のように取り込むことにより、たとえばベーステンプレート (@salesforce/retail-react-app) がこのファイルをインポートするたびに、同じ値がエクスポートされることが期待されることです。

overrides 内のファイルが、ベーステンプレート内の同等の ECMAScript エクスポートと同じ ECMAScript エクスポートのエクスポートに失敗すると、以下のような予期しないエラーが発生する場合があります。

constants.js ファイルを overrides ディレクトリに次のように追加すると、エラーが発生します。

エラーメッセージの例です。

このエラーの理由は、CAT_MENU_DEFAULT_ROOT_CATEGORY@salesforce/retail-react-app/components/_app/index.jsx で予期されたエクスポートであり、それを上記の唯一のエクスポート CUSTOM_MESSAGE で上書きする過程で、constants.js によってエクスポートされるとき、基礎となる「API コントラクト」(ファイルが指定された値に依存する機能、この場合は CAT_MENU_DEFAULT_ROOT_CATEGORY) が上書きによって破られてしまうためです。

正しいアプローチは次のようになります。このアプローチにより、必須かつ予期されるエクスポートとしての CAT_MENU_DEFAULT_ROOT_CATEGORY の省略が回避され、エクスポートされた API の一貫性が維持されます。

このアプローチは、追加の変更の場合にのみ機能します。次の例では、基になる retail-react-app/constants.js ファイルのエクスポートにない CUSTOM_MESSAGE エクスポートを追加します。

このアプローチは、DEFAULT_LOCALEretail-react-app/constants によってエクスポートされ、その値を変更する次のシナリオにおいて機能します。

テンプレートフックは、Retail React App テンプレートを操作するための新しい方法で、これまではコマンド npx pwa-kit-create-app (現在は npx @salesforce/pwa-kit-create-app に名前変更) を介して利用可能でした。

テンプレート拡張機能の一部として、新しい「テンプレートフック」が @salesforce/retail-react-app に追加されました。これは、次のリストに列挙されているコンポーネントの小さなサブセットのグローバルな追加をサポートします。デフォルトでは、これらの各コンポーネントは null を返し、完成した PWA Kit 実装プロジェクトに不必要な肥大化を避けるために意図的に空になっています。ベース @salesforce/retail-react-app は、これらのコンポーネントに機能を追加することはありません。テンプレートフックは常に null を返します。これは、顧客の実装がこれらの場所でテンプレートに「フック」して、必要以上のファイルを上書きすることなくプロジェクトをカスタマイズできるようにするためです。

@salesforce/retail-react-app@1.0.0 の時点で、次のテンプレートフックが利用可能です。

  • app/components/_app/partials/above-header.jsx
  • app/pages/product-list/partials/above-page-header.jsx

PWA Kit v3 のリリース時点では、公開されている拡張可能なベーステンプレートは @salesforce/retail-react-app のみです (今後さらに多くのテンプレートが追加される予定です)。

ベーステンプレートとして使用するには、PWA Kit プロジェクトを npm に公開し、そのすべての ECMAScript インポートにパッケージ名と一致する値を接頭辞として付ける必要があります。たとえば、@salesforce/retail-react-app は npm で公開されており、そのインポートには @salesforce/retail-react-app という接頭辞が付きます。この接頭辞はパッケージのルートディレクトリへの参照であり、パッケージがベーステンプレートとして適切に動作するために必要です。

PWA Kit プロジェクトを拡張可能なベーステンプレートとして使用するには、すべてのインポートに <npm package name> を使用する必要があります。たとえば、@salesforce/retail-react-apppackage.jsonccExtensibility を追加します。これにより、ローカル IDE 参照 (@salesforce/retail-react-app/app/components/_app など) が適切に解決されます。