Salesforce Developers Japan Blog

Lightning Web コンポーネントにおけるモジュール解決

(この投稿は René Winkelmeyer(Salesforce.com) による『Lightning Web Components Module Resolution』の翻訳です)

 

コンポーネントベースの UI フレームワークを採用する利点の一つとして、再利用性が挙げられます。これは Web Components をベースとした UI フレームワークである Lightning Web コンポーネント (LWC)にも当てはまります。LWC オープンソース向けに新たにリリースされたモジュール解決の機能は、ローカルモジュールの解決だけでなく、サードパーティモジュールとして配布する場合やそれらを利用する際に有用です。このブログ記事では、LWC オープンソースのプロジェクトにおけるモジュール解決の方法をご紹介します。

 

モジュール解決とは

モジュール解決とは、プロジェクト内のコードで記述された様々なインポートを処理するプロセスのことです。以下に 2 種類のインポートを使った例を提示します。

// Component 1 - app.html
<template>
<my-child></my-child>
</template>

// Component 2 - apexChild.js
import { LightningElement } from 'lwc';
import someFunMethod from '@salesforce/apex/MyClass.someFunMethod';

export default class ApexChild extends LightningElement {

上のマークアップと JavaScript はそれぞれに異なるインポートを使っていることがわかります。まず、HTMLファイルはマークアップを介して my-child コンポーネントを参照しています。これも一種のインポートです。次に、JavaScript ファイルでは Apex メソッドをインポートしています (なぜ LWC オープンソースで Apex クラスを呼び出せるのかは後述します)。後者の方が皆さんがイメージするインポートに近いかもしれませんね。

 

ビルド時にコンパイラがこれらのインポートを正常に処理するためは、ウェブコンポーネントと Apex メソッドの実体がプロジェクト内のどこに保管されているのかを把握しなければなりません。このプロセスをモジュール解決と呼びます。

 

オープンソースとプラットフォーム

これから説明する LWC のモジュール解決の仕組みと方法は、LWC オープンソースでのみ適用できるものです。Salesforce DX プロジェクトで LWC を開発する場合は、Salesforce のプラットフォームが提供するモジュール解決のメカニズムが適用されます。

 

Salesforce DX プロジェクトで開発した LWC のコードは、一旦 Lightning プラットフォームにデプロイされた後、組織内のメタデータと合わせてコンパイルされるため、その時までモジュールの解決も行われません。一方、LWC オープンソースプロジェクトではコンパイルがローカル環境で事前に行われるため、一般的にはサーバーへのアップロード前にモジュールも解決されます。

 

次に、モジュール解決に関する設定と設定方法を見ていきます。

 

モジュールのソース

ESLintPrettier のような他のツールでも設定情報の保管先の指定が必要なのと同様に、LWC の module resolver がモジュールを探しにいく場所を事前に設定しておく必要があります。そのためには、package.json の中に新しい lwc キーを作成するか、プロジェクトディレクトリのルートに lwc.config.json ファイルを作成します。後者は新しい create-lwc-app プロジェクトでのデフォルトとなっています。

 

LWC module resolver では、以下の 3 種類の ModuleRecords を指定できます。

