Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better support for fable-compiler-js #3794

Closed
ken-okabe opened this issue Mar 26, 2024 · 17 comments
Closed

Better support for fable-compiler-js #3794

ken-okabe opened this issue Mar 26, 2024 · 17 comments

Comments

@ken-okabe
Copy link

ken-okabe commented Mar 26, 2024

Description

Fable and fable-compiler-js are astonishing projects/products.

According to the Changelog of fable-compiler-js, recently, the project status has achieved to stable.

1.0.0 - 2024-02-12
Release stable version

I'm happy to see the stable version release and really appreciate your continued development.

I’m writing articles on F# and planning to introduce Fable. Especially, I observe that fable-compiler-js is promising as an AltJS solution with an easy setup using just Node.js, without the need for a .NET installation.

Here, I would like to request better support for fable-compiler-js.

README.md

The information:

This version is somewhat experimental

looks outdated, and

npm install fable-compiler-js
npx fable <PROJECT_PATH> <OUT_DIR> [--options]

although the installation succeeded, the npx fable command fails:
fable-compiler-js package.json dependencies are broken #3793

After manual modification by myself, npx fable seems still unusable.

$ npx fable --help

file:///home/ken/Documents/p/c/fable/counter/node_modules/@fable-org/fable-compiler-js/dist/app.min.js:1
import{getAssembliesDir as Js}from"@fable-org/fable-metadata";import*as te from"fs";import*as he from"path";import Zs from"child_process";import{createRequire as Us}from"node:module";import{init as
...
...
SyntaxError: The requested module '@fable-org/fable-standalone' does not provide an export named 'init'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:134:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:217:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:323:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:120:12)

Node.js v21.7.1

Repro code

https://github.com/fable-compiler/Fable/blob/main/src/fable-compiler-js/README.md
https://www.npmjs.com/package/@fable-org/fable-compiler-js

npm install fable-compiler-js
npx fable --help

Expected and actual results

Better support for fable-compiler-js

Related information

@fable-org/fable-compiler-js
1.1.0

@MangelMaxime
Copy link
Member

According to the Changelog of fable-compiler-js, recently, the project status has achieved to stable.

In reality, it has been released as stable since a long time. The beta / stable version you see here is because we recently moved all Fable NPM package under @fable-org because we didn't have access to some of the packages preventing us to make new releases.

I’m writing articles on F# and planning to introduce Fable. Especially, I observe that fable-compiler-js is promising as an AltJS solution with an easy setup using just Node.js, without the need for a .NET installation.

Using Fable without .NET installation is in theory possible in reality you it will mostly never be the case. You need to have .NET installed in order to have your editors/IDE running unless you like using bloc note to edit code.

fable-compiler-js was mostly used to test out performance of Fable compiled JavaScript and also from time to time to write build script and run them on JavaScript instead of using dotnet fsx.

If your goal is to write documentation about using Fable, I would encourage you to guide your users to use the .NET version of Fable which is the one that we currently actively develop. fable-compiler-js still benefit from the latest features/fixes that we add to Fable thanks to fable-standalone package but beside that we don't really actively support it or think about it.

I will still look into making the CLI tool works again, as it is actually used in the CI so I think it should not be too difficult to fix.

@ken-okabe
Copy link
Author

@MangelMaxime
Thanks for your explanation and suggestion.
I appreciate it, and please close my issues.

the .NET version of Fable which is the one that we currently actively develop.

I understand that, and the below is absolutely not a feature request but for a future reference and my personal interest.

Has anyone here compiled Fable to WASM?

@MangelMaxime
Copy link
Member

Thanks for your explanation and suggestion.
I appreciate it, and please close my issues.

No problems with the issues you opened, we should fix fable-compiler-js to make it work. I start the work or it in #3795, it seems to be packaged correctly. But now, I believe that there are a few bugs in the actual code of fable-compiler-js it fails to create a folder on my disk when testing out. Will continue to investigate in the coming days.

Has anyone here compiled Fable to WASM?

Not that I know of. But this is something that was discussed in the past, as it could perhaps improve the packaging of Fable via NPM and makes it possible to better structure the REPL etc.

But by lack of time, it has not been explored yet.

@ken-okabe
Copy link
Author

I start the work or it in #3795

Thank you for your prompt contributions.

it could perhaps improve the packaging of Fable via NPM and makes it possible to better structure the REPL etc.

Exactly. As I mentioned earlier, since Fable compiles to JS, I see F# has huge potential as an AltJS.

Currently, people use

  • TypeScript -> JavaScript

Alternatively, we use

  • F# -> JavaScript

Recently, interesting things going on at StackBlitz using WASM.

Introducing WebContainers: Run Node.js natively in your browser

Announcing Native Language Support in WebContainers

@ncave
Copy link
Collaborator

ncave commented Mar 27, 2024

@ken-okabe Thank you for opening this issue, and thank you @MangelMaxime for fixing the build.

I agree with @MangelMaxime that most users should be steered towards the .NET tool release of Fable, as it is the most supported and capable version for cross-platform transpiling of F#.

That said, fable-compiler-js can also work just fine in some cases, but it has certain limitations:

  • Due to using Regex instead of full .NET/MSBuild to parse the F# project(s), it has some limitations on what project build features it can support (e.g. no support for build variables, conditional includes, build actions , etc). Adding more support for project build features is possible, but has diminishing returns, as we don't have as a goal to rewrite MSBuild in JavaScript. But if your F# project(s) targeting Fable are simple, that usually works fine.
  • It doesn't support the full range of options that the .NET version of Fable supports (e.g. --watch etc.).
    This can certainly be improved with community contributions, if someone is interested.
  • The main issue with fable-compiler-js is speed. Running it on Node.js is about 3x slower than the .NET version (and using Bun doesn't make it much faster). That said, for small to mid-size projects the 3x speed difference may or may not be an issue.

About compiling Fable to WASM, we have not tried yet, but since WASM is only ~30% faster than JS, in theory it will still make it 2x slower than the .NET Fable.

To clarify, currently we have several ways to compile to WASM:

  • Fable -> Rust -> WASM: The Fable to Rust support has progressed a lot, and can compile to native or WASM (see a demo here), but is still not as fully featured as to compile Fable itself.
  • .NET AOT -> WASM: This probably works, there is a test version of fable-compiler-js using .NET that can be tinkered with, if you're interested. As the .NET AOT WASM generated code is still somewhat slower than the one Rust generates, I wouldn't be surprised if it's even slower than using JavaScript. That said, now that WASM Garbage Collection is a thing, perhaps the .NET-generated WASM code will improve in the future.

To summarize, fable-compiler-js can definitely work for small/simple projects, but given it's limitations it's more of a technological demo. There could in theory be other useful purposes based on it, like building a JavaScript-only VSCode LSP extension, for example. It's not going to be very fast, obviously, but it can run in a browser.

And lastly, if using Fable without any additional dependencies is a goal, it's possible to go completely the other direction and just build a native binary (.NET AOT to native). Of course, it's not going to be cross-platform, you'll have to build binaries for each platform you target, but is very fast. Anecdotally it used to be as much as 2x faster than managed .NET Fable version, but recent improvements in F# compiler may have shrunk that difference somewhat. Here is the test project again if you want to tinker with it.

@ken-okabe
Copy link
Author

ken-okabe commented Mar 27, 2024

@ncave I really appreciate your elaborate explanation, and the provided information truly helps me out.
I'm always impressed by your contribution to fable-library-rust since I've read the article for Fable 4 Alpha Release.

Yes, I know fable-raytracer. Thank you. Such a versatile, great testing project providing results that we exactly need to know. I've followed the discussion there. Quite interesting!

Rust>WASM is the most successful manner and now the eco is huge. It has a superior performance and portability(small footprint), but here, the only problem for me is that I really don't want to code Rust. So I've found your work is extremely valuable.

In fact, years ago, I asked you how you manage .net GC into non-GC Rust code, somewhere.

In real-time systems like Drone, auto-pilot or space projects, where latency and predictability are critical, code with garbage collection (GC) like .NET can be problematic. Real-time systems require Rust code without GC.

Fable -> Rust -> WASM: The Fable to Rust support has progressed a lot

So, I'm happy to hear the progress.

About compiling Fable to WASM, we have not tried yet, but since WASM is only ~30% faster than JS, in theory it will still make it 2x slower than the .NET Fable.

.NET AOT -> WASM: This probably works

Thanks, that's what I thought.

@ken-okabe
Copy link
Author

ken-okabe commented Mar 28, 2024

I want to clarify why I'm so interested in Fable working on JS/WASM, not on .NET.

Recently, I found and studied VanJS: Reactive UI Framework without React/JSX.

https://risingstars.js.org/2023/en#section-framework
image

It is such a minimal clean implementation, and here is the live demo.

https://jsfiddle.net/gh/get/library/pure/vanjs-org/vanjs-org.github.io/tree/master/jsfiddle/home/counter

image

It's just vanillaJS(VanJS named after this), no JSX, no compile.

I was impressed by the simple design of VanJS. Then, thanks to Fable(such an astonishing hard work), I thought it easy to develop a thin F# wrapper/bridge for VanJS; that should be named VanFS.

  • The basic UI composition functions: van.tags and van.add are 1:1 bindings.

  • For the state management: van.state and van.derive, since they are not functional at all like any others(React/SolidjS/Vue etc.), I wrapped the native VanJS state object with a small FRP(FunctionReactiveProgramming) library with monad structure that I've kept developing for years.

  • For design and ready-made functionalities, Material Web Component 1.0 has just been released, so I decided to make VanFS battery included.

After a few days of work, here is the Program.fs and output:

module CounterApp

open Browser
open Browser.Types
open Fable.Core.JsInterop

open Van.Basic // import tags, add
open Van.MaterialComponents // import materialTags
open Van.Timeline // import Timeline

// use the identical tag names for the standard HTML tags
let h1: Tag = tags?h1
let span: Tag = tags?span
// any tag names can be defined for each material components
let filledButton: Tag = materialTags?``md-filled-button``

let Counter =
    fun _ ->
        let counter = Timeline 0

        counter
        |> bindT logT
        |> ignore

        span [
            h1 [ "❤️ "; counter.el; ];
            filledButton [{|onclick =
                        fun _ ->
                            counter
                            |> nextT (counter.lastVal + 1)|};
                    "👍"];
            filledButton [{|onclick =
                        fun _ ->
                            counter
                            |> nextT (counter.lastVal - 1)|};
                    "👎"];
        ]

add [document.body; Counter()]
|> ignore

image

Upcoming release of VanFS.


Currently, people use

  • TypeScript -> JavaScript

Alternatively, we use

  • F# -> JavaScript

The reason people feel more comfortable with JavaScript and TypeScript is its strong and seamless integration to the UI Ecosystem.

However, if the Web UI relies on a simple function-based composition like VanJS, and ready-made components are easily imported as custom tags like Material Web Components, the web-developers no longer have to depend on JS/TS because the code becomes just for function calls based on tags(HTML standard/Web Components).

The current situation can change. F# as an AltJS.

Having said that, TypeScript is seamlessly included in NPM Eco.

https://www.npmjs.com/package/typescript

npm install -D typescript

then, I thought Fable was also the same.

https://www.npmjs.com/package/@fable-org/fable-compiler-js

npm install fable-compiler-js

If a compiler is available as an NPM package, the setup process is simplified. package.json dependencies can bundle the compiler within an NPM project with a single command: npm install.

Thanks to your elaborated answers, I now understand fable-compiler-js isn't the best path forward. However, any compiler distributed as an NPM package offers a convenient setup for the web community, including a WASM version.

I hope @ncave finds this discussion interesting 😊

@ncave
Copy link
Collaborator

ncave commented Mar 28, 2024

@ken-okabe I can't open https://github.com/vanjs-org/van right now for some reason, but nice to see another framework used with Fable, thanks for sharing!

I see there is also this: https://www.npmjs.com/package/vanjs-component

@MangelMaxime
Copy link
Member

Thanks for all the information in this thread.

Like @ncave I can't access the van repo right now, Github gives me a 404. 🤷‍♂️

But reading, through Van description here made me think about F# Sutil which is a pure F# framework to create web application without React, etc. I think it is based on how Svelte works but without needing a compilation phase other that Fable.

Having said that, TypeScript is seamlessly included in NPM Eco.

npmjs.com/package/typescript

npm install -D typescript

then, I thought Fable was also the same.

npmjs.com/package/@fable-org/fable-compiler-js

npm install fable-compiler-js

If a compiler is available as an NPM package, the setup process is simplified. package.json dependencies can bundle the compiler within an NPM project with a single command: npm install.

True, the main difference ATM is that TypeScript have it's binding on NPM too. While Fable, needs them to be on NuGet ATM. In the past, we had Fable + libraries distributed through NPM but the problem, is that then people are exposed to unusual ways of using F#.

For example, instead of referencing a library using PackageReference, users had to go find the project in their node_modules:

Which looked something like that (I don't exactly remember how it was written)

<Compile Include="node_modules/my-fable-library/Fle1.fs">
<Compile Include="node_modules/my-fable-library/Fle2.fs">
...
<ProjectReference Include="node_modules/my-fable-library/Project.fsproj">

There are discussions in creating a custom target framework for Fable, which could perhaps changes things a bit. But now, we need to be backward compatible in anything we do IHMO otherwise it will break the ecosystem and I don't think it would be able to move it forward fast enough without loosing too many users etc.

@ken-okabe
Copy link
Author

ken-okabe commented Mar 28, 2024

This is so weird.
I've checked the official site/repo hours ago.

https://www.npmjs.com/package/vanjs-core
is still active, but obviously, they intentionally deleted the official site and Github repository at the same time.
https://github.com/vanjs-org itself disappears.
JSfiddle page is deleted, too.
I have no idea what's going on...

@ken-okabe
Copy link
Author

@MangelMaxime
Thanks for your thought.
True, come to think of it, if one wants to use F#, the one will install .NET framework locally.

@ken-okabe
Copy link
Author

@ncave
@MangelMaxime

https://vanjs.org/
is up again.

On the top of page:

🚨 My GitHub account was flagged due to some suspicious login activities (I've no idea what happened). As a result, all VanJS repos hosted in GitHub and my GitHub accounts are currently unavailable. This website (which was hosted via GitHub Pages) was also down for 2 hours (recovered after migrated to Deno Deploy). I have reached out to GitHub support team and will do my best to restore the access of my GitHub repos. Sorry for all the inconvenience. 🙏🙏🙏

@ncave
Copy link
Collaborator

ncave commented Mar 29, 2024

@ken-okabe Just out of curiosity, if VanJS is so small (just 140 lines of JS), do you even need to depend on it for VanFS, can't you directly generate vanilla JavaScript?

@ken-okabe
Copy link
Author

I've released the first version of VanFS!
https://github.com/ken-okabe/vanfs
I'm happy to have mentioned Fable in the README. Thanks again for this great product!

@ncave
I am truly sorry for the delayed response.

if VanJS is so small (just 140 lines of JS), do you even need to depend on it for VanFS, can't you directly generate vanilla JavaScript?

You're right. If it were a virtualDOM framework, I could probably build my own.
However, VanJS isn't a virtualDOM framework, and I confess I don't fully understand the underlying tricks.
In fact, despite being a small library, the project is consistently updated: https://github.com/vanjs-org/van/releases and the community trust the project.

After all, it's written in JavaScript, so instead of reinventing the wheel, I wanted to depend on the project.

PS. Thanks a lot for your constant contributions to the Fable Rust!

@ncave
Copy link
Collaborator

ncave commented May 1, 2024

@ken-okabe Congratulations on the VanFS release! Yes, on second thought it makes sense to take an existing JS front-end framework as a dependency, to benefit from its ecosystem.

@ken-okabe
Copy link
Author

ken-okabe commented May 1, 2024

@ken-okabe Congratulations on the VanFS release! Yes, on second thought it's makes sense to take an existing JS front-end framework as a dependency, to benefit from its ecosystem.

Correct, and another aspect would be an advertisement/marketing 😉

Being recognized as an extension of a popular and highly trusted project such as VanJS would bring immense benefits in terms of raising awareness among many people about how great F# and Fable are.

I personally believe that F# and Fable are truly amazing and would like to contribute in any way to spread.

Actually, I contacted the author: Tao Xin and thankfully, had my project listed on the VanJS top page. I hope that as VanJS grows in popularity, the Fable-powered project will gain recognition among a wider audience!

image

@MangelMaxime
Copy link
Member

This should be fixed now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants