Skip to content

fix(deps): update dependency esbuild to ^0.15.0

Renovate requested to merge renovate/esbuild-0.x into main

This MR contains the following updates:

Package Type Update Change
esbuild dependencies minor ^0.14.39 -> ^0.15.0

Dependency Lookup Warnings

Warnings were logged while processing this repo. Please check the Dependency Dashboard for more information.


Release Notes

evanw/esbuild

v0.15.14

Compare Source

  • Fix parsing of TypeScript infer inside a conditional extends (#​2675)

    Unlike JavaScript, parsing TypeScript sometimes requires backtracking. The infer A type operator can take an optional constraint of the form infer A extends B. However, this syntax conflicts with the similar conditional type operator A extends B ? C : D in cases where the syntax is combined, such as infer A extends B ? C : D. This is supposed to be parsed as (infer A) extends B ? C : D. Previously esbuild incorrectly parsed this as (infer A extends B) ? C : D instead, which is a parse error since the ?: conditional operator requires the extends keyword as part of the conditional type. TypeScript disambiguates by speculatively parsing the extends after the infer, but backtracking if a ? token is encountered afterward. With this release, esbuild should now do the same thing, so esbuild should now correctly parse these types. Here's a real-world example of such a type:

    type Normalized<T> = T extends Array<infer A extends object ? infer A : never>
      ? Dictionary<Normalized<A>>
      : {
          [P in keyof T]: T[P] extends Array<infer A extends object ? infer A : never>
            ? Dictionary<Normalized<A>>
            : Normalized<T[P]>
        }
  • Avoid unnecessary watch mode rebuilds when debug logging is enabled (#​2661)

    When debug-level logs are enabled (such as with --log-level=debug), esbuild's path resolution subsystem generates debug log messages that say something like "Read 20 entries for directory /home/user" to help you debug what esbuild's path resolution is doing. This caused esbuild's watch mode subsystem to add a dependency on the full list of entries in that directory since if that changes, the generated log message would also have to be updated. However, meant that on systems where a parent directory undergoes constant directory entry churn, esbuild's watch mode would continue to rebuild if --log-level=debug was passed.

    With this release, these debug log messages are now generated by "peeking" at the file system state while bypassing esbuild's watch mode dependency tracking. So now watch mode doesn't consider the count of directory entries in these debug log messages to be a part of the build that needs to be kept up to date when the file system state changes.

v0.15.13

Compare Source

  • Add support for the TypeScript 4.9 satisfies operator (#​2509)

    TypeScript 4.9 introduces a new operator called satisfies that lets you check that a given value satisfies a less specific type without casting it to that less specific type and without generating any additional code at run-time. It looks like this:

    const value = { foo: 1, bar: false } satisfies Record<string, number | boolean>
    console.log(value.foo.toFixed(1)) // TypeScript knows that "foo" is a number here

    Before this existed, you could use a cast with as to check that a value satisfies a less specific type, but that removes any additional knowledge that TypeScript has about that specific value:

    const value = { foo: 1, bar: false } as Record<string, number | boolean>
    console.log(value.foo.toFixed(1)) // TypeScript no longer knows that "foo" is a number

    You can read more about this feature in TypeScript's blog post for 4.9 as well as the associated TypeScript issue for this feature.

    This feature was implemented in esbuild by @​magic-akari.

  • Fix watch mode constantly rebuilding if the parent directory is inaccessible (#​2640)

    Android is unusual in that it has an inaccessible directory in the path to the root, which esbuild was not originally built to handle. To handle cases like this, the path resolution layer in esbuild has a hack where it treats inaccessible directories as empty. However, esbuild's watch implementation currently triggers a rebuild if a directory previously encountered an error but the directory now exists. The assumption is that the previous error was caused by the directory not existing. Although that's usually the case, it's not the case for this particular parent directory on Android. Instead the error is that the directory previously existed but was inaccessible.

    This discrepancy between esbuild's path resolution layer and its watch mode was causing watch mode to rebuild continuously on Android. With this release, esbuild's watch mode instead checks for an error status change in the readdir file system call, so watch mode should no longer rebuild continuously on Android.

  • Apply a fix for a rare deadlock with the JavaScript API (#​1842, #​2485)

    There have been reports of esbuild sometimes exiting with an "all goroutines are asleep" deadlock message from the Go language runtime. This issue hasn't made much progress until recently, where a possible cause was discovered (thanks to @​jfirebaugh for the investigation). This release contains a possible fix for that possible cause, so this deadlock may have been fixed. The fix cannot be easily verified because the deadlock is non-deterministic and rare. If this was indeed the cause, then this issue only affected the JavaScript API in situations where esbuild was already in the process of exiting.

    In detail: The underlying cause is that Go's sync.WaitGroup API for waiting for a set of goroutines to finish is not fully thread-safe. Specifically it's not safe to call Add() concurrently with Wait() when the wait group counter is zero due to a data race. This situation could come up with esbuild's JavaScript API when the host JavaScript process closes the child process's stdin and the child process (with no active tasks) calls Wait() to check that there are no active tasks, at the same time as esbuild's watchdog timer calls Add() to add an active task (that pings the host to see if it's still there). The fix in this release is to avoid calling Add() once we learn that stdin has been closed but before we call Wait().

v0.15.12

Compare Source

  • Fix minifier correctness bug with single-use substitutions (#​2619)

    When minification is enabled, esbuild will attempt to eliminate variables that are only used once in certain cases. For example, esbuild minifies this code:

    function getEmailForUser(name) {
      let users = db.table('users');
      let user = users.find({ name });
      let email = user?.get('email');
      return email;
    }

    into this code:

    function getEmailForUser(e){return db.table("users").find({name:e})?.get("email")}

    However, this transformation had a bug where esbuild did not correctly consider the "read" part of binary read-modify-write assignment operators. For example, it's incorrect to minify the following code into bar += fn() because the call to fn() might modify bar:

    const foo = fn();
    bar += foo;

    In addition to fixing this correctness bug, this release also improves esbuild's output in the case where all values being skipped over are primitives:

    function toneMapLuminance(r, g, b) {
      let hdr = luminance(r, g, b)
      let decay = 1 / (1 + hdr)
      return 1 - decay
    }

    Previous releases of esbuild didn't substitute these single-use variables here, but esbuild will now minify this to the following code starting with this release:

    function toneMapLuminance(e,n,a){return 1-1/(1+luminance(e,n,a))}

v0.15.11

Compare Source

  • Fix various edge cases regarding template tags and this (#​2610)

    This release fixes some bugs where the value of this wasn't correctly preserved when evaluating template tags in a few edge cases. These edge cases are listed below:

    async function test() {
      class Foo { foo() { return this } }
      class Bar extends Foo {
        a = async () => super.foo``
        b = async () => super['foo']``
        c = async (foo) => super[foo]``
      }
      function foo() { return this }
      const obj = { foo }
      const bar = new Bar
      console.log(
        (await bar.a()) === bar,
        (await bar.b()) === bar,
        (await bar.c('foo')) === bar,
        { foo }.foo``.foo === foo,
        (true && obj.foo)`` !== obj,
        (false || obj.foo)`` !== obj,
        (null ?? obj.foo)`` !== obj,
      )
    }
    test()

    Each edge case in the code above previously incorrectly printed false when run through esbuild with --minify --target=es6 but now correctly prints true. These edge cases are unlikely to have affected real-world code.

v0.15.10

Compare Source

  • Add support for node's "pattern trailers" syntax (#​2569)

    After esbuild implemented node's exports feature in package.json, node changed the feature to also allow text after * wildcards in patterns. Previously the * was required to be at the end of the pattern. It lets you do something like this:

    {
      "exports": {
        "./features/*": "./features/*.js",
        "./features/*.js": "./features/*.js"
      }
    }

    With this release, esbuild now supports these types of patterns too.

  • Fix subpath imports with Yarn PnP (#​2545)

    Node has a little-used feature called subpath imports which are package-internal imports that start with # and that go through the imports map in package.json. Previously esbuild had a bug that caused esbuild to not handle these correctly in packages installed via Yarn's "Plug'n'Play" installation strategy. The problem was that subpath imports were being checked after Yarn PnP instead of before. This release reorders these checks, which should allow subpath imports to work in this case.

  • Link from JS to CSS in the metafile (#​1861, #​2565)

    When you import CSS into a bundled JS file, esbuild creates a parallel CSS bundle next to your JS bundle. So if app.ts imports some CSS files and you bundle it, esbuild will give you app.js and app.css. You would then add both <script src="app.js"></script> and <link href="app.css" rel="stylesheet"> to your HTML to include everything in the page. This approach is more efficient than having esbuild insert additional JavaScript into app.js that downloads and includes app.css because it means the browser can download and parse both the CSS and the JS in parallel (and potentially apply the CSS before the JS has even finished downloading).

    However, sometimes it's difficult to generate the <link> tag. One case is when you've added [hash] to the entry names setting to include a content hash in the file name. Then the file name will look something like app-GX7G2SBE.css and may change across subsequent builds. You can tell esbuild to generate build metadata using the metafile API option but the metadata only tells you which generated JS bundle corresponds to a JS entry point (via the entryPoint property), not which file corresponds to the associated CSS bundle. Working around this was hacky and involved string manipulation.

    This release adds the cssBundle property to the metafile to make this easier. It's present on the metadata for the generated JS bundle and points to the associated CSS bundle. So to generate the HTML tags for a given JS entry point, you first find the output file with the entryPoint you are looking for (and put that in a <script> tag), then check for the cssBundle property to find the associated CSS bundle (and put that in a <link> tag).

    One thing to note is that there is deliberately no jsBundle property mapping the other way because it's not a 1:1 relationship. Two JS bundles can share the same CSS bundle in the case where the associated CSS bundles have the same name and content. In that case there would be no one value for a hypothetical jsBundle property to have.

v0.15.9

Compare Source

  • Fix an obscure npm package installation issue with --omit=optional (#​2558)

    The previous release introduced a regression with npm install esbuild --omit=optional where the file node_modules/.bin/esbuild would no longer be present after installation. That could cause any package scripts which used the esbuild command to no longer work. This release fixes the regression so node_modules/.bin/esbuild should now be present again after installation. This regression only affected people installing esbuild using npm with either the --omit=optional or --no-optional flag, which is a somewhat unusual situation.

    More details:

    The reason for this regression is due to some obscure npm implementation details. Since the Go compiler doesn't support trivial cross-compiling on certain Android platforms, esbuild's installer installs a WebAssembly shim on those platforms instead. In the previous release I attempted to simplify esbuild's WebAssembly shims to depend on the esbuild-wasm package instead of including another whole copy of the WebAssembly binary (to make publishing faster and to save on file system space after installation). However, both the esbuild package and the esbuild-wasm package provide a binary called esbuild and it turns out that adding esbuild-wasm as a nested dependency of the esbuild package (specifically esbuild optionally depends on @esbuild/android-arm which depends on esbuild-wasm) caused npm to be confused about what node_modules/.bin/esbuild is supposed to be.

    It's pretty strange and unexpected that disabling the installation of optional dependencies altogether would suddenly cause an optional dependency's dependency to conflict with the top-level package. What happens under the hood is that if --omit=optional is present, npm attempts to uninstall the esbuild-wasm nested dependency at the end of npm install (even though the esbuild-wasm package was never installed due to --omit=optional). This uninstallation causes node_modules/.bin/esbuild to be deleted.

    After doing a full investigation, I discovered that npm's handling of the .bin directory is deliberately very brittle. When multiple packages in the dependency tree put something in .bin with the same name, the end result is non-deterministic/random. What you get in .bin might be from one package, from the other package, or might be missing entirely. The workaround suggested by npm is to just avoid having two packages that put something in .bin with the same name. So this was fixed by making the @esbuild/android-arm and esbuild-android-64 packages each include another whole copy of the WebAssembly binary, which works because these packages don't put anything in .bin.

v0.15.8

Compare Source

  • Fix JSX name collision edge case (#​2534)

    Code generated by esbuild could have a name collision in the following edge case:

    • The JSX transformation mode is set to automatic, which causes import statements to be inserted
    • An element uses a {...spread} followed by a key={...}, which uses the legacy createElement fallback imported from react
    • Another import uses a name that ends with react such as @remix-run/react
    • The output format has been set to CommonJS so that import statements are converted into require calls

    In this case, esbuild previously generated two variables with the same name import_react, like this:

    var import_react = require("react");
    var import_react2 = require("@&#8203;remix-run/react");

    That bug is fixed in this release. The code generated by esbuild no longer contains a name collision.

  • Fall back to WebAssembly on Android ARM (#​1556, #​1578, #​2335, #​2526)

    Go's compiler supports trivial cross-compiling to almost all platforms without installing any additional software other than the Go compiler itself. This has made it very easy for esbuild to publish native binary executables for many platforms. However, it strangely doesn't support cross-compiling to Android ARM without installing the Android build tools.

    So instead of publishing a native esbuild binary executable to npm, this release publishes a WebAssembly fallback build. This is essentially the same as the esbuild-wasm package but it's installed automatically when you install the esbuild package on Android ARM. So packages that depend on the esbuild package should now work on Android ARM. This change has not yet been tested end-to-end because I don't have a 32-bit Android ARM device myself, but in theory it should work.

    This inherits the drawbacks of WebAssembly including significantly slower performance than native as well as potentially also more severe memory usage limitations and lack of certain features (e.g. --serve). If you want to use a native binary executable of esbuild on Android ARM, you may be able to build it yourself from source after installing the Android build tools.

  • Attempt to better support Yarn's ignorePatternData feature (#​2495)

    Part of resolving paths in a project using Yarn's Plug'n'Play feature involves evaluating a regular expression in the ignorePatternData property of .pnp.data.json. However, it turns out that the particular regular expressions generated by Yarn use some syntax that works with JavaScript regular expressions but that does not work with Go regular expressions.

    In this release, esbuild will now strip some of the the problematic syntax from the regular expression before compiling it, which should hopefully allow it to be compiled by Go's regular expression engine. The specific character sequences that esbuild currently strips are as follows:

    • (?!\.)
    • (?!(?:^|\/)\.)
    • (?!\.{1,2}(?:\/|$))
    • (?!(?:^|\/)\.{1,2}(?:\/|$))

    These seem to be used by Yarn to avoid the . and .. path segments in the middle of relative paths. The removal of these character sequences seems relatively harmless in this case since esbuild shouldn't ever generate such path segments. This change should add support to esbuild for Yarn's pnpIgnorePatterns feature.

  • Fix non-determinism issue with legacy block-level function declarations and strict mode (#​2537)

    When function declaration statements are nested inside a block in strict mode, they are supposed to only be available within that block's scope. But in "sloppy mode" (which is what non-strict mode is commonly called), they are supposed to be available within the whole function's scope:

    // This returns 1 due to strict mode
    function test1() {
      'use strict'
      function fn() { return 1 }
      if (true) { function fn() { return 2 } }
      return fn()
    }
    
    // This returns 2 due to sloppy mode
    function test2() {
      function fn() { return 1 }
      if (true) { function fn() { return 2 } }
      return fn()
    }

    To implement this, esbuild compiles these two functions differently to reflect their different semantics:

    function test1() {
      "use strict";
      function fn() {
        return 1;
      }
      if (true) {
        let fn2 = function() {
          return 2;
        };
      }
      return fn();
    }
    function test2() {
      function fn() {
        return 1;
      }
      if (true) {
        let fn2 = function() {
          return 2;
        };
        var fn = fn2;
      }
      return fn();
    }

    However, the compilation had a subtle bug where the automatically-generated function-level symbols for multible hoisted block-level function declarations in the same block a sloppy-mode context were generated in a random order if the output was in strict mode, which could be the case if TypeScript's alwaysStrict setting was set to true. This lead to non-determinism in the output as the minifier would randomly exchange the generated names for these symbols on different runs. This bug has been fixed by sorting the keys of the unordered map before iterating over them.

  • Fix parsing of @keyframes with string identifiers (#​2555)

    Firefox supports @keyframes with string identifier names. Previously this was treated as a syntax error by esbuild as it doesn't work in any other browser. The specification allows for this however, so it's technically not a syntax error (even though it would be unwise to use this feature at the moment). There was also a bug where esbuild would remove the identifier name in this case as the syntax wasn't recognized.

    This release changes esbuild's parsing of @keyframes to now consider this case to be an unrecognized CSS rule. That means it will be passed through unmodified (so you can now use esbuild to bundle this Firefox-specific CSS) but the CSS will not be pretty-printed or minified. I don't think it makes sense for esbuild to have special code to handle this Firefox-specific syntax at this time. This decision can be revisited in the future if other browsers add support for this feature.

  • Add the --jsx-side-effects API option (#​2539, #​2546)

    By default esbuild assumes that JSX expressions are side-effect free, which means they are annoated with /* @&#8203;__PURE__ */ comments and are removed during bundling when they are unused. This follows the common use of JSX for virtual DOM and applies to the vast majority of JSX libraries. However, some people have written JSX libraries that don't have this property. JSX expressions can have arbitrary side effects and can't be removed. If you are using such a library, you can now pass --jsx-side-effects to tell esbuild that JSX expressions have side effects so it won't remove them when they are unused.

    This feature was contributed by @​rtsao.

v0.15.7

Compare Source

  • Add --watch=forever to allow esbuild to never terminate (#​1511, #​1885)

    Currently using esbuild's watch mode via --watch from the CLI will stop watching if stdin is closed. The rationale is that stdin is automatically closed by the OS when the parent process exits, so stopping watch mode when stdin is closed ensures that esbuild's watch mode doesn't keep running forever after the parent process has been closed. For example, it would be bad if you wrote a shell script that did esbuild --watch & to run esbuild's watch mode in the background, and every time you run the script it creates a new esbuild process that runs forever.

    However, there are cases when it makes sense for esbuild's watch mode to never exit. One such case is within a short-lived VM where the lifetime of all processes inside the VM is expected to be the lifetime of the VM. Previously you could easily do this by piping the output of a long-lived command into esbuild's stdin such as sleep 999999999 | esbuild --watch &. However, this possibility often doesn't occur to people, and it also doesn't work on Windows. People also sometimes attempt to keep esbuild open by piping an infinite stream of data to esbuild such as with esbuild --watch </dev/zero & which causes esbuild to spin at 100% CPU. So with this release, esbuild now has a --watch=forever flag that will not stop watch mode when stdin is closed.

  • Work around PATH without node in install script (#​2519)

    Some people install esbuild's npm package in an environment without the node command in their PATH. This fails on Windows because esbuild's install script runs the esbuild command before exiting as a sanity check, and on Windows the esbuild command has to be a JavaScript file because of some internal details about how npm handles the bin folder (specifically the esbuild command lacks the .exe extension, which is required on Windows). This release attempts to work around this problem by using process.execPath instead of "node" as the command for running node. In theory this means the installer can now still function on Windows if something is wrong with PATH.

v0.15.6

Compare Source

  • Lower for await loops (#​1930)

    This release lowers for await loops to the equivalent for loop containing await when esbuild is configured such that for await loops are unsupported. This transform still requires at least generator functions to be supported since esbuild's lowering of await currently relies on generators. This new transformation is mostly modeled after what the TypeScript compiler does. Here's an example:

    async function f() {
      for await (let x of y)
        x()
    }

    The code above will now become the following code with --target=es2017 (omitting the code for the __forAwait helper function):

    async function f() {
      try {
        for (var iter = __forAwait(y), more, temp, error; more = !(temp = await iter.next()).done; more = false) {
          let x = temp.value;
          x();
        }
      } catch (temp) {
        error = [temp];
      } finally {
        try {
          more && (temp = iter.return) && await temp.call(iter);
        } finally {
          if (error)
            throw error[0];
        }
      }
    }
  • Automatically fix invalid supported configurations (#​2497)

    The --target= setting lets you tell esbuild to target a specific version of one or more JavaScript runtimes such as chrome80,node14 and esbuild will restrict its output to only those features supported by all targeted JavaScript runtimes. More recently, esbuild introduced the --supported: setting that lets you override which features are supported on a per-feature basis. However, this now lets you configure nonsensical things such as --supported:async-await=false --supported:async-generator=true. Previously doing this could result in esbuild building successfully but producing invalid output.

    Starting with this release, esbuild will now attempt to automatically fix nonsensical feature override configurations by introducing more overrides until the configuration makes sense. So now the configuration from previous example will be changed such that async-await=false implies async-generator=false. The full list of implications that were introduced is below:

    • async-await=false implies:

      • async-generator=false
      • for-await=false
      • top-level-await=false
    • generator=false implies:

      • async-generator=false
    • object-accessors=false implies:

      • class-private-accessor=false
      • class-private-static-accessor=false
    • class-field=false implies:

      • class-private-field=false
    • class-static-field=false implies:

      • class-private-static-field=false
    • class=false implies:

      • class-field=false
      • class-private-accessor=false
      • class-private-brand-check=false
      • class-private-field=false
      • class-private-method=false
      • class-private-static-accessor=false
      • class-private-static-field=false
      • class-private-static-method=false
      • class-static-blocks=false
      • class-static-field=false
  • Implement a small minification improvement (#​2496)

    Some people write code that contains a label with an immediate break such as x: break x. Previously this code was not removed during minification but it will now be removed during minification starting with this release.

  • Fix installing esbuild via Yarn with enableScripts: false configured (#​2457)

    If esbuild is installed with Yarn with the enableScripts: false setting configured, then Yarn will not "unplug" the esbuild package (i.e. it will keep the entire package inside a .zip file). This messes with esbuild's library code that extracts the platform-specific binary executable because that code copies the binary executable into the esbuild package directory, and Yarn's .zip file system shim doesn't let you write to a directory inside of a .zip file. This release fixes this problem by writing to the node_modules/.cache/esbuild directory instead in this case. So you should now be able to use esbuild with Yarn when enableScripts: false is configured.

    This fix was contributed by @​jonaskuske.

v0.15.5

Compare Source

  • Fix issues with Yarn PnP and Yarn's workspaces feature (#​2476)

    This release makes sure esbuild works with a Yarn feature called workspaces. Previously esbuild wasn't tested in this scenario, but this scenario now has test coverage. Getting this to work involved further tweaks to esbuild's custom code for what happens after Yarn PnP's path resolution algorithm runs, which is not currently covered by Yarn's PnP specification. These tweaks also fix exports map resolution with Yarn PnP for non-empty subpaths, which wasn't previously working.

v0.15.4

Compare Source

  • Consider TypeScript import assignments to be side-effect free (#​2468)

    TypeScript has a legacy import syntax for working with TypeScript namespaces that looks like this:

    import { someNamespace } from './some-file'
    import bar = someNamespace.foo;
    
    // some-file.ts
    export namespace someNamespace {
      export let foo = 123
    }

    Since esbuild converts TypeScript into JavaScript one file at a time, it doesn't know if bar is supposed to be a value or a type (or both, which TypeScript actually allows in this case). This is problematic because values are supposed to be kept during the conversion but types are supposed to be removed during the conversion. Currently esbuild keeps bar in the output, which is done because someNamespace.foo is a property access and property accesses run code that could potentially have a side effect (although there is no side effect in this case).

    With this release, esbuild will now consider someNamespace.foo to have no side effects. This means bar will now be removed when bundling and when tree shaking is enabled. Note that it will still not be removed when tree shaking is disabled. This is because in this mode, esbuild supports adding additional code to the end of the generated output that's in the same scope as the module. That code could potentially make use of bar, so it would be incorrect to remove it. If you want bar to be removed, you'll have to enable tree shaking (which tells esbuild that nothing else depends on the unexported top-level symbols in the generated output).

  • Change the order of the banner and the "use strict" directive (#​2467)

    Previously the top of the file contained the following things in order:

    1. The hashbang comment (see below) from the source code, if present
    2. The "use strict" directive from the source code, if present
    3. The content of esbuild's banner API option, if specified

    This was problematic for people that used the banner API option to insert the hashbang comment instead of using esbuild's hashbang comment preservation feature. So with this release, the order has now been changed to:

    1. The hashbang comment (see below) from the source code, if present
    2. The content of esbuild's banner API option, if specified
    3. The "use strict" directive from the source code, if present

    I'm considering this change to be a bug fix instead of a breaking change because esbuild's documentation states that the banner API option can be used to "insert an arbitrary string at the beginning of generated JavaScript files". While this isn't technically true because esbuild may still insert the original hashbang comment before the banner, it's at least more correct now because the banner will now come before the "use strict" directive.

    For context: JavaScript files recently allowed using a hashbang comment, which starts with #! and which must start at the very first character of the file. It allows Unix systems to execute the file directly as a script without needing to prefix it by the node command. This comment typically has the value #!/usr/bin/env node. Hashbang comments will be a part of ES2023 when it's released next year.

  • Fix exports maps with Yarn PnP path resolution (#​2473)

    The Yarn PnP specification says that to resolve a package path, you first resolve it to the absolute path of a directory, and then you run node's module resolution algorithm on it. Previously esbuild followed this part of the specification. However, doing this means that exports in package.json is not respected because node's module resolution algorithm doesn't interpret exports for absolute paths. So with this release, esbuild will now use a modified algorithm that deviates from both specifications but that should hopefully behave more similar to what Yarn actually does: node's module resolution algorithm is run with the original import path but starting from the directory returned by Yarn PnP.

v0.15.3

Compare Source

  • Change the Yarn PnP manifest to a singleton (#​2463)

    Previously esbuild searched for the Yarn PnP manifest in the parent directories of each file. But with Yarn's enableGlobalCache setting it's possible to configure Yarn PnP's implementation to reach outside of the directory subtree containing the Yarn PnP manifest. This was causing esbuild to fail to bundle projects with the enableGlobalCache setting enabled.

    To handle this case, esbuild will now only search for the Yarn PnP manifest in the current working directory of the esbuild process. If you're using esbuild's CLI, this means you will now have to cd into the appropriate directory first. If you're using esbuild's API, you can override esbuild's value for the current working directory with the absWorkingDir API option.

  • Fix Yarn PnP resolution failures due to backslashes in paths on Windows (#​2462)

    Previously dependencies of a Yarn PnP virtual dependency failed to resolve on Windows. This was because Windows uses \ instead of / as a path separator, and the path manipulation algorithms used for Yarn PnP expected /. This release converts \ into / in Windows paths, which fixes this issue.

  • Fix sideEffects patterns containing slashes on Windows (#​2465)

    The sideEffects field in package.json lets you specify an array of patterns to mark which files have side effects (which causes all other files to be considered to not have side effects by exclusion). That looks like this:

    "sideEffects": [
      "**/index.js",
      "**/index.prod.js"
    ]

    However, the presence of the / character in the pattern meant that the pattern failed to match Windows-style paths, which broke sideEffects on Windows in this case. This release fixes this problem by adding additional code to handle Windows-style paths.

v0.15.2

Compare Source

  • Fix Yarn PnP issue with packages containing index.js (#​2455, #​2461)

    Yarn PnP's tests require the resolved paths to end in /. That's not how the rest of esbuild's internals work, however, and doing this messed up esbuild's node module path resolution regarding automatically-detected index.js files. Previously packages that relied on implicit index.js resolution rules didn't work with esbuild under Yarn PnP. Removing this slash has fixed esbuild's path resolution behavior regarding index.js, which should now the same both with and without Yarn PnP.

  • Fix Yarn PnP support for extends in tsconfig.json (#​2456)

    Previously using extends in tsconfig.json with a path in a Yarn PnP package didn't work. This is because the process of setting up package path resolution rules requires parsing tsconfig.json files (due to the baseUrl and paths features) and resolving extends to a package path requires package path resolution rules to already be set up, which is a circular dependency. This cycle is broken by using special rules for extends in tsconfig.json that bypasses esbuild's normal package path resolution process. This is why using extends with a Yarn PnP package didn't automatically work. With this release, these special rules have been modified to check for a Yarn PnP manifest so this case should work now.

  • Fix Yarn PnP support in esbuild-wasm (#​2458)

    When running esbuild via WebAssembly, Yarn PnP support previously failed because Go's file system internals return EINVAL when trying to read a .zip file as a directory when run with WebAssembly. This was unexpected because Go's file system internals return ENOTDIR for this case on native. This release updates esbuild to treat EINVAL like ENOTDIR in this case, which fixes using esbuild-wasm to bundle a Yarn PnP project.

    Note that to be able to use esbuild-wasm for Yarn PnP successfully, you currently have to run it using node instead of yarn node. This is because the file system shim that Yarn overwrites node's native file system API with currently generates invalid file descriptors with negative values when inside a .zip file. This prevents esbuild from working correctly because Go's file system internals don't expect syscalls that succeed without an error to return an invalid file descriptor. Yarn is working on fixing their use of invalid file descriptors.

v0.15.1

Compare Source

  • Fix parsing of TypeScript infer inside a conditional extends (#​2675)

    Unlike JavaScript, parsing TypeScript sometimes requires backtracking. The infer A type operator can take an optional constraint of the form infer A extends B. However, this syntax conflicts with the similar conditional type operator A extends B ? C : D in cases where the syntax is combined, such as infer A extends B ? C : D. This is supposed to be parsed as (infer A) extends B ? C : D. Previously esbuild incorrectly parsed this as (infer A extends B) ? C : D instead, which is a parse error since the ?: conditional operator requires the extends keyword as part of the conditional type. TypeScript disambiguates by speculatively parsing the extends after the infer, but backtracking if a ? token is encountered afterward. With this release, esbuild should now do the same thing, so esbuild should now correctly parse these types. Here's a real-world example of such a type:

    type Normalized<T> = T extends Array<infer A extends object ? infer A : never>
      ? Dictionary<Normalized<A>>
      : {
          [P in keyof T]: T[P] extends Array<infer A extends object ? infer A : never>
            ? Dictionary<Normalized<A>>
            : Normalized<T[P]>
        }
  • Avoid unnecessary watch mode rebuilds when debug logging is enabled (#​2661)

    When debug-level logs are enabled (such as with --log-level=debug), esbuild's path resolution subsystem generates debug log messages that say something like "Read 20 entries for directory /home/user" to help you debug what esbuild's path resolution is doing. This caused esbuild's watch mode subsystem to add a dependency on the full list of entries in that directory since if that changes, the generated log message would also have to be updated. However, meant that on systems where a parent directory undergoes constant directory entry churn, esbuild's watch mode would continue to rebuild if --log-level=debug was passed.

    With this release, these debug log messages are now generated by "peeking" at the file system state while bypassing esbuild's watch mode dependency tracking. So now watch mode doesn't consider the count of directory entries in these debug log messages to be a part of the build that needs to be kept up to date when the file system state changes.

v0.15.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.14.0. See the documentation about semver for more information.

  • Implement the Yarn Plug'n'Play module resolution algorithm (#​154, #​237, #​1263, #​2451)

    Node comes with a package manager called npm, which installs packages into a node_modules folder. Node and esbuild both come with built-in rules for resolving import paths to packages within node_modules, so packages installed via npm work automatically without any configuration. However, many people use an alternative package manager called Yarn. While Yarn can install packages using node_modules, it also offers a different package installation strategy called Plug'n'Play, which is often shortened to "PnP" (not to be confused with pnpm, which is an entirely different unrelated package manager).

    Plug'n'Play installs packages as .zip files on your file system. The packages are never actually unzipped. Since Node doesn't know anything about Yarn's package installation strategy, this means you can no longer run your code with Node as it won't be able to find your packages. Instead, you need to run your code with Yarn, which applies patches to Node's file system APIs before running your code. These patches attempt to make zip files seem like normal directories. When running under Yarn, using Node's file system API to read ./some.zip/lib/file.js actually automatically extracts lib/file.js from ./some.zip at run-time as if it was a normal file. Other file system APIs behave similarly. However, these patches don't work with esbuild because esbuild is not written in JavaScript; it's a native binary executable that interacts with the file system directly through the operating system.

    Previously the workaround for using esbuild with Plug'n'Play was to use the @yarnpkg/esbuild-plugin-pnp plugin with esbuild's JavaScript API. However, this wasn't great because the plugin needed to potentially intercept every single import path and file load to check whether it was a Plug'n'Play package, which has an unusually high performance cost. It also meant that certain subtleties of path resolution rules within a .zip file could differ slightly from the way esbuild normally works since path resolution inside .zip files was implemented by Yarn, not by esbuild (which is due to a limitation of esbuild's plugin API).

    With this release, esbuild now contains an independent implementation of Yarn's Plug'n'Play algorithm (which is used when esbuild finds a .pnp.js, .pnp.cjs, or .pnp.data.json file in the directory tree). Creating additional implementations of this algorithm recently became possible because Yarn's package manifest format was recently documented: https://yarnpkg.com/advanced/pnp-spec/. This should mean that you can now use esbuild to bundle Plug'n'Play projects without any additional configuration (so you shouldn't need @yarnpkg/esbuild-plugin-pnp anymore). Bundling these projects should now happen much faster as Yarn no longer even needs to be run at all. Bundling the Yarn codebase itself with esbuild before and after this change seems to demonstrate over a 10x speedup (3.4s to 0.24s). And path resolution rules within Yarn packages should now be consistent with how esbuild handles regular Node packages. For example, fields such as module and browser in package.json files within .zip files should now be respected.

    Keep in mind that this is brand new code and there may be some initial issues to work through before esbuild's implementation is solid. Yarn's Plug'n'Play specification is also brand new and may need some follow-up edits to guide new implementations to match Yarn's exact behavior. If you try this out, make sure to test it before committing to using it, and let me know if anything isn't working as expected. Should you need to debug esbuild's path resolution, you may find --log-level=verbose helpful.


Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

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

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

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


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

This MR has been generated by Renovate Bot.

Merge request reports