Fable: Bring back fable-loader?

Created on 8 Oct 2020  ·  19Comments  ·  Source: fable-compiler/Fable

Continuation of discussion here: https://github.com/fable-compiler/Fable/issues/2166#issuecomment-703923084

Nagareyama will become a standalone dotnet tool and it won't be as much Webpack-focused as Fable 2 was, but we could try to release a new fable-loader version that just calls the dotnet tool to make it easier to upgrade existing projects.

I'm preparing a presentation for F# eXchange about the changes in Fable and I can write a blog after so we can get feedback from users. I think most will like the move as it aligns with other F# tools but I might be wrong, let's see.

discussion

Most helpful comment

Hi @inosik! The goal of Nagareyama is to make Fable simpler by removing the .NET/JS mixture in the compiler itself. So Fable will become a "pure" dotnet tool distributed through Nuget and its only responsibility will be to spit out JS files (avoiding complex interaction with JS tooling). So to compile an F# project (given its Fable-compatible) the only thing you'll need to do is:

dotnet tool install fable
dotnet fable src/Client

You can set an output directory if you want but by default Fable will put the .js files next to the .fs ones.

Then users can do whatever they need with the JS files. In the common case of bundling them with webpack, they only have to set the last generated .js file as the entry in the webpack.config (no extra npm dependencies like fable-loader needed). For simplicity, Fable can run a command after the compilation as in dotnet fable src/Client --run webpack or dotnet fable watch src/Client --run webpack-dev-server

All 19 comments

What does the workflow you have in mind look like, if we won't have a loader? Will Fable simply compile the F# code to an output directory and then Webpack or any other bundler takes over from there?

I haven't been watching the progress on Nagareyama lately, so forgive me if this has been clarified somewhere already.

Having Fable be simple to set up without having to use a template would certainly be really nice.

Hi @inosik! The goal of Nagareyama is to make Fable simpler by removing the .NET/JS mixture in the compiler itself. So Fable will become a "pure" dotnet tool distributed through Nuget and its only responsibility will be to spit out JS files (avoiding complex interaction with JS tooling). So to compile an F# project (given its Fable-compatible) the only thing you'll need to do is:

dotnet tool install fable
dotnet fable src/Client

You can set an output directory if you want but by default Fable will put the .js files next to the .fs ones.

Then users can do whatever they need with the JS files. In the common case of bundling them with webpack, they only have to set the last generated .js file as the entry in the webpack.config (no extra npm dependencies like fable-loader needed). For simplicity, Fable can run a command after the compilation as in dotnet fable src/Client --run webpack or dotnet fable watch src/Client --run webpack-dev-server

I like the new approach.
Would this change impair speed optimizations in the future, like HMR? There isn't some kind of concurrency loss?

I don't think it should affect HMR, but please report if you see something. It's true that by using --run Webpack won't start until the compilation is complete, for that there's the --runFast option that will run the subprocess before the F#/Fable compilation. So if the JS files already exist (usually from a previous compilation) Webpack can work in parallel and the time from 0 to have the server running is reduced basically to the bundling time. And once Fable has finished in parallel, watch compilations are very fast as usual.

I'm currently trying to put together an app with a backend using Azure Functions (to be hosted on Azure Static Websites) and what I'm struggling with is setting up redirects for the local development environment when using parcel as shown in the minimal fable3 example.

Personally, I'd be quite excited to get rid of webpack and simplify the stack but this some guidance on how to setup something like a SAFE stack app would be super helpful. If that could be done without fable-loader, then it should be ok not to have it.

Any tips would be appreciated :-)

@uxsoft Did you already see the _API Proxy_ page in Parcels docs? But it looks like it's an upcoming feature of Parcel v2, which apparently isn't released yet. It looks like Parcel doesn't have a built-in proxy in v1.

I found a blog post from one year ago, where the author describes how to set up a dev environment with Parcel and an Express server which acts as a proxy. Maybe this helps.

@uxsoft Also, just to upgrade your project without touching Webpack, you can follow this example: https://github.com/MangelMaxime/fulma-demo/pull/43

Well in that case there should be no need for fable-loader if we can pipe to webpack dev server anyway, no?

Yes, that's right. The only purpose of the "new" fable-loader would be to let users upgrade to Fable 3 without touching their webpack config or build script, but I'd personally prefer if people get used to the new way as it is aligned with other F# tools and to avoid having to maintain/document two ways of using Fable.

In any case, from the feedback received it seems users are happy with the change, so I will close this issue for now to avoid confusion. We can revisit later if needed.

New fable-loader could be useful in case some JS tooling takes webpack config as an input/entry point. For example - Cloudflare Workers: https://developers.cloudflare.com/workers/cli-wrangler/webpack

@Krzysztof-Cieslak Deprecating fable-loader doesn't mean you can't use Fable with webpack anymore, it just means it becomes easier to use in webpack because it just becomes this:

  • dotnet fable triggers Fable as a dotnet CLI tool
  • Fable compiles F# files to JS files (modules)
  • Webpack bundles them

Instead of:

  • fable-loader triggers Fable as entry module loader
  • Fable compiles F# files into JSONified Babel AST (relatively slow)
  • babel-loader takes JSON babel AST and outputs JS code
  • Webpack bundles them

I raised the issue with @alfonsogarciacaro because I thought it would make migration easier if we kept fable-loader and just updated fablec-compiler (the npm package) to latest Fable but maybe it would be a lot better if we simplified all our templates and removed these loaders altogether (also works nicely with other bundles like parcel)

Sure, it "just" means I need to put additional steps in my build process for something that used to be single step.

Your call if that's reasonable trade off /shrug

The build step is still just one command, except now it doesn't _require_ extra webpack loaders and can easily be used with different bundlers.

# compile project using Fable
dotnet fable ./src 

# compile project then trigger bundler (production build)
dotnet fable ./src --run webpack

# build in watch mode
dotnet fable watch ./src

# build in watch mode and trigger a bundler after compilation
dotnet fable watch ./src --run webpack-dev-server

Webpack only needs to know the entry JS file that is generated from Fable ({lastFile}.fs.js) as the entry file for bundling and minification. fable-loader and babel-loader aren't needed.

@alfonsogarciacaro Is there a dotnet fable run that is equivalent to dotnet run which automatically triggers node {lastFile}.fs.js after compilation finishes successfully? Might be an interesting command to show off Fable 🤔

The build step is still just one command

Not really in a case of CF Workers tooling - it used to be a single call to wrangler dev (which under the hood used webpack somehow, which under the hood executed fable with loader). Now it will be first compiling fable, and then calling wrangler.

Again, it's not a big deal - I'm more just giving an example where old workflow was fitting better.

I really liked the previous model too but now that I have tried the new stuff, I really appreciate the perf increase of the compilation when all that overhead is removed

Does something like dotnet fable dev --run wrangler dev work? In theory, we could have a new fable-loader that just calls dotnet fable but there may be issues with starting/stopping the processes, the watcher, etc. So I'd prefer to avoid it.

dotnet fable watch src --outDir tmp --run wrangler dev seems to do what I want (i.e. both fable and wrangler in watch mode).

There's some issue with wrangler process not getting killed after I killed dotnet fable process, but it's not a huge problem.

Cool! Hmm, there were some reports about a similar issue but I thought they were fixed. Can you please have a look at #2212? Are you using Windows? I'm only hooking the termination events in Windows because I didn't see that issue in macOS but we may need to do that as well: https://github.com/fable-compiler/Fable/blob/111a0b2175bf605594817f849109fedef04eb6f3/src/Fable.Cli/Util.fs#L127-L136

Was this page helpful?
0 / 5 - 0 ratings