evanw/esbuild
### [`v0.15.18`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01518)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.17...v0.15.18)
- Performance improvements for both JS and CSS
This release brings noticeable performance improvements for JS parsing and for CSS parsing and printing. Here's an example benchmark for using esbuild to pretty-print a single large minified CSS file and JS file:
| Test case | Previous release | This release |
|----------------|------------------|--------------------|
| 4.8mb CSS file | 19ms | 11ms (1.7x faster) |
| 5.8mb JS file | 36ms | 32ms (1.1x faster) |
The performance improvements were very straightforward:
- Identifiers were being scanned using a generic character advancement function instead of using custom inline code. Advancing past each character involved UTF-8 decoding as well as updating multiple member variables. This was sped up using loop that skips UTF-8 decoding entirely and that only updates member variables once at the end. This is faster because identifiers are plain ASCII in the vast majority of cases, so Unicode decoding is almost always unnecessary.
- CSS identifiers and CSS strings were still being printed one character at a time. Apparently I forgot to move this part of esbuild's CSS infrastructure beyond the proof-of-concept stage. These were both very obvious in the profiler, so I think maybe I have just never profiled esbuild's CSS printing before?
- There was unnecessary work being done that was related to source maps when source map output was disabled. I likely haven't observed this before because esbuild's benchmarks always have source maps enabled. This work is now disabled when it's not going to be used.
I definitely should have caught these performance issues earlier. Better late than never I suppose.
### [`v0.15.17`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01517)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.16...v0.15.17)
- Search for missing source map code on the file system ([#2711](https://togithub.com/evanw/esbuild/issues/2711))
[Source maps](https://sourcemaps.info/spec.html) are JSON files that map from compiled code back to the original code. They provide the original source code using two arrays: `sources` (required) and `sourcesContent` (optional). When bundling is enabled, esbuild is able to bundle code with source maps that was compiled by other tools (e.g. with Webpack) and emit source maps that map all the way back to the original code (e.g. before Webpack compiled it).
Previously if the input source maps omitted the optional `sourcesContent` array, esbuild would use `null` for the source content in the source map that it generates (since the source content isn't available). However, sometimes the original source code is actually still present on the file system. With this release, esbuild will now try to find the original source code using the path in the `sources` array and will use that instead of `null` if it was found.
- Fix parsing bug with TypeScript `infer` and `extends` ([#2712](https://togithub.com/evanw/esbuild/issues/2712))
This release fixes a bug where esbuild incorrectly failed to parse valid TypeScript code that nests `extends` inside `infer` inside `extends`, such as in the example below:
```ts
type A = {};
type B = {} extends infer T extends {} ? A : never;
```
TypeScript code that does this should now be parsed correctly.
- Use `WebAssembly.instantiateStreaming` if available ([#1036](https://togithub.com/evanw/esbuild/pull/1036), [#1900](https://togithub.com/evanw/esbuild/pull/1900))
Currently the WebAssembly version of esbuild uses `fetch` to download `esbuild.wasm` and then `WebAssembly.instantiate` to compile it. There is a newer API called `WebAssembly.instantiateStreaming` that both downloads and compiles at the same time, which can be a performance improvement if both downloading and compiling are slow. With this release, esbuild now attempts to use `WebAssembly.instantiateStreaming` and falls back to the original approach if that fails.
The implementation for this builds on a PR by [@lbwa](https://togithub.com/lbwa).
- Preserve Webpack comments inside constructor calls ([#2439](https://togithub.com/evanw/esbuild/issues/2439))
This improves the use of esbuild as a faster TypeScript-to-JavaScript frontend for Webpack, which has special [magic comments](https://webpack.js.org/api/module-methods/#magic-comments) inside `new Worker()` expressions that affect Webpack's behavior.
### [`v0.15.16`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01516)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.15...v0.15.16)
- Add a package alias feature ([#2191](https://togithub.com/evanw/esbuild/issues/2191))
With this release, you can now easily substitute one package for another at build time with the new `alias` feature. For example, `--alias:oldpkg=newpkg` replaces all imports of `oldpkg` with `newpkg`. One use case for this is easily replacing a node-only package with a browser-friendly package in 3rd-party code that you don't control. These new substitutions happen first before all of esbuild's existing path resolution logic.
Note that when an import path is substituted using an alias, the resulting import path is resolved in the working directory instead of in the directory containing the source file with the import path. If needed, the working directory can be set with the `cd` command when using the CLI or with the `absWorkingDir` setting when using the JS or Go APIs.
- Fix crash when pretty-printing minified JSX with object spread of object literal with computed property ([#2697](https://togithub.com/evanw/esbuild/issues/2697))
JSX elements are translated to JavaScript function calls and JSX element attributes are translated to properties on a JavaScript object literal. These properties are always either strings (e.g. in ``, `y` is a string) or an object spread (e.g. in ``, `y` is an object spread) because JSX doesn't provide syntax for directly passing a computed property as a JSX attribute. However, esbuild's minifier has a rule that tries to inline object spread with an inline object literal in JavaScript. For example, `x = { ...{ y } }` is minified to `x={y}` when minification is enabled. This means that there is a way to generate a non-string non-spread JSX attribute in esbuild's internal representation. One example is with ``. When minification is enabled, esbuild's internal representation of this is something like `` due to object spread inlining, which is not valid JSX syntax. If this internal representation is then pretty-printed as JSX using `--minify --jsx=preserve`, esbuild previously crashed when trying to print this invalid syntax. With this release, esbuild will now print `` in this scenario instead of crashing.
### [`v0.15.15`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01515)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.14...v0.15.15)
- Remove duplicate CSS rules across files ([#2688](https://togithub.com/evanw/esbuild/issues/2688))
When two or more CSS rules are exactly the same (even if they are not adjacent), all but the last one can safely be removed:
```css
/* Before */
a { color: red; }
span { font-weight: bold; }
a { color: red; }
/* After */
span { font-weight: bold; }
a { color: red; }
```
Previously esbuild only did this transformation within a single source file. But with this release, esbuild will now do this transformation across source files, which may lead to smaller CSS output if the same rules are repeated across multiple CSS source files in the same bundle. This transformation is only enabled when minifying (specifically when syntax minification is enabled).
- Add `deno` as a valid value for `target` ([#2686](https://togithub.com/evanw/esbuild/issues/2686))
The `target` setting in esbuild allows you to enable or disable JavaScript syntax features for a given version of a set of target JavaScript VMs. Previously [Deno](https://deno.land/) was not one of the JavaScript VMs that esbuild supported with `target`, but it will now be supported starting from this release. For example, versions of Deno older than v1.2 don't support the new `||=` operator, so adding e.g. `--target=deno1.0` to esbuild now lets you tell esbuild to transpile `||=` to older JavaScript.
- Fix the `esbuild-wasm` package in Node v19 ([#2683](https://togithub.com/evanw/esbuild/issues/2683))
A recent change to Node v19 added a non-writable `crypto` property to the global object: [https://github.com/nodejs/node/pull/44897](https://togithub.com/nodejs/node/pull/44897). This conflicts with Go's WebAssembly shim code, which overwrites the global `crypto` property. As a result, all Go-based WebAssembly code that uses the built-in shim (including esbuild) is now broken on Node v19. This release of esbuild fixes the issue by reconfiguring the global `crypto` property to be writable before invoking Go's WebAssembly shim code.
- Fix CSS dimension printing exponent confusion edge case ([#2677](https://togithub.com/evanw/esbuild/issues/2677))
In CSS, a dimension token has a numeric "value" part and an identifier "unit" part. For example, the dimension token `32px` has a value of `32` and a unit of `px`. The unit can be any valid CSS identifier. The value can be any number in floating-point format including an optional exponent (e.g. `-3.14e-0` has an exponent of `e-0`). The full details of this syntax are here: https://www.w3.org/TR/css-syntax-3/.
To maintain the integrity of the dimension token through the printing process, esbuild must handle the edge case where the unit looks like an exponent. One such case is the dimension `1e\32` which has the value `1` and the unit `e2`. It would be bad if this dimension token was printed such that a CSS parser would parse it as a number token with the value `1e2` instead of a dimension token. The way esbuild currently does this is to escape the leading `e` in the dimension unit, so esbuild would parse `1e\32` but print `1\65 2` (both `1e\32` and `1\65 2` represent a dimension token with a value of `1` and a unit of `e2`).
However, there is an even narrower edge case regarding this edge case. If the value part of the dimension token itself has an `e`, then it's not necessary to escape the `e` in the dimension unit because a CSS parser won't confuse the unit with the exponent even though it looks like one (since a number can only have at most one exponent). This came up because the grammar for the CSS `unicode-range` property uses a hack that lets you specify a hexadecimal range without quotes even though CSS has no token for a hexadecimal range. The hack is to allow the hexadecimal range to be parsed as a dimension token and optionally also a number token. Here is the grammar for `unicode-range`:
unicode-range =
#
=
u '+' '?'* |
u '?'* |
u '?'* |
u |
u |
u '+' '?'+
and here is an example `unicode-range` declaration that was problematic for esbuild:
```css
@font-face {
unicode-range: U+0e2e-0e2f;
}
```
This is parsed as a dimension with a value of `+0e2` and a unit of `e-0e2f`. This was problematic for esbuild because the unit starts with `e-0` which could be confused with an exponent when appended after a number, so esbuild was escaping the `e` character in the unit. However, this escaping is unnecessary because in this case the dimension value already has an exponent in it. With this release, esbuild will no longer unnecessarily escape the `e` in the dimension unit in these cases, which should fix the printing of `unicode-range` declarations.
An aside: You may be wondering why esbuild is trying to escape the `e` at all and why it doesn't just pass through the original source code unmodified. The reason why esbuild does this is that, for robustness, esbuild's AST generally tries to omit semantically-unrelated information and esbuild's code printers always try to preserve the semantics of the underlying AST. That way the rest of esbuild's internals can just deal with semantics instead of presentation. They don't have to think about how the AST will be printed when changing the AST. This is the same reason that esbuild's JavaScript AST doesn't have a "parentheses" node (e.g. `a * (b + c)` is represented by the AST `multiply(a, add(b, c))` instead of `multiply(a, parentheses(add(b, c)))`). Instead, the printer automatically inserts parentheses as necessary to maintain the semantics of the AST, which means all of the optimizations that run over the AST don't have to worry about keeping the parentheses up to date. Similarly, the CSS AST for the dimension token stores the actual unit and the printer makes sure the unit is properly escaped depending on what value it's placed after. All of the other code operating on CSS ASTs doesn't have to worry about parsing escapes to compare units or about keeping escapes up to date when the AST is modified. Hopefully that makes sense.
- Attempt to avoid creating the `node_modules/.cache` directory for people that use Yarn 2+ in Plug'n'Play mode ([#2685](https://togithub.com/evanw/esbuild/issues/2685))
When Yarn's PnP mode is enabled, packages installed by Yarn may or may not be put inside `.zip` files. The specific heuristics for when this happens change over time in between Yarn versions. This is problematic for esbuild because esbuild's JavaScript package needs to execute a binary file inside the package. Yarn makes extensive modifications to Node's file system APIs at run time to pretend that `.zip` files are normal directories and to make it hard to tell whether a file is real or not (since in theory it doesn't matter). But they haven't modified Node's `child_process.execFileSync` API so attempting to execute a file inside a zip file fails. To get around this, esbuild previously used Node's file system APIs to copy the binary executable to another location before invoking `execFileSync`. Under the hood this caused Yarn to extract the file from the zip file into a real file that can then be run.
However, esbuild copied its executable into `node_modules/.cache/esbuild`. This is the [official recommendation from the Yarn team](https://yarnpkg.com/advanced/rulebook/#packages-should-never-write-inside-their-own-folder-outside-of-postinstall) for where packages are supposed to put these types of files when Yarn PnP is being used. However, users of Yarn PnP with esbuild find this really annoying because they don't like looking at the `node_modules` directory. With this release, esbuild now sets `"preferUnplugged": true` in its `package.json` files, which tells newer versions of Yarn to not put esbuild's packages in a zip file. There may exist older versions of Yarn that don't support `preferUnplugged`. In that case esbuild should still copy the executable to a cache directory, so it should still run (hopefully, since I haven't tested this myself). Note that esbuild setting `"preferUnplugged": true` may have the side effect of esbuild taking up more space on the file system in the event that multiple platforms are installed simultaneously, or that you're using an older version of Yarn that always installs packages for all platforms. In that case you may want to update to a newer version of Yarn since Yarn has recently changed to only install packages for the current platform.
### [`v0.15.14`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01514)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.13...v0.15.14)
- Fix parsing of TypeScript `infer` inside a conditional `extends` ([#2675](https://togithub.com/evanw/esbuild/issues/2675))
Unlike JavaScript, parsing TypeScript sometimes requires backtracking. The `infer A` type operator can take an optional constraint of the form `infer A extends B`. However, this syntax conflicts with the similar conditional type operator `A extends B ? C : D` in cases where the syntax is combined, such as `infer A extends B ? C : D`. This is supposed to be parsed as `(infer A) extends B ? C : D`. Previously esbuild incorrectly parsed this as `(infer A extends B) ? C : D` instead, which is a parse error since the `?:` conditional operator requires the `extends` keyword as part of the conditional type. TypeScript disambiguates by speculatively parsing the `extends` after the `infer`, but backtracking if a `?` token is encountered afterward. With this release, esbuild should now do the same thing, so esbuild should now correctly parse these types. Here's a real-world example of such a type:
```ts
type Normalized = T extends Array
? Dictionary>
: {
[P in keyof T]: T[P] extends Array
? Dictionary>
: Normalized
}
```
- Avoid unnecessary watch mode rebuilds when debug logging is enabled ([#2661](https://togithub.com/evanw/esbuild/issues/2661))
When debug-level logs are enabled (such as with `--log-level=debug`), esbuild's path resolution subsystem generates debug log messages that say something like "Read 20 entries for directory /home/user" to help you debug what esbuild's path resolution is doing. This caused esbuild's watch mode subsystem to add a dependency on the full list of entries in that directory since if that changes, the generated log message would also have to be updated. However, meant that on systems where a parent directory undergoes constant directory entry churn, esbuild's watch mode would continue to rebuild if `--log-level=debug` was passed.
With this release, these debug log messages are now generated by "peeking" at the file system state while bypassing esbuild's watch mode dependency tracking. So now watch mode doesn't consider the count of directory entries in these debug log messages to be a part of the build that needs to be kept up to date when the file system state changes.
### [`v0.15.13`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01513)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.12...v0.15.13)
- Add support for the TypeScript 4.9 `satisfies` operator ([#2509](https://togithub.com/evanw/esbuild/pull/2509))
TypeScript 4.9 introduces a new operator called `satisfies` that lets you check that a given value satisfies a less specific type without casting it to that less specific type and without generating any additional code at run-time. It looks like this:
```ts
const value = { foo: 1, bar: false } satisfies Record
console.log(value.foo.toFixed(1)) // TypeScript knows that "foo" is a number here
```
Before this existed, you could use a cast with `as` to check that a value satisfies a less specific type, but that removes any additional knowledge that TypeScript has about that specific value:
```ts
const value = { foo: 1, bar: false } as Record
console.log(value.foo.toFixed(1)) // TypeScript no longer knows that "foo" is a number
```
You can read more about this feature in [TypeScript's blog post for 4.9](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-rc/#the-satisfies-operator) as well as [the associated TypeScript issue for this feature](https://togithub.com/microsoft/TypeScript/issues/47920).
This feature was implemented in esbuild by [@magic-akari](https://togithub.com/magic-akari).
- Fix watch mode constantly rebuilding if the parent directory is inaccessible ([#2640](https://togithub.com/evanw/esbuild/issues/2640))
Android is unusual in that it has an inaccessible directory in the path to the root, which esbuild was not originally built to handle. To handle cases like this, the path resolution layer in esbuild has a hack where it treats inaccessible directories as empty. However, esbuild's watch implementation currently triggers a rebuild if a directory previously encountered an error but the directory now exists. The assumption is that the previous error was caused by the directory not existing. Although that's usually the case, it's not the case for this particular parent directory on Android. Instead the error is that the directory previously existed but was inaccessible.
This discrepancy between esbuild's path resolution layer and its watch mode was causing watch mode to rebuild continuously on Android. With this release, esbuild's watch mode instead checks for an error status change in the `readdir` file system call, so watch mode should no longer rebuild continuously on Android.
- Apply a fix for a rare deadlock with the JavaScript API ([#1842](https://togithub.com/evanw/esbuild/issues/1842), [#2485](https://togithub.com/evanw/esbuild/issues/2485))
There have been reports of esbuild sometimes exiting with an "all goroutines are asleep" deadlock message from the Go language runtime. This issue hasn't made much progress until recently, where a possible cause was discovered (thanks to [@jfirebaugh](https://togithub.com/jfirebaugh) for the investigation). This release contains a possible fix for that possible cause, so this deadlock may have been fixed. The fix cannot be easily verified because the deadlock is non-deterministic and rare. If this was indeed the cause, then this issue only affected the JavaScript API in situations where esbuild was already in the process of exiting.
In detail: The underlying cause is that Go's [`sync.WaitGroup`](https://pkg.go.dev/sync#WaitGroup) API for waiting for a set of goroutines to finish is not fully thread-safe. Specifically it's not safe to call `Add()` concurrently with `Wait()` when the wait group counter is zero due to a data race. This situation could come up with esbuild's JavaScript API when the host JavaScript process closes the child process's stdin and the child process (with no active tasks) calls `Wait()` to check that there are no active tasks, at the same time as esbuild's watchdog timer calls `Add()` to add an active task (that pings the host to see if it's still there). The fix in this release is to avoid calling `Add()` once we learn that stdin has been closed but before we call `Wait()`.
### [`v0.15.12`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01512)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.11...v0.15.12)
- Fix minifier correctness bug with single-use substitutions ([#2619](https://togithub.com/evanw/esbuild/issues/2619))
When minification is enabled, esbuild will attempt to eliminate variables that are only used once in certain cases. For example, esbuild minifies this code:
```js
function getEmailForUser(name) {
let users = db.table('users');
let user = users.find({ name });
let email = user?.get('email');
return email;
}
```
into this code:
```js
function getEmailForUser(e){return db.table("users").find({name:e})?.get("email")}
```
However, this transformation had a bug where esbuild did not correctly consider the "read" part of binary read-modify-write assignment operators. For example, it's incorrect to minify the following code into `bar += fn()` because the call to `fn()` might modify `bar`:
```js
const foo = fn();
bar += foo;
```
In addition to fixing this correctness bug, this release also improves esbuild's output in the case where all values being skipped over are primitives:
```js
function toneMapLuminance(r, g, b) {
let hdr = luminance(r, g, b)
let decay = 1 / (1 + hdr)
return 1 - decay
}
```
Previous releases of esbuild didn't substitute these single-use variables here, but esbuild will now minify this to the following code starting with this release:
```js
function toneMapLuminance(e,n,a){return 1-1/(1+luminance(e,n,a))}
```
### [`v0.15.11`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01511)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.10...v0.15.11)
- Fix various edge cases regarding template tags and `this` ([#2610](https://togithub.com/evanw/esbuild/issues/2610))
This release fixes some bugs where the value of `this` wasn't correctly preserved when evaluating template tags in a few edge cases. These edge cases are listed below:
```js
async function test() {
class Foo { foo() { return this } }
class Bar extends Foo {
a = async () => super.foo``
b = async () => super['foo']``
c = async (foo) => super[foo]``
}
function foo() { return this }
const obj = { foo }
const bar = new Bar
console.log(
(await bar.a()) === bar,
(await bar.b()) === bar,
(await bar.c('foo')) === bar,
{ foo }.foo``.foo === foo,
(true && obj.foo)`` !== obj,
(false || obj.foo)`` !== obj,
(null ?? obj.foo)`` !== obj,
)
}
test()
```
Each edge case in the code above previously incorrectly printed `false` when run through esbuild with `--minify --target=es6` but now correctly prints `true`. These edge cases are unlikely to have affected real-world code.
### [`v0.15.10`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01510)
[Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.9...v0.15.10)
- Add support for node's "pattern trailers" syntax ([#2569](https://togithub.com/evanw/esbuild/issues/2569))
After esbuild implemented node's `exports` feature in `package.json`, node changed the feature to also allow text after `*` wildcards in patterns. Previously the `*` was required to be at the end of the pattern. It lets you do something like this:
```json
{
"exports": {
"./features/*": "./features/*.js",
"./features/*.js": "./features/*.js"
}
}
```
With this release, esbuild now supports these types of patterns too.
- Fix subpath imports with Yarn PnP ([#2545](https://togithub.com/evanw/esbuild/issues/2545))
Node has a little-used feature called [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) which are package-internal imports that start with `#` and that go through the `imports` map in `package.json`. Previously esbuild had a bug that caused esbuild to not handle these correctly in packages installed via Yarn's "Plug'n'Play" installation strategy. The problem was that subpath imports were being checked after Yarn PnP instead of before. This release reorders these checks, which should allow subpath imports to work in this case.
- Link from JS to CSS in the metafile ([#1861](https://togithub.com/evanw/esbuild/issues/1861), [#2565](https://togithub.com/evanw/esbuild/issues/2565))
When you import CSS into a bundled JS file, esbuild creates a parallel CSS bundle next to your JS bundle. So if `app.ts` imports some CSS files and you bundle it, esbuild will give you `app.js` and `app.css`. You would then add both `` and `` to your HTML to include everything in the page. This approach is more efficient than having esbuild insert additional JavaScript into `app.js` that downloads and includes `app.css` because it means the browser can download and parse both the CSS and the JS in parallel (and potentially apply the CSS before the JS has even finished downloading).
However, sometimes it's difficult to generate the `` tag. One case is when you've added `[hash]` to the [entry names](https://esbuild.github.io/api/#entry-names) setting to include a content hash in the file name. Then the file name will look something like `app-GX7G2SBE.css` and may change across subsequent builds. You can tell esbuild to generate build metadata using the `metafile` API option but the metadata only tells you which generated JS bundle corresponds to a JS entry point (via the `entryPoint` property), not which file corresponds to the associated CSS bundle. Working around this was hacky and involved string manipulation.
This release adds the `cssBundle` property to the metafile to make this easier. It's present on the metadata for the generated JS bundle and points to the associated CSS bundle. So to generate the HTML tags for a given JS entry point, you first find the output file with the `entryPoint` you are looking for (and put that in a ` Githubissues.
Githubissues is a development platform for aggregating issues.
This PR contains the following updates:
0.15.6
->0.15.18
Release Notes
evanw/esbuild
### [`v0.15.18`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01518) [Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.17...v0.15.18) - Performance improvements for both JS and CSS This release brings noticeable performance improvements for JS parsing and for CSS parsing and printing. Here's an example benchmark for using esbuild to pretty-print a single large minified CSS file and JS file: | Test case | Previous release | This release | |----------------|------------------|--------------------| | 4.8mb CSS file | 19ms | 11ms (1.7x faster) | | 5.8mb JS file | 36ms | 32ms (1.1x faster) | The performance improvements were very straightforward: - Identifiers were being scanned using a generic character advancement function instead of using custom inline code. Advancing past each character involved UTF-8 decoding as well as updating multiple member variables. This was sped up using loop that skips UTF-8 decoding entirely and that only updates member variables once at the end. This is faster because identifiers are plain ASCII in the vast majority of cases, so Unicode decoding is almost always unnecessary. - CSS identifiers and CSS strings were still being printed one character at a time. Apparently I forgot to move this part of esbuild's CSS infrastructure beyond the proof-of-concept stage. These were both very obvious in the profiler, so I think maybe I have just never profiled esbuild's CSS printing before? - There was unnecessary work being done that was related to source maps when source map output was disabled. I likely haven't observed this before because esbuild's benchmarks always have source maps enabled. This work is now disabled when it's not going to be used. I definitely should have caught these performance issues earlier. Better late than never I suppose. ### [`v0.15.17`](https://togithub.com/evanw/esbuild/blob/HEAD/CHANGELOG.md#01517) [Compare Source](https://togithub.com/evanw/esbuild/compare/v0.15.16...v0.15.17) - Search for missing source map code on the file system ([#2711](https://togithub.com/evanw/esbuild/issues/2711)) [Source maps](https://sourcemaps.info/spec.html) are JSON files that map from compiled code back to the original code. They provide the original source code using two arrays: `sources` (required) and `sourcesContent` (optional). When bundling is enabled, esbuild is able to bundle code with source maps that was compiled by other tools (e.g. with Webpack) and emit source maps that map all the way back to the original code (e.g. before Webpack compiled it). Previously if the input source maps omitted the optional `sourcesContent` array, esbuild would use `null` for the source content in the source map that it generates (since the source content isn't available). However, sometimes the original source code is actually still present on the file system. With this release, esbuild will now try to find the original source code using the path in the `sources` array and will use that instead of `null` if it was found. - Fix parsing bug with TypeScript `infer` and `extends` ([#2712](https://togithub.com/evanw/esbuild/issues/2712)) This release fixes a bug where esbuild incorrectly failed to parse valid TypeScript code that nests `extends` inside `infer` inside `extends`, such as in the example below: ```ts type A