因为最近毕业设计要写答辩ppt,里面需要统计工作量,因此写了一个用来统计代码行数的小demo。

主要实现思路是先遍历一个文件夹下的所有文件,然后挑选出所有的文件,返回一个按文件格式划分的对象,使用这个对象就可以统计各种类型文件的行数了。
感觉基于那个文件对象还可以搞一些类似文本分析等功能,以后有时间就做。
除此之外,类里面还有一些静态方法例如直接统计某个文本的行数(使用换行符分割)、获取文件夹所有文件等小功能也可以拆出来用。
仓库地址

具体使用

先实例化一个对象,然后指定需要忽略的文件格式和文件夹,以下面的代码为例:
先把这段代码放在项目的根目录下,指定当前文件夹作为起始搜索路径,指定vue、md、js、py格式,忽略node_modules和项目打出来的包。

具体输出如下:

打印的第一个是所有的文件名,第二个是指定格式文件的总行数。

{
  vue: [
    'D:\\code\\design-frontend\\src\\App.vue',
    'D:\\code\\design-frontend\\src\\pages\\ForgetPassword.vue',
    'D:\\code\\design-frontend\\src\\pages\\Login.vue',
    'D:\\code\\design-frontend\\src\\pages\\Main.vue',
    'D:\\code\\design-frontend\\src\\pages\\SignUp.vue',
    'D:\\code\\design-frontend\\src\\pages\\Test.vue',
    'D:\\code\\design-frontend\\src\\components\\desc\\DescFunction.vue',
    'D:\\code\\design-frontend\\src\\components\\desc\\DescTask.vue',
    'D:\\code\\design-frontend\\src\\components\\personal\\revoke.vue',
    'D:\\code\\design-frontend\\src\\components\\personal\\UpdateInfo.vue',
    'D:\\code\\design-frontend\\src\\components\\personal\\UpdatePassword.vue',
    'D:\\code\\design-frontend\\src\\components\\task\\TaskList.vue',
    'D:\\code\\design-frontend\\src\\components\\task\\TaskUpload.vue',
    'D:\\code\\design-frontend\\src\\components\\visual\\DirectedGraph.vue',
    'D:\\code\\design-frontend\\src\\components\\visual\\TopologicalGraph.vue',
    'D:\\code\\design-frontend\\src\\components\\visual\\VisualGragh.vue',
    'D:\\code\\design-frontend\\src\\components\\visual\\VisualTree.vue',
    'D:\\code\\design-frontend\\src\\components\\widget\\CodeButton.vue',
    'D:\\code\\design-frontend\\src\\components\\widget\\Markdown.vue'
  ],
  js: [
    'D:\\code\\design-frontend\\.eslintrc.js',
    'D:\\code\\design-frontend\\.postcssrc.js',
    'D:\\code\\design-frontend\\LineReader.js',
    'D:\\code\\design-frontend\\vue.config.js',
    'D:\\code\\design-frontend\\build\\build.js',
    'D:\\code\\design-frontend\\build\\check-versions.js',
    'D:\\code\\design-frontend\\build\\utils.js',
    'D:\\code\\design-frontend\\build\\vue-loader.conf.js',
    'D:\\code\\design-frontend\\build\\webpack.base.conf.js',
    'D:\\code\\design-frontend\\build\\webpack.dev.conf.js',
    'D:\\code\\design-frontend\\build\\webpack.prod.conf.js',
    'D:\\code\\design-frontend\\config\\dev.env.js',
    'D:\\code\\design-frontend\\config\\index.js',
    'D:\\code\\design-frontend\\config\\prod.env.js',
    'D:\\code\\design-frontend\\src\\main.js',
    'D:\\code\\design-frontend\\src\\antdOptimize\\icons.js',
    'D:\\code\\design-frontend\\src\\apis\\apis.js',
    'D:\\code\\design-frontend\\src\\apis\\request.js',
    'D:\\code\\design-frontend\\src\\router\\index.js',
    'D:\\code\\design-frontend\\src\\store\\index.js',
    'D:\\code\\design-frontend\\src\\utils\\encrypt.js',
    'D:\\code\\design-frontend\\src\\utils\\utils.js'
  ],
  py: [],
  md: [
    'D:\\code\\design-frontend\\README.md',
    'D:\\code\\design-frontend\\src\\docs\\ForgetPasswordTips.md',
    'D:\\code\\design-frontend\\src\\docs\\revokeTips.md',
    'D:\\code\\design-frontend\\src\\docs\\SignUpTips.md',
    'D:\\code\\design-frontend\\src\\docs\\SystemDesc.md',
    'D:\\code\\design-frontend\\src\\docs\\UpdatePasswordTips.md',
    'D:\\code\\design-frontend\\src\\docs\\UploadDataDesc.md',
    'D:\\code\\design-frontend\\src\\docs\\UploadInfoTips.md',
    'D:\\code\\design-frontend\\src\\docs\\VisualTreeExample.md',
    'D:\\code\\design-frontend\\src\\docs\\VisualTreeTips.md'
  ]
}
{ vue: 2336, js: 1606, py: 0, md: 125 }
const fs = require("fs");
const path = require("path");
class LineReader {
  /**
   * 创建一个LineReader实例
   * @param {*} path
   * @param {*} filetypes
   * @param {*} ignoreddir
   */
  constructor(path, filetypes, ignoreddir) {
    this.path = path;
    this.filetypes = filetypes;
    this.ignoreddir = ignoreddir;
    this.dirstructure = LineReader.getDirectoryStructure(
      this.path,
      this.filetypes,
      this.ignoreddir
    );
  }
  /**
   * 对于一个指定的文本类文件返回行数
   * @param {*} filepath
   * @returns
   */
  static checkSingleFileLine(filepath) {
    const filecontent = fs.readFileSync(filepath, "utf-8");
    return filecontent.split("\n").length;
  }
  /**
   * 判断一个路径是否为文件夹
   * @param {*} path
   * @returns
   */
  static isDir(path) {
    return fs.lstatSync(path).isDirectory();
  }
  /**
   * 获取一个文件夹的所有文件,并以对象的形式返回
   * @param {*} pathName
   * @param {*} suffixes
   * @param {*} ignoreddir
   * @returns 按文件格式对象为键的对象
   */
  static getDirectoryStructure = function(pathName, suffixes, ignoreddir) {
    // 定义一个队列,用于遍历整个文件夹
    let arr = [pathName];
    // 定义最终的结果
    let files = {};
    // 按照文件格式向结果对象中的每个文件格式添加一个空数组
    for (let i = 0; i < suffixes.length; i++) {
      files[suffixes[i]] = [];
    }
    // 对文件夹进行DFS
    while (arr.length) {
      // 取出队首
      let curpath = arr.shift();
      if (LineReader.isDir(curpath)) {
        // 当路径为文件夹时
        const curdir = fs.readdirSync(curpath);
        for (let i = 0; i < curdir.length; i++) {
          const newpath = path.join(curpath, curdir[i]);
          if (ignoreddir.indexOf(newpath) < 0) {
            // 判断是否为需要忽略的文件夹,若不是则入队
            arr.push(newpath);
          }
        }
      } else {
        // 当路径为文件时,拆分文件名并统计格式
        const temp = curpath.split(".");
        const cursuffix = temp[temp.length - 1];
        const index = suffixes.indexOf(cursuffix);
        if (index > -1)
          // 若为指定格式则添加至结果对象的所属格式中
          files[suffixes[index]].push(curpath);
      }
    }
    return files;
  };
  /**
   * 获取linereader实例的所有行数
   * @returns 以文件格式为键名的对象
   */
  getLineStatistics() {
    // 取出已经计算好的文件夹内容
    const dir = this.dirstructure;
    // 取出所有文件格式
    let keys = Object.keys(dir);
    let linenums = {};
    for (let i = 0; i < keys.length; i++) {
      // 获取当前应当遍历的文件格式
      const typename = keys[i];
      // 取出当前文件格式对应的所有文件
      const someTypeFiles = dir[typename];
      // 初始化当前文件格式的行数为0
      linenums[typename] = 0;
      for (let j = 0; j < someTypeFiles.length; j++) {
        // 加上当前文件的行数
        linenums[typename] += LineReader.checkSingleFileLine(someTypeFiles[j]);
      }
    }
    return linenums;
  }
}
/** 测试 */
const reader = new LineReader(
  path.resolve("./"),
  ["vue", "txt", "js", "py"],
  [
    path.join(path.resolve("./"), "node_modules"),
    path.join(path.resolve("./"), "dist")
  ]
);
console.log(reader.dirstructure);
console.log(reader.getLineStatistics());