You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/docs/2-steps/setup.md
+3-38Lines changed: 3 additions & 38 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,41 +3,6 @@ title: Setup
3
3
layout: standard
4
4
---
5
5
6
-
## Prerequisites
7
-
8
-
In order to use Fable you will need the following tools:
9
-
10
-
:::warning
11
-
Fable 3, currently has trouble with .NET 7.
12
-
13
-
If you have .NET 7 installed on your system, you will need to add a `global.json` file to your project to ensure .NET 6 is used.
14
-
15
-
*Example:*
16
-
17
-
`{
18
-
"sdk": {
19
-
"version": "6.0.0",
20
-
"rollForward": "latestMinor"
21
-
}
22
-
}
23
-
`
24
-
25
-
You can also give a try to [Fable 4](https://fable.io/blog/2022/2022-09-28-fable-4-theta.html), as it already has .NET 7 support.
26
-
:::
27
-
28
-
-[.NET Core SDK](https://dotnet.microsoft.com/) to work with F# files and dependencies
29
-
-[Node.js](https://nodejs.org/) to execute JS code
30
-
- A JS package manager, like [npm](https://www.npmjs.com/) (which comes with Node) or [yarn](https://yarnpkg.com/)
31
-
32
-
Having the above tools, you may consider using [Femto](https://fable.io/blog/2019/2019-06-29-Introducing-Femto.html) to ease package management.
33
-
34
-
## Development tools
35
-
36
-
Basically you will need an editor that lets you code in F# with features like hints on hover, autocomplete, syntax highlighting and so on... and there are many of them to suit your code styling!
37
-
38
-
-[Visual Studio Code](https://code.visualstudio.com/) with [Ionide](http://ionide.io/)
Copy file name to clipboardExpand all lines: docs/docs/2-steps/your-first-fable-project.md
+7-50Lines changed: 7 additions & 50 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,54 +3,11 @@ title: Start a new project
3
3
layout: standard
4
4
---
5
5
6
-
Now we're ready, let's start a new project using Fable!
7
-
8
-
<ulclass="textual-steps">
9
-
10
-
<li>
11
-
12
-
## Use a Fable template
13
-
14
-
The easiest way to get started with Fable is to use a template (learn more about [dotnet templates](https://docs.microsoft.com/en-us/dotnet/core/tools/custom-templates#installing-a-template)). For a minimal Fable project, create and navigate to a new directory and run the following commands (first one is only needed if you haven't installed the template yet in your system).
15
-
16
-
1.`dotnet new --install Fable.Template`
17
-
2.`dotnet new fable`
18
-
19
-
The rest of this document applies to Fable.Template. Alternatively, if you want specific examples or a more comprehensive template with more tooling and libraries installed, please check one of the following:
20
-
21
-
- Clone the [Fable 3 samples](https://github.com/fable-compiler/fable3-samples) repository to learn how to use Fable with different kinds of apps (browser, nodejs, testing, etc)
22
-
- Build a [React](https://reactjs.org/) app in F# with [Feliz template](https://zaid-ajaj.github.io/Feliz/#/Feliz/ProjectTemplate)
23
-
- Write a frontend app fully in F# without JS dependencies with [Sutil](https://davedawkins.github.io/Sutil/#documentation-installation)
24
-
- Get up to speed with [SAFE Template](https://safe-stack.github.io/docs/quickstart/) which covers both the frontend and backend sides of your app
25
-
26
-
</li>
27
-
28
-
<li>
29
-
30
-
## Install dependencies
31
-
32
-
**JS dependencies** are listed in the `package.json` file. You can run `npm install`, which will download the needed packages to the `node_modules` folder and create a lock file.
33
-
34
-
**.NET dependencies** are listed in the `src/App.fsproj` file. You can install them by running `dotnet restore src`, but this is already automatically done by Fable.
35
-
36
-
:::info
37
-
Lock files (like `package-lock.json` if you're using npm) should be committed to ensure reproducible builds whenever anybody clones your repo. For .NET dependencies you can create a lock file by using [Paket](https://fsprojects.github.io/Paket/).
38
-
:::
39
-
40
-
</li>
41
-
42
-
<li>
43
-
44
-
## Build & run the app
45
-
46
-
Here we go. If you've already installed JS dependencies, just run `npm start`. After a few seconds, your web server app will be running in the background until you kill it.
47
-
48
-
- You can access your project at [http://localhost:8080/](http://localhost:8080/) with your favorite browser.
49
-
- The server is in “watch” mode. Each time you save an `.fs` F# source file, Fable rebuilds the project automatically. If the build succeeds, you will see your changes in the browser without refreshing the page; if not, nothing changes in the browser, and you can see the build errors in the server’s console output.
50
-
51
-
:::info
52
-
The `npm start` command is just an alias for `dotnet fable watch src --run webpack-dev-server`, check the "scripts" section of the `package.json` file. Please also check the `README.md` file shipped with the template to get up-to-date instructions.
Copy file name to clipboardExpand all lines: docs/docs/communicate/fable-from-js.md
+3-272Lines changed: 3 additions & 272 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,275 +3,6 @@ title: Call Fable from JavaScript
3
3
layout: standard
4
4
---
5
5
6
-
Sometimes, we'd like to use the power of Fable in our JavaScript apps. For instance, to create a new
7
-
[js node in Node-RED](https://nodered.org/docs/creating-nodes/first-node) or call some handy F# code from our new Node.js serverless function or even call some powerful json parsing into our JavaScript app.
8
-
9
-
It may allow you to play with Fable and add features, one at a time. So what does it take to call Fable from JavaScript? First you need to understand a bit how the generated JS code looks like so you can call it correctly.
10
-
11
-
> Remember you can use the [Fable REPL](https://fable.io/repl/) to easily check the generated JS for your F# code!
12
-
13
-
## Name mangling
14
-
15
-
Because JS doesn't support overloading or multiple modules in a single file, Fable needs to mangle the name of some members to avoid clashes, which makes it difficult to call such functions or values from JS. However, there're some cases where Fable guarantees names won't change in order to improve interop:
16
-
17
-
- Record fields
18
-
- Interface and abstract members
19
-
- Functions and values in the root module
20
-
21
-
What's a root module? Because F# accepts multiple modules in the same file, we consider the root module the first one containing actual members and is not nested by any other.
22
-
23
-
```fsharp
24
-
// It doesn't matter if we have a long namespace, Fable only starts
25
-
// counting when it finds actual code
26
-
module A.Long.Namespace.RootModule
27
-
28
-
// The name of this function will be the same in JS
29
-
let add (x: int) (y: int) = x + y
30
-
31
-
module Nested =
32
-
// This will be prefixed with the name of the module in JS
33
-
let add (x: int) (y: int) = x * y
34
-
```
35
-
36
-
In F# it's possible to have more than one root module in a single file, in that case everything will be mangled. You should avoid this pattern if you want to expose code to JS:
37
-
38
-
```fsharp
39
-
namespace SharedNamespace
40
-
41
-
// Here both functions will be mangled
42
-
// to prevent name conflicts
43
-
module Foo =
44
-
let add x y = x + y
45
-
46
-
module Bar =
47
-
let add x y = x * y
48
-
```
49
-
50
-
### Custom behaviour
51
-
52
-
In some cases, it's possible to change the default behavior towards name mangling:
53
-
54
-
- If you want to have all members attached to a class (as in standard JS classes) and not-mangled use the `AttachMembers` attribute. But be aware **overloads won't work** in this case.
55
-
- If you are not planning to use an interface to interact with JS and want to have overloaded members, you can decorate the interface declaration with the `Mangle` attribute. Note: Interfaces coming from .NET BCL (like System.Collections.IEnumerator) are mangled by default.
56
-
57
-
## Common types and objects
58
-
59
-
Some F#/.NET types have [counterparts in JS](/../dotnet/compatibility.html). Fable takes advantage of this to compile to native types that are more performant and reduce bundle size. You can also use this to improve interop when exchanging data between F# and JS. The most important common types are:
60
-
61
-
-**Strings and booleans** behave the same in F# and JS.
62
-
-**Chars** are compiled as JS strings of length 1. This is mainly because string indexing in JS gives you another string. But you can use a char as a number with an explicit conversion like `int16 '家'`.
63
-
-**Numeric types** compile to JS numbers, except for `long`, `decimal` and `bigint`.
64
-
-**Arrays** (and `ResizeArray`) compile to JS arrays. _Numeric arrays_ compile to [Typed Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray) in most situations, though this shouldn't make a difference for most common operations like indexing, iterating or mapping. You can disable this behavior with [the `typedArrays` option](https://www.npmjs.com/package/fable-loader#options).
65
-
- Any **IEnumerable** (or `seq`) can be traversed in JS as if it were an [Iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators#Iterables).
66
-
-**DateTime** compiles to JS `Date`.
67
-
-**Regex** compiles to JS `RegExp`.
68
-
- Mutable **dictionaries** (not F# maps) compile to [ES2015 Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map).
69
-
- Mutable **hashsets** (not F# sets) compile to [ES2015 Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).
70
-
71
-
> If the dictionary or hashset requires custom or structural equality, Fable will generate a custom type, but it will share the same properties as JS maps and sets.
72
-
73
-
-**Objects**: As seen above, only record fields and interface members will be attached to objects without name mangling. Take this into account when sending to or receiving an object from JS.
// Fails, this member is not actually attached to the object
107
-
record.FiveTimes();
108
-
109
-
var myClass =createClass(5);
110
-
111
-
// Fails
112
-
myClass.Value;
113
-
114
-
// Ok, this is an interface member
115
-
myClass.Square(); // 25
116
-
```
117
-
118
-
## Functions: automatic uncurrying
119
-
120
-
Fable will automatically uncurry functions in many situations: when they're passed as functions, when set as a record field... So in most cases you can pass them to and from JS as if they were functions without curried arguments.
121
-
122
-
```fsharp
123
-
let execute (f: int->int->int) x y =
124
-
f x y
125
-
```
126
-
127
-
```js
128
-
import { execute } from"./TestFunctions.fs"
129
-
130
-
execute(function (x, y) { return x * y }, 3, 5) // 15
131
-
```
132
-
133
-
> Check [this](https://fsharpforfunandprofit.com/posts/currying/) for more information on function currying in F# (and other functional languages).
134
-
135
-
### Using delegates for disambiguation
136
-
137
-
There are some situations where Fable uncurrying mechanism can get confused, particularly with functions that return other functions. Let's consider the following example:
138
-
139
-
```fsharp
140
-
open Fable.Core.JsInterop
141
-
142
-
let myEffect() =
143
-
printfn "Effect!"
144
-
fun () -> printfn "Cleaning up"
145
-
146
-
// Method from a JS module, expects a function
147
-
// that returns another function for disposing
148
-
let useEffect (effect: unit -> (unit -> unit)): unit =
149
-
importMember "my-js-module"
150
-
151
-
// Fails, Fable thinks this is a 2-arity function
152
-
useEffect myEffect
153
-
```
154
-
155
-
The problem here is the compiler cannot tell `unit -> unit -> unit` apart from `unit -> (unit -> unit)`, it can only see a 2-arity lambda (a function accepting two arguments). This won't be an issue if all your code is in F#, but if you're sending the function to JS as in this case, Fable will incorrectly try to uncurry it causing unexpected results.
156
-
157
-
To disambiguate these cases, you can use [delegates](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/delegates), like `System.Func` which are not curried:
158
-
159
-
```fsharp
160
-
open System
161
-
162
-
// Remove the ambiguity by using a delegate
163
-
let useEffect (effect: Func<unit, (unit -> unit)>): unit =
164
-
importMember "my-js-module"
165
-
166
-
// Works
167
-
useEffect(Func<_,_> myEffect)
168
-
```
169
-
170
-
## Call Fable source code from a JS file
171
-
172
-
Webpack makes it very easy to include files in different programming languages in your project by using loaders. Because in a Fable project we assume you're already using the [fable-loader](https://www.npmjs.com/package/fable-loader), if you have a file like the following:
173
-
174
-
```fsharp
175
-
module HelloFable
176
-
177
-
let sayHelloFable() = "Hello Fable!"
178
-
```
179
-
180
-
Importing and using it from JS is as simple as if it were another JS file:
181
-
182
-
```js
183
-
import { sayHelloFable } from"./HelloFable.fs"
184
-
185
-
console.log(sayHelloFable());
186
-
```
187
-
188
-
### Importing Fable code from Typescript
189
-
190
-
For better or worse, Typescript wants to check your imported modules and because it doesn't know a thing of F#, it will complain if you try to import an .fs file:
191
-
192
-
```ts
193
-
// Typescript will complain because it cannot read the file
194
-
import { sayHelloFable } from"./HelloFable.fs"
195
-
```
196
-
197
-
To appease the Typescript compiler, we need a [declaration file](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html), which also gives us the opportunity to tell Typescript what is actually exported by our Fable code. If you place a `HelloFable.d.ts` file like the following, the import above will work:
If your project is a web app and you're using Webpack, it just takes two lines of code in the Webpack configuration in the `output` section of `module.exports`:
212
-
213
-
```js
214
-
libraryTarget:'var',
215
-
library:'MyFableLib'
216
-
```
217
-
218
-
For instance:
219
-
220
-
```js
221
-
output: {
222
-
path:path.join(__dirname, "./public"),
223
-
filename:"bundle.js",
224
-
libraryTarget:'var',
225
-
library:'MyFableLib'
226
-
},
227
-
```
228
-
229
-
This tells Webpack that we want our Fable code to be available from a global variable named `MyFableLib`. That's it!
230
-
231
-
:::info
232
-
Only the public functions and values in the **last file of the project** will be exposed.
233
-
:::
234
-
235
-
#### Let's try!
236
-
237
-
Let's compile the HelloFable app from above with a webpack.config.js that includes the following:
238
-
239
-
```js
240
-
output: {
241
-
...
242
-
libraryTarget:'var',
243
-
library:'OMGFable'
244
-
}
245
-
```
246
-
247
-
Now let's try this directly in our `index.html` file:
248
-
249
-
```html
250
-
<body>
251
-
<scriptsrc="bundle.js"></script>
252
-
<scripttype="text/JavaScript">
253
-
alert( OMGFable.sayHelloFable() );
254
-
</script>
255
-
</body>
256
-
```
257
-
258
-
Et voilà! We're done! You can find a [full sample here](https://github.com/fable-compiler/fable2-samples/tree/master/interopFableFromJS).
259
-
260
-
### ...from your Node.js app
261
-
262
-
Basically it's the same thing. If you want to see a complete sample using a `commonjs` output instead of `var`, please [check this project](https://github.com/fable-compiler/fable2-samples/tree/master/nodejsbundle). There you'll see that we've added the following lines to the Webpack config:
263
-
264
-
```js
265
-
library:"app",
266
-
libraryTarget:'commonjs'
267
-
```
268
-
269
-
and then you can call your code from JavaScript like this:
270
-
271
-
```js
272
-
let app =require("./App.js");
273
-
```
274
-
275
-
### Learn more about Webpack `libraryTarget`
276
-
277
-
If you want to know what your options are, please consult the [official documentation](https://webpack.js.org/configuration/output/#outputlibrarytarget).
0 commit comments