mauss
Version:
lightweight, modular, type-safe utilities
57 lines (56 loc) • 2.65 kB
JavaScript
/**
* A type-safe template string function that accepts a string template with placeholders and returns a function that can take in an object with the same keys as the placeholders. The function will replace the placeholders with the corresponding values from the object. Parameters of the braces can be prefixed with a question mark `?` to make it optional to the type system and will fallback to an empty string if it's not defined in the table.
*
* This assumes the braces inside the template string are balanced and not nested. The function will not throw an error if the braces are not balanced, but the result will be unexpected. If you're using TypeScript and are passing a [string literal](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types), it will point out any unbalanced braces by throwing an error from the compiler.
*
* The template string is parsed into an array of strings, which are then executed with the provided table of functions, which is an object with the key being the name of the braces from the template string, and the value being the function to manipulate the name of the braces.
*
* @example
*
* ```javascript
* import { tsf } from 'mauss/std';
* const render = tsf('https://api.example.com/v1/{category}/{id}');
* function publish({ category, id }) {
* const prefix = // ...
* const url = render({
* category: () => category !== 'new' && category,
* id: (v) => prefix + uuid(`${v}-${id}`),
* });
* return fetch(url);
* }
* ```
*
* @returns a function to replace the placeholders
*/
export function tsf(template) {
const parts = [];
for (let i = 0, start = 0; i < template.length; i += 1) {
if (template[i] === '{') {
if (i > start)
parts.push(template.slice(start, i));
const end = template.indexOf('}', i);
if (end === -1 /** missing closing */) {
parts.push(template.slice(i));
break;
}
parts.push(template.slice(i + 1, end));
start = (i = end) + 1;
}
else if (i === template.length - 1) {
parts.push(template.slice(start));
}
}
return function render(table) {
let transformed = '';
for (let i = 0; i < parts.length; i += 1) {
const replace = table[parts[i]];
if (typeof replace === 'function') {
transformed += replace(parts[i]) || '';
}
else {
transformed += replace || (parts[i][0] !== '?' && parts[i]) || '';
}
}
return transformed;
};
}