Converting strings to template strings

# KOLANICH (10 years ago)

I needed a functionality but haven't found it. See stackoverflow.com/questions/29182244/convert-a-string-to-a-template-string for more details. I think that this should be included into standard;

Also we need a standard format string functionality like msdn.microsoft.com/en-us/library/system.string.format.aspx and docs.python.org/2/library/string.html#string

# Andrea Giammarchi (10 years ago)

There's no such functionality indeed but you might want to have a look at this gist: gist.github.com/WebReflection/8f227532143e63649804

It gives you the ability to write 'test1 ${1 + 2} test2 ${3 + 4}' .template(); and read test1 3 test2 7 or to pass an object similar to .Net String.format so that your Stack overflow code would be like the following:


let a = "b:${b}";
let b = 10;

console.log(a.template({b:b}));

// or

console.log(a.template({b:27}));

You pass named properties and it works with nested properties too (i.e. ${down.the.road})

It does use Function which is safe, compared to eval, and needed to eventually de-opt from 'use strict' but of course you could write your own parser avoiding Function completely.

Finally, I agree it would be nice to be able to have a standard way to template strings in JS, the templating as it is plays very poorly with runtime generated strings, using eval for that looks the dirtiest thing on earth.

# Mark S. Miller (10 years ago)

On Sun, Mar 22, 2015 at 6:46 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

There's no such functionality indeed but you might want to have a look at this gist: gist.github.com/WebReflection/8f227532143e63649804

It gives you the ability to write 'test1 ${1 + 2} test2 ${3 + 4}' .template(); and read test1 3 test2 7 or to pass an object similar to .Net String.format so that your Stack overflow code would be like the following:


let a = "b:${b}";
let b = 10;

console.log(a.template({b:b}));

// or

console.log(a.template({b:27}));

You pass named properties and it works with nested properties too (i.e. ${down.the.road})

It does use Function which is safe,

Function is safe almost everywhere, but it is worth pointing out

bugs.webkit.org/show_bug.cgi?id=106160, bugs.webkit.org/show_bug.cgi?id=131137 test_CANT_SAFELY_VERIFY_SYNTAX at code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/repairES5.js#3198 repair_CANT_SAFELY_VERIFY_SYNTAX at code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/repairES5.js#4170

After the repair, the Function constructor is safe again on Safari, but at considerable expense.

# Andrea Giammarchi (10 years ago)

thanks for pointing that out but if I understand the problem correctly then the snippet I've suggested concatenates strings and will never produce those problematic syntax errors. Can I say it's still safe? Or do you think it might have some problem in Safari?

# Mark Miller (10 years ago)

The pattern [\S\s]*? admits a lot. Why are you confident that it can't contain a string that, for example, closes the function with an unbalanced "}", then has an evil expression which evaluates, followed by an unbalanced "{" so the whole thing still parses?

# Mark S. Miller (10 years ago)

Why on earth are you avoiding strict mode? I can't even begin to think of the hazards from handling a user-provided string to be parsed non-strict. Nor should anyone bother; sloppy mode is a mess that should simply be avoided for all new code -- especially in the careful handling of a user provided string!

# Mark S. Miller (10 years ago)

Is it your intention to have these weird pseudo-template strings skip over enclosing lexical scopes and go right to the global scope? What document does

function(document) {
  ...
  '....${...document...}...'.template()
  ...
}

access? With a genuine template string

function(document) {
  ...
  `....${...document...}...`
  ...
}

it would access the lexically enclosing one, not the global one.

Alternatively, if you use SES's confine[1] rather than new Function(...).call(...), the string would evaluate in an environment whose only bindings are the safe globals and the properties of the second argument to confine.

[1] code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#901

# Andrea Giammarchi (10 years ago)

the snippet is extremely simplified and just works for 99.9% of cases it's meant to be used, a way to provide properties via objects within a string used as template. The string should not be parsed in the wild, same you won't evaluate ever string templates without knowing the source. Of course if you use strings to implement eval like logic you are doing it wrong and makes no sense to use the template but again, that snippets provides what's not possible with string templates.

