深度脚本解析npm run dev做了什么

npm run dev做了什么

执行后,会去找package.json中定义的内容:

也就是执行vue-cli-service脚本的内容。所有执行的脚本都在/mode-modules/.bin文件夹内,位置如下:

这个脚本的内容是什么呢?如下:

js 复制代码
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,,/,g')")

case `uname` in
    *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac

if [ -x "$basedir/node" ]; then
  "$basedir/node"  "$basedir/../@vue/cli-service/bin/vue-cli-service.js" "$@"
  ret=$?
else 
  node  "$basedir/../@vue/cli-service/bin/vue-cli-service.js" "$@"
  ret=$?
fi
exit $ret

这个脚本的意思是什么,一下是豆包的逐行解释:

js 复制代码
以下是对该脚本逐行的详细分析:

### 1. `#!/bin/sh`
这是脚本的 shebang 行,它告诉系统使用 `/bin/sh` 这个 shell 解释器来执行该脚本。`/bin/sh` 通常是系统默认的 shell 解释器,不同的系统可能有所不同,但一般会指向一个兼容 POSIX 标准的 shell。

### 2. `basedir=$(dirname "$(echo "$0" | sed -e 's,,/,g')")`
- `$0`:在 shell 脚本中,`$0` 表示当前脚本的名称,包含完整的路径。
- `echo "$0"`:将 `$0` 的值输出。
- `sed -e 's,,/,g'`:`sed` 是一个流编辑器,`-e` 选项用于指定要执行的脚本命令。`s,,/,g` 是一个替换命令,将输入中的所有反斜杠 `` 替换为正斜杠 `/`。这一步主要是为了处理 Windows 路径中的反斜杠,使其符合 Unix 风格的路径格式。
- `dirname ...`:`dirname` 命令用于获取文件路径中的目录部分。
- 整行的作用是获取当前脚本所在的目录,并将其赋值给变量 `basedir`。

### 3. `case `uname` in`
- `uname`:这是一个用于获取操作系统名称的命令。执行 `uname` 会返回当前操作系统的内核名称,例如在 Linux 系统上返回 `Linux`,在 macOS 上返回 `Darwin`,在 Windows 的 Cygwin、MinGW 或 MSYS 环境中会返回包含 `CYGWIN`、`MINGW` 或 `MSYS` 的字符串。
- `case ... in`:这是 shell 中的条件判断语句,用于根据 `uname` 的输出结果进行不同的处理。

### 4. `*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;`
- `*CYGWIN*|*MINGW*|*MSYS*`:这是 `case` 语句的匹配模式,表示如果 `uname` 的输出包含 `CYGWIN`、`MINGW` 或 `MSYS`,则执行后面的命令。
- `cygpath -w "$basedir"`:`cygpath` 是 Cygwin 环境中的一个工具,用于在 Unix 风格路径和 Windows 风格路径之间进行转换。`-w` 选项表示将 Unix 风格的路径转换为 Windows 风格的路径。
- `;;`:表示该匹配模式的处理结束。

### 5. `if [ -x "$basedir/node" ]; then`
- `[ -x ... ]`:这是 shell 中的条件测试语句,`-x` 选项用于检查文件是否存在且具有可执行权限。
- 整行的作用是检查 `$basedir` 目录下是否存在名为 `node` 的可执行文件。

### 6. `"$basedir/node"  "$basedir/../@vue/cli-service/bin/vue-cli-service.js" "$@"`
- 如果 `$basedir` 目录下存在可执行的 `node` 文件,则使用该 `node` 解释器来执行 `$basedir/../@vue/cli-service/bin/vue-cli-service.js` 脚本。
- `"$@"`:表示将脚本接收到的所有参数原封不动地传递给 `vue-cli-service.js` 脚本。

### 7. `ret=$?`
- `$?`:在 shell 中,`$?` 表示上一个命令的退出状态码。退出状态码为 0 通常表示命令执行成功,非 0 表示执行失败。
- 这行代码将 `node` 执行 `vue-cli-service.js` 脚本后的退出状态码赋值给变量 `ret`。

### 8. `else`
- 当 `$basedir` 目录下不存在可执行的 `node` 文件时,执行 `else` 后面的代码块。

### 9. `node  "$basedir/../@vue/cli-service/bin/vue-cli-service.js" "$@"`
- 使用系统默认的 `node` 解释器来执行 `$basedir/../@vue/cli-service/bin/vue-cli-service.js` 脚本,并将脚本接收到的所有参数传递给它。

### 10. `ret=$?`
- 同样,将系统默认 `node` 执行 `vue-cli-service.js` 脚本后的退出状态码赋值给变量 `ret`。

