LoginSignup
5
3

More than 3 years have passed since last update.

CLI のテンプレートプロジェクト by node and TypeScript

Posted at

node で CLI(Command Line Interface) を開発する機会が数回あって、せっかくなのでテンプレートプロジェクトとしてまとめてみた。

テンプレートプロジェクト

必要なモノ

  • nodejs: v11.13.0+
  • typescript: v3.7.3+

試し方

  1. 上記のリポジトリを Clone する
  2. リポジトリのディレクトリに cd して npm ci する
  3. npm run build する
  4. npm link する
  5. source ~/.bash_profile を行うかまたはターミナルを再起動する

これでどのディレクトリでも my-great コマンドが使用できるようになる。

$ my-great hello -f Echizen -s Ooka -a 42

Hello Echizen Ooka.
You're 42 years old.
$ my-great something wrong param

Command Line Interface for My great service

  Sample for CLI.

Commands

  my-great hello -f <first_name> -s <second_name>   Say Hello.
  my-great version                                  Show version.

アンインストール

npm uninstall -g @amay/my-great-cli

要点

コマンドライン引数の解析と使用方法の表示

yargs とかいろいろあったけど、自分的に使いやすかったのでこれを選択。

プログラム構成

my-great hello -f <first_name> -s <second_name>   Say Hello.
my-great version                                  Show version.

のように、第一引数を「コマンド」とし、第2引数以降をそのコマンド専用の引数群としたかったので、index.ts で第一引数のみを parse して取得し、コマンド毎に command-xxxx.ts へ委譲している。

command-line-args では commandLineArgs(this.paramDef, { partial: true })partial:true を設定すると、引数定義(paramDef) に存在しない引数があっても無視する。

cli コマンド名

cliコマンド名 my-greatpackage.jsonbin: で指定している。

package.json

{
  "name": "@amay/my-great-cli",
<省略>
  "bin": {
    "my-great": "build/index.js"
  },
<省略>

ビルドされた ./build/index.js を指すように設定している。
ちなみに npm run 経由で node を実行する場合は、引数の前に -- を付ける(例: node ./build/index.js -- version)。

必須引数のチェック

command-line-args では 引数の必須チェックを自力で行わなければならない ようなので、定義体の paramDefrequire: boolean を追加し、パースした実際の引数である XxxxConfigrequire = true な項目が含まれているかをチェックするようにした。

// Valid require params
const requiresNotSetted = this.paramDef
  .filter(x => x.require)
  .filter(x => cfg[x.name] == null)
  .map(x => `--${x.name}`);

if (requiresNotSetted.length > 0) {
  console.log(`Param: ${requiresNotSetted.join(' ')} is required.`);
  console.log(`------------------------------------`);
  this.usage[1].optionList = this.paramDef;
  const usg = commandLineUsage(this.usage)
  console.log(usg);  
  return -1;
}

kebab-case VS camelCase VS snake_case

コマンドの引数は kebab-case がデファクトスタンダードの模様。
command-line-args では commandLineArgs(this.paramDef, { camelCase: true }) とすると、--first-name に渡された引数を、firstName 変数に格納してくれる。

が、前述の必須引数のチェックが(定義体と実体の変数名が異なるため)正しく機能しなくなるので妥協案として snake_case の --first_name を採用している。

コマンドを追加するには

  1. index.tsCommandTypexxxx を増やす
  2. command-xxx.ts(CommandXxxx クラス) を作る
  3. index.tscommandMap に追加する
  4. mainUsage になんか書く

参考

5
3
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
5
3