以往每次建模組都需要手動建立覺得很繁瑣,看了公司專案的有自動化建立的工具,參考了下,是用到Yargs,能夠自定義命令、引數來處理問題。通過學習yargs,順便自己寫一個自動化建立模組工具出來。
1. 步驟
-
第一步:需要傳遞的引數
存在三個引數
file: 資料夾以及模組名(必填)
path: 檔案存放路徑
type: 模板型別
-
第二步:建立一個命令,並且處理傳入的引數
command(cmd, desc, [builder], [handler])
用command方法建立了一個命令,將先前定義的引數放到command的bulider裡面。
[handler]處理傳參。
-
第三步:處理引數
1.建立資料夾
2.建立檔案
-
第四步:自定義模板
能夠建立檔案是不夠的,我們需要檔案內容是我們定義好的模板內容,所以我們先定義好模板。
-
第五步:做一些體驗優化,建立檔案提示,重複檔案提示
2. 目錄
ReactTemplat、VueTemplate: 是自定義模板,可以根據自己的模板內容、結構來自定義。
cli.js: 用來建立命令,並且將引數傳給start檔案處理。
start.js: 用來處理引數,建立資料夾及檔案,利用process程式與使用者進行互動。
handle.js: 處理檔案的方法。
3. 幫助資訊
node ./tools/cli add -h // 顯示幫助資訊
Options:
--version Show version number [boolean]
-h Show help [boolean]
--file, -f create a file [required]
--path, -p file path [default: ""]
--type, -t file‘s type [choices: "vue", "react"] [default: "vue"]
Examples:
node tools/cli add -p views -f test -t vue 在views目錄下建立一個test模板
複製程式碼
4.用法
node tools/cli add -f test -p views -t vue
複製程式碼
在views目錄下建立一個test模組,模組型別是vue。
新建模組時會先判斷存放路徑是否存在該資料夾。
遇到重複的模組可選是否需要覆蓋
cli.js
const argv = require('yargs')
.command(
'add',
'create a file',
function(yargs) {
return yargs
.option('file', {
alias: 'f',
describe: 'create a file'
})
.option('path', {
alias: 'p',
describe: 'file path',
default: ''
})
.option('type', {
alias: 't',
describe: 'file‘s type',
choices: ['vue', 'react'], // 現在有兩個模板供選擇,可以根據自己的專案情況設計模板
default: 'vue'
})
.demandOption(['file'], 'Please provide file to work with this tool')
.example(
'node tools/cli add -p views -f test -t vue',
'在views目錄下建立一個test模板'
)
},
function(argv) {
// 根據引數,建立模板
start(argv)
}
)
.help('h').argv
複製程式碼
start.js
const handel = require('./handle')
const colors = require('colors')
const path = require('path')
module.exports = async function(argv) {
argv.file = argv.file.toString()
const existPathFolder = await handel.existFolder(path.resolve(argv.path))
const fileName =
argv.file.substring(0, 1).toUpperCase() + argv.file.substring(1)
let className = ''
for (let i = 0; i < argv.file.length; i++) {
if (/[A-Z]/.test(argv.file[i])) {
className += '-'
}
className += argv.file[i].toLowerCase()
}
if (argv.path !== '') {
argv.path += '/'
}
const filePath = path.resolve(argv.path) + '/' + fileName
process.stdin.setEncoding('utf8')
const createFileData = {
filePath,
fileName,
className,
type: argv.type
}
// 不存在path的資料夾
if (!existPathFolder) {
console.warn(
colors.green(`是否建立 ${path.resolve(argv.path)} 資料夾?:y/n`)
)
process.stdin.on('data', async chunk => {
chunk = chunk.replace(/[\s\n]/, '')
if (chunk === 'y') {
// 建立path資料夾
await handel.createFolder(path.resolve(argv.path))
// 建立元件資料夾
await handel.createFolder(filePath)
// 建立檔案
await handel.createFile(createFileData)
process.exit()
} else if (chunk === 'n') {
process.exit()
} else {
console.warn(colors.red('請輸入正確指令:y/n'))
process.exit()
}
})
} else {
// 判斷元件資料夾是否存在
const existFileFolder = await handel.existFolder(filePath)
if (existFileFolder) {
console.warn(colors.green(`${fileName}資料夾已存在,是否覆蓋?:y/n`))
process.stdin.on('data', async chunk => {
chunk = chunk.replace(/[\s\n]/, '')
if (chunk === 'y') {
// 建立元件資料夾
await handel.createFolder(filePath)
// 建立檔案
await handel.createFile(createFileData)
process.exit()
} else if (chunk === 'n') {
process.exit()
} else {
console.warn(colors.red('請輸入正確指令:y/n'))
process.exit()
}
})
} else {
// 建立元件資料夾
await handel.createFolder(filePath)
// 建立檔案
await handel.createFile(createFileData)
process.exit()
}
}
}
複製程式碼
handle.js
const fs = require('fs')
const colors = require('colors')
const path = require('path')
module.exports = {
existFolder: async function(path) {
// 判斷是否存在argv.path的資料夾
return new Promise(function(resolve, reject) {
return fs.exists(path, e => {
resolve(e)
})
})
},
/**
*建立資料夾
@param filePath 檔案路徑
*/
createFolder: function(filePath) {
return new Promise(function(resolve, reject) {
fs.mkdir(filePath, function(err) {
if (err) {
if (err.errno === -2) {
console.log(colors.red('找不到目錄'))
} else if (err.errno === -17) {
}
} else {
console.log(colors.green('建立資料夾: '))
console.log(colors.underline(`${filePath}`))
}
resolve()
})
})
},
/**
* @param args:{
* filePath 檔案路徑
* fileName 檔名
* className 樣式名
* type 檔案型別
* }
*/
createFile: function({ filePath, fileName, className, type }) {
const data = {
fileName,
filePath,
className
}
// 模板路徑
switch (type) {
case 'vue':
data.templateFolderPath = path.join(__dirname, './VueTemplate')
break
case 'react':
data.templateFolderPath = path.join(__dirname, './VueTemplate')
break
default:
data.templateFolderPath = path.join(__dirname, './VueTemplate')
}
return new Promise(async (resolve, reject) => {
await this.readAndWiteFile(data, resolve)
})
},
/**
* 讀取模板內容並且寫到新建檔案裡面
* @param args:{
* templateFolderPath 模板路徑
* fileName 檔名
* filePath 檔案路徑
* className 樣式名字
* }
* @param resolve
*/
readAndWiteFile: function(
{ templateFolderPath, fileName, filePath, className },
resolve
) {
fs.readdir(templateFolderPath, 'utf8', (err, files) => {
if (err) {
console.log(colors.red(err))
return false
}
files.forEach(templateName => {
const FileName = templateName
.replace('TemplateName', fileName)
.replace('.txt', '')
// 1.建立檔案
fs.createWriteStream(`${filePath}/${FileName}`)
// 2.讀取、寫入模板內容
const content = fs
.readFileSync(`${templateFolderPath}/${templateName}`)
.toString() // 讀取模板檔案
.replace(/\${TemplateName}/g, FileName.split('.')[0])
.replace(/\${template-name}/g, className) // 替換模板內容
// 將templateName替換成對應的檔名
fs.writeFileSync(`${filePath}/${FileName}`, content, 'utf8')
console.log(colors.green('寫入檔案: '))
console.log(colors.underline(`${filePath}/${FileName}`))
})
resolve()
})
}
}
複製程式碼
這種方法比較繁瑣,下面優化了兩個版本
1.無需填寫手寫路徑跟型別
(1)命令: node tools2/cli add -f fileName
(2)提示選擇模板
(3)選擇放置位置,提供進入下一層,返回上一層操作
2. 無需逐層選擇路徑
針對上面一旦出現深層路徑的話,操作會很繁瑣,所以提供動態選擇路徑,根據關鍵字提示路徑地址。
步驟
(1)命令: node tools3/cli
(2)填寫檔名
(3)提示選擇模板
(4)提示放置地址