以往每次建模块都需要手动创建觉得很繁琐,看了公司项目的有自动化创建的工具,参考了下,是用到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()
})
}
}