タイトルではbatファイルと書かれていますが、実際に作成されるのはcmdファイルです。Windows環境の話です。
package.jsonでbin属性が指定されたパッケージをnpm installすると、node_modules/.bin/フォルダの中にcmdファイルが作られ、シェルからコマンドを実行する事が出来る。gulpやhttp-serverコマンドの様に。
それを真似して作ってみたが、作成されたcmdファイルの中身は以下のような内容になった。このコマンドを実行しても、jsファイルはnodeで実行されない。
@"%~dp0\..\command\xxxx.js" %*
npmに公開されている既存のパッケージをgit cloneし、npm installすると以下のcmdファイルが作成される。これは想定通り。
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\command\xxxx.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\command\xxxx.js" %*
)
この違いがどこから生じるのか確認した。このcmdファイルはnpm install時に作成されるので、npmコマンドのディレクトリをIF EXIST
でgrep。以下の箇所でcmdファイルの中身が作り分けられていると確認出来た。
var cmd
if (longProg) {
cmd = "@IF EXIST " + longProg + " (\r\n"
+ " " + longProg + " " + args + " " + target + " %*\r\n"
+ ") ELSE (\r\n"
+ " @SETLOCAL\r\n"
+ " @SET PATHEXT=%PATHEXT:;.JS;=;%\r\n"
+ " " + prog + " " + args + " " + target + " %*\r\n"
+ ")"
} else {
cmd = "@" + prog + " " + args + " " + target + " %*\r\n"
}
githubではここ になる。
この処理のlongProg変数を遡ると、同ファイルの以下の処理に辿り着く。
ファイルを実際に読み込み、1行目が正規表現にマッチしているかで分岐している。
var mkdir = require("mkdirp")
, path = require("path")
, shebangExpr = /^#\!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+)(.*)$/
//略
fs.readFile(from, "utf8", function (er, data) {
if (er) return writeShim_(from, to, null, null, cb)
var firstLine = data.trim().split(/\r*\n/)[0]
, shebang = firstLine.match(shebangExpr)
if (!shebang) return writeShim_(from, to, null, null, cb)
var prog = shebang[1]
, args = shebang[2] || ""
return writeShim_(from, to, prog, args, cb)
})
よって、package.jsonのbinコマンドで指定されているjsファイルの1行目に以下の記述を加えると、node経由でjsファイルが実行されるcmdファイルが作成される
# !/usr/bin/env node
調べたあとに、公式ドキュメントに書いてある事がわかった。母国語で書いていただかないと・・・