Node.js 如何检测 script 脚本是在项目本身运行

假设有一个脚本 check.mjs 如何检测它自己是在自身项目内运行?

💭 背景

postinstall 运行时机有两个:被其他项目安装 npm i foo 或自己项目运行 npm i。如果想让自己项目安装时不运行或者做一些特殊操作,则需要检测脚本是否被自身运行。

假设有 package.json

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

故代码可以这样写:

mjs 复制代码
// 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

mjs 复制代码
/**
 * @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

mjs 复制代码
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 文件中包含以下内容:

json 复制代码
{
  "name": "foo",
  "version": "1.2.5"
}

在运行时,Node.js 会将以下环境变量设置为:

  • npm_package_name"foo"
  • npm_package_version"1.2.5"

这些环境变量可以在脚本中通过 process.env 访问9

------ 官方文档

json 复制代码
// package.json
{
  "name": "foo"
  "scrpts": {
    "say:name": "echo What is the pkg name? $npm_package_name"
  }
}

npm run say:name

sh 复制代码
❯ 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

sh 复制代码
❯ bun say:name 
$ echo What is the pkg name? $npm_package_name
What is the pkg name? foo

那检测逻辑怎么写呢。有两种写法。

  1. 直接在 package.json 中判断(但是兼容性不好)

Linux:

json 复制代码
// package.json
"postinstall": "[ $npm_package_name = foo ] && echo '被自身运行无需 check' || echo 被其他项目安装后执行"

Windows:

json 复制代码
// package.json
"postinstall": "[ %npm_package_name% = foo ] && echo '被自身运行无需 check' || echo 被其他项目安装后执行"
  1. 写到 Node.js 脚本。
mjs 复制代码
/**
 * @returns {boolean}
 */
function isRunInItself() {
  return process.env.npm_package_name === 'foo'
}

🧠 总结

最严谨是用方法1,最简单则使用方法3。

相关推荐
Rain5098 小时前
2.1 Nest.js 项目初始化与模块化架构
开发语言·前端·javascript·后端·架构·数据分析·node.js
矩阵科学15 小时前
Langchain.js 实战五:Agent 实战
langchain·node.js
终将老去的穷苦程序员18 小时前
npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1,因为在此系统上禁止运行脚
前端·npm·node.js
之歆18 小时前
Day10_Node.js 与 Express 开发实战指南:从零到一构建专业级 Web 服务
前端·node.js·express
想要成为糕糕手1 天前
RESTful Todo 任务清单 —— 从零学习前后端
api·restful·bun
GuWenyue2 天前
10分钟搞定TodoList实战!从0搭建Bun+TS的RESTful接口服务
前端·typescript·bun
之歆2 天前
Node.js 与 NPM 包管理完全指南
前端·npm·node.js
12点一刻2 天前
npx 使用入门教程:是什么、怎么用、和 npm 有什么区别
前端·npm·node.js
Sca_杰2 天前
速通抖音开放平台API-生活服务商应用
javascript·node.js
console.log('npc')2 天前
核心实战篇 生成式 UI+A2UI 协议 + 全栈 Agent 项目落地
node.js·react·#生成式ui·a2ui协议·ui agent·ai前端实战