Salesforce Developers Japan Blog

LWC × WebviewでVSCodeエクステンション開発

オリジナル記事

How to Build a Webview-Powered VS Code Extension with LWC

 

Visual Studio Code ( VSCode ) のエクステンション開発において、ネイティブ機能を使ったインタラクティブな画面開発は、決して簡単なものではありません。しかしWebviewを使えば、話は別です。ローカル環境で動作するWebアプリケーションを、VSCode内でも動作させることができ、さらにIDEとコミュニケーションも可能です。この記事では、オープンソースのLightning Web Component (LWC OSS) を使ってWebアプリケーションを構築し、それを使ってVSCodeエクステンションを開発する方法をご紹介します。さらにLWC開発をスピードアップさせる便利なTipsも紹介します。

 

VSCode エクステンションとは?

VSCodeは拡張性をその主な原則のひとつと位置付けており、VSCodeエクステンションはVSCodeに追加機能やカスタマイズ性をもたらすプラグインのことです。新しいプログラミング言語をサポートしたり、自動化タスクを実行したり、ユーザーが操作可能なUIを構築することもできます。出来ることがとにかく豊富なのです。 Salesforceが公開している人気のエクステンション『Salesforce Extension Pack』はご存知かもしれません。

VSCodeエクステンション開発の選択肢のひとつに、WebviewとWebview APIがあります。Webviewはiframeのように、HTML, JavaScript, CSSで構築されたUIをレンダリングします。VSCodeが提供するAPIを使えば、このVSCodeに埋め込まれたWebview UIと、VSCodeエクステンションのコードの間で双方向にメッセージのやりとりが可能です。

Webviewを用いたVSCodeエクステンションの例として、Alba RibasとKotaro Nishinoが最近開発した『LWCビルダー』というプロジェクトがあります。LWCビルダーを使えば、Salesforce上で利用する Lightning Web Componentの作成が非常に簡単になります。LWCをどのページに配置したいか、どんなプロパティをビルダー上で定義させたいかなどをUI上で定義し、設定ファイルをコード不要で作成できます。毎回LWCの公式ドキュメントにアクセスして定義方法を確認したり、.js-meta.xmlファイルで指定できる値を覚える必要も、CSSやSVGファイルを手動で作成したり、@api デコレーターを公開プロパティごとに定義する必要もありません。更に、このエクステンションはコンポーネントファイルの中身を作成前にプレビューすることができ、最後に“Create” ボタンをクリックするだけでlwcバンドルがプロジェクト内に新規作成されます。

このlwcバンドルを定義・生成するUIは、LWC OSSアプリケーションで構築され、VSCodeのWebviewに埋め込まれている状態です。この『LWCビルダー』は、Github上のVSIXファイルをダウンロードし、VS Codeのコマンドパレットの Extensions: Install from VSIX… からインストールすることで、利用可能です。

以下のビデオで、LWCビルダーで出来ることを少しだけ紹介します。コマンドパレットからエクステンションを起動し、コンポーネントの基本情報と、Lightning Experienceのホームページに配置された時にビルダー上で設定できるプロパティを1つ定義しています。また、Account オブジェクトのレコードページで表示させるようにも定義しています。最後に、作成されるコンポーネントの内容をプレビューで確認し、Createボタンをクリックで新規lwcが作成されています。

 

VSCode エクステンションの作り方

VSCode エクステンションは、JavaScript または TypeScriptで開発できます。プロジェクトの基礎を作成するために、 “Yeoman“ と “VSCode エクステンションジェネレーター“ パッケージを利用します。npm install -g yo generator-code コマンドを実行してインストールします。そして、yo code を実行すると create-lwc-app と似たウィザードが開かれます。ここでエクステンション開発プロジェクトの設定を選び、基礎となるテンプレートを作成します。

例えば、TypeScriptのエクステンションを作ると、以下のようなプロジェクト構造が作成されます。extension.ts がこのアプリケーションのエントリーポイントです。

 

LWC OSS アプリケーションをWebview内に埋め込む

