Node 开发npm脚手架(类似vue-cli)

Node 开发npm脚手架(类似vue-cli)

npm install tengyu-cli -g

源码已经上传码云:https://gitee.com/bingtengaoyu/tengyu_cli.git

一.构建脚手架思路

  1. 构建脚手架的目的

    为了快速构建项目,修改配置信息,达到自动化前端工程的目的

  2. 思路:两种方式

    第一种方式:从GitHub拉取代码,修改配置,需要在GitHub提供一个可供下载的模板,完成初始化,下载后解压zip包,然后删除zip包,修改配置文件,配置代理,下载依赖,启动项目;

    第二种方式:将压缩后的zip模板放到服务器上,脚手架从服务器下载对应的模板,根据提示输入配置信息,完成初始化,下载后解压zip包,然后删除zip包,修改配置文件,配置代理,下载依赖,启动项目

二. 初始化项目

npm init

三. 修改package.json入口文件

{
  "name": "tengyu-cli",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",  // 入口文件
  "bin": {
    "tengyu": "index.js"
  },
  "scripts": {
    "serve": "node index.js",
    "dev": "node index.js",
    "start": "node index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "gengbingbing",
  "license": "ISC",
  "dependencies": {
    "chalk": "^3.0.0",
    "commander": "^4.1.1",
    "cross-spawn": "^7.0.1",
    "download-git-repo": "^3.0.2",
    "handlebars": "^4.7.3",
    "inquirer": "^7.0.4",
    "log-symbols": "^3.0.0",
    "ora": "^4.0.3",
    "request": "^2.88.2"
  }
}

四. 下载依赖

npm install 

五. 开始编写脚手架(直接上源码)

index.js

#!/usr/bin/env node
​
const program = require('commander');       //设计命令行
const download = require('download-git-repo');      //github仓库下载
const inquirer = require('inquirer');       //命令行答询
const handlebars = require('handlebars');       //修改字符
const ora = require('ora');         //命令行中加载状态标识
const chalk = require('chalk');     //命令行输出字符颜色
const logSymbols = require('log-symbols');      //命令行输出符号
const fs = require('fs');
const request = require('request');
const { resolve } = require("path");
const install = require("./utils/install");
​
console.log(chalk.green(`
                           tengyu cli 命令
    ------------------------------------------------------------
       tengyu init <template name> projectName  |  初始化项目 
       tengyu -V                                |  查看版本号    
       tengyu -h                                |  查看帮助      
       tengyu list                              |  查看模板列表  
       tengyu download                          |  下载zip模板  
    ------------------------------------------------------------
`));
// 可用模板
const templates = {
    'react-npm-template': {
        url: 'https://gitee.com/bingtengaoyu/reactAntd',
        downloadUrl: 'https://gitee.com:bingtengaoyu/reactAntd#master',
        description: 'react基础模板'
    },
    'vue-tools': {
        url: 'https://gitee.com/bingtengaoyu/vueTools',
        downloadUrl: 'https://gitee.com:bingtengaoyu/vueTools#master',
        description: 'vue常用组件'
    }
}
​
// tengyu -V|--version
program.version('1.0.0');  // -v|--version时输出版本号0.1.0
​
// tengyu init <template> <project>
program
    .command('init <template> <project>')
    .description('初始化项目模板')
    .action((templateName, projectName) => {
        console.log(templateName, templates);
        let downloadUrl = templates[templateName].downloadUrl;
        //下载github项目,下载墙loading提示
        const spinner = ora('正在下载模板...').start();
        //第一个参数是github仓库地址,第二个参数是创建的项目目录名,第三个参数是clone
        download(downloadUrl, projectName, { clone: true }, err => {
            if (err) {
                console.log(logSymbols.error, chalk.red('项目模板下载失败\n   只能下载list列表中有的模板'));
                console.log(err);
            } else {
                spinner.succeed('项目模板下载成功');
                //命令行答询
                inquirer.prompt([
                    {
                        type: 'input',
                        name: 'appid',
                        message: '请输入appid',
                        default: ''
                    },
                    {
                        type: 'input',
                        name: 'name',
                        message: '请输入项目名称',
                        default: projectName
                    },
                    {
                        type: 'input',
                        name: 'description',
                        message: '请输入项目简介',
                        default: ''
                    },
                    {
                        type: 'input',
                        name: 'author',
                        message: '请输入作者名称',
                        default: ''
                    }
                ]).then(answers => {
                    //根据命令行答询结果修改package.json文件
                    let packsgeContent = fs.readFileSync(`${projectName}/package.json`, 'utf8');
                    let packageResult = handlebars.compile(packsgeContent)(answers);
                    fs.writeFileSync(`${projectName}/package.json`, packageResult);
                    console.log(packageResult)
                    fs.writeFileSync(`${projectName}/config.js`, `module.exports = ${JSON.stringify(answers)}`);
​
                    console.log(logSymbols.success, chalk.green('项目初始化成功,开始下载依赖...'));
​
                    install({ cwd: `${resolve('./')}/${projectName}` }).then(data => {
                        console.log(logSymbols.success, chalk.green('项目依赖下载成功!'));
                    });
​
                    //用chalk和log-symbols改变命令行输出样式
                })
            }
        })
    })
​
// 下载zip模板
program
    .command('download')
    .description('初始化项目模板')
    .action((templateName, projectName) => {
        inquirer.prompt([
            {
                type: 'input',
                name: 'project_name',
                message: '请输入项目名称',
                default: 'tengyu-template'
            },
            {
                type: 'list',
                name: 'template_name',
                message: '请选择需要下载的模板',
                choices: [
                    'react快速开发模板',
                    'vue工具集'
                ],
                default: 'react-npm-template'
            }
        ]).then(answers => {
            let url = ''
            switch (answers.template_name) {
                case 'react快速开发模板':
                    url = templates['react-npm-template'].url;
                    break;
                case 'vue工具集':
                    url = templates['vue-tools'].url;
                    break;
                default:
                    url = templates['react-npm-template'].url
            }
​
            function downloadFile(uri, fileName, callback) {
                var stream = fs.createWriteStream(fileName);
                request(uri).pipe(stream).on('close', callback);
            }
​
            downloadFile(url, `${answers.project_name}.zip`, function () {
                console.log(logSymbols.success, chalk.green(`${answers.template_name}下载完毕!`));
                return
            });
        })
    })
​
// tengyu list
program
    .command('list')
    .description('查看所有可用模板')
    .action(() => {
        console.log(chalk.green(`
                              tengyu 模板
            -----------------------------------------------
                 react-npm-template   react快速开发模板  
                 vue-tools            vue工具集
            -----------------------------------------------
        `))
    })
​
program.parse(process.argv);

util-install.js文件(用于下载依赖)

使用cross-spawn模块进行自定义cmd命令的编写

const spawn = require("cross-spawn");
​
module.exports = function install(options) {
    const cwd = options.cwd || process.cwd();
    return new Promise((resolve, reject) => {
        const command = options.isYarn ? "yarn" : "npm";
        const args = ["install", "--save", "--save-exact", "--loglevel", "error"];
        const child = spawn(command, args, { cwd, stdio: ["pipe", process.stdout, process.stderr] });
​
        child.once("close", code => {
            if (code !== 0) {
                reject({
                    command: `${command} ${args.join(" ")}`
                });
                return;
            }
            resolve();
        });
        child.once("error", reject);
    });
};

至此一个简单的脚手架已经开发完成

npm link之后即可在本地进行试运行了

六. 发布npm库

  1. 设置自己的npm代理,不要设置为了淘宝代理等;

  2. 去npm注册自己的账号;

  3. 上传自己的npm脚手架

    npm login // 登录
    ​
    npm publish // 推送自己的脚手架到npm库
  4. 大功告成,可以下载自己的npm插件了。

猜你喜欢

转载自blog.csdn.net/qq_23334071/article/details/105158728