Template literals come in handy for code generation
Recently I found that JavaScript template strings are very helpful for usages like code generation
The task
Provided that I am working to transform a Vue-like DSL to normal HTML.
First, the most essential usage should be supported
<div>hello world</div>
to
<div>hello world</div>
The codegen logic probably looks like this:
function generate(ast){
// ...
return `<${ast.tag}>${ast.text}</${ast.tag}>`
}
Although the fact would be more complicated, it’s easy, right?
But once there is a user-defined variable, it would be much different. For example,
<div>{{ msg }}</div>
should be transformed to
`<div>` + msg + '</div>'
And the codgen logic would become:
generate
outputs a runtime expression, which is represented in string format and returns an HTML string. Because msg
is a runtime variable and it should be represented as an expression in the generated code
It’s obviously cumbersome even at the first glance since I have to decide if a snippet should be wrapped with quotes "
and use the connector sign +
frequently to concatenate them.
How to improve it?
Tagged template
There are 3 kinds of things we have to handle
- Hand-written characters like
<
,/>
- Strings in the generated code like
ast.tag
- Expressions in the generated code like
ast.binding
It needs a simple and clear way to triage them. And JavaScript template strings come rescue
Besides using template strings to facilitate daily string concatenation, we can define our custom concatenation which is called “tagged templates”.
For example,
literals
is an array that contains all hand-written literal stringsexps
is an array that contains all occurred variables
So what my custom template will do is
- wrap literal strings with quotes
"
- wrap or not wrap the variables with quotes based on their type
- concatenate them with plus sign
+
The template’s implementation will be like this:
It is oversimplified and only for illustrative purposes
Now I can easily generate the target code like I use everyday template strings, but with a function name. No need to think about the quotes and plus any more!
Recall the 3 kinds of things I mentioned above, for expressions in the generated code, wrap them in an object’s property expression
to distinguish them from the second kind