Releases: evanw/esbuild
v0.8.24
-
Share reference-counted service instances internally (#600)
Now calling
startService()multiple times will share the underlying esbuild child process as long as the lifetimes of the service objects overlap (i.e. the time fromstartService()toservice.stop()). This is just an internal change; there is no change to the public API. It should result in a faster implementation that uses less memory if your code callsstartService()multiple times. Previously each call tostartService()generated a separate esbuild child process. -
Fix re-exports of a side-effect free CommonJS module (#605)
This release fixes a regression introduced in version 0.8.19 in which an
importof anexport {...} fromre-export of a CommonJS module does not include the CommonJS module if it has been marked as"sideEffect": falsein itspackage.jsonfile. This was the case with the Ramda library, and was due to an unhandled case in the linker. -
Optionally take binary executable path from environment variable (#592)
You can now set the
ESBUILD_BINARY_PATHenvironment variable to cause the JavaScript API to use a different binary executable path. This is useful if you want to substitute a modified version of theesbuildbinary that contains some extra debugging information.
v0.8.23
-
Fix non-string objects being passed to
transformSync(#596)The transform function is only supposed to take a string. The type definitions also specify that the input must be a string. However, it happened to convert non-string inputs to a string and some code relied on that behavior. A change in 0.8.22 broke that behavior for
transformSyncspecifically forUint8Arrayobjects, which became an array of numbers instead of a string. This release ensures that the conversion to a string is done up front to avoid something unexpected happening in the implementation. Future releases will likely enforce that the input is a string and throw an error otherwise. -
Revert the speedup to
transformSyncandbuildSync(#595)This speedup relies on the
worker_threadsmodule in node. However, when esbuild is used vianode -ras innode -r esbuild-register file.ts, the worker thread created by esbuild somehow ends up being completely detached from the main thread. This may be a bug in node itself. Regardless, the approach esbuild was using to improve speed doesn't work in all cases so it has been reverted. It's unclear if it's possible to work around this issue. This approach for improving the speed of synchronous APIs may be a dead end.
v0.8.22
-
Escape fewer characters in virtual module paths (#588)
If a module's path is not in the
filenamespace (i.e. it was created by a plugin), esbuild doesn't assume it's a file system path. The meaning of these paths is entirely up to the plugin. It could be anything including a HTTP URL, a string of code, or randomly-generated characters.Currently esbuild generates a file name for these virtual modules using an internal "human-friendly identifier" that can also be used as a valid JavaScript identifier, which is sometimes used to for example derive the name of the default export of a bundled module. But that means virtual module paths which do happen to represent file system paths could cause more characters to be escaped than necessary. For example, esbuild escapes
-to_because-is not valid in a JavaScript identifier.This release separates the file names derived from virtual module paths from the internal "human-friendly identifier" concept. Characters in the virtual module path that are valid in file paths are no longer escaped.
In the future the output file name of a virtual module will likely be completely customizable with a plugin, so it will be possible to have different behavior for this if desired. But that isn't possible quite yet.
-
Speed up the JavaScript
buildSyncandtransformSyncAPIs (#590)Previously the
buildSyncandtransformSyncAPI calls created a new child esbuild process on every call because communicating with a long-lived child process is asynchronous in node. However, there's a trick that can work around this limitation: esbuild can communicate with the long-lived child process from a child thread using node'sworker_threadsmodule and block the main thread using JavaScript's new Atomics API. This was a tip from @cspotcode.This approach has now been implemented. A quick benchmark shows that
transformSyncis now 1.5x to 15x faster than it used to be. The speedup depends on the size of the input (smaller inputs get a bigger speedup). The worker thread and child process should automatically be terminated when there are no more event handlers registered on the main thread, so there is no explicitstop()call like there is with a service object. -
Distribute a 32-bit Linux ARM binary executable via npm (#528)
You should now be able to use npm to install esbuild on a 32-bit Linux ARM device. This lets you run esbuild on a Raspberry Pi. Note that this target isn't officially supported because it's not covered by any automated tests.
v0.8.21
-
On-resolve plugins now apply to entry points (#546)
Previously entry points were required to already be resolved to valid file system paths. This meant that on-resolve plugins didn't run, which breaks certain workflows. Now entry point paths are resolved using normal import resolution rules.
To avoid making this a breaking change, there is now special behavior for entry point path resolution. If the entry point path exists relative to the current working directory and the path does not start with
./or../, esbuild will now automatically insert a leading./at the start of the path to prevent the path from being interpreted as anode_modulespackage path. This is only done if the file actually exists to avoid introducing./for paths with special plugin-specific syntax. -
Enable the build API in the browser (#527)
Previously you could only use the transform API in the browser, not the build API. You can now use the build API in the browser too. There is currently no in-browser file system so the build API will not do anything by default. Using this API requires you to use plugins to provide your own file system. Instructions for running esbuild in the browser can be found here: https://esbuild.github.io/api/#running-in-the-browser.
-
Set the importer to
sourcefilein on-resolve plugins for stdinWhen the stdin feature is used with on-resolve plugins, the importer for any import paths in stdin is currently always set to
<stdin>. Thesourcefileoption provides a way to set the file name of stdin but it wasn't carried through to on-resolve plugins due to an oversight. This release changes this behavior so nowsourcefileis used instead of<stdin>if present. In addition, if the stdin resolve directory is also specified the importer will be placed in thefilenamespace similar to a normal file.
v0.8.20
-
Fix an edge case with class body initialization
When bundling, top-level class statements are rewritten to variable declarations initialized to a class expression. This avoids a severe performance pitfall in Safari when there are a large number of class statements. However, this transformation was done incorrectly if a class contained a static field that references the class name in its own initializer:
class Foo { static foo = new Foo }
In that specific case, the transformed code could crash when run because the class name is not yet initialized when the static field initializer is run. Only JavaScript code was affected. TypeScript code was not affected. This release fixes this bug.
-
Remove more types of statements as dead code (#580)
This change improves dead-code elimination in the case where unused statements follow an unconditional jump, such as a
return:if (true) return if (something) thisIsDeadCode()
These unused statements are removed in more cases than in the previous release. Some statements may still be kept that contain hoisted symbols (
varandfunctionstatements) because they could potentially impact the code before the conditional jump.
v0.8.19
-
Handle non-ambiguous multi-path re-exports (#568)
Wildcard re-exports using the
export * from 'path'syntax can potentially result in name collisions that cause an export name to be ambiguous. For example, the following code would result in an ambiguous export if botha.jsandb.jsexport a symbol with the same name:export * from './a.js' export * from './b.js'
Ambiguous exports have two consequences. First, any ambiguous names are silently excluded from the set of exported names. If you use an
import * aswildcard import, the excluded names will not be present. Second, attempting to explicitly import an ambiguous name using animport {} fromimport clause will result in a module instantiation error.This release fixes a bug where esbuild could in certain cases consider a name ambiguous when it actually isn't. Specifically this happens with longer chains of mixed wildcard and named re-exports. Here is one such case:
// entry.js import {x, y} from './not-ambiguous.js' console.log(x, y)
// /not-ambiguous.js export * from './a.js' export * from './b.js'
// /a.js export * from './c.js'
// /b.js export {x} from './c.js'
// /c.js export let x = 1, y = 2
Previously bundling
entry.jswith esbuild would incorrectly generate an error about an ambiguousxexport. Now this case builds successfully without an error. -
Omit warnings about non-string paths in
await import()inside atryblock (#574)Bundling code that uses
require()orimport()with a non-string path currently generates a warning, because the target of that import will not be included in the bundle. This is helpful to warn about because other bundlers handle this case differently (e.g. Webpack bundles the entire directory tree and emulates a file system lookup) so existing code may expect the target of the import to be bundled.You can avoid the warning with esbuild by surrounding the call to
require()with atryblock. The thinking is that if there is a surroundingtryblock, presumably the code is expecting therequire()call to possibly fail and is prepared to handle the error. However, there is currently no way to avoid the warning forimport()expressions. This release introduces an analogous behavior forimport()expressions. You can now avoid the warning with esbuild if you useawait import()and surround it with atryblock.
v0.8.18
-
Fix a bug with certain complex optional chains (#573)
The
?.optional chaining operator only runs the right side of the operator if the left side is undefined, otherwise it returns undefined. This operator can be applied to both property accesses and function calls, and these can be combined into long chains of operators. These expressions must be transformed to a chain of?:operators if the?.operator isn't supported in the configured target environment. However, esbuild had a bug where an optional call of an optional property with a further property access afterward didn't preserve the value ofthisfor the call. This bug has been fixed. -
Fix a renaming bug with external imports
There was a possibility of a cross-module name collision while bundling in a certain edge case. Specifically, when multiple files both contained an
importstatement to an external module and then both of those files were imported usingrequire. For example:// index.js console.log(require('./a.js'), require('./b.js'))
// a.js export {exists} from 'fs'
// b.js export {exists} from 'fs'
In this case the files
a.jsandb.jsare converted to CommonJS format so they can be imported usingrequire:// a.js import {exists} from "fs"; var require_a = __commonJS((exports) => { __export(exports, { exists: () => exists }); }); // b.js import {exists} from "fs"; var require_b = __commonJS((exports) => { __export(exports, { exists: () => exists }); }); // index.js console.log(require_a(), require_b());
However, the
existssymbol has been duplicated without being renamed. This is will result in a syntax error at run-time. The reason this happens is that the statements in the filesa.jsandb.jsare placed in a nested scope because they are inside the CommonJS closure. Theimportstatements were extracted outside the closure but the symbols they declared were incorrectly not added to the outer scope. This problem has been fixed, and this edge case should no longer result in name collisions.
v0.8.17
-
Get esbuild working on the Apple M1 chip via Rosetta 2 (#564)
The Go compiler toolchain does not yet support the new Apple M1 chip. Go version 1.15 is currently in a feature freeze period so support will be added in the next version, Go 1.16, which will be released in February.
This release changes the install script to install the executable for macOS
x64on macOSarm64too. Doing this should still work because of the executable translation layer built into macOS. This change was contributed by @sod.
v0.8.16
-
Improve TypeScript type definitions (#559)
The return value of the
buildAPI has some optional fields that are undefined unless certain arguments are present. That meant you had to use the!null assertion operator to avoid a type error if you have the TypeScriptstrictNullCheckssetting enabled in your project. This release adds additional type information so that if the relevant arguments are present, the TypeScript compiler can tell that these optional fields on the return value will never be undefined. This change was contributed by @lukeed. -
Omit a warning about
require.mainwhen targeting CommonJS (#560)A common pattern in code that's intended to be run in node is to check if
require.main === module. That will be true if the current file is being run from the command line but false if the current file is being run because some other code calledrequire()on it. Previously esbuild generated a warning about an unexpected use ofrequire. Now this warning is no longer generated forrequire.mainwhen the output format iscjs. -
Warn about defining
process.env.NODE_ENVas an identifier (#466)The define feature can be used to replace an expression with either a JSON literal or an identifier. Forgetting to put quotes around a string turns it into an identifier, which is a common mistake. This release introduces a warning when you define
process.env.NODE_ENVas an identifier instead of a string. It's very common to use define to replaceprocess.env.NODE_ENVwith either"production"or"development"and sometimes people accidentally replace it withproductionordevelopmentinstead. This is worth warning about because otherwise there would be no indication that something is wrong until the code crashes when run. -
Allow starting a local server at a specific host address (#563)
By default, esbuild's local HTTP server is only available on the internal loopback address. This is deliberate behavior for security reasons, since the local network environment may not be trusted. However, it can be useful to run the server on a different address when developing with esbuild inside of a virtual machine/docker container or to request development assets from a remote testing device on the same network at a different IP address. With this release, you can now optionally specify the host in addition to the port:
esbuild --serve=192.168.0.1:8000esbuild.serve({ host: '192.168.0.1', port: 8000, }, { ... })
server, err := api.Serve(api.ServeOptions{ Host: "192.168.0.1", Port: 8000, }, api.BuildOptions{ ... })
This change was contributed by @jamalc.
v0.8.15
-
Allow
pathswithoutbaseUrlintsconfig.jsonThis feature was recently released in TypeScript 4.1. The
pathsfeature intsconfig.jsonallows you to do custom import path rewriting. For example, you can map paths matching@namespace/*to the path./namespace/src/*relative to thetsconfig.jsonfile. Previously using thepathsfeature required you to additionally specifybaseUrlso that the compiler could know which directory the path aliases were supposed to be relative to.However, specifying
baseUrlhas the potentially-problematic side effect of causing all import paths to be looked up relative to thebaseUrldirectory, which could potentially cause package paths to accidentally be redirected to non-package files. SpecifyingbaseUrlalso causes Visual Studio Code's auto-import feature to generate paths relative to thebaseUrldirectory instead of relative to the directory containing the current file. There is more information about the problems this causes here: microsoft/TypeScript#31869.With TypeScript 4.1, you can now omit
baseUrlwhen usingpaths. When you do this, it as if you had written"baseUrl": "."instead for the purpose of thepathsfeature, but thebaseUrlvalue is not actually set and does not affect path resolution. Thesetsconfig.jsonfiles are now supported by esbuild. -
Fix evaluation order issue with import cycles and CommonJS-style output formats (#542)
Previously entry points involved in an import cycle could cause evaluation order issues if the output format was
iifeorcjsinstead ofesm. This happened because this edge case was handled by treating the entry point file as a CommonJS file, which extracted the code into a CommonJS wrapper. Here's an example:Input files:
// index.js import { test } from './lib' export function fn() { return 42 } if (test() !== 42) throw 'failure'
// lib.js import { fn } from './index' export let test = fn
Previous output (problematic):
// index.js var require_esbuild = __commonJS((exports) => { __export(exports, { fn: () => fn2 }); function fn2() { return 42; } if (test() !== 42) throw "failure"; }); // lib.js var index = __toModule(require_esbuild()); var test = index.fn; module.exports = require_esbuild();
This approach changed the evaluation order because the CommonJS wrapper conflates both binding and evaluation. Binding and evaluation need to be separated to correctly handle this edge case. This edge case is now handled by inlining what would have been the contents of the CommonJS wrapper into the entry point location itself.
Current output (fixed):
// index.js __export(exports, { fn: () => fn }); // lib.js var test = fn; // index.js function fn() { return 42; } if (test() !== 42) throw "failure";