/tag-params

Transform a generic string into parameters suitable for template literals functions tags.

Primary LanguageJavaScriptISC LicenseISC

tag-params

Build Status Coverage Status

Transform a generic string into parameters suitable for template literals functions tags.

import {params} from 'tag-params';
// const {params} = require('tag-params');
// <script src="//unpkg.com/tag-params"></script>

console.log(
  params('Random: ${Math.random()}!')
);
// [["Random: ", "!"], 0.3456787643]

console.log(
  params('Hello ${user}', {user: 'test'})
);
// [["Hello ", ""], "test"]

// invoke tags through the returned parameters
genericTag(...params(content, namespace));

API

There are 3 utilities exported by this module, so that accordingly with your import, you should get:

  • params, the main utility, which parses and resolves values in one go.
  • parse, which returns a {template, values} object, with mapped "chunks" in the template, and the list of values (interpolations/holes), where each value is a string.
  • partial, which uses parse and returns a callback that will map new values through the optional object passed along.

params(content:string, object?:any) => [string[], ...any[]]

It's the "default" use case of this utility. It parses the content and returns a [template, ...values] Array with values retrieved through the optional object. If no object is passed along, it simply evaluates interpolations as plain JavaScript.

This utility is a shortcut for a one-off partial(content)(object) call.

parse(content:string, transform?:function) => {template:string[], values:string[]}

It parses a string, and it uses the optional transform callback, which is no-op as default, to assign each value to the list of expected values.

The transform optional callback is specially useful when the interpolated content might contains HTML normalized chars, such as value =&gt; stuff(value) instead of value => stuff(value), which is normal when the content is retrieved via element.innerHTML, as example.

The template property contains all chunks around ${...} interpolations, while values contains all interpolations content as string.

partial(content:string, transform?:function) => (object?) => [string[], ...any[]]

This utility parses the content through an optional transform, and returns a callback that accepts a new object each time.

This is particularly useful to avoid parsing the same template content over and over, and just update its interpolation values through the optional object.

import {partial} from 'tag-params';
const update = partial('Hello ${user}!');

console.log(update({user: 'First'}));
// [["Hello ", "!"], "First"]

console.log(update({user: 'Second'}));
// [["Hello ", "!"], "Second"]

// always true
console.assert(
  update({user: 'First'})[0] ===
  update({user: 'Second'})[0]
);

The main advantage of this utility is that it parses the content and it creates the template Array only once, meaning every template literal based library could benefit from it, using the uniqueness of the template to parse complex chunks of HTML, or anything else, once, enabling repeated updates at almost zero performance cost.

Use Cases

The most common/requested use case for this is likely landing templates on the page and use their content, as shown in this CodePen example:

<template id="list">
  <ul>${items.map(function (item) {
    return html`<li>${item.text}</li>`;
  })}</ul>
</template>
<div id="app"></div>

<script type="module">
import {params} from '//unpkg.com/tag-params?module';
import {render, html} from '//unpkg.com/uhtml?module';

render(
  document.getElementById('app'),
  html(...params(
    document.getElementById('list').innerHTML,
    {
      html,
      items: [{text: 'first'}, {text: 'second'}]
    }
  ))
);
</script>

This works well with libraries such as uhtml, lighterhtml, or hyperHTML, as well as any library based on template literals tags.

However, this module can work with literally any possible template literal tag function, as these all share the same signature, hence will accept transformed chunks, as template, and the rest of the interpolations as values.

Caveats

Please note this module inevitably needs/uses Function to evaluate the code within a with statement, as there's no other way to evaluate interpolations through passed data.

Moreover, the current interpolations parser is extremely rudimental, it simply skips extra { and } chars within the value, but it doesn't parse all possible JS syntax.

This means that if an interpolation contains a string such as ${"breaking { char"} or ${"breaking } char"} the result will break.

The good practice here is to pass strings via the object, instead of hard coding these within interpolations, as this won't likely get fixed any time soon (if ever).

Compatibility

Every JavaScript engine, either client, server, or IoT, that supports string[index] access, as I couldn't bother myself adding a slow and jurassic string.charAt(index) in the code.