英文:
Node program/module reading its own package.json: is that possible?
问题
我有一个带有一些依赖项和通过相对文件路径导入的各种自己模块的Node CLI程序。我希望代码能够读取自己的package.json
文件,但是我无法弄清楚在安装程序时(也就是说,当它的包通过npm install
或npm install -g
安装时)如何“找到”它。
阅读和解析package.json
很容易——当从其自己的源目录运行时。当然这是对的,因为文件路径"./package.json"有效。
我不介意读取文件和解析它,但我不知道一个解决“.”相对路径的机制,它的工作方式与
import foo from "./lib/foo.mjs";
一样。这样的导入在程序(或者说包)在系统上的任何位置都可以正常工作,只要正确的node_modules/.bin
目录在我的PATH中,无论我在那时使用的工作目录是什么。
也许这是不可能的,但想要加载package.json
似乎并不那么奇怪。
英文:
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.
答案1
得分: 2
你可以尝试解析 `process.argv[1]`,它指向执行的脚本路径(可能是带有 index.js 的目录名称):
import fs from 'fs';
const path = process.argv[1].split('/');
let found;
while (path.length) {
const dirname = path.join('/');
if (fs.statSync(dirname || '/').isDirectory) {
const filename = dirname + '/package.json';
if (fs.existsSync(filename)) {
found = filename;
break;
}
}
path.pop();
}
if (!found) {
// 我们很愚蠢没有找到
}
const json = JSON.parse(fs.readFileSync(found));
console.log(JSON.stringify(json));
输出:
{"name":"package-json","version":"1.0.0","description":"","main":"index.js","type":"module","scripts":{"test":"echo "Error: no test specified" && exit 1"},"author":"","license":"ISC"}
<details>
<summary>英文:</summary>
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):
import fs from 'fs';
const path = process.argv[1].split('/');
let found;
while (path.length) {
const dirname = path.join('/');
if (fs.statSync(dirname || '/').isDirectory) {
const filename = dirname + '/package.json';
if (fs.existsSync(filename)) {
found = filename;
break;
}
}
path.pop();
}
if (!found) {
// we are silly not to found
}
const json = JSON.parse(fs.readFileSync(found));
console.log(JSON.stringify(json));
Output:
{"name":"package-json","version":"1.0.0","description":"","main":"index.js","type":"module","scripts":{"test":"echo "Error: no test specified" && exit 1"},"author":"","license":"ISC"}
</details>
# 答案2
**得分**: 1
这是我在野外找到的东西,仍然让我感到困惑,但它有效。基本上,这就是Nenashev先生在他的回答中所做的事情,但它利用了内置工具(我不知道存在)。
第一步是从“module”模块导入`createRequire()`函数:
```javascript
import { createRequire } from "module";
该函数返回一个函数,其行为类似于CommonJS的require()
,以给定的路径或文件URL作为参数开始。现在,这非常有用,但我们需要起始目录,这是我的问题根本(Nenashev先生的回答解决了这个问题)。这将我们带到了对我来说新的工具号二,import.meta
。这是一个具有一个属性“url”的对象,其值是调用它的模块的“file://”URL。
因此,拥有这两个东西,我可以编写:
const require = createRequire(import.meta.url);
const PACKAGE = require("./package.json");
即使(例如)我将我的包全局安装并且cd
到任何随机目录,这也可以工作。只要我的Node程序的启动器在我的PATH中,无论我从哪里运行它,那个“file://”URL都将是包文件实际所在目录的绝对路径。
总之,快速获取package.json
的方法是:
import { createRequire } from "module";
const PACKAGE = createRequire(import.meta.url)("./package.json");
而且是同步的,太好了。
英文:
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:
import { createRequire } from "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:
const require = createRequire(import.meta.url);
const PACKAGE = require("./package.json");
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:
import { createRequire } from "module";
const PACKAGE = createRequire(import.meta.url)("./package.json");
And it's synchronous, yay.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论