icu-minify 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Jan Amann
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # `icu-minify`
2
+
3
+ ICU message format compiler with a 650 bytes runtime ✨
4
+
5
+ ## Features
6
+
7
+ 1. **Build-time compilation** ⚡
8
+ Converts ICU messages to a compact JSON intermediate representation
9
+ 2. **Minimal runtime** 📦
10
+ 650 bytes (minified + compressed) with zero runtime dependencies
11
+ 3. **Full ICU support** 🌍
12
+ `{arguments}`, `plural`, `select`, `selectordinal`, `date`, `time`, `number` and `<tags>`
13
+
14
+ ## Usage
15
+
16
+ ```ts
17
+ import compile from 'icu-minify/compile';
18
+ import format from 'icu-minify/format';
19
+
20
+ // At build time
21
+ const compiled = compile('Hello {name}!');
22
+
23
+ // At runtime
24
+ format(compiled, 'en', {name: 'World'});
25
+ ```
26
+
27
+ ## Acknowledgments
28
+
29
+ This library is heavily inspired by [`icu-to-json`](https://github.com/jantimon/icu-to-json) and [`@lingui/message-utils`](https://github.com/lingui/js-lingui/tree/main/packages/message-utils), which similarly use an array-based intermediate representation for compiled messages.
30
+
31
+ ## Design
32
+
33
+ For detailed design rationale, motivation, tradeoffs, and implementation details, see [RFC: Ahead-of-time compilation of ICU messages](../../rfcs/002-icu-message-precompilation.md).
package/compile.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Needed for projects with `moduleResolution: 'node'`
2
+ export {default} from './dist/types/compile.d.ts';
@@ -0,0 +1,149 @@
1
+ import { parse, TYPE } from '@formatjs/icu-messageformat-parser';
2
+ import { T as TYPE_POUND, a as TYPE_NUMBER, b as TYPE_DATE, c as TYPE_TIME, d as TYPE_SELECT, e as TYPE_SELECTORDINAL, f as TYPE_PLURAL } from './types-Dl2aHte_.js';
3
+
4
+ function compile(message) {
5
+ const ast = parse(message);
6
+ const compiled = compileNodes(ast);
7
+ if (compiled.length === 0) {
8
+ return '';
9
+ }
10
+ if (compiled.length === 1 && typeof compiled[0] === 'string') {
11
+ return compiled[0];
12
+ }
13
+ return compiled;
14
+ }
15
+ function compileNodes(nodes) {
16
+ const result = [];
17
+ for (const node of nodes) {
18
+ const compiled = compileNode(node);
19
+ if (typeof compiled === 'string' && result.length > 0 && typeof result[result.length - 1] === 'string') {
20
+ result[result.length - 1] += compiled;
21
+ } else {
22
+ result.push(compiled);
23
+ }
24
+ }
25
+ return result;
26
+ }
27
+ function compileNodesToNode(nodes) {
28
+ const compiled = compileNodes(nodes);
29
+ if (compiled.length === 0) {
30
+ return '';
31
+ }
32
+ if (compiled.length === 1) {
33
+ const node = compiled[0];
34
+ // Only unwrap strings and pound signs, not array-based nodes (tags, typed nodes)
35
+ // This preserves structure for formatBranch to correctly identify single nodes vs arrays
36
+ if (typeof node === 'string' || node === TYPE_POUND) {
37
+ return node;
38
+ }
39
+ }
40
+ return compiled;
41
+ }
42
+ function compileNode(node) {
43
+ switch (node.type) {
44
+ case TYPE.literal:
45
+ return node.value;
46
+ case TYPE.argument:
47
+ return [node.value];
48
+ case TYPE.number:
49
+ return compileNumber(node);
50
+ case TYPE.date:
51
+ return compileDate(node);
52
+ case TYPE.time:
53
+ return compileTime(node);
54
+ case TYPE.select:
55
+ return compileSelect(node);
56
+ case TYPE.plural:
57
+ return compilePlural(node);
58
+ case TYPE.pound:
59
+ return TYPE_POUND;
60
+ case TYPE.tag:
61
+ return compileTag(node);
62
+ default:
63
+ throw new Error(`Unknown AST node type: ${node.type}`);
64
+ }
65
+ }
66
+ function compileNumber(node) {
67
+ const result = [node.value, TYPE_NUMBER];
68
+ const style = compileNumberStyle(node.style);
69
+ if (style !== undefined) {
70
+ result.push(style);
71
+ }
72
+ return result;
73
+ }
74
+ function compileNumberStyle(style) {
75
+ if (!style) {
76
+ return undefined;
77
+ }
78
+ if (typeof style === 'string') {
79
+ return style;
80
+ }
81
+ if ('parsedOptions' in style) {
82
+ const opts = style.parsedOptions;
83
+ return Object.keys(opts).length > 0 ? opts : undefined;
84
+ }
85
+ return undefined;
86
+ }
87
+ function compileDate(node) {
88
+ const result = [node.value, TYPE_DATE];
89
+ const style = compileDateTimeStyle(node.style);
90
+ if (style !== undefined) {
91
+ result.push(style);
92
+ }
93
+ return result;
94
+ }
95
+ function compileTime(node) {
96
+ const result = [node.value, TYPE_TIME];
97
+ const style = compileDateTimeStyle(node.style);
98
+ if (style !== undefined) {
99
+ result.push(style);
100
+ }
101
+ return result;
102
+ }
103
+ function compileDateTimeStyle(style) {
104
+ if (!style) {
105
+ return undefined;
106
+ }
107
+ if (typeof style === 'string') {
108
+ return style;
109
+ }
110
+ if ('parsedOptions' in style) {
111
+ const opts = style.parsedOptions;
112
+ return Object.keys(opts).length > 0 ? opts : undefined;
113
+ }
114
+ return undefined;
115
+ }
116
+ function compileSelect(node) {
117
+ const options = {};
118
+ for (const [key, option] of Object.entries(node.options)) {
119
+ options[key] = compileNodesToNode(option.value);
120
+ }
121
+ return [node.value, TYPE_SELECT, options];
122
+ }
123
+
124
+ // Plural offset is not supported
125
+ function compilePlural(node) {
126
+ if (node.offset) {
127
+ throw new Error('Plural offsets are not supported');
128
+ }
129
+ const options = {};
130
+ for (const [key, option] of Object.entries(node.options)) {
131
+ options[key] = compileNodesToNode(option.value);
132
+ }
133
+ return [node.value, node.pluralType === 'ordinal' ? TYPE_SELECTORDINAL : TYPE_PLURAL, options];
134
+ }
135
+ function compileTag(node) {
136
+ const children = compileNodes(node.children);
137
+ const result = [node.value];
138
+
139
+ // Tags have no type number - detected at runtime by typeof node[1] !== 'number'
140
+ // Empty tags get an empty string child to distinguish from simple arguments
141
+ if (children.length === 0) {
142
+ result.push('');
143
+ } else {
144
+ result.push(...children);
145
+ }
146
+ return result;
147
+ }
148
+
149
+ export { compile as default };
@@ -0,0 +1,212 @@
1
+ import { T as TYPE_POUND, c as TYPE_TIME, b as TYPE_DATE, a as TYPE_NUMBER, e as TYPE_SELECTORDINAL, f as TYPE_PLURAL, d as TYPE_SELECT } from './types-Dl2aHte_.js';
2
+
3
+ // Re-export CompiledMessage type for consumers
4
+
5
+ // Could potentially share this with `use-intl` if we had a shared package for both
6
+
7
+ function format(message, locale, values = {}, options) {
8
+ if (typeof message === 'string') {
9
+ return message;
10
+ }
11
+ const result = formatNodes(message, locale, values, options, undefined);
12
+ return optimizeResult(result);
13
+ }
14
+ function formatNodes(nodes, locale, values, options, pluralCtx) {
15
+ const result = [];
16
+ for (const node of nodes) {
17
+ const formatted = formatNode(node, locale, values, options, pluralCtx);
18
+ if (Array.isArray(formatted)) {
19
+ result.push(...formatted);
20
+ } else {
21
+ result.push(formatted);
22
+ }
23
+ }
24
+ return result;
25
+ }
26
+ function formatNode(node, locale, values, options, rawPluralCtx) {
27
+ if (typeof node === 'string') {
28
+ return node;
29
+ }
30
+ if (node === TYPE_POUND) {
31
+ if (!rawPluralCtx) {
32
+ throw new Error('# used outside of plural context');
33
+ }
34
+ const pluralCtx = rawPluralCtx;
35
+ return options.formatters.getNumberFormat(pluralCtx.locale).format(pluralCtx.value);
36
+ }
37
+ const [name, type, ...rest] = node;
38
+
39
+ // Simple argument: ["name"]
40
+ if (type === undefined) {
41
+ const value = getValue(values, name);
42
+ {
43
+ if (typeof value === 'boolean') {
44
+ throw new Error(`Invalid value for argument "${name}": Boolean values are not supported and should be converted to strings if needed.`);
45
+ }
46
+ if (value instanceof Date) {
47
+ throw new Error(`Invalid value for argument "${name}": Date values are not supported for plain parameters. Use date formatting instead (e.g. {${name}, date}).`);
48
+ }
49
+ }
50
+ return String(value);
51
+ }
52
+
53
+ // Tag: ["tagName", child1, child2, ...] - detected by non-number or pound marker
54
+ if (typeof type !== 'number' || type === TYPE_POUND) {
55
+ return formatTag(name, [type, ...rest], locale, values, options, rawPluralCtx);
56
+ }
57
+
58
+ // Typed nodes: ["name", TYPE, ...]
59
+ switch (type) {
60
+ case TYPE_SELECT:
61
+ return formatSelect(name, rest[0], locale, values, options, rawPluralCtx);
62
+ case TYPE_PLURAL:
63
+ return formatPlural(name, rest[0], locale, values, options, 'cardinal');
64
+ case TYPE_SELECTORDINAL:
65
+ return formatPlural(name, rest[0], locale, values, options, 'ordinal');
66
+ case TYPE_NUMBER:
67
+ return formatNumberValue(name, rest[0], locale, values, options);
68
+ case TYPE_DATE:
69
+ return formatDateTimeValue(name, rest[0], locale, values, options, 'date');
70
+ case TYPE_TIME:
71
+ return formatDateTimeValue(name, rest[0], locale, values, options, 'time');
72
+ default:
73
+ {
74
+ throw new Error(`Unknown compiled node type: ${type}`);
75
+ }
76
+ }
77
+ }
78
+ function getValue(values, name) {
79
+ if (!(name in values)) {
80
+ throw new Error(`Missing value for argument "${name}"`);
81
+ }
82
+ return values[name];
83
+ }
84
+ function formatSelect(name, options, locale, values, formatOptions, pluralCtx) {
85
+ const value = String(getValue(values, name));
86
+ const branch = options[value] ?? options.other;
87
+ if (!branch) {
88
+ throw new Error(`No matching branch for select "${name}" with value "${value}"`);
89
+ }
90
+ return formatBranch(branch, locale, values, formatOptions, pluralCtx);
91
+ }
92
+ function formatPlural(name, options, locale, values, formatOptions, pluralType) {
93
+ const rawValue = getValue(values, name);
94
+ if (typeof rawValue !== 'number') {
95
+ throw new Error(`Expected number for plural argument "${name}", got ${typeof rawValue}`);
96
+ }
97
+ const value = rawValue;
98
+ const exactKey = `=${value}`;
99
+ if (exactKey in options) {
100
+ return formatBranch(options[exactKey], locale, values, formatOptions, {
101
+ value,
102
+ locale
103
+ });
104
+ }
105
+ const category = formatOptions.formatters.getPluralRules(locale, {
106
+ type: pluralType
107
+ }).select(value);
108
+ const branch = options[category] ?? options.other;
109
+ if (!branch) {
110
+ throw new Error(`No matching branch for plural "${name}" with category "${category}"`);
111
+ }
112
+ return formatBranch(branch, locale, values, formatOptions, {
113
+ value,
114
+ locale
115
+ });
116
+ }
117
+ function formatBranch(branch, locale, values, formatOptions, pluralCtx) {
118
+ if (typeof branch === 'string') {
119
+ return branch;
120
+ }
121
+ if (branch === TYPE_POUND) {
122
+ return formatNode(branch, locale, values, formatOptions, pluralCtx);
123
+ }
124
+ // Branch is an array - either a single complex node wrapped in array, or multiple nodes
125
+ // formatNodes handles both correctly via formatNode's tag detection
126
+ return formatNodes(branch, locale, values, formatOptions, pluralCtx);
127
+ }
128
+ function formatNumberValue(name, style, locale, values, formatOptions) {
129
+ const rawValue = getValue(values, name);
130
+ if (typeof rawValue !== 'number') {
131
+ throw new Error(`Expected number for argument "${name}", got ${typeof rawValue}`);
132
+ }
133
+ const value = rawValue;
134
+ const opts = getNumberFormatOptions(style, formatOptions);
135
+ return formatOptions.formatters.getNumberFormat(locale, opts).format(value);
136
+ }
137
+ function formatDateTimeValue(name, style, locale, values, formatOptions, type) {
138
+ const rawValue = getValue(values, name);
139
+ if (!(rawValue instanceof Date)) {
140
+ throw new Error(`Expected Date for argument "${name}", got ${typeof rawValue}`);
141
+ }
142
+ const date = rawValue;
143
+ const baseOpts = getDateTimeFormatOptions(style, type, formatOptions);
144
+
145
+ // Global time zone is used as default, but format-specific one takes precedence
146
+ const opts = {
147
+ ...baseOpts,
148
+ timeZone: baseOpts?.timeZone ?? formatOptions.timeZone
149
+ };
150
+ return formatOptions.formatters.getDateTimeFormat(locale, opts).format(date);
151
+ }
152
+ function getNumberFormatOptions(style, formatOptions) {
153
+ if (!style) return undefined;
154
+ if (typeof style === 'string') {
155
+ if (formatOptions.formats?.number?.[style]) {
156
+ return formatOptions.formats.number[style];
157
+ }
158
+ {
159
+ throw new Error(`Missing number format "${style}"`);
160
+ }
161
+ }
162
+ return style;
163
+ }
164
+ function getDateTimeFormatOptions(style, type, formatOptions) {
165
+ if (!style) return undefined;
166
+ if (typeof style === 'string') {
167
+ const resolved = formatOptions.formats?.dateTime?.[style];
168
+ if (!resolved) {
169
+ throw new Error(`Missing ${type} format "${style}"`);
170
+ }
171
+ return resolved;
172
+ }
173
+ return style;
174
+ }
175
+ function formatTag(name, children, locale, values, formatOptions, pluralCtx) {
176
+ const rawHandler = getValue(values, name);
177
+ if (typeof rawHandler !== 'function') {
178
+ throw new Error(`Expected function for tag handler "${name}"`);
179
+ }
180
+ const handler = rawHandler;
181
+ const formattedChildren = formatNodes(children, locale, values, formatOptions, pluralCtx);
182
+ const optimized = optimizeResult(formattedChildren);
183
+ const childArray = Array.isArray(optimized) ? optimized : [optimized];
184
+ return handler(childArray);
185
+ }
186
+ function optimizeResult(result) {
187
+ if (result.length === 0) {
188
+ return '';
189
+ }
190
+ const merged = [];
191
+ let currentString = '';
192
+ for (const item of result) {
193
+ if (typeof item === 'string') {
194
+ currentString += item;
195
+ } else {
196
+ if (currentString) {
197
+ merged.push(currentString);
198
+ currentString = '';
199
+ }
200
+ merged.push(item);
201
+ }
202
+ }
203
+ if (currentString) {
204
+ merged.push(currentString);
205
+ }
206
+ if (merged.length === 1) {
207
+ return merged[0];
208
+ }
209
+ return merged;
210
+ }
211
+
212
+ export { format as default };
@@ -0,0 +1,30 @@
1
+ const TYPE_POUND = 0;
2
+ const TYPE_SELECT = 1;
3
+ const TYPE_PLURAL = 2;
4
+ const TYPE_SELECTORDINAL = 3;
5
+ const TYPE_NUMBER = 4;
6
+ const TYPE_DATE = 5;
7
+ const TYPE_TIME = 6;
8
+
9
+ // Plain text literal
10
+
11
+ // Simple argument reference: ["name"]
12
+
13
+ // Pound sign (#) - represents the number in plural contexts
14
+
15
+ // Select: ["name", TYPE_SELECT, {options}]
16
+
17
+ // Plural: ["name", TYPE_PLURAL, {options}]
18
+
19
+ // Select ordinal: ["name", TYPE_SELECTORDINAL, {options}]
20
+
21
+ // Number format: ["name", TYPE_NUMBER, style?]
22
+
23
+ // Date format: ["name", TYPE_DATE, style?]
24
+
25
+ // Time format: ["name", TYPE_TIME, style?]
26
+
27
+ // Tags have no type constant - detected at runtime by
28
+ // format: ["tagName", child1, child2, ...]
29
+
30
+ export { TYPE_POUND as T, TYPE_NUMBER as a, TYPE_DATE as b, TYPE_TIME as c, TYPE_SELECT as d, TYPE_SELECTORDINAL as e, TYPE_PLURAL as f };
@@ -0,0 +1 @@
1
+ import{parse as t,TYPE as n}from"@formatjs/icu-messageformat-parser";import{T as e,a as r,b as s,c as o,d as u,e as i,f as c}from"./types-DqTLNw7x.js";function a(n){const e=f(t(n));return 0===e.length?"":1===e.length&&"string"==typeof e[0]?e[0]:e}function f(t){const n=[];for(const e of t){const t=p(e);"string"==typeof t&&n.length>0&&"string"==typeof n[n.length-1]?n[n.length-1]+=t:n.push(t)}return n}function l(t){const n=f(t);if(0===n.length)return"";if(1===n.length){const t=n[0];if("string"==typeof t||t===e)return t}return n}function p(t){switch(t.type){case n.literal:return t.value;case n.argument:return[t.value];case n.number:return function(t){const n=[t.value,r],e=function(t){if(!t)return;if("string"==typeof t)return t;if("parsedOptions"in t){const n=t.parsedOptions;return Object.keys(n).length>0?n:void 0}return}(t.style);void 0!==e&&n.push(e);return n}(t);case n.date:return function(t){const n=[t.value,s],e=g(t.style);void 0!==e&&n.push(e);return n}(t);case n.time:return function(t){const n=[t.value,o],e=g(t.style);void 0!==e&&n.push(e);return n}(t);case n.select:return function(t){const n={};for(const[e,r]of Object.entries(t.options))n[e]=l(r.value);return[t.value,u,n]}(t);case n.plural:return function(t){const n={};for(const[e,r]of Object.entries(t.options))n[e]=l(r.value);return[t.value,"ordinal"===t.pluralType?i:c,n]}(t);case n.pound:return e;case n.tag:return function(t){const n=f(t.children),e=[t.value];0===n.length?e.push(""):e.push(...n);return e}(t);default:throw new Error(`Unknown AST node type: ${t.type}`)}}function g(t){if(t){if("string"==typeof t)return t;if("parsedOptions"in t){const n=t.parsedOptions;return Object.keys(n).length>0?n:void 0}}}export{a as default};
@@ -0,0 +1 @@
1
+ import{T as t,c as r,b as n,a as e,e as o,f as u,d as s}from"./types-DqTLNw7x.js";function f(t,r,n={},e){if("string"==typeof t)return t;return g(i(t,r,n,e,void 0))}function i(t,r,n,e,o){const u=[];for(const s of t){const t=a(s,r,n,e,o);Array.isArray(t)?u.push(...t):u.push(t)}return u}function a(f,a,y,d,h){if("string"==typeof f)return f;if(f===t){const t=h;return d.formatters.getNumberFormat(t.locale).format(t.value)}const[b,v,...A]=f;if(void 0===v){const t=c(y,b);return String(t)}if("number"!=typeof v||v===t)return function(t,r,n,e,o,u){const s=c(e,t),f=s,a=i(r,n,e,o,u),m=g(a),l=Array.isArray(m)?m:[m];return f(l)}(b,[v,...A],a,y,d,h);switch(v){case s:return function(t,r,n,e,o,u){const s=String(c(e,t)),f=r[s]??r.other;return l(f,n,e,o,u)}(b,A[0],a,y,d,h);case u:return m(b,A[0],a,y,d,"cardinal");case o:return m(b,A[0],a,y,d,"ordinal");case e:return function(t,r,n,e,o){const u=c(e,t),s=u,f=function(t,r){if(!t)return;if("string"==typeof t)return r.formats?.number?.[t]?r.formats.number[t]:void 0;return t}(r,o);return o.formatters.getNumberFormat(n,f).format(s)}(b,A[0],a,y,d);case n:return p(b,A[0],a,y,d,"date");case r:return p(b,A[0],a,y,d,"time");default:return""}}function c(t,r){return t[r]}function m(t,r,n,e,o,u){const s=c(e,t),f=`=${s}`;if(f in r)return l(r[f],n,e,o,{value:s,locale:n});return l(r[o.formatters.getPluralRules(n,{type:u}).select(s)]??r.other,n,e,o,{value:s,locale:n})}function l(r,n,e,o,u){return"string"==typeof r?r:r===t?a(r,n,e,o,u):i(r,n,e,o,u)}function p(t,r,n,e,o,u){const s=c(e,t),f=function(t,r,n){if(!t)return;if("string"==typeof t){const r=n.formats?.dateTime?.[t];return r}return t}(r,0,o),i={...f,timeZone:f?.timeZone??o.timeZone};return o.formatters.getDateTimeFormat(n,i).format(s)}function g(t){if(0===t.length)return"";const r=[];let n="";for(const e of t)"string"==typeof e?n+=e:(n&&(r.push(n),n=""),r.push(e));return n&&r.push(n),1===r.length?r[0]:r}export{f as default};
@@ -0,0 +1 @@
1
+ const a=0,s=1,c=2,e=3,o=4,t=5,b=6;export{a as T,o as a,t as b,b as c,s as d,e,c as f};
@@ -0,0 +1,2 @@
1
+ import { type CompiledMessage } from './types.js';
2
+ export default function compile(message: string): CompiledMessage;
@@ -0,0 +1,17 @@
1
+ import { type CompiledMessage } from './types.js';
2
+ export type { CompiledMessage } from './types.js';
3
+ export type FormatValues<RichTextElement = unknown> = Record<string, string | number | boolean | Date | ((chunks: Array<string | RichTextElement>) => RichTextElement)>;
4
+ export type Formats = {
5
+ dateTime?: Record<string, Intl.DateTimeFormatOptions>;
6
+ number?: Record<string, Intl.NumberFormatOptions>;
7
+ };
8
+ export type FormatOptions = {
9
+ formats?: Formats;
10
+ formatters: {
11
+ getDateTimeFormat(...args: ConstructorParameters<typeof Intl.DateTimeFormat>): Intl.DateTimeFormat;
12
+ getNumberFormat(...args: ConstructorParameters<typeof Intl.NumberFormat>): Intl.NumberFormat;
13
+ getPluralRules(...args: ConstructorParameters<typeof Intl.PluralRules>): Intl.PluralRules;
14
+ };
15
+ timeZone?: string;
16
+ };
17
+ export default function format<RichTextElement = string>(message: CompiledMessage, locale: string, values: FormatValues<RichTextElement> | undefined, options: FormatOptions): string | RichTextElement | Array<string | RichTextElement>;
@@ -0,0 +1,29 @@
1
+ export declare const TYPE_POUND = 0;
2
+ export declare const TYPE_SELECT = 1;
3
+ export declare const TYPE_PLURAL = 2;
4
+ export declare const TYPE_SELECTORDINAL = 3;
5
+ export declare const TYPE_NUMBER = 4;
6
+ export declare const TYPE_DATE = 5;
7
+ export declare const TYPE_TIME = 6;
8
+ export type NumberStyleOptions = Intl.NumberFormatOptions;
9
+ export type NumberStyle = string | NumberStyleOptions;
10
+ export type DateTimeStyleOptions = Intl.DateTimeFormatOptions;
11
+ export type DateTimeStyle = string | DateTimeStyleOptions;
12
+ export type PluralOptions = Record<string, CompiledNode>;
13
+ export type SelectOptions = Record<string, CompiledNode>;
14
+ export type CompiledPlainTextNode = string;
15
+ export type CompiledSimpleArgNode = [string];
16
+ export type CompiledPoundNode = typeof TYPE_POUND;
17
+ export type CompiledSelectNode = [string, typeof TYPE_SELECT, SelectOptions];
18
+ export type CompiledPluralNode = [string, typeof TYPE_PLURAL, PluralOptions];
19
+ export type CompiledSelectOrdinalNode = [
20
+ string,
21
+ typeof TYPE_SELECTORDINAL,
22
+ PluralOptions
23
+ ];
24
+ export type CompiledNumberNode = [string, typeof TYPE_NUMBER, NumberStyle?];
25
+ export type CompiledDateNode = [string, typeof TYPE_DATE, DateTimeStyle?];
26
+ export type CompiledTimeNode = [string, typeof TYPE_TIME, DateTimeStyle?];
27
+ export type CompiledTagNode = [string, unknown, ...Array<unknown>];
28
+ export type CompiledNode = CompiledPlainTextNode | CompiledSimpleArgNode | CompiledPoundNode | CompiledSelectNode | CompiledPluralNode | CompiledSelectOrdinalNode | CompiledNumberNode | CompiledDateNode | CompiledTimeNode | CompiledTagNode;
29
+ export type CompiledMessage = string | Array<CompiledNode>;
package/format.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ // Needed for projects with `moduleResolution: 'node'`
2
+ export {default} from './dist/types/format.d.ts';
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "icu-minify",
3
+ "version": "0.0.1",
4
+ "sideEffects": false,
5
+ "author": "Jan Amann <jan@amann.work>",
6
+ "description": "ICU message format compiler with a <1KB runtime bundle footprint",
7
+ "license": "MIT",
8
+ "homepage": "https://next-intl.dev",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/amannn/next-intl.git"
12
+ },
13
+ "type": "module",
14
+ "exports": {
15
+ "./compile": {
16
+ "types": "./dist/types/compile.d.ts",
17
+ "development": "./dist/esm/development/compile.js",
18
+ "default": "./dist/esm/production/compile.js"
19
+ },
20
+ "./format": {
21
+ "types": "./dist/types/format.d.ts",
22
+ "development": "./dist/esm/development/format.js",
23
+ "default": "./dist/esm/production/format.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "compile.d.ts",
29
+ "format.d.ts"
30
+ ],
31
+ "keywords": [
32
+ "icu",
33
+ "messageformat",
34
+ "i18n",
35
+ "internationalization",
36
+ "compiler",
37
+ "minify"
38
+ ],
39
+ "dependencies": {
40
+ "@formatjs/icu-messageformat-parser": "^3.4.0"
41
+ },
42
+ "devDependencies": {
43
+ "@arethetypeswrong/cli": "^0.17.4",
44
+ "@size-limit/preset-small-lib": "^11.2.0",
45
+ "@types/node": "^20.14.5",
46
+ "eslint": "9.11.1",
47
+ "eslint-config-molindo": "^8.0.0",
48
+ "intl-messageformat": "^11.1.0",
49
+ "prettier": "^3.3.3",
50
+ "publint": "^0.2.8",
51
+ "rollup": "^4.18.0",
52
+ "size-limit": "^11.2.0",
53
+ "typescript": "^5.5.3",
54
+ "vitest": "^3.0.8",
55
+ "tools": "^1.0.0"
56
+ },
57
+ "prettier": "eslint-config-molindo/.prettierrc.json",
58
+ "scripts": {
59
+ "build": "rm -rf dist && rollup -c",
60
+ "test": "TZ=Europe/Berlin vitest",
61
+ "lint": "pnpm run lint:source && pnpm run lint:package",
62
+ "lint:source": "eslint src test && tsc --noEmit && pnpm run lint:prettier",
63
+ "lint:package": "publint && attw --pack --ignore-rules=cjs-resolves-to-esm",
64
+ "lint:prettier": "prettier src --check",
65
+ "size": "size-limit"
66
+ }
67
+ }