你可能用到過不少前端腳手架工具,有沒有試想過到底如何寫一個屬於你的腳手架呢?javascript
commander.js 命令行工具前端
download-git-repo git倉庫代碼下載java
chalk 命令行輸出樣式美化node
Inquirer.js 命令行交互react
ora 命令行加載中效果git
建立項目目錄後執行npm init
按照提示完成初始化項目。github
安裝上面咱們提到過的這幾個腳手架依賴工具,執行npm install chalk commander download-git-repo inquirer ora --save
完成安裝。npm
項目初始化完成後,建立bin
文件和commands
文件。bin文件爲可執行命令入口目錄,commands則負責編寫一些命令交互。json
#!/usr/bin/env node process.env.NODE_PATH = __dirname + '/../node_modules/' const { resolve } = require('path') const res = command => resolve(__dirname, '../commands/', command) const program = require('commander') program.version(require('../package').version ) program.usage('<command>') program.command('init') .option('-f, --foo', 'enable some foo') .description('Generate a new project') .alias('i') .action(() => { require(res('init')) }) if(!program.args.length){ program.help() }
建立該react-cli.js
爲可執行命令入口文件,而且定義了一個'init'命令,執行命令後會去commands 目錄下尋找對應的init.js
文件後端
const {prompt} = require('inquirer') const program = require('commander') const chalk = require('chalk') const download = require('download-git-repo') const ora = require('ora') const fs = require('fs') const path = require('path') const option = program.parse(process.argv).args[0] const defaultName = typeof option === 'string' ? option : 'react-project' const tplList = require(`${__dirname}/../templates`) const tplLists = Object.keys(tplList) || []; const question = [ { type: 'input', name: 'name', message: 'Project name', default: defaultName, filter(val) { return val.trim() }, validate(val) { const validate = (val.trim().split(" ")).length === 1 return validate || 'Project name is not allowed to have spaces '; }, transformer(val) { return val; } }, { type: 'list', name: 'template', message: 'Project template', choices: tplLists, default: tplLists[0], validate(val) { return true; }, transformer(val) { return val; } }, { type: 'input', name: 'description', message: 'Project description', default: 'React project', validate (val) { return true; }, transformer(val) { return val; } }, { type: 'input', name: 'author', message: 'Author', default: 'project author', validate (val) { return true; }, transformer(val) { return val; } } ] module.exports = prompt(question).then(({name, template, description, author}) => { const projectName = name; const templateName = template; const gitPlace = tplList[templateName]['place']; const gitBranch = tplList[templateName]['branch']; const spinner = ora('Downloading please wait...'); spinner.start(); download(`${gitPlace}${gitBranch}`, `./${projectName}`, (err) => { if (err) { console.log(chalk.red(err)) process.exit() } fs.readFile(`./${projectName}/package.json`, 'utf8', function (err, data) { if(err) { spinner.stop(); console.error(err); return; } const packageJson = JSON.parse(data); packageJson.name = name; packageJson.description = description; packageJson.author = author; var updatePackageJson = JSON.stringify(packageJson, null, 2); fs.writeFile(`./${projectName}/package.json`, updatePackageJson, 'utf8', function (err) { if(err) { spinner.stop(); console.error(err); return; } else { spinner.stop(); console.log(chalk.green('project init successfully!')) console.log(` ${chalk.bgWhite.black(' Run Application ')} ${chalk.yellow(`cd ${name}`)} ${chalk.yellow('npm install')} ${chalk.yellow('npm start')} `); } }); }); }) })
1. program.parse(process.argv)
能夠解析執行init 時候傳入的參數, 咱們能夠拿到這個參數作爲項目建立的目錄名,若是沒有傳入該參數則爲其設置一個默認目錄名稱。
2. 命令行交互問答
question
數組爲交互命令配置,數組中每個對象都對應一個執行命令時候的一個問題type
爲該提問的類型,name
爲該問題的名字,能夠在後面經過name拿到該問題的用戶輸入答案message
爲問題的提示default
則爲用戶沒輸入時的默認爲其提供一個答案validate
方法能夠校驗用戶輸入的內容,返回true時校驗經過,若不正確能夠返回對應的字符串提示文案transformer
爲用戶輸入問題答案後將對應的答案展現到問題位置,須要有返回值,返回到字符串爲展現內容3. 問答結束的回調
download-git-repo
工具來下載git倉庫代碼'Hzy0913/react-template'
即爲下載該倉庫master的代碼,若是須要切換對應分支則在倉庫地址後面加入對應分支名,如'Hzy0913/react-template#complete'
。'./projectName'
,便可在當前執行命令目錄下生成對應的文件名。ora
模塊能夠爲咱們生成下載時候的旋轉圖標,ora方法傳入的第一個參數爲等待時候的提示文案並生成實例,在實例對象上調用start()方法開始出現旋轉動畫和提示,stop()方法中止。package.json
文件生成用戶自定義輸入的內容,node的fs
模塊的readFile方法能夠幫助咱們獲取生成文件的內容,writeFile則能夠寫入內容chalk
模塊能夠幫助咱們美化輸出內容。文章中腳手架示例代碼能夠見 build-react-cli
node bin/react-cli
運行。backage.json
中的bin
對象,key爲腳本執行的名字,value爲執行目錄,如"bin": {"build-react": "bin/react-cli"}
,便可在輸入build-react的時候等同於執行 node bin/react-cli
命令,在咱們全局安裝腳手架的時候,bin對象裏面的內容便可變成全局可執行命令。npm publish
便可發佈,注意包名不能與現有的npm裏的相同、每次發佈新版本的包時須要修改package.json
裏的版本號,發佈的包只有在24小時內能夠刪除。