你們新年好!
年前在工做中接到任務要開發一個本身的CLI,便去了解了一下。發現並不難,只需運用nodejs的相關api便可。node
目前實現的功能爲:git
下面將分步去解析一個CLI的製做過程,也算是一次記錄回憶的過程。github
用 npm init 生成項目的package.json文件。而後編輯該文件主要加上npm
"bin": {
"jsm": "./bin/jsm.js"
},
複製代碼
而後在當前目錄建立你本身的腳本文件,對應上述配置爲 mkdir bin && touch bin/jsm.js
編輯建立好的文件,加上json
#!/usr/bin/env node console.log('Hello CLI') 複製代碼
接下來在項目的根目錄運行一下 npm i -g
, 如今就能夠在命令行使用jsm命令了。
注意: 必定要在開頭加上#!/usr/bin/env node
, 不然沒法運行。api
詳解:package.json文件只有加上了bin字段,才能在控制檯使用你的命令,對應的這裏的命令就是jsm,對應的執行文件爲bin/jsm.js。 其實"jsm"命令就是 "node bin/jsm.js" 的別稱,只有你用npm i -g全局安裝後才能夠用,開發的過程當中直接用node bin/jsm.js便可。
markdown
一個CLI須要經過命令行輸入各類參數,能夠直接用nodejs的process相關api進行解析,可是更推薦使用commander這個npm包能夠大大簡化解析的過程。 npm i commander
安裝, 而後更改以前的腳本文件添加app
const program = require('commander'); program .command('create <type> [name] [otherParams...]') .alias('c') .description('Generates new code') .action(function (type, name, otherParams) { console.log('type', type); console.log('name', name); console.log('other', otherParams); // 在這裏執行具體的操做 }); program.parse(process.argv); 複製代碼
如今在終端執行一下 node bin/jsm.js c component myComponent state=1 title=HelloCLI
應該就能看到輸入的各類信息信息了。至此命令解析部分就基本ok了,其餘更多的用法能夠參考官方例子函數
詳解:command第一個參數爲命令名稱,alias爲命令的別稱, 其中<>包裹的爲必選參數 []爲選填參數 帶有...的參數爲剩餘參數的集合。
oop
接下來須要根據上一步輸入的命令去作一些事情。具體到一個腳手架CLI通常主要作兩件事,快速的生成一個新項目和快速的建立對應的樣板文件。 既然須要建立文件就少不了對nodejs的fs模塊的運用,這裏用的一個加強版的fs-extra
下面封裝兩個經常使用的文件處理函數
//寫入文件 function write(path, str) { fs.writeFileSync(path, str); } //拷貝文件 function copyTemplate(from, to) { from = path.join(__dirname, from); write(to, fs.readFileSync(from, 'utf-8')); } 複製代碼
命令以下
program .command('new [name]') .alias('n') .description('Creates a new project') .action(function (name) { const projectName = name || 'myApp'; init({ app: projectName }) }); 複製代碼
init函數主要作了兩件事:
const fs = require('fs-extra'); const chalk = require('chalk'); const {basename, join} = require('path'); const readline = require('readline'); const download = require('download-git-repo'); const ora = require('ora'); const vfs = require('vinyl-fs'); const map = require('map-stream'); const template = 'stmu1320/Jsm-boilerplate'; // 建立函數 function createProject(dest) { const spinner = ora('downloading template') spinner.start() if (fs.existsSync(boilerplatePath)) fs.emptyDirSync(boilerplatePath) download(template, 'boilerplate', function (err) { spinner.stop() if (err) { console.log(err) process.exit() } fs .ensureDir(dest) .then(() => { vfs .src(['**/*', '!node_modules/**/*'], { cwd: boilerplatePath, cwdbase: true, dot: true, }) .pipe(map(copyLog)) .pipe(vfs.dest(dest)) .on('end', function() { const app = basename(dest); const configPath = `${dest}/config.json`; const configFile = JSON.parse(fs.readFileSync(configPath, 'utf-8')); configFile.dist = `../build/${app}`; configFile.title = app; configFile.description = `${app}-project`; write(configPath, JSON.stringify(configFile, null, 2)); // 這一部分執行依賴包安裝,具體代碼請查看文末連接 message.info('run install packages'); require('./install')({ success: initComplete.bind(null, app), cwd: dest, }); }) .resume(); }) .catch(err => { console.log(err); process.exit(); }); }) } function init({app}) { const dest = process.cwd(); const appDir = join(dest, `./${app}`); createProject(appDir); } 複製代碼
生成樣板文件這一部分,其實就是拷貝一個文件到指定的地方而已,固然還應該根據參數改變文件的具體內容。
program .command('create <type> [name] [otherParams...]') .alias('c') .description('Generates new code') .action(function (type, name, otherParams) { const acceptList = ['component', 'route'] if (!acceptList.find(item => item === type)) { message.light('create type must one of [component | route]') process.exit() } const params = paramsToObj(otherParams) params.name = name || 'example' generate({ type, params }) }); //生成文件入口函數 function generate({type, params}) { const pkgPath = findPkgPath(process.cwd()) if (!pkgPath) { message.error('No \'package.json\' file was found for the project.') process.exit() } const dist = path.join(pkgPath, `./src/${type}s`); fs .ensureDir(dist) .then(() => { switch (type) { case 'component': // 具體代碼請查看文末連接 createComponent(dist, params); break; case 'route': createRoute(dist, params); break; default: break; } }) .catch(err => { console.log(err); process.exit(1); }); } 複製代碼
到這裏一個基本的腳手架CLI就差很少了,剩下的是幫助信息等友好提示的東西了。文章的全部源碼點擊這裏
也歡迎你們安裝試用一下 npm i -g jsm-cli
。