Node.js スクリプトをコマンドのように使えるようにする方法
2019年、あけましておめでとうございます。今年も宜しくお願い致します。
今日は、Node.js で書いたちょっとしたスクリプトを、コマンドとして使えるようにする手順をまとめる。大きく2種類のやり方があるのでそれぞれ紹介する。
試した環境は macOS Mojave・Node.js v10.7.0・npm v6.1.0。
目次
外部パッケージに依存せず単一の JS ファイルで済む場合
依存モジュールがなく、単一の JS ファイルで済むようなコードであれば、そのファイルを PATH が通っているところに置くだけでコマンド化できる。
先にコード全量
# PATH が通っているところならどこでも
$ cd /usr/local/bin/
# サンプルスクリプトを作る。Shebang 重要
$ cat << EOF > my_node_command
> #!/usr/bin/env node
> console.log(process.cwd());
> EOF
# コマンドを実行したカレントディレクトリを表示するだけのコード
# 実行権を付与する
$ chmod +x my_node_command
# 好きなところに移動して使う
$ cd ~/work/
# 実行
$ my_node_command
/Users/Neos21/work
# コマンドを実行したカレントディレクトリが表示される
以下、もう少し個別に詳細を説明する。
- 参考 : bashのヒアドキュメントを活用する - Qiita … 毎回
$ cat << EOF > output.txt
な書き方忘れる
PATH が通っているところにスクリプトファイルを置く
コマンドとして実行するには、環境変数 PATH
が通っているディレクトリに、拡張子なしのファイルとしてスクリプトを置く必要がある。
上述の例では /usr/local/bin/
という、大抵 PATH
に設定されているディレクトリを選択したが、その他にインストールしたファイル群と混ざりそうなので、僕はユーザホーム直下に ~/bin/
というディレクトリを作り、コレを ~/.bash_profile
にて環境変数 PATH
に追加して利用している。
Node.js スクリプトとして動作させるための Shebang を設定する
Node.js スクリプトの Shebang は以下のとおり。
#!/usr/bin/env node
コレがないと Node.js 製のスクリプトとして解釈してもらえない。
スクリプトを書く
スクリプトファイルの1行目に Shebang を書いたら、後はスクリプトを書くだけ。
外部モジュールは、fs
や path
など、Node.js 本体に組み込みのモジュールであれば require()
して利用できた。それ以外の npm install
して使えるようなパッケージは、グローバルインストールしてあっても使うことはできない。外部 npm パッケージを使いたい場合は、後述する方法で実現できる。
さて、一例だが、こんなコードならスクリプトファイル単体で動かせた。
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const util = require('util');
// fs.readFile() を Promise 化する
const readFile = util.promisify(fs.readFile);
console.log('カレントディレクトリに package.json があったらその中身を出力する');
readFile(path.join(process.cwd(), 'package.json'), 'UTF-8')
.then((packageJson) => {
console.log(packageJson);
})
.catch((error) => {
console.error(error);
});
Util.promisify()
なる便利なヤーツが Node.js に組み込まれていたので、fs.readFile()
をコレで Promise 化した。
__dirname
だと、スクリプトファイルの格納場所が返されてしまうので、スクリプトファイルを呼び出したカレントディレクトリを取得するには、process.cwd()
を使っている。
ファイルに実行権を付与する
先程のようなスクリプトを my_node_command
というファイル名で保存したとして、コレをコマンドのように実行できるようにするには、chmod
コマンドによるファイルのアクセス権限の変更が必要。実行権限を付けないといけない。
$ chmod +x my_node_command
コレで OK。Node.js スクリプトとして JS ファイル単体で済ませられるモノなら、このようなやり方でコマンド化できた。
外部モジュールに依存する Node.js スクリプトをコマンド化する場合
外部モジュールに依存する Node.js スクリプトをコマンド化したい場合は、npm パッケージをローカルで作り、それをグローバルインストールすることで実現できる。
まずは適当な作業ディレクトリを作り $ npm init
。package.json
が生成されるので、$ npm install --save
で好きなライブラリをインストールし、それを使用したスクリプトを作っていく。
最近のトレンドだと、CLI ベースで動作するスクリプトは、プロジェクトルートに cli.js
というファイル名で置き、package.json
からは以下のようにコマンド名を定義してやるのが多い。
{
"name": "neo",
"version": "1.0.0",
"description": "Neo's Commands",
"main": "./lib/index.js",
"bin": {
"neo": "./cli.js" // 「neo」コマンドで動作するようにする
},
"dependencies": {
// 依存パッケージ…
}
}
cli.js
の1行目は #!/usr/bin/env node
という Shebang で始めること。
で、こんな風に作ったスクリプトを $ neo
コマンドで実行できるようにするには、この作業ディレクトリで $ npm install -g
する。
package.json
があるディレクトリで npm i -g
とすると、そのディレクトリ配下の資材がグローバルインストールされる。MacOS で試した限りは、資材がコピーされるのではなくこのディレクトリへのエイリアスが貼られるので、一度グローバルインストールさせた後にスクリプトを書き換えたり、別のライブラリをローカルインストールしたりしても、npm install -g
で登録し直す必要はなく、即座に変更が反映される。
# こんな作業ディレクトリだとする
$ pwd
/Users/Neos21/work/neo
# グローバルインストールすると
$ npm install -g
/usr/local/bin/neo -> /usr/local/lib/node_modules/neo/bin/neo
+ neo@1.0.0
updated 1 package in 0.225s
# 確かにエイリアスが貼られていることが分かる
$ ls -l /usr/local/lib/node_modules/
total 0
lrwxr-xr-x 1 Neos21 admin 45 11 30 11:03 neo -> ../../../../Users/Neos21/work/neo
drwxr-xr-x 25 Neos21 admin 800 7 23 16:21 npm
エイリアスになるので、「作業ディレクトリ」とは言いながらも、ファイルを消したりやたらと移動させたりしないよう注意。
この作業ディレクトリを Git 管理しておいて、PC 環境が変わった時は git clone
→ npm install
→ npm install -g
としてやれば、元の環境が復元できそうだ。
- 参考 : A Guide to Creating a NodeJS Command-Line Package
npm link
でも良いのかしら