next-formatter 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/core.mjs DELETED
@@ -1,196 +0,0 @@
1
- // src/core.ts
2
- var DEFAULT_RULES = {
3
- compactThreshold: 1e4,
4
- minimumFractionDigits: 0,
5
- maximumFractionDigits: 2,
6
- currencyDisplay: "narrowSymbol",
7
- numberFormat: {},
8
- dateFormat: { year: "numeric", month: "short", day: "2-digit" },
9
- dateTimeFormat: {
10
- year: "numeric",
11
- month: "short",
12
- day: "2-digit",
13
- hour: "2-digit",
14
- minute: "2-digit",
15
- hour12: true
16
- }
17
- };
18
- function createFormatters(config = {}) {
19
- const locale = config.locale ?? "en-US";
20
- const defaultCurrency = config.currency ?? "USD";
21
- const fallback = config.fallback ?? "\u2014";
22
- const rules = {
23
- ...DEFAULT_RULES,
24
- ...config.rules
25
- };
26
- function toNumber(value) {
27
- if (value == null || value === "") return null;
28
- const n = typeof value === "number" ? value : Number(value);
29
- return Number.isNaN(n) ? null : n;
30
- }
31
- function toValidDate(value) {
32
- if (value == null || value === "") return null;
33
- const date = value instanceof Date ? value : new Date(value);
34
- return Number.isNaN(date.getTime()) ? null : date;
35
- }
36
- function isLarge(n) {
37
- return Math.abs(n) >= rules.compactThreshold;
38
- }
39
- function normalizeICU(str) {
40
- return str.replace(/\u202f/g, " ").replace(/\u00a0/g, " ").trim();
41
- }
42
- function alignFractionDigits(opts) {
43
- const { minimumFractionDigits, maximumFractionDigits, maximumSignificantDigits, minimumSignificantDigits } = opts;
44
- if (minimumFractionDigits !== void 0 && maximumFractionDigits !== void 0 && minimumFractionDigits !== maximumFractionDigits && maximumSignificantDigits === void 0 && minimumSignificantDigits === void 0) {
45
- return { ...opts, minimumFractionDigits: maximumFractionDigits };
46
- }
47
- return opts;
48
- }
49
- const numberCache = /* @__PURE__ */ new Map();
50
- function getNumberFormatter(options) {
51
- const key = JSON.stringify({ locale, ...options });
52
- if (!numberCache.has(key)) {
53
- numberCache.set(key, new Intl.NumberFormat(locale, options));
54
- }
55
- return numberCache.get(key);
56
- }
57
- const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto", style: "long" });
58
- const formatters = {
59
- /**
60
- * Format a plain number.
61
- * @example
62
- * fmt.number(1234567) // "1.2M"
63
- * fmt.number(1234, { notation: "standard" }) // "1,234"
64
- * fmt.number(1.5678, { maximumFractionDigits: 1 }) // "1.6"
65
- */
66
- number(value, options = {}) {
67
- const n = toNumber(value);
68
- if (n == null) return fallback;
69
- return normalizeICU(
70
- getNumberFormatter(
71
- alignFractionDigits({
72
- style: "decimal",
73
- notation: isLarge(n) ? "compact" : "standard",
74
- minimumFractionDigits: rules.minimumFractionDigits,
75
- maximumFractionDigits: rules.maximumFractionDigits,
76
- ...rules.numberFormat,
77
- ...options
78
- })
79
- ).format(n)
80
- );
81
- },
82
- /**
83
- * Format a currency value.
84
- * @example
85
- * fmt.currency(49900) // "$49.9K"
86
- * fmt.currency(1234, { currency: "EUR" }) // "€1,234"
87
- * fmt.currency(1234, { currencyDisplay: "code" }) // "USD 1,234"
88
- * fmt.currency(1234, { minimumFractionDigits: 2 }) // "$1,234.00"
89
- */
90
- currency(value, options = {}) {
91
- const n = toNumber(value);
92
- if (n == null) return fallback;
93
- const { currency: overrideCurrency, currencyDisplay, ...rest } = options;
94
- return normalizeICU(
95
- getNumberFormatter(
96
- alignFractionDigits({
97
- style: "currency",
98
- currency: overrideCurrency ?? defaultCurrency,
99
- notation: isLarge(n) ? "compact" : "standard",
100
- minimumFractionDigits: rules.minimumFractionDigits,
101
- maximumFractionDigits: rules.maximumFractionDigits,
102
- currencyDisplay: currencyDisplay ?? rules.currencyDisplay,
103
- ...rules.numberFormat,
104
- ...rest
105
- })
106
- ).format(n)
107
- );
108
- },
109
- /**
110
- * Format a percentage. Input is treated as a raw percentage (50 → "50%").
111
- * Uses minimumFractionDigits and maximumFractionDigits from rules.
112
- * For values that would round to zero, maximumSignificantDigits is set to
113
- * max(maximumFractionDigits, 4) to preserve meaningful precision.
114
- * @example
115
- * fmt.percentage(12.5) // "12.50%" (with default rules min=0, max=2 → aligned to 2)
116
- * fmt.percentage(0.001) // "0.001%" (wouldBeZero → uses significantDigits)
117
- * fmt.percentage(12.5, { maximumFractionDigits: 0 }) // "13%"
118
- */
119
- percentage(value, options = {}) {
120
- const n = toNumber(value);
121
- if (n == null) return fallback;
122
- const normalized = n / 100;
123
- const wouldBeZero = parseFloat(normalized.toFixed(3)) === 0;
124
- const sigDigits = Math.max(rules.maximumFractionDigits, 4);
125
- return getNumberFormatter({
126
- style: "percent",
127
- ...wouldBeZero ? { maximumSignificantDigits: sigDigits } : alignFractionDigits({
128
- minimumFractionDigits: rules.minimumFractionDigits,
129
- maximumFractionDigits: rules.maximumFractionDigits
130
- }),
131
- ...options
132
- }).format(normalized);
133
- },
134
- /**
135
- * Format a duration in seconds. Pure math — no Intl, no ICU risk.
136
- * @example
137
- * fmt.duration(150) // "2m 30s"
138
- * fmt.duration(45) // "45s"
139
- */
140
- duration(value) {
141
- const n = toNumber(value);
142
- if (n == null) return fallback;
143
- const mins = Math.floor(n / 60);
144
- const secs = n % 60;
145
- return mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
146
- },
147
- /**
148
- * Format a date.
149
- * @example
150
- * fmt.date("2024-01-15") // "Jan 15, 2024"
151
- * fmt.date("2024-01-15", { dateStyle: "full" }) // "Monday, January 15, 2024"
152
- */
153
- date(value, options = {}) {
154
- const date = toValidDate(value);
155
- if (!date) return fallback;
156
- const resolvedOptions = options.dateStyle ? options : { ...rules.dateFormat, ...options };
157
- return normalizeICU(new Intl.DateTimeFormat(locale, resolvedOptions).format(date));
158
- },
159
- dateTime(value, options = {}) {
160
- const date = toValidDate(value);
161
- if (!date) return fallback;
162
- const resolvedOptions = options.dateStyle || options.timeStyle ? options : { ...rules.dateTimeFormat, ...options };
163
- return normalizeICU(new Intl.DateTimeFormat(locale, resolvedOptions).format(date));
164
- },
165
- /**
166
- * Format a date as relative time.
167
- * Always pass an explicit `now` timestamp — this ensures consistent
168
- * output whether called from a server or client component.
169
- * @example
170
- * const now = Date.now();
171
- * fmt.relativeTime("2024-01-15", now) // "3 months ago"
172
- */
173
- relativeTime(value, now = Date.now()) {
174
- const date = toValidDate(value);
175
- if (!date) return fallback;
176
- const diffSec = Math.round((date.getTime() - now) / 1e3);
177
- const abs = Math.abs(diffSec);
178
- if (abs < 60) return rtf.format(diffSec, "second");
179
- const min = Math.round(diffSec / 60);
180
- if (Math.abs(min) < 60) return rtf.format(min, "minute");
181
- const hr = Math.round(min / 60);
182
- if (Math.abs(hr) < 24) return rtf.format(hr, "hour");
183
- const day = Math.round(hr / 24);
184
- if (Math.abs(day) < 7) return rtf.format(day, "day");
185
- const week = Math.round(day / 7);
186
- if (Math.abs(week) < 4) return rtf.format(week, "week");
187
- const month = Math.round(day / 30);
188
- if (Math.abs(month) < 12) return rtf.format(month, "month");
189
- const year = Math.round(day / 365);
190
- return rtf.format(year, "year");
191
- }
192
- };
193
- return formatters;
194
- }
195
-
196
- export { createFormatters };
@@ -1,24 +0,0 @@
1
- // src/create.ts
2
- async function resolveLocale(resolver, defaultLocale) {
3
- const resolved = await resolver?.();
4
- if (resolved) return resolved;
5
- if (defaultLocale) return defaultLocale;
6
- try {
7
- const { headers } = await import('next/headers');
8
- const h = await headers();
9
- const acceptLanguage = h.get("accept-language");
10
- if (acceptLanguage) {
11
- const locale = acceptLanguage.split(",")[0]?.trim().split(";")[0]?.trim();
12
- if (locale) return locale;
13
- }
14
- } catch {
15
- }
16
- return "en-US";
17
- }
18
- async function resolveCurrency(resolver, defaultCurrency) {
19
- const resolved = await resolver?.();
20
- if (resolved) return resolved;
21
- return defaultCurrency ?? "USD";
22
- }
23
-
24
- export { resolveCurrency, resolveLocale };
File without changes