Skip to content

Instantly share code, notes, and snippets.

@splatterxl
Last active August 10, 2023 16:43
Show Gist options
  • Save splatterxl/fdca9f4fb5d0ed6b44bf3dcf9366b9ed to your computer and use it in GitHub Desktop.
Save splatterxl/fdca9f4fb5d0ed6b44bf3dcf9366b9ed to your computer and use it in GitHub Desktop.
/**
* Print the amount of LoC (lines of code) you have in a directory (defaults to ., can be configured
* with the second command-line argument.
*
* - Counts comments.
* - Has a basic blocklist for generated directories/files.
*
* Copyright (C) 2021, 2022 Splatterxl.
*/
import { opendir, readFile } from "fs/promises";
import { join, resolve, basename } from "path";
let i = 0;
const args = process.argv.slice(2);
async function* walk(path, cb) {
if (!cb(path)) return;
const dir = await opendir(path);
for await (const item of dir) {
i += 1;
const file = join(dir.path, item.name);
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(`${i} items scanned...`);
if (item.isFile()) {
if (cb(file)) yield file;
} else if (item.isDirectory()) {
if (cb(file)) yield* walk(file, cb);
}
}
}
const path = args.find(v => !v.startsWith('-'));
const commandsDirectory = path?.startsWith("/")
? path
: resolve(process.cwd() + "/" + (path ?? ""));
console.log(`read dir ${commandsDirectory}`);
function match(file, regex, alert = false) {
const matches = regex.test(file);
if (alert && matches && args.includes("--debug"))
console.log(` => skipping ${file.replace(process.cwd(), ".")} because it matches ${regex}`);
return matches;
}
const cb = (file) =>
// ignored folders
!["node_modules", "target", ".git", ".pnpm", "dist", "build", ".next", ".elixir_ls", ".yarn"].some((v) =>
match(file, new RegExp(`\\/${v}\\/|\\/${v}$`, "gi"), true)
) &&
// ignored files
![
".DS_Store",
"package.json",
"package-lock.json",
"LICENSE",
"CONTRIBUTING",
].some((v) => match(basename(file), new RegExp(`^${v}$`, "gi"), true)) &&
// ignored extensions
![
".png",
".jpg",
".webp",
".webm",
".svg",
".exe",
".beam",
".pem",
".crt",
".gpg",
".pgp",
".key",
".asc",
".ini",
".lock",
".jar",
".gz",
".tar",
].some((v) => match(basename(file), new RegExp(`${v}$`, "gi"))) &&
// package manager files
![
".npmignore",
".gitignore",
".yarnignore",
".pnpmignore",
"pnpm-lock.yaml",
"gradlew",
"gradlew.bat",
".yarnrc",
].some((v) => match(basename(file), new RegExp(`${v}$`, "gi"), true));
const languages = {
js: "JavaScript",
ts: "TypeScript",
py: "Python",
rb: "Ruby",
go: "Go",
java: "Java",
c: "C",
h: "C Header",
cpp: "C++",
hpp: "C++ Header",
cs: "C#",
html: "HTML",
htm: "HTML",
css: "CSS",
scss: "SCSS",
sass: "SASS",
less: "LESS",
php: "PHP",
ex: "Elixir",
exs: "Elixir",
elm: "Elm",
erl: "Erlang",
erb: "ERB",
txt: "Plain Text",
md: "Markdown",
rst: "reStructuredText",
json: "JSON",
yml: "YAML",
yaml: "YAML",
xml: "XML",
ini: "INI configuration",
toml: "TOML",
sh: "Bash",
bat: "Batch",
cmd: "Batch",
ps1: "PowerShell",
ps: "PowerShell",
pl: "Perl",
jsx: "JavaScript React",
tsx: "TypeScript React",
rs: "Rust",
zsh: "ZSH",
vim: "Vim",
lua: "Lua",
sql: "SQL",
kt: "Kotlin",
kts: "Kotlin",
groovy: "Groovy",
gradle: "Gradle",
};
const singleFiles = ["Dockerfile", "Makefile"];
let loc = 0;
let files = new Map();
for await (const file of walk(commandsDirectory, cb)) {
if (args.includes("--debug")) console.log("--- processing %s", file.replace(process.cwd(), "."));
let arr = (await readFile(file, "utf-8")).match(/.*/g);
let exts = file.split(/\\|\//g).reverse()[0].split(/\./g);
let ext = exts[exts.length - 1];
// track .d.ts files
if (exts.length > 3 && exts[exts.length - 2] == "d" && ext == "ts") {
ext = exts[exts.length - 2] + "." + ext;
}
if ((exts.length < 2 && !singleFiles.includes(ext)) || exts[0] == "")
continue;
loc += arr.length;
if (!files.has(languages[ext] ?? ext)) {
files.set(languages[ext] ?? ext, arr.length);
} else {
let existing = files.get(languages[ext] ?? ext);
files.set(languages[ext] ?? ext, existing + arr.length);
}
}
process.stdout.write("done\n");
console.log("-----------");
console.log(`${loc} lines of code`);
for (let [lang, loc] of [...files].sort((a, b) => b[1] - a[1])) {
console.log(`\t${lang}: ${loc} lines`);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment