vue实现简易脚手架
前言:
为什么需要自己的脚手架
- 公司内部后台管理项目架构基本一样,每次新建项目都要拷贝重命名
- 没有权限或者不知道框架的存在
- 通过简单命令一键下载,快速帮助我们搭建预定义好的项目模板
实现过程:
一:脚手架组织结构
二:配置package.json
文件
配置完成后,执行npm install命令【不建议修改dependencies版本】
{
"name": "ifweb",
"version": "0.0.1",
"description": "后台管理系统脚手架",
"bin": {
"ifweb": "./bin/cli.js"
},
"scripts": {
"watch": "npm run compile -- --watch"
},
"keywords": [
"fweb-cli",
"fweb",
"后台管理",
"管理系统",
"脚手架"
],
"license": "MIT",
"dependencies": {
"axios": "^0.21.1",
"chalk": "^4.1.2",
"commander": "^8.1.0",
"download-git-repo": "^3.0.2",
"figlet": "^1.5.2",
"fs-extra": "^10.0.0",
"inquirer": "^8.1.2",
"ora": "^5.4.0"
}
}
三:配置command
目录
3.1:配置http.js
文件内容
const axios = require('axios')
axios.interceptors.response.use((res) => {
return res.data
})
/**
* 获取模版列表
* @return Promise
*/
async function getRepoList() {
return axios.get('https://api.github.com/orgs/xxx/xxx/repos')
}
/**
* 获取版本信息
* @param {string} repo 模版名称
*
*/
async function getTagList(repo) {
return axios.get(
`https://api.github.com/repos/xxx/xxx/${repo}/tags`
)
}
module.exports = {
getRepoList,
getTagList
}
3.2:配置generator.js
文件内容
const { getRepoList, getTagList } = require('./http')
const ora = require('ora')
const inquirer = require('inquirer')
const util = require('util')
const path = require('path')
const downloadGit = require('download-git-repo')
const chalk = require('chalk')
/**
* 添加动画
* @param {*} fn
* @param {*} message
* @param {...any} args
* @returns
*/
const drawLoading = async (fn, message, ...args) => {
// 使用 ora 初始化,传入提示信息 message
const spinner = ora(message)
// 开始加载动画
spinner.start()
try {
// 执行传入方法 fn
const result = await fn(...args)
// 状态修改为成功
spinner.succeed()
return result
} catch (error) {
// 状态修为失败
spinner.fail('Request fail, refetch ....', error)
}
}
class Generator {
constructor(name, targetDir) {
// 目录名称
this.name = name
// // 创建位置
this.targetDir = targetDir
// 对 download-git-repo 进行 promise 化改造
this.downloadGit = util.promisify(downloadGit)
}
/**
* 获取用户选择的模板
* @returns 用户选择的模板名称
*/
async getRepo() {
const repoList = await drawLoading(getRepoList, 'wait fetch template')
if (!repoList) return
const repos = repoList.map((item) => item.name)
const { repo } = await inquirer.prompt({
name: 'repo',
type: 'list',
choices: repos,
message: 'Please choose a template to create project'
})
return repo
}
/**
* 获取用户选择的版本
* @param {*} repo 已选择模板
* @returns 选择的 tag
*/
async getTag(repo) {
// 基于 repo 结果,远程拉取对应的 tag 列表
const tags = await drawLoading(getTagList, 'waiting fetch tag', repo)
if (!tags) return
// 过滤需要的 tag 名称
const tagsList = tags.map((item) => item.name)
// 用户选择自己需要下载的 tag
const { tag } = await inquirer.prompt({
name: 'tag',
type: 'list',
choices: tagsList,
message: 'Place choose a tag to create project'
})
return tag
}
/**
* 下载远程模板tag
*/
async download(repo, tag) {
// 下载地址
const requestUrl = `xxx/${repo}${tag ? '#' + tag : ''}`
// 调用下载方法
await drawLoading(
this.downloadGit,
'waiting download template',
requestUrl,
path.resolve(process.cwd(), this.targetDir)
)
}
/**
* 核心创建逻辑
*/
async create() {
const repo = await this.getRepo()
const tag = await this.getTag(repo)
// 下载模板到模板目录
await this.download(repo, tag)
// 模板使用提示
console.log(`\r\nSuccessfully created project ${chalk.cyan(this.name)}`)
console.log(`\r\n cd ${chalk.cyan(this.name)}`)
console.log(' npm run dev\r\n')
}
}
module.exports = Generator
3.3:配置init.js
文件内容
const path = require('path')
const fs = require('fs-extra')
const inquirer = require('inquirer')
const Generator = require('./generator')
module.exports = async function (name, options) {
// 当前命令行选择的目录
const cwd = process.cwd()
// 需要创建的目录地址
const targetAir = path.join(cwd, name)
// 目录是否已经存在
if (fs.existsSync(targetAir)) {
if (options.force) {
await fs.remove(targetAir)
} else {
// 询问用户是否确定要覆盖
let { action } = await inquirer.prompt([
{
name: 'action',
type: 'list',
message: 'Target directory already exists Pick an actions:',
choices: [
{
name: 'Overwrite',
value: 'overwrite'
},
{
name: 'Cancel',
value: false
}
]
}
])
if (!action) {
return
} else if (action === 'overwrite') {
//移除已经存在的目录
console.log(`\r\nRemoving`)
await fs.remove(targetAir)
}
}
}
const generator = new Generator(name, targetAir)
generator.create()
}
四:配置bin
目录
4.1 配置cli.js
文件内容
#! /usr/bin/env node
const commander = require('commander')
const chalk = require('chalk')
var figlet = require('figlet')
commander
.command('create <app-name>')
.description('create a new project')
.option('-f, --force', 'overwrite target directory if it exit')
.action((name, options) => {
console.log('name:', name, 'option:', options)
require('../command/init.js')(name, options)
})
.version(`v${require('../package.json').version}`)
.usage('<command> [option]')
// 配置 config 命令
commander
.command('config [value]')
.description('inspect and modify the config')
.option('-g, --get <path>', 'get value from option')
.option('-s, --set <path> <value>')
.option('-d, --delete <path>', 'delete option from config')
.action((value, options) => {
console.log(value, options)
})
commander.on('--help', () => {
// 使用 figlet 绘制 Logo
console.log(
'\r\n' +
figlet.textSync('FWEB', {
font: 'Ghost',
horizontalLayout: 'default',
verticalLayout: 'default',
width: 80,
whitespaceBreak: true
})
)
// 新增说明信息
console.log(
`\r\nRun ${chalk.cyan(`ifweb <command> --help`)} show details\r\n`
)
})
commander.parse(process.argv)
五:执行命令
5.1 本地调试
打开项目终端,执行npm link
链接到全局
5.2 全局安装脚手架
// 打开终端执行:
npm install -g ifweb
// 根据自身情况,选择磁盘执行:
cd code
ifweb create <project-name>
5.3 测试通过,脚手架发布到npm
注册npm账号:www.npmjs.com npm镜像恢复为原始路径:npm config set registry registry.npmjs.org/ 打开项目终端,执行:
// 添加npm用户
npm adduser
// 发布到npm
npm publish
六:获取模板信息
6.1 代码已上传至github/gitlab仓库
6.2 github:仓库获取地址
api.github.com/orgs/xxx/xxx/repos
6.3 github:仓库版本获取地址
api.github.com/repos/xxx/xxx
转载自:https://juejin.cn/post/7234795795215990845