コマンドラインで使えるテンプレートエンジンを探していたのですが、手頃なものとして ejs-cli があったのでこれを採用してみました。ただし、かなり古いツールだったため、近代化を目的にコードを ES6 に対応させました。コマンドラインで使用する際、JSON を手書きするのはつらいので、YAML や環境変数からもデータを読み込めるようにしました。また、JSON に構文エラーがある場合には、より詳細なエラーメッセージが表示されるよう改良しています。

ejs-cli

GitHub で公開しています。オリジナルのコードが跡形もなくなってしまったので、勝手にバージョン 2.3.0 を名乗っています。

動機

コマンドラインで使える手軽なテンプレートエンジンを探していました。。最もシンプルなのは envsubst ですが、単純な変数置換のみで条件分岐などが書けません。HTML 向けではありますが、本格的なテンプレートエンジンの中では EJS が比較的手軽に使えます。ejs-cli をインストールしてみたところ、以下のような warning が出力されました。

$ sudo npm install -g ejs-cli
Password:
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported

added 38 packages in 4s

4 packages are looking for funding
  run `npm fund` for details
$ 

長期的に使いたかったため、これらの warning を解消することにしました。

Enhanced バージョンを名乗る @wcj/ejs-cli というモジュールもあるのですが、こちらは実質的に別物です。HTML 特化型で、テンプレートから JSON の変数を指定する際に必ず GLOBAL. を前置する必要があるなど、手軽さが失われています。EJS 自身も CLI を持っているのですが、こちらも方向性が異なるように感じます。そうした理由から、今でも ejs-cli が使われ続けているのではないかと思います。

ES6 に対応する

async モジュールへの依存解消

オリジナルは async モジュールを使って書かれていて、コールバック地獄です。async/await で書き直しました。というか、こういう作業は ChatGPT に読ませて「書き直して」と頼めばやってくれます。だいぶすっきりして読みやすくなりました。

chalk, mkdirp, yargs への依存解消

Node.js の標準モジュールでこれらの機能は代替可能なため、使用を停止しました。Node.js 22.x 以降の使用が必須になりますが、この方がおそらく将来的に安定して使えるでしょう。glob も Node.js に取り込まれそう(現在は experimental)なので、stable になったらこちらも依存解消する予定です。

機能の整理・拡充

新しいオプションスイッチを追加しました。

  1. -j/--json FILE
  2. -y/--yaml FILE
  3. --env
  4. -s/--string STRING
  5. -z, --config FILE

-O/--options オプションは機能が複雑なため、非推奨(deprecated)にしました(まだ使えます)。以下に記載する単機能のオプションに分割しています。オリジナルの ejs-cli がテンプレートに渡す data のことを options と呼んでいる理由は不明ですが、紛らわしいので data で統一しています。

-j/--json オプションはその名の通り、JSON ファイルを data として読み込みます。

$ ejs-cli template.ejs --json data.json > output.html

JSON は手で打ち込むには面倒なので、YAML も読み込めるようにしました(-y/--yaml オプション)。

$ ejs-cli template.ejs --yaml data.yaml > output.html

ちょっとしたスクリプトに組み込むには、ヒアドキュメントにできると便利です。上記 2つのオプションは、- を指定して、標準入力から読み込むこともできます。

ejs-cli template.ejs --yaml - << EOF
title: hello world
tags: 
  - multiple
  - keywords
  - here
EOF

コマンドラインとの親和性を高めるために、環境変数も data として使えるようにしました。--env を指定すると、以下のように使用できます。

$ STAMP=`date +%Y-%m-%d` ejs-cli template.ejs --env

オリジナルでサポートされていた、コマンドラインで直接 JSON を書く方法(--string オプション)も引き続き使用可能です(個人的にはあまり実用性を感じませんが)。

JSON に構文エラーがある場合、オリジナルでは単に Fail to parse JSON としか表示されませんでしたが、 現在はパーサが返す詳細なエラーメッセージを表示します。

$ cat | ejs-cli template.ejs -j -
{
  "a": 1
  "b": 2
}
^D
Fail to parse JSON: Expected ',' or '}' after property value in JSON at position 13 (line 3 column 3)
$ 

YAML も同様に、構文エラーがあればパーサのエラー出力を表示します。

$ cat | ejs-cli example/test1.ejs -y -
bad: yaml:
^D
Fail to parse YAML: bad indentation of a mapping entry (1:10)

 1 | bad: yaml:
--------------^
$ 

これにより、エラーが発生しても原因がすぐにわかります。

-z/--config オプションは EJS に渡すオプションパラメータが書かれたファイルを指定します。オプションパラメータは JSON で作成します。設定できる内容は、公式ドキュメントを参照してください。以下のようなファイルを作成して、適当なファイル名で保存しておきます(ここでは options.json)。

{
  "openDelimiter": "{",
  "closeDelimiter": "}"
}

この例では、EJS のテンプレートの開始・終了記号を変更しています。以下のように指定します。

$ ejs-cli template.ejs -j data.json -z options.json
...
$ 

この機能は使用頻度は高くないと想定しているので、「一応使える」程度の実装です。

注意点

-e/--exclude の指定方法が変更されました。オリジナルは文字列比較を使った独自実装でしたが、現在は glob モジュールに処理を委ねています。以下に使用例を示します。

$ ejs-cli --base-dir src/ "**/*.ejs" --exclude "**/partials/**" --out dest/ --yaml data.yaml

おわりに

私はこのテンプレートエンジンを、コマンドラインでちょっとしたテキストファイルを生成する目的で使っています。ご興味があれば、ぜひお試しください。

Buy me a coffee によるご支援も歓迎しております。