Libon

Vite 源码分析(二)

5Mins #vite
分析 Vite@3 源码实现之 vite/src/node/cli.ts

ToC

cac API

import { cac } from 'cac' 用于引入解析函数。

initial

const cli = cac('vite') 用于初始化一个参数解析器,接收一个可选参数,如果传入,则会在使用命令并附加 --hep 命令的时候将传入的参数替代命令原本的名称。比如我的 package.json 如下:

1
{
2
"bin": {
3
"vite": "./test.js"
4
}
5
}

test.js 如下:

1
#!/usr/bin/env node
2
3
import { cac } from 'cac'
4
5
const cli = cac()
6
7
cli.help()
8
cli.parse()

那么在获取帮助的时候就会有这样的提示:

1
vite
2
3
Usage:
4
$ vite <command> [options]
5
6
Options:
7
-h, --help Display this message

但如果我传入 test-viteconst cli = cac('test-vite'))则会变成:

1
test-vite
2
3
Usage:
4
$ test-vite <command> [options]
5
6
Options:
7
-h, --help Display this message

参数主要是起帮助性的提示作用,不会影响功能。

command

创建一条命令,当在执行的时候,如果名称一致将匹配到它:

1
cli.command('[root]', 'start dev server') // default command

在设置以后,使用帮助的时候也会打印出对应的提示信息:

1
Commands:
2
[root] start dev server

alias

给命令创建别名

1
cli
2
.alias('serve') // the command is called 'serve' in Vite's API
3
.alias('dev') // alias to align with the script name

这样设置的话,就有了三种启动方式了:vitevite devvite serve

option

给命令添加选项参数

1
cli.option('--host [host]', `[string] specify hostname`)

action

在执行命令的回调函数,在执行的时候,会将所有收集到的参数依次传递:

1
cli.action(async (root: string, options: BuildOptions & GlobalCLIOptions) => {})

help

为命令行的所有命令创建一个 --help 命令,同时添加一个 -h 的别名

parse

用于结束命令行的参数设置,只有在执行 cli.parse() 函数后,所有的设置才会被真正应用。

version

用于提示命令行当前的具体版本

1
cli.version(VERSION)

devServer action

在了解了 cac 的主要 API 之后,我们先从 vite dev/server 开始吧。首先我们需要找到 cac 的 action 回调方法,其他的命令也是一样的,因为 action 方法是每个命令执行的起点,由这个回调函数来决定这个命令具体要做哪些事情。devServer 对 action 的处理为:

1
const { createServer } = await import('./server') // -> packages/vite/src/node/index.ts
2
try {
3
// 创建一个 devServer 实例
4
const server = await createServer({
5
root, // 这里指的是调用 vite|vite dev|vite serve 时第一个位置的参数,比如: vite ./src
6
base: options.base, // 可以通过 --base 设置起始路径:vite --base ./
7
mode: options.mode, // 可以通过 --mode 设置是开发还是生产环境:vite --mode production
8
configFile: options.config, // 可以通过 --config 来指定配置文件的文件路径:vite --config ./vite.config.js
9
logLevel: options.logLevel, // 可以通过 --log-level 设置日志打印的级别,设置 warn 后,控制台就只会输出警告和错误信息,设置为错误就只会输出错误信息,而忽略警告和普通日志:vite --log-level error
10
clearScreen: options.clearScreen, // 可以通过 --clear-screen 设置在产出新的日志时是否覆盖旧日志
41 collapsed lines
11
optimizeDeps: { force: options.force }, // 可以通过 --force 设置是否强制优化某些依赖的构建
12
server: cleanOptions(options) // 删掉所有可以解析的参数,将剩下的传递给 devServer
13
})
14
// 如果服务器未能成功创建
15
if (!server.httpServer) {
16
throw new Error('HTTP server not available')
17
}
18
// 启动服务器的监听
19
await server.listen()
20
// 获取到以启动服务器时的配置生成的日志打印器,用于打印服务器启动耗时及 url 信息
21
// 如果 log-level 被设置成 silent、error、warn 的话,则启动耗时和 url 地址都不会在控制台打印
22
const info = server.config.logger.info
23
24
// @ts-ignore
25
const viteStartTime = global.__vite_start_time ?? false // 获取服务启动前的运行时长
26
const startupDurationString = viteStartTime
27
? colors.dim(
28
`ready in ${colors.white(
29
colors.bold(Math.ceil(performance.now() - viteStartTime))
30
)} ms`
31
)
32
: ''
33
34
info(
35
`\n ${colors.green(
36
`${colors.bold('VITE')} v${VERSION}`
37
)} ${startupDurationString}\n`,
38
// 如果在这之前触发过警告或错误打印,说明这中间有异常,则将启动完成的日志清除
39
{ clear: !server.config.logger.hasWarned }
40
)
41
// 打印 Local 和获取 Network 的数量并依次打印
42
server.printUrls()
43
} catch (e) {
44
// 如果在创建服务器时出错则打印错误日志
45
createLogger(options.logLevel).error(
46
colors.red(`error when starting dev server:\n${e.stack}`),
47
{ error: e }
48
)
49
// 并结束这个进程
50
process.exit(1)
51
}

cleanOptions

1
/**
2
* removing global flags before passing as command specific sub-configs
3
*/
4
function cleanOptions<Options extends GlobalCLIOptions>(
5
options: Options
6
): Omit<Options, keyof GlobalCLIOptions> {
7
const ret = { ...options }
8
delete ret['--']
9
delete ret.c
10
delete ret.config
12 collapsed lines
11
delete ret.base
12
delete ret.l
13
delete ret.logLevel
14
delete ret.clearScreen
15
delete ret.d
16
delete ret.debug
17
delete ret.f
18
delete ret.filter
19
delete ret.m
20
delete ret.mode
21
return ret
22
}

以上.


CD ..