Skip to content

Releases: evanw/esbuild

v0.8.37

30 Jan 12:36
Compare
Choose a tag to compare
  • Improve ambiguous import handling (#723)

    It is an error to try to import a name from a file where there are multiple matching exports due to multiple export * from statements from files which export that name. This release contains a few improvements to ambiguous import handling:

    1. This release fixes a bug where named export shadowing didn't work correctly with multiple levels of re-exports. A named export closer in the re-export chain is supposed to hide a named export deeper in the re-export chain without causing an ambiguous import. The bug caused this case to be incorrectly flagged as an error even though it should have been allowed. This case is now allowed without an error.

    2. Previously the error message just said that there was an ambiguous import but didn't have any additional information. With this release, the error message also points out where the two different exports that have collided are in their original source files. Hopefully this should make it quicker to diagnose these types of issues.

    3. Real JavaScript environments only treat ambiguous imports as an error if they are explicitly a named import. Using the import * as syntax and then accessing the ambiguous import with a property access results in undefined instead of an error. Previously esbuild also treated this case as an error because it automatically rewrites star-import syntax to named-import syntax to improve tree shaking. With this release, this case is now treated as a warning instead of an error and the import will be automatically replaced with an undefined literal in the bundled code.

  • Reuse automatically-generated temporary *.node files (#719)

    The previous change to hide the automatically-generated N-API native node extensions from Yarn 2 writes these *.node files to the system's temporary directory. A new one was being created on each run which is wasteful even though they are only a few kilobytes in size. With this release *.node files will now be reused if they are already present in the system's temporary directory, so a new one is no longer created on each run. This fix was contributed by @kzc.

  • Fix the serve API with outfile (#707)

    This release fixes a bug where the serve API did not work with the outfile setting. Using this setting with the serve API should now work fine.

  • Warn about duplicate keys in object literals

    Using a duplicate key in an object literal such as {x: 1, x: 2} is now a warning. This is allowed in JavaScript but results in subsequent keys overwriting the previous key. It's usually a copy/paste error and isn't ever useful so it's worth warning about.

  • Avoid generating duplicate keys in JSON metadata

    The output map that is generated when the metafile feature is active could potentially have duplicate keys if the file loader is used, there are multiple entry points, and two or more entry points reference the same file. This is harmless because both keys mapped to the same value, but it's confusing and unnecessary. Duplicate keys are no longer present in the output map in this latest release.

  • Make the JSON metafile structure match the type definitions (#726)

    Previously imports and/or exports could be missing from entries in the output map in certain cases (specifically for source maps and files loaded with the file loader). This was problematic because the TypeScript type definitions for the metafile say that the imports and exports properties are non-optional. With this release, the imports and exports properties are now always present so the existing TypeScript type definitions are now accurate.

  • Update from Go 1.15.5 to Go 1.15.7

    The version of Go used to build the released binary executables on npm is now Go 1.15.7. This change shouldn't result in any visible changes to esbuild. It was only upgraded because the Go extension for the VSCode IDE now uses the official gopls Go language service and this extension wanted the latest version of Go.

v0.8.36

26 Jan 11:17
Compare
Choose a tag to compare
  • Fix an issue with writing large files to stdout using the WebAssembly executable

    The previous release introduced a regression where large output files written to stdout were incorrectly truncated when using the WebAssembly esbuild command. This regression was due to a missing callback to the JavaScript write() function when called on the stdout stream. The regression has been fixed.

  • Hide the N-API native node extensions from Yarn 2

    The previous release introduced some very small (1-2kb) *.node native extensions to fix a bug with node failing to exit properly. However, this causes Yarn 2 to unzip the esbuild package, which is undesirable. This release puts these native node extensions inside JavaScript code instead to hide them from Yarn 2. The native extensions are written to a temporary file at run-time if necessary.

v0.8.35

26 Jan 09:00
Compare
Choose a tag to compare
  • Fix a commonly-missed corner case with await inside **

    I recently discovered an interesting discussion about JavaScript syntax entitled "Most implementations seem to have missed that await x ** 2 is not legal". Indeed esbuild has missed this, but this is not surprising because V8 has missed this as well and I usually test esbuild against V8 to test if esbuild is conformant with the JavaScript standard. Regardless, it sounds like the result of the discussion is that the specification should stay the same and implementations should be fixed. This release fixes this bug in esbuild's parser. The syntax await x ** 2 is no longer allowed and parentheses are now preserved for the syntax (await x) ** 2.

  • Allow namespaced names in JSX syntax (#702)

    XML-style namespaced names with a : in the middle are a part of the JSX specification but they are explicitly unimplemented by React and TypeScript so esbuild doesn't currently support them. However, there was a user request to support this feature since it's part of the JSX specification and esbuild's JSX support can be used for non-React purposes. So this release now supports namespaced names in JSX expressions:

    let xml =
      <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
               xmlns:dc="http://purl.org/dc/elements/1.1/">
        <rdf:Description rdf:ID="local-record">
          <dc:title>Local Record</dc:title>
        </rdf:Description>
      </rdf:RDF>

    This JSX expression is now transformed by esbuild to the following JavaScript:

    let xml = React.createElement("rdf:RDF", {
      "xmlns:rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
      "xmlns:dc": "http://purl.org/dc/elements/1.1/"
    }, React.createElement("rdf:Description", {
      "rdf:ID": "local-record"
    }, React.createElement("dc:title", null, "Local Record")));

    Note that if you are trying to namespace your React components, this is not the feature to use. You should be using a . instead of a : for namespacing your React components since . resolves to a JavaScript property access.

  • Fix worker: false in esbuild's browser-based JavaScript API

    The browser-based JavaScript API creates a web worker by default but this can be disabled by passing worker: false. When you do this the WebAssembly code is run in the current thread which will lock up the thread. This is mainly useful if you're calling the JavaScript API from within a web worker and you want to avoid creating another nested web worker.

    This option was unintentionally broken when the internal JavaScript web worker source code was moved from an inline function to a string in version 0.5.20. The regression has been fixed and the worker: false scenario now has test coverage.

  • Fix absolute paths with the esbuild-wasm package on Windows (#687)

    The package esbuild-wasm has an esbuild command implemented using WebAssembly instead of using native code. It uses node's WebAssembly implementation and calls methods on node's fs module to access the file system.

    Go's path/filepath module has a bug where Windows paths are interpreted as Unix paths when targeting WebAssembly: golang/go#43768. This causes multiple issues including absolute paths such as C:\path\to\file.js being interpreted as relative paths (since they don't start with a /) and being joined onto the end of other paths.

    To fix this, esbuild now does all of its own path handling instead of using Go's path handling code. The esbuild code base now contains a forked copy of path/filepath that can handle both Windows and Unix paths. The decision about which one to use is made at run-time. When targeting WebAssembly, the presence of the C:\ directory is used to determine if Windows-style paths should be used.

    With this release, it should now be possible to use Windows-style paths with esbuild's WebAssembly implementation on Windows.

  • Fix using stdin with the esbuild-wasm package on Windows (#687)

    Node has an old bug (nodejs/node#19831, nodejs/node#35997) where fs.read returns an EOF error at the end of stdin on Windows. This causes Go's WebAssembly implementation to panic when esbuild tries to read from stdin.

    The workaround was to manually check for this case and then ignore the error in this specific case. With this release, it should now be possible to pipe something to the esbuild command on Windows.

  • Fix stdout and stderr not supporting Unicode in the esbuild-wasm package on Windows (#687)

    Node's fs.write API is broken when writing Unicode to stdout and stderr on Windows, and this will never be fixed: nodejs/node#24550. This is problematic for Go's WebAssembly implementation because it uses this API for writing to all file descriptors.

    The workaround is to manually intercept the file descriptors for stdout and stderr and redirect them to process.stdout and process.stderr respectively. Passing Unicode text to write() on these objects instead of on the fs API strangely works fine. So with this release, Unicode text should now display correctly when using esbuild's WebAssembly implementation on Windows (or at least, as correctly as the poor Unicode support in Windows Command Prompt allows).

  • Add a hack for faster command-line execution for the WebAssembly module in certain cases

    Node has an unfortunate bug where the node process is unnecessarily kept open while a WebAssembly module is being optimized: nodejs/node#36616. This means cases where running esbuild should take a few milliseconds can end up taking many seconds instead.

    The workaround is to force node to exit by ending the process early. This is done in one of two ways depending on the exit code. For non-zero exit codes (i.e. when there is a build error), the esbuild command now calls process.kill(process.pid) to avoid the hang.

    For zero exit codes, the esbuild command now loads a N-API native node extension that calls the operating system's exit(0) function. This is done without requiring node-gyp by precompiling each supported platform and just including all of them in the esbuild-wasm package since they are so small. If this hack doesn't work in certain cases, the process should exit anyway just potentially many seconds later. Currently the only supported platforms for this hack are 64-bit macOS, Windows, and Linux.

  • Fix non-absolute paths with the esbuild-wasm package in the browser (#693)

    When using esbuild in the browser via WebAssembly, it was not possible to specify an non-absolute output path. Normally you can do this and esbuild will just convert it to an absolute path by resolving it as a relative path from the current working directory. However, Go's WebAssembly implementation has no current working directory so the conversion operation to an absolute path failed, causing esbuild's API to fail.

    With this release, esbuild should now behave as if the current working directory is / in the browser. For example, this means calling the build() API with outfile: 'file.js' should now generate an output file called /file.js instead of causing an error.

v0.8.34

21 Jan 23:41
Compare
Choose a tag to compare
  • Fix a parser bug about suffix expressions after an arrow function body (#701)

    The JavaScript parser incorrectly handled suffix expressions after a non-expression arrow function body. In practice, this came up when a semicolon was omitted from the end of an expression statement and the following expression could be considered a suffix expression:

    x = () => {}
    (y)

    This was incorrectly parsed as (x = () => {})(y); instead of x = () => {}; y;. With this release, this edge case should now be parsed correctly.

  • Add new neutral platform to help text (#695)

    The new --platform=neutral API option that was added in the previous release was incorrectly not listed in the CLI help text for the platform feature. This omission has been fixed. The fix was contributed by @hardfist.

v0.8.33

18 Jan 02:11
Compare
Choose a tag to compare
  • Fix esbuild potentially exiting early during incremental rebuilds

    The change in the previous release to make calling stop() optional caused a regression for incremental rebuilds where calling rebuild() could potentially cause the process to exit early before the incremental rebuild is completed. This is because the implementation of rebuild() was missing a reference count to track that the service is now temporarily needed again. This omission was an oversight, and has now been fixed.

  • Fix using the new sourcesContent option with the transform API (#682)

    Due to an oversight, the sourcesContent: false option that was added in version 0.8.27 didn't work with the JavaScript transform API. This was unintentional and has been fixed. This fix was contributed by @jschaf.

  • Insert the object spread shim in constructor methods after the super() call (#678)

    This fixes an issue with the transform for object spread to older compile targets. Previously the following code would be transformed to code that crashes when run if the compile target is es2017 or lower:

    class Derived extends Base {
      prop = null;
      constructor({ ...args }) {
        super(args);
      }
    }

    This code was incorrectly compiled to something like this, which will throw ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor:

    class Derived extends Base {
      constructor(_a) {
        __publicField(this, "prop", null);
        var args = __rest(_a, []);
        super(args);
      }
    }

    With this release, it will now be compiled to something like this instead:

    class Derived extends Base {
      constructor(_a) {
        var args = __rest(_a, []);
        super(args);
        __publicField(this, "prop", null);
      }
    }
  • Add the --platform=neutral API option (#674)

    There are currently two platform values: browser (the default) and node. These settings are a convenient way to configure multiple defaults for other API options for maximum compatibility. However, some users want to configure everything themselves so esbuild does not assume any platform-specific behavior. In this case you can now use --platform=neutral to disable platform-specific default values. Note that this means if you want to use npm-style packages you will have to configure a main field yourself with something like --main-fields=main.

  • Provide minified and non-minified versions of in-browser API library (#616)

    The in-browser JavaScript API libraries for esbuild are in the esbuild-wasm package. There are two: esbuild-wasm/lib/browser.js in UMD format and esbuild-wasm/esm/browser.js in ESM format. Previously these were minified since they contain a large string of JavaScript that cannot be minified by other tools. Now they are no longer minified, and there are new minified versions available at esbuild-wasm/lib/browser.min.js and esbuild-wasm/esm/browser.min.js.

v0.8.32

14 Jan 05:23
Compare
Choose a tag to compare
  • Calling stop() on the JavaScript API is now optional (#656)

    The JavaScript implementation of esbuild's API now calls unref() internally so node will now exit even if the internal long-lived esbuild process is still running. You should no longer need to explicitly call stop() on the service returned by startService(), which simplifies service lifetime management. This feature was contributed by @SalvatorePreviti.

  • Fix bug in metafile path generation (#662)

    Certain import path metadata in the JSON file generated by the --metafile setting could be incorrect in scenarios with code splitting active and multiple entry points in different subdirectories. The incorrect paths referred to cross-chunk imports of other generated code splitting chunks and were incorrectly relative to the subdirectory inside the output directory instead of relative to the output directory itself. This issue has been fixed.

  • Add kind to import paths in metafile JSON (#655)

    The --metafile flag generates build metadata in JSON format describing the input and output files in the build. Previously import path objects only had a path property. With this release, they now also have a kind property that describes the way the file was imported. The value is a string that is equal to one of the following values:

    For JavaScript files:

    • import-statement
    • require-call
    • dynamic-import
    • require-resolve

    For CSS files:

    • import-rule
    • url-token
  • Add support for TypeScript 4.2 syntax

    Most of the new features included in the TypeScript 4.2 beta announcement are type system features that don't apply to esbuild. But there's one upcoming feature that adds new syntax: abstract construct signatures. They look like this:

    let Ctor: abstract new () => HasArea = Shape;

    This new syntax can now be parsed by esbuild.

  • Add detail to errors and warnings (#654)

    Errors and warnings returned by the JavaScript and Go APIs now have a detail property which contains the original error. This is relevant if a custom JavaScript exception is thrown or a custom Go error is returned from inside a plugin callback.

  • Disable code warnings inside node_modules directories even with plugins (#666)

    Some of the warnings that esbuild generates exist to point out suspicious looking code that is likely a bug. An example is typeof x == 'null' since the typeof operator never generates the string null. Arguably these warnings belong in a linter instead of in esbuild since esbuild is a bundler, but I figured that some warnings about obviously broken code would still be helpful because many people don't run linters. It's part of my quest to improve software quality. And these warnings have caught real bugs in published code so they aren't meaningless. The warning must be considered very unlikely to be a false positive to be included.

    A change was added in version 0.7.4 to exclude files inside node_modules directories from these warnings. Even if the warnings flag a real bug, the warning is frustrating as a user because it's mostly non-actionable. The only resolution other than turning off warnings is to file an issue with the package, since code in published packages is immutable.

    However, since then the plugin API has been released and this behavior didn't apply if the import path was resolved by a plugin. It only applied if the import path was resolved by esbuild itself. That problem is fixed in this release. Now these warnings will be omitted from any file with node_modules in its path, even if the path originated from a plugin.

  • Remove the warning about self-assignment (#666)

    This warning was added in version 0.8.11 and warns about self-assignment such as x = x. The rationale is that this is likely a copy/paste error. However, it triggers too often for cross-compiled TypeScript code so the false positive rate is too high. The warning has now been removed.

  • Disable constant folding for the ?: operator when not minifying (#657)

    When minification is not enabled, the ?: operator will now no longer be simplified if the condition evaluates to true or false. This could result in slower builds in certain cases because esbuild may now scan more files unnecessarily during bundling. This change was made because of a user request.

v0.8.31

07 Jan 02:04
Compare
Choose a tag to compare
  • Fix minification issue from previous release (#648)

    The minification optimization to omit certain continue and return statements when it's implied by control flow in version 0.8.29 caused a regression when the branch condition uses a hoisted function:

    if (fn()) return;
    ...
    function fn() {}

    In that case, transforming the code by inverting the condition and moving the following statements inside the branch is not valid because the function is no longer hoisted to above the branch condition. This release fixes the regression by avoiding this optimization in cases like this.

  • Add the option --sourcemap=both (#650)

    This new option puts the generated source map both an inline //# sourceMappingURL= data URL comment inside the output file and in an external file next to the output file. Using it is also possible with the transform API, which will cause it to return both an inline data URL comment in the code value and the source map JSON in the map value.

  • Tree-shake unused code with --format=iife (#639)

    When the output format is IIFE (which wraps the code in an immediately-invoked function expression), esbuild now assumes that it's safe to remove unused code. This is an assumption that esbuild always makes when bundling but that esbuild previously didn't make when not bundling. Now esbuild will remove code even when not bundling as long as the output format is IIFE.

    This is only done for the IIFE output format because people are currently using the other formats to compile "partial modules", meaning they expect to be able to append code to esbuild's output and have that appended code be able to reference unused code inside esbuild's output. So it's not safe for esbuild to remove unused code in those cases. The IIFE output format wraps everything in a closure so unused code is not exposed to the module-level scope. Appended code will not be able to access unused code inside the closure so that means it's safe to remove.

v0.8.30

06 Jan 10:29
Compare
Choose a tag to compare
  • Fix @jsx and @jsxFrag comments without trailing spaces

    The --jsx-factory and --jsx-fragment settings can be set on a per-file basis using // @jsx name or // @jsxFrag name comments. Comments of the form /* @jsx name */ or /* @jsxFrag name */ will also work. However, there was a bug where comments of the form /* @jsx name*/ or /* @jsxFrag name*/ (a multi-line comment without a trailing space at the end) did not work. This bug has been fixed, and you now no longer need a trailing space for multi-line comments.

  • Minification improvements

    • The expression before a switch statement is now folded into the value. This means fn(); switch (x) { ... } turns into switch (fn(), x) { ... }.

    • Uses of === and !== are converted to == or != if the types of both sides can easily be statically determined. This means (x & 1) === 0 turns into (x & 1) == 0.

    • Equality comparisons are removed if both sides are boolean and one side is a constant. This means !x === true turns into !x.

    • Certain unary and binary operators are now removed if unused. This means if (a() === b()) {} turns into a(), b();.

    • The comma operator is now extracted from certain expressions. This means (a, b) + c turns into a, b + c.

    • Minification now takes advantage of the left-associativity of certain operators. This means a && (b && c) turns into a && b && c.

    • Computed properties that are strings now become no longer computed. This means {['a']: b} turns into {a: b} and class { ['a'] = b } turns into class { a = b }.

    • Repeated if-jump statements are now merged. This means if (a) break; if (b) break; turns into if (a || b) break;.

  • Fix issues with nested source maps (#638)

    A nested source map happens when an input file has a valid //# sourceMappingURL= comment that points to a valid source map file. In that case, esbuild will read that source map and use it to map back to the original source code from the generated file. This only happens if you enable source map generation in esbuild via --sourcemap. This release fixes the following issues:

    • Generated source maps were incorrect when an input file had a nested source map and the input source map had more than one source file. This regression was introduced by an optimization in version 0.8.25 that parallelizes the generation of certain internal source map data structures. The index into the generated sources array was incorrectly incremented by 1 for every input file instead of by the number of sources in the input source map. This issue has been fixed and now has test coverage.

    • Generated source maps were incorrect when an input file had a nested source map, the file starts with a local variable, the previous file ends with a local variable of that same type, and the input source map is missing a mapping at the start of the file. An optimization was added in version 0.7.18 that splices together local variable declarations from separate files when they end up adjacent to each other in the generated output file (i.e. var a=0;var b=2; becomes var a=0,b=2; when a and b are in separate files). The source map splicing was expecting a mapping at the start of the file and that isn't necessarily the case when using nested source maps. The optimization has been disabled for now to fix source map generation, and this specific case has test coverage.

v0.8.29

02 Jan 12:25
Compare
Choose a tag to compare
  • Allow entry points outside of the outbase directory (#634)

    When esbuild generates the output path for a bundled entry point, it computes the relative path from the outbase directory to the input entry point file and then joins that relative path to the output directory. For example, if there are two entry points src/pages/home/index.ts and src/pages/about/index.ts, the outbase directory is src, and the output directory is out, the output directory will contain out/pages/home/index.js and out/pages/about/index.js.

    However, this means that the outbase directory is expected to contain all entry point files (even implicit entry point files from import() expressions). If an entry point isn't under the outbase directory then esbuild will to try to write the output file outside of the output directory, since the path of the entry point relative to outbase will start with ../ which is then joined to the output directory. This is unintentional. All output files are supposed to be written inside of the output directory.

    This release fixes the problem by creating a directory with the name _.._ in the output directory for output file paths of entry points that are not inside the outbase directory. So if the previous example was bundled with an outbase directory of temp, the output directory will contain out/_.._/pages/home/index.js and out/_.._/pages/about/index.js. Doing this instead of stripping the leading ../ off the relative path is necessary to avoid collisions between different entry points with the same path suffix.

  • Minification improvements

    This release contains the following minification improvements:

    • Expressions of the form !(a == b) are now converted to a != b. This also applies similarly for the other three equality operators.

    • A trailing continue; statement inside the body of a loop is now removed.

    • Minification can now omit certain continue and return statements when it's implied by control flow:

      // Before minification
      function fn() {
        if (a) return;
        while (b) {
          if (c) continue;
          d();
        }
      }
      
      // After minification
      function fn() {
        if (!a)
          for (; b; )
            c || d();
      }
    • Certain single-use variables are now inlined if the use directly follows the variable:

      // Before minification
      let result = fn();
      let callback = result.callback;
      return callback.call(this);
      // After minification
      return fn().callback.call(this);

      This transformation is only done when it's safe to do so. The safety conditions are complex but at a high level, an expression cannot be reordered past another expression if either of them could possibly have side effects.

v0.8.28

31 Dec 11:17
Compare
Choose a tag to compare
  • Add a --summary flag that prints helpful information after a build (#631)

    Normally esbuild's CLI doesn't print anything after doing a build if nothing went wrong. This allows esbuild to be used as part of a more complex chain of tools without the output cluttering the terminal. However, sometimes it is nice to have a quick overview in your terminal of what the build just did. You can now add the --summary flag when using the CLI and esbuild will print a summary of what the build generated. It looks something like this:

    $ ./esbuild --summary --bundle src/Three.js --outfile=build/three.js --sourcemap
    
      build/three.js      1.0mb ⚠️
      build/three.js.map  1.8mb
    
    ⚡ Done in 43ms
    
  • Keep unused imports in TypeScript code in one specific case (#604)

    The official TypeScript compiler always removes imported symbols that aren't used as values when converting TypeScript to JavaScript. This is because these symbols could be types and not removing them could result in a run-time module instantiation failure because of missing exports. This even happens when the tsconfig.json setting "importsNotUsedAsValues" is set to "preserve". Doing this just keeps the import statement itself but confusingly still removes the imports that aren't used as values.

    Previously esbuild always exactly matched the behavior of the official TypeScript compiler regarding import removal. However, that is problematic when trying to use esbuild to compile a partial module such as when converting TypeScript to JavaScript inside a file written in the Svelte programming language. Here is an example:

    <script lang="ts">
      import Counter from './Counter.svelte';
      export let name: string = 'world';
    </script>
    <main>
      <h1>Hello {name}!</h1>
      <Counter />
    </main>

    The current Svelte compiler plugin for TypeScript only provides esbuild with the contents of the <script> tag so to esbuild, the import Counter appears to be unused and is removed.

    In this release, esbuild deliberately deviates from the behavior of the official TypeScript compiler if all of these conditions are met:

    • The "importsNotUsedAsValues" field in tsconfig.json must be present and must not be set to "remove". This is necessary because this is the only case where esbuild can assume that all imports are values instead of types. Any imports that are types will cause a type error when the code is run through the TypeScript type checker. To import types when the importsNotUsedAsValues setting is active, you must use the TypeScript-specific import type syntax instead.

    • You must not be using esbuild as a bundler. When bundling, esbuild needs to assume that it's not seeing a partial file because the bundling process requires renaming symbols to avoid cross-file name collisions.

    • You must not have identifier minification enabled. It's useless to preserve unused imports in this case because referencing them by name won't work anyway. And keeping the unused imports would be counter-productive to minification since they would be extra unnecessary data in the output file.

    This should hopefully allow esbuild to be used as a TypeScript-to-JavaScript converter for programming languages such as Svelte, at least in many cases. The build pipeline in esbuild wasn't designed for compiling partial modules and this still won't be a fully robust solution (e.g. some variables may be renamed to avoid name collisions in rare cases). But it's possible that these cases are very unlikely to come up in practice. Basically this change to keep unused imports in this case should be useful at best and harmless at worst.