codys-linecounter
A fast, lightweight line counter for real codebases. Cody recursively scans a project and separates physical lines into production code, tests, comments, structured data, and blanks.
JavaScriptCody's Line Counter
A fast, lightweight line counter for real codebases. Cody recursively scans a project and separates physical lines into production code, tests, comments, structured data, and blanks.
Cody's Line Counter
Language Kind Files Lines Code Test Comments Data Blank
────────── ──── ───── ───── ──── ──── ──────── ──── ─────
TypeScript code 8 1,240 812 206 104 0 118
JSON data 3 126 0 0 0 126 0
────────── ──── ───── ───── ──── ──── ──────── ──── ─────
Total 11 1,366 812 206 104 126 118
Highlights
- Recursively scans one path or many paths
- Recognizes more than 50 programming and data formats
- Detects
.test.*,.spec.*,tests/,__tests__/,_test.go,test_*.py,*_spec.rb, .NET test projects, and custom patterns - Uses a stateful lexer for line comments, block comments, strings, and nested comments—not a collection of line-only regular expressions
- Respects root and nested
.gitignorefiles, including negations - Skips dependencies, generated output, binary files, oversized files, and symbolic links safely by default
- Offers stable JSON output and a typed ESM API
- Has one small runtime dependency and supports Node.js 20+
Install
Run it without installing:
npx codys-linecounter
Install it globally:
npm install --global codys-linecounter
cody-lines
Or add it to a project:
npm install --save-dev codys-linecounter
npx cody-lines
Both cody-lines and codys-linecounter are available as command names.
CLI
# Scan the current project
cody-lines
# Scan selected roots and show every included file
cody-lines src test packages --files
# Feed a report to another program
cody-lines . --json
# Add project-specific exclusions and test conventions
cody-lines . --exclude "generated/" --test-pattern "benchmarks/**"
Usage:
cody-lines [paths...] [options]
Options:
--json Print machine-readable JSON
--files Include and display per-file results
--exclude <pattern> Add an ignore-style pattern (repeatable)
--test-pattern <pattern> Add a test-file pattern (repeatable)
--no-gitignore Do not read .gitignore files
--no-default-ignore Scan generated and dependency directories
--include-unknown Count unknown text files as data
--follow-symlinks Follow symbolic links (cycles are detected)
--max-file-size <size> Maximum file size, e.g. 500KB or 10MB
--concurrency <count> Concurrent file reads (default: 32)
--sort <lines|name|files> Sort table rows (default: lines)
--strict Stop on the first unreadable entry
--fail-on-warning Exit with status 1 if warnings occur
-h, --help Show help
-v, --version Show the package version
Patterns passed to --exclude and --test-pattern use gitignore-style
syntax. Place -- before a path that starts with a hyphen.
The default maximum file size is 10 MiB. KB and MB use decimal units;
KiB and MiB use binary units.
What the categories mean
Every physical line belongs to exactly one category:
- Code: executable or declarative content in a production source file
- Test: executable or declarative content in a recognized test file
- Comments: a comment-only line in either production or test code
- Data: non-comment content in JSON, YAML, TOML, XML, CSV, Markdown, and other data formats
- Blank: a whitespace-only line
Consequently, this invariant always holds:
total = code + test + comments + data + blank
A line containing both code and an inline comment is code (or test/data, depending on its file). Blank lines inside block comments remain blank. Test detection is file-based; a production file containing an inline test-only conditional is still production code.
Default exclusions
Cody respects .gitignore by default. It also excludes common sources of
duplicated or third-party lines, including:
- version-control metadata and dependency folders such as
.git/,node_modules/,vendor/, and virtual environments - build and coverage output such as
dist/,build/,target/,.next/, andcoverage/ - minified assets, source maps, and generated dependency lockfiles
Use --no-default-ignore when those files are intentionally part of the
measurement. Explicit file roots are scanned even when their names match a
default exclusion.
Symbolic links are not followed unless --follow-symlinks is set. When they
are followed, Cody resolves targets and detects directory cycles. Be aware
that a symlink may lead outside the requested project.
JavaScript API
The package is ESM and includes TypeScript declarations.
import { countLines, formatReport } from 'codys-linecounter';
const report = await countLines(['src', 'test'], {
exclude: ['src/generated/**'],
includeFiles: true,
testPatterns: ['benchmarks/**'],
});
console.log(report.summary.lines.code);
console.log(formatReport(report, { files: true }));
countLines(inputs?, options?) accepts a path, an array of paths, or no input
(which means the current directory).
Important options:
| Option | Type | Default | Purpose |
|---|---|---|---|
cwd | string | process.cwd() | Base for relative input paths |
exclude | string[] | [] | Additional gitignore-style exclusions |
testPatterns | string[] | [] | Additional test-file patterns |
includeFiles | boolean | false | Populate report.files |
includeUnknown | boolean | false | Treat unknown non-binary files as data |
respectGitignore | boolean | true | Apply nested .gitignore files |
defaultExcludes | boolean | true | Apply Cody's generated/dependency exclusions |
followSymlinks | boolean | false | Resolve symlinks and detect cycles |
maxFileSize | number | 10485760 | Per-file byte limit |
concurrency | number | 32 | Concurrent reads, from 1 to 256 |
strict | boolean | false | Throw instead of recording read warnings |
signal | AbortSignal | — | Cancel an in-progress scan |
onFile | function | — | Receive each completed file result |
The report contains a schema version, summary, language aggregates, optional file results, skip counts, warnings, and elapsed time. JSON produced by the CLI has the same shape.
Additional exports include detectLanguage, findLanguage, isTestFile,
languages, DEFAULT_EXCLUDES, formatReport, and ScanError.
Accuracy and scope
Cody counts physical lines, not AST statements or logical expressions.
Lexical classification is intentionally language-aware but does not fully
parse every grammar. Ambiguous extensions use the most common interpretation
(for example, .m is Objective-C), and embedded languages in a single
template file are reported under the template's language.
Binary files and unknown extensions are skipped. Use --include-unknown only
when treating unrecognized UTF-8 text as data is useful.
Development
npm install
npm run validate
npm run test:coverage
npm pack --dry-run
See CONTRIBUTING.md for contribution guidance and SECURITY.md for vulnerability reporting.
License
MIT © 2026 CodyKoInABox