コマンドラインツールにテンプレート機能を取り入れるための調査と実験
はじめに
Yeoman の generator みたいなテンプレート機能を自前のコマンドラインツール*1に作り込む必要が生じたので調査しました.
結論としては yeoman-environment と mem-fs-editor,inquirer あたりを組み合わせることで容易に実現できました.
Yeomon の調査
今回は Interacting with the file system に記載されている機能の一部 (templates からのファイルコピーと ejs テンプレート処理) が欲しいのでそこらへんを中心に調べます.
ejs によるテンプレート処理については this.fs.copyTpl
を使えば良いことがマニュアルから読み取れます.パッケージは mem-fs-editor のようです.
それからコピー元のパスである templatePath
を解決するための情報については,コードを調べてみると yeoman-environment
がこの辺りの機能を担っていることがわかります.
https://github.com/yeoman/environment/blob/v2.8.0/lib/resolver.js#L42-L76
また,コピー先を決定する destinationPath
は .yo-rc.json
や引数による指定がなければ process.cwd()
から取得しているようです.
https://github.com/yeoman/generator/blob/v4.5.0/lib/index.js#L162-L176
https://github.com/yeoman/generator/blob/v4.5.0/lib/index.js#L831
https://github.com/yeoman/environment/blob/master/lib/environment.js#L209
実験
まずは yeoman-environment
の方から.npm install --save yeoman-environment
して以下のようなコードを書いて実行すると,
// file: /path/to/use-yeoman-environment/index.js const y = require('yeoman-environment'); const env = y.createEnv(); const res = env.lookup({ packagePatterns: 'yeoman-*', filePatterns: '*\/environment.js' }); console.log(res);
$ node index.js [ { generatorPath: '/path/to/use-yeoman-environment/node_modules/yeoman-environment/lib/environment.js', packagePath: '/path/to/use-yeoman-environment/node_modules/yeoman-environment', namespace: 'path:to:use-yeoman-environment:node_modules:yeoman-environment:lib:environment', registered: true } ]
こういう結果が返ってきます.なのでテンプレートデータのコピー元の絶対パス解決には packagePath
を使えば良さそうです.
mem-fs-editor
の方は以下のように利用します.
const memfs = require('mem-fs'); const editor = require('mem-fs-editor'); const fs = editor.create(memfs.create()); const context = { name: 'hoge' }; fs.copyTpl('template/test.json', 'dest/test.json', context); fs.commit(() => console.log('保存した'));
commit
を呼び出すとファイルとして書き出されます.
試作
実際にテンプレートから雛形を生成する試作コマンド proto-cli
を作成します.
proto-cli/ template/ index.js package.json main.js package.json
template/
以下の内容を雛形としてコピーします.template/package.json
の中身は以下の通り.
{ "name": "<%= name %>", "version": "<%= version %>", "description": "<%= description %>", "private": true, "main": "index.js" }
そして main.js
の中身は以下の通り.ユーザー入力は inquirer
でハンドリングします.
#!/usr/bin/env node const yeoman = require('yeoman-environment'); const memfs = require('mem-fs'); const editor = require('mem-fs-editor'); const inquirer = require('inquirer'); const path = require('path'); const fs = editor.create(memfs.create()); const env = yeoman.createEnv(); const res = env.lookup({ packagePatterns: 'proto-cli', filePatterns: 'main.js' }); const templateRoot = path.join(res[0].packagePath, 'template'); const destinationRoot = process.cwd(); const templatePath = file => path.join(templateRoot, file); const destinationPath = file => path.join(destinationRoot, file); const copyTemplate = context => { fs.copyTpl( templatePath('package.json'), destinationPath('package.json'), context ); fs.copy( templatePath('index.js'), destinationPath('index.js') ); fs.commit(() => console.log('initialized')); }; inquirer.prompt([ { name: 'name', message: 'package name', default: path.basename(__dirname) }, { name: 'version', message: 'version', default: '0.1.0' }, { name: 'description', message: 'description' }, { name: 'author', message: 'author' } ]).then(answers => { copyTemplate(answers); });
実際に動くコードはこちら. github.com
npm link
した後,適当なディレクトリの中で proto-cli
を実行すると動作を確認できます.
おわりに
yeoman-environment と mem-fs-editor (+ inquirer) を利用することで期待するテンプレート機能を実装することができました.当初は自前で実装しようと思っていましたが便利な形でパッケージ化されていたため助かりました.
さらにここから commander を組み合わせることで,よく見かける CLI ツールが作成できます.
*1:環境は Node.js