我正在参与掘金创作者训练营第6期,点击了解活动详情
前言
想必各位掘友在平时的工作中应该都使用过下面这些命令:
- Vue-cli 快速创建一个项目
vue create my-project
- 搭建第一个 Vite 项目
yarn create vite
# npm 6.x
npm create vite@latest my-vue-app --template vue
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app --template vue
# yarn
yarn create vite my-vue-app --template vue
# pnpm
pnpm create vite my-vue-app --template vue
就算你没用过上面这两个,那你也应该用过类似的,例子太多,就不一一举例了。
这些命令的作用都是快速的帮我们在当前文件夹创建一个新的项目,也就是一个脚手架工程。
掘友们有想过它是怎么实现的吗❓❓❓
如果不知道,那咱接着往下看,今天笔者就一步一步的带你实现个简版的。
原理
我们就以npm create vite@latest my-app --template vue
为例来说。
先看下执行的效果:
执行完以后,多了个my-app的文件夹:
解释命令执行原理
执行上面的命令,会进行以下的操作:
npm
会下载create-vite
这个包的最新版本- 这个包里的
package.json
中的lib
中有一个create-vite
命令(口说无凭,下面有图😂) - 然后会执行
create-vite my-app --template vue
,其中my-app
是命令的参数,这里指创建的项目所在的文件夹;--template vue
是命令的选项,这里指你需要什么样的项目模板。 - 接下来的逻辑也不难想象,就是根据我们传入的项目模板,下载下来,然后写到我们传入的文件夹下面。
用yarn
也好,npx
也罢,其实原理都差不多。在此就不赘述了,我们进入具体的实现环节。
实现
理一下需求
- 实现命令行交互工具
- 根据传入的选项,参数,下载对应的模板
- 写入指定的文件夹
创建项目
mkdir create-my-cli
npm init -y
cd cretae-my-cli
code .
创建文件
按照下图或你个性化的方式创建文件和目录:
安装依赖
npm install chalk@4.1.2 commander@9.2.0 download-git-repo@3.0.2 ora@5.4.1
下面简单介绍下这几个包的作用:
chalk - npm (npmjs.com) 用于处理终端字符串样式,如果字体的颜色,背景什么的
上面两个包经常配合起来使用,下面是一个简单的🌰:
// test.js
const ora = require('ora');
const chalk = require('chalk');
const spinner = ora(`Loading ${chalk.red('unicorns')}`).start();
setTimeout(() => spinner.stop(), 2000)
在package.json
中配置一个脚本,看看效果:
// package.json
"scripts": {
"test": "node ./src/test.js"
},
效果如下:
download-git-repo - npm (npmjs.com)下载github仓库中的工程,就像git clone
那样
大家感兴趣可以去看官方的文档,有详细的介绍和具体的使用示例,在此不过多赘述了。
命令行交互实现
#!/usr/bin/env node
// 进度
const ora = require('ora')
// 字体
const chalk = require('chalk')
// 命令行交互
const { Command, Argument } = require('commander')
const { log } = console
const program = new Command()
program
.name('create-my-cli')
.version('1.0.0', '-v, --version')
.argument('<project-directory>')
.description('clone a optional repository into a newly created <project-directory>')
program.parse()
简要解释下上面的代码:
第一行#!/usr/bin/env node
是指明,这是一个node
的脚本,接着引入了ora
,chalk
,commander
三个包。然后就是简单的定义了我们的命令,包括name
, 版本version
,参数argument
,还有对命令的一个简单介绍。效果是啥样的呢❓
在我们看之前,先做以下配置:
在package.json
中配置一下bin
:
"bin": {
"create-my-cli": "src/index.js"
},
然后执行npm link
,看到以下截图就代表成功了:
这样我们就可以在任何一个地方调用create-my-cli
这个命令了:
接着我们再加一个选项,用来选模板,加一个参数(用来自定义文件夹):
//之前的代码
program
.name('create-my-cli')
.version('1.0.0', '-v, --version')
.argument('[project-directory]', 'directory', 'your-app')
.option('-t --template <template>', 'choose a template')
.description('clone a optional repository into a newly created <project-directory>')
.action((directory) => {
const options = program.opts();
log(options.template)
log(directory)
})
//之前的代码
再运行下,看效果:
成功的获取到了参数和选项。接下来就是根据传入的模板下载对应的项目到指定的文件夹。
下载模板到本地
我们先测试下,下载一个包到本地:
const ora = require('ora')
const chalk = require('chalk')
const spinner = ora(`Loading ${chalk.red('unicorns')}`).start()
const { rmdirSync } = require('fs')
const { log } = console
const downloadGitRepo = require('download-git-repo')
function download(url, directory) {
return new Promise((resolve, reject) => {
rmdirSync(directory, { recursive: true, force: true })
downloadGitRepo(`direct:${url}`, directory, { clone: true }, err => {
if (err) return reject(err)
resolve()
spinner.stop()
})
})
}
download('https://gitee.com/lvzhenglei/vue-next-lib-template.git', 'testdownload')
.then(() => {
log('SUCCESS')
})
.catch(err => log(err))
运行下试试:
再看下我们的文件已经成功的下载下来了:
最终实现
#!/usr/bin/env node
// 进度
const ora = require('ora')
// 字体
const chalk = require('chalk')
const { rmdirSync } = require('fs')
const downloadGitRepo = require('download-git-repo')
// 命令行交互
const { Command, Argument } = require('commander')
const { log } = console
const program = new Command()
// 可下载的模板
const repositoryMap = {
basic: 'useVue3',
admin: 'vue3-demo-admin',
lib: 'vue-next-lib-template'
}
function download(url, directory) {
return new Promise((resolve, reject) => {
rmdirSync(directory, { recursive: true, force: true })
downloadGitRepo(`direct:${url}`, directory, { clone: true }, err => {
if (err) return reject(err)
resolve()
})
})
}
let start, end
program
.name('create-my-cli')
.version('1.0.0', '-v, --version')
.argument('[project-directory]', 'directory', 'your-app')
.option('-t --template <template>', 'choose a template')
.description('clone a optional repository into a newly created <project-directory>')
.action((directory) => {
const options = program.opts();
log(chalk.red.bold(`Welcome to create-my-cli`))
if (options.template && !Reflect.has(repositoryMap, options.template)) {
log(`传入的模板: ${options.template}不可用,可选的有: basic, admin, lib`)
}
log(`Wait a mininute, ${chalk.green.bold('App template')} will be downloaded to ${chalk.green.bold(directory)}`)
start = Date.now()
const spinner = ora(chalk.hex('#DEADED').bold("👻 I'm trying......")).start()
const repository = options.template ? repositoryMap[options.template] || 'useVue3' : 'useVue3'
spinner.color = 'green'
download(`https://gitee.com/lvzhenglei/${repository}.git`, directory)
.then(() => {
end = Date.now()
spinner.stop()
log(chalk.green.bold(`success in ${(end - start) / 1000} s`))
log(`
then, you can do like this:
1. cd ${directory}
2. yarn / npm i
3. yarn dev / npm run dev
`)
})
.catch(err => {
log(chalk.red.bold('fail, reason:', err))
spinner.stop()
})
})
program.parse()
👻👻👻👻Show Time👻👻👻👻
项目也已经成功创建了。
后面还有呢!接着往下看
发布
功能实现了,接下来我们把这个包发布到npm
仓库,让别人也能使用。
在发布之前,先写下README
,要让别人很容易理解到你这个包的作用是啥。笔者在此先简单写下:
然后在package.json
中加一下这个配置:
"files": [
"/src",
"README.md"
],
这个配置表明,只需要发布src
目录和README.md
这个文件。
- 先去注册一个
npm
账户,如果你没有的话,这个就略过了🤣 npm login
,输入密码,验证码回车即可,像下图这样就是登录成功了。npm publish
第一次发布: 说没有权限发布,说明这个包名npm
仓库已经有了,只能改下名字了,加个后缀吧,再发布一下: 成功了,然后我们去试试:
没这个命令,因为我们把包名改了,所以得把命令也跟着改了保持一致
"bin": {
"create-my-cli-lv": "src/index.js"
},
再发布一下:
再来试试:
✅成功 ✅项目也已经下载好了:
附上笔者上传的包: create-vite-app-lv - npm (npmjs.com)
到这,咱们的内容就都完成了,感谢观看,点个赞吧