Skip to content

Releases: evanw/esbuild

v0.8.47

18 Feb 02:04
Compare
Choose a tag to compare
  • Release native binaries for the Apple M1 chip (#550)

    Previously installing esbuild on a M1 actually installed the x86-64 version, which required the Rosetta 2 translator. This was because Go hadn't yet released support for the M1. Now that Go 1.16.0 has been released, esbuild can support the M1 natively. It's supported by esbuild starting with this release. There are reports of the native version being 1.4x faster than the translated version. This change was contributed by @rtsao.

  • Omit warning about require.someProperty when targeting CommonJS (#812)

    The require.cache property allows introspecting the state of the require cache, generally without affecting what is imported/bundled.

    Since esbuild's static analyzer only detects direct calls to require, it currently warns about uses of require in any situation other than a direct call since that means the value is "escaping" the analyzer. This is meant to detect and warn about indirect calls such as ['fs', 'path'].map(require).

    However, this warning is not relevant when accessing a property off of the require object such as require.cache because a property access does not result in capturing the value of require. Now a warning is no longer generated for require.someProperty when the output format is cjs. This allows for the use of features such as require.cache and require.extensions. This fix was contributed by @huonw.

  • Support ignored URL parameters at the end of import paths (#826)

    If path resolution fails, ebuild will now try again with the URL query and/or fragment removed. This helps handle ancient CSS code like this that contains hacks for Internet Explorer:

    @font-face {
      src:
        url("./themes/default/assets/fonts/icons.eot?#iefix") format('embedded-opentype'),
        url("./themes/default/assets/fonts/icons.woff2") format('woff2'),
        url("./themes/default/assets/fonts/icons.woff") format('woff'),
        url("./themes/default/assets/fonts/icons.ttf") format('truetype'),
        url("./themes/default/assets/fonts/icons.svg#icons") format('svg');
    }

    Previously path resolution would fail because these files do not end with the .eot?#iefix or .svg#icons extensions. Now path resolution should succeed. The URL query and fragment are not unconditionally stripped because there is apparently code in the wild that uses # as a directory name. So esbuild will still try to resolve the full import path first and only try to reinterpret the path as a URL if that fails.

  • Prevent paths starting with / from being used as relative paths on Windows (#822)

    On Windows, absolute paths start with a drive letter such as C:\... instead of with a slash like /.... This means that paths starting with a / can actually be used as relative paths. For example, this means an import of /subfolder/image.png will match the file at the path ./subfolder/image.png. This is problematic for Windows users because they may accidentally make use of these paths and then try to run their code on a non-Windows platform only for it to fail to build.

    Now paths starting with a / are always treated as an absolute path on all platforms. This means you can no longer import files at a relative path that starts with / on Windows. You should be using a ./ prefix instead.

  • Warn when importing a path with the wrong case

    Importing a path with the wrong case (e.g. File.js instead of file.js) will work on Windows and sometimes on macOS because they have case-insensitive file systems, but it will never work on Linux because it has a case-sensitive file system. To help you make your code more portable and to avoid cross-platform build failures, esbuild now issues a warning when you do this.

v0.8.46

14 Feb 02:53
Compare
Choose a tag to compare
  • Fix minification of .0 in CSS (#804)

    If you write .0 instead of 0 in CSS and enabled --minify, esbuild would previously minify this token incorrectly (the token was deleted). This bug has been fixed and esbuild should now minify this token to 0.

  • Support range requests in local HTTP server

    The local HTTP server built in to esbuild now supports range requests, which are necessary for video playback in Safari. This means you can now use <video> tags in your HTML pages with esbuild's local HTTP server.

v0.8.45

13 Feb 10:56
Compare
Choose a tag to compare
  • Add the --servedir= flag (#796)

    The --serve flag starts a local web server and serves the files that would normally be written to the output directory. So for example if you had an entry point called src/app.ts and an output directory of --outdir=www/js, using esbuild with --serve would expose the generated output file via http://localhost:8000/app.js (but not write anything to www/js). This can then be used in combination with your normal development server (running concurrently on another port) by adding <script src="http://localhost:8000/app.js"></script> in your HTML file. So esbuild with the --serve flag is meant to augment your normal development server, not replace it.

    This release introduces a new --servedir= flag which gives you the option of replacing your normal development server with esbuild. The directory you pass here will be "underlayed" below the output directory. Specifically when an incoming HTTP request comes in esbuild will first check if it matches one of the generated output files and if so, serve the output file directly from memory. Otherwise esbuild will fall back to serving content from the serve directory on the file system. In other words, server's URL structure behaves like a normal file server in a world where esbuild had written the generated output files to the file system (even though the output files actually only exist in memory).

    So for example if you had an entry point called src/app.ts and an output directory of --outdir=www/js, using esbuild with --servedir=www would expose the entire contents of the www directory via http://localhost:8000/ except for the http://localhost:8000/js/app.js URL which would contain the compiled contents of src/app.ts. This lets you have a www/index.html file containing just <script src="/js/app.js"></script> and use one web server instead of two.

    The benefit of doing things this way is that you can use the exact same HTML pages in development and production. In development you can run esbuild with --servedir= and esbuild will serve the generated output files directly. For production you can omit that flag and esbuild will write the generated files to the file system. In both cases you should be getting the exact same result in the browser with the exact same code in both development and production.

    This will of course not support all workflows, but that's intentional. This is designed to be a quality-of-life improvement for the simple case of building a small static website with some HTML, JavaScript, and CSS. More advanced setups may prefer to avoid the --servedir= feature and e.g. configure a NGINX reverse proxy to esbuild's local server to integrate esbuild into a larger existing development setup.

    One unintended consequence of this feature is that esbuild can now be used as a general local HTTP server via esbuild --servedir=.. Without any entry points, esbuild won't actually build anything and will just serve files like a normal web server. This isn't the intended use case but it could perhaps be a useful side effect of this feature.

  • Remove absolute paths for disabled packages from source maps (#786)

    This change is similar to the one from the previous release for disabled files, but it applies to package paths instead of relative paths. It's relevant when using packages that override dependencies with alternative packages using the browser field in their package.json file. Using relative paths instead of absolute paths fixes a determinism issue where build output was different on different systems. This fix was contributed by @eelco.

  • Handle absolute paths in tsconfig.json (#792)

    Some automatically-generated tsconfig.json paths can have absolute paths in them. This is allowed by the TypeScript compiler (specifically in the paths and extends fields). With this release, esbuild now supports absolute paths in paths and extends too.

  • Change the watch mode output format (#793)

    Previously esbuild would print a "..." animation to the console while watch mode was scanning for changes. The intent of this was to a) not take up too much space in the terminal and b) show that esbuild's watch mode isn't frozen. Since the release I have gotten feedback that this isn't desirable. People want more feedback about what's happening and want to be able to run regexes over the stderr stream instead of using esbuild's actual API.

    This release changes the output format for watch mode. Now esbuild will print [watch] build started when watch mode triggers a rebuild and [watch] build finished when the rebuild is complete. Any build errors will be printed in between those two log messages.

    Note that this means esbuild's watch mode output is now more verbose, especially when there are frequent file changes. If you want to hide these new messages you can use --log-level= with a level other than info.

v0.8.44

11 Feb 06:27
Compare
Choose a tag to compare
  • Create a logo for esbuild (#61)

    This release introduces a logo for esbuild:

         

    Inspirations for the logo include:

    • The fast-forward symbol because esbuild is extremely fast and because one of esbuild's goals is to accelerate the evolution of the whole web tooling ecosystem.

    • The right-shift symbol because esbuild's production optimizations make your code smaller and because esbuild itself contains many low-level optimizations for speed.

    Having a logo for esbuild should make it easier to include esbuild in lists of other tools since the other tools often all have logos.

  • Add support for node's --preserve-symlinks flag (#781)

    This release adds the --preserve-symlinks flag which behaves like the corresponding flag in node. Without the flag, esbuild and node will use the real path (after resolving symlinks) as the identity of a file. This means that a given file can only be instantiated once. With the flag, esbuild and node will use the original path (without resolving symlinks) as the identity of a file. This means that a given file can be instantiated multiple times, once for every symlink pointing to it. Each copy will have its own identity so the resulting bundle may contain duplicate files. This option is useful if your code relies on this flag in node (or the resolve.symlinks setting in Webpack).

  • Ignore a leading byte order mark (BOM) in CSS files (#776)

    Some text editors insert a U+FEFF code point at the start of text files. This is a zero-width non-breaking space character. Using one at the start of a file is a convention which is meant to indicate that the contents of the file are UTF-8 encoded. When this is done, the character is called a byte order mark.

    Unlike JavaScript, CSS does not treat U+FEFF as whitespace. It is treated as an identifier instead. This was causing esbuild to misinterpret files starting with a BOM as starting with an extra identifier, which could then cause the initial CSS rule in the file to be parsed incorrectly.

    Now esbuild will skip over a BOM if it's present before beginning to parse CSS. This should prevent issues when working with these files.

  • Add message notes to the API

    The internal logging system has the ability to attach additional notes to messages to provide more information. These show up as additional log messages in the terminal when using the command-line interface. Here is an example of a note:

     > src/structs/RTree.js: warning: Duplicate key "compareMinX" in object literal
        469 │     compareMinX: function (a, b)
            ╵     ~~~~~~~~~~~
       src/structs/RTree.js: note: The original "compareMinX" is here
        206 │     compareMinX: compareNodeMinX,
            ╵     ~~~~~~~~~~~
    

    With this release, notes are also supported in the JS and Go APIs. This means you can now generate your own notes using plugins as well as inspect the notes generated by esbuild.

  • Add origin information to errors from plugins (#780)

    Errors thrown during JavaScript plugin callback evaluation will now be annoated to show where that plugin callback was registered. That looks like this:

     > example-plugin.js: error: [example-plugin] foo.bar is not a function
        15 │         foo.bar();
           ╵             ^
        at ./example-plugin.js:15:13
        at ./node_modules/esbuild/lib/main.js:750:34
    
       example-plugin.js: note: This error came from the "onLoad" callback registered here
        13 │       build.onLoad({ filter: /.*/ }, args => {
           ╵             ~~~~~~
        at setup (./example-plugin.js:13:13)
        at handlePlugins (./node_modules/esbuild/lib/main.js:668:7)
    

    This should make it easier to debug crashes in plugin code.

  • Fix a regression with the synchronous JavaScript API (#784)

    In version 0.8.39, a change was made to avoid dangling esbuild processes when node exits abnormally. The change introduced a periodic ping between the child esbuild process and its host process. If the ping doesn't go through, the child process is able to detect that the host process is no longer there. Then it knows to exit since it's no longer being used.

    This caused a problem with the synchronous JavaScript API calls which run the esbuild child process in a single-response mode. The ping message was interpreted as a second response and tripped up the message protocol. Pings are only useful for the asynchronous API calls. Running the pings during synchronous API calls was unintentional. With this release pings are no longer run for synchronous API calls so this regression should be fixed.

  • Remove absolute paths for disabled files from source maps (#785)

    Files can be ignored (i.e. set to empty) using the browser field in package.json. Specifically, you can set the browser field to a map where the key is the module name and the value is false. This is a convention followed by several bundlers including esbuild.

    Previously ignoring a file caused that file's path to appear as an absolute path in any generated source map. This is problematic because it means different source maps will be generated on different systems, since the absolute path contains system-specific directory information. Now esbuild will treat these paths the same way it treats other paths and will put a relative path in the source map.

v0.8.43

08 Feb 09:22
Compare
Choose a tag to compare
  • Support the XDG_CACHE_HOME environment variable (#757)

    On Linux, the install script for esbuild currently caches downloaded binary executables in ~/.cache/esbuild/bin. This change means esbuild will now try installing to $XDG_CACHE_HOME/esbuild/bin instead of the XDG_CACHE_HOME environment variable exists. This allows you to customize the cache directory on Linux. The specification that defines XDG_CACHE_HOME is here.

  • Further improve constant folding of branches (#765)

    At a high level, this release adds the following substitutions to improve constant folding and dead code elimination:

    • if (anything && falsyWithSideEffects)if (anything, falsyWithSideEffects)
    • if (anything || truthyWithSideEffects)if (anything, truthyWithSideEffects)
    • if (anything && truthyNoSideEffects)if (anything)
    • if (anything || falsyNoSideEffects)if (anything)
    • if (anything, truthyOrFalsy)anything; if (truthyOrFalsy)

    And also these substitutions for unused expressions:

    • primitive == primitiveprimitive, primitive
    • typeof identifier → (remove entirely)

    The actual substitutions are more complex since they are more comprehensive but they essentially result in this high-level behavior. Note that these substitutions are only done when minification is enabled.

  • Fix an edge case with CSS variable syntax (#760)

    CSS variables are whitespace-sensitive even though other CSS syntax is mostly not whitespace sensitive. It is apparently common for this to cause problems with CSS tooling that pretty-prints and minifies CSS, including esbuild before this release. Some examples of issues with other tools include postcss/postcss#1404 and tailwindlabs/tailwindcss#2889. The issue affects code like this:

    div {
      --some-var: ;
      some-decl: var(--some-var, );
    }

    It would be a change in semantics to minify this code to either --some-var:; or var(--some-var,) due to the whitespace significance of CSS variables, so such transformations are invalid. With this release, esbuild should now preserve whitespace in these two situations (CSS variable declarations and CSS variable references).

  • Add support for recursive symlinks during path resolution (#766)

    Previously recursive symlinks (a symlink that points to another symlink) were an unhandled case in the path resolution algorithm. Now these cases should be supported up to a depth of 256 symlinks. This means esbuild's path resolution should now work with multi-level yarn link scenarios.

  • Fix subtle circular dependency issue (#758)

    If esbuild is used to transform TypeScript to JavaScript without bundling (i.e. each file is transformed individually), the output format is CommonJS, and the original TypeScript code contains an import cycle where at least one of the links in the cycle is an export * as re-export statement, there could be certain situations where evaluating the transformed code results in an import being undefined. This is caused by the __esModule marker being added after the call to require() for the first transformed re-export statement. The fix was to move the marker to before the first call to require(). The __esModule marker is a convention from Babel that esbuild reuses which marks a module as being originally in the ECMAScript module format instead of the CommonJS module format.

  • Add support for the NODE_PATH environment variable

    This is a rarely-used feature of Node's module resolution algorithm. From the documentation:

    If the NODE_PATH environment variable is set to a colon-delimited list of absolute paths, then Node.js will search those paths for modules if they are not found elsewhere.

    On Windows, NODE_PATH is delimited by semicolons (;) instead of colons.

    The CLI takes the list of node paths from the value of the NODE_PATH environment variable, but the JS and Go APIs take the list as an array of strings instead (called nodePaths in JS and NodePaths in Go).

v0.8.42

05 Feb 07:46
Compare
Choose a tag to compare
  • Fix crash with block-level function declaration and --keep-names (#755)

    This release fixes a crash with block-level function declarations and the --keep-names option. The crash affected code that looks like this:

    if (true) function f() {}
    assert.strictEqual(f.name, 'f')
  • Disallow additional features in strict mode

    This change improves esbuild's compliance with the JavaScript specification. It is now an error to use legacy octal numeric literals and the identifiers implements, interface, let, package, private, protected, public, static, and yield in strict mode code.

  • Basic support for watch mode with plugins (#752)

    With this release, watch mode should now work with simple on-load plugins. Watch mode is implemented by tracking all file system accesses made by esbuild as it does a build. However, this doesn't catch external file system accesses such as those made by plugins. Now if an on-load plugin is used on a path in the file namespace, esbuild will also read the file during watch mode so that watch mode is aware of the file system access. Note that there is not yet API support for a plugin to return additional paths for watch mode to monitor.

  • Make JavaScript API error format more consistent (#745)

    If a JavaScript error is thrown while validating the build options, the thrown error should now have errors and warnings properties just like normal build errors. Previously these properties were only present if the build itself failed but not if build options were invalid. This consistency should make it easier to process errors from the build API call.

v0.8.41

04 Feb 14:22
Compare
Choose a tag to compare
  • Fix memory leak with watch mode when using the CLI (#750)

    This release fixes a memory leak when using --watch from the CLI (command-line interface). When esbuild was in this state, every incremental build resulted in more memory being consumed. This problem did not affect users of the JS API or Go API, only users of the CLI API.

    The problem was that the GC (garbage collector) was disabled. Oops. This is done by default for speed when you use esbuild via the CLI, which makes sense for most CLI use cases because the process is usually short-lived and doesn't need to waste time cleaning up memory. But it does not make sense for flags that cause esbuild to be a long-running process.

    Previously the only exception to this rule was the --serve flag. When I added watch mode, I forgot to enable GC for the --watch flag too. With this release, the GC is enabled for both the --serve and the --watch flags so esbuild should no longer leak memory in watch mode.

  • Special-case certain syntax with --format=esm (#749)

    You can now no longer use the following syntax features with the esm output format:

    • The with statement: with (x) {}
    • Delete of a bare identifier: delete x

    In addition, the following syntax feature is transformed when using the esm output format:

    • For-in variable initializers: for (var x = y in {}) {}x = y; for (var x in {}) {}

    The reason is because all JavaScript engines interpret code in the esm output format as strict mode and these syntax features are disallowed in strict mode. Note that this new strict mode handling behavior in esbuild is only dependent on the output format. It does not depend on the presence or absence of "use strict" directives.

  • Basic "use strict" tracking

    The JavaScript parser now tracks "use strict" directives and propagates strict mode status through the code. In addition, files containing the import and/or export keywords are also considered to be in strict mode. Strict mode handling is complex and esbuild currently doesn't implement all strict mode checks. But the changes in this release are a starting point. It is now an error to use certain syntax features such as a with statement within a strict mode scope.

  • Fix a minifier bug with with statements

    The minifier removes references to local variables if they are unused. However, that's not correct to do inside a with statement scope because what appears to be an identifier may actually be a property access, and property accesses could have arbitrary side effects if they resolve to a getter or setter method. Now all identifier expressions inside with statements are preserved when minifying.

  • Transform block-level function declarations

    Block-level function declarations are now transformed into equivalent syntax that avoids block-level declarations. Strict mode and non-strict mode have subtly incompatible behavior for how block-level function declarations are interpreted. Doing this transformation prevents problems with code that was originally strict mode that is run as non-strict mode and vice versa.

    Now esbuild uses the presence or absence of a strict mode scope to determine how to interpret the block-level function declaration and then converts it to the equivalent unambiguous syntax such that it works the same regardless of whether or not the current scope is in strict mode:

    // This original code:
    while (!y) {
      function y() {}
    }
    
    // is transformed into this code in strict mode:
    while (!y) {
      let y2 = function() {};
    }
    
    // and into this code when not in strict mode:
    while (!y) {
      let y2 = function() {};
      var y = y2;
    }

v0.8.40

04 Feb 04:19
Compare
Choose a tag to compare
  • Fix TypeScript parameter decorators on class constructors (#734)

    This release fixes a TypeScript translation bug where parameter decorators on class constructors were translated incorrectly. Affected code looks like this:

    class Example {
      constructor(@decorator param: any) {}
    }

    This bug has been fixed. In addition, decorators are no longer allowed on class constructors themselves because they are not allowed in TypeScript.

  • Resolve browser entries in package.json with no file extension (#740)

    This fix changes how esbuild interprets the browser field in package.json. It will now remap imports without a file extension to browser map entries without a file extension, which improves compatibility with Webpack. Specifically, a package.json file with "browser": {"./file": "./something.js"} will now match an import of ./file. Previously the package.json file had to contain something like "browser": {"./file.js": "./something.js"} instead. Note that for compatibility with the rest of the ecosystem, a remapping of ./file will counter-intuitively not match an import of ./file.js even though it works fine in the other direction.

  • Warning: npm v7 bug may prevent esbuild installation

    This is a warning for people reading these release notes, not a code change. I have discovered a bug in npm v7 where your package-lock.json file can become corrupted such that no postinstall scripts are run. This bug affects all packages with postinstall scripts, not just esbuild, and happens when running npm v7 on a package-lock.json file from npm v6 or earlier. It seems like deleting and regenerating your package-lock.json file is a valid workaround that should get esbuild working again.

v0.8.39

01 Feb 09:46
Compare
Choose a tag to compare
  • Fix the JavaScript watch mode API exiting early (#730)

    The previous release contained a bug that caused the JavaScript watch mode API to exit early in some cases. This bug should now be fixed. The problem was caused by some code that shouldn't even need to exist now that you are no longer required to call stop() on an esbuild service created by startService() (it was made optional in version 0.8.32). I took the opportunity to clean up the internals of esbuild's JavaScript API implementation which ended up removing the entire section of code that contained this bug.

  • Add an API option for a per-build working directory (#689)

    You can now use the absWorkingDir API option to customize the current working directory. It will default to the value of process.cwd() at the time of the call to startService() when not specified, which matches the existing behavior. The working directory is used for a few different things including resolving relative paths given as API options to absolute paths and pretty-printing absolute paths as relative paths in log messages.

    In addition to being a useful feature, this change also simplifies esbuild's internals. Previously esbuild had to maintain separate child processes if the current working directory was changed in between build API calls. Now esbuild will always reuse the same child process across all build API calls. The stop() call on the startService() API is also now a no-op (it doesn't do anything anymore) and the startService() API may be removed in future releases.

  • Fix stray esbuild process after node exits (#643)

    I discovered that using esbuild's JavaScript incremental build API could result in the child esbuild process not exiting when the parent node process exits. This was due to a reference counting issue. The bug has been fixed so this shouldn't happen anymore.

v0.8.38

31 Jan 10:11
Compare
Choose a tag to compare
  • Implement a simple cross-platform watch mode (#21)

    With this release, you can use the --watch flag to run esbuild in watch mode which watches the file system for changes and does an incremental build when something has changed. The watch mode implementation uses polling instead of OS-specific file system events for portability.

    Note that it is still possible to implement watch mode yourself using esbuild's incremental build API and a file watcher library of your choice if you don't want to use a polling-based approach. Also note that this watch mode feature is about improving developer convenience and does not have any effect on incremental build time (i.e. watch mode is not faster than other forms of incremental builds).

    The new polling system is intended to use relatively little CPU vs. a traditional polling system that scans the whole directory tree at once. The file system is still scanned regularly but each scan only checks a random subset of your files to reduce CPU usage. This means a change to a file will be picked up soon after the change is made but not necessarily instantly. With the current heuristics, large projects should be completely scanned around every 2 seconds so in the worst case it could take up to 2 seconds for a change to be noticed. However, after a change has been noticed the change's path goes on a short list of recently changed paths which are checked on every scan, so further changes to recently changed files should be noticed almost instantly.

  • Add pluginData to pass data between plugins (#696)

    You can now return additional data from a plugin in the optional pluginData field and it will be passed to the next plugin that runs in the plugin chain. So if you return it from an onLoad plugin, it will be passed to the onResolve plugins for any imports in that file, and if you return it from an onResolve plugin, an arbitrary one will be passed to the onLoad plugin when it loads the file (it's arbitrary since the relationship is many-to-one). This is useful to pass data between different plugins without them having to coordinate directly.