🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

messageformat

Package Overview
Dependencies
Maintainers
0
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

messageformat - npm Package Compare versions

Comparing version

to
4.0.0-9

lib/dir-utils.d.ts

7

lib/cst/names.js

@@ -20,3 +20,4 @@ "use strict";

(cc >= 0x370 && cc <= 0x37d) ||
(cc >= 0x37f && cc <= 0x1fff) ||
(cc >= 0x37f && cc <= 0x61b) ||
(cc >= 0x61d && cc <= 0x1fff) ||
(cc >= 0x200c && cc <= 0x200d) ||

@@ -53,6 +54,6 @@ (cc >= 0x2070 && cc <= 0x2187) ||

cc = src.charCodeAt(++pos);
const name = src.substring(nameStart, pos);
const value = src.substring(nameStart, pos).normalize();
if (bidiChars.has(cc))
pos += 1;
return { value: name, end: pos };
return { value, end: pos };
}

@@ -59,0 +60,0 @@ exports.parseNameValue = parseNameValue;

@@ -116,5 +116,10 @@ "use strict";

ctx.onError('missing-syntax', pos, "' '");
const key = ch === '*'
? ({ type: '*', start: pos, end: pos + 1 })
: (0, values_js_1.parseLiteral)(ctx, pos, true);
let key;
if (ch === '*') {
key = { type: '*', start: pos, end: pos + 1 };
}
else {
key = (0, values_js_1.parseLiteral)(ctx, pos, true);
key.value = key.value.normalize();
}
if (key.end === pos)

@@ -121,0 +126,0 @@ break; // error; reported in pattern.errors

@@ -53,3 +53,3 @@ "use strict";

