Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@formatjs/icu-messageformat-parser

Package Overview
Dependencies
Maintainers
3
Versions
92
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@formatjs/icu-messageformat-parser - npm Package Compare versions

Comparing version
3.2.1
to
3.3.0
+6
-6
date-time-pattern-generator.d.ts
/**
* Returns the best matching date time pattern if a date time skeleton
* pattern is provided with a locale. Follows the Unicode specification:
* https://www.unicode.org/reports/tr35/tr35-dates.html#table-mapping-requested-time-skeletons-to-patterns
* @param skeleton date time skeleton pattern that possibly includes j, J or C
* @param locale
*/
* Returns the best matching date time pattern if a date time skeleton
* pattern is provided with a locale. Follows the Unicode specification:
* https://www.unicode.org/reports/tr35/tr35-dates.html#table-mapping-requested-time-skeletons-to-patterns
* @param skeleton date time skeleton pattern that possibly includes j, J or C
* @param locale
*/
export declare function getBestPattern(skeleton: string, locale: Intl.Locale): string;

@@ -1,83 +0,68 @@

import { timeData } from './time-data.generated.js';
import { timeData } from "./time-data.generated.js";
/**
* Returns the best matching date time pattern if a date time skeleton
* pattern is provided with a locale. Follows the Unicode specification:
* https://www.unicode.org/reports/tr35/tr35-dates.html#table-mapping-requested-time-skeletons-to-patterns
* @param skeleton date time skeleton pattern that possibly includes j, J or C
* @param locale
*/
* Returns the best matching date time pattern if a date time skeleton
* pattern is provided with a locale. Follows the Unicode specification:
* https://www.unicode.org/reports/tr35/tr35-dates.html#table-mapping-requested-time-skeletons-to-patterns
* @param skeleton date time skeleton pattern that possibly includes j, J or C
* @param locale
*/
export function getBestPattern(skeleton, locale) {
var skeletonCopy = '';
for (var patternPos = 0; patternPos < skeleton.length; patternPos++) {
var patternChar = skeleton.charAt(patternPos);
if (patternChar === 'j') {
var extraLength = 0;
while (patternPos + 1 < skeleton.length &&
skeleton.charAt(patternPos + 1) === patternChar) {
extraLength++;
patternPos++;
}
var hourLen = 1 + (extraLength & 1);
var dayPeriodLen = extraLength < 2 ? 1 : 3 + (extraLength >> 1);
var dayPeriodChar = 'a';
var hourChar = getDefaultHourSymbolFromLocale(locale);
if (hourChar == 'H' || hourChar == 'k') {
dayPeriodLen = 0;
}
while (dayPeriodLen-- > 0) {
skeletonCopy += dayPeriodChar;
}
while (hourLen-- > 0) {
skeletonCopy = hourChar + skeletonCopy;
}
}
else if (patternChar === 'J') {
skeletonCopy += 'H';
}
else {
skeletonCopy += patternChar;
}
}
return skeletonCopy;
let skeletonCopy = "";
for (let patternPos = 0; patternPos < skeleton.length; patternPos++) {
const patternChar = skeleton.charAt(patternPos);
if (patternChar === "j") {
let extraLength = 0;
while (patternPos + 1 < skeleton.length && skeleton.charAt(patternPos + 1) === patternChar) {
extraLength++;
patternPos++;
}
let hourLen = 1 + (extraLength & 1);
let dayPeriodLen = extraLength < 2 ? 1 : 3 + (extraLength >> 1);
let dayPeriodChar = "a";
let hourChar = getDefaultHourSymbolFromLocale(locale);
if (hourChar == "H" || hourChar == "k") {
dayPeriodLen = 0;
}
while (dayPeriodLen-- > 0) {
skeletonCopy += dayPeriodChar;
}
while (hourLen-- > 0) {
skeletonCopy = hourChar + skeletonCopy;
}
} else if (patternChar === "J") {
skeletonCopy += "H";
} else {
skeletonCopy += patternChar;
}
}
return skeletonCopy;
}
/**
* Maps the [hour cycle type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/hourCycle)
* of the given `locale` to the corresponding time pattern.
* @param locale
*/
* Maps the [hour cycle type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/hourCycle)
* of the given `locale` to the corresponding time pattern.
* @param locale
*/
function getDefaultHourSymbolFromLocale(locale) {
var hourCycle = locale.hourCycle;
if (hourCycle === undefined &&
// @ts-ignore hourCycle(s) is not identified yet
locale.hourCycles &&
// @ts-ignore
locale.hourCycles.length) {
// @ts-ignore
hourCycle = locale.hourCycles[0];
}
if (hourCycle) {
switch (hourCycle) {
case 'h24':
return 'k';
case 'h23':
return 'H';
case 'h12':
return 'h';
case 'h11':
return 'K';
default:
throw new Error('Invalid hourCycle');
}
}
// TODO: Once hourCycle is fully supported remove the following with data generation
var languageTag = locale.language;
var regionTag;
if (languageTag !== 'root') {
regionTag = locale.maximize().region;
}
var hourCycles = timeData[regionTag || ''] ||
timeData[languageTag || ''] ||
timeData["".concat(languageTag, "-001")] ||
timeData['001'];
return hourCycles[0];
let hourCycle = locale.hourCycle;
if (hourCycle === undefined && locale.hourCycles && locale.hourCycles.length) {
// @ts-ignore
hourCycle = locale.hourCycles[0];
}
if (hourCycle) {
switch (hourCycle) {
case "h24": return "k";
case "h23": return "H";
case "h12": return "h";
case "h11": return "K";
default: throw new Error("Invalid hourCycle");
}
}
// TODO: Once hourCycle is fully supported remove the following with data generation
const languageTag = locale.language;
let regionTag;
if (languageTag !== "root") {
regionTag = locale.maximize().region;
}
const hourCycles = timeData[regionTag || ""] || timeData[languageTag || ""] || timeData[`${languageTag}-001`] || timeData["001"];
return hourCycles[0];
}

@@ -1,68 +0,68 @@

import { Location } from './types.js';
import { type Location } from "./types.js";
export interface ParserError {
kind: ErrorKind;
message: string;
location: Location;
kind: ErrorKind;
message: string;
location: Location;
}
export declare enum ErrorKind {
/** Argument is unclosed (e.g. `{0`) */
EXPECT_ARGUMENT_CLOSING_BRACE = 1,
/** Argument is empty (e.g. `{}`). */
EMPTY_ARGUMENT = 2,
/** Argument is malformed (e.g. `{foo!}``) */
MALFORMED_ARGUMENT = 3,
/** Expect an argument type (e.g. `{foo,}`) */
EXPECT_ARGUMENT_TYPE = 4,
/** Unsupported argument type (e.g. `{foo,foo}`) */
INVALID_ARGUMENT_TYPE = 5,
/** Expect an argument style (e.g. `{foo, number, }`) */
EXPECT_ARGUMENT_STYLE = 6,
/** The number skeleton is invalid. */
INVALID_NUMBER_SKELETON = 7,
/** The date time skeleton is invalid. */
INVALID_DATE_TIME_SKELETON = 8,
/** Exepct a number skeleton following the `::` (e.g. `{foo, number, ::}`) */
EXPECT_NUMBER_SKELETON = 9,
/** Exepct a date time skeleton following the `::` (e.g. `{foo, date, ::}`) */
EXPECT_DATE_TIME_SKELETON = 10,
/** Unmatched apostrophes in the argument style (e.g. `{foo, number, 'test`) */
UNCLOSED_QUOTE_IN_ARGUMENT_STYLE = 11,
/** Missing select argument options (e.g. `{foo, select}`) */
EXPECT_SELECT_ARGUMENT_OPTIONS = 12,
/** Expecting an offset value in `plural` or `selectordinal` argument (e.g `{foo, plural, offset}`) */
EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE = 13,
/** Offset value in `plural` or `selectordinal` is invalid (e.g. `{foo, plural, offset: x}`) */
INVALID_PLURAL_ARGUMENT_OFFSET_VALUE = 14,
/** Expecting a selector in `select` argument (e.g `{foo, select}`) */
EXPECT_SELECT_ARGUMENT_SELECTOR = 15,
/** Expecting a selector in `plural` or `selectordinal` argument (e.g `{foo, plural}`) */
EXPECT_PLURAL_ARGUMENT_SELECTOR = 16,
/** Expecting a message fragment after the `select` selector (e.g. `{foo, select, apple}`) */
EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT = 17,
/**
* Expecting a message fragment after the `plural` or `selectordinal` selector
* (e.g. `{foo, plural, one}`)
*/
EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT = 18,
/** Selector in `plural` or `selectordinal` is malformed (e.g. `{foo, plural, =x {#}}`) */
INVALID_PLURAL_ARGUMENT_SELECTOR = 19,
/**
* Duplicate selectors in `plural` or `selectordinal` argument.
* (e.g. {foo, plural, one {#} one {#}})
*/
DUPLICATE_PLURAL_ARGUMENT_SELECTOR = 20,
/** Duplicate selectors in `select` argument.
* (e.g. {foo, select, apple {apple} apple {apple}})
*/
DUPLICATE_SELECT_ARGUMENT_SELECTOR = 21,
/** Plural or select argument option must have `other` clause. */
MISSING_OTHER_CLAUSE = 22,
/** The tag is malformed. (e.g. `<bold!>foo</bold!>) */
INVALID_TAG = 23,
/** The tag name is invalid. (e.g. `<123>foo</123>`) */
INVALID_TAG_NAME = 25,
/** The closing tag does not match the opening tag. (e.g. `<bold>foo</italic>`) */
UNMATCHED_CLOSING_TAG = 26,
/** The opening tag has unmatched closing tag. (e.g. `<bold>foo`) */
UNCLOSED_TAG = 27
/** Argument is unclosed (e.g. `{0`) */
EXPECT_ARGUMENT_CLOSING_BRACE = 1,
/** Argument is empty (e.g. `{}`). */
EMPTY_ARGUMENT = 2,
/** Argument is malformed (e.g. `{foo!}``) */
MALFORMED_ARGUMENT = 3,
/** Expect an argument type (e.g. `{foo,}`) */
EXPECT_ARGUMENT_TYPE = 4,
/** Unsupported argument type (e.g. `{foo,foo}`) */
INVALID_ARGUMENT_TYPE = 5,
/** Expect an argument style (e.g. `{foo, number, }`) */
EXPECT_ARGUMENT_STYLE = 6,
/** The number skeleton is invalid. */
INVALID_NUMBER_SKELETON = 7,
/** The date time skeleton is invalid. */
INVALID_DATE_TIME_SKELETON = 8,
/** Exepct a number skeleton following the `::` (e.g. `{foo, number, ::}`) */
EXPECT_NUMBER_SKELETON = 9,
/** Exepct a date time skeleton following the `::` (e.g. `{foo, date, ::}`) */
EXPECT_DATE_TIME_SKELETON = 10,
/** Unmatched apostrophes in the argument style (e.g. `{foo, number, 'test`) */
UNCLOSED_QUOTE_IN_ARGUMENT_STYLE = 11,
/** Missing select argument options (e.g. `{foo, select}`) */
EXPECT_SELECT_ARGUMENT_OPTIONS = 12,
/** Expecting an offset value in `plural` or `selectordinal` argument (e.g `{foo, plural, offset}`) */
EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE = 13,
/** Offset value in `plural` or `selectordinal` is invalid (e.g. `{foo, plural, offset: x}`) */
INVALID_PLURAL_ARGUMENT_OFFSET_VALUE = 14,
/** Expecting a selector in `select` argument (e.g `{foo, select}`) */
EXPECT_SELECT_ARGUMENT_SELECTOR = 15,
/** Expecting a selector in `plural` or `selectordinal` argument (e.g `{foo, plural}`) */
EXPECT_PLURAL_ARGUMENT_SELECTOR = 16,
/** Expecting a message fragment after the `select` selector (e.g. `{foo, select, apple}`) */
EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT = 17,
/**
* Expecting a message fragment after the `plural` or `selectordinal` selector
* (e.g. `{foo, plural, one}`)
*/
EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT = 18,
/** Selector in `plural` or `selectordinal` is malformed (e.g. `{foo, plural, =x {#}}`) */
INVALID_PLURAL_ARGUMENT_SELECTOR = 19,
/**
* Duplicate selectors in `plural` or `selectordinal` argument.
* (e.g. {foo, plural, one {#} one {#}})
*/
DUPLICATE_PLURAL_ARGUMENT_SELECTOR = 20,
/** Duplicate selectors in `select` argument.
* (e.g. {foo, select, apple {apple} apple {apple}})
*/
DUPLICATE_SELECT_ARGUMENT_SELECTOR = 21,
/** Plural or select argument option must have `other` clause. */
MISSING_OTHER_CLAUSE = 22,
/** The tag is malformed. (e.g. `<bold!>foo</bold!>) */
INVALID_TAG = 23,
/** The tag name is invalid. (e.g. `<123>foo</123>`) */
INVALID_TAG_NAME = 25,
/** The closing tag does not match the opening tag. (e.g. `<bold>foo</italic>`) */
UNMATCHED_CLOSING_TAG = 26,
/** The opening tag has unmatched closing tag. (e.g. `<bold>foo`) */
UNCLOSED_TAG = 27
}
+64
-63

@@ -1,63 +0,64 @@

