我想本地模仿vue-cli-service 的调用方式,如何创建自己的 执行脚本,必须要node_modules吗{
"name": "commander",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"gyk":"gyk"
},
"bin": {
"gyk": "bin/gyk.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
这是一个非常硬核且有趣的实验!你正在尝试制作一个属于自己的 CLI 工具。
要实现 npm run gyk 并且让系统识别到 gyk 这个命令(而不需要写 node bin/gyk.js),你需要理解 软链接(Symlink) 的机制。
在本地开发环境中,不需要 把包发布到 npm 仓库,也不一定非要装在 node_modules 里,但你需要模拟 npm install 的过程。
以下是实现你目标的三种方法,从最简单到最规范:
方法一:npm link (最推荐,模拟真实开发)
这是最标准的方式。npm link 会在全局或项目中创建一个软链接,欺骗系统以为这个包被"安装"了。
步骤:
-
准备文件结构 :
确保你的目录结构如下:
textmy-project/ ├── package.json (你贴的那段代码) └── bin/ └── gyk.js (你的执行脚本) -
编写脚本 (
bin/gyk.js) :
非常重要 :文件的第一行必须是 Shebang (#!/usr/bin/env node),告诉系统用 Node 来执行它。javascript#!/usr/bin/env node console.log('🎉 恭喜!你成功调用了本地的 gyk 命令!'); console.log('参数列表:', process.argv); -
赋予执行权限 (Mac/Linux 必需):
bashchmod +x bin/gyk.js -
创建链接 (核心步骤) :
在
package.json所在的目录下运行:bashnpm link这一步发生了什么?
npm 会读取你的
package.json中的bin字段,并在你的全局 npm 目录(通常是/usr/local/bin或 AppData)下创建一个名为gyk的快捷方式,指向你本地的bin/gyk.js。 -
验证 :
现在,你在终端的任何位置 直接输入
gyk,或者在项目中运行npm run gyk,应该都能成功了。
方法二:本地依赖安装 (File Protocol)
如果你不想污染全局环境,只想在当前项目里用。
-
准备两个目录:
my-cli-tool/(存放你的 gyk 工具代码,有bin字段的 package.json)my-app/(你要使用这个工具的项目)
-
在
my-app中安装my-cli-tool:在
my-app目录下运行:bashnpm install ../my-cli-tool(路径根据实际情况调整)
-
结果 :
npm 会把
my-cli-tool安装到my-app/node_modules下,并且自动检测��bin字段,把gyk命令链接到my-app/node_modules/.bin/gyk。此时在
my-app的package.json里写"scripts": { "test": "gyk" }即可正常工作。
方法三:自我引用 (Self-Referencing, 仅限 yarn/pnpm 或较新 npm)
如果你只有一个项目,既是工具本身,又是使用者(就像你现在的结构)。
在标准的 npm 安装流程发生之前,.bin 目录是不会自动生成的。所以直接运行 npm run gyk (其中 "gyk": "gyk") 通常会报错 command not found,因为 npm 还没根据 bin 字段创建链接。
解决方法:
你需要手动告诉 npm 把自己链接一下。
-
确保
package.json如你所示。 -
确保
bin/gyk.js有#!/usr/bin/env node头部。 -
执行安装 :
bashnpm installnpm 某些版本在 install 自己时,会尝试处理 bin 链接。
如果 npm install 后 npm run gyk 依然报错,请回到 方法一 (npm link),这是开发本地 CLI 工具最稳健的方法。
常见坑点排查
-
报错
#!\/usr\/bin\/env: No such file or directory(Windows):这通常是因为你的文件换行符是 CRLF(Windows 风格)。CLI 工具的头部 Shebang 必须要是 LF(Unix 风格)。
- 解决 :在编辑器里把
bin/gyk.js的换行符格式改为 LF。
- 解决 :在编辑器里把
-
报错
Permission denied:- 解决 :
chmod +x bin/gyk.js
- 解决 :
-
修改代码后不生效 :
如果你用了
npm link,修改代码是即时生效的,不需要重启或重新安装。
总结
要实现你的需求,最简单快捷的命令是:
- 添加
#!/usr/bin/env node到 js 文件头。 - 在项目根目录执行
npm link。 - 尽情享受你的
gyk命令吧!
npm run gyk 发生了什么 ,我还如何写gyk 对应的执行程序
这是一个很好的切入点,能帮你彻底理解
npm scripts的运行机制。
当你输入 npm run gyk 时,并不是 npm 自己在执行什么黑魔法,它本质上是一个命令行的"快捷方式"运行器。
以下是详细的发生过程,以及你可以通过哪几种方式编写 gyk 的对应程序。
一、 npm run gyk 到底发生了什么?
当你按下回车时,npm 按顺序做了这三件大事:
1. 查找配方 (Lookup)
npm 去当前目录下的 package.json 文件里找 scripts 对象,看里面有没有一个叫 "gyk" 的 key。
- 如果找不到,报错
Missing script: "gyk"。 - 如果找到了,比如
"gyk": "node scripts/my-task.js",它提取出后面的命令字符串。
2. 魔法路径增强 (Magic PATH) ------ 最关键的一步
npm 会临时把当前项目的 ./node_modules/.bin 目录加入到系统的环境变量 PATH 的最前面。
- 这意味着什么? 这意味着你可以在脚本里直接运行
webpack、vue-cli-service、eslint,而不需要写出完整的长路径(比如./node_modules/.bin/webpack)。 - 注意:这个 PATH 修改只在脚本运行期间有效,运行完就恢复原样。
3. 启动 Shell 执行 (Spawn Shell)
npm 启动一个系统 Shell(Windows 上通常是 cmd.exe,Mac/Linux 上是 sh),然后把那个命令字符串扔给 Shell 去执行。
二、 你如何编写 gyk 对应的程序?
你有三种主要的方式来编写这个"执行程序",从简单到复杂:
方式 1:直接写 Shell 命令 (最简单)
如果逻辑很简单,直接写在 package.json 里。
json
// package.json
{
"scripts": {
// 运行 gyk 时,直接在控制台打印一句话
"gyk": "echo '我是 GYK,我正在运行!' && mkdir new-folder"
}
}
方式 2:编写自定义 Node.js 脚本 (最推荐)
这是前端工程化中最常用的方式。你可以利用 Node.js 的强大能力做任何事(读写文件、网络请求、编译代码)。
步骤:
-
新建文件 :在项目根目录创建一个
my-gyk-script.js。javascript// my-gyk-script.js console.log('--- GYK 程序启动 ---'); // 这里的代码是在 Node 环境运行的 const fs = require('fs'); const path = require('path'); // 比如:创建一个叫 demo.txt 的文件 fs.writeFileSync('demo.txt', '这是 gyk 创建的文件'); console.log('文件创建成功!'); -
配置 package.json:
json{ "scripts": { "gyk": "node my-gyk-script.js" } } -
运行 :
npm run gyk。
方式 3:使用 Shell 脚本 (Mac/Linux 常用)
如果你在 Mac 或 Linux 下,可以写 .sh 文件。
-
新建
script.sh:bash#!/bin/bash echo "正在清理缓存..." rm -rf ./dist echo "完成" -
配置:
"gyk": "bash script.sh"(注意 Windows 默认没有 bash,可能报错)。
三、 进阶:如何接收参数?
假设你想运行 npm run gyk test,然后在你的脚本里拿到 test 这个参数。
在你的 my-gyk-script.js 中:
javascript
// process.argv 是一个数组,包含了启动 Node 进程时的所有参数
// [0]: node 的绝对路径
// [1]: 当前脚本文件的绝对路径
// [2]: 用户传进来的第一个参数
const args = process.argv.slice(2); // 去掉前两个固定的
console.log('接收到的参数:', args);
if (args[0] === 'fast') {
console.log('执行快速模式...');
} else {
console.log('执行普通模式...');
}
运行命令(注意中间的 --):
你需要用 -- 把 npm 的参数和传给你脚本的参数隔开:
npm run gyk -- fast
四、 底层揭秘:别人家的库(如 vue-cli-service)是怎么做到的?
你可能会问:"我自己写的脚本要用 node xxx.js 来运行,为什么 vue-cli-service 可以直接当命令用?"
这是因为 Vue CLI 的库在发布时,在其 package.json 里定义了 bin 字段:
json
// @vue/cli-service 的 package.json (位于 node_modules 下)
{
"name": "@vue/cli-service",
"bin": {
"vue-cli-service": "bin/vue-cli-service.js"
}
}
当你 npm install @vue/cli-service 时,npm 会自动读取这个 bin 字段,并创建一个软链接(快捷方式) 到你的 ./node_modules/.bin/ 目录下。
所以,当你在 scripts 里写 "build": "vue-cli-service build" 时:
- npm 把
./node_modules/.bin加入 PATH。 - Shell 找到了
vue-cli-service这个可执行文件(实际上指向了那个 JS 文件)。 - 脚本开始执行。
总结
你想写 gyk 程序,最实用的路径是:写一个 Node.js 脚本文件,然后在 package.json 的 scripts 中用 node your-script.js 来调用它。