sindresorhus/pretty-bytes
Convert bytes to a human readable string: 1337 → 1.34 kB
Slowing — last commit 8mo ago
worst-casePermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
last commit was 8mo ago; Scorecard "Branch-Protection" is 0/10…
- ✓Last commit 8mo ago
- ✓5 active contributors
- ✓MIT licensed
- ✓CI configured
- ✓Tests present
- ⚠Slowing — last commit 8mo ago
- ⚠Small team — 5 top contributors
- ⚠Single-maintainer risk — top contributor 88% of commits
- ⚠Scorecard: marked unmaintained (0/10)
- ⚠Scorecard: default branch unprotected (0/10)
What would change the verdict?
- →Deploy as-is WAIT → GO if: 1 commit in the last 180 days; bring "Branch-Protection" to ≥3/10 (see scorecard report)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests + OpenSSF Scorecard
Embed this verdict
[](https://repopilot.app/r/sindresorhus/pretty-bytes)Paste into your README — the badge live-updates from the latest cached analysis.
Onboarding doc
Onboarding: sindresorhus/pretty-bytes
Generated by RepoPilot · 2026-05-05 · Source
Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/sindresorhus/pretty-bytes shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
Verdict
WAIT — Slowing — last commit 8mo ago
- Last commit 8mo ago
- 5 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Slowing — last commit 8mo ago
- ⚠ Small team — 5 top contributors
- ⚠ Single-maintainer risk — top contributor 88% of commits
- ⚠ Scorecard: marked unmaintained (0/10)
- ⚠ Scorecard: default branch unprotected (0/10)
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests + OpenSSF Scorecard</sub>
Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live sindresorhus/pretty-bytes
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/sindresorhus/pretty-bytes.
What it runs against: a local clone of sindresorhus/pretty-bytes — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in sindresorhus/pretty-bytes | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 256 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of sindresorhus/pretty-bytes. If you don't
# have one yet, run these first:
#
# git clone https://github.com/sindresorhus/pretty-bytes.git
# cd pretty-bytes
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of sindresorhus/pretty-bytes and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "sindresorhus/pretty-bytes(\\.git)?\\b" \\
&& ok "origin remote is sindresorhus/pretty-bytes" \\
|| miss "origin remote is not sindresorhus/pretty-bytes (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "index.js" \\
&& ok "index.js" \\
|| miss "missing critical file: index.js"
test -f "index.d.ts" \\
&& ok "index.d.ts" \\
|| miss "missing critical file: index.d.ts"
test -f "package.json" \\
&& ok "package.json" \\
|| miss "missing critical file: package.json"
test -f "test.js" \\
&& ok "test.js" \\
|| miss "missing critical file: test.js"
test -f "readme.md" \\
&& ok "readme.md" \\
|| miss "missing critical file: readme.md"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 256 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~226d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/sindresorhus/pretty-bytes"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
TL;DR
A lightweight utility that converts byte counts into human-readable strings using SI prefixes (base-10, e.g. 1337 bytes → '1.34 kB'). It supports binary prefixes (base-2 for KiB/MiB), bit formatting, locale-aware number formatting, and signed output for displaying file size differences. Written as a pure ESM module with TypeScript type definitions. Single-file ESM module: index.js exports one default function prettyBytes(), with type definitions in index.d.ts. No monorepo structure; test suite in test.js covers all functionality. Includes TypeScript definition tests (index.test-d.ts) to validate type definitions match implementation.
Who it's for
Frontend and backend developers building file upload interfaces, progress displays, storage dashboards, or system monitoring tools who need quick byte-to-human conversion without external dependencies. Used by library maintainers who need this functionality in their own packages (it's a dependency of other npm packages).
Maturity & risk
Production-ready and actively maintained. Version 7.1.0 with Node ≥20 requirement indicates modern maintenance. Contains comprehensive test coverage (test.js) and TypeScript definitions (index.d.ts), with CI/CD via GitHub Actions (main.yml). Single maintainer (Sindre Sorhus) but well-established with stable API.
Standard open source risks apply.
Active areas of work
No active development signals visible in the provided data (no PR/issue counts, commit recency, or milestone information). The package appears stable and feature-complete at v7.1.0 with no breaking changes mentioned.
Get running
git clone https://github.com/sindresorhus/pretty-bytes.git
cd pretty-bytes
npm install
npm test
Daily commands:
npm test
Runs: xo (linting), ava (unit tests in test.js), and tsd (TypeScript definition tests in index.test-d.ts).
Map of the codebase
index.js— Core entry point implementing the byte-to-human-readable conversion algorithm with locale and formatting support; all functionality flows through here.index.d.ts— TypeScript type definitions defining the public API contract; required for type safety and IDE autocomplete in consuming projects.package.json— Package metadata, ESM export configuration, and Node 20+ engine requirement; critical for correct module resolution and runtime compatibility.test.js— Test suite validating all options (signed, bits, locale, fixedWidth) and edge cases; ensures behavior stability across releases.readme.md— API documentation and usage examples; primary reference for understanding function signature, options, and locale behavior.
Components & responsibilities
- prettyBytes() function (JavaScript, Intl.NumberFormat) — Main export; accepts number/bigint and options object; orchestrates conversion, locale formatting, and output styling
- Failure mode: Returns NaN or throws if input is invalid; incorrect locale throws from Intl; fixedWidth overflow not truncated
- Unit conversion logic (JavaScript math, array iteration) — Determines correct unit (B, kB, MB, etc.) by iterating UNITS and comparing against threshold; calculates scaled value
- Failure mode: Very large numbers may exceed largest unit (EB); precision loss for very small fractional values
- Locale formatting (Intl.NumberFormat API) — Delegates number formatting to Intl.NumberFormat for decimal separator and digit grouping per locale
- Failure mode: Unsupported locale codes silently default to 'en-US'; some locales may not have expected separators
- Sign and padding logic (String manipulation, padding) — Prepends sign character (+ or −) if signed option is true; left-pads with spaces to fixedWidth if specified
- Failure mode: fixedWidth not respected if output exceeds width; no truncation occurs
Data flow
Caller→prettyBytes()— Input: number/bigint and optional options object (signed, bits, locale, fixedWidth)prettyBytes()→Unit conversion— Determines scale and unit based on input value magnitudeUnit conversion→Intl.NumberFormat— Passes scaled numeric value and locale to Intl for localized formattingIntl.NumberFormat→prettyBytes()— Returns locale-formatted number string (e.g., '1,34' in de-DE)prettyBytes()→Sign/padding logic— Applies sign prefix and fixedWidth padding to formatted stringSign/padding logic→Caller— Output: human-readable byte string (e.g., '+1.34 kB', ' 1.34 kB')
How to make changes
Add a new formatting option
- Add the new option property to the TypeScript interface in index.d.ts (e.g., maximumFractionDigits?: number) (
index.d.ts) - Implement the logic in index.js to read and apply the new option in the formatting algorithm (
index.js) - Add test cases in test.js covering the new option with various inputs and edge cases (
test.js) - Add usage example and documentation to readme.md showing the new option in action (
readme.md)
Add support for a new locale
- The locale option is already implemented in index.js using Intl.NumberFormat; add test case in test.js with the new locale code (
test.js) - Run tests to validate the locale is correctly formatted by the native Intl API (
test.js)
Modify unit conversion thresholds or units list
- Edit index.js where the UNITS array or conversion constants are defined (kB, MB, GB, etc. progression) (
index.js) - Update test.js with test cases reflecting the new thresholds and unit boundaries (
test.js) - Document the change in readme.md if it affects user-visible behavior (
readme.md)
Why these technologies
- ECMAScript Module (ESM) — Modern JavaScript module system with explicit exports; enables tree-shaking and is the standard for contemporary npm packages.
- Intl.NumberFormat — Native browser/Node API for locale-aware number formatting; eliminates dependency on external locale libraries and provides accurate localization.
- TypeScript — Provides type safety and IntelliSense for consumers; tsd validates type definitions match implementation at build time.
- ava + xo — ava provides isolated test execution; xo enforces consistent code style. Together they ensure quality and maintainability with minimal configuration.
Trade-offs already made
-
Base-10 units (kB, MB) instead of binary units (KiB, MiB)
- Why: Simpler, more familiar to end users; matches SI standard and common usage.
- Consequence: Users comparing file sizes to OS tools that use base-2 will see different numbers; readme clarifies this and links to explanation.
-
Locale formatting via Intl.NumberFormat rather than custom rules
- Why: Avoids maintaining a locale-specific rules database and leverages native APIs.
- Consequence: Locale support is limited to what Node.js/browser Intl supports; edge cases in rare locales may not format perfectly.
-
Single zero-dependency module
- Why: Minimizes bundle size and external risk; package is small and focused.
- Consequence: Cannot delegate formatting; must implement locale handling directly via Intl.
-
Node 20+ engine requirement
- Why: Ensures modern JavaScript features and stable Intl implementation.
- Consequence: Users on Node <20 cannot use this version; would need to use older releases.
Non-goals (don't propose these)
- Does not handle binary (base-2) units like KiB, MiB — only decimal SI units
- Does not provide reverse conversion (human string back to bytes)
- Not a real-time or streaming formatter — processes a single number per call
- Does not provide custom unit lists or thresholds via options
- Does not handle negative bigint values in all locales (edge case per Node.js Intl)
Anti-patterns to avoid
- Hardcoded unit thresholds (Low) —
index.js (UNITS array logic): Unit progression (B → kB → MB → GB → TB → PB → EB) is fixed in code; adding custom units or thresholds requires code modification rather than configuration. - Silent locale fallback (Low) —
index.js (Intl.NumberFormat instantiation): Invalid locale codes do not throw; Intl silently uses en-US fallback, potentially masking configuration errors. - No input validation messaging (Medium) —
index.js: Invalid inputs (e.g., non-numeric types) cause Intl errors rather than descriptive validation messages; error context is lost.
Performance hotspots
index.js (unit loop)(Algorithm efficiency) — Iterates through UNITS array until threshold is exceeded; worst case O(n) where n=9 units. Negligible for typical inputs but not memoized.Intl.NumberFormat instantiation(Runtime performance (microsecond-level)) — Creating a new Intl.NumberFormat instance per call; could be cached by locale for repeated calls with same locale.
Traps & gotchas
None obvious from provided data. Module is straightforward: ESM-only (no CommonJS), Node 20+ only (uses modern Intl API). No environment variables, external services, or config files required. The locale option uses browser/Node Intl.NumberFormat, so ensure test environment has locale data available (typically standard in modern Node.js).
Architecture
Concepts to learn
- SI Prefix (base-10) — pretty-bytes defaults to SI prefixes (kB, MB, GB where 1 kB = 1000 bytes) as opposed to binary prefixes; understanding this distinction is critical for choosing the right output format
- Binary Prefix (base-2) — The binary option switches to KiB/MiB/GiB (where 1 KiB = 1024 bytes), essential for displaying memory usage accurately; this repo lets users toggle between both standards
- Intl.NumberFormat API — pretty-bytes uses this modern Web API for locale-aware number formatting (decimal separators, thousand separators); understanding it helps modify localization behavior
- Significant Figures — The default behavior rounds to 3 significant digits (e.g., 1.34 kB); the repo trades off between readability and precision, tunable via minimumFractionDigits/maximumFractionDigits
- Truncation vs. Rounding — When minimumFractionDigits/maximumFractionDigits are set, values are truncated rather than rounded for 'more intuitive' output; this design choice affects test expectations and user-facing behavior
- ESM (ECMAScript Modules) — pretty-bytes is ESM-only (no CommonJS fallback); contributors must understand import/export syntax and Node.js ESM handling, including the 'type': 'module' package.json directive
- BigInt Type Support — The function accepts both number and bigint inputs for handling arbitrarily large byte counts; this requires conditional logic and type checking not obvious in a simple number→string converter
Related repos
yg/filesize— Alternative bytes-to-human conversion library with support for more formats (metric, IEC, SI) and more configuration optionsevgenyneu/js-file-download— Pairs with pretty-bytes: handles file downloads while pretty-bytes displays the file size to userssindresorhus/bytes-to-string— Similar conversion concept for different unit systems (temperature, distance); shows pattern of small, focused utilities by same maintainersindresorhus/parse-bytes— Inverse operation: parses human-readable strings like '1.34 kB' back into byte counts, complementing pretty-bytes for two-way conversion
PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive test coverage for edge cases in index.test-d.ts
The TypeScript definition file (index.d.ts) exports type definitions for prettyBytes with various options (bits, signed, fixedWidth, locale), but the TypeScript tests in index.test-d.ts likely lack comprehensive type assertion tests. This ensures the type definitions match the actual implementation and catch type-related regressions early.
- [ ] Review index.d.ts to identify all exported types and option combinations
- [ ] Add type assertion tests in index.test-d.ts for bigint parameter support
- [ ] Add type assertion tests for all options combinations (bits + signed, bits + locale, fixedWidth with other options)
- [ ] Add type assertion tests to ensure invalid option combinations are caught by TypeScript
- [ ] Run 'npm run test' to verify all type tests pass with tsd
Add unit tests for locale-specific formatting in test.js
The README demonstrates locale support (e.g., German with 'de' locale producing '1,34 kB'), but test.js likely lacks comprehensive locale-specific test cases. This is critical to ensure internationalization doesn't regress and handles edge cases across different locales.
- [ ] Review test.js to identify existing locale tests
- [ ] Add test cases for multiple locales (at least 'de', 'fr', 'es') with number/decimal separators
- [ ] Add tests for locale-specific decimal formatting (comma vs period)
- [ ] Add edge case tests: very large numbers with locale, negative numbers with locale
- [ ] Run 'npm test' to ensure all tests pass
Add test cases for signed option combinations in test.js
The README shows the signed option produces '+42 B' format, but test.js may lack comprehensive coverage of signed format with other options (like bits, locale, fixedWidth). This ensures the feature behaves correctly in all scenarios.
- [ ] Review test.js for existing signed option test coverage
- [ ] Add tests for signed + bits combination (e.g., '+1.34 kbit')
- [ ] Add tests for signed + fixedWidth combination with positive and negative values
- [ ] Add tests for signed + locale combination across multiple locales
- [ ] Add edge case: signed option with zero value (should it be '+0 B' or '0 B'?)
- [ ] Run 'npm test' to verify all tests pass
Good first issues
- Add tests for edge case combinations (e.g., simultaneous use of signed: true, binary: true, and locale: 'de') in test.js to improve coverage
- Create a simple CLI wrapper that reads file paths and outputs pretty-bytes output (currently only exported as library), useful for shell scripting and could live as an example
- Add minimumFractionDigits and maximumFractionDigits tests to index.test-d.ts to verify TypeScript correctly validates these new options with various numeric inputs
Top contributors
- @sindresorhus — 64 commits
- @nmoinvaz — 3 commits
- @NMinhNguyen — 2 commits
- @Turbo87 — 2 commits
- @kevva — 2 commits
Recent commits
b7849f7— 7.1.0 (sindresorhus)73df489— AddfixedWidthoption for right-aligned output (sindresorhus)b64cee5— Fix truncation behavior with fraction digits options (sindresorhus)b637640— AddnonBreakingSpaceoption (sindresorhus)3c77fd1— 7.0.1 (sindresorhus)c9fd951— Fix precision with thebinaryoption (#88) (siddhant-nair)4186190— Fix CI (sindresorhus)2658a8e— 7.0.0 (sindresorhus)13d3727— Require Node.js 20 (sindresorhus)386b35a— Add support forBigInt(#85) (kikar)
Security observations
This is a lightweight utility package with excellent security posture. No critical or high-severity vulnerabilities detected. The codebase has minimal dependencies (only dev dependencies for linting/testing), reducing attack surface. No hardcoded secrets, injection risks, or infrastructure misconfigurations identified. The package follows secure defaults with 'sideEffects: false' and proper module exports. The single low-severity issue relates to Node.js version specification flexibility rather than actual security flaws. The package appears well-maintained by a trusted author (Sindre Sorhus) with proper testing and linting in place.
- Low · Permissive Node.js Engine Requirement —
package.json - engines field. The package specifies 'node': '>=20' which allows any Node.js version 20 or higher. While this is reasonable for a small utility, it means users on unpatched or vulnerable Node.js versions could use this package without awareness. Fix: Consider specifying a more recent minimum version (e.g., '>=20.10.0') to encourage users to run patched Node.js versions. Document the minimum required version in README.
LLM-derived; treat as a starting point, not a security audit.
Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.