### 11. `exit $ret`
- `exit` 命令用于终止当前脚本的执行,并返回指定的退出状态码。这里将之前保存的退出状态码 `ret` 作为脚本的退出状态码返回,这样调用该脚本的程序可以根据这个状态码判断脚本是否执行成功。

### 总结
这个脚本的主要功能是执行 `@vue/cli-service/bin/vue-cli-service.js` 脚本。它会优先使用 `$basedir` 目录下的 `node` 解释器,如果该解释器不存在,则使用系统默认的 `node` 解释器。同时,脚本还会处理 Windows 环境下的路径格式问题。

看完上述解释,我们会发现,第8行到第12行的内容是:如果文件夹下有node解释器就用,否则用系统默认的 node 解释器。我的文件夹里是没有node解释器的,这里不做截图解释。

那么,系统默认的 node 解释器在哪里呢? 我本地用nvm管理node版本,本项目适用v14版本。所以我的地址在/用户/xxx/.nvm/versions/node/v14.x.x/bin/node解释器。

脚本最终执行的是@vue里的vue-cli-service.js文件,文件路径如下: @vue/cli-service/bin/vue-cli-service.js的文件内容如下:

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

const semver = require('semver')
const { error } = require('@vue/cli-shared-utils')
const requiredVersion = require('../package.json').engines.node

if (!semver.satisfies(process.version, requiredVersion)) {
  error(
    `You are using Node ${process.version}, but vue-cli-service ` +
    `requires Node ${requiredVersion}.
Please upgrade your Node version.`
  )
  process.exit(1)
}

const Service = require('../lib/Service')
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())

const rawArgv = process.argv.slice(2)
const args = require('minimist')(rawArgv, {
  boolean: [
    // build
    'modern',
    'report',
    'report-json',
    'watch',
    // serve
    'open',
    'copy',
    'https',
    // inspect
    'verbose'
  ]
})
const command = args._[0]

service.run(command, args, rawArgv).catch(err => {
  error(err)
  process.exit(1)
})

文件分析结束。

万恶之源,为啥看这个?

主要分享本次解决bug的思路历程,bug本身是没有技术含量的。

我clone了一个项目,install的时候一直报错,所以直接拿了node-modules包过来,run dev还是失败了,报错是sh: vue-cli-service: command not found。

熟悉的sh报错,意思是脚本没执行成,vue-cli-service找不到。但是我在.bin目录里确实找到了这个脚本,所以怀疑是环境PATH没对上。这个时候我们可以cmd里输出环境PATH,或者用户文件夹里找.profile相关的文件,看有没有本项目的.bin地址。

但是我偷懒了,直接打开了cmd报错的最后一行:找到记录失败信息的log,然后让AI分析它。bug.log内容如下(懒得看可以跳过,直接看下面的AI分析):

js 复制代码
0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli   '/Users/张三/.nvm/versions/node/v14.18.3/bin/node',
1 verbose cli   '/Users/张三/.nvm/versions/node/v14.18.3/bin/npm',
1 verbose cli   'run',
1 verbose cli   'dev'
1 verbose cli ]
2 info using npm@6.14.15
3 info using node@v14.18.3
4 verbose run-script [ 'predev', 'dev', 'postdev' ]
5 info lifecycle app@2.1.0~predev: app@2.1.0
6 info lifecycle app@2.1.0~dev: app@2.1.0
7 verbose lifecycle app@2.1.0~dev: unsafe-perm in lifecycle true
8 verbose lifecycle app@2.1.0~dev: 
PATH: 
/Users/张三/.nvm/versions/node/v14.18.3/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:
/Users/张三/Desktop/我的项目包裹/myProjectName/node_modules/.bin:
/Users/张三/.nvm/versions/node/v14.18.3/bin:
/Users/张三/.volta/bin:
/usr/local/bin:
/usr/local/sbin:
/Library/Frameworks/Python.framework/Versions/3.11/bin:
/Users/张三/.volta/bin:
/usr/local/bin:
/usr/local/sbin:
~/bin:
/usr/local/bin:
/usr/bin:
/bin:
/usr/sbin:
/sbin
9 verbose lifecycle app@2.1.0~dev: CWD: /Users/张三/Desktop/我的项目包裹/myProjectName

10 silly lifecycle app@2.1.0~dev: Args: [ '-c', 'vue-cli-service serve' ]

11 info lifecycle app@2.1.0~dev: Failed to exec dev script

12 verbose stack Error: app@2.1.0 dev: `vue-cli-service serve`

12 verbose stack spawn ENOENT