  • Directories
  • Aliases
  • npm

 

これらの ModuleRecords は以下のように指定します。

{
    "modules": [
        {
            "dir": "src/modules"
        },
        {
            "name": "@salesforce/apex/MyClass.someFunMethod",
            "path": "src/hacky/apex/myClassSomeFunMethod/index.js"
        },
        {
            "npm": "lwc-recipes-oss-ui-components"
        }
    ]
}

dir ModuleRecord エントリは、module resolver に、現在のプロジェクトルートからの相対的なローカルフォルダである src/modules 内のモジュールを探すように指定してします。module resolver は、指定されたディレクトリ内の LWC モジュールを再帰的に探索し、ファイル名を解決します。ローカルディレクトの指定は常に一つ以上必要であることに注意してください。また、これは create-lwc-app を使って新しい LWC オープンソースプロジェクトを作成する際のデフォルトの設定でもあります。

 

2 番目のエントリは、ModuleRecord 型のエイリアスを表しています。通常 Salesforce プラットフォーム外では Apex を直接呼び出すことはできません。しかし、エイリアス型を利用する事で適切に Apex クラス名を指定する事ができます。エイリアス型は、特定のモジュール名に対し別名を定義でき、指定されたモジュール名に対して解決すべきコンポーネントパスを明示的に定義することになります。このため、module resolver によって、プラットフォーム外で Apex クラスをエイリアスでインポートすることができるようになります。

 

npm ModuleRecord はこれまでとは異なった方法で指定します。ここではディレクトリパスを指定するのではなく、プロジェクトに追加した npm パッケージ(devDependency)の名前を指定しています 。これにより、サードパーティの LWC の npm パッケージを利用したコンポーネントを配布することができます。この ModuelRecord 型は、最初に説明した利点である、再利用可能なコンポーネントに繋がります。

 

上記の lwc-recipes-oss-ui-components の例に基づいて、このような配布可能なパッケージを作成するために create-lwc-app プロジェクト内で何を設定しなければならないかを見てみましょう。

 

コンポーネントを配布するための準備

独自の Lightning Web Component パッケージを配布するには、2つのステップを完了する必要があります。

 

まず、プロジェクトからどのコンポーネントを公開して、どのモジュールを利用するかを明示的に定義する必要があります。lwc.config.json ファイルの expose キーで定義します。

{
    "modules": [
        {
            "dir": "src/modules"
        }
    ],
    "expose": [
        "ui/button",
        "ui/card",
        "ui/input",
        "ui/navfooter",
        "ui/output",
        "ui/select"
    ]
}

なぜ、わざわざ公開するコンポーネントを定義しないといけないのでしょうか。それは、あなたがパッケージ内のモジュールを将来的に変更する可能性があり、利用者に直接呼び出してほしくない事があるからです。直接的な呼び出しを許可するコンポーネントを明示的に定義しておくことで、利用者には利用してもいいものだけを開示します。このように、パッケージの API 規約を定義するのは重要なステップです。

 

次のステップでは、実際の src フォルダ (またはソースフォルダのへのエイリアス) を npm パッケージに追加します。実際のソースコードのコンパイルは利用者のローカルマシン上で行われるため、互換性の無い LWC バージョンでコンポーネントをコンパイルするとトラブルの原因となるので注意が必要です。

 

テスト

責任ある開発者として、私たちは自分のコンポーネントをテストしなければいけません。自分で作成したコンポーネントをテストする場合は、必要なソースは全て手元にあるため、実際にコンポーネントを動作させながらテストが可能です。しかし、npm パッケージとして提供されるコンポーネントを使う場合は、予めテスト用にスタブを作成しておく必要があります。

 

そのためには、ローカルプロジェクト内でスタブを作成しなければなりません。lwc-recipes-oss-ui-components パッケージのスタブを作成した例が、LWC Recipes OSS サンプルアプリにあります。スタブを作成した後は、ローカルプロジェクトで Jest を設定します。これにより、テスト中で利用するサードパーティの npm パッケージのモジュール名をローカルのスタブにマッピングすることができます。エンジニアリングチームでは、これをよりスムーズにするためのオプションを継続的に検討しています。

 

sfdx-lwc-jest リポジトリでは、Salesforce DX プロジェクト内でJest テストを実行する際に基本 Lightning コンポーネントをどのようにスタブ化しているのか確認できます。

 

まとめ

Lightning Web Components オープンソースの新しいモジュール解決の機能は、開発者に高い柔軟性を提供します。より柔軟なプロジェクト構成が可能になるだけでなく、独自の LWC npm ライブラリをビルドして配布することも可能になります。モジュール解決の実装については、RFCs: Module Resolution で詳しく説明されています。

 

ES6 モジュールの内部パッケージのインポートを解決するための組み込みメカニズムが搭載されているため、node 14 の利用が本格化するにつれて、この実装の一部は時代遅れになるかもしれないことに注意してください。

 

より実用的な例が、こちらのリポジトリ(エイリアスされた Apex モジュールを含む)や LWC Recipes OSS で公開されています。LWC モジュールの解決方法の詳細については、私の CodeLive セッションも合わせてご確認ください。

 

著者について

René Winkelmeyer は、Salesforce のアーキテクト兼デベロッパーエバンジェリストです。彼は、エンタープライズ・インテグレーション、Lightning の他、Salesforce プラットフォームでできるクールなことを中心に活動しています。

Twitter: @muenzpraeger

コメント

Lightning Web コンポーネントにおけるモジュール解決