export var ErrorKind;
(function (ErrorKind) {
/** Argument is unclosed (e.g. `{0`) */
ErrorKind[ErrorKind["EXPECT_ARGUMENT_CLOSING_BRACE"] = 1] = "EXPECT_ARGUMENT_CLOSING_BRACE";
/** Argument is empty (e.g. `{}`). */
ErrorKind[ErrorKind["EMPTY_ARGUMENT"] = 2] = "EMPTY_ARGUMENT";
/** Argument is malformed (e.g. `{foo!}``) */
ErrorKind[ErrorKind["MALFORMED_ARGUMENT"] = 3] = "MALFORMED_ARGUMENT";
/** Expect an argument type (e.g. `{foo,}`) */
ErrorKind[ErrorKind["EXPECT_ARGUMENT_TYPE"] = 4] = "EXPECT_ARGUMENT_TYPE";
/** Unsupported argument type (e.g. `{foo,foo}`) */
ErrorKind[ErrorKind["INVALID_ARGUMENT_TYPE"] = 5] = "INVALID_ARGUMENT_TYPE";
/** Expect an argument style (e.g. `{foo, number, }`) */
ErrorKind[ErrorKind["EXPECT_ARGUMENT_STYLE"] = 6] = "EXPECT_ARGUMENT_STYLE";
/** The number skeleton is invalid. */
ErrorKind[ErrorKind["INVALID_NUMBER_SKELETON"] = 7] = "INVALID_NUMBER_SKELETON";
/** The date time skeleton is invalid. */
ErrorKind[ErrorKind["INVALID_DATE_TIME_SKELETON"] = 8] = "INVALID_DATE_TIME_SKELETON";
/** Exepct a number skeleton following the `::` (e.g. `{foo, number, ::}`) */
ErrorKind[ErrorKind["EXPECT_NUMBER_SKELETON"] = 9] = "EXPECT_NUMBER_SKELETON";
/** Exepct a date time skeleton following the `::` (e.g. `{foo, date, ::}`) */
ErrorKind[ErrorKind["EXPECT_DATE_TIME_SKELETON"] = 10] = "EXPECT_DATE_TIME_SKELETON";
/** Unmatched apostrophes in the argument style (e.g. `{foo, number, 'test`) */
ErrorKind[ErrorKind["UNCLOSED_QUOTE_IN_ARGUMENT_STYLE"] = 11] = "UNCLOSED_QUOTE_IN_ARGUMENT_STYLE";
/** Missing select argument options (e.g. `{foo, select}`) */
ErrorKind[ErrorKind["EXPECT_SELECT_ARGUMENT_OPTIONS"] = 12] = "EXPECT_SELECT_ARGUMENT_OPTIONS";
/** Expecting an offset value in `plural` or `selectordinal` argument (e.g `{foo, plural, offset}`) */
ErrorKind[ErrorKind["EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE"] = 13] = "EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE";
/** Offset value in `plural` or `selectordinal` is invalid (e.g. `{foo, plural, offset: x}`) */
ErrorKind[ErrorKind["INVALID_PLURAL_ARGUMENT_OFFSET_VALUE"] = 14] = "INVALID_PLURAL_ARGUMENT_OFFSET_VALUE";
/** Expecting a selector in `select` argument (e.g `{foo, select}`) */
ErrorKind[ErrorKind["EXPECT_SELECT_ARGUMENT_SELECTOR"] = 15] = "EXPECT_SELECT_ARGUMENT_SELECTOR";
/** Expecting a selector in `plural` or `selectordinal` argument (e.g `{foo, plural}`) */
ErrorKind[ErrorKind["EXPECT_PLURAL_ARGUMENT_SELECTOR"] = 16] = "EXPECT_PLURAL_ARGUMENT_SELECTOR";
/** Expecting a message fragment after the `select` selector (e.g. `{foo, select, apple}`) */
ErrorKind[ErrorKind["EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT"] = 17] = "EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT";
/**
* Expecting a message fragment after the `plural` or `selectordinal` selector
* (e.g. `{foo, plural, one}`)
*/
ErrorKind[ErrorKind["EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT"] = 18] = "EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT";
/** Selector in `plural` or `selectordinal` is malformed (e.g. `{foo, plural, =x {#}}`) */
ErrorKind[ErrorKind["INVALID_PLURAL_ARGUMENT_SELECTOR"] = 19] = "INVALID_PLURAL_ARGUMENT_SELECTOR";
/**
* Duplicate selectors in `plural` or `selectordinal` argument.
* (e.g. {foo, plural, one {#} one {#}})
*/
ErrorKind[ErrorKind["DUPLICATE_PLURAL_ARGUMENT_SELECTOR"] = 20] = "DUPLICATE_PLURAL_ARGUMENT_SELECTOR";
/** Duplicate selectors in `select` argument.
* (e.g. {foo, select, apple {apple} apple {apple}})
*/
ErrorKind[ErrorKind["DUPLICATE_SELECT_ARGUMENT_SELECTOR"] = 21] = "DUPLICATE_SELECT_ARGUMENT_SELECTOR";
/** Plural or select argument option must have `other` clause. */
ErrorKind[ErrorKind["MISSING_OTHER_CLAUSE"] = 22] = "MISSING_OTHER_CLAUSE";
/** The tag is malformed. (e.g. `<bold!>foo</bold!>) */
ErrorKind[ErrorKind["INVALID_TAG"] = 23] = "INVALID_TAG";
/** The tag name is invalid. (e.g. `<123>foo</123>`) */
ErrorKind[ErrorKind["INVALID_TAG_NAME"] = 25] = "INVALID_TAG_NAME";
/** The closing tag does not match the opening tag. (e.g. `<bold>foo</italic>`) */
ErrorKind[ErrorKind["UNMATCHED_CLOSING_TAG"] = 26] = "UNMATCHED_CLOSING_TAG";
/** The opening tag has unmatched closing tag. (e.g. `<bold>foo`) */
ErrorKind[ErrorKind["UNCLOSED_TAG"] = 27] = "UNCLOSED_TAG";
})(ErrorKind || (ErrorKind = {}));
import "./types.js";
export let ErrorKind = /* @__PURE__ */ function(ErrorKind) {
/** Argument is unclosed (e.g. `{0`) */
ErrorKind[ErrorKind["EXPECT_ARGUMENT_CLOSING_BRACE"] = 1] = "EXPECT_ARGUMENT_CLOSING_BRACE";
/** Argument is empty (e.g. `{}`). */
ErrorKind[ErrorKind["EMPTY_ARGUMENT"] = 2] = "EMPTY_ARGUMENT";
/** Argument is malformed (e.g. `{foo!}``) */
ErrorKind[ErrorKind["MALFORMED_ARGUMENT"] = 3] = "MALFORMED_ARGUMENT";
/** Expect an argument type (e.g. `{foo,}`) */
ErrorKind[ErrorKind["EXPECT_ARGUMENT_TYPE"] = 4] = "EXPECT_ARGUMENT_TYPE";
/** Unsupported argument type (e.g. `{foo,foo}`) */
ErrorKind[ErrorKind["INVALID_ARGUMENT_TYPE"] = 5] = "INVALID_ARGUMENT_TYPE";
/** Expect an argument style (e.g. `{foo, number, }`) */
ErrorKind[ErrorKind["EXPECT_ARGUMENT_STYLE"] = 6] = "EXPECT_ARGUMENT_STYLE";
/** The number skeleton is invalid. */
ErrorKind[ErrorKind["INVALID_NUMBER_SKELETON"] = 7] = "INVALID_NUMBER_SKELETON";
/** The date time skeleton is invalid. */
ErrorKind[ErrorKind["INVALID_DATE_TIME_SKELETON"] = 8] = "INVALID_DATE_TIME_SKELETON";
/** Exepct a number skeleton following the `::` (e.g. `{foo, number, ::}`) */
ErrorKind[ErrorKind["EXPECT_NUMBER_SKELETON"] = 9] = "EXPECT_NUMBER_SKELETON";
/** Exepct a date time skeleton following the `::` (e.g. `{foo, date, ::}`) */
ErrorKind[ErrorKind["EXPECT_DATE_TIME_SKELETON"] = 10] = "EXPECT_DATE_TIME_SKELETON";
/** Unmatched apostrophes in the argument style (e.g. `{foo, number, 'test`) */
ErrorKind[ErrorKind["UNCLOSED_QUOTE_IN_ARGUMENT_STYLE"] = 11] = "UNCLOSED_QUOTE_IN_ARGUMENT_STYLE";
/** Missing select argument options (e.g. `{foo, select}`) */
ErrorKind[ErrorKind["EXPECT_SELECT_ARGUMENT_OPTIONS"] = 12] = "EXPECT_SELECT_ARGUMENT_OPTIONS";
/** Expecting an offset value in `plural` or `selectordinal` argument (e.g `{foo, plural, offset}`) */
ErrorKind[ErrorKind["EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE"] = 13] = "EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE";
/** Offset value in `plural` or `selectordinal` is invalid (e.g. `{foo, plural, offset: x}`) */
ErrorKind[ErrorKind["INVALID_PLURAL_ARGUMENT_OFFSET_VALUE"] = 14] = "INVALID_PLURAL_ARGUMENT_OFFSET_VALUE";
/** Expecting a selector in `select` argument (e.g `{foo, select}`) */
ErrorKind[ErrorKind["EXPECT_SELECT_ARGUMENT_SELECTOR"] = 15] = "EXPECT_SELECT_ARGUMENT_SELECTOR";
/** Expecting a selector in `plural` or `selectordinal` argument (e.g `{foo, plural}`) */
ErrorKind[ErrorKind["EXPECT_PLURAL_ARGUMENT_SELECTOR"] = 16] = "EXPECT_PLURAL_ARGUMENT_SELECTOR";
/** Expecting a message fragment after the `select` selector (e.g. `{foo, select, apple}`) */
ErrorKind[ErrorKind["EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT"] = 17] = "EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT";
/**
* Expecting a message fragment after the `plural` or `selectordinal` selector
* (e.g. `{foo, plural, one}`)
*/
ErrorKind[ErrorKind["EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT"] = 18] = "EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT";
/** Selector in `plural` or `selectordinal` is malformed (e.g. `{foo, plural, =x {#}}`) */
ErrorKind[ErrorKind["INVALID_PLURAL_ARGUMENT_SELECTOR"] = 19] = "INVALID_PLURAL_ARGUMENT_SELECTOR";
/**
* Duplicate selectors in `plural` or `selectordinal` argument.
* (e.g. {foo, plural, one {#} one {#}})
*/
ErrorKind[ErrorKind["DUPLICATE_PLURAL_ARGUMENT_SELECTOR"] = 20] = "DUPLICATE_PLURAL_ARGUMENT_SELECTOR";
/** Duplicate selectors in `select` argument.
* (e.g. {foo, select, apple {apple} apple {apple}})
*/
ErrorKind[ErrorKind["DUPLICATE_SELECT_ARGUMENT_SELECTOR"] = 21] = "DUPLICATE_SELECT_ARGUMENT_SELECTOR";
/** Plural or select argument option must have `other` clause. */
ErrorKind[ErrorKind["MISSING_OTHER_CLAUSE"] = 22] = "MISSING_OTHER_CLAUSE";
/** The tag is malformed. (e.g. `<bold!>foo</bold!>) */
ErrorKind[ErrorKind["INVALID_TAG"] = 23] = "INVALID_TAG";
/** The tag name is invalid. (e.g. `<123>foo</123>`) */
ErrorKind[ErrorKind["INVALID_TAG_NAME"] = 25] = "INVALID_TAG_NAME";
/** The closing tag does not match the opening tag. (e.g. `<bold>foo</italic>`) */
ErrorKind[ErrorKind["UNMATCHED_CLOSING_TAG"] = 26] = "UNMATCHED_CLOSING_TAG";
/** The opening tag has unmatched closing tag. (e.g. `<bold>foo`) */
ErrorKind[ErrorKind["UNCLOSED_TAG"] = 27] = "UNCLOSED_TAG";
return ErrorKind;
}({});

@@ -1,7 +0,8 @@

import { Parser, ParserOptions } from './parser.js';
import { MessageFormatElement } from './types.js';
import { Parser, type ParserOptions } from "./parser.js";
import { type MessageFormatElement } from "./types.js";
export declare function parse(message: string, opts?: ParserOptions): MessageFormatElement[];
export * from './types.js';
export * from "./types.js";
export type { ParserOptions };
// only for testing
export declare const _Parser: typeof Parser;
export { isStructurallySame } from './manipulator.js';
export { isStructurallySame } from "./manipulator.js";
+40
-42

@@ -1,46 +0,44 @@

