Copal is a library for string format and interpolation.
Copal is available on npm:
npm install @botsocket/copalconst Copal = require('@botsocket/copal');
const template = Copal.parse('My first name is {first} and my full name is {first + " " + last}');
template.resolve({ first: 'John', last: 'Doe' }); // My first name is John and my full name is John DoeParses a source string to a template where:
source: The source string to parse.options: Optional options where:reference: An optional factory function of signaturefunction (path) {}that returns a resolver of signaturefunction (context) {}. Refer totemplate.resolve()for example usage.wrap: An optional string that is used to wrap the template if nested inside another. Defaults to".functions: An optional hash of function names and their implementations.constants: An optional hash of constants and their values (can be of type number, boolean, string ornull).
The template syntax consists of expressions and raw strings. Each expression must be wrapped within {} braces and failing to match them will result in the expression being returned as is:
const template = Copal.parse('This is {x}');
template.resolve({ x: 1 }); // This is 1
const template2 = Copal.parse('x {');
template2.resolve(); // x {Expressions use a simple mathematical syntax such as (x + y * 2) / 3 where named variables (x, y) are dot-separated path references resolved from the given context. Most references can be used as is, but some can create ambiguity for the parser and must be enclosed within [] braces, for example paths that contain reserved operators or array indices.
const template = Copal.parse('This is {x + 1}');
template.resolve({ x: 1 }); // This is 2
const template2 = Copal.parse('This is {[0.1] + 1}'); // 0.1 is misinterpreted as a number instead of a reference
template2.resolve([[0, 1]]); // This is 2
const template3 = Copal.parse('This is {[x.*.y]}'); // x.*.y is misinterpreted as a multiplcation instead of a reference
template3.resolve({ x: [{ y: 1 }, { y: 2 }, { y: 3 }] }); // This is 1, 2, 3Javascript Math methods and constants are supported out of the box:
const template = Copal.parse('This is {x * (PI + E)}');
template.resolve({ x: 2 }); // This is (2 * (Math.PI + Math.E))
const template2 = Copal.parse('This is {sin(x)}');
template2.resolve({ x: 1 }); // This is (Math.sin(1))To override the provided functions or constants, use the functions or constants option:
const template = Copal.parse('This is {PI}', { constants: { PI: 3.14 } });
template.resolve(); // This is 3.14
const template2 = Copal.parse('This is {random()}', {
functions: {
random() {
return 12345;
},
},
});
template2.resolve(); // This is 12345Expressions can only operate on null, strings, booleans and numbers. If a reference or a function returns undefined, null will be used instead. Internal implementation relies on native calculation so string concatenations, NaNs and Infinitys are expected (with the exception of null + "x" producing "x" instead of "nullx"). Supported operators are: ^, **, *, /, %, +, -, <, <=, >, >=, ==, !=, &&, || (in this order of precedence).
To escape an expression, use \\ before the { brace.
const template = Copal.parse('{if(x == "someValue", 1, 2)}');
template.resolve({ x: 'someValue' }); // 1
template.resolve({ x: 'somOtherValue' }); // 2
const template2 = Copal.parse('Escaped path {x\\.y.y}');
template2.resolve({ 'x.y': { y: 1 } }); // Escaped path 1
const template3 = Copal.parse('Escaped \\{a}');
template3.resolve({ a: 1 }); // Escaped {a}
const template4 = Copal.parse('Escaped \\{{a}}');
template4.resolve({ a: 1 }); // Escaped {1}
const template5 = Copal.parse('Escaped {a\\}}');
template5.resolve({ 'a}': 1 }); // Escaped 1The source of the template.
const template = Template.parse('{x}');
template.source; // {x}Evaluates expressions and resolves the current template where:
context: The context to lookup.
const template = Copal.parse('This is {x + 1}');
template.resolve({ x: 2 }); // This is 3A custom resolver can be provided to customize how references are resolved:
const template = Copal.parse('Custom reference {x}, {y}', {
reference: (path) => {
return (context) => context[`prefix-${path}`];
},
});
template.resolve({ 'prefix-x': 1, 'prefix-y': 2 }); // Custom reference 1, 2Checks if a value is a valid template where:
value: The value to check.
Copal.isTemplate(Copal.parse('x')); // true