12 verbose stack     at ChildProcess.<anonymous> (/Users/张三/.nvm/versions/node/v14.18.3/lib/node_modules/npm/node_modules/npm-lifecycle/lib/spawn.js:48:18)
12 verbose stack     at ChildProcess.emit (events.js:400:28)
12 verbose stack     at maybeClose (internal/child_process.js:1058:16)
12 verbose stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:293:5)
13 verbose pkgid app@2.1.0
14 verbose cwd /Users/张三/Desktop/我的项目包裹/myProjectName
15 verbose Darwin 21.6.0
16 verbose argv "/Users/张三/.nvm/versions/node/v14.18.3/bin/node" "/Users/张三/.nvm/versions/node/v14.18.3/bin/npm" "run" "dev"
17 verbose node v14.18.3
18 verbose npm  v6.14.15
19 error code ELIFECYCLE
20 error syscall spawn
21 error file sh
22 error errno ENOENT
23 error app@2.1.0 dev: `vue-cli-service serve`
23 error spawn ENOENT
24 error Failed at the app@2.1.0 dev script.
24 error This is probably not a problem with npm. There is likely additional logging output above.
25 verbose exit [ 1, true ]

这里推荐一个Vscode免费AI插件Copilot,直接编译器内搜名字安装就行。
AI Copilot分析如下:(懒得看AI分析也可以,直接看下面的综述)

js 复制代码
这个日志文件显示了在运行 npm run dev 命令时发生的错误。以下是逐行分析:

1). 1 verbose cli 'run', 'dev' 表示运行了 npm run dev 命令。
2). 2 info using npm@6.14.15 和 3 info using node@v14.18.3 表示使用的 npm 和 Node.js 版本。
3). 4 verbose run-script [ 'predev', 'dev', 'postdev' ] 表示将依次运行 predev、dev 和 postdev 脚本。
4). 5 info lifecycle app@2.1.0~predev: app@2.1.0 和 6 info lifecycle app@2.1.0~dev: app@2.1.0 表示进入了 predev 和 dev 生命周期钩子。
5). 7 verbose lifecycle app@2.1.0~dev: unsafe-perm in lifecycle true 表示在生命周期中使用了 unsafe-perm 选项。
6). 8 verbose lifecycle app@2.1.0~dev: PATH: ... 表示当前的 PATH 环境变量。
7). 9 verbose lifecycle app@2.1.0~dev: CWD: /Users/xiehuimin/Desktop/改需求项目/changqing-vue 表示当前工作目录。
8). 10 silly lifecycle app@2.1.0~dev: Args: [ '-c', 'vue-cli-service serve' ] 表示运行 vue-cli-service serve 命令。
9). 11 info lifecycle app@2.1.0~dev: Failed to exec dev script 表示执行 dev 脚本失败。
10). 12 verbose stack Error: app@2.1.0 dev: vue-cli-service serve 和 12 verbose stack spawn ENOENT 表示发生了 ENOENT 错误,意味着找不到 vue-cli-service 命令。
总结:问题的根本原因是 vue-cli-service 命令未找到。可能的解决方案包括:

1). 确认 vue-cli-service 是否已安装在项目的 node_modules 中。
2). 确认 .bin 是否在 PATH 环境变量中。
3). 重新安装依赖:运行 npm install 或 yarn install。

综上所述,造成vue-cli-service 命令未找到的原因可能有3个,我们逐个解决。

  1. 确认 vue-cli-service 是否已安装在项目的 node_modules 中。

    我们可以在文件夹中手动找到这个文件夹,肉眼能看到它。

  2. 确认 .bin 是否在 PATH 环境变量中。

    其实log文件中已经写了PATH内容了,但我们还是可以cmd里echo $PATH看看输出内容,或者根据你使用的 shell,不同的配置文件可能是 ~/.bashrc, ~/.zshrc, ~/.profile 等找到环境配置文件直接看PATH定义。我本地的PATH是配置过动态的(具体理解可以自己AI):

js 复制代码
export VOLTA_HOME="$HOME/.volta"
export PATH="$VOLTA_HOME/bin:$PATH"
export NVM_DIR="$HOME/.nvm"
[ -s "/usr/local/opt/nvm/nvm.sh" ] && . "/usr/local/opt/nvm/nvm.sh"  # This loads nvm
  [ -s "/usr/local/opt/nvm/etc/bash_completion.d/nvm" ] && . "/usr/local/opt/nvm/etc/bash_completion.d/nvm"  # This loads nvm bash_completion

证明PATH地址没出问题。

  1. 重新安装依赖:运行 npm install 或 yarn install

问题出在这里,我mode-modules包拿到后,跟本地项目没对上,具体原因没分析出来,可能是某个配置或解析?

总之,需要删除lock文件,重新install就好了。

HE~!

相关推荐
weixin_382395234 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__4 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.4 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~5 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华6 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子8 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅9 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni9 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学10 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端