- 本文参加了由公众号@若川视野 发起的每周源码共读活动, 点击了解详情一起参与。
- 这是源码共读的第37期,链接
准备
-
将大佬准备好的代码拉下来
git clone https://github.com/lxchuan12/vite-analysis.git -
安装包管理工具和依赖
cd vite-analysis/vite2 # 体验一下pnpm npm i -g pnpm pnpm i -
用vs code打开clone的代码,
ctrl+shift+P, 输入Auto Attach打开自动附加智能模式,这样调试一遍代码就知道create-vite是如何工作的了。
调试代码
我理解的npm create vite@lastest 干了啥:将你选定的模板复制到你命名的项目目录下,并告诉你这个项目怎么跑起来。
# 跑一下create-vite的入口文件,vs Code就自动进入调试模式了,按F10(单步跳过)查看代码如何执行的。
node vite2/packages/create-vite/index.js
下面逐行解释一下init 方法!
命令中获取项目存放位置目录和模板
let targetDir = formatTargetDir(argv._[0])
let template = argv.template || argv.t
const defaultTargetDir = 'vite-project' // 项目存放位置也是项目默认Project name
const getProjectName = () =>
targetDir === '.' ? path.basename(path.resolve()) : targetDir
argv._[0] 获取项目要存放的位置,例:执行node vite2/packages/create-vite/index.js ./my-vite, 则argv._ 的值为['.\my-vite\'] 。调试项目时并未指定位置,也未指定模板,所以targetDir和template的值均为undefined;getProjectName方法,如果是指定当前目录,则返回当前目录名称,否则返回targetDir。
获取用户输入的Project name和选择的模板名称等
let result = {}
try {
result = await prompts(
[
// ......
]
)
} catch (cancelled) {
console.log(cancelled.message)
return
}
// user choice associated with prompts
const { framework, overwrite, packageName, variant } = result
prompts方法会在终端和用户进行交互,最终拿到用户输入的值,下方是我选择的值:
最终result的值:
由于我选的默认值vite-project也是一个空目录或者说是一个合法目录,不需要清空vite-project目录下的文件,所以result中没有overwrite属性。
确定用户项目存放路径和用户选择的模板在vite中存放的位置
const root = path.join(cwd, targetDir)
// root: 'F:\vite-analysis\vite2\vite-project'
// 创建root目录,就是用户创建项目的那个目录
if (overwrite) {
emptyDir(root)
} else if (!fs.existsSync(root)) {
fs.mkdirSync(root, { recursive: true })
}
// determine template
template = variant || framework || template
console.log(`\nScaffolding project in ${root}...`)
// import.meta.url返回当前模块的 URL 路径。
// 最终拿到vite中模板存放目录
// templateDir: F:\vite-analysis\vite2\packages\create-vite\template-react-ts
const templateDir = path.resolve(
fileURLToPath(import.meta.url),
'..',
`template-${template}`
)
将vite中的模板复制到用户创建的目录
// copy 文件
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)
// files: (8) ['index.html', 'package.json', 'public', 'src', 'tsconfig.json', 'tsconfig.node.json', 'vite.config.ts', '_gitignore']
// 这里复制了除了package.json的其他所有文件,因为要修改package.json的内容,所以没有直接复制过去
for (const file of files.filter((f) => f !== 'package.json')) {
write(file)
}
复制package.json文件
// 读取package.json
const pkg = JSON.parse(
fs.readFileSync(path.join(templateDir, `package.json`), 'utf-8')
)
// 修改name
pkg.name = packageName || getProjectName()
// 复制package.json
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
}
到此,init函数走完,项目创建完成。