9
0

スキャフォールディングツール Scaffdog 使ってみた

Last updated at Posted at 2023-12-03

はじめに

本記事はプロもくチャット Adevent Calendar2023の4日目です

スキャフォールディングツールとは

スキャフォールディングツールとは、プログラミングの開発やテストの際に汎用的なコードを生成する際に利用するツールです.

Node.jsにおけるスキャフォールディングツール

npm trands

今回は、scaffdogについてまとめていきます🧑‍💻

scafffdog について

Markdownに記載したテンプレートに従ってファイルを生成するツールです。

ロゴのスカーフつけている犬🐕
かわゆす...😍

scaffdog install 方法

$ npm i -D scaffdog
$ npx scaffdog init

? Please enter a document name. atom

Setup of scaffdog 🐶 is complete!

  ✔ .scaffdog/config.js
  ✔ .scaffdog/atom.md

Now you can do scaffold by running $ scaffdog generate.

Please refer to the following documents and customize it.
https://scaff.dog/docs/templates
package.json
  "scripts": {
    ...
+   "scaffold": "scaffdog generate"
  },

これで、設定 完了 👍

試しに実行

$ npm run scafold

? Please select a document. Atom
? Please select the output destination directory. src
? Please enter any text. test

🐶 Generated 1 file!

     ✔ src/test.md

✨  Done in 6.74s.

scafold 基本記述

ここでは、基本的な記述の内容を記載します。

Attributes

最初に各種設定を入力

.scaffdog/atom.md
---
name: "atom"
root: "src/_components/atom"
output: "." # 
ignore: [] 
questions:
  name: "Please Enter a Component Name..."
  hasReadme:
    confirm: "Is it hab readme? (Default: true)"
    initial: true
---
... 以下に作成したいファイルを記載する
key 説明
name string 実行時に出てくるテンプレート名
root string 生成するテンプレートのrootディレクトリ
output string, string[] 出力先 or 出力先の候補
ignore string, string[] 出力先から除外するディレクトリ
questions object 実行時に表示される質問内容


Questions

Questionには、以下の4つの回答方法があります

  • Free text
  • Boolean
  • Choice
  • Array

Free Text

文字列の値を受け入れるためのプロンプトを呼び出します。
利用例)ファイル名やディレクトリ名など

free text
---
questions:
    name: # 回答内容代入先
        message: "Please Enter a Component Name..." # 質問内容
        initial: "button" # デフォルト回答 (デフォルト値を設定したい場合のみ)

    # デフォルト回答が不要の場合、下記の記載でも可
    name: "Please Enter a Component Name..." # 回答内容代入先 : 質問内容
---

Boolean

Boolean値を受け入れるためのプロンプトを呼び出します。
利用例)ファイルの作成の分岐など

boolean
---
questions:
    hasReadme: # 回答内容代入先
        confirm: "Is it hab readme? (Default: true)" # 質問内容
        initial: true # デフォルト回答 (デフォルト値を設定したい場合のみ)
---

Choice

リストから単一の値を選択するためのプロンプトを呼び出します

choice
---
questions:
    value2: # 回答内容代入先
        message: "Please select a value." # 質問内容
            choices: # 選択項目
                - "A"
                - "B"
                - "C"
            initial: "C" # デフォルト回答 (デフォルト値を設定したい場合のみ)
---

Array

リストから複数の値を選択するためのプロンプトを呼び出します

array
---
questions:
    value1: # 回答内容代入先
        message: "Please select a value." # 質問内容
            multiple: true # 複数選択可能フラグ
            choices: # 選択項目
                - "A"
                - "B"
                - "C"
            initial: ["C"] # デフォルト回答 (デフォルト値を設定したい場合のみ)
---

条件分岐

また下記のように、回答内容によって質問項目を表示させるか分岐させることも可能です

---
questions:
    # Free Text
    name: # 回答内容代入先
        message: "Please Enter a Component Name..." # 質問内容
    test: # 回答内容代入先
        if: contains(inputs.name, 'Form') # 表示内容条件 (この場合 name の回答が"Form"の場合表示される)
        confirm: 'Do you need a test?' # 質問内容

    # Boolean
    hasReadme: # 回答内容代入先
        confirm: "Is it hab readme? (Default: true)" # 質問内容
        initial: true # デフォルト回答
    test: # 回答内容代入先
        if: inputs.hasReadme # 表示内容条件 (この場合 hasReadme の回答が"True"の場合表示される)
        message: "Please Enter a Test Name..." # 質問内容
---

Template Engine, Built-in Helpers, Injection

ここらは書き始めたらキリがないので公式サイトをご覧ください
条件分岐や、ループ処理、ヘルパー関数など様々なものがあります。
気になったらぜひ見に行ってみてください😆

構築例

Vite / React / Chakra UI

ファイルツリー

