|
7 | 7 | > [!IMPORTANT] |
8 | 8 | > This project is up for adoption. I'm looking for eager people to maintain this.<br>Please open a [discussion](https://github.com/fable-compiler/vite-plugin-fable/discussions) if you are interested! |
9 | 9 |
|
10 | | -## What is this? |
| 10 | +Compile [F#](https://fsharp.org/) with [Fable](https://fable.io/) from inside [Vite](https://vite.dev/), so a `.fs` file is just another module Vite can import. |
11 | 11 |
|
12 | | -Alright, the `tl;dr` is that I don't like the current way of how you can use [Fable](https://fable.io) with [Vite](https://vitejs.dev). |
13 | | -I'm referring to the steps in the [get started with Vite](https://fable.io/docs/getting-started/javascript.html#browser), I don't like it and have an alternate take on it. |
| 12 | +The usual setup puts Fable in front of your dev server (`dotnet fable watch --run vite`). This plugin does not. You run `vite`, and F# is compiled on demand and updated over HMR, the same way Vite treats TypeScript, JSX or Sass. |
14 | 13 |
|
15 | | -More thoughts on this can be read from the [documentation](https://fable.io/vite-plugin-fable/). |
| 14 | +## Requirements |
16 | 15 |
|
17 | | -## Current status |
| 16 | +- The **.NET 10 SDK** on your `PATH`. Reading your `.fsproj` means asking MSBuild about it. Check with `dotnet --version`. |
| 17 | +- **Vite 8** (peer dependency). |
18 | 18 |
|
19 | | -A first package was pushed to npm. This was merely to reserve the package name. |
20 | | -You can read the code, that's is it for now. |
| 19 | +You do not need Fable as a dotnet tool. The compiler ships prebuilt inside the package, so there is no post-install step and installing with `--ignore-scripts` is fine. |
21 | 20 |
|
22 | | -### Recent Notes |
| 21 | +## Install |
23 | 22 |
|
24 | | -Support for the latest .NET runtime was added in [v0.1.1](https://github.com/fable-compiler/vite-plugin-fable/blob/main/CHANGELOG.md#011---2025-06-03). Please upgrade to the latest version. Earlier versions may fail silently if the .NET 8 runtime is missing—see the changelog for details. |
| 23 | +```bash |
| 24 | +npm install -D vite-plugin-fable |
| 25 | +bun install -D vite-plugin-fable |
| 26 | +``` |
| 27 | + |
| 28 | +## Getting started |
| 29 | + |
| 30 | +Add the plugin to your Vite config: |
| 31 | + |
| 32 | +```js |
| 33 | +// vite.config.js |
| 34 | +import { defineConfig } from "vite"; |
| 35 | +import fable from "vite-plugin-fable"; |
| 36 | + |
| 37 | +export default defineConfig({ |
| 38 | + plugins: [fable()], |
| 39 | +}); |
| 40 | +``` |
| 41 | + |
| 42 | +The plugin compiles the single `.fsproj` next to your Vite config. If there is more than one, point at the one you want with `fable({ fsproj: "./src/App.fsproj" })`. |
| 43 | + |
| 44 | +Import your F# entry point as a module: |
| 45 | + |
| 46 | +```html |
| 47 | +<script type="module"> |
| 48 | + import "/App.fs"; |
| 49 | +</script> |
| 50 | +``` |
| 51 | + |
| 52 | +`<script type="module" src="/App.fs">` does not work: Vite only resolves the `.fs` extension inside module resolution. See [vitejs/vite#9981](https://github.com/vitejs/vite/pull/9981). |
| 53 | + |
| 54 | +Now start Vite. The plugin stays quiet and prints one line per compile: |
| 55 | + |
| 56 | +```text |
| 57 | + VITE v8.2.2 ready in 376 ms |
| 58 | +
|
| 59 | + ➜ Local: http://localhost:5173/ |
| 60 | + 12:32:44 PM [vite] [fable] compiled App.fsproj in 1.53s |
| 61 | +``` |
| 62 | + |
| 63 | +### With React |
| 64 | + |
| 65 | +The most common setup, using [Fable.Core.JSX](https://fable.io/blog/2022/2022-10-12-react-jsx.html): |
| 66 | + |
| 67 | +```js |
| 68 | +// vite.config.js |
| 69 | +import { defineConfig } from "vite"; |
| 70 | +import fable from "vite-plugin-fable"; |
| 71 | +import react from "@vitejs/plugin-react"; |
| 72 | + |
| 73 | +export default defineConfig({ |
| 74 | + plugins: [fable({ jsx: "automatic" }), react({ include: /\.fs$/ })], |
| 75 | +}); |
| 76 | +``` |
| 77 | + |
| 78 | +Two things to know about that line: |
| 79 | + |
| 80 | +- **Order matters.** `fable` goes before `react`. |
| 81 | +- **The two options do different jobs.** `fable({ jsx })` is what turns Fable's JSX into JavaScript; Vite cannot do it for a `.fs` module. `react({ include: /\.fs$/ })` is what makes `.fs` components Fast Refresh boundaries, so an edit updates in place instead of reloading the page. |
| 82 | + |
| 83 | +Using [Feliz.CompilerPlugins](https://www.nuget.org/packages/Feliz.CompilerPlugins) or plain [Fable.React](https://www.nuget.org/packages/Fable.React) instead? Those emit classic-runtime React and need a different `react()` filter. The [recipes](https://fable.io/vite-plugin-fable/recipes.html) page has both, plus the React Compiler. |
| 84 | + |
| 85 | +## Documentation |
| 86 | + |
| 87 | +- [Getting started](https://fable.io/vite-plugin-fable/getting-started.html) |
| 88 | +- [Plugin options](https://fable.io/vite-plugin-fable/recipes.html#Plugin-options), every option the plugin accepts |
| 89 | +- [Recipes](https://fable.io/vite-plugin-fable/recipes.html), including React, an alternative `fsproj`, and Debug versus Release |
| 90 | +- [Debugging](https://fable.io/vite-plugin-fable/debug.html), what the plugin prints and how to ask the daemon what it is doing |
| 91 | +- [How does this work?](https://fable.io/vite-plugin-fable/how.html) |
| 92 | +- [Changelog](https://github.com/fable-compiler/vite-plugin-fable/blob/main/CHANGELOG.md) |
25 | 93 |
|
26 | 94 | ## Video |
27 | 95 |
|
28 | 96 | I talked a little bit about this project during this stream: |
29 | 97 |
|
30 | 98 | [](https://youtu.be/mnqwwtSQfRU?si=VpDDv3SzHikXL5iu&t=141 "vite-plugin-fable") |
| 99 | + |
| 100 | +## License |
| 101 | + |
| 102 | +[Apache-2.0](https://github.com/fable-compiler/vite-plugin-fable/blob/main/LICENSE) |
0 commit comments