Releases: evanw/esbuild
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
x64
on macOSarm64
too. 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
build
API 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 TypeScriptstrictNullChecks
setting 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.main
when 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.main
when the output format iscjs
. -
Warn about defining
process.env.NODE_ENV
as 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_ENV
as an identifier instead of a string. It's very common to use define to replaceprocess.env.NODE_ENV
with either"production"
or"development"
and sometimes people accidentally replace it withproduction
ordevelopment
instead. 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:8000
esbuild.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
paths
withoutbaseUrl
intsconfig.json
This feature was recently released in TypeScript 4.1. The
paths
feature intsconfig.json
allows you to do custom import path rewriting. For example, you can map paths matching@namespace/*
to the path./namespace/src/*
relative to thetsconfig.json
file. Previously using thepaths
feature required you to additionally specifybaseUrl
so that the compiler could know which directory the path aliases were supposed to be relative to.However, specifying
baseUrl
has the potentially-problematic side effect of causing all import paths to be looked up relative to thebaseUrl
directory, which could potentially cause package paths to accidentally be redirected to non-package files. SpecifyingbaseUrl
also causes Visual Studio Code's auto-import feature to generate paths relative to thebaseUrl
directory 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
baseUrl
when usingpaths
. When you do this, it as if you had written"baseUrl": "."
instead for the purpose of thepaths
feature, but thebaseUrl
value is not actually set and does not affect path resolution. Thesetsconfig.json
files 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
iife
orcjs
instead 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";
v0.8.14
-
Fix a concurrency bug caused by an error message change (#556)
An improvement to the error message for path resolution was introduced in version 0.8.12. It detects when a relative path is being interpreted as a package path because you forgot to start the path with
./
:> src/posts/index.js: error: Could not resolve "PostCreate" (use "./PostCreate" to import "src/posts/PostCreate.js") 2 │ import PostCreate from 'PostCreate'; ╵ ~~~~~~~~~~~~
This is implemented by re-running path resolution for package path resolution failures as a relative path instead. Unfortunately, this second path resolution operation wasn't guarded by a mutex and could result in concurrency bugs. This issue only occurs when path resolution fails. It is fixed in this release.
v0.8.13
-
Assigning to a
const
symbol is now an error when bundlingThis change was made because esbuild may need to change a
const
symbol into a non-constant symbol in certain situations. One situation is when the "avoid TDZ" option is enabled. Another situation is some potential upcoming changes to lazily-evaluate certain modules for code splitting purposes. Making this an error gives esbuild the freedom to do these code transformations without potentially causing problems where constants are mutated. This has already been a warning for a while so code that does this should already have been obvious. This warning was made an error in a patch release because the expectation is that no real code relies on this behavior outside of conformance tests. -
Fix for the
--keep-names
option and anonymous lowered classesThis release fixes an issue where names were not preserved for anonymous classes that contained newer JavaScript syntax when targeting an older version of JavaScript. This was because that causes the class expression to be transformed into a sequence expression, which was then not recognized as a class expression. For example, the class did not have the name
foo
in the code below when the target was set toes6
:let foo = class { #privateMethod() {} }
The
name
property of this class object is nowfoo
. -
Fix captured class names when class name is re-assigned
This fixes a corner case with class lowering to better match the JavaScript specification. In JavaScript, the body of a class statement contains an implicit constant symbol with the same name as the symbol of the class statement itself. Lowering certain class features such as private methods means moving them outside the class body, in which case the contents of the private method are no longer within the scope of the constant symbol. This can lead to a behavior change if the class is later re-assigned:
class Foo { static test() { return this.#method() } static #method() { return Foo } } let old = Foo Foo = class Bar {} console.log(old.test() === old) // This should be true
Previously this would print
false
when transformed to ES6 by esbuild. This now printstrue
. The current transformed output looks like this:var _method, method_fn; const Foo2 = class { static test() { return __privateMethod(this, _method, method_fn).call(this); } }; let Foo = Foo2; _method = new WeakSet(); method_fn = function() { return Foo2; }; _method.add(Foo); let old = Foo; Foo = class Bar { }; console.log(old.test() === old);
-
The
--allow-tdz
option is now always applied during bundlingThis option turns top-level
let
,const
, andclass
statements intovar
statements to work around some severe performance issues in the JavaScript run-time environment in Safari. Previously you had to explicitly enable this option. Now this behavior will always happen, and there is no way to turn it off. This means the--allow-tdz
option is now meaningless and no longer does anything. It will be removed in a future release. -
When bundling and minifying,
const
is now converted intolet
This was done because it's semantically equivalent but shorter. It's a valid transformation because assignment to a
const
symbol is now a compile-time error when bundling, so changingconst
tolet
should now not affect run-time behavior.
v0.8.12
-
Added an API for incremental builds (#21)
There is now an API for incremental builds. This is what using the API looks like from JavaScript:
require('esbuild').build({ entryPoints: ['app.js'], bundle: true, outfile: 'out.js', incremental: true, }).then(result => { // The "rebuild" method is present if "incremental" is true. It returns a // promise that resolves to the same kind of object that "build" returns. // You can call "rebuild" as many times as you like. result.rebuild().then(result2 => { // Call "dispose" when you're done to free up resources. result.rebuild.dispose() }) })
Using the API from Go is similar, except there is no need to manually dispose of the rebuild callback:
result := api.Build(api.BuildOptions{ EntryPoints: []string{"app.js"}, Bundle: true, Outfile: "out.js", Incremental: true, }) result2 := result.Rebuild()
Incremental builds are more efficient than regular builds because some data is cached and can be reused if the original files haven't changed since the last build. There are currently two forms of caching used by the incremental build API:
-
Files are stored in memory and are not re-read from the file system if the file metadata hasn't changed since the last build. This optimization only applies to file system paths. It does not apply to virtual modules created by plugins.
-
Parsed ASTs are stored in memory and re-parsing the AST is avoided if the file contents haven't changed since the last build. This optimization applies to virtual modules created by plugins in addition to file system modules, as long as the virtual module path remains the same.
This is just the initial release of the incremental build API. Incremental build times still have room for improvement. Right now esbuild still re-resolves, re-loads, and re-links everything even if none of the input files have changed. Improvements to the incremental build mechanism will be coming in later releases.
-
-
Support for a local file server (#537)
You can now run esbuild with the
--serve
flag to start a local server that serves the output files over HTTP. This is intended to be used during development. You can point your<script>
tag to a local server URL and your JavaScript and CSS files will be automatically built by esbuild whenever that URL is accessed. The server defaults to port 8000 but you can customize the port with--serve=...
.There is also an equivalent API for JavaScript:
require('esbuild').serve({ port: 8000, },{ entryPoints: ['app.js'], bundle: true, outfile: 'out.js', incremental: true, }).then(server => { // Call "stop" on the server when you're done server.stop() })
and for Go:
server, err := api.Serve(api.ServeOptions{ Port: 8000, }, api.BuildOptions{ EntryPoints: []string{"app.js"}, Bundle: true, Outfile: "out.js", Incremental: true, }) // Call "stop" on the server when you're done server.Stop()
This is a similar use case to "watch mode" in other tools where something automatically rebuilds your code when a file has changed on disk. The difference is that you don't encounter the problem where you make an edit, switch to your browser, and reload only to load the old files because the rebuild hasn't finished yet. Using a HTTP request instead of a file system access gives the rebuild tool the ability to delay the load until the rebuild operation has finished so your build is always up to date.
-
Install to a temporary directory for Windows (#547)
The install script runs
npm
in a temporary directory to download the correct binary executable for the current architecture. It then removes the temporary directory after the installation. However, removing a directory is sometimes impossible on Windows. To work around this problem, the install script now installs to the system's temporary directory instead of a directory inside the project itself. That way it's not problematic if a directory is left behind by the install script. This change was contributed by @Djaler. -
Fix the public path ending up in the metafile (#549)
The change in version 0.8.7 to include the public path in import paths of code splitting chunks caused a regression where the public path was also included in the list of chunk imports in the metafile. This was unintentional. Now the public path setting should not affect the metafile contents.
v0.8.11
-
Fix parsing of casts in TypeScript followed by certain tokens
This aligns esbuild's TypeScript parser with the official TypeScript parser as far as parsing of
as
casts. It's not valid to form an expression after anas
cast if the next token is a(
,[
,++
,--
,?.
, assignment operator, or template literal. Previously esbuild wouldn't generate an error for these expressions. This is normally not a problem because the TypeScript compiler itself would reject the code as invalid. However, if the next token starts on a new line, that new token may be the start of another statement. In that case the code generated by esbuild was different than the code generated by the TypeScript compiler. This difference has been fixed. -
Implement wildcards for external paths (#406)
You can now use a
*
wildcard character with the--external
option to mark all files matching a certain pattern as external, which will remove them from the bundle. For example, you can now do--external:*.png
to remove all.png
files. When a*
wildcard character is present in an external path, that pattern will be applied to the original path in the source code instead of to the path after it has been resolved to a real file system path. This lets you match on paths that aren't real file system paths. -
Add a warning about self-assignment
This release adds a warning for code that assigns an identifier to itself (e.g.
x = x
). This code is likely a mistake since doing this has no effect. This warning is not generated for assignments to global variables, since that can have side effects, and self-assignments with TypeScript casts, since those can be useful for changing the type of a variable in TypeScript. The warning is also not generated for code inside anode_modules
folder.
v0.8.10
-
Fix parsing of conditional types in TypeScript (#541)
Conditional types in TypeScript take the form
A extends B ? C : D
. Parsing of conditional types in esbuild was incorrect. The?
can only follow anextends
clause but esbuild didn't require theextends
clause, which potentially led to build failures or miscompilation. The parsing for this syntax has been fixed and should now match the behavior of the TypeScript compiler. This fix was contributed by @rtsao. -
Ignore comments for character frequency analysis (#543)
Character frequency analysis is used to derive the order of minified names for better gzip compression. The idea is to prefer using the most-used characters in the non-symbol parts of the document (keywords, strings, etc.) over characters that are less-used or absent. This is a very slight win, and is only approximate based on the input text instead of the output text because otherwise it would require minifying twice.
Right now comments are included in this character frequency histogram. This is not a correctness issue but it does mean that documents with the same code but different comments may be minified to different output files. This release fixes this difference by removing comments from the character frequency histogram.
-
Add an option to ignore tree-shaking annotations (#458)
Tree shaking is the term the JavaScript community uses for dead code elimination, a common compiler optimization that automatically removes unreachable code. Since JavaScript is a dynamic language, identifying unused code is sometimes very difficult for a compiler, so the community has developed certain annotations to help tell compilers what code should be considered unused. Currently there two forms of tree-shaking annotations that esbuild supports: inline
/* @__PURE__ */
comments before function calls and thesideEffects
field inpackage.json
.These annotations can be problematic because the compiler depends completely on developers for accuracy and the annotations are occasionally incorrect. The
sideEffects
field is particularly error-prone because by default it causes all files in your package to be considered dead code if no imports are used. If you add a new file containing side effects and forget to update that field, your package will break when people try to bundle it.This release adds a new flag
--tree-shaking=ignore-annotations
to allow you to bundle code that contains incorrect tree-shaking annotations with esbuild. An example of such code is @tensorflow/tfjs. Ideally the--tree-shaking=ignore-annotations
flag is only a temporary workaround. You should report these issues to the maintainer of the package to get them fixed since they will trip up other people too. -
Add support for absolute
baseUrl
paths intsconfig.json
filesPreviously esbuild always joined the
baseUrl
path to the end of the current directory path. However, if thebaseUrl
was an absolute path, that would end up including the current directory path twice. This situation could arise internally in certain cases involving multipletsconfig.json
files andextends
fields even if thetsconfig.json
files themselves didn't have absolute paths. Absolute paths are now not modified and should work correctly. -
Fix crash for modules that do
module.exports = null
(#532)The code generated by esbuild would crash at run-time if a module overwrote
module.exports
with null or undefined. This has been fixed and no longer crashes.
v0.8.9
-
Add support for the
mips64le
architecture (#523)You should now be able to install esbuild on the
mips64le
architecture. This build target is second-tier as it's not covered by CI, but I tested it in an emulator and it appears to work at the moment. -
Fix for packages with inconsistent side effect markings
Packages can have multiple entry points in their
package.json
file. Two commonly-used ones are specified using the fieldsmain
andmodule
. Packages can also mark files in the package as not having side effects using thesideEffects
field. Some packages have one entry point marked as having side effects and the other entry point as not having side effects. This is arguably a problem with the package itself. However, this caused an issue with esbuild's automatic entry point field selection method where it would incorrectly consider bothmain
andmodule
to not have side effects if one of them was marked as not having side effects. Nowmain
andmodule
will only be considered to not have side effects if the individual file was marked as not having side effects. -
Warn about
import './file'
when./file
was marked as having no side effectsFiles in packages containing
"sideEffects": false
in the enclosingpackage.json
file are intended to be automatically removed from the bundle if they aren't used. However, code containingimport './file'
is likely trying to import that file for a side effect. This is a conflict of intentions so it seems like a good idea to warn about this. It's likely a configuration error by the author of the package. The warning points to the location inpackage.json
that caused this situation. -
Add support for glob-style tests in
sideEffects
arraysThe
sideEffects
field inpackage.json
can optionally contain an array of files that are considered to have side effects. Any file not in that list will be removed if the import isn't used. Webpack supports the*
and?
wildcard characters in these file strings. With this release, esbuild supports these wildcard characters too.