github evanw/esbuild v0.8.54

latest releases: v0.24.0, v0.23.1, v0.23.0...
3 years ago
  • Fix ordering issue with private class methods (#901)

    This release fixes an ordering issue with private class fields where private methods were not available inside class field initializers. The issue affected code such as the following when the compilation target was set to es2020 or lower:

    class A {
      pub = this.#priv;
      #priv() {
        return 'Inside #priv';
      }
    }
    assert(new A().pub() === 'Inside #priv');

    With this release, code that does this should now work correctly.

  • Fix --keep-names for private class members

    Normal class methods and class fields don't need special-casing with esbuild when the --keep-names option is enabled because esbuild doesn't rename property names and doesn't transform class syntax in a way that breaks method names, so the names are kept without needing to generate any additional code.

    However, this is not the case for private class methods and private class fields. When esbuild transforms these for --target=es2020 and earlier, the private class methods and private class field initializers are turned into code that uses a WeakMap or a WeakSet for access to preserve the privacy semantics. This ends up breaking the .name property and previously --keep-names didn't handle this edge case.

    With this release, --keep-names will also preserve the names of private class methods and private class fields. That means code like this should now work with --keep-names --target=es2020:

    class Foo {
      #foo() {}
      #bar = () => {}
      test() {
        assert(this.#foo.name === '#foo')
        assert(this.#bar.name === '#bar')
      }
    }
  • Fix cross-chunk import paths (#899)

    This release fixes an issue with the --chunk-names= feature where import paths in between two different automatically-generated code splitting chunks were relative to the output directory instead of relative to the importing chunk. This caused an import failure with the imported chunk if the chunk names setting was configured to put the chunks into a subdirectory. This bug has been fixed.

  • Remove the guarantee that direct eval can access imported symbols

    Using direct eval when bundling is not a good idea because esbuild must assume that it can potentially reach anything in any of the containing scopes. Using direct eval has the following negative consequences:

    • All names in all containing scopes are frozen and are not renamed during bundling, since the code in the direct eval could potentially access them. This prevents code in all scopes containing the call to direct eval from being minified or from being removed as dead code.

    • The entire file is converted to CommonJS. This increases code size and decreases performance because exports are now resolved at run-time instead of at compile-time. Normally name collisions with other files are avoided by renaming conflicting symbols, but direct eval prevents symbol renaming so name collisions are prevented by wrapping the file in a CommonJS closure instead.

    • Even with all of esbuild's special-casing of direct eval, referencing an ESM import from direct eval still doesn't necessarily work. ESM imports are live bindings to a symbol from another file and are represented by referencing that symbol directly in the flattened bundle. That symbol may use a different name which could break direct eval.

    I recently realized that the last consequence of direct eval (the problem about not being able to reference import symbols) could cause subtle correctness bugs. Specifically esbuild tries to prevent the imported symbol from being renamed, but doing so could cause name collisions that make the resulting bundle crash when it's evaluated. Two files containing direct eval that both import the same symbol from a third file but that import it with different aliases create a system of unsatisfiable naming constraints.

    So this release contains these changes to address this:

    1. Direct eval is no longer guaranteed to be able to access imported symbols. This means imported symbols may be renamed or removed as dead code even though a call to direct eval could theoretically need to access them. If you need this to work, you'll have to store the relevant imports in a variable in a nested scope and move the call to direct eval into that nested scope.

    2. Using direct eval in a file in ESM format is now a warning. This is because the semantics of direct eval are poorly understood (most people don't intend to use direct eval at all) and because the negative consequences of bundling code with direct eval are usually unexpected and undesired. Of the few valid use cases for direct eval, it is usually a good idea to rewrite your code to avoid using direct eval in the first place.

      For example, if you write code that looks like this:

      export function runCodeWithFeatureFlags(code) {
        let featureFlags = {...}
        eval(code) // "code" should be able to access "featureFlags"
      }

      you should almost certainly write the code this way instead:

      export function runCodeWithFeatureFlags(code) {
        let featureFlags = {...}
        let fn = new Function('featureFlags', code)
        fn(featureFlags)
      }

      This still gives code access to featureFlags but avoids all of the negative consequences of bundling code with direct eval.

Don't miss a new esbuild release

NewReleases is sending notifications on new releases.