共通モジュールの扱い方
◆ プロジェクト内の複数のパッケージで共通のモジュールをロードしたい
◆ それぞれのパッケージ内でモジュールを持つと更新が不便
◆ パッケージ化してそれをローカルインストールで node_modules に入れると便利
◆ パッケージ名とそのルートからの相対パスで指定できる
◆ シンボリックリンクなので更新は即時反映される
◆ 自分のパッケージ内をルートからパス指定したい場合にも使える
◆ それぞれのパッケージ内でモジュールを持つと更新が不便
◆ パッケージ化してそれをローカルインストールで node_modules に入れると便利
◆ パッケージ名とそのルートからの相対パスで指定できる
◆ シンボリックリンクなので更新は即時反映される
◆ 自分のパッケージ内をルートからパス指定したい場合にも使える
共通モジュールを扱いたい
1 つのプロジェクト関連で複数のパッケージがあります基本はどれも JavaScript で Node.js だったり Webpack や parcel を使ったものです
package.json があるフォルダをルートにしているものです
パッケージごとにインストールするパッケージが別だったりで分けて入るものの一応同じプロジェクトです
なので共通の定数や処理が少しあります
イメージ
- project/
----- client/
--------- client1/
------------- package.json
--------- client2/
------------- package.json
----- server/
--------- server1/
------------- package.json
--------- server2/
------------- package.json
----- batch/
--------- package.json
----- support-tool/
--------- package.json
それをどういうふうに扱うかが迷うところです
パッケージ内に埋め込む
まず 1 つ目の案は パッケージを分けている以上 独立しているものですし パッケージ外の JavaScript ファイルを相対パスでロードするのも気持ち悪いので それぞれのパッケージ内に同じファイルをコピーするというものです1 パッケージだけ更新とかもできますし これはこれで良い気はします
ただ やっぱり更新が面倒なんですよね
パッケージが増えればそれだけ数が増えますし更新漏れが起きるかもしれません
コピーをスクリプト化しておいても場所が変わったとかパッケージ増えたとかでの手間はあります
node_modules にパッケージ化して配置
そういう面で楽にしようとすると node_modules 以下に共通のデータを export するパッケージを入れるのが向いています個々のパッケージ内の node_modules に配置すると更新が大変なのは変わらないのでプロジェクト全体ファイルのルートに node_modules を作ります
個別に更新はできませんが 基本的に全部まとめて行うもので 個別に更新したいなんてほぼないのでそこまで問題ではありません
それに node_modules は一番近いものが優先されるのでひとつだけ変更したいならそのパッケージ内の node_modules に変更した版を入れる手もあります
この方法だと いちいち更新する手間はかかりませんしパッケージ名 (node_modules のフォルダ名) で require すればいいので良いこと多めです
ただしデメリットもあって node_modules を npm / yarn を使わずに変更するのはあまり良い方法ではないです
update などのタイミングで package.json に存在しないなら消されてしまいます
プロジェクトルートに package.json を置いて全体で共通のパッケージを入れるわけでもないならここの node_modules は npm / yarn で管理してないので大丈夫とも言えますがあまり気が進みません
npm で管理
問題が npm / yarn で管理してないことならすればいいじゃないと思ったのでローカルからインストールしてみます共通部分をパッケージ化して package.json も用意します
そのフォルダをプロジェクトのルートに配置して そのフォルダをインストールします
npm install ./common
package.json の dependencies にはファイルスキームで記述されていました
参考までに 普通の npm パッケージで node_fetch もインストールしてみた package.json の中身です
{
"dependencies": {
"common": "file:common",
"node-fetch": "^2.6.0"
}
}
ローカルインストールの対象がプロジェクトのフォルダ外だとそのフォルダも持ってきて同じ相対パスで配置しないといけなくて面倒ですが プロジェクト内であれば気にしなくて済みます
これで node_modules にインストールされたので各パッケージ内では common というパッケージを読み込むだけで共通の定数や処理を使えます
const { app_name, sharedkey } = require("common")
ちなみに ローカルインストールではシンボリックリンク(ジャンクション)が作られるようです
コピーされるのだと同じファイルが被るなぁとか思ってましたが実体は 1 つなので無駄に容量をとったりコピーに時間がかかることもありません
しかも 実体が同じなので変更は即時反映されます
common パッケージ更新したあとに npm update 忘れたなんてことがありません
全体で共通化したいものに限らずプロジェクト内のパッケージは全部プロジェクトルートの package.json にインストールしておけば パッケージ A からパッケージ B のモジュールを使いたいとかあってもパッケージ名とルートからのパス指定で済みますし結構便利かもしれません
パッケージルートに移動するまでに ../ を繰り返すのが苦痛ですからね
別パッケージに限らず 自身のパッケージのルートからのパスを指定するのにも使えます
- common/
----- d1/
--------- d11/
------------- d111/
----------------- foo.js
----- d2/
--------- bar.js
foo.js から bar.js をインポートするとき
require("../../../d2/bar.js)
の代わりに
require("common/d2/bar.js")
が使えます
パッケージ自身を dependencies に加える
これでひらめいたのが自身を dependencies に加えるというものです上みたいなプロジェクト内にパッケージがいくつもあるとか言う場合に限らず使えます
{
"name": "test",
"version": "1.0.0",
"dependencies": {
"self": "file:."
}
}
これで npm install すると require("self/file.js") のように self を自分のパッケージルートとしてパスを書けます
parcel だと ~ がルートを表すとかありましたが Node.js はまだそういうのがなかった気がします
ES Modules に合わせてその辺の改善もあった気はするものの LTS だと今年秋の 14 くらいでしょうか