Skip to content

v0.8.55

Compare
Choose a tag to compare
@github-actions github-actions released this 04 Mar 05:12
  • 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.