如何快速了解项目源文件的构成?基于 Node.js 实现项目源代码数据统计工具

news2024/11/26 5:35:56

当希望了解一个项目的代码规模时,首先可能会想对项目源文件的数量、类型分布、代码行数等做一下数据统计。使用 Linux/git 命令可以满足简单的统计需求,使用流行的 cloc 工具可以实现详细的源代码分析数据。此外也可以使用 Node.js 编码简单的实现个性化数据收集与分析。

1 使用 Linux 命令实现源文件代码行数统计

简单的代码行数统计可以通过 linux 自带的命令实现。示例:

# 统计 src 目录下 ts、tsx、scss 源文件的行数
find ./src "(" -name "*.ts" -or -name "*.tsx" -or -name "*.scss" ")" -print | xargs wc -l 

如果项目使用 git 管理,还可以使用 git 命令统计提交者的代码行数等。示例(Linux/MacBook):

# 统计某个人的代码提交行数
git log --author="renxia" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -

# 统计每个人的代码提交行数
git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; git log --author="$name" --pretty=tformat: --numstat | awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf "added lines: %s, removed lines: %s, total lines: %s\n", add, subs, loc }' -; done 

2 使用 cloc 代码行数统计工具

当希望得到更为全面的统计数据时,可能会通过搜索引擎检索了解到代码行数统计工具cloc(Count lines of code)

cloc 基于 Perl 语言开发,支持多种系统环境下的快捷安装。安装 cloc 的方法参考:

npm install -g cloc# https://www.npmjs.com/package/cloc
sudo apt-get install cloc# Debian, Ubuntu
sudo yum install cloc# Red Hat, Fedora
sudo pacman -S cloc# Arch
sudo pkg install cloc# FreeBSD
sudo port install cloc # Mac OS X with MacPorts 

cloc 经过多年的开源社区迭代,功能已相当丰富,支持数十种常见编程语言的源代码和注释分析与行数统计,支持按目录、文件、甚至压缩文件、git commit hash 等各种方式进行代码行数统计、diff 差异对比分析等。其支持的命令行参数多达数十个。

cloc 的一些用法示例:

# 查看支持的编程语言类型
cloc --show-lang

# 统计 src 和 plugins 目录
cloc src plugins

# 统计 src 目录,按 `代码+注释+空行` 为分母计算输出为百分比格式
cloc --by-percent cmb src

# 统计当前目录下 master.zip 压缩文件
cloc master.zip

# 统计 src/lzwme.ts 文件
cloc src/lzwme.ts

# 统计两次提交(git commitId head)之间的文件与源代码差异
cloc --diff 22e7cf83 ef25fe47 

cloc 工具以代码行数统计为核心目标,提供了多样化的参数,可以实现绝大部分针对代码行数相关的数据统计需求。

但是对于如文件大小统计、按大小或行数输出 Top N、输出重复文件列表等个性化的需求,则仍需编码实现。

3 基于 Node.js 实现个性化的项目源代码统计

在不考虑归档文件、各种编程语言的注释解析、git diff 等复杂场景的前提下,项目代码统计可以简化为文本类文件的数量汇总、大小与行数计算,于是自行编码也可以简单的实现个性化的项目源代码数据统计。其逻辑实现大致包含如下几个要点:

  • 源文件扫描,得到文件列表
  • 文件分类、大小与行数计算
  • 重复文件统计
  • 数据汇总与打印
  • more…

近期在一个前端开发辅助工具项目中,基于以上几个要点,使用 Node.js 尝试实现了简单的项目源代码数据统计功能。下面尝试对相关主要逻辑实现进行逐一分析。

3.1 项目源文件快速扫描

使用 Node.jsfs 模块可以对指定的目录进行文件扫描。但当涉及规则过滤等参数处理时,其实现起来较为繁琐。

这里使用了 fast-glob 库实现指定目录的源文件扫描。示例:

// 使用 fast-glob 扫描 src 和 plugins 目录下的 ts 和 tsx 文件
import glob from 'fast-glob';

const rootDir = process.cwd();
const src = ['src', 'plugins'];
const pattern = '**/*.{ts,tsx}';
const exclude = ['**/node_modules/**'];
const fileList = await glob(src.map(src => `${src}/${pattern}`),{ cwd: rootDir, absolute: true, ignore: exclude }
); 

上面示例展示的逻辑为使用 fast-glob 扫描 src 和 plugins 目录下的 ts 和 tsx 文件。将指定目录、匹配规则、过滤规则等变量参数化,即可实现可定制的目录文件扫描方法。

3.2 文件分类、大小与行数计算

