From ecc1578b9267861b86b9f028690aaed66afcc0a3 Mon Sep 17 00:00:00 2001 From: luna Date: Thu, 16 Jul 2026 20:36:06 +0000 Subject: [PATCH] Ignore generic metadata files in counts --- README.md | 7 +++++ src/file-filter.ts | 57 ++++++++++++++++++++++++++++++++++++ src/services/repo-counter.ts | 5 ++++ tests/file-filter.test.js | 12 ++++++++ 4 files changed, 81 insertions(+) create mode 100644 src/file-filter.ts create mode 100644 tests/file-filter.test.js diff --git a/README.md b/README.md index dd3d5b5..8cc0cd9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ Tiny API that clones a Git repo and counts its non-empty lines of code. - Optional API key enforcement - Per-repo in-memory caching - Extension-based language breakdown +- Generic project metadata files are ignored during counting - Basic rate limiting - Bounded concurrent scans so the host does not get hammered - SSH key file support for private repos @@ -66,6 +67,12 @@ curl "http://localhost:3000/ssh/public-key?ssh_key=loc_via_git_ed25519" - The service also sweeps stale temp directories in case a process dies mid-scan. - Cache entries live in memory only and expire after `CACHE_TTL_MINUTES`. +## Counting rules + +- Only text files are counted. +- Empty lines are ignored. +- Generic project metadata files are skipped with a filename blacklist, for example `package.json`, lockfiles, `tsconfig.json`, and similar config/build files. + ## Configuration Copy `.env.example` to `.env` and adjust: diff --git a/src/file-filter.ts b/src/file-filter.ts new file mode 100644 index 0000000..fa57a09 --- /dev/null +++ b/src/file-filter.ts @@ -0,0 +1,57 @@ +import path from "node:path"; + +const ignoredFileNames = new Set([ + ".editorconfig", + ".gitattributes", + ".gitignore", + ".gitmodules", + ".npmrc", + ".nvmrc", + ".prettierignore", + ".prettierrc", + ".prettierrc.json", + ".yarnrc", + ".yarnrc.yml", + "bun.lock", + "bun.lockb", + "cargo.lock", + "composer.json", + "composer.lock", + "deno.json", + "deno.lock", + "docker-compose.yaml", + "docker-compose.yml", + "eslint.config.js", + "eslint.config.mjs", + "global.json", + "go.mod", + "go.sum", + "gradle.properties", + "makefile", + "package-lock.json", + "package.json", + "pnpm-lock.yaml", + "poetry.lock", + "pom.xml", + "requirements.txt", + "rollup.config.js", + "rollup.config.mjs", + "settings.gradle", + "settings.gradle.kts", + "tsconfig.base.json", + "tsconfig.build.json", + "tsconfig.json", + "tsconfig.node.json", + "turbo.json", + "vite.config.js", + "vite.config.mjs", + "vite.config.ts", + "webpack.config.js", + "webpack.config.ts", + "yarn.lock" +]); + +export function shouldIgnoreCountFile(filePath: string): boolean { + const fileName = path.basename(filePath).toLowerCase(); + return ignoredFileNames.has(fileName); +} diff --git a/src/services/repo-counter.ts b/src/services/repo-counter.ts index 7ffb35e..ee2ec9b 100644 --- a/src/services/repo-counter.ts +++ b/src/services/repo-counter.ts @@ -5,6 +5,7 @@ import path from "node:path"; import type { RuntimeConfig } from "../config.js"; import { HttpError } from "../errors.js"; +import { shouldIgnoreCountFile } from "../file-filter.js"; import { detectLanguage } from "../language.js"; import { Semaphore } from "../lib/semaphore.js"; import type { CountRequest, CountResult, LanguageStat } from "../types.js"; @@ -183,6 +184,10 @@ async function countDirectory(rootDir: string): Promise<{ continue; } + if (shouldIgnoreCountFile(fullPath)) { + continue; + } + const lines = await countFileLines(fullPath); const language = detectLanguage(fullPath); const current = languages.get(language) ?? { language, files: 0, lines: 0 }; diff --git a/tests/file-filter.test.js b/tests/file-filter.test.js new file mode 100644 index 0000000..67b9864 --- /dev/null +++ b/tests/file-filter.test.js @@ -0,0 +1,12 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +import { shouldIgnoreCountFile } from "../dist/file-filter.js"; + +test("shouldIgnoreCountFile skips generic project metadata files", () => { + assert.equal(shouldIgnoreCountFile("/tmp/package.json"), true); + assert.equal(shouldIgnoreCountFile("/tmp/tsconfig.json"), true); + assert.equal(shouldIgnoreCountFile("/tmp/docker-compose.yml"), true); + assert.equal(shouldIgnoreCountFile("/tmp/src/index.ts"), false); + assert.equal(shouldIgnoreCountFile("/tmp/README.md"), false); +});