evanw/esbuild
### [`v0.14.31`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01431)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.30...v0.14.31)
- Add support for parsing "optional variance annotations" from TypeScript 4.7 ([#2102](https://togithub.com/evanw/esbuild/pull/2102))
The upcoming version of TypeScript now lets you specify `in` and/or `out` on certain type parameters (specifically only on a type alias, an interface declaration, or a class declaration). These modifiers control type paramemter covariance and contravariance:
```ts
type Provider = () => T;
type Consumer = (x: T) => void;
type Mapper = (x: T) => U;
type Processor = (x: T) => T;
```
With this release, esbuild can now parse these new type parameter modifiers. This feature was contributed by [@magic-akari](https://togithub.com/magic-akari).
- Improve support for `super()` constructor calls in nested locations ([#2134](https://togithub.com/evanw/esbuild/issues/2134))
In JavaScript, derived classes must call `super()` somewhere in the `constructor` method before being able to access `this`. Class public instance fields, class private instance fields, and TypeScript constructor parameter properties can all potentially cause code which uses `this` to be inserted into the constructor body, which must be inserted after the `super()` call. To make these insertions straightforward to implement, the TypeScript compiler doesn't allow calling `super()` 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 to `super()` appears anywhere in the constructor body:
```ts
// 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 `@container` CSS rule ([#2127](https://togithub.com/evanw/esbuild/pull/2127))
This release adds support for [`@container`](https://drafts.csswg.org/css-contain-3/#container-rule) in 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:
```css
/* 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](https://togithub.com/yisibl).
- Avoid CSS cascade-dependent keywords in the `font-family` property ([#2135](https://togithub.com/evanw/esbuild/pull/2135))
In CSS, [`initial`](https://developer.mozilla.org/en-US/docs/Web/CSS/initial), [`inherit`](https://developer.mozilla.org/en-US/docs/Web/CSS/inherit), and [`unset`](https://developer.mozilla.org/en-US/docs/Web/CSS/unset) are [CSS-wide keywords](https://drafts.csswg.org/css-values-4/#css-wide-keywords) which means they have special behavior when they are specified as a property value. For example, while `font-family: 'Arial'` (as a string) and `font-family: Arial` (as an identifier) are the same, `font-family: 'inherit'` (as a string) uses the font family named `inherit` but `font-family: inherit` (as an identifier) inherits the font family from the parent element. This means esbuild must not unquote these CSS-wide keywords (and `default`, 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](https://drafts.csswg.org/css-cascade-5/#defaulting-keywords) of which there are two: [`revert`](https://developer.mozilla.org/en-US/docs/Web/CSS/revert) and [`revert-layer`](https://developer.mozilla.org/en-US/docs/Web/CSS/revert-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:
```css
/* 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](https://togithub.com/yisibl).
### [`v0.14.30`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01430)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.29...v0.14.30)
- Change the context of TypeScript parameter decorators ([#2147](https://togithub.com/evanw/esbuild/issues/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:
```ts
class Class {
method(@decorator() arg) {}
}
```
becomes this JavaScript code:
```js
class Class {
method(arg) {}
}
__decorate([
__param(0, decorator())
], Class.prototype, "method", null);
```
This has several consequences:
- Whether `await` is allowed inside a decorator expression or not depends on whether the class declaration itself is in an `async` context or not. With this release, you can now use `await` inside a decorator expression when the class declaration is either inside an `async` function 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.
```ts
// Using "await" inside a decorator expression is now allowed
async function fn(foo: Promise) {
class Class {
method(@decorator(await foo) arg) {}
}
return Class
}
```
Also while TypeScript currently allows `await` to be used like this in `async` functions, it doesn't currently allow `yield` to be used like this in generator functions. It's not yet clear whether this behavior with `yield` is a bug or by design, so I haven't made any changes to esbuild's handling of `yield` inside 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.
```ts
// 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.
```ts
// 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`):
```js
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`):
```js
let arg = 1;
class Class {
method(arg2 = 2) {
}
}
__decorateClass([
__decorateParam(0, decorator(arg))
], Class.prototype, "method", 1);
```
- Fix some obscure edge cases with `super` property access
This release fixes the following obscure problems with `super` when targeting an older JavaScript environment such as `--target=es6`:
1. The compiler could previously crash when a lowered `async` arrow function contained a class with a field initializer that used a `super` property access:
```js
let foo = async () => class extends Object {
bar = super.toString
}
```
2. The compiler could previously generate incorrect code when a lowered `async` method of a derived class contained a nested class with a computed class member involving a `super` property access on the derived class:
```js
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))
```
3. The compiler could previously generate incorrect code when a default-exported class contained a `super` property access inside a lowered static private class field:
```js
class Foo {
static foo = 123
}
export default class extends Foo {
static #foo = super.foo
static bar = this.#foo
}
```
### [`v0.14.29`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01429)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.28...v0.14.29)
- Fix a minification bug with a double-nested `if` inside a label followed by `else` ([#2139](https://togithub.com/evanw/esbuild/issues/2139))
This fixes a minification bug that affects the edge case where `if` is followed by `else` and the `if` contains a label that contains a nested `if`. Normally esbuild's AST printer automatically wraps the body of a single-statement `if` in braces to avoid the "dangling else" `if`/`else` ambiguity common to C-like languages (where the `else` accidentally becomes associated with the inner `if` instead of the outer `if`). 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:
```js
// 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 `baseUrl` and `paths` in `tsconfig.json` ([#2119](https://togithub.com/evanw/esbuild/issues/2119))
In `tsconfig.json`, TypeScript forbids non-relative values inside `paths` if `baseUrl` is not present, and esbuild does too. However, TypeScript checked this after the entire `tsconfig.json` hierarchy was parsed while esbuild incorrectly checked this immediately when parsing the file containing the `paths` map. This caused incorrect warnings to be generated for `tsconfig.json` files that specify a `baseUrl` value and that inherit a `paths` value from an `extends` clause. Now esbuild will only check for non-relative `paths` values 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](https://togithub.com/evanw/esbuild/issues/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`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01428)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.27...v0.14.28)
- Add support for some new CSS rules ([#2115](https://togithub.com/evanw/esbuild/issues/2115), [#2116](https://togithub.com/evanw/esbuild/issues/2116), [#2117](https://togithub.com/evanw/esbuild/issues/2117))
This release adds support for [`@font-palette-values`](https://drafts.csswg.org/css-fonts-4/#font-palette-values), [`@counter-style`](https://developer.mozilla.org/en-US/docs/Web/CSS/@counter-style), and [`@font-feature-values`](https://developer.mozilla.org/en-US/docs/Web/CSS/@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:
```css
/* 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](https://togithub.com/evanw/esbuild/issues/2105))
Binary executables for this version are now published with Go version 1.18.0. The [Go release notes](https://go.dev/doc/go1.18) 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-wasm` package 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`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01427)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.26...v0.14.27)
- Avoid generating an enumerable `default` import for CommonJS files in Babel mode ([#2097](https://togithub.com/evanw/esbuild/issues/2097))
Importing a CommonJS module into an ES module can be done in two different ways. In node mode the `default` import is always set to `module.exports`, while in Babel mode the `default` import passes through to `module.exports.default` instead. Node mode is triggered when the importing file ends in `.mjs`, has `type: "module"` in its `package.json` file, or the imported module does not have a `__esModule` marker.
Previously esbuild always created the forwarding `default` import in Babel mode, even if `module.exports` had no property called `default`. This was problematic because the getter named `default` still 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 named `default` will now only be added in Babel mode if the `default` property exists at the time of the import.
- Fix a circular import edge case regarding ESM-to-CommonJS conversion ([#1894](https://togithub.com/evanw/esbuild/issues/1894), [#2059](https://togithub.com/evanw/esbuild/pull/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.exports` and 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 `__esModule` which 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 `__esModule` that 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.exports` object 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 into `require()` calls). This release changes `module.exports` initialization 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](https://togithub.com/indutny).
### [`v0.14.26`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01426)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.25...v0.14.26)
- Fix a tree shaking regression regarding `var` declarations ([#2080](https://togithub.com/evanw/esbuild/issues/2080), [#2085](https://togithub.com/evanw/esbuild/pull/2085), [#2098](https://togithub.com/evanw/esbuild/issues/2098), [#2099](https://togithub.com/evanw/esbuild/issues/2099))
Version 0.14.8 of esbuild enabled removal of duplicate function declarations when minification is enabled (see [#610](https://togithub.com/evanw/esbuild/issues/610)):
```js
// 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 `var` declarations in which all but the last declaration was dropped if tree-shaking was enabled. This only happens for top-level `var` declarations that re-declare the same variable multiple times. This regression has now been fixed:
```js
// 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](https://togithub.com/evanw/esbuild/pull/2038))
The upcoming version of TypeScript now lets you specify `<...>` type parameters on a JavaScript identifier without using a call expression:
```ts
const ErrorMap = Map; // new () => Map
const errorMap = new ErrorMap(); // Map
```
With this release, esbuild can now parse these new type annotations. This feature was contributed by [@g-plane](https://togithub.com/g-plane).
- Avoid `new Function` in esbuild's library code ([#2081](https://togithub.com/evanw/esbuild/issues/2081))
Some JavaScript environments such as Cloudflare Workers or Deno Deploy don't allow `new Function` because 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 using `new Function` so it will be able to run even when this restriction is in place.
- Drop superfluous `__name()` calls ([#2062](https://togithub.com/evanw/esbuild/pull/2062))
When the `--keep-names` option is specified, esbuild inserts calls to a `__name` helper function to ensure that the `.name` property 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 `__name` helper function when the name of the function or class object was not renamed during the linking process after all:
```js
// 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 `__name` is now no longer printed. This change was contributed by [@indutny](https://togithub.com/indutny).
### [`v0.14.25`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01425)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.24...v0.14.25)
- Reduce minification of CSS transforms to avoid Safari bugs ([#2057](https://togithub.com/evanw/esbuild/issues/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 `transform` specification](https://drafts.csswg.org/css-transforms-1/#transform-rendering) 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 `none` for the `transform` property 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:
```css
/* 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 `?.` operator
This adds new code minification rules that shorten code with the `?.` optional chaining operator when the result is equivalent:
```ts
// 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](https://togithub.com/evanw/esbuild/issues/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](https://togithub.com/evanw/esbuild/issues/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-wasm` package but it's installed automatically when you install the `esbuild` package on Android x64. So packages that depend on the `esbuild` package 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`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01424)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.23...v0.14.24)
- Allow `es2022` as a target environment ([#2012](https://togithub.com/evanw/esbuild/issues/2012))
TypeScript recently [added support for `es2022`](https://devblogs.microsoft.com/typescript/announcing-typescript-4-6/#target-es2022) as 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's `es2022` target may change in the future when the specification is finalized. Right now I have made the `es2022` target enable support for the syntax-related [finished proposals](https://togithub.com/tc39/proposals/blob/main/finished-proposals.md) that are marked as `2022`:
- 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](https://togithub.com/microsoft/TypeScript/issues/40594).
- Match `define` to strings in index expressions ([#2050](https://togithub.com/evanw/esbuild/issues/2050))
With this release, configuring `--define:foo.bar=baz` now matches and replaces both `foo.bar` and `foo['bar']` expressions in the original source code. This is necessary for people who have enabled TypeScript's [`noPropertyAccessFromIndexSignature` feature](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature), which prevents you from using normal property access syntax on a type with an index signature such as in the following code:
```ts
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`:
```js
baz;
foo["bar"];
```
Now esbuild will generate the following output instead:
```js
baz;
baz;
```
- Add `--mangle-quoted` to mangle quoted properties ([#218](https://togithub.com/evanw/esbuild/issues/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 mangle `foo._bar` but not `foo['_bar']`. There are some coding patterns where renaming quoted property names is desirable, such as when using TypeScript's [`noPropertyAccessFromIndexSignature` feature](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature) or when using TypeScript's [discriminated union narrowing behavior](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions):
```ts
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 value` check tells TypeScript to narrow the type of `value` to `Foo` in the true branch and to `Bar` in 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-quoted` to also rename property names inside string literals:
```js
// 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 namespace` statements ([#2070](https://togithub.com/evanw/esbuild/issues/2070))
TypeScript `.d.ts` type declaration files can sometimes contain statements of the form `export 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.ts` files 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's `main` field in `package.json` is a `.d.ts` file.
Previously esbuild only allowed `export as namespace` statements inside a `declare` context:
```ts
declare module Foo {
export as namespace foo;
}
```
Now esbuild will also allow these statements outside of a `declare` context:
```ts
export as namespace foo;
```
These statements are still just ignored and discarded.
- Strip import assertions from unrecognized `import()` expressions ([#2036](https://togithub.com/evanw/esbuild/issues/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 to `import()` wasn't a string literal. This problem is now fixed:
```js
// 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](https://togithub.com/evanw/esbuild/issues/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.ts` rules in `paths` in `tsconfig.json` files ([#2074](https://togithub.com/evanw/esbuild/issues/2074), [#2075](https://togithub.com/evanw/esbuild/pull/2075))
TypeScript's `tsconfig.json` configuration file has a `paths` field 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.ts` TypeScript 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.ts` file during the build.
With this release, esbuild will now ignore rules in `paths` that result in a `.d.ts` file during path resolution. This means code that does this should now be able to be bundled without modifying its `tsconfig.json` file to remove the `.d.ts` rule. This change was contributed by [@magic-akari](https://togithub.com/magic-akari).
- Disable Go compiler optimizations for the Linux RISC-V 64bit build ([#2035](https://togithub.com/evanw/esbuild/pull/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](https://togithub.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](https://togithub.com/piggynl).
### [`v0.14.23`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01423)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.22...v0.14.23)
- Update feature database to indicate that node 16.14+ supports import assertions ([#2030](https://togithub.com/evanw/esbuild/issues/2030))
Node versions 16.14 and above now support import assertions according to [these release notes](https://togithub.com/nodejs/node/blob/6db686710ee1579452b2908a7a41b91cb729b944/doc/changelogs/CHANGELOG_V16.md#16.14.0). This release updates esbuild's internal feature compatibility database with this information, so esbuild no longer strips import assertions with `--target=node16.14`:
```js
// 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 `@layer` rules ([#2027](https://togithub.com/evanw/esbuild/issues/2027))
This adds basic parsing support for a new CSS feature called `@layer` that changes how the CSS cascade works. Adding parsing support for this rule to esbuild means esbuild can now minify the contents of `@layer` rules:
```css
/* 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 `@layer` here:
- Documentation: https://developer.mozilla.org/en-US/docs/Web/CSS/[@layer](https://togithub.com/layer)
- Motivation: https://developer.chrome.com/blog/cascade-layers/
Note that the support added in this release is only for parsing and printing `@layer` rules. The bundler does not yet know about these rules and bundling with `@layer` may 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`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01422)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.21...v0.14.22)
- Preserve whitespace for token lists that look like CSS variable declarations ([#2020](https://togithub.com/evanw/esbuild/issues/2020))
Previously esbuild removed the whitespace after the CSS variable declaration in the following CSS:
```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 case.
- Ignore legal comments when merging adjacent duplicate CSS rules ([#2016](https://togithub.com/evanw/esbuild/issues/2016))
This release now generates more compact minified CSS when there are legal comments in between two adjacent rules with identical content:
```css
/* Original code */
a { color: red }
/* @preserve */
b { color: red }
/* Old output (with --minify) */
a{color:red}/* @preserve */b{color:red}
/* New output (with --minify) */
a,b{color:red}/* @preserve */
```
- Block `onResolve` and `onLoad` until `onStart` ends ([#1967](https://togithub.com/evanw/esbuild/issues/1967))
This release changes the semantics of the `onStart` callback. All `onStart` callbacks from all plugins are run concurrently so that a slow plugin doesn't hold up the entire build. That's still the case. However, previously the only thing waiting for the `onStart` callbacks to finish was the end of the build. This meant that `onResolve` and/or `onLoad` callbacks could sometimes run before `onStart` had finished. This was by design but violated user expectations. With this release, all `onStart` callbacks must finish before any `onResolve` and/or `onLoad` callbacks are run.
- Add a self-referential `default` export to the JS API ([#1897](https://togithub.com/evanw/esbuild/issues/1897))
Some people try to use esbuild's API using `import esbuild from 'esbuild'` instead of `import * as esbuild from 'esbuild'` (i.e. using a default import instead of a namespace import). There is no `default` export so that wasn't ever intended to work. But it would work sometimes depending on which tools you used and how they were configured so some people still wrote code this way. This release tries to make that work by adding a self-referential `default` export that is equal to esbuild's module namespace object.
More detail: The published package for esbuild's JS API is in CommonJS format, although the source code for esbuild's JS API is in ESM format. The original ESM code for esbuild's JS API has no export named `default` so using a default import like this doesn't work with Babel-compatible toolchains (since they respect the semantics of the original ESM code). However, it happens to work with node-compatible toolchains because node's implementation of importing CommonJS from ESM broke compatibility with existing conventions and automatically creates a `default` export which is set to `module.exports`. This is an unfortunate compatibility headache because it means the `default` import only works sometimes. This release tries to fix this by explicitly creating a self-referential `default` export. It now doesn't matter if you do `esbuild.build()`, `esbuild.default.build()`, or `esbuild.default.default.build()` because they should all do the same thing. Hopefully this means people don't have to deal with this problem anymore.
- Handle `write` errors when esbuild's child process is killed ([#2007](https://togithub.com/evanw/esbuild/issues/2007))
If you type Ctrl+C in a terminal when a script that uses esbuild's JS library is running, esbuild's child process may be killed before the parent process. In that case calls to the `write()` syscall may fail with an `EPIPE` error. Previously this resulted in an uncaught exception because esbuild didn't handle this case. Starting with this release, esbuild should now catch these errors and redirect them into a general `The service was stopped` error which should be returned from whatever top-level API calls were in progress.
- Better error message when browser WASM bugs are present ([#1863](https://togithub.com/evanw/esbuild/issues/1863))
Safari's WebAssembly implementation appears to be broken somehow, at least when running esbuild. Sometimes this manifests as a stack overflow and sometimes as a Go panic. Previously a Go panic resulted in the error message `Can't find variable: fs` but this should now result in the Go panic being printed to the console. Using esbuild's WebAssembly library in Safari is still broken but now there's a more helpful error message.
More detail: When Go panics, it prints a stack trace to stderr (i.e. file descriptor 2). Go's WebAssembly shim calls out to node's `fs.writeSync()` function to do this, and it converts calls to `fs.writeSync()` into calls to `console.log()` in the browser by providing a shim for `fs`. However, Go's shim code stores the shim on `window.fs` in the browser. This is undesirable because it pollutes the global scope and leads to brittle code that can break if other code also uses `window.fs`. To avoid this, esbuild shadows the global object by wrapping Go's shim. But that broke bare references to `fs` since the shim is no longer stored on `window.fs`. This release now stores the shim in a local variable named `fs` so that bare references to `fs` work correctly.
- Undo incorrect dead-code elimination with destructuring ([#1183](https://togithub.com/evanw/esbuild/issues/1183))
Previously esbuild eliminated these statements as dead code if tree-shaking was enabled:
```js
let [a] = {}
let { b } = null
```
This is incorrect because both of these lines will throw an error when evaluated. With this release, esbuild now preserves these statements even when tree shaking is enabled.
- Update to Go 1.17.7
The version of the Go compiler used to compile esbuild has been upgraded from Go 1.17.6 to Go 1.17.7, which contains a few [compiler and security bug fixes](https://togithub.com/golang/go/issues?q=milestone%3AGo1.17.7+label%3ACherryPickApproved).
### [`v0.14.21`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01421)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.20...v0.14.21)
- Handle an additional `browser` map edge case ([#2001](https://togithub.com/evanw/esbuild/pull/2001), [#2002](https://togithub.com/evanw/esbuild/issues/2002))
There is a community convention around the `browser` field in `package.json` that allows remapping import paths within a package when the package is bundled for use within a browser. There isn't a rigorous definition of how it's supposed to work and every bundler implements it differently. The approach esbuild uses is to try to be "maximally compatible" in that if at least one bundler exhibits a particular behavior regarding the `browser` map that allows a mapping to work, then esbuild also attempts to make that work.
I have a collection of test cases for this going here: https://github.com/evanw/package-json-browser-tests. However, I was missing test coverage for the edge case where a package path import in a subdirectory of the package could potentially match a remapping. The "maximally compatible" approach means replicating bugs in Browserify's implementation of the feature where package paths are mistaken for relative paths and are still remapped. Here's a specific example of an edge case that's now handled:
- `entry.js`:
```js
require('pkg/sub')
```
- `node_modules/pkg/package.json`:
```json
{
"browser": {
"./sub": "./sub/foo.js",
"./sub/sub": "./sub/bar.js"
}
}
```
- `node_modules/pkg/sub/foo.js`:
```js
require('sub')
```
- `node_modules/pkg/sub/bar.js`:
```js
console.log('works')
```
The import path `sub` in `require('sub')` is mistaken for a relative path by Browserify due to a bug in Browserify, so Browserify treats it as if it were `./sub` instead. This is a Browserify-specific behavior and currently doesn't happen in any other bundler (except for esbuild, which attempts to replicate Browserify's bug).
Previously esbuild was incorrectly resolving `./sub` relative to the top-level package directory instead of to the subdirectory in this case, which meant `./sub` was incorrectly matching `"./sub": "./sub/foo.js"` instead of `"./sub/sub": "./sub/bar.js"`. This has been fixed so esbuild can now emulate Browserify's bug correctly in this edge case.
- Support for esbuild with Linux on RISC-V 64bit ([#2000](https://togithub.com/evanw/esbuild/pull/2000))
With this release, esbuild now has a published binary executable for the RISC-V 64bit architecture in the [`esbuild-linux-riscv64`](https://www.npmjs.com/package/esbuild-linux-riscv64) npm package. This change was contributed by [@piggynl](https://togithub.com/piggynl).
### [`v0.14.20`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01420)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.19...v0.14.20)
- Fix property mangling and keyword properties ([#1998](https://togithub.com/evanw/esbuild/issues/1998))
Previously enabling property mangling with `--mangle-props=` failed to add a space before property names after a keyword. This bug has been fixed:
```js
// Original code
class Foo {
static foo = {
get bar() {}
}
}
// Old output (with --minify --mangle-props=.)
class Foo{statics={gett(){}}}
// New output (with --minify --mangle-props=.)
class Foo{static s={get t(){}}}
```
### [`v0.14.19`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01419)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.18...v0.14.19)
- Special-case `const` inlining at the top of a scope ([#1317](https://togithub.com/evanw/esbuild/issues/1317), [#1981](https://togithub.com/evanw/esbuild/issues/1981))
The minifier now inlines `const` variables (even across modules during bundling) if a certain set of specific requirements are met:
- All `const` variables to be inlined are at the top of their scope
- That scope doesn't contain any `import` or `export` statements with paths
- All constants to be inlined are `null`, `undefined`, `true`, `false`, an integer, or a short real number
- Any expression outside of a small list of allowed ones stops constant identification
Practically speaking this basically means that you can trigger this optimization by just putting the constants you want inlined into a separate file (e.g. `constants.js`) and bundling everything together.
These specific conditions are present to avoid esbuild unintentionally causing any behavior changes by inlining constants when the variable reference could potentially be evaluated before being declared. It's possible to identify more cases where constants can be inlined but doing so may require complex call graph analysis so it has not been implemented. Although these specific heuristics may change over time, this general approach to constant inlining should continue to work going forward.
Here's an example:
```js
// Original code
const bold = 1 << 0;
const italic = 1 << 1;
const underline = 1 << 2;
const font = bold | italic | underline;
console.log(font);
// Old output (with --minify --bundle)
(()=>{var o=1<<0,n=1<<1,c=1<<2,t=o|n|c;console.log(t);})();
// New output (with --minify --bundle)
(()=>{console.log(7);})();
```
### [`v0.14.18`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01418)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.17...v0.14.18)
- Add the `--mangle-cache=` feature ([#1977](https://togithub.com/evanw/esbuild/issues/1977))
This release adds a cache API for the newly-released `--mangle-props=` feature. When enabled, all mangled property renamings are recorded in the cache during the initial build. Subsequent builds reuse the renamings stored in the cache and add additional renamings for any newly-added properties. This has a few consequences:
- You can customize what mangled properties are renamed to by editing the cache before passing it to esbuild (the cache is a map of the original name to the mangled name).
- The cache serves as a list of all properties that were mangled. You can easily scan it to see if there are any unexpected property renamings.
- You can disable mangling for individual properties by setting the renamed value to `false` instead of to a string. This is similar to the `--reserve-props=` setting but on a per-property basis.
- You can ensure consistent renaming between builds (e.g. a main-thread file and a web worker, or a library and a plugin). Without this feature, each build would do an independent renaming operation and the mangled property names likely wouldn't be consistent.
Here's how to use it:
- CLI
```sh
$ esbuild example.ts --mangle-props=_$ --mangle-cache=cache.json
```
- JS API
```js
let result = await esbuild.build({
entryPoints: ['example.ts'],
mangleProps: /_$/,
mangleCache: {
customRenaming_: '__c',
disabledRenaming_: false,
},
})
let updatedMangleCache = result.mangleCache
```
- Go API
```go
result := api.Build(api.BuildOptions{
EntryPoints: []string{"example.ts"},
MangleProps: "_$",
MangleCache: map[string]interface{}{
"customRenaming_": "__c",
"disabledRenaming_": false,
},
})
updatedMangleCache := result.MangleCache
```
The above code would do something like the following:
```js
// Original code
x = {
customRenaming_: 1,
disabledRenaming_: 2,
otherProp_: 3,
}
// Generated code
x = {
__c: 1,
disabledRenaming_: 2,
a: 3
};
// Updated mangle cache
{
"customRenaming_": "__c",
"disabledRenaming_": false,
"otherProp_": "a"
}
```
- Add `opera` and `ie` as possible target environments
You can now target [Opera](https://www.opera.com/) and/or [Internet Explorer](https://www.microsoft.com/en-us/download/internet-explorer.aspx) using the `--target=` setting. For example, `--target=opera45,ie9` targets Opera 45 and Internet Explorer 9. This change does not add any additional features to esbuild's code transformation pipeline to transform newer syntax so that it works in Internet Explorer. It just adds information about what features are supported in these browsers to esbuild's internal feature compatibility table.
- Minify `typeof x !== 'undefined'` to `typeof x < 'u'`
This release introduces a small improvement for code that does a lot of `typeof` checks against `undefined`:
```js
// Original code
y = typeof x !== 'undefined';
// Old output (with --minify)
y=typeof x!="undefined";
// New output (with --minify)
y=typeof x<"u";
```
This transformation is only active when minification is enabled, and is disabled if the language target is set lower than ES2020 or if Internet Explorer is set as a target environment. Before ES2020, implementations were allowed to return non-standard values from the `typeof` operator for a few objects. Internet Explorer took advantage of this to sometimes return the string `'unknown'` instead of `'undefined'`. But this has been removed from the specification and Internet Explorer was the only engine to do this, so this minification is valid for code that does not need to target Internet Explorer.
### [`v0.14.17`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01417)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.16...v0.14.17)
- Attempt to fix an install script issue on Ubuntu Linux ([#1711](https://togithub.com/evanw/esbuild/issues/1711))
There have been some reports of esbuild failing to install on Ubuntu Linux for a while now. I haven't been able to reproduce this myself due to lack of reproduction instructions until today, when I learned that the issue only happens when you install node from the [Snap Store](https://snapcraft.io/) instead of downloading the [official version of node](https://nodejs.org/dist/).
The problem appears to be that when node is installed from the Snap Store, install scripts are run with stderr not being writable? This then appears to cause a problem for esbuild's install script when it uses `execFileSync` to validate that the esbuild binary is working correctly. This throws the error `EACCES: permission denied, write` even though this particular command never writes to stderr.
Node's documentation says that stderr for `execFileSync` defaults to that of the parent process. Forcing it to `'pipe'` instead appears to fix the issue, although I still don't fully understand what's happening or why. I'm publishing this small change regardless to see if it fixes this install script edge case.
- Avoid a syntax error due to `--mangle-props=.` and `super()` ([#1976](https://togithub.com/evanw/esbuild/issues/1976))
This release fixes an issue where passing `--mangle-props=.` (i.e. telling esbuild to mangle every single property) caused a syntax error with code like this:
```js
class Foo {}
class Bar extends Foo {
constructor() {
super();
}
}
```
The problem was that `constructor` was being renamed to another method, which then made it no longer a constructor, which meant that `super()` was now a syntax error. I have added a workaround that avoids renaming any property named `constructor` so that esbuild doesn't generate a syntax error here.
Despite this fix, I highly recommend not using `--mangle-props=.` because your code will almost certainly be broken. You will have to manually add every single property that you don't want mangled to `--reserve-props=` which is an excessive maintenance burden (e.g. reserve `parse` to use `JSON.parse`). Instead I recommend using a common pattern for all properties you intend to be mangled that is unlikely to appear in the APIs you use such as "ends in an underscore." This is an opt-in approach instead of an opt-out approach. It also makes it obvious when reading the code which properties will be mangled and which ones won't be.
### [`v0.14.16`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01416)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.15...v0.14.16)
- Support property name mangling with some TypeScript syntax features
The newly-released `--mangle-props=` feature previously only affected JavaScript syntax features. This release adds support for using mangle props with certain TypeScript syntax features:
- **TypeScript parameter properties**
Parameter properties are a TypeScript-only shorthand way of initializing a class field directly from the constructor argument list. Previously parameter properties were not treated as properties to be mangled. They should now be handled correctly:
```ts
// Original code
class Foo {
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 contains the following updates:
^0.13.2
->^0.14.0
Release Notes
evanw/esbuild
### [`v0.14.31`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01431) [Compare Source](https://togithub.com/evanw/esbuild/compare/v0.14.30...v0.14.31) - Add support for parsing "optional variance annotations" from TypeScript 4.7 ([#2102](https://togithub.com/evanw/esbuild/pull/2102)) The upcoming version of TypeScript now lets you specify `in` and/or `out` on certain type parameters (specifically only on a type alias, an interface declaration, or a class declaration). These modifiers control type paramemter covariance and contravariance: ```ts type ProviderConfiguration
📅 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.
This PR has been generated by WhiteSource Renovate. View repository job log here.