要約
debowerifyを利用している場合は,underscore/Lo-Dash系テンプレートの読み込みにはtemplateifyを使うのがよい.
解決法をGoogleで検索したが,私には見つけられなかったので,私の考えた解決法をここに記述することにした.
状況説明
- browserifyでフロントエンドのCoffeeScriptをコンパイル
- サーバーサイドのサードパーティ・ライブラリはnpmで,フロントエンドのサードパーティ・ライブラリをbowerで管理
- 小さいプロジェクトなので分離せず,1つのプロジェクトとして管理している
- bower管理下のライブラリの読み込みのため,debowerifyを適用
- フロントエンドのテンプレートとして,underscore/Lo-Dash系テンプレートを使用
- そのため,jstifyも適用しようとした
その結果
エラーが発生した...
以下,最小構成での再現手順.
$ cd /path/to/proj
$ npm install bower browerify debowerify jstify
$ node_modules/.bin/bower install underscore
$ echo "var t = require('./t.jst');" > a.js
$ echo -n foobar > t.jst
$ node_modules/.bin/browserify -t debowerify -t jstify a.js
Error: Cannot find module 'underscore' from '/path/to/proj'
at /path/to/proj/node_modules/browserify/node_modules/resolve/lib/async.js:50:17
at process (/path/to/proj/node_modules/browserify/node_modules/resolve/lib/async.js:119:43)
at /path/to/proj/node_modules/browserify/node_modules/resolve/lib/async.js:128:21
at load (/path/to/proj/node_modules/browserify/node_modules/resolve/lib/async.js:60:43)
at /path/to/proj/node_modules/browserify/node_modules/resolve/lib/async.js:66:22
at /path/to/proj/node_modules/browserify/node_modules/resolve/lib/async.js:21:47
at Object.oncomplete (fs.js:107:15)
ちなみに,echo "var _ = require('underscore');" > a.jsでは問題は発生しない.
原因分析
以下の内容は,browserify/debowerify/jstifyのソースコードをくまなく調べた結果ではないので,誤りが含まれているかもしれない.誤りを見つけた方は,指摘していただけるとありがたい.
まず,jstifyのソースを確認した.変換後のファイルにvar _ = require('underscore');が含まれており,これが見つからなというエラーであることがわかった.
次に,console.logなどでdebowerifyとjstifyの呼び出しログを確認した.
- transformの適用順番と-tパラメーターによるtransformの指定順番の間には関係はないようだ
- transformの名前の辞書順で適用しているように見える
- 今回の場合だと,debowerify jstifyの順番で適用する
- 1つのファイルに対するtransformは実行前に確定
- そのため,再適用されないようだ
- 今回の場合だと,jstify後に再び一連のtransformを適用するということはないようだ
どうにかしてtransformの順番を入れ替えることができれば問題が解決するのではないかと考えたが,transformの先頭で適用対象のファイルを拡張子で選別していることがわかった.
- https://github.com/eugeneware/debowerify/blob/master/index.js#L9
- https://github.com/zertosh/jstify/blob/master/index.js#L25
指定したtransformをファイルにすべて適用するみたいなので,当然と言えば当然か.
browserifyの用途から考えて,入力ファイルを改名する機能はなさそうだ.少し調べただけでは見つからない.
では,どうすればいいのか?
解決法1
templateifyを使う.
$ cd /path/to/proj
$ npm install bower browerify debowerify templateify
$ node_modules/.bin/bower install underscore
$ echo "var t = require('underscore').template(require('./t.template'));" > a.js
$ echo -n foobar > t.jst
$ node_modules/.bin/browserify -t debowerify -t templateify a.js
テンプレートファイルの拡張子は.templateとなる点に注意.
解決法2
bowerを使うのをやめて,フロントエンドのサードパーティ・ライブラリの管理にもnpmを使用する.
サーバーとフロントエンドのプロジェクトが分離されているなら,特に管理上の問題もないので,これでもいい気がする.
解決法3
変換を2回に分けてしまう.
- テンプレートを変換し,一旦結果をJSファイルに出力
- テンプレートのJSファイルを
requireするようにし,これをbrowserifyで変換
ソースファイルに変換後のテンプレートのJSファイル名を書くことになるため,おすすめはできない.
補足
jstify以外のtransform後の結果にrequireを含むようなtransformでは同じ問題が発生する.例えば,browserify-compile-templatesでも同じ問題が発生する.