.
├── .eslintrc.cjs
├── .gitignore
├── .prettierrc
├── .scaffdog
│   ├── Atom.md
│   ├── Organism.md
│   ├── config.js
│   └── templates
│       ├── atom
│       │   ├── component.stories.tsx
│       │   ├── component.tsx
│       │   ├── index.ts
│       │   └── type.ts
│       └── organisms
│           ├── component.stories.tsx
│           ├── component.test.ts
│           ├── component.tsx
│           ├── hooks.ts
│           ├── index.ts
│           └── type.ts
├── .storybook
│   ├── main.ts
│   └── preview.tsx
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── public
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── _components
│   │   ├── atoms
│   │   │   └── TextField
│   │   │       ├── TextField.stories.tsx
│   │   │       ├── TextField.tsx
│   │   │       ├── index.ts
│   │   │       └── type.ts
│   │   └── organisms
│   │       └── Auth
│   │           └── Login
│   │               ├── Login.stories.tsx
│   │               ├── Login.test.ts
│   │               ├── Login.tsx
│   │               ├── hooks.ts
│   │               ├── index.ts
│   │               └── type.ts
│   ├── index.css
│   ├── main.tsx
│   ├── pages
│   ├── constants
│   ├── libs
│   ├── assets
│   ├── theme
│   ├── types
│   ├── utils
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

scaffdog テンプレート

.scaffdog/Organism.md
{{ /* Attributes */ }}
---
name: "Organisms"
root: "src/_components/organisms"
output: "."
ignore: []
questions:
    directory: "Please Enter a Directory Name..."
    name: "Please Enter a Component Name..."
    hasReadme:
        confirm: "Is it hab readme?(Default: true)"
        initial: true
    hasHooks:
      confirm: "Is it hab hooks?(Default: true)"
      initial: true
    hasTest:
      confirm: "Is it hab test?(Default: false)"
      initial: false
---

{{ /* 変数設定 ヘルパー関数 */ }}
# Variables

- name: `{{ inputs.name | pascal }}` {{ /* pascal を用いてパスカルケースに変換 */ }}
- directory: `{{ inputs.directory }}`

{{ /* 以下生成するファイルのパス設定、生成内容の各種テンプレート読み込み */ }}

{{ /* index */ }}
# `{{ directory }}/{{ name }}/index.ts`
```typescript
{{ 'templates/organisms/index.ts' | read }}
```

{{ /* tsx */ }}
# `{{ directory }}/{{ name }}/{{ name }}.tsx`
```typescript
{{ 'templates/organisms/component.tsx' | read }}
```

{{ /* storybook */ }}
# `{{ directory }}/{{ name }}/{{ name }}.stories.tsx`
```typescript
{{ 'templates/organisms/component.stories.tsx' | read }}
```

{{ /* type */ }}
# `{{ directory }}/{{ name }}/type.ts`
```typescript
{{ 'templates/organisms/type.ts' | read }}
```

{{ /* hooks */ }}
# `{{ !inputs.hasHooks && '!' }}{{ directory }}/{{ name }}/hooks.ts`
```typescript
{{ 'templates/organisms/hooks.ts' | read }}
```

{{ /* テスト */ }}
# `{{ !inputs.hasTest && '!' }}{{ directory }}/{{ name }}/{{ name }}.test.ts`
```typescript
{{ 'templates/organisms/component.test.ts' | read }}
```
.scaffdog/templates/organisms/index.ts
export * from './{{ name }}';
.scaffdog/templates/organisms/index.ts
export * from './{{ name }}';
.scaffdog/templates/organisms/component.tsx
import { type {{ name }}Props } from './type';
import { memo } from 'react';

const {{ name }}Component = ({}: {{ name }}Props) => {
    return (
        <>
            {{ name }} Component
        </>
    )
}

const {{ name }} = memo<{{ name }}Props>(({}) => {
    return (
        <{{name}}Component />
    );
});

export default {{ name }}
.scaffdog/templates/organisms/component.stories.tsx
import {type Meta, type StoryObj} from "@storybook/react";
import {{ name }} from './{{ name }}';

const meta: Meta<typeof {{ name }}> = {
    component: {{ name}},
    title: 'Atomic Design/Organisms/{{ directory | pascal }}/{{ name }}',
    tags: ['autodocs'],
    argTypes: {}
}

export default meta;

type Story = StoryObj<typeof meta>;

export const Base: Story = {
    name: 'base',
    args: {}
}
.scaffdog/templates/organisms/type.ts
export type {{ name }}Props = {}
.scaffdog/templates/organisms/hooks.ts
{{ /* 未設定 */ }}
.scaffdog/templates/organisms/component.test.ts
{{ /* 未設定 */ }}

実行動画

まとめ

今回記載していませんが、指定の行に追加などカスタマイズすることもでき、コマンドひとつでテンプレートをもとに複数ファイルが作成することができるので、時間の短縮ができ効率もアップするのでおすすめです:relaxed:

閲覧ありがとうございますした。
記事書くの初なので・・・間違ってる部分とかあればコメントでご指示いただけますと幸いです。


参考サイト

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0