JavaScriptでAdobeXDプラグインを作ってマークアップ作業を効率化

AdventCalender2020「ちょっとした工夫で効率化！01【PR】パソナテック」の19日目です。日付が変わってしまいましたが、寝てないのでまだ19日ということで良しとします。
（テーマは、ちょっとした工夫ですが、「ちょっとした工夫」に収まってないかもしれないので、工夫の導入記事ということで、予めご了承ください。）

エディタをカスタマイズや拡張機能を作ることで、日常の作業効率が上がるのは、デベロッパーの皆さんには当然の発想です。また、ブラウザの拡張機能なども、Webマーケティングの業務補助をするツールなどもよく使われるかと思います。拡張機能のような小技で作業効率を上げるというのはエクセルのマクロが、そういった分野のパイオニアといったところでしょうか。

本記事で、デザインツールでの拡張機能/プラグインを作り方を紹介し、ビジネスロジックを設計しているフロントエンド開発者や、LPなどのHTMLコーディングを日々行う人にとって苦痛な業務であるマークアップを少しでも楽にできればと思います。

## Adobe XD

デザインデータの作成は、以前は、Adobe Photoshop、Adobe Illustratorで作られることが主流でしたが、Adobe XD、Sketch、Figmaなどが最近使われることが増えているようですね。
Adobe XDは無料で使えますし、Adobe Illustratorで作成されたデータも開けるので、最近よく使います。

### Adobe XDのプラグインとは

Adobe XDのプラグインは、大きく分けて3種類のパターンがあるかと思います。

- デザイン作業を行う。
- XDファイルから、何かをとりだす。
- 外部サーバと連携して、デザインデータを加工する。

例えば、こんな物がつくれそうです。

- デザインデータの色を10%薄くする。
- 色相環を180度回転させる。
- テキストを抽出する。
- HTMLマークアップしたものを出力する。

実際には、画像やフリーアイコンを挿入するといったプラグインがよく使われるかと思います。また、有償でとても便利なプラグインも提供されています。
[こんな感じのHTMLやCSSをコピーできるプラグイン](https://twitter.com/tomo_thumb/status/1319035253239934976/video/1) を作成しているところで、得られた知識を導入ステップを紹介したいと思います。

### JavaScriptでプラグインが作れる

他のデザインソフトではどうか知りませんが、AdobeXDのプラグインはJavaScriptで作ることができます。たしか、PhotoshopでもJavaScriptが使えて、ちょっとしたものを作った記憶があります。

Web開発に関わる多くのデベロッパーはJavaScriptもかけると思いますので、比較的身近なデザインツールの拡張をJavaScriptで行えるのはすごくメリットありますね。
複雑なことをやろうとすると、難易度は上がってしまいますが、力試しにも良いかもしれませんね。
本記事では、必要最低限を紹介することでプラグイン作りの導入になればと思います。

## 環境の準備
前置きが長くなりましたが、いよいよコードを。と言いたいところですが、デベロッパー登録が必要です。もう少し我慢です。

まずはAdobeアカウントにログインし、[Developer console](https://console.adobe.io/projects)にアクセスしてください。

1. 「**Create new project**」から新規プロジェクトを作成します。
2. 名称はあとから変更も可能なので、なんでもよいです。
3. Add Plugin をクリック。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10512/eb9940c5-2a8d-bf02-824e-6a648813a9ac.png)

4. Adobe XD をクリックして、Save。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10512/d5834b92-eef5-142a-c740-6374cbc6b14a.png)

5. **Download the starter project** の **Download** をクリックし、スタータープロジェクトを一式ダウンロード。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10512/d485a145-acf6-716b-28c6-0067d47b90f7.png)


5でダウンロードしたzipファイルが基本のファイルが揃っています。
必要なファイルは、次の2ファイルだけです。

- **manifest.json** ： 設定ファイル
- **main.js** ： メインスクリプトファイル

### manifest.json
プラグインの設定ファイルです。
まずは、サンプルのファイルを紹介します。

```json
{
  "name": "プラグインの名称",
  "host": {
	"app": "XD",
	"minVersion": "21.0"
  },
  "id":"プラグインプロジェクトのID",
  "uiEntryPoints" : [
    {
      "type" : "menu",
      "commandId" : "createRectangle"
    }
  ],
  "version" : "0.0.1",
}
```
ここで重要なのは、

