Figma PluginをVue.js + TypeScript + Sass + Bulmaで開発する

# About

[Figma Plugin](https://www.figma.com/plugin-docs/intro/)に未来を感じています。

公式ドキュメントには、[UIをReactで構築する流れ(Building React)](https://www.figma.com/plugin-docs/bundling-react/)が紹介されています。
しかし、個人的に(会社的に)はVueに慣れていることもあり、以下の環境で構築してみようと試みました。

* Vue.js
* TypeScript
* Sass
* Bulma

注: Figma Pluginはあまり関係なく、ほぼWebpackの設定の問題ですw

# 結論

GitHubにコードを置いておきました。

https://github.com/hosopy/figma-plugin-vue-sample

# 解説: ビルド環境

まず、公式ドキュメントの[Bundling with Webpack](https://www.figma.com/plugin-docs/bundling-webpack/)を参考に、Webpackベースのコードベースを構築しました。
[公式GitHubにサンプル](https://github.com/figma/plugin-samples/tree/master/webpack)があります。

以下は、Bundling with Webpackのコードベースからの差分に関する解説です。

## package.json

Vue.js, Sass, Bulmaを使うために必要なパッケージを追加しました。

```sh
$ npm install --save vue vue-class-component vue-property-decorator bulma
$ npm install --save-dev node-sass sass-loader vue-loader vue-template-compiler
```

## tsconfig.json

[Vue.jsのTypeScript推奨構成](https://jp.vuejs.org/v2/guide/typescript.html#%E6%8E%A8%E5%A5%A8%E6%A7%8B%E6%88%90)に変更しました。

```json:tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "strict": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "module": "es2015"
  }
}

```

## webpack.config.js

主な変更点

* `rules`
  * `vue-loader`の設定を追加
  * `ts-loader`のオプションに`appendTsSuffixTo: [/\.vue$/]`を追加
  * `sass-loader`の設定を追加
* `resolve`
  * `.vue`に関する設定を追加
* `plugins`
  * `VueLoaderPlugin`を追加

```javascript
...

const VueLoaderPlugin = require('vue-loader/lib/plugin')

...

module.exports = (env, argv) => ({
  ...

  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader',
        options: {
          loaders: {
            'scss': 'vue-style-loader!css-loader!sass-loader',
            'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
          }
        }
      },
      {
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          appendTsSuffixTo: [/\.vue$/]
        }
      },
      ...
      {
        test: /\.scss$/,
        loader: [
          { loader: 'style-loader' },
          { loader: 'css-loader' },
          { loader: 'sass-loader' }
        ]
      },
      ...
    ],
  },

  resolve: {
    extensions: ['.tsx', '.ts', '.jsx', '.js', '.vue'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },

  plugins: [
    ...
    new VueLoaderPlugin()
  ]
})

```

# 解説: UIの実装

[Reactのサンプル](https://github.com/figma/plugin-samples/tree/master/react)と同じく、指定した数の正方形を描画するRectangle CreatorをVueで実装してみました。

まず、UIのエントリポイント`src/ui.html`と`src/ui.ts`です。

```html:src/ui.html
<div id="app"></div>
```

```typescript:src/ui.ts
import Vue from 'Vue'
import RectangleCreator from './RectangleCreator'
import './ui.scss'

new Vue({
  el: '#app',
  render (h) {
    return h('rectangle-creator')
  },
  components: {
    RectangleCreator
  }
})
```

`src/ui.scss`では、Bulmaを読み込んで変数の設定などを行います。

```sass:src/ui.scss
@charset "utf-8";

@import "~bulma/sass/utilities/initial-variables";
@import "~bulma/sass/utilities/functions";

$brand: #18A0FB;
$primary: $brand;

@import "~bulma/bulma";
```

最後に、BulmaのCSSを使ってRectangle Creatorを書き換えてみます。

```vue:src/RectangleCreator.vue
<template>
  <div class="container">
    <h2 class="title is-4">Rectangle Creator</h2>

    <div class="field">
      <label class="label">Count</label>
      <p class="control">
        <input class="input is-small" type="number" v-model="count">
      </p>
    </div>

    <div class="field is-grouped is-grouped-centered">
      <p class="control">
        <button class="button is-primary is-small" @click="create">
          Create
        </button>
      </p>
      <p class="control">
        <button class="button is-small" @click="cancel">
          Cancel
        </button>
      </p>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'

@Component({
  name: 'RectangleCreator'
})
export default class RectangleCreator extends Vue {
  count: number = 5

  create () {
    parent.postMessage({ pluginMessage: { type: 'create-rectangles', count: this.count } }, '*')
  }

  cancel () {
    parent.postMessage({ pluginMessage: { type: 'cancel' } }, '*')
  }
}
</script>

<style lang="scss">
.container {
  padding: 0.75rem;
}
</style>
```

実行するとこんな感じ

![スクリーンショット 2019-08-22 17.05.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1489/bd182480-a4fe-5b13-f565-da80c2c3530e.png)


# 参考: 途中ハマったこと

## 初期提供のfigma.d.tsが古い問題

Figma Desktop Appでプラグインの初期生成したときに提供される`figma.d.ts`は古い?もので問題があり、コンパイルエラーが発生しました。
(2019/08/22現在)

具体的には、`LineHeight`のインタフェース定義でエラーになります。

```typescript
interface LineHeight {
  readonly value: number
  readonly unit: "PIXELS" | "PERCENT"
} | {
  readonly unit: "AUTO"
}
```

[GitHubのサンプルのfigma.d.ts](https://github.com/figma/plugin-samples/blob/master/webpack/figma.d.ts)を参考に、型エイリアスに書き換えることで解決しました。

```typescript
type LineHeight = {
  readonly value: number
  readonly unit: "PIXELS" | "PERCENT"
} | {
  readonly unit: "AUTO"
}
```