Unless there's some other way to do it in Javascript, the best way seems to be to decorate the original function with a wrapper to process the literal tag. Assuming the $${variable}
is what's wanted, something like this seems to be the trick:
export function literal_aware_tag(func) {
return (strings, ...values) => {
const new_strings = [];
const new_values = [];
const new_raw = [];
new_strings.raw = new_raw;
const len = strings.length;
let cur_str = '';
let cur_raw = '';
for (let i = 0; i < len - 1; i++) {
if (strings[i].endsWith('$')) {
cur_str += strings[i].slice(0, -1) + values[i];
cur_raw += strings.raw[i].slice(0, -1) + values[i];
} else {
new_strings.push(cur_str + strings[i]);
new_raw.push(cur_raw + strings.raw[i]);
new_values.push(values[i]);
cur_str = '';
cur_raw = '';
}
}
new_strings.push(cur_str + strings[len - 1]);
new_raw.push(cur_raw + strings.raw[len - 1]);
return func(new_strings, ...new_values);
};
}
or in TypeScript:
declare type TemplateTag = (strings: TemplateStringsArray, ...values: any[]) => any;
export function literal_aware_tag(func: TemplateTag): TemplateTag {
return (strings: TemplateStringsArray, ...values) => {
const new_strings = [];
const new_values = [];
const new_raw = [];
(new_strings as any).raw = new_raw;
const len = strings.length;
let cur_str = '';
let cur_raw = '';
for (let i = 0; i < len - 1; i++) {
if (strings[i].endsWith('$')) {
cur_str += strings[i].slice(0, -1) + values[i];
cur_raw += strings.raw[i].slice(0, -1) + values[i];
} else {
new_strings.push(cur_str + strings[i]);
new_raw.push(cur_raw + strings.raw[i]);
new_values.push(values[i]);
cur_str = '';
cur_raw = '';
}
}
new_strings.push(cur_str + strings[len - 1]);
new_raw.push(cur_raw + strings.raw[len - 1]);
return func(<TemplateStringsArray><any> new_strings, ...new_values);
};
}
Then a using application can decorate a non-literal-aware tag with:
import {html as original_html} from 'lit';
const html = literal_aware_tag(original_html);
function my_func(tag, value) {
return html`<$${tag}>${value}</$${tag}>`;
}
This seems to be a general solution to tagged literals.
For the specific example of Lit (lit-html
) this usage will invalidate the caching functions that make Lit so fast, but there are some cases where it's worth it to interpolate literal text that cannot be put into Lit syntax. (Particularly when decorating the css
tag to include house style, like a certain font-family set, that is stored in a variable once, and is not expected to change while the application is running, but avoids hardcoding it multiple times, but still allowing parts of the application that will change to run).
hello ${name}
\`` is. – Michael Scott Asato Cuthbert Dec 28 '22 at 09:32