LINE株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。 LINEヤフー Tech Blog

Blog


社内のデザイナーの業務をサポートする LDSG Figma Plugin の工夫したところ、ハマりどころ

こんにちは!LINE Growth Technology UITチームの keishima です。普段はフロントエンドエンジニアとして LINEポイントクラブの開発に携わっています。
本記事では、 UIT 新春 Tech blog 5日目として、社内向けに作った Figma プラグインの技術的な面にフォーカスした記事をお届けします。
Figma プラグインを作った背景や、プラグインの機能紹介は、同時に公開したこちらの記事をご覧ください。(先にそちらを読むのがおすすめです!)

Figma プラグインとは?どうやって作るの?

みなさんは Figma を使ったことはありますか? Figma とは UI デザインを行うためのツールで、類似のものに Adobe XD や Sketch などがあります。
Figma はWeb ベースで作られたツールであるため、アプリをインストールしなくてもブラウザから利用することができます。
また、ユーザー自身でプラグインを作成することも可能で、コミュニティでも多くのプラグインが公開されています。

Figma 自身と同じように、プラグインも Web ベースで構成されています。
プラグインの開発者は HTML / JS / CSS と Figma が提供するプラグイン用の API を使って、自身の好きなプラグインなどを作成することができます。

Figma のプラグインは UI 部分を担当するコード(以後 UI script とします)と、Scene の操作を行うコード(以後 Scene script とします)に分かれています。
UI script からは Scene を直接操作することができないため、プラグインで Scene script とメッセージをやり取りする形で行う必要があります。
Google Chrome の拡張機能を作ることをイメージしてもらうとわかりやすいかもしれません。

UI 部分には好きな UI フレームワークやライブラリを使用できます。今回作成した Figma プラグインでは Vue2 を使用しました。

また、Figma プラグインでは UI を持たないプラグインを作成することも可能です。
最新の API など、Figma プラグインについてより詳しく知りたい方は、Figma Developers をご確認ください。

達成したいゴールとその設計

今回私たちが作った社内向けに作った Figma プラグインでは、同じく社内向けに公開されている Figma の Team Library から Component や Text Style を呼び出すような操作を行います。
しかし、2022年1月現在では、Figma プラグインのAPI から Team Library に直接アクセスすることはできません。
Team Library に含まれる Component を呼び出したい場合には、その Component の key を使って figma.importComponentByKeyAsync() などを呼び出す必要があります。そのため、まずは key を取得するための仕組みを構築する必要がありました。

Figma にはプラグインから操作できる API の他に、REST API も用意されており、そちらからは Team Library のデータにアクセスすることができます。
余談ですが、Figma の REST API ドキュメントページでは、実際にAPIに対してリクエストを送って中身をみることができる機能があり、開発初期段階に欲しいデータが取れるかどうか、検証するのにとても役立ちました。

調査をしていくと、 LDSG が Figma ユーザー向けに提供している Team Library 内には Variants を含むものがほとんどですが、一部 Variant を含まないコンポーネントがあることがわかりました。
Variant を含むコンポーネントは Component Sets として、含まないコンポーネントは Component として、それぞれ別の API エンドポイントから取得する必要があります。
また、Figma プラグインの API から呼び出す際にも Component と Component Set は区別する必要がありますが、プロパティはほとんど同じであったため、私たちは内部的に2種類のデータをまとめて管理するようにしました。

// こんな感じのデータ(以下のinterfaceは一部です)として持っておいて…
export interface ComponentOrComponentSet {
  type: 'COMPONENT' | 'COMPONENT_SET';
  key: string;
  thumbnail_url: string;
  name: string;
  description: string;
  containing_frame?: {
    name: string;
    pageName: string;
  };
}
 
// …typeによってScene上に呼び出す際に処理を分ける
if (type === 'COMPONENT_SET') {
  await figma.importComponentSetByKeyAsync(key);
} else {
  await figma.importComponentByKeyAsync(key);
}

バックエンド には LINE のプライベートクラウドである Verda が提供するFunctions (AWSのLambda相当) と オブジェクトストレージ (AWSのS3相当) を利用しています。
Functions に用意したAPIを叩くと、Team Library から情報を取得する処理が走ります。私たちは、このAPIを叩くための Figma プラグイン(Admin Plugin)を別で作成しました。

今回のプロジェクトにサーバーサイドのエンジニアはいませんでしたが、以上のような構成で実現することができました。

ここで私たちの作った Figma プラグインの中身の設計についてもご紹介します。前のセクションでもご紹介した通り、UI script には Vue2 を利用していますが、補助的に rxjs を活用しています。
UI script と Scene script 間では多くのメッセージをやり取りします。それぞれの script でメッセージを購読するための rx observable を用意して、必要な場所で適宜 subscribe をするような形にしています。
ただし、多くの部分では rxjs であることを意識せずに、 UI script と Scene script 間のメッセージのやり取りを実装することができるようになっています。
コードの一部は 社内向けの npm パッケージとして切り出し、先ほど紹介した Admin Plugin でも利用しています。

文章だけではわかりにくいと思うので、コードの一例をお見せします。
例えば、「今までにプラグイン経由で Scene に追加したコンポーネントの履歴を取得する処理」を実装する場合、UI script はこのようなコードを書きます。

import { send } from '@linecorp/figma-plugin-toolkit';
 
async getComponentHistory() {
      const { data } = await send<string[]>(COMPONENTS.GET_HISTORY, { type: 'COMPONENT' });
      this.history = data;
}

Scene script では MessageBody クラスのインスタンスが渡されて、その respond メソッドを利用することで
改めて Scene 側から postMessage 処理を書いて、 UI 側でそれを受け取る処理をしなくても双方向でのデータのやり取りを行うことができるようになっています。

