hygenで分岐する選択肢を作成する

昨日、hygenに関する説明としてこちらの記事を作成しました。
https://qiita.com/ikea_shark_jk/items/7cf561af4a107b079d65

その際、`_template/<command>/index.js`についてはなくても問題ないとして省略したんですが、流石にサンプルで使っておいて説明しないのはどうなのかと一晩経って思い直したので、今回index.jsを用いた分岐選択肢を作成するまでの流れを解説したいと思います。

## 作成する分岐のフローチャート

今回は、こちらのフローチャートに応じた質問を生成します。

```mermaid
graph TB
  start([hygen component new])
  start --> jsorts[Javascript or Typescript]
  jsorts --> isNextPage{Is NextPage}
  isNextPage --> |true| useappdir{Is this app dir}
  useappdir -->|No| apiorpage[API　or　Page]
  useappdir --> |Yes| dirpath[what　is　path]
  apiorpage --> dirpath
  dirpath --> genpage([generate　page　or　api])
  isNextPage --> |false| atomic[which　is　atomic　design　category]
  atomic --> componentname[what　is　component　name?]
  componentname --> gencomponent([generate component])
```

## index.jsで小さな設問を書いてみる

まずは、`index.js`でちょっとした質問ができるようにします。
前回の記事でprompt.jsによって設定した、選択肢形式の設問を設置します。

```js
module.exports = {
  prompt: ({ prompter }) => {
    return prompter
      .prompt({
        type: "select",
        name: "user_name",
        message: "What's your name?",
        choices: ["foo", "bar", "baz", "ikea_shark"],
      })
      .then(({ user_name }) => {
        return { user_name };
      });
  },
};
```

こんな感じのPromiseみを感じる記述をします。
thenの引数は、nameの値です。

```
$ pnpm hygen sample new
? What's your name? … 
❯ foo
  bar
  baz
  ikea_shark
Loaded templates: _templates
       added: app/hello.js
```

単一の設問であれば、こうして作成できます。

次に、2つ以上の質問があるケースを記述します。
prompter.promptを無限に羅列しても良いですが、複数の設問のために`inquirer.prompt`というものが用意されているのでこちらを使用します。

prompter.promptの引数が連想配列なのに対し、inquirer.promptはその連想配列の配列を渡すことで実現できます。
質問順序は、配列の順序に則します。

```js
module.exports = {
  prompt: ({ inquirer }) => {
    return inquirer
      .prompt([
        {
          type: "select",
          name: "user_name",
          message: "What's your name?",
          choices: ["foo", "bar", "baz", "ikea_shark"],
        },
        {
          type: "select",
          name: "js_or_ts",
          message: "Javascript or Typescript",
          choices: ["Javascript", "Typescript"],
        },
      ])
      .then(({ user_name, js_or_ts }) => {
        return { user_name, js_or_ts };
      });
  },
};

```


```
$ pnpm hygen sample new
✔ What's your name? · ikea_shark
✔ Javascript or Typescript · Typescript

Loaded templates: _templates
       added: app/hello.js
```

これで、質問を複数設置できました。

もし、設問と設問の間に処理を挟みたい場合は、最後のreturnで値ではなくprompter.promptを渡すことで実現できます。

```js
module.exports = {
  prompt: ({ prompter }) => {
    return prompter
      .prompt({
        type: "select",
        name: "user_name",
        message: "What's your name?",
        choices: ["foo", "bar", "baz", "ikea_shark"],
      })
      .then(({ user_name }) => {
        return prompter.prompt({
          type: "select",
          name: "js_or_user",
          message: `Javascript or ${user_name}`,
          choices: ["Javascript", user_name],
        })
        .then(({ js_or_user }) => {
          return { user_name, js_or_user }
        });
      });
  },
};

```


```
$ pnpm hygen sample new
✔ What's your name? · ikea_shark
✔ Javascript or ikea_shark · ikea_shark

Loaded templates: _templates
       added: app/hello.js
```

これで一通りの書き方がわかりました。
あとは、これらのルールに従ってフローチャートに合わせて記述すれば完成です！

```_template/component/new/index.js
module.exports = {
  prompt: ({ prompter, inquirer }) => {
    return inquirer
      .prompt([
        {
          type: "select",
          name: "js_or_ts",
          message: "Javascript or Typescript",
          choices: ["Javascript", "Typescript"],
        },
        {
          type: "confirm",
          name: "is_next_page",
          message: "Is NextPage",
        },
      ])
      .then(({ js_or_ts, is_next_page }) => {
        if (is_next_page) {
          return prompter
            .prompt({
              type: "confirm",
              name: "use_app_dir",
              message: "Is this app dir",
            })
            .then(({ use_app_dir }) => {
              const ask_api_or_page = {
                type: "select",
                name: "api_or_page",
                message: "API or Page",
                choices: ["API", "Page"],
              };
              const ask_dir_path = {
                type: "input",
                name: "dir_path",
                message: "what is path",
              };
              return use_app_dir
                ? prompter.prompt(ask_dir_path).then(({ dir_path }) => {
                    return {
                      api_or_page: null,
                      dir_path,
                      use_app_dir,
                      js_or_ts,
                      is_next_page,
                      atomic: null,
                      component_name: null,
                    };
                  })
                : inquirer
                    .prompt([ask_api_or_page, ask_dir_path])
                    .then(({ api_or_page, dir_path }) => {
                      return {
                        api_or_page,
                        dir_path,
                        use_app_dir,
                        js_or_ts,
                        is_next_page,
                        atomic: null,
                        component_name: null,
                      };
                    });
            });
        } else {
          return inquirer
            .prompt([
              {
                type: "select",
                name: "atomic",
                message: "what is atomic design category",
                choices: ["atoms", "molecules", "organisms", "templates"],
              },
              {
                type: "input",
                name: "component_name",
                message: "what is component name",
              },
            ])
            .then(({ atomic, component_name }) => {
              return {
                atomic,
                component_name,
                js_or_ts,
                is_next_page,
                api_or_page: null,
                dir_path: null,
                use_app_dir: null,
              };
            });
        }
      });
  },
};

```

実行するとこうなります。

```
$ pnpm hygen component new
✔ Javascript or Typescript · Typescript
✔ Is NextPage (y/N) · false
✔ what is atomic design category · atoms
✔ what is component name · something_component

Loaded templates: _templates
```

## 生成するファイルを選択する

分岐を好きに弄れるようになりましたが、これだけではまだ不足です。
今のままだとファイルの中身は好きにいじれますが、ファイルの生成そのものを操作できません。
なので、任意のファイルを生成する方法を解説します。


とはいったものの、方法はとても簡単です。

```
---
to: "<%= is_next_page ? `app/hello.js` : null  %>"
---
```

こうやって、テンプレートの`to`にnullを渡してあげるとファイルの生成を阻止できます。
これで、分岐内容に応じて好きにファイルを生成できるようになりますね。

注意点として、生成するしないに関係なくテンプレートとなるファイルに`index.js`で返していないkeyが存在する場合はエラーを吐きます。
なので、分岐上にその値が存在しなかったとしても、nullでいいので何かしら値を当てておきましょう。

## 終わりに

以上で分岐を持つhygenの解説を終わります。
あまりゴリゴリ分岐を増やすとファイルが見にくくなるので、その辺りは自分の心かチームに確認して程よい感じに留めておきましょう。