- **id** : **プラグインプロジェクトのID**。Developer Consoleで確認することができます（スタータープロジェクトをダウンロードした時点で、すでに入力されているかもしれません）
- **name** : **自分のプラグインの名称**。好きな名称をつけましょう。
- **host** : **動作するXDのバージョン情報**。（ダウンロードしたものから変更しないで良いと思います。XDのアップデートで動作しなくなった場合に、再確認すると良いです）
- **uiEntryPoints** : **本プラグインで提供する機能の一覧（配列）**。この例では、「createRectangle」という機能が提供されます。**（main.jsで認識させる識別子と考えれば良いです）**。後述しますが、ここの項目でキーボードショートカットを設定することもできます。

### main.js

まずは、ダウンロードしたプログラムを見てみましょう

```jsx
const {Rectangle, Color} = require("scenegraph"); 

function rectangleHandlerFunction(selection) {  // ★1
    const newElement = new Rectangle(); 
    newElement.width = 100;
    newElement.height = 50;
    newElement.fill = new Color("Purple");

    selection.insertionParent.addChild(newElement);
    newElement.moveInParentCoordinates(100, 100);
}

module.exports = {
    commands: {
        createRectangle: rectangleHandlerFunction // ★2
    }
};
```

横100 x 縦50の紫色の長方形オブジェクトが、XYがそれぞれ100pxの位置に描画されるプログラムです。

ここで重要なのは2つです。

- ★1 ： メインのプログラムを記述します。**functionの名称は自由に記述可能**です。
- ★2 ： **manifest.jsonのuiEntryPointsのcommandIdで指定した識別子**を、**commandsのキー**とし、メインプログラムを割り当てます。


### 開発版プラブインとしてインストール

では、動かしてみましょう。自分で利用するだけであれば、開発プラグインとして認識させると良いです。
XDのメニューバーの、**「プラグイン→開発版→開発フォルダーを表示」**からインストール先のディレクトリを表示できますので、ここにプロジェクトディレクトリを配置します。
（複数のプラグインを作成している場合は、複数のプロジェクトディレクトリをここに配置すれば良いです。）![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10512/aa4cb8aa-c7d4-fc54-da79-31ca00188716.png)


配置が終わったら、先と同じようにメニューから**「プラグイン→開発版→プラグインを再読み込み」**からプラグインを読み込むことができます。（表示されない場合は、XDを再起動すると良いです）

プログラムが認識されると、メニューバーのプラグインの一覧に、作成したプラグインの名称が並んでいると思います。それをクリックすると、プラグインが実行されます。


## プラグインの事前知識・概念

### Scenegraph・SceneNode

XD特有で、最低限理解する必要がある概念としては、

- **Scenegraph**
- **SceneNode**

があります。

デザインファイルを開くと、複数のレイヤーが重なってレイアウトされます。また複数のレイヤーをグループ化していくことで、HTMLのDOMのようなツリー構造を作ることができます。
**HTMLのDOMにあたるものが、XDのScenegraph**と考えるとイメージしやすいです。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10512/9a4384c6-8093-5a1c-589b-ae08d2c3c3e0.png)



また、**HTMLのpやdivといったタグに相当するものは、SceneNodeと呼ばれるコンポーネント(Node)**になります。次のような物があります。

- GraphicNode
    - Artboard：アートボード
    - Rectangle：長方形、正方形
    - Elipse：楕円、正円
    - Polygon：多角形
    - Line：線
    - Path：パス
    - Text：テキスト
    - ...など
- Group：グループ
- ...その他

