Skip to content

Releases: evanw/esbuild

v0.8.57

08 Mar 04:33
Compare
Choose a tag to compare
  • Fix overlapping chunk names when code splitting is active (#928)

    Code splitting chunks use a content hash in their file name. This is good for caching because it means the file name is guaranteed to change if the chunk contents change, and the file name is guaranteed to stay the same if the chunk contents don't change (e.g. someone only modifies a comment). However, using a pure content hash can cause bugs if two separate chunks end up with the same contents.

    A high-level example would be two identical copies of a library being accidentally collapsed into a single copy. While this results in a smaller bundle, this is incorrect because each copy might need to have its own state and so must be represented independently in the bundle.

    This release fixes this issue by mixing additional information into the file name hash, which is no longer a content hash. The information includes the paths of the input files as well as the ranges of code within the file that are included in the chunk. File paths are used because they are a stable file identifier, but the relative path is used with / as the path separator to hopefully eliminate cross-platform differences between Unix and Windows.

  • Fix --keep-names for lowered class fields

    Anonymous function expressions used in class field initializers are automatically assigned a .name property in JavaScript:

    class Example {
      field1 = () => {}
      static field2 = () => {}
    }
    assert(new Example().field1.name === 'field1')
    assert(Example.field2.name === 'field2')

    This usually doesn't need special handling from esbuild's --keep-names option because esbuild doesn't modify field names, so the .name property will not change. However, esbuild will relocate the field initializer if the configured language target doesn't support class fields (e.g. --target=es6). In that case the .name property wasn't preserved even when --keep-names was specified. This bug has been fixed. Now the .name property should be preserved in this case as long as you enable --keep-names.

  • Enable importing certain data URLs in CSS and JavaScript

    You can now import data URLs of type text/css using a CSS @import rule and import data URLs of type text/javascript and application/json using a JavaScript import statement. For example, doing this is now possible:

    import 'data:text/javascript,console.log("hello!");';
    import _ from 'data:application/json,"world!"';

    This is for compatibility with node which supports this feature natively. Importing from a data URL is sometimes useful for injecting code to be evaluated before an external import without needing to generate a separate imported file.

v0.8.56

05 Mar 12:09
Compare
Choose a tag to compare
  • Fix a discrepancy with esbuild's tsconfig.json implementation (#913)

    If a tsconfig.json file contains a "baseUrl" value and "extends" another tsconfig.json file that contains a "paths" value, the base URL used for interpreting the paths should be the overridden value. Previously esbuild incorrectly used the inherited value, but with this release esbuild will now use the overridden value instead.

  • Work around the Jest testing framework breaking node's Buffer API (#914)

    Running esbuild within a Jest test fails because Jest causes Buffer instances to not be considered Uint8Array instances, which then breaks the code esbuild uses to communicate with its child process. More info is here: jestjs/jest#4422. This release contains a workaround that copies each Buffer object into a Uint8Array object when this invariant is broken. That should prevent esbuild from crashing when it's run from within a Jest test.

  • Better handling of implicit main fields in package.json

    If esbuild's automatic main vs. module detection is enabled for package.json files, esbuild will now use index.js as an implicit main field if the main field is missing but index.js is present. This means if a package.json file only contains a module field but not a main field and the package is imported using both an ESM import statement and a CommonJS require call, the index.js file will now be picked instead of the file in the module field.

v0.8.55

04 Mar 05:12
Compare
Choose a tag to compare
  • Align more closely with node's default import behavior for CommonJS (#532)

    Note: This could be considered a breaking change or a bug fix depending on your point of view.

    Importing a CommonJS file into an ESM file does not behave the same everywhere. Historically people compiled their ESM code into CommonJS using Babel before ESM was supported natively. More recently, node has made it possible to use ESM syntax natively but to still import CommonJS files into ESM. These behave differently in many ways but one of the most unfortunate differences is how the default export is handled.

    When you import a normal CommonJS file, both Babel and node agree that the value of module.exports should be stored in the ESM import named default. However, if the CommonJS file used to be an ESM file but was compiled into a CommonJS file, Babel will set the ESM import named default to the value of the original ESM export named default while node will continue to set the ESM import named default to the value of module.exports. Babel detects if a CommonJS file used to be an ESM file by the presence of the exports.__esModule = true marker.

    This is unfortunate because it means there is no general way to make code work with both ecosystems. With Babel the code import * as someFile from './some-file' can access the original default export with someFile.default but with node you need to use someFile.default.default instead. Previously esbuild followed Babel's approach but starting with this release, esbuild will now try to use a blend between the Babel and node approaches.

    This is the new behavior: importing a CommonJS file will set the default import to module.exports in all cases except when module.exports.__esModule && "default" in module.exports, in which case it will fall through to module.exports.default. In other words: in cases where the default import was previously undefined for CommonJS files when exports.__esModule === true, the default import will now be module.exports. This should hopefully keep Babel cross-compiled ESM code mostly working but at the same time now enable some node-oriented code to start working.

    If you are authoring a library using ESM but shipping it as CommonJS, the best way to avoid this mess is to just never use default exports in ESM. Only use named exports with names other than default.

  • Fix bug when ESM file has empty exports and is converted to CommonJS (#910)

    A file containing the contents export {} is still considered to be an ESM file even though it has no exports. However, if a file containing this edge case is converted to CommonJS internally during bundling (e.g. when it is the target of require()), esbuild failed to mark the exports symbol from the CommonJS wrapping closure as used even though it is actually needed. This resulted in an output file that crashed when run. The exports symbol is now considered used in this case, so the bug has been fixed.

  • Avoid introducing this for imported function calls

    It is possible to import a function exported by a CommonJS file into an ESM file like this:

    import {fn} from './cjs-file.js'
    console.log(fn())

    When you do this, esbuild currently transforms your code into something like this:

    var cjs_file = __toModule(require("./cjs-file.js"));
    console.log(cjs_file.fn());

    However, doing that changes the value of this observed by the export fn. The property access cjs_file.fn is in the syntactic "call target" position so the value of this becomes the value of cjs_file. With this release, esbuild will now use a different syntax in this case to avoid passing cjs_file as this:

    var cjs_file = __toModule(require("./cjs-file.js"));
    console.log((0, cjs_file.fn)());

    This change in esbuild mirrors a similar recent TypeScript compiler change, and also makes esbuild more consistent with Babel which already does this transformation.

v0.8.54

03 Mar 00:18
Compare
Choose a tag to compare
  • 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.

v0.8.53

27 Feb 00:42
Compare
Choose a tag to compare
  • Support chunk and asset file name templates (#733, #888)

    This release introduces the --chunk-names= and --asset-names= flags. These flags let you customize the output paths for chunks and assets within the output directory. Each output path is a template and currently supports these placeholders:

    • [name]: The original name of the file. This will be chunk for chunks and will be the original file name (without the extension) for assets.
    • [hash]: The content hash of the file. This is not necessarily stable across different esbuild versions but will be stable within the same esbuild version.

    For example, if you want to move all chunks and assets into separate subdirectories, you could use --chunk-names=chunks/[name]-[hash] and --asset-names=assets/[name]-[hash]. Note that the path template should not include the file extension since the file extension is always automatically added to the end of the path template.

    Additional name template features are planned in the future including a [dir] placeholder for the relative path from the outbase directory to the original input directory as well as an --entry-names= flag, but these extra features have not been implemented yet.

  • Handle this in class static field initializers (#885)

    When you use this in a static field initializer inside a class statement or expression, it references the class object itself:

    class Foo {
      static Bar = class extends this {
      }
    }
    assert(new Foo.Bar() instanceof Foo)

    This case previously wasn't handled because doing this is a compile error in TypeScript code. However, JavaScript does allow this so esbuild needs to be able to handle this. This edge case should now work correctly with this release.

  • Do not warn about dynamic imports when .catch() is detected (#893)

    Previously esbuild avoids warning about unbundled import() expressions when using the try { await import(_) } pattern, since presumably the try block is there to handle the run-time failure of the import() expression failing. This release adds some new patterns that will also suppress the warning: import(_).catch(_), import(_).then(_).catch(_), and import(_).then(_, _).

  • CSS namespaces are no longer supported

    CSS namespaces are a weird feature that appears to only really be useful for styling XML. And the world has moved on from XHTML to HTML5 so pretty much no one uses CSS namespaces anymore. They are also complicated to support in a bundler because CSS namespaces are file-scoped, which means:

    • Default namespaces can be different in different files, in which case some default namespaces would have to be converted to prefixed namespaces to avoid collisions.

    • Prefixed namespaces from different files can use the same name, in which case some prefixed namespaces would need to be renamed to avoid collisions.

    Instead of implementing all of that for an extremely obscure feature, CSS namespaces are now just explicitly not supported. The code to handle @namespace has been removed from esbuild. This will likely not affect anyone, especially because bundling code using CSS namespaces with esbuild didn't even work correctly in the first place.

v0.8.52

25 Feb 05:10
Compare
Choose a tag to compare
  • Fix a concurrent map write with the --inject: feature (#878)

    This release fixes an issue where esbuild could potentially crash sometimes with a concurrent map write when using injected files and entry points that were neither relative nor absolute paths. This was an edge case where esbuild's low-level file subsystem was being used without being behind a mutex lock. This regression was likely introduced in version 0.8.21. The cause of the crash has been fixed.

  • Provide kind to onResolve plugins (#879)

    Plugins that add onResolve callbacks now have access to the kind parameter which tells you what kind of import is being resolved. It will be one of the following values:

    • "entry-point" in JS (api.ResolveEntryPoint in Go)

      An entry point provided by the user

    • "import-statement" in JS (api.ResolveJSImportStatement in Go)

      A JavaScript import or export statement

    • "require-call" in JS (api.ResolveJSRequireCall in Go)

      A JavaScript call to require(...) with a string argument

    • "dynamic-import" in JS (api.ResolveJSDynamicImport in Go)

      A JavaScript import(...) expression with a string argument

    • "require-resolve" in JS (api.ResolveJSRequireResolve in Go)

      A JavaScript call to require.resolve(...) with a string argument

    • "import-rule" in JS (api.ResolveCSSImportRule in Go)

      A CSS @import rule

    • "url-token" in JS (api.ResolveCSSURLToken in Go)

      A CSS url(...) token

    These values are pretty much identical to the kind field in the JSON metadata file.

v0.8.51

23 Feb 10:18
Compare
Choose a tag to compare
  • The stderr log format now contains line numbers after file names (#865)

    Error messages in stderr now have a line and column number after the file name.

    Before:

     > 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,
            ╵     ~~~~~~~~~~~
    

    After:

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

    This should make log messages slightly easier to parse if you want to parse stderr instead of using esbuild's API. Previously you needed a multi-line regular expression to get the line number, but now that the line number is duplicated in two places you should only need a single-line regular expression.

    Note that this is still the hacky way to get error information and is potentially unstable, since it will break if the log format changes. Log messages are mainly intended for humans. The straightforward and stable way to do this is still to use esbuild's API, which returns log messages as an array of objects.

  • Allow --define with import.meta

    The --define feature lets you replace specific identifiers and member expression chains with compile-time constants. However, it previously didn't work with import.meta because this is a special case in the grammar. The import keyword is not actually an identifier expression. This distinction isn't helpful though, and it's not unreasonable to want to use the --define feature to replace import.meta properties too.

    With this release, it's now possible to use e.g. --define:import.meta.foo=123 to replace specific properties accessed off of the import.meta object as well as to use e.g. --define:import.meta={\"foo\":123} to substitute the entire import.meta expression with something else.

  • Fix a race condition with multiple injected files (#871)

    Using multiple injected files could cause a data race that trips Go's race detector. The data race has been fixed in this release. The fix was contributed by @Deleplace.

  • Change --serve behavior to serve on all interfaces (#866)

    The default address for the --serve flag has changed from 127.0.0.1 (serve on the loopback interface) to 0.0.0.0 (serve on all interfaces). You can still manually specify either one using --serve=127.0.0.1:8000 or --serve=0.0.0.0:8000. This just changes the default behavior that happens when you pass --serve with no host address (or when you just use the --servedir= flag without --serve=).

    In addition, you can now also specify an IPv6 address. Previously there was a parsing issue that prevented this. For example, you can pass --serve=[::1]:8000 to serve on the loopback interface and --serve=[::]:8000 to serve on all interfaces.

  • Change the import resolution rules of absolute paths (#862)

    Previously absolute paths were considered to be pre-resolved by the resolver (in contrast to relative and package paths, which need to be converted to an absolute path). This meant that absolute paths which did not actually exist caused a failure in the loader when it tried to load the path instead of in the resolver when it tried to resolve the path.

    With the previous change in version 0.8.47 to support removing URL query and/or hash parameters from the path, path resolution can now be run multiple times. If path resolution fails and the path contains a ? and/or #, path resolution is re-run with the URL query/hash parameters removed. It is problematic to consider absolute paths to be pre-resolved because it means that paths containing query/hash parameters make the loader try to load the wrong path, and do not run the resolver again with the parameter suffix removed.

    In this release, esbuild will now validate absolute paths in the resolver. So invalid paths will now fail in the resolver and retry without the parameter suffix instead of failing in the loader, which correctly handles a parameter suffix on absolute paths. In addition, this release now handles implicit file extensions on absolute paths. This makes esbuild a more accurate copy of node's module resolution algorithm, which does this as well.

  • Output files in metafile now have entryPoint (#711)

    There is now an optional entryPoint property on each output file in the JSON metadata file generated with the --metafile= flag. It is only present for output files that are the bundled results of entry point files, and contains the path name of the corresponding input entry point file. This property is not present on other kinds of output files (e.g. code splitting chunks). This feature was contributed by @remorses.

v0.8.50

21 Feb 10:42
Compare
Choose a tag to compare
  • Using direct eval now pulls in module and exports

    Use of direct eval forces the file to become a CommonJS module and disables dead code elimination in the entire file. The CommonJS closure is necessary to avoid name collisions with other modules, since eval means symbols in the file can no longer be renamed to avoid collisions.

    However, the CommonJS module and exports variables that are arguments to the closure previously weren't considered to be used in this scenario, meaning they may be omitted as dead code for size reasons. This could cause code inside eval to behave incorrectly. Now use of direct eval automatically counts as a use of both module and exports so these variables should now always be present in this case.

  • Always remove all "use asm" directives (#856)

    The asm.js subset of JavaScript has complicated validation rules that are triggered by this directive. The parser and code generator in esbuild was not designed with asm.js in mind and round-tripping asm.js code through esbuild will very likely cause it to no longer validate as asm.js. When this happens, V8 prints a warning and people don't like seeing the warning. The warning looks like this:

    (node:58335) V8: example.js:3 Invalid asm.js: Unexpected token
    (Use `node --trace-warnings ...` to show where the warning was created)
    

    I am deliberately not attempting to preserve the validity of asm.js code because it's a complicated legacy format and it's obsolete now that WebAssembly exists. By removing all "use asm" directives, the code will just become normal JavaScript and work fine without generating a warning.

  • Fix a variable hoisting edge case (#857)

    It is allowed to use a nested var hoisted declaration with the same name as a top-level function declaration. In that case the two symbols should merge and be treated as the same symbol:

    async function x() {}
    {
      var x;
    }

    The parser previously allowed this for regular functions but not for async or generator functions. Now with this release, this behavior is also allowed for these special kinds of functions too.

  • Remove empty CSS rules when minifying (#851)

    Empty rules with no content such as div {} are now removed when CSS is minified. This change was contributed by @susiwen8.

v0.8.49

19 Feb 08:36
Compare
Choose a tag to compare
  • Work around a problem with pnpm and NODE_PATH (#816)

    In version 0.8.43, esbuild added support for node's NODE_PATH environment variable which contains a list of global folders to use during path resolution. However, this causes a problem when esbuild is installed with pnpm, an alternative JavaScript package manager. Specifically pnpm adds a bogus path to NODE_PATH that doesn't exist but that has a file as a parent directory. Previously this caused esbuild to fail with the error not a directory. Now with this release, esbuild will ignore this bogus path instead of giving an error.

  • Add more names to the global no-side-effect list (#842)

    This release adds almost all known globals from the browser and node to the list of known globals. Membership in this list means accessing the global is assumed to have no side effects. That means tree shaking is allowed to remove unused references to these globals. For example, since HTMLElement is now in the known globals list, the following class will now be removed when unused:

    class MyElement extends HTMLElement {
    }

    In addition, membership in this list relaxes ordering constraints for the purposes of minification. It allows esbuild to reorder references to these globals past other expressions. For example, since console.log is now in the known globals list, the following simplification will now be performed during minification:

    // Original
    export default (a) => {
      if (a) console.log(b); else console.log(c)
    }
    
    // Minified (previous release)
    export default (a) => {
      a ? console.log(b) : console.log(c);
    };
    
    // Minified (this release)
    export default (a) => {
      console.log(a ? b : c);
    };

    This transformation is not generally safe because the console.log property access might evaluate code which could potentially change the value of a. This is only considered safe in this instance because console.log is now in the known globals list.

    Note that membership in this list does not say anything about whether the function has side effects when called. It only says that the identifier has no side effects when referenced. So console.log() is still considered to have side effects even though console.log is now considered to be free of side effects.

    The following globals are not on the list and are considered to have side effects:

    • scrollX
    • scrollY
    • innerWidth
    • innerHeight
    • pageXOffset
    • pageYOffset
    • localStorage
    • sessionStorage

    Accessing layout-related properties can trigger a layout and accessing storage-related properties can throw an exception if certain privacy settings are enabled. Both of these behaviors are considered side effects.

  • Fix a TypeScript parser regression (#846)

    Restrictions on array and object destructuring patterns in the previous release introduced a regression where arrays or objects in TypeScript code could fail to parse if they were wrapped in a double layer of parentheses. This was due to the speculative parsing of arrow function arguments. The regression has been fixed.

  • Add the Go-specific cli.ParseServeOptions() API (#834)

    This API is specifically for people trying to emulate esbuild's CLI in Go. It lets you share esbuild's logic of parsing the --serve= and --servedir= flags. Use it like this:

    serveOptions, args, err := cli.ParseServeOptions([]string{
      "--serve=8000",
    })
    buildOptions, err := cli.ParseBuildOptions(args)
    result := api.Serve(serveOptions, buildOptions)

v0.8.48

18 Feb 14:17
Compare
Choose a tag to compare
  • Fix some parsing edge cases (#835)

    This release fixes the following edge cases:

    • Code using in inside a template literal inside a for loop initializer such as for (let x = `${a in b ? '0' : '1'}`; false; ); is now allowed. Previously the in operator was incorrectly considered to be part of a for-in loop.

    • In TypeScript, it's not valid to have a newline in between the async and the < tokens inside the code async <T>() => {}. Previously this was incorrectly treated as an asynchronous arrow function expression.

    • Code of the form new async() must construct the function called async. Previously this was incorrectly treated as new (async())() instead due to the speculative parsing of asynchronous arrow functions.

    • Code of the form new async () => {} must not be allowed. Previously this was incorrectly allowed since the speculative parsing of asynchronous arrow functions did not check the precedence level.

    • It's not valid to start an initializer expression in a for-of loop with the token let such as for (let.foo of bar) {}. This is now forbidden. In addition, the code generator now respects this rule so for ((let.foo) of bar) {} is now printed as for ((let).foo of bar) {}.

    • Array and object binding patterns do not allow a comma after rest elements, so code such as [...a, b] = [c] is invalid. This case is correctly handled by esbuild. However, it's possible to have both an array or object binding pattern and an array or object literal on the left-hand side of a destructuring assignment such as [[...a, b].c] = [d]. In that case it should be allowed for a comma to come after the spread element in the array or object literal expression. Previously this was incorrectly treated as an error by esbuild.

    • It's technically allowed (although perhaps not ever actually useful) to call super() from within a default argument initializer like this:

      class Derived extends Base {
        constructor(arg = super()) {
        }
      }

      Previously esbuild did not permit this, which is incorrect. Doing this is now permitted.

    • It is an error to use arguments in a class field initializer such as class { x = arguments[0] }, but it is not an error to use arguments in a computed class property name such as class { [arguments[0]] = x } or inside TypeScript decorators such as class { @decorator(arguments[0]) x() {} }. Previously all of these cases were an error in esbuild, which is incorrect. Using arguments inside computed class property names and TypeScript decorators is now allowed.

    • It is not permitted to use a function declaration inside an if statement such as if (0) function f() {} in strict mode. Previously this was allowed, but this is now forbidden.

    • It is not permitted to re-declare a generator and/or asynchronous function declaration inside a block scope:

      // This is allowed
      function *a() {}
      function *a() {}
      
      // This is allowed
      function f() {
        function *b() {}
        function *b() {}
      }
      
      // This is not allowed
      {
        function *c() {}
        function *c() {}
      }

      The parser now enforces this rule.

    • Legacy octal escape sequences are octal escape sequences other than \0 with a single zero. These are forbidden in untagged template literals and in all strings in strict mode code. Previously esbuild didn't enforce this rule, but it is now enforced.

    • Technically the directive prologue is allowed to contain multiple directives, so strict mode should still be applied even if a "use strict"; directive is preceded by another directive. For example, "use \000"; "use strict"; should be a syntax error because strict mode is active. This technicality has now been implemented.

    • It is supposed to be a syntax error if a use strict directive is inside a function with a non-simple parameter list, such as (x = 1) => { 'use strict' }. Previously esbuild allowed this code, but now this code is a syntax error.

    • It is forbidden for a template literal tag to be an optional chain such as a?.b`c`. This rule is now enforced by esbuild, so code like this is now a syntax error. In addition, the code generator now avoids generating this syntax by wrapping any optional chain template literal tags in parentheses.

    • According to the standard, all code inside a class statement or expression should be in strict mode. Previously esbuild treated code inside a class as the same strict mode status as the surrounding code, but now code in a class is always interpreted as strict mode code.

    • Duplicate bindings in the same parameter list are not allowed if the parameter list isn't simple, such as in the code function f(a, [a]) {}, or if the parameter list belongs to an arrow function or a method. This rule is now enforced by esbuild's parser, so doing this is now a syntax error.

    • Array and object destructuring patterns are only valid if they are not surrounded by parentheses. Previously esbuild incorrectly allowed code such as ([]) = [] and ({}) = {}. This invalid code is now a syntax error.

    • It is now an error to use the shorthand property syntax ({yield}) inside a generator and ({await}) inside an asynchronous function. Previously those cases were incorrectly allowed.

    • A newline in between async and a method name is no longer allowed. Instead, this is a syntax error inside an object literal and a class field inside a class body.

  • Remove the local web server feature from the WebAssembly package (#836)

    This feature didn't work anyway (maybe sockets don't work with Go's WebAssembly target?) and including it added around 3mb of unnecessary extra code to the WebAssembly module file. Removing this brings the size of the WebAssembly module from around 11mb down to 8.3mb.