Update dependency esbuild to v0.12.15 #610

Merged
konrad merged 1 commits from renovate/esbuild-0.x into main 2021-07-25 20:39:25 +00:00
Member

This PR contains the following updates:

Package Type Update Change
esbuild devDependencies minor 0.8.51 -> 0.12.15

Release Notes

evanw/esbuild

v0.12.15

Compare Source

  • Fix a bug with var() in CSS color lowering (#​1421)

    This release fixes a bug with esbuild's handling of the rgb and hsl color functions when they contain var(). Each var() token sequence can be substituted for any number of tokens including zero or more than one, but previously esbuild's output was only correct if each var() inside of rgb or hsl contained exactly one token. With this release, esbuild will now not attempt to transform newer CSS color syntax to older CSS color syntax if it contains var():

    /* Original code */
    a {
      color: hsl(var(--hs), var(--l));
    }
    
    /* Old output */
    a {
      color: hsl(var(--hs), ,, var(--l));
    }
    
    /* New output */
    a {
      color: hsl(var(--hs), var(--l));
    }
    

    The bug with the old output above happened because esbuild considered the arguments to hsl as matching the pattern hsl(h s l) which is the new space-separated form allowed by CSS Color Module Level 4. Then esbuild tried to convert this to the form hsl(h, s, l) which is more widely supported by older browsers. But this substitution doesn't work in the presence of var(), so it has now been disabled in that case.

v0.12.14

Compare Source

  • Fix the file loader with custom namespaces (#​1404)

    This fixes a regression from version 0.12.12 where using a plugin to load an input file with the file loader in a custom namespace caused esbuild to write the contents of that input file to the path associated with that namespace instead of to a path inside of the output directory. With this release, the file loader should now always copy the file somewhere inside of the output directory.

v0.12.13

Compare Source

  • Fix using JS synchronous API from from non-main threads (#​1406)

    This release fixes an issue with the new implementation of the synchronous JS API calls (transformSync and buildSync) when they are used from a thread other than the main thread. The problem happened because esbuild's new implementation uses node's worker_threads library internally and non-main threads were incorrectly assumed to be esbuild's internal thread instead of potentially another unrelated thread. Now esbuild's synchronous JS APIs should work correctly when called from non-main threads.

v0.12.12

Compare Source

  • Fix file loader import paths when subdirectories are present (#​1044)

    Using the file loader for a file type causes importing affected files to copy the file into the output directory and to embed the path to the copied file into the code that imported it. However, esbuild previously always embedded the path relative to the output directory itself. This is problematic when the importing code is generated within a subdirectory inside the output directory, since then the relative path is wrong. For example:

    $ cat src/example/entry.css
    div {
      background: url(../images/image.png);
    }
    
    $ esbuild --bundle src/example/entry.css --outdir=out --outbase=src --loader:.png=file
    
    $ find out -type f
    out/example/entry.css
    out/image-55DNWN2R.png
    
    $ cat out/example/entry.css
    /* src/example/entry.css */
    div {
      background: url(./image-55DNWN2R.png);
    }
    

    This is output from the previous version of esbuild. The above asset reference in out/example/entry.css is wrong. The path should start with ../ because the two files are in different directories.

    With this release, the asset references present in output files will now be the full relative path from the output file to the asset, so imports should now work correctly when the entry point is in a subdirectory within the output directory. This change affects asset reference paths in both CSS and JS output files.

    Note that if you want asset reference paths to be independent of the subdirectory in which they reside, you can use the --public-path setting to provide the common path that all asset reference paths should be constructed relative to. Specifically --public-path=. should bring back the old problematic behavior in case you need it.

  • Add support for [dir] in --asset-names (#​1196)

    You can now use path templates such as --asset-names=[dir]/[name]-[hash] to copy the input directory structure of your asset files (i.e. input files loaded with the file loader) to the output directory. Here's an example:

    $ cat entry.css
    header {
      background: url(images/common/header.png);
    }
    main {
      background: url(images/home/hero.png);
    }
    
    $ esbuild --bundle entry.css --outdir=out --asset-names=[dir]/[name]-[hash] --loader:.png=file
    
    $ find out -type f
    out/images/home/hero-55DNWN2R.png
    out/images/common/header-55DNWN2R.png
    out/entry.css
    
    $ cat out/entry.css
    /* entry.css */
    header {
      background: url(./images/common/header-55DNWN2R.png);
    }
    main {
      background: url(./images/home/hero-55DNWN2R.png);
    }
    

v0.12.11

Compare Source

  • Enable faster synchronous transforms with the JS API by default (#​1000)

    Currently the synchronous JavaScript API calls transformSync and buildSync spawn a new child process on every call. This is due to limitations with node's child_process API. Doing this means transformSync and buildSync are much slower than transform and build, which share the same child process across calls.

    This release improves the performance of transformSync and buildSync by up to 20x. It enables a hack where node's worker_threads API and atomics are used to block the main thread while asynchronous communication with a single long-lived child process happens in a worker. Previously this was only enabled when the ESBUILD_WORKER_THREADS environment variable was set to 1. But this experiment has been available for a while (since version 0.9.6) without any reported issues. Now this hack will be enabled by default. It can be disabled by setting ESBUILD_WORKER_THREADS to 0 before running node.

  • Fix nested output directories with WebAssembly on Windows (#​1399)

    Many functions in Go's standard library have a bug where they do not work on Windows when using Go with WebAssembly. This is a long-standing bug and is a fault with the design of the standard library, so it's unlikely to be fixed. Basically Go's standard library is designed to bake "Windows or not" decision into the compiled executable, but WebAssembly is platform-independent which makes "Windows or not" is a run-time decision instead of a compile-time decision. Oops.

    I have been working around this by trying to avoid using path-related functions in the Go standard library and doing all path manipulation by myself instead. This involved completely replacing Go's path/filepath library. However, I missed the os.MkdirAll function which is also does path manipulation but is outside of the path/filepath package. This meant that nested output directories failed to be created on Windows, which caused a build error. This problem only affected the esbuild-wasm package.

    This release manually reimplements nested output directory creation to work around this bug in the Go standard library. So nested output directories should now work on Windows with the esbuild-wasm package.

v0.12.10

Compare Source

  • Add a target for ES2021

    It's now possible to use --target=es2021 to target the newly-released JavaScript version ES2021. The only difference between that and --target=es2020 is that logical assignment operators such as a ||= b are not converted to regular assignment operators such as a || (a = b).

  • Minify the syntax Infinity to 1 / 0 (#​1385)

    The --minify-syntax flag (automatically enabled by --minify) will now minify the expression Infinity to 1 / 0, which uses fewer bytes:

    // Original code
    const a = Infinity;
    
    // Output with "--minify-syntax"
    const a = 1 / 0;
    

    This change was contributed by @​Gusted.

  • Minify syntax in the CSS transform property (#​1390)

    This release includes various size reductions for CSS transform matrix syntax when minification is enabled:

    /* Original code */
    div {
      transform: translate3d(0, 0, 10px) scale3d(200%, 200%, 1) rotate3d(0, 0, 1, 45deg);
    }
    
    /* Output with "--minify-syntax" */
    div {
      transform: translateZ(10px) scale(2) rotate(45deg);
    }
    

    The translate3d to translateZ conversion was contributed by @​steambap.

  • Support for the case-sensitive flag in CSS attribute selectors (#​1397)

    You can now use the case-sensitive CSS attribute selector flag s such as in [type="a" s] { list-style: lower-alpha; }. Previously doing this caused a warning about unrecognized syntax.

v0.12.9

Compare Source

  • Allow this with --define (#​1361)

    You can now override the default value of top-level this with the --define feature. Top-level this defaults to being undefined in ECMAScript modules and exports in CommonJS modules. For example:

    // Original code
    ((obj) => {
      ...
    })(this);
    
    // Output with "--define:this=window"
    ((obj) => {
      ...
    })(window);
    

    Note that overriding what top-level this is will likely break code that uses it correctly. So this new feature is only useful in certain cases.

  • Fix CSS minification issue with !important and duplicate declarations (#​1372)

    Previously CSS with duplicate declarations for the same property where the first one was marked with !important was sometimes minified incorrectly. For example:

    .selector {
      padding: 10px !important;
      padding: 0;
    }
    

    This was incorrectly minified as .selector{padding:0}. The bug affected three properties: padding, margin, and border-radius. With this release, this code will now be minified as .selector{padding:10px!important;padding:0} instead which means there is no longer a difference between minified and non-minified code in this case.

v0.12.8

Compare Source

  • Plugins can now specify sideEffects: false (#​1009)

    The default path resolution behavior in esbuild determines if a given file can be considered side-effect free (in the Webpack-specific sense) by reading the contents of the nearest enclosing package.json file and looking for "sideEffects": false. However, up until now this was impossible to achieve in an esbuild plugin because there was no way of returning this metadata back to esbuild.

    With this release, esbuild plugins can now return sideEffects: false to mark a file as having no side effects. Here's an example:

    esbuild.build({
      entryPoints: ['app.js'],
      bundle: true,
      plugins: [{
        name: 'env-plugin',
        setup(build) {
          build.onResolve({ filter: /^env$/ }, args => ({
            path: args.path,
            namespace: 'some-ns',
            sideEffects: false,
          }))
          build.onLoad({ filter: /.*/, namespace: 'some-ns' }, () => ({
            contents: `export default self.env || (self.env = getEnv())`,
          }))
        },
      }],
    })
    

    This plugin creates a virtual module that can be generated by importing the string env. However, since the plugin returns sideEffects: false, the generated virtual module will not be included in the bundle if all of the imported values from the module env end up being unused.

    This feature was contributed by @​chriscasola.

  • Remove a warning about unsupported source map comments (#​1358)

    This removes a warning that indicated when a source map comment couldn't be supported. Specifically, this happens when you enable source map generation and esbuild encounters a file with a source map comment pointing to an external file but doesn't have enough information to know where to look for that external file (basically when the source file doesn't have an associated directory to use for path resolution). In this case esbuild can't respect the input source map because it cannot be located. The warning was annoying so it has been removed. Source maps still won't work, however.

v0.12.7

Compare Source

  • Quote object properties that are modern Unicode identifiers (#​1349)

    In ES6 and above, an identifier is a character sequence starting with a character in the ID_Start Unicode category and followed by zero or more characters in the ID_Continue Unicode category, and these categories must be drawn from Unicode version 5.1 or above.

    But in ES5, an identifier is a character sequence starting with a character in one of the Lu, Ll, Lt, Lm, Lo, Nl Unicode categories and followed by zero or more characters in the Lu, Ll, Lt, Lm, Lo, Nl, Mn, Mc, Nd, Pc Unicode categories, and these categories must be drawn from Unicode version 3.0 or above.

    Previously esbuild always used the ES6+ identifier validation test when deciding whether to use an identifier or a quoted string to encode an object property but with this release, it will use the ES5 validation test instead:

    // Original code
    x.ꓷꓶꓲꓵꓭꓢꓱ = { ꓷꓶꓲꓵꓭꓢꓱ: y };
    
    // Old output
    x.ꓷꓶꓲꓵꓭꓢꓱ = { ꓷꓶꓲꓵꓭꓢꓱ: y };
    
    // New output
    x["ꓷꓶꓲꓵꓭꓢꓱ"] = { "ꓷꓶꓲꓵꓭꓢꓱ": y };
    

    This approach should ensure maximum compatibility with all JavaScript environments that support ES5 and above. Note that this means minified files containing Unicode properties may be slightly larger than before.

  • Ignore tsconfig.json files inside node_modules (#​1355)

    Package authors often publish their tsconfig.json files to npm because of npm's default-include publishing model and because these authors probably don't know about .npmignore files. People trying to use these packages with esbuild have historically complained that esbuild is respecting tsconfig.json in these cases. The assumption is that the package author published these files by accident.

    With this release, esbuild will no longer respect tsconfig.json files when the source file is inside a node_modules folder. Note that tsconfig.json files inside node_modules are still parsed, and extending tsconfig.json files from inside a package is still supported.

  • Fix missing --metafile when using --watch (#​1357)

    Due to an oversight, the --metafile setting didn't work when --watch was also specified. This only affected the command-line interface. With this release, the --metafile setting should now work in this case.

  • Add a hidden __esModule property to modules in ESM format (#​1338)

    Module namespace objects from ESM files will now have a hidden __esModule property. This improves compatibility with code that has been converted from ESM syntax to CommonJS by Babel or TypeScript. For example:

    // Input TypeScript code
    import x from "y"
    console.log(x)
    
    // Output JavaScript code from the TypeScript compiler
    var __importDefault = (this && this.__importDefault) || function (mod) {
        return (mod && mod.__esModule) ? mod : { "default": mod };
    };
    Object.defineProperty(exports, "__esModule", { value: true });
    const y_1 = __importDefault(require("y"));
    console.log(y_1.default);
    

    If the object returned by require("y") doesn't have an __esModule property, then y_1 will be the object { "default": require("y") }. If the file "y" is in ESM format and has a default export of, say, the value null, that means y_1 will now be { "default": { "default": null } } and you will need to use y_1.default.default to access the default value. Adding an automatically-generated __esModule property when converting files in ESM format to CommonJS is required to make this code work correctly (i.e. for the value to be accessible via just y_1.default instead).

    With this release, code in ESM format will now have an automatically-generated __esModule property to satisfy this convention. The property is non-enumerable so it shouldn't show up when iterating over the properties of the object. As a result, the export name __esModule is now reserved for use with esbuild. It's now an error to create an export with the name __esModule.

    This fix was contributed by @​lbwa.

v0.12.6

Compare Source

  • Improve template literal lowering transformation conformance (#​1327)

    This release contains the following improvements to template literal lowering for environments that don't support tagged template literals natively (such as --target=es5):

    • For tagged template literals, the arrays of strings that are passed to the tag function are now frozen and immutable. They are also now cached so they should now compare identical between multiple template evaluations:

      // Original code
      console.log(tag`\u{10000}`)
      
      // Old output
      console.log(tag(__template(["𐀀"], ["\\u{10000}"])));
      
      // New output
      var _a;
      console.log(tag(_a || (_a = __template(["𐀀"], ["\\u{10000}"]))));
      
    • For tagged template literals, the generated code size is now smaller in the common case where there are no escape sequences, since in that case there is no distinction between "raw" and "cooked" values:

      // Original code
      console.log(tag`some text without escape sequences`)
      
      // Old output
      console.log(tag(__template(["some text without escape sequences"], ["some text without escape sequences"])));
      
      // New output
      var _a;
      console.log(tag(_a || (_a = __template(["some text without escape sequences"]))));
      
    • For non-tagged template literals, the generated code now uses chains of .concat() calls instead of string addition:

      // Original code
      console.log(`an ${example} template ${literal}`)
      
      // Old output
      console.log("an " + example + " template " + literal);
      
      // New output
      console.log("an ".concat(example, " template ").concat(literal));
      

      The old output was incorrect for several reasons including that toString must be called instead of valueOf for objects and that passing a Symbol instance should throw instead of converting the symbol to a string. Using .concat() instead of string addition fixes both of those correctness issues. And you can't use a single .concat() call because side effects must happen inline instead of at the end.

  • Only respect target in tsconfig.json when esbuild's target is not configured (#​1332)

    In version 0.12.4, esbuild began respecting the target setting in tsconfig.json. However, sometimes tsconfig.json contains target values that should not be used. With this release, esbuild will now only use the target value in tsconfig.json as the language level when esbuild's target setting is not configured. If esbuild's target setting is configured then the target value in tsconfig.json is now ignored.

  • Fix the order of CSS imported from JS (#​1342)

    Importing CSS from JS when bundling causes esbuild to generate a sibling CSS output file next to the resulting JS output file containing the bundled CSS. The order of the imported CSS files in the output was accidentally the inverse order of the order in which the JS files were evaluated. Instead the order of the imported CSS files should match the order in which the JS files were evaluated. This fix was contributed by @​dmitrage.

  • Fix an edge case with transforming export default class (#​1346)

    Statements of the form export default class x {} were incorrectly transformed to class x {} var y = x; export {y as default} instead of class x {} export {x as default}. Transforming these statements like this is incorrect in the rare case that the class is later reassigned by name within the same file such as export default class x {} x = null. Here the imported value should be null but was incorrectly the class object instead. This is unlikely to matter in real-world code but it has still been fixed to improve correctness.

v0.12.5

Compare Source

  • Add support for lowering tagged template literals to ES5 (#​297)

    This release adds support for lowering tagged template literals such as String.raw`\unicode` to target environments that don't support them such as --target=es5 (non-tagged template literals were already supported). Each literal turns into a function call to a helper function:

    // Original code
    console.log(String.raw`\unicode`)
    
    // Lowered code
    console.log(String.raw(__template([void 0], ["\\unicode"])));
    
  • Change class field behavior to match TypeScript 4.3

    TypeScript 4.3 includes a subtle breaking change that wasn't mentioned in the TypeScript 4.3 blog post: class fields will now be compiled with different semantics if "target": "ESNext" is present in tsconfig.json. Specifically in this case useDefineForClassFields will default to true when not specified instead of false. This means class field behavior in TypeScript code will now match JavaScript instead of doing something else:

    class Base {
      set foo(value) { console.log('set', value) }
    }
    class Derived extends Base {
      foo = 123
    }
    new Derived()
    

    In TypeScript 4.2 and below, the TypeScript compiler would generate code that prints set 123 when tsconfig.json contains "target": "ESNext" but in TypeScript 4.3, the TypeScript compiler will now generate code that doesn't print anything. This is the difference between "assign" semantics and "define" semantics. With this release, esbuild has been changed to follow the TypeScript 4.3 behavior.

  • Avoid generating the character sequence </script> (#​1322)

    If the output of esbuild is inlined into a <script>...</script> tag inside an HTML file, the character sequence </script> inside the JavaScript code will accidentally cause the script tag to be terminated early. There are at least four such cases where this can happen:

    console.log('</script>')
    console.log(1</script>/.exec(x).length)
    console.log(String.raw`</script>`)
    // @&#8203;license </script>
    

    With this release, esbuild will now handle all of these cases and avoid generating the problematic character sequence:

    console.log('<\/script>');
    console.log(1< /script>/.exec(x).length);
    console.log(String.raw(__template(["<\/script>"], ["<\/script>"])));
    // @&#8203;license <\/script>
    
  • Change the triple-slash reference comment for Deno (#​1325)

    The comment in esbuild's JavaScript API implementation for Deno that references the TypeScript type declarations has been changed from /// <reference path="./mod.d.ts" /> to /// <reference types="./mod.d.ts" />. This comment was copied from Deno's documentation but apparently Deno's documentation was incorrect. The comment in esbuild's Deno bundle has been changed to reflect Deno's latest documentation.

v0.12.4

Compare Source

  • Reorder name preservation before TypeScript decorator evaluation (#​1316)

    The --keep-names option ensures the .name property on functions and classes remains the same after bundling. However, this was being enforced after TypeScript decorator evaluation which meant that the decorator could observe the incorrect name. This has been fixed and now .name preservation happens before decorator evaluation instead.

  • Potential fix for a determinism issue (#​1304)

    This release contains a potential fix for an unverified issue with non-determinism in esbuild. The regression was apparently introduced in 0.11.13 and may be related to parallelism that was introduced around the point where dynamic import() expressions are added to the list of entry points. Hopefully this fix should resolve the regression.

  • Respect target in tsconfig.json (#​277)

    Each JavaScript file that esbuild bundles will now be transformed according to the target language level from the nearest enclosing tsconfig.json file. This is in addition to esbuild's own --target setting; the two settings are merged by transforming any JavaScript language feature that is unsupported in either esbuild's configured --target value or the target property in the tsconfig.json file.

v0.12.3

Compare Source

  • Ensure JSX element names start with a capital letter (#​1309)

    The JSX specification only describes the syntax and says nothing about how to interpret it. But React (and therefore esbuild) treats JSX tags that start with a lower-case ASCII character as strings instead of identifiers. That way the tag <i/> always refers to the italic HTML element i and never to a local variable named i.

    However, esbuild may rename identifiers for any number of reasons such as when minification is enabled. Previously esbuild could sometimes rename identifiers used as tag names such that they start with a lower-case ASCII character. This is problematic when JSX syntax preservation is enabled since subsequent JSX processing would then turn these identifier references into strings.

    With this release, esbuild will now make sure identifiers used in tag names start with an upper-case ASCII character instead when JSX syntax preservation is enabled. This should avoid problems when using esbuild with JSX transformation tools.

  • Fix a single hyphen being treated as a CSS name (#​1310)

    CSS identifiers are allowed to start with a - character if (approximately) the following character is a letter, an escape sequence, a non-ASCII character, the character _, or another - character. This check is used in certain places when printing CSS to determine whether a token is a valid identifier and can be printed as such or whether it's an invalid identifier and needs to be quoted as a string. One such place is in attribute selectors such as [a*=b].

    However, esbuild had a bug where a single - character was incorrectly treated as a valid identifier in this case. This is because the end of string became U+FFFD (the Unicode replacement character) which is a non-ASCII character and a valid name-start code point. With this release a single - character is no longer treated as a valid identifier. This fix was contributed by @​lbwa.

v0.12.2

Compare Source

  • Fix various code generation and minification issues (#​1305)

    This release fixes the following issues, which were all identified by running esbuild against the latest UglifyJS test suite:

    • The in operator is now surrounded parentheses inside arrow function expression bodies inside for loop initializers:

      // Original code
      for ((x => y in z); 0; ) ;
      
      // Old output
      for ((x) => y in z; 0; ) ;
      
      // New output
      for ((x) => (y in z); 0; ) ;
      

      Without this, the in operator would cause the for loop to be considered a for-in loop instead.

    • The statement return undefined; is no longer minified to return; inside async generator functions:

      // Original code
      return undefined;
      
      // Old output
      return;
      
      // New output
      return void 0;
      

      Using return undefined; inside an async generator function has the same effect as return await undefined; which schedules a task in the event loop and runs code in a different order than just return;, which doesn't hide an implicit await expression.

    • Property access expressions are no longer inlined in template tag position:

      // Original code
      (null, a.b)``, (null, a[b])``;
      
      // Old output
      a.b``, a[b]``;
      
      // New output
      (0, a.b)``, (0, a[b])``;
      

      The expression a.b`c` is different than the expression (0, a.b)`c`. The first calls the function a.b with a as the value for this but the second calls the function a.b with the default value for this (the global object in non-strict mode or undefined in strict mode).

    • Verbatim __proto__ properties inside object spread are no longer inlined when minifying:

      // Original code
      x = { ...{ __proto__: { y: true } } }.y;
      
      // Old output
      x = { __proto__: { y: !0 } }.y;
      
      // New output
      x = { ...{ __proto__: { y: !0 } } }.y;
      

      A verbatim (i.e. non-computed non-method) property called __proto__ inside an object literal actually sets the prototype of the surrounding object literal. It does not add an "own property" called __proto__ to that object literal, so inlining it into the parent object literal would be incorrect. The presence of a __proto__ property now stops esbuild from applying the object spread inlining optimization when minifying.

    • The value of this has now been fixed for lowered private class members that are used as template tags:

      // Original code
      x = (new (class {
        a = this.#c``;
        b = 1;
        #c() { return this }
      })).a.b;
      
      // Old output
      var _c, c_fn, _a;
      x = new (_a = class {
        constructor() {
          __privateAdd(this, _c);
          __publicField(this, "a", __privateMethod(this, _c, c_fn)``);
          __publicField(this, "b", 1);
        }
      }, _c = new WeakSet(), c_fn = function() {
        return this;
      }, _a)().a.b;
      
      // New output
      var _c, c_fn, _a;
      x = new (_a = class {
        constructor() {
          __privateAdd(this, _c);
          __publicField(this, "a", __privateMethod(this, _c, c_fn).bind(this)``);
          __publicField(this, "b", 1);
        }
      }, _c = new WeakSet(), c_fn = function() {
        return this;
      }, _a)().a.b;
      

      The value of this here should be an instance of the class because the template tag is a property access expression. However, it was previously the default value (the global object in non-strict mode or undefined in strict mode) instead due to the private member transformation, which is incorrect.

    • Invalid escape sequences are now allowed in tagged template literals

      This implements the template literal revision feature: https://github.com/tc39/proposal-template-literal-revision. It allows you to process tagged template literals using custom semantics that don't follow JavaScript escape sequence rules without causing a syntax error:

      console.log((x => x.raw)`invalid \unicode escape sequence`)
      

v0.12.1

Compare Source

  • Fix a bug with var() in CSS color lowering (#​1421)

    This release fixes a bug with esbuild's handling of the rgb and hsl color functions when they contain var(). Each var() token sequence can be substituted for any number of tokens including zero or more than one, but previously esbuild's output was only correct if each var() inside of rgb or hsl contained exactly one token. With this release, esbuild will now not attempt to transform newer CSS color syntax to older CSS color syntax if it contains var():

    /* Original code */
    a {
      color: hsl(var(--hs), var(--l));
    }
    
    /* Old output */
    a {
      color: hsl(var(--hs), ,, var(--l));
    }
    
    /* New output */
    a {
      color: hsl(var(--hs), var(--l));
    }
    

    The bug with the old output above happened because esbuild considered the arguments to hsl as matching the pattern hsl(h s l) which is the new space-separated form allowed by CSS Color Module Level 4. Then esbuild tried to convert this to the form hsl(h, s, l) which is more widely supported by older browsers. But this substitution doesn't work in the presence of var(), so it has now been disabled in that case.

v0.12.0

Compare Source

This release contains backwards-incompatible changes. Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as recommended by npm). You should either be pinning the exact version of esbuild in your package.json file or be using a version range syntax that only accepts patch upgrades such as ~0.11.0. See the documentation about semver for more information.

The breaking changes in this release relate to CSS import order and also build scenarios where both the inject and define API options are used (see below for details). These breaking changes are as follows:

  • Fix bundled CSS import order (#​465)

    JS and CSS use different import ordering algorithms. In JS, importing a file that has already been imported is a no-op but in CSS, importing a file that has already been imported re-imports the file. A simple way to imagine this is to view each @import rule in CSS as being replaced by the contents of that file similar to #include in C/C++. However, this is incorrect in the case of @import cycles because it would cause infinite expansion. A more accurate way to imagine this is that in CSS, a file is evaluated at the last @import location while in JS, a file is evaluated at the first import location.

    Previously esbuild followed JS import order rules for CSS but now esbuild will follow CSS import order rules. This is a breaking change because it means your CSS may behave differently when bundled. Note that CSS import order rules are somewhat unintuitive because evaluation order matters. In CSS, using @import multiple times can end up unintentionally erasing overriding styles. For example, consider the following files:

    /* entry.css */
    @&#8203;import "./color.css";
    @&#8203;import "./background.css";
    
    /* color.css */
    @&#8203;import "./reset.css";
    body {
      color: white;
    }
    
    /* background.css */
    @&#8203;import "./reset.css";
    body {
      background: black;
    }
    
    /* reset.css */
    body {
      background: white;
      color: black;
    }
    

    Because of how CSS import order works, entry.css will now be bundled like this:

    /* color.css */
    body {
      color: white;
    }
    
    /* reset.css */
    body {
      background: white;
      color: black;
    }
    
    /* background.css */
    body {
      background: black;
    }
    

    This means the body will unintuitively be all black! The file reset.css is evaluated at the location of the last @import instead of the first @import. The fix for this case is to remove the nested imports of reset.css and to import reset.css exactly once at the top of entry.css.

    Note that while the evaluation order of external CSS imports is preserved with respect to other external CSS imports, the evaluation order of external CSS imports is not preserved with respect to other internal CSS imports. All external CSS imports are "hoisted" to the top of the bundle. The alternative would be to generate many smaller chunks which is usually undesirable. So in this case esbuild's CSS bundling behavior will not match the browser.

  • Fix bundled CSS when using JS code splitting (#​608)

    Previously esbuild generated incorrect CSS output when JS code splitting was enabled and the JS code being bundled imported CSS files. CSS code that was reachable via multiple JS entry points was split off into a shared CSS chunk, but that chunk was not actually imported anywhere so the shared CSS was missing. This happened because both CSS and JS code splitting were experimental features that are still in progress and weren't tested together.

    Now esbuild's CSS output should contain all reachable CSS code when JS code splitting is enabled. Note that this does not mean code splitting works for CSS files. Each CSS output file simply contains the transitive set of all CSS reachable from the JS entry point including through dynamic import() and require() expressions. Specifically, the bundler constructs a virtual CSS file for each JS entry point consisting only of @import rules for each CSS file imported into a JS file. These @import rules are constructed in JS source order, but then the bundler uses CSS import order from that point forward to bundle this virtual CSS file into the final CSS output file.

    This model makes the most sense when CSS files are imported into JS files via JS import statements. Importing CSS via import() and require() (either directly or transitively through multiple intermediate JS files) should still "work" in the sense that all reachable CSS should be included in the output, but in this case esbuild will pick an arbitrary (but consistent) import order. The import order may not match the order that the JS files are evaluated in because JS evaluation order of dynamic imports is only determined at run-time while CSS bundling happens at compile-time.

    It's possible to implement code splitting for CSS such that CSS code used between multiple entry points is shared. However, CSS lacks a mechanism for "lazily" importing code (i.e. disconnecting the import location with the evaluation location) so CSS code splitting could potentially need to generate a huge number of very small chunks to preserve import order. It's unclear if this would end up being a net win or not as far as browser download time. So sharing-based code splitting is currently not supported for CSS.

    It's theoretically possible to implement code splitting for CSS such that CSS from a dynamically-imported JS file (e.g. via import()) is placed into a separate chunk. However, due to how @import order works this would in theory end up re-evaluating all shared dependencies which could overwrite overloaded styles and unintentionally change the way the page is rendered. For example, constructing a single-page app architecture such that each page is JS-driven and can transition to other JS-driven pages via import() could end up with pages that look different depending on what order you visit them in. This is clearly undesirable. The simple way to address this is to just not support dynamic-import code splitting for CSS either.

  • Change "define" to have higher priority than "inject" (#​660)

    The "define" and "inject" features are both ways of replacing certain expressions in your source code with other things expressions. Previously esbuild's behavior ran "inject" before "define", which could lead to some undesirable behavior. For example (from the react npm package):

    if (process.env.NODE_ENV === 'production') {
      module.exports = require('./cjs/react.production.min.js');
    } else {
      module.exports = require('./cjs/react.development.js');
    }
    

    If you use "define" to replace process.env.NODE_ENV with "production" and "inject" to replace process with a shim that emulates node's process API, then process was previously replaced first and then process.env.NODE_ENV wasn't matched because process referred to the injected shim. This wasn't ideal because it means esbuild didn't detect the branch condition as a constant (since it doesn't know how the shim behaves at run-time) and bundled both the development and production versions of the package.

    With this release, esbuild will now run "define" before "inject". In the above example this means that process.env.NODE_ENV will now be replaced with "production", the injected shim will not be included, and only the production version of the package will be bundled. This feature was contributed by @​rtsao.

In addition to the breaking changes above, the following features are also included in this release:

  • Add support for the NO_COLOR environment variable

    The CLI will now omit color if the NO_COLOR environment variable is present, which is an existing convention that is followed by some other software. See https://no-color.org/ for more information.

v0.11.23

Compare Source

  • Add a shim function for unbundled uses of require (#​1202)

    Modules in CommonJS format automatically get three variables injected into their scope: module, exports, and require. These allow the code to import other modules and to export things from itself. The bundler automatically rewrites uses of module and exports to refer to the module's exports and certain uses of require to a helper function that loads the imported module.

    Not all uses of require can be converted though, and un-converted uses of require will end up in the output. This is problematic because require is only present at run-time if the output is run as a CommonJS module. Otherwise require is undefined, which means esbuild's behavior is inconsistent between compile-time and run-time. The module and exports variables are objects at compile-time and run-time but require is a function at compile-time and undefined at run-time. This causes code that checks for typeof require to have inconsistent behavior:

    if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
      console.log('CommonJS detected')
    }
    

    In the above example, ideally CommonJS detected would always be printed since the code is being bundled with a CommonJS-aware bundler. To fix this, esbuild will now substitute references to require with a stub __require function when bundling if the output format is something other than CommonJS. This should ensure that require is now consistent between compile-time and run-time. When bundled, code that uses unbundled references to require will now look something like this:

    var __require = (x) => {
      if (typeof require !== "undefined")
        return require(x);
      throw new Error('Dynamic require of "' + x + '" is not supported');
    };
    
    var __commonJS = (cb, mod) => () => (mod || cb((mod = {exports: {}}).exports, mod), mod.exports);
    
    var require_example = __commonJS((exports, module) => {
      if (typeof __require === "function" && typeof exports === "object" && typeof module === "object") {
        console.log("CommonJS detected");
      }
    });
    
    require_example();
    
  • Fix incorrect caching of internal helper function library (#​1292)

    This release fixes a bug where running esbuild multiple times with different configurations sometimes resulted in code that would crash at run-time. The bug was introduced in version 0.11.19 and happened because esbuild's internal helper function library is parsed once and cached per configuration, but the new profiler name option was accidentally not included in the cache key. This option is now included in the cache key so this bug should now be fixed.

  • Minor performance improvements

    This release contains some small performance improvements to offset an earlier minor performance regression due to the addition of certain features such as hashing for entry point files. The benchmark times on the esbuild website should now be accurate again (versions of esbuild after the regression but before this release were slightly slower than the benchmark).

v0.11.22

Compare Source

  • Add support for the "import assertions" proposal

    This is new JavaScript syntax that was shipped in Chrome 91. It looks like this:

    import './foo.json' assert { type: 'json' }
    import('./bar.json', { assert: { type: 'json' } })
    

    On the web, the content type for a given URL is determined by the Content-Type HTTP header instead of the file extension. So adding support for importing non-JS content types such as JSON to the web could cause security issues since importing JSON from an untrusted source is safe while importing JS from an untrusted source is not.

    Import assertions are a new feature to address this security concern and unblock non-JS content types on the web. They cause the import to fail if the Content-Type header doesn't match the expected value. This prevents security issues for data-oriented content types such as JSON since it guarantees that data-oriented content will never accidentally be evaluated as code instead of data. More information about the proposal is available here: https://github.com/tc39/proposal-import-assertions.

    This release includes support for parsing and printing import assertions. They will be printed if the configured target environment supports them (currently only in esnext and chrome91), otherwise they will be omitted. If they aren't supported in the configured target environment and it's not possible to omit them, which is the case for certain dynamic import() expressions, then using them is a syntax error. Import assertions are otherwise unused by the bundler.

  • Forbid the token sequence for ( async of when not followed by =>

    This follows a recently-fixed ambiguity in the JavaScript specification, which you can read about here: https://github.com/tc39/ecma262/pull/2256. Prior to this change in the specification, it was ambiguous whether this token sequence should be parsed as for ( async of => or for ( async of ;. V8 and esbuild expected => after for ( async of while SpiderMonkey and JavaScriptCore did something else.

    The ambiguity has been removed and the token sequence for ( async of is now forbidden by the specification when not followed by =>, so esbuild now forbids this as well. Note that the token sequence for await (async of is still allowed even when not followed by =>. Code such as for ((async) of []) ; is still allowed and will now be printed with parentheses to avoid the grammar ambiguity.

  • Restrict super property access to inside of methods

    You can now only use super.x and super[x] expressions inside of methods. Previously these expressions were incorrectly allowed everywhere. This means esbuild now follows the JavaScript language specification more closely.

v0.11.21

Compare Source

  • TypeScript override for parameter properties (#​1262)

    You can now use the override keyword instead of or in addition to the public, private, protected, and readonly keywords for declaring a TypeScript parameter property:

    class Derived extends Base {
      constructor(override field: any) {
      }
    }
    

    This feature was recently added to the TypeScript compiler and will presumably be in an upcoming version of the TypeScript language. Support for this feature in esbuild was contributed by @​g-plane.

  • Fix duplicate export errors due to TypeScript import-equals statements (#​1283)

    TypeScript has a special import-equals statement that is not part of JavaScript. It looks like this:

    import a = foo.a
    import b = a.b
    import c = b.c
    
    import x = foo.x
    import y = x.y
    import z = y.z
    
    export let bar = c
    

    Each import can be a type or a value and type-only imports need to be eliminated when converting this code to JavaScript, since types do not exist at run-time. The TypeScript compiler generates the following JavaScript code for this example:

    var a = foo.a;
    var b = a.b;
    var c = b.c;
    export let bar = c;
    

    The x, y, and z import statements are eliminated in esbuild by iterating over imports and exports multiple times and continuing to remove unused TypeScript import-equals statements until none are left. The first pass removes z and marks y as unused, the second pass removes y and marks x as unused, and the third pass removes x.

    However, this had the side effect of making esbuild incorrectly think that a single export is exported twice (because it's processed more than once). This release fixes that bug by only iterating multiple times over imports, not exports. There should no longer be duplicate export errors for this case.

  • Add support for type-only TypeScript import-equals statements (#​1285)

    This adds support for the following new TypeScript syntax that was added in version 4.2:

    import type React = require('react')
    

    Unlike import React = require('react'), this statement is a type declaration instead of a value declaration and should be omitted from the generated code. See microsoft/TypeScript#​41573 for details. This feature was contributed by @​g-plane.

v0.11.20

Compare Source

  • Omit warning about duplicate JSON keys from inside node_modules (#​1254)

    This release no longer warns about duplicate keys inside package.json files inside node_modules. There are packages like this that are published to npm, and this warning is unactionable. Now esbuild will only issue this warning outside of node_modules directories.

  • Add CSS minification for box-shadow values

    The CSS box-shadow property is now minified when --mangle-syntax is enabled. This includes trimming length values and minifying color representations.

  • Fix object spread transform for non-spread getters (#​1259)

    When transforming an object literal containing object spread (the ... syntax), properties inside the spread should be evaluated but properties outside the spread should not be evaluated. Previously esbuild's object spread transform incorrectly evaluated properties in both cases. Consider this example:

    var obj = {
      ...{ get x() { console.log(1) } },
      get y() { console.log(3) },
    }
    console.log(2)
    obj.y
    

    This should print out 1 2 3 because the non-spread getter should not be evaluated. Instead, esbuild was incorrectly transforming this into code that printed 1 3 2. This issue should now be fixed with this release.

  • Prevent private class members from being added more than once

    This fixes a corner case with the private class member implementation. Constructors in JavaScript can return an object other than this, so private class members can actually be added to objects other than this. This can be abused to attach completely private metadata to other objects:

    class Base {
      constructor(x) {
        return x
      }
    }
    class Derived extends Base {
      #y
      static is(z) {
        return #y in z
      }
    }
    const foo = {}
    new Derived(foo)
    console.log(Derived.is(foo)) // true
    

    This already worked in code transformed by esbuild for older browsers. However, calling new Derived(foo) multiple times in the above code was incorrectly allowed. This should not be allowed because it would mean that the private field #y would be re-declared. This is no longer allowed starting from this release.

v0.11.19

Compare Source

  • Allow esbuild to be restarted in Deno (#​1238)

    The esbuild API for Deno has an extra function called stop() that doesn't exist in esbuild's API for node. This is because Deno doesn't provide a way to stop esbuild automatically, so calling stop() is required to allow Deno to exit. However, once stopped the esbuild API could not be restarted.

    With this release, you can now continue to use esbuild after calling stop(). This will restart esbuild's API and means that you will need to call stop() again for Deno to be able to exit. This feature was contributed by @​lucacasonato.

  • Fix code splitting edge case (#​1252)

    This release fixes an edge case where bundling with code splitting enabled generated incorrect code if multiple ESM entry points re-exported the same re-exported symbol from a CommonJS file. In this case the cross-chunk symbol dependency should be the variable that holds the return value from the require() call instead of the original ESM named import clause item. When this bug occurred, the generated ESM code contained an export and import for a symbol that didn't exist, which caused a module initialization error. This case should now work correctly.

  • Fix code generation with declare class fields (#​1242)

    This fixes a bug with TypeScript code that uses declare on a class field and your tsconfig.json file has "useDefineForClassFields": true. Fields marked as declare should not be defined in the generated code, but they were incorrectly being declared as undefined. These fields are now correctly omitted from the generated code.

  • Annotate module wrapper functions in debug builds (#​1236)

    Sometimes esbuild needs to wrap certain modules in a function when bundling. This is done both for lazy evaluation and for CommonJS modules that use a top-level return statement. Previously these functions were all anonymous, so stack traces for errors thrown during initialization looked like this:

    Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
        at getElectronPath (out.js:16:13)
        at out.js:19:21
        at out.js:1:45
        at out.js:24:3
        at out.js:1:45
        at out.js:29:3
        at out.js:1:45
        at Object.<anonymous> (out.js:33:1)
    

    This release adds names to these anonymous functions when minification is disabled. The above stack trace now looks like this:

    Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
        at getElectronPath (out.js:19:15)
        at node_modules/electron/index.js (out.js:22:23)
        at __require (out.js:2:44)
        at src/base/window.js (out.js:29:5)
        at __require (out.js:2:44)
        at src/base/kiosk.js (out.js:36:5)
        at __require (out.js:2:44)
        at Object.<anonymous> (out.js:41:1)
    

    This is similar to Webpack's development-mode behavior:

    Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
        at getElectronPath (out.js:23:11)
        at Object../node_modules/electron/index.js (out.js:27:18)
        at __webpack_require__ (out.js:96:41)
        at Object../src/base/window.js (out.js:49:1)
        at __webpack_require__ (out.js:96:41)
        at Object../src/base/kiosk.js (out.js:38:1)
        at __webpack_require__ (out.js:96:41)
        at out.js:109:1
        at out.js:111:3
        at Object.<anonymous> (out.js:113:12)
    

    These descriptive function names will additionally be available when using a profiler such as the one included in the "Performance" tab in Chrome Developer Tools. Previously all functions were named (anonymous) which made it difficult to investigate performance issues during bundle initialization.

  • Add CSS minification for more cases

    The following CSS minification cases are now supported:

    • The CSS margin property family is now minified including combining the margin-top, margin-right, margin-bottom, and margin-left properties into a single margin property.

    • The CSS padding property family is now minified including combining the padding-top, padding-right, padding-bottom, and padding-left properties into a single padding property.

    • The CSS border-radius property family is now minified including combining the border-top-left-radius, border-top-right-radius, border-bottom-right-radius, and border-bottom-left-radius properties into a single border-radius property.

    • The four special pseudo-elements ::before, ::after, ::first-line, and ::first-letter are allowed to be parsed with one : for legacy reasons, so the :: is now converted to : for these pseudo-elements.

    • Duplicate CSS rules are now deduplicated. Only the last rule is kept, since that's the only one that has any effect. This applies for both top-level rules and nested rules.

  • Preserve quotes around properties when minification is disabled (#​1251)

    Previously the parser did not distinguish between unquoted and quoted properties, since there is no semantic difference. However, some tools such as Google Closure Compiler with "advanced mode" enabled attach their own semantic meaning to quoted properties, and processing code intended for Google Closure Compiler's advanced mode with esbuild was changing those semantics. The distinction between unquoted and quoted properties is now made in the following cases:

    import * as ns from 'external-pkg'
    console.log([
      { x: 1, 'y': 2 },
      { x() {}, 'y'() {} },
      class { x = 1; 'y' = 2 },
      class { x() {}; 'y'() {} },
      { x: x, 'y': y } = z,
      [x.x, y['y']],
      [ns.x, ns['y']],
    ])
    

    The parser will now preserve the quoted properties in these cases as long as --minify-syntax is not enabled. This does not mean that esbuild is officially supporting Google Closure Compiler's advanced mode, just that quoted properties are now preserved when the AST is pretty-printed. Google Closure Compiler's advanced mode accepts a language that shares syntax with JavaScript but that deviates from JavaScript semantics and there could potentially be other situations where preprocessing code intended for Google Closure Compiler's advanced mode with esbuild first causes it to break. If that happens, that is not a bug with esbuild.

v0.11.18

Compare Source

  • Add support for OpenBSD on x86-64 (#​1235)

    Someone has asked for OpenBSD to be supported on x86-64. It should now be supported starting with this release.

  • Fix an incorrect warning about top-level this

    This was introduced in the previous release, and happens when using a top-level async arrow function with a compilation target that doesn't support it. The reason is that doing this generates a shim that preserves the value of this. However, this warning message is confusing because there is not necessarily any this present in the source code. The warning message has been removed in this case. Now it should only show up if this is actually present in the source code.

v0.11.17

Compare Source

  • Fix building with a large stdin string with Deno (#​1219)

    When I did the initial port of esbuild's node-based API to Deno, I didn't realize that Deno's write(bytes) function doesn't actually write the provided bytes. Instead it may only write some of those bytes and needs to be repeatedly called again until it writes everything. This meant that calling esbuild's Deno-based API could hang if the API request was large enough, which can happen in practice when using the stdin string feature. The write API is now called in a loop so these hangs in Deno should now be fixed.

  • Add a warning about replacing this with undefined in ESM code (#​1225)

    There is existing JavaScript code that sometimes references top-level this as a way to access the global scope. However, top-level this is actually specified to be undefined inside of ECMAScript module code, which makes referencing top-level this inside ESM code useless. This issue can come up when the existing JavaScript code is adapted for ESM by adding import and/or export. All top-level references to this are replaced with undefined when bundling to make sure ECMAScript module behavior is emulated correctly regardless of the environment in which the resulting code is run.

    With this release, esbuild will now warn about this when bundling:

     > example.mjs:1:61: warning: Top-level "this" will be replaced with undefined since this file is an ECMAScript module
        1 │ export let Array = (typeof window !== 'undefined' ? window : this).Array
          ╵                                                              ~~~~
       example.mjs:1:0: note: This file is considered an ECMAScript module because of the "export" keyword here
        1 │ export let Array = (typeof window !== 'undefined' ? window : this).Array
          ╵ ~~~~~~
    

    This warning is not unique to esbuild. Rollup also already has a similar warning:

    (!) `this` has been rewritten to `undefined`
    https://rollupjs.org/guide/en/#error-this-is-undefined
    example.mjs
    1: export let Array = (typeof window !== 'undefined' ? window : this).Array
                                                                    ^
    
  • Allow a string literal as a JSX fragment (#​1217)

    TypeScript's JSX implementation allows you to configure a custom JSX factory and a custom JSX fragment, but requires that they are both valid JavaScript identifier member expression chains. Since esbuild's JSX implementation is based on TypeScript, esbuild has the same requirement. So React.createElement is a valid JSX factory value but ['React', 'createElement'] is not.

    However, the Mithril framework has decided to use "[" as a JSX fragment, which is not a valid JavaScript identifier member expression chain. This meant that using Mithril with esbuild required a workaround. In this release, esbuild now lets you use a string literal as a custom JSX fragment. It should now be easier to use esbuild's JSX implementation with libraries such as Mithril.

  • Fix metafile in onEnd with watch mode enabled (#​1186)

    This release fixes a bug where the metafile property was incorrectly undefined inside plugin onEnd callbacks if watch mode is enabled for all builds after the first build. The metafile property was accidentally being set after calling onEnd instead of before.

v0.11.16

Compare Source

  • Fix TypeScript enum edge case (#​1198)

    In TypeScript, you can reference the inner closure variable in an enum within the inner closure by name:

    enum A { B = A }
    

    The TypeScript compiler generates the following code for this case:

    var A;
    (function (A) {
      A[A["B"] = A] = "B";
    })(A || (A = {}));
    

    However, TypeScript also lets you declare an enum value with the same name as the inner closure variable. In that case, the value "shadows" the declaration of the inner closure variable:

    enum A { A = 1, B = A }
    

    The TypeScript compiler generates the following code for this case:

    var A;
    (function (A) {
      A[A["A"] = 1] = "A";
      A[A["B"] = 1] = "B";
    })(A || (A = {}));
    

    Previously esbuild reported a duplicate variable declaration error in the second case due to the collision between the enum value and the inner closure variable with the same name. With this release, the shadowing is now handled correctly.

  • Parse the @-moz-document CSS rule (#​1203)

    This feature has been removed from the web because it's actively harmful, at least according to this discussion. However, there is one exception where @-moz-document url-prefix() { is accepted by Firefox to basically be an "if Firefox" conditional rule. Because of this, esbuild now parses the @-moz-document CSS rule. This should result in better pretty-printing and minification and no more warning when this rule is used.

  • Fix syntax error in TypeScript-specific speculative arrow function parsing (#​1211)

    Because of grammar ambiguities, expressions that start with a parenthesis are parsed using what's called a "cover grammar" that is a super-position of both a parenthesized expression and an arrow function parameter list. In JavaScript, the cover grammar is unambiguously an arrow function if and only if the following token is a => token.

    But in TypeScript, the expression is still ambiguously a parenthesized expression or an arrow function if the following token is a : since it may be the second half of the ?: operator or a return type annotation. This requires speculatively attempting to reduce the cover grammar to an arrow function parameter list.

    However, when doing this esbuild eagerly reported an error if a default argument was encountered and the target is es5 (esbuild doesn't support lowering default arguments to ES5). This is problematic in the following TypeScript code since the parenthesized code turns out to not be an arrow function parameter list:

    function foo(check, hover) {
      return check ? (hover = 2, bar) : baz();
    }
    

    Previously this code incorrectly generated an error since hover = 2 was incorrectly eagerly validated as a default argument. With this release, the reporting of the default argument error when targeting es5 is now done lazily and only when it's determined that the parenthesized code should actually be interpreted as an arrow function parameter list.

  • Further changes to the behavior of the browser field (#​1209)

    This release includes some changes to how the browser field in package.json is interpreted to better match how Browserify, Webpack, Parcel, and Rollup behave. The interpretation of this map in esbuild is intended to be applied if and only if it's applied by any one of these bundlers. However, there were some cases where esbuild applied the mapping and none of the other bundlers did, which could lead to build failures. These cases have been added to my growing list of browser field test cases and esbuild's behavior should now be consistent with other bundlers again.

  • Avoid placing a super() call inside a return statement (#​1208)

    When minification is enabled, an expression followed by a return statement (e.g. a(); return b) is merged into a single statement (e.g. return a(), b). This is done because it sometimes results in smaller code. If the return statement is the only statement in a block and the block is in a single-statement context, the block can be removed which saves a few characters.

    Previously esbuild applied this rule to calls to super() inside of constructors. Doing that broke esbuild's class lowering transform that tries to insert class field initializers after the super() call. This transform isn't robust and only scans the top-level statement list inside the constructor, so inserting the super() call inside of the return statement means class field initializers were inserted before the super() call instead of after. This could lead to run-time crashes due to initialization failure.

    With this release, top-level calls to super() will no longer be placed inside return statements (in addition to various other kinds of statements such as throw, which are now also handled). This should avoid class field initializers being inserted before the super() call.

  • Fix a bug with onEnd and watch mode (#​1186)

    This release fixes a bug where onEnd plugin callbacks only worked with watch mode when an onRebuild watch mode callback was present. Now onEnd callbacks should fire even if there is no onRebuild callback.

  • Fix an edge case with minified export names and code splitting (#​1201)

    The names of symbols imported from other chunks were previously not considered for renaming during minified name assignment. This could cause a syntax error due to a name collision when two symbols have the same original name. This was just an oversight and has been fixed, so symbols imported from other chunks should now be renamed when minification is enabled.

  • Provide a friendly error message when you forget async (#​1216)

    If the parser hits a parse error inside a non-asynchronous function or arrow expression and the previous token is await, esbuild will now report a friendly error about a missing async keyword instead of reporting the parse error. This behavior matches other JavaScript parsers including TypeScript, Babel, and V8.

    The previous error looked like this:

     > test.ts:2:8: error: Expected ";" but found "f"
        2 │   await f();
          ╵         ^
    

    The error now looks like this:

     > example.js:2:2: error: "await" can only be used inside an "async" function
        2 │   await f();
          ╵   ~~~~~
       example.js:1:0: note: Consider adding the "async" keyword here
        1 │ function f() {
          │ ^
          ╵ async
    

v0.11.15

Compare Source

  • Provide options for how to handle legal comments (#​919)

    A "legal comment" is considered to be any comment that contains @license or @preserve or that starts with //! or /*!. These comments are preserved in output files by esbuild since that follows the intent of the original authors of the code.

    However, some people want to remove the automatically-generated license information before they distribute their code. To facilitate this, esbuild now provides several options for how to handle legal comments (via --legal-comments= in the CLI and legalComments in the JS API):

    • none: Do not preserve any legal comments
    • inline: Preserve all statement-level legal comments
    • eof: Move all statement-level legal comments to the end of the file
    • linked: Move all statement-level legal comments to a .LEGAL.txt file and link to them with a comment
    • external: Move all statement-level legal comments to a .LEGAL.txt file but to not link to them

    The default behavior is eof when bundling and inline otherwise.

  • Add onStart and onEnd callbacks to the plugin API

    Plugins can now register callbacks to run when a build is started and ended:

    const result = await esbuild.build({
      ...
      incremental: true,
      plugins: [{
        name: 'example',
        setup(build) {
          build.onStart(() => console.log('build started'))
          build.onEnd(result => console.log('build ended', result))
        },
      }],
    })
    await result.rebuild()
    

    One benefit of onStart and onEnd is that they are run for all builds including rebuilds (relevant for incremental mode, watch mode, or serve mode), so they should be a good place to do work related to the build lifecycle.

    More details:

    • build.onStart()

      You should not use an onStart callback for initialization since it can be run multiple times. If you want to initialize something, just put your plugin initialization code directly inside the setup function instead.

      The onStart callback can be async and can return a promise. However, the build does not wait for the promise to be resolved before starting, so a slow onStart callback will not necessarily slow down the build. All onStart callbacks are also run concurrently, not consecutively. The returned promise is purely for error reporting, and matters when the onStart callback needs to do an asynchronous operation that may fail. If your plugin needs to wait for an asynchronous task in onStart to complete before any onResolve or onLoad callbacks are run, you will need to have your onResolve or onLoad callbacks block on that task from onStart.

      Note that onStart callbacks do not have the ability to mutate build.initialOptions. The initial options can only be modified within the setup function and are consumed once the setup function returns. All rebuilds use the same initial options so the initial options are never re-consumed, and modifications to build.initialOptions that are done within onStart are ignored.

    • build.onEnd()

      All onEnd callbacks are run in serial and each callback is given access to the final build result. It can modify the build result before returning and can delay the end of the build by returning a promise. If you want to be able to inspect the build graph, you should set build.initialOptions.metafile = true and the build graph will be returned as the metafile property on the build result object.

v0.11.14

Compare Source

  • Implement arbitrary module namespace identifiers

    This introduces new JavaScript syntax:

    import {'🍕' as food} from 'file'
    export {food as '🧀'}
    

    The proposal for this feature appears to not be going through the regular TC39 process. It is being done as a subtle direct pull request instead. It seems appropriate for esbuild to support this feature since it has been implemented in V8 and has now shipped in Chrome 90 and node 16.

    According to the proposal, this feature is intended to improve interop with non-JavaScript languages which use exports that aren't valid JavaScript identifiers such as Foo::~Foo. In particular, WebAssembly allows any valid UTF-8 string as to be used as an export alias.

    This feature was actually already partially possible in previous versions of JavaScript via the computed property syntax:

    import * as ns from './file.json'
    console.log(ns['🍕'])
    

    However, doing this is very un-ergonomic and exporting something as an arbitrary name is impossible outside of export * from. So this proposal is designed to fully fill out the possibility matrix and make arbitrary alias names a proper first-class feature.

  • Implement more accurate sideEffects behavior from Webpack (#​1184)

    This release adds support for the implicit **/ prefix that must be added to paths in the sideEffects array in package.json if the path does not contain /. Another way of saying this is if package.json contains a sideEffects array with a string that doesn't contain a / then it should be treated as a file name instead of a path. Previously esbuild treated all strings in this array as paths, which does not match how Webpack behaves. The result of this meant that esbuild could consider files to have no side effects while Webpack would consider the same files to have side effects. This bug should now be fixed.

v0.11.13

Compare Source

  • Implement ergonomic brand checks for private fields

    This introduces new JavaScript syntax:

    class Foo {
      #field
      static isFoo(x) {
        return #foo in x // This is an "ergonomic brand check"
      }
    }
    assert(Foo.isFoo(new Foo))
    

    The TC39 proposal for this feature is currently at stage 3 but has already been shipped in Chrome 91 and has also landed in Firefox. It seems reasonably inevitable given that it's already shipping and that it's a very simple feature, so it seems appropriate to add this feature to esbuild.

  • Add the --allow-overwrite flag (#​1152)

    This is a new flag that allows output files to overwrite input files. It's not enabled by default because doing so means overwriting your source code, which can lead to data loss if your code is not checked in. But supporting this makes certain workflows easier by avoiding the need for a temporary directory so doing this is now supported.

  • Minify property accesses on object literals (#​1166)

    The code {a: {b: 1}}.a.b will now be minified to 1. This optimization is relatively complex and hard to do safely. Here are some tricky cases that are correctly handled:

    var obj = {a: 1}
    assert({a: 1, a: 2}.a === 2)
    assert({a: 1, [String.fromCharCode(97)]: 2}.a === 2)
    assert({__proto__: obj}.a === 1)
    assert({__proto__: null}.a === undefined)
    assert({__proto__: null}.__proto__ === undefined)
    assert({a: function() { return this.b }, b: 1}.a() === 1)
    assert(({a: 1}.a = 2) === 2)
    assert(++{a: 1}.a === 2)
    assert.throws(() => { new ({ a() {} }.a) })
    
  • Improve arrow function parsing edge cases

    There are now more situations where arrow expressions are not allowed. This improves esbuild's alignment with the JavaScript specification. Some examples of cases that were previously allowed but that are now no longer allowed:

    1 + x => {}
    console.log(x || async y => {})
    class Foo extends async () => {} {}
    

v0.11.12

Compare Source

  • Fix a bug where -0 and 0 were collapsed to the same value (#​1159)

    Previously esbuild would collapse Object.is(x ? 0 : -0, -0) into Object.is((x, 0), -0) during minification, which is incorrect. The IEEE floating-point value -0 is a different bit pattern than 0 and while they both compare equal, the difference is detectable in a few scenarios such as when using Object.is(). The minification transformation now checks for -0 vs. 0 and no longer has this bug. This fix was contributed by @​rtsao.

  • Match the TypeScript compiler's output in a strange edge case (#​1158)

    With this release, esbuild's TypeScript-to-JavaScript transform will no longer omit the namespace in this case:

    namespace Something {
      export declare function Print(a: string): void
    }
    Something.Print = function(a) {}
    

    This was previously omitted because TypeScript omits empty namespaces, and the namespace was considered empty because the export declare function statement isn't "real":

    namespace Something {
      export declare function Print(a: string): void
      setTimeout(() => Print('test'))
    }
    Something.Print = function(a) {}
    

    The TypeScript compiler compiles the above code into the following:

    var Something;
    (function (Something) {
      setTimeout(() => Print('test'));
    })(Something || (Something = {}));
    Something.Print = function (a) { };
    

    Notice how Something.Print is never called, and what appears to be a reference to the Print symbol on the namespace Something is actually a reference to the global variable Print. I can only assume this is a bug in TypeScript, but it's important to replicate this behavior inside esbuild for TypeScript compatibility.

    The TypeScript-to-JavaScript transform in esbuild has been updated to match the TypeScript compiler's output in both of these cases.

  • Separate the debug log level into debug and verbose

    You can now use --log-level=debug to get some additional information that might indicate some problems with your build, but that has a high-enough false-positive rate that it isn't appropriate for warnings, which are on by default. Enabling the debug log level no longer generates a torrent of debug information like it did in the past; that behavior is now reserved for the verbose log level instead.

v0.11.11

Compare Source

  • Initial support for Deno (#​936)

    You can now use esbuild in the Deno JavaScript environment via esbuild's official Deno package. Using it looks something like this:

    import * as esbuild from 'https://deno.land/x/esbuild@v0.11.11/mod.js'
    const ts = 'let hasProcess: boolean = typeof process != "null"'
    const result = await esbuild.transform(ts, { loader: 'ts', logLevel: 'warning' })
    console.log('result:', result)
    esbuild.stop()
    

    It has basically the same API as esbuild's npm package with one addition: you need to call stop() when you're done because unlike node, Deno doesn't provide the necessary APIs to allow Deno to exit while esbuild's internal child process is still running.

  • Remove warnings about non-bundled use of require and import (#​1153, #​1142, #​1132, #​1045, #​812, #​661, #​574, #​512, #​495, #​480, #​453, #​410, #​80)

    Previously esbuild had warnings when bundling about uses of require and import that are not of the form require(<string literal>) or import(<string literal>). These warnings existed because the bundling process must be able to statically-analyze all dynamic imports to determine which files must be included. Here are some real-world examples of cases that esbuild doesn't statically analyze:

    • From mongoose:

      require('./driver').set(require(global.MONGOOSE_DRIVER_PATH));
      
    • From moment:

      aliasedRequire = require;
      aliasedRequire('./locale/' + name);
      
    • From logform:

      function exposeFormat(name) {
        Object.defineProperty(format, name, {
          get() { return require(`./${name}.js`); }
        });
      }
      exposeFormat('align');
      

    All of these dynamic imports will not be bundled (i.e. they will be left as-is) and will crash at run-time if they are evaluated. Some of these crashes are ok since the code paths may have error handling or the code paths may never be used. Other crashes are not ok because the crash will actually be hit.

    The warning from esbuild existed to let you know that esbuild is aware that it's generating a potentially broken bundle. If you discover that your bundle is broken, it's nice to have a warning from esbuild to point out where the problem is. And it was just a warning so the build process still finishes and successfully generates output files. If you didn't want to see the warning, it was easy to turn it off via --log-level=error.

    However, there have been quite a few complaints about this warning. Some people seem to not understand the difference between a warning and an error, and think the build has failed even though output files were generated. Other people do not want to see the warning but also do not want to enable --log-level=error.

    This release removes this warning for both require and import. Now when you try to bundle code with esbuild that contains dynamic imports not of the form require(<string literal>) or import(<string literal>), esbuild will just silently generate a potentially broken bundle. This may affect people coming from other bundlers that support certain forms of dynamic imports that are not compatible with esbuild such as the Webpack-specific dynamic import() with pattern matching.

v0.11.10

Compare Source

  • Provide more information about exports map import failures if possible (#​1143)

    Node has a new feature where you can add an exports map to your package.json file to control how external import paths map to the files in your package. You can change which paths map to which files as well as make it impossible to import certain files (i.e. the files are private).

    If path resolution fails due to an exports map and the failure is not related to import conditions, esbuild's current error message for this just says that the import isn't possible:

     > example.js:1:15: error: Could not resolve "vanillajs-datepicker/js/i18n/locales/ca" (mark it as external to exclude it from the bundle)
        1 │ import ca from 'vanillajs-datepicker/js/i18n/locales/ca'
          ╵                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       node_modules/vanillajs-datepicker/package.json:6:13: note: The path "./js/i18n/locales/ca" is not exported by package "vanillajs-datepicker"
        6 │   "exports": {
          ╵              ^
    

    This error message matches the error that node itself throws. However, the message could be improved in the case where someone is trying to import a file using its file system path and that path is actually exported by the package, just under a different export path. This case comes up a lot when using TypeScript because the TypeScript compiler (and therefore the Visual Studio Code IDE) still doesn't support package exports.

    With this release, esbuild will now do a reverse lookup of the file system path using the exports map to determine what the correct import path should be:

     > example.js:1:15: error: Could not resolve "vanillajs-datepicker/js/i18n/locales/ca" (mark it as external to exclude it from the bundle)
         1 │ import ca from 'vanillajs-datepicker/js/i18n/locales/ca'
           ╵                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       node_modules/vanillajs-datepicker/package.json:6:13: note: The path "./js/i18n/locales/ca" is not exported by package "vanillajs-datepicker"
         6 │   "exports": {
           ╵              ^
       node_modules/vanillajs-datepicker/package.json:12:19: note: The file "./js/i18n/locales/ca.js" is exported at path "./locales/ca"
        12 │     "./locales/*": "./js/i18n/locales/*.js",
           ╵                    ~~~~~~~~~~~~~~~~~~~~~~~~
       example.js:1:15: note: Import from "vanillajs-datepicker/locales/ca" to get the file "node_modules/vanillajs-datepicker/js/i18n/locales/ca.js"
         1 │ import ca from 'vanillajs-datepicker/js/i18n/locales/ca'
           │                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ╵                "vanillajs-datepicker/locales/ca"
    

    Hopefully this should enable people encountering this issue to fix the problem themselves.

v0.11.9

Compare Source

  • Fix escaping of non-BMP characters in property names (#​977)

    Property names in object literals do not have to be quoted if the property is a valid JavaScript identifier. This is defined as starting with a character in the ID_Start Unicode category and ending with zero or more characters in the ID_Continue Unicode category. However, esbuild had a bug where non-BMP characters (i.e. characters encoded using two UTF-16 code units instead of one) were always checked against ID_Continue instead of ID_Start because they included a code unit that wasn't at the start. This could result in invalid JavaScript being generated when using --charset=utf8 because ID_Continue is a superset of ID_Start and contains some characters that are not valid at the start of an identifier. This bug has been fixed.

  • Be maximally liberal in the interpretation of the browser field (#​740)

    The browser field in package.json is an informal convention followed by browser-specific bundlers that allows package authors to substitute certain node-specific import paths with alternative browser-specific import paths. It doesn't have a rigorous specification and the canonical description of the feature doesn't include any tests. As a result, each bundler implements this feature differently. I have tried to create a survey of how different bundlers interpret the browser field and the results are very inconsistent.

    This release attempts to change esbuild to support the union of the behavior of all other bundlers. That way if people have the browser field working with some other bundler and they switch to esbuild, the browser field shouldn't ever suddenly stop working. This seemed like the most principled approach to take in this situation.

    The drawback of this approach is that it means the browser field may start working when switching to esbuild when it was previously not working. This could cause bugs, but I consider this to be a problem with the package (i.e. not using a more well-supported form of the browser field), not a problem with esbuild itself.

v0.11.8

Compare Source

  • Fix hash calculation for code splitting and dynamic imports (#​1076)

    The hash included in the file name of each output file is intended to change if and only if anything relevant to the content of that output file changes. It includes:

    • The contents of the file with the paths of other output files omitted
    • The output path of the file the final hash omitted
    • Some information about the input files involved in that output file
    • The contents of the associated source map, if there is one
    • All of the information above for all transitive dependencies found by following import statements

    However, this didn't include dynamic import() expressions due to an oversight. With this release, dynamic import() expressions are now also counted as transitive dependencies. This fixes an issue where the content of an output file could change without its hash also changing. As a side effect of this change, dynamic imports inside output files of other output files are now listed in the metadata file if the metafile setting is enabled.

  • Refactor the internal module graph representation

    This release changes a large amount of code relating to esbuild's internal module graph. The changes are mostly organizational and help consolidate most of the logic around maintaining various module graph invariants into a separate file where it's easier to audit. The Go language doesn't have great abstraction capabilities (e.g. no zero-cost iterators) so the enforcement of this new abstraction is unfortunately done by convention instead of by the compiler, and there is currently still some code that bypasses the abstraction. But it's better than it was before.

    Another relevant change was moving a number of special cases that happened during the tree shaking traversal into the graph itself instead. Previously there were quite a few implicit dependency rules that were checked in specific places, which was hard to follow. Encoding these special case constraints into the graph itself makes the problem easier to reason about and should hopefully make the code more regular and robust.

    Finally, this set of changes brings back full support for the sideEffects annotation in package.json. It was previously disabled when code splitting was active as a temporary measure due to the discovery of some bugs in that scenario. But I believe these bugs have been resolved now that tree shaking and code splitting are done in separate passes (see the previous release for more information).

v0.11.7

Compare Source

  • 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.

v0.11.6

Compare Source

  • Fix an incorrect minification transformation (#​1121)

    This release removes an incorrect substitution rule in esbuild's peephole optimizer, which is run when minification is enabled. The incorrect rule transformed if(a && falsy) into if(a, falsy) which is equivalent if falsy has no side effects (such as the literal false). However, the rule didn't check that the expression is side-effect free first which could result in miscompiled code. I have removed the rule instead of modifying it to check for the lack of side effects first because while the code is slightly smaller, it may also be more expensive at run-time which is undesirable. The size savings are also very insignificant.

  • Change how NODE_PATH works to match node (#​1117)

    Node searches for packages in nearby node_modules directories, but it also allows you to inject extra directories to search for packages in using the NODE_PATH environment variable. This is supported when using esbuild's CLI as well as via the nodePaths option when using esbuild's API.

    Node's module resolution algorithm is well-documented, and esbuild's path resolution is designed to follow it. The full algorithm is here: https://nodejs.org/api/modules.html#modules_all_together. However, it appears that the documented algorithm is incorrect with regard to NODE_PATH. The documentation says NODE_PATH directories should take precedence over node_modules directories, and so that's how esbuild worked. However, in practice node actually does it the other way around.

    Starting with this release, esbuild will now allow node_modules directories to take precedence over NODE_PATH directories. This is a deviation from the published algorithm.

  • Provide a better error message for incorrectly-quoted JSX attributes (#​959, #​1115)

    People sometimes try to use the output of JSON.stringify() as a JSX attribute when automatically-generating JSX code. Doing so is incorrect because JSX strings work like XML instead of like JS (since JSX is XML-in-JS). Specifically, using a backslash before a quote does not cause it to be escaped:

    //     JSX ends the "content" attribute here and sets "content" to 'some so-called \\'
    //                                            v
    let button = <Button content="some so-called \"button text\"" />
    //                                                        ^
    //         There is no "=" after the JSX attribute "text", so we expect a ">"
    

    It's not just esbuild; Babel and TypeScript also treat this as a syntax error. All of these JSX parsers are just following the JSX specification. This has come up twice now so it could be worth having a dedicated error message. Previously esbuild had a generic syntax error like this:

     > example.jsx:1:58: error: Expected ">" but found "\\"
        1 │ let button = <Button content="some so-called \"button text\"" />
          ╵                                                           ^
    

    Now esbuild will provide more information if it detects this case:

     > example.jsx:1:58: error: Unexpected backslash in JSX element
        1 │ let button = <Button content="some so-called \"button text\"" />
          ╵                                                           ^
       example.jsx:1:45: note: Quoted JSX attributes use XML-style escapes instead of JavaScript-style escapes
        1 │ let button = <Button content="some so-called \"button text\"" />
          │                                              ~~
          ╵                                              &quot;
       example.jsx:1:29: note: Consider using a JavaScript string inside {...} instead of a quoted JSX attribute
        1 │ let button = <Button content="some so-called \"button text\"" />
          │                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          ╵                              {"some so-called \"button text\""}
    

v0.11.5

Compare Source

  • Add support for the override keyword in TypeScript 4.3 (#​1105)

    The latest version of TypeScript (now in beta) adds a new keyword called override that can be used on class members. You can read more about this feature in Microsoft's blog post about TypeScript 4.3. It looks like this:

    class SpecializedComponent extends SomeComponent {
      override show() {
        // ...
      }
    }
    

    With this release, esbuild will now ignore the override keyword when parsing TypeScript code instead of treating this keyword as a syntax error, which means esbuild can now support TypeScript 4.3 syntax. This change was contributed by @​g-plane.

  • Allow async plugin setup functions

    With this release, you can now return a promise from your plugin's setup function to delay the start of the build:

    let slowInitPlugin = {
      name: 'slow-init',
      async setup(build) {
        // Delay the start of the build
        await new Promise(r => setTimeout(r, 1000))
      },
    }
    

    This is useful if your plugin needs to do something asynchronous before the build starts. For example, you may need some asynchronous information before modifying the initialOptions object, which must be done before the build starts for the modifications to take effect.

  • Add some optimizations around hashing

    This release contains two optimizations to the hashes used in output file names:

    1. Hash generation now happens in parallel with other work, and other work only blocks on the hash computation if the hash ends up being needed (which is only if [hash] is included in --entry-names=, and potentially --chunk-names= if it's relevant). This is a performance improvement because --entry-names= does not include [hash] in the default case, so bundling time no longer always includes hashing time.

    2. The hashing algorithm has been changed from SHA1 to xxHash (specifically this Go implementation) which means the hashing step is around 6x faster than before. Thanks to @​Jarred-Sumner for the suggestion.

  • Disable tree shaking annotations when code splitting is active (#​1070, #​1081)

    Support for Webpack's "sideEffects": false annotation in package.json is now disabled when code splitting is enabled and there is more than one entry point. This avoids a bug that could cause generated chunks to reference each other in some cases. Now all chunks generated by code splitting should be acyclic.

v0.11.4

Compare Source

  • Avoid name collisions with TypeScript helper functions (#​1102)

    Helper functions are sometimes used when transforming newer JavaScript syntax for older browsers. For example, let {x, ...y} = {z} is transformed into let _a = {z}, {x} = _a, y = __rest(_a, ["x"]) which uses the __rest helper function. Many of esbuild's transforms were modeled after the transforms in the TypeScript compiler, so many of the helper functions use the same names as TypeScript's helper functions.

    However, the TypeScript compiler doesn't avoid name collisions with existing identifiers in the transformed code. This means that post-processing esbuild's output with the TypeScript compiler (e.g. for lowering ES6 to ES5) will cause issues since TypeScript will fail to call its own helper functions: microsoft/TypeScript#​43296. There is also a problem where TypeScript's tslib library overwrites globals with these names, which can overwrite esbuild's helper functions if code bundled with esbuild is run in the global scope.

    To avoid these problems, esbuild will now use different names for its helper functions.

  • Fix a chunk hashing issue (#​1099)

    Previously the chunk hashing algorithm skipped hashing entry point chunks when the --entry-names= setting doesn't contain [hash], since the hash wasn't used in the file name. However, this is no longer correct with the change in version 0.11.0 that made dynamic entry point chunks use --chunk-names= instead of --entry-names= since --chunk-names= can still contain [hash].

    With this release, chunk contents will now always be hashed regardless of the chunk type. This makes esbuild somewhat slower than before in the common case, but it fixes this correctness issue.

v0.11.3

Compare Source

  • Auto-define process.env.NODE_ENV when platform is set to browser

    All code in the React world has the requirement that the specific expression process.env.NODE_ENV must be replaced with a string at compile-time or your code will immediately crash at run-time. This is a common stumbling point for people when they start using esbuild with React. Previously bundling code with esbuild containing process.env.NODE_ENV without defining a string replacement first was a warning that warned you about the lack of a define.

    With this release esbuild will now attempt to define process.env.NODE_ENV automatically instead of warning about it. This will be implicitly defined to "production" if minification is enabled and "development" otherwise. This automatic behavior only happens when the platform is browser, since process is not a valid browser API and will never exist in the browser. This is also only done if there are no existing defines for process, process.env, or process.env.NODE_ENV so you can override the automatic value if necessary. If you need to disable this behavior, you can use the neutral platform instead of the browser platform.

  • Retain side-effect free intermediate re-exporting files (#​1088)

    This fixes a subtle bug with esbuild's support for Webpack's "sideEffects": false annotation in package.json when combined with re-export statements. A re-export is when you import something from one file and then export it again. You can re-export something with export * from or export {foo} from or import {foo} from followed by export {foo}.

    The bug was that files which only contain re-exports and that are marked as being side-effect free were not being included in the bundle if you import one of the re-exported symbols. This is because esbuild's implementation of re-export linking caused the original importing file to "short circuit" the re-export and just import straight from the file containing the final symbol, skipping the file containing the re-export entirely.

    This was normally not observable since the intermediate file consisted entirely of re-exports, which have no side effects. However, a recent change to allow ESM files to be lazily-initialized relies on all intermediate files being included in the bundle to trigger the initialization of the lazy evaluation wrappers. So the behavior of skipping over re-export files is now causing the imported symbols to not be initialized if the re-exported file is marked as lazily-evaluated.

    The fix is to track all re-exports in the import chain from the original file to the file containing the final symbol and then retain all of those statements if the import ends up being used.

  • Add a very verbose debug log level

    This log level is an experiment. Enabling it logs a lot of information (currently only about path resolution). The idea is that if you are having an obscure issue, the debug log level might contain some useful information. Unlike normal logs which are meant to mainly provide actionable information, these debug logs are intentionally mostly noise and are designed to be searched through instead.

    Here is an example of debug-level log output:

     > debug: Resolving import "react" in directory "src" of type "import-statement"
       note: Read 26 entries for directory "src"
       note: Searching for "react" in "node_modules" directories starting from "src"
       note: Attempting to load "src/react" as a file
       note: Failed to find file "src/react"
       note: Failed to find file "src/react.tsx"
       note: Failed to find file "src/react.ts"
       note: Failed to find file "src/react.js"
       note: Failed to find file "src/react.css"
       note: Failed to find file "src/react.svg"
       note: Attempting to load "src/react" as a directory
       note: Failed to read directory "src/react"
       note: Parsed package name "react" and package subpath "."
       note: Checking for a package in the directory "node_modules/react"
       note: Read 7 entries for directory "node_modules/react"
       note: Read 393 entries for directory "node_modules"
       note: Attempting to load "node_modules/react" as a file
       note: Failed to find file "node_modules/react"
       note: Failed to find file "node_modules/react.tsx"
       note: Failed to find file "node_modules/react.ts"
       note: Failed to find file "node_modules/react.js"
       note: Failed to find file "node_modules/react.css"
       note: Failed to find file "node_modules/react.svg"
       note: Attempting to load "node_modules/react" as a directory
       note: Read 7 entries for directory "node_modules/react"
       note: Resolved to "node_modules/react/index.js" using the "main" field in "node_modules/react/package.json"
       note: Read 7 entries for directory "node_modules/react"
       note: Read 7 entries for directory "node_modules/react"
       note: Primary path is "node_modules/react/index.js" in namespace "file"
    

v0.11.2

Compare Source

  • Add a shim function for unbundled uses of require (#​1202)

    Modules in CommonJS format automatically get three variables injected into their scope: module, exports, and require. These allow the code to import other modules and to export things from itself. The bundler automatically rewrites uses of module and exports to refer to the module's exports and certain uses of require to a helper function that loads the imported module.

    Not all uses of require can be converted though, and un-converted uses of require will end up in the output. This is problematic because require is only present at run-time if the output is run as a CommonJS module. Otherwise require is undefined, which means esbuild's behavior is inconsistent between compile-time and run-time. The module and exports variables are objects at compile-time and run-time but require is a function at compile-time and undefined at run-time. This causes code that checks for typeof require to have inconsistent behavior:

    if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') {
      console.log('CommonJS detected')
    }
    

    In the above example, ideally CommonJS detected would always be printed since the code is being bundled with a CommonJS-aware bundler. To fix this, esbuild will now substitute references to require with a stub __require function when bundling if the output format is something other than CommonJS. This should ensure that require is now consistent between compile-time and run-time. When bundled, code that uses unbundled references to require will now look something like this:

    var __require = (x) => {
      if (typeof require !== "undefined")
        return require(x);
      throw new Error('Dynamic require of "' + x + '" is not supported');
    };
    
    var __commonJS = (cb, mod) => () => (mod || cb((mod = {exports: {}}).exports, mod), mod.exports);
    
    var require_example = __commonJS((exports, module) => {
      if (typeof __require === "function" && typeof exports === "object" && typeof module === "object") {
        console.log("CommonJS detected");
      }
    });
    
    require_example();
    
  • Fix incorrect caching of internal helper function library (#​1292)

    This release fixes a bug where running esbuild multiple times with different configurations sometimes resulted in code that would crash at run-time. The bug was introduced in version 0.11.19 and happened because esbuild's internal helper function library is parsed once and cached per configuration, but the new profiler name option was accidentally not included in the cache key. This option is now included in the cache key so this bug should now be fixed.

  • Minor performance improvements

    This release contains some small performance improvements to offset an earlier minor performance regression due to the addition of certain features such as hashing for entry point files. The benchmark times on the esbuild website should now be accurate again (versions of esbuild after the regression but before this release were slightly slower than the benchmark).

v0.11.1

Compare Source

  • Allow esbuild to be restarted in Deno (#​1238)

    The esbuild API for Deno has an extra function called stop() that doesn't exist in esbuild's API for node. This is because Deno doesn't provide a way to stop esbuild automatically, so calling stop() is required to allow Deno to exit. However, once stopped the esbuild API could not be restarted.

    With this release, you can now continue to use esbuild after calling stop(). This will restart esbuild's API and means that you will need to call stop() again for Deno to be able to exit. This feature was contributed by @​lucacasonato.

  • Fix code splitting edge case (#​1252)

    This release fixes an edge case where bundling with code splitting enabled generated incorrect code if multiple ESM entry points re-exported the same re-exported symbol from a CommonJS file. In this case the cross-chunk symbol dependency should be the variable that holds the return value from the require() call instead of the original ESM named import clause item. When this bug occurred, the generated ESM code contained an export and import for a symbol that didn't exist, which caused a module initialization error. This case should now work correctly.

  • Fix code generation with declare class fields (#​1242)

    This fixes a bug with TypeScript code that uses declare on a class field and your tsconfig.json file has "useDefineForClassFields": true. Fields marked as declare should not be defined in the generated code, but they were incorrectly being declared as undefined. These fields are now correctly omitted from the generated code.

  • Annotate module wrapper functions in debug builds (#​1236)

    Sometimes esbuild needs to wrap certain modules in a function when bundling. This is done both for lazy evaluation and for CommonJS modules that use a top-level return statement. Previously these functions were all anonymous, so stack traces for errors thrown during initialization looked like this:

    Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
        at getElectronPath (out.js:16:13)
        at out.js:19:21
        at out.js:1:45
        at out.js:24:3
        at out.js:1:45
        at out.js:29:3
        at out.js:1:45
        at Object.<anonymous> (out.js:33:1)
    

    This release adds names to these anonymous functions when minification is disabled. The above stack trace now looks like this:

    Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
        at getElectronPath (out.js:19:15)
        at node_modules/electron/index.js (out.js:22:23)
        at __require (out.js:2:44)
        at src/base/window.js (out.js:29:5)
        at __require (out.js:2:44)
        at src/base/kiosk.js (out.js:36:5)
        at __require (out.js:2:44)
        at Object.<anonymous> (out.js:41:1)
    

    This is similar to Webpack's development-mode behavior:

    Error: Electron failed to install correctly, please delete node_modules/electron and try installing again
        at getElectronPath (out.js:23:11)
        at Object../node_modules/electron/index.js (out.js:27:18)
        at __webpack_require__ (out.js:96:41)
        at Object../src/base/window.js (out.js:49:1)
        at __webpack_require__ (out.js:96:41)
        at Object../src/base/kiosk.js (out.js:38:1)
        at __webpack_require__ (out.js:96:41)
        at out.js:109:1
        at out.js:111:3
        at Object.<anonymous> (out.js:113:12)
    

    These descriptive function names will additionally be available when using a profiler such as the one included in the "Performance" tab in Chrome Developer Tools. Previously all functions were named (anonymous) which made it difficult to investigate performance issues during bundle initialization.

  • Add CSS minification for more cases

    The following CSS minification cases are now supported:

    • The CSS margin property family is now minified including combining the margin-top, margin-right, margin-bottom, and margin-left properties into a single margin property.

    • The CSS padding property family is now minified including combining the padding-top, padding-right, padding-bottom, and padding-left properties into a single padding property.

    • The CSS border-radius property family is now minified including combining the border-top-left-radius, border-top-right-radius, border-bottom-right-radius, and border-bottom-left-radius properties into a single border-radius property.

    • The four special pseudo-elements ::before, ::after, ::first-line, and ::first-letter are allowed to be parsed with one : for legacy reasons, so the :: is now converted to : for these pseudo-elements.

    • Duplicate CSS rules are now deduplicated. Only the last rule is kept, since that's the only one that has any effect. This applies for both top-level rules and nested rules.

  • Preserve quotes around properties when minification is disabled (#​1251)

    Previously the parser did not distinguish between unquoted and quoted properties, since there is no semantic difference. However, some tools such as Google Closure Compiler with "advanced mode" enabled attach their own semantic meaning to quoted properties, and processing code intended for Google Closure Compiler's advanced mode with esbuild was changing those semantics. The distinction between unquoted and quoted properties is now made in the following cases:

    import * as ns from 'external-pkg'
    console.log([
      { x: 1, 'y': 2 },
      { x() {}, 'y'() {} },
      class { x = 1; 'y' = 2 },
      class { x() {}; 'y'() {} },
      { x: x, 'y': y } = z,
      [x.x, y['y']],
      [ns.x, ns['y']],
    ])
    

    The parser will now preserve the quoted properties in these cases as long as --minify-syntax is not enabled. This does not mean that esbuild is officially supporting Google Closure Compiler's advanced mode, just that quoted properties are now preserved when the AST is pretty-printed. Google Closure Compiler's advanced mode accepts a language that shares syntax with JavaScript but that deviates from JavaScript semantics and there could potentially be other situations where preprocessing code intended for Google Closure Compiler's advanced mode with esbuild first causes it to break. If that happens, that is not a bug with esbuild.

v0.11.0

Compare Source

This release contains backwards-incompatible changes. Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as recommended by npm). You should either be pinning the exact version of esbuild in your package.json file or be using a version range syntax that only accepts patch upgrades such as ~0.10.0. See the documentation about semver for more information.

The changes in this release mostly relate to how entry points are handled. The way output paths are generated has changed in some cases, so you may need to update how you refer to the output path for a given entry point when you update to this release (see below for details). These breaking changes are as follows:

  • Change how require() and import() of ESM works (#​667, #​706)

    Previously if you call require() on an ESM file, or call import() on an ESM file with code splitting disabled, esbuild would convert the ESM file to CommonJS. For example, if you had the following input files:

    // cjs-file.js
    console.log(require('./esm-file.js').foo)
    
    // esm-file.js
    export let foo = bar()
    

    The previous bundling behavior would generate something like this:

    var require_esm_file = __commonJS((exports) => {
      __markAsModule(exports);
      __export(exports, {
        foo: () => foo
      });
      var foo = bar();
    });
    console.log(require_esm_file().foo);
    

    This behavior has been changed and esbuild now generates something like this instead:

    var esm_file_exports = {};
    __export(esm_file_exports, {
      foo: () => foo
    });
    var foo;
    var init_esm_file = __esm(() => {
      foo = bar();
    });
    console.log((init_esm_file(), esm_file_exports).foo);
    

    The variables have been pulled out of the lazily-initialized closure and are accessible to the rest of the module's scope. Some benefits of this approach:

    • If another file does import {foo} from "./esm-file.js", it will just reference foo directly and will not pay the performance penalty or code size overhead of the dynamic property accesses that come with CommonJS-style exports. So this improves performance and reduces code size in some cases.

    • This fixes a long-standing bug (#​706) where entry point exports could be broken if the entry point is a target of a require() call and the output format was ESM. This happened because previously calling require() on an entry point converted it to CommonJS, which then meant it only had a single default export, and the exported variables were inside the CommonJS closure and inaccessible to an ESM-style export {} clause. Now calling require() on an entry point only causes it to be lazily-initialized but all exports are still in the module scope and can still be exported using a normal export {} clause.

    • Now that this has been changed, import() of a module with top-level await (#​253) is now allowed when code splitting is disabled. Previously this didn't work because import() with code splitting disabled was implemented by converting the module to CommonJS and using Promise.resolve().then(() => require()), but converting a module with top-level await to CommonJS is impossible because the CommonJS call signature must be synchronous. Now that this implemented using lazy initialization instead of CommonJS conversion, the closure wrapping the ESM file can now be async and the import() expression can be replaced by a call to the lazy initializer.

    • Adding the ability for ESM files to be lazily-initialized is an important step toward additional future code splitting improvements including: manual chunk names (#​207), correct import evaluation order (#​399), and correct top-level await evaluation order (#​253). These features all need to make use of deferred evaluation of ESM code.

    In addition, calling require() on an ESM file now recursively wraps all transitive dependencies of that file instead of just wrapping that ESM file itself. This is an increase in the size of the generated code, but it is important for correctness (#​667). Calling require() on a module means its evaluation order is determined at run-time, which means the evaluation order of all dependencies must also be determined at run-time. If you don't want the increase in code size, you should use an import statement instead of a require() call.

  • Dynamic imports now use chunk names instead of entry names (#​1056)

    Previously the output paths of dynamic imports (files imported using the import() syntax) were determined by the --entry-names= setting. However, this can cause problems if you configure the --entry-names= setting to omit both [dir] and [hash] because then two dynamic imports with the same name will cause an output file name collision.

    Now dynamic imports use the --chunk-names= setting instead, which is used for automatically-generated chunks. This setting is effectively required to include [hash] so dynamic import name collisions should now be avoided.

    In addition, dynamic imports no longer affect the automatically-computed default value of outbase. By default outbase is computed to be the lowest common ancestor directory of all entry points. Previously dynamic imports were considered entry points in this calculation so adding a dynamic entry point could unexpectedly affect entry point output file paths. This issue has now been fixed.

  • Allow custom output paths for individual entry points

    By default, esbuild will automatically generate an output path for each entry point by computing the relative path from the outbase directory to the entry point path, and then joining that relative path to the outdir directory. The output path can be customized using outpath, but that only works for a single file. Sometimes you may need custom output paths while using multiple entry points. You can now do this by passing the entry points as a map instead of an array:

    • CLI
      esbuild out1=in1.js out2=in2.js --outdir=out

    • JS

      esbuild.build({
        entryPoints: {
          out1: 'in1.js',
          out2: 'in2.js',
        },
        outdir: 'out',
      })
      
    • Go

      api.Build(api.BuildOptions{
        EntryPointsAdvanced: []api.EntryPoint{{
          OutputPath: "out1",
          InputPath: "in1.js",
        }, {
          OutputPath: "out2",
          InputPath: "in2.js",
        }},
        Outdir: "out",
      })
      

    This will cause esbuild to generate the files out/out1.js and out/out2.js inside the output directory. These custom output paths are used as input for the --entry-names= path template setting, so you can use something like --entry-names=[dir]/[name]-[hash] to add an automatically-computed hash to each entry point while still using the custom output path.

  • Derive entry point output paths from the original input path (#​945)

    Previously esbuild would determine the output path for an entry point by looking at the post-resolved path. For example, running esbuild --bundle react --outdir=out would generate the output path out/index.js because the input path react was resolved to node_modules/react/index.js. With this release, the output path is now determined by looking at the pre-resolved path. For example, running esbuild --bundle react --outdir=out now generates the output path out/react.js. If you need to keep using the output path that esbuild previously generated with the old behavior, you can use the custom output path feature (described above).

  • Use the file namespace for file entry points (#​791)

    Plugins that contain an onResolve callback with the file filter don't apply to entry point paths because it's not clear that entry point paths are files. For example, you could potentially bundle an entry point of https://www.example.com/file.js with a HTTP plugin that automatically downloads data from the server at that URL. But this behavior can be unexpected for people writing plugins.

    With this release, esbuild will do a quick check first to see if the entry point path exists on the file system before running plugins. If it exists as a file, the namespace will now be file for that entry point path. This only checks the exact entry point name and doesn't attempt to search for the file, so for example it won't handle cases where you pass a package path as an entry point or where you pass an entry point without an extension. Hopefully this should help improve this situation in the common case where the entry point is an exact path.

In addition to the breaking changes above, the following features are also included in this release:

  • Warn about mutation of private methods (#​1067)

    Mutating a private method in JavaScript is not allowed, and will throw at run-time:

    class Foo {
      #method() {}
      mutate() {
        this.#method = () => {}
      }
    }
    

    This is the case both when esbuild passes the syntax through untransformed and when esbuild transforms the syntax into the equivalent code that uses a WeakSet to emulate private methods in older browsers. However, it's clear from this code that doing this will always throw, so this code is almost surely a mistake. With this release, esbuild will now warn when you do this. This change was contributed by @​jridgewell.

  • Fix some obscure TypeScript type parsing edge cases

    In TypeScript, type parameters come after a type and are placed in angle brackets like Foo<T>. However, certain built-in types do not accept type parameters including primitive types such as number. This means if (x as number < 1) {} is not a syntax error while if (x as Foo < 1) {} is a syntax error. This release changes TypeScript type parsing to allow type parameters in a more restricted set of situations, which should hopefully better resolve these type parsing ambiguities.

v0.10.2

Compare Source

  • Fix a crash that was introduced in the previous release (#​1064)

    This crash happens when code splitting is active and there is a CSS entry point as well as two or more JavaScript entry points. There is a known issue where CSS bundling does not work when code splitting is active (code splitting is still a work in progress, see #​608) so doing this will likely not work as expected. But esbuild obviously shouldn't crash. This release fixes the crash, although esbuild still does not yet generate the correct CSS output in this case.

  • Fix private fields inside destructuring assignment (#​1066)

    Private field syntax (i.e. this.#field) is supported for older language targets by converting the code into accesses into a WeakMap. However, although regular assignment (i.e. this.#field = 1) was handled destructuring assignment (i.e. [this.#field] = [1]) was not handled due to an oversight. Support for private fields inside destructuring assignment is now included with this release.

  • Fix an issue with direct eval and top-level symbols

    It was previously the case that using direct eval caused the file containing it to be considered a CommonJS file, even if the file used ESM syntax. This was because the evaluated code could potentially attempt to interact with top-level symbols by name and the CommonJS closure was used to isolate those symbols from other modules so their names could be preserved (otherwise their names may need to be renamed to avoid collisions). However, ESM files are no longer convertable to CommonJS files due to the need to support top-level await.

    This caused a bug where scope hoisting could potentially merge two modules containing direct eval and containing the same top-level symbol name into the same scope. These symbols were prevented from being renamed due to the direct eval, which caused a syntax error at run-time due to the name collision.

    Because of this, esbuild is dropping the guarantee that using direct eval in an ESM file will be able to access top-level symbols. These symbols are now free to be renamed to avoid name collisions, and will now be minified when identifier minification is enabled. This is unlikely to affect real-world code because most real-world uses of direct eval only attempt to access local variables, not top-level symbols.

    Using direct eval in an ESM file when bundling with esbuild will generate a warning. The warning is not new and is present in previous releases of esbuild as well. The way to avoid the warning is to avoid direct eval, since direct eval is somewhat of an anti-pattern and there are better alternatives.

v0.10.1

Compare Source

  • Expose metafile to onRebuild in watch mode (#​1057)

    Previously the build results returned to the watch mode onRebuild callback was missing the metafile property when the metafile: true option was present. This bug has been fixed.

  • Add a formatMessages API (#​1058)

    This API lets you print log messages to the terminal using the same log format that esbuild itself uses. This can be used to filter esbuild's warnings while still making the output look the same. Here's an example of calling this API:

    import esbuild from 'esbuild'
    
    const formatted = await esbuild.formatMessages([{
      text: '"test" has already been declared',
      location: { file: 'file.js', line: 2, column: 4, length: 4, lineText: 'let test = "second"' },
      notes: [{
        text: '"test" was originally declared here',
        location: { file: 'file.js', line: 1, column: 4, length: 4, lineText: 'let test = "first"' },
      }],
    }], {
      kind: 'error',
      color: true,
      terminalWidth: 100,
    })
    
    process.stdout.write(formatted.join(''))
    
  • Remove the file splitting optimization (#​998)

    This release removes the "file splitting optimization" that has up to this point been a part of esbuild's code splitting algorithm. This optimization allowed code within a single file to end up in separate chunks as long as that code had no side effects. For example, bundling two entry points that both use a disjoint set of code from a shared file consisting only of code without side effects would previously not generate any shared code chunks at all.

    This optimization is being removed because the top-level await feature was added to JavaScript after this optimization was added, and performing this optimization in the presence of top-level await is more difficult than before. The correct evaulation order of a module graph containing top-level await is extremely complicated and is specified at the module boundary. Moving code that is marked as having no side effects across module boundaries under these additional constraints is even more complexity and is getting in the way of implementing top-level await. So the optimization has been removed to unblock work on top-level await, which esbuild must support.

v0.10.0

Compare Source

This release contains backwards-incompatible changes. Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as recommended by npm). You should either be pinning the exact version of esbuild in your package.json file or be using a version range syntax that only accepts patch upgrades such as ~0.9.0. See the documentation about semver for more information.

That said, there are no breaking API changes in this release. The breaking changes are instead about how input files are interpreted and/or how output files are generated in some cases. So upgrading should be relatively straightforward as your API calls should still work the same way, but please make sure to test your code when you upgrade because the output may be different. These breaking changes are as follows:

  • No longer support module or exports in an ESM file (#​769)

    This removes support for using CommonJS exports in a file with ESM exports. Previously this worked by converting the ESM file to CommonJS and then mixing the CommonJS and ESM exports into the same exports object. But it turns out that supporting this is additional complexity for the bundler, so it has been removed. It's also not something that works in real JavaScript environments since modules will never support both export syntaxes at once.

    Note that this doesn't remove support for using require in ESM files. Doing this still works (and can be made to work in a real ESM environment by assigning to globalThis.require). This also doesn't remove support for using import in CommonJS files. Doing this also still works.

  • No longer change import() to require() (#​1029)

    Previously esbuild's transform for import() matched TypeScript's behavior, which is to transform it into Promise.resolve().then(() => require()) when the current output format is something other than ESM. This was done when an import is external (i.e. not bundled), either due to the expression not being a string or due to the string matching an external import path.

    With this release, esbuild will no longer do this. Now import() expressions will be preserved in the output instead. These expressions can be handled in non-ESM code by arranging for the import identifier to be a function that imports ESM code. This is how node works, so it will now be possible to use import() with node when the output format is something other than ESM.

  • Run-time export * as statements no longer convert the file to CommonJS

    Certain export * as statements require a bundler to evaluate them at run-time instead of at compile-time like the JavaScript specification. This is the case when re-exporting symbols from an external file and a file in CommonJS format.

    Previously esbuild would handle this by converting the module containing the export * as statement to CommonJS too, since CommonJS exports are evaluated at run-time while ESM exports are evaluated at bundle-time. However, this is undesirable because tree shaking only works for ESM, not for CommonJS, and the CommonJS wrapper causes additional code bloat. Another upcoming problem is that top-level await cannot work within a CommonJS module because CommonJS require() is synchronous.

    With this release, esbuild will now convert modules containing a run-time export * as statement to a special ESM-plus-dynamic-fallback mode. In this mode, named exports present at bundle time can still be imported directly by name, but any imports that don't match one of the explicit named imports present at bundle time will be converted to a property access on the fallback object instead of being a bundle error. These property accesses are then resolved at run-time and will be undefined if the export is missing.

  • Change whether certain files are interpreted as ESM or CommonJS (#​1043)

    The bundling algorithm currently doesn't contain any logic that requires flagging modules as CommonJS vs. ESM beforehand. Instead it handles a superset and then sort of decides later if the module should be treated as CommonJS vs. ESM based on whether the module uses the module or exports variables and/or the exports keyword.

    With this release, files that follow node's rules for module types will be flagged as explicitly ESM. This includes files that end in .mjs and files within a package containing "type": "module" in the enclosing package.json file. The CommonJS module and exports features will be unavailable in these files. This matters most for files without any exports, since then it's otherwise ambiguous what the module type is.

    In addition, files without exports should now accurately fall back to being considered CommonJS. They should now generate a default export of an empty object when imported using an import statement, since that's what happens in node when you import a CommonJS file into an ESM file in node. Previously the default export could be undefined because these export-less files were sort of treated as ESM but with missing import errors turned into warnings instead.

    This is an edge case that rarely comes up in practice, since you usually never import things from a module that has no exports.

In addition to the breaking changes above, the following features are also included in this release:

  • Initial support for bundling with top-level await (#​253)

    Top-level await is a feature that lets you use an await expression at the top level (outside of an async function). Here is an example:

    let promise = fetch('https://www.example.com/data')
    export let data = await promise.then(x => x.json())
    

    Top-level await only works in ECMAScript modules, and does not work in CommonJS modules. This means that you must use an import statement or an import() expression to import a module containing top-level await. You cannot use require() because it's synchronous while top-level await is asynchronous. There should be a descriptive error message when you try to do this.

    This initial release only has limited support for top-level await. It is only supported with the esm output format, but not with the iife or cjs output formats. In addition, the compilation is not correct in that two modules that both contain top-level await and that are siblings in the import graph will be evaluated in serial instead of in parallel. Full support for top-level await will come in a future release.

  • Add the ability to set sourceRoot in source maps (#​1028)

    You can now use the --source-root= flag to set the sourceRoot field in source maps generated by esbuild. When a sourceRoot is present in a source map, all source paths are resolved relative to it. This is particularly useful when you are hosting compiled code on a server and you want to point the source files to a GitHub repo, such as what AMP does.

    Here is the description of sourceRoot from the source map specification:

    An optional source root, useful for relocating source files on a server or removing repeated values in the "sources" entry. This value is prepended to the individual entries in the "source" field. If the sources are not absolute URLs after prepending of the "sourceRoot", the sources are resolved relative to the SourceMap (like resolving script src in a html document).

    This feature was contributed by @​jridgewell.

  • Allow plugins to return custom file watcher paths

    Currently esbuild's watch mode automatically watches all file system paths that are handled by esbuild itself, and also automatically watches the paths of files loaded by plugins when the paths are in the file namespace. The paths of files that plugins load in namespaces other than the file namespace are not automatically watched.

    Also, esbuild never automatically watches any file system paths that are consulted by the plugin during its processing, since esbuild is not aware of those paths. For example, this means that if a plugin calls require.resolve(), all of the various "does this file exist" checks that it does will not be watched automatically. So if one of those files is created in the future, esbuild's watch mode will not rebuild automatically even though the build is now outdated.

    To fix this problem, this release introduces the watchFiles and watchDirs properties on plugin return values. Plugins can specify these to add additional custom file system paths to esbuild's internal watch list. Paths in the watchFiles array cause esbuild to rebuild if the file contents change, and paths in the watchDirs array cause esbuild to rebuild if the set of directory entry names changes for that directory path.

    Note that watchDirs does not cause esbuild to rebuild if any of the contents of files inside that directory are changed. It also does not recursively traverse through subdirectories. It only watches the set of directory entry names (i.e. the output of the Unix ls command).

v0.9.7

Compare Source

  • Add support for Android on ARM 64-bit (#​803)

    This release includes support for Android in the official esbuild package. It should now be possible to install and run esbuild on Android devices through npm.

  • Fix incorrect MIME types on Windows (#​1030)

    The web server built into esbuild uses the file extension to determine the value of the Content-Type header. This was previously done using the mime.TypeByExtension() function from Go's standard library. However, this function is apparently broken on Windows because installed programs can change MIME types in the Windows registry: golang/go#​32350. This release fixes the problem by using a copy of Go's mime.TypeByExtension() function without the part that reads from the Windows registry.

  • Using a top-level return inside an ECMAScript module is now forbidden

    The CommonJS module format is implemented as an anonymous function wrapper, so technically you can use a top-level return statement and it will actually work. Some packages in the wild use this to exit early from module initialization, so esbuild supports this. However, the ECMAScript module format doesn't allow top-level returns. With this release, esbuild no longer allows top-level returns in ECMAScript modules.

v0.9.6

Compare Source

  • Expose build options to plugins (#​373)

    Plugins can now access build options from within the plugin using the initialOptions property. For example:

    let nodeEnvPlugin = {
      name: 'node-env',
      setup(build) {
        const options = build.initialOptions
        options.define = options.define || {}
        options.define['process.env.NODE_ENV'] =
          options.minify ? '"production"' : '"development"'
      },
    }
    
  • Fix an edge case with the object spread transform (#​1017)

    This release fixes esbuild's object spread transform in cases where property assignment could be different than property definition. For example:

    console.log({
      get x() {},
      ...{x: 1},
    })
    

    This should print {x: 1} but transforming this through esbuild with --target=es6 causes the resulting code to throw an error. The problem is that esbuild currently transforms this code to a call to Object.assign and that uses property assignment semantics, which causes the assignment to throw (since you can't assign to a getter-only property).

    With this release, esbuild will now transform this into code that manually loops over the properties and copies them over one-by-one using Object.defineProperty instead. This uses property definition semantics which better matches the specification.

  • Fix a TypeScript parsing edge case with arrow function return types (#​1016)

    This release fixes the following TypeScript parsing edge case:

    ():Array<number>=>{return [1]}
    

    This was tripping up esbuild's TypeScript parser because the >= token was split into a > token and a = token because the > token is needed to close the type parameter list, but the = token was not being combined with the following > token to form a => token. This is normally not an issue because there is normally a space in between the > and the => tokens here. The issue only happened when the spaces were removed. This bug has been fixed. Now after the >= token is split, esbuild will expand the = token into the following characters if possible, which can result in a =>, ==, or === token.

  • Enable faster synchronous transforms under a flag (#​1000)

    Currently the synchronous JavaScript API calls transformSync and buildSync spawn a new child process on every call. This is due to limitations with node's child_process API. Doing this means transformSync and buildSync are much slower than transform and build, which share the same child process across calls.

    There was previously a workaround for this limitation that uses node's worker_threads API and atomics to block the main thread while asynchronous communication happens in a worker, but that was reverted due to a bug in node's worker_threads implementation. Now that this bug has been fixed by node, I am re-enabling this workaround. This should result in transformSync and buildSync being much faster.

    This approach is experimental and is currently only enabled if the ESBUILD_WORKER_THREADS environment variable is present. If this use case matters to you, please try it out and let me know if you find any problems with it.

  • Update how optional chains are compiled to match new V8 versions (#​1019)

    An optional chain is an expression that uses the ?. operator, which roughly avoids evaluation of the right-hand side if the left-hand side is null or undefined. So a?.b is basically equivalent to a == null ? void 0 : a.b. When the language target is set to es2019 or below, esbuild will transform optional chain expressions into equivalent expressions that do not use the ?. operator.

    This transform is designed to match the behavior of V8 exactly, and is designed to do something similar to the equivalent transform done by the TypeScript compiler. However, V8 has recently changed its behavior in two cases:

    This release changes esbuild's transform to match V8's new behavior. The transform in the TypeScript compiler is still emulating the old behavior as of version 4.2.3, so these syntax forms should be avoided in TypeScript code for portability.

v0.9.5

Compare Source

  • Fix parsing of the [dir] placeholder (#​1013)

    The entry names feature in the previous release accidentally didn't include parsing for the [dir] placeholder, so the [dir] placeholder was passed through verbatim into the resulting output paths. This release fixes the bug, which means you can now use the [dir] placeholder. Sorry about the oversight.

v0.9.4

Compare Source

  • Enable hashes in entry point file paths (#​518)

    This release adds the new --entry-names= flag. It's similar to the --chunk-names= and --asset-names= flags except it sets the output paths for entry point files. The pattern defaults to [dir]/[name] which should be equivalent to the previous entry point output path behavior, so this should be a backward-compatible change.

    This change has the following consequences:

    • It is now possible for entry point output paths to contain a hash. For example, this now happens if you pass --entry-names=[dir]/[name]-[hash]. This means you can now use esbuild to generate output files such that all output paths have a hash in them, which means it should now be possible to serve the output files with an infinite cache lifetime so they are only downloaded once and then cached by the browser forever.

    • It is now possible to prevent the generation of subdirectories inside the output directory. Previously esbuild replicated the directory structure of the input entry points relative to the outbase directory (which defaults to the lowest common ancestor directory across all entry points). This value is substituted into the newly-added [dir] placeholder. But you can now omit it by omitting that placeholder, like this: --entry-names=[name].

    • Source map names should now be equal to the corresponding output file name plus an additional .map extension. Previously the hashes were content hashes, so the source map had a different hash than the corresponding output file because they had different contents. Now they have the same hash so finding the source map should now be easier (just add .map).

    • Due to the way the new hashing algorithm works, all chunks can now be generated fully in parallel instead of some chunks having to wait until their dependency chunks have been generated first. The import paths for dependency chunks are now swapped in after chunk generation in a second pass (detailed below). This could theoretically result in a speedup although I haven't done any benchmarks around this.

    Implementing this feature required overhauling how hashes are calculated to prevent the chicken-and-egg hashing problem due to dynamic imports, which can cause cycles in the import graph of the resulting output files when code splitting is enabled. Since generating a hash involved first hashing all of your dependencies, you could end up in a situation where you needed to know the hash to calculate the hash (if a file was a dependency of itself).

    The hashing algorithm now works in three steps (potentially subject to change in the future):

    1. The initial versions of all output files are generated in parallel, with temporary paths used for any imports of other output files. Each temporary path is a randomly-generated string that is unique for each output file. An initial source map is also generated at this step if source maps are enabled.

      The hash for the first step includes: the raw content of the output file excluding the temporary paths, the relative file paths of all input files present in that output file, the relative output path for the resulting output file (with [hash] for the hash that hasn't been computed yet), and contents of the initial source map.

    2. After the initial versions of all output files have been generated, calculate the final hash and final output path for each output file. Calculating the final output path involves substituting the final hash for the [hash] placeholder in the entry name template.

      The hash for the second step includes: the hash from the first step for this file and all of its transitive dependencies.

    3. After all output files have a final output path, the import paths in each output file for importing other output files are substituted. Source map offsets also have to be adjusted because the final output path is likely a different length than the temporary path used in the first step. This is also done in parallel for each output file.

      This whole algorithm roughly means the hash of a given output file should change if an only if any input file in that output file or any output file it depends on is changed. So the output path and therefore the browser's cache key should not change for a given output file in between builds if none of the relevant input files were changed.

  • Fix importing a path containing a ? character on Windows (#​989)

    On Windows, the ? character is not allowed in path names. This causes esbuild to fail to import paths containing this character. This is usually fine because people don't put ? in their file names for this reason. However, the import paths for some ancient CSS code contains the ? character as a hack to work around a bug in Internet Explorer:

    @&#8203;font-face {
      src:
        url("./icons.eot?#iefix") format('embedded-opentype'),
        url("./icons.woff2") format('woff2'),
        url("./icons.woff") format('woff'),
        url("./icons.ttf") format('truetype'),
        url("./icons.svg#icons") format('svg');
    }
    

    The intent is for the bundler to ignore the ?#iefix part. However, there may actually be a file called icons.eot?#iefix on the file system so esbuild checks the file system for both icons.eot?#iefix and icons.eot. This check was triggering this issue. With this release, an invalid path is considered the same as a missing file so bundling code like this should now work on Windows.

  • Parse and ignore the deprecated @-ms-viewport CSS rule (#​997)

    The @viewport rule has been deprecated and removed from the web. Modern browsers now completely ignore this rule. However, in theory it sounds like would still work for mobile versions of Internet Explorer, if those still exist. The https://ant.design/ library contains an instance of the @-ms-viewport rule and it currently causes a warning with esbuild, so this release adds support for parsing this rule to disable the warning.

  • Avoid mutating the binary executable file in place (#​963)

    This release changes the install script for the esbuild npm package to use the "rename a temporary file" approach instead of the "write the file directly" approach to replace the esbuild command stub file with the real binary executable. This should hopefully work around a problem with the pnpm package manager and its use of hard links.

  • Avoid warning about potential issues with sideEffects in packages (#​999)

    Bare imports such as import "foo" mean the package is only imported for its side effects. Doing this when the package contains "sideEffects": false in package.json causes a warning because it means esbuild will not import the file since it has been marked as having no side effects, even though the import statement clearly expects it to have side effects. This is usually caused by an incorrect sideEffects annotation in the package.

    However, this warning is not immediately actionable if the file containing the import statement is itself in a package. So with this release, esbuild will no longer issue this warning if the file containing the import is inside a node_modules folder. Note that even though the warning is no longer there, this situation can still result in a broken bundle if the sideEffects annotation is incorrect.

v0.9.3

Compare Source

  • Fix path resolution with the exports field for scoped packages

    This release fixes a bug where the exports field in package.json files was not being detected for scoped packages (i.e. packages of the form @scope/pkg-name instead of just pkg-name). The exports field should now be respected for these kinds of packages.

  • Improved error message in exports failure case

    Node's new conditional exports feature can be non-intuitive and hard to use. Now that esbuild supports this feature (as of version 0.9.0), you can get into a situation where it's impossible to import a package if the package's exports field in its package.json file isn't configured correctly.

    Previously the error message for this looked like this:

     > entry.js:1:7: error: Could not resolve "jotai" (mark it as external to exclude it from the bundle)
         1 │ import 'jotai'
           ╵        ~~~~~~~
       node_modules/jotai/package.json:16:13: note: The path "." is not exported by "jotai"
        16 │   "exports": {
           ╵              ^
    

    With this release, the error message will now provide additional information about why the package cannot be imported:

     > entry.js:1:7: error: Could not resolve "jotai" (mark it as external to exclude it from the bundle)
         1 │ import 'jotai'
           ╵        ~~~~~~~
       node_modules/jotai/package.json:16:13: note: The path "." is not currently exported by package "jotai"
        16 │   "exports": {
           ╵              ^
       node_modules/jotai/package.json:18:9: note: None of the conditions provided ("module", "require", "types") match any of the currently active conditions ("browser", "default", "import")
        18 │     ".": {
           ╵          ^
       entry.js:1:7: note: Consider using a "require()" call to import this package
         1 │ import 'jotai'
           ╵        ~~~~~~~
    

    In this case, one solution could be import this module using require() since this package provides an export for the require condition. Another solution could be to pass --conditions=module to esbuild since this package provides an export for the module condition (the types condition is likely not valid JavaScript code).

    This problem occurs because this package doesn't provide an import path for ESM code using the import condition and also doesn't provide a fallback import path using the default condition.

  • Mention glob syntax in entry point error messages (#​976)

    In this release, including a * in the entry point path now causes the failure message to tell you that glob syntax must be expanded first before passing the paths to esbuild. People that hit this are usually converting an existing CLI command to a JavaScript API call and don't know that glob expansion is done by their shell instead of by esbuild. An appropriate fix is to use a library such as glob to expand the glob pattern first before passing the paths to esbuild.

  • Raise certain VM versions in the JavaScript feature compatibility table

    JavaScript VM feature compatibility data is derived from this dataset: https://kangax.github.io/compat-table/. The scripts that process the dataset expand the data to include all VM versions that support a given feature (e.g. chrome44, chrome45, chrome46, ...) so esbuild takes the minimum observed version as the first version for which the feature is supported.

    However, some features can have subtests that each check a different aspect of the feature. In this case the desired version is the minimum version within each individual subtest, but the maximum of those versions across all subtests (since esbuild should only use the feature if it works in all cases). Previously esbuild computed the minimum version across all subtests, but now esbuild computes the maximum version across all subtests. This means esbuild will now lower JavaScript syntax in more cases.

  • Mention the configured target environment in error messages (#​975)

    Using newer JavaScript syntax with an older target environment (e.g. chrome10) can cause a build error if esbuild doesn't support transforming that syntax such that it is compatible with that target environment. Previously the error message was generic but with this release, the target environment is called outp explicitly in the error message. This is helpful if esbuild is being wrapped by some other tool since the other tool can obscure what target environment is actually being passed to esbuild.

  • Fix an issue with Unicode and source maps

    This release fixes a bug where non-ASCII content that ended up in an output file but that was not part of an input file could throw off source mappings. An example of this would be passing a string containing non-ASCII characters to the globalName setting with the minify setting active and the charset setting set to utf8. The conditions for this bug are fairly specific and unlikely to be hit, so it's unsurprising that this issue hasn't been discovered earlier. It's also unlikely that this issue affected real-world code.

    The underlying cause is that while the meaning of column numbers in source maps is undefined in the specification, in practice most tools treat it as the number of UTF-16 code units from the start of the line. The bug happened because column increments for outside-of-file characters were incorrectly counted using byte offsets instead of UTF-16 code unit counts.

v0.9.2

Compare Source

  • Fix export name annotations in CommonJS output for node (#​960)

    The previous release introduced a regression that caused a syntax error when building ESM files that have a default export with --platform=node. This is because the generated export contained the default keyword like this: 0 && (module.exports = {default});. This regression has been fixed.

v0.9.1

Compare Source

  • Fix bundling when parent directory is inaccessible (#​938)

    Previously bundling with esbuild when a parent directory is inaccessible did not work because esbuild would try to read the directory to search for a node_modules folder and would then fail the build when that failed. In practice this caused issues in certain Linux environments where a directory close to the root directory was inaccessible (e.g. on Android). With this release, esbuild will treat inaccessible directories as empty to allow for the node_modules search to continue past the inaccessible directory and into its parent directory. This means it should now be possible to bundle with esbuild in these situations.

  • Avoid allocations in JavaScript API stdout processing (#​941)

    This release improves the efficiency of the JavaScript API. The API runs the binary esbuild executable in a child process and then communicates with it over stdin/stdout. Previously the stdout buffer containing the remaining partial message was copied after each batch of messages due to a bug. This was unintentional and unnecessary, and has been removed. Now this part of the code no longer involves any allocations. This fix was contributed by @​jridgewell.

  • Support conditional @import syntax when not bundling (#​953)

    Previously conditional CSS imports such as @import "print.css" print; was not supported at all and was considered a syntax error. With this release, it is now supported in all cases except when bundling an internal import. Support for bundling internal CSS imports is planned but will happen in a later release.

  • Always lower object spread and rest when targeting V8 (#​951)

    This release causes object spread (e.g. a = {...b}) and object rest (e.g. {...a} = b) to always be lowered to a manual implementation instead of using native syntax when the --target= parameter includes a V8-based JavaScript runtime such as chrome, edge, or node. It turns out this feature is implemented inefficiently in V8 and copying properties over to a new object is around a 2x performance improvement. In addition, doing this manually instead of using the native implementation generates a lot less work for the garbage collector. You can see V8 bug 11536 for details. If the V8 performance bug is eventually fixed, the translation of this syntax will be disabled again for V8-based targets containing the bug fix.

  • Fix object rest return value (#​956)

    This release fixes a bug where the value of an object rest assignment was incorrect if the object rest assignment was lowered:

    // This code was affected
    let x, y
    console.log({x, ...y} = {x: 1, y: 2})
    

    Previously this code would incorrectly print {y: 2} (the value assigned to y) when the object rest expression was lowered (i.e. with --target=es2017 or below). Now this code will correctly print {x: 1, y: 2} instead. This bug did not affect code that did not rely on the return value of the assignment expression, such as this code:

    // This code was not affected
    let x, y
    ({x, ...y} = {x: 1, y: 2})
    
  • Basic support for CSS page margin rules (#​955)

    There are 16 different special at-rules that can be nested inside the @page rule. They are defined in this specification. Previously esbuild treated these as unknown rules, but with this release esbuild will now treat these as known rules. The only real difference in behavior is that esbuild will no longer warn about these rules being unknown.

  • Add export name annotations to CommonJS output for node

    When you import a CommonJS file using an ESM import statement in node, the default import is the value of module.exports in the CommonJS file. In addition, node attempts to generate named exports for properties of the module.exports object.

    Except that node doesn't actually ever look at the properties of that object to determine the export names. Instead it parses the CommonJS file and scans the AST for certain syntax patterns. A full list of supported patterns can be found in the documentation for the cjs-module-lexer package. This library doesn't currently support the syntax patterns used by esbuild.

    While esbuild could adapt its syntax to these patterns, the patterns are less compact than the ones used by esbuild and doing this would lead to code bloat. Supporting two separate ways of generating export getters would also complicate esbuild's internal implementation, which is undesirable.

    Another alternative could be to update the implementation of cjs-module-lexer to support the specific patterns used by esbuild. This is also undesirable because this pattern detection would break when minification is enabled, this would tightly couple esbuild's output format with node and prevent esbuild from changing it, and it wouldn't work for existing and previous versions of node that still have the old version of this library.

    Instead, esbuild will now add additional code to "annotate" ESM files that have been converted to CommonJS when esbuild's platform has been set to node. The annotation is dead code but is still detected by the cjs-module-lexer library. If the original ESM file has the exports foo and bar, the additional annotation code will look like this:

    0 && (module.exports = {foo, bar});
    

    This allows you to use named imports with an ESM import statement in node (previously you could only use the default import):

    import { foo, bar } from './file-built-by-esbuild.cjs'
    

v0.9.0

Compare Source

This release contains backwards-incompatible changes. Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as recommended by npm). You should either be pinning the exact version of esbuild in your package.json file or be using a version range syntax that only accepts patch upgrades such as ^0.8.0. See the documentation about semver for more information.

  • Add support for node's exports field in package.json files (#​187)

    This feature was recently added to node. It allows you to rewrite what import paths inside your package map to as well as to prevent people from importing certain files in your package. Adding support for this to esbuild is a breaking change (i.e. code that was working fine before can easily stop working) so adding support for it has been delayed until this breaking change release.

    One way to use this feature is to remap import paths for your package. For example, this would remap an import of your-pkg/esm/lib.js (the "public" import path) to your-pkg/dist/esm/lib.js (the "private" file system path):

    {
      "name": "your-pkg",
      "exports": {
        "./esm/*": "./dist/esm/*",
        "./cjs/*": "./dist/cjs/*"
      }
    }
    

    Another way to use this feature is to have conditional imports where the same import path can mean different things in different situations. For example, this would remap require('your-pkg') to your-pkg/required.cjs and import 'your-pkg' to your-pkg/imported.mjs:

    {
      "name": "your-pkg",
      "exports": {
        "import": "./imported.mjs",
        "require": "./required.cjs"
      }
    }
    

    There is built-in support for the import and require conditions depending on the kind of import and the browser and node conditions depending on the current platform. In addition, the default condition always applies regardless of the current configuration settings and can be used as a catch-all fallback condition.

    Note that when you use conditions, your package may end up in the bundle multiple times! This is a subtle issue that can cause bugs due to duplicate copies of your code's state in addition to bloating the resulting bundle. This is commonly known as the dual package hazard. The primary way of avoiding this is to put all of your code in the require condition and have the import condition just be a light wrapper that calls require on your package and re-exports the package using ESM syntax.

    There is also support for custom conditions with the --conditions= flag. The meaning of these is entirely up to package authors. For example, you could imagine a package that requires you to configure --conditions=test,en-US. Node has currently only endorsed the development and production custom conditions for recommended use.

  • Remove the esbuild.startService() API

    Due to #​656, Calling service.stop() no longer does anything, so there is no longer a strong reason for keeping the esbuild.startService() API around. The primary thing it currently does is just make the API more complicated and harder to use. You can now just call esbuild.build() and esbuild.transform() directly instead of calling esbuild.startService().then(service => service.build()) or esbuild.startService().then(service => service.transform()).

    If you are using esbuild in the browser, you now need to call esbuild.initialize({ wasmURL }) and wait for the returned promise before calling esbuild.transform(). It takes the same options that esbuild.startService() used to take. Note that the esbuild.buildSync() and esbuild.transformSync() APIs still exist when using esbuild in node. Nothing has changed about the synchronous esbuild APIs.

  • Remove the metafile from outputFiles (#​633)

    Previously using metafile with the API is unnecessarily cumbersome because you have to extract the JSON metadata from the output file yourself instead of it just being provided to you as a return value. This is especially a bummer if you are using write: false because then you need to use a for loop over the output files and do string comparisons with the file paths to try to find the one corresponding to the metafile. Returning the metadata directly is an important UX improvement for the API. It means you can now do this:

    const result = await esbuild.build({
      entryPoints: ['entry.js'],
      bundle: true,
      metafile: true,
    })
    console.log(result.metafile.outputs)
    
  • The banner and footer options are now language-specific (#​712)

    The --banner= and --footer= options now require you to pass the file type:

    • CLI:

      esbuild --banner:js=//banner --footer:js=//footer
      esbuild --banner:css=/*banner*/ --footer:css=/*footer*/
      
    • JavaScript

      esbuild.build({
        banner: { js: '//banner', css: '/*banner*/' },
        footer: { js: '//footer', css: '/*footer*/' },
      })
      
    • Go

      api.Build(api.BuildOptions{
        Banner: map[string]string{"js": "//banner"},
        Footer: map[string]string{"js": "//footer"},
      })
      api.Build(api.BuildOptions{
        Banner: map[string]string{"css": "/*banner*/"},
        Footer: map[string]string{"css": "/*footer*/"},
      })
      

    This was changed because the feature was originally added in a JavaScript-specific manner, which was an oversight. CSS banners and footers must be separate from JavaScript banners and footers to avoid injecting JavaScript syntax into your CSS files.

  • The extensions .mjs and .cjs are no longer implicit

    Previously the "resolve extensions" setting included .mjs and .cjs but this is no longer the case. This wasn't a good default because it doesn't match node's behavior and could break some packages. You now have to either explicitly specify these extensions or configure the "resolve extensions" setting yourself.

  • Remove the --summary flag and instead just always print a summary (#​704)

    The summary can be disabled if you don't want it by passing --log-level=warning instead. And it can be enabled in the API by setting logLevel: 'info'. I'm going to try this because I believe it will improve the UX. People have this problem with esbuild when they first try it where it runs so quickly that they think it must be broken, only to later discover that it actually worked fine. While this is funny, it seems like a good indication that the UX could be improved. So I'm going to try automatically printing a summary to see how that goes. Note that the summary is not printed if incremental builds are active (this includes the watch and serve modes).

  • Rename --error-limit= to --log-limit=

    This parameter has been renamed because it now applies to both warnings and errors, not just to errors. Previously setting the error limit did not apply any limits to the number of warnings printed, which could sometimes result in a deluge of warnings that are problematic for Windows Command Prompt, which is very slow to print to and has very limited scrollback. Now the log limit applies to the total number of log messages including both errors and warnings, so no more than that number of messages will be printed. The log usually prints log messages immediately but it will now intentionally hold back warnings when approaching the limit to make room for possible future errors during a build. So if a build fails you should be guaranteed to see an error message (i.e. warnings can't use up the entire log limit and then prevent errors from being printed).

  • Remove the deprecated --avoid-tdz option

    This option is now always enabled and cannot be disabled, so it is being removed from the API. The existing API parameter no longer does anything so this removal has no effect the generated output.

  • Remove SpinnerBusy and SpinnerIdle from the Go API

    These options were part of an experiment with the CLI that didn't work out. Watch mode no longer uses a spinner because it turns out people want to be able to interleave esbuild's stderr pipe with other tools and were getting tripped up by the spinner animation. These options no longer do anything and have been removed.

v0.8.57

Compare Source

  • 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

Compare Source

  • 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: https://github.com/facebook/jest/issues/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

Compare Source

  • 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

Compare Source

  • 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

Compare Source

  • 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

Compare Source

  • 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.


Configuration

📅 Schedule: At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box.

This PR has been generated by Renovate Bot.

This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [esbuild](https://github.com/evanw/esbuild) | devDependencies | minor | [`0.8.51` -> `0.12.15`](https://renovatebot.com/diffs/npm/esbuild/0.8.51/0.12.15) | --- ### Release Notes <details> <summary>evanw/esbuild</summary> ### [`v0.12.15`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01215) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.14...v0.12.15) - Fix a bug with `var()` in CSS color lowering ([#&#8203;1421](https://github.com/evanw/esbuild/issues/1421)) This release fixes a bug with esbuild's handling of the `rgb` and `hsl` color functions when they contain `var()`. Each `var()` token sequence can be substituted for any number of tokens including zero or more than one, but previously esbuild's output was only correct if each `var()` inside of `rgb` or `hsl` contained exactly one token. With this release, esbuild will now not attempt to transform newer CSS color syntax to older CSS color syntax if it contains `var()`: /* Original code */ a { color: hsl(var(--hs), var(--l)); } /* Old output */ a { color: hsl(var(--hs), ,, var(--l)); } /* New output */ a { color: hsl(var(--hs), var(--l)); } The bug with the old output above happened because esbuild considered the arguments to `hsl` as matching the pattern `hsl(h s l)` which is the new space-separated form allowed by [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#the-hsl-notation). Then esbuild tried to convert this to the form `hsl(h, s, l)` which is more widely supported by older browsers. But this substitution doesn't work in the presence of `var()`, so it has now been disabled in that case. ### [`v0.12.14`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01214) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.13...v0.12.14) - Fix the `file` loader with custom namespaces ([#&#8203;1404](https://github.com/evanw/esbuild/issues/1404)) This fixes a regression from version 0.12.12 where using a plugin to load an input file with the `file` loader in a custom namespace caused esbuild to write the contents of that input file to the path associated with that namespace instead of to a path inside of the output directory. With this release, the `file` loader should now always copy the file somewhere inside of the output directory. ### [`v0.12.13`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01213) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.12...v0.12.13) - Fix using JS synchronous API from from non-main threads ([#&#8203;1406](https://github.com/evanw/esbuild/issues/1406)) This release fixes an issue with the new implementation of the synchronous JS API calls (`transformSync` and `buildSync`) when they are used from a thread other than the main thread. The problem happened because esbuild's new implementation uses node's `worker_threads` library internally and non-main threads were incorrectly assumed to be esbuild's internal thread instead of potentially another unrelated thread. Now esbuild's synchronous JS APIs should work correctly when called from non-main threads. ### [`v0.12.12`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01212) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.11...v0.12.12) - Fix `file` loader import paths when subdirectories are present ([#&#8203;1044](https://github.com/evanw/esbuild/issues/1044)) Using the `file` loader for a file type causes importing affected files to copy the file into the output directory and to embed the path to the copied file into the code that imported it. However, esbuild previously always embedded the path relative to the output directory itself. This is problematic when the importing code is generated within a subdirectory inside the output directory, since then the relative path is wrong. For example: $ cat src/example/entry.css div { background: url(../images/image.png); } $ esbuild --bundle src/example/entry.css --outdir=out --outbase=src --loader:.png=file $ find out -type f out/example/entry.css out/image-55DNWN2R.png $ cat out/example/entry.css /* src/example/entry.css */ div { background: url(./image-55DNWN2R.png); } This is output from the previous version of esbuild. The above asset reference in `out/example/entry.css` is wrong. The path should start with `../` because the two files are in different directories. With this release, the asset references present in output files will now be the full relative path from the output file to the asset, so imports should now work correctly when the entry point is in a subdirectory within the output directory. This change affects asset reference paths in both CSS and JS output files. Note that if you want asset reference paths to be independent of the subdirectory in which they reside, you can use the `--public-path` setting to provide the common path that all asset reference paths should be constructed relative to. Specifically `--public-path=.` should bring back the old problematic behavior in case you need it. - Add support for `[dir]` in `--asset-names` ([#&#8203;1196](https://github.com/evanw/esbuild/pull/1196)) You can now use path templates such as `--asset-names=[dir]/[name]-[hash]` to copy the input directory structure of your asset files (i.e. input files loaded with the `file` loader) to the output directory. Here's an example: $ cat entry.css header { background: url(images/common/header.png); } main { background: url(images/home/hero.png); } $ esbuild --bundle entry.css --outdir=out --asset-names=[dir]/[name]-[hash] --loader:.png=file $ find out -type f out/images/home/hero-55DNWN2R.png out/images/common/header-55DNWN2R.png out/entry.css $ cat out/entry.css /* entry.css */ header { background: url(./images/common/header-55DNWN2R.png); } main { background: url(./images/home/hero-55DNWN2R.png); } ### [`v0.12.11`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01211) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.10...v0.12.11) - Enable faster synchronous transforms with the JS API by default ([#&#8203;1000](https://github.com/evanw/esbuild/issues/1000)) Currently the synchronous JavaScript API calls `transformSync` and `buildSync` spawn a new child process on every call. This is due to limitations with node's `child_process` API. Doing this means `transformSync` and `buildSync` are much slower than `transform` and `build`, which share the same child process across calls. This release improves the performance of `transformSync` and `buildSync` by up to 20x. It enables a hack where node's `worker_threads` API and atomics are used to block the main thread while asynchronous communication with a single long-lived child process happens in a worker. Previously this was only enabled when the `ESBUILD_WORKER_THREADS` environment variable was set to `1`. But this experiment has been available for a while (since version 0.9.6) without any reported issues. Now this hack will be enabled by default. It can be disabled by setting `ESBUILD_WORKER_THREADS` to `0` before running node. - Fix nested output directories with WebAssembly on Windows ([#&#8203;1399](https://github.com/evanw/esbuild/issues/1399)) Many functions in Go's standard library have a bug where they do not work on Windows when using Go with WebAssembly. This is a long-standing bug and is a fault with the design of the standard library, so it's unlikely to be fixed. Basically Go's standard library is designed to bake "Windows or not" decision into the compiled executable, but WebAssembly is platform-independent which makes "Windows or not" is a run-time decision instead of a compile-time decision. Oops. I have been working around this by trying to avoid using path-related functions in the Go standard library and doing all path manipulation by myself instead. This involved completely replacing Go's `path/filepath` library. However, I missed the `os.MkdirAll` function which is also does path manipulation but is outside of the `path/filepath` package. This meant that nested output directories failed to be created on Windows, which caused a build error. This problem only affected the `esbuild-wasm` package. This release manually reimplements nested output directory creation to work around this bug in the Go standard library. So nested output directories should now work on Windows with the `esbuild-wasm` package. ### [`v0.12.10`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01210) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.9...v0.12.10) - Add a target for ES2021 It's now possible to use `--target=es2021` to target the newly-released JavaScript version ES2021. The only difference between that and `--target=es2020` is that logical assignment operators such as `a ||= b` are not converted to regular assignment operators such as `a || (a = b)`. - Minify the syntax `Infinity` to `1 / 0` ([#&#8203;1385](https://github.com/evanw/esbuild/pull/1385)) The `--minify-syntax` flag (automatically enabled by `--minify`) will now minify the expression `Infinity` to `1 / 0`, which uses fewer bytes: ```js // Original code const a = Infinity; // Output with "--minify-syntax" const a = 1 / 0; ``` This change was contributed by [@&#8203;Gusted](https://github.com/Gusted). - Minify syntax in the CSS `transform` property ([#&#8203;1390](https://github.com/evanw/esbuild/pull/1390)) This release includes various size reductions for CSS transform matrix syntax when minification is enabled: ```css /* Original code */ div { transform: translate3d(0, 0, 10px) scale3d(200%, 200%, 1) rotate3d(0, 0, 1, 45deg); } /* Output with "--minify-syntax" */ div { transform: translateZ(10px) scale(2) rotate(45deg); } ``` The `translate3d` to `translateZ` conversion was contributed by [@&#8203;steambap](https://github.com/steambap). - Support for the case-sensitive flag in CSS attribute selectors ([#&#8203;1397](https://github.com/evanw/esbuild/issues/1397)) You can now use the case-sensitive CSS attribute selector flag `s` such as in `[type="a" s] { list-style: lower-alpha; }`. Previously doing this caused a warning about unrecognized syntax. ### [`v0.12.9`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0129) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.8...v0.12.9) - Allow `this` with `--define` ([#&#8203;1361](https://github.com/evanw/esbuild/issues/1361)) You can now override the default value of top-level `this` with the `--define` feature. Top-level `this` defaults to being `undefined` in ECMAScript modules and `exports` in CommonJS modules. For example: ```js // Original code ((obj) => { ... })(this); // Output with "--define:this=window" ((obj) => { ... })(window); ``` Note that overriding what top-level `this` is will likely break code that uses it correctly. So this new feature is only useful in certain cases. - Fix CSS minification issue with `!important` and duplicate declarations ([#&#8203;1372](https://github.com/evanw/esbuild/issues/1372)) Previously CSS with duplicate declarations for the same property where the first one was marked with `!important` was sometimes minified incorrectly. For example: ```css .selector { padding: 10px !important; padding: 0; } ``` This was incorrectly minified as `.selector{padding:0}`. The bug affected three properties: `padding`, `margin`, and `border-radius`. With this release, this code will now be minified as `.selector{padding:10px!important;padding:0}` instead which means there is no longer a difference between minified and non-minified code in this case. ### [`v0.12.8`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0128) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.7...v0.12.8) - Plugins can now specify `sideEffects: false` ([#&#8203;1009](https://github.com/evanw/esbuild/issues/1009)) The default path resolution behavior in esbuild determines if a given file can be considered side-effect free (in the [Webpack-specific sense](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free)) by reading the contents of the nearest enclosing `package.json` file and looking for `"sideEffects": false`. However, up until now this was impossible to achieve in an esbuild plugin because there was no way of returning this metadata back to esbuild. With this release, esbuild plugins can now return `sideEffects: false` to mark a file as having no side effects. Here's an example: ```js esbuild.build({ entryPoints: ['app.js'], bundle: true, plugins: [{ name: 'env-plugin', setup(build) { build.onResolve({ filter: /^env$/ }, args => ({ path: args.path, namespace: 'some-ns', sideEffects: false, })) build.onLoad({ filter: /.*/, namespace: 'some-ns' }, () => ({ contents: `export default self.env || (self.env = getEnv())`, })) }, }], }) ``` This plugin creates a virtual module that can be generated by importing the string `env`. However, since the plugin returns `sideEffects: false`, the generated virtual module will not be included in the bundle if all of the imported values from the module `env` end up being unused. This feature was contributed by [@&#8203;chriscasola](https://github.com/chriscasola). - Remove a warning about unsupported source map comments ([#&#8203;1358](https://github.com/evanw/esbuild/issues/1358)) This removes a warning that indicated when a source map comment couldn't be supported. Specifically, this happens when you enable source map generation and esbuild encounters a file with a source map comment pointing to an external file but doesn't have enough information to know where to look for that external file (basically when the source file doesn't have an associated directory to use for path resolution). In this case esbuild can't respect the input source map because it cannot be located. The warning was annoying so it has been removed. Source maps still won't work, however. ### [`v0.12.7`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0127) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.6...v0.12.7) - Quote object properties that are modern Unicode identifiers ([#&#8203;1349](https://github.com/evanw/esbuild/issues/1349)) In ES6 and above, an identifier is a character sequence starting with a character in the `ID_Start` Unicode category and followed by zero or more characters in the `ID_Continue` Unicode category, and these categories must be drawn from Unicode version 5.1 or above. But in ES5, an identifier is a character sequence starting with a character in one of the `Lu, Ll, Lt, Lm, Lo, Nl` Unicode categories and followed by zero or more characters in the `Lu, Ll, Lt, Lm, Lo, Nl, Mn, Mc, Nd, Pc` Unicode categories, and these categories must be drawn from Unicode version 3.0 or above. Previously esbuild always used the ES6+ identifier validation test when deciding whether to use an identifier or a quoted string to encode an object property but with this release, it will use the ES5 validation test instead: ```js // Original code x.ꓷꓶꓲꓵꓭꓢꓱ = { ꓷꓶꓲꓵꓭꓢꓱ: y }; // Old output x.ꓷꓶꓲꓵꓭꓢꓱ = { ꓷꓶꓲꓵꓭꓢꓱ: y }; // New output x["ꓷꓶꓲꓵꓭꓢꓱ"] = { "ꓷꓶꓲꓵꓭꓢꓱ": y }; ``` This approach should ensure maximum compatibility with all JavaScript environments that support ES5 and above. Note that this means minified files containing Unicode properties may be slightly larger than before. - Ignore `tsconfig.json` files inside `node_modules` ([#&#8203;1355](https://github.com/evanw/esbuild/issues/1355)) Package authors often publish their `tsconfig.json` files to npm because of npm's default-include publishing model and because these authors probably don't know about `.npmignore` files. People trying to use these packages with esbuild have historically complained that esbuild is respecting `tsconfig.json` in these cases. The assumption is that the package author published these files by accident. With this release, esbuild will no longer respect `tsconfig.json` files when the source file is inside a `node_modules` folder. Note that `tsconfig.json` files inside `node_modules` are still parsed, and extending `tsconfig.json` files from inside a package is still supported. - Fix missing `--metafile` when using `--watch` ([#&#8203;1357](https://github.com/evanw/esbuild/issues/1357)) Due to an oversight, the `--metafile` setting didn't work when `--watch` was also specified. This only affected the command-line interface. With this release, the `--metafile` setting should now work in this case. - Add a hidden `__esModule` property to modules in ESM format ([#&#8203;1338](https://github.com/evanw/esbuild/pull/1338)) Module namespace objects from ESM files will now have a hidden `__esModule` property. This improves compatibility with code that has been converted from ESM syntax to CommonJS by Babel or TypeScript. For example: ```js // Input TypeScript code import x from "y" console.log(x) // Output JavaScript code from the TypeScript compiler var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const y_1 = __importDefault(require("y")); console.log(y_1.default); ``` If the object returned by `require("y")` doesn't have an `__esModule` property, then `y_1` will be the object `{ "default": require("y") }`. If the file `"y"` is in ESM format and has a default export of, say, the value `null`, that means `y_1` will now be `{ "default": { "default": null } }` and you will need to use `y_1.default.default` to access the default value. Adding an automatically-generated `__esModule` property when converting files in ESM format to CommonJS is required to make this code work correctly (i.e. for the value to be accessible via just `y_1.default` instead). With this release, code in ESM format will now have an automatically-generated `__esModule` property to satisfy this convention. The property is non-enumerable so it shouldn't show up when iterating over the properties of the object. As a result, the export name `__esModule` is now reserved for use with esbuild. It's now an error to create an export with the name `__esModule`. This fix was contributed by [@&#8203;lbwa](https://github.com/lbwa). ### [`v0.12.6`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0126) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.5...v0.12.6) - Improve template literal lowering transformation conformance ([#&#8203;1327](https://github.com/evanw/esbuild/issues/1327)) This release contains the following improvements to template literal lowering for environments that don't support tagged template literals natively (such as `--target=es5`): - For tagged template literals, the arrays of strings that are passed to the tag function are now frozen and immutable. They are also now cached so they should now compare identical between multiple template evaluations: ```js // Original code console.log(tag`\u{10000}`) // Old output console.log(tag(__template(["𐀀"], ["\\u{10000}"]))); // New output var _a; console.log(tag(_a || (_a = __template(["𐀀"], ["\\u{10000}"])))); ``` - For tagged template literals, the generated code size is now smaller in the common case where there are no escape sequences, since in that case there is no distinction between "raw" and "cooked" values: ```js // Original code console.log(tag`some text without escape sequences`) // Old output console.log(tag(__template(["some text without escape sequences"], ["some text without escape sequences"]))); // New output var _a; console.log(tag(_a || (_a = __template(["some text without escape sequences"])))); ``` - For non-tagged template literals, the generated code now uses chains of `.concat()` calls instead of string addition: ```js // Original code console.log(`an ${example} template ${literal}`) // Old output console.log("an " + example + " template " + literal); // New output console.log("an ".concat(example, " template ").concat(literal)); ``` The old output was incorrect for several reasons including that `toString` must be called instead of `valueOf` for objects and that passing a `Symbol` instance should throw instead of converting the symbol to a string. Using `.concat()` instead of string addition fixes both of those correctness issues. And you can't use a single `.concat()` call because side effects must happen inline instead of at the end. - Only respect `target` in `tsconfig.json` when esbuild's target is not configured ([#&#8203;1332](https://github.com/evanw/esbuild/issues/1332)) In version 0.12.4, esbuild began respecting the `target` setting in `tsconfig.json`. However, sometimes `tsconfig.json` contains target values that should not be used. With this release, esbuild will now only use the `target` value in `tsconfig.json` as the language level when esbuild's `target` setting is not configured. If esbuild's `target` setting is configured then the `target` value in `tsconfig.json` is now ignored. - Fix the order of CSS imported from JS ([#&#8203;1342](https://github.com/evanw/esbuild/pull/1342)) Importing CSS from JS when bundling causes esbuild to generate a sibling CSS output file next to the resulting JS output file containing the bundled CSS. The order of the imported CSS files in the output was accidentally the inverse order of the order in which the JS files were evaluated. Instead the order of the imported CSS files should match the order in which the JS files were evaluated. This fix was contributed by [@&#8203;dmitrage](https://github.com/dmitrage). - Fix an edge case with transforming `export default class` ([#&#8203;1346](https://github.com/evanw/esbuild/issues/1346)) Statements of the form `export default class x {}` were incorrectly transformed to `class x {} var y = x; export {y as default}` instead of `class x {} export {x as default}`. Transforming these statements like this is incorrect in the rare case that the class is later reassigned by name within the same file such as `export default class x {} x = null`. Here the imported value should be `null` but was incorrectly the class object instead. This is unlikely to matter in real-world code but it has still been fixed to improve correctness. ### [`v0.12.5`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0125) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.4...v0.12.5) - Add support for lowering tagged template literals to ES5 ([#&#8203;297](https://github.com/evanw/esbuild/issues/297)) This release adds support for lowering tagged template literals such as `` String.raw`\unicode` `` to target environments that don't support them such as `--target=es5` (non-tagged template literals were already supported). Each literal turns into a function call to a helper function: ```js // Original code console.log(String.raw`\unicode`) // Lowered code console.log(String.raw(__template([void 0], ["\\unicode"]))); ``` - Change class field behavior to match TypeScript 4.3 TypeScript 4.3 includes a subtle breaking change that wasn't mentioned in the [TypeScript 4.3 blog post](https://devblogs.microsoft.com/typescript/announcing-typescript-4-3/): class fields will now be compiled with different semantics if `"target": "ESNext"` is present in `tsconfig.json`. Specifically in this case `useDefineForClassFields` will default to `true` when not specified instead of `false`. This means class field behavior in TypeScript code will now match JavaScript instead of doing something else: ```js class Base { set foo(value) { console.log('set', value) } } class Derived extends Base { foo = 123 } new Derived() ``` In TypeScript 4.2 and below, the TypeScript compiler would generate code that prints `set 123` when `tsconfig.json` contains `"target": "ESNext"` but in TypeScript 4.3, the TypeScript compiler will now generate code that doesn't print anything. This is the difference between "assign" semantics and "define" semantics. With this release, esbuild has been changed to follow the TypeScript 4.3 behavior. - Avoid generating the character sequence `</script>` ([#&#8203;1322](https://github.com/evanw/esbuild/issues/1322)) If the output of esbuild is inlined into a `<script>...</script>` tag inside an HTML file, the character sequence `</script>` inside the JavaScript code will accidentally cause the script tag to be terminated early. There are at least four such cases where this can happen: ```js console.log('</script>') console.log(1</script>/.exec(x).length) console.log(String.raw`</script>`) // @&#8203;license </script> ``` With this release, esbuild will now handle all of these cases and avoid generating the problematic character sequence: ```js console.log('<\/script>'); console.log(1< /script>/.exec(x).length); console.log(String.raw(__template(["<\/script>"], ["<\/script>"]))); // @&#8203;license <\/script> ``` - Change the triple-slash reference comment for Deno ([#&#8203;1325](https://github.com/evanw/esbuild/issues/1325)) The comment in esbuild's JavaScript API implementation for Deno that references the TypeScript type declarations has been changed from `/// <reference path="./mod.d.ts" />` to `/// <reference types="./mod.d.ts" />`. This comment was copied from Deno's documentation but apparently Deno's documentation was incorrect. The comment in esbuild's Deno bundle has been changed to reflect Deno's latest documentation. ### [`v0.12.4`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0124) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.3...v0.12.4) - Reorder name preservation before TypeScript decorator evaluation ([#&#8203;1316](https://github.com/evanw/esbuild/issues/1316)) The `--keep-names` option ensures the `.name` property on functions and classes remains the same after bundling. However, this was being enforced after TypeScript decorator evaluation which meant that the decorator could observe the incorrect name. This has been fixed and now `.name` preservation happens before decorator evaluation instead. - Potential fix for a determinism issue ([#&#8203;1304](https://github.com/evanw/esbuild/issues/1304)) This release contains a potential fix for an unverified issue with non-determinism in esbuild. The regression was apparently introduced in 0.11.13 and may be related to parallelism that was introduced around the point where dynamic `import()` expressions are added to the list of entry points. Hopefully this fix should resolve the regression. - Respect `target` in `tsconfig.json` ([#&#8203;277](https://github.com/evanw/esbuild/issues/277)) Each JavaScript file that esbuild bundles will now be transformed according to the [`target`](https://www.typescriptlang.org/tsconfig#target) language level from the nearest enclosing `tsconfig.json` file. This is in addition to esbuild's own `--target` setting; the two settings are merged by transforming any JavaScript language feature that is unsupported in either esbuild's configured `--target` value or the `target` property in the `tsconfig.json` file. ### [`v0.12.3`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0123) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.2...v0.12.3) - Ensure JSX element names start with a capital letter ([#&#8203;1309](https://github.com/evanw/esbuild/issues/1309)) The JSX specification only describes the syntax and says nothing about how to interpret it. But React (and therefore esbuild) treats JSX tags that start with a lower-case ASCII character as strings instead of identifiers. That way the tag `<i/>` always refers to the italic HTML element `i` and never to a local variable named `i`. However, esbuild may rename identifiers for any number of reasons such as when minification is enabled. Previously esbuild could sometimes rename identifiers used as tag names such that they start with a lower-case ASCII character. This is problematic when JSX syntax preservation is enabled since subsequent JSX processing would then turn these identifier references into strings. With this release, esbuild will now make sure identifiers used in tag names start with an upper-case ASCII character instead when JSX syntax preservation is enabled. This should avoid problems when using esbuild with JSX transformation tools. - Fix a single hyphen being treated as a CSS name ([#&#8203;1310](https://github.com/evanw/esbuild/pull/1310)) CSS identifiers are allowed to start with a `-` character if (approximately) the following character is a letter, an escape sequence, a non-ASCII character, the character `_`, or another `-` character. This check is used in certain places when printing CSS to determine whether a token is a valid identifier and can be printed as such or whether it's an invalid identifier and needs to be quoted as a string. One such place is in attribute selectors such as `[a*=b]`. However, esbuild had a bug where a single `-` character was incorrectly treated as a valid identifier in this case. This is because the end of string became U+FFFD (the Unicode replacement character) which is a non-ASCII character and a valid name-start code point. With this release a single `-` character is no longer treated as a valid identifier. This fix was contributed by [@&#8203;lbwa](https://github.com/lbwa). ### [`v0.12.2`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0122) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.1...v0.12.2) - Fix various code generation and minification issues ([#&#8203;1305](https://github.com/evanw/esbuild/issues/1305)) This release fixes the following issues, which were all identified by running esbuild against the latest UglifyJS test suite: - The `in` operator is now surrounded parentheses inside arrow function expression bodies inside `for` loop initializers: ```js // Original code for ((x => y in z); 0; ) ; // Old output for ((x) => y in z; 0; ) ; // New output for ((x) => (y in z); 0; ) ; ``` Without this, the `in` operator would cause the for loop to be considered a for-in loop instead. - The statement `return undefined;` is no longer minified to `return;` inside async generator functions: ```js // Original code return undefined; // Old output return; // New output return void 0; ``` Using `return undefined;` inside an async generator function has the same effect as `return await undefined;` which schedules a task in the event loop and runs code in a different order than just `return;`, which doesn't hide an implicit `await` expression. - Property access expressions are no longer inlined in template tag position: ```js // Original code (null, a.b)``, (null, a[b])``; // Old output a.b``, a[b]``; // New output (0, a.b)``, (0, a[b])``; ``` The expression `` a.b`c` `` is different than the expression `` (0, a.b)`c` ``. The first calls the function `a.b` with `a` as the value for `this` but the second calls the function `a.b` with the default value for `this` (the global object in non-strict mode or `undefined` in strict mode). - Verbatim `__proto__` properties inside object spread are no longer inlined when minifying: ```js // Original code x = { ...{ __proto__: { y: true } } }.y; // Old output x = { __proto__: { y: !0 } }.y; // New output x = { ...{ __proto__: { y: !0 } } }.y; ``` A verbatim (i.e. non-computed non-method) property called `__proto__` inside an object literal actually sets the prototype of the surrounding object literal. It does not add an "own property" called `__proto__` to that object literal, so inlining it into the parent object literal would be incorrect. The presence of a `__proto__` property now stops esbuild from applying the object spread inlining optimization when minifying. - The value of `this` has now been fixed for lowered private class members that are used as template tags: ```js // Original code x = (new (class { a = this.#c``; b = 1; #c() { return this } })).a.b; // Old output var _c, c_fn, _a; x = new (_a = class { constructor() { __privateAdd(this, _c); __publicField(this, "a", __privateMethod(this, _c, c_fn)``); __publicField(this, "b", 1); } }, _c = new WeakSet(), c_fn = function() { return this; }, _a)().a.b; // New output var _c, c_fn, _a; x = new (_a = class { constructor() { __privateAdd(this, _c); __publicField(this, "a", __privateMethod(this, _c, c_fn).bind(this)``); __publicField(this, "b", 1); } }, _c = new WeakSet(), c_fn = function() { return this; }, _a)().a.b; ``` The value of `this` here should be an instance of the class because the template tag is a property access expression. However, it was previously the default value (the global object in non-strict mode or `undefined` in strict mode) instead due to the private member transformation, which is incorrect. - Invalid escape sequences are now allowed in tagged template literals This implements the template literal revision feature: https://github.com/tc39/proposal-template-literal-revision. It allows you to process tagged template literals using custom semantics that don't follow JavaScript escape sequence rules without causing a syntax error: ```js console.log((x => x.raw)`invalid \unicode escape sequence`) ``` ### [`v0.12.1`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01215) [Compare Source](https://github.com/evanw/esbuild/compare/v0.12.0...v0.12.1) - Fix a bug with `var()` in CSS color lowering ([#&#8203;1421](https://github.com/evanw/esbuild/issues/1421)) This release fixes a bug with esbuild's handling of the `rgb` and `hsl` color functions when they contain `var()`. Each `var()` token sequence can be substituted for any number of tokens including zero or more than one, but previously esbuild's output was only correct if each `var()` inside of `rgb` or `hsl` contained exactly one token. With this release, esbuild will now not attempt to transform newer CSS color syntax to older CSS color syntax if it contains `var()`: /* Original code */ a { color: hsl(var(--hs), var(--l)); } /* Old output */ a { color: hsl(var(--hs), ,, var(--l)); } /* New output */ a { color: hsl(var(--hs), var(--l)); } The bug with the old output above happened because esbuild considered the arguments to `hsl` as matching the pattern `hsl(h s l)` which is the new space-separated form allowed by [CSS Color Module Level 4](https://drafts.csswg.org/css-color/#the-hsl-notation). Then esbuild tried to convert this to the form `hsl(h, s, l)` which is more widely supported by older browsers. But this substitution doesn't work in the presence of `var()`, so it has now been disabled in that case. ### [`v0.12.0`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0120) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.23...v0.12.0) **This release contains backwards-incompatible changes.** Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as [recommended by npm](https://docs.npmjs.com/cli/v6/using-npm/semver/)). You should either be pinning the exact version of `esbuild` in your `package.json` file or be using a version range syntax that only accepts patch upgrades such as `~0.11.0`. See the documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information. The breaking changes in this release relate to CSS import order and also build scenarios where both the `inject` and `define` API options are used (see below for details). These breaking changes are as follows: - Fix bundled CSS import order ([#&#8203;465](https://github.com/evanw/esbuild/issues/465)) JS and CSS use different import ordering algorithms. In JS, importing a file that has already been imported is a no-op but in CSS, importing a file that has already been imported re-imports the file. A simple way to imagine this is to view each `@import` rule in CSS as being replaced by the contents of that file similar to `#include` in C/C++. However, this is incorrect in the case of `@import` cycles because it would cause infinite expansion. A more accurate way to imagine this is that in CSS, a file is evaluated at the *last* `@import` location while in JS, a file is evaluated at the *first* `import` location. Previously esbuild followed JS import order rules for CSS but now esbuild will follow CSS import order rules. This is a breaking change because it means your CSS may behave differently when bundled. Note that CSS import order rules are somewhat unintuitive because evaluation order matters. In CSS, using `@import` multiple times can end up unintentionally erasing overriding styles. For example, consider the following files: ```css /* entry.css */ @&#8203;import "./color.css"; @&#8203;import "./background.css"; ``` ```css /* color.css */ @&#8203;import "./reset.css"; body { color: white; } ``` ```css /* background.css */ @&#8203;import "./reset.css"; body { background: black; } ``` ```css /* reset.css */ body { background: white; color: black; } ``` Because of how CSS import order works, `entry.css` will now be bundled like this: ```css /* color.css */ body { color: white; } /* reset.css */ body { background: white; color: black; } /* background.css */ body { background: black; } ``` This means the body will unintuitively be all black! The file `reset.css` is evaluated at the location of the *last* `@import` instead of the *first* `@import`. The fix for this case is to remove the nested imports of `reset.css` and to import `reset.css` exactly once at the top of `entry.css`. Note that while the evaluation order of external CSS imports is preserved with respect to other external CSS imports, the evaluation order of external CSS imports is *not* preserved with respect to other internal CSS imports. All external CSS imports are "hoisted" to the top of the bundle. The alternative would be to generate many smaller chunks which is usually undesirable. So in this case esbuild's CSS bundling behavior will not match the browser. - Fix bundled CSS when using JS code splitting ([#&#8203;608](https://github.com/evanw/esbuild/issues/608)) Previously esbuild generated incorrect CSS output when JS code splitting was enabled and the JS code being bundled imported CSS files. CSS code that was reachable via multiple JS entry points was split off into a shared CSS chunk, but that chunk was not actually imported anywhere so the shared CSS was missing. This happened because both CSS and JS code splitting were experimental features that are still in progress and weren't tested together. Now esbuild's CSS output should contain all reachable CSS code when JS code splitting is enabled. Note that this does *not* mean code splitting works for CSS files. Each CSS output file simply contains the transitive set of all CSS reachable from the JS entry point including through dynamic `import()` and `require()` expressions. Specifically, the bundler constructs a virtual CSS file for each JS entry point consisting only of `@import` rules for each CSS file imported into a JS file. These `@import` rules are constructed in JS source order, but then the bundler uses CSS import order from that point forward to bundle this virtual CSS file into the final CSS output file. This model makes the most sense when CSS files are imported into JS files via JS `import` statements. Importing CSS via `import()` and `require()` (either directly or transitively through multiple intermediate JS files) should still "work" in the sense that all reachable CSS should be included in the output, but in this case esbuild will pick an arbitrary (but consistent) import order. The import order may not match the order that the JS files are evaluated in because JS evaluation order of dynamic imports is only determined at run-time while CSS bundling happens at compile-time. It's possible to implement code splitting for CSS such that CSS code used between multiple entry points is shared. However, CSS lacks a mechanism for "lazily" importing code (i.e. disconnecting the import location with the evaluation location) so CSS code splitting could potentially need to generate a huge number of very small chunks to preserve import order. It's unclear if this would end up being a net win or not as far as browser download time. So sharing-based code splitting is currently not supported for CSS. It's theoretically possible to implement code splitting for CSS such that CSS from a dynamically-imported JS file (e.g. via `import()`) is placed into a separate chunk. However, due to how `@import` order works this would in theory end up re-evaluating all shared dependencies which could overwrite overloaded styles and unintentionally change the way the page is rendered. For example, constructing a single-page app architecture such that each page is JS-driven and can transition to other JS-driven pages via `import()` could end up with pages that look different depending on what order you visit them in. This is clearly undesirable. The simple way to address this is to just not support dynamic-import code splitting for CSS either. - Change "define" to have higher priority than "inject" ([#&#8203;660](https://github.com/evanw/esbuild/issues/660)) The "define" and "inject" features are both ways of replacing certain expressions in your source code with other things expressions. Previously esbuild's behavior ran "inject" before "define", which could lead to some undesirable behavior. For example (from the `react` npm package): ```js if (process.env.NODE_ENV === 'production') { module.exports = require('./cjs/react.production.min.js'); } else { module.exports = require('./cjs/react.development.js'); } ``` If you use "define" to replace `process.env.NODE_ENV` with `"production"` and "inject" to replace `process` with a shim that emulates node's process API, then `process` was previously replaced first and then `process.env.NODE_ENV` wasn't matched because `process` referred to the injected shim. This wasn't ideal because it means esbuild didn't detect the branch condition as a constant (since it doesn't know how the shim behaves at run-time) and bundled both the development and production versions of the package. With this release, esbuild will now run "define" before "inject". In the above example this means that `process.env.NODE_ENV` will now be replaced with `"production"`, the injected shim will not be included, and only the production version of the package will be bundled. This feature was contributed by [@&#8203;rtsao](https://github.com/rtsao). In addition to the breaking changes above, the following features are also included in this release: - Add support for the `NO_COLOR` environment variable The CLI will now omit color if the `NO_COLOR` environment variable is present, which is an existing convention that is followed by some other software. See https://no-color.org/ for more information. ### [`v0.11.23`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01123) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.22...v0.11.23) - Add a shim function for unbundled uses of `require` ([#&#8203;1202](https://github.com/evanw/esbuild/issues/1202)) Modules in CommonJS format automatically get three variables injected into their scope: `module`, `exports`, and `require`. These allow the code to import other modules and to export things from itself. The bundler automatically rewrites uses of `module` and `exports` to refer to the module's exports and certain uses of `require` to a helper function that loads the imported module. Not all uses of `require` can be converted though, and un-converted uses of `require` will end up in the output. This is problematic because `require` is only present at run-time if the output is run as a CommonJS module. Otherwise `require` is undefined, which means esbuild's behavior is inconsistent between compile-time and run-time. The `module` and `exports` variables are objects at compile-time and run-time but `require` is a function at compile-time and undefined at run-time. This causes code that checks for `typeof require` to have inconsistent behavior: ```js if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { console.log('CommonJS detected') } ``` In the above example, ideally `CommonJS detected` would always be printed since the code is being bundled with a CommonJS-aware bundler. To fix this, esbuild will now substitute references to `require` with a stub `__require` function when bundling if the output format is something other than CommonJS. This should ensure that `require` is now consistent between compile-time and run-time. When bundled, code that uses unbundled references to `require` will now look something like this: ```js var __require = (x) => { if (typeof require !== "undefined") return require(x); throw new Error('Dynamic require of "' + x + '" is not supported'); }; var __commonJS = (cb, mod) => () => (mod || cb((mod = {exports: {}}).exports, mod), mod.exports); var require_example = __commonJS((exports, module) => { if (typeof __require === "function" && typeof exports === "object" && typeof module === "object") { console.log("CommonJS detected"); } }); require_example(); ``` - Fix incorrect caching of internal helper function library ([#&#8203;1292](https://github.com/evanw/esbuild/issues/1292)) This release fixes a bug where running esbuild multiple times with different configurations sometimes resulted in code that would crash at run-time. The bug was introduced in version 0.11.19 and happened because esbuild's internal helper function library is parsed once and cached per configuration, but the new profiler name option was accidentally not included in the cache key. This option is now included in the cache key so this bug should now be fixed. - Minor performance improvements This release contains some small performance improvements to offset an earlier minor performance regression due to the addition of certain features such as hashing for entry point files. The benchmark times on the esbuild website should now be accurate again (versions of esbuild after the regression but before this release were slightly slower than the benchmark). ### [`v0.11.22`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01122) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.21...v0.11.22) - Add support for the "import assertions" proposal This is new JavaScript syntax that was shipped in Chrome 91. It looks like this: ```js import './foo.json' assert { type: 'json' } import('./bar.json', { assert: { type: 'json' } }) ``` On the web, the content type for a given URL is determined by the `Content-Type` HTTP header instead of the file extension. So adding support for importing non-JS content types such as JSON to the web could cause [security issues](https://github.com/WICG/webcomponents/issues/839) since importing JSON from an untrusted source is safe while importing JS from an untrusted source is not. Import assertions are a new feature to address this security concern and unblock non-JS content types on the web. They cause the import to fail if the `Content-Type` header doesn't match the expected value. This prevents security issues for data-oriented content types such as JSON since it guarantees that data-oriented content will never accidentally be evaluated as code instead of data. More information about the proposal is available here: https://github.com/tc39/proposal-import-assertions. This release includes support for parsing and printing import assertions. They will be printed if the configured target environment supports them (currently only in `esnext` and `chrome91`), otherwise they will be omitted. If they aren't supported in the configured target environment and it's not possible to omit them, which is the case for certain dynamic `import()` expressions, then using them is a syntax error. Import assertions are otherwise unused by the bundler. - Forbid the token sequence `for ( async of` when not followed by `=>` This follows a recently-fixed ambiguity in the JavaScript specification, which you can read about here: https://github.com/tc39/ecma262/pull/2256. Prior to this change in the specification, it was ambiguous whether this token sequence should be parsed as `for ( async of =>` or `for ( async of ;`. V8 and esbuild expected `=>` after `for ( async of` while SpiderMonkey and JavaScriptCore did something else. The ambiguity has been removed and the token sequence `for ( async of` is now forbidden by the specification when not followed by `=>`, so esbuild now forbids this as well. Note that the token sequence `for await (async of` is still allowed even when not followed by `=>`. Code such as `for ((async) of []) ;` is still allowed and will now be printed with parentheses to avoid the grammar ambiguity. - Restrict `super` property access to inside of methods You can now only use `super.x` and `super[x]` expressions inside of methods. Previously these expressions were incorrectly allowed everywhere. This means esbuild now follows the JavaScript language specification more closely. ### [`v0.11.21`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01121) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.20...v0.11.21) - TypeScript `override` for parameter properties ([#&#8203;1262](https://github.com/evanw/esbuild/pull/1262)) You can now use the `override` keyword instead of or in addition to the `public`, `private`, `protected`, and `readonly` keywords for declaring a TypeScript parameter property: ```ts class Derived extends Base { constructor(override field: any) { } } ``` This feature was [recently added to the TypeScript compiler](https://github.com/microsoft/TypeScript/pull/43831) and will presumably be in an upcoming version of the TypeScript language. Support for this feature in esbuild was contributed by [@&#8203;g-plane](https://github.com/g-plane). - Fix duplicate export errors due to TypeScript import-equals statements ([#&#8203;1283](https://github.com/evanw/esbuild/issues/1283)) TypeScript has a special import-equals statement that is not part of JavaScript. It looks like this: ```ts import a = foo.a import b = a.b import c = b.c import x = foo.x import y = x.y import z = y.z export let bar = c ``` Each import can be a type or a value and type-only imports need to be eliminated when converting this code to JavaScript, since types do not exist at run-time. The TypeScript compiler generates the following JavaScript code for this example: ```js var a = foo.a; var b = a.b; var c = b.c; export let bar = c; ``` The `x`, `y`, and `z` import statements are eliminated in esbuild by iterating over imports and exports multiple times and continuing to remove unused TypeScript import-equals statements until none are left. The first pass removes `z` and marks `y` as unused, the second pass removes `y` and marks `x` as unused, and the third pass removes `x`. However, this had the side effect of making esbuild incorrectly think that a single export is exported twice (because it's processed more than once). This release fixes that bug by only iterating multiple times over imports, not exports. There should no longer be duplicate export errors for this case. - Add support for type-only TypeScript import-equals statements ([#&#8203;1285](https://github.com/evanw/esbuild/pull/1285)) This adds support for the following new TypeScript syntax that was added in version 4.2: ```ts import type React = require('react') ``` Unlike `import React = require('react')`, this statement is a type declaration instead of a value declaration and should be omitted from the generated code. See [microsoft/TypeScript#&#8203;41573](https://github.com/microsoft/TypeScript/pull/41573) for details. This feature was contributed by [@&#8203;g-plane](https://github.com/g-plane). ### [`v0.11.20`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01120) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.19...v0.11.20) - Omit warning about duplicate JSON keys from inside `node_modules` ([#&#8203;1254](https://github.com/evanw/esbuild/issues/1254)) This release no longer warns about duplicate keys inside `package.json` files inside `node_modules`. There are packages like this that are published to npm, and this warning is unactionable. Now esbuild will only issue this warning outside of `node_modules` directories. - Add CSS minification for `box-shadow` values The CSS `box-shadow` property is now minified when `--mangle-syntax` is enabled. This includes trimming length values and minifying color representations. - Fix object spread transform for non-spread getters ([#&#8203;1259](https://github.com/evanw/esbuild/issues/1259)) When transforming an object literal containing object spread (the `...` syntax), properties inside the spread should be evaluated but properties outside the spread should not be evaluated. Previously esbuild's object spread transform incorrectly evaluated properties in both cases. Consider this example: ```js var obj = { ...{ get x() { console.log(1) } }, get y() { console.log(3) }, } console.log(2) obj.y ``` This should print out `1 2 3` because the non-spread getter should not be evaluated. Instead, esbuild was incorrectly transforming this into code that printed `1 3 2`. This issue should now be fixed with this release. - Prevent private class members from being added more than once This fixes a corner case with the private class member implementation. Constructors in JavaScript can return an object other than `this`, so private class members can actually be added to objects other than `this`. This can be abused to attach completely private metadata to other objects: ```js class Base { constructor(x) { return x } } class Derived extends Base { #y static is(z) { return #y in z } } const foo = {} new Derived(foo) console.log(Derived.is(foo)) // true ``` This already worked in code transformed by esbuild for older browsers. However, calling `new Derived(foo)` multiple times in the above code was incorrectly allowed. This should not be allowed because it would mean that the private field `#y` would be re-declared. This is no longer allowed starting from this release. ### [`v0.11.19`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01119) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.18...v0.11.19) - Allow esbuild to be restarted in Deno ([#&#8203;1238](https://github.com/evanw/esbuild/pull/1238)) The esbuild API for [Deno](https://deno.land) has an extra function called `stop()` that doesn't exist in esbuild's API for node. This is because Deno doesn't provide a way to stop esbuild automatically, so calling `stop()` is required to allow Deno to exit. However, once stopped the esbuild API could not be restarted. With this release, you can now continue to use esbuild after calling `stop()`. This will restart esbuild's API and means that you will need to call `stop()` again for Deno to be able to exit. This feature was contributed by [@&#8203;lucacasonato](https://github.com/lucacasonato). - Fix code splitting edge case ([#&#8203;1252](https://github.com/evanw/esbuild/issues/1252)) This release fixes an edge case where bundling with code splitting enabled generated incorrect code if multiple ESM entry points re-exported the same re-exported symbol from a CommonJS file. In this case the cross-chunk symbol dependency should be the variable that holds the return value from the `require()` call instead of the original ESM named `import` clause item. When this bug occurred, the generated ESM code contained an export and import for a symbol that didn't exist, which caused a module initialization error. This case should now work correctly. - Fix code generation with `declare` class fields ([#&#8203;1242](https://github.com/evanw/esbuild/issues/1242)) This fixes a bug with TypeScript code that uses `declare` on a class field and your `tsconfig.json` file has `"useDefineForClassFields": true`. Fields marked as `declare` should not be defined in the generated code, but they were incorrectly being declared as `undefined`. These fields are now correctly omitted from the generated code. - Annotate module wrapper functions in debug builds ([#&#8203;1236](https://github.com/evanw/esbuild/pull/1236)) Sometimes esbuild needs to wrap certain modules in a function when bundling. This is done both for lazy evaluation and for CommonJS modules that use a top-level `return` statement. Previously these functions were all anonymous, so stack traces for errors thrown during initialization looked like this: Error: Electron failed to install correctly, please delete node_modules/electron and try installing again at getElectronPath (out.js:16:13) at out.js:19:21 at out.js:1:45 at out.js:24:3 at out.js:1:45 at out.js:29:3 at out.js:1:45 at Object.<anonymous> (out.js:33:1) This release adds names to these anonymous functions when minification is disabled. The above stack trace now looks like this: Error: Electron failed to install correctly, please delete node_modules/electron and try installing again at getElectronPath (out.js:19:15) at node_modules/electron/index.js (out.js:22:23) at __require (out.js:2:44) at src/base/window.js (out.js:29:5) at __require (out.js:2:44) at src/base/kiosk.js (out.js:36:5) at __require (out.js:2:44) at Object.<anonymous> (out.js:41:1) This is similar to Webpack's development-mode behavior: Error: Electron failed to install correctly, please delete node_modules/electron and try installing again at getElectronPath (out.js:23:11) at Object../node_modules/electron/index.js (out.js:27:18) at __webpack_require__ (out.js:96:41) at Object../src/base/window.js (out.js:49:1) at __webpack_require__ (out.js:96:41) at Object../src/base/kiosk.js (out.js:38:1) at __webpack_require__ (out.js:96:41) at out.js:109:1 at out.js:111:3 at Object.<anonymous> (out.js:113:12) These descriptive function names will additionally be available when using a profiler such as the one included in the "Performance" tab in Chrome Developer Tools. Previously all functions were named `(anonymous)` which made it difficult to investigate performance issues during bundle initialization. - Add CSS minification for more cases The following CSS minification cases are now supported: - The CSS `margin` property family is now minified including combining the `margin-top`, `margin-right`, `margin-bottom`, and `margin-left` properties into a single `margin` property. - The CSS `padding` property family is now minified including combining the `padding-top`, `padding-right`, `padding-bottom`, and `padding-left` properties into a single `padding` property. - The CSS `border-radius` property family is now minified including combining the `border-top-left-radius`, `border-top-right-radius`, `border-bottom-right-radius`, and `border-bottom-left-radius` properties into a single `border-radius` property. - The four special pseudo-elements `::before`, `::after`, `::first-line`, and `::first-letter` are allowed to be parsed with one `:` for legacy reasons, so the `::` is now converted to `:` for these pseudo-elements. - Duplicate CSS rules are now deduplicated. Only the last rule is kept, since that's the only one that has any effect. This applies for both top-level rules and nested rules. - Preserve quotes around properties when minification is disabled ([#&#8203;1251](https://github.com/evanw/esbuild/issues/1251)) Previously the parser did not distinguish between unquoted and quoted properties, since there is no semantic difference. However, some tools such as [Google Closure Compiler](https://developers.google.com/closure/compiler) with "advanced mode" enabled attach their own semantic meaning to quoted properties, and processing code intended for Google Closure Compiler's advanced mode with esbuild was changing those semantics. The distinction between unquoted and quoted properties is now made in the following cases: ```js import * as ns from 'external-pkg' console.log([ { x: 1, 'y': 2 }, { x() {}, 'y'() {} }, class { x = 1; 'y' = 2 }, class { x() {}; 'y'() {} }, { x: x, 'y': y } = z, [x.x, y['y']], [ns.x, ns['y']], ]) ``` The parser will now preserve the quoted properties in these cases as long as `--minify-syntax` is not enabled. This does not mean that esbuild is officially supporting Google Closure Compiler's advanced mode, just that quoted properties are now preserved when the AST is pretty-printed. Google Closure Compiler's advanced mode accepts a language that shares syntax with JavaScript but that deviates from JavaScript semantics and there could potentially be other situations where preprocessing code intended for Google Closure Compiler's advanced mode with esbuild first causes it to break. If that happens, that is not a bug with esbuild. ### [`v0.11.18`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01118) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.17...v0.11.18) - Add support for OpenBSD on x86-64 ([#&#8203;1235](https://github.com/evanw/esbuild/issues/1235)) Someone has asked for OpenBSD to be supported on x86-64. It should now be supported starting with this release. - Fix an incorrect warning about top-level `this` This was introduced in the previous release, and happens when using a top-level `async` arrow function with a compilation target that doesn't support it. The reason is that doing this generates a shim that preserves the value of `this`. However, this warning message is confusing because there is not necessarily any `this` present in the source code. The warning message has been removed in this case. Now it should only show up if `this` is actually present in the source code. ### [`v0.11.17`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01117) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.16...v0.11.17) - Fix building with a large `stdin` string with Deno ([#&#8203;1219](https://github.com/evanw/esbuild/issues/1219)) When I did the initial port of esbuild's node-based API to Deno, I didn't realize that Deno's `write(bytes)` function doesn't actually write the provided bytes. Instead it may only write some of those bytes and needs to be repeatedly called again until it writes everything. This meant that calling esbuild's Deno-based API could hang if the API request was large enough, which can happen in practice when using the `stdin` string feature. The `write` API is now called in a loop so these hangs in Deno should now be fixed. - Add a warning about replacing `this` with `undefined` in ESM code ([#&#8203;1225](https://github.com/evanw/esbuild/issues/1225)) There is existing JavaScript code that sometimes references top-level `this` as a way to access the global scope. However, top-level `this` is actually specified to be `undefined` inside of ECMAScript module code, which makes referencing top-level `this` inside ESM code useless. This issue can come up when the existing JavaScript code is adapted for ESM by adding `import` and/or `export`. All top-level references to `this` are replaced with `undefined` when bundling to make sure ECMAScript module behavior is emulated correctly regardless of the environment in which the resulting code is run. With this release, esbuild will now warn about this when bundling: > example.mjs:1:61: warning: Top-level "this" will be replaced with undefined since this file is an ECMAScript module 1 │ export let Array = (typeof window !== 'undefined' ? window : this).Array ╵ ~~~~ example.mjs:1:0: note: This file is considered an ECMAScript module because of the "export" keyword here 1 │ export let Array = (typeof window !== 'undefined' ? window : this).Array ╵ ~~~~~~ This warning is not unique to esbuild. Rollup also already has a similar warning: (!) `this` has been rewritten to `undefined` https://rollupjs.org/guide/en/#error-this-is-undefined example.mjs 1: export let Array = (typeof window !== 'undefined' ? window : this).Array ^ - Allow a string literal as a JSX fragment ([#&#8203;1217](https://github.com/evanw/esbuild/issues/1217)) TypeScript's JSX implementation allows you to configure a custom JSX factory and a custom JSX fragment, but requires that they are both valid JavaScript identifier member expression chains. Since esbuild's JSX implementation is based on TypeScript, esbuild has the same requirement. So `React.createElement` is a valid JSX factory value but `['React', 'createElement']` is not. However, the [Mithril](https://mithril.js.org/jsx.html) framework has decided to use `"["` as a JSX fragment, which is not a valid JavaScript identifier member expression chain. This meant that using Mithril with esbuild required a workaround. In this release, esbuild now lets you use a string literal as a custom JSX fragment. It should now be easier to use esbuild's JSX implementation with libraries such as Mithril. - Fix `metafile` in `onEnd` with `watch` mode enabled ([#&#8203;1186](https://github.com/evanw/esbuild/issues/1186)) This release fixes a bug where the `metafile` property was incorrectly undefined inside plugin `onEnd` callbacks if `watch` mode is enabled for all builds after the first build. The `metafile` property was accidentally being set after calling `onEnd` instead of before. ### [`v0.11.16`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01116) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.15...v0.11.16) - Fix TypeScript `enum` edge case ([#&#8203;1198](https://github.com/evanw/esbuild/issues/1198)) In TypeScript, you can reference the inner closure variable in an `enum` within the inner closure by name: ```ts enum A { B = A } ``` The TypeScript compiler generates the following code for this case: ```ts var A; (function (A) { A[A["B"] = A] = "B"; })(A || (A = {})); ``` However, TypeScript also lets you declare an `enum` value with the same name as the inner closure variable. In that case, the value "shadows" the declaration of the inner closure variable: ```ts enum A { A = 1, B = A } ``` The TypeScript compiler generates the following code for this case: ```ts var A; (function (A) { A[A["A"] = 1] = "A"; A[A["B"] = 1] = "B"; })(A || (A = {})); ``` Previously esbuild reported a duplicate variable declaration error in the second case due to the collision between the `enum` value and the inner closure variable with the same name. With this release, the shadowing is now handled correctly. - Parse the `@-moz-document` CSS rule ([#&#8203;1203](https://github.com/evanw/esbuild/issues/1203)) This feature has been removed from the web because it's actively harmful, at least according to [this discussion](https://bugzilla.mozilla.org/show_bug.cgi?id=1035091). However, there is one exception where `@-moz-document url-prefix() {` is accepted by Firefox to basically be an "if Firefox" conditional rule. Because of this, esbuild now parses the `@-moz-document` CSS rule. This should result in better pretty-printing and minification and no more warning when this rule is used. - Fix syntax error in TypeScript-specific speculative arrow function parsing ([#&#8203;1211](https://github.com/evanw/esbuild/issues/1211)) Because of grammar ambiguities, expressions that start with a parenthesis are parsed using what's called a "cover grammar" that is a super-position of both a parenthesized expression and an arrow function parameter list. In JavaScript, the cover grammar is unambiguously an arrow function if and only if the following token is a `=>` token. But in TypeScript, the expression is still ambiguously a parenthesized expression or an arrow function if the following token is a `:` since it may be the second half of the `?:` operator or a return type annotation. This requires speculatively attempting to reduce the cover grammar to an arrow function parameter list. However, when doing this esbuild eagerly reported an error if a default argument was encountered and the target is `es5` (esbuild doesn't support lowering default arguments to ES5). This is problematic in the following TypeScript code since the parenthesized code turns out to not be an arrow function parameter list: ```ts function foo(check, hover) { return check ? (hover = 2, bar) : baz(); } ``` Previously this code incorrectly generated an error since `hover = 2` was incorrectly eagerly validated as a default argument. With this release, the reporting of the default argument error when targeting `es5` is now done lazily and only when it's determined that the parenthesized code should actually be interpreted as an arrow function parameter list. - Further changes to the behavior of the `browser` field ([#&#8203;1209](https://github.com/evanw/esbuild/issues/1209)) This release includes some changes to how the `browser` field in `package.json` is interpreted to better match how Browserify, Webpack, Parcel, and Rollup behave. The interpretation of this map in esbuild is intended to be applied if and only if it's applied by any one of these bundlers. However, there were some cases where esbuild applied the mapping and none of the other bundlers did, which could lead to build failures. These cases have been added to my [growing list of `browser` field test cases](https://github.com/evanw/package-json-browser-tests) and esbuild's behavior should now be consistent with other bundlers again. - Avoid placing a `super()` call inside a `return` statement ([#&#8203;1208](https://github.com/evanw/esbuild/issues/1208)) When minification is enabled, an expression followed by a return statement (e.g. `a(); return b`) is merged into a single statement (e.g. `return a(), b`). This is done because it sometimes results in smaller code. If the return statement is the only statement in a block and the block is in a single-statement context, the block can be removed which saves a few characters. Previously esbuild applied this rule to calls to `super()` inside of constructors. Doing that broke esbuild's class lowering transform that tries to insert class field initializers after the `super()` call. This transform isn't robust and only scans the top-level statement list inside the constructor, so inserting the `super()` call inside of the `return` statement means class field initializers were inserted before the `super()` call instead of after. This could lead to run-time crashes due to initialization failure. With this release, top-level calls to `super()` will no longer be placed inside `return` statements (in addition to various other kinds of statements such as `throw`, which are now also handled). This should avoid class field initializers being inserted before the `super()` call. - Fix a bug with `onEnd` and watch mode ([#&#8203;1186](https://github.com/evanw/esbuild/issues/1186)) This release fixes a bug where `onEnd` plugin callbacks only worked with watch mode when an `onRebuild` watch mode callback was present. Now `onEnd` callbacks should fire even if there is no `onRebuild` callback. - Fix an edge case with minified export names and code splitting ([#&#8203;1201](https://github.com/evanw/esbuild/issues/1201)) The names of symbols imported from other chunks were previously not considered for renaming during minified name assignment. This could cause a syntax error due to a name collision when two symbols have the same original name. This was just an oversight and has been fixed, so symbols imported from other chunks should now be renamed when minification is enabled. - Provide a friendly error message when you forget `async` ([#&#8203;1216](https://github.com/evanw/esbuild/issues/1216)) If the parser hits a parse error inside a non-asynchronous function or arrow expression and the previous token is `await`, esbuild will now report a friendly error about a missing `async` keyword instead of reporting the parse error. This behavior matches other JavaScript parsers including TypeScript, Babel, and V8. The previous error looked like this: > test.ts:2:8: error: Expected ";" but found "f" 2 │ await f(); ╵ ^ The error now looks like this: > example.js:2:2: error: "await" can only be used inside an "async" function 2 │ await f(); ╵ ~~~~~ example.js:1:0: note: Consider adding the "async" keyword here 1 │ function f() { │ ^ ╵ async ### [`v0.11.15`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01115) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.14...v0.11.15) - Provide options for how to handle legal comments ([#&#8203;919](https://github.com/evanw/esbuild/issues/919)) A "legal comment" is considered to be any comment that contains `@license` or `@preserve` or that starts with `//!` or `/*!`. These comments are preserved in output files by esbuild since that follows the intent of the original authors of the code. However, some people want to remove the automatically-generated license information before they distribute their code. To facilitate this, esbuild now provides several options for how to handle legal comments (via `--legal-comments=` in the CLI and `legalComments` in the JS API): - `none`: Do not preserve any legal comments - `inline`: Preserve all statement-level legal comments - `eof`: Move all statement-level legal comments to the end of the file - `linked`: Move all statement-level legal comments to a `.LEGAL.txt` file and link to them with a comment - `external`: Move all statement-level legal comments to a `.LEGAL.txt` file but to not link to them The default behavior is `eof` when bundling and `inline` otherwise. - Add `onStart` and `onEnd` callbacks to the plugin API Plugins can now register callbacks to run when a build is started and ended: ```js const result = await esbuild.build({ ... incremental: true, plugins: [{ name: 'example', setup(build) { build.onStart(() => console.log('build started')) build.onEnd(result => console.log('build ended', result)) }, }], }) await result.rebuild() ``` One benefit of `onStart` and `onEnd` is that they are run for all builds including rebuilds (relevant for incremental mode, watch mode, or serve mode), so they should be a good place to do work related to the build lifecycle. More details: - `build.onStart()` You should not use an `onStart` callback for initialization since it can be run multiple times. If you want to initialize something, just put your plugin initialization code directly inside the `setup` function instead. The `onStart` callback can be `async` and can return a promise. However, the build does not wait for the promise to be resolved before starting, so a slow `onStart` callback will not necessarily slow down the build. All `onStart` callbacks are also run concurrently, not consecutively. The returned promise is purely for error reporting, and matters when the `onStart` callback needs to do an asynchronous operation that may fail. If your plugin needs to wait for an asynchronous task in `onStart` to complete before any `onResolve` or `onLoad` callbacks are run, you will need to have your `onResolve` or `onLoad` callbacks block on that task from `onStart`. Note that `onStart` callbacks do not have the ability to mutate `build.initialOptions`. The initial options can only be modified within the `setup` function and are consumed once the `setup` function returns. All rebuilds use the same initial options so the initial options are never re-consumed, and modifications to `build.initialOptions` that are done within `onStart` are ignored. - `build.onEnd()` All `onEnd` callbacks are run in serial and each callback is given access to the final build result. It can modify the build result before returning and can delay the end of the build by returning a promise. If you want to be able to inspect the build graph, you should set `build.initialOptions.metafile = true` and the build graph will be returned as the `metafile` property on the build result object. ### [`v0.11.14`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01114) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.13...v0.11.14) - Implement arbitrary module namespace identifiers This introduces new JavaScript syntax: ```js import {'🍕' as food} from 'file' export {food as '🧀'} ``` [The proposal for this feature](https://github.com/bmeck/proposal-arbitrary-module-namespace-identifiers) appears to not be going through the regular TC39 process. It is being done as a subtle [direct pull request](https://github.com/tc39/ecma262/pull/2154) instead. It seems appropriate for esbuild to support this feature since it has been implemented in V8 and has now shipped in Chrome 90 and node 16. According to the proposal, this feature is intended to improve interop with non-JavaScript languages which use exports that aren't valid JavaScript identifiers such as `Foo::~Foo`. In particular, WebAssembly allows any valid UTF-8 string as to be used as an export alias. This feature was actually already partially possible in previous versions of JavaScript via the computed property syntax: ```js import * as ns from './file.json' console.log(ns['🍕']) ``` However, doing this is very un-ergonomic and exporting something as an arbitrary name is impossible outside of `export * from`. So this proposal is designed to fully fill out the possibility matrix and make arbitrary alias names a proper first-class feature. - Implement more accurate `sideEffects` behavior from Webpack ([#&#8203;1184](https://github.com/evanw/esbuild/issues/1184)) This release adds support for the implicit `**/` prefix that must be added to paths in the `sideEffects` array in `package.json` if the path does not contain `/`. Another way of saying this is if `package.json` contains a `sideEffects` array with a string that doesn't contain a `/` then it should be treated as a file name instead of a path. Previously esbuild treated all strings in this array as paths, which does not match how Webpack behaves. The result of this meant that esbuild could consider files to have no side effects while Webpack would consider the same files to have side effects. This bug should now be fixed. ### [`v0.11.13`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01113) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.12...v0.11.13) - Implement ergonomic brand checks for private fields This introduces new JavaScript syntax: ```js class Foo { #field static isFoo(x) { return #foo in x // This is an "ergonomic brand check" } } assert(Foo.isFoo(new Foo)) ``` [The TC39 proposal for this feature](https://github.com/tc39/proposal-private-fields-in-in) is currently at stage 3 but has already been shipped in Chrome 91 and has also landed in Firefox. It seems reasonably inevitable given that it's already shipping and that it's a very simple feature, so it seems appropriate to add this feature to esbuild. - Add the `--allow-overwrite` flag ([#&#8203;1152](https://github.com/evanw/esbuild/issues/1152)) This is a new flag that allows output files to overwrite input files. It's not enabled by default because doing so means overwriting your source code, which can lead to data loss if your code is not checked in. But supporting this makes certain workflows easier by avoiding the need for a temporary directory so doing this is now supported. - Minify property accesses on object literals ([#&#8203;1166](https://github.com/evanw/esbuild/issues/1166)) The code `{a: {b: 1}}.a.b` will now be minified to `1`. This optimization is relatively complex and hard to do safely. Here are some tricky cases that are correctly handled: ```js var obj = {a: 1} assert({a: 1, a: 2}.a === 2) assert({a: 1, [String.fromCharCode(97)]: 2}.a === 2) assert({__proto__: obj}.a === 1) assert({__proto__: null}.a === undefined) assert({__proto__: null}.__proto__ === undefined) assert({a: function() { return this.b }, b: 1}.a() === 1) assert(({a: 1}.a = 2) === 2) assert(++{a: 1}.a === 2) assert.throws(() => { new ({ a() {} }.a) }) ``` - Improve arrow function parsing edge cases There are now more situations where arrow expressions are not allowed. This improves esbuild's alignment with the JavaScript specification. Some examples of cases that were previously allowed but that are now no longer allowed: ```js 1 + x => {} console.log(x || async y => {}) class Foo extends async () => {} {} ``` ### [`v0.11.12`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01112) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.11...v0.11.12) - Fix a bug where `-0` and `0` were collapsed to the same value ([#&#8203;1159](https://github.com/evanw/esbuild/issues/1159)) Previously esbuild would collapse `Object.is(x ? 0 : -0, -0)` into `Object.is((x, 0), -0)` during minification, which is incorrect. The IEEE floating-point value `-0` is a different bit pattern than `0` and while they both compare equal, the difference is detectable in a few scenarios such as when using `Object.is()`. The minification transformation now checks for `-0` vs. `0` and no longer has this bug. This fix was contributed by [@&#8203;rtsao](https://github.com/rtsao). - Match the TypeScript compiler's output in a strange edge case ([#&#8203;1158](https://github.com/evanw/esbuild/issues/1158)) With this release, esbuild's TypeScript-to-JavaScript transform will no longer omit the namespace in this case: ```ts namespace Something { export declare function Print(a: string): void } Something.Print = function(a) {} ``` This was previously omitted because TypeScript omits empty namespaces, and the namespace was considered empty because the `export declare function` statement isn't "real": ```ts namespace Something { export declare function Print(a: string): void setTimeout(() => Print('test')) } Something.Print = function(a) {} ``` The TypeScript compiler compiles the above code into the following: ```js var Something; (function (Something) { setTimeout(() => Print('test')); })(Something || (Something = {})); Something.Print = function (a) { }; ``` Notice how `Something.Print` is never called, and what appears to be a reference to the `Print` symbol on the namespace `Something` is actually a reference to the global variable `Print`. I can only assume this is a bug in TypeScript, but it's important to replicate this behavior inside esbuild for TypeScript compatibility. The TypeScript-to-JavaScript transform in esbuild has been updated to match the TypeScript compiler's output in both of these cases. - Separate the `debug` log level into `debug` and `verbose` You can now use `--log-level=debug` to get some additional information that might indicate some problems with your build, but that has a high-enough false-positive rate that it isn't appropriate for warnings, which are on by default. Enabling the `debug` log level no longer generates a torrent of debug information like it did in the past; that behavior is now reserved for the `verbose` log level instead. ### [`v0.11.11`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01111) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.10...v0.11.11) - Initial support for Deno ([#&#8203;936](https://github.com/evanw/esbuild/issues/936)) You can now use esbuild in the [Deno](https://deno.land/) JavaScript environment via esbuild's official Deno package. Using it looks something like this: ```js import * as esbuild from 'https://deno.land/x/esbuild@v0.11.11/mod.js' const ts = 'let hasProcess: boolean = typeof process != "null"' const result = await esbuild.transform(ts, { loader: 'ts', logLevel: 'warning' }) console.log('result:', result) esbuild.stop() ``` It has basically the same API as esbuild's npm package with one addition: you need to call `stop()` when you're done because unlike node, Deno doesn't provide the necessary APIs to allow Deno to exit while esbuild's internal child process is still running. - Remove warnings about non-bundled use of `require` and `import` ([#&#8203;1153](https://github.com/evanw/esbuild/issues/1153), [#&#8203;1142](https://github.com/evanw/esbuild/issues/1142), [#&#8203;1132](https://github.com/evanw/esbuild/issues/1132), [#&#8203;1045](https://github.com/evanw/esbuild/issues/1045), [#&#8203;812](https://github.com/evanw/esbuild/issues/812), [#&#8203;661](https://github.com/evanw/esbuild/issues/661), [#&#8203;574](https://github.com/evanw/esbuild/issues/574), [#&#8203;512](https://github.com/evanw/esbuild/issues/512), [#&#8203;495](https://github.com/evanw/esbuild/issues/495), [#&#8203;480](https://github.com/evanw/esbuild/issues/480), [#&#8203;453](https://github.com/evanw/esbuild/issues/453), [#&#8203;410](https://github.com/evanw/esbuild/issues/410), [#&#8203;80](https://github.com/evanw/esbuild/issues/80)) Previously esbuild had warnings when bundling about uses of `require` and `import` that are not of the form `require(<string literal>)` or `import(<string literal>)`. These warnings existed because the bundling process must be able to statically-analyze all dynamic imports to determine which files must be included. Here are some real-world examples of cases that esbuild doesn't statically analyze: - From [`mongoose`](https://www.npmjs.com/package/mongoose): ```js require('./driver').set(require(global.MONGOOSE_DRIVER_PATH)); ``` - From [`moment`](https://www.npmjs.com/package/moment): ```js aliasedRequire = require; aliasedRequire('./locale/' + name); ``` - From [`logform`](https://www.npmjs.com/package/logform): ```js function exposeFormat(name) { Object.defineProperty(format, name, { get() { return require(`./${name}.js`); } }); } exposeFormat('align'); ``` All of these dynamic imports will not be bundled (i.e. they will be left as-is) and will crash at run-time if they are evaluated. Some of these crashes are ok since the code paths may have error handling or the code paths may never be used. Other crashes are not ok because the crash will actually be hit. The warning from esbuild existed to let you know that esbuild is aware that it's generating a potentially broken bundle. If you discover that your bundle is broken, it's nice to have a warning from esbuild to point out where the problem is. And it was just a warning so the build process still finishes and successfully generates output files. If you didn't want to see the warning, it was easy to turn it off via `--log-level=error`. However, there have been quite a few complaints about this warning. Some people seem to not understand the difference between a warning and an error, and think the build has failed even though output files were generated. Other people do not want to see the warning but also do not want to enable `--log-level=error`. This release removes this warning for both `require` and `import`. Now when you try to bundle code with esbuild that contains dynamic imports not of the form `require(<string literal>)` or `import(<string literal>)`, esbuild will just silently generate a potentially broken bundle. This may affect people coming from other bundlers that support certain forms of dynamic imports that are not compatible with esbuild such as the [Webpack-specific dynamic `import()` with pattern matching](https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import). ### [`v0.11.10`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01110) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.9...v0.11.10) - Provide more information about `exports` map import failures if possible ([#&#8203;1143](https://github.com/evanw/esbuild/issues/1143)) Node has a new feature where you can [add an `exports` map to your `package.json` file](https://nodejs.org/api/packages.html#packages_package_entry_points) to control how external import paths map to the files in your package. You can change which paths map to which files as well as make it impossible to import certain files (i.e. the files are private). If path resolution fails due to an `exports` map and the failure is not related to import conditions, esbuild's current error message for this just says that the import isn't possible: > example.js:1:15: error: Could not resolve "vanillajs-datepicker/js/i18n/locales/ca" (mark it as external to exclude it from the bundle) 1 │ import ca from 'vanillajs-datepicker/js/i18n/locales/ca' ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node_modules/vanillajs-datepicker/package.json:6:13: note: The path "./js/i18n/locales/ca" is not exported by package "vanillajs-datepicker" 6 │ "exports": { ╵ ^ This error message matches the error that node itself throws. However, the message could be improved in the case where someone is trying to import a file using its file system path and that path is actually exported by the package, just under a different export path. This case comes up a lot when using TypeScript because the TypeScript compiler (and therefore the Visual Studio Code IDE) [still doesn't support package `exports`](https://github.com/microsoft/TypeScript/issues/33079). With this release, esbuild will now do a reverse lookup of the file system path using the `exports` map to determine what the correct import path should be: > example.js:1:15: error: Could not resolve "vanillajs-datepicker/js/i18n/locales/ca" (mark it as external to exclude it from the bundle) 1 │ import ca from 'vanillajs-datepicker/js/i18n/locales/ca' ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ node_modules/vanillajs-datepicker/package.json:6:13: note: The path "./js/i18n/locales/ca" is not exported by package "vanillajs-datepicker" 6 │ "exports": { ╵ ^ node_modules/vanillajs-datepicker/package.json:12:19: note: The file "./js/i18n/locales/ca.js" is exported at path "./locales/ca" 12 │ "./locales/*": "./js/i18n/locales/*.js", ╵ ~~~~~~~~~~~~~~~~~~~~~~~~ example.js:1:15: note: Import from "vanillajs-datepicker/locales/ca" to get the file "node_modules/vanillajs-datepicker/js/i18n/locales/ca.js" 1 │ import ca from 'vanillajs-datepicker/js/i18n/locales/ca' │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ╵ "vanillajs-datepicker/locales/ca" Hopefully this should enable people encountering this issue to fix the problem themselves. ### [`v0.11.9`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0119) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.8...v0.11.9) - Fix escaping of non-BMP characters in property names ([#&#8203;977](https://github.com/evanw/esbuild/issues/977)) Property names in object literals do not have to be quoted if the property is a valid JavaScript identifier. This is defined as starting with a character in the `ID_Start` Unicode category and ending with zero or more characters in the `ID_Continue` Unicode category. However, esbuild had a bug where non-BMP characters (i.e. characters encoded using two UTF-16 code units instead of one) were always checked against `ID_Continue` instead of `ID_Start` because they included a code unit that wasn't at the start. This could result in invalid JavaScript being generated when using `--charset=utf8` because `ID_Continue` is a superset of `ID_Start` and contains some characters that are not valid at the start of an identifier. This bug has been fixed. - Be maximally liberal in the interpretation of the `browser` field ([#&#8203;740](https://github.com/evanw/esbuild/issues/740)) The `browser` field in `package.json` is an informal convention followed by browser-specific bundlers that allows package authors to substitute certain node-specific import paths with alternative browser-specific import paths. It doesn't have a rigorous specification and the [canonical description](https://github.com/defunctzombie/package-browser-field-spec) of the feature doesn't include any tests. As a result, each bundler implements this feature differently. I have tried to create a [survey of how different bundlers interpret the `browser` field](https://github.com/evanw/package-json-browser-tests) and the results are very inconsistent. This release attempts to change esbuild to support the union of the behavior of all other bundlers. That way if people have the `browser` field working with some other bundler and they switch to esbuild, the `browser` field shouldn't ever suddenly stop working. This seemed like the most principled approach to take in this situation. The drawback of this approach is that it means the `browser` field may start working when switching to esbuild when it was previously not working. This could cause bugs, but I consider this to be a problem with the package (i.e. not using a more well-supported form of the `browser` field), not a problem with esbuild itself. ### [`v0.11.8`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0118) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.7...v0.11.8) - Fix hash calculation for code splitting and dynamic imports ([#&#8203;1076](https://github.com/evanw/esbuild/issues/1076)) The hash included in the file name of each output file is intended to change if and only if anything relevant to the content of that output file changes. It includes: - The contents of the file with the paths of other output files omitted - The output path of the file the final hash omitted - Some information about the input files involved in that output file - The contents of the associated source map, if there is one - All of the information above for all transitive dependencies found by following `import` statements However, this didn't include dynamic `import()` expressions due to an oversight. With this release, dynamic `import()` expressions are now also counted as transitive dependencies. This fixes an issue where the content of an output file could change without its hash also changing. As a side effect of this change, dynamic imports inside output files of other output files are now listed in the metadata file if the `metafile` setting is enabled. - Refactor the internal module graph representation This release changes a large amount of code relating to esbuild's internal module graph. The changes are mostly organizational and help consolidate most of the logic around maintaining various module graph invariants into a separate file where it's easier to audit. The Go language doesn't have great abstraction capabilities (e.g. no zero-cost iterators) so the enforcement of this new abstraction is unfortunately done by convention instead of by the compiler, and there is currently still some code that bypasses the abstraction. But it's better than it was before. Another relevant change was moving a number of special cases that happened during the tree shaking traversal into the graph itself instead. Previously there were quite a few implicit dependency rules that were checked in specific places, which was hard to follow. Encoding these special case constraints into the graph itself makes the problem easier to reason about and should hopefully make the code more regular and robust. Finally, this set of changes brings back full support for the `sideEffects` annotation in `package.json`. It was previously disabled when code splitting was active as a temporary measure due to the discovery of some bugs in that scenario. But I believe these bugs have been resolved now that tree shaking and code splitting are done in separate passes (see the previous release for more information). ### [`v0.11.7`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0117) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.6...v0.11.7) - Fix incorrect chunk reference with code splitting, css, and dynamic imports ([#&#8203;1125](https://github.com/evanw/esbuild/issues/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 ([#&#8203;1123](https://github.com/evanw/esbuild/issues/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): ```js 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`): ```js 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: ```js 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: ```js 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 ([#&#8203;1131](https://github.com/evanw/esbuild/issues/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: ```js 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: ```js 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. ### [`v0.11.6`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0116) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.5...v0.11.6) - Fix an incorrect minification transformation ([#&#8203;1121](https://github.com/evanw/esbuild/issues/1121)) This release removes an incorrect substitution rule in esbuild's peephole optimizer, which is run when minification is enabled. The incorrect rule transformed `if(a && falsy)` into `if(a, falsy)` which is equivalent if `falsy` has no side effects (such as the literal `false`). However, the rule didn't check that the expression is side-effect free first which could result in miscompiled code. I have removed the rule instead of modifying it to check for the lack of side effects first because while the code is slightly smaller, it may also be more expensive at run-time which is undesirable. The size savings are also very insignificant. - Change how `NODE_PATH` works to match node ([#&#8203;1117](https://github.com/evanw/esbuild/issues/1117)) Node searches for packages in nearby `node_modules` directories, but it also allows you to inject extra directories to search for packages in using the `NODE_PATH` environment variable. This is supported when using esbuild's CLI as well as via the `nodePaths` option when using esbuild's API. Node's module resolution algorithm is well-documented, and esbuild's path resolution is designed to follow it. The full algorithm is here: https://nodejs.org/api/modules.html#modules_all_together. However, it appears that the documented algorithm is incorrect with regard to `NODE_PATH`. The documentation says `NODE_PATH` directories should take precedence over `node_modules` directories, and so that's how esbuild worked. However, in practice node actually does it the other way around. Starting with this release, esbuild will now allow `node_modules` directories to take precedence over `NODE_PATH` directories. This is a deviation from the published algorithm. - Provide a better error message for incorrectly-quoted JSX attributes ([#&#8203;959](https://github.com/evanw/esbuild/issues/959), [#&#8203;1115](https://github.com/evanw/esbuild/issues/1115)) People sometimes try to use the output of `JSON.stringify()` as a JSX attribute when automatically-generating JSX code. Doing so is incorrect because JSX strings work like XML instead of like JS (since JSX is XML-in-JS). Specifically, using a backslash before a quote does not cause it to be escaped: ```jsx // JSX ends the "content" attribute here and sets "content" to 'some so-called \\' // v let button = <Button content="some so-called \"button text\"" /> // ^ // There is no "=" after the JSX attribute "text", so we expect a ">" ``` It's not just esbuild; Babel and TypeScript also treat this as a syntax error. All of these JSX parsers are just following [the JSX specification](https://facebook.github.io/jsx/). This has come up twice now so it could be worth having a dedicated error message. Previously esbuild had a generic syntax error like this: > example.jsx:1:58: error: Expected ">" but found "\\" 1 │ let button = <Button content="some so-called \"button text\"" /> ╵ ^ Now esbuild will provide more information if it detects this case: > example.jsx:1:58: error: Unexpected backslash in JSX element 1 │ let button = <Button content="some so-called \"button text\"" /> ╵ ^ example.jsx:1:45: note: Quoted JSX attributes use XML-style escapes instead of JavaScript-style escapes 1 │ let button = <Button content="some so-called \"button text\"" /> │ ~~ ╵ &quot; example.jsx:1:29: note: Consider using a JavaScript string inside {...} instead of a quoted JSX attribute 1 │ let button = <Button content="some so-called \"button text\"" /> │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ╵ {"some so-called \"button text\""} ### [`v0.11.5`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0115) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.4...v0.11.5) - Add support for the `override` keyword in TypeScript 4.3 ([#&#8203;1105](https://github.com/evanw/esbuild/pull/1105)) The latest version of TypeScript (now in beta) adds a new keyword called `override` that can be used on class members. You can read more about this feature in [Microsoft's blog post about TypeScript 4.3](https://devblogs.microsoft.com/typescript/announcing-typescript-4-3-beta/#override-and-the-noimplicitoverride-flag). It looks like this: ```ts class SpecializedComponent extends SomeComponent { override show() { // ... } } ``` With this release, esbuild will now ignore the `override` keyword when parsing TypeScript code instead of treating this keyword as a syntax error, which means esbuild can now support TypeScript 4.3 syntax. This change was contributed by [@&#8203;g-plane](https://github.com/g-plane). - Allow `async` plugin `setup` functions With this release, you can now return a promise from your plugin's `setup` function to delay the start of the build: ```js let slowInitPlugin = { name: 'slow-init', async setup(build) { // Delay the start of the build await new Promise(r => setTimeout(r, 1000)) }, } ``` This is useful if your plugin needs to do something asynchronous before the build starts. For example, you may need some asynchronous information before modifying the `initialOptions` object, which must be done before the build starts for the modifications to take effect. - Add some optimizations around hashing This release contains two optimizations to the hashes used in output file names: 1. Hash generation now happens in parallel with other work, and other work only blocks on the hash computation if the hash ends up being needed (which is only if `[hash]` is included in `--entry-names=`, and potentially `--chunk-names=` if it's relevant). This is a performance improvement because `--entry-names=` does not include `[hash]` in the default case, so bundling time no longer always includes hashing time. 2. The hashing algorithm has been changed from SHA1 to [xxHash](https://github.com/Cyan4973/xxHash) (specifically [this Go implementation](https://github.com/cespare/xxhash)) which means the hashing step is around 6x faster than before. Thanks to [@&#8203;Jarred-Sumner](https://github.com/Jarred-Sumner) for the suggestion. - Disable tree shaking annotations when code splitting is active ([#&#8203;1070](https://github.com/evanw/esbuild/issues/1070), [#&#8203;1081](https://github.com/evanw/esbuild/issues/1081)) Support for [Webpack's `"sideEffects": false` annotation](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) in `package.json` is now disabled when code splitting is enabled and there is more than one entry point. This avoids a bug that could cause generated chunks to reference each other in some cases. Now all chunks generated by code splitting should be acyclic. ### [`v0.11.4`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0114) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.3...v0.11.4) - Avoid name collisions with TypeScript helper functions ([#&#8203;1102](https://github.com/evanw/esbuild/issues/1102)) Helper functions are sometimes used when transforming newer JavaScript syntax for older browsers. For example, `let {x, ...y} = {z}` is transformed into `let _a = {z}, {x} = _a, y = __rest(_a, ["x"])` which uses the `__rest` helper function. Many of esbuild's transforms were modeled after the transforms in the TypeScript compiler, so many of the helper functions use the same names as TypeScript's helper functions. However, the TypeScript compiler doesn't avoid name collisions with existing identifiers in the transformed code. This means that post-processing esbuild's output with the TypeScript compiler (e.g. for lowering ES6 to ES5) will cause issues since TypeScript will fail to call its own helper functions: [microsoft/TypeScript#&#8203;43296](https://github.com/microsoft/TypeScript/issues/43296). There is also a problem where TypeScript's `tslib` library overwrites globals with these names, which can overwrite esbuild's helper functions if code bundled with esbuild is run in the global scope. To avoid these problems, esbuild will now use different names for its helper functions. - Fix a chunk hashing issue ([#&#8203;1099](https://github.com/evanw/esbuild/issues/1099)) Previously the chunk hashing algorithm skipped hashing entry point chunks when the `--entry-names=` setting doesn't contain `[hash]`, since the hash wasn't used in the file name. However, this is no longer correct with the change in version 0.11.0 that made dynamic entry point chunks use `--chunk-names=` instead of `--entry-names=` since `--chunk-names=` can still contain `[hash]`. With this release, chunk contents will now always be hashed regardless of the chunk type. This makes esbuild somewhat slower than before in the common case, but it fixes this correctness issue. ### [`v0.11.3`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0113) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.2...v0.11.3) - Auto-define `process.env.NODE_ENV` when platform is set to `browser` All code in the React world has the requirement that the specific expression `process.env.NODE_ENV` must be replaced with a string at compile-time or your code will immediately crash at run-time. This is a common stumbling point for people when they start using esbuild with React. Previously bundling code with esbuild containing `process.env.NODE_ENV` without defining a string replacement first was a warning that warned you about the lack of a define. With this release esbuild will now attempt to define `process.env.NODE_ENV` automatically instead of warning about it. This will be implicitly defined to `"production"` if minification is enabled and `"development"` otherwise. This automatic behavior only happens when the platform is `browser`, since `process` is not a valid browser API and will never exist in the browser. This is also only done if there are no existing defines for `process`, `process.env`, or `process.env.NODE_ENV` so you can override the automatic value if necessary. If you need to disable this behavior, you can use the `neutral` platform instead of the `browser` platform. - Retain side-effect free intermediate re-exporting files ([#&#8203;1088](https://github.com/evanw/esbuild/issues/1088)) This fixes a subtle bug with esbuild's support for [Webpack's `"sideEffects": false` annotation](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) in `package.json` when combined with re-export statements. A re-export is when you import something from one file and then export it again. You can re-export something with `export * from` or `export {foo} from` or `import {foo} from` followed by `export {foo}`. The bug was that files which only contain re-exports and that are marked as being side-effect free were not being included in the bundle if you import one of the re-exported symbols. This is because esbuild's implementation of re-export linking caused the original importing file to "short circuit" the re-export and just import straight from the file containing the final symbol, skipping the file containing the re-export entirely. This was normally not observable since the intermediate file consisted entirely of re-exports, which have no side effects. However, a recent change to allow ESM files to be lazily-initialized relies on all intermediate files being included in the bundle to trigger the initialization of the lazy evaluation wrappers. So the behavior of skipping over re-export files is now causing the imported symbols to not be initialized if the re-exported file is marked as lazily-evaluated. The fix is to track all re-exports in the import chain from the original file to the file containing the final symbol and then retain all of those statements if the import ends up being used. - Add a very verbose `debug` log level This log level is an experiment. Enabling it logs a lot of information (currently only about path resolution). The idea is that if you are having an obscure issue, the debug log level might contain some useful information. Unlike normal logs which are meant to mainly provide actionable information, these debug logs are intentionally mostly noise and are designed to be searched through instead. Here is an example of debug-level log output: > debug: Resolving import "react" in directory "src" of type "import-statement" note: Read 26 entries for directory "src" note: Searching for "react" in "node_modules" directories starting from "src" note: Attempting to load "src/react" as a file note: Failed to find file "src/react" note: Failed to find file "src/react.tsx" note: Failed to find file "src/react.ts" note: Failed to find file "src/react.js" note: Failed to find file "src/react.css" note: Failed to find file "src/react.svg" note: Attempting to load "src/react" as a directory note: Failed to read directory "src/react" note: Parsed package name "react" and package subpath "." note: Checking for a package in the directory "node_modules/react" note: Read 7 entries for directory "node_modules/react" note: Read 393 entries for directory "node_modules" note: Attempting to load "node_modules/react" as a file note: Failed to find file "node_modules/react" note: Failed to find file "node_modules/react.tsx" note: Failed to find file "node_modules/react.ts" note: Failed to find file "node_modules/react.js" note: Failed to find file "node_modules/react.css" note: Failed to find file "node_modules/react.svg" note: Attempting to load "node_modules/react" as a directory note: Read 7 entries for directory "node_modules/react" note: Resolved to "node_modules/react/index.js" using the "main" field in "node_modules/react/package.json" note: Read 7 entries for directory "node_modules/react" note: Read 7 entries for directory "node_modules/react" note: Primary path is "node_modules/react/index.js" in namespace "file" ### [`v0.11.2`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01123) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.1...v0.11.2) - Add a shim function for unbundled uses of `require` ([#&#8203;1202](https://github.com/evanw/esbuild/issues/1202)) Modules in CommonJS format automatically get three variables injected into their scope: `module`, `exports`, and `require`. These allow the code to import other modules and to export things from itself. The bundler automatically rewrites uses of `module` and `exports` to refer to the module's exports and certain uses of `require` to a helper function that loads the imported module. Not all uses of `require` can be converted though, and un-converted uses of `require` will end up in the output. This is problematic because `require` is only present at run-time if the output is run as a CommonJS module. Otherwise `require` is undefined, which means esbuild's behavior is inconsistent between compile-time and run-time. The `module` and `exports` variables are objects at compile-time and run-time but `require` is a function at compile-time and undefined at run-time. This causes code that checks for `typeof require` to have inconsistent behavior: ```js if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { console.log('CommonJS detected') } ``` In the above example, ideally `CommonJS detected` would always be printed since the code is being bundled with a CommonJS-aware bundler. To fix this, esbuild will now substitute references to `require` with a stub `__require` function when bundling if the output format is something other than CommonJS. This should ensure that `require` is now consistent between compile-time and run-time. When bundled, code that uses unbundled references to `require` will now look something like this: ```js var __require = (x) => { if (typeof require !== "undefined") return require(x); throw new Error('Dynamic require of "' + x + '" is not supported'); }; var __commonJS = (cb, mod) => () => (mod || cb((mod = {exports: {}}).exports, mod), mod.exports); var require_example = __commonJS((exports, module) => { if (typeof __require === "function" && typeof exports === "object" && typeof module === "object") { console.log("CommonJS detected"); } }); require_example(); ``` - Fix incorrect caching of internal helper function library ([#&#8203;1292](https://github.com/evanw/esbuild/issues/1292)) This release fixes a bug where running esbuild multiple times with different configurations sometimes resulted in code that would crash at run-time. The bug was introduced in version 0.11.19 and happened because esbuild's internal helper function library is parsed once and cached per configuration, but the new profiler name option was accidentally not included in the cache key. This option is now included in the cache key so this bug should now be fixed. - Minor performance improvements This release contains some small performance improvements to offset an earlier minor performance regression due to the addition of certain features such as hashing for entry point files. The benchmark times on the esbuild website should now be accurate again (versions of esbuild after the regression but before this release were slightly slower than the benchmark). ### [`v0.11.1`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;01119) [Compare Source](https://github.com/evanw/esbuild/compare/v0.11.0...v0.11.1) - Allow esbuild to be restarted in Deno ([#&#8203;1238](https://github.com/evanw/esbuild/pull/1238)) The esbuild API for [Deno](https://deno.land) has an extra function called `stop()` that doesn't exist in esbuild's API for node. This is because Deno doesn't provide a way to stop esbuild automatically, so calling `stop()` is required to allow Deno to exit. However, once stopped the esbuild API could not be restarted. With this release, you can now continue to use esbuild after calling `stop()`. This will restart esbuild's API and means that you will need to call `stop()` again for Deno to be able to exit. This feature was contributed by [@&#8203;lucacasonato](https://github.com/lucacasonato). - Fix code splitting edge case ([#&#8203;1252](https://github.com/evanw/esbuild/issues/1252)) This release fixes an edge case where bundling with code splitting enabled generated incorrect code if multiple ESM entry points re-exported the same re-exported symbol from a CommonJS file. In this case the cross-chunk symbol dependency should be the variable that holds the return value from the `require()` call instead of the original ESM named `import` clause item. When this bug occurred, the generated ESM code contained an export and import for a symbol that didn't exist, which caused a module initialization error. This case should now work correctly. - Fix code generation with `declare` class fields ([#&#8203;1242](https://github.com/evanw/esbuild/issues/1242)) This fixes a bug with TypeScript code that uses `declare` on a class field and your `tsconfig.json` file has `"useDefineForClassFields": true`. Fields marked as `declare` should not be defined in the generated code, but they were incorrectly being declared as `undefined`. These fields are now correctly omitted from the generated code. - Annotate module wrapper functions in debug builds ([#&#8203;1236](https://github.com/evanw/esbuild/pull/1236)) Sometimes esbuild needs to wrap certain modules in a function when bundling. This is done both for lazy evaluation and for CommonJS modules that use a top-level `return` statement. Previously these functions were all anonymous, so stack traces for errors thrown during initialization looked like this: Error: Electron failed to install correctly, please delete node_modules/electron and try installing again at getElectronPath (out.js:16:13) at out.js:19:21 at out.js:1:45 at out.js:24:3 at out.js:1:45 at out.js:29:3 at out.js:1:45 at Object.<anonymous> (out.js:33:1) This release adds names to these anonymous functions when minification is disabled. The above stack trace now looks like this: Error: Electron failed to install correctly, please delete node_modules/electron and try installing again at getElectronPath (out.js:19:15) at node_modules/electron/index.js (out.js:22:23) at __require (out.js:2:44) at src/base/window.js (out.js:29:5) at __require (out.js:2:44) at src/base/kiosk.js (out.js:36:5) at __require (out.js:2:44) at Object.<anonymous> (out.js:41:1) This is similar to Webpack's development-mode behavior: Error: Electron failed to install correctly, please delete node_modules/electron and try installing again at getElectronPath (out.js:23:11) at Object../node_modules/electron/index.js (out.js:27:18) at __webpack_require__ (out.js:96:41) at Object../src/base/window.js (out.js:49:1) at __webpack_require__ (out.js:96:41) at Object../src/base/kiosk.js (out.js:38:1) at __webpack_require__ (out.js:96:41) at out.js:109:1 at out.js:111:3 at Object.<anonymous> (out.js:113:12) These descriptive function names will additionally be available when using a profiler such as the one included in the "Performance" tab in Chrome Developer Tools. Previously all functions were named `(anonymous)` which made it difficult to investigate performance issues during bundle initialization. - Add CSS minification for more cases The following CSS minification cases are now supported: - The CSS `margin` property family is now minified including combining the `margin-top`, `margin-right`, `margin-bottom`, and `margin-left` properties into a single `margin` property. - The CSS `padding` property family is now minified including combining the `padding-top`, `padding-right`, `padding-bottom`, and `padding-left` properties into a single `padding` property. - The CSS `border-radius` property family is now minified including combining the `border-top-left-radius`, `border-top-right-radius`, `border-bottom-right-radius`, and `border-bottom-left-radius` properties into a single `border-radius` property. - The four special pseudo-elements `::before`, `::after`, `::first-line`, and `::first-letter` are allowed to be parsed with one `:` for legacy reasons, so the `::` is now converted to `:` for these pseudo-elements. - Duplicate CSS rules are now deduplicated. Only the last rule is kept, since that's the only one that has any effect. This applies for both top-level rules and nested rules. - Preserve quotes around properties when minification is disabled ([#&#8203;1251](https://github.com/evanw/esbuild/issues/1251)) Previously the parser did not distinguish between unquoted and quoted properties, since there is no semantic difference. However, some tools such as [Google Closure Compiler](https://developers.google.com/closure/compiler) with "advanced mode" enabled attach their own semantic meaning to quoted properties, and processing code intended for Google Closure Compiler's advanced mode with esbuild was changing those semantics. The distinction between unquoted and quoted properties is now made in the following cases: ```js import * as ns from 'external-pkg' console.log([ { x: 1, 'y': 2 }, { x() {}, 'y'() {} }, class { x = 1; 'y' = 2 }, class { x() {}; 'y'() {} }, { x: x, 'y': y } = z, [x.x, y['y']], [ns.x, ns['y']], ]) ``` The parser will now preserve the quoted properties in these cases as long as `--minify-syntax` is not enabled. This does not mean that esbuild is officially supporting Google Closure Compiler's advanced mode, just that quoted properties are now preserved when the AST is pretty-printed. Google Closure Compiler's advanced mode accepts a language that shares syntax with JavaScript but that deviates from JavaScript semantics and there could potentially be other situations where preprocessing code intended for Google Closure Compiler's advanced mode with esbuild first causes it to break. If that happens, that is not a bug with esbuild. ### [`v0.11.0`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0110) [Compare Source](https://github.com/evanw/esbuild/compare/v0.10.2...v0.11.0) **This release contains backwards-incompatible changes.** Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as [recommended by npm](https://docs.npmjs.com/cli/v6/using-npm/semver/)). You should either be pinning the exact version of `esbuild` in your `package.json` file or be using a version range syntax that only accepts patch upgrades such as `~0.10.0`. See the documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information. The changes in this release mostly relate to how entry points are handled. The way output paths are generated has changed in some cases, so you may need to update how you refer to the output path for a given entry point when you update to this release (see below for details). These breaking changes are as follows: - Change how `require()` and `import()` of ESM works ([#&#8203;667](https://github.com/evanw/esbuild/issues/667), [#&#8203;706](https://github.com/evanw/esbuild/issues/706)) Previously if you call `require()` on an ESM file, or call `import()` on an ESM file with code splitting disabled, esbuild would convert the ESM file to CommonJS. For example, if you had the following input files: ```js // cjs-file.js console.log(require('./esm-file.js').foo) // esm-file.js export let foo = bar() ``` The previous bundling behavior would generate something like this: ```js var require_esm_file = __commonJS((exports) => { __markAsModule(exports); __export(exports, { foo: () => foo }); var foo = bar(); }); console.log(require_esm_file().foo); ``` This behavior has been changed and esbuild now generates something like this instead: ```js var esm_file_exports = {}; __export(esm_file_exports, { foo: () => foo }); var foo; var init_esm_file = __esm(() => { foo = bar(); }); console.log((init_esm_file(), esm_file_exports).foo); ``` The variables have been pulled out of the lazily-initialized closure and are accessible to the rest of the module's scope. Some benefits of this approach: - If another file does `import {foo} from "./esm-file.js"`, it will just reference `foo` directly and will not pay the performance penalty or code size overhead of the dynamic property accesses that come with CommonJS-style exports. So this improves performance and reduces code size in some cases. - This fixes a long-standing bug ([#&#8203;706](https://github.com/evanw/esbuild/issues/706)) where entry point exports could be broken if the entry point is a target of a `require()` call and the output format was ESM. This happened because previously calling `require()` on an entry point converted it to CommonJS, which then meant it only had a single `default` export, and the exported variables were inside the CommonJS closure and inaccessible to an ESM-style `export {}` clause. Now calling `require()` on an entry point only causes it to be lazily-initialized but all exports are still in the module scope and can still be exported using a normal `export {}` clause. - Now that this has been changed, `import()` of a module with top-level await ([#&#8203;253](https://github.com/evanw/esbuild/issues/253)) is now allowed when code splitting is disabled. Previously this didn't work because `import()` with code splitting disabled was implemented by converting the module to CommonJS and using `Promise.resolve().then(() => require())`, but converting a module with top-level await to CommonJS is impossible because the CommonJS call signature must be synchronous. Now that this implemented using lazy initialization instead of CommonJS conversion, the closure wrapping the ESM file can now be `async` and the `import()` expression can be replaced by a call to the lazy initializer. - Adding the ability for ESM files to be lazily-initialized is an important step toward additional future code splitting improvements including: manual chunk names ([#&#8203;207](https://github.com/evanw/esbuild/issues/207)), correct import evaluation order ([#&#8203;399](https://github.com/evanw/esbuild/issues/399)), and correct top-level await evaluation order ([#&#8203;253](https://github.com/evanw/esbuild/issues/253)). These features all need to make use of deferred evaluation of ESM code. In addition, calling `require()` on an ESM file now recursively wraps all transitive dependencies of that file instead of just wrapping that ESM file itself. This is an increase in the size of the generated code, but it is important for correctness ([#&#8203;667](https://github.com/evanw/esbuild/issues/667)). Calling `require()` on a module means its evaluation order is determined at run-time, which means the evaluation order of all dependencies must also be determined at run-time. If you don't want the increase in code size, you should use an `import` statement instead of a `require()` call. - Dynamic imports now use chunk names instead of entry names ([#&#8203;1056](https://github.com/evanw/esbuild/issues/1056)) Previously the output paths of dynamic imports (files imported using the `import()` syntax) were determined by the `--entry-names=` setting. However, this can cause problems if you configure the `--entry-names=` setting to omit both `[dir]` and `[hash]` because then two dynamic imports with the same name will cause an output file name collision. Now dynamic imports use the `--chunk-names=` setting instead, which is used for automatically-generated chunks. This setting is effectively required to include `[hash]` so dynamic import name collisions should now be avoided. In addition, dynamic imports no longer affect the automatically-computed default value of `outbase`. By default `outbase` is computed to be the [lowest common ancestor](https://en.wikipedia.org/wiki/Lowest_common_ancestor) directory of all entry points. Previously dynamic imports were considered entry points in this calculation so adding a dynamic entry point could unexpectedly affect entry point output file paths. This issue has now been fixed. - Allow custom output paths for individual entry points By default, esbuild will automatically generate an output path for each entry point by computing the relative path from the `outbase` directory to the entry point path, and then joining that relative path to the `outdir` directory. The output path can be customized using `outpath`, but that only works for a single file. Sometimes you may need custom output paths while using multiple entry points. You can now do this by passing the entry points as a map instead of an array: - CLI esbuild out1=in1.js out2=in2.js --outdir=out - JS ```js esbuild.build({ entryPoints: { out1: 'in1.js', out2: 'in2.js', }, outdir: 'out', }) ``` - Go ```go api.Build(api.BuildOptions{ EntryPointsAdvanced: []api.EntryPoint{{ OutputPath: "out1", InputPath: "in1.js", }, { OutputPath: "out2", InputPath: "in2.js", }}, Outdir: "out", }) ``` This will cause esbuild to generate the files `out/out1.js` and `out/out2.js` inside the output directory. These custom output paths are used as input for the `--entry-names=` path template setting, so you can use something like `--entry-names=[dir]/[name]-[hash]` to add an automatically-computed hash to each entry point while still using the custom output path. - Derive entry point output paths from the original input path ([#&#8203;945](https://github.com/evanw/esbuild/issues/945)) Previously esbuild would determine the output path for an entry point by looking at the post-resolved path. For example, running `esbuild --bundle react --outdir=out` would generate the output path `out/index.js` because the input path `react` was resolved to `node_modules/react/index.js`. With this release, the output path is now determined by looking at the pre-resolved path. For example, running `esbuild --bundle react --outdir=out` now generates the output path `out/react.js`. If you need to keep using the output path that esbuild previously generated with the old behavior, you can use the custom output path feature (described above). - Use the `file` namespace for file entry points ([#&#8203;791](https://github.com/evanw/esbuild/issues/791)) Plugins that contain an `onResolve` callback with the `file` filter don't apply to entry point paths because it's not clear that entry point paths are files. For example, you could potentially bundle an entry point of `https://www.example.com/file.js` with a HTTP plugin that automatically downloads data from the server at that URL. But this behavior can be unexpected for people writing plugins. With this release, esbuild will do a quick check first to see if the entry point path exists on the file system before running plugins. If it exists as a file, the namespace will now be `file` for that entry point path. This only checks the exact entry point name and doesn't attempt to search for the file, so for example it won't handle cases where you pass a package path as an entry point or where you pass an entry point without an extension. Hopefully this should help improve this situation in the common case where the entry point is an exact path. In addition to the breaking changes above, the following features are also included in this release: - Warn about mutation of private methods ([#&#8203;1067](https://github.com/evanw/esbuild/pull/1067)) Mutating a private method in JavaScript is not allowed, and will throw at run-time: ```js class Foo { #method() {} mutate() { this.#method = () => {} } } ``` This is the case both when esbuild passes the syntax through untransformed and when esbuild transforms the syntax into the equivalent code that uses a `WeakSet` to emulate private methods in older browsers. However, it's clear from this code that doing this will always throw, so this code is almost surely a mistake. With this release, esbuild will now warn when you do this. This change was contributed by [@&#8203;jridgewell](https://github.com/jridgewell). - Fix some obscure TypeScript type parsing edge cases In TypeScript, type parameters come after a type and are placed in angle brackets like `Foo<T>`. However, certain built-in types do not accept type parameters including primitive types such as `number`. This means `if (x as number < 1) {}` is not a syntax error while `if (x as Foo < 1) {}` is a syntax error. This release changes TypeScript type parsing to allow type parameters in a more restricted set of situations, which should hopefully better resolve these type parsing ambiguities. ### [`v0.10.2`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0102) [Compare Source](https://github.com/evanw/esbuild/compare/v0.10.1...v0.10.2) - Fix a crash that was introduced in the previous release ([#&#8203;1064](https://github.com/evanw/esbuild/issues/1064)) This crash happens when code splitting is active and there is a CSS entry point as well as two or more JavaScript entry points. There is a known issue where CSS bundling does not work when code splitting is active (code splitting is still a work in progress, see [#&#8203;608](https://github.com/evanw/esbuild/issues/608)) so doing this will likely not work as expected. But esbuild obviously shouldn't crash. This release fixes the crash, although esbuild still does not yet generate the correct CSS output in this case. - Fix private fields inside destructuring assignment ([#&#8203;1066](https://github.com/evanw/esbuild/issues/1066)) Private field syntax (i.e. `this.#field`) is supported for older language targets by converting the code into accesses into a `WeakMap`. However, although regular assignment (i.e. `this.#field = 1`) was handled destructuring assignment (i.e. `[this.#field] = [1]`) was not handled due to an oversight. Support for private fields inside destructuring assignment is now included with this release. - Fix an issue with direct `eval` and top-level symbols It was previously the case that using direct `eval` caused the file containing it to be considered a CommonJS file, even if the file used ESM syntax. This was because the evaluated code could potentially attempt to interact with top-level symbols by name and the CommonJS closure was used to isolate those symbols from other modules so their names could be preserved (otherwise their names may need to be renamed to avoid collisions). However, ESM files are no longer convertable to CommonJS files due to the need to support top-level await. This caused a bug where scope hoisting could potentially merge two modules containing direct `eval` and containing the same top-level symbol name into the same scope. These symbols were prevented from being renamed due to the direct `eval`, which caused a syntax error at run-time due to the name collision. Because of this, esbuild is dropping the guarantee that using direct `eval` in an ESM file will be able to access top-level symbols. These symbols are now free to be renamed to avoid name collisions, and will now be minified when identifier minification is enabled. This is unlikely to affect real-world code because most real-world uses of direct `eval` only attempt to access local variables, not top-level symbols. Using direct `eval` in an ESM file when bundling with esbuild will generate a warning. The warning is not new and is present in previous releases of esbuild as well. The way to avoid the warning is to avoid direct `eval`, since direct `eval` is somewhat of an anti-pattern and there are better alternatives. ### [`v0.10.1`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0101) [Compare Source](https://github.com/evanw/esbuild/compare/v0.10.0...v0.10.1) - Expose `metafile` to `onRebuild` in watch mode ([#&#8203;1057](https://github.com/evanw/esbuild/issues/1057)) Previously the build results returned to the watch mode `onRebuild` callback was missing the `metafile` property when the `metafile: true` option was present. This bug has been fixed. - Add a `formatMessages` API ([#&#8203;1058](https://github.com/evanw/esbuild/issues/1058)) This API lets you print log messages to the terminal using the same log format that esbuild itself uses. This can be used to filter esbuild's warnings while still making the output look the same. Here's an example of calling this API: ```js import esbuild from 'esbuild' const formatted = await esbuild.formatMessages([{ text: '"test" has already been declared', location: { file: 'file.js', line: 2, column: 4, length: 4, lineText: 'let test = "second"' }, notes: [{ text: '"test" was originally declared here', location: { file: 'file.js', line: 1, column: 4, length: 4, lineText: 'let test = "first"' }, }], }], { kind: 'error', color: true, terminalWidth: 100, }) process.stdout.write(formatted.join('')) ``` - Remove the file splitting optimization ([#&#8203;998](https://github.com/evanw/esbuild/issues/998)) This release removes the "file splitting optimization" that has up to this point been a part of esbuild's code splitting algorithm. This optimization allowed code within a single file to end up in separate chunks as long as that code had no side effects. For example, bundling two entry points that both use a disjoint set of code from a shared file consisting only of code without side effects would previously not generate any shared code chunks at all. This optimization is being removed because the top-level await feature was added to JavaScript after this optimization was added, and performing this optimization in the presence of top-level await is more difficult than before. The correct evaulation order of a module graph containing top-level await is extremely complicated and is specified at the module boundary. Moving code that is marked as having no side effects across module boundaries under these additional constraints is even more complexity and is getting in the way of implementing top-level await. So the optimization has been removed to unblock work on top-level await, which esbuild must support. ### [`v0.10.0`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0100) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.7...v0.10.0) **This release contains backwards-incompatible changes.** Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as [recommended by npm](https://docs.npmjs.com/cli/v6/using-npm/semver/)). You should either be pinning the exact version of `esbuild` in your `package.json` file or be using a version range syntax that only accepts patch upgrades such as `~0.9.0`. See the documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information. That said, there are no breaking API changes in this release. The breaking changes are instead about how input files are interpreted and/or how output files are generated in some cases. So upgrading should be relatively straightforward as your API calls should still work the same way, but please make sure to test your code when you upgrade because the output may be different. These breaking changes are as follows: - No longer support `module` or `exports` in an ESM file ([#&#8203;769](https://github.com/evanw/esbuild/issues/769)) This removes support for using CommonJS exports in a file with ESM exports. Previously this worked by converting the ESM file to CommonJS and then mixing the CommonJS and ESM exports into the same `exports` object. But it turns out that supporting this is additional complexity for the bundler, so it has been removed. It's also not something that works in real JavaScript environments since modules will never support both export syntaxes at once. Note that this doesn't remove support for using `require` in ESM files. Doing this still works (and can be made to work in a real ESM environment by assigning to `globalThis.require`). This also doesn't remove support for using `import` in CommonJS files. Doing this also still works. - No longer change `import()` to `require()` ([#&#8203;1029](https://github.com/evanw/esbuild/issues/1029)) Previously esbuild's transform for `import()` matched TypeScript's behavior, which is to transform it into `Promise.resolve().then(() => require())` when the current output format is something other than ESM. This was done when an import is external (i.e. not bundled), either due to the expression not being a string or due to the string matching an external import path. With this release, esbuild will no longer do this. Now `import()` expressions will be preserved in the output instead. These expressions can be handled in non-ESM code by arranging for the `import` identifier to be a function that imports ESM code. This is how node works, so it will now be possible to use `import()` with node when the output format is something other than ESM. - Run-time `export * as` statements no longer convert the file to CommonJS Certain `export * as` statements require a bundler to evaluate them at run-time instead of at compile-time like the JavaScript specification. This is the case when re-exporting symbols from an external file and a file in CommonJS format. Previously esbuild would handle this by converting the module containing the `export * as` statement to CommonJS too, since CommonJS exports are evaluated at run-time while ESM exports are evaluated at bundle-time. However, this is undesirable because tree shaking only works for ESM, not for CommonJS, and the CommonJS wrapper causes additional code bloat. Another upcoming problem is that top-level await cannot work within a CommonJS module because CommonJS `require()` is synchronous. With this release, esbuild will now convert modules containing a run-time `export * as` statement to a special ESM-plus-dynamic-fallback mode. In this mode, named exports present at bundle time can still be imported directly by name, but any imports that don't match one of the explicit named imports present at bundle time will be converted to a property access on the fallback object instead of being a bundle error. These property accesses are then resolved at run-time and will be undefined if the export is missing. - Change whether certain files are interpreted as ESM or CommonJS ([#&#8203;1043](https://github.com/evanw/esbuild/issues/1043)) The bundling algorithm currently doesn't contain any logic that requires flagging modules as CommonJS vs. ESM beforehand. Instead it handles a superset and then sort of decides later if the module should be treated as CommonJS vs. ESM based on whether the module uses the `module` or `exports` variables and/or the `exports` keyword. With this release, files that follow [node's rules for module types](https://nodejs.org/api/packages.html#packages_type) will be flagged as explicitly ESM. This includes files that end in `.mjs` and files within a package containing `"type": "module"` in the enclosing `package.json` file. The CommonJS `module` and `exports` features will be unavailable in these files. This matters most for files without any exports, since then it's otherwise ambiguous what the module type is. In addition, files without exports should now accurately fall back to being considered CommonJS. They should now generate a `default` export of an empty object when imported using an `import` statement, since that's what happens in node when you import a CommonJS file into an ESM file in node. Previously the default export could be undefined because these export-less files were sort of treated as ESM but with missing import errors turned into warnings instead. This is an edge case that rarely comes up in practice, since you usually never import things from a module that has no exports. In addition to the breaking changes above, the following features are also included in this release: - Initial support for bundling with top-level await ([#&#8203;253](https://github.com/evanw/esbuild/issues/253)) Top-level await is a feature that lets you use an `await` expression at the top level (outside of an `async` function). Here is an example: ```js let promise = fetch('https://www.example.com/data') export let data = await promise.then(x => x.json()) ``` Top-level await only works in ECMAScript modules, and does not work in CommonJS modules. This means that you must use an `import` statement or an `import()` expression to import a module containing top-level await. You cannot use `require()` because it's synchronous while top-level await is asynchronous. There should be a descriptive error message when you try to do this. This initial release only has limited support for top-level await. It is only supported with the `esm` output format, but not with the `iife` or `cjs` output formats. In addition, the compilation is not correct in that two modules that both contain top-level await and that are siblings in the import graph will be evaluated in serial instead of in parallel. Full support for top-level await will come in a future release. - Add the ability to set `sourceRoot` in source maps ([#&#8203;1028](https://github.com/evanw/esbuild/pull/1028)) You can now use the `--source-root=` flag to set the `sourceRoot` field in source maps generated by esbuild. When a `sourceRoot` is present in a source map, all source paths are resolved relative to it. This is particularly useful when you are hosting compiled code on a server and you want to point the source files to a GitHub repo, such as [what AMP does](https://cdn.ampproject.org/v0.mjs.map). Here is the description of `sourceRoot` from [the source map specification](https://sourcemaps.info/spec.html): > An optional source root, useful for relocating source files on a server or removing repeated values in the "sources" entry. This value is prepended to the individual entries in the "source" field. If the sources are not absolute URLs after prepending of the "sourceRoot", the sources are resolved relative to the SourceMap (like resolving script src in a html document). This feature was contributed by [@&#8203;jridgewell](https://github.com/jridgewell). - Allow plugins to return custom file watcher paths Currently esbuild's watch mode automatically watches all file system paths that are handled by esbuild itself, and also automatically watches the paths of files loaded by plugins when the paths are in the `file` namespace. The paths of files that plugins load in namespaces other than the `file` namespace are not automatically watched. Also, esbuild never automatically watches any file system paths that are consulted by the plugin during its processing, since esbuild is not aware of those paths. For example, this means that if a plugin calls `require.resolve()`, all of the various "does this file exist" checks that it does will not be watched automatically. So if one of those files is created in the future, esbuild's watch mode will not rebuild automatically even though the build is now outdated. To fix this problem, this release introduces the `watchFiles` and `watchDirs` properties on plugin return values. Plugins can specify these to add additional custom file system paths to esbuild's internal watch list. Paths in the `watchFiles` array cause esbuild to rebuild if the file contents change, and paths in the `watchDirs` array cause esbuild to rebuild if the set of directory entry names changes for that directory path. Note that `watchDirs` does not cause esbuild to rebuild if any of the contents of files inside that directory are changed. It also does not recursively traverse through subdirectories. It only watches the set of directory entry names (i.e. the output of the Unix `ls` command). ### [`v0.9.7`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;097) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.6...v0.9.7) - Add support for Android on ARM 64-bit ([#&#8203;803](https://github.com/evanw/esbuild/issues/803)) This release includes support for Android in the official `esbuild` package. It should now be possible to install and run esbuild on Android devices through npm. - Fix incorrect MIME types on Windows ([#&#8203;1030](https://github.com/evanw/esbuild/issues/1030)) The web server built into esbuild uses the file extension to determine the value of the `Content-Type` header. This was previously done using the `mime.TypeByExtension()` function from Go's standard library. However, this function is apparently broken on Windows because installed programs can change MIME types in the Windows registry: [golang/go#&#8203;32350](https://github.com/golang/go/issues/32350). This release fixes the problem by using a copy of Go's `mime.TypeByExtension()` function without the part that reads from the Windows registry. - Using a top-level return inside an ECMAScript module is now forbidden The CommonJS module format is implemented as an anonymous function wrapper, so technically you can use a top-level `return` statement and it will actually work. Some packages in the wild use this to exit early from module initialization, so esbuild supports this. However, the ECMAScript module format doesn't allow top-level returns. With this release, esbuild no longer allows top-level returns in ECMAScript modules. ### [`v0.9.6`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;096) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.5...v0.9.6) - Expose build options to plugins ([#&#8203;373](https://github.com/evanw/esbuild/issues/373)) Plugins can now access build options from within the plugin using the `initialOptions` property. For example: ```js let nodeEnvPlugin = { name: 'node-env', setup(build) { const options = build.initialOptions options.define = options.define || {} options.define['process.env.NODE_ENV'] = options.minify ? '"production"' : '"development"' }, } ``` - Fix an edge case with the object spread transform ([#&#8203;1017](https://github.com/evanw/esbuild/issues/1017)) This release fixes esbuild's object spread transform in cases where property assignment could be different than property definition. For example: ```js console.log({ get x() {}, ...{x: 1}, }) ``` This should print `{x: 1}` but transforming this through esbuild with `--target=es6` causes the resulting code to throw an error. The problem is that esbuild currently transforms this code to a call to `Object.assign` and that uses property assignment semantics, which causes the assignment to throw (since you can't assign to a getter-only property). With this release, esbuild will now transform this into code that manually loops over the properties and copies them over one-by-one using `Object.defineProperty` instead. This uses property definition semantics which better matches the specification. - Fix a TypeScript parsing edge case with arrow function return types ([#&#8203;1016](https://github.com/evanw/esbuild/issues/1016)) This release fixes the following TypeScript parsing edge case: ```ts ():Array<number>=>{return [1]} ``` This was tripping up esbuild's TypeScript parser because the `>=` token was split into a `>` token and a `=` token because the `>` token is needed to close the type parameter list, but the `=` token was not being combined with the following `>` token to form a `=>` token. This is normally not an issue because there is normally a space in between the `>` and the `=>` tokens here. The issue only happened when the spaces were removed. This bug has been fixed. Now after the `>=` token is split, esbuild will expand the `=` token into the following characters if possible, which can result in a `=>`, `==`, or `===` token. - Enable faster synchronous transforms under a flag ([#&#8203;1000](https://github.com/evanw/esbuild/issues/1000)) Currently the synchronous JavaScript API calls `transformSync` and `buildSync` spawn a new child process on every call. This is due to limitations with node's `child_process` API. Doing this means `transformSync` and `buildSync` are much slower than `transform` and `build`, which share the same child process across calls. There was previously a workaround for this limitation that uses node's `worker_threads` API and atomics to block the main thread while asynchronous communication happens in a worker, but that was reverted due to a bug in node's `worker_threads` implementation. Now that this bug has been fixed by node, I am re-enabling this workaround. This should result in `transformSync` and `buildSync` being much faster. This approach is experimental and is currently only enabled if the `ESBUILD_WORKER_THREADS` environment variable is present. If this use case matters to you, please try it out and let me know if you find any problems with it. - Update how optional chains are compiled to match new V8 versions ([#&#8203;1019](https://github.com/evanw/esbuild/issues/1019)) An optional chain is an expression that uses the `?.` operator, which roughly avoids evaluation of the right-hand side if the left-hand side is `null` or `undefined`. So `a?.b` is basically equivalent to `a == null ? void 0 : a.b`. When the language target is set to `es2019` or below, esbuild will transform optional chain expressions into equivalent expressions that do not use the `?.` operator. This transform is designed to match the behavior of V8 exactly, and is designed to do something similar to the equivalent transform done by the TypeScript compiler. However, V8 has recently changed its behavior in two cases: - Forced call of an optional member expression should propagate the object to the method: ```js const o = { m() { return this; } }; assert((o?.m)() === o); ``` V8 bug: https://bugs.chromium.org/p/v8/issues/detail?id=10024 - Optional call of `eval` must be an indirect eval: ```js globalThis.a = 'global'; var b = (a => eval?.('a'))('local'); assert(b === 'global'); ``` V8 bug: https://bugs.chromium.org/p/v8/issues/detail?id=10630 This release changes esbuild's transform to match V8's new behavior. The transform in the TypeScript compiler is still emulating the old behavior as of version 4.2.3, so these syntax forms should be avoided in TypeScript code for portability. ### [`v0.9.5`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;095) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.4...v0.9.5) - Fix parsing of the `[dir]` placeholder ([#&#8203;1013](https://github.com/evanw/esbuild/issues/1013)) The entry names feature in the previous release accidentally didn't include parsing for the `[dir]` placeholder, so the `[dir]` placeholder was passed through verbatim into the resulting output paths. This release fixes the bug, which means you can now use the `[dir]` placeholder. Sorry about the oversight. ### [`v0.9.4`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;094) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.3...v0.9.4) - Enable hashes in entry point file paths ([#&#8203;518](https://github.com/evanw/esbuild/issues/518)) This release adds the new `--entry-names=` flag. It's similar to the `--chunk-names=` and `--asset-names=` flags except it sets the output paths for entry point files. The pattern defaults to `[dir]/[name]` which should be equivalent to the previous entry point output path behavior, so this should be a backward-compatible change. This change has the following consequences: - It is now possible for entry point output paths to contain a hash. For example, this now happens if you pass `--entry-names=[dir]/[name]-[hash]`. This means you can now use esbuild to generate output files such that all output paths have a hash in them, which means it should now be possible to serve the output files with an infinite cache lifetime so they are only downloaded once and then cached by the browser forever. - It is now possible to prevent the generation of subdirectories inside the output directory. Previously esbuild replicated the directory structure of the input entry points relative to the `outbase` directory (which defaults to the [lowest common ancestor](https://en.wikipedia.org/wiki/Lowest_common_ancestor) directory across all entry points). This value is substituted into the newly-added `[dir]` placeholder. But you can now omit it by omitting that placeholder, like this: `--entry-names=[name]`. - Source map names should now be equal to the corresponding output file name plus an additional `.map` extension. Previously the hashes were content hashes, so the source map had a different hash than the corresponding output file because they had different contents. Now they have the same hash so finding the source map should now be easier (just add `.map`). - Due to the way the new hashing algorithm works, all chunks can now be generated fully in parallel instead of some chunks having to wait until their dependency chunks have been generated first. The import paths for dependency chunks are now swapped in after chunk generation in a second pass (detailed below). This could theoretically result in a speedup although I haven't done any benchmarks around this. Implementing this feature required overhauling how hashes are calculated to prevent the chicken-and-egg hashing problem due to dynamic imports, which can cause cycles in the import graph of the resulting output files when code splitting is enabled. Since generating a hash involved first hashing all of your dependencies, you could end up in a situation where you needed to know the hash to calculate the hash (if a file was a dependency of itself). The hashing algorithm now works in three steps (potentially subject to change in the future): 1. The initial versions of all output files are generated in parallel, with temporary paths used for any imports of other output files. Each temporary path is a randomly-generated string that is unique for each output file. An initial source map is also generated at this step if source maps are enabled. The hash for the first step includes: the raw content of the output file excluding the temporary paths, the relative file paths of all input files present in that output file, the relative output path for the resulting output file (with `[hash]` for the hash that hasn't been computed yet), and contents of the initial source map. 2. After the initial versions of all output files have been generated, calculate the final hash and final output path for each output file. Calculating the final output path involves substituting the final hash for the `[hash]` placeholder in the entry name template. The hash for the second step includes: the hash from the first step for this file and all of its transitive dependencies. 3. After all output files have a final output path, the import paths in each output file for importing other output files are substituted. Source map offsets also have to be adjusted because the final output path is likely a different length than the temporary path used in the first step. This is also done in parallel for each output file. This whole algorithm roughly means the hash of a given output file should change if an only if any input file in that output file or any output file it depends on is changed. So the output path and therefore the browser's cache key should not change for a given output file in between builds if none of the relevant input files were changed. - Fix importing a path containing a `?` character on Windows ([#&#8203;989](https://github.com/evanw/esbuild/issues/989)) On Windows, the `?` character is not allowed in path names. This causes esbuild to fail to import paths containing this character. This is usually fine because people don't put `?` in their file names for this reason. However, the import paths for some ancient CSS code contains the `?` character as a hack to work around a bug in Internet Explorer: ```css @&#8203;font-face { src: url("./icons.eot?#iefix") format('embedded-opentype'), url("./icons.woff2") format('woff2'), url("./icons.woff") format('woff'), url("./icons.ttf") format('truetype'), url("./icons.svg#icons") format('svg'); } ``` The intent is for the bundler to ignore the `?#iefix` part. However, there may actually be a file called `icons.eot?#iefix` on the file system so esbuild checks the file system for both `icons.eot?#iefix` and `icons.eot`. This check was triggering this issue. With this release, an invalid path is considered the same as a missing file so bundling code like this should now work on Windows. - Parse and ignore the deprecated `@-ms-viewport` CSS rule ([#&#8203;997](https://github.com/evanw/esbuild/issues/997)) The [`@viewport`](https://www.w3.org/TR/css-device-adapt-1/#atviewport-rule) rule has been deprecated and removed from the web. Modern browsers now completely ignore this rule. However, in theory it sounds like would still work for mobile versions of Internet Explorer, if those still exist. The https://ant.design/ library contains an instance of the `@-ms-viewport` rule and it currently causes a warning with esbuild, so this release adds support for parsing this rule to disable the warning. - Avoid mutating the binary executable file in place ([#&#8203;963](https://github.com/evanw/esbuild/issues/963)) This release changes the install script for the `esbuild` npm package to use the "rename a temporary file" approach instead of the "write the file directly" approach to replace the `esbuild` command stub file with the real binary executable. This should hopefully work around a problem with the [pnpm](https://pnpm.js.org/) package manager and its use of hard links. - Avoid warning about potential issues with `sideEffects` in packages ([#&#8203;999](https://github.com/evanw/esbuild/issues/999)) Bare imports such as `import "foo"` mean the package is only imported for its side effects. Doing this when the package contains `"sideEffects": false` in `package.json` causes a warning because it means esbuild will not import the file since it has been marked as having no side effects, even though the import statement clearly expects it to have side effects. This is usually caused by an incorrect `sideEffects` annotation in the package. However, this warning is not immediately actionable if the file containing the import statement is itself in a package. So with this release, esbuild will no longer issue this warning if the file containing the import is inside a `node_modules` folder. Note that even though the warning is no longer there, this situation can still result in a broken bundle if the `sideEffects` annotation is incorrect. ### [`v0.9.3`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;093) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.2...v0.9.3) - Fix path resolution with the `exports` field for scoped packages This release fixes a bug where the `exports` field in `package.json` files was not being detected for scoped packages (i.e. packages of the form `@scope/pkg-name` instead of just `pkg-name`). The `exports` field should now be respected for these kinds of packages. - Improved error message in `exports` failure case Node's new [conditional exports feature](https://nodejs.org/docs/latest/api/packages.html#packages_conditional_exports) can be non-intuitive and hard to use. Now that esbuild supports this feature (as of version 0.9.0), you can get into a situation where it's impossible to import a package if the package's `exports` field in its `package.json` file isn't configured correctly. Previously the error message for this looked like this: > entry.js:1:7: error: Could not resolve "jotai" (mark it as external to exclude it from the bundle) 1 │ import 'jotai' ╵ ~~~~~~~ node_modules/jotai/package.json:16:13: note: The path "." is not exported by "jotai" 16 │ "exports": { ╵ ^ With this release, the error message will now provide additional information about why the package cannot be imported: > entry.js:1:7: error: Could not resolve "jotai" (mark it as external to exclude it from the bundle) 1 │ import 'jotai' ╵ ~~~~~~~ node_modules/jotai/package.json:16:13: note: The path "." is not currently exported by package "jotai" 16 │ "exports": { ╵ ^ node_modules/jotai/package.json:18:9: note: None of the conditions provided ("module", "require", "types") match any of the currently active conditions ("browser", "default", "import") 18 │ ".": { ╵ ^ entry.js:1:7: note: Consider using a "require()" call to import this package 1 │ import 'jotai' ╵ ~~~~~~~ In this case, one solution could be import this module using `require()` since this package provides an export for the `require` condition. Another solution could be to pass `--conditions=module` to esbuild since this package provides an export for the `module` condition (the `types` condition is likely not valid JavaScript code). This problem occurs because this package doesn't provide an import path for ESM code using the `import` condition and also doesn't provide a fallback import path using the `default` condition. - Mention glob syntax in entry point error messages ([#&#8203;976](https://github.com/evanw/esbuild/issues/976)) In this release, including a `*` in the entry point path now causes the failure message to tell you that glob syntax must be expanded first before passing the paths to esbuild. People that hit this are usually converting an existing CLI command to a JavaScript API call and don't know that glob expansion is done by their shell instead of by esbuild. An appropriate fix is to use a library such as [`glob`](https://www.npmjs.com/package/glob) to expand the glob pattern first before passing the paths to esbuild. - Raise certain VM versions in the JavaScript feature compatibility table JavaScript VM feature compatibility data is derived from this dataset: https://kangax.github.io/compat-table/. The scripts that process the dataset expand the data to include all VM versions that support a given feature (e.g. `chrome44`, `chrome45`, `chrome46`, ...) so esbuild takes the minimum observed version as the first version for which the feature is supported. However, some features can have subtests that each check a different aspect of the feature. In this case the desired version is the minimum version within each individual subtest, but the maximum of those versions across all subtests (since esbuild should only use the feature if it works in all cases). Previously esbuild computed the minimum version across all subtests, but now esbuild computes the maximum version across all subtests. This means esbuild will now lower JavaScript syntax in more cases. - Mention the configured target environment in error messages ([#&#8203;975](https://github.com/evanw/esbuild/issues/975)) Using newer JavaScript syntax with an older target environment (e.g. `chrome10`) can cause a build error if esbuild doesn't support transforming that syntax such that it is compatible with that target environment. Previously the error message was generic but with this release, the target environment is called outp explicitly in the error message. This is helpful if esbuild is being wrapped by some other tool since the other tool can obscure what target environment is actually being passed to esbuild. - Fix an issue with Unicode and source maps This release fixes a bug where non-ASCII content that ended up in an output file but that was not part of an input file could throw off source mappings. An example of this would be passing a string containing non-ASCII characters to the `globalName` setting with the `minify` setting active and the `charset` setting set to `utf8`. The conditions for this bug are fairly specific and unlikely to be hit, so it's unsurprising that this issue hasn't been discovered earlier. It's also unlikely that this issue affected real-world code. The underlying cause is that while the meaning of column numbers in source maps is undefined in the specification, in practice most tools treat it as the number of UTF-16 code units from the start of the line. The bug happened because column increments for outside-of-file characters were incorrectly counted using byte offsets instead of UTF-16 code unit counts. ### [`v0.9.2`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;092) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.1...v0.9.2) - Fix export name annotations in CommonJS output for node ([#&#8203;960](https://github.com/evanw/esbuild/issues/960)) The previous release introduced a regression that caused a syntax error when building ESM files that have a default export with `--platform=node`. This is because the generated export contained the `default` keyword like this: `0 && (module.exports = {default});`. This regression has been fixed. ### [`v0.9.1`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;091) [Compare Source](https://github.com/evanw/esbuild/compare/v0.9.0...v0.9.1) - Fix bundling when parent directory is inaccessible ([#&#8203;938](https://github.com/evanw/esbuild/issues/938)) Previously bundling with esbuild when a parent directory is inaccessible did not work because esbuild would try to read the directory to search for a `node_modules` folder and would then fail the build when that failed. In practice this caused issues in certain Linux environments where a directory close to the root directory was inaccessible (e.g. on Android). With this release, esbuild will treat inaccessible directories as empty to allow for the `node_modules` search to continue past the inaccessible directory and into its parent directory. This means it should now be possible to bundle with esbuild in these situations. - Avoid allocations in JavaScript API stdout processing ([#&#8203;941](https://github.com/evanw/esbuild/pull/941)) This release improves the efficiency of the JavaScript API. The API runs the binary esbuild executable in a child process and then communicates with it over stdin/stdout. Previously the stdout buffer containing the remaining partial message was copied after each batch of messages due to a bug. This was unintentional and unnecessary, and has been removed. Now this part of the code no longer involves any allocations. This fix was contributed by [@&#8203;jridgewell](https://github.com/jridgewell). - Support conditional `@import` syntax when not bundling ([#&#8203;953](https://github.com/evanw/esbuild/issues/953)) Previously conditional CSS imports such as `@import "print.css" print;` was not supported at all and was considered a syntax error. With this release, it is now supported in all cases except when bundling an internal import. Support for bundling internal CSS imports is planned but will happen in a later release. - Always lower object spread and rest when targeting V8 ([#&#8203;951](https://github.com/evanw/esbuild/issues/951)) This release causes object spread (e.g. `a = {...b}`) and object rest (e.g. `{...a} = b`) to always be lowered to a manual implementation instead of using native syntax when the `--target=` parameter includes a V8-based JavaScript runtime such as `chrome`, `edge`, or `node`. It turns out this feature is implemented inefficiently in V8 and copying properties over to a new object is around a 2x performance improvement. In addition, doing this manually instead of using the native implementation generates a lot less work for the garbage collector. You can see [V8 bug 11536](https://bugs.chromium.org/p/v8/issues/detail?id=11536) for details. If the V8 performance bug is eventually fixed, the translation of this syntax will be disabled again for V8-based targets containing the bug fix. - Fix object rest return value ([#&#8203;956](https://github.com/evanw/esbuild/issues/956)) This release fixes a bug where the value of an object rest assignment was incorrect if the object rest assignment was lowered: ```js // This code was affected let x, y console.log({x, ...y} = {x: 1, y: 2}) ``` Previously this code would incorrectly print `{y: 2}` (the value assigned to `y`) when the object rest expression was lowered (i.e. with `--target=es2017` or below). Now this code will correctly print `{x: 1, y: 2}` instead. This bug did not affect code that did not rely on the return value of the assignment expression, such as this code: ```js // This code was not affected let x, y ({x, ...y} = {x: 1, y: 2}) ``` - Basic support for CSS page margin rules ([#&#8203;955](https://github.com/evanw/esbuild/issues/955)) There are 16 different special at-rules that can be nested inside the `@page` rule. They are defined in [this specification](https://www.w3.org/TR/css-page-3/#syntax-page-selector). Previously esbuild treated these as unknown rules, but with this release esbuild will now treat these as known rules. The only real difference in behavior is that esbuild will no longer warn about these rules being unknown. - Add export name annotations to CommonJS output for node When you import a CommonJS file using an ESM `import` statement in node, the `default` import is the value of `module.exports` in the CommonJS file. In addition, node attempts to generate named exports for properties of the `module.exports` object. Except that node doesn't actually ever look at the properties of that object to determine the export names. Instead it parses the CommonJS file and scans the AST for certain syntax patterns. A full list of supported patterns can be found in the [documentation for the `cjs-module-lexer` package](https://github.com/guybedford/cjs-module-lexer#grammar). This library doesn't currently support the syntax patterns used by esbuild. While esbuild could adapt its syntax to these patterns, the patterns are less compact than the ones used by esbuild and doing this would lead to code bloat. Supporting two separate ways of generating export getters would also complicate esbuild's internal implementation, which is undesirable. Another alternative could be to update the implementation of `cjs-module-lexer` to support the specific patterns used by esbuild. This is also undesirable because this pattern detection would break when minification is enabled, this would tightly couple esbuild's output format with node and prevent esbuild from changing it, and it wouldn't work for existing and previous versions of node that still have the old version of this library. Instead, esbuild will now add additional code to "annotate" ESM files that have been converted to CommonJS when esbuild's platform has been set to `node`. The annotation is dead code but is still detected by the `cjs-module-lexer` library. If the original ESM file has the exports `foo` and `bar`, the additional annotation code will look like this: ```js 0 && (module.exports = {foo, bar}); ``` This allows you to use named imports with an ESM `import` statement in node (previously you could only use the `default` import): ```js import { foo, bar } from './file-built-by-esbuild.cjs' ``` ### [`v0.9.0`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;090) [Compare Source](https://github.com/evanw/esbuild/compare/v0.8.57...v0.9.0) **This release contains backwards-incompatible changes.** Since esbuild is before version 1.0.0, these changes have been released as a new minor version to reflect this (as [recommended by npm](https://docs.npmjs.com/cli/v6/using-npm/semver/)). You should either be pinning the exact version of `esbuild` in your `package.json` file or be using a version range syntax that only accepts patch upgrades such as `^0.8.0`. See the documentation about [semver](https://docs.npmjs.com/cli/v6/using-npm/semver/) for more information. - Add support for node's `exports` field in `package.json` files ([#&#8203;187](https://github.com/evanw/esbuild/issues/187)) This feature was recently added to node. It allows you to rewrite what import paths inside your package map to as well as to prevent people from importing certain files in your package. Adding support for this to esbuild is a breaking change (i.e. code that was working fine before can easily stop working) so adding support for it has been delayed until this breaking change release. One way to use this feature is to remap import paths for your package. For example, this would remap an import of `your-pkg/esm/lib.js` (the "public" import path) to `your-pkg/dist/esm/lib.js` (the "private" file system path): ```json { "name": "your-pkg", "exports": { "./esm/*": "./dist/esm/*", "./cjs/*": "./dist/cjs/*" } } ``` Another way to use this feature is to have conditional imports where the same import path can mean different things in different situations. For example, this would remap `require('your-pkg')` to `your-pkg/required.cjs` and `import 'your-pkg'` to `your-pkg/imported.mjs`: ```json { "name": "your-pkg", "exports": { "import": "./imported.mjs", "require": "./required.cjs" } } ``` There is built-in support for the `import` and `require` conditions depending on the kind of import and the `browser` and `node` conditions depending on the current platform. In addition, the `default` condition always applies regardless of the current configuration settings and can be used as a catch-all fallback condition. Note that when you use conditions, *your package may end up in the bundle multiple times!* This is a subtle issue that can cause bugs due to duplicate copies of your code's state in addition to bloating the resulting bundle. This is commonly known as the [dual package hazard](https://nodejs.org/docs/latest/api/packages.html#packages_dual_package_hazard). The primary way of avoiding this is to put all of your code in the `require` condition and have the `import` condition just be a light wrapper that calls `require` on your package and re-exports the package using ESM syntax. There is also support for custom conditions with the `--conditions=` flag. The meaning of these is entirely up to package authors. For example, you could imagine a package that requires you to configure `--conditions=test,en-US`. Node has currently only endorsed the `development` and `production` custom conditions for recommended use. - Remove the `esbuild.startService()` API Due to [#&#8203;656](https://github.com/evanw/esbuild/issues/656), Calling `service.stop()` no longer does anything, so there is no longer a strong reason for keeping the `esbuild.startService()` API around. The primary thing it currently does is just make the API more complicated and harder to use. You can now just call `esbuild.build()` and `esbuild.transform()` directly instead of calling `esbuild.startService().then(service => service.build())` or `esbuild.startService().then(service => service.transform())`. If you are using esbuild in the browser, you now need to call `esbuild.initialize({ wasmURL })` and wait for the returned promise before calling `esbuild.transform()`. It takes the same options that `esbuild.startService()` used to take. Note that the `esbuild.buildSync()` and `esbuild.transformSync()` APIs still exist when using esbuild in node. Nothing has changed about the synchronous esbuild APIs. - Remove the `metafile` from `outputFiles` ([#&#8203;633](https://github.com/evanw/esbuild/issues/633)) Previously using `metafile` with the API is unnecessarily cumbersome because you have to extract the JSON metadata from the output file yourself instead of it just being provided to you as a return value. This is especially a bummer if you are using `write: false` because then you need to use a for loop over the output files and do string comparisons with the file paths to try to find the one corresponding to the `metafile`. Returning the metadata directly is an important UX improvement for the API. It means you can now do this: ```js const result = await esbuild.build({ entryPoints: ['entry.js'], bundle: true, metafile: true, }) console.log(result.metafile.outputs) ``` - The banner and footer options are now language-specific ([#&#8203;712](https://github.com/evanw/esbuild/issues/712)) The `--banner=` and `--footer=` options now require you to pass the file type: - CLI: esbuild --banner:js=//banner --footer:js=//footer esbuild --banner:css=/*banner*/ --footer:css=/*footer*/ - JavaScript ```js esbuild.build({ banner: { js: '//banner', css: '/*banner*/' }, footer: { js: '//footer', css: '/*footer*/' }, }) ``` - Go ```go api.Build(api.BuildOptions{ Banner: map[string]string{"js": "//banner"}, Footer: map[string]string{"js": "//footer"}, }) api.Build(api.BuildOptions{ Banner: map[string]string{"css": "/*banner*/"}, Footer: map[string]string{"css": "/*footer*/"}, }) ``` This was changed because the feature was originally added in a JavaScript-specific manner, which was an oversight. CSS banners and footers must be separate from JavaScript banners and footers to avoid injecting JavaScript syntax into your CSS files. - The extensions `.mjs` and `.cjs` are no longer implicit Previously the "resolve extensions" setting included `.mjs` and `.cjs` but this is no longer the case. This wasn't a good default because it doesn't match node's behavior and could break some packages. You now have to either explicitly specify these extensions or configure the "resolve extensions" setting yourself. - Remove the `--summary` flag and instead just always print a summary ([#&#8203;704](https://github.com/evanw/esbuild/issues/704)) The summary can be disabled if you don't want it by passing `--log-level=warning` instead. And it can be enabled in the API by setting `logLevel: 'info'`. I'm going to try this because I believe it will improve the UX. People have this problem with esbuild when they first try it where it runs so quickly that they think it must be broken, only to later discover that it actually worked fine. While this is funny, it seems like a good indication that the UX could be improved. So I'm going to try automatically printing a summary to see how that goes. Note that the summary is not printed if incremental builds are active (this includes the watch and serve modes). - Rename `--error-limit=` to `--log-limit=` This parameter has been renamed because it now applies to both warnings and errors, not just to errors. Previously setting the error limit did not apply any limits to the number of warnings printed, which could sometimes result in a deluge of warnings that are problematic for Windows Command Prompt, which is very slow to print to and has very limited scrollback. Now the log limit applies to the total number of log messages including both errors and warnings, so no more than that number of messages will be printed. The log usually prints log messages immediately but it will now intentionally hold back warnings when approaching the limit to make room for possible future errors during a build. So if a build fails you should be guaranteed to see an error message (i.e. warnings can't use up the entire log limit and then prevent errors from being printed). - Remove the deprecated `--avoid-tdz` option This option is now always enabled and cannot be disabled, so it is being removed from the API. The existing API parameter no longer does anything so this removal has no effect the generated output. - Remove `SpinnerBusy` and `SpinnerIdle` from the Go API These options were part of an experiment with the CLI that didn't work out. Watch mode no longer uses a spinner because it turns out people want to be able to interleave esbuild's stderr pipe with other tools and were getting tripped up by the spinner animation. These options no longer do anything and have been removed. ### [`v0.8.57`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0857) [Compare Source](https://github.com/evanw/esbuild/compare/v0.8.56...v0.8.57) - Fix overlapping chunk names when code splitting is active ([#&#8203;928](https://github.com/evanw/esbuild/issues/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: ```js 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: ```js import 'data:text/javascript,console.log("hello!");'; import _ from 'data:application/json,"world!"'; ``` This is for compatibility with node which [supports this feature natively](https://nodejs.org/docs/latest/api/esm.html#esm_data_imports). 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`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0856) [Compare Source](https://github.com/evanw/esbuild/compare/v0.8.55...v0.8.56) - Fix a discrepancy with esbuild's `tsconfig.json` implementation ([#&#8203;913](https://github.com/evanw/esbuild/issues/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 ([#&#8203;914](https://github.com/evanw/esbuild/issues/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: https://github.com/facebook/jest/issues/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`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0855) [Compare Source](https://github.com/evanw/esbuild/compare/v0.8.54...v0.8.55) - Align more closely with node's `default` import behavior for CommonJS ([#&#8203;532](https://github.com/evanw/esbuild/issues/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 ([#&#8203;910](https://github.com/evanw/esbuild/issues/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: ```js import {fn} from './cjs-file.js' console.log(fn()) ``` When you do this, esbuild currently transforms your code into something like this: ```js 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`: ```js 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](https://github.com/microsoft/TypeScript/pull/35877), and also makes esbuild more consistent with Babel which already does this transformation. ### [`v0.8.54`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0854) [Compare Source](https://github.com/evanw/esbuild/compare/v0.8.53...v0.8.54) - Fix ordering issue with private class methods ([#&#8203;901](https://github.com/evanw/esbuild/issues/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: ```js 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`: ```js class Foo { #foo() {} #bar = () => {} test() { assert(this.#foo.name === '#foo') assert(this.#bar.name === '#bar') } } ``` - Fix cross-chunk import paths ([#&#8203;899](https://github.com/evanw/esbuild/issues/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: ```js 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: ```js 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`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0853) [Compare Source](https://github.com/evanw/esbuild/compare/v0.8.52...v0.8.53) - Support chunk and asset file name templates ([#&#8203;733](https://github.com/evanw/esbuild/issues/733), [#&#8203;888](https://github.com/evanw/esbuild/issues/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 ([#&#8203;885](https://github.com/evanw/esbuild/issues/885)) When you use `this` in a static field initializer inside a `class` statement or expression, it references the class object itself: ```js 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 ([#&#8203;893](https://github.com/evanw/esbuild/issues/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](https://developer.mozilla.org/en-US/docs/Web/CSS/@&#8203;namespace) 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`](https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#&#8203;0852) [Compare Source](https://github.com/evanw/esbuild/compare/v0.8.51...v0.8.52) - Fix a concurrent map write with the `--inject:` feature ([#&#8203;878](https://github.com/evanw/esbuild/issues/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 ([#&#8203;879](https://github.com/evanw/esbuild/issues/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. </details> --- ### Configuration 📅 **Schedule**: At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box. --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
renovate added the
dependencies
label 2021-07-25 15:01:58 +00:00
renovate added 1 commit 2021-07-25 15:02:00 +00:00
continuous-integration/drone/pr Build is passing Details
893901b51e
Update dependency esbuild to v0.12.15
konrad merged commit 7bcec2ba31 into main 2021-07-25 20:39:25 +00:00
This repo is archived. You cannot comment on pull requests.
No description provided.