Libon

Vite 源码分析(二)

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

ToC

cac API

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

initial

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

{
  "bin": {
    "vite": "./test.js"
  }
}

test.js 如下:

#!/usr/bin/env node

import { cac } from 'cac'

const cli = cac()

cli.help()
cli.parse()

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

vite

Usage:
  $ vite <command> [options]

Options:
  -h, --help  Display this message

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

test-vite

Usage:
  $ test-vite <command> [options]

Options:
  -h, --help  Display this message

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

command

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

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

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

Commands:
  [root]  start dev server

alias

给命令创建别名

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

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

option

给命令添加选项参数

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

action

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

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

help

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

parse

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

version

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

cli.version(VERSION)

devServer action

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

const { createServer } = await import('./server') // -> packages/vite/src/node/index.ts
  try {
    // 创建一个 devServer 实例
    const server = await createServer({
      root, // 这里指的是调用 vite|vite dev|vite serve 时第一个位置的参数,比如: vite ./src
      base: options.base, // 可以通过 --base 设置起始路径:vite --base ./
      mode: options.mode, // 可以通过 --mode 设置是开发还是生产环境:vite --mode production
      configFile: options.config, // 可以通过 --config 来指定配置文件的文件路径:vite --config ./vite.config.js
      logLevel: options.logLevel, // 可以通过 --log-level 设置日志打印的级别,设置 warn 后,控制台就只会输出警告和错误信息,设置为错误就只会输出错误信息,而忽略警告和普通日志:vite --log-level error
      clearScreen: options.clearScreen, // 可以通过 --clear-screen 设置在产出新的日志时是否覆盖旧日志
      optimizeDeps: { force: options.force }, // 可以通过 --force 设置是否强制优化某些依赖的构建
      server: cleanOptions(options) // 删掉所有可以解析的参数,将剩下的传递给 devServer
    })
		// 如果服务器未能成功创建
    if (!server.httpServer) {
      throw new Error('HTTP server not available')
    }
		// 启动服务器的监听
    await server.listen()
    // 获取到以启动服务器时的配置生成的日志打印器,用于打印服务器启动耗时及 url 信息
    // 如果 log-level 被设置成 silent、error、warn 的话,则启动耗时和 url 地址都不会在控制台打印
    const info = server.config.logger.info

    // @ts-ignore
    const viteStartTime = global.__vite_start_time ?? false // 获取服务启动前的运行时长
    const startupDurationString = viteStartTime
      ? colors.dim(
          `ready in ${colors.white(
            colors.bold(Math.ceil(performance.now() - viteStartTime))
          )} ms`
        )
      : ''

    info(
      `\n  ${colors.green(
        `${colors.bold('VITE')} v${VERSION}`
      )}  ${startupDurationString}\n`,
			// 如果在这之前触发过警告或错误打印,说明这中间有异常,则将启动完成的日志清除
      { clear: !server.config.logger.hasWarned }
    )
		// 打印 Local 和获取 Network 的数量并依次打印
    server.printUrls()
  } catch (e) {
		// 如果在创建服务器时出错则打印错误日志
    createLogger(options.logLevel).error(
      colors.red(`error when starting dev server:\n${e.stack}`),
      { error: e }
    )
		// 并结束这个进程
    process.exit(1)
  }

cleanOptions

/**
 * removing global flags before passing as command specific sub-configs
 */
function cleanOptions<Options extends GlobalCLIOptions>(
  options: Options
): Omit<Options, keyof GlobalCLIOptions> {
  const ret = { ...options }
  delete ret['--']
  delete ret.c
  delete ret.config
  delete ret.base
  delete ret.l
  delete ret.logLevel
  delete ret.clearScreen
  delete ret.d
  delete ret.debug
  delete ret.f
  delete ret.filter
  delete ret.m
  delete ret.mode
  return ret
}

以上.


CD ..