Getting started with Fable and Webpack

Getting started with Fable and Webpack

Introduction

Fable is new F# to JavaScript compiler created by Alfonso Garcia-Caro. In this post I’ll go, step by step, through process of creating client-side (browser) applications using it.

This post is targeted at F# developers without lot of knowladge about Node.js and JS ecosystem and it should let any F# developer to get started with Fable.

Requirements

Fable requires having both F# 4 and node 4.4 or bigger installed in your computer.

Setting up project

The first thing we need to do is creating new directory and initializing node project:

mkdir fable-test
cd fable-test
npm init

Npm is is the package manager for JavaScript. It lets users to find, share, and reuse packages of code or install additional tools

npm init will create package.json file. This file is project file for any Node.js based project (both client and server side). At start it will contain only basic information about a project, something like that:

{
  "name": "fable-test",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "test": "..."
  },
  "author": "",
  "license": "MIT",
}

Installing development tools.

Next step is installing all development dependencies (tools) which we’ll be using for our project. To do so we are using npm install command:

npm install --save-dev fable-compiler
npm install --save-dev webpack
npm install --save-dev source-map-loader

First line installs Fable compiler for our project. Second command installs Webpack. Webpack is a module bundler - it takes modules with dependencies and generates static assets representing those modules. In our case it would take generated by Fable files together with other JS libraries we will depend upon and create single file output which can be easily added to page. Last dependency is plugin to Webpack which will enable nice debugging story for our application (debugging F# files in the browser)

It’s also worth noticing that those commands modified package.json file, now it contains also following block:

"devDependencies": {
  "fable-compiler": "^0.2.12",
  "source-map-loader": "^0.1.5",
  "webpack": "^1.13.0"
},

Installing dependencies

Now we will install our code dependencies using also npm install.

npm install --save core-js
npm install --save fable-core

core-js is polyfill ensuring that code generated by Fable will run in any browser. fable-core is standard Fable library.

Those commands again modified package.json file, this time adding following information:

"dependencies": {
    "core-js": "^2.4.0",
    "fable-core": "0.0.21"
  }

Creating F# script and HTML file

Fable supports both fsproj and plain fsx files. For simplicity in this example let’s use fsx file. Create src\code.fsx with follwing content:

#r "../node_modules/fable-core/Fable.Core.dll"

open Fable.Core 
open Fable.Import
 
Node.require.Invoke("core-js") |> ignore

let element = Browser.document.getElementById "sample"
element.innerText <- "Hello, world !!"

This code should be pretty straightforward, only tricky part is Node.require.Invoke("core-js") which is importing Node.js module (installed by npm).

Let’s also create very simple HTML file public\index.html ( public will be our output folder):

<!doctype html>
<html>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
</head>
<body>
  <div id='sample'></div>
  <script src="bundle.js"></script>
</body>
</html>

Configuration

First step is creating configuration for Fable compiler. It’s done by creating fableconfig.json file.

{
  "module": "commonjs",
  "sourceMaps": true,
  "projFile": "./src/code.fsx",
  "outDir": "temp",
  "scripts": {
    "prebuild": "npm install",
    "postbuild": "webpack"
  },
  "targets": {
    "watch": {
      "scripts": {
        "postbuild": "webpack --watch"
      }
    }
  }
}

First we define module type - in our case it’s commonjs (as it’s working well with Webpack). Next we define that we want to use soruce maps (it allows F# file debugging), entry point of application, and output directory for compiled JS files. Next step is defining some small scripts which will be run before and after every build - before we want to run npm install to restore all dependencies, after build we run webpack to create bundled output file. Last part defines additional target which will be executed in watch mode. In such case we want to run webpack also in watch mode.

watch mode will make Fable and Webpack to recompile project after every file save without need to execute any additional commands.

Webpack configuration

Second step is to create webpack configuration. Let’s create webpack.config.js file:

var path = require("path");
var webpack = require("webpack");

var cfg = {
  devtool: "source-map",
  entry: "./temp/code.js",
  output: {
    path: path.join(__dirname, "public"),
    filename: "bundle.js"
  },
  module: {
    preLoaders: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "source-map-loader"
      }
    ]
  }
};

module.exports = cfg;

In this file we define that we want to use source maps, entry point for webpack ( so file generated by Fable), output path, and usage of any additional plugins (in our case it will be source-map-loader installed before)

Npm scripts

Easiest way to run tools installed by npm is by using scripts section in package.json file. Let’s put there following scripts:

"scripts": {
  "build": "fable",
  "watch": "fable -w --target watch"
},

build just starts Fable. watch, as name suggests, starts Fable in watch mode, so code is regenerated every time we save F# file. We can run those scripts using npm run <script_name> command.

VSCode configuration

Last, optional step is creating VSCode configuration. It’s fairly easy since editor has decent integration with npm. All we need to do is create .vscode\tasks.json file and put there following code:

{
  "version": "0.1.0",
  "command": "npm",
  "isShellCommand": true,
  "showOutput": "always",
  "suppressTaskName": true,
  "tasks": [
    {
      "taskName": "install",
      "args": ["install"]
    },
    {
      "taskName": "build",
      "args": ["run", "build"],
      "isBuildCommand": true
    },
    {
      "taskName": "watch",
      "args": ["run", "watch"],
      "isWatching": true
    }
  ]
}

Here we define that our tasks are using npm and we define arguments send to npm with every task. We can run those task usign Tasks: Run Task command (build task can be also run using Tasks: Run Build Command command or Ctrl + Shift + B)

Summary

In this post we have shortly moved all steps necessary to create browser, client-side applications using F#, Fable and Webpack. Whole source code of this sample application is on GitHub - https://github.com/Krzysztof-Cieslak/fable-webpack-demo

Krzysztof Cieslak's Picture

About Krzysztof Cieslak

Krzysztof is a F# developer and consultant, open source contributor, conference speaker

Lodz, Poland http://kcieslak.io