【若川视野 x 源码共读】第37期 | create-vite 如何初始化一个项目

184 阅读2分钟

前言

这是我第一次在掘金写源共读文章,希望大家一起来参加学习写文章

1.准备源码

//源码地址
https://github.com/vitejs/vite/blob/HEAD/packages/create-vite/index.js
//复制一份代码到自己的仓库
git clone https://github.com/vitejs/vite.git
//安装依赖
pnpm i 
//然后找到packages目录下的create-vite的index.js开始学习源码

2.学习源码

用到的库

import fs from 'node:fs' //文件操作
import path from 'node:path'  //路径操作
import {
  fileURLToPath
} from 'node:url'  //将文件URL解码为路径字符串
import minimist from 'minimist' // 解析命令行参数
import prompts from 'prompts' // 询问选择 
import {
  blue,
  cyan,
  green,
  lightRed,
  magenta,
  red,
  reset,
  yellow
} from 'kolorist' // 终端颜色输出

formatTargetDir 利用正则将反斜杠/替换 为空字符串

function formatTargetDir(targetDir) {
 return targetDir ? targetDir.trim().replace(/\/+$/g, ''):''}

getProjectName 获取项目名称

  • path.resolve() 当前目录的绝对路径
  • path.basename() 最后一截路径
  const getProjectName = () =>
    targetDir === '.' ? path.basename(path.resolve()) : targetDir

询问项目名、选择框架,选择框架变体等

 try {
    result = await prompts(
      [{
          type: targetDir ? null : 'text',
          name: 'projectName',
          message: reset('Project name:'),
          initial: defaultTargetDir,
          onState: (state) => {
            targetDir = formatTargetDir(state.value) || defaultTargetDir
          }
        },
....

创建文件夹


  const root = path.join(cwd, targetDir)
  if (overwrite) {
    //如果当前目录出现相同名字的输出目录并选择了覆盖,则清空文件夹内的文件
    emptyDir(root)
  } else if (!fs.existsSync(root)) {
    // 新建文件夹
    fs.mkdirSync(root, {
      recursive: true
    })
  }

获取模板文件

    // determine template  选择的项目模板
  template = variant || framework || template

  const templateDir = path.resolve(
    fileURLToPath(
      import.meta.url),
    '..',
    `template-${template}`
  )

写入文件和项目名

  //写入文件函数
  const write = (file, content) => {
    const targetPath = renameFiles[file] ?
      path.join(root, renameFiles[file]) :
      path.join(root, file)
    if (content) {
      fs.writeFileSync(targetPath, content)
    } else {
      copy(path.join(templateDir, file), targetPath)
    }
  }
//根据模板路径的文件写入目标路径
  const files = fs.readdirSync(templateDir)
  for (const file of files.filter((f) => f !== 'package.json')) {
    write(file)
  }
  const pkg = JSON.parse(
      fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8')
  )

//把项目名称写入package.json
  pkg.name = packageName || getProjectName()

  write('package.json', JSON.stringify(pkg, null, 2))

安装完提示

// 打印安装完成后的信息,用哪个包管理器就提示哪个包管理器的运行命令
  const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent)
  const pkgManager = pkgInfo ? pkgInfo.name : 'npm'

  console.log(`\nDone. Now run:\n`)
  if (root !== cwd) {
    console.log(`  cd ${path.relative(cwd, root)}`)
  }
  switch (pkgManager) {
    case 'yarn':
      console.log('  yarn')
      console.log('  yarn dev')
      break
    default:
      console.log(`  ${pkgManager} install`)
      console.log(`  ${pkgManager} run dev`)
      break
  }

3.流程总结

  • 根据输入项目名称创建目录,如果已存在改目录,提示是否覆盖
  • 根据选择的项目模板及变体(ts)读取对应的框架模板写入到项目名称目录,重写package.json的项目名称
  • 使用了哪个包管理器创建项目,那么就输出 npm/yarn/pnpm 相应的命令提示运行