Skip to content

Commit

Permalink
lower non-tag template literals to es5 (#297)
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Aug 12, 2020
1 parent 3087d02 commit ce281d6
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
Initial testing shows that upgrading Go reduces the esbuild binary size on macOS from 7.4mb to 5.3mb, which is a 30% smaller binary! I assume the binary size savings are similar for other platforms. Run-time performance on the esbuild benchmarks seems consistent with previous releases.

* Lower non-tag template literals to ES5 ([#297](https://github.com/evanw/esbuild/issues/297))

You can now use non-tag template literals such as `` `abc` `` and `` `a${b}c` `` with `--target=es5` and esbuild will convert them to string addition such as `"abc"` and `"a" + b + "c"` instead of reporting an error.

## 0.6.20

* Symbols are now renamed separately per chunk ([#16](https://github.com/evanw/esbuild/issues/16))
Expand Down
1 change: 1 addition & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,7 @@ type EString struct{ Value []uint16 }

type TemplatePart struct {
Value Expr
TailLoc Loc
Tail []uint16
TailRaw string // This is only filled out for tagged template literals
}
Expand Down
39 changes: 36 additions & 3 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1849,15 +1849,47 @@ func (p *parser) parsePrefix(level ast.L, errors *deferredErrors, flags exprFlag
return ast.Expr{Loc: loc, Data: &ast.EString{Value: value}}

case lexer.TNoSubstitutionTemplateLiteral:
p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range())
head := p.lexer.StringLiteral
p.lexer.Next()
if p.UnsupportedFeatures.Has(compat.TemplateLiteral) {
return ast.Expr{Loc: loc, Data: &ast.EString{Value: head}}
}
return ast.Expr{Loc: loc, Data: &ast.ETemplate{Head: head}}

case lexer.TTemplateHead:
p.markSyntaxFeature(compat.TemplateLiteral, p.lexer.Range())
head := p.lexer.StringLiteral
parts := p.parseTemplateParts(false /* includeRaw */)
if p.UnsupportedFeatures.Has(compat.TemplateLiteral) {
var value ast.Expr
if len(head) == 0 {
// "`${x}y`" => "x + 'y'"
part := parts[0]
value = ast.Expr{Loc: loc, Data: &ast.EBinary{
Op: ast.BinOpAdd,
Left: part.Value,
Right: ast.Expr{Loc: part.TailLoc, Data: &ast.EString{Value: part.Tail}},
}}
parts = parts[1:]
} else {
// "`x${y}`" => "'x' + y"
value = ast.Expr{Loc: loc, Data: &ast.EString{Value: head}}
}
for _, part := range parts {
value = ast.Expr{Loc: loc, Data: &ast.EBinary{
Op: ast.BinOpAdd,
Left: value,
Right: part.Value,
}}
if len(part.Tail) > 0 {
value = ast.Expr{Loc: loc, Data: &ast.EBinary{
Op: ast.BinOpAdd,
Left: value,
Right: ast.Expr{Loc: part.TailLoc, Data: &ast.EString{Value: part.Tail}},
}}
}
}
return value
}
return ast.Expr{Loc: loc, Data: &ast.ETemplate{Head: head, Parts: parts}}

case lexer.TNumericLiteral:
Expand Down Expand Up @@ -3172,13 +3204,14 @@ func (p *parser) parseTemplateParts(includeRaw bool) []ast.TemplatePart {
for {
p.lexer.Next()
value := p.parseExpr(ast.LLowest)
tailLoc := p.lexer.Loc()
p.lexer.RescanCloseBraceAsTemplateToken()
tail := p.lexer.StringLiteral
tailRaw := ""
if includeRaw {
tailRaw = p.lexer.RawTemplateContents()
}
parts = append(parts, ast.TemplatePart{Value: value, Tail: tail, TailRaw: tailRaw})
parts = append(parts, ast.TemplatePart{Value: value, TailLoc: tailLoc, Tail: tail, TailRaw: tailRaw})
if p.lexer.Token == lexer.TTemplateTail {
p.lexer.Next()
break
Expand Down
2 changes: 1 addition & 1 deletion internal/parser/parser_lower.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (p *parser) markSyntaxFeature(feature compat.Feature, r ast.Range) (didGene
name = "object literal extensions"

case compat.TemplateLiteral:
name = "template literals"
name = "tagged template literals"

case compat.Destructuring:
name = "destructuring"
Expand Down
17 changes: 11 additions & 6 deletions internal/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2374,14 +2374,19 @@ func TestES5(t *testing.T) {
"<stdin>: error: Transforming destructuring to the configured target environment is not supported yet\n")
expectParseErrorTarget(t, 5, "for ({} in []);",
"<stdin>: error: Transforming destructuring to the configured target environment is not supported yet\n")
expectParseErrorTarget(t, 5, "`abc`;",
"<stdin>: error: Transforming template literals to the configured target environment is not supported yet\n")
expectParseErrorTarget(t, 5, "`a${b}c`;",
"<stdin>: error: Transforming template literals to the configured target environment is not supported yet\n")
expectPrintedTarget(t, 5, "`abc`;", "\"abc\";\n")
expectPrintedTarget(t, 5, "`a${b}`;", "\"a\" + b;\n")
expectPrintedTarget(t, 5, "`${a}b`;", "a + \"b\";\n")
expectPrintedTarget(t, 5, "`${a}${b}`;", "a + \"\" + b;\n")
expectPrintedTarget(t, 5, "`a${b}c`;", "\"a\" + b + \"c\";\n")
expectPrintedTarget(t, 5, "`a${b}${c}`;", "\"a\" + b + c;\n")
expectPrintedTarget(t, 5, "`a${b}${c}d`;", "\"a\" + b + c + \"d\";\n")
expectPrintedTarget(t, 5, "`a${b}c${d}`;", "\"a\" + b + \"c\" + d;\n")
expectPrintedTarget(t, 5, "`a${b}c${d}e`;", "\"a\" + b + \"c\" + d + \"e\";\n")
expectParseErrorTarget(t, 5, "tag`abc`;",
"<stdin>: error: Transforming template literals to the configured target environment is not supported yet\n")
"<stdin>: error: Transforming tagged template literals to the configured target environment is not supported yet\n")
expectParseErrorTarget(t, 5, "tag`a${b}c`;",
"<stdin>: error: Transforming template literals to the configured target environment is not supported yet\n")
"<stdin>: error: Transforming tagged template literals to the configured target environment is not supported yet\n")
expectParseErrorTarget(t, 5, "class Foo { constructor() { new.target } }",
"<stdin>: error: Transforming class syntax to the configured target environment is not supported yet\n"+
"<stdin>: error: Transforming object literal extensions to the configured target environment is not supported yet\n"+
Expand Down

0 comments on commit ce281d6

Please sign in to comment.