function stringifyPattern({ body, braces }, braced) {
let str = braced ? braces?.[0]?.value ?? '{{' : '';
let str = braced ? (braces?.[0]?.value ?? '{{') : '';
for (const el of body) {

@@ -56,0 +56,0 @@ str +=

@@ -73,3 +73,5 @@ "use strict";

else {
keys.push(literal(true));
const key = literal(true);
key.value = key.value.normalize();
keys.push(key);
}

@@ -76,0 +78,0 @@ }

@@ -37,3 +37,3 @@ import { type MessageNode } from './data-model/types.js';

export declare class MessageResolutionError extends MessageError {
type: 'bad-function-result' | 'bad-operand' | 'bad-option' | 'unresolved-variable';
type: 'bad-function-result' | 'bad-operand' | 'bad-option' | 'unresolved-variable' | 'unsupported-operation';
source: string;

@@ -40,0 +40,0 @@ constructor(type: typeof MessageResolutionError.prototype.type, message: string, source: string);

@@ -1,2 +0,2 @@

import type { MessageValue } from './functions/index.js';
import type { MessageValue } from './message-value.js';
import type { MessageFunctions } from './messageformat.js';

@@ -7,3 +7,3 @@ export interface Context {

localeMatcher: 'best fit' | 'lookup';
locales: string[];
locales: Intl.Locale[];
/** Cache for local variables */

@@ -10,0 +10,0 @@ localVars: WeakSet<MessageValue>;

@@ -7,6 +7,13 @@ export type { MessageDateTimePart } from './functions/datetime.js';

/** @beta */
export interface MessageBiDiIsolationPart {
type: 'bidiIsolation';
value: '\u2066' | '\u2067' | '\u2068' | '\u2069';
}
/** @beta */
export interface MessageExpressionPart {
type: string;
source: string;
dir?: 'ltr' | 'rtl';
locale?: string;
id?: string;
parts?: Array<{

@@ -30,2 +37,3 @@ type: string;

name: string;
id?: string;
options?: {

@@ -36,2 +44,2 @@ [key: string]: unknown;

/** @beta */
export type MessagePart = MessageExpressionPart | MessageLiteralPart | MessageMarkupPart;
export type MessagePart = MessageBiDiIsolationPart | MessageExpressionPart | MessageLiteralPart | MessageMarkupPart;
import type { MessageExpressionPart } from '../formatted-parts.js';
import type { MessageFunctionContext, MessageValue } from './index.js';
import type { MessageValue } from '../message-value.js';
import type { MessageFunctionContext } from '../resolve/function-context.js';
/** @beta */

@@ -7,3 +8,3 @@ export interface MessageDateTime extends MessageValue {

readonly source: string;
readonly locale: string;
readonly dir: 'ltr' | 'rtl' | 'auto';
readonly options: Readonly<Intl.DateTimeFormatOptions>;

@@ -10,0 +11,0 @@ toParts(): [MessageDateTimePart];

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.time = exports.date = exports.datetime = void 0;
const dir_utils_js_1 = require("../dir-utils.js");
const errors_js_1 = require("../errors.js");
const utils_js_1 = require("./utils.js");
const localeOptions = [
'calendar',
'localeMatcher',
'hour12',
'hourCycle',
'numberingSystem',
'timeZone'
];
const styleOptions = new Set(['dateStyle', 'timeStyle']);
const fieldOptions = new Set([
'weekday',
'era',
'year',
'month',
'day',
'hour',
'minute',
'second',
'fractionalSecondDigits',
'timeZoneName'
]);
/**

@@ -21,3 +27,5 @@ * `datetime` accepts a Date, number or string as its input

*/
const datetime = (ctx, options, input) => dateTimeImplementation(ctx, options, input, res => {
const datetime = (ctx, options, input) => dateTimeImplementation(ctx, input, res => {
let hasStyle = false;
let hasFields = false;
for (const [name, value] of Object.entries(options)) {

@@ -32,2 +40,3 @@ if (value === undefined)

res[name] = (0, utils_js_1.asPositiveInteger)(value);
hasFields = true;
break;

@@ -39,14 +48,21 @@ case 'hour12':

res[name] = (0, utils_js_1.asString)(value);
if (!hasStyle && styleOptions.has(name))
hasStyle = true;
if (!hasFields && fieldOptions.has(name))
hasFields = true;
}
}
catch {
const msg = `Value ${value} is not valid for :datetime option ${name}`;
throw new errors_js_1.MessageResolutionError('bad-option', msg, ctx.source);
const msg = `Value ${value} is not valid for :datetime ${name} option`;
ctx.onError(new errors_js_1.MessageResolutionError('bad-option', msg, ctx.source));
}
}
// Set defaults if localeMatcher is the only option
if (Object.keys(res).length <= 1) {
if (!hasStyle && !hasFields) {
res.dateStyle = 'medium';
res.timeStyle = 'short';
}
else if (hasStyle && hasFields) {
const msg = 'Style and field options cannot be both set for :datetime';
throw new errors_js_1.MessageResolutionError('bad-option', msg, ctx.source);
}
});

@@ -60,15 +76,30 @@ exports.datetime = datetime;

*/
const date = (ctx, options, input) => dateTimeImplementation(ctx, options, input, res => {
const ds = options.style ?? res.dateStyle ?? 'medium';
const date = (ctx, options, input) => dateTimeImplementation(ctx, input, res => {
for (const name of Object.keys(res)) {
if (!localeOptions.includes(name))
if (styleOptions.has(name) || fieldOptions.has(name))
delete res[name];
}
try {
res.dateStyle = (0, utils_js_1.asString)(ds);
for (const [name, value] of Object.entries(options)) {
if (value === undefined)
continue;
try {
switch (name) {
case 'style':
res.dateStyle = (0, utils_js_1.asString)(value);
break;
case 'hour12':
res[name] = (0, utils_js_1.asBoolean)(value);
break;
case 'calendar':
case 'numberingSystem':
case 'timeZone':
res[name] = (0, utils_js_1.asString)(value);
}
}
catch {
const msg = `Value ${value} is not valid for :date ${name} option`;
ctx.onError(new errors_js_1.MessageResolutionError('bad-option', msg, ctx.source));
}
}
catch {
const msg = `Value ${ds} is not valid for :date style option`;
throw new errors_js_1.MessageResolutionError('bad-option', msg, ctx.source);
}
res.dateStyle ??= 'medium';
});

@@ -82,19 +113,34 @@ exports.date = date;

*/
const time = (ctx, options, input) => dateTimeImplementation(ctx, options, input, res => {
const ts = options.style ?? res.timeStyle ?? 'short';
const time = (ctx, options, input) => dateTimeImplementation(ctx, input, res => {
for (const name of Object.keys(res)) {
if (!localeOptions.includes(name))
if (styleOptions.has(name) || fieldOptions.has(name))
delete res[name];
}
try {
res.timeStyle = (0, utils_js_1.asString)(ts);
for (const [name, value] of Object.entries(options)) {
if (value === undefined)
continue;
try {
switch (name) {
case 'style':
res.timeStyle = (0, utils_js_1.asString)(value);
break;
case 'hour12':
res[name] = (0, utils_js_1.asBoolean)(value);
break;
case 'calendar':
case 'numberingSystem':
case 'timeZone':
res[name] = (0, utils_js_1.asString)(value);
}
}
catch {
const msg = `Value ${value} is not valid for :time ${name} option`;
ctx.onError(new errors_js_1.MessageResolutionError('bad-option', msg, ctx.source));
}
}
catch {
const msg = `Value ${ts} is not valid for :time style option`;
throw new errors_js_1.MessageResolutionError('bad-option', msg, ctx.source);
}
res.timeStyle ??= 'short';
});
exports.time = time;
function dateTimeImplementation({ localeMatcher, locales, source }, options, input, parseOptions) {
const lc = (0, utils_js_1.mergeLocales)(locales, input, options);
function dateTimeImplementation(ctx, input, parseOptions) {
const { localeMatcher, locales, source } = ctx;
const opt = { localeMatcher };

@@ -125,2 +171,3 @@ if (input && typeof input === 'object') {

let locale;
let dir = ctx.dir;
let dtf;

@@ -131,4 +178,8 @@ let str;

source,
get locale() {
return (locale ??= Intl.DateTimeFormat.supportedLocalesOf(lc, opt)[0]);
get dir() {
if (dir == null) {
locale ??= Intl.DateTimeFormat.supportedLocalesOf(locales, opt)[0];
dir = (0, dir_utils_js_1.getLocaleDir)(locale);
}
return dir;
},

@@ -139,9 +190,12 @@ get options() {

toParts() {
dtf ??= new Intl.DateTimeFormat(lc, opt);
dtf ??= new Intl.DateTimeFormat(locales, opt);
const parts = dtf.formatToParts(date);
locale ??= dtf.resolvedOptions().locale;
return [{ type: 'datetime', source, locale, parts }];
dir ??= (0, dir_utils_js_1.getLocaleDir)(locale);
return dir === 'ltr' || dir === 'rtl'
? [{ type: 'datetime', source, dir, locale, parts }]
: [{ type: 'datetime', source, locale, parts }];
},
toString() {
dtf ??= new Intl.DateTimeFormat(lc, opt);
dtf ??= new Intl.DateTimeFormat(locales, opt);
str ??= dtf.format(date);

@@ -148,0 +202,0 @@ return str;

import type { MessageExpressionPart } from '../formatted-parts.js';
import type { MessageValue } from './index.js';
import type { MessageValue } from '../message-value.js';
/**

@@ -11,3 +11,2 @@ * Used to represent runtime/formatting errors.

readonly source: string;
readonly locale: 'und';
toParts(): [MessageFallbackPart];

@@ -14,0 +13,0 @@ toString(): string;

@@ -6,3 +6,2 @@ "use strict";

type: 'fallback',
locale: 'und',
source,

@@ -9,0 +8,0 @@ toParts: () => [{ type: 'fallback', source }],

@@ -1,17 +0,9 @@

import type { MessageExpressionPart } from '../formatted-parts.js';
export type { MessageFunctionContext } from '../data-model/function-context.js';
export { type MessageDateTime, date, datetime, time } from './datetime.js';
export { type MessageFallback, fallback } from './fallback.js';
export { type MessageNumber, integer, number } from './number.js';
export { type MessageString, string } from './string.js';
export { type MessageUnknownValue, unknown } from './unknown.js';
export interface MessageValue {
readonly type: string;
readonly locale: string;
readonly source: string;
readonly options?: Readonly<object>;
selectKey?: (keys: Set<string>) => string | null;
toParts?: () => MessageExpressionPart[];
toString?: () => string;
valueOf?: () => unknown;
}
export type { MessageValue } from '../message-value.js';
export type { MessageFunctionContext } from '../resolve/function-context.js';
export { currency } from './currency.js';
export { date, datetime, time, type MessageDateTime } from './datetime.js';
export { fallback, type MessageFallback } from './fallback.js';
export { math } from './math.js';
export { integer, number, type MessageNumber } from './number.js';
export { string, type MessageString } from './string.js';
export { unknown, type MessageUnknownValue } from './unknown.js';
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unknown = exports.string = exports.number = exports.integer = exports.fallback = exports.time = exports.datetime = exports.date = void 0;
exports.unknown = exports.string = exports.number = exports.integer = exports.math = exports.fallback = exports.time = exports.datetime = exports.date = exports.currency = void 0;
var currency_js_1 = require("./currency.js");
Object.defineProperty(exports, "currency", { enumerable: true, get: function () { return currency_js_1.currency; } });
var datetime_js_1 = require("./datetime.js");

@@ -10,2 +12,4 @@ Object.defineProperty(exports, "date", { enumerable: true, get: function () { return datetime_js_1.date; } });

Object.defineProperty(exports, "fallback", { enumerable: true, get: function () { return fallback_js_1.fallback; } });
var math_js_1 = require("./math.js");
Object.defineProperty(exports, "math", { enumerable: true, get: function () { return math_js_1.math; } });
var number_js_1 = require("./number.js");

@@ -12,0 +16,0 @@ Object.defineProperty(exports, "integer", { enumerable: true, get: function () { return number_js_1.integer; } });

import type { MessageExpressionPart } from '../formatted-parts.js';
import type { MessageFunctionContext, MessageValue } from './index.js';
import type { MessageValue } from '../message-value.js';
import type { MessageFunctionContext } from '../resolve/function-context.js';
/** @beta */

@@ -7,3 +8,3 @@ export interface MessageNumber extends MessageValue {

readonly source: string;
readonly locale: string;
readonly dir: 'ltr' | 'rtl' | 'auto';
readonly options: Readonly<Intl.NumberFormatOptions & Intl.PluralRulesOptions>;

@@ -32,2 +33,6 @@ /**

}
export declare function readNumericOperand(value: unknown, source: string): {
value: number | bigint;
options: unknown;
};
/**

@@ -42,3 +47,3 @@ * `number` accepts a number, BigInt or string representing a JSON number as input

*/
export declare function number({ localeMatcher, locales, source }: MessageFunctionContext, options: Record<string | symbol, unknown>, input?: unknown): MessageNumber;
export declare function number(ctx: MessageFunctionContext, exprOpt: Record<string | symbol, unknown>, operand?: unknown): MessageNumber;
/**

@@ -45,0 +50,0 @@ * `integer` accepts a number, BigInt or string representing a JSON number as input

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.integer = exports.number = void 0;
exports.integer = exports.number = exports.readNumericOperand = void 0;
const dir_utils_js_1 = require("../dir-utils.js");
const errors_js_1 = require("../errors.js");
const utils_js_1 = require("./utils.js");
const INT = Symbol('INT');
/**
* `number` accepts a number, BigInt or string representing a JSON number as input
* and formats it with the same options as
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat | Intl.NumberFormat}.
* It also supports plural category selection via
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules | Intl.PluralRules}.
*
* @beta
*/
function number({ localeMatcher, locales, source }, options, input) {
const opt = {
localeMatcher
};
let value = input;
function readNumericOperand(value, source) {
let options = undefined;
if (typeof value === 'object') {
const valueOf = value?.valueOf;
if (typeof valueOf === 'function') {
Object.assign(opt, value.options);
options = value.options;
value = valueOf.call(value);

@@ -40,3 +29,23 @@ }

}
for (const [name, optval] of Object.entries(options)) {
return { value, options };
}
exports.readNumericOperand = readNumericOperand;
/**
* `number` accepts a number, BigInt or string representing a JSON number as input
* and formats it with the same options as
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat | Intl.NumberFormat}.
* It also supports plural category selection via
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules | Intl.PluralRules}.
*
* @beta
*/
function number(ctx, exprOpt, operand) {
const { locales, source } = ctx;
const options = {
localeMatcher: ctx.localeMatcher
};
const input = readNumericOperand(operand, source);
const value = input.value;
Object.assign(options, input.options);
for (const [name, optval] of Object.entries(exprOpt)) {
if (optval === undefined)

@@ -56,10 +65,13 @@ continue;

// @ts-expect-error TS types don't know about roundingIncrement
opt[name] = (0, utils_js_1.asPositiveInteger)(optval);
options[name] = (0, utils_js_1.asPositiveInteger)(optval);
break;
case 'useGrouping':
opt[name] = (0, utils_js_1.asBoolean)(optval);
case 'useGrouping': {
const strval = (0, utils_js_1.asString)(optval);
// @ts-expect-error TS type is wrong
options[name] = strval === 'never' ? false : strval;
break;
}
default:
// @ts-expect-error Unknown options will be ignored
opt[name] = (0, utils_js_1.asString)(optval);
options[name] = (0, utils_js_1.asString)(optval);
}

@@ -69,10 +81,10 @@ }

const msg = `Value ${optval} is not valid for :number option ${name}`;
throw new errors_js_1.MessageResolutionError('bad-option', msg, source);
ctx.onError(new errors_js_1.MessageResolutionError('bad-option', msg, source));
}
}
const num = Number.isFinite(value) && options[INT]
const num = Number.isFinite(value) && exprOpt[INT]
? Math.round(value)
: value;
const lc = (0, utils_js_1.mergeLocales)(locales, input, options);
let locale;
let dir = ctx.dir;
let nf;

@@ -84,7 +96,11 @@ let cat;

source,
get locale() {
return (locale ??= Intl.NumberFormat.supportedLocalesOf(lc, opt)[0]);
get dir() {
if (dir == null) {
locale ??= Intl.NumberFormat.supportedLocalesOf(locales, options)[0];
dir = (0, dir_utils_js_1.getLocaleDir)(locale);
}
return dir;
},
get options() {
return { ...opt };
return { ...options };
},

@@ -95,19 +111,22 @@ selectKey(keys) {

return str;
if (opt.select === 'exact')
if (options.select === 'exact')
return null;
const pluralOpt = opt.select
? { ...opt, select: undefined, type: opt.select }
: opt;
const pluralOpt = options.select
? { ...options, select: undefined, type: options.select }
: options;
// Intl.PluralRules needs a number, not bigint
cat ??= new Intl.PluralRules(lc, pluralOpt).select(Number(num));
cat ??= new Intl.PluralRules(locales, pluralOpt).select(Number(num));
return keys.has(cat) ? cat : null;
},
toParts() {
nf ??= new Intl.NumberFormat(lc, opt);
nf ??= new Intl.NumberFormat(locales, options);
const parts = nf.formatToParts(num);
locale ??= nf.resolvedOptions().locale;
return [{ type: 'number', source, locale, parts }];
dir ??= (0, dir_utils_js_1.getLocaleDir)(locale);
return dir === 'ltr' || dir === 'rtl'
? [{ type: 'number', source, dir, locale, parts }]
: [{ type: 'number', source, locale, parts }];
},
toString() {
nf ??= new Intl.NumberFormat(lc, opt);
nf ??= new Intl.NumberFormat(locales, options);
str ??= nf.format(num);

@@ -114,0 +133,0 @@ return str;

import type { MessageExpressionPart } from '../formatted-parts.js';
import type { MessageFunctionContext, MessageValue } from './index.js';
import type { MessageValue } from '../message-value.js';
import type { MessageFunctionContext } from '../resolve/function-context.js';
/** @beta */

@@ -7,3 +8,3 @@ export interface MessageString extends MessageValue {

readonly source: string;
readonly locale: string;
readonly dir: 'ltr' | 'rtl' | 'auto';
selectKey(keys: Set<string>): string | null;

@@ -27,2 +28,2 @@ toParts(): [MessageStringPart];

* @beta */
export declare function string({ locales, source }: Pick<MessageFunctionContext, 'locales' | 'source'>, options: Record<string, unknown>, input?: unknown): MessageString;
export declare function string(ctx: Pick<MessageFunctionContext, 'dir' | 'locales' | 'source'>, _options: Record<string, unknown>, input?: unknown): MessageString;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.string = void 0;
const utils_js_1 = require("./utils.js");
/**

@@ -11,11 +10,17 @@ * Accepts any input, and parses any non-string value using `String()`.

* @beta */
function string({ locales, source }, options, input) {
function string(ctx, _options, input) {
const str = input === undefined ? '' : String(input);
const [locale] = (0, utils_js_1.mergeLocales)(locales, input, options);
const selStr = str.normalize();
return {
type: 'string',
source,
locale,
selectKey: keys => (keys.has(str) ? str : null),
toParts: () => [{ type: 'string', source, locale, value: str }],
source: ctx.source,
dir: ctx.dir ?? 'auto',
selectKey: keys => (keys.has(selStr) ? selStr : null),
toParts() {
const { dir, source } = ctx;
const locale = ctx.locales[0];
return dir === 'ltr' || dir === 'rtl'
? [{ type: 'string', source, dir, locale, value: str }]
: [{ type: 'string', source, locale, value: str }];
},
toString: () => str,

@@ -22,0 +27,0 @@ valueOf: () => str

import type { MessageExpressionPart } from '../formatted-parts.js';
import type { MessageValue } from './index.js';
import type { MessageValue } from '../message-value.js';
/** @beta */

@@ -7,3 +7,3 @@ export interface MessageUnknownValue extends MessageValue {

readonly source: string;
readonly locale: 'und';
readonly dir: 'auto';
toParts(): [MessageUnknownPart];

@@ -10,0 +10,0 @@ toString(): string;

@@ -8,3 +8,3 @@ "use strict";

source,
locale: 'und',
dir: 'auto',
toParts: () => [{ type: 'unknown', source, value: input }],

@@ -11,0 +11,0 @@ toString: () => String(input),

@@ -0,1 +1,2 @@

export { getLocaleDir } from '../dir-utils.js';
/**

@@ -13,3 +14,3 @@ * Utility function for custom functions.

* Utility function for custom functions.
* Cast a value as a positive integer,
* Cast a value as a non-negative integer,
* unwrapping objects using their `valueOf()` methods.

@@ -31,10 +32,1 @@ * Also accepts JSON string reprentations of integers.

export declare function asString(value: unknown): string;
/**
* Utility function for custom functions.
* Merge the locales set for the message,
* an `options` property on the input,
* and the `locale` option of the expression.
*
* @beta
*/
export declare function mergeLocales(locales: string[], input: unknown, options: Record<string, unknown> | null): string[];
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeLocales = exports.asString = exports.asPositiveInteger = exports.asBoolean = void 0;
exports.asString = exports.asPositiveInteger = exports.asBoolean = exports.getLocaleDir = void 0;
var dir_utils_js_1 = require("../dir-utils.js");
Object.defineProperty(exports, "getLocaleDir", { enumerable: true, get: function () { return dir_utils_js_1.getLocaleDir; } });
/**

@@ -14,3 +16,3 @@ * Utility function for custom functions.

function asBoolean(value) {
if (value instanceof Boolean)
if (value && typeof value === 'object')
value = value.valueOf();

@@ -30,3 +32,3 @@ if (typeof value === 'boolean')

* Utility function for custom functions.
* Cast a value as a positive integer,
* Cast a value as a non-negative integer,
* unwrapping objects using their `valueOf()` methods.

@@ -39,5 +41,5 @@ * Also accepts JSON string reprentations of integers.

function asPositiveInteger(value) {
if (value instanceof Number)
value = Number(value);
if (typeof value === 'object' && value)
if (value && typeof value === 'object')
value = value.valueOf();
if (value && typeof value === 'object')
value = String(value);

@@ -62,2 +64,4 @@ if (typeof value === 'string' && /^(0|[1-9][0-9]*)$/.test(value)) {

function asString(value) {
if (value && typeof value === 'object')
value = value.valueOf();
if (typeof value === 'string')

@@ -70,29 +74,1 @@ return value;

exports.asString = asString;
/**
* Utility function for custom functions.
* Merge the locales set for the message,
* an `options` property on the input,
* and the `locale` option of the expression.
*
* @beta
*/
function mergeLocales(locales, input, options) {
// Message locales are always included, but have the lowest precedence
let lc = locales;
// Next, use options from input object
if (input && typeof input === 'object' && 'locale' in input) {
if (typeof input.locale === 'string') {
lc = [input.locale, ...lc];
}
else if (Array.isArray(input.locale) &&
input.locale.every(lc => typeof lc === 'string')) {
lc = [...input.locale, ...lc];
}
}
// Explicit locale in expression options is preferred over all others
if (options?.locale) {
lc = [...asString(options.locale).split(','), ...lc];
}
return lc;
}
exports.mergeLocales = mergeLocales;

@@ -1,5 +0,5 @@

import type { MessageFunctionContext } from './data-model/function-context.js';
import type { Message } from './data-model/types.js';
import type { MessagePart } from './formatted-parts.js';
import { MessageValue } from './functions/index.js';
import { type MessageValue } from './message-value.js';
import type { MessageFunctionContext } from './resolve/function-context.js';
/**

@@ -16,2 +16,17 @@ * The runtime function registry available when resolving {@link FunctionRef} elements.

/**
* The bidi isolation strategy for messages,
* i.e. how parts with different or unknown directionalities are isolated from each other.
*
* The default `'default'` strategy isolates all placeholders,
* except when both the message and the placeholder are known to be left-to-right.
*
* The `'none'` strategy applies no isolation at all.
*/
bidiIsolation?: 'default' | 'none';
/**
* Explicitly set the message's base direction.
* If not set, the direction is detected from the primary locale.
*/
dir?: 'ltr' | 'rtl' | 'auto';
/**
* If given multiple locales,

@@ -42,2 +57,4 @@ * determines which algorithm to use when selecting between them;

resolvedOptions(): {
bidiIsolation: boolean;
dir: "auto" | "ltr" | "rtl";
functions: Readonly<Readonly<MessageFunctions>>;

@@ -44,0 +61,0 @@ localeMatcher: "lookup" | "best fit";

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageFormat = void 0;
const format_markup_js_1 = require("./data-model/format-markup.js");
const resolve_expression_js_1 = require("./data-model/resolve-expression.js");
const resolve_variable_js_1 = require("./data-model/resolve-variable.js");
const parse_js_1 = require("./data-model/parse.js");
const validate_js_1 = require("./data-model/validate.js");
const dir_utils_js_1 = require("./dir-utils.js");
const errors_js_1 = require("./errors.js");
const index_js_1 = require("./functions/index.js");
const parse_js_1 = require("./data-model/parse.js");
const message_value_js_1 = require("./message-value.js");
const format_markup_js_1 = require("./resolve/format-markup.js");
const resolve_expression_js_1 = require("./resolve/resolve-expression.js");
const resolve_variable_js_1 = require("./resolve/resolve-variable.js");
const select_pattern_js_1 = require("./select-pattern.js");
const defaultFunctions = Object.freeze({
currency: index_js_1.currency,
date: index_js_1.date,
datetime: index_js_1.datetime,
integer: index_js_1.integer,
math: index_js_1.math,
number: index_js_1.number,

@@ -26,2 +30,4 @@ string: index_js_1.string,

class MessageFormat {
#bidiIsolation;
#dir;
#localeMatcher;

@@ -32,8 +38,10 @@ #locales;

constructor(locales, source, options) {
this.#bidiIsolation = options?.bidiIsolation !== 'none';
this.#localeMatcher = options?.localeMatcher ?? 'best fit';
this.#locales = Array.isArray(locales)
? locales.slice()
? locales.map(lc => new Intl.Locale(lc))
: locales
? [locales]
? [new Intl.Locale(locales)]
: [];
this.#dir = options?.dir ?? (0, dir_utils_js_1.getLocaleDir)(this.#locales[0]);
this.#message = typeof source === 'string' ? (0, parse_js_1.parseMessage)(source) : source;

@@ -54,3 +62,7 @@ (0, validate_js_1.validate)(this.#message, (type, node) => {

}
else if (elem.type !== 'markup') {
else if (elem.type === 'markup') {
// Handle errors, but discard results
(0, format_markup_js_1.formatMarkup)(ctx, elem);
}
else {
let mv;

@@ -60,3 +72,10 @@ try {

if (typeof mv.toString === 'function') {
res += mv.toString();
if (this.#bidiIsolation &&
(this.#dir !== 'ltr' || mv.dir !== 'ltr' || mv[message_value_js_1.BIDI_ISOLATE])) {
const pre = mv.dir === 'ltr' ? dir_utils_js_1.LRI : mv.dir === 'rtl' ? dir_utils_js_1.RLI : dir_utils_js_1.FSI;
res += pre + mv.toString() + dir_utils_js_1.PDI;
}
else {
res += mv.toString();
}
}

@@ -70,3 +89,4 @@ else {

ctx.onError(error);
res += `{${mv?.source ?? '�'}}`;
const errStr = `{${mv?.source ?? '�'}}`;
res += this.#bidiIsolation ? dir_utils_js_1.FSI + errStr + dir_utils_js_1.PDI : errStr;
}

@@ -92,3 +112,14 @@ }

if (typeof mv.toParts === 'function') {
parts.push(...mv.toParts());
const mp = mv.toParts();
if (this.#bidiIsolation &&
(this.#dir !== 'ltr' || mv.dir !== 'ltr' || mv[message_value_js_1.BIDI_ISOLATE])) {
const pre = mv.dir === 'ltr' ? dir_utils_js_1.LRI : mv.dir === 'rtl' ? dir_utils_js_1.RLI : dir_utils_js_1.FSI;
parts.push({ type: 'bidiIsolation', value: pre }, ...mp, {
type: 'bidiIsolation',
value: dir_utils_js_1.PDI
});
}
else {
parts.push(...mp);
}
}

@@ -102,3 +133,12 @@ else {

ctx.onError(error);
parts.push({ type: 'fallback', source: mv?.source ?? '�' });
const fb = { type: 'fallback', source: mv?.source ?? '�' };
if (this.#bidiIsolation) {
parts.push({ type: 'bidiIsolation', value: dir_utils_js_1.FSI }, fb, {
type: 'bidiIsolation',
value: dir_utils_js_1.PDI
});
}
else {
parts.push(fb);
}
}

@@ -111,2 +151,4 @@ }

return {
bidiIsolation: this.#bidiIsolation,
dir: this.#dir,
functions: Object.freeze(this.#functions),

@@ -127,3 +169,3 @@ localeMatcher: this.#localeMatcher

for (const decl of this.#message.declarations) {
scope[decl.name] = new resolve_variable_js_1.UnresolvedExpression(decl.value, decl.type === 'input' ? msgParams ?? {} : undefined);
scope[decl.name] = new resolve_variable_js_1.UnresolvedExpression(decl.value, decl.type === 'input' ? (msgParams ?? {}) : undefined);
}

@@ -130,0 +172,0 @@ const ctx = {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectPattern = void 0;
const resolve_variable_js_1 = require("./data-model/resolve-variable.js");
const errors_js_1 = require("./errors.js");
const resolve_variable_js_1 = require("./resolve/resolve-variable.js");
function selectPattern(context, message) {

@@ -7,0 +7,0 @@ switch (message.type) {

{
"name": "messageformat",
"version": "4.0.0-8",
"version": "4.0.0-9",
"description": "Intl.MessageFormat / Unicode MessageFormat 2 parser, runtime and polyfill",

@@ -5,0 +5,0 @@ "keywords": [