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
|
- Optional API key enforcement
|
||||||
- Per-repo in-memory caching
|
- Per-repo in-memory caching
|
||||||
- Extension-based language breakdown
|
- Extension-based language breakdown
|
||||||
|
- Generic project metadata files are ignored during counting
|
||||||
- Basic rate limiting
|
- Basic rate limiting
|
||||||
- Bounded concurrent scans so the host does not get hammered
|
- Bounded concurrent scans so the host does not get hammered
|
||||||
- SSH key file support for private repos
|
- 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.
|
- 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`.
|
- 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
|
## Configuration
|
||||||
|
|
||||||
Copy `.env.example` to `.env` and adjust:
|
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 type { RuntimeConfig } from "../config.js";
|
||||||
import { HttpError } from "../errors.js";
|
import { HttpError } from "../errors.js";
|
||||||
|
import { shouldIgnoreCountFile } from "../file-filter.js";
|
||||||
import { detectLanguage } from "../language.js";
|
import { detectLanguage } from "../language.js";
|
||||||
import { Semaphore } from "../lib/semaphore.js";
|
import { Semaphore } from "../lib/semaphore.js";
|
||||||
import type { CountRequest, CountResult, LanguageStat } from "../types.js";
|
import type { CountRequest, CountResult, LanguageStat } from "../types.js";
|
||||||
@@ -183,6 +184,10 @@ async function countDirectory(rootDir: string): Promise<{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldIgnoreCountFile(fullPath)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const lines = await countFileLines(fullPath);
|
const lines = await countFileLines(fullPath);
|
||||||
const language = detectLanguage(fullPath);
|
const language = detectLanguage(fullPath);
|
||||||
const current = languages.get(language) ?? { language, files: 0, lines: 0 };
|
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