Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,11 @@ These changes improve throughput and reduce memory pressure when working with la
- [formatFileSize](#formatfilesize) - Converts a number of bytes into a human-readable file size string (B, KB, MB, GB, TB).
- [formatOrdinal](#formatordinal) - Converts a number into its ordinal string representation (e.g., 1 → "1st", 2 → "2nd").
- [formatList](#formatlist) - Formats an array of strings into a human-readable list with proper commas and "and".
- [formatCreditCard](#formatcreditcard) - Formats a credit card number by grouping digits into readable parts.
- [formatToOctal](#formattotoctal) - Converts a decimal number to octal, optional "0o" prefix.
- [formatTemperature](#formattemperature) - Converts temperatures between Celsius, Fahrenheit, and Kelvin.
- [formatToBinary](#formattobinary) - Converts a decimal integer to a binary string with optional bit grouping.
- [formatToHexadecimal](#formattohexadecimal) - Converts temperatures between Celsius, Fahrenheit, and Kelvin.

- [formatToDecimal](#formattodecimal) - Converts base-2/8/16 strings to decimal.

## 📋 API Reference
Expand Down Expand Up @@ -1486,6 +1486,27 @@ formatList(['apple', 123]); // TypeError
| --------- | -------- | -------- | ----------------------------------------- |
| arr | string[] | required | The array of strings to format as a list. |

#### <a id="formatcreditcard"></a>formatCreditCard(cardNumber)
Formats a credit card number into readable groups of digits separated by spaces.
Supports 15-digit (AmEx) and 16-digit (Visa/MasterCard) numbers.
Non-digit characters are automatically stripped before formatting.
Throws an error if the input is not a string.

```javascript
import { formatCreditCard } from 'stringzy';

formatCreditCard('1234567812345678'); // "1234 5678 1234 5678"
formatCreditCard('4111111111111111'); // "4111 1111 1111 1111"
formatCreditCard('378282246310005'); // "3782 822463 10005" (AmEx)
formatCreditCard('4111-1111-1111-1111'); // "4111 1111 1111 1111"
formatCreditCard('123'); // "" (invalid length)
formatCreditCard(''); // "" (empty string)
```

| Parameter | Type | Default | Description |
| ---------- | ------ | -------- | ------------------------------------------------------------------- |
| cardNumber | string | required | The credit card number to format. Cannot include non-digit characters. |

#### <a id="formattotoctal"></a>formatToOctal(num, options?)

Converts a decimal number to its octal (base-8) string representation. Supports negatives and an optional `0o` prefix.
Expand Down
29 changes: 29 additions & 0 deletions src/formatting/creditCard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Formats a credit card number by grouping digits into readable parts.
*
* @param {string} cardNumber - The credit card number to format.
* @returns {string} The formatted credit card number.
* @throws {TypeError} If the input is not a string.
*/
export function formatCreditCard(cardNumber: string): string {
if (typeof cardNumber !== 'string') {
throw new TypeError('Input must be a string');
}

// Remove all non-digit characters
const cleaned = cardNumber.replace(/\D/g, '');

// Only accept 15 or 16 digit card numbers
if (cleaned.length !== 15 && cleaned.length !== 16) {
return '';
}

// Format based on length:
// 16 digits → 4-4-4-4 (Visa, MasterCard)
if (cleaned.length === 16) {
return cleaned.replace(/(\d{4})(?=\d)/g, '$1 ').trim();
}

// 15 digits → 4-6-5 (AmEx)
return cleaned.replace(/(\d{4})(\d{6})(\d{5})/, '$1 $2 $3');
}
3 changes: 3 additions & 0 deletions src/formatting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { formatPercentage } from './percentage';
export { formatFileSize } from './fileSize';
export { formatOrdinal } from './ordinal';
export { formatList } from './listToString';
export { formatCreditCard } from './creditCard';
export { formatToOctal } from './octal';
export { formatTemperature } from './temperature';
export { formatToBinary } from './binary';
Expand All @@ -24,6 +25,7 @@ import { formatPercentage } from './percentage';
import { formatFileSize } from './fileSize';
import { formatOrdinal } from './ordinal';
import { formatList } from './listToString';
import { formatCreditCard } from './creditCard';
import { formatToOctal } from './octal';
import { formatTemperature } from './temperature';
import { formatToBinary } from './binary';
Expand All @@ -41,6 +43,7 @@ export const formatting = {
formatFileSize,
formatOrdinal,
formatList,
formatCreditCard,
formatTemperature,
formatToBinary,
formatToHexadecimal,
Expand Down
31 changes: 31 additions & 0 deletions src/tests/formatting/creditCard.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, it } from 'node:test';
import assert from 'node:assert';
import { formatCreditCard } from '../../formatting/creditCard';

describe('formatCreditCard', () => {
it('formats 16-digit card numbers correctly', () => {
assert.strictEqual(formatCreditCard('1234567812345678'), '1234 5678 1234 5678');
assert.strictEqual(formatCreditCard('4111111111111111'), '4111 1111 1111 1111');
});

it('formats 15-digit card numbers (AmEx) correctly', () => {
assert.strictEqual(formatCreditCard('378282246310005'), '3782 822463 10005');
});

it('removes non-digit characters before formatting', () => {
assert.strictEqual(formatCreditCard('4111-1111-1111-1111'), '4111 1111 1111 1111');
assert.strictEqual(formatCreditCard('3782 8224 6310 005'), '3782 822463 10005');
});

it('returns empty string for invalid lengths', () => {
assert.strictEqual(formatCreditCard('123'), '');
assert.strictEqual(formatCreditCard('11112222333344445555'), '');
assert.strictEqual(formatCreditCard(''), '');
});

it('throws an error if input is not a string', () => {
assert.throws(() => formatCreditCard(1234567812345678 as any), /Input must be a string/);
assert.throws(() => formatCreditCard(null as any), /Input must be a string/);
assert.throws(() => formatCreditCard(undefined as any), /Input must be a string/);
});
});