messageformat
Version:
Intl.MessageFormat / Unicode MessageFormat 2 parser, runtime and polyfill
96 lines (95 loc) • 3.3 kB
JavaScript
/**
* Apply visitor functions to message nodes.
*
* The visitors are applied in source order, starting from the root.
* Visitors for nodes that contain other nodes may return a callback function
* that will be called with no arguments when exiting the node.
*
* If set, the `node` visitor is called for all {@link Node} values
* for which an explicit visitor is not defined.
*
* Many visitors will be called with additional arguments
* identifying some of the context for the visited node.
*
* @category Message Data Model
*/
export function visit(msg, visitors) {
const { node, pattern } = visitors;
const { functionRef = node, attributes = null, declaration = node, expression = node, key = node, markup = node, options = null, value = node, variant = node } = visitors;
const handleOptions = (options_, context) => {
if (options_) {
const end = options?.(options_, context);
if (value) {
for (const value_ of options_.values()) {
value(value_, context, 'option');
}
}
end?.();
}
};
const handleAttributes = (attributes_, context) => {
if (attributes_) {
const end = attributes?.(attributes_, context);
if (value) {
for (const value_ of attributes_.values()) {
if (value_ !== true)
value(value_, context, 'attribute');
}
}
end?.();
}
};
const handleElement = (exp, context) => {
if (typeof exp === 'object') {
let end;
switch (exp.type) {
case 'expression': {
end = expression?.(exp, context);
if (exp.arg)
value?.(exp.arg, context, 'arg');
if (exp.functionRef) {
const endA = functionRef?.(exp.functionRef, context, exp.arg);
handleOptions(exp.functionRef.options, context);
endA?.();
}
handleAttributes(exp.attributes, context);
break;
}
case 'markup': {
end = markup?.(exp, context);
handleOptions(exp.options, context);
handleAttributes(exp.attributes, context);
break;
}
}
end?.();
}
};
const handlePattern = (pat) => {
const end = pattern?.(pat);
for (const el of pat)
handleElement(el, 'placeholder');
end?.();
};
for (const decl of msg.declarations) {
const end = declaration?.(decl);
if (decl.value)
handleElement(decl.value, 'declaration');
end?.();
}
if (msg.type === 'message') {
handlePattern(msg.pattern);
}
else {
if (value)
for (const sel of msg.selectors)
value(sel, 'selector', 'arg');
for (const vari of msg.variants) {
const end = variant?.(vari);
if (key)
vari.keys.forEach(key);
handlePattern(vari.value);
end?.();
}
}
}