object-template-string
v0.1.0
Published
Template strings with pluggable reference resolution, conditional branches and variable trees
Readme
object-template-string
Template strings with pluggable reference resolution, conditional branches, and variable trees.
No dependencies. Knows nothing about forms, stores, or rules engines: it is given a way to look references up, and it fills strings in.
Extracted from the three separate ${...} implementations that had grown up in
object-rules-engine (stringTemplateReplace), the graphics connector API (substituteString),
and the element visualizer's localization templates, so that json-form-core and the element
builder can share one.
Syntax
${reference}
${reference|formatter}
${reference|formatter:arg1,arg2|otherFormatter}
\${not a reference}A reference is whatever the resolver understands. Nothing is assumed about it, so the path language a form already writes goes through unchanged:
${team_1} a named variable
${factoids/0/value} a path into a tree of variables
${$[node_value]../team_1/team_id} a form path, resolved by json-form-coreA string being typed always reads as something: an unclosed ${, or a ${} pointing at nothing,
is text rather than an error.
Filling a template in
import { renderTemplateString } from "object-template-string";
renderTemplateString("${team_1} vs ${team_2}, ${kickoff|date}", {
team_1: "Bears",
team_2: "Packers",
kickoff: "2026-09-05T17:00:00Z"
});
// "Bears vs Packers, Sep 5, 2026"values may be an object keyed by reference, a nested object to follow references through, a Map,
or a function of the reference.
Values that are not known yet
options.missing says what is shown where a value has not arrived:
| missing | shows |
|---|---|
| empty (default) | nothing |
| name | what the reference is called, via options.nameFromReference |
| keep | the reference as it was written |
| throw | refuses |
name is what a template string editor wants: before the data loads, the editor shows
Date: Formatted Value rather than : .
Asynchronously, and as things change
await substituteTemplateString(template, async (reference) => fetchValue(reference));
const unsubscribe = subscribeTemplateString(template, (reference, onValue) =>
store.subscribe(pathFor(reference), onValue), (string) => { ... });subscribeTemplateString follows each distinct reference once, gives the string back only when it
comes out differently, and emits once for the whole first round of subscriptions rather than once
per reference.
Reading differently depending on the data
A template may be a list of branches. The first whose condition passes is used; a branch with no condition fits anything:
[
{ "condition": { "and": { "$[node_value]event_type": { "eq": "game" } } },
"template": "${team_1} vs ${team_2}, ${timestamp|date}" },
{ "template": "${team} bye week, ${timestamp|date}" }
]Conditions are somebody else's language. conditionsPass and conditionArgs are handed in — pass
object-rules-engine's and the conditions above are its DSL:
subscribeTemplateConfig(config, (string) => { ... }, {
subscribeResolver,
conditionsPass: (condition, getValue) => checkConditions(condition, { getValue }),
conditionArgs: (condition) => conditionsArgs(condition)
});Everything every branch points at is followed, not only the branch fitting now, so changing case needs no resubscribing.
What a template may point at
A graphic says what it can offer, in the shape it offers it in:
{
name: "Big Number",
children: {
factoids: {
name: "Factoids",
children: {
0: {
name: "Factoid 1",
children: {
date: { name: "Date" }, // not loaded yet
value: { name: "Formatted Value", value: "4.2%" } // known
}
}
}
}
}
}A branch carrying no value is one whose value is not known yet.
flattenVariableTree(tree); // a flat list an editor can offer, with breadcrumb labels
variableTreeResolver(tree); // fill a template in from the tree
previewTemplateString(t, tree); // values where known, names where notFormatters
number, percent, percentPoints, sign, date, time, datetime, upper, lower, trim,
fallback, json. Add your own with options.formatters; options.locale and options.timeZone
are passed to all of them.
options.transformations is a per-reference function run before formatters, which is how the
visualizer's localization templates already work.
Tests
npm install
npm test