Node.js のモジュール解決はシンボリックリンク解決後を基準にしてる
◆ シンボリックリンク解決後の実体があるパスを基準にモジュールを探してる
◆ --preserve-symlinks(-main) オプションでシンボリックリンクのままのパスを基準にできる
◆ --preserve-symlinks(-main) オプションでシンボリックリンクのままのパスを基準にできる
Node.js は require するときにシンボリックリンクを解決後のパスを基準にしているようです
普段はあまりこういうところでシンボリックリンクを使わないのですが 複数パッケージで同じモジュールを使うところで手抜きで使おうとしたらハマりました
a.js は app1 と app2 の両方で使いたいですが全く同じものでよく 変更するときは両方が変わってほしいです
common フォルダを用意して a.js の本体をここに書き それぞれの a.js は require するだけにします
common/a.js に require がなければこれでも動きますが common/a.js はどのパッケージにも属していないことになり common/a.js から各 app フォルダの node_modules は使えません
common フォルダに package.json をおいてインストールすることもできますが app1 などにすでにインストール済みなのでそっちを使いたいです
シンボリックリンクにすれば楽に対応できるね と思ってこうしました
これならそれぞれに a.js が実際に配置されてるのと同じ扱いです
しかし動かしてみると Error: Cannot find module 'xxxxx'
モジュールが見つからないようです
エラーメッセージを見ると 「/opt/common/a.js」 を基準にモジュールを探そうとしているようでした
試しに app1 と common に b.js を置いて require("./b.js") でモジュールを読み込もうとすると common/b.js が読み込まれました
node_modules も相対パスもシンボリックリンクを解決した後のパスを基準にしてるようです
シンボリックリンクのまま扱ってほしいのでオプションがないか探すと
--preserve-symlinks
--preserve-symlinks-main
というのがありました
node コマンドに引数として渡すメインモジュールと require でロードされるファイルでオプションが分かれてるようです
で実行するなら
という感じで指定するとシンボリックリンクを解決する前のパスを基準にしてくれて無事モジュールをロードできました
その場合 使おうとしているパッケージは両方の node_modules に入っているはずなのでエラーにはなりません
ですが app2/a.js 内では app1 の node_modules からモジュールがロードされます
実体が違う事による問題が起きるかもしれませんし バージョンが違っているかもしれません
app1/index.js, app2/index.js
app1/a.js
app1/node_modules/pkg0/index.js
app2/node_modules/pkg0/index.js
Node.js でシンボリックリンクを含むファイルを扱うときは気をつけたほうが良さそうです
普段はあまりこういうところでシンボリックリンクを使わないのですが 複数パッケージで同じモジュールを使うところで手抜きで使おうとしたらハマりました
シンボリックリンクとモジュール解決
例えばこういうフォルダ構造があります/opt/
app1/
a.js
package.json
yarn.lock
node_modules/
app2/
a.js
package.json
yarn.lock
node_modules/
a.js は app1 と app2 の両方で使いたいですが全く同じものでよく 変更するときは両方が変わってほしいです
common フォルダを用意して a.js の本体をここに書き それぞれの a.js は require するだけにします
/opt/
app1/
a.js (require("../common/a.js"))
package.json
yarn.lock
node_modules/
app2/
a.js (require("../common/a.js"))
package.json
yarn.lock
node_modules/
common/
a.js
common/a.js に require がなければこれでも動きますが common/a.js はどのパッケージにも属していないことになり common/a.js から各 app フォルダの node_modules は使えません
common フォルダに package.json をおいてインストールすることもできますが app1 などにすでにインストール済みなのでそっちを使いたいです
シンボリックリンクにすれば楽に対応できるね と思ってこうしました
/opt/
app1/
a.js ---> ../common/a.js
package.json
yarn.lock
node_modules/
app2/
a.js ---> ../common/a.js
package.json
yarn.lock
node_modules/
common/
a.js
これならそれぞれに a.js が実際に配置されてるのと同じ扱いです
しかし動かしてみると Error: Cannot find module 'xxxxx'
モジュールが見つからないようです
エラーメッセージを見ると 「/opt/common/a.js」 を基準にモジュールを探そうとしているようでした
試しに app1 と common に b.js を置いて require("./b.js") でモジュールを読み込もうとすると common/b.js が読み込まれました
node_modules も相対パスもシンボリックリンクを解決した後のパスを基準にしてるようです
シンボリックリンクのまま扱ってほしいのでオプションがないか探すと
--preserve-symlinks
--preserve-symlinks-main
というのがありました
node コマンドに引数として渡すメインモジュールと require でロードされるファイルでオプションが分かれてるようです
node a.js
で実行するなら
node --preserve-symlinks-main a.js
という感じで指定するとシンボリックリンクを解決する前のパスを基準にしてくれて無事モジュールをロードできました
もっと分かりづらい問題になったかも
この例では common に切り分けましたが app2/a.js を app1/a.js へのシンボリックリンクとすることも考えられますその場合 使おうとしているパッケージは両方の node_modules に入っているはずなのでエラーにはなりません
ですが app2/a.js 内では app1 の node_modules からモジュールがロードされます
実体が違う事による問題が起きるかもしれませんし バージョンが違っているかもしれません
/opt/
app1/
index.js
a.js
node_modules/
pkg0/
index.js
app2/
index.js
a.js ---> ../app1/a.js
node_modules/
pkg0/
index.js
app1/index.js, app2/index.js
const pkg0 = require("pkg0")
const a = require("./a.js")
console.log(pkg0.version, a.version)
app1/a.js
module.exports = require("pkg0")
app1/node_modules/pkg0/index.js
exports.version = 1
app2/node_modules/pkg0/index.js
exports.version = 2
[root@tmp0 ~]# node /opt/app1/index.js
1 1
[root@tmp0 ~]# node /opt/app2/index.js
2 1
Node.js でシンボリックリンクを含むファイルを扱うときは気をつけたほうが良さそうです