实现一个自己的脚手架

发布时间 2023-04-25 09:07:19作者: spongeCoder

简介

本文章主要是记录一下,前端实现一个脚手架的过程。本文章创建的脚手架实现了模版创建以及从git拉取两个功能,自定义的功能暂未实现。

npm地址

git仓库地址

需要准备的依赖

1、inquirer : 是常规交互式命令行用户接口的集合,提供给 Node.js 一个方便嵌入,漂亮的命令行接口node ,会简化询问终端用户问题,解析,验证答案,提供错误反馈等等功能;

2、ora:一个终端loading动画;

3、shelljs:用于执行shell操作;

4、chalk:终端显示文字美化;

开始创建脚手架

一、首先初始化node项目

npm init -y 创建 package.json,并在package.json文件中添加,bin里边的key将是你的全局命令而value是你的命令的入口文件;

"bin": {"mycli": "index.js"  },

二、创建脚手架

首先在入口文件的顶部写上如下代码,这句代码的意思是使用node解释器,对代码进行解释;

#!/usr/bin/env node
1、获取要创建的项目名称以及项目源(template模板、git)
   // 问题列表 
  const projectTypeList = [
        {
            type: "input",
            message: "请输入项目名称",
            name: "name"
        },
        {
            type: 'list',
            message: '选择项目类型',
            name: 'projectType',
            choices: ['template', 'custom', 'git', 'updateTemplate'] // 通过fs模块读取tempate目录下的目录列表
        }
    ]
  
    // 获取输入答案
    let { projectType, name } = await inquirer.prompt(projectTypeList);
        // 获取执行命令的路径
       let _path = process.cwd();
        // 进入
        shell.cd(_path)
        // 判断是否在目录下已存在重名文件
        await hasFile(name)
2、判断目录下是否存在同名文件

判断目录下是否存在同名文件,如果存在就询问是否删除,不删除结束进程;

async function hasFile(name) {

    if (existsSync(name)) {

        ora().warn(chalk.red(`目录下已存在${name}文件夹`))

        var questions = [
            {
                type: 'confirm',
                name: 'isRemoveDir',
                message: `是否删除 ${name} ?`,
                default: false,
            }
        ]

        try {
            const { isRemoveDir } = await inquirer.prompt(questions)
            if (isRemoveDir) {
                shell.rm('-rf', name);
                ora().succeed(chalk.green(chalk.green(`删除 ${name}成功`)))
            } else {
                ora().fail(chalk.red("项目构建失败"))
                process.exit();
            }

        } catch (error) {
            log(chalk.red('createProject错误:', error))
        }
    }

}
3、创建项目

使用fs模块读取本地存在模板并返回给用户,用户选择模版,使用shelljs 将用户选择的模版复制到当前文件夹下;

  const templateList = [
        {
            type: 'list',
            message: '请选择项目类型',
            name: 'template',
            choices: readdirSync(path.join(__dirname, 'template')) // 通过fs模块读取tempate目录下的目录列表
        }
    ]

        const { template } = await inquirer.prompt(templateList)


            let np = path.join(__dirname, 'template', template);

            shell.cp('-R', np + '/', name) // shell cp

            ora().succeed(chalk.green(`拉取 '${template}' 项目成功!`))
  
             shell.cd(name) // shell cd
4、安装依赖

询问用户是否需要自动安装依赖,此处使用exec另开一个进程执行依赖安装操作,如果在此进程进行会阻塞进程,loading动画将会停止;

  const autoInstall = [
                {
                    type: 'confirm',
                    name: 'isInstall',
                    message: `是否安装依赖(node_modules)?`,
                    default: false,
                }
            ]

            let { isInstall } = await inquirer.prompt(autoInstall)

            if (isInstall) {
                log('安装模块 --- npm install')
                ora().info(`${chalk.yellow('安装耗时可能会很长,请耐心等待,您也可以通过 ctrl+c停止安装, 手动 npm install')}`)
                const spinner = ora(`${chalk.blue('安装依赖中')}`)
                spinner.start()
                exec('npm install', { encoding: 'utf- 8' }, function (err, stdout, stderr) {
                    if (err) console.log(err)
                }).on('exit', function (code) {
                    spinner.succeed('模块安装完成')
                    shell.exec("npm start");
                    spinner.succeed('项目启动成功')
                    process.exit()
                });
            } else {
                process.exit()
            }
5、项目启动
 shell.exec("npm start");

三、总结

使用模板创建项目的流程大体就是这些,使用git仓库代码后续更新,后续要有一些优化代码并没有贴出,exec我是对其进行了一个封装封装成了promise方便使用,因为后边这个方法会经常使用。其次还有全局命令的原理会另写文章进行解释。

文章中如有问题欢迎大家批评指正?。