From 0ef6e7f0e080bb41774590d75b54d8146b7f3112 Mon Sep 17 00:00:00 2001 From: Farkhanda-Dalal <7farkhanda@gmail.com> Date: Thu, 16 Oct 2025 18:46:19 +0530 Subject: [PATCH] Added File Size Formatting --- README.md | 27 +++++++++++++++++ src/formatting/fileSize.ts | 42 +++++++++++++++++++++++++++ src/formatting/index.ts | 5 +++- src/tests/formatting/fileSize.test.ts | 41 ++++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/formatting/fileSize.ts create mode 100644 src/tests/formatting/fileSize.test.ts diff --git a/README.md b/README.md index 608bf13..6249ff1 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ const count = stringzy.analyze.wordCount('Hello world'); // 2 - [trim](#trim) - Removes unnecessary whitespace from a string. - [formatRomanNumeral](#formatromannumeral) - Converts a positive integer into its Roman numeral representation. - [formatPercentage](#formatpercentage) - Converts a number into a percentage string with configurable decimal precision. +- [formatFileSize](#formatfilesize) - Converts a number of bytes into a human-readable file size string (B, KB, MB, GB, TB). ## 📋 API Reference @@ -1352,6 +1353,32 @@ formatPercentage(0.5, -1); // TypeError | num | number | required | The number to convert into a percentage string. | | precision | number | 2 | The number of decimal places to include in the output. | +#### formatFileSize(bytes, precision) +Converts a number of bytes into a human-readable file size string (B, KB, MB, GB, TB).
+Automatically scales the unit based on the size and supports configurable decimal precision.
+Throws an error for invalid, non-numeric, or negative inputs.
+ +```javascript +import { formatFileSize } from 'stringzy'; + +formatFileSize(123); // "123 B" +formatFileSize(1024); // "1 KB" +formatFileSize(1048576); // "1 MB" +formatFileSize(1073741824); // "1 GB" +formatFileSize(1572864); // "1.5 MB" +formatFileSize(1500, 3); // "1.465 KB" + +// Invalid cases +formatFileSize(-1024); // RangeError +formatFileSize('1024'); // TypeError +formatFileSize(1024, -1); // TypeError +``` + +| Parameter | Type | Default | Description | +| --------- | ------ | -------- | --------------------------------------------------------------- | +| bytes | number | required | The number of bytes to convert into a human-readable file size. | +| precision | number | 2 | The number of decimal places for fractional sizes. | + ## 🔧 Usage Patterns ### Individual Function Imports diff --git a/src/formatting/fileSize.ts b/src/formatting/fileSize.ts new file mode 100644 index 0000000..8a690a1 --- /dev/null +++ b/src/formatting/fileSize.ts @@ -0,0 +1,42 @@ +/** + * Converts a number of bytes into a human-readable file size string (B, KB, MB, GB, TB). + * + * Supports values from bytes up to terabytes, with automatic unit scaling and correct rounding. + * + * Examples: + * 123 → "123 B" + * 1024 → "1 KB" + * 1048576 → "1 MB" + * 1073741824 → "1 GB" + * 1572864 → "1.5 MB" + * + * @param {number} bytes - The number of bytes to convert. + * @param {number} [precision=2] - The number of decimal places to include for non-integer conversions. + * @returns {string} The formatted file size string with units. + * @throws {TypeError} If the input is not a number or precision is not a valid number. + */ +export function formatFileSize(bytes: number, precision: number = 2): string { + if (typeof bytes !== 'number' || Number.isNaN(bytes)) { + throw new TypeError('Input must be a number'); + } + + if (typeof precision !== 'number' || Number.isNaN(precision) || precision < 0) { + throw new TypeError('Precision must be a non-negative number'); + } + + if (bytes < 0) { + throw new RangeError('File size cannot be negative'); + } + + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + if (bytes === 0) return `0 B`; + + const i = Math.floor(Math.log(bytes) / Math.log(1024)); + const value = bytes / Math.pow(1024, i); + + // ✅ Fix: trim trailing zeros by converting to number before string + const rounded = + value % 1 === 0 ? value.toString() : parseFloat(value.toFixed(precision)).toString(); + + return `${rounded} ${units[i]}`; +} diff --git a/src/formatting/index.ts b/src/formatting/index.ts index 9d8bd32..7dbc60d 100644 --- a/src/formatting/index.ts +++ b/src/formatting/index.ts @@ -5,6 +5,7 @@ export { formatDuration } from './duration'; export { trim } from './trim'; export { formatRomanNumeral } from './romanNumerals'; export { formatPercentage } from './percentage'; +export { formatFileSize } from './fileSize'; import { capitalize } from './capitalize'; import { formatNumber } from './number'; @@ -13,6 +14,7 @@ import { formatDuration } from './duration'; import { trim } from './trim'; import { formatRomanNumeral } from './romanNumerals'; import { formatPercentage } from './percentage'; +import { formatFileSize } from './fileSize'; export const formatting = { capitalize, @@ -21,5 +23,6 @@ export const formatting = { formatDuration, trim, formatRomanNumeral, - formatPercentage + formatPercentage, + formatFileSize }; diff --git a/src/tests/formatting/fileSize.test.ts b/src/tests/formatting/fileSize.test.ts new file mode 100644 index 0000000..112411a --- /dev/null +++ b/src/tests/formatting/fileSize.test.ts @@ -0,0 +1,41 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert'; +import { formatFileSize } from '../../formatting/fileSize'; + +describe('formatFileSize', () => { + it('formats basic sizes correctly', () => { + assert.strictEqual(formatFileSize(123), '123 B'); + assert.strictEqual(formatFileSize(1024), '1 KB'); + assert.strictEqual(formatFileSize(1048576), '1 MB'); + assert.strictEqual(formatFileSize(1073741824), '1 GB'); + }); + + it('handles fractional sizes with correct rounding', () => { + assert.strictEqual(formatFileSize(1572864), '1.5 MB'); // 1.5 MB + assert.strictEqual(formatFileSize(1536, 1), '1.5 KB'); // 1.5 KB + }); + + it('handles precision configuration', () => { + assert.strictEqual(formatFileSize(1500, 0), '1 KB'); // rounds down + assert.strictEqual(formatFileSize(1500, 3), '1.465 KB'); + }); + + it('handles zero correctly', () => { + assert.strictEqual(formatFileSize(0), '0 B'); + }); + + it('throws TypeError for invalid inputs', () => { + assert.throws(() => formatFileSize('1024' as any), /Input must be a number/); + assert.throws(() => formatFileSize(null as any), /Input must be a number/); + assert.throws(() => formatFileSize(undefined as any), /Input must be a number/); + }); + + it('throws TypeError for invalid precision', () => { + assert.throws(() => formatFileSize(1024, -1), /Precision must be a non-negative number/); + assert.throws(() => formatFileSize(1024, '2' as any), /Precision must be a non-negative number/); + }); + + it('throws RangeError for negative byte values', () => { + assert.throws(() => formatFileSize(-1024), /File size cannot be negative/); + }); +});