Display-layer formatters with a shared "missing input → em-dash" convention across admin and customer UIs. Thin wrappers over @arraypress/date-utils, file-size, text, and gravatar — the value here is not the underlying formatting (those libs own that) but the agreed-on fallback behaviour so every list cell, stat tile, and CSV preview renders empties the same way.
Every admin app ends up with a lib/utils.ts containing versions of:
// 2-line wrappers, repeated across apps and admin/customer splits
function formatDate(d) { if (!d) return '—'; return dateTime(d); }
function formatSize(b) { if (!b) return '—'; return formatSize(b); }
function truncateId(id) { if (!id) return '—'; return truncate(id, 16, { position: 'middle' }); }Over time these drift — one app uses '—', another uses 'N/A', a third uses an empty string, and the same dashboard shows three conventions in three tabs. This module codifies the answer: import here, get the same output everywhere.
npm install @arraypress/formatPeer libs (@arraypress/date-utils, file-size, text, gravatar) are installed transitively.
import {
formatDate, formatSize, truncateId, getGravatarUrl,
formatPrice, formatUserAgent, formatCountry, formatMime,
} from '@arraypress/format';
formatDate('2026-04-15T10:00:00Z'); // '15 Apr 2026 10:00'
formatDate(null); // '—'
formatDate(null, 'Never'); // 'Never'
formatSize(4_700_000); // '4.7 MB'
formatSize(0); // '—' (treats 0 as missing)
truncateId('paypal-capture-ABCDEFG12345XYZ'); // 'paypal-capture-…XYZ'
getGravatarUrl('user@example.com'); // https://…/gravatar/…?s=40
getGravatarUrl('user@example.com', 80);
formatPrice(25); // '$25.00'
formatPrice(10.5, 'GBP', '—', 'en-GB'); // '£10.50'
formatPrice(0); // '$0.00' — 0 is a real price
formatPrice(null); // '—'
formatUserAgent('Mozilla/5.0 … Chrome/118 …'); // 'Chrome on Windows'
formatUserAgent(null); // '—'
formatCountry('US'); // 'United States'
formatCountry('XX'); // '—' (invalid code)
formatMime('application/pdf'); // 'Document'
formatMime('image/png'); // 'Image'
formatMime(null); // '—'Every formatter takes an optional fallback second argument — use it to override on a call-by-call basis (formatDate(lastLogin, 'Never')).
| Export | Purpose |
|---|---|
DASH |
The canonical placeholder constant — '—' (U+2014 em-dash). Import this rather than hard-coding '—' in app code. |
formatDate(iso, fallback?) |
Thin wrapper over @arraypress/date-utils's dateTime. null/undefined/'' → fallback. |
formatSize(bytes, fallback?) |
Thin wrapper over @arraypress/file-size's formatSize. null/undefined/0 → fallback (0 is treated as missing). |
truncateId(id, fallback?) |
Thin wrapper over @arraypress/text's truncate. Defaults to 16-char middle-position truncation — what transaction IDs / storage keys / hashes look good as in table cells. |
getGravatarUrl(email, size?) |
Signature-simplified wrapper over @arraypress/gravatar's getAvatarUrlSync. No fallback — Gravatar already serves a default silhouette. |
formatPrice(amount, currency?, fallback?, locale?) |
Format a numeric amount as localised currency via Intl.NumberFormat. Amount in major units (dollars, not cents). null/undefined/NaN → fallback; 0 formats normally (legitimate free price). |
formatUserAgent(ua, fallback?) |
Thin wrapper over @arraypress/user-agent's format. Collapses the underlying lib's "Unknown Browser on Unknown OS" output to the dash convention. |
formatCountry(code, fallback?) |
Thin wrapper over @arraypress/countries's getName. Validates via isValid so unknown codes don't echo back as "XX" — they collapse to the fallback instead. |
formatMime(mime, fallback?) |
Thin wrapper over @arraypress/mime-types's getLabel. Collapses the lib's "Unknown" placeholder to the dash. |
MIT