npm-cli-造轮子初体验

我亲手造了一个 npm CLI 命令,然后发现 vite、eslint 全是这么来的

实习第一天,我连「npm 包怎么变成一个命令行命令」都没认真想过。直到今天,我和一个 AI 搭子从零搓了一个 hello 命令------然后突然意识到,我每天在用的那些工具,背后藏的是同一套小秘密。


一、先建一个 npm 项目

bash

bash 复制代码
mkdir hello-cli
cd hello-cli
npm init -y

跑完之后,目录里多了一个 package.json

go 复制代码
hello-cli/
└── package.json

这一步的意义其实就一句话:

告诉 npm:「这里是一个 Node.js 包项目。」


二、在 package.json 里声明一个命令

我们往 package.json 里加了一行关键的东西:

json

json 复制代码
{
  "name": "hello-cli",
  "version": "1.0.0",
  "bin": {
    "hello": "./cli.js"
  }
}

重点就是这段:

json

json 复制代码
"bin": {
  "hello": "./cli.js"
}

它的意思是:

如果用户在终端输入 hello,就去执行 ./cli.js

bin 本质上是一张「命令名 → 文件」的地图 🗺️。


三、写真正干活的代码

然后我们创建了 cli.js

javascript

javascript 复制代码
#!/usr/bin/env node

console.log("🎉 Hello! 我的第一个 npm CLI 包!");

开头这行:

javascript

javascript 复制代码
#!/usr/bin/env node

是告诉系统:

用 Node.js 来运行这个文件。

下面那行 console.log,就是这个命令真正要做的事。


四、把它装到全局

接下来执行:

bash

erlang 复制代码
npm install -g .

这里两个细节:

  • . 表示「当前目录这个包」
  • -g 表示「全局安装」

npm 做了一件很关键的事------它读了 package.json,发现了 bin,然后在系统里注册了 hello 这个命令


五、终于可以直接运行了

以前要这样跑:

bash

复制代码
node cli.js

现在只要:

bash

复制代码
hello

终端立刻输出:

objectivec 复制代码
🎉 Hello! 我的第一个 npm CLI 包!

🎉 你的电脑刚刚「认识」了一个你自己创造的新命令


整个流程串起来

go 复制代码
创建项目
   │
   ↓
package.json
   │
   ↓
定义 bin:hello → cli.js
   │
   ↓
npm install -g .
   │
   ↓
系统注册 hello 命令
   │
   ↓
终端输入 hello
   │
   ↓
Node 执行 cli.js
   │
   ↓
看到输出

我们真正搞懂的,其实就这几样东西:

东西 作用
package.json 描述 npm 包的信息
bin 暴露命令入口(命令名 → 文件)
cli.js 命令实际执行的代码
#!/usr/bin/env node 指定用 Node 执行(主要给 Unix 看)
npm install -g 全局安装命令
CLI Command Line Interface,命令行工具

让我「哦!」一下的三个彩蛋

这个实验做完,我又追问了几个问题,结果挖出三个挺反直觉的点:

1. 在 Windows 上,那行 shebang 其实「没生效」

我写的 #!/usr/bin/env node,是 Unix / Mac 的规矩。Windows 根本不看这行。

那我的 hello 为什么还能跑?因为 npm install -g 在 Windows 上不是建软链接,而是生成了几个包装文件

objectivec 复制代码
hello        ← 给 Git Bash 用
hello.cmd    ← 给 CMD 用
hello.ps1    ← 给 PowerShell 用

真正找到 node 去执行 cli.js 的,是这几个 .cmd / .ps1。shebang 只是「跨平台留个后手」,让同一份代码在 Mac 上也能跑。很多人以为删了 shebang Windows 就坏了------其实没关系。

install -g . 是「复制一份装进全局」。你每改一次 cli.js,都得重装,很烦。

开发阶段应该这样:

bash

bash 复制代码
npm link      # 在包目录里执行一次

它会做一个软链接 指向你的项目。之后你改代码,hello 命令立刻是最新的,不用重装。用完 npm unlink 解绑即可。

3. 命令还分「全局」和「本地」

你装的是全局命令-g)。但真实项目里,90% 的命令其实是本地的,躺在:

bash 复制代码
node_modules/.bin/

比如你 npm install 之后,vite 其实装在 node_modules/.bin/vite,并没进全局。你能在 package.jsonscripts 里直接写 "dev": "vite",是因为 npm 跑 script 时会自动把 node_modules/.bin 加进 PATH


结尾:你刚打开的是一扇门

最有意思的一点是------你刚刚做的事,和很多大型工具本质一样:

markdown 复制代码
你的 hello
      ↓
npm 包
      ↓
bin
      ↓
终端命令

和:

markdown 复制代码
vite
      ↓
npm 包
      ↓
bin
      ↓
vite 命令

同一套机制

只是 Vite 里面不是打印一句 Hello,而是启动开发服务器、构建项目 😂

所以你刚刚其实不是「玩了个小例子」,而是亲手拆开了 npm 生态的一扇门 🚪✨ 以后看到 viteeslinttsc 这些命令,你会知道它们背后都藏着一个类似的 bin 小秘密。

一句话钉进脑子:

bin 定义命令、scripts 编排命令、.bin 存放本地命令、npm link 用于开发、shebang 是给 Unix 看的。


本文所有示例均为脱敏后的通用技术演示,不涉及任何公司项目细节。

相关推荐
前端工作日常21 小时前
我学习到的npx内部实现机制
npm
minglie12 天前
npm ali-oss库依赖冲突
前端·npm
屑曦晨3 天前
npm 全局包迁移指南:从系统目录到用户目录
npm·node.js
勉灬之8 天前
利用双网卡服务器搭建 Verdaccio 中转,解决内网 npm 依赖下载问题
运维·服务器·npm
Flynt16 天前
npm v12 来了:allowScripts 默认关闭,我的项目差点跑不起来
安全·npm·node.js
JuliusDeng18 天前
一文搞懂 `.npmrc`:npm 源、SSL 与 `_authToken` 配置避坑
npm·前端工程化
kyriewen22 天前
2026 年了,这 6 个 npm 包可以卸载了——浏览器原生 API 已经能替代
前端·javascript·npm
l1o3v1e4ding1 个月前
windows安装Claude Code,并接入Deepseek-v4模型 ,提供离线安装包
git·npm·node.js·claude code·cc-switchcc
TT_Close1 个月前
别再复制旧 Flutter 工程了,真正拖慢你的不是业务代码
flutter·npm·visual studio code