Fractalで作るスタイルガイドでcomponent単位にファイルを書き出す

## はじめに
最近わたしが所属するWeb制作チームでスタイルガイドが導入されました。
デザイナーとエンジニアが一緒にWebサイトを制作するにあたって、よく使うコンポーネントがスタイルガイドにまとめてあると、共通の作業が効率化できて非常に便利です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/162655/67cba012-024e-a11e-fdfd-44badf0ed003.png" width="500px" alt="fractal-demo-screenshot" title="fractal-demo-screenshot">
私のチームでは、スタイルガイドを[Fractal](https://fractal.build/)というフレームワークで構築しました。
この記事では、その構築時に培った知見を少しだけ紹介します。

## この記事では
FractalのCLIで引数として入力したcomponentのhbsファイルを、一つのHTMLファイルにレンダリングするスクリプトを紹介します。

## 前提
- すでにFractalを用いたスタイルガイドの枠組みを作っている or 理解している<br>
（まだの方はこちらへGO 👉 [Fractal - Getting Started](https://fractal.build/guide/getting-started)）

## ソースコード

```js:fractal.js
const fs = require('fs');
const path = require('path');

// To create new dir func if there is nothing on the dirPath
let makeDir = (dirPath) => { if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath); } };
makeDir('./exports');

// Custom command func for export
function exportComponents(args, done) {
  const app = this.fractal;
  const target = args.component;

  let targetItem;
  const componentList = [];
  for (let item of app.components.flatten()) { 
    componentList.push(item.handle);
    if (item.handle === target) targetItem = item;
  }

  if (targetItem) {
    targetItem.render(null, null, {
      preview: true
    }).then(function (html) {
      const filePath = path.join('./exports/', args.options.output || '', `${targetItem.handle}.html`);
      fs.writeFile(filePath, html, function (err) {
        if (err) app.cli.console.error(`Error exporting ${args.component} - ${err.message}`);
        else app.cli.console.success(`Component ${args.component} exported to ${filePath}`);
        done();
      });
    });
  } else {
    app.cli.console.error(`Component ${args.component} not found`);
  }
}

// Set custom export command func to Fractal CLI
fractal.cli.command('export <component>', exportComponents, {
  description: 'export a component',
  options: [
    ['-l, --layout', 'export the component within it\'s preview layout.'],
    ['-o, --output <output-dir>', 'The directory to export the component into, relative to the CWD.'],
  ]
});
```

```json:package.json
{
  "name": "fractal-demo",
  "version": "1.0.0",
  "scripts": {
    "export": "fractal export"
  }
...
}
```

### 実行例
```console:console
$ npm run export button

> fractal-demo@1.0.0 export /Users/username/GitHub/fractal-demo
> fractal export "button"

✔ Component button exported to export/01-ui-parts/button/01-button/index.html
```

### 実行結果
HTMLファイルが書き出されました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/162655/3a90d61c-ec64-2e4a-c865-e646c04f93a0.png" width="500px">

### 参考サイト

- [Github issue - Possibility to export components only as rendered HTML files](https://github.com/frctl/fractal/issues/140#issuecomment-254654538)
- [Github issue - Specify target components to Builder](https://github.com/frctl/fractal/issues/169#issuecomment-256166317)

## なぜやったか
ひとえに、必要なコンポーネントのHTMLファイルを一つ一つ切り出して使いたかったからです。
公式で用意されたレンダリング機能は、テンプレートエンジン[Handlebars](https://handlebarsjs.com/)を基にした独自のテンプレートの構造で書き出されてしまうため、コンポーネント単位で一つのHTMLを取得するには、上記スクリプトのように[Custom commands](https://fractal.build/guide/cli/custom-commands)で機能を自作する必要がありました。

ちなみに現在は未実装ですが、今後公式で同様のレンダリング機能（[buildメソッド](https://fractal.build/api/entities/builder)等）が追加されるかもしれません。

## おまけ: スクリプトの応用

CLIツール開発ができる[PLOP](https://plopjs.com)や[oclif](https://oclif.io/)などと組み合わせると、例えば以下の機能が拡張できて便利です。

1. レンダリングしたいコンポーネントを対話的に選択できるようになる
2. HTMLの関連ファイルも同時に取得できるようになる

### 実行例
```console:console
$ npm run export

> fractal-demo@1.0.0 export /Users/username/GitHub/fractal-demo
> plop export

? Which component?: 01-ui-parts
❯ 01-ui-parts
  02-header-parts
  03-footer-parts
  04-praph-parts
? What new component category?: button
❯ button
  title
  sub-title
  navbar
  register
[SUCCESS] function Exported: 01-ui-parts < button
```

### 実行結果
この例では、選択したコンポーネントのHTMLファイルとその関連ファイルを、コンポーネントごとのフォルダに出力するようにしました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/162655/29e25bfe-672d-c148-35a3-1de2e8766f35.png" width="800px">

Node.jsのCLIツール開発は他にも[Vorpal](http://vorpal.js.org/)や[Commander](https://github.com/tj/commander.js/)、[Ginit](https://github.com/sitepoint-editors/ginit)など色々ありますので、好みのものを選ぶといいと思いますよ。