// Copyright 2022 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import type { MessageFormatElement, LiteralElement, ArgumentElement, NumberElement, DateElement, TimeElement, SelectElement, PluralElement, PoundElement, TagElement, } from '@formatjs/icu-messageformat-parser'; import { TYPE } from '@formatjs/icu-messageformat-parser'; export type VisitorMethod = ( element: T ) => void; export type Visitor = { enterLiteral?: VisitorMethod; exitLiteral?: VisitorMethod; enterArgument?: VisitorMethod; exitArgument?: VisitorMethod; enterNumber?: VisitorMethod; exitNumber?: VisitorMethod; enterDate?: VisitorMethod; exitDate?: VisitorMethod; enterTime?: VisitorMethod; exitTime?: VisitorMethod; enterSelect?: VisitorMethod; exitSelect?: VisitorMethod; enterPlural?: VisitorMethod; exitPlural?: VisitorMethod; enterPound?: VisitorMethod; exitPound?: VisitorMethod; enterTag?: VisitorMethod; exitTag?: VisitorMethod; }; export function traverse( elements: Array, visitor: Visitor ): void { for (const element of elements) { if (element.type === TYPE.literal) { visitor.enterLiteral?.(element); visitor.exitLiteral?.(element); } else if (element.type === TYPE.argument) { visitor.enterArgument?.(element); visitor.exitArgument?.(element); } else if (element.type === TYPE.number) { visitor.enterNumber?.(element); visitor.exitNumber?.(element); } else if (element.type === TYPE.date) { visitor.enterDate?.(element); visitor.exitDate?.(element); } else if (element.type === TYPE.time) { visitor.enterTime?.(element); visitor.exitTime?.(element); } else if (element.type === TYPE.select) { visitor.enterSelect?.(element); for (const node of Object.values(element.options)) { traverse(node.value, visitor); } visitor.exitSelect?.(element); } else if (element.type === TYPE.plural) { visitor.enterPlural?.(element); for (const node of Object.values(element.options)) { traverse(node.value, visitor); } visitor.exitPlural?.(element); } else if (element.type === TYPE.pound) { visitor.enterPound?.(element); visitor.exitPound?.(element); } else if (element.type === TYPE.tag) { visitor.enterTag?.(element); traverse(element.children, visitor); visitor.exitTag?.(element); } else { unreachable(element); } } } function unreachable(x: never): never { throw new Error(`unreachable: ${x}`); }