js项目升级为ts的工具

947 阅读2分钟

前言

老板:小陈,最近接手了一个js项目,听说ts比较高大上啊,看看这两三天能不能搞定

我内心os:我丢,这么古老的项目,500+的js文件,让我两三天搞定......

我:噢噢,我先看看.......

1723303847204.png

打工人,打工魂,打工的都是人上人,没办法,还是得硬着头皮上

解决方案

一开始,我也是想直接一个一个文件地替换,500+多个啊,这要搞到何年何月........

如果,我先将.js文件转换为.ts文件,.jsx文件转换为.tsx不就完了吗,至于类型标注,就在每个文件顶部加入 // @ts-nocheck , 不就搞定了吗

虽然不是很完美,但是可以准点下班,说不定还能摸摸鱼....

1723304246051.png

实现效果

1.gif

实现核心代码

下方都有源码地址,请放心食用

1、文件夹/文件递归遍历读取内容

/**
 * @description:
 * @param directory 目录文件夹
 * @param useSubdirectories 是否继续向该目录下的文件夹遍历文件
 * @param extList 文件夹名
 * @return {*}
 */
function enumerableFile(
  directory,
  useSubdirectories = false,
  extList = ['.java']
) {
  return new Promise((resolve, reject) => {
    const logFun = (text, val = false) => {
      console.log(`文件夹${directory}下未存在${extList.join('/')}文件`)
      reject(val)
    }
    if (path) {
      const filesList = []
      // 递归读取文件
      function readFileList(directory, useSubdirectories, extList) {
        const files = fs.readdirSync(directory)
        if (files && Array.isArray(files) && files.length) {
          files.forEach((item) => {
            const fullPath = path.join(directory, item)
            const stat = fs.statSync(fullPath)

            if (stat.isDirectory() && useSubdirectories) {
              readFileList(
                path.join(directory, item),
                useSubdirectories,
                extList
              )
            } else {
              const info = getPathInfo(fullPath)
              extList.includes(info.ext) && filesList.push(fullPath)
            }
          })
        } else {
          logFun(`文件夹${directory}下未存在${extList.join('/')}文件`)
        }
      }

      readFileList(directory, useSubdirectories, extList)
      // 生成需要的对象
      if (filesList.length) {
        const res = filesList.map((item) => ({
          path: item,
          ...getPathInfo(item),
        }))
        //  console.log(`遍历文件夹${directory}下的${extList.join('/')}文件`, res);
        resolve(res)
      } else {
        logFun(`文件夹${directory}下未存在${extList.join('/')}文件`)
      }
    } else {
      logFun(`读取文件夹名不能为空!`)
    }
  })
}

2、重命名文件

/**
 * @description: 重命名文件后缀
 * @param {string} filePath 文件路径
 * @param {Object} converRules 转换规则 , 键为旧后缀名,值为新后缀名
 * @param {boolean} isBackupOldFile 是否备份旧文件,true 则表示为备份旧的,加入特定名称, false表示为不备份,直接替换
 * @return {*}
 */
function reExtname(
  filePath,
  converRules = {
    '.js': '.ts',
    '.jsx': '.tsx',
  },
  isBackupOldFile = false
) {
  return new Promise((resolve, reject) => {
    if (filePath) {
      const oldExtNames = Object.keys(converRules)
      if (oldExtNames?.length) {
        const newExtNames = Object.values(converRules)
        const bool = newExtNames.every((ele) => Boolean(ele))
        if (bool) {
          isFileExisted(filePath)
            .then((res) => {
              const fileInfo = getPathInfo(filePath)

              console.log('fileInfo=====>', fileInfo)

              const { ext, name, dir } = fileInfo
              if (ext) {
                const newExtName = converRules[ext]
                if (newExtName) {
                  let newPath = `${dir}\\${name}${newExtName}`
                  let oldPath = filePath

                  // 如果需要备份旧文件
                  if (isBackupOldFile) {
                    // 如果已经备份,则不再备份
                    const backupName = 'backups'
                    if (filePath.indexOf(backupName) === -1) {
                      console.log('进入备份=====>')
                      const newBackupPath = `${dir}\\${name}-${backupName}${ext}`

                      fs.rename(oldPath, newBackupPath, (error) => {
                        if (error) {
                          reject('文件备份失败:', error)
                        }
                      })

                      oldPath = newBackupPath
                    }
                  }

                  fs.rename(oldPath, newPath, (error) => {
                    if (error) {
                      reject('文件重命名失败:', error)
                    } else {
                      console.log(`重命名成功,${filePath} ===> ${newPath}`)
                      resolve(newPath)
                    }
                  })
                } else {
                  reject('文件后缀名不满足转换匹配规则')
                }
              } else {
                reject('文件后缀名不存在,无法实现转换')
              }
            })
            .catch((res) => {
              console.error('重命名出错', res)
            })
        } else {
          reject('重命名文件后缀名不能为空')
        }
      } else {
        reject('重命名文件后缀名不能为空')
      }
    } else {
      reject('未获取到重命名文件路径')
    }
  })
}

3、文件内容追加方法

/**
 * @description: 追加文件头部内容,内容追加在前面
 * @param {*} path 文件路径
 * @param {*} data 追加的文件内容
 * @param {*} isRepeatWrite 是否重复写入
 * @param {*} escapeChar 转义字符
 * @return {*}
 */
function appendToHeadData(
  path,
  data,
  isRepeatWrite = false,
  escapeChar = '\n'
) {
  return new Promise((resolve, reject) => {
    if (path) {
      if (data) {
        readFileFun(path)
          .then((fileData) => {
            return Promise.resolve(fileData)
          })
          .then((fileData) => {
            const newData = `${data}${escapeChar}${fileData}`
            if (isRepeatWrite) {
              return parseFile(path, newData)
            } else {
              if (fileData.indexOf(data) === -1) {
                return parseFile(path, newData)
              } else {
                return Promise.reject('内容已追加,不可重复追加')
              }
            }
          })
          .then((res) => {
            console.log('追加内容成功:', res)
            res && resolve(path)
          })
          .catch((err) => {
            reject('追加头部内容失败:' + err)
          })
      } else {
        reject('追加内容文件内容不能为空')
      }
    } else {
      reject('追加内容文件路径不能为空')
    }
  })
}

总结

主要是使用node模块fs

  • 遍历读取文件夹/文件,找到目标文件
  • 使用文件重命名后缀
  • 追加文件内容

源码

file-convert : github.com/ArcherNull/…

完结撒花。。。