手把手教你写一个简版 create-vite 并发布到npm仓库

562 阅读4分钟

我正在参与掘金创作者训练营第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为例来说。

先看下执行的效果:

image.png

执行完以后,多了个my-app的文件夹: image.png

解释命令执行原理

执行上面的命令,会进行以下的操作:

  • npm 会下载 create-vite 这个包的最新版本
  • 这个包里的package.json中的lib中有一个create-vite命令(口说无凭,下面有图😂)
  • 然后会执行 create-vite my-app --template vue,其中my-app是命令的参数,这里指创建的项目所在的文件夹;--template vue是命令的选项,这里指你需要什么样的项目模板。
  • 接下来的逻辑也不难想象,就是根据我们传入的项目模板,下载下来,然后写到我们传入的文件夹下面。

image.png

yarn也好,npx也罢,其实原理都差不多。在此就不赘述了,我们进入具体的实现环节。

实现

理一下需求

  1. 实现命令行交互工具
  2. 根据传入的选项,参数,下载对应的模板
  3. 写入指定的文件夹

创建项目

mkdir create-my-cli

npm init -y

cd cretae-my-cli

code .

image.png

创建文件

按照下图或你个性化的方式创建文件和目录:

image.png

安装依赖

npm install chalk@4.1.2 commander@9.2.0 download-git-repo@3.0.2 ora@5.4.1

下面简单介绍下这几个包的作用:

chalk - npm (npmjs.com) 用于处理终端字符串样式,如果字体的颜色,背景什么的

ora - 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"
},

效果如下:

test.gif

commander - npm (npmjs.com) image.png

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的脚本,接着引入了orachalkcommander三个包。然后就是简单的定义了我们的命令,包括name, 版本version,参数argument,还有对命令的一个简单介绍。效果是啥样的呢❓

在我们看之前,先做以下配置:

package.json中配置一下bin:

"bin": {
    "create-my-cli": "src/index.js"
 },

然后执行npm link,看到以下截图就代表成功了: image.png

这样我们就可以在任何一个地方调用create-my-cli这个命令了:

test1.gif

接着我们再加一个选项,用来选模板,加一个参数(用来自定义文件夹):

//之前的代码
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)
  })
//之前的代码

再运行下,看效果:

test2.gif

成功的获取到了参数和选项。接下来就是根据传入的模板下载对应的项目到指定的文件夹。

下载模板到本地

我们先测试下,下载一个包到本地:

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))

运行下试试: test3.gif

再看下我们的文件已经成功的下载下来了:

image.png

最终实现

#!/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👻👻👻👻

test4.gif 项目也已经成功创建了。 image.png

image.png 后面还有呢!接着往下看

发布

功能实现了,接下来我们把这个包发布到npm仓库,让别人也能使用。 在发布之前,先写下README,要让别人很容易理解到你这个包的作用是啥。笔者在此先简单写下:

image.png

然后在package.json中加一下这个配置:

"files": [
    "/src",
    "README.md"
  ],

这个配置表明,只需要发布src目录和README.md这个文件。

  1. 先去注册一个npm账户,如果你没有的话,这个就略过了🤣
  2. npm login,输入密码,验证码回车即可,像下图这样就是登录成功了。 image.png
  3. npm publish 第一次发布: image.png 说没有权限发布,说明这个包名npm仓库已经有了,只能改下名字了,加个后缀吧,再发布一下: image.png 成功了,然后我们去试试:

image.png

没这个命令,因为我们把包名改了,所以得把命令也跟着改了保持一致

"bin": {
    "create-my-cli-lv": "src/index.js"
},

再发布一下: image.png

再来试试:

✅成功 test5.gif ✅项目也已经下载好了: image.png

附上笔者上传的包: create-vite-app-lv - npm (npmjs.com)

到这,咱们的内容就都完成了,感谢观看,点个赞吧