使用node开发一个拉取项目模板工具

前段时间在公司项目中发现了一个问题,公司项目大部分使用vue开发,涉及到了pc,h5,微信公众号,企业微信等页面;

而这些页面的项目管理并不是统一配置的,可以说是各有各的风格;

对于这些头疼的配置,想到了vue-cli下载模板的功能;是否也是可以使用类似功能实现一个统一配置的项目模板呢?

之后查阅了部分资料,搭配成型的插件使用是完全可以达到效果;那该怎么做呢?

前置准备的插件:

  • chalk: 美化Node输出神器
  • commander:开发命令行工具,是 一个帮助快速开发Nodejs命令行工具的package。
  • figlet:终端输出特殊的文字,这些文字只包含 ANSI 对应的字符。
  • handlebars:模板语言,用模板和输入对象来生成 HTML 或其他文本格式
  • inquirer:Node中的交互式命令行界面(CLI)工具
  • ora:终端微调器,主要用来作为等待图标使用

开发步骤:

首先在本地创建一个文件夹目录用于存放你的代码(当然最好使用git仓库存放);

然后使用 npm init -y 初始化,这样根目录就多了一个 package.json 文件,接着对该文件进行部分修改,在文件中,添加bin字段,并指向安装的核心代码路径(bin的作用这里不做介绍,可自行百度)具体修改如下:

{
    ...

    "bin": {
        "temp-pro": "bin/temp-pro.js"
    }

    ...
}

这里的 temp-pro.js 就是核心代码了;当然 temp-pro是npm install package_name 包名,你可以换成自己的;

核心代码中,使用 commander 做一些提示命令,如:-v 获取版本, --help获取帮助, create <name> 创建项目等等;

逻辑并不是太复杂,简单的说,就是输入特定的命令(获取版本,帮助,创建),在创建项目前会判断项目是否已经存在,不存在会根据配置的git地址进行克隆下载;

会有固定的几种模板:

  • vue模板:pc系统(自己搭建封装的一个简单案例)和h5
  • node模板:nest(ts)+mySql 和 express+mongodb
  • app模板:uniapp,flutter(暂未开发)

然后根据自己的需求选择,当然私有库是需要登录的,特别适用于私有化的操作;

可以参考简单的流程图:

以下是参考代码:

#!/usr/bin/env node

const program = require("commander");
const { handlerHelp, isExistFileName } = require("./handler");

program
    .version(require("../package.json").version, "-v, --version")
    .on("--help", handlerHelp)
    .command("create <name>")
    .action(name => {
        isExistFileName(name);
    });
program.parse(process.argv);
const chalk = require("chalk");
const fs = require("fs");
const path = require('path')

const inquirer = require("inquirer");
const ora = require("ora");
const figlet = require("figlet");
const handlebars = require("handlebars");
const child_process = require('child_process')
const { inquirerConfig, gitConfig } = require("./inquirer");

const spinner = ora("正在下载项目模板, 请稍等...");

const LOGO = "TEMPPRO";


const removeDirectory = (url) => {
    const files = fs.readdirSync(url);
  
    files.forEach(function (file, index) {
      const curPath = path.join(url, file);
  
      if (fs.statSync(curPath).isDirectory()) removeDirectory(curPath);
      else fs.unlinkSync(curPath);
    });
    fs.rmdirSync(url);
  }


/**
 * @description 给help美化颜色
 */
const handlerHelp = () => {
    console.log();
    console.log(
        `运行 ${chalk.cyan("wz-cli <command> --help")} 了解给定命令的详细用法。`
    );
    console.log();
};
exports.handlerHelp = handlerHelp;


/**
 * @description 检测文件名是否存在
 * @param {String} name
 * @returns
 */
const isExistFileName = name => {
    if (!name) return
    if (!fs.existsSync(name)) {
        handlerInquirer(name);
    } else {
        spinner.fail(chalk.red.bold(`Error: 项目【${name}】已存在,请更改项目名称!`))
    }
};

/**
 * @description 提供用户界面和查询会话流程
 * @param {String} name
 */
const handlerInquirer = name => {
    inquirer.prompt(inquirerConfig).then(answers => {
        spinner.info(chalk.green.bold(`正在下载项目模板, 请稍等...`));

        const { keywords = "" } = answers;
        const findGit = gitConfig.find(git => git.name === keywords) || {};
        if (findGit.url !== "" && findGit.checkout !== "") {
            downloadProject(findGit.url, findGit.checkout, name, answers);
        } else {
            spinner.fail(chalk.red.bold(`Error: 模板地址不存在!`));
        }
    });
};

/**
 * @description 根据git地址下载文件
 * @param {String} url  git 地址
 * @param checkout
 * @param {String} name 项目名字
 * @param {*} answers 会话流程回调参数
 */
const downloadProject = (url, checkout, name, answers) => {

    child_process.exec(`git clone -b ${checkout} ${url} ${name}`, (error, stdout) => {
        if (error) {
            removeDirectory(name);
            return spinner.fail(chalk.red.bold(error.message));
        }

        // removeDirectory(`${name}/.git`);
        spinner.succeed(chalk.green.bold("项目模板初始化完成..."));
        const fileName = `${name}/package.json`;
        const meta = {
            name,
            description: answers.description,
            author: answers.author,
        };
        if (fs.existsSync(fileName)) {
            const content = fs.readFileSync(fileName).toString();
            const result = handlebars.compile(content)(meta);
            fs.writeFileSync(fileName, result);
        }
        handlerLogo(name);
    })


};

/**
 * @description 打印logo
 * @param {String} name 项目名
 * @returns
 */
const handlerLogo = name => {
    if (!name) return;
    console.log(
        "\r\n" +
        figlet.textSync(LOGO, {
            horizontalLayout: "default",
            verticalLayout: "default",
            width: 80,
            whitespaceBreak: true,
        }) +
        "\r\n"
    );

    console.log(chalk.green(`\t打开项目:cd ${name}`));
    console.log(
        chalk.green(
            `\t安装依赖:npm install or cnpm install or yarn install or pnpm install`
        )
    );
};

exports.isExistFileName = isExistFileName;
const gitConfig = [
    {
        name: "Pc(vue3 + Element-Plus + abk公司内部使用)",
        url: "http://xxx.com/frontend/xxx.git",
        checkout: 'pc-admin-template'
    },
    {
        name: "H5(vue3 + vant + abk公司内部使用)",
        url: "http://xxx.com/frontend/xxx.git",
        checkout: 'h5-template'
    },
    {
        name: "Node(Nest + MySql + 个人使用)",
        url: "https://gitee.com/wang-xiaoze/nest-api-admin.git",
        checkout: 'master'
    },
    {
        name: "Node(Express + MongoDB + 公共模板)",
        url: "https://gitee.com/wang-xiaoze/node-service-template.git",
        checkout: 'master'
    },
];
exports.gitConfig = gitConfig;

const inquirerConfig = [
    {
        type: "input",
        name: "description",
        message: "Please enter a project description?",
        default: "An interesting project template",
    },
    {
        type: "input",
        name: "author",
        message: "Please enter the author's name?",
        default: "王小泽(wangxiaoze@petalmail.com)",
    },
    {
        type: "list",
        name: "keywords",
        message: "Please select the required project template?",
        choices: gitConfig.map(item => item.name),
        default: 0,
    },
];
exports.inquirerConfig = inquirerConfig;

源码地址:

  1. npm包:temp-pro
  2. temp-pro github源代码
  3. 项目模板地址,包含上面的vue, node, app

如果有错误的地方还请大家多多指教,共同学习哦!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值