🏭

PLOPでComponentファイルを雛形から生成しよう

2022/06/30に公開
2

こんにちはmofmofでエンジニアをしているshwldです。

Componentを作るときに、storybook, jest, graphqlなどのファイルをひとまとめにして作ることが増えてきました。

└ components
  └ todo
    └ TodoCreateForm
       ├ index.tsx
       ├ TodoCreateForm.graphql
       ├ TodoCreateForm.stories.tsx
       ├ TodoCreateForm.test.tsx
       ├ TodoCreateForm.tsx

これを解消するためにHygenを使っていたのですが、PLOPのほうが好きだなと感じたので共有します。

PLOPでコンポーネントを作る体験

> yarn plop component
...
? src/components/{path please}
? component name please

? の質問に回答していくとコンポーネントが作成されます。

todo
TodoDeleteButton

と回答すると、

✔  ++ /src/components/todo/TodoDeleteButton/index.tsx
✔  ++ /src/components/todo/TodoDeleteButton/TodoDeleteButton.tsx
✔  ++ /src/components/todo/TodoDeleteButton/TodoDeleteButton.stories.tsx
✔  ++ /src/components/todo/TodoDeleteButton/TodoDeleteButton.test.tsx
✔  ++ /src/components/todo/TodoDeleteButton/TodoDeleteButton.graphql

このように生成されます。

この雛形を作るための設定

propfile.jsでpromptとテンプレートの定義を行います。

/plopfile.js
module.exports = function (
  /** @type {import('plop').NodePlopAPI} */
  plop
) {
  plop.setGenerator('component', {
    description: 'react component',
    prompts: [
      // 入力させたい値につけたnameをactionsやtemplate内で参照できます
      {
        type: 'input',
        name: 'parentPath',
        message: 'src/components/{path please}',
      },
      {
        type: 'input',
        name: 'name',
        message: 'component name please',
      },
    ],
    actions: [
      {
        type: 'add',
        path: 'src/components/{{parentPath}}/{{name}}/index.tsx',
        templateFile: 'plop-templates/component/index.tsx.hbs',
      },
      {
        type: 'add',
        path: 'src/components/{{parentPath}}/{{name}}/{{name}}.tsx',
        templateFile: 'plop-templates/component/Component.tsx.hbs',
      },
      {
        type: 'add',
        path: 'src/components/{{parentPath}}/{{name}}/{{name}}.stories.tsx',
        templateFile: 'plop-templates/component/Component.stories.tsx.hbs',
      },
      {
        type: 'add',
        path: 'src/components/{{parentPath}}/{{name}}/{{name}}.test.tsx',
        templateFile: 'plop-templates/component/Component.test.tsx.hbs',
      },
      {
        type: 'add',
        path: 'src/components/{{parentPath}}/{{name}}/{{name}}.graphql',
        templateFile: 'plop-templates/component/Component.graphql.hbs',
      },
    ],
  });
};

生成したいファイルの分だけテンプレートファイル(.hbs)を作成します。

テンプレート内で{{pascalCase name}}, {{name}}のようにして参照できます。またpascalCaseなどがデフォルトで利用できるようになっているのでよく使います。

/plop-templates/component/index.tsx.hbs
export * from './{{name}}';
/plop-templates/component/Component.tsx.hbs
import { ReactNode, VFC } from 'react';

export const {{pascalCase name}}: VFC<{ children?: ReactNode }> = () => {
  return (
    <div></div>
  );
};
/plop-templates/component/Component.stories.tsx.hbs
// 略
/plop-templates/component/Component.test.tsx.hbs
// 略
/plop-templates/component/Component.graphql.hbs
// 略

好きなところ

  • テンプレートと生成の設定が完全に分かれている
  • promptで定義したnameをテンプレートと合わせるだけなので直感的に使える。生成のために書くコードが最小

TIPS

フォルダを選択させる

/propfile.js
const fs = require('fs');

const features = fs
  .readdirSync('src/features')
  .map((it) => ({ name: it, value: it }));

module.exports = function (
  /** @type {import('plop').NodePlopAPI} */
  plop
) {
  plop.setGenerator('query', {
    description: 'graphql query',
    prompts: [
      {
        type: 'list',
        name: 'name',
        message: 'feature name please',
        choices: features,
      },
    ],
    ...

このようにpromptで選択できる

? feature name please (Use arrow keys)
❯ todo
  account

index.ts等のファイルにexportだけ追記したい

└ components
  └ Hoge
     ├ index.tsx
     ├ Button.tsx
     ├ Card.tsx
     ├ ListItem.tsx

上記のようなフォルダで

/components/Hoge/index.tsx
export * from 'Button.tsx'
export * from 'Card.tsx'
export * from 'ListItem.tsx'

のようなindex.tsxが存在しているとき。

Buttonを生成したら、index.tsxに1行書き加えてもらいたいときありますよね。
そのようなときは、actionsにappendを書きます

propfile.js
    ...
    actions: [
      {
        type: 'add',
        path: 'src/components/{{module}}/{{name}}.tsx',
        templateFile: 'plop-templates/component.tsx.hbs',
      },
      {
        type: 'append',
        path: 'src/components/{{module}}/index.tsx',
        template: "export * from './{{name}}';",
      },
    ]
    ...

まとめ

使いやすい!おすすめです

Discussion

masakinihirotamasakinihirota
    templateFile: "plop-templates/component/Component.graphql.hbs",
    templateFile: "plop-templates/component/Component.stories.tsx.hbs",
    templateFile: "plop-templates/component/Component.test.tsx.hbs",
    templateFile: "plop-templates/component/Component.tsx.hbs",
    templateFile: "plop-templates/component/index.tsx.hbs",

この記事はテンプレートファイルを5つ用意する必要があるが、初めて自動生成ツールを使う自分にとってはもう少しわかりやすく書いてほしかった、わかってしまえば簡単なツールだと思う。

shwldshwld

コメントありがとうございます。
templateFileがファイル数分必要な点や全体的に説明が足りてなかったです。

取り急ぎ、必要なファイルが5つあることを追記しました。