LWCビルダーでは、アプリケーションを2つの役割が異なるアプリケーション(プロジェクト)に分けています。ひとつはVSCodeとは独立したWebview内のUIパーツとなるLWC OSSで構築されたWebアプリケーション。もうひとつは VSCodeエクステンションのネイティブ機能部分のアプリケーションです。
LWC OSSアプリケーションの部分を npmパッケージ化し、VSCodeエクステンションのアプリケーション側でインポートし、Webview に埋め込んでいます。プロジェクトを分離したことで、開発サイクルがシンプルかつ高速になり、バージョンアップもVSCodeエクステンション側で package.json ファイルのバージョン番号を更新するだけで済みました。もちろんこれ以外の方法でも開発はできますが、私たちはこの手法でスムーズに開発することができました。

{
  // npmパッケージとしてLWC OSSアプリをVSCode Extプロジェクト側に取り込む
 "dependencies": {
   "lwc-builder-ui": "^0.1.20"
 }
 //...
}

Webviewを作成するには WebviewPanel をインスタンス化し、UI(HTML)を読み込ませます。私たちの場合、UI部分はコンパイルされたLWC OSSアプリケーションを node_modules フォルダから読み込ませます(直前のステップで npm install でダウンロード済み)。読み込ませる前に、少しHTMLソースに変更を加えています。

this.webviewPanel = vscode.window.createWebviewPanel(
      'lwcBuilder',
      'LWC Builder',
      vscode.ViewColumn.One,
      {
        // スクリプトの使用をWebview内で有効化する
        enableScripts: true,
      }
    );

    // Webviewパネル内のコンテンツをセットする
    const pathToLwcDist = path.join(context.extensionPath, LWC_BUILDER_UI_PATH);
    const pathToHtml = path.join(pathToLwcDist, HTML_FILE);
    let html = fs.readFileSync(pathToHtml).toString();
    // HTML読み込み
    this.webviewPanel.webview.html = HtmlUtils.transformHtml(html, pathToLwcDist, webview);
);

 

ここでいうHTMLソースへの変更とは、LWCビルダーの場合は以下を含みました。

  • HTMLの<script> と <link> タグは、VS Code内ではセキュリティの観点から直接ローカルファイルにアクセスできない為、asWebviewUri関数を使って特別なURIに置き換える 必要があります。例えば、通常のWebアプリケーションで <script src=“index.js”> という記述は <script src=“vscode-webview-resource:index.js”> に置き換えられます。<link>タグも同様です。
  • ローカルのスクリプトやスタイルシートファイルを読み込みるようにするために、CSP meta タグを追加しました。

 

VSCodeの機能にWebviewからアクセスする

Webview内に埋め込まれたアプリケーションと、VSCodeエクステンションは、双方向にメッセージを受け渡しする事ができます。この時、受信側でメッセージを購読するリスナーを定義し、送信側はメッセージを発行するように実装します。

例として、LWCビルダーではLWC OSSアプリから、VSCodeエクステンションにどのようにメッセージを送っているか見てみましょう。ユーザーがWebview UI内で入力を完了したら、“Create”ボタンを押します。するとLWC OSSアプリはWebviewに対して入力内容を含んだメッセージを送信します。

onButtonClick() {
    // VSCodeのWebviewにメッセージを送信
    const message = new LWCBuilderEvent('create_button_clicked', this.contents);
    this.vscode?.postMessage(message);
}

メッセージハンドラーもしくはリスナーは、Webview Panelにアタッチします。Webview側でメッセージを受信したら、VSCodeエクステンションはメッセージの内容に従ってLWCファイルを生成します。ファイル生成には いくつかのVS Code API を使用します。

export class WebviewInstance {
  // ...
 this.webviewPanel.webview.onDidReceiveMessage(
     this.onDidReceiveMessageHandler,
     this,
     this.subscriptions
  );

  protected onDidReceiveMessageHandler(event: LWCBuilderEvent): void {
    // ユーザー入力UIから受け取ったメッセージを処理
    switch (event.type) {
      case 'create_button_clicked':
        createLwcFolder(event.payload, this.lwcFolderUri);
      case 'error':
        vscode.window.showErrorMessage(event.error);
        return;
      default:
        vscode.window.showInformationMessage(`Unknown event: ${event.type}`);
     }
   }
   // ...
}