To Mark S. Miller, the strict is a must have for the with statement, that snippets works down to IE5 so I don't actually see or understand your surprise.

Of course I can implement a full syntax parser instead but then I'd loose on size, simplicity, and portability (performance)

String templates are not good for templates, these work only for statically defined code and in place meaning you cannot grab content from a DB and inject values like names or numbers as you would do with any templating system.

So what is your suggested solution?

# Mark S. Miller (10 years ago)

On Sun, Mar 22, 2015 at 10:27 AM, Andrea Giammarchi < andrea.giammarchi at gmail.com> wrote:

the snippet is extremely simplified and just works for 99.9% of cases it's meant to be used, a way to provide properties via objects within a string used as template. The string should not be parsed in the wild, same you won't evaluate ever string templates without knowing the source. Of course if you use strings to implement eval like logic you are doing it wrong and makes no sense to use the template but again, that snippets provides what's not possible with string templates.

To Mark S. Miller, the strict is a must have for the with statement, that snippets works down to IE5 so I don't actually see or understand your surprise.

Of course I can implement a full syntax parser instead but then I'd loose on size, simplicity, and portability (performance)

Of course. SES's confine function < code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#901>

does not use a parser on any browser with adequate ES5 support, i.e., any modern browser. Even on Safari, it compensates for Safari's code injection vulnerabilities bugs.webkit.org/show_bug.cgi?id=106160 using the

verifyStrictFunctionBodyByEvalThrowing trick at < code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/repairES5.js#914>.

Even at the 100x slowdown compared to 'new Function(...)' which we use on other browsers, it is still much cheaper than doing our own parse -- if nothing else, by virtue of not needing to download a parser.

String templates are not good for templates, these work only for statically defined code and in place meaning you cannot grab content from a DB and inject values like names or numbers as you would do with any templating system.

I still don't understand this. Could you give a simple example and explain what it is trying to do? Thanks.

So what is your suggested solution?

If it is indeed well motivated not to use templating, and to eval user-provided code strings, then use SES's confine.

The implementation of SES's confine does resort to an internal bit of sloppy code in order to do a 'with', like your's. But SES puts the user-provided source string inside a strict function nested within this 'with'. See securableWrapperSrc at < code.google.com/p/google-caja/source/browse/trunk/src/com/google/caja/ses/startSES.js#702>.

No user-provided string is parsed in sloppy mode (except for the ugly-but-harmless double check in verifyStrictFunctionBodyByEvalThrowing needed to allow lexically nested functions).

SES needs to resort to a 'with' because it uses the scope object to emulate an ES5 global scope, where the global variables/properties change over time. In your case, are you interested in a changing set of variable/property names? If a fixed set, do the values of these properties change over time? Are you trying to turn writes to the variables into writes to the properties? In either case, yes, you still need an internal sloppy 'with' like SES's implementation.

If you're only concerned about a fixed set of properties, and only about capturing their current values, then you don't need a 'with'. Please let me know if you're interested in how.

# Andrea Giammarchi (10 years ago)

Mark, is really deadly simple 5 lines of code script which aim is to access properties without having conflicts with names (using with(this)) without needing SES or anything else in, without needing to change, it's simplified string templating that works with dynamic strings.

Why do we need dynamic strings? Because internationalization, just to name one.

// your lang object
var lang = {
  en: {
    greetings: 'Hello ${name}!'
  },
  it: {
    greetings: 'Ciao ${name}!'
  }
};

// one i18n string to show
var greetings = lang[user.lang].greetings;

// there you go
alert(greetings.template(user));

The current gist is even overenginered because accepts an optional replacer per each property but the concept is the following:


function getString() {
  with (this) return name;
}

alert('Ciao ' + getString.call({name: 'Mark'}) + '!');

It has been used for I think about 10 years in most famous templating solutions out there because it's a simple and pragmatic approach. I don't see real-world harm on it, just convenient ease.

