tag-params
Version:
Transform a generic string into parameters suitable for template literals functions tags
96 lines (88 loc) • 3.62 kB
JavaScript
/**
* @typedef {object} ParseResult an object with parsed results
* @property {string[]} template - the list of chunks around interpolations
* @property {string[]} values - interpolations as strings
*/
/**
* @typedef {[string[], ...any[]]} TagArguments an array to use as template
* literals tag arguments
*/
/**
* @callback Partial a callback that re-evaluate each time new data associated
* to the same template-like array.
* @param {object} [object] the optional data to evaluate as interpolated values
* @returns {TagArguments} an array to use as template literals tag arguments
*/
/**
* The default `transform` callback
* @param {string} value the interpolation value as string
*/
const noop = value => value;
/**
* The default "null" fallback when no object is passed to the Partial.
*/
const fallback = Object.create(null);
/**
* Given a string and an optional object carrying references used through
* such string interpolation, returns an array that can be used within any
* template literal function tag.
* @param {string} content the string to parse/convert as template chunks
* @param {object} [object] the optional data to evaluate as interpolated values
* @returns {TagArguments} an array to use as template literals tag arguments
*/
export const params = (content, object) => partial(content)(object);
/**
* Given a string and an optional function used to transform each value
* found as interpolated content, returns an object with a `template` and
* a `values` properties, as arrays, containing the template chunks,
* and all its interpolations as strings.
* @param {string} content the string to parse/convert as template chunks
* @param {function} [transform] the optional function to modify string values
* @returns {ParseResult} an object with `template` and `values` arrays.
*/
export const parse = (content, transform) => {
const fn = transform || noop;
const template = [];
const values = [];
const {length} = content;
let i = 0;
while (i <= length) {
let open = content.indexOf('${', i);
if (-1 < open) {
template.push(content.slice(i, open));
open = (i = open + 2);
let close = 1;
// TODO: this *might* break if the interpolation has strings
// containing random `{}` chars ... but implementing
// a whole JS parser here doesn't seem worth it
// for such irrelevant edge-case ... or does it?
while (0 < close && i < length) {
const c = content[i++];
close += c === '{' ? 1 : (c === '}' ? -1 : 0);
}
values.push(fn(content.slice(open, i - 1)));
}
else {
template.push(content.slice(i));
i = length + 1;
}
}
return {template, values};
};
/**
* Given a string and an optional function used to transform each value
* found as interpolated content, returns a callback that can be used to
* repeatedly generate new content from the same template array.
* @param {string} content the string to parse/convert as template chunks
* @param {function} [transform] the optional function to modify string values
* @returns {Partial} a function that accepts an optional object to generate
* new content, through the same template, each time.
*/
export const partial = (content, transform) => {
const {template, values} = parse(content, transform);
const args = [template];
const rest = Function(
'return function(){with(arguments[0])return[' + values + ']}'
)();
return object => args.concat(rest(object || fallback));
};