以下の図は、全体の流れを示しています:

 

エクステンションを起動する為のContribution Pointsを定義する

VSCodeエクステンションを起動するためには、Contribution Points を定義する必要があります。Contribution Points とは、開発したエクステンションをユーザーに起動させたいエントリーポイントの事です。典型的な Contribution Point としてコマンドパレットがありますが、それ以外にもテキスト選択時や、メニューをクリックした時など様々あります。Contribution Pointsは package.json 内で定義します。LWCビルダーでは、2つの Contribution Points を設けています。ひとつはSFDXプロジェクト内のコマンドパレット。もうひとつは同じくSFDXプロジェクト内で lwc フォルダーをメニューで右クリックした時です。

{
  "contributes": {
    "commands": [
      {
        // コマンドを定義
        "command": "lwc-builder.openLWCBuilder",
        "title": "Open LWC Builder"
      }
    ],
    "menus": {
      // プロジェクトファイル一覧メニュー(explorer)を右クリック
      "explorer/context": [
        {
          // コマンドを登録
          "command": "lwc-builder.openLWCBuilder",
          "title": "Open LWC Builder",
          // 起動条件はSFDXプロジェクトである、かつフォルダ名がlwcである。
          "when": "explorerResourceIsFolder && resourceFilename == lwc && sfdx:project_opened"
         }
      ],
      "commandPalette": [
        {
          "command": "lwc-builder.openLWCBuilder",
          // コマンドパレットはデフォルトで追加されるが、今回はSFDXプロジェクトに限定したい
          "when": "sfdx:project_opened"
        }
      ]
    }
  },
  // ...
}

Contribution points は、登録されたコマンドを実行します。エクステンション開発では、エントリーファイル(LWCビルダーの場合はextension.ts)で、コマンドを定義します。LWCビルダーでは openLWCBuilder コマンドを定義し、Webviewを生成しています。このコマンドを2つのContribution pointsに割り当てています。

const openLWCBuilderCommand = vscode.commands.registerCommand(
    'lwc-builder.openLWCBuilder',
    (uri: vscode.Uri) => {
      new WebviewInstance(context, uri);
    }
  );

context.subscriptions.push(openLWCBuilderCommand);

 

パッケージ化とVSIXファイルの公開

最後にエクステンションのパッケージ化と公開についてですが、これは非常に簡単にです。vsce (“Visual Studio Code Extensions”) というこの目的の為のコマンドラインツールを使います。npm install -g vsce でインストールします。そして、 vsce package を実行し、VSCodeにインストール可能な.vsix ファイルを生成します。 また、必要に応じて vsce publish を実行すると、VS Code Marketplaceに公開することができます。

 

まとめ

VSCodeエクステンションは、VSCodeのカスタマイズ性や拡張性を大きく広げ、非常に強力です。エクステンションによってディベロッパー 体験はよりスピーディかつ簡単に、そして楽しくもなります!
VSCodeエクステンション開発は決して難しい事ではなく、何より強調したいのはVSCodeの公式ドキュメントはわかりやすく、サンプルも豊富で素晴らしいという事です。

VSCodeエクステンションは、Webviewで自由なWebアプリケーションを簡単に埋め込むことで、より柔軟になります。LWCビルダーは当初はLWC OSSで構築されたスタンドアローンなWebアプリでした。そのWebアプリをWebview内で再利用する事ができ、一方でスタンドアローンでも実行可能です。

VSCodeエクステンション開発と LWC OSSアプリ開発は私たちにとって多くの学びがあり、非常に良い経験でした。LWCビルダーが開発者の生産性向上に役立つことを願っています。LWC OSSアプリVS Codeエクステンション のソースコードは公開されており、引き続きあなたの素晴らしいアイデアで改善していける事を望んでいます。

 

著者について

Alba Rivasは、 SalesforceのLead Developer Evangelistで、Lightning Web ComponentsとLightning 採用戦略 領域を担当。Twittter @AlbaSFDC

 

訳者:

西野洸太朗(Kotaro Nishino)

Salesforceのデモエンジニア。最近の開発物はレコードクローン (Record Clone)

コメント

LWC × WebviewでVSCodeエクステンション開発