Of course performance is not optimal and of course you don't pass user defined strings there but even doing so I don't see any difference from opening a console writing down whatever that is. It's Function, and thanks gosh still capable of giving with ability which in this case I think is extremely convenient.

However, now I am curious about how would access those properties without using with and avoiding a whole parser for the string.

Thanks and Best

# Jason Orendorff (10 years ago)

On Mon, Mar 23, 2015 at 1:55 AM, Mark S. Miller <erights at google.com> wrote:

String templates are not good for templates, these work only for statically defined code and in place meaning you cannot grab content from a DB and inject values like names or numbers as you would do with any templating system.

I still don't understand this. Could you give a simple example and explain what it is trying to do? Thanks.

Filling in templates with data is extremely useful in web development. Many libraries do just this one thing and nothing else. Mustache, JSRender, and nano are all popular. Ruby has ERB built-in; there's Jinja for Python and a million more.

All these libraries work something like this:

// You compile a template into an object.
var t = TemplateLibrary.compile(templateFileName);
// You pass some data to the template object and it builds a string.
resultDiv.innerHTML = t.render({data: resultArray});

Each library is thus a compiler or interpreter for a template language. Template languages are interesting. The best ones are designed for simplicity and clarity. Some offer a weak safety property: e.g. a template can't execute arbitrary JS code of its choice and will do no worse than call getters on the objects you pass to it. Various template languages thus have richer or poorer expression syntax, by design. All have straightforward loop syntax. They are, in short, superficially like ES6 template strings, but otherwise about as different as can be. ES6 template strings are not a replacement for template libraries.

But from the few data points I have, approximately 100% of web developers, when they first hear "template strings are in ES6", think that means something like Mustache in the standard library. Some initially try to use the feature that way and get frustrated. I expect widespread confusion on this point.

# Domenic Denicola (10 years ago)

Is it too late to go back to quasi-literals?

(Joking!)

# Axel Rauschmayer (10 years ago)

But from the few data points I have, approximately 100% of web developers, when they first hear "template strings are in ES6", think that means something like Mustache in the standard library. Some initially try to use the feature that way and get frustrated. I expect widespread confusion on this point.

Pedagogically, a name change would make a lot of sense, yes! I’ve even seen fairly knowledgeable people get this wrong.

# Brendan Eich (10 years ago)

Jason Orendorff wrote:

But from the few data points I have, approximately 100% of web developers, when they first hear "template strings are in ES6", think that means something like Mustache in the standard library. Some initially try to use the feature that way and get frustrated. I expect widespread confusion on this point.

This.

A function wrapped around a template string, where the function's parameters occur in embedded expressions, goes a long way. But you have to write the function, after teaching people the basics and apologizing for misleading them with the t-word.

# Andrea Giammarchi (10 years ago)

Even a function wouldn't scale that well for i18n purpose, 'cause you should write either a function per language or each language switch statement per function.

I see current ES6 string templates good for debug purpose only, and not much else ... maybe English centric developers tools so few, definitively good use cases, but nothing that useful or powerful for the known Web.


var template = ``Hello ${name} !``;

template({name: 'there'}); // Hello there !

But I know, double back-tick might look too like double rainbow: "OMG what does it mean" ??!

# Kyle Simpson (10 years ago)

I've dubbed them "Interpolated String Literals" (or "Interpolated Strings") in my writings and materials going forward. I mention "Template Literals" and then explain why it's a bad name.

# Michael Ficarra (10 years ago)

What have you been calling the MemberExpression TemplateLiteral and CallExpression TemplateLiteral forms?

# caridy (10 years ago)

In general, if you want to have control over those arguments, reusing the template without the need of the wrapping function, you will have to provide a way to apply logic inside the template string, that complicates things a lot.

As for i18n, ICU messages can solve that part of the puzzle providing structure and logic for complex messages. FYI we are working

# Kyle Simpson (10 years ago)

What have you been calling the MemberExpression TemplateLiteral and CallExpression TemplateLiteral forms?

Those are two variations of the "Tagged String Literals" form.