遍历目录扫描得到的文件列表,按文件后缀分类,计算文件大小、行数、md5等信息并缓存,即可得到所有源文件的基本信息。示例:

 const result = {startTime: Date.now(),endTime: 0,total: 0,totalSize: 0,totalLine: 0,totalBlank: 0,topNByLine: [] as string[],topNBySize: [] as string[],exts: {} as Record<string,{total: number;totalSize: number;totalLine: number;totalBlank: number;list: { filepath: string; line: number; blank: number; stat: Stats }[];}>,};//const fileList = getFileList(...);// 所有文件按路径的映射缓存const allFilesInfo = {} as { [filepath: string]: { filepath: string; line: number; md5: string; stat: Stats } };result.total = fileList.length;for (const filepath of fileList) {let ext = extname(filepath).replace(/^./, '').toLowerCase();// 单元测试文件独立统计if (filepath.endsWith(`.test.${ext}`)) ext = `test.${ext}`;else if (filepath.endsWith(`.spec.${ext}`)) ext = `spec.${ext}`;const fileStat = await promises.stat(filepath);const item = { filepath, line: 0, blank: 0, md5: md5(filepath), stat: fileStat };allFilesInfo[filepath] = item;result.totalSize += fileStat.size;// 文件类型分类统计if (!result.exts[ext]) result.exts[ext] = { total: 0, totalSize: 0, totalLine: 0, totalBlank: 0, list: [] };result.exts[ext].list.push(item);result.exts[ext].total++;result.exts[ext].totalSize += fileStat.size;// 文本文件,统计行数if (isTextFile(filepath)) {const content = await promises.readFile(filepath, 'utf8');// 不计算首尾的空格与空行const contentLines = content.trim().split('\n');item.line = contentLines.length;item.blank = contentLines.filter(line => line.trim() === '').length;result.exts[ext].totalLine += item.line;result.exts[ext].totalBlank += item.blank;result.totalLine += item.line;result.totalBlank += item.blank;}} 

上面示例代码包含了如下几个要点:

  • 文件总数统计
  • 计算并统计文件大小、md5(用于重复文件比对)
  • 按文件后缀进行分类
  • 仅对文本文件统计行数
  • 忽略文件首尾的空格与空行

3.3 统计数据结果打印

通过 console 输出打印信息,需注意数据格式的处理。示例:

 const { green, cyanBright, magentaBright } = color;const extsList = Object.entries(result.exts).sort((a, b) => b[1].total - a[1].total);const widths = { row: 70, ext: 10, sep: 15 };const statsInfo = [];for (const [ext, list] of extsList) {statsInfo.push([cyanBright(padSpace(ext, widths.ext)),magentaBright(padSpace(formatQty(list.total), widths.sep)),green(padSpace(formatQty(list.totalBlank), widths.sep)),green(padSpace(formatQty(list.totalLine), widths.sep)),green(padSpace(formatMem(list.totalSize), widths.sep)),].join(''));}console.log(statsInfo); 

其要点主要有:

  • 文件类型按数量排序
  • 使用''.padStart''.padEnd 实现列数据对齐(padSpace)
  • 代码行数数据千分位处理(formatQty)
  • 文件大小数据格式化处理(formatMem)

此外,也可以将结果数据输出至文件以便进一步分析使用。

3.4 个性化数据统计:TopN、重复文件等

基于文件遍历时得到的文件大小、行数信息,可进行降序排序得到 TopN 列表,基于文件 md5 信息,可统计重复文件数据。

按文件大小、行数 TopN 统计示例:

const topN = 50; // 可由输入参数指定

if (topN > 0) {result.topNByLine = fileList.sort((a, b) => allFilesInfo[b].line - allFilesInfo[a].line).slice(0, topN);result.topNBySize = fileList.sort((a, b) => allFilesInfo[b].stat.size - allFilesInfo[a].stat.size).slice(0, topN);const topNfilesByLine = result.topNByLine.map(d => `${greenBright(padSpace(formatQty(allFilesInfo[d].line), 10))} ${showFullPath ? d : fixToshortPath(d, rootDir)}`);statsInfo.push(` ${cyanBright(`Top ${topN} Files By Lines:`)}${fileListToString(topNfilesByLine, '')}`);const topNfilesBySize = result.topNBySize.map(d => `${greenBright(padSpace(formatMem(allFilesInfo[d].stat.size), 10))} ${showFullPath ? d : fixToshortPath(d, rootDir)}`);statsInfo.push(` ${cyanBright(`Top ${topN} Files By Size:`)}${fileListToString(topNfilesBySize, '')}`);
} 

重复文件统计示例:

