Quick start with Fable
This article is also published in Thai at FSharp Thai website.
Fable is F# script to Javascript compiler, just like Elm or Purescript, it is a functional language by extending F# language to be suitable with JS technology. As current writing, Fable seems to be in an experimental beta version. The community is somewhat small (from my observation in Github). Not sure if it will survive.. Anyway..
Installation
Pre-requisites: F# 4.0 and NodeJS 4.4+ (I use Node 6.4.0) must be installed in the machine in order to use fable.
Fable compiler can be installed via NodeJS.
npm install -g fable-compiler
Quick Start
There are some project samples written with Fable. I try to summarize what you need the least in order to start a JS client side project.
Hello World with RequireJS
Create a directory and initialize Node package file.
mkdir demo && cd demo
npm init
Fable uses Plugin concept to extend its libraries. fable-core
is a main JS client side interface library. It can be installed via npm
.
core-js
is a ES6-7 polyfill library, this should also be installed to ensure that code generated from Fable should be able to run in most browsers.
And of course, this sample needs requirejs
.
npm install --save fable-core core-js requirejs
Let's start with an HTML page. Create index.html
at the root of project. This HTML should be served via file protocol so the path references in it are all relative.
We are going to generate JS into out
sub-directory so we configure RequireJS to find needed JS in out
. The Fable generated JS will refer to fable-core
module, so it must be declared too.
<!doctype html>
<html>
<body>
<div id="content" />
<script src="node_modules/core-js/client/core.min.js"></script>
<script src="node_modules/requirejs/require.js"></script>
<script>
requirejs.config({
baseUrl: 'out', // Set the baseUrl to the path of the compiled JS code
paths: {
// Explicit path to core lib (relative to baseUrl, omit .js)
'fable-core': '../node_modules/fable-core/fable-core.min'
}
});
requirejs(["hello"]);
</script>
</body>
</html>
Create hello.fsx
(F# script) in the root of directory. We are going to put Hello world!
inside the div
tag content
. We will also write the words in the browser's console log. Let's create hello.fsx
.
#r "node_modules/fable-core/Fable.Core.dll"
open Fable.Core
open Fable.Import
let GreetingWords = "Hello world!"
// Browser is in Fable.Import namespace.
let content = Browser.document.getElementById "content"
content.innerHTML <- (sprintf "<h1>%s</h1>" GreetingWords)
Browser.window.console.log GreetingWords
Unfortunately, there is no API document currently. You have to look into the library code directly to see what is available. At least they are all written in F# which I hope you are already familair :) For example, all browsers related code are in https://github.com/fable-compiler/Fable/blob/master/src/fable/Fable.Core/Import/Fable.Import.Browser.fs
To compile Fable, you can either use only command line fable
or use Fable configuration file. Most examples use the config file so do I. Fable config file is named fableconfig.json
.
{
"module": "amd",
"sourceMaps": true,
"projFile": "./hello.fsx",
"outDir": "out",
"scripts": {
"prebuild": "npm install"
}
}
Fable can also generate CommonJS module format. This sample we use RequireJS so it is set to AMD.
Then we are ready to go. Just compile it with fable
.
fable
hello.js
and hello.js.map
should be generated in out
directory. Open index.html
with a browser to see Hello world!.
Hello world with Webpack
Krzysztof has written a blog about using Fable with Webpack. Here I use his technique with Hello world example and trim some code to show only minimum requirements.
First, install webpack
if your machine does not have it yet. (sudo
is needed if on *nix).
npm install -g webpack
Create a directory and install needed components.
mkdir demo && cd demo
npm init
npm install --save-dev webpack source-map-loader
npm install --save core-js fable-core
Create index.html
. It is shorter since no RequireJS setup.
<!doctype html>
<html>
<body>
<div id="content" />
<script src="out/bundle.js"></script>
</body>
</html>
There is no change in hello.fsx
.
#r "node_modules/fable-core/Fable.Core.dll"
open Fable.Core
open Fable.Import
let GreetingWords = "Hello world!"
// Browser is in Fable.Import namespace.
let content = Browser.document.getElementById "content"
content.innerHTML <- (sprintf "<h1>%s</h1>" GreetingWords)
Browser.window.console.log GreetingWords
Create fableconfig.json
. Note that we issue webpack
command after building.
{
"module": "commonjs",
"sourceMaps": true,
"projFile": "./hello.fsx",
"outDir": "out",
"scripts": {
"prebuild": "npm install",
"postbuild": "webpack"
}
}
Finally, create webpack.config.js
on the root of directory.
var path = require("path")
var webpack = require("webpack")
module.exports = {
devtool: "source-map",
entry: "./out/hello.js",
output: {
path: path.join(__dirname, "out"),
filename: "bundle.js"
},
module: {
preLoaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "source-map-loader"
}
]
}
}
That's it. Just run fable
and open index.html
in your browser.