import { __assign } from "tslib";
import { ErrorKind } from './error.js';
import { Parser } from './parser.js';
import { isDateElement, isDateTimeSkeleton, isNumberElement, isNumberSkeleton, isPluralElement, isSelectElement, isTagElement, isTimeElement, } from './types.js';
import { ErrorKind } from "./error.js";
import { Parser } from "./parser.js";
import { isDateElement, isDateTimeSkeleton, isNumberElement, isNumberSkeleton, isPluralElement, isSelectElement, isTagElement, isTimeElement } from "./types.js";
function pruneLocation(els) {
els.forEach(function (el) {
delete el.location;
if (isSelectElement(el) || isPluralElement(el)) {
for (var k in el.options) {
delete el.options[k].location;
pruneLocation(el.options[k].value);
}
}
else if (isNumberElement(el) && isNumberSkeleton(el.style)) {
delete el.style.location;
}
else if ((isDateElement(el) || isTimeElement(el)) &&
isDateTimeSkeleton(el.style)) {
delete el.style.location;
}
else if (isTagElement(el)) {
pruneLocation(el.children);
}
});
els.forEach((el) => {
delete el.location;
if (isSelectElement(el) || isPluralElement(el)) {
for (const k in el.options) {
delete el.options[k].location;
pruneLocation(el.options[k].value);
}
} else if (isNumberElement(el) && isNumberSkeleton(el.style)) {
delete el.style.location;
} else if ((isDateElement(el) || isTimeElement(el)) && isDateTimeSkeleton(el.style)) {
delete el.style.location;
} else if (isTagElement(el)) {
pruneLocation(el.children);
}
});
}
export function parse(message, opts) {
if (opts === void 0) { opts = {}; }
opts = __assign({ shouldParseSkeletons: true, requiresOtherClause: true }, opts);
var result = new Parser(message, opts).parse();
if (result.err) {
var error = SyntaxError(ErrorKind[result.err.kind]);
// @ts-expect-error Assign to error object
error.location = result.err.location;
// @ts-expect-error Assign to error object
error.originalMessage = result.err.message;
throw error;
}
if (!(opts === null || opts === void 0 ? void 0 : opts.captureLocation)) {
pruneLocation(result.val);
}
return result.val;
export function parse(message, opts = {}) {
opts = {
shouldParseSkeletons: true,
requiresOtherClause: true,
...opts
};
const result = new Parser(message, opts).parse();
if (result.err) {
const error = SyntaxError(ErrorKind[result.err.kind]);
// @ts-expect-error Assign to error object
error.location = result.err.location;
// @ts-expect-error Assign to error object
error.originalMessage = result.err.message;
throw error;
}
if (!opts?.captureLocation) {
pruneLocation(result.val);
}
return result.val;
}
export * from './types.js';
export * from "./types.js";
// only for testing
export var _Parser = Parser;
export { isStructurallySame } from './manipulator.js';
export const _Parser = Parser;
export { isStructurallySame } from "./manipulator.js";

@@ -1,26 +0,26 @@

import { MessageFormatElement } from './types.js';
import { type MessageFormatElement } from "./types.js";
/**
* Hoist all selectors to the beginning of the AST & flatten the
* resulting options. E.g:
* "I have {count, plural, one{a dog} other{many dogs}}"
* becomes "{count, plural, one{I have a dog} other{I have many dogs}}".
* If there are multiple selectors, the order of which one is hoisted 1st
* is non-deterministic.
* The goal is to provide as many full sentences as possible since fragmented
* sentences are not translator-friendly
* @param ast AST
*/
* Hoist all selectors to the beginning of the AST & flatten the
* resulting options. E.g:
* "I have {count, plural, one{a dog} other{many dogs}}"
* becomes "{count, plural, one{I have a dog} other{I have many dogs}}".
* If there are multiple selectors, the order of which one is hoisted 1st
* is non-deterministic.
* The goal is to provide as many full sentences as possible since fragmented
* sentences are not translator-friendly
* @param ast AST
*/
export declare function hoistSelectors(ast: MessageFormatElement[]): MessageFormatElement[];
interface IsStructurallySameResult {
error?: Error;
success: boolean;
error?: Error;
success: boolean;
}
/**
* Check if 2 ASTs are structurally the same. This primarily means that
* they have the same variables with the same type
* @param a
* @param b
* @returns
*/
* Check if 2 ASTs are structurally the same. This primarily means that
* they have the same variables with the same type
* @param a
* @param b
* @returns
*/
export declare function isStructurallySame(a: MessageFormatElement[], b: MessageFormatElement[]): IsStructurallySameResult;
export {};

@@ -1,178 +0,177 @@

import { __assign, __spreadArray } from "tslib";
import { isArgumentElement, isDateElement, isNumberElement, isPluralElement, isPoundElement, isSelectElement, isTagElement, isTimeElement, TYPE, } from './types.js';
import { isArgumentElement, isDateElement, isNumberElement, isPluralElement, isPoundElement, isSelectElement, isTagElement, isTimeElement, TYPE } from "./types.js";
function cloneDeep(obj) {
if (Array.isArray(obj)) {
// @ts-expect-error meh
return obj.map(cloneDeep);
}
if (obj !== null && typeof obj === 'object') {
// @ts-expect-error meh
return Object.keys(obj).reduce(function (cloned, k) {
// @ts-expect-error meh
cloned[k] = cloneDeep(obj[k]);
return cloned;
}, {});
}
return obj;
if (Array.isArray(obj)) {
// @ts-expect-error meh
return obj.map(cloneDeep);
}
if (obj !== null && typeof obj === "object") {
// @ts-expect-error meh
return Object.keys(obj).reduce((cloned, k) => {
// @ts-expect-error meh
cloned[k] = cloneDeep(obj[k]);
return cloned;
}, {});
}
return obj;
}
/**
* Replace pound elements with number elements referencing the given variable.
* This is needed when nesting plurals - the # in the outer plural should become
* an explicit variable reference when nested inside another plural.
* GH #4202
*/
* Replace pound elements with number elements referencing the given variable.
* This is needed when nesting plurals - the # in the outer plural should become
* an explicit variable reference when nested inside another plural.
* GH #4202
*/
function replacePoundWithArgument(ast, variableName) {
return ast.map(function (el) {
if (isPoundElement(el)) {
// Replace # with {variableName, number}
return {
type: TYPE.number,
value: variableName,
style: null,
location: el.location,
};
}
if (isPluralElement(el) || isSelectElement(el)) {
// Recursively process options
var newOptions = {};
for (var _i = 0, _a = Object.keys(el.options); _i < _a.length; _i++) {
var key = _a[_i];
newOptions[key] = {
value: replacePoundWithArgument(el.options[key].value, variableName),
};
}
return __assign(__assign({}, el), { options: newOptions });
}
if (isTagElement(el)) {
return __assign(__assign({}, el), { children: replacePoundWithArgument(el.children, variableName) });
}
return el;
});
return ast.map((el) => {
if (isPoundElement(el)) {
// Replace # with {variableName, number}
return {
type: TYPE.number,
value: variableName,
style: null,
location: el.location
};
}
if (isPluralElement(el) || isSelectElement(el)) {
// Recursively process options
const newOptions = {};
for (const key of Object.keys(el.options)) {
newOptions[key] = { value: replacePoundWithArgument(el.options[key].value, variableName) };
}
return {
...el,
options: newOptions
};
}
if (isTagElement(el)) {
return {
...el,
children: replacePoundWithArgument(el.children, variableName)
};
}
return el;
});
}
function hoistPluralOrSelectElement(ast, el, positionToInject) {
// pull this out of the ast and move it to the top
var cloned = cloneDeep(el);
var options = cloned.options;
// GH #4202: Check if there are other plural/select elements after this one
var afterElements = ast.slice(positionToInject + 1);
var hasSubsequentPluralOrSelect = afterElements.some(isPluralOrSelectElement);
cloned.options = Object.keys(options).reduce(function (all, k) {
var optionValue = options[k].value;
// GH #4202: If there are subsequent plurals/selects and this is a plural,
// replace # with explicit variable reference to avoid ambiguity
if (hasSubsequentPluralOrSelect && isPluralElement(el)) {
optionValue = replacePoundWithArgument(optionValue, el.value);
}
var newValue = hoistSelectors(__spreadArray(__spreadArray(__spreadArray([], ast.slice(0, positionToInject), true), optionValue, true), afterElements, true));
all[k] = {
value: newValue,
};
return all;
}, {});
return cloned;
// pull this out of the ast and move it to the top
const cloned = cloneDeep(el);
const { options } = cloned;
// GH #4202: Check if there are other plural/select elements after this one
const afterElements = ast.slice(positionToInject + 1);
const hasSubsequentPluralOrSelect = afterElements.some(isPluralOrSelectElement);
cloned.options = Object.keys(options).reduce((all, k) => {
let optionValue = options[k].value;
// GH #4202: If there are subsequent plurals/selects and this is a plural,
// replace # with explicit variable reference to avoid ambiguity
if (hasSubsequentPluralOrSelect && isPluralElement(el)) {
optionValue = replacePoundWithArgument(optionValue, el.value);
}
const newValue = hoistSelectors([
...ast.slice(0, positionToInject),
...optionValue,
...afterElements
]);
all[k] = { value: newValue };
return all;
}, {});
return cloned;
}
function isPluralOrSelectElement(el) {
return isPluralElement(el) || isSelectElement(el);
return isPluralElement(el) || isSelectElement(el);
}
function findPluralOrSelectElement(ast) {
return !!ast.find(function (el) {
if (isPluralOrSelectElement(el)) {
return true;
}
if (isTagElement(el)) {
return findPluralOrSelectElement(el.children);
}
return false;
});
return !!ast.find((el) => {
if (isPluralOrSelectElement(el)) {
return true;
}
if (isTagElement(el)) {
return findPluralOrSelectElement(el.children);
}
return false;
});
}
/**
* Hoist all selectors to the beginning of the AST & flatten the
* resulting options. E.g:
* "I have {count, plural, one{a dog} other{many dogs}}"
* becomes "{count, plural, one{I have a dog} other{I have many dogs}}".
* If there are multiple selectors, the order of which one is hoisted 1st
* is non-deterministic.
* The goal is to provide as many full sentences as possible since fragmented
* sentences are not translator-friendly
* @param ast AST
*/
* Hoist all selectors to the beginning of the AST & flatten the
* resulting options. E.g:
* "I have {count, plural, one{a dog} other{many dogs}}"
* becomes "{count, plural, one{I have a dog} other{I have many dogs}}".
* If there are multiple selectors, the order of which one is hoisted 1st
* is non-deterministic.
* The goal is to provide as many full sentences as possible since fragmented
* sentences are not translator-friendly
* @param ast AST
*/
export function hoistSelectors(ast) {
for (var i = 0; i < ast.length; i++) {
var el = ast[i];
if (isPluralOrSelectElement(el)) {
return [hoistPluralOrSelectElement(ast, el, i)];
}
if (isTagElement(el) && findPluralOrSelectElement([el])) {
throw new Error('Cannot hoist plural/select within a tag element. Please put the tag element inside each plural/select option');
}
}
return ast;
for (let i = 0; i < ast.length; i++) {
const el = ast[i];
if (isPluralOrSelectElement(el)) {
return [hoistPluralOrSelectElement(ast, el, i)];
}
if (isTagElement(el) && findPluralOrSelectElement([el])) {
throw new Error("Cannot hoist plural/select within a tag element. Please put the tag element inside each plural/select option");
}
}
return ast;
}
/**
* Collect all variables in an AST to Record<string, TYPE>
* @param ast AST to collect variables from
* @param vars Record of variable name to variable type
*/
function collectVariables(ast, vars) {
if (vars === void 0) { vars = new Map(); }
ast.forEach(function (el) {
if (isArgumentElement(el) ||
isDateElement(el) ||
isTimeElement(el) ||
isNumberElement(el)) {
if (el.value in vars && vars.get(el.value) !== el.type) {
throw new Error("Variable ".concat(el.value, " has conflicting types"));
}
vars.set(el.value, el.type);
}
if (isPluralElement(el) || isSelectElement(el)) {
vars.set(el.value, el.type);
Object.keys(el.options).forEach(function (k) {
collectVariables(el.options[k].value, vars);
});
}
if (isTagElement(el)) {
vars.set(el.value, el.type);
collectVariables(el.children, vars);
}
});
* Collect all variables in an AST to Record<string, TYPE>
* @param ast AST to collect variables from
* @param vars Record of variable name to variable type
*/
function collectVariables(ast, vars = new Map()) {
ast.forEach((el) => {
if (isArgumentElement(el) || isDateElement(el) || isTimeElement(el) || isNumberElement(el)) {
if (el.value in vars && vars.get(el.value) !== el.type) {
throw new Error(`Variable ${el.value} has conflicting types`);
}
vars.set(el.value, el.type);
}
if (isPluralElement(el) || isSelectElement(el)) {
vars.set(el.value, el.type);
Object.keys(el.options).forEach((k) => {
collectVariables(el.options[k].value, vars);
});
}
if (isTagElement(el)) {
vars.set(el.value, el.type);
collectVariables(el.children, vars);
}
});
}
/**
* Check if 2 ASTs are structurally the same. This primarily means that
* they have the same variables with the same type
* @param a
* @param b
* @returns
*/
* Check if 2 ASTs are structurally the same. This primarily means that
* they have the same variables with the same type
* @param a
* @param b
* @returns
*/
export function isStructurallySame(a, b) {
var aVars = new Map();
var bVars = new Map();
collectVariables(a, aVars);
collectVariables(b, bVars);
if (aVars.size !== bVars.size) {
return {
success: false,
error: new Error("Different number of variables: [".concat(Array.from(aVars.keys()).join(', '), "] vs [").concat(Array.from(bVars.keys()).join(', '), "]")),
};
}
return Array.from(aVars.entries()).reduce(function (result, _a) {
var key = _a[0], type = _a[1];
if (!result.success) {
return result;
}
var bType = bVars.get(key);
if (bType == null) {
return {
success: false,
error: new Error("Missing variable ".concat(key, " in message")),
};
}
if (bType !== type) {
return {
success: false,
error: new Error("Variable ".concat(key, " has conflicting types: ").concat(TYPE[type], " vs ").concat(TYPE[bType])),
};
}
return result;
}, { success: true });
const aVars = new Map();
const bVars = new Map();
collectVariables(a, aVars);
collectVariables(b, bVars);
if (aVars.size !== bVars.size) {
return {
success: false,
error: new Error(`Different number of variables: [${Array.from(aVars.keys()).join(", ")}] vs [${Array.from(bVars.keys()).join(", ")}]`)
};
}
return Array.from(aVars.entries()).reduce((result, [key, type]) => {
if (!result.success) {
return result;
}
const bType = bVars.get(key);
if (bType == null) {
return {
success: false,
error: new Error(`Missing variable ${key} in message`)
};
}
if (bType !== type) {
return {
success: false,
error: new Error(`Variable ${key} has conflicting types: ${TYPE[type]} vs ${TYPE[bType]}`)
};
}
return result;
}, { success: true });
}
export declare function parse(): void;
export * from './types.js';
export * from "./types.js";
export declare const _Parser: undefined;
export { isStructurallySame } from './manipulator.js';
export { isStructurallySame } from "./manipulator.js";
export function parse() {
throw new Error("You're trying to format an uncompiled message with react-intl without parser, please import from 'react-intl' instead");
throw new Error("You're trying to format an uncompiled message with react-intl without parser, please import from 'react-intl' instead");
}
export * from './types.js';
export var _Parser = undefined;
export { isStructurallySame } from './manipulator.js';
export * from "./types.js";
export const _Parser = undefined;
export { isStructurallySame } from "./manipulator.js";
{
"name": "@formatjs/icu-messageformat-parser",
"version": "3.2.1",
"version": "3.3.0",
"license": "MIT",

@@ -15,4 +15,4 @@ "type": "module",

"tslib": "^2.8.0",
"@formatjs/ecma402-abstract": "3.0.7",
"@formatjs/icu-skeleton-parser": "2.0.7"
"@formatjs/ecma402-abstract": "3.0.8",
"@formatjs/icu-skeleton-parser": "2.0.8"
},

@@ -19,0 +19,0 @@ "repository": {

+142
-139

@@ -1,147 +0,150 @@

import { ParserError } from './error.js';
import { MessageFormatElement } from './types.js';
import { type ParserError } from "./error.js";
import { type MessageFormatElement } from "./types.js";
export interface Position {
/** Offset in terms of UTF-16 *code unit*. */
offset: number;
line: number;
/** Column offset in terms of unicode *code point*. */
column: number;
/** Offset in terms of UTF-16 *code unit*. */
offset: number;
line: number;
/** Column offset in terms of unicode *code point*. */
column: number;
}
export interface ParserOptions {
/**
* Whether to treat HTML/XML tags as string literal
* instead of parsing them as tag token.
* When this is false we only allow simple tags without
* any attributes
*/
ignoreTag?: boolean;
/**
* Should `select`, `selectordinal`, and `plural` arguments always include
* the `other` case clause.
*/
requiresOtherClause?: boolean;
/**
* Whether to parse number/datetime skeleton
* into Intl.NumberFormatOptions and Intl.DateTimeFormatOptions, respectively.
*/
shouldParseSkeletons?: boolean;
/**
* Capture location info in AST
* Default is false
*/
captureLocation?: boolean;
/**
* Instance of Intl.Locale to resolve locale-dependent skeleton
*/
locale?: Intl.Locale;
/**
* Whether to treat HTML/XML tags as string literal
* instead of parsing them as tag token.
* When this is false we only allow simple tags without
* any attributes
*/
ignoreTag?: boolean;
/**
* Should `select`, `selectordinal`, and `plural` arguments always include
* the `other` case clause.
*/
requiresOtherClause?: boolean;
/**
* Whether to parse number/datetime skeleton
* into Intl.NumberFormatOptions and Intl.DateTimeFormatOptions, respectively.
*/
shouldParseSkeletons?: boolean;
/**
* Capture location info in AST
* Default is false
*/
captureLocation?: boolean;
/**
* Instance of Intl.Locale to resolve locale-dependent skeleton
*/
locale?: Intl.Locale;
}
export type Result<T, E> = {
val: T;
err: null;
export type Result<
T,
E
> = {
val: T;
err: null;
} | {
val: null;
err: E;
val: null;
err: E;
};
export declare class Parser {
private message;
private position;
private locale?;
private ignoreTag;
private requiresOtherClause;
private shouldParseSkeletons?;
constructor(message: string, options?: ParserOptions);
parse(): Result<MessageFormatElement[], ParserError>;
private parseMessage;
/**
* A tag name must start with an ASCII lower/upper case letter. The grammar is based on the
* [custom element name][] except that a dash is NOT always mandatory and uppercase letters
* are accepted:
*
* ```
* tag ::= "<" tagName (whitespace)* "/>" | "<" tagName (whitespace)* ">" message "</" tagName (whitespace)* ">"
* tagName ::= [a-z] (PENChar)*
* PENChar ::=
* "-" | "." | [0-9] | "_" | [a-z] | [A-Z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] |
* [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] |
* [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
* ```
*
* [custom element name]: https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
* NOTE: We're a bit more lax here since HTML technically does not allow uppercase HTML element but we do
* since other tag-based engines like React allow it
*/
private parseTag;
/**
* This method assumes that the caller has peeked ahead for the first tag character.
*/
private parseTagName;
private parseLiteral;
tryParseLeftAngleBracket(): string | null;
/**
* Starting with ICU 4.8, an ASCII apostrophe only starts quoted text if it immediately precedes
* a character that requires quoting (that is, "only where needed"), and works the same in
* nested messages as on the top level of the pattern. The new behavior is otherwise compatible.
*/
private tryParseQuote;
private tryParseUnquoted;
private parseArgument;
/**
* Advance the parser until the end of the identifier, if it is currently on
* an identifier character. Return an empty string otherwise.
*/
private parseIdentifierIfPossible;
private parseArgumentOptions;
private tryParseArgumentClose;
/**
* See: https://github.com/unicode-org/icu/blob/af7ed1f6d2298013dc303628438ec4abe1f16479/icu4c/source/common/messagepattern.cpp#L659
*/
private parseSimpleArgStyleIfPossible;
private parseNumberSkeletonFromString;
/**
* @param nesting_level The current nesting level of messages.
* This can be positive when parsing message fragment in select or plural argument options.
* @param parent_arg_type The parent argument's type.
* @param parsed_first_identifier If provided, this is the first identifier-like selector of
* the argument. It is a by-product of a previous parsing attempt.
* @param expecting_close_tag If true, this message is directly or indirectly nested inside
* between a pair of opening and closing tags. The nested message will not parse beyond
* the closing tag boundary.
*/
private tryParsePluralOrSelectOptions;
private tryParseDecimalInteger;
private offset;
private isEOF;
private clonePosition;
/**
* Return the code point at the current position of the parser.
* Throws if the index is out of bound.
*/
private char;
private error;
/** Bump the parser to the next UTF-16 code unit. */
private bump;
/**
* If the substring starting at the current position of the parser has
* the given prefix, then bump the parser to the character immediately
* following the prefix and return true. Otherwise, don't bump the parser
* and return false.
*/
private bumpIf;
/**
* Bump the parser until the pattern character is found and return `true`.
* Otherwise bump to the end of the file and return `false`.
*/
private bumpUntil;
/**
* Bump the parser to the target offset.
* If target offset is beyond the end of the input, bump the parser to the end of the input.
*/
private bumpTo;
/** advance the parser through all whitespace to the next non-whitespace code unit. */
private bumpSpace;
/**
* Peek at the *next* Unicode codepoint in the input without advancing the parser.
* If the input has been exhausted, then this returns null.
*/
private peek;
private message;
private position;
private locale?;
private ignoreTag;
private requiresOtherClause;
private shouldParseSkeletons?;
constructor(message: string, options?: ParserOptions);
parse(): Result<MessageFormatElement[], ParserError>;
private parseMessage;
/**
* A tag name must start with an ASCII lower/upper case letter. The grammar is based on the
* [custom element name][] except that a dash is NOT always mandatory and uppercase letters
* are accepted:
*
* ```
* tag ::= "<" tagName (whitespace)* "/>" | "<" tagName (whitespace)* ">" message "</" tagName (whitespace)* ">"
* tagName ::= [a-z] (PENChar)*
* PENChar ::=
* "-" | "." | [0-9] | "_" | [a-z] | [A-Z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] |
* [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] |
* [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
* ```
*
* [custom element name]: https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
* NOTE: We're a bit more lax here since HTML technically does not allow uppercase HTML element but we do
* since other tag-based engines like React allow it
*/
private parseTag;
/**
* This method assumes that the caller has peeked ahead for the first tag character.
*/
private parseTagName;
private parseLiteral;
tryParseLeftAngleBracket(): string | null;
/**
* Starting with ICU 4.8, an ASCII apostrophe only starts quoted text if it immediately precedes
* a character that requires quoting (that is, "only where needed"), and works the same in
* nested messages as on the top level of the pattern. The new behavior is otherwise compatible.
*/
private tryParseQuote;
private tryParseUnquoted;
private parseArgument;
/**
* Advance the parser until the end of the identifier, if it is currently on
* an identifier character. Return an empty string otherwise.
*/
private parseIdentifierIfPossible;
private parseArgumentOptions;
private tryParseArgumentClose;
/**
* See: https://github.com/unicode-org/icu/blob/af7ed1f6d2298013dc303628438ec4abe1f16479/icu4c/source/common/messagepattern.cpp#L659
*/
private parseSimpleArgStyleIfPossible;
private parseNumberSkeletonFromString;
/**
* @param nesting_level The current nesting level of messages.
* This can be positive when parsing message fragment in select or plural argument options.
* @param parent_arg_type The parent argument's type.
* @param parsed_first_identifier If provided, this is the first identifier-like selector of
* the argument. It is a by-product of a previous parsing attempt.
* @param expecting_close_tag If true, this message is directly or indirectly nested inside
* between a pair of opening and closing tags. The nested message will not parse beyond
* the closing tag boundary.
*/
private tryParsePluralOrSelectOptions;
private tryParseDecimalInteger;
private offset;
private isEOF;
private clonePosition;
/**
* Return the code point at the current position of the parser.
* Throws if the index is out of bound.
*/
private char;
private error;
/** Bump the parser to the next UTF-16 code unit. */
private bump;
/**
* If the substring starting at the current position of the parser has
* the given prefix, then bump the parser to the character immediately
* following the prefix and return true. Otherwise, don't bump the parser
* and return false.
*/
private bumpIf;
/**
* Bump the parser until the pattern character is found and return `true`.
* Otherwise bump to the end of the file and return `false`.
*/
private bumpUntil;
/**
* Bump the parser to the target offset.
* If target offset is beyond the end of the input, bump the parser to the end of the input.
*/
private bumpTo;
/** advance the parser through all whitespace to the next non-whitespace code unit. */
private bumpSpace;
/**
* Peek at the *next* Unicode codepoint in the input without advancing the parser.
* If the input has been exhausted, then this returns null.
*/
private peek;
}
+839
-900

@@ -1,918 +0,857 @@

import { __assign } from "tslib";
import { ErrorKind } from './error.js';
import { SKELETON_TYPE, TYPE, } from './types.js';
import { SPACE_SEPARATOR_REGEX } from './regex.generated.js';
import { parseNumberSkeleton, parseNumberSkeletonFromString, parseDateTimeSkeleton, } from '@formatjs/icu-skeleton-parser';
import { getBestPattern } from './date-time-pattern-generator.js';
var SPACE_SEPARATOR_START_REGEX = new RegExp("^".concat(SPACE_SEPARATOR_REGEX.source, "*"));
var SPACE_SEPARATOR_END_REGEX = new RegExp("".concat(SPACE_SEPARATOR_REGEX.source, "*$"));
import { ErrorKind } from "./error.js";
import { SKELETON_TYPE, TYPE } from "./types.js";
import { SPACE_SEPARATOR_REGEX } from "./regex.generated.js";
import { parseNumberSkeleton, parseNumberSkeletonFromString, parseDateTimeSkeleton } from "@formatjs/icu-skeleton-parser";
import { getBestPattern } from "./date-time-pattern-generator.js";
const SPACE_SEPARATOR_START_REGEX = new RegExp(`^${SPACE_SEPARATOR_REGEX.source}*`);
const SPACE_SEPARATOR_END_REGEX = new RegExp(`${SPACE_SEPARATOR_REGEX.source}*$`);
function createLocation(start, end) {
return { start: start, end: end };
return {
start,
end
};
}
// #region Ponyfills
// Consolidate these variables up top for easier toggling during debugging
var hasNativeFromEntries = !!Object.fromEntries;
var hasTrimStart = !!String.prototype.trimStart;
var hasTrimEnd = !!String.prototype.trimEnd;
var fromEntries =
// native
hasNativeFromEntries
? Object.fromEntries
: // Ponyfill
function fromEntries(entries) {
var obj = {};
for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
var _a = entries_1[_i], k = _a[0], v = _a[1];
obj[k] = v;
}
return obj;
};
var trimStart = hasTrimStart
? // Native
function trimStart(s) {
return s.trimStart();
}
: // Ponyfill
function trimStart(s) {
return s.replace(SPACE_SEPARATOR_START_REGEX, '');
};
var trimEnd = hasTrimEnd
? // Native
function trimEnd(s) {
return s.trimEnd();
}
: // Ponyfill
function trimEnd(s) {
return s.replace(SPACE_SEPARATOR_END_REGEX, '');
};
const hasNativeFromEntries = !!Object.fromEntries;
const hasTrimStart = !!String.prototype.trimStart;
const hasTrimEnd = !!String.prototype.trimEnd;
const fromEntries = hasNativeFromEntries ? Object.fromEntries : function fromEntries(entries) {
const obj = {};
for (const [k, v] of entries) {
obj[k] = v;
}
return obj;
};
const trimStart = hasTrimStart ? function trimStart(s) {
return s.trimStart();
} : function trimStart(s) {
return s.replace(SPACE_SEPARATOR_START_REGEX, "");
};
const trimEnd = hasTrimEnd ? function trimEnd(s) {
return s.trimEnd();
} : function trimEnd(s) {
return s.replace(SPACE_SEPARATOR_END_REGEX, "");
};
// #endregion
var IDENTIFIER_PREFIX_RE = new RegExp('([^\\p{White_Space}\\p{Pattern_Syntax}]*)', 'yu');
const IDENTIFIER_PREFIX_RE = new RegExp("([^\\p{White_Space}\\p{Pattern_Syntax}]*)", "yu");
function matchIdentifierAtIndex(s, index) {
var _a;
IDENTIFIER_PREFIX_RE.lastIndex = index;
var match = IDENTIFIER_PREFIX_RE.exec(s);
return (_a = match[1]) !== null && _a !== void 0 ? _a : '';
IDENTIFIER_PREFIX_RE.lastIndex = index;
const match = IDENTIFIER_PREFIX_RE.exec(s);
return match[1] ?? "";
}
var Parser = /** @class */ (function () {
function Parser(message, options) {
if (options === void 0) { options = {}; }
this.message = message;
this.position = { offset: 0, line: 1, column: 1 };
this.ignoreTag = !!options.ignoreTag;
this.locale = options.locale;
this.requiresOtherClause = !!options.requiresOtherClause;
this.shouldParseSkeletons = !!options.shouldParseSkeletons;
}
Parser.prototype.parse = function () {
if (this.offset() !== 0) {
throw Error('parser can only be used once');
}
return this.parseMessage(0, '', false);
};
Parser.prototype.parseMessage = function (nestingLevel, parentArgType, expectingCloseTag) {
var elements = [];
while (!this.isEOF()) {
var char = this.char();
if (char === 123 /* `{` */) {
var result = this.parseArgument(nestingLevel, expectingCloseTag);
if (result.err) {
return result;
}
elements.push(result.val);
}
else if (char === 125 /* `}` */ && nestingLevel > 0) {
break;
}
else if (char === 35 /* `#` */ &&
(parentArgType === 'plural' || parentArgType === 'selectordinal')) {
var position = this.clonePosition();
this.bump();
elements.push({
type: TYPE.pound,
location: createLocation(position, this.clonePosition()),
});
}
else if (char === 60 /* `<` */ &&
!this.ignoreTag &&
this.peek() === 47 // char code for '/'
) {
if (expectingCloseTag) {
break;
}
else {
return this.error(ErrorKind.UNMATCHED_CLOSING_TAG, createLocation(this.clonePosition(), this.clonePosition()));
}
}
else if (char === 60 /* `<` */ &&
!this.ignoreTag &&
_isAlpha(this.peek() || 0)) {
var result = this.parseTag(nestingLevel, parentArgType);
if (result.err) {
return result;
}
elements.push(result.val);
}
else {
var result = this.parseLiteral(nestingLevel, parentArgType);
if (result.err) {
return result;
}
elements.push(result.val);
}
}
return { val: elements, err: null };
};
/**
* A tag name must start with an ASCII lower/upper case letter. The grammar is based on the
* [custom element name][] except that a dash is NOT always mandatory and uppercase letters
* are accepted:
*
* ```
* tag ::= "<" tagName (whitespace)* "/>" | "<" tagName (whitespace)* ">" message "</" tagName (whitespace)* ">"
* tagName ::= [a-z] (PENChar)*
* PENChar ::=
* "-" | "." | [0-9] | "_" | [a-z] | [A-Z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] |
* [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] |
* [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
* ```
*
* [custom element name]: https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
* NOTE: We're a bit more lax here since HTML technically does not allow uppercase HTML element but we do
* since other tag-based engines like React allow it
*/
Parser.prototype.parseTag = function (nestingLevel, parentArgType) {
var startPosition = this.clonePosition();
this.bump(); // `<`
var tagName = this.parseTagName();
this.bumpSpace();
if (this.bumpIf('/>')) {
// Self closing tag
return {
val: {
type: TYPE.literal,
value: "<".concat(tagName, "/>"),
location: createLocation(startPosition, this.clonePosition()),
},
err: null,
};
}
else if (this.bumpIf('>')) {
var childrenResult = this.parseMessage(nestingLevel + 1, parentArgType, true);
if (childrenResult.err) {
return childrenResult;
}
var children = childrenResult.val;
// Expecting a close tag
var endTagStartPosition = this.clonePosition();
if (this.bumpIf('</')) {
if (this.isEOF() || !_isAlpha(this.char())) {
return this.error(ErrorKind.INVALID_TAG, createLocation(endTagStartPosition, this.clonePosition()));
}
var closingTagNameStartPosition = this.clonePosition();
var closingTagName = this.parseTagName();
if (tagName !== closingTagName) {
return this.error(ErrorKind.UNMATCHED_CLOSING_TAG, createLocation(closingTagNameStartPosition, this.clonePosition()));
}
this.bumpSpace();
if (!this.bumpIf('>')) {
return this.error(ErrorKind.INVALID_TAG, createLocation(endTagStartPosition, this.clonePosition()));
}
return {
val: {
type: TYPE.tag,
value: tagName,
children: children,
location: createLocation(startPosition, this.clonePosition()),
},
err: null,
};
}
else {
return this.error(ErrorKind.UNCLOSED_TAG, createLocation(startPosition, this.clonePosition()));
}
}
else {
return this.error(ErrorKind.INVALID_TAG, createLocation(startPosition, this.clonePosition()));
}
};
/**
* This method assumes that the caller has peeked ahead for the first tag character.
*/
Parser.prototype.parseTagName = function () {
var startOffset = this.offset();
this.bump(); // the first tag name character
while (!this.isEOF() && _isPotentialElementNameChar(this.char())) {
this.bump();
}
return this.message.slice(startOffset, this.offset());
};
Parser.prototype.parseLiteral = function (nestingLevel, parentArgType) {
var start = this.clonePosition();
var value = '';
while (true) {
var parseQuoteResult = this.tryParseQuote(parentArgType);
if (parseQuoteResult) {
value += parseQuoteResult;
continue;
}
var parseUnquotedResult = this.tryParseUnquoted(nestingLevel, parentArgType);
if (parseUnquotedResult) {
value += parseUnquotedResult;
continue;
}
var parseLeftAngleResult = this.tryParseLeftAngleBracket();
if (parseLeftAngleResult) {
value += parseLeftAngleResult;
continue;
}
break;
}
var location = createLocation(start, this.clonePosition());
return {
val: { type: TYPE.literal, value: value, location: location },
err: null,
};
};
Parser.prototype.tryParseLeftAngleBracket = function () {
if (!this.isEOF() &&
this.char() === 60 /* `<` */ &&
(this.ignoreTag ||
// If at the opening tag or closing tag position, bail.
!_isAlphaOrSlash(this.peek() || 0))) {
this.bump(); // `<`
return '<';
}
return null;
};
/**
* Starting with ICU 4.8, an ASCII apostrophe only starts quoted text if it immediately precedes
* a character that requires quoting (that is, "only where needed"), and works the same in
* nested messages as on the top level of the pattern. The new behavior is otherwise compatible.
*/
Parser.prototype.tryParseQuote = function (parentArgType) {
if (this.isEOF() || this.char() !== 39 /* `'` */) {
return null;
}
// Parse escaped char following the apostrophe, or early return if there is no escaped char.
// Check if is valid escaped character
switch (this.peek()) {
case 39 /* `'` */:
// double quote, should return as a single quote.
this.bump();
this.bump();
return "'";
// '{', '<', '>', '}'
case 123:
case 60:
case 62:
case 125:
break;
case 35: // '#'
if (parentArgType === 'plural' || parentArgType === 'selectordinal') {
break;
}
return null;
default:
return null;
}
this.bump(); // apostrophe
var codePoints = [this.char()]; // escaped char
this.bump();
// read chars until the optional closing apostrophe is found
while (!this.isEOF()) {
var ch = this.char();
if (ch === 39 /* `'` */) {
if (this.peek() === 39 /* `'` */) {
codePoints.push(39);
// Bump one more time because we need to skip 2 characters.
this.bump();
}
else {
// Optional closing apostrophe.
this.bump();
break;
}
}
else {
codePoints.push(ch);
}
this.bump();
}
return String.fromCodePoint.apply(String, codePoints);
};
Parser.prototype.tryParseUnquoted = function (nestingLevel, parentArgType) {
if (this.isEOF()) {
return null;
}
var ch = this.char();
if (ch === 60 /* `<` */ ||
ch === 123 /* `{` */ ||
(ch === 35 /* `#` */ &&
(parentArgType === 'plural' || parentArgType === 'selectordinal')) ||
(ch === 125 /* `}` */ && nestingLevel > 0)) {
return null;
}
else {
this.bump();
return String.fromCodePoint(ch);
}
};
Parser.prototype.parseArgument = function (nestingLevel, expectingCloseTag) {
var openingBracePosition = this.clonePosition();
this.bump(); // `{`
this.bumpSpace();
if (this.isEOF()) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
if (this.char() === 125 /* `}` */) {
this.bump();
return this.error(ErrorKind.EMPTY_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
}
// argument name
var value = this.parseIdentifierIfPossible().value;
if (!value) {
return this.error(ErrorKind.MALFORMED_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
}
this.bumpSpace();
if (this.isEOF()) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
switch (this.char()) {
// Simple argument: `{name}`
case 125 /* `}` */: {
this.bump(); // `}`
return {
val: {
type: TYPE.argument,
// value does not include the opening and closing braces.
value: value,
location: createLocation(openingBracePosition, this.clonePosition()),
},
err: null,
};
}
// Argument with options: `{name, format, ...}`
case 44 /* `,` */: {
this.bump(); // `,`
this.bumpSpace();
if (this.isEOF()) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
return this.parseArgumentOptions(nestingLevel, expectingCloseTag, value, openingBracePosition);
}
default:
return this.error(ErrorKind.MALFORMED_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
}
};
/**
* Advance the parser until the end of the identifier, if it is currently on
* an identifier character. Return an empty string otherwise.
*/
Parser.prototype.parseIdentifierIfPossible = function () {
var startingPosition = this.clonePosition();
var startOffset = this.offset();
var value = matchIdentifierAtIndex(this.message, startOffset);
var endOffset = startOffset + value.length;
this.bumpTo(endOffset);
var endPosition = this.clonePosition();
var location = createLocation(startingPosition, endPosition);
return { value: value, location: location };
};
Parser.prototype.parseArgumentOptions = function (nestingLevel, expectingCloseTag, value, openingBracePosition) {
var _a;
// Parse this range:
// {name, type, style}
// ^---^
var typeStartPosition = this.clonePosition();
var argType = this.parseIdentifierIfPossible().value;
var typeEndPosition = this.clonePosition();
switch (argType) {
case '':
// Expecting a style string number, date, time, plural, selectordinal, or select.
return this.error(ErrorKind.EXPECT_ARGUMENT_TYPE, createLocation(typeStartPosition, typeEndPosition));
case 'number':
case 'date':
case 'time': {
// Parse this range:
// {name, number, style}
// ^-------^
this.bumpSpace();
var styleAndLocation = null;
if (this.bumpIf(',')) {
this.bumpSpace();
var styleStartPosition = this.clonePosition();
var result = this.parseSimpleArgStyleIfPossible();
if (result.err) {
return result;
}
var style = trimEnd(result.val);
if (style.length === 0) {
return this.error(ErrorKind.EXPECT_ARGUMENT_STYLE, createLocation(this.clonePosition(), this.clonePosition()));
}
var styleLocation = createLocation(styleStartPosition, this.clonePosition());
styleAndLocation = { style: style, styleLocation: styleLocation };
}
var argCloseResult = this.tryParseArgumentClose(openingBracePosition);
if (argCloseResult.err) {
return argCloseResult;
}
var location_1 = createLocation(openingBracePosition, this.clonePosition());
// Extract style or skeleton
if (styleAndLocation && styleAndLocation.style.startsWith('::')) {
// Skeleton starts with `::`.
var skeleton = trimStart(styleAndLocation.style.slice(2));
if (argType === 'number') {
var result = this.parseNumberSkeletonFromString(skeleton, styleAndLocation.styleLocation);
if (result.err) {
return result;
}
return {
val: { type: TYPE.number, value: value, location: location_1, style: result.val },
err: null,
};
}
else {
if (skeleton.length === 0) {
return this.error(ErrorKind.EXPECT_DATE_TIME_SKELETON, location_1);
}
var dateTimePattern = skeleton;
// Get "best match" pattern only if locale is passed, if not, let it
// pass as-is where `parseDateTimeSkeleton()` will throw an error
// for unsupported patterns.
if (this.locale) {
dateTimePattern = getBestPattern(skeleton, this.locale);
}
var style = {
type: SKELETON_TYPE.dateTime,
pattern: dateTimePattern,
location: styleAndLocation.styleLocation,
parsedOptions: this.shouldParseSkeletons
? parseDateTimeSkeleton(dateTimePattern)
: {},
};
var type = argType === 'date' ? TYPE.date : TYPE.time;
return {
val: { type: type, value: value, location: location_1, style: style },
err: null,
};
}
}
// Regular style or no style.
return {
val: {
type: argType === 'number'
? TYPE.number
: argType === 'date'
? TYPE.date
: TYPE.time,
value: value,
location: location_1,
style: (_a = styleAndLocation === null || styleAndLocation === void 0 ? void 0 : styleAndLocation.style) !== null && _a !== void 0 ? _a : null,
},
err: null,
};
}
case 'plural':
case 'selectordinal':
case 'select': {
// Parse this range:
// {name, plural, options}
// ^---------^
var typeEndPosition_1 = this.clonePosition();
this.bumpSpace();
if (!this.bumpIf(',')) {
return this.error(ErrorKind.EXPECT_SELECT_ARGUMENT_OPTIONS, createLocation(typeEndPosition_1, __assign({}, typeEndPosition_1)));
}
this.bumpSpace();
// Parse offset:
// {name, plural, offset:1, options}
// ^-----^
//
// or the first option:
//
// {name, plural, one {...} other {...}}
// ^--^
var identifierAndLocation = this.parseIdentifierIfPossible();
var pluralOffset = 0;
if (argType !== 'select' && identifierAndLocation.value === 'offset') {
if (!this.bumpIf(':')) {
return this.error(ErrorKind.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE, createLocation(this.clonePosition(), this.clonePosition()));
}
this.bumpSpace();
var result = this.tryParseDecimalInteger(ErrorKind.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE, ErrorKind.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);
if (result.err) {
return result;
}
// Parse another identifier for option parsing
this.bumpSpace();
identifierAndLocation = this.parseIdentifierIfPossible();
pluralOffset = result.val;
}
var optionsResult = this.tryParsePluralOrSelectOptions(nestingLevel, argType, expectingCloseTag, identifierAndLocation);
if (optionsResult.err) {
return optionsResult;
}
var argCloseResult = this.tryParseArgumentClose(openingBracePosition);
if (argCloseResult.err) {
return argCloseResult;
}
var location_2 = createLocation(openingBracePosition, this.clonePosition());
if (argType === 'select') {
return {
val: {
type: TYPE.select,
value: value,
options: fromEntries(optionsResult.val),
location: location_2,
},
err: null,
};
}
else {
return {
val: {
type: TYPE.plural,
value: value,
options: fromEntries(optionsResult.val),
offset: pluralOffset,
pluralType: argType === 'plural' ? 'cardinal' : 'ordinal',
location: location_2,
},
err: null,
};
}
}
default:
return this.error(ErrorKind.INVALID_ARGUMENT_TYPE, createLocation(typeStartPosition, typeEndPosition));
}
};
Parser.prototype.tryParseArgumentClose = function (openingBracePosition) {
// Parse: {value, number, ::currency/GBP }
//
if (this.isEOF() || this.char() !== 125 /* `}` */) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
this.bump(); // `}`
return { val: true, err: null };
};
/**
* See: https://github.com/unicode-org/icu/blob/af7ed1f6d2298013dc303628438ec4abe1f16479/icu4c/source/common/messagepattern.cpp#L659
*/
Parser.prototype.parseSimpleArgStyleIfPossible = function () {
var nestedBraces = 0;
var startPosition = this.clonePosition();
while (!this.isEOF()) {
var ch = this.char();
switch (ch) {
case 39 /* `'` */: {
// Treat apostrophe as quoting but include it in the style part.
// Find the end of the quoted literal text.
this.bump();
var apostrophePosition = this.clonePosition();
if (!this.bumpUntil("'")) {
return this.error(ErrorKind.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE, createLocation(apostrophePosition, this.clonePosition()));
}
this.bump();
break;
}
case 123 /* `{` */: {
nestedBraces += 1;
this.bump();
break;
}
case 125 /* `}` */: {
if (nestedBraces > 0) {
nestedBraces -= 1;
}
else {
return {
val: this.message.slice(startPosition.offset, this.offset()),
err: null,
};
}
break;
}
default:
this.bump();
break;
}
}
return {
val: this.message.slice(startPosition.offset, this.offset()),
err: null,
};
};
Parser.prototype.parseNumberSkeletonFromString = function (skeleton, location) {
var tokens = [];
try {
tokens = parseNumberSkeletonFromString(skeleton);
}
catch (_a) {
return this.error(ErrorKind.INVALID_NUMBER_SKELETON, location);
}
return {
val: {
type: SKELETON_TYPE.number,
tokens: tokens,
location: location,
parsedOptions: this.shouldParseSkeletons
? parseNumberSkeleton(tokens)
: {},
},
err: null,
};
};
/**
* @param nesting_level The current nesting level of messages.
* This can be positive when parsing message fragment in select or plural argument options.
* @param parent_arg_type The parent argument's type.
* @param parsed_first_identifier If provided, this is the first identifier-like selector of
* the argument. It is a by-product of a previous parsing attempt.
* @param expecting_close_tag If true, this message is directly or indirectly nested inside
* between a pair of opening and closing tags. The nested message will not parse beyond
* the closing tag boundary.
*/
Parser.prototype.tryParsePluralOrSelectOptions = function (nestingLevel, parentArgType, expectCloseTag, parsedFirstIdentifier) {
var _a;
var hasOtherClause = false;
var options = [];
var parsedSelectors = new Set();
var selector = parsedFirstIdentifier.value, selectorLocation = parsedFirstIdentifier.location;
// Parse:
// one {one apple}
// ^--^
while (true) {
if (selector.length === 0) {
var startPosition = this.clonePosition();
if (parentArgType !== 'select' && this.bumpIf('=')) {
// Try parse `={number}` selector
var result = this.tryParseDecimalInteger(ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR, ErrorKind.INVALID_PLURAL_ARGUMENT_SELECTOR);
if (result.err) {
return result;
}
selectorLocation = createLocation(startPosition, this.clonePosition());
selector = this.message.slice(startPosition.offset, this.offset());
}
else {
break;
}
}
// Duplicate selector clauses
if (parsedSelectors.has(selector)) {
return this.error(parentArgType === 'select'
? ErrorKind.DUPLICATE_SELECT_ARGUMENT_SELECTOR
: ErrorKind.DUPLICATE_PLURAL_ARGUMENT_SELECTOR, selectorLocation);
}
if (selector === 'other') {
hasOtherClause = true;
}
// Parse:
// one {one apple}
// ^----------^
this.bumpSpace();
var openingBracePosition = this.clonePosition();
if (!this.bumpIf('{')) {
return this.error(parentArgType === 'select'
? ErrorKind.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT
: ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT, createLocation(this.clonePosition(), this.clonePosition()));
}
var fragmentResult = this.parseMessage(nestingLevel + 1, parentArgType, expectCloseTag);
if (fragmentResult.err) {
return fragmentResult;
}
var argCloseResult = this.tryParseArgumentClose(openingBracePosition);
if (argCloseResult.err) {
return argCloseResult;
}
options.push([
selector,
{
value: fragmentResult.val,
location: createLocation(openingBracePosition, this.clonePosition()),
},
]);
// Keep track of the existing selectors
parsedSelectors.add(selector);
// Prep next selector clause.
this.bumpSpace();
(_a = this.parseIdentifierIfPossible(), selector = _a.value, selectorLocation = _a.location);
}
if (options.length === 0) {
return this.error(parentArgType === 'select'
? ErrorKind.EXPECT_SELECT_ARGUMENT_SELECTOR
: ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR, createLocation(this.clonePosition(), this.clonePosition()));
}
if (this.requiresOtherClause && !hasOtherClause) {
return this.error(ErrorKind.MISSING_OTHER_CLAUSE, createLocation(this.clonePosition(), this.clonePosition()));
}
return { val: options, err: null };
};
Parser.prototype.tryParseDecimalInteger = function (expectNumberError, invalidNumberError) {
var sign = 1;
var startingPosition = this.clonePosition();
if (this.bumpIf('+')) {
}
else if (this.bumpIf('-')) {
sign = -1;
}
var hasDigits = false;
var decimal = 0;
while (!this.isEOF()) {
var ch = this.char();
if (ch >= 48 /* `0` */ && ch <= 57 /* `9` */) {
hasDigits = true;
decimal = decimal * 10 + (ch - 48);
this.bump();
}
else {
break;
}
}
var location = createLocation(startingPosition, this.clonePosition());
if (!hasDigits) {
return this.error(expectNumberError, location);
}
decimal *= sign;
if (!Number.isSafeInteger(decimal)) {
return this.error(invalidNumberError, location);
}
return { val: decimal, err: null };
};
Parser.prototype.offset = function () {
return this.position.offset;
};
Parser.prototype.isEOF = function () {
return this.offset() === this.message.length;
};
Parser.prototype.clonePosition = function () {
// This is much faster than `Object.assign` or spread.
return {
offset: this.position.offset,
line: this.position.line,
column: this.position.column,
};
};
/**
* Return the code point at the current position of the parser.
* Throws if the index is out of bound.
*/
Parser.prototype.char = function () {
var offset = this.position.offset;
if (offset >= this.message.length) {
throw Error('out of bound');
}
var code = this.message.codePointAt(offset);
if (code === undefined) {
throw Error("Offset ".concat(offset, " is at invalid UTF-16 code unit boundary"));
}
return code;
};
Parser.prototype.error = function (kind, location) {
return {
val: null,
err: {
kind: kind,
message: this.message,
location: location,
},
};
};
/** Bump the parser to the next UTF-16 code unit. */
Parser.prototype.bump = function () {
if (this.isEOF()) {
return;
}
var code = this.char();
if (code === 10 /* '\n' */) {
this.position.line += 1;
this.position.column = 1;
this.position.offset += 1;
}
else {
this.position.column += 1;
// 0 ~ 0x10000 -> unicode BMP, otherwise skip the surrogate pair.
this.position.offset += code < 0x10000 ? 1 : 2;
}
};
/**
* If the substring starting at the current position of the parser has
* the given prefix, then bump the parser to the character immediately
* following the prefix and return true. Otherwise, don't bump the parser
* and return false.
*/
Parser.prototype.bumpIf = function (prefix) {
if (this.message.startsWith(prefix, this.offset())) {
for (var i = 0; i < prefix.length; i++) {
this.bump();
}
return true;
}
return false;
};
/**
* Bump the parser until the pattern character is found and return `true`.
* Otherwise bump to the end of the file and return `false`.
*/
Parser.prototype.bumpUntil = function (pattern) {
var currentOffset = this.offset();
var index = this.message.indexOf(pattern, currentOffset);
if (index >= 0) {
this.bumpTo(index);
return true;
}
else {
this.bumpTo(this.message.length);
return false;
}
};
/**
* Bump the parser to the target offset.
* If target offset is beyond the end of the input, bump the parser to the end of the input.
*/
Parser.prototype.bumpTo = function (targetOffset) {
if (this.offset() > targetOffset) {
throw Error("targetOffset ".concat(targetOffset, " must be greater than or equal to the current offset ").concat(this.offset()));
}
targetOffset = Math.min(targetOffset, this.message.length);
while (true) {
var offset = this.offset();
if (offset === targetOffset) {
break;
}
if (offset > targetOffset) {
throw Error("targetOffset ".concat(targetOffset, " is at invalid UTF-16 code unit boundary"));
}
this.bump();
if (this.isEOF()) {
break;
}
}
};
/** advance the parser through all whitespace to the next non-whitespace code unit. */
Parser.prototype.bumpSpace = function () {
while (!this.isEOF() && _isWhiteSpace(this.char())) {
this.bump();
}
};
/**
* Peek at the *next* Unicode codepoint in the input without advancing the parser.
* If the input has been exhausted, then this returns null.
*/
Parser.prototype.peek = function () {
if (this.isEOF()) {
return null;
}
var code = this.char();
var offset = this.offset();
var nextCode = this.message.charCodeAt(offset + (code >= 0x10000 ? 2 : 1));
return nextCode !== null && nextCode !== void 0 ? nextCode : null;
};
return Parser;
}());
export { Parser };
export class Parser {
message;
position;
locale;
ignoreTag;
requiresOtherClause;
shouldParseSkeletons;
constructor(message, options = {}) {
this.message = message;
this.position = {
offset: 0,
line: 1,
column: 1
};
this.ignoreTag = !!options.ignoreTag;
this.locale = options.locale;
this.requiresOtherClause = !!options.requiresOtherClause;
this.shouldParseSkeletons = !!options.shouldParseSkeletons;
}
parse() {
if (this.offset() !== 0) {
throw Error("parser can only be used once");
}
return this.parseMessage(0, "", false);
}
parseMessage(nestingLevel, parentArgType, expectingCloseTag) {
let elements = [];
while (!this.isEOF()) {
const char = this.char();
if (char === 123) {
const result = this.parseArgument(nestingLevel, expectingCloseTag);
if (result.err) {
return result;
}
elements.push(result.val);
} else if (char === 125 && nestingLevel > 0) {
break;
} else if (char === 35 && (parentArgType === "plural" || parentArgType === "selectordinal")) {
const position = this.clonePosition();
this.bump();
elements.push({
type: TYPE.pound,
location: createLocation(position, this.clonePosition())
});
} else if (char === 60 && !this.ignoreTag && this.peek() === 47) {
if (expectingCloseTag) {
break;
} else {
return this.error(ErrorKind.UNMATCHED_CLOSING_TAG, createLocation(this.clonePosition(), this.clonePosition()));
}
} else if (char === 60 && !this.ignoreTag && _isAlpha(this.peek() || 0)) {
const result = this.parseTag(nestingLevel, parentArgType);
if (result.err) {
return result;
}
elements.push(result.val);
} else {
const result = this.parseLiteral(nestingLevel, parentArgType);
if (result.err) {
return result;
}
elements.push(result.val);
}
}
return {
val: elements,
err: null
};
}
/**
* A tag name must start with an ASCII lower/upper case letter. The grammar is based on the
* [custom element name][] except that a dash is NOT always mandatory and uppercase letters
* are accepted:
*
* ```
* tag ::= "<" tagName (whitespace)* "/>" | "<" tagName (whitespace)* ">" message "</" tagName (whitespace)* ">"
* tagName ::= [a-z] (PENChar)*
* PENChar ::=
* "-" | "." | [0-9] | "_" | [a-z] | [A-Z] | #xB7 | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x37D] |
* [#x37F-#x1FFF] | [#x200C-#x200D] | [#x203F-#x2040] | [#x2070-#x218F] | [#x2C00-#x2FEF] |
* [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
* ```
*
* [custom element name]: https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name
* NOTE: We're a bit more lax here since HTML technically does not allow uppercase HTML element but we do
* since other tag-based engines like React allow it
*/
parseTag(nestingLevel, parentArgType) {
const startPosition = this.clonePosition();
this.bump();
const tagName = this.parseTagName();
this.bumpSpace();
if (this.bumpIf("/>")) {
// Self closing tag
return {
val: {
type: TYPE.literal,
value: `<${tagName}/>`,
location: createLocation(startPosition, this.clonePosition())
},
err: null
};
} else if (this.bumpIf(">")) {
const childrenResult = this.parseMessage(nestingLevel + 1, parentArgType, true);
if (childrenResult.err) {
return childrenResult;
}
const children = childrenResult.val;
// Expecting a close tag
const endTagStartPosition = this.clonePosition();
if (this.bumpIf("</")) {
if (this.isEOF() || !_isAlpha(this.char())) {
return this.error(ErrorKind.INVALID_TAG, createLocation(endTagStartPosition, this.clonePosition()));
}
const closingTagNameStartPosition = this.clonePosition();
const closingTagName = this.parseTagName();
if (tagName !== closingTagName) {
return this.error(ErrorKind.UNMATCHED_CLOSING_TAG, createLocation(closingTagNameStartPosition, this.clonePosition()));
}
this.bumpSpace();
if (!this.bumpIf(">")) {
return this.error(ErrorKind.INVALID_TAG, createLocation(endTagStartPosition, this.clonePosition()));
}
return {
val: {
type: TYPE.tag,
value: tagName,
children,
location: createLocation(startPosition, this.clonePosition())
},
err: null
};
} else {
return this.error(ErrorKind.UNCLOSED_TAG, createLocation(startPosition, this.clonePosition()));
}
} else {
return this.error(ErrorKind.INVALID_TAG, createLocation(startPosition, this.clonePosition()));
}
}
/**
* This method assumes that the caller has peeked ahead for the first tag character.
*/
parseTagName() {
const startOffset = this.offset();
this.bump();
while (!this.isEOF() && _isPotentialElementNameChar(this.char())) {
this.bump();
}
return this.message.slice(startOffset, this.offset());
}
parseLiteral(nestingLevel, parentArgType) {
const start = this.clonePosition();
let value = "";
while (true) {
const parseQuoteResult = this.tryParseQuote(parentArgType);
if (parseQuoteResult) {
value += parseQuoteResult;
continue;
}
const parseUnquotedResult = this.tryParseUnquoted(nestingLevel, parentArgType);
if (parseUnquotedResult) {
value += parseUnquotedResult;
continue;
}
const parseLeftAngleResult = this.tryParseLeftAngleBracket();
if (parseLeftAngleResult) {
value += parseLeftAngleResult;
continue;
}
break;
}
const location = createLocation(start, this.clonePosition());
return {
val: {
type: TYPE.literal,
value,
location
},
err: null
};
}
tryParseLeftAngleBracket() {
if (!this.isEOF() && this.char() === 60 && (this.ignoreTag || !_isAlphaOrSlash(this.peek() || 0))) {
this.bump();
return "<";
}
return null;
}
/**
* Starting with ICU 4.8, an ASCII apostrophe only starts quoted text if it immediately precedes
* a character that requires quoting (that is, "only where needed"), and works the same in
* nested messages as on the top level of the pattern. The new behavior is otherwise compatible.
*/
tryParseQuote(parentArgType) {
if (this.isEOF() || this.char() !== 39) {
return null;
}
// Parse escaped char following the apostrophe, or early return if there is no escaped char.
// Check if is valid escaped character
switch (this.peek()) {
case 39:
// double quote, should return as a single quote.
this.bump();
this.bump();
return "'";
case 123:
case 60:
case 62:
case 125: break;
case 35:
if (parentArgType === "plural" || parentArgType === "selectordinal") {
break;
}
return null;
default: return null;
}
this.bump();
const codePoints = [this.char()];
this.bump();
// read chars until the optional closing apostrophe is found
while (!this.isEOF()) {
const ch = this.char();
if (ch === 39) {
if (this.peek() === 39) {
codePoints.push(39);
// Bump one more time because we need to skip 2 characters.
this.bump();
} else {
// Optional closing apostrophe.
this.bump();
break;
}
} else {
codePoints.push(ch);
}
this.bump();
}
return String.fromCodePoint(...codePoints);
}
tryParseUnquoted(nestingLevel, parentArgType) {
if (this.isEOF()) {
return null;
}
const ch = this.char();
if (ch === 60 || ch === 123 || ch === 35 && (parentArgType === "plural" || parentArgType === "selectordinal") || ch === 125 && nestingLevel > 0) {
return null;
} else {
this.bump();
return String.fromCodePoint(ch);
}
}
parseArgument(nestingLevel, expectingCloseTag) {
const openingBracePosition = this.clonePosition();
this.bump();
this.bumpSpace();
if (this.isEOF()) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
if (this.char() === 125) {
this.bump();
return this.error(ErrorKind.EMPTY_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
}
// argument name
let value = this.parseIdentifierIfPossible().value;
if (!value) {
return this.error(ErrorKind.MALFORMED_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
}
this.bumpSpace();
if (this.isEOF()) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
switch (this.char()) {
case 125: {
this.bump();
return {
val: {
type: TYPE.argument,
value,
location: createLocation(openingBracePosition, this.clonePosition())
},
err: null
};
}
case 44: {
this.bump();
this.bumpSpace();
if (this.isEOF()) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
return this.parseArgumentOptions(nestingLevel, expectingCloseTag, value, openingBracePosition);
}
default: return this.error(ErrorKind.MALFORMED_ARGUMENT, createLocation(openingBracePosition, this.clonePosition()));
}
}
/**
* Advance the parser until the end of the identifier, if it is currently on
* an identifier character. Return an empty string otherwise.
*/
parseIdentifierIfPossible() {
const startingPosition = this.clonePosition();
const startOffset = this.offset();
const value = matchIdentifierAtIndex(this.message, startOffset);
const endOffset = startOffset + value.length;
this.bumpTo(endOffset);
const endPosition = this.clonePosition();
const location = createLocation(startingPosition, endPosition);
return {
value,
location
};
}
parseArgumentOptions(nestingLevel, expectingCloseTag, value, openingBracePosition) {
// Parse this range:
// {name, type, style}
// ^---^
let typeStartPosition = this.clonePosition();
let argType = this.parseIdentifierIfPossible().value;
let typeEndPosition = this.clonePosition();
switch (argType) {
case "":
// Expecting a style string number, date, time, plural, selectordinal, or select.
return this.error(ErrorKind.EXPECT_ARGUMENT_TYPE, createLocation(typeStartPosition, typeEndPosition));
case "number":
case "date":
case "time": {
// Parse this range:
// {name, number, style}
// ^-------^
this.bumpSpace();
let styleAndLocation = null;
if (this.bumpIf(",")) {
this.bumpSpace();
const styleStartPosition = this.clonePosition();
const result = this.parseSimpleArgStyleIfPossible();
if (result.err) {
return result;
}
const style = trimEnd(result.val);
if (style.length === 0) {
return this.error(ErrorKind.EXPECT_ARGUMENT_STYLE, createLocation(this.clonePosition(), this.clonePosition()));
}
const styleLocation = createLocation(styleStartPosition, this.clonePosition());
styleAndLocation = {
style,
styleLocation
};
}
const argCloseResult = this.tryParseArgumentClose(openingBracePosition);
if (argCloseResult.err) {
return argCloseResult;
}
const location = createLocation(openingBracePosition, this.clonePosition());
// Extract style or skeleton
if (styleAndLocation && styleAndLocation.style.startsWith("::")) {
// Skeleton starts with `::`.
let skeleton = trimStart(styleAndLocation.style.slice(2));
if (argType === "number") {
const result = this.parseNumberSkeletonFromString(skeleton, styleAndLocation.styleLocation);
if (result.err) {
return result;
}
return {
val: {
type: TYPE.number,
value,
location,
style: result.val
},
err: null
};
} else {
if (skeleton.length === 0) {
return this.error(ErrorKind.EXPECT_DATE_TIME_SKELETON, location);
}
let dateTimePattern = skeleton;
// Get "best match" pattern only if locale is passed, if not, let it
// pass as-is where `parseDateTimeSkeleton()` will throw an error
// for unsupported patterns.
if (this.locale) {
dateTimePattern = getBestPattern(skeleton, this.locale);
}
const style = {
type: SKELETON_TYPE.dateTime,
pattern: dateTimePattern,
location: styleAndLocation.styleLocation,
parsedOptions: this.shouldParseSkeletons ? parseDateTimeSkeleton(dateTimePattern) : {}
};
const type = argType === "date" ? TYPE.date : TYPE.time;
return {
val: {
type,
value,
location,
style
},
err: null
};
}
}
// Regular style or no style.
return {
val: {
type: argType === "number" ? TYPE.number : argType === "date" ? TYPE.date : TYPE.time,
value,
location,
style: styleAndLocation?.style ?? null
},
err: null
};
}
case "plural":
case "selectordinal":
case "select": {
// Parse this range:
// {name, plural, options}
// ^---------^
const typeEndPosition = this.clonePosition();
this.bumpSpace();
if (!this.bumpIf(",")) {
return this.error(ErrorKind.EXPECT_SELECT_ARGUMENT_OPTIONS, createLocation(typeEndPosition, { ...typeEndPosition }));
}
this.bumpSpace();
// Parse offset:
// {name, plural, offset:1, options}
// ^-----^
//
// or the first option:
//
// {name, plural, one {...} other {...}}
// ^--^
let identifierAndLocation = this.parseIdentifierIfPossible();
let pluralOffset = 0;
if (argType !== "select" && identifierAndLocation.value === "offset") {
if (!this.bumpIf(":")) {
return this.error(ErrorKind.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE, createLocation(this.clonePosition(), this.clonePosition()));
}
this.bumpSpace();
const result = this.tryParseDecimalInteger(ErrorKind.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE, ErrorKind.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE);
if (result.err) {
return result;
}
// Parse another identifier for option parsing
this.bumpSpace();
identifierAndLocation = this.parseIdentifierIfPossible();
pluralOffset = result.val;
}
const optionsResult = this.tryParsePluralOrSelectOptions(nestingLevel, argType, expectingCloseTag, identifierAndLocation);
if (optionsResult.err) {
return optionsResult;
}
const argCloseResult = this.tryParseArgumentClose(openingBracePosition);
if (argCloseResult.err) {
return argCloseResult;
}
const location = createLocation(openingBracePosition, this.clonePosition());
if (argType === "select") {
return {
val: {
type: TYPE.select,
value,
options: fromEntries(optionsResult.val),
location
},
err: null
};
} else {
return {
val: {
type: TYPE.plural,
value,
options: fromEntries(optionsResult.val),
offset: pluralOffset,
pluralType: argType === "plural" ? "cardinal" : "ordinal",
location
},
err: null
};
}
}
default: return this.error(ErrorKind.INVALID_ARGUMENT_TYPE, createLocation(typeStartPosition, typeEndPosition));
}
}
tryParseArgumentClose(openingBracePosition) {
// Parse: {value, number, ::currency/GBP }
//
if (this.isEOF() || this.char() !== 125) {
return this.error(ErrorKind.EXPECT_ARGUMENT_CLOSING_BRACE, createLocation(openingBracePosition, this.clonePosition()));
}
this.bump();
return {
val: true,
err: null
};
}
/**
* See: https://github.com/unicode-org/icu/blob/af7ed1f6d2298013dc303628438ec4abe1f16479/icu4c/source/common/messagepattern.cpp#L659
*/
parseSimpleArgStyleIfPossible() {
let nestedBraces = 0;
const startPosition = this.clonePosition();
while (!this.isEOF()) {
const ch = this.char();
switch (ch) {
case 39: {
// Treat apostrophe as quoting but include it in the style part.
// Find the end of the quoted literal text.
this.bump();
let apostrophePosition = this.clonePosition();
if (!this.bumpUntil("'")) {
return this.error(ErrorKind.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE, createLocation(apostrophePosition, this.clonePosition()));
}
this.bump();
break;
}
case 123: {
nestedBraces += 1;
this.bump();
break;
}
case 125: {
if (nestedBraces > 0) {
nestedBraces -= 1;
} else {
return {
val: this.message.slice(startPosition.offset, this.offset()),
err: null
};
}
break;
}
default:
this.bump();
break;
}
}
return {
val: this.message.slice(startPosition.offset, this.offset()),
err: null
};
}
parseNumberSkeletonFromString(skeleton, location) {
let tokens = [];
try {
tokens = parseNumberSkeletonFromString(skeleton);
} catch {
return this.error(ErrorKind.INVALID_NUMBER_SKELETON, location);
}
return {
val: {
type: SKELETON_TYPE.number,
tokens,
location,
parsedOptions: this.shouldParseSkeletons ? parseNumberSkeleton(tokens) : {}
},
err: null
};
}
/**
* @param nesting_level The current nesting level of messages.
* This can be positive when parsing message fragment in select or plural argument options.
* @param parent_arg_type The parent argument's type.
* @param parsed_first_identifier If provided, this is the first identifier-like selector of
* the argument. It is a by-product of a previous parsing attempt.
* @param expecting_close_tag If true, this message is directly or indirectly nested inside
* between a pair of opening and closing tags. The nested message will not parse beyond
* the closing tag boundary.
*/
tryParsePluralOrSelectOptions(nestingLevel, parentArgType, expectCloseTag, parsedFirstIdentifier) {
let hasOtherClause = false;
const options = [];
const parsedSelectors = new Set();
let { value: selector, location: selectorLocation } = parsedFirstIdentifier;
// Parse:
// one {one apple}
// ^--^
while (true) {
if (selector.length === 0) {
const startPosition = this.clonePosition();
if (parentArgType !== "select" && this.bumpIf("=")) {
// Try parse `={number}` selector
const result = this.tryParseDecimalInteger(ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR, ErrorKind.INVALID_PLURAL_ARGUMENT_SELECTOR);
if (result.err) {
return result;
}
selectorLocation = createLocation(startPosition, this.clonePosition());
selector = this.message.slice(startPosition.offset, this.offset());
} else {
break;
}
}
// Duplicate selector clauses
if (parsedSelectors.has(selector)) {
return this.error(parentArgType === "select" ? ErrorKind.DUPLICATE_SELECT_ARGUMENT_SELECTOR : ErrorKind.DUPLICATE_PLURAL_ARGUMENT_SELECTOR, selectorLocation);
}
if (selector === "other") {
hasOtherClause = true;
}
// Parse:
// one {one apple}
// ^----------^
this.bumpSpace();
const openingBracePosition = this.clonePosition();
if (!this.bumpIf("{")) {
return this.error(parentArgType === "select" ? ErrorKind.EXPECT_SELECT_ARGUMENT_SELECTOR_FRAGMENT : ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR_FRAGMENT, createLocation(this.clonePosition(), this.clonePosition()));
}
const fragmentResult = this.parseMessage(nestingLevel + 1, parentArgType, expectCloseTag);
if (fragmentResult.err) {
return fragmentResult;
}
const argCloseResult = this.tryParseArgumentClose(openingBracePosition);
if (argCloseResult.err) {
return argCloseResult;
}
options.push([selector, {
value: fragmentResult.val,
location: createLocation(openingBracePosition, this.clonePosition())
}]);
// Keep track of the existing selectors
parsedSelectors.add(selector);
// Prep next selector clause.
this.bumpSpace();
({value: selector, location: selectorLocation} = this.parseIdentifierIfPossible());
}
if (options.length === 0) {
return this.error(parentArgType === "select" ? ErrorKind.EXPECT_SELECT_ARGUMENT_SELECTOR : ErrorKind.EXPECT_PLURAL_ARGUMENT_SELECTOR, createLocation(this.clonePosition(), this.clonePosition()));
}
if (this.requiresOtherClause && !hasOtherClause) {
return this.error(ErrorKind.MISSING_OTHER_CLAUSE, createLocation(this.clonePosition(), this.clonePosition()));
}
return {
val: options,
err: null
};
}
tryParseDecimalInteger(expectNumberError, invalidNumberError) {
let sign = 1;
const startingPosition = this.clonePosition();
if (this.bumpIf("+")) {} else if (this.bumpIf("-")) {
sign = -1;
}
let hasDigits = false;
let decimal = 0;
while (!this.isEOF()) {
const ch = this.char();
if (ch >= 48 && ch <= 57) {
hasDigits = true;
decimal = decimal * 10 + (ch - 48);
this.bump();
} else {
break;
}
}
const location = createLocation(startingPosition, this.clonePosition());
if (!hasDigits) {
return this.error(expectNumberError, location);
}
decimal *= sign;
if (!Number.isSafeInteger(decimal)) {
return this.error(invalidNumberError, location);
}
return {
val: decimal,
err: null
};
}
offset() {
return this.position.offset;
}
isEOF() {
return this.offset() === this.message.length;
}
clonePosition() {
// This is much faster than `Object.assign` or spread.
return {
offset: this.position.offset,
line: this.position.line,
column: this.position.column
};
}
/**
* Return the code point at the current position of the parser.
* Throws if the index is out of bound.
*/
char() {
const offset = this.position.offset;
if (offset >= this.message.length) {
throw Error("out of bound");
}
const code = this.message.codePointAt(offset);
if (code === undefined) {
throw Error(`Offset ${offset} is at invalid UTF-16 code unit boundary`);
}
return code;
}
error(kind, location) {
return {
val: null,
err: {
kind,
message: this.message,
location
}
};
}
/** Bump the parser to the next UTF-16 code unit. */
bump() {
if (this.isEOF()) {
return;
}
const code = this.char();
if (code === 10) {
this.position.line += 1;
this.position.column = 1;
this.position.offset += 1;
} else {
this.position.column += 1;
// 0 ~ 0x10000 -> unicode BMP, otherwise skip the surrogate pair.
this.position.offset += code < 65536 ? 1 : 2;
}
}
/**
* If the substring starting at the current position of the parser has
* the given prefix, then bump the parser to the character immediately
* following the prefix and return true. Otherwise, don't bump the parser
* and return false.
*/
bumpIf(prefix) {
if (this.message.startsWith(prefix, this.offset())) {
for (let i = 0; i < prefix.length; i++) {
this.bump();
}
return true;
}
return false;
}
/**
* Bump the parser until the pattern character is found and return `true`.
* Otherwise bump to the end of the file and return `false`.
*/
bumpUntil(pattern) {
const currentOffset = this.offset();
const index = this.message.indexOf(pattern, currentOffset);
if (index >= 0) {
this.bumpTo(index);
return true;
} else {
this.bumpTo(this.message.length);
return false;
}
}
/**
* Bump the parser to the target offset.
* If target offset is beyond the end of the input, bump the parser to the end of the input.
*/
bumpTo(targetOffset) {
if (this.offset() > targetOffset) {
throw Error(`targetOffset ${targetOffset} must be greater than or equal to the current offset ${this.offset()}`);
}
targetOffset = Math.min(targetOffset, this.message.length);
while (true) {
const offset = this.offset();
if (offset === targetOffset) {
break;
}
if (offset > targetOffset) {
throw Error(`targetOffset ${targetOffset} is at invalid UTF-16 code unit boundary`);
}
this.bump();
if (this.isEOF()) {
break;
}
}
}
/** advance the parser through all whitespace to the next non-whitespace code unit. */
bumpSpace() {
while (!this.isEOF() && _isWhiteSpace(this.char())) {
this.bump();
}
}
/**
* Peek at the *next* Unicode codepoint in the input without advancing the parser.
* If the input has been exhausted, then this returns null.
*/
peek() {
if (this.isEOF()) {
return null;
}
const code = this.char();
const offset = this.offset();
const nextCode = this.message.charCodeAt(offset + (code >= 65536 ? 2 : 1));
return nextCode ?? null;
}
}
/**
* This check if codepoint is alphabet (lower & uppercase)
* @param codepoint
* @returns
*/
* This check if codepoint is alphabet (lower & uppercase)
* @param codepoint
* @returns
*/
function _isAlpha(codepoint) {
return ((codepoint >= 97 && codepoint <= 122) ||
(codepoint >= 65 && codepoint <= 90));
return codepoint >= 97 && codepoint <= 122 || codepoint >= 65 && codepoint <= 90;
}
function _isAlphaOrSlash(codepoint) {
return _isAlpha(codepoint) || codepoint === 47; /* '/' */
return _isAlpha(codepoint) || codepoint === 47;
}
/** See `parseTag` function docs. */
function _isPotentialElementNameChar(c) {
return (c === 45 /* '-' */ ||
c === 46 /* '.' */ ||
(c >= 48 && c <= 57) /* 0..9 */ ||
c === 95 /* '_' */ ||
(c >= 97 && c <= 122) /** a..z */ ||
(c >= 65 && c <= 90) /* A..Z */ ||
c == 0xb7 ||
(c >= 0xc0 && c <= 0xd6) ||
(c >= 0xd8 && c <= 0xf6) ||
(c >= 0xf8 && c <= 0x37d) ||
(c >= 0x37f && c <= 0x1fff) ||
(c >= 0x200c && c <= 0x200d) ||
(c >= 0x203f && c <= 0x2040) ||
(c >= 0x2070 && c <= 0x218f) ||
(c >= 0x2c00 && c <= 0x2fef) ||
(c >= 0x3001 && c <= 0xd7ff) ||
(c >= 0xf900 && c <= 0xfdcf) ||
(c >= 0xfdf0 && c <= 0xfffd) ||
(c >= 0x10000 && c <= 0xeffff));
return c === 45 || c === 46 || c >= 48 && c <= 57 || c === 95 || c >= 97 && c <= 122 || c >= 65 && c <= 90 || c == 183 || c >= 192 && c <= 214 || c >= 216 && c <= 246 || c >= 248 && c <= 893 || c >= 895 && c <= 8191 || c >= 8204 && c <= 8205 || c >= 8255 && c <= 8256 || c >= 8304 && c <= 8591 || c >= 11264 && c <= 12271 || c >= 12289 && c <= 55295 || c >= 63744 && c <= 64975 || c >= 65008 && c <= 65533 || c >= 65536 && c <= 983039;
}
/**
* Code point equivalent of regex `\p{White_Space}`.
* From: https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
*/
* Code point equivalent of regex `\p{White_Space}`.
* From: https://www.unicode.org/Public/UCD/latest/ucd/PropList.txt
*/
function _isWhiteSpace(c) {
return ((c >= 0x0009 && c <= 0x000d) ||
c === 0x0020 ||
c === 0x0085 ||
(c >= 0x200e && c <= 0x200f) ||
c === 0x2028 ||
c === 0x2029);
return c >= 9 && c <= 13 || c === 32 || c === 133 || c >= 8206 && c <= 8207 || c === 8232 || c === 8233;
}

@@ -1,4 +0,4 @@

import { DateTimeSkeleton, MessageFormatElement } from './types.js';
import { type DateTimeSkeleton, type MessageFormatElement } from "./types.js";
export declare function printAST(ast: MessageFormatElement[]): string;
export declare function doPrintAST(ast: MessageFormatElement[], isInPlural: boolean): string;
export declare function printDateTimeSkeleton(style: DateTimeSkeleton): string;

@@ -1,101 +0,90 @@

import { __spreadArray } from "tslib";
import { isArgumentElement, isDateElement, isLiteralElement, isNumberElement, isPluralElement, isPoundElement, isSelectElement, isTagElement, isTimeElement, SKELETON_TYPE, TYPE, } from './types.js';
import "@formatjs/icu-skeleton-parser";
import { isArgumentElement, isDateElement, isLiteralElement, isNumberElement, isPluralElement, isPoundElement, isSelectElement, isTagElement, isTimeElement, SKELETON_TYPE, TYPE } from "./types.js";
export function printAST(ast) {
return doPrintAST(ast, false);
return doPrintAST(ast, false);
}
export function doPrintAST(ast, isInPlural) {
var printedNodes = ast.map(function (el, i) {
if (isLiteralElement(el)) {
return printLiteralElement(el, isInPlural, i === 0, i === ast.length - 1);
}
if (isArgumentElement(el)) {
return printArgumentElement(el);
}
if (isDateElement(el) || isTimeElement(el) || isNumberElement(el)) {
return printSimpleFormatElement(el);
}
if (isPluralElement(el)) {
return printPluralElement(el);
}
if (isSelectElement(el)) {
return printSelectElement(el);
}
if (isPoundElement(el)) {
return '#';
}
if (isTagElement(el)) {
return printTagElement(el);
}
});
return printedNodes.join('');
const printedNodes = ast.map((el, i) => {
if (isLiteralElement(el)) {
return printLiteralElement(el, isInPlural, i === 0, i === ast.length - 1);
}
if (isArgumentElement(el)) {
return printArgumentElement(el);
}
if (isDateElement(el) || isTimeElement(el) || isNumberElement(el)) {
return printSimpleFormatElement(el);
}
if (isPluralElement(el)) {
return printPluralElement(el);
}
if (isSelectElement(el)) {
return printSelectElement(el);
}
if (isPoundElement(el)) {
return "#";
}
if (isTagElement(el)) {
return printTagElement(el);
}
});
return printedNodes.join("");
}
function printTagElement(el) {
return "<".concat(el.value, ">").concat(printAST(el.children), "</").concat(el.value, ">");
return `<${el.value}>${printAST(el.children)}</${el.value}>`;
}
function printEscapedMessage(message) {
return message.replace(/([{}](?:[\s\S]*[{}])?)/, "'$1'");
return message.replace(/([{}](?:[\s\S]*[{}])?)/, `'$1'`);
}
function printLiteralElement(_a, isInPlural, isFirstEl, isLastEl) {
var value = _a.value;
var escaped = value;
// If this literal starts with a ' and its not the 1st node, this means the node before it is non-literal
// and the `'` needs to be unescaped
if (!isFirstEl && escaped[0] === "'") {
escaped = "''".concat(escaped.slice(1));
}
// Same logic but for last el
if (!isLastEl && escaped[escaped.length - 1] === "'") {
escaped = "".concat(escaped.slice(0, escaped.length - 1), "''");
}
escaped = printEscapedMessage(escaped);
return isInPlural ? escaped.replace('#', "'#'") : escaped;
function printLiteralElement({ value }, isInPlural, isFirstEl, isLastEl) {
let escaped = value;
// If this literal starts with a ' and its not the 1st node, this means the node before it is non-literal
// and the `'` needs to be unescaped
if (!isFirstEl && escaped[0] === `'`) {
escaped = `''${escaped.slice(1)}`;
}
// Same logic but for last el
if (!isLastEl && escaped[escaped.length - 1] === `'`) {
escaped = `${escaped.slice(0, escaped.length - 1)}''`;
}
escaped = printEscapedMessage(escaped);
return isInPlural ? escaped.replace("#", "'#'") : escaped;
}
function printArgumentElement(_a) {
var value = _a.value;
return "{".concat(value, "}");
function printArgumentElement({ value }) {
return `{${value}}`;
}
function printSimpleFormatElement(el) {
return "{".concat(el.value, ", ").concat(TYPE[el.type]).concat(el.style ? ", ".concat(printArgumentStyle(el.style)) : '', "}");
return `{${el.value}, ${TYPE[el.type]}${el.style ? `, ${printArgumentStyle(el.style)}` : ""}}`;
}
function printNumberSkeletonToken(token) {
var stem = token.stem, options = token.options;
return options.length === 0
? stem
: "".concat(stem).concat(options.map(function (o) { return "/".concat(o); }).join(''));
const { stem, options } = token;
return options.length === 0 ? stem : `${stem}${options.map((o) => `/${o}`).join("")}`;
}
function printArgumentStyle(style) {
if (typeof style === 'string') {
return printEscapedMessage(style);
}
else if (style.type === SKELETON_TYPE.dateTime) {
return "::".concat(printDateTimeSkeleton(style));
}
else {
return "::".concat(style.tokens.map(printNumberSkeletonToken).join(' '));
}
if (typeof style === "string") {
return printEscapedMessage(style);
} else if (style.type === SKELETON_TYPE.dateTime) {
return `::${printDateTimeSkeleton(style)}`;
} else {
return `::${style.tokens.map(printNumberSkeletonToken).join(" ")}`;
}
}
export function printDateTimeSkeleton(style) {
return style.pattern;
return style.pattern;
}
function printSelectElement(el) {
var msg = [
el.value,
'select',
Object.keys(el.options)
.map(function (id) { return "".concat(id, "{").concat(doPrintAST(el.options[id].value, false), "}"); })
.join(' '),
].join(',');
return "{".concat(msg, "}");
const msg = [
el.value,
"select",
Object.keys(el.options).map((id) => `${id}{${doPrintAST(el.options[id].value, false)}}`).join(" ")
].join(",");
return `{${msg}}`;
}
function printPluralElement(el) {
var type = el.pluralType === 'cardinal' ? 'plural' : 'selectordinal';
var msg = [
el.value,
type,
__spreadArray([
el.offset ? "offset:".concat(el.offset) : ''
], Object.keys(el.options).map(function (id) { return "".concat(id, "{").concat(doPrintAST(el.options[id].value, true), "}"); }), true).filter(Boolean)
.join(' '),
].join(',');
return "{".concat(msg, "}");
const type = el.pluralType === "cardinal" ? "plural" : "selectordinal";
const msg = [
el.value,
type,
[el.offset ? `offset:${el.offset}` : "", ...Object.keys(el.options).map((id) => `${id}{${doPrintAST(el.options[id].value, true)}}`)].filter(Boolean).join(" ")
].join(",");
return `{${msg}}`;
}

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

// @generated from regex-gen.ts
export declare const SPACE_SEPARATOR_REGEX: RegExp;
export declare const WHITE_SPACE_REGEX: RegExp;
// @generated from regex-gen.ts
export var SPACE_SEPARATOR_REGEX = /[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/;
export var WHITE_SPACE_REGEX = /[\t-\r \x85\u200E\u200F\u2028\u2029]/;
export const SPACE_SEPARATOR_REGEX = /[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/;
export const WHITE_SPACE_REGEX = /[\t-\r \x85\u200E\u200F\u2028\u2029]/;

@@ -0,1 +1,3 @@

// @generated from time-data-gen.ts
// prettier-ignore
export declare const timeData: Record<string, string[]>;
// @generated from time-data-gen.ts
// prettier-ignore
export var timeData = {
"001": [
"H",
"h"
],
"419": [
"h",
"H",
"hB",
"hb"
],
"AC": [
"H",
"h",
"hb",
"hB"
],
"AD": [
"H",
"hB"
],
"AE": [
"h",
"hB",
"hb",
"H"
],
"AF": [
"H",
"hb",
"hB",
"h"
],
"AG": [
"h",
"hb",
"H",
"hB"
],
"AI": [
"H",
"h",
"hb",
"hB"
],
"AL": [
"h",
"H",
"hB"
],
"AM": [
"H",
"hB"
],
"AO": [
"H",
"hB"
],
"AR": [
"h",
"H",
"hB",
"hb"
],
"AS": [
"h",
"H"
],
"AT": [
"H",
"hB"
],
"AU": [
"h",
"hb",
"H",
"hB"
],
"AW": [
"H",
"hB"
],
"AX": [
"H"
],
"AZ": [
"H",
"hB",
"h"
],
"BA": [
"H",
"hB",
"h"
],
"BB": [
"h",
"hb",
"H",
"hB"
],
"BD": [
"h",
"hB",
"H"
],
"BE": [
"H",
"hB"
],
"BF": [
"H",
"hB"
],
"BG": [
"H",
"hB",
"h"
],
"BH": [
"h",
"hB",
"hb",
"H"
],
"BI": [
"H",
"h"
],
"BJ": [
"H",
"hB"
],
"BL": [
"H",
"hB"
],
"BM": [
"h",
"hb",
"H",
"hB"
],
"BN": [
"hb",
"hB",
"h",
"H"
],
"BO": [
"h",
"H",
"hB",
"hb"
],
"BQ": [
"H"
],
"BR": [
"H",
"hB"
],
"BS": [
"h",
"hb",
"H",
"hB"
],
"BT": [
"h",
"H"
],
"BW": [
"H",
"h",
"hb",
"hB"
],
"BY": [
"H",
"h"
],
"BZ": [
"H",
"h",
"hb",
"hB"
],
"CA": [
"h",
"hb",
"H",
"hB"
],
"CC": [
"H",
"h",
"hb",
"hB"
],
"CD": [
"hB",
"H"
],
"CF": [
"H",
"h",
"hB"
],
"CG": [
"H",
"hB"
],
"CH": [
"H",
"hB",
"h"
],
"CI": [
"H",
"hB"
],
"CK": [
"H",
"h",
"hb",
"hB"
],
"CL": [
"h",
"H",
"hB",
"hb"
],
"CM": [
"H",
"h",
"hB"
],
"CN": [
"H",
"hB",
"hb",
"h"
],
"CO": [
"h",
"H",
"hB",
"hb"
],
"CP": [
"H"
],
"CR": [
"h",
"H",
"hB",
"hb"
],
"CU": [
"h",
"H",
"hB",
"hb"
],
"CV": [
"H",
"hB"
],
"CW": [
"H",
"hB"
],
"CX": [
"H",
"h",
"hb",
"hB"
],
"CY": [
"h",
"H",
"hb",
"hB"
],
"CZ": [
"H"
],
"DE": [
"H",
"hB"
],
"DG": [
"H",
"h",
"hb",
"hB"
],
"DJ": [
"h",
"H"
],
"DK": [
"H"
],
"DM": [
"h",
"hb",
"H",
"hB"
],
"DO": [
"h",
"H",
"hB",
"hb"
],
"DZ": [
"h",
"hB",
"hb",
"H"
],
"EA": [
"H",
"h",
"hB",
"hb"
],
"EC": [
"h",
"H",
"hB",
"hb"
],
"EE": [
"H",
"hB"
],
"EG": [
"h",
"hB",
"hb",
"H"
],
"EH": [
"h",
"hB",
"hb",
"H"
],
"ER": [
"h",
"H"
],
"ES": [
"H",
"hB",
"h",
"hb"
],
"ET": [
"hB",
"hb",
"h",
"H"
],
"FI": [
"H"
],
"FJ": [
"h",
"hb",
"H",
"hB"
],
"FK": [
"H",
"h",
"hb",
"hB"
],
"FM": [
"h",
"hb",
"H",
"hB"
],
"FO": [
"H",
"h"
],
"FR": [
"H",
"hB"
],
"GA": [
"H",
"hB"
],
"GB": [
"H",
"h",
"hb",
"hB"
],
"GD": [
"h",
"hb",
"H",
"hB"
],
"GE": [
"H",
"hB",
"h"
],
"GF": [
"H",
"hB"
],
"GG": [
"H",
"h",
"hb",
"hB"
],
"GH": [
"h",
"H"
],
"GI": [
"H",
"h",
"hb",
"hB"
],
"GL": [
"H",
"h"
],
"GM": [
"h",
"hb",
"H",
"hB"
],
"GN": [
"H",
"hB"
],
"GP": [
"H",
"hB"
],
"GQ": [
"H",
"hB",
"h",
"hb"
],
"GR": [
"h",
"H",
"hb",
"hB"
],
"GS": [
"H",
"h",
"hb",
"hB"
],
"GT": [
"h",
"H",
"hB",
"hb"
],
"GU": [
"h",
"hb",
"H",
"hB"
],
"GW": [
"H",
"hB"
],
"GY": [
"h",
"hb",
"H",
"hB"
],
"HK": [
"h",
"hB",
"hb",
"H"
],
"HN": [
"h",
"H",
"hB",
"hb"
],
"HR": [
"H",
"hB"
],
"HU": [
"H",
"h"
],
"IC": [
"H",
"h",
"hB",
"hb"
],
"ID": [
"H"
],
"IE": [
"H",
"h",
"hb",
"hB"
],
"IL": [
"H",
"hB"
],
"IM": [
"H",
"h",
"hb",
"hB"
],
"IN": [
"h",
"H"
],
"IO": [
"H",
"h",
"hb",
"hB"
],
"IQ": [
"h",
"hB",
"hb",
"H"
],
"IR": [
"hB",
"H"
],
"IS": [
"H"
],
"IT": [
"H",
"hB"
],
"JE": [
"H",
"h",
"hb",
"hB"
],
"JM": [
"h",
"hb",
"H",
"hB"
],
"JO": [
"h",
"hB",
"hb",
"H"
],
"JP": [
"H",
"K",
"h"
],
"KE": [
"hB",
"hb",
"H",
"h"
],
"KG": [
"H",
"h",
"hB",
"hb"
],
"KH": [
"hB",
"h",
"H",
"hb"
],
"KI": [
"h",
"hb",
"H",
"hB"
],
"KM": [
"H",
"h",
"hB",
"hb"
],
"KN": [
"h",
"hb",
"H",
"hB"
],
"KP": [
"h",
"H",
"hB",
"hb"
],
"KR": [
"h",
"H",
"hB",
"hb"
],
"KW": [
"h",
"hB",
"hb",
"H"
],
"KY": [
"h",
"hb",
"H",
"hB"
],
"KZ": [
"H",
"hB"
],
"LA": [
"H",
"hb",
"hB",
"h"
],
"LB": [
"h",
"hB",
"hb",
"H"
],
"LC": [
"h",
"hb",
"H",
"hB"
],
"LI": [
"H",
"hB",
"h"
],
"LK": [
"H",
"h",
"hB",
"hb"
],
"LR": [
"h",
"hb",
"H",
"hB"
],
"LS": [
"h",
"H"
],
"LT": [
"H",
"h",
"hb",
"hB"
],
"LU": [
"H",
"h",
"hB"
],
"LV": [
"H",
"hB",
"hb",
"h"
],
"LY": [
"h",
"hB",
"hb",
"H"
],
"MA": [
"H",
"h",
"hB",
"hb"
],
"MC": [
"H",
"hB"
],
"MD": [
"H",
"hB"
],
"ME": [
"H",
"hB",
"h"
],
"MF": [
"H",
"hB"
],
"MG": [
"H",
"h"
],
"MH": [
"h",
"hb",
"H",
"hB"
],
"MK": [
"H",
"h",
"hb",
"hB"
],
"ML": [
"H"
],
"MM": [
"hB",
"hb",
"H",
"h"
],
"MN": [
"H",
"h",
"hb",
"hB"
],
"MO": [
"h",
"hB",
"hb",
"H"
],
"MP": [
"h",
"hb",
"H",
"hB"
],
"MQ": [
"H",
"hB"
],
"MR": [
"h",
"hB",
"hb",
"H"
],
"MS": [
"H",
"h",
"hb",
"hB"
],
"MT": [
"H",
"h"
],
"MU": [
"H",
"h"
],
"MV": [
"H",
"h"
],
"MW": [
"h",
"hb",
"H",
"hB"
],
"MX": [
"h",
"H",
"hB",
"hb"
],
"MY": [
"hb",
"hB",
"h",
"H"
],
"MZ": [
"H",
"hB"
],
"NA": [
"h",
"H",
"hB",
"hb"
],
"NC": [
"H",
"hB"
],
"NE": [
"H"
],
"NF": [
"H",
"h",
"hb",
"hB"
],
"NG": [
"H",
"h",
"hb",
"hB"
],
"NI": [
"h",
"H",
"hB",
"hb"
],
"NL": [
"H",
"hB"
],
"NO": [
"H",
"h"
],
"NP": [
"H",
"h",
"hB"
],
"NR": [
"H",
"h",
"hb",
"hB"
],
"NU": [
"H",
"h",
"hb",
"hB"
],
"NZ": [
"h",
"hb",
"H",
"hB"
],
"OM": [
"h",
"hB",
"hb",
"H"
],
"PA": [
"h",
"H",
"hB",
"hb"
],
"PE": [
"h",
"H",
"hB",
"hb"
],
"PF": [
"H",
"h",
"hB"
],
"PG": [
"h",
"H"
],
"PH": [
"h",
"hB",
"hb",
"H"
],
"PK": [
"h",
"hB",
"H"
],
"PL": [
"H",
"h"
],
"PM": [
"H",
"hB"
],
"PN": [
"H",
"h",
"hb",
"hB"
],
"PR": [
"h",
"H",
"hB",
"hb"
],
"PS": [
"h",
"hB",
"hb",
"H"
],
"PT": [
"H",
"hB"
],
"PW": [
"h",
"H"
],
"PY": [
"h",
"H",
"hB",
"hb"
],
"QA": [
"h",
"hB",
"hb",
"H"
],
"RE": [
"H",
"hB"
],
"RO": [
"H",
"hB"
],
"RS": [
"H",
"hB",
"h"
],
"RU": [
"H"
],
"RW": [
"H",
"h"
],
"SA": [
"h",
"hB",
"hb",
"H"
],
"SB": [
"h",
"hb",
"H",
"hB"
],
"SC": [
"H",
"h",
"hB"
],
"SD": [
"h",
"hB",
"hb",
"H"
],
"SE": [
"H"
],
"SG": [
"h",
"hb",
"H",
"hB"
],
"SH": [
"H",
"h",
"hb",
"hB"
],
"SI": [
"H",
"hB"
],
"SJ": [
"H"
],
"SK": [
"H"
],
"SL": [
"h",
"hb",
"H",
"hB"
],
"SM": [
"H",
"h",
"hB"
],
"SN": [
"H",
"h",
"hB"
],
"SO": [
"h",
"H"
],
"SR": [
"H",
"hB"
],
"SS": [
"h",
"hb",
"H",
"hB"
],
"ST": [
"H",
"hB"
],
"SV": [
"h",
"H",
"hB",
"hb"
],
"SX": [
"H",
"h",
"hb",
"hB"
],
"SY": [
"h",
"hB",
"hb",
"H"
],
"SZ": [
"h",
"hb",
"H",
"hB"
],
"TA": [
"H",
"h",
"hb",
"hB"
],
"TC": [
"h",
"hb",
"H",
"hB"
],
"TD": [
"h",
"H",
"hB"
],
"TF": [
"H",
"h",
"hB"
],
"TG": [
"H",
"hB"
],
"TH": [
"H",
"h"
],
"TJ": [
"H",
"h"
],
"TL": [
"H",
"hB",
"hb",
"h"
],
"TM": [
"H",
"h"
],
"TN": [
"h",
"hB",
"hb",
"H"
],
"TO": [
"h",
"H"
],
"TR": [
"H",
"hB"
],
"TT": [
"h",
"hb",
"H",
"hB"
],
"TW": [
"hB",
"hb",
"h",
"H"
],
"TZ": [
"hB",
"hb",
"H",
"h"
],
"UA": [
"H",
"hB",
"h"
],
"UG": [
"hB",
"hb",
"H",
"h"
],
"UM": [
"h",
"hb",
"H",
"hB"
],
"US": [
"h",
"hb",
"H",
"hB"
],
"UY": [
"h",
"H",
"hB",
"hb"
],
"UZ": [
"H",
"hB",
"h"
],
"VA": [
"H",
"h",
"hB"
],
"VC": [
"h",
"hb",
"H",
"hB"
],
"VE": [
"h",
"H",
"hB",
"hb"
],
"VG": [
"h",
"hb",
"H",
"hB"
],
"VI": [
"h",
"hb",
"H",
"hB"
],
"VN": [
"H",
"h"
],
"VU": [
"h",
"H"
],
"WF": [
"H",
"hB"
],
"WS": [
"h",
"H"
],
"XK": [
"H",
"hB",
"h"
],
"YE": [
"h",
"hB",
"hb",
"H"
],
"YT": [
"H",
"hB"
],
"ZA": [
"H",
"h",
"hb",
"hB"
],
"ZM": [
"h",
"hb",
"H",
"hB"
],
"ZW": [
"H",
"h"
],
"af-ZA": [
"H",
"h",
"hB",
"hb"
],
"ar-001": [
"h",
"hB",
"hb",
"H"
],
"ca-ES": [
"H",
"h",
"hB"
],
"en-001": [
"h",
"hb",
"H",
"hB"
],
"en-HK": [
"h",
"hb",
"H",
"hB"
],
"en-IL": [
"H",
"h",
"hb",
"hB"
],
"en-MY": [
"h",
"hb",
"H",
"hB"
],
"es-BR": [
"H",
"h",
"hB",
"hb"
],
"es-ES": [
"H",
"h",
"hB",
"hb"
],
"es-GQ": [
"H",
"h",
"hB",
"hb"
],
"fr-CA": [
"H",
"h",
"hB"
],
"gl-ES": [
"H",
"h",
"hB"
],
"gu-IN": [
"hB",
"hb",
"h",
"H"
],
"hi-IN": [
"hB",
"h",
"H"
],
"it-CH": [
"H",
"h",
"hB"
],
"it-IT": [
"H",
"h",
"hB"
],
"kn-IN": [
"hB",
"h",
"H"
],
"ku-SY": [
"H",
"hB"
],
"ml-IN": [
"hB",
"h",
"H"
],
"mr-IN": [
"hB",
"hb",
"h",
"H"
],
"pa-IN": [
"hB",
"hb",
"h",
"H"
],
"ta-IN": [
"hB",
"h",
"hb",
"H"
],
"te-IN": [
"hB",
"h",
"H"
],
"zu-ZA": [
"H",
"hB",
"hb",
"h"
]
export const timeData = {
"001": ["H", "h"],
"419": [
"h",
"H",
"hB",
"hb"
],
"AC": [
"H",
"h",
"hb",
"hB"
],
"AD": ["H", "hB"],
"AE": [
"h",
"hB",
"hb",
"H"
],
"AF": [
"H",
"hb",
"hB",
"h"
],
"AG": [
"h",
"hb",
"H",
"hB"
],
"AI": [
"H",
"h",
"hb",
"hB"
],
"AL": [
"h",
"H",
"hB"
],
"AM": ["H", "hB"],
"AO": ["H", "hB"],
"AR": [
"h",
"H",
"hB",
"hb"
],
"AS": ["h", "H"],
"AT": ["H", "hB"],
"AU": [
"h",
"hb",
"H",
"hB"
],
"AW": ["H", "hB"],
"AX": ["H"],
"AZ": [
"H",
"hB",
"h"
],
"BA": [
"H",
"hB",
"h"
],
"BB": [
"h",
"hb",
"H",
"hB"
],
"BD": [
"h",
"hB",
"H"
],
"BE": ["H", "hB"],
"BF": ["H", "hB"],
"BG": [
"H",
"hB",
"h"
],
"BH": [
"h",
"hB",
"hb",
"H"
],
"BI": ["H", "h"],
"BJ": ["H", "hB"],
"BL": ["H", "hB"],
"BM": [
"h",
"hb",
"H",
"hB"
],
"BN": [
"hb",
"hB",
"h",
"H"
],
"BO": [
"h",
"H",
"hB",
"hb"
],
"BQ": ["H"],
"BR": ["H", "hB"],
"BS": [
"h",
"hb",
"H",
"hB"
],
"BT": ["h", "H"],
"BW": [
"H",
"h",
"hb",
"hB"
],
"BY": ["H", "h"],
"BZ": [
"H",
"h",
"hb",
"hB"
],
"CA": [
"h",
"hb",
"H",
"hB"
],
"CC": [
"H",
"h",
"hb",
"hB"
],
"CD": ["hB", "H"],
"CF": [
"H",
"h",
"hB"
],
"CG": ["H", "hB"],
"CH": [
"H",
"hB",
"h"
],
"CI": ["H", "hB"],
"CK": [
"H",
"h",
"hb",
"hB"
],
"CL": [
"h",
"H",
"hB",
"hb"
],
"CM": [
"H",
"h",
"hB"
],
"CN": [
"H",
"hB",
"hb",
"h"
],
"CO": [
"h",
"H",
"hB",
"hb"
],
"CP": ["H"],
"CR": [
"h",
"H",
"hB",
"hb"
],
"CU": [
"h",
"H",
"hB",
"hb"
],
"CV": ["H", "hB"],
"CW": ["H", "hB"],
"CX": [
"H",
"h",
"hb",
"hB"
],
"CY": [
"h",
"H",
"hb",
"hB"
],
"CZ": ["H"],
"DE": ["H", "hB"],
"DG": [
"H",
"h",
"hb",
"hB"
],
"DJ": ["h", "H"],
"DK": ["H"],
"DM": [
"h",
"hb",
"H",
"hB"
],
"DO": [
"h",
"H",
"hB",
"hb"
],
"DZ": [
"h",
"hB",
"hb",
"H"
],
"EA": [
"H",
"h",
"hB",
"hb"
],
"EC": [
"h",
"H",
"hB",
"hb"
],
"EE": ["H", "hB"],
"EG": [
"h",
"hB",
"hb",
"H"
],
"EH": [
"h",
"hB",
"hb",
"H"
],
"ER": ["h", "H"],
"ES": [
"H",
"hB",
"h",
"hb"
],
"ET": [
"hB",
"hb",
"h",
"H"
],
"FI": ["H"],
"FJ": [
"h",
"hb",
"H",
"hB"
],
"FK": [
"H",
"h",
"hb",
"hB"
],
"FM": [
"h",
"hb",
"H",
"hB"
],
"FO": ["H", "h"],
"FR": ["H", "hB"],
"GA": ["H", "hB"],
"GB": [
"H",
"h",
"hb",
"hB"
],
"GD": [
"h",
"hb",
"H",
"hB"
],
"GE": [
"H",
"hB",
"h"
],
"GF": ["H", "hB"],
"GG": [
"H",
"h",
"hb",
"hB"
],
"GH": ["h", "H"],
"GI": [
"H",
"h",
"hb",
"hB"
],
"GL": ["H", "h"],
"GM": [
"h",
"hb",
"H",
"hB"
],
"GN": ["H", "hB"],
"GP": ["H", "hB"],
"GQ": [
"H",
"hB",
"h",
"hb"
],
"GR": [
"h",
"H",
"hb",
"hB"
],
"GS": [
"H",
"h",
"hb",
"hB"
],
"GT": [
"h",
"H",
"hB",
"hb"
],
"GU": [
"h",
"hb",
"H",
"hB"
],
"GW": ["H", "hB"],
"GY": [
"h",
"hb",
"H",
"hB"
],
"HK": [
"h",
"hB",
"hb",
"H"
],
"HN": [
"h",
"H",
"hB",
"hb"
],
"HR": ["H", "hB"],
"HU": ["H", "h"],
"IC": [
"H",
"h",
"hB",
"hb"
],
"ID": ["H"],
"IE": [
"H",
"h",
"hb",
"hB"
],
"IL": ["H", "hB"],
"IM": [
"H",
"h",
"hb",
"hB"
],
"IN": ["h", "H"],
"IO": [
"H",
"h",
"hb",
"hB"
],
"IQ": [
"h",
"hB",
"hb",
"H"
],
"IR": ["hB", "H"],
"IS": ["H"],
"IT": ["H", "hB"],
"JE": [
"H",
"h",
"hb",
"hB"
],
"JM": [
"h",
"hb",
"H",
"hB"
],
"JO": [
"h",
"hB",
"hb",
"H"
],
"JP": [
"H",
"K",
"h"
],
"KE": [
"hB",
"hb",
"H",
"h"
],
"KG": [
"H",
"h",
"hB",
"hb"
],
"KH": [
"hB",
"h",
"H",
"hb"
],
"KI": [
"h",
"hb",
"H",
"hB"
],
"KM": [
"H",
"h",
"hB",
"hb"
],
"KN": [
"h",
"hb",
"H",
"hB"
],
"KP": [
"h",
"H",
"hB",
"hb"
],
"KR": [
"h",
"H",
"hB",
"hb"
],
"KW": [
"h",
"hB",
"hb",
"H"
],
"KY": [
"h",
"hb",
"H",
"hB"
],
"KZ": ["H", "hB"],
"LA": [
"H",
"hb",
"hB",
"h"
],
"LB": [
"h",
"hB",
"hb",
"H"
],
"LC": [
"h",
"hb",
"H",
"hB"
],
"LI": [
"H",
"hB",
"h"
],
"LK": [
"H",
"h",
"hB",
"hb"
],
"LR": [
"h",
"hb",
"H",
"hB"
],
"LS": ["h", "H"],
"LT": [
"H",
"h",
"hb",
"hB"
],
"LU": [
"H",
"h",
"hB"
],
"LV": [
"H",
"hB",
"hb",
"h"
],
"LY": [
"h",
"hB",
"hb",
"H"
],
"MA": [
"H",
"h",
"hB",
"hb"
],
"MC": ["H", "hB"],
"MD": ["H", "hB"],
"ME": [
"H",
"hB",
"h"
],
"MF": ["H", "hB"],
"MG": ["H", "h"],
"MH": [
"h",
"hb",
"H",
"hB"
],
"MK": [
"H",
"h",
"hb",
"hB"
],
"ML": ["H"],
"MM": [
"hB",
"hb",
"H",
"h"
],
"MN": [
"H",
"h",
"hb",
"hB"
],
"MO": [
"h",
"hB",
"hb",
"H"
],
"MP": [
"h",
"hb",
"H",
"hB"
],
"MQ": ["H", "hB"],
"MR": [
"h",
"hB",
"hb",
"H"
],
"MS": [
"H",
"h",
"hb",
"hB"
],
"MT": ["H", "h"],
"MU": ["H", "h"],
"MV": ["H", "h"],
"MW": [
"h",
"hb",
"H",
"hB"
],
"MX": [
"h",
"H",
"hB",
"hb"
],
"MY": [
"hb",
"hB",
"h",
"H"
],
"MZ": ["H", "hB"],
"NA": [
"h",
"H",
"hB",
"hb"
],
"NC": ["H", "hB"],
"NE": ["H"],
"NF": [
"H",
"h",
"hb",
"hB"
],
"NG": [
"H",
"h",
"hb",
"hB"
],
"NI": [
"h",
"H",
"hB",
"hb"
],
"NL": ["H", "hB"],
"NO": ["H", "h"],
"NP": [
"H",
"h",
"hB"
],
"NR": [
"H",
"h",
"hb",
"hB"
],
"NU": [
"H",
"h",
"hb",
"hB"
],
"NZ": [
"h",
"hb",
"H",
"hB"
],
"OM": [
"h",
"hB",
"hb",
"H"
],
"PA": [
"h",
"H",
"hB",
"hb"
],
"PE": [
"h",
"H",
"hB",
"hb"
],
"PF": [
"H",
"h",
"hB"
],
"PG": ["h", "H"],
"PH": [
"h",
"hB",
"hb",
"H"
],
"PK": [
"h",
"hB",
"H"
],
"PL": ["H", "h"],
"PM": ["H", "hB"],
"PN": [
"H",
"h",
"hb",
"hB"
],
"PR": [
"h",
"H",
"hB",
"hb"
],
"PS": [
"h",
"hB",
"hb",
"H"
],
"PT": ["H", "hB"],
"PW": ["h", "H"],
"PY": [
"h",
"H",
"hB",
"hb"
],
"QA": [
"h",
"hB",
"hb",
"H"
],
"RE": ["H", "hB"],
"RO": ["H", "hB"],
"RS": [
"H",
"hB",
"h"
],
"RU": ["H"],
"RW": ["H", "h"],
"SA": [
"h",
"hB",
"hb",
"H"
],
"SB": [
"h",
"hb",
"H",
"hB"
],
"SC": [
"H",
"h",
"hB"
],
"SD": [
"h",
"hB",
"hb",
"H"
],
"SE": ["H"],
"SG": [
"h",
"hb",
"H",
"hB"
],
"SH": [
"H",
"h",
"hb",
"hB"
],
"SI": ["H", "hB"],
"SJ": ["H"],
"SK": ["H"],
"SL": [
"h",
"hb",
"H",
"hB"
],
"SM": [
"H",
"h",
"hB"
],
"SN": [
"H",
"h",
"hB"
],
"SO": ["h", "H"],
"SR": ["H", "hB"],
"SS": [
"h",
"hb",
"H",
"hB"
],
"ST": ["H", "hB"],
"SV": [
"h",
"H",
"hB",
"hb"
],
"SX": [
"H",
"h",
"hb",
"hB"
],
"SY": [
"h",
"hB",
"hb",
"H"
],
"SZ": [
"h",
"hb",
"H",
"hB"
],
"TA": [
"H",
"h",
"hb",
"hB"
],
"TC": [
"h",
"hb",
"H",
"hB"
],
"TD": [
"h",
"H",
"hB"
],
"TF": [
"H",
"h",
"hB"
],
"TG": ["H", "hB"],
"TH": ["H", "h"],
"TJ": ["H", "h"],
"TL": [
"H",
"hB",
"hb",
"h"
],
"TM": ["H", "h"],
"TN": [
"h",
"hB",
"hb",
"H"
],
"TO": ["h", "H"],
"TR": ["H", "hB"],
"TT": [
"h",
"hb",
"H",
"hB"
],
"TW": [
"hB",
"hb",
"h",
"H"
],
"TZ": [
"hB",
"hb",
"H",
"h"
],
"UA": [
"H",
"hB",
"h"
],
"UG": [
"hB",
"hb",
"H",
"h"
],
"UM": [
"h",
"hb",
"H",
"hB"
],
"US": [
"h",
"hb",
"H",
"hB"
],
"UY": [
"h",
"H",
"hB",
"hb"
],
"UZ": [
"H",
"hB",
"h"
],
"VA": [
"H",
"h",
"hB"
],
"VC": [
"h",
"hb",
"H",
"hB"
],
"VE": [
"h",
"H",
"hB",
"hb"
],
"VG": [
"h",
"hb",
"H",
"hB"
],
"VI": [
"h",
"hb",
"H",
"hB"
],
"VN": ["H", "h"],
"VU": ["h", "H"],
"WF": ["H", "hB"],
"WS": ["h", "H"],
"XK": [
"H",
"hB",
"h"
],
"YE": [
"h",
"hB",
"hb",
"H"
],
"YT": ["H", "hB"],
"ZA": [
"H",
"h",
"hb",
"hB"
],
"ZM": [
"h",
"hb",
"H",
"hB"
],
"ZW": ["H", "h"],
"af-ZA": [
"H",
"h",
"hB",
"hb"
],
"ar-001": [
"h",
"hB",
"hb",
"H"
],
"ca-ES": [
"H",
"h",
"hB"
],
"en-001": [
"h",
"hb",
"H",
"hB"
],
"en-HK": [
"h",
"hb",
"H",
"hB"
],
"en-IL": [
"H",
"h",
"hb",
"hB"
],
"en-MY": [
"h",
"hb",
"H",
"hB"
],
"es-BR": [
"H",
"h",
"hB",
"hb"
],
"es-ES": [
"H",
"h",
"hB",
"hb"
],
"es-GQ": [
"H",
"h",
"hB",
"hb"
],
"fr-CA": [
"H",
"h",
"hB"
],
"gl-ES": [
"H",
"h",
"hB"
],
"gu-IN": [
"hB",
"hb",
"h",
"H"
],
"hi-IN": [
"hB",
"h",
"H"
],
"it-CH": [
"H",
"h",
"hB"
],
"it-IT": [
"H",
"h",
"hB"
],
"kn-IN": [
"hB",
"h",
"H"
],
"ku-SY": ["H", "hB"],
"ml-IN": [
"hB",
"h",
"H"
],
"mr-IN": [
"hB",
"hb",
"h",
"H"
],
"pa-IN": [
"hB",
"hb",
"h",
"H"
],
"ta-IN": [
"hB",
"h",
"hb",
"H"
],
"te-IN": [
"hB",
"h",
"H"
],
"zu-ZA": [
"H",
"hB",
"hb",
"h"
]
};

@@ -1,62 +0,62 @@

import type { NumberFormatOptions } from '@formatjs/ecma402-abstract';
import { NumberSkeletonToken } from '@formatjs/icu-skeleton-parser';
import type { NumberFormatOptions } from "@formatjs/ecma402-abstract";
import { type NumberSkeletonToken } from "@formatjs/icu-skeleton-parser";
export interface ExtendedNumberFormatOptions extends NumberFormatOptions {
scale?: number;
scale?: number;
}
export declare enum TYPE {
/**
* Raw text
*/
literal = 0,
/**
* Variable w/o any format, e.g `var` in `this is a {var}`
*/
argument = 1,
/**
* Variable w/ number format
*/
number = 2,
/**
* Variable w/ date format
*/
date = 3,
/**
* Variable w/ time format
*/
time = 4,
/**
* Variable w/ select format
*/
select = 5,
/**
* Variable w/ plural format
*/
plural = 6,
/**
* Only possible within plural argument.
* This is the `#` symbol that will be substituted with the count.
*/
pound = 7,
/**
* XML-like tag
*/
tag = 8
/**
* Raw text
*/
literal = 0,
/**
* Variable w/o any format, e.g `var` in `this is a {var}`
*/
argument = 1,
/**
* Variable w/ number format
*/
number = 2,
/**
* Variable w/ date format
*/
date = 3,
/**
* Variable w/ time format
*/
time = 4,
/**
* Variable w/ select format
*/
select = 5,
/**
* Variable w/ plural format
*/
plural = 6,
/**
* Only possible within plural argument.
* This is the `#` symbol that will be substituted with the count.
*/
pound = 7,
/**
* XML-like tag
*/
tag = 8
}
export declare enum SKELETON_TYPE {
number = 0,
dateTime = 1
number = 0,
dateTime = 1
}
export interface LocationDetails {
offset: number;
line: number;
column: number;
offset: number;
line: number;
column: number;
}
export interface Location {
start: LocationDetails;
end: LocationDetails;
start: LocationDetails;
end: LocationDetails;
}
export interface BaseElement<T extends TYPE> {
type: T;
value: string;
location?: Location;
type: T;
value: string;
location?: Location;
}

@@ -66,6 +66,9 @@ export type LiteralElement = BaseElement<TYPE.literal>;

export interface TagElement extends BaseElement<TYPE.tag> {
children: MessageFormatElement[];
children: MessageFormatElement[];
}
export interface SimpleFormatElement<T extends TYPE, S extends Skeleton> extends BaseElement<T> {
style?: string | S | null;
export interface SimpleFormatElement<
T extends TYPE,
S extends Skeleton
> extends BaseElement<T> {
style?: string | S | null;
}

@@ -75,36 +78,36 @@ export type NumberElement = SimpleFormatElement<TYPE.number, NumberSkeleton>;

export type TimeElement = SimpleFormatElement<TYPE.time, DateTimeSkeleton>;
export type ValidPluralRule = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other' | string;
export type ValidPluralRule = "zero" | "one" | "two" | "few" | "many" | "other" | string;
export interface PluralOrSelectOption {
value: MessageFormatElement[];
location?: Location;
value: MessageFormatElement[];
location?: Location;
}
export interface SelectElement extends BaseElement<TYPE.select> {
options: Record<string, PluralOrSelectOption>;
options: Record<string, PluralOrSelectOption>;
}
export interface PluralElement extends BaseElement<TYPE.plural> {
options: Record<ValidPluralRule, PluralOrSelectOption>;
offset: number;
pluralType: Intl.PluralRulesOptions['type'];
options: Record<ValidPluralRule, PluralOrSelectOption>;
offset: number;
pluralType: Intl.PluralRulesOptions["type"];
}
export interface PoundElement {
type: TYPE.pound;
location?: Location;
type: TYPE.pound;
location?: Location;
}
export type MessageFormatElement = ArgumentElement | DateElement | LiteralElement | NumberElement | PluralElement | PoundElement | SelectElement | TagElement | TimeElement;
export interface NumberSkeleton {
type: SKELETON_TYPE.number;
tokens: NumberSkeletonToken[];
location?: Location;
parsedOptions: ExtendedNumberFormatOptions;
type: SKELETON_TYPE.number;
tokens: NumberSkeletonToken[];
location?: Location;
parsedOptions: ExtendedNumberFormatOptions;
}
export interface DateTimeSkeleton {
type: SKELETON_TYPE.dateTime;
pattern: string;
location?: Location;
parsedOptions: Intl.DateTimeFormatOptions;
type: SKELETON_TYPE.dateTime;
pattern: string;
location?: Location;
parsedOptions: Intl.DateTimeFormatOptions;
}
export type Skeleton = NumberSkeleton | DateTimeSkeleton;
/**
* Type Guards
*/
* Type Guards
*/
export declare function isLiteralElement(el: MessageFormatElement): el is LiteralElement;

@@ -119,5 +122,5 @@ export declare function isArgumentElement(el: MessageFormatElement): el is ArgumentElement;

export declare function isTagElement(el: MessageFormatElement): el is TagElement;
export declare function isNumberSkeleton(el: NumberElement['style'] | Skeleton): el is NumberSkeleton;
export declare function isDateTimeSkeleton(el?: DateElement['style'] | TimeElement['style'] | Skeleton): el is DateTimeSkeleton;
export declare function isNumberSkeleton(el: NumberElement["style"] | Skeleton): el is NumberSkeleton;
export declare function isDateTimeSkeleton(el?: DateElement["style"] | TimeElement["style"] | Skeleton): el is DateTimeSkeleton;
export declare function createLiteralElement(value: string): LiteralElement;
export declare function createNumberElement(value: string, style?: string | null): NumberElement;
+68
-67

@@ -1,94 +0,95 @@

export var TYPE;
(function (TYPE) {
/**
* Raw text
*/
TYPE[TYPE["literal"] = 0] = "literal";
/**
* Variable w/o any format, e.g `var` in `this is a {var}`
*/
TYPE[TYPE["argument"] = 1] = "argument";
/**
* Variable w/ number format
*/
TYPE[TYPE["number"] = 2] = "number";
/**
* Variable w/ date format
*/
TYPE[TYPE["date"] = 3] = "date";
/**
* Variable w/ time format
*/
TYPE[TYPE["time"] = 4] = "time";
/**
* Variable w/ select format
*/
TYPE[TYPE["select"] = 5] = "select";
/**
* Variable w/ plural format
*/
TYPE[TYPE["plural"] = 6] = "plural";
/**
* Only possible within plural argument.
* This is the `#` symbol that will be substituted with the count.
*/
TYPE[TYPE["pound"] = 7] = "pound";
/**
* XML-like tag
*/
TYPE[TYPE["tag"] = 8] = "tag";
})(TYPE || (TYPE = {}));
export var SKELETON_TYPE;
(function (SKELETON_TYPE) {
SKELETON_TYPE[SKELETON_TYPE["number"] = 0] = "number";
SKELETON_TYPE[SKELETON_TYPE["dateTime"] = 1] = "dateTime";
})(SKELETON_TYPE || (SKELETON_TYPE = {}));
import "@formatjs/icu-skeleton-parser";
export let TYPE = /* @__PURE__ */ function(TYPE) {
/**
* Raw text
*/
TYPE[TYPE["literal"] = 0] = "literal";
/**
* Variable w/o any format, e.g `var` in `this is a {var}`
*/
TYPE[TYPE["argument"] = 1] = "argument";
/**
* Variable w/ number format
*/
TYPE[TYPE["number"] = 2] = "number";
/**
* Variable w/ date format
*/
TYPE[TYPE["date"] = 3] = "date";
/**
* Variable w/ time format
*/
TYPE[TYPE["time"] = 4] = "time";
/**
* Variable w/ select format
*/
TYPE[TYPE["select"] = 5] = "select";
/**
* Variable w/ plural format
*/
TYPE[TYPE["plural"] = 6] = "plural";
/**
* Only possible within plural argument.
* This is the `#` symbol that will be substituted with the count.
*/
TYPE[TYPE["pound"] = 7] = "pound";
/**
* XML-like tag
*/
TYPE[TYPE["tag"] = 8] = "tag";
return TYPE;
}({});
export let SKELETON_TYPE = /* @__PURE__ */ function(SKELETON_TYPE) {
SKELETON_TYPE[SKELETON_TYPE["number"] = 0] = "number";
SKELETON_TYPE[SKELETON_TYPE["dateTime"] = 1] = "dateTime";
return SKELETON_TYPE;
}({});
/**
* Type Guards
*/
* Type Guards
*/
export function isLiteralElement(el) {
return el.type === TYPE.literal;
return el.type === TYPE.literal;
}
export function isArgumentElement(el) {
return el.type === TYPE.argument;
return el.type === TYPE.argument;
}
export function isNumberElement(el) {
return el.type === TYPE.number;
return el.type === TYPE.number;
}
export function isDateElement(el) {
return el.type === TYPE.date;
return el.type === TYPE.date;
}
export function isTimeElement(el) {
return el.type === TYPE.time;
return el.type === TYPE.time;
}
export function isSelectElement(el) {
return el.type === TYPE.select;
return el.type === TYPE.select;
}
export function isPluralElement(el) {
return el.type === TYPE.plural;
return el.type === TYPE.plural;
}
export function isPoundElement(el) {
return el.type === TYPE.pound;
return el.type === TYPE.pound;
}
export function isTagElement(el) {
return el.type === TYPE.tag;
return el.type === TYPE.tag;
}
export function isNumberSkeleton(el) {
return !!(el && typeof el === 'object' && el.type === SKELETON_TYPE.number);
return !!(el && typeof el === "object" && el.type === SKELETON_TYPE.number);
}
export function isDateTimeSkeleton(el) {
return !!(el && typeof el === 'object' && el.type === SKELETON_TYPE.dateTime);
return !!(el && typeof el === "object" && el.type === SKELETON_TYPE.dateTime);
}
export function createLiteralElement(value) {
return {
type: TYPE.literal,
value: value,
};
return {
type: TYPE.literal,
value
};
}
export function createNumberElement(value, style) {
return {
type: TYPE.number,
value: value,
style: style,
};
return {
type: TYPE.number,
value,
style
};
}