// 重复文件统计const filepathByMd5 = {} as { [md5: string]: string[] };const duplicates = new Set< string>();Object.values(allFilesInfo).forEach(d => {if (!filepathByMd5[d.md5]) {filepathByMd5[d.md5] = [d.filepath];} else {filepathByMd5[d.md5].push(d.filepath);duplicates.add(d.md5);}});if (duplicates.size > 0) {// 按重复文件数量降序const list = [...duplicates].map(d => filepathByMd5[d]).sort((a, b) => b.length - a.length);result.duplicates = list;const duplicatesTotal = list.reduce((total, item) => total + item.length, 0);statsInfo.push(cyanBright(` Duplicate files[${list.length} - ${duplicatesTotal}]:`));list.forEach(item => {item = item.map(d => `${showFullPath ? d : fixToshortPath(d, options.rootDir)} [${magentaBright(allFilesInfo[d].line)}]`);statsInfo.push(`├─ ${item.join('\n│')}\n`);});} 

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/345678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1.7配置OSPF手动汇总

实验7:配置OSPF手动汇总 实验目的实现OSPF路由汇总的配置阐明OSPF引入的外部路由时进行路由汇总的方法实验拓扑配置OSPF手动汇总实验拓扑如图1-17所示。 图1-17 配置OSPF手动汇总 实验步骤配置IP地址,配置OSPF(和实验6一致,此处略)在…

我还是学生,需要做副业吗?致各位迷茫的学生们

大家好&#xff0c;我是蝶衣王的小编&#xff0c;今天跟大家聊一聊&#xff0c;学生党应该做副业吗本文仅支持成人学生党查看&#xff0c;如果您还是未成年人&#xff0c;请立即退出本文。如今你唯一要做的就是好好学习&#xff0c;不要想那么多关于金钱的事&#xff0c;等到你…

CMMI-立项管理流程

立项管理&#xff08;Project Initialization Management, PIM&#xff09;的目的是&#xff1a;&#xff08;1&#xff09;采纳符合机构最大利益的立项建议&#xff0c;通过立项管理使该建议成为正式的项目&#xff08;即合法化&#xff09;。&#xff08;2&#xff09;杜绝不…

2023年阿里云活动有哪些实例规格的云服务器?如何选择这些实例规格

2023年阿里云活动有哪些实例规格的云服务器&#xff1f;新手用户通过阿里云活动选购阿里云服务器的时候实例规格应该怎么选&#xff0c;因为同配置的云服务器往往有多种不同是规格的云服务器可供选择&#xff0c;而且不同实例规格的云服务器之间价格差别还比较大&#xff0c;因…

CPP2022-28-期末模拟测试01

6-1 实现一个计算三角形面积的简单函数&#xff08;假设输入的边长合理&#xff09;。 分数 10 全屏浏览题目 切换布局 作者 王和兴 单位 东北大学秦皇岛分校 实现一个计算三角形面积的简单函数&#xff08;假设输入的边长合理&#xff09;。 函数接口定义&#xff1a; do…

Seata-server 源码学习(一)

Seata源码学习引入 学习了Seata的应用以后&#xff0c;我们从这开始要开始分析Seata的源码相关内容 源码下载 官方地址&#xff1a;https://seata.io/zh-cn/blog/download.html 通过idea打开seata-1.4.2版本的源码 回顾AT模式 其实在之前的应用课程中&#xff0c;我们已经用…

Windows 离线安装 MySQL 8

目录 1. 下载离线安装包 2. 上传解压 3 配置 my.ini 文件 4 设置系统环境变量 5 安装 MySQL 6 登录 MySQL 客户环境是内网环境&#xff0c;不能访问外网&#xff0c;只能离线安装 MySQL 了。 1. 下载离线安装包 MySQL 离线压缩包官网下载地址&#xff1a;MySQL :: Down…

Java基础面试题——面向对象和集合专题

文章目录1. 面向对象和面向过程的区别2. 介绍下Java中的基本数据类型3. 标识符的命名规则4. instanceof关键字的作用5.重载和重写的区别6.介绍下内部类7.介绍下Java中的四种引用8.HashCode的作用9.有没有可能两个不相等的对象有相同的hashcode10.深拷贝和浅拷贝的区别是什么?1…

STM32单片机DS18B20测温程序源代码

OLED液晶屏电路接口DS18B20电路接口STM32单片机DS18B20测温程序源代码#include "sys.h"#define LED_RED PBout(12)#define LED_GREEN PBout(13)#define LED_YELLOW PBout(14)#define LED_BLUE PBout(15)#define DS18B20_IO_IN() {GPIOA->CRL&0XFFFFFFF0;GPIOA…

使用Arthas定位问题

