Yargs寫一個新建模板工具

YDJFE發表於2019-03-04

Yargs官方地址

以往每次建模組都需要手動建立覺得很繁瑣,看了公司專案的有自動化建立的工具,參考了下,是用到Yargs,能夠自定義命令、引數來處理問題。通過學習yargs,順便自己寫一個自動化建立模組工具出來。

1. 步驟

  • 第一步:需要傳遞的引數

     存在三個引數
     file: 資料夾以及模組名(必填)
     path: 檔案存放路徑
     type: 模板型別
  • 第二步:建立一個命令,並且處理傳入的引數

     command(cmd, desc, [builder], [handler])
     用command方法建立了一個命令,將先前定義的引數放到command的bulider裡面。
     [handler]處理傳參。
  • 第三步:處理引數

     1.建立資料夾
     2.建立檔案
  • 第四步:自定義模板

     能夠建立檔案是不夠的,我們需要檔案內容是我們定義好的模板內容,所以我們先定義好模板。
  • 第五步:做一些體驗優化,建立檔案提示,重複檔案提示

2. 目錄

Yargs寫一個新建模板工具

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。

新建模組時會先判斷存放路徑是否存在該資料夾。

Yargs寫一個新建模板工具

遇到重複的模組可選是否需要覆蓋

Yargs寫一個新建模板工具

檔案地址

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)選擇放置位置,提供進入下一層,返回上一層操作

Yargs寫一個新建模板工具

2. 無需逐層選擇路徑

針對上面一旦出現深層路徑的話,操作會很繁瑣,所以提供動態選擇路徑,根據關鍵字提示路徑地址。

Yargs寫一個新建模板工具

步驟

(1)命令: node tools3/cli

(2)填寫檔名

(3)提示選擇模板

(4)提示放置地址

相關文章