I have a Node CLI program with some dependencies and various of its own modules imported via relative file paths. I would like for the code to be able to read its own package.json
file, but I can’t figure out how to "find" it when the program is installed (that is, when its package is installed with npm install
or npm install -g
).
Reading and parsing package.json
is easy — when running it from its own source directory. Of course that’s true, because the file path "./package.json" works.
I don’t mind reading the file and parsing it, but I don’t know of a mechanism to resolve "."-relative paths that works the same way as
import foo from "./lib/foo.mjs";
works. Such imports work when the program (well the package) has been installed anywhere on the system if the proper node_modules/.bin
directory is in my PATH, regardless of the working directory I’m using at the time.
Maybe this is impossible, but wanting to load package.json
doesn’t seem that weird.
2
Answers
This is something I found in the wild and it's still making my head spin, but it works. It's basically what Mr Nenashev's code does in his answer, but it leverages built-in tools (that I did not know existed).
Step one is to import the
createRequire()
function from the "module" module:That function returns a function that acts like the CommonJS
require()
, starting from a given path or file URL as an argument. Now, that's super useful, but we need the starting directory, which is the root of my problem (and which Mr Nenashev's answer solves). That brings us to new-to-me tool number two,import.meta
. That's an object with one property, "url", and the value of that is a "file://" URL for the module from which it's called.Thus, armed with those two things, I can write:
This will work even if (for example) I install my package globally and
cd
to any random directory. So long as my Node program's launcher is in my PATH, from anywhere I run it that "file://" URL will be the absolute path to the real directory where the package files live.So to sum up, a very quick way to get
package.json
is:And it's synchronous, yay.
You can try to parse
process.argv[1]
which points to the path of the executed script (could be a directory name with index.js):Output: