There are two big community projects existing for transpiling F# code to JavaScript: WebSharper and Fable. Hovewer, the focus of the two projects are somewhat different, here is how I see it:
JavaScript.Array
or Optional
(erased option) types when needed for interop. Also, you can have server and client code living inside same project, encapsulating whole server-client functionality and JavaScript dependencies that can be distributed via NuGet.WebSharper.Core.Macro
and Generator
types. Macros allow custom translation logic for annotated methods/types, while generators can create JS code output from some logic executing in compile-time that returns a JS string, F# quotation or WebSharper AST. For example macros allow for auto-implemented two-way lensing in WebSharper.UI
.WebSharper's main shortfall is that its output can be pretty crude and does not integrate with module systems and bundlers well enough. The original intention was that your main web project is a .NET project that can handle everything for you, this legacy got carried along and there was always more focus on improvements on the input side (C# support, more language and framework features, etc.) than on the output side. WebSharper 7 is under development now that will bring output up to modern JavaScript standards with module output, JS classes, get/set properties, arrow functions and more.
While this update is under way, let us look at a minimal version of Try WebSharper, a simple web project that exposes JS translations of either F# or C# source code input on a minimal UI. This will eventually be a good tool to demo WebSharper 7's improved output.
The project is available on Github.
Clone and run it with:
git clone https://github.com/Jand42/CompileJSLive
cd CompileJSLive
dotnet run
The project uses both the WebSharper.Compiler.FSharp
and WebSharper.Compiler.CSharp
packages, which are containing the WebSharper compiler libraries as references to be used by our code, not as tools (which are used for translating WebSharper-enabled projects but not invoking the compiler).
These depend on FSharp.Compiler.Services
and Microsoft.CodeAnalysis.CSharp
(Roslyn) respectively, so we don't need to directly reference these.
WebSharper uses metadata embedded in dll-s to guide its translation to conform to already translated projects with embedded JS files in them. Some of this metadata is used for the server-side runtime of web projects to enable client-server features of websharper for example creating controls on the server which will be filled in on the client with content (the client
helper functions in Site.fs
). Usually this runtime metadata does not need to contain any code expressions, just the overall shape of the generated JS so that event handlers and client-side placeholders can refer to it. But for on-the-fly translation, we also need the inline expressions from metadata, which are expanded upon use. This can be made accessible with a single new option in wsconfig.json
: "runtimeMetadata": "inlines"
.
Now the server-side translation of both F# and C# are pretty similar in this sample:
The project showcases how to do this simply. Note however that the Compilation has a UseLocalMacros = false
constructor argument, this is guarding against executing user-submitted code via macro functionality. Without this, macros would still not run, as we would need to load a dynamically compiled assembly created from the source into an assembly load context, this argument is just making sure WebSharper does not even attempts to look up macros from current project and gives a specific error about it.
This is a small project for playing around, there are many complexities, but I hope I could give an understandable but very concise summary.
Contributors are welcome on WebSharper, feel free to send me a message on F# Software Foundation Slack.
Can’t find what you were looking for? Drop us a line.