Babel plugins: 'loose' mode caveats

Recently I was working on a JavaScript project that had babel setup to transpile ES6+ code into ES5. For some functionality, I had to write a function to get unique items in an array. The source code looks like this.

// Source code
const getUnique = arr => [...new Set(arr)];

Then I execute the function expecting it to work fine. but, it didn’t 😅

/**
 * Expected output: [1,2]
 * Actual output: [Set({1, 2})] 😮
*/
const uniqueArr = getUnique([1,2,1]);

I wasn’t sure what was happening. Why did it produce the wrong result? I double-checked the package versions, build tool config, restarted my build. But, the issue still persisted.

Finally, I decided to check the transpiled code.

// Transpiled code
var getUnique = function getUnique(arr) {
  return [].concat(new Set(arr));
};

There it was! wrongly transpiled code. now I knew it was my babel config.

{
  "presets": {
    [
      "@babel/preset-env",
      {
          "loose": true,          "modules": false,
      }
    ]
  }
}

For some reason, loose mode option was set to true and was the real culprit 🙆‍♂️

What does loose mode mean?

Many babel plugins have two modes:

  • Normal mode: closely follows the ECMAScript 6 standard
  • Loose mode: produces smaller & simple ES5 code, more like the handwritten one

Here is a example of generated code of loose mode disabled(default) vs loose mode enabled.

loose mode might generate potentially faster, smaller and old browser engine compatible ES5 code. but, it might also introduce unintended results when switching from transpiled ES6+ to native ES6+ at later stage as it produces code that is less faithful to ES6+ semantics.

As discussed in this GitHub issue

In loose mode, all iterables are assumed to be arrays.

So, when using loose mode, some of the basic operations like converting a string to an array using spread operators might not work as expected.

// Input code
const charArr = [..."hello"];

/**
 * Transpiled code in loose mode(Produces wrong result)
 *   Expected output: ["h", "e", "l", "l", "o"]
 *   Actual output: ["hello"]
*/
var charArr = [].concat("hello");

Should I use the loose mode?

  • Avoid it in large or shared projects unless you and everybody working on the project are okay with its limitations.
  • If you are a library author, it should probably be okay to use it as you have full control over the source code.

https://twitter.com/_developit/status/1192456661681811458

This was a fun issue I had to deal with recently. Hope you enjoyed reading this blog 😇 If you did, give me a follow @ganapativs 🙌


References:

Thanks: