【JavaScript】XDファイルをSwiftへと書き換えるプラグインを作ってみた

残業はしたくないけど、残業代だけは欲しい、TOSHです。
久しぶりのQiitaになりました。そして、Qiitaの記事としては、初めてのJavaSctiptです。動的型付け言語ってやばいですねぇ〜、魔言語ですねぇ〜

# TDLR
XDファイルから色の項目を抜き取ってをSwiftファイルのUIColorのExtensionへと書き換えるプラグインをJavaScriptで作りました。こちらのGitHubのリンクを参照してください。
https://github.com/tosh7/SwiftColorConverter

#初めに
先日デザイナーさんから、XDのファイルをもらいました。イメージはしたの画像のような、様々な色を定義した、ファイルになります。
ここから、全部の色を確認して、UIColorのExtensionを作成しても良いのですが、何せ、手作業は面倒なので、自動化する方法はないのかというところから、このプロジェクトが始まりました。<img width="896" alt="スクリーンショット 2020-06-16 23.31.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180994/23e7fd7c-90d8-65b8-b937-0d8ab7093372.png">

# 作り方
XDのプラグインの作り方はあまり、Qiitaなどに記事は多く何ですが、さすがはAdobeさん、リファレンスは充実しています！（残念ながら全て英語です。諦めて読みましょう）そして嬉しいことに、サンプルコードも充実しています。困ったことがあった場合にはここに立ち返るようにしましょう！
[リファレンス](https://adobexdplatform.com/plugin-docs/)
[サンプルコード集](https://github.com/AdobeXD/Plugin-Samples)

## 使用言語について
基本的には、JavaScriptになります。また、HTMLやCSSも使えるのですが、これらはXD上にViewを出したい場合に使用するためのものなのなので、今回は使用しません。サンプルのコードをみる限りだと、VueやReactも使用できるようですね。

## はじめ方
プラグインは、`/Users/ユーザー名/Library/Application Support/Adobe/Adobe XD/develop`のディレクトリの下に置く必要があります。
基本的には、`プラグイン/開発版/プラグインの作成`を選択すると自動的に作成できるかと思います。
この際、Mustで必要になってくるのは、`main.js`と`manifest.json`の二つのファイルになります。
いかに、`manifest.json`のファイルの紹介を書いていこうと思います。

```manifest.json
{
    "name": "SwiftColorConverter",
    "id": "固有のKey",
    "version": "1.0.0",
    "description": "オブジェクトのカラーコードをSwift化します",
    "host":{
        "app": "XD",
        "minVersion":"13.0.0"
    },
    "uiEntryPoints":[
        {
          "type":"menu",
          "label":"swiftColorConverter",
          "commandId":"swiftColorConverter"
        }
    ]
}
```
このファイルは基本的な設定項目を列挙するためのファイルなので、そこまで、突起するべきことはありません。

次に、`main.js`の中身を見ていこうと思います。

```main.js
const fs = require("uxp").storage.localFileSystem;

async function swiftColorConverter(selection) {
    //選択中のアイテム
    const items = selection.items;

    if(items.length === 0) {
        console.log("オブジェクトが選択されていません");
        return;
    }

    let colorArray = [];
    items.forEach(item => {
        //オブジェクトが円ではない場合、色を取得しない
        if(item.constructor.name === "Ellipse") {
            colorArray.push(item.fill);
        } else {
            console.log("このオブジェクトから色は取得できませんでした");
        }
    })

    //フォルダの書き出し  
    const userFolder = await fs.getFolder();
    const newFile = await userFolder.createEntry("UIColor+extension.swift", {overwrite: true});
    newFile.write(swiftConvertModel(colorArray));
}

function swiftConvertModel(colorArray) {
    let swiftText = 'import UIKit\n\nextension UIColor {\n    public enum Name: String {\n';
    for(let i = 0; i < colorArray.length; i++) {
        const colorName = colorArray[i].value.toString(16) + 'Color';
        swiftText += '        case ' + colorName + '\n'
    }
    swiftText += '    }\n\n'
    swiftText += '    public convenience init(name: Name) {\n        switch name {\n'
    for(let i = 0; i < colorArray.length; i++) {
        //カラーコードを16進数、RGBへと変換
        const colorName = colorArray[i].value.toString(16) + 'Color';
        const colorCode = '0x' + colorArray[i].value.toString(16).slice(2);
        const red = (colorArray[i].r / 255).toFixed(10);
        const green = (colorArray[i].g / 255).toFixed(10);
        const blue = (colorArray[i].b / 255).toFixed(10);
        swiftText += '        case .' + colorName + ':\n            self.init(hex: ' + colorCode + ')'
        swiftText += ' //#colorLiteral(red: ' + red + ', green: ' + green + ', blue: ' + blue + ', alpha: 1)\n'
    }
    swiftText += '        }\n    }\n'
    swiftText += '}'
    return swiftText;
}

module.exports = {
    commands: {
        swiftColorConverter: swiftColorConverter
    }
}
```
次の項目でこのコードの内容を説明していこうと思います。

## メインソッドの呼び出し
manifestで設定したこまんとのひもづけ行います。これを行うことで、プラグイン実行時に、`swiftColorConverter`をよぞ出すことができます。
この時、引数としてセットされている、`selection`はコマンド実行時に選択されているオブジェクトが配列として渡されます。

```javascript
module.exports = {
    commands: {
        swiftColorConverter: swiftColorConverter
    }
}
```

## カラーコードの取得方法
カラーコードは基本的に、hexとRGBの両方で取得したいと思います。その取得方法を紹介します。
先ほど、取得した、selectionItemの要素を一つをitemとします。また、今回は特に、円形のオブジェクトのみの色を選択することにしています。
XDのクラスの一つにColorというClassがあるのですが、そのクラスが色を管理しています。今回はオブジェクトをぬりつぶしている色を取得したいので、
item.fillのようにしてアクセスをします。[Colorクラスのリファレンス](https://adobexdplatform.com/plugin-docs/reference/Color.html?h=color)
hex, RGBの値の取得方法は以下の通りになります。

```javascript
//hex
//Colorのvalueを16進法に変換する
const hex = item.fill.value.toString(16)

//RGB
const red = item.fill.r
const green = item.fill.g
const blue = item.fill.b
```

## ファイルの書き出し方法
まず、ファイルの一番上に、

```javascript
const fs = require("uxp").storage.localFileSystem;
```
と書きましょう。これがないと、ファイルへのアクセスができません。

では、続いて、ファイルの書き出し方法を紹介していきたいと思ます。
今回、awaitを使用するので、元のメソッドをasyncにしましょう！

```javascript
//ここで保存する先のフォルダを指定します。
const userFolder = await fs.getFolder();
//ファイル名をここで決めています。ファイル名ファイル形式を変更したい際には、ここを変更してください！
const newFile = await userFolder.createEntry("UIColor+extension.swift", {overwrite: true});
```

## Swiftファイルの生成
ここからは気合です。大変だと思うのですが、自分の作りたい形式に変更させていましょう！
自分の場合は以下のように、この設定をしました。

```javascript
function swiftConvertModel(colorArray) {
    let swiftText = 'import UIKit\n\nextension UIColor {\n    public enum Name: String {\n';
    for(let i = 0; i < colorArray.length; i++) {
        const colorName = colorArray[i].value.toString(16) + 'Color';
        swiftText += '        case ' + colorName + '\n'
    }
    swiftText += '    }\n\n'
    swiftText += '    public convenience init(name: Name) {\n        switch name {\n'
    for(let i = 0; i < colorArray.length; i++) {
        //カラーコードを16進数、RGBへと変換
        const colorName = colorArray[i].value.toString(16) + 'Color';
        const colorCode = '0x' + colorArray[i].value.toString(16).slice(2);
        const red = (colorArray[i].r / 255).toFixed(10);
        const green = (colorArray[i].g / 255).toFixed(10);
        const blue = (colorArray[i].b / 255).toFixed(10);
        swiftText += '        case .' + colorName + ':\n            self.init(hex: ' + colorCode + ')'
        swiftText += ' //#colorLiteral(red: ' + red + ', green: ' + green + ', blue: ' + blue + ', alpha: 1)\n'
    }
    swiftText += '        }\n    }\n'
    swiftText += '}'
    return swiftText;
}
```

# 実際に、実行してみた
色を取得したいオブジェクトを選択し、XDのメニューから、プラグイン/開発版/プラグインを再度読み込みを選択
![howToUse1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180994/074374fe-d729-21cf-a9ef-1eacee554379.png)

XDの、メニューからSwiftColorConverterを選択
![howToUse2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180994/764478d7-7e97-977a-21b3-3b68ea3a335e.png)

実際に実行してみると、
このようなSwiftファイルが生成されます。

```UIColor+Extension.swift
import UIKit

extension UIColor {
    public enum Name: String {
        case ff808080Color
        case ffacacacColor
        case ffeaea1eColor
        case ff000000Color
        case ff38f647Color
        case fff2a1a7Color
    }

    public convenience init(name: Name) {
        switch name {
        case .ff808080Color:
            self.init(hex: 0x808080) //#colorLiteral(red: 0.5019607843, green: 0.5019607843, blue: 0.5019607843, alpha: 1)
        case .ffacacacColor:
            self.init(hex: 0xacacac) //#colorLiteral(red: 0.6745098039, green: 0.6745098039, blue: 0.6745098039, alpha: 1)
        case .ffeaea1eColor:
            self.init(hex: 0xeaea1e) //#colorLiteral(red: 0.9176470588, green: 0.9176470588, blue: 0.1176470588, alpha: 1)
        case .ff000000Color:
            self.init(hex: 0x000000) //#colorLiteral(red: 0.0000000000, green: 0.0000000000, blue: 0.0000000000, alpha: 1)
        case .ff38f647Color:
            self.init(hex: 0x38f647) //#colorLiteral(red: 0.2196078431, green: 0.9647058824, blue: 0.2784313725, alpha: 1)
        case .fff2a1a7Color:
            self.init(hex: 0xf2a1a7) //#colorLiteral(red: 0.9490196078, green: 0.6313725490, blue: 0.6549019608, alpha: 1)
        }
    }
}
```
# まとめ
よかったらstarお願いします！
https://github.com/tosh7/SwiftColorConverter