Node.js 如何检测 script 脚本是在项目本身运行
假设有一个脚本 check.mjs 如何检测它自己是在自身项目内运行?
💭 背景
postinstall 运行时机有两个:被其他项目安装 npm i foo 或自己项目运行 npm i。如果想让自己项目安装时不运行或者做一些特殊操作,则需要检测脚本是否被自身运行。
假设有 package.json
{
"scripts": {
"postinstall": "node ./scripts/check.mjs"
}
}
check.mjs 的 isRunInItself
如何写?有三种方式:
📁 方式 1:cwd 和 dirname 比较
原理:cwd 为运行时路径,dirname 为其磁盘路径,如果脚本 check.mjs 被自身运行,则二者相同,否则不同。
假设 check.mjs 的路径为 /temp/foo/check.mjs,它被 bar 项目依赖,bar 路径为 /workspace/bar
自身运行
- cwd: /temp/foo/
- dirname: /temp/foo/
被安装后运行
- cwd: /temp/foo/node_modules/foo
- dirname: /workspace/bar
故代码可以这样写:
// check.mjs
/**
* @returns {boolean}
*/
function isRunInItself() {
// 获取当前工作目录
const currentDir = process.cwd()
// 获取 foo 包的根目录
const __dirname = import.meta.dirname
const fooRootDir = path.resolve(__dirname, '..')
if (currentDir === fooRootDir) {
log('正在本包目录安装,跳过检测...')
return true
}
return false
}
📦 方式 2:检测当前项目 package.json name
原理:检查当前运行目录的 package.json name
/**
* @returns {boolean}
*/
function isRunInItself() {
// so if name in package.json in the current dir is @neural/utils then skip check
if (fs.existsSync('./package.json')) {
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'))
if (packageJson.name === PACKAGE_NAME) {
// log('Skip check because package name is @neural/utils')
return true
}
}
return false
}
读取 json 也可以直接用更高级的写法,利用 Node.js v18.20.0 引入的 import attributes:
const { default: packageJson } = await import('./package.json'), { with: { type: 'json' } })
// 如果只需要 name 可以再解构下
const { default: { name } } = await import('./package.json'), { with: { type: 'json' } })
方式 3:🗝️ 环境变量获取 package.json name
原理:利用 Node.js 鲜为人知的一个隐藏知识点。Node.js 在运行时会将 package.json 的字段注入环境变量。
Node.js 在运行时会将
package.json
文件中的字段注入到环境变量中。例如,如果package.json
文件中包含以下内容:{ "name": "foo", "version": "1.2.5" }
在运行时,Node.js 会将以下环境变量设置为:
npm_package_name
为"foo"
npm_package_version
为"1.2.5"
这些环境变量可以在脚本中通过
process.env
访问[9]。—— 官方文档。
// package.json
{
"name": "foo"
"scrpts": {
"say:name": "echo What is the pkg name? $npm_package_name"
}
}
npm run say:name
:
❯ npm run say:name
> foo@0.0.20 say:name
> echo What is the pkg name? $npm_package_name
What is the pkg name? foo
注意:如果是 Windows 则环境变量需要改成 %npm_package_name%
bun 兼容性是真的好,Windows 下也无需改。bun say:name
:
❯ bun say:name
$ echo What is the pkg name? $npm_package_name
What is the pkg name? foo
那检测逻辑怎么写呢。有两种写法。
- 直接在 package.json 中判断(但是兼容性不好)
Linux:
// package.json
"postinstall": "[ $npm_package_name = foo ] && echo '被自身运行无需 check' || echo 被其他项目安装后执行"
Windows:
// package.json
"postinstall": "[ %npm_package_name% = foo ] && echo '被自身运行无需 check' || echo 被其他项目安装后执行"
- 写到 Node.js 脚本。
/**
* @returns {boolean}
*/
function isRunInItself() {
return process.env.npm_package_name === 'foo'
}
🧠 总结
最严谨是用方法1,最简单则使用方法3。