-
Keep inlined constants when direct
eval
is present (#2361)Version 0.14.19 of esbuild added inlining of certain
const
variables during minification, which replaces all references to the variable with the initializer and then removes the variable declaration. However, this could generate incorrect code when directeval
is present because the directeval
could reference the constant by name. This release fixes the problem by preserving theconst
variable declaration in this case:// Original code console.log((() => { const x = 123; return x + eval('x') })) // Old output (with --minify) console.log(()=>123+eval("x")); // New output (with --minify) console.log(()=>{const x=123;return 123+eval("x")});
-
Fix an incorrect error in TypeScript when targeting ES5 (#2375)
Previously when compiling TypeScript code to ES5, esbuild could incorrectly consider the following syntax forms as a transformation error:
0 ? ([]) : 1 ? ({}) : 2;
The error messages looked like this:
✘ [ERROR] Transforming destructuring to the configured target environment ("es5") is not supported yet example.ts:1:5: 1 │ 0 ? ([]) : 1 ? ({}) : 2; ╵ ^ ✘ [ERROR] Transforming destructuring to the configured target environment ("es5") is not supported yet example.ts:1:16: 1 │ 0 ? ([]) : 1 ? ({}) : 2; ╵ ^
These parenthesized literals followed by a colon look like the start of an arrow function expression followed by a TypeScript return type (e.g.
([]) : 1
could be the start of the TypeScript arrow function([]): 1 => 1
). Unlike in JavaScript, parsing arrow functions in TypeScript requires backtracking. In this case esbuild correctly determined that this expression wasn't an arrow function after all but the check for destructuring was incorrectly not covered under the backtracking process. With this release, the error message is now only reported if the parser successfully parses an arrow function without backtracking. -
Fix generated TypeScript
enum
comments containing*/
(#2369, #2371)TypeScript
enum
values that are equal to a number or string literal are inlined (references to the enum are replaced with the literal value) and have a/* ... */
comment after them with the original enum name to improve readability. However, this comment is omitted if the enum name contains the character sequence*/
because that would end the comment early and cause a syntax error:// Original TypeScript enum Foo { '/*' = 1, '*/' = 2 } console.log(Foo['/*'], Foo['*/']) // Generated JavaScript console.log(1 /* /* */, 2);
This was originally handled correctly when TypeScript
enum
inlining was initially implemented since it was only supported within a single file. However, when esbuild was later extended to support TypeScriptenum
inlining across files, this special case where the enum name contains*/
was not handled in that new code. Starting with this release, esbuild will now handle enums with names containing*/
correctly when they are inlined across files:// foo.ts export enum Foo { '/*' = 1, '*/' = 2 } // bar.ts import { Foo } from './foo' console.log(Foo['/*'], Foo['*/']) // Old output (with --bundle --format=esm) console.log(1 /* /* */, 2 /* */ */); // New output (with --bundle --format=esm) console.log(1 /* /* */, 2);
This fix was contributed by @magic-akari.
-
Allow
declare
class fields to be initialized (#2380)This release fixes an oversight in the TypeScript parser that disallowed initializers for
declare
class fields. TypeScript actually allows the following limited initializer expressions forreadonly
fields:declare const enum a { b = 0 } class Foo { // These are allowed by TypeScript declare readonly a = 0 declare readonly b = -0 declare readonly c = 0n declare readonly d = -0n declare readonly e = 'x' declare readonly f = `x` declare readonly g = a.b declare readonly h = a['b'] // These are not allowed by TypeScript declare readonly x = (0) declare readonly y = null declare readonly z = -a.b }
So with this release, esbuild now allows initializers for
declare
class fields too. To future-proof this in case TypeScript allows more expressions as initializers in the future (such asnull
), esbuild will allow any expression as an initializer and will leave the specifics of TypeScript's special-casing here to the TypeScript type checker. -
Fix a bug in esbuild's feature compatibility table generator (#2365)
Passing specific JavaScript engines to esbuild's
--target
flag restricts esbuild to only using JavaScript features that are supported on those engines in the output files that esbuild generates. The data for this feature is automatically derived from this compatibility table with a script: https://kangax.github.io/compat-table/.However, the script had a bug that could incorrectly consider a JavaScript syntax feature to be supported in a given engine even when it doesn't actually work in that engine. Specifically this bug happened when a certain aspect of JavaScript syntax has always worked incorrectly in that engine and the bug in that engine has never been fixed. This situation hasn't really come up before because previously esbuild pretty much only targeted JavaScript engines that always fix their bugs, but the two new JavaScript engines that were added in the previous release (Hermes and Rhino) have many aspects of the JavaScript specification that have never been implemented, and may never be implemented. For example, the
let
andconst
keywords are not implemented correctly in those engines.With this release, esbuild's compatibility table generator script has been fixed and as a result, esbuild will now correctly consider a JavaScript syntax feature to be unsupported in a given engine if there is some aspect of that syntax that is broken in all known versions of that engine. This means that the following JavaScript syntax features are no longer considered to be supported by these engines (represented using esbuild's internal names for these syntax features):
Hermes:
arrow
const-and-let
default-argument
generator
optional-catch-binding
optional-chain
rest-argument
template-literal
Rhino:
arrow
const-and-let
destructuring
for-of
generator
object-extensions
template-literal
IE:
const-and-let