Skip to content

count-loc.sh — Lines of Code Counter

count-loc.sh — Lines of Code Counter

Location: scripts/count-loc.sh Added: 2026-03-08 Shell: zsh (no external dependencies — standard POSIX tools only)


Overview

count-loc.sh is a zero-dependency shell script that counts lines of code across the entire repository, broken down by language. It is designed to run quickly against a local clone without requiring any third-party tools such as tokei or cloc.

It lives in scripts/ alongside the other TypeScript utility scripts (sync-version.ts, generate-docs.ts, etc.) and follows the same convention of being run from the repository root.


Usage

Terminal window
# Make executable once
chmod +x scripts/count-loc.sh
# Full language breakdown (default)
./scripts/count-loc.sh
# Exclude lock files, *.d.ts, and minified files
./scripts/count-loc.sh --no-vendor
# Print only the grand total — useful for CI badges or scripting
./scripts/count-loc.sh --total
# Help
./scripts/count-loc.sh --help

Options

FlagDescription
(none)Count all recognised source files; print a per-language table
--no-vendorAdditionally exclude lock files and generated/minified artefacts
--totalPrint only the integer grand total and exit
--help / -hPrint usage and exit

Sample Output

Language Lines Share
------------------------------ ---------- ------
TypeScript 14823 71.2%
Markdown 3201 15.4%
YAML 892 4.3%
JSON 741 3.6%
Shell 312 1.5%
CSS 289 1.4%
HTML 201 1.0%
TOML 198 1.0%
Python 155 0.7%
------------------------------ ---------- ------
TOTAL 20812 100%

How It Works

1. Repo-root resolution

The script uses zsh’s ${0:A:h} (absolute path of the script’s directory) and navigates one level up to find the repo root, so it works correctly regardless of where it is invoked from:

Terminal window
SCRIPT_DIR="${0:A:h}" # → /path/to/repo/scripts
REPO_ROOT="${SCRIPT_DIR:h}" # → /path/to/repo
cd "$REPO_ROOT"

2. Directory pruning

find prune expressions are built dynamically from PRUNE_DIRS to skip noisy directories in a single traversal pass:

node_modules .git dist build .wrangler
output coverage .turbo .next .angular

3. Language detection

Files are matched by extension using an associative array (typeset -A EXT_LANG). Dockerfiles (no extension) are matched by name pattern instead.

Recognised extensions:

Extension(s)Language
.tsTypeScript
.tsxTypeScript (TSX)
.jsJavaScript
.mjs / .cjsJavaScript (ESM / CJS)
.cssCSS
.scssSCSS
.htmlHTML
.pyPython
.sh / .zshShell / Zsh
.tomlTOML
.yaml / .ymlYAML
.jsonJSON
.mdMarkdown
.sqlSQL
Dockerfile*Dockerfile

4. Vendor filtering (--no-vendor)

When --no-vendor is passed, files matching the following patterns are excluded via grep -v after collection:

pnpm-lock.yaml package-lock.json deno.lock yarn.lock
*.min.js *.min.css *.generated.ts *.d.ts

5. Line counting

Lines are counted with xargs wc -l, which is the fastest approach on macOS and Linux for large file sets. The total is extracted from wc’s own summary line and accumulated per language.


What Is and Is Not Counted

Always counted (default mode)

  • All source files matching the recognised extensions above
  • Lock files (pnpm-lock.yaml, deno.lock, etc.)
  • TypeScript declaration files (*.d.ts)
  • Minified files

Excluded by default

  • node_modules/
  • .git/
  • dist/, build/, output/
  • .wrangler/, .angular/, .turbo/, .next/
  • coverage/

Additionally excluded with --no-vendor

  • pnpm-lock.yaml, package-lock.json, deno.lock, yarn.lock
  • *.d.ts
  • *.min.js, *.min.css
  • *.generated.ts

Note: The script counts all lines (including blank lines and comments). It does not perform semantic filtering. For blank/comment-stripped counts, use tokei or cloc (see Alternatives below).


Integration

CI / GitHub Actions

Use --total to surface the line count as a step output or log annotation:

- name: Count lines of code
run: |
chmod +x scripts/count-loc.sh
LOC=$(./scripts/count-loc.sh --total)
echo "Total LOC: $LOC"
echo "loc=$LOC" >> "$GITHUB_OUTPUT"

Pre-commit hook

.git/hooks/pre-commit
#!/usr/bin/env zsh
echo "Repository LOC:"
./scripts/count-loc.sh --no-vendor

Alternatives

For richer output (blank lines, comment lines, source lines broken out separately), install one of these popular tools:

Terminal window
# tokei — fastest, Rust-based
brew install tokei
tokei .
# cloc — Perl-based, very detailed
brew install cloc
cloc --exclude-dir=node_modules,.git .

Both are referenced in a comment at the bottom of count-loc.sh as a reminder.