Releases: evanw/esbuild
v0.11.0
This release contains backwards-incompatible changes. Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as recommended by npm). You should either be pinning the exact version of esbuild
in your package.json
file or be using a version range syntax that only accepts patch upgrades such as ~0.10.0
. See the documentation about semver for more information.
The changes in this release mostly relate to how entry points are handled. The way output paths are generated has changed in some cases, so you may need to update how you refer to the output path for a given entry point when you update to this release (see below for details). These breaking changes are as follows:
-
Change how
require()
andimport()
of ESM works (#667, #706)Previously if you call
require()
on an ESM file, or callimport()
on an ESM file with code splitting disabled, esbuild would convert the ESM file to CommonJS. For example, if you had the following input files:// cjs-file.js console.log(require('./esm-file.js').foo) // esm-file.js export let foo = bar()
The previous bundling behavior would generate something like this:
var require_esm_file = __commonJS((exports) => { __markAsModule(exports); __export(exports, { foo: () => foo }); var foo = bar(); }); console.log(require_esm_file().foo);
This behavior has been changed and esbuild now generates something like this instead:
var esm_file_exports = {}; __export(esm_file_exports, { foo: () => foo }); var foo; var init_esm_file = __esm(() => { foo = bar(); }); console.log((init_esm_file(), esm_file_exports).foo);
The variables have been pulled out of the lazily-initialized closure and are accessible to the rest of the module's scope. Some benefits of this approach:
-
If another file does
import {foo} from "./esm-file.js"
, it will just referencefoo
directly and will not pay the performance penalty or code size overhead of the dynamic property accesses that come with CommonJS-style exports. So this improves performance and reduces code size in some cases. -
This fixes a long-standing bug (#706) where entry point exports could be broken if the entry point is a target of a
require()
call and the output format was ESM. This happened because previously callingrequire()
on an entry point converted it to CommonJS, which then meant it only had a singledefault
export, and the exported variables were inside the CommonJS closure and inaccessible to an ESM-styleexport {}
clause. Now callingrequire()
on an entry point only causes it to be lazily-initialized but all exports are still in the module scope and can still be exported using a normalexport {}
clause. -
Now that this has been changed,
import()
of a module with top-level await (#253) is now allowed when code splitting is disabled. Previously this didn't work becauseimport()
with code splitting disabled was implemented by converting the module to CommonJS and usingPromise.resolve().then(() => require())
, but converting a module with top-level await to CommonJS is impossible because the CommonJS call signature must be synchronous. Now that this implemented using lazy initialization instead of CommonJS conversion, the closure wrapping the ESM file can now beasync
and theimport()
expression can be replaced by a call to the lazy initializer. -
Adding the ability for ESM files to be lazily-initialized is an important step toward additional future code splitting improvements including: manual chunk names (#207), correct import evaluation order (#399), and correct top-level await evaluation order (#253). These features all need to make use of deferred evaluation of ESM code.
In addition, calling
require()
on an ESM file now recursively wraps all transitive dependencies of that file instead of just wrapping that ESM file itself. This is an increase in the size of the generated code, but it is important for correctness (#667). Callingrequire()
on a module means its evaluation order is determined at run-time, which means the evaluation order of all dependencies must also be determined at run-time. If you don't want the increase in code size, you should use animport
statement instead of arequire()
call. -
-
Dynamic imports now use chunk names instead of entry names (#1056)
Previously the output paths of dynamic imports (files imported using the
import()
syntax) were determined by the--entry-names=
setting. However, this can cause problems if you configure the--entry-names=
setting to omit both[dir]
and[hash]
because then two dynamic imports with the same name will cause an output file name collision.Now dynamic imports use the
--chunk-names=
setting instead, which is used for automatically-generated chunks. This setting is effectively required to include[hash]
so dynamic import name collisions should now be avoided.In addition, dynamic imports no longer affect the automatically-computed default value of
outbase
. By defaultoutbase
is computed to be the lowest common ancestor directory of all entry points. Previously dynamic imports were considered entry points in this calculation so adding a dynamic entry point could unexpectedly affect entry point output file paths. This issue has now been fixed. -
Allow custom output paths for individual entry points
By default, esbuild will automatically generate an output path for each entry point by computing the relative path from the
outbase
directory to the entry point path, and then joining that relative path to theoutdir
directory. The output path can be customized usingoutpath
, but that only works for a single file. Sometimes you may need custom output paths while using multiple entry points. You can now do this by passing the entry points as a map instead of an array:-
CLI
esbuild out1=in1.js out2=in2.js --outdir=out
-
JS
esbuild.build({ entryPoints: { out1: 'in1.js', out2: 'in2.js', }, outdir: 'out', })
-
Go
api.Build(api.BuildOptions{ EntryPointsAdvanced: []api.EntryPoint{{ OutputPath: "out1", InputPath: "in1.js", }, { OutputPath: "out2", InputPath: "in2.js", }}, Outdir: "out", })
This will cause esbuild to generate the files
out/out1.js
andout/out2.js
inside the output directory. These custom output paths are used as input for the--entry-names=
path template setting, so you can use something like--entry-names=[dir]/[name]-[hash]
to add an automatically-computed hash to each entry point while still using the custom output path. -
-
Derive entry point output paths from the original input path (#945)
Previously esbuild would determine the output path for an entry point by looking at the post-resolved path. For example, running
esbuild --bundle react --outdir=out
would generate the output pathout/index.js
because the input pathreact
was resolved tonode_modules/react/index.js
. With this release, the output path is now determined by looking at the pre-resolved path. For example, runningesbuild --bundle react --outdir=out
now generates the output pathout/react.js
. If you need to keep using the output path that esbuild previously generated with the old behavior, you can use the custom output path feature (described above). -
Use the
file
namespace for file entry points (#791)Plugins that contain an
onResolve
callback with thefile
filter don't apply to entry point paths because it's not clear that entry point paths are files. For example, you could potentially bundle an entry point ofhttps://www.example.com/file.js
with a HTTP plugin that automatically downloads data from the server at that URL. But this behavior can be unexpected for people writing plugins.With this release, esbuild will do a quick check first to see if the entry point path exists on the file system before running plugins. If it exists as a file, the namespace will now be
file
for that entry point path. This only checks the exact entry point name and doesn't attempt to search for the file, so for example it won't handle cases where you pass a package path as an entry point or where you pass an entry point without an extension. Hopefully this should help improve this situation in the common case where the entry point is an exact path.
In addition to the breaking changes above, the following features are also included in this release:
-
Warn about mutation of private methods (#1067)
Mutating a private method in JavaScript is not allowed, and will throw at run-time:
class Foo { #method() {} mutate() { this.#method = () => {} } }
This is the case both when esbuild passes the syntax through untransformed and when esbuild transforms the syntax into the equivalent code that uses a `We...
v0.10.2
-
Fix a crash that was introduced in the previous release (#1064)
This crash happens when code splitting is active and there is a CSS entry point as well as two or more JavaScript entry points. There is a known issue where CSS bundling does not work when code splitting is active (code splitting is still a work in progress, see #608) so doing this will likely not work as expected. But esbuild obviously shouldn't crash. This release fixes the crash, although esbuild still does not yet generate the correct CSS output in this case.
-
Fix private fields inside destructuring assignment (#1066)
Private field syntax (i.e.
this.#field
) is supported for older language targets by converting the code into accesses into aWeakMap
. However, although regular assignment (i.e.this.#field = 1
) was handled destructuring assignment (i.e.[this.#field] = [1]
) was not handled due to an oversight. Support for private fields inside destructuring assignment is now included with this release. -
Fix an issue with direct
eval
and top-level symbolsIt was previously the case that using direct
eval
caused the file containing it to be considered a CommonJS file, even if the file used ESM syntax. This was because the evaluated code could potentially attempt to interact with top-level symbols by name and the CommonJS closure was used to isolate those symbols from other modules so their names could be preserved (otherwise their names may need to be renamed to avoid collisions). However, ESM files are no longer convertable to CommonJS files due to the need to support top-level await.This caused a bug where scope hoisting could potentially merge two modules containing direct
eval
and containing the same top-level symbol name into the same scope. These symbols were prevented from being renamed due to the directeval
, which caused a syntax error at run-time due to the name collision.Because of this, esbuild is dropping the guarantee that using direct
eval
in an ESM file will be able to access top-level symbols. These symbols are now free to be renamed to avoid name collisions, and will now be minified when identifier minification is enabled. This is unlikely to affect real-world code because most real-world uses of directeval
only attempt to access local variables, not top-level symbols.Using direct
eval
in an ESM file when bundling with esbuild will generate a warning. The warning is not new and is present in previous releases of esbuild as well. The way to avoid the warning is to avoid directeval
, since directeval
is somewhat of an anti-pattern and there are better alternatives.
v0.10.1
-
Expose
metafile
toonRebuild
in watch mode (#1057)Previously the build results returned to the watch mode
onRebuild
callback was missing themetafile
property when themetafile: true
option was present. This bug has been fixed. -
Add a
formatMessages
API (#1058)This API lets you print log messages to the terminal using the same log format that esbuild itself uses. This can be used to filter esbuild's warnings while still making the output look the same. Here's an example of calling this API:
import esbuild from 'esbuild' const formatted = await esbuild.formatMessages([{ text: '"test" has already been declared', location: { file: 'file.js', line: 2, column: 4, length: 4, lineText: 'let test = "second"' }, notes: [{ text: '"test" was originally declared here', location: { file: 'file.js', line: 1, column: 4, length: 4, lineText: 'let test = "first"' }, }], }], { kind: 'error', color: true, terminalWidth: 100, }) process.stdout.write(formatted.join(''))
-
Remove the file splitting optimization (#998)
This release removes the "file splitting optimization" that has up to this point been a part of esbuild's code splitting algorithm. This optimization allowed code within a single file to end up in separate chunks as long as that code had no side effects. For example, bundling two entry points that both use a disjoint set of code from a shared file consisting only of code without side effects would previously not generate any shared code chunks at all.
This optimization is being removed because the top-level await feature was added to JavaScript after this optimization was added, and performing this optimization in the presence of top-level await is more difficult than before. The correct evaulation order of a module graph containing top-level await is extremely complicated and is specified at the module boundary. Moving code that is marked as having no side effects across module boundaries under these additional constraints is even more complexity and is getting in the way of implementing top-level await. So the optimization has been removed to unblock work on top-level await, which esbuild must support.
v0.10.0
This release contains backwards-incompatible changes. Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as recommended by npm). You should either be pinning the exact version of esbuild
in your package.json
file or be using a version range syntax that only accepts patch upgrades such as ~0.9.0
. See the documentation about semver for more information.
That said, there are no breaking API changes in this release. The breaking changes are instead about how input files are interpreted and/or how output files are generated in some cases. So upgrading should be relatively straightforward as your API calls should still work the same way, but please make sure to test your code when you upgrade because the output may be different. These breaking changes are as follows:
-
No longer support
module
orexports
in an ESM file (#769)This removes support for using CommonJS exports in a file with ESM exports. Previously this worked by converting the ESM file to CommonJS and then mixing the CommonJS and ESM exports into the same
exports
object. But it turns out that supporting this is additional complexity for the bundler, so it has been removed. It's also not something that works in real JavaScript environments since modules will never support both export syntaxes at once.Note that this doesn't remove support for using
require
in ESM files. Doing this still works (and can be made to work in a real ESM environment by assigning toglobalThis.require
). This also doesn't remove support for usingimport
in CommonJS files. Doing this also still works. -
No longer change
import()
torequire()
(#1029)Previously esbuild's transform for
import()
matched TypeScript's behavior, which is to transform it intoPromise.resolve().then(() => require())
when the current output format is something other than ESM. This was done when an import is external (i.e. not bundled), either due to the expression not being a string or due to the string matching an external import path.With this release, esbuild will no longer do this. Now
import()
expressions will be preserved in the output instead. These expressions can be handled in non-ESM code by arranging for theimport
identifier to be a function that imports ESM code. This is how node works, so it will now be possible to useimport()
with node when the output format is something other than ESM. -
Run-time
export * as
statements no longer convert the file to CommonJSCertain
export * as
statements require a bundler to evaluate them at run-time instead of at compile-time like the JavaScript specification. This is the case when re-exporting symbols from an external file and a file in CommonJS format.Previously esbuild would handle this by converting the module containing the
export * as
statement to CommonJS too, since CommonJS exports are evaluated at run-time while ESM exports are evaluated at bundle-time. However, this is undesirable because tree shaking only works for ESM, not for CommonJS, and the CommonJS wrapper causes additional code bloat. Another upcoming problem is that top-level await cannot work within a CommonJS module because CommonJSrequire()
is synchronous.With this release, esbuild will now convert modules containing a run-time
export * as
statement to a special ESM-plus-dynamic-fallback mode. In this mode, named exports present at bundle time can still be imported directly by name, but any imports that don't match one of the explicit named imports present at bundle time will be converted to a property access on the fallback object instead of being a bundle error. These property accesses are then resolved at run-time and will be undefined if the export is missing. -
Change whether certain files are interpreted as ESM or CommonJS (#1043)
The bundling algorithm currently doesn't contain any logic that requires flagging modules as CommonJS vs. ESM beforehand. Instead it handles a superset and then sort of decides later if the module should be treated as CommonJS vs. ESM based on whether the module uses the
module
orexports
variables and/or theexports
keyword.With this release, files that follow node's rules for module types will be flagged as explicitly ESM. This includes files that end in
.mjs
and files within a package containing"type": "module"
in the enclosingpackage.json
file. The CommonJSmodule
andexports
features will be unavailable in these files. This matters most for files without any exports, since then it's otherwise ambiguous what the module type is.In addition, files without exports should now accurately fall back to being considered CommonJS. They should now generate a
default
export of an empty object when imported using animport
statement, since that's what happens in node when you import a CommonJS file into an ESM file in node. Previously the default export could be undefined because these export-less files were sort of treated as ESM but with missing import errors turned into warnings instead.This is an edge case that rarely comes up in practice, since you usually never import things from a module that has no exports.
In addition to the breaking changes above, the following features are also included in this release:
-
Initial support for bundling with top-level await (#253)
Top-level await is a feature that lets you use an
await
expression at the top level (outside of anasync
function). Here is an example:let promise = fetch('https://www.example.com/data') export let data = await promise.then(x => x.json())
Top-level await only works in ECMAScript modules, and does not work in CommonJS modules. This means that you must use an
import
statement or animport()
expression to import a module containing top-level await. You cannot userequire()
because it's synchronous while top-level await is asynchronous. There should be a descriptive error message when you try to do this.This initial release only has limited support for top-level await. It is only supported with the
esm
output format, but not with theiife
orcjs
output formats. In addition, the compilation is not correct in that two modules that both contain top-level await and that are siblings in the import graph will be evaluated in serial instead of in parallel. Full support for top-level await will come in a future release. -
Add the ability to set
sourceRoot
in source maps (#1028)You can now use the
--source-root=
flag to set thesourceRoot
field in source maps generated by esbuild. When asourceRoot
is present in a source map, all source paths are resolved relative to it. This is particularly useful when you are hosting compiled code on a server and you want to point the source files to a GitHub repo, such as what AMP does.Here is the description of
sourceRoot
from the source map specification:An optional source root, useful for relocating source files on a server or removing repeated values in the "sources" entry. This value is prepended to the individual entries in the "source" field. If the sources are not absolute URLs after prepending of the "sourceRoot", the sources are resolved relative to the SourceMap (like resolving script src in a html document).
This feature was contributed by @jridgewell.
-
Allow plugins to return custom file watcher paths
Currently esbuild's watch mode automatically watches all file system paths that are handled by esbuild itself, and also automatically watches the paths of files loaded by plugins when the paths are in the
file
namespace. The paths of files that plugins load in namespaces other than thefile
namespace are not automatically watched.Also, esbuild never automatically watches any file system paths that are consulted by the plugin during its processing, since esbuild is not aware of those paths. For example, this means that if a plugin calls
require.resolve()
, all of the various "does this file exist" checks that it does will not be watched automatically. So if one of those files is created in the future, esbuild's watch mode will not rebuild automatically even though the build is now outdated.To fix this problem, this release introduces the
watchFiles
andwatchDirs
properties on plugin return values. Plugins can specify these to add additional custom file system paths to esbuild's internal watch list. Paths in thewatchFiles
array cause esbuild to rebuild if the file contents change, and paths in thewatchDirs
array cause esbuild to rebuild if the set of directory entry names changes for that directory path.Note that
watchDirs
does not cause esbuild to rebuild if any of the contents of files inside that directory are changed. It also does not recursively traverse through subdirectories. It only watches the set of directory entry names (i.e. the output of the Unixls
command).
v0.9.7
-
Add support for Android on ARM 64-bit (#803)
This release includes support for Android in the official
esbuild
package. It should now be possible to install and run esbuild on Android devices through npm. -
Fix incorrect MIME types on Windows (#1030)
The web server built into esbuild uses the file extension to determine the value of the
Content-Type
header. This was previously done using themime.TypeByExtension()
function from Go's standard library. However, this function is apparently broken on Windows because installed programs can change MIME types in the Windows registry: golang/go#32350. This release fixes the problem by using a copy of Go'smime.TypeByExtension()
function without the part that reads from the Windows registry. -
Using a top-level return inside an ECMAScript module is now forbidden
The CommonJS module format is implemented as an anonymous function wrapper, so technically you can use a top-level
return
statement and it will actually work. Some packages in the wild use this to exit early from module initialization, so esbuild supports this. However, the ECMAScript module format doesn't allow top-level returns. With this release, esbuild no longer allows top-level returns in ECMAScript modules.
v0.9.6
-
Expose build options to plugins (#373)
Plugins can now access build options from within the plugin using the
initialOptions
property. For example:let nodeEnvPlugin = { name: 'node-env', setup(build) { const options = build.initialOptions options.define = options.define || {} options.define['process.env.NODE_ENV'] = options.minify ? '"production"' : '"development"' }, }
-
Fix an edge case with the object spread transform (#1017)
This release fixes esbuild's object spread transform in cases where property assignment could be different than property definition. For example:
console.log({ get x() {}, ...{x: 1}, })
This should print
{x: 1}
but transforming this through esbuild with--target=es6
causes the resulting code to throw an error. The problem is that esbuild currently transforms this code to a call toObject.assign
and that uses property assignment semantics, which causes the assignment to throw (since you can't assign to a getter-only property).With this release, esbuild will now transform this into code that manually loops over the properties and copies them over one-by-one using
Object.defineProperty
instead. This uses property definition semantics which better matches the specification. -
Fix a TypeScript parsing edge case with arrow function return types (#1016)
This release fixes the following TypeScript parsing edge case:
():Array<number>=>{return [1]}
This was tripping up esbuild's TypeScript parser because the
>=
token was split into a>
token and a=
token because the>
token is needed to close the type parameter list, but the=
token was not being combined with the following>
token to form a=>
token. This is normally not an issue because there is normally a space in between the>
and the=>
tokens here. The issue only happened when the spaces were removed. This bug has been fixed. Now after the>=
token is split, esbuild will expand the=
token into the following characters if possible, which can result in a=>
,==
, or===
token. -
Enable faster synchronous transforms under a flag (#1000)
Currently the synchronous JavaScript API calls
transformSync
andbuildSync
spawn a new child process on every call. This is due to limitations with node'schild_process
API. Doing this meanstransformSync
andbuildSync
are much slower thantransform
andbuild
, which share the same child process across calls.There was previously a workaround for this limitation that uses node's
worker_threads
API and atomics to block the main thread while asynchronous communication happens in a worker, but that was reverted due to a bug in node'sworker_threads
implementation. Now that this bug has been fixed by node, I am re-enabling this workaround. This should result intransformSync
andbuildSync
being much faster.This approach is experimental and is currently only enabled if the
ESBUILD_WORKER_THREADS
environment variable is present. If this use case matters to you, please try it out and let me know if you find any problems with it. -
Update how optional chains are compiled to match new V8 versions (#1019)
An optional chain is an expression that uses the
?.
operator, which roughly avoids evaluation of the right-hand side if the left-hand side isnull
orundefined
. Soa?.b
is basically equivalent toa == null ? void 0 : a.b
. When the language target is set toes2019
or below, esbuild will transform optional chain expressions into equivalent expressions that do not use the?.
operator.This transform is designed to match the behavior of V8 exactly, and is designed to do something similar to the equivalent transform done by the TypeScript compiler. However, V8 has recently changed its behavior in two cases:
-
Forced call of an optional member expression should propagate the object to the method:
const o = { m() { return this; } }; assert((o?.m)() === o);
V8 bug: https://bugs.chromium.org/p/v8/issues/detail?id=10024
-
Optional call of
eval
must be an indirect eval:globalThis.a = 'global'; var b = (a => eval?.('a'))('local'); assert(b === 'global');
V8 bug: https://bugs.chromium.org/p/v8/issues/detail?id=10630
This release changes esbuild's transform to match V8's new behavior. The transform in the TypeScript compiler is still emulating the old behavior as of version 4.2.3, so these syntax forms should be avoided in TypeScript code for portability.
-
v0.9.5
-
Fix parsing of the
[dir]
placeholder (#1013)The entry names feature in the previous release accidentally didn't include parsing for the
[dir]
placeholder, so the[dir]
placeholder was passed through verbatim into the resulting output paths. This release fixes the bug, which means you can now use the[dir]
placeholder. Sorry about the oversight.
v0.9.4
-
Enable hashes in entry point file paths (#518)
This release adds the new
--entry-names=
flag. It's similar to the--chunk-names=
and--asset-names=
flags except it sets the output paths for entry point files. The pattern defaults to[dir]/[name]
which should be equivalent to the previous entry point output path behavior, so this should be a backward-compatible change.This change has the following consequences:
-
It is now possible for entry point output paths to contain a hash. For example, this now happens if you pass
--entry-names=[dir]/[name]-[hash]
. This means you can now use esbuild to generate output files such that all output paths have a hash in them, which means it should now be possible to serve the output files with an infinite cache lifetime so they are only downloaded once and then cached by the browser forever. -
It is now possible to prevent the generation of subdirectories inside the output directory. Previously esbuild replicated the directory structure of the input entry points relative to the
outbase
directory (which defaults to the lowest common ancestor directory across all entry points). This value is substituted into the newly-added[dir]
placeholder. But you can now omit it by omitting that placeholder, like this:--entry-names=[name]
. -
Source map names should now be equal to the corresponding output file name plus an additional
.map
extension. Previously the hashes were content hashes, so the source map had a different hash than the corresponding output file because they had different contents. Now they have the same hash so finding the source map should now be easier (just add.map
). -
Due to the way the new hashing algorithm works, all chunks can now be generated fully in parallel instead of some chunks having to wait until their dependency chunks have been generated first. The import paths for dependency chunks are now swapped in after chunk generation in a second pass (detailed below). This could theoretically result in a speedup although I haven't done any benchmarks around this.
Implementing this feature required overhauling how hashes are calculated to prevent the chicken-and-egg hashing problem due to dynamic imports, which can cause cycles in the import graph of the resulting output files when code splitting is enabled. Since generating a hash involved first hashing all of your dependencies, you could end up in a situation where you needed to know the hash to calculate the hash (if a file was a dependency of itself).
The hashing algorithm now works in three steps (potentially subject to change in the future):
-
The initial versions of all output files are generated in parallel, with temporary paths used for any imports of other output files. Each temporary path is a randomly-generated string that is unique for each output file. An initial source map is also generated at this step if source maps are enabled.
The hash for the first step includes: the raw content of the output file excluding the temporary paths, the relative file paths of all input files present in that output file, the relative output path for the resulting output file (with
[hash]
for the hash that hasn't been computed yet), and contents of the initial source map. -
After the initial versions of all output files have been generated, calculate the final hash and final output path for each output file. Calculating the final output path involves substituting the final hash for the
[hash]
placeholder in the entry name template.The hash for the second step includes: the hash from the first step for this file and all of its transitive dependencies.
-
After all output files have a final output path, the import paths in each output file for importing other output files are substituted. Source map offsets also have to be adjusted because the final output path is likely a different length than the temporary path used in the first step. This is also done in parallel for each output file.
This whole algorithm roughly means the hash of a given output file should change if an only if any input file in that output file or any output file it depends on is changed. So the output path and therefore the browser's cache key should not change for a given output file in between builds if none of the relevant input files were changed.
-
-
Fix importing a path containing a
?
character on Windows (#989)On Windows, the
?
character is not allowed in path names. This causes esbuild to fail to import paths containing this character. This is usually fine because people don't put?
in their file names for this reason. However, the import paths for some ancient CSS code contains the?
character as a hack to work around a bug in Internet Explorer:@font-face { src: url("./icons.eot?#iefix") format('embedded-opentype'), url("./icons.woff2") format('woff2'), url("./icons.woff") format('woff'), url("./icons.ttf") format('truetype'), url("./icons.svg#icons") format('svg'); }
The intent is for the bundler to ignore the
?#iefix
part. However, there may actually be a file calledicons.eot?#iefix
on the file system so esbuild checks the file system for bothicons.eot?#iefix
andicons.eot
. This check was triggering this issue. With this release, an invalid path is considered the same as a missing file so bundling code like this should now work on Windows. -
Parse and ignore the deprecated
@-ms-viewport
CSS rule (#997)The
@viewport
rule has been deprecated and removed from the web. Modern browsers now completely ignore this rule. However, in theory it sounds like would still work for mobile versions of Internet Explorer, if those still exist. The https://ant.design/ library contains an instance of the@-ms-viewport
rule and it currently causes a warning with esbuild, so this release adds support for parsing this rule to disable the warning. -
Avoid mutating the binary executable file in place (#963)
This release changes the install script for the
esbuild
npm package to use the "rename a temporary file" approach instead of the "write the file directly" approach to replace theesbuild
command stub file with the real binary executable. This should hopefully work around a problem with the pnpm package manager and its use of hard links. -
Avoid warning about potential issues with
sideEffects
in packages (#999)Bare imports such as
import "foo"
mean the package is only imported for its side effects. Doing this when the package contains"sideEffects": false
inpackage.json
causes a warning because it means esbuild will not import the file since it has been marked as having no side effects, even though the import statement clearly expects it to have side effects. This is usually caused by an incorrectsideEffects
annotation in the package.However, this warning is not immediately actionable if the file containing the import statement is itself in a package. So with this release, esbuild will no longer issue this warning if the file containing the import is inside a
node_modules
folder. Note that even though the warning is no longer there, this situation can still result in a broken bundle if thesideEffects
annotation is incorrect.
v0.9.3
-
Fix path resolution with the
exports
field for scoped packagesThis release fixes a bug where the
exports
field inpackage.json
files was not being detected for scoped packages (i.e. packages of the form@scope/pkg-name
instead of justpkg-name
). Theexports
field should now be respected for these kinds of packages. -
Improved error message in
exports
failure caseNode's new conditional exports feature can be non-intuitive and hard to use. Now that esbuild supports this feature (as of version 0.9.0), you can get into a situation where it's impossible to import a package if the package's
exports
field in itspackage.json
file isn't configured correctly.Previously the error message for this looked like this:
> entry.js:1:7: error: Could not resolve "jotai" (mark it as external to exclude it from the bundle) 1 │ import 'jotai' ╵ ~~~~~~~ node_modules/jotai/package.json:16:13: note: The path "." is not exported by "jotai" 16 │ "exports": { ╵ ^
With this release, the error message will now provide additional information about why the package cannot be imported:
> entry.js:1:7: error: Could not resolve "jotai" (mark it as external to exclude it from the bundle) 1 │ import 'jotai' ╵ ~~~~~~~ node_modules/jotai/package.json:16:13: note: The path "." is not currently exported by package "jotai" 16 │ "exports": { ╵ ^ node_modules/jotai/package.json:18:9: note: None of the conditions provided ("module", "require", "types") match any of the currently active conditions ("browser", "default", "import") 18 │ ".": { ╵ ^ entry.js:1:7: note: Consider using a "require()" call to import this package 1 │ import 'jotai' ╵ ~~~~~~~
In this case, one solution could be import this module using
require()
since this package provides an export for therequire
condition. Another solution could be to pass--conditions=module
to esbuild since this package provides an export for themodule
condition (thetypes
condition is likely not valid JavaScript code).This problem occurs because this package doesn't provide an import path for ESM code using the
import
condition and also doesn't provide a fallback import path using thedefault
condition. -
Mention glob syntax in entry point error messages (#976)
In this release, including a
*
in the entry point path now causes the failure message to tell you that glob syntax must be expanded first before passing the paths to esbuild. People that hit this are usually converting an existing CLI command to a JavaScript API call and don't know that glob expansion is done by their shell instead of by esbuild. An appropriate fix is to use a library such asglob
to expand the glob pattern first before passing the paths to esbuild. -
Raise certain VM versions in the JavaScript feature compatibility table
JavaScript VM feature compatibility data is derived from this dataset: https://kangax.github.io/compat-table/. The scripts that process the dataset expand the data to include all VM versions that support a given feature (e.g.
chrome44
,chrome45
,chrome46
, ...) so esbuild takes the minimum observed version as the first version for which the feature is supported.However, some features can have subtests that each check a different aspect of the feature. In this case the desired version is the minimum version within each individual subtest, but the maximum of those versions across all subtests (since esbuild should only use the feature if it works in all cases). Previously esbuild computed the minimum version across all subtests, but now esbuild computes the maximum version across all subtests. This means esbuild will now lower JavaScript syntax in more cases.
-
Mention the configured target environment in error messages (#975)
Using newer JavaScript syntax with an older target environment (e.g.
chrome10
) can cause a build error if esbuild doesn't support transforming that syntax such that it is compatible with that target environment. Previously the error message was generic but with this release, the target environment is called outp explicitly in the error message. This is helpful if esbuild is being wrapped by some other tool since the other tool can obscure what target environment is actually being passed to esbuild. -
Fix an issue with Unicode and source maps
This release fixes a bug where non-ASCII content that ended up in an output file but that was not part of an input file could throw off source mappings. An example of this would be passing a string containing non-ASCII characters to the
globalName
setting with theminify
setting active and thecharset
setting set toutf8
. The conditions for this bug are fairly specific and unlikely to be hit, so it's unsurprising that this issue hasn't been discovered earlier. It's also unlikely that this issue affected real-world code.The underlying cause is that while the meaning of column numbers in source maps is undefined in the specification, in practice most tools treat it as the number of UTF-16 code units from the start of the line. The bug happened because column increments for outside-of-file characters were incorrectly counted using byte offsets instead of UTF-16 code unit counts.
v0.9.2
-
Fix export name annotations in CommonJS output for node (#960)
The previous release introduced a regression that caused a syntax error when building ESM files that have a default export with
--platform=node
. This is because the generated export contained thedefault
keyword like this:0 && (module.exports = {default});
. This regression has been fixed.