結論
require.resolve
を使う
Use the internal require() machinery to look up the location of a module, but rather than loading the module, just return the resolved filename.
const path = require.resolve(<PACKAGE>)
https://nodejs.org/dist/latest-v16.x/docs/api/modules.html#requireresolverequest-options
環境
% node -v v16.13.2 % npm -v 8.10.0
動作確認
npm init -y npm init -y -w apps/api-a npm i cowsay --save-exact npm i express --save-exact npm i express@4.0.0 --save-exact -w apps/api-a
require-resolve % tree -L 3 -I node_modules . ├── apps │ └── api-a │ ├── index.js │ └── package.json ├── package-lock.json └── package.json
apps/api-a/package.json
{ "name": "api-a", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "express": "4.0.0" } }
package.json
{ "name": "require-resolve", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "workspaces": [ "apps/api-a" ], "dependencies": { "cowsay": "1.5.0", "express": "4.18.1" } }
CommonJSの場合
apps/api-a/index.js
const cowsay = require("cowsay"); console.log(require.resolve("cowsay")); const express = require("express"); console.log(require.resolve("express"));
実行結果
node apps/api-a/index.js /.../require-resolve/node_modules/cowsay/index.js # さかのぼって参照 /.../require-resolve/apps/api-a/node_modules/express/index.js # 最も近いもの
挙動の説明
https://nodejs.org/dist/latest-v16.x/docs/api/modules.html#loading-from-node_modules-folders
上のドキュメントにある通り、node_modulesをさかのぼって参照する。
ディレクトリルートにexpress@4.18.1、exapi-aにexpress@4.0.0がインストールされており、
apps/api-a/index.js
は一番近いexpressを見に行く。
どのnode_modulesを利用しているかはrequrie.resolve
で取得できる。
ES Modulesの場合
apps/api-a/index.js
console.log(await import.meta.resolve("cowsay")); console.log(await import.meta.resolve("express"));
apps/api-a/package.json
"type": "module",
実行結果
% node --experimental-import-meta-resolve apps/api-a/index.js
file:///.../require-resolve/node_modules/cowsay/index.js
file:///.../require-resolve/apps/api-a/node_modules/express/index.js
もしくは
import { createRequire } from "module"; const require = createRequire(import.meta.url); console.log(require.resolve("cowsay")); console.log(require.resolve("express"));
% node apps/api-a/index.js /.../require-resolve/node_modules/cowsay/index.js /.../require-resolve/apps/api-a/node_modules/express/index.js
参考ドキュメント
https://nodejs.org/dist/latest-v16.x/docs/api/esm.html#importmetaresolvespecifier-parent https://nodejs.org/dist/latest-v16.x/docs/api/esm.html#no-requireresolve
まとめ
どのnode_modulesを参照しているのかパスを取得する方法をまとめた。
モノレポはnode_modulesが複数できるので、node_module内のファイルを参照するときにパスを考慮する必要がある。