今までコンポーネントの雛形やスニペットは、
VSCodeのUser Snippetsを使ってたけど、
複数ファイルの生成やリッチなプロンプト、Gitでの管理などは難しい。。
そのあたりの問題を解決してくれる
リッチなコード生成ツールを見つけたので、試してみたときの備忘録(*´ω`*)
インストール
npmやHomebrewでインストール
# npm $ npm i -g hygen # or $ npx hygen ... # Homebrew $ brew tap jondot/tap $ brew install hygen
とりあえず実行(Quick Start)
# 用意されてる初期設定用のテンプレートを実行 $ npx hygen init self # ./_templatesに作成される $ tree _templates/ _templates/ ├── generator │ ├── help │ │ └── index.ejs.t │ ├── new │ │ └── hello.ejs.t │ └── with-prompt │ ├── hello.ejs.t │ └── prompt.ejs.t └── init └── repo └── new-repo.ejs.t
実行時のオプションとテンプレートディレクトリの構成
実行は、GENERATOR
とACTION
(と、追加のデータdata-options
)を指定するだけ。
$ npx hygen --help Usage: hygen [option] GENERATOR ACTION [--name NAME] [data-options] Options: -h, --help # Show this message and quit --dry # Perform a dry run. Files will be generated but not saved.
テンプレートディレクトリの構成はこんな感じになっているので、
指定した_templates/<GENERATOR>/<ACTION>
にあるファイルが生成される。
_templates/ └── <GENERATOR> └── <ACTION> └── index.ejs.t
テンプレートファイルの構成(.ejs.t
)
テンプレートファイル(.ejs.t
)は「frontmatter」と「body」の2部構成。
対象のディレクトリにあるすべての.ejs.t
が生成される。
--- <----- frontmatter section to: app/emails/<%= name %>.html --- Hello <%= name %>, <%= message %> <----- body, ejs (version <%= version %>)
frontmatter部分
指定した変数がbody部分のパラメタとして渡される。
いくつか特別はプロパティがあり、
to:
はファイルを生成する配置先を指定するプロパティ。
body部分
こっちがテンプレートの本体。
EJS(Embedded JavaScript templates)を使っていて、この書き方に従っている。
インタラクティブなコード生成(prompt.js)
.ejs.t
だけだと、即コード生成してくれるけど、
指定するパラメタを忘れるので、対話的にしたい。
その場合は、prompt.js
を配置すればOK
(index.[js|ts]
やprompt.[js|ts|cjs]
でもOK)
(tsの場合はts-node
が必要)
- hygen/prompt.ts at v6.2.11 · jondot/hygen
_templates/ └── init └── new ├── hello.ejs.t └── prompt.js
// prompt.js module.exports = [ { type: "input", name: "message", message: "What's your message?", }, ];
// hello.ejs.t --- to: hello.js --- console.log("<%= message %>")
実行するとこんな感じで対話的にできる。
$ npx hygen init new ✔ What's your message? · hello Loaded templates: _templates added: hello.js $ cat hello.js console.log("hello")
プロンプト自体は、Enquirerが使われているので、
Prompt Options | Enquirerをみるとよい。
より高度なプロンプト
prompter
を使って、回答に応じた追加の質問とかもできる。
// prompt.js module.exports = { prompt: ({ prompter, args }) => prompter .prompt({ type: "input", name: "email", message: "What's your email?", }) .then(({ email }) => prompter.prompt({ type: "input", name: "emailConfirmation", message: `Please type your email [${email}] again:`, }) ), };
prompt:
の返り値がbody部分(ejs)で使えるパラメタになるので、
パラメタの追加や加工がしたい場合は、ここでできる。
// prompt.ts module.exports = { prompt: ({ prompter, args }) => { return prompter .prompt({ type: "input", name: "email", message: "What's your email?", }) .then((answer) => { return { ...answer, addParam: "ex-param" }; }); }, };
おまけ
モノレポでも楽にするプロンプト
モノレポのときは、複数配置先があるけど、
いろいろ選びたくないなと思ったけど、
ディレクトリを読み込んで、プロンプトを構成するといい感じらしい。
. └── src ├── apps │ ├── customer │ ├── notification │ ├── order │ ├── payment │ └── product ├── clients │ └── frontend └── packages ├── payment-clients ├── test-data └── test-utils
const fs = require('fs'); const exclude = ['things-to-ignore', '.DS_Store']; const dirs = fs.readdirSync('./src/apps'); const apps = dirs.reduce((acc, d) => { if (!exclude.includes(d)) acc.push({ name: d, value: d }); return acc; }, []); module.exports = [ { type: 'input', name: 'name', message: 'Package name', }, { type: 'multiselect', name: 'apps', message: 'Which App to add to?', limit: 5, choices: apps, }, ];
以上!! これでテンプレートも柔軟でバージョン管理できる。。(*´ω`*)