chore(deps): update dependency esbuild to v0.14.36
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| esbuild | 0.13.15 -> 0.14.36 |
Release Notes
evanw/esbuild
v0.14.36
-
Revert path metadata validation for now (#2177)
This release reverts the path metadata validation that was introduced in the previous release. This validation has uncovered a potential issue with how esbuild handles
onResolvecallbacks in plugins that will need to be fixed before path metadata validation is re-enabled.
v0.14.35
-
Add support for parsing
typeofon #private fields from TypeScript 4.7 (#2174)The upcoming version of TypeScript now lets you use
#privatefields intypeoftype expressions:https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#typeof-on-private-fields
class Container { #data = "hello!"; get data(): typeof this.#data { return this.#data; } set data(value: typeof this.#data) { this.#data = value; } }With this release, esbuild can now parse these new type expressions as well. This feature was contributed by @magic-akari.
-
Add Opera and IE to internal CSS feature support matrix (#2170)
Version 0.14.18 of esbuild added Opera and IE as available target environments, and added them to the internal JS feature support matrix. CSS feature support was overlooked, however. This release adds knowledge of Opera and IE to esbuild's internal CSS feature support matrix:
/* Original input */ a { color: rgba(0, 0, 0, 0.5); } /* Old output (with --target=opera49 --minify) */ a{color:rgba(0,0,0,.5)} /* New output (with --target=opera49 --minify) */ a{color:#​00000080}The fix for this issue was contributed by @sapphi-red.
-
Change TypeScript class field behavior when targeting ES2022
TypeScript 4.3 introduced a breaking change where class field behavior changes from assign semantics to define semantics when the
targetsetting intsconfig.jsonis set toESNext. Specifically, the default value for TypeScript'suseDefineForClassFieldssetting when unspecified istrueif and only iftargetisESNext. TypeScript 4.6 introduced another change where this behavior now happens for bothESNextandES2022. Presumably this will be the case forES2023and up as well. With this release, esbuild's behavior has also been changed to match. Now configuring esbuild with--target=es2022will also cause TypeScript files to use the new class field behavior. -
Validate that path metadata returned by plugins is consistent
The plugin API assumes that all metadata for the same path returned by a plugin's
onResolvecallback is consistent. Previously this assumption was just assumed without any enforcement. Starting with this release, esbuild will now enforce this by generating a build error if this assumption is violated. The lack of validation has not been an issue (I have never heard of this being a problem), but it still seems like a good idea to enforce it. Here's a simple example of a plugin that generates inconsistentsideEffectsmetadata:let buggyPlugin = { name: 'buggy', setup(build) { let count = 0 build.onResolve({ filter: /^react$/ }, args => { return { path: require.resolve(args.path), sideEffects: count++ > 0, } }) }, }Since esbuild processes everything in parallel, the set of metadata that ends up being used for a given path is essentially random since it's whatever the task scheduler decides to schedule first. Thus if a plugin does not consistently provide the same metadata for a given path, subsequent builds may return different results. This new validation check prevents this problem.
Here's the new error message that's shown when this happens:
✘ [ERROR] [plugin buggy] Detected inconsistent metadata for the path "node_modules/react/index.js" when it was imported here: button.tsx:1:30: 1 │ import { createElement } from 'react' ╵ ~~~~~~~ The original metadata for that path comes from when it was imported here: app.tsx:1:23: 1 │ import * as React from 'react' ╵ ~~~~~~~ The difference in metadata is displayed below: { - "sideEffects": true, + "sideEffects": false, } This is a bug in the "buggy" plugin. Plugins provide metadata for a given path in an "onResolve" callback. All metadata provided for the same path must be consistent to ensure deterministic builds. Due to parallelism, one set of provided metadata will be randomly chosen for a given path, so providing inconsistent metadata for the same path can cause non-determinism. -
Suggest enabling a missing condition when
exportsmap fails (#2163)This release adds another suggestion to the error message that happens when an
exportsmap lookup fails if the failure could potentially be fixed by adding a missing condition. Here's what the new error message looks like (which now suggests--conditions=moduleas a possible workaround):✘ [ERROR] Could not resolve "@​sentry/electron/main" index.js:1:24: 1 │ import * as Sentry from '@​sentry/electron/main' ╵ ~~~~~~~~~~~~~~~~~~~~~~~ The path "./main" is not currently exported by package "@​sentry/electron": node_modules/@​sentry/electron/package.json:8:13: 8 │ "exports": { ╵ ^ None of the conditions provided ("require", "module") match any of the currently active conditions ("browser", "default", "import"): node_modules/@​sentry/electron/package.json:16:14: 16 │ "./main": { ╵ ^ Consider enabling the "module" condition if this package expects it to be enabled. You can use "--conditions=module" to do that: node_modules/@​sentry/electron/package.json:18:6: 18 │ "module": "./esm/main/index.js" ╵ ~~~~~~~~ Consider using a "require()" call to import this file, which will work because the "require" condition is supported by this package: index.js:1:24: 1 │ import * as Sentry from '@​sentry/electron/main' ╵ ~~~~~~~~~~~~~~~~~~~~~~~ You can mark the path "@​sentry/electron/main" as external to exclude it from the bundle, which will remove this error.This particular package had an issue where it was using the Webpack-specific
modulecondition without providing adefaultcondition. It looks like the intent in this case was to use the standardimportcondition instead. This specific change wasn't suggested here because this error message is for package consumers, not package authors.
v0.14.34
Something went wrong with the publishing script for the previous release. Publishing again.
v0.14.33
-
Fix a regression regarding
super(#2158)This fixes a regression from the previous release regarding classes with a super class, a private member, and a static field in the scenario where the static field needs to be lowered but where private members are supported by the configured target environment. In this scenario, esbuild could incorrectly inject the instance field initializers that use
thisinto the constructor before the call tosuper(), which is invalid. This problem has now been fixed (notice thatthisis now used aftersuper()instead of before):// Original code class Foo extends Object { static FOO; constructor() { super(); } #foo; } // Old output (with --bundle) var _foo; var Foo = class extends Object { constructor() { __privateAdd(this, _foo, void 0); super(); } }; _foo = new WeakMap(); __publicField(Foo, "FOO"); // New output (with --bundle) var _foo; var Foo = class extends Object { constructor() { super(); __privateAdd(this, _foo, void 0); } }; _foo = new WeakMap(); __publicField(Foo, "FOO");During parsing, esbuild scans the class and makes certain decisions about the class such as whether to lower all static fields, whether to lower each private member, or whether calls to
super()need to be tracked and adjusted. Previously esbuild made two passes through the class members to compute this information. However, with the newsuper()call lowering logic added in the previous release, we now need three passes to capture the whole dependency chain for this case: 1) lowering static fields requires 2) lowering private members which requires 3) adjustingsuper()calls.The reason lowering static fields requires lowering private members is because lowering static fields moves their initializers outside of the class body, where they can't access private members anymore. Consider this code:
class Foo { get #foo() {} static bar = new Foo().#foo }We can't just lower static fields without also lowering private members, since that causes a syntax error:
class Foo { get #foo() {} } Foo.bar = new Foo().#foo;And the reason lowering private members requires adjusting
super()calls is because the injected private member initializers usethis, which is only accessible aftersuper()calls in the constructor. -
Fix an issue with
--keep-namesnot keeping some names (#2149)This release fixes a regression with
--keep-namesfrom version 0.14.26. PR #2062 attempted to remove superfluous calls to the__namehelper function by omitting calls of the form__name(foo, "foo")where the name of the symbol in the first argument is equal to the string in the second argument. This was assuming that the initializer for the symbol would automatically be assigned the expected.nameproperty by the JavaScript VM, which turned out to be an incorrect assumption. To fix the regression, this PR has been reverted.The assumption is true in many cases but isn't true when the initializer is moved into another automatically-generated variable, which can sometimes be necessary during the various syntax transformations that esbuild does. For example, consider the following code:
class Foo { static get #foo() { return Foo.name } static get foo() { return this.#foo } } let Bar = Foo Foo = { name: 'Bar' } console.log(Foo.name, Bar.name)This code should print
Bar Foo. With--keep-names --target=es6that code is lowered by esbuild into the following code (omitting the helper function definitions for brevity):var _foo, foo_get; const _Foo = class { static get foo() { return __privateGet(this, _foo, foo_get); } }; let Foo = _Foo; __name(Foo, "Foo"); _foo = new WeakSet(); foo_get = /* @​__PURE__ */ __name(function() { return _Foo.name; }, "#foo"); __privateAdd(Foo, _foo); let Bar = Foo; Foo = { name: "Bar" }; console.log(Foo.name, Bar.name);The injection of the automatically-generated
_Foovariable is necessary to preserve the semantics of the capturedFoobinding for methods defined within the class body, even when the definition needs to be moved outside of the class body during code transformation. Due to a JavaScript quirk, this binding is immutable and does not change even ifFoois later reassigned. The PR that was reverted was incorrectly removing the call to__name(Foo, "Foo"), which turned out to be necessary after all in this case. -
Print some large integers using hexadecimal when minifying (#2162)
When
--minifyis active, esbuild will now use one fewer byte to represent certain large integers:// Original code x = 123456787654321; // Old output (with --minify) x=123456787654321; // New output (with --minify) x=0x704885f926b1;This works because a hexadecimal representation can be shorter than a decimal representation starting at around 1012 and above.
This optimization made me realize that there's probably an opportunity to optimize printed numbers for smaller gzipped size instead of or in addition to just optimizing for minimal uncompressed byte count. The gzip algorithm does better with repetitive sequences, so for example
0xFFFFFFFFis probably a better representation than4294967295even though the byte counts are the same. As far as I know, no JavaScript minifier does this optimization yet. I don't know enough about how gzip works to know if this is a good idea or what the right metric for this might be. -
Add Linux ARM64 support for Deno (#2156)
This release adds Linux ARM64 support to esbuild's Deno API implementation, which allows esbuild to be used with Deno on a Raspberry Pi.
v0.14.32
-
Fix
superusage in lowered private methods (#2039)Previously esbuild failed to transform
superproperty accesses inside private methods in the case when private methods have to be lowered because the target environment doesn't support them. The generated code still contained thesuperkeyword even though the method was moved outside of the class body, which is a syntax error in JavaScript. This release fixes this transformation issue and now produces valid code:// Original code class Derived extends Base { #foo() { super.foo() } bar() { this.#foo() } } // Old output (with --target=es6) var _foo, foo_fn; class Derived extends Base { constructor() { super(...arguments); __privateAdd(this, _foo); } bar() { __privateMethod(this, _foo, foo_fn).call(this); } } _foo = new WeakSet(); foo_fn = function() { super.foo(); }; // New output (with --target=es6) var _foo, foo_fn; const _Derived = class extends Base { constructor() { super(...arguments); __privateAdd(this, _foo); } bar() { __privateMethod(this, _foo, foo_fn).call(this); } }; let Derived = _Derived; _foo = new WeakSet(); foo_fn = function() { __superGet(_Derived.prototype, this, "foo").call(this); };Because of this change, lowered
superproperty accesses on instances were rewritten so that they can exist outside of the class body. This rewrite affects code generation for allsuperproperty accesses on instances including those inside loweredasyncfunctions. The new approach is different but should be equivalent to the old approach:// Original code class Foo { foo = async () => super.foo() } // Old output (with --target=es6) class Foo { constructor() { __publicField(this, "foo", () => { var __superGet = (key) => super[key]; return __async(this, null, function* () { return __superGet("foo").call(this); }); }); } } // New output (with --target=es6) class Foo { constructor() { __publicField(this, "foo", () => __async(this, null, function* () { return __superGet(Foo.prototype, this, "foo").call(this); })); } } -
Fix some tree-shaking bugs regarding property side effects
This release fixes some cases where side effects in computed properties were not being handled correctly. Specifically primitives and private names as properties should not be considered to have side effects, and object literals as properties should be considered to have side effects:
// Original code let shouldRemove = { [1]: 2 } let shouldRemove2 = class { #foo } let shouldKeep = class { [{ toString() { sideEffect() } }] } // Old output (with --tree-shaking=true) let shouldRemove = { [1]: 2 }; let shouldRemove2 = class { #foo; }; // New output (with --tree-shaking=true) let shouldKeep = class { [{ toString() { sideEffect(); } }]; }; -
Add the
wasmModuleoption to theinitializeJS API (#1093)The
initializeJS API must be called when using esbuild in the browser to provide the WebAssembly module for esbuild to use. Previously the only way to do that was using thewasmURLAPI option like this:await esbuild.initialize({ wasmURL: '/node_modules/esbuild-wasm/esbuild.wasm', }) console.log(await esbuild.transform('1+2'))With this release, you can now also initialize esbuild using a
WebAssembly.Moduleinstance using thewasmModuleAPI option instead. The example above is equivalent to the following code:await esbuild.initialize({ wasmModule: await WebAssembly.compileStreaming(fetch('/node_modules/esbuild-wasm/esbuild.wasm')) }) console.log(await esbuild.transform('1+2'))This could be useful for environments where you want more control over how the WebAssembly download happens or where downloading the WebAssembly module is not possible.
v0.14.31
-
Add support for parsing "optional variance annotations" from TypeScript 4.7 (#2102)
The upcoming version of TypeScript now lets you specify
inand/orouton certain type parameters (specifically only on a type alias, an interface declaration, or a class declaration). These modifiers control type paramemter covariance and contravariance:type Provider<out T> = () => T; type Consumer<in T> = (x: T) => void; type Mapper<in T, out U> = (x: T) => U; type Processor<in out T> = (x: T) => T;With this release, esbuild can now parse these new type parameter modifiers. This feature was contributed by @magic-akari.
-
Improve support for
super()constructor calls in nested locations (#2134)In JavaScript, derived classes must call
super()somewhere in theconstructormethod before being able to accessthis. Class public instance fields, class private instance fields, and TypeScript constructor parameter properties can all potentially cause code which usesthisto be inserted into the constructor body, which must be inserted after thesuper()call. To make these insertions straightforward to implement, the TypeScript compiler doesn't allow callingsuper()somewhere other than in a root-level statement in the constructor body in these cases.Previously esbuild's class transformations only worked correctly when
super()was called in a root-level statement in the constructor body, just like the TypeScript compiler. But with this release, esbuild should now generate correct code as long as the call tosuper()appears anywhere in the constructor body:// Original code class Foo extends Bar { constructor(public skip = false) { if (skip) { super(null) return } super({ keys: [] }) } } // Old output (incorrect) class Foo extends Bar { constructor(skip = false) { if (skip) { super(null); return; } super({ keys: [] }); this.skip = skip; } } // New output (correct) class Foo extends Bar { constructor(skip = false) { var __super = (...args) => { super(...args); this.skip = skip; }; if (skip) { __super(null); return; } __super({ keys: [] }); } } -
Add support for the new
@containerCSS rule (#2127)This release adds support for
@containerin CSS files. This means esbuild will now pretty-print and minify these rules better since it now better understands the internal structure of these rules:/* Original code */ @​container (width <= 150px) { #inner { color: yellow; } } /* Old output (with --minify) */ @​container (width <= 150px){#inner {color: yellow;}} /* New output (with --minify) */ @​container (width <= 150px){#inner{color:#ff0}}This was contributed by @yisibl.
-
Avoid CSS cascade-dependent keywords in the
font-familyproperty (#2135)In CSS,
initial,inherit, andunsetare CSS-wide keywords which means they have special behavior when they are specified as a property value. For example, whilefont-family: 'Arial'(as a string) andfont-family: Arial(as an identifier) are the same,font-family: 'inherit'(as a string) uses the font family namedinheritbutfont-family: inherit(as an identifier) inherits the font family from the parent element. This means esbuild must not unquote these CSS-wide keywords (anddefault, which is also reserved) during minification to avoid changing the meaning of the minified CSS.The current draft of the new CSS Cascading and Inheritance Level 5 specification adds another concept called cascade-dependent keywords of which there are two:
revertandrevert-layer. This release of esbuild guards against unquoting these additional keywords as well to avoid accidentally breaking pages that use a font with the same name:/* Original code */ a { font-family: 'revert'; } b { font-family: 'revert-layer', 'Segoe UI', serif; } /* Old output (with --minify) */ a{font-family:revert}b{font-family:revert-layer,Segoe UI,serif} /* New output (with --minify) */ a{font-family:"revert"}b{font-family:"revert-layer",Segoe UI,serif}This fix was contributed by @yisibl.
v0.14.30
-
Change the context of TypeScript parameter decorators (#2147)
While TypeScript parameter decorators are expressions, they are not evaluated where they exist in the code. They are moved to after the class declaration and evaluated there instead. Specifically this TypeScript code:
class Class { method(@​decorator() arg) {} }becomes this JavaScript code:
class Class { method(arg) {} } __decorate([ __param(0, decorator()) ], Class.prototype, "method", null);This has several consequences:
-
Whether
awaitis allowed inside a decorator expression or not depends on whether the class declaration itself is in anasynccontext or not. With this release, you can now useawaitinside a decorator expression when the class declaration is either inside anasyncfunction or is at the top-level of an ES module and top-level await is supported. Note that the TypeScript compiler currently has a bug regarding this edge case: https://github.com/microsoft/TypeScript/issues/48509.// Using "await" inside a decorator expression is now allowed async function fn(foo: Promise<any>) { class Class { method(@​decorator(await foo) arg) {} } return Class }Also while TypeScript currently allows
awaitto be used like this inasyncfunctions, it doesn't currently allowyieldto be used like this in generator functions. It's not yet clear whether this behavior withyieldis a bug or by design, so I haven't made any changes to esbuild's handling ofyieldinside decorator expressions in this release. -
Since the scope of a decorator expression is the scope enclosing the class declaration, they cannot access private identifiers. Previously this was incorrectly allowed but with this release, esbuild no longer allows this. Note that the TypeScript compiler currently has a bug regarding this edge case: https://github.com/microsoft/TypeScript/issues/48515.
// Using private names inside a decorator expression is no longer allowed class Class { static #priv = 123 method(@​decorator(Class.#priv) arg) {} } -
Since the scope of a decorator expression is the scope enclosing the class declaration, identifiers inside parameter decorator expressions should never be resolved to a parameter of the enclosing method. Previously this could happen, which was a bug with esbuild. This bug no longer happens in this release.
// Name collisions now resolve to the outer name instead of the inner name let arg = 1 class Class { method(@​decorator(arg) arg = 2) {} }Specifically previous versions of esbuild generated the following incorrect JavaScript (notice the use of
arg2):let arg = 1; class Class { method(arg2 = 2) { } } __decorateClass([ __decorateParam(0, decorator(arg2)) ], Class.prototype, "method", 1);This release now generates the following correct JavaScript (notice the use of
arg):let arg = 1; class Class { method(arg2 = 2) { } } __decorateClass([ __decorateParam(0, decorator(arg)) ], Class.prototype, "method", 1);
-
-
Fix some obscure edge cases with
superproperty accessThis release fixes the following obscure problems with
superwhen targeting an older JavaScript environment such as--target=es6:-
The compiler could previously crash when a lowered
asyncarrow function contained a class with a field initializer that used asuperproperty access:let foo = async () => class extends Object { bar = super.toString } -
The compiler could previously generate incorrect code when a lowered
asyncmethod of a derived class contained a nested class with a computed class member involving asuperproperty access on the derived class:class Base { foo() { return 'bar' } } class Derived extends Base { async foo() { return new class { [super.foo()] = 'success' } } } new Derived().foo().then(obj => console.log(obj.bar)) -
The compiler could previously generate incorrect code when a default-exported class contained a
superproperty access inside a lowered static private class field:class Foo { static foo = 123 } export default class extends Foo { static #foo = super.foo static bar = this.#foo }
-
v0.14.29
-
Fix a minification bug with a double-nested
ifinside a label followed byelse(#2139)This fixes a minification bug that affects the edge case where
ifis followed byelseand theifcontains a label that contains a nestedif. Normally esbuild's AST printer automatically wraps the body of a single-statementifin braces to avoid the "dangling else"if/elseambiguity common to C-like languages (where theelseaccidentally becomes associated with the innerifinstead of the outerif). However, I was missing automatic wrapping of label statements, which did not have test coverage because they are a rarely-used feature. This release fixes the bug:// Original code if (a) b: { if (c) break b } else if (d) e() // Old output (with --minify) if(a)e:if(c)break e;else d&&e(); // New output (with --minify) if(a){e:if(c)break e}else d&&e(); -
Fix edge case regarding
baseUrlandpathsintsconfig.json(#2119)In
tsconfig.json, TypeScript forbids non-relative values insidepathsifbaseUrlis not present, and esbuild does too. However, TypeScript checked this after the entiretsconfig.jsonhierarchy was parsed while esbuild incorrectly checked this immediately when parsing the file containing thepathsmap. This caused incorrect warnings to be generated fortsconfig.jsonfiles that specify abaseUrlvalue and that inherit apathsvalue from anextendsclause. Now esbuild will only check for non-relativepathsvalues after the entire hierarchy has been parsed to avoid generating incorrect warnings. -
Better handle errors where the esbuild binary executable is corrupted or missing (#2129)
If the esbuild binary executable is corrupted or missing, previously there was one situation where esbuild's JavaScript API could hang instead of generating an error. This release changes esbuild's library code to generate an error instead in this case.
v0.14.28
-
Add support for some new CSS rules (#2115, #2116, #2117)
This release adds support for
@font-palette-values,@counter-style, and@font-feature-values. This means esbuild will now pretty-print and minify these rules better since it now better understands the internal structure of these rules:/* Original code */ @​font-palette-values --Foo { base-palette: 1; } @​counter-style bar { symbols: b a r; } @​font-feature-values Bop { @​styleset { test: 1; } } /* Old output (with --minify) */ @​font-palette-values --Foo{base-palette: 1;}@​counter-style bar{symbols: b a r;}@​font-feature-values Bop{@​styleset {test: 1;}} /* New output (with --minify) */ @​font-palette-values --Foo{base-palette:1}@​counter-style bar{symbols:b a r}@​font-feature-values Bop{@​styleset{test:1}} -
Upgrade to Go 1.18.0 (#2105)
Binary executables for this version are now published with Go version 1.18.0. The Go release notes say that the linker generates smaller binaries and that on 64-bit ARM chips, compiled binaries run around 10% faster. On an M1 MacBook Pro, esbuild's benchmark runs approximately 8% faster than before and the binary executable is approximately 4% smaller than before.
This also fixes a regression from version 0.14.26 of esbuild where the browser builds of the
esbuild-wasmpackage could fail to be bundled due to the use of built-in node libraries. The primary WebAssembly shim for Go 1.18.0 no longer uses built-in node libraries.
v0.14.27
-
Avoid generating an enumerable
defaultimport for CommonJS files in Babel mode (#2097)Importing a CommonJS module into an ES module can be done in two different ways. In node mode the
defaultimport is always set tomodule.exports, while in Babel mode thedefaultimport passes through tomodule.exports.defaultinstead. Node mode is triggered when the importing file ends in.mjs, hastype: "module"in itspackage.jsonfile, or the imported module does not have a__esModulemarker.Previously esbuild always created the forwarding
defaultimport in Babel mode, even ifmodule.exportshad no property calleddefault. This was problematic because the getter nameddefaultstill showed up as a property on the imported namespace object, and could potentially interfere with code that iterated over the properties of the imported namespace object. With this release the getter nameddefaultwill now only be added in Babel mode if thedefaultproperty exists at the time of the import. -
Fix a circular import edge case regarding ESM-to-CommonJS conversion (#1894, #2059)
This fixes a regression that was introduced in version 0.14.5 of esbuild. Ever since that version, esbuild now creates two separate export objects when you convert an ES module file into a CommonJS module: one for ES modules and one for CommonJS modules. The one for CommonJS modules is written to
module.exportsand exported from the file, and the one for ES modules is internal and can be accessed by bundling code that imports the entry point (for example, the entry point might import itself to be able to inspect its own exports).The reason for these two separate export objects is that CommonJS modules are supposed to see a special export called
__esModulewhich indicates that the module used to be an ES module, while ES modules are not supposed to see any automatically-added export named__esModule. This matters for real-world code both because people sometimes iterate over the properties of ES module export namespace objects and because some people write ES module code containing their own exports named__esModulethat they expect other ES module code to be able to read.However, this change to split exports into two separate objects broke ES module re-exports in the edge case where the imported module is involved in an import cycle. This happened because the CommonJS
module.exportsobject was no longer mutated as exports were added. Instead it was being initialized at the end of the generated file after the import statements to other modules (which are converted intorequire()calls). This release changesmodule.exportsinitialization to happen earlier in the file and then double-writes further exports to both the ES module and CommonJS module export objects.This fix was contributed by @indutny.
v0.14.26
-
Fix a tree shaking regression regarding
vardeclarations (#2080, #2085, #2098, #2099)Version 0.14.8 of esbuild enabled removal of duplicate function declarations when minification is enabled (see #610):
// Original code function x() { return 1 } console.log(x()) function x() { return 2 } // Output (with --minify-syntax) console.log(x()); function x() { return 2; }This transformation is safe because function declarations are "hoisted" in JavaScript, which means they are all done first before any other code is evaluted. This means the last function declaration will overwrite all previous function declarations with the same name.
However, this introduced an unintentional regression for
vardeclarations in which all but the last declaration was dropped if tree-shaking was enabled. This only happens for top-levelvardeclarations that re-declare the same variable multiple times. This regression has now been fixed:// Original code var x = 1 console.log(x) var x = 2 // Old output (with --tree-shaking=true) console.log(x); var x = 2; // New output (with --tree-shaking=true) var x = 1; console.log(x); var x = 2;This case now has test coverage.
-
Add support for parsing "instantiation expressions" from TypeScript 4.7 (#2038)
The upcoming version of TypeScript now lets you specify
<...>type parameters on a JavaScript identifier without using a call expression:const ErrorMap = Map<string, Error>; // new () => Map<string, Error> const errorMap = new ErrorMap(); // Map<string, Error>With this release, esbuild can now parse these new type annotations. This feature was contributed by @g-plane.
-
Avoid
new Functionin esbuild's library code (#2081)Some JavaScript environments such as Cloudflare Workers or Deno Deploy don't allow
new Functionbecause they disallow dynamic JavaScript evaluation. Previously esbuild's WebAssembly-based library used this to construct the WebAssembly worker function. With this release, the code is now inlined without usingnew Functionso it will be able to run even when this restriction is in place. -
Drop superfluous
__name()calls (#2062)When the
--keep-namesoption is specified, esbuild inserts calls to a__namehelper function to ensure that the.nameproperty on function and class objects remains consistent even if the function or class name is renamed to avoid a name collision or because name minification is enabled. With this release, esbuild will now try to omit these calls to the__namehelper function when the name of the function or class object was not renamed during the linking process after all:// Original code import { foo as foo1 } from 'data:text/javascript,export function foo() { return "foo1" }' import { foo as foo2 } from 'data:text/javascript,export function foo() { return "foo2" }' console.log(foo1.name, foo2.name) // Old output (with --bundle --keep-names) (() => { var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); function foo() { return "foo1"; } __name(foo, "foo"); function foo2() { return "foo2"; } __name(foo2, "foo"); console.log(foo.name, foo2.name); })(); // New output (with --bundle --keep-names) (() => { var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); function foo() { return "foo1"; } function foo2() { return "foo2"; } __name(foo2, "foo"); console.log(foo.name, foo2.name); })();Notice how one of the calls to
__nameis now no longer printed. This change was contributed by @indutny.
v0.14.25
-
Reduce minification of CSS transforms to avoid Safari bugs (#2057)
In Safari, applying a 3D CSS transform to an element can cause it to render in a different order than applying a 2D CSS transform even if the transformation matrix is identical. I believe this is a bug in Safari because the CSS
transformspecification doesn't seem to distinguish between 2D and 3D transforms as far as rendering order:For elements whose layout is governed by the CSS box model, any value other than
nonefor thetransformproperty results in the creation of a stacking context.This bug means that minifying a 3D transform into a 2D transform must be avoided even though it's a valid transformation because it can cause rendering differences in Safari. Previously esbuild sometimes minified 3D CSS transforms into 2D CSS transforms but with this release, esbuild will no longer do that:
/* Original code */ div { transform: matrix3d(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) } /* Old output (with --minify) */ div{transform:scale(2)} /* New output (with --minify) */ div{transform:scale3d(2,2,1)} -
Minification now takes advantage of the
?.operatorThis adds new code minification rules that shorten code with the
?.optional chaining operator when the result is equivalent:// Original code let foo = (x) => { if (x !== null && x !== undefined) x.y() return x === null || x === undefined ? undefined : x.z } // Old output (with --minify) let foo=n=>(n!=null&&n.y(),n==null?void 0:n.z); // New output (with --minify) let foo=n=>(n?.y(),n?.z);This only takes effect when minification is enabled and when the configured target environment is known to support the optional chaining operator. As always, make sure to set
--target=to the appropriate language target if you are running the minified code in an environment that doesn't support the latest JavaScript features. -
Add source mapping information for some non-executable tokens (#1448)
Code coverage tools can generate reports that tell you if any code exists that has not been run (or "covered") during your tests. You can use this information to add additional tests for code that isn't currently covered.
Some popular JavaScript code coverage tools have bugs where they incorrectly consider lines without any executable code as uncovered, even though there's no test you could possibly write that would cause those lines to be executed. For example, they apparently complain about the lines that only contain the trailing
}token of an object literal.With this release, esbuild now generates source mappings for some of these trailing non-executable tokens. This may not successfully work around bugs in code coverage tools because there are many non-executable tokens in JavaScript and esbuild doesn't map them all (the drawback of mapping these extra tokens is that esbuild will use more memory, build more slowly, and output a bigger source map). The true solution is to fix the bugs in the code coverage tools in the first place.
-
Fall back to WebAssembly on Android x64 (#2068)
Go's compiler supports trivial cross-compiling to almost all platforms without installing any additional software other than the Go compiler itself. This has made it very easy for esbuild to publish native binary executables for many platforms. However, it strangely doesn't support cross-compiling to Android x64 without installing the Android build tools. So instead of publishing a native esbuild binary executable to npm, this release publishes a WebAssembly fallback build. This is essentially the same as the
esbuild-wasmpackage but it's installed automatically when you install theesbuildpackage on Android x64. So packages that depend on theesbuildpackage should now work on Android x64. If you want to use a native binary executable of esbuild on Android x64, you may be able to build it yourself from source after installing the Android build tools. -
Update to Go 1.17.8
The version of the Go compiler used to compile esbuild has been upgraded from Go 1.17.7 to Go 1.17.8, which fixes the RISC-V 64-bit build. Compiler optimizations for the RISC-V 64-bit build have now been re-enabled.
v0.14.24
-
Allow
es2022as a target environment (#2012)TypeScript recently added support for
es2022as a compilation target so esbuild now supports this too. Support for this is preliminary as there is no published ES2022 specification yet (i.e. https://tc39.es/ecma262/2021/ exists but https://tc39.es/ecma262/2022/ is a 404 error). The meaning of esbuild'ses2022target may change in the future when the specification is finalized. Right now I have made thees2022target enable support for the syntax-related finished proposals that are marked as2022:- Class fields
- Class private members
- Class static blocks
- Ergonomic class private member checks
- Top-level await
I have also included the "arbitrary module namespace names" feature since I'm guessing it will end up in the ES2022 specification (this syntax feature was added to the specification without a proposal). TypeScript has not added support for this yet.
-
Match
defineto strings in index expressions (#2050)With this release, configuring
--define:foo.bar=baznow matches and replaces bothfoo.barandfoo['bar']expressions in the original source code. This is necessary for people who have enabled TypeScript'snoPropertyAccessFromIndexSignaturefeature, which prevents you from using normal property access syntax on a type with an index signature such as in the following code:declare let foo: { [key: string]: any } foo.bar // This is a type error if noPropertyAccessFromIndexSignature is enabled foo['bar']Previously esbuild would generate the following output with
--define:foo.bar=baz:baz; foo["bar"];Now esbuild will generate the following output instead:
baz; baz; -
Add
--mangle-quotedto mangle quoted properties (#218)The
--mangle-props=flag tells esbuild to automatically rename all properties matching the provided regular expression to shorter names to save space. Previously esbuild never modified the contents of string literals. In particular,--mangle-props=_would manglefoo._barbut notfoo['_bar']. There are some coding patterns where renaming quoted property names is desirable, such as when using TypeScript'snoPropertyAccessFromIndexSignaturefeature or when using TypeScript's discriminated union narrowing behavior:interface Foo { _foo: string } interface Bar { _bar: number } declare const value: Foo | Bar console.log('_foo' in value ? value._foo : value._bar)The
'_foo' in valuecheck tells TypeScript to narrow the type ofvaluetoFooin the true branch and toBarin the false branch. Previously esbuild didn't mangle the property name'_foo'because it was inside a string literal. With this release, you can now use--mangle-quotedto also rename property names inside string literals:// Old output (with --mangle-props=_) console.log("_foo" in value ? value.a : value.b); // New output (with --mangle-props=_ --mangle-quoted) console.log("a" in value ? value.a : value.b); -
Parse and discard TypeScript
export as namespacestatements (#2070)TypeScript
.d.tstype declaration files can sometimes contain statements of the formexport as namespace foo;. I believe these serve to declare that the module adds a property of that name to the global object. You aren't supposed to feed.d.tsfiles to esbuild so this normally doesn't matter, but sometimes esbuild can end up having to parse them. One such case is if you import a type-only package who'smainfield inpackage.jsonis a.d.tsfile.Previously esbuild only allowed
export as namespacestatements inside adeclarecontext:declare module Foo { export as namespace foo; }Now esbuild will also allow these statements outside of a
declarecontext:export as namespace foo;These statements are still just ignored and discarded.
-
Strip import assertions from unrecognized
import()expressions (#2036)The new "import assertions" JavaScript language feature adds an optional second argument to dynamic
import()expressions, which esbuild does support. However, this optional argument must be stripped when targeting older JavaScript environments for which this second argument would be a syntax error. Previously esbuild failed to strip this second argument in cases when the first argument toimport()wasn't a string literal. This problem is now fixed:// Original code console.log(import(foo, { assert: { type: 'json' } })) // Old output (with --target=es6) console.log(import(foo, { assert: { type: "json" } })); // New output (with --target=es6) console.log(import(foo)); -
Remove simplified statement-level literal expressions (#2063)
With this release, esbuild now removes simplified statement-level expressions if the simplified result is a literal expression even when minification is disabled. Previously this was only done when minification is enabled. This change was only made because some people are bothered by seeing top-level literal expressions. This change has no effect on code behavior.
-
Ignore
.d.tsrules inpathsintsconfig.jsonfiles (#2074, #2075)TypeScript's
tsconfig.jsonconfiguration file has apathsfield that lets you remap import paths to alternative files on the file system. This field is interpreted by esbuild during bundling so that esbuild's behavior matches that of the TypeScript type checker. However, people sometimes override import paths to JavaScript files to instead point to a.d.tsTypeScript type declaration file for that JavaScript file. The intent of this is to just use the remapping for type information and not to actually import the.d.tsfile during the build.With this release, esbuild will now ignore rules in
pathsthat result in a.d.tsfile during path resolution. This means code that does this should now be able to be bundled without modifying itstsconfig.jsonfile to remove the.d.tsrule. This change was contributed by @magic-akari. -
Disable Go compiler optimizations for the Linux RISC-V 64bit build (#2035)
Go's RISC-V 64bit compiler target has a fatal compiler optimization bug that causes esbuild to crash when it's run: https://github.com/golang/go/issues/51101. As a temporary workaround until a version of the Go compiler with the fix is published, Go compiler optimizations have been disabled for RISC-V. The 7.7mb esbuild binary executable for RISC-V is now 8.7mb instead. This workaround was contributed by @piggynl.
v0.14.23
-
Update feature database to indicate that node 16.14+ supports import assertions (#2030)
Node versions 16.14 and above now support import assertions according to these release notes. This release updates esbuild's internal feature compatibility database with this information, so esbuild no longer strips import assertions with
--target=node16.14:// Original code import data from './package.json' assert { type: 'json' } console.log(data) // Old output (with --target=node16.14) import data from "./package.json"; console.log(data); // New output (with --target=node16.14) import data from "./package.json" assert { type: "json" }; console.log(data); -
Basic support for CSS
@layerrules (#2027)This adds basic parsing support for a new CSS feature called
@layerthat changes how the CSS cascade works. Adding parsing support for this rule to esbuild means esbuild can now minify the contents of@layerrules:/* Original code */ @​layer a { @​layer b { div { color: yellow; margin: 0.0px; } } } /* Old output (with --minify) */ @​layer a{@​layer b {div {color: yellow; margin: 0px;}}} /* New output (with --minify) */ @​layer a.b{div{color:#ff0;margin:0}}You can read more about
@layerhere:- Documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/@layer
- Motivation: https://developer.chrome.com/blog/cascade-layers/
Note that the support added in this release is only for parsing and printing
@layerrules. The bundler does not yet know about these rules and bundling with@layermay result in behavior changes since these new rules have unusual ordering constraints that behave differently than all other CSS rules. Specifically the order is derived from the first instance while with every other CSS rule, the order is derived from the last instance.
v0.14.22
-
Preserve whitespace for token lists that look like CSS variable declarations (#2020)
Previously esbuild removed the whitespace after the CSS variable declaration in the following CSS:
/* Original input */ @​supports (--foo: ){html{background:green}} /* Previous output */ @​supports (--foo:){html{background:green}}However, that broke rendering in Chrome as it caused Chrome to ignore the entire rule. This did not break rendering in Firefox and Safari, so there's a browser bug either with Chrome or with both Firefox and Safari. In any case, esbuild now preserves whitespace after the CSS variable declaration in this ca
Configuration
📅 Schedule: At any time (no schedule defined).
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
- [ ] If you want to rebase/retry this PR, click this checkbox.
This PR has been generated by WhiteSource Renovate. View repository job log here.
⚠️ No Changeset found
Latest commit: 883f9e2c1ee2cbd38f6359d67207dd5014e54a2b
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
This PR includes no changesets
When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR