Skip to content

Releases: evanw/esbuild

v0.8.17

29 Nov 12:58
Compare
Choose a tag to compare
  • 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 macOS arm64 too. Doing this should still work because of the executable translation layer built into macOS. This change was contributed by @sod.

v0.8.16

26 Nov 19:43
Compare
Choose a tag to compare
  • 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 TypeScript strictNullChecks 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 called require() on it. Previously esbuild generated a warning about an unexpected use of require. Now this warning is no longer generated for require.main when the output format is cjs.

  • 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 replace process.env.NODE_ENV with either "production" or "development" and sometimes people accidentally replace it with production or development 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

25 Nov 09:43
Compare
Choose a tag to compare
  • Allow paths without baseUrl in tsconfig.json

    This feature was recently released in TypeScript 4.1. The paths feature in tsconfig.json allows you to do custom import path rewriting. For example, you can map paths matching @namespace/* to the path ./namespace/src/* relative to the tsconfig.json file. Previously using the paths feature required you to additionally specify baseUrl 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 the baseUrl directory, which could potentially cause package paths to accidentally be redirected to non-package files. Specifying baseUrl also causes Visual Studio Code's auto-import feature to generate paths relative to the baseUrl 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 using paths. When you do this, it as if you had written "baseUrl": "." instead for the purpose of the paths feature, but the baseUrl value is not actually set and does not affect path resolution. These tsconfig.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 or cjs instead of esm. 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

24 Nov 06:51
Compare
Choose a tag to compare
  • 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

23 Nov 19:02
Compare
Choose a tag to compare
  • Assigning to a const symbol is now an error when bundling

    This 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 classes

    This 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 to es6:

    let foo = class {
      #privateMethod() {}
    }

    The name property of this class object is now foo.

  • 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 prints true. 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 bundling

    This option turns top-level let, const, and class statements into var 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 into let

    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 changing const to let should now not affect run-time behavior.

v0.8.12

21 Nov 06:53
Compare
Choose a tag to compare
  • 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

18 Nov 22:26
Compare
Choose a tag to compare
  • 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 an as 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 a node_modules folder.

v0.8.10

18 Nov 05:13
Compare
Choose a tag to compare
  • 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 an extends clause but esbuild didn't require the extends 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 the sideEffects field in package.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 in tsconfig.json files

    Previously esbuild always joined the baseUrl path to the end of the current directory path. However, if the baseUrl was an absolute path, that would end up including the current directory path twice. This situation could arise internally in certain cases involving multiple tsconfig.json files and extends fields even if the tsconfig.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

17 Nov 08:15
Compare
Choose a tag to compare
  • 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 fields main and module. Packages can also mark files in the package as not having side effects using the sideEffects 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 both main and module to not have side effects if one of them was marked as not having side effects. Now main and module 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 effects

    Files in packages containing "sideEffects": false in the enclosing package.json file are intended to be automatically removed from the bundle if they aren't used. However, code containing import './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 in package.json that caused this situation.

  • Add support for glob-style tests in sideEffects arrays

    The sideEffects field in package.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.