github evanw/esbuild v0.11.7

latest releases: v0.20.2, v0.20.1, v0.20.0...
3 years ago
  • Fix incorrect chunk reference with code splitting, css, and dynamic imports (#1125)

    This release fixes a bug where when you use code splitting, CSS imports in JS, and dynamic imports all combined, the dynamic import incorrectly references the sibling CSS chunk for the dynamic import instead of the primary JS chunk. In this scenario the entry point file corresponds to two different output chunks (one for CSS and one for JS) and the wrong chunk was being picked. This bug has been fixed.

  • Split apart tree shaking and code splitting (#1123)

    The original code splitting algorithm allowed for files to be split apart and for different parts of the same file to end up in different chunks based on which entry points needed which parts. This was done at the same time as tree shaking by essentially performing tree shaking multiple times, once per entry point, and tracking which entry points each file part is live in. Each file part that is live in at least one entry point was then assigned to a code splitting chunk with all of the other code that is live in the same set of entry points. This ensures that entry points only import code that they will use (i.e. no code will be downloaded by an entry point that is guaranteed to not be used).

    This file-splitting feature has been removed because it doesn't work well with the recently-added top-level await JavaScript syntax, which has complex evaluation order rules that operate at file boundaries. File parts now have a single boolean flag for whether they are live or not instead of a set of flags that track which entry points that part is reachable from (reachability is still tracked at the file level).

    However, this change appears to have introduced some subtly incorrect behavior with code splitting because there is now an implicit dependency in the import graph between adjacent parts within the same file even if the two parts are unrelated and don't reference each other. This is due to the fact each entry point that references one part pulls in the file (but not the whole file, only the parts that are live in at least one entry point). So liveness must be fully computed first before code splitting is computed.

    This release splits apart tree shaking and code splitting into two separate passes, which fixes certain cases where two generated code splitting chunks ended up each importing symbols from the other and causing a cycle. There should hopefully no longer be cycles in generated code splitting chunks.

  • Make this work in static class fields in TypeScript files

    Currently this is mis-compiled in static fields in TypeScript files if the useDefineForClassFields setting in tsconfig.json is false (the default value):

    class Foo {
      static foo = 123
      static bar = this.foo
    }
    console.log(Foo.bar)

    This is currently compiled into the code below, which is incorrect because it changes the value of this (it's supposed to refer to Foo):

    class Foo {
    }
    Foo.foo = 123;
    Foo.bar = this.foo;
    console.log(Foo.bar);

    This was an intentionally unhandled case because the TypeScript compiler doesn't handle this either (esbuild's currently incorrect output matches the output from the TypeScript compiler, which is also currently incorrect). However, the TypeScript compiler might fix their output at some point in which case esbuild's behavior would become problematic.

    So this release now generates the correct output:

    const _Foo = class {
    };
    let Foo = _Foo;
    Foo.foo = 123;
    Foo.bar = _Foo.foo;
    console.log(Foo.bar);

    Presumably the TypeScript compiler will be fixed to also generate something like this in the future. If you're wondering why esbuild generates the extra _Foo variable, it's defensive code to handle the possibility of the class being reassigned, since class declarations are not constants:

    class Foo {
      static foo = 123
      static bar = () => Foo.foo
    }
    let bar = Foo.bar
    Foo = { foo: 321 }
    console.log(bar())

    We can't just move the initializer containing Foo.foo outside of the class body because in JavaScript, the class name is shadowed inside the class body by a special hidden constant that is equal to the class object. Even if the class is reassigned later, references to that shadowing symbol within the class body should still refer to the original class object.

  • Various fixes for private class members (#1131)

    This release fixes multiple issues with esbuild's handling of the #private syntax. Previously there could be scenarios where references to this.#private could be moved outside of the class body, which would cause them to become invalid (since the #private name is only available within the class body). One such case is when TypeScript's useDefineForClassFields setting has the value false (which is the default value), which causes class field initializers to be replaced with assignment expressions to avoid using "define" semantics:

    class Foo {
      static #foo = 123
      static bar = Foo.#foo
    }

    Previously this was turned into the following code, which is incorrect because Foo.#foo was moved outside of the class body:

    class Foo {
      static #foo = 123;
    }
    Foo.bar = Foo.#foo;

    This is now handled by converting the private field syntax into normal JavaScript that emulates it with a WeakMap instead.

    This conversion is fairly conservative to make sure certain edge cases are covered, so this release may unfortunately convert more private fields than previous releases, even when the target is esnext. It should be possible to improve this transformation in future releases so that this happens less often while still preserving correctness.

Don't miss a new esbuild release

NewReleases is sending notifications on new releases.