Skip to content

Commit

Permalink
fix #1349: quote modern unicode object properties
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jun 7, 2021
1 parent 236039d commit 5065800
Show file tree
Hide file tree
Showing 16 changed files with 204 additions and 152 deletions.
21 changes: 13 additions & 8 deletions CHANGELOG.md
Expand Up @@ -2,22 +2,27 @@

## Unreleased

* Add support for ES5-style identifiers ([#1349](https://github.com/evanw/esbuild/issues/1349))
* Quote object properties that are modern Unicode identifiers ([#1349](https://github.com/evanw/esbuild/issues/1349))

In ES6 and above, an identifier is a character sequence starting with a character in the `ID_Start` Unicode category and followed by zero or more characters in the `ID_Continue` Unicode category, and these categories must be drawn from Unicode version 5.1 or above.

But in ES5, an identifier is a character sequence starting with a character in one of the `Lu, Ll, Lt, Lm, Lo, Nl` Unicode categories and followed by zero or more characters in the `Lu, Ll, Lt, Lm, Lo, Nl, Mn, Mc, Nd, Pc` Unicode categories, and these categories must be drawn from Unicode version 3.0.
But in ES5, an identifier is a character sequence starting with a character in one of the `Lu, Ll, Lt, Lm, Lo, Nl` Unicode categories and followed by zero or more characters in the `Lu, Ll, Lt, Lm, Lo, Nl, Mn, Mc, Nd, Pc` Unicode categories, and these categories must be drawn from Unicode version 3.0 or above.

Previously esbuild always used the ES6+ identifier validation test but with this release, it will use the ES5 validation test when the target environment doesn't support ES6+ identifiers. This identifier validation test is used in decisions related to code printing. For example:
Previously esbuild always used the ES6+ identifier validation test when deciding whether to use an identifier or a quoted string to encode an object property but with this release, it will use the ES5 validation test instead:

```
$ echo x.ꓷꓶꓲꓵꓭꓢꓱ | ./esbuild --charset=utf8
x.ꓷꓶꓲꓵꓭꓢꓱ;
```js
// Original code
x.ꓷꓶꓲꓵꓭꓢꓱ = { ꓷꓶꓲꓵꓭꓢꓱ: y };

$ echo x.ꓷꓶꓲꓵꓭꓢꓱ | ./esbuild --charset=utf8 --target=es5
x["ꓷꓶꓲꓵꓭꓢꓱ"];
// Old output
x.ꓷꓶꓲꓵꓭꓢꓱ = { ꓷꓶꓲꓵꓭꓢꓱ: y };

// New output
x["ꓷꓶꓲꓵꓭꓢꓱ"] = { "ꓷꓶꓲꓵꓭꓢꓱ": y };
```

This approach should ensure maximum compatibility with all JavaScript environments that support ES5 and above. Note that this means minified files containing Unicode properties may be slightly larger than before.

## 0.12.6

* Improve template literal lowering transformation conformance ([#1327](https://github.com/evanw/esbuild/issues/1327))
Expand Down
8 changes: 4 additions & 4 deletions internal/bundler/linker.go
Expand Up @@ -1660,7 +1660,7 @@ func (c *linkerContext) generateCodeForLazyExport(sourceIndex uint32) {
clone.Properties = append(make([]js_ast.Property, 0, len(clone.Properties)), clone.Properties...)
for i, property := range clone.Properties {
if str, ok := property.Key.Data.(*js_ast.EString); ok &&
(!file.IsEntryPoint() || js_lexer.IsIdentifierUTF16(str.Value, 0) ||
(!file.IsEntryPoint() || js_lexer.IsIdentifierUTF16(str.Value) ||
!c.options.UnsupportedJSFeatures.Has(compat.ArbitraryModuleNamespaceNames)) {
name := js_lexer.UTF16ToString(str.Value)
exportRef := generateExport(name, name, property.ValueOrNil).ref
Expand Down Expand Up @@ -4178,7 +4178,7 @@ func (c *linkerContext) renameSymbolsInChunk(chunk *chunkInfo, filesInOrder []ui
}

// When we're not minifying, just append numbers to symbol names to avoid collisions
r := renamer.NewNumberRenamer(c.graph.Symbols, reservedNames, c.options.UnsupportedJSFeatures)
r := renamer.NewNumberRenamer(c.graph.Symbols, reservedNames)
nestedScopes := make(map[uint32][]*js_ast.Scope)

timer.Begin("Add top-level symbols")
Expand Down Expand Up @@ -4705,7 +4705,7 @@ func (c *linkerContext) generateGlobalNamePrefix() string {
join = ";"
}

if js_printer.CanQuoteIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if js_printer.CanEscapeIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if c.options.ASCIIOnly {
prefix = string(js_printer.QuoteIdentifier(nil, prefix, c.options.UnsupportedJSFeatures))
}
Expand All @@ -4717,7 +4717,7 @@ func (c *linkerContext) generateGlobalNamePrefix() string {

for _, name := range c.options.GlobalName[1:] {
oldPrefix := prefix
if js_printer.CanQuoteIdentifier(name, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if js_printer.CanEscapeIdentifier(name, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
if c.options.ASCIIOnly {
name = string(js_printer.QuoteIdentifier(nil, name, c.options.UnsupportedJSFeatures))
}
Expand Down
10 changes: 0 additions & 10 deletions internal/compat/js_table.go
Expand Up @@ -79,7 +79,6 @@ const (
TemplateLiteral
TopLevelAwait
UnicodeEscapes
UpdatedIdentifiers
)

func (features JSFeature) Has(feature JSFeature) bool {
Expand Down Expand Up @@ -418,15 +417,6 @@ var jsTable = map[JSFeature]map[Engine][]int{
Node: {4},
Safari: {9},
},
UpdatedIdentifiers: {
Chrome: {58},
Edge: {14},
ES: {2015},
Firefox: {52},
IOS: {14},
Node: {8},
Safari: {14},
},
}

func isVersionLessThan(a []int, b []int) bool {
Expand Down

0 comments on commit 5065800

Please sign in to comment.