We're live-coding on Twitch! Join us!
Dynamic Imports and F# Pipes Officially Land in Babel 7.5

Dynamic Imports and F# Pipes Officially Land in Babel 7.5

A few days ago, the Babel team release two new feature proposals among all the awesome features we are looking forward to at the next major release. Over time, they have added a lot new things added into the JavaScript compiler, and this time, this proposals looks like something we can all jump on. In this article we’ll be looking at two of these new features and discussing use cases.

F# Pipeline Operator

The pipeline operator proposal is still in its stage 1 of development, which also means its specification is still being defined. Babel has supported the “Smart” variant of the pipeline operator proposal since version 7.3.0, and the "minimal" variant since 7.0.0-beta and this time, we’ve been offered the F# variant as a new proposal.

The F# variant is different from the existing smart variant, how you might ask? Instead of having the concept of "topic references" (#) as we did previously, we’ll be using arrow functions instead. The obvious advantage of this is that it makes it more similar to modern day JavaScript however giving us a slightly less concise syntax. In other words, the pipeline operator is essentially a useful syntactic sugar on a function call with a single argument. This means that:

sqrt(64)

Is now equivalent to

64 |> sqrt

This allows for greater readability when chaining several functions together.

Chaining Functions With The F#\\\*\\\* Variant**

This is precisely where the effects of the pipeline operator is most obvious. Consider you have three functions like this:

function doubleSay (str) {
  return str + ", " + str;
}
function capitalize (str) {
  return str[0].toUpperCase() + str.substring(1);
}
function exclaim (str) {
  return str + '!';
}

Here, we are chaining three functions together and we would like to read the values from all of them with one function call. Here’s a typical invocation of this function

Essential Reading: Learn React from Scratch! (2019 Edition)
let result = exclaim(capitalize(doubleSay("hello")));
console.log(result) //=> "Hello, hello!"

There’s nothing wrong with this approach however it can be made a lot cleaner with the F# variant of the pipeline operator. Consider this equivalence:

let result = "hello"
  |> doubleSay
  |> capitalize
  |> exclaim;
console.log(result) //=> "Hello, hello!"

F#\\\*\\\* Variant On Functions With Multiple Parameters**

The good thing about this particular situation is that the pipeline operator does not need to perform any special kind of operation to handle cases of functions with multiple parameters, we leave it all to JavaScript. Let’s demonstrate with these functions

    function double (x) { return x + x; }
function add (x, y) { return x + y; }

function boundScore (min, max, score) {
  return Math.max(min, Math.min(max, score));
}

With the smart variant implementation, to invoke this functions, we’ll probably do something like this:

let person = { score: 25 };
let newScore = boundScore( 0, 100, add(7, double(person.score)))
console.log(newScore) //=> 57

The new and probably better way that the pipeline operator offers, as opposed to the previous Smart Variant is that since we have access to arrow functions, you can use them to handle multi-argument functions — like add() and boundScore().

let person = { score: 25 };
let newScore = person.score
  |> double
  |> (n => add(7, n))
  |> (n => boundScore(0, 100, n));
console.log(newScore) //=> 57

As much as handling multiple parameter functions is not a problem, it is clearly evident that the pipeline operator works better with the single-argument arrow function syntax. However, this wouldn’t surprise anyone knowing that by default, the pipeline operator always pipes a single result value.

Using The Pipeline Operator With Partial Application Syntax

The partial application syntax proposal (currently at stage 1 proposal) introduces a new syntax using the ? token in an argument list which allows you to partially apply an argument list to a call expression by acting as a placeholder for an argument.

If the partial syntax proposal gets accepted, what it means for the pipeline operator is a better and more accurate way of handling such functions as the one we previously discussed. What this means is that instead of having

let person = { score: 25 };
let newScore = person.score
  |> double
  |> (n => add(7, n))
  |> (n => boundScore(0, 100, n));
console.log(newScore) //=> 57

We could completely get rid of the underscores or any other parameter there and replace it with the ? token.

let person = { score: 25 };
let newScore = person.score
  |> double
  |> add(7, ?)
  |> boundScore(0, 100, ?);
console.log(newScore) //=> 57

This is a lot easier to understand and use. However, let’s not get carried away just yet, they are both still just proposals and there’s no saying what we’ll end up getting, we can only hope that our feedbacks will throw enough weight behind our favourite features.

Using The F#\\\*\\\* Pipeline Operator With Await**

Each proposal has a different solution to await in a pipeline and the F# variant is no exception. It handles Await in a somewhat similar but different manner from the smart pipeline alternative.

// Smart Pipeline
let newScore = fetch(url)
  |> await #
  |> #.json()
  |> await #
  |> #.ID;

Like we know, the F# pipeline introduces arrow functions to handle cases such as this.

// F# pipe line
let newScore = fetch(url)
  |> await
  |> r => r.json()
  |> await
  |> obj => obj.ID;

Getting Started With The F#\\\*\\\* Pipeline Operator**

Like we’ve been saying, this is still just a feature proposal and not yet available in any stable release. However, like every other proposal, you can try it out and leave feedback to the team working on it.

So test this proposal variant immediately and get a first hand experience of all that we’ve been saying, simply modify your babel.config file and add the pipeline operator plugin to it

module.exports = {
  plugins: [
    ["@babel/proposal-pipeline-operator", { proposal: "fsharp" }]
  ]
};

More About This Proposal

To dive deeper into all this and get even more knowledge and resources, feel free to read up the release notes here.

Dynamic Imports

Babel has had support for parsing dynamic imports for a long time now, but there hasn’t been a consistent way to transform it and that is what this new proposal brings to the table.

Prior to this new proposal,

  1. If you use webpack or rollup for dynamic imports you would need to include @babel/plugin-syntax-dynamic-import and not transform it with babel.
  2. If you used N``ode, then you would consequently need to use the babel-plugin-dynamic-import-node plugin to transform it.

The point we are making here is that, for every module, there’s a different way of transforming it. Usually, it is to use a unique transformation plugin for that module. To that effect, the new dynamic imports proposal offers us a way to unify all the above listed use cases under a single entry point which is @babel/plugin-proposal-dynamic-import.

This way, we have a single reusable plugin to transform all modules (Webpack, Roll up, Node etc). This plugin however must be used alongside one of the module transform plugins because Babel needs to know which module loading system you are trying to target.

For instance, if you’re targeting AMD, this will be a valid configuration:

module.exports = {
  plugins: [
    "@babel/plugin-proposal-dynamic-import",
    "@babel/plugin-transform-modules-amd"
  ]
};

Why? Because we specified the target module in this case, AMD. If we didn’t, then there’s no way babel would know if the target module is Node or Rollup just like in the configuration below:

module.exports = {
  plugins: [
    "@babel/plugin-proposal-dynamic-import"
  ]
};

The first config specifies the module transform plugin we’re targeting, which is what babel requires and the latter doesn’t. If you’re only parsing import() expressions without transforming them, you can just include the @babel/plugin-syntax-dynamic-import package.

Dynamic importing is enabled by default when using @babel/preset-env. You also don’t need to worry about webpack or rollup support, both babel-loader and rollup-plugin-babel automatically disable the Babel transform in other to allow the bundler handle it correctly.

Conclusion

Our goal was to introduce these new feature proposals added to the new babel release and show you how to get started with them. i hope that this post gives you enough information to go ahead and test these proposals. Also feel free to leave valuable feedback on your findings for the Babel team.

Like this article? Follow @codebeast on Twitter