功能概述 首先&#xff0c;Arthas的常用功能大概有以下几个&#xff1a; 解决依赖冲突 sc命令&#xff1a;模糊查看当前 JVM 中是否加载了包含关键字的类&#xff0c;以及获取其完全名称。 sc -d 关键字 注意使用 sc -d 命令&#xff0c;获取 classLoaderHash命令&#xff1a…

Java 快速判断一个 IP 是否在给定的网段内

目录方法一&#xff1a;借助于 Java 提供的 InetAddress方法二&#xff1a;撸个算法实现&#xff08;二进制计算&#xff09;其他数字转为子网掩码要在Java中判断一个IP地址是否在给定的网段内&#xff0c;可以使用子网掩码将IP地址和子网掩码进行与操作来提取网络地址&#xf…

计算机网络入门

一&#xff0c;计算机网络在信息时代中的作用 21世纪的一些重要特征就是数字化&#xff0c;网络化和信息化&#xff0c;它是一个以网络为核心的信息时代。有三类大家很熟悉的网络&#xff0c;即电信网络&#xff0c;有线电视网络和计算机网络。按照最初的服务分工&#xff0c;…

GB28181-2022注册注销基本要求、注册重定向解读和技术实现

规范解读GB28181-2022注册、注销基本要求相对GB28181-2016版本&#xff0c;做了一定的调整&#xff0c;新调整的部分如下&#xff1a;——更改了注册和注销基本要求&#xff08;见 9.1.1&#xff0c;2016 年版的 9.1.1&#xff09;。1.增加对NAT模式网络传输要求&#xff0c;宜…

Vulnhub 渗透练习(一)—— Breach 1.0

环境搭建 环境下载&#xff1a; https://www.vulnhub.com/entry/breach-1,152/ 环境描述&#xff1a; Vulnhub 中对此环境的描述&#xff1a; VM 配置有静态 IP 地址 (192.168.110.140)&#xff0c;因此您需要将仅主机适配器配置到该子网。 这里我用的是 VMware &#xff0…

零信任-腾讯零信任iOA介绍(4)

​腾讯零信任介绍 腾讯零信任是一种信息安全架构&#xff0c;旨在通过限制对计算设备、数据和应用程序的访问来保护敏感信息。腾讯零信任的主要思想是&#xff0c;任何计算设备、数据或应用程序都不应被自动信任&#xff0c;并需要经过授权后才能访问敏感信息。 腾讯零信任的…

MyBatis的工作原理

1、读取MyBatis 配置文件&#xff1a;mybatis-config.xml 为MyBatis 的全局配置文件&#xff0c;配置了MyBatis 的运行环境等信息&#xff0c;例如数据库连接信息。 2、加载映射文件。映射文件即SQL 映射文件&#xff0c;该文件中配置了操作数据库的SQL 语句&#xff0c;需要在…

运动耳机买什么样的好、最好用的运动耳机排行榜

2月中旬&#xff0c;气温回暖&#xff0c;路面冰雪融化&#xff0c;又到了运动的好时节。难道还要每天上下班后就回家躺着嘛&#xff0c;浪费时间可耻&#xff0c;为什么不做一些更有意义的事情呢&#xff1f;即刻出发&#xff0c;开始空余&#xff0c;享受运动锻炼的乐趣&…

如何开发一个小游戏?其中有什么难点

如果仅仅针对个人开发者来讲&#xff0c;要独立开发一款大型游戏几乎无可能&#xff0c;更大成功的可能还是开发一款类似《羊了个羊》这样洗脑的小程序游戏。 所以这里主要论述小游戏开发的情况&#xff0c;也就是小程序游戏&#xff0c;首先从小游戏的开发流程来看&#xff1…

大数据之-Nifi-认识Nifi_Nifi的核心概念_Nifi核心架构_Nifi的性能_Nifi的关键特性---大数据之Nifi工作笔记0001

用来管理不同系统之间的信息流的工具. ETL工具 kettle是数据的转换 比如kettle来说,如果需要做ETL的数据特别大量特别多,他就会支持不了数据的转换会有崩溃的现象 可以看到nifi解决的是dataflow的问题,解决的是数据流的问题 可以看到Nifi的用处,用来处理数据的分发,是BS架构…

jenkins实现接口自动化持续集成(python+pytest+ Allure+git)

在用python做自动化测试时&#xff0c;我们写好代码&#xff0c;然后需要执行才能得到测试报告&#xff0c;这时我们可以通过 Jenkins 来进一步完成自动化工作。借助Jenkins&#xff0c;我们可以结合 Git/SVN 自动拉取代码&#xff0c;通过设置定时构建实现自动触发脚本执行&am…