什麼是Nodejs Cli應用?
簡單來說就是在指令行可以使用nodejs來執行的應用,例如:vue-cli、creat-react-app、webpack-cli等;在前端開發過程中我們會用到很多的工具,這些工具在安裝過後可以直接使用指令行執行;注意在全局安裝和在項目安裝不同。
// 全局安裝,直接執行指令
> npm install webpack webpack-cli -g
> webpack
// 項目安裝,需要借助npx執行
> npm install webpack webpack-cli --save-dev
> npx webpack
Nodejs Cli應用的工作流程!
1、啟動過程:
指令行執行指令 => 根據package.json中bin查詢入口 => 執行入口js檔案cli.js
2、執行過程:
指令行執行js檔案功能啟動=> 指令行詢問使用者問題 => 結合問題答案+模闆等檔案 => 生成結構檔案
Nodejs Cli應用的入口檔案:cli.js
1、入口檔案路徑 ,首先在package.json 中添加bin字段
{
"name": "ncl",
"main": "index.js",
"bin": {
"ncl": "./cli.js" //入口檔案,ncl和name保持一緻
},
...
}
2、入口檔案特定的檔案頭 ,在cli.js頂部輸入
3、入口檔案權限
// 如果是 Linux 或者 macOS 系統下還需要修改此檔案的讀寫權限為 755
// 具體就是通過 chmod 755 cli.js 實作修改
4、簡單測試子產品
npm link 可以将子產品連結到全局,也可以連結到使用該子產品的項目node_modules中;這樣在開發子產品的過程中,不用釋出到npm也可以使用子產品進行測試。
> npm link // 在自定義子產品項目的目錄執行,将子產品連接配接到全局
> ncl // 直接執行子產品,使用子產品名
注意:在 Window PowerShell 腳本中直接執行子產品名不會成功,而在 Windows指令腳本(cmd)中直接執行子產品名就可以成功,具體原因尚不清楚。
Nodejs Cli應用的示例
該示例的功能實作一個自定義的腳手架:在指令行詢問使用者一些簡單的問題作為參數,然後自動生成一些項目檔案。其中的檔案可以通過模闆生成,也可以傳遞資料到模闆。
1、安裝一些依賴子產品
> npm install inquirer --save //nodejs環境下,實作指令行的使用者互動插件
> npm install ejs --save //模闆引擎
2、cli.js中定義指令行詢問使用者問題
- inquire.prompt進行指令行的使用者詢問操作
- inquirer.prompt傳回值為一個promise對象
- inquirer.prompt的參數為一個數組
const inquirer = require('inquirer')
inquirer.prompt([
{
type: 'input',
name: 'name',
message: '請輸入項目名稱?'
}
])
.then(anwsers => {
// anwsers: { name: "xxx" } //anwsers傳回一個結果對象
})
3、擷取模闆目錄和目标生成目錄
const path = require('path')
// 模闆目錄
// __dirname 擷取目前執行代碼檔案的絕對路徑
// tmplDir 為templates的絕對路徑
const tmplDir = path.join(__dirname, 'templates')
// 目标目錄
// process.cwd()傳回 Node.js 程序的目前工作目錄。
// process參考api文檔:http://nodejs.cn/api/process.html
const destDir = process.cwd()
4、模闆引擎渲染模闆
const ejs = require('ejs')
const path = require('path')
// 通過模闆引擎渲染檔案
// 參數1:fileDir為檔案的絕對路徑
// 參數2:渲染模闆所需變量,存在anwsers對象裡面
// 參數3:回調函數,result為新檔案
ejs.renderFile(fileDir, anwsers, (err, result) => {
if (err) throw err
// 将結果寫入目标檔案路徑
fs.writeFileSync(fileDestDir, result)
})
// 一個package.json作為模闆的示例:
{
"name": "<%= name %>",
"version": "<%= version %>",
"description": "<%= description %>",
"author": "<%= author %>",
"bin": "cli.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "ISC"
}
5、讀取目錄下的檔案
// 将模闆下的檔案全部轉換到目标目錄
// 參數1:path
// 參數2:回調函數,參數files為檔案相對路徑組成的數組
fs.readdir(tmplDir, (err, files) => {
if (err) throw err
files.forEach(file => {
// 通過模闆引擎渲染檔案
// 處理 file
})
})
})
6、将檔案寫入路徑
// 将結果寫入目标檔案路徑
// 參數1:檔案的絕對路徑
// 參數2:檔案内容
fs.writeFileSync(fileDestDir, result)
模闆檔案相關
這裡是根據自己的需求将需要自動生成的檔案放到模闆目錄下,沒有變化的或者統一的檔案就不需要使用模闆引擎。
1、模闆路徑:templates;
2、通常将整理好的項目結構整體拷貝到templates下,例如:vue的示例源檔案、lint檔案、package.json等等;
測試子產品執行
本地開發可以使用npm link關聯子產品目錄和依賴此子產品的項目node_modules目錄;也可以釋出到npm源上後直接安裝使用子產品。
1、關聯子產品:
> cd nodejs-cli-sample //Nodejs Cli應用的目錄
> npm link // 将子產品連接配接到全局
2、執行子產品:
> cd nodejs-cli-demo // 在項目目錄執行子產品
> ncl // 直接執行子產品,使用子產品名(ncl 是項目nodejs-cli-sample的名稱)
釋出Nodejs Cli應用
1、可以直接使用npm publish釋出到源上
> npm publish --registry=https://registry.xxxx
2、要考慮到npm源是否有寫權限,可以釋出到自己公司的npm源上或者yarn源上
// 淘寶鏡像源是隻讀的,publish不上去
// 釋出到yarn的鏡像源之後,使用淘寶鏡像源時可以手動同步加快子產品下載下傳速度
yarn publish --registry https://registry.yarnpkg.com/
完整示例代碼
1、NodeJs Cli應用cli.js 入口檔案
#!/usr/bin/env node
// Node CLI 應用入口檔案必須要有這樣的檔案頭
// 如果是 Linux 或者 macOS 系統下還需要修改此檔案的讀寫權限為 755
// 具體就是通過 chmod 755 cli.js 實作修改
const fs = require('fs') // 檔案讀寫
const path = require('path') // 路徑擷取
const inquirer = require('inquirer') //指令行使用者互動
const ejs = require('ejs') // 模闆引擎
// 腳手架的工作過程:啟動 => 指令行詢問使用者問題 => 結合問題答案+模闆 => 生成結構檔案
inquirer.prompt([
{
type: 'input',
name: 'name',
message: '請輸入項目名稱(\'\')'
},
{
type: 'input',
name: 'version',
message: '請輸入項目版本号(1.0.0)'
},
{
type: 'input',
name: 'description',
message: '請輸入項目備注(\'\')'
},
{
type: 'input',
name: 'author',
message: '請輸入作者名稱(\'\')'
}
])
.then(anwsers => {
// anwsers: { name: "xxx" } //anwsers傳回一個結果對象
// 模闆目錄絕對路徑
const tmplDir = path.join(__dirname, 'templates')
// 目标目錄
const destDir = process.cwd()
// 讀取目錄下所有檔案
let readFiles = (dir) => {
return new Promise((resolve, reject)=>{
// 參數1:目錄路徑
// 參數2:回調函數(錯誤對象,files為檔案相對路徑組成的數組)
fs.readdir(dir, (err, files) => {
if (err) reject(err)
resolve(files)
})
})
}
// 處理模闆檔案
let ejsRender = (file) => {
return new Promise((resolve, reject)=>{
// 模闆檔案絕對路徑
let dir = path.join(tmplDir, file)
// 參數1:檔案路徑
// 參數2:資料對象
// 參數3:回調函數(錯誤對象,渲染後的新檔案)
ejs.renderFile(dir, anwsers, (err, result) => {
if (err) reject(err)
resolve(result)
})
})
}
// 1、先讀取目錄下所有檔案
// 2、使用ejs渲染所有模闆
// 3、再将新檔案寫到目标路徑
readFiles(tmplDir).then((files)=>{
files.forEach(file => {
ejsRender(file).then((result)=>{
// 目标檔案絕對路徑,file其實是檔案相對路徑
let fileDestDir = path.join(destDir, file)
// 将結果寫入目标檔案路徑
// 參數1:檔案絕對路徑
// 參數2:渲染後新檔案
fs.writeFileSync(fileDestDir, result)
},throwError)
})
},throwError)
})
/**
* 錯誤處理函數
* @param {*錯誤對象} error
*/
function throwError(error){
throw error
}
2、package.json 示例模闆檔案,使用的ejs模闆引擎
{
"name": "<%= name %>",
"version": "<%= version %>",
"description": "<%= description %>",
"author": "<%= author %>",
"bin": "cli.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"license": "ISC"
}
轉載自:https://segmentfault.com/a/1190000040246466