ccount

A tiny, zero-dependency utility for counting how often a substring occurs in a string.

Library
npm
v2.0.1
6stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
23/100Needs Attention
Development Activity0
Maintenance20
Community12
Maturity60
Momentum0

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
59/100Fair
Architecture75
Code Quality92
Innovation25
Learning Curve45

ccount is a minimal JavaScript utility that counts how many times a substring occurs within a larger string. It is fully typed via JSDoc and TypeScript, ships as an ESM-only module with zero runtime dependencies, and works across Node.js, Deno, and modern browsers.

Despite its simplicity, ccount is a foundational building block in the unified/remark/micromark ecosystem, where it’s used internally by markdown and syntax-tree tooling for small counting tasks, such as deciding whether to prefer single or double quotes based on which occurs more often in a string.

What You Get

  • A single exported function, ccount(value, substring), with no other API surface
  • Full TypeScript typings out of the box, generated from JSDoc annotations
  • Zero runtime dependencies
  • An ESM-only build that runs unmodified in Node.js 14.14+/16+, Deno, and browsers

Common Use Cases

  • Choosing between single and double quotes when generating code, based on which occurs more frequently in a string
  • Counting characters (e.g. newlines or delimiters) while building or serializing syntax trees
  • Validating string content against a minimum or maximum occurrence threshold
  • Lightweight text analysis without adding a full string-utilities dependency

Under The Hood

Architecture ccount has no internal layering to speak of — the entire package is a single exported function, ccount(value, substring) in index.js, that coerces its input to a string and loops over String.prototype.indexOf to tally non-overlapping matches. Type annotations are supplied via JSDoc comments rather than a separate .ts source file, and tsc in checkJs mode compiles those annotations into a standalone index.d.ts declaration file. There’s no dependency injection, no multi-module data flow, and nothing to break if the “core abstraction” changed, since there effectively is none beyond the exported function’s signature.

Tech Stack The package is authored in plain ESM JavaScript (“type”: “module”) with JSDoc type annotations, built into TypeScript declarations via tsc —build against a tsconfig.json targeting es2020/node16. Linting and formatting run through xo (an opinionated ESLint preset) integrated with Prettier, and the README itself is linted via remark-cli with remark-preset-wooorm. Tests run on Node’s built-in node:test runner, coverage is enforced via c8, and CI (GitHub Actions) runs the full install/test suite across multiple Node versions on every push and pull request.

Code Quality A single test.js file exercises the public API using node:test and node:assert/strict, covering the happy path, non-string coercion, an intentionally invalid argument that should throw a TypeError, and multi-byte/emoji substrings. Coverage is enforced at 100% via c8 —check-coverage —100, and type coverage is separately checked at 100% via the type-coverage package. Naming is minimal and self-explanatory, error handling is explicit (a single thrown TypeError rather than a swallowed failure), and the whole pipeline — build, lint, format, test — runs in CI on every change.

What Makes It Unique There’s no proprietary technique here — it’s a straightforward loop over indexOf that any developer could write in a few lines, and comparable helpers exist inside larger general-purpose string or utility libraries. Its actual value is as an extremely small, dependency-free, fully typed primitive that lets ecosystem tooling (notably the unified/remark/micromark markdown toolchain, which uses it internally for small counting tasks) avoid pulling in a heavier utility dependency just for this one operation.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search