Node 開發npm腳手架(類似vue-cli)
npm install tengyu-cli -g
一.構建腳手架思路
-
構建腳手架的目的
爲了快速構建項目,修改配置信息,達到自動化前端工程的目的
-
思路:兩種方式
第一種方式:從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插件了。