Node 开发npm脚手架(类似vue-cli)
npm install tengyu-cli -g
源码已经上传码云:https://gitee.com/bingtengaoyu/tengyu_cli.git
一.构建脚手架思路
-
构建脚手架的目的
为了快速构建项目,修改配置信息,达到自动化前端工程的目的
-
思路:两种方式
第一种方式:从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库
-
设置自己的npm代理,不要设置为了淘宝代理等;
-
去npm注册自己的账号;
-
上传自己的npm脚手架
npm login // 登录 npm publish // 推送自己的脚手架到npm库
-
大功告成,可以下载自己的npm插件了。