import { MessageBody } from '@linecorp/figma-plugin-toolkit';
 
const STORAGE_TYPE_KEYS = {
  COMPONENT: 'component-history',
  LAICON: 'laicon-history',
  ASSETS: 'assets-history',
};
 
export async function getComponentHistory(type: STORAGE_TYPE): Promise<string[]> {
  const key = STORAGE_TYPE_KEYS[type];
  const history = (await figma.clientStorage.getAsync(key)) || '[]';
  return JSON.parse(history);
}
 
export async function execGetComponentHistory(msg: MessageBody<GetComponentHistoryArgs>): Promise<void> {
  const result = await getComponentHistory(msg.data.type);
  msg.respond({ data: result });
}

工夫したところ & ハマったどころ

Figma プラグインを開発するために、いくつか工夫した点やハマった点があったのでご紹介します。

UIの作り込みで工夫したこと

UIの開発をしやすくするために、通常のビルドとは別に
webpack-dev-server や hot reload を備えた、一般的な Web アプリケーションを作るような機能を備えた dev:ui という npm script を用意しました。
これを使うことで Figma の App 以外でも UI 部分の開発を行うことができるようになりました。新しい画面を作るときはもちろん、この後触れる各ブラウザでの表示確認の話にも関係しますが、
「Safari で見たときにここの表示がおかしい」みたいな問題があるときにとても便利に機能してくれました。

また、プロジェクト外で大きく助けとなったのは LDSG チームが提供している LDSG の Web Components です。
現時点で LDSG の Web Components はスマートフォン向けに設計されているものなので、デザイン含めて多少の修正を行う必要はありましたが
UIの挙動やアクセシビリティについてよく練られたものであるため、プラグイン実装の工数を大幅に削減してくれました。

各ブラウザで動作確認は行ったほうがいい

Figma は Electron ベースのアプリを提供しており、それは Chromium をベースとしているため、
プラグインを開発しながら Chrome での動作確認もほとんど済むような形になっています。ただし、Safari と Firefox でも念のため動作確認を行うことを強くおすすめします。
例えばコンポーネントをドラッグアンドドロップで配置する機能を実装したときに、Chrome 系以外の環境ではドロップした位置が正しく取得できない不具合に遭遇しました。
結局この問題を回避することは難しく、Chrome 系以外の環境ではドラッグアンドドロップを行っても、表示している領域の中央にコンポーネントを配置するようにしました。

また、これは Figma に限らない問題ですが、CSS のレンダリング結果や、デフォルトのスタイルなどが微妙に異なることがありました。

ある日突然Devビルドが動かなくなった

開発時にはビルド速度を優先させるため、webpack の dev mode を使用して開発を行っていました。
開発の序盤では特にこれで問題はありませんでしたが、実装する機能が増えてきた頃から少しずつ開発版のプラグインを起動するのに時間がかかるようになりました。
そしてある時点から起動してもプラグインウィンドウ内が真っ白になったままで、プラグインが動かなくなる問題に遭遇しました。

調べてみると、生成された index.html のファイルサイズが 8.4MB になっており、大きすぎて読み込みができていないのでは?ということになりました。
試しに prod mode でビルドを行うと、minify が行われるため、2MB弱におさまっており、正しく起動できることを確認できました。

prod mode でやっていることと同じように、dev mode でも Terser を使った minify を行うように設定を調整しましたが、watch 時の差分発生時に行われるビルドに時間がかかるようになってしまいました。
そこで、 ts-loader と webpack 標準の terser の代わりに、esbuild-loader と esbuild-minify-plugin (型チェックには fork-ts-checker-webpack-plugin を別途追加) を導入するようにしたところ、常時 minify が有効でもビルド時間はとても短く済むようになりました。開発時のプラグイン起動速度も大幅に向上したため、全体として開発体験がとても良くなりました。

Figma プラグイン開発の今後に期待したいこと

2022年1月現在、Figma でプラグイン開発を行う上で少し不便な点もいくつかありました。

一つ目は、プラグインのリリースを行う際に、Figma のネイティブアプリを使用する必要がある点です。一般的なアプリケーションのように、CI 環境からデプロイを行うことができないのでリリース時に緊張しました。
二つ目は、チームで開発を行う際に、最初にリリースした人しか次バージョンのデプロイを行えないという点です。担当者が退職などでプロジェクトを離れてしまうと、現状ではサポートに連絡をしてユーザーを付け替えてもらう必要があるようです。

まとめ

この記事では、LINEのデザインシステムをサポートする Figma プラグインの技術的な面や、Figma プラグインを作る上で工夫したこと、困ったことなどをご紹介しました。
プラグイン自身の制約と向き合う必要があるなど、一筋縄ではいかないこともありましたが、デザイナー業務の効率化に貢献できたことがうれしいです。

みなさんも Figma でなにかもの足りないと感じたときには、プラグインの開発を検討してみてはいかがでしょうか?

採用情報

LINE Growth Technology や LINE ではフロントエンジニアを募集しています。

UIT 新春 Tech blog記事一覧

  1. Web フォントを使って contenteditable から脱出する
  2. 業務で見つけた! Conditional Types
  3. 業務で役に立つVS Code機能拡張を作ってみた話
  4. フロントエンド開発の業務を支えるちょっと珍しいチームのご紹介
  5. 社内のデザイナーの業務をサポートする LDSG Figma Plugin の工夫したところ、ハマりどころ
  6. 2022年におけるフロントエンド開発のベースライン
  7. Prettier への支援開始のお知らせと企業が OSS に対して支援するということ
  8. 続 LINE 社内用 NPMパッケージの管理戦略