Last active
August 10, 2023 16:43
-
-
Save splatterxl/fdca9f4fb5d0ed6b44bf3dcf9366b9ed to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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