开发自己的项目模板cli
为什么要开发这个?
每次创建项目都有一堆配置太麻烦,轻则花1-2小时找配置,配置没找对可能延误1-2天,把项目模板放在git托管平台,每次都去下载不就行了!可以,但做法太low
分析
- 提供一个模板选择列表,让选择是要创建什么样的模板
- 提供一个配置文件,能配置自己可选的项目模板地址
- 选择完模板之后,提示输入项目名称,然后自动从git远程仓库下载该模板,并用该模板创建项目
需要用到的库
- download-git-repo: node拉取git公共仓库的库
- inquirer: 交互式命令行工具
- ora: 用于显示加载中等待提示
开始开发
添加依赖
pnpm add download-git-repo inquirer ora
pnpm add @types/inquirer ts-node typescript @types/node -D
修改package.json
json
{
"name": "demo01",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"download-git-repo": "^3.0.2",
"gitly": "^2.2.2",
"inquirer": "^9.1.4",
"ora": "^6.1.2"
},
"devDependencies": {
"@types/inquirer": "^9.0.3",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"
}
}
项目根目录添加 tsconfig.json
json
{
"compilerOptions": {
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "esnext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"strict": true, /* Enable all strict type-checking options. */
"moduleResolution": "node",
"baseUrl": "./",
"paths": {
"@/*": [ "./src/*" ]
},
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}
编写/测试运行代码
demo01.ts
ts
import inquirer, { QuestionCollection } from 'inquirer'
//@ts-ignore
import download from 'download-git-repo'
import path from 'path'
import ora from 'ora'
interface IPromptOption {
/**
* 项目名
*/
projectName: string
/**
* 下载的模板名
*/
templateName: string
}
interface TemplateInfo {
// 模板压缩文件下载地址
downloadUrl: string
// 模板描述
desc: string
}
const repositoryList: Record<string, TemplateInfo> = {
vue3: {
downloadUrl:
'https://gitcode.net/pzy_666/front-project-template/-/archive/vue3/front-project-template-vue3.zip',
desc: 'vue3项目基础模板',
},
'vue3-element-plus': {
downloadUrl:
'https://gitcode.net/pzy_666/front-project-template/-/archive/vue3-element-plus/front-project-template-vue3-element-plus.zip',
desc: 'vue3项目基础模板上整合进element-plus',
},
'vue3-vant': {
downloadUrl:
'https://gitcode.net/pzy_666/front-project-template/-/archive/vue3-vant/front-project-template-vue3-vant.zip',
desc: 'vue3项目基础模板上整合进vant',
},
'vitepress-doc': {
downloadUrl:
'direct:https://gitcode.net/pzy_666/front-project-template/-/archive/vitepress-doc/front-project-template-vitepress-doc.zip',
desc: '用于编写文档的vitepress模板',
},
}
const projectTemplateChoices = Object.keys(repositoryList).map(
propertyName => ({
name: repositoryList[propertyName].desc,
value: propertyName,
})
)
/**
* 问题确认列表
*
* @var {[type]}
*/
const PROMPT_LIST: QuestionCollection = [
{
type: 'input',
message: '请输入项目名',
name: 'projectName',
default: 'demo',
},
{
type: 'list',
message: '请选择需要的项目模板',
name: 'templateName',
choices: [
{ name: 'vue3项目基础模板', value: 'vue3' },
{
name: 'vue3项目基础模板上整合进element-plus',
value: 'vue3-element-plus',
},
{ name: 'vue3项目基础模板上整合进vant', value: 'vue3-vant' },
{ name: '用于编写文档的vitepress模板', value: 'vitepress-doc' },
],
},
]
function entry() {
inquirer.prompt<IPromptOption>(PROMPT_LIST).then(async answer => {
const newOra = ora('开始创建项目...').start()
const CURRENT_PATH = process.cwd() // 获取当前路径
const downloadUrl = `direct:${
repositoryList[answer.templateName].downloadUrl
}`
const targetPath = path.resolve(CURRENT_PATH, answer.projectName) // 目标路径
await download(downloadUrl, targetPath, {}, (err: any) => {
if (err) {
newOra.fail(`项目创建失败:${err.message}`)
console.log(err)
}
newOra.succeed(`项目创建成功
cd ${answer.projectName}
pnpm install
pnpm run dev
`)
})
})
}
entry()
运行代码
node --loader ts-node/esm .\demo01.ts