スタイル情報を取得するには、それそれのコンポーネントのプロパティにアクセスすることで取得可能です。（後述）
詳しくは[APIドキュメント](https://adobexdplatform.com/plugin-docs/reference/scenegraph.html)に記載されています。


### 使えるJavascript

さて、JavaScriptで作れると書きましたが、一部機能が制限されています。
ES5の大半は使えます。ES2015については次のような物が使えます

- テンプレートリテラル
- Class
- let, const
- スプレッド演算子
- アローファンクション
- async / await / promise

**package.jsonやnode_modulesは読み込まない**ので、npmパッケージを使いたい場合は、別途webpackでビルドしたmain.jsを配置するといったアプローチを取る形になります。[公式ドキュメントでReactやwebpackの利用手順なども解説されています。](https://adobexdplatform.com/plugin-docs/tutorials/quick-start-react/)


## 実践
それでは例として、XD上で選択しているテキストレイヤーのテキストやCSSスタイルをクリップボードにコピーするプログラムを作ってみます。

### キーボードショートカット
メニューバーを操作するのが億劫ですので、キーボードショートカットで実行できるようにします。

manifest.jsonを編集します。
**uiEntryPoints**の**shortcut.mac**と**shortcut.win** にキーを設定します。

```jsx
{
  ...
  "uiEntryPoints": [
    {
      "type": "menu",
      "label": "テキストをコピー",
      "commandId": "copyText",
      "shortcut": {
        "mac": "Ctrl+T",
        "win": "Ctrl+Shift+T"
      }
    },
    {
      "type": "menu",
      "label": "Inline CSSをコピー",
      "commandId": "copyStyle",
      "shortcut": {
        "mac": "Ctrl+S",
        "win": "Ctrl+Shift+S"
      }
    }
  ]
}
```

main.jsは次のようになります。

```jsx
const clipboard = require("clipboard"); // ★1

function copyText(selection) { // ★2
    let node = selection.items[0];    
    clipboard.copyText(node.text); // ★3
}
function copyStyle(selection) {
    let node = selection.items[0];
    let style = '';
    styles += addStyle('font-size', this.node.fontSize + "px"); // ★4
    if(node.fillEnabled) {
      if(node.fill.toHex() !== "#000000"){
        styles += addStyle('color', this.node.fill.toHex());
      }
    }
    clipboard.copyText(styles);
}

function addStyle(key, val){  // ★5
    return "\n  "+ key+": "+val+";";
}

module.exports = {
    commands: {
        copyText:copyText,
        copyStyle:copyStyle,
    }
};
```

- ★1 ：クリップボードに保存したいので、クリップボードAPIを使います。
- ★2 ：functionに渡される引数(selection)に、XD上で選択しているオブジェクトが渡されます
- ★3 ：XDで選択した直下のNodeはselection.items[0]にてアクセスできます。textプロパティでテキストを取得しています。
- ★4 ：fontSizeプロパティでフォントサイズを取得しています。
- ★5 ：CSSフォーマットに整形するための独自関数です。

単純なサンプルですので、これだけです。
編集が終わったら、先と同じようにメニューから**「プラグイン→開発版→プラグインを再読み込み」**からプラグインを読み込みして、テキストレイヤーを選択の上、キーボードショートカットを実行してみましょう！

今回は、テキストレイヤーの場合に限定していますが、長方形、楕円など、それぞれ独自のプロパティを持っていますので、ドキュメントを参照ください。
[https://adobexdplatform.com/plugin-docs/reference/scenegraph.html](https://adobexdplatform.com/plugin-docs/reference/scenegraph.html)


### デバッグ方法
JavaScriptでのデバッグは、console.log()が使われますが、XDでは、開発者コンソールというのが用意されているので、console.logの結果を、この開発者コンソールに出力することができます。
開発者コンソールは**「プラグイン→開発版→開発者コンソール」**をクリックして表示できます。

## まとめ
本記事は、導入用として、すごく簡易的な物を紹介しましたが、グループレイヤー内のサブツリーを捜査していくようなプログラムを組むことで、大きなグループをまとめて、HTML/CSS出力したりもでき、非常に便利になりそうです。

出力は文字列を整形するだけで、HTML出力だけでなく、ReactやFlutterのような、宣言的なViewコンポーネントフレームワークのコンポーネントとしても出力ができそうですね。


ドキュメントは英語しかありませんが、チュートリアルも充実していますし、かなり丁寧に記載されている印象です。
また、一般公開されているプラグインをインストールすると、プラグインディレクトリに配置されますので、どのようにプログラムが記述されているかを確認することができます。

以上です、本記事がXD愛用している皆さんの参考になれば幸いです！