前端脚手架搭建(part4)动态插入

例如控制模板的name、选择使用的插件,动态插入

之前vue模板是有选择是否使用pinia、unocss,通过用户的选择,在项目中动态配置插件

需要用到ejs读取模板,然后动态修改

npm install ejs

在libs/utils/index.js添加ejs模板的操作函数

import fs from 'fs-extra'
import path from 'path'
import ejs from 'ejs'

const {existsSync,statSync,readdirSync,rmdirSync,unlinkSync} = fs;
/**
* 文件读写操作
* @param {string} targetDir 当期目录
* @param {string} filename  文件名
* @param {object} answer 回答
*/
export function fileControl(targetDir, filename, answer) {
    fs.readFile(path.join(targetDir, filename)).then(res => {
        const str = res.toString()
        const html = ejs.render(str, answer)
        fs.writeFile(path.join(targetDir, filename), html)
    })
}
/**
* 判断是否有同名文件夹
* @param {string} projectName  文件名
*/
export const isFileExist = (projectName) => {
    const targetDir = path.join(process.cwd(), projectName)
    return fs.existsSync(targetDir)
  }
  /*path:文件名*/
export function delPath(path) {
    if (!existsSync(path)) {
      console.log("路径不存在");
      return "路径不存在";
    }
    const info = statSync(path);
    if (info.isDirectory()) {//目录
      const data = readdirSync(path);
      if (data.length > 0) {
        for (let i = 0; i < data.length; i++) {
          delPath(`${path}/${data[i]}`); //使用递归
          if (i == data.length - 1) { //删了目录里的内容就删掉这个目录
            delPath(`${path}`);
          }
        }
      } else {
        rmdirSync(path);//删除空目录
        return true
      }
    } else if (info.isFile()) {
      unlinkSync(path);//删除文件
      return true
    }
  }

在libs/data/vue.config.json

[
    {
      "name": "pinia",
      "message": "是否使用pinia", 
      "type": "confirm",
      "default": true
    },
    {
        "name": "unocss",
        "message": "是否使用unocss", 
        "type": "confirm",
        "default": true
      }
  ]
  

在libs/data/react.config.json

[ 
{
    "name": "mobx",
    "message": "是否使用mobx", 
    "type": "confirm",
    "default": true
  },
    {
        "name": "unocss",
        "message": "是否使用unocss", 
        "type": "confirm",
        "default": true
      }
  ]
  

libs/constant.js

import fs from 'fs-extra'
import path from 'path'
import { fileURLToPath } from 'url'
const __filenameNew = fileURLToPath(import.meta.url)
const __dirnameNew = path.dirname(__filenameNew)

// 根目录
export const rootPath = __dirnameNew.slice(0, __dirnameNew.length - 4)

// 获取package.json文件内容
export const packageJsonData = JSON.parse(fs.readFileSync(rootPath + 'package.json', 'utf8'))


// 获取当前模板列表
export const questionList = JSON.parse(fs.readFileSync(rootPath + 'libs/data/demo.config.json', 'utf8'))

//vue插件配置
export const vueConfigList = JSON.parse(fs.readFileSync(rootPath + 'libs/data/vue.config.json', 'utf8'))
//react 插件配置
export const reactConfigList = JSON.parse(fs.readFileSync(rootPath + 'libs/data/react.config.json', 'utf8'))

libs/command/index.js

主要更改handleDownLoad 逻辑


import downLoad from 'download-git-repo'
import path from 'path'
import inquirer from 'inquirer'
import ora from 'ora';
import chalk from 'chalk';
import fs from 'fs-extra'
import {questionList,vueConfigList,reactConfigList} from '../constant.js';
import {isFileExist,fileControl,delPath} from '../utils/index.js'
//设置检测重名的问题交互
const folder = [
    {
        name: "folder",
        message: "当前目录已存在,请选择",
        type: "list",
        choices: [
            {
                name: "覆盖",
                value: "cover"
            },
            {
                name: "重命名",
                value: "rename"
            }
        ]
    },
    {
        name: 'newName',
        // 这里设置当前面用户选择重命名后,让用户输入新名称
        when: answer => answer.folder === 'rename',
        type: 'input',
        message: '目录名称为:'
    }

]

const handleDownLoad = (projectName) => {
    inquirer.prompt(questionList).then(res => {
        const targetDir = path.join(process.cwd(), projectName)
        // 获取到第一项中,用户选择的值,这里偷了个懒,大家可以根据答案和问题获取对应下载链接。
        const info = questionList[0].choices.find(item => item.value === res.features)
        const configAnswer = res.features === 'vue' ? vueConfigList : reactConfigList
        inquirer.prompt(configAnswer).then(result => {
          /*** 初始化loading图标文字 start */
          const spinner = ora('模版下载中 ...')
          /*** 初始化loading图标文字 end */
          spinner.start()
          downLoad(`direct:${info.link}`, targetDir, { clone: true }, (err) => {
            if (err) {
              spinner.fail()
              console.log(chalk.red(err))
              console.log(chalk.red('获取模版失败'))
            } else {
              // 获取所有的用户问题答案,并以用户create名称为项目名称
              const answer = { name: projectName, ...res, ...result }
              // 处理package.json对应交互变更
              fileControl(targetDir, 'package.json', answer)
              //处理vite.config.ts
              fileControl(targetDir, 'vite.config.ts', answer)
              //处理index.html
              fileControl(targetDir, 'index.html', answer)
              if (answer.features === 'vue') {
                // 处理入口文件相关变更main.js
                fileControl(targetDir, '/src/main.ts', answer)
                if (!answer.pinia) {
                  delPath(path.join(targetDir, '/src/store'))
                }
              } else if (answer.features === 'react') {
                fileControl(targetDir, '/src/main.tsx', answer)
                fileControl(targetDir, '/src/views/index/index.tsx', answer)
                if (!answer.mobx) {
                  delPath(path.join(targetDir, '/src/store'))
                }
              }
              const spinner = ora('获取模版成功')
              spinner.succeed()
              // console.log(chalk.green('获取模版成功'))
    
            }
            spinner.stop()
          })
        })
    
      })
}
const handleChecKName = (name) => {
    // 清空控制台
    console.clear()
    if (isFileExist(name)) {
        inquirer.prompt(folder).then(res => {
            if (res.folder === 'cover') {
                const targetDir = path.join(process.cwd(), name)
                const loadingMsg = ora(`正在删除 ${chalk.cyan(targetDir)}...`)
                loadingMsg.start()
                fs.remove(targetDir).then(val => {
                    loadingMsg.succeed()
                    loadingMsg.stop()
                    handleDownLoad(name)
                })
            } else {
                handleChecKName(res.newName)
            }
        })
    } else {
        handleDownLoad(name)
    }
}
export default handleChecKName;

执行命名npx mfex-project create mfex-react

就会把相关配置引入

不使用unocss和mobox

前端脚手架搭建结束,下面可以发到npm上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值