你可能用到過不少前端腳手架工具,有沒有試想過到底如何寫一個屬於你的腳手架呢?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
安裝。json
項目初始化完成後,建立bin
文件和commands
文件。bin文件爲可執行命令入口目錄,commands則負責編寫一些命令交互。後端
#!/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
運行。package.json
中的bin
對象,key爲腳本執行的名字,value爲執行目錄,如"bin": {"build-react": "bin/react-cli"}
,便可在輸入build-react的時候等同於執行 node bin/react-cli
命令,在咱們全局安裝腳手架的時候,bin對象裏面的內容便可變成全局可執行命令。npm publish
便可發佈,注意包名不能與現有的npm裏的相同、每次發佈新版本的包時須要修改package.json
裏的版本號,發佈的包只有在24小時內能夠刪除。