This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user