Migration to Fable 2

Mangel MaximeOctober 1, 2018

TL;DR

With this document we are going to convert a Fable 1 project into a Fable 2 project.

This guide has been written by converting Fulma.Minimal template from Fable 1 to Fable 2.

You can see all the changes made by looking at this commit.

Upgrade to Babel 7

During Fable 2 development, Babel 7 has been released so we need to upgrade to it. Helpfully for us, they created a tool to help us.

  1. Install npx, this a CLI program allowing us to run npm packages.

    • Maybe it came with node/npm: npx --help
    • For npm: npm install -g npx
    • For yarn: yarn global add npx
  2. Run npx babel-upgrade --write, this will modify the nearest package.json

Upgrade fable's npm package

  1. Upgrade fable-loader to latest version 2.x.x

    • For npm: npm update fable-loader@latest
    • For yarn: yarn upgrade fable-loader@latest
  2. Remove fable-utils, it is not needed anymore

    • For npm: npm uninstall fable-utils
    • For yarn: yarn remove fable-utils

Update your webpack.config.js

In order to best benefit of new Fable 2 tree-shaking and module size reduction we encourage you to upgrade all your webpack related packages to their latest version.

Here are the key points to consider for updating your webpack.config.js:

1. Remove resolve.modules

Now, fable will create a .fable folder located near your package.json so JavaScript tools works natively.

Remove this lines from your webpack.config.js

resolve: {
    modules: [
        "node_modules/",
        resolve("./node_modules/")
    ]
},
2. Remove any reference to fable-utils
With Fable 1
const fableUtils = require("fable-utils");

// ...

var babelOptions = fableUtils.resolveBabelOptions({
    presets: [
        // ...
With Fable 2




var babelOptions = {
    presets: [
        // ...
3. Update babelOptions

Because you are now using Babel 7, you need to use @babel scope if available.

With Fable 1
var babelOptions = fableUtils.resolveBabelOptions({
    presets: [
        ["env", {
            "targets": {
                "browsers": ["last 2 versions"]
            },
            "modules": false
        }]
    ]
});
babel-polyfill
With Fable 2
var babelOptions = {
    presets: [
        ["@babel/preset-env", {
            "targets": {
                "browsers": ["last 2 versions"]
            },
            "modules": false
        }]
    ]
};
@babel/polyfill
4. Remove unnecessary absolute paths

Because Fable 2, now creates a .fable folder you are not forced anymore to use absolute filepaths. All the filepaths are now relative to the nearest package.json found by Fable.

If you are not sure of your current working directory, you can run Fable.

Fable (2.0.1) daemon started on port 56569
CWD: /Users/maximemangel/Workspaces/Github/MangelMaxime/Fulma/templates/Content

Here the current working directory is /Users/maximemangel/Workspaces/Github/MangelMaxime/Fulma/templates/Content

If you are using latest version of Webpack you still need to provide an absolute path for the output

module.exports = {
    // ...
    output: {
        path: path.join(__dirname, CONFIG.outputDir),
    }
    // ...
};

You can find here a webpack.config.js file to serve as a template for yours.

If needed you can also take a look at the Fulma.Minimal diff here.

Update fable nuget package

Run .paket/paket.exe update to install the latest version of your dependencies. You can check that you are using the correct version of the package by comparing with this list:

At the time of writing here are the version retrieved
PackageVersion
Common to any Fable project
Fable.Core2.0.0
dotnet-fable2.0.1
For projects using Elmish
Fable.React4.0.1
Fable.Elmish2.0.0
Fable.Elmish.React2.0.0
Fable.Elmish.Browser2.0.0
Fable.Elmish.Debugger2.0.0
Fable.Elmish.HMR2.0.0
PackageVersion
General libraries
Fable.PowerPack2.0.1
Fulma1.0.0
Fulma.Extensions1.0.0
Thoth.Json2.1.0

Breaking changes

Fable 2 introduce severals breaking changes, it will inform you by displaying an obsolete warning or by generating a warning in the ouput.

Most important changes are:

  • PojoAttribute is not needed anymore
  • PassGenericsAttribute is also deprecated. If you need to resolve a generic argument, please inline the function. If you only need the Type of a generic argument, you can use the Fable.Core.Inject attribute with ITypeResolver.
  • JSON serialization, you now need to choose between Thoth.Json and Fable.SimpleJson. If you need to support serialization both on Fable and .Net side you need to use Thoth.Json as Fable.SimpleJson is based on a JavaScript library.

If you want to quickly test Fable 2 and not convert all your ofJson/toJson reference yet. You can use the following polyfills:

open Thoth.Json

let inline toJson x = Encode.Auto.toString(0, x)

let inline ofJson<'T> json = Decode.Auto.unsafeFromString<'T>(json)

See Thoth.Json auto decoder documentation for more information about them.