diff --git a/docs/components/component-elmish.md b/docs/components/component-elmish.md index 11c6f00f7..063389c45 100644 --- a/docs/components/component-elmish.md +++ b/docs/components/component-elmish.md @@ -4,7 +4,7 @@ Elmish is a library for building single page applications in F#, following the [model-view-update](https://guide.elm-lang.org/architecture/) architecture made famous by [Elm](http://elm-lang.org). -> The following diagram is a simplified, high-level view of the MVU pattern. `Model` in this case refers to your application's state, with `Update` and `View` the two functions that handle the flow of messaging. If you wish to read more, we also recommend reading the excellent [Elmish Book](https://zaid-ajaj.github.io/the-elmish-book/#/chapters/elm/the-architecture). +> The following diagram is a simplified, high-level view of the MVU pattern. `Model` in this case refers to your application's state, with `Update` and `View` as the two functions that handle the flow of messaging. If you wish to read more, we also recommend reading the excellent [Elmish Book](https://zaid-ajaj.github.io/the-elmish-book/#/chapters/elm/the-architecture). ```mermaid stateDiagram-v2 @@ -14,7 +14,7 @@ stateDiagram-v2 ``` ## How does Elmish integrate with SAFE? -Elmish is the library used to build the front-end application in SAFE and that application is compiled to JavaScript by [Fable](component-fable.md) to run in the browser. The [SAFE Stack template](../template-overview.md) comes pre-bundled with the [Elmish React](https://elmish.github.io/react/) module, which (as the name suggests) uses the [React](https://reactjs.org/) library to handle the heavy lifting of modifyng the DOM in an efficient way. This allow us to use the pure functional style of the MVU pattern whilst still retaining the ability to have a highly performant user interface. +Elmish is the library used to build the front-end application in SAFE and that application is compiled to JavaScript by [Fable](component-fable.md) to run in the browser. The [SAFE Stack template](../template-overview.md) comes pre-bundled with the [Elmish React](https://elmish.github.io/react/) module, which (as the name suggests) uses the [React](https://reactjs.org/) library to handle the heavy lifting of modifying the DOM in an efficient way. This allows us to use the pure functional style of the MVU pattern whilst still retaining the ability to have a highly performant user interface. Because Elmish works alongside React, it is possible to use the vast number of available React components from the JavaScript ecosystem within our Elmish applications. diff --git a/docs/components/component-saturn.md b/docs/components/component-saturn.md index bdb553b44..000ce7d74 100644 --- a/docs/components/component-saturn.md +++ b/docs/components/component-saturn.md @@ -15,7 +15,7 @@ Saturn provides the ability to drive your SAFE applications from the server. It * Hosting of your client-side assets, such as HTML, CSS and any JavaScript files generated by Fable. * Other cross cutting concerns e.g. authentication etc. -It also integrates with SAFE to allow seamless sharing of types and functions, since Fable will convert most F# into JavaScript. In addition, you can seamless transport data between client and server using either the Fable.JSON or Fable.Remoting libraries, both of which have support for Saturn. You can read more about this [here](../features/feature-clientserver.md). +It also integrates with SAFE to allow seamless sharing of types and functions, since Fable will convert most F# into JavaScript. In addition, you can seamlessly transport data between client and server using either the Fable.JSON or Fable.Remoting libraries, both of which have support for Saturn. You can read more about this [here](../features/feature-clientserver.md). ```mermaid flowchart TB diff --git a/docs/faq/faq-troubleshooting.md b/docs/faq/faq-troubleshooting.md index 11aba7420..958eab841 100644 --- a/docs/faq/faq-troubleshooting.md +++ b/docs/faq/faq-troubleshooting.md @@ -2,7 +2,7 @@ You may receive an error when trying to run the app, e.g. the current version might require `{"node":"~18 || ~20","npm":"~9 || ~10"}` but your locally installed versions are different. Ideally we'd like to install different versions side-by-side, which we can do using [Node Version Manager](https://www.freecodecamp.org/news/node-version-manager-nvm-install-guide/). -Once NVM is installed, identify the version of Node that you'd like to install by checking this [matrix](https://nodejs.org/en/download/releases). For our example here we can identify version `20.10.0` as satifying both the Node and npm version requirements. To install this version for the current project run: +Once NVM is installed, identify the version of Node that you'd like to install by checking this [matrix](https://nodejs.org/en/download/releases). For our example here we can identify version `20.10.0` as satisfying both the Node and npm version requirements. To install this version for the current project run: ``` nvm install 20.10.0 @@ -31,7 +31,7 @@ Whilst these messages can be safely ignored, you can eliminate them by installin ### Node Process does not stop after stopping the VS Code debugger VS Code does not kill the Fable process when you stop the debugger, leaving it running as a "zombie". In such a case, you will have to explicitly kill the process otherwise it will hold onto -port 8080 and prevent you starting new instances. This should be easily doable by sending Ctrl+C in the Terminal window in VS Code for `Watch Client` task. Tracked [here](https://github.com/SAFE-Stack/SAFE-template/issues/191). +port 8080 and prevent you from starting new instances. This should be easily doable by sending Ctrl+C in the Terminal window in VS Code for `Watch Client` task. Tracked [here](https://github.com/SAFE-Stack/SAFE-template/issues/191).
@@ -50,7 +50,7 @@ WARNING in entrypoint size limit: The following entrypoint(s) combined asset siz We're striving to optimise the bundle size, however with a number of different options and dependencies it's not that easy to stay below the Webpack recommended limit. -To minimize the bundle size in your project you can try restricting browser compatibility by modifying the [Babel Preset targets for Browserslist](https://babeljs.io/docs/en/babel-preset-env#targets) and thus using less polyfills. +To minimize the bundle size in your project, you can try restricting browser compatibility by modifying the [Babel Preset targets for Browserslist](https://babeljs.io/docs/en/babel-preset-env#targets) and thus using fewer polyfills. For more info, see this [issue](https://github.com/SAFE-Stack/SAFE-template/issues/185). diff --git a/docs/features/feature-azurefunctions.md b/docs/features/feature-azurefunctions.md index 5806be547..bf46fd98c 100644 --- a/docs/features/feature-azurefunctions.md +++ b/docs/features/feature-azurefunctions.md @@ -17,7 +17,7 @@ For SAFE apps we see various use cases for FAAS: The [Azure Portal](https://portal.azure.com) allows you to create and edit Functions and their source code via an online editor. -For a short test go to the portal, click the "New" button and search for "Function App". Click through the wizard to create a new Function App. Open the app when it's created and add a new function. Pick "Timer" as scenario and F# as language. +For a short test go to the portal, click the "New" button and search for "Function App". Click through the wizard to create a new Function App. Open the app when it's created and add a new function. Pick "Timer" as the scenario and F# as the language. Replace the contents of function.json with: @@ -50,7 +50,7 @@ and replace the run.fsx with the following F# code: Now observe the logs to see that the function runs every minute and outputs the message about the meetup duration. -While it seems very convenient, the online editor should only be used for testing and prototyping. In SAFE-Stack you usually benefit from reusing your domain model at various places [see Client/Server](feature-clientserver.md) - so we recommend to use "precompiled Azure Functions" as described below. +While it seems very convenient, the online editor should only be used for testing and prototyping. In SAFE-Stack you usually benefit from reusing your domain model at various places [see Client/Server](feature-clientserver.md) - so we recommend using "precompiled Azure Functions" as described below. ## Deployment In SAFE-Stack scenarios we recommend all deployments should be automated. Here, we discuss two options for deploying your functions apps into Azure. @@ -68,4 +68,4 @@ In the case of a CI server etc., you will need to install the Functions Core Too ### HTTPS Upload Since Azure Functions sits on top of Azure App Service, the same mechanisms for deployment there also exist here. In this case, you can use the exact same HTTPS upload capabilities of the App Service to upload a zip of your functions app into your Functions app. The standard SAFE Template can generate this for you for the core SAFE application as part of the FAKE script; the exact same mechanism can be utilised for your functions app. -As per the standard App Service, HTTPS upload uses a user/pass supplied in the header of the zip which is PUT into the functions app. This user / pass can be taken from the App Service in the Azure Portal directly, or extracted during deployment of your ARM template (as per the FAKE script does for the App Service). \ No newline at end of file +As per the standard App Service, HTTPS upload uses a user/pass supplied in the header of the zip which is PUT into the functions app. This user / pass can be taken from the App Service in the Azure Portal directly, or extracted during the deployment of your ARM template (as per the FAKE script does for the App Service). diff --git a/docs/features/feature-clientserver-basics.md b/docs/features/feature-clientserver-basics.md index f8e092e61..4aef7dc47 100644 --- a/docs/features/feature-clientserver-basics.md +++ b/docs/features/feature-clientserver-basics.md @@ -22,7 +22,7 @@ Reference this project from your server project. You can now reference those typ Finally, reference this project in your client project (as above). You can now reference those types on the client; Fable will automatically convert your F# types into JavaScript in the background. ## Sharing Behaviour -You can also share behaviour using the same mechanism at that for sharing types. This is extremely useful for e.g shared validation or business logic that needs to occur on both client and server. +You can also share behaviour using the same mechanism as for sharing types. This is extremely useful for e.g shared validation or business logic that needs to occur on both client and server. Fable will translate your functions into native JavaScript, and will even translate many calls to the .NET base class library into corresponding JavaScript! This allows you to compile your domain model and domain logic to many many different targets including: @@ -35,4 +35,4 @@ Fable will translate your functions into native JavaScript, and will even transl You can read more about this [on the Fable website](http://fable.io/docs/compatibility.html). ## Conditional sharing -When sharing assets between client and server, you may wish to have different implementations for the "client" and "server" sides. For example, if the client-side version of a function should call an NPM package but the server-side version should use a NuGet package. This is a more advanced scenario where you may require different implementations for the JS and .NET version of code. In such situations, you can use `#IF` directives to conditionally compile code for either platform - see the [Fable](https://fable.io/) website for more information. +When sharing assets between client and server, you may wish to have different implementations for the "client" and "server" sides. For example, if the client-side version of a function should call an NPM package but the server-side version should use a NuGet package. This is a more advanced scenario where you may require different implementations for the JS and .NET versions of code. In such situations, you can use `#IF` directives to conditionally compile code for either platform - see the [Fable](https://fable.io/) website for more information. diff --git a/docs/features/feature-clientserver-bridge.md b/docs/features/feature-clientserver-bridge.md index 7295d6d7f..31d13e4cd 100644 --- a/docs/features/feature-clientserver-bridge.md +++ b/docs/features/feature-clientserver-bridge.md @@ -47,24 +47,24 @@ let update clientDispatch msg state = state, Cmd.none ``` -The above example mimics what would have been a `GET` request to the server to get user data from database. However, now the client sends a fire-and-forget message to the server to load users, and at some point the server messages the current client back with the results. Notice that the server could have decided to do other things than just messaging the client back: for example, it could have broadcasted the same message to other clients updating their local state of the users. +The above example mimics what would have been a `GET` request to the server to get user data from a database. However, now the client sends a fire-and-forget message to the server to load users, and at some point the server messages the current client back with the results. Notice that the server could have decided to do other things than just messaging the client back: for example, it could have broadcasted the same message to other clients updating their local state of the users. ## When to use Elmish.Bridge There are many scenarios where it makes sense to use Elmish.Bridge: * Chat-like applications with many connected users through many channels * Syncing price data in real-time while viewing ticket prices -* Multiplayer games that need real-time update of game states +* Multiplayer games that need real-time updates of game states * Other applications of web sockets through an Elmish model ## Things to consider -The biggest distinction between using this and "raw" Saturn is that your web server becomes a stateful service. This introduces several differences for application design. +The biggest distinction between using this and "raw" Saturn is that your web server becomes a stateful service. This introduces several differences in application design. -1. The server state has a lifespan equal to the that of the process under which the server instance is running. This means if the server application restarts then the server state will be reset. +1. The server state has a lifespan equal to that of the process under which the server instance is running. This means if the server application restarts then the server state will be reset. 2. The server state is *local to the server instance*. This means that if you run multiple web servers, they won't be sharing the same server state by default. -As of now there is no built-in persistence for the state, but you can implement this yourself using any number of persistance layers such as Redis Cache, Azure Tables or Blobs etc. +As of now there is no built-in persistence for the state, but you can implement this yourself using any number of persistence layers such as Redis Cache, Azure Tables or Blobs etc. In addition Elmish.Bridge does not use standard HTTP verbs for communication, but rather websockets. Therefore, it is not a suitable technology for an open web server that can serve requests from other sources than Elmish.Bridge clients. diff --git a/docs/features/feature-clientserver-remoting.md b/docs/features/feature-clientserver-remoting.md index 3813df362..85c6c88f4 100644 --- a/docs/features/feature-clientserver-remoting.md +++ b/docs/features/feature-clientserver-remoting.md @@ -47,4 +47,4 @@ async { } ``` -Notice here, there is no need to configure routes or JSON serialization, worry about HTTP verbs, or even involve yourself with the Giraffe pipeline. If you open your browser network tab, you can easily [inspect](https://zaid-ajaj.github.io/Fable.Remoting/#/advanced/raw-http-communication) what remoting is doing behind the scenes. \ No newline at end of file +Notice here, that there is no need to configure routes or JSON serialization, worry about HTTP verbs, or even involve yourself with the Giraffe pipeline. If you open your browser network tab, you can easily [inspect](https://zaid-ajaj.github.io/Fable.Remoting/#/advanced/raw-http-communication) what remoting is doing behind the scenes. diff --git a/docs/features/feature-clientserver-serialization.md b/docs/features/feature-clientserver-serialization.md index 5528bc9ea..65a965e7b 100644 --- a/docs/features/feature-clientserver-serialization.md +++ b/docs/features/feature-clientserver-serialization.md @@ -3,7 +3,7 @@ When using basic HTTP communication between the client and server, you'll need to consider how to deserialize data from JSON to F# types. -In order to guarantee that the serialization / deserialization routines between client and server are compatible, you should replace the JSON converter in Giraffe / Saturn with the [Thoth](https://mangelmaxime.github.io/Thoth/index.html) library's serializer. This is the same library as that used in Fable for deserialization, and so will work seamlessly together. +In order to guarantee that the serialization / deserialization routines between client and server are compatible, you should replace the JSON converter in Giraffe / Saturn with the [Thoth](https://mangelmaxime.github.io/Thoth/index.html) library's serializer. This is the same library as that used in Fable for deserialization, and so they will work seamlessly together. ```fsharp let configureSerialization (services:IServiceCollection) = @@ -22,7 +22,7 @@ type Customer = ``` ### Automatic Decoders -Automatic decoders are the quickest and easier way to deserialize data. It works by Thoth trying to decode JSON automatically from a raw string to an F# type using automatic mapping rules. In the sample below, we fetch data from the `/api/customers` endpoint and have Thoth create a strongly-typed Decoder for a `Customer` array. +Automatic decoders are the quickest and easiest way to deserialize data. It works by Thoth trying to decode JSON automatically from a raw string to an F# type using automatic mapping rules. In the sample below, we fetch data from the `/api/customers` endpoint and have Thoth create a strongly-typed Decoder for a `Customer` array. ```fsharp fetchAs "/api/customers" (Decode.Auto.generateDecoder()) [] @@ -49,7 +49,7 @@ Notice how the decoder is bound to a single Customer, and not an array. This way Manual decoders give you total control over how you rehydrate an object from JSON. Use them when: * The JSON does not directly map 1:1 with your F# types -* You want flexibility to evolve JSON and F# types independently +* You want the flexibility to evolve JSON and F# types independently * You are calling an external service and need fine-grained control over the deserialization process * You are using F# on the client and another language on the server @@ -69,4 +69,4 @@ You can now replace the automatically generated decoder from earlier. You can al Decode.fromString customerDecoder """{ "id": 67, "customerName": "Joe Bloggs" }""" ``` -If decoding fails on any field, an error case will be returned. \ No newline at end of file +If decoding fails on any field, an error case will be returned. diff --git a/docs/features/feature-hmr.md b/docs/features/feature-hmr.md index 499ffeb36..b7fab7bca 100644 --- a/docs/features/feature-hmr.md +++ b/docs/features/feature-hmr.md @@ -1,7 +1,7 @@ -Hot Module Replacement (HMR) allows to update the UI of an application while it is running, without a full reload. In SAFE stack apps, this can dramatically speed up the development for web and mobile GUIs, since there is no need to "stop" and "reload" an application. Instead, you can make changes to your views and have them immediately update in the browser, without the need to restart the application. +Hot Module Replacement (HMR) allows the update of the UI of an application while it is running, without a full reload. In SAFE stack apps, this can dramatically speed up the development of web and mobile GUIs, since there is no need to "stop" and "reload" an application. Instead, you can make changes to your views and have them immediately update in the browser, without the need to restart the application. ## How does it work? -In case of web development, the [Vite](https://vitejs.dev/) development server will automatically refresh the changed parts of your [elmish](https://github.com/elmish/elmish) views whenever you save a file. Alternatively, in the case of mobile app development, this is achieved through [React Native](https://facebook.github.io/react-native/)'s own bundler. +In the case of web development, the [Vite](https://vitejs.dev/) development server will automatically refresh the changed parts of your [elmish](https://github.com/elmish/elmish) views whenever you save a file. Alternatively, in the case of mobile app development, this is achieved through [React Native](https://facebook.github.io/react-native/)'s own bundler. ## Why does it work so well with SAFE? Since SAFE uses the Model-View-Update architecture with immutable models, the application state only changes when a message is processed; this fits the HMR model very nicely. Here's an example of HMR in action to change the input of a textbox to automatically convert the input to upper case. diff --git a/docs/index.md b/docs/index.md index 655a1aafd..d4a4d3834 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,4 @@ -Welcome to the SAFE documentation site! This site contains all the documentation you'll need to quickly starting creating SAFE apps in F#. +Welcome to the SAFE documentation site! This site contains all the documentation you'll need to quickly start creating SAFE apps in F#. If you've not heard of SAFE before, please feel free to start with the [introduction](intro.md). Alternatively, you can immediately try out the [quickstart](quickstart.md) guide and tutorial, or simply browse through the documentation. @@ -8,4 +8,4 @@ We hope you enjoy using SAFE as much as we do! The SAFE team :) -![](img/safe-logo.png) \ No newline at end of file +![](img/safe-logo.png) diff --git a/docs/learning.md b/docs/learning.md index c5d8ae7a8..d6d4dda0c 100644 --- a/docs/learning.md +++ b/docs/learning.md @@ -29,7 +29,7 @@ This repository shows how to use Azure services to implement a SAFE application This application is a real-time chat application built on SAFE that uses the [AKKA framework](https://getakka.net/) to manage actors that represent chat users, including Akka Streams and the Akkling F# library. ### [SAFE Nightwatch](https://github.com/SAFE-Stack/SAFE-Nightwatch) -This application is a sample mobile application using the [React Native](https://facebook.github.io/react-native/) library, built on top of the SAFE stack. React Native permits a very similar programming when writing SAFE applications as browser applications, so the experience should be very familiar to you. +This application is a sample mobile application using the [React Native](https://facebook.github.io/react-native/) library, built on top of the SAFE stack. React Native permits very similar programming when writing SAFE applications as browser applications, so the experience should be very familiar to you. ## Videos diff --git a/docs/recipes/build/docker-image.md b/docs/recipes/build/docker-image.md index 537e02f6d..55995d90c 100644 --- a/docs/recipes/build/docker-image.md +++ b/docs/recipes/build/docker-image.md @@ -1,6 +1,6 @@ # How do I build with docker? -Using [Docker](https://www.docker.com/) makes it possible to deploy your application as a docker container or release an image on docker hub. This recipe walks you through creating a `Dockerfile` and automating the build and test process with [Docker Hub](https://hub.docker.com/). +Using [Docker](https://www.docker.com/) makes it possible to deploy your application as a docker container or release an image on the Docker hub. This recipe walks you through creating a `Dockerfile` and automating the build and test process with [Docker Hub](https://hub.docker.com/). #### 1. Create a .dockerignore file @@ -55,7 +55,7 @@ This uses [multistage builds](https://docs.docker.com/develop/develop-images/mul 1. Build the image `docker build -t my-safe-app .` 2. Run the container `docker run -it -p 8080:8080 my-safe-app` -3. Open the page in browser at [http://localhost:8080](http://localhost:8080) +3. Open the page in a browser at [http://localhost:8080](http://localhost:8080) @@ -86,7 +86,7 @@ To run the tests execute the command `docker-compose -f docker-compose.server.te > Not recommended for most applications -If you often build with docker locally, you may wish to make the build faster by optimising the Dockerfile for caching. For example, it is not necessary to download all paket and npm dependencies on every build unless there have been changes to the dependencies. +If you often build with Docker locally, you may wish to make the build faster by optimising the Dockerfile for caching. For example, it is not necessary to download all Paket and NPM dependencies on every build unless there have been changes to the dependencies. Furthermore, the client and server can be built in separate build stages so that they are cached independently. Enable [Docker BuildKit](https://docs.docker.com/develop/develop-images/build_enhancements/) to build them concurrently. diff --git a/docs/recipes/build/remove-fake.md b/docs/recipes/build/remove-fake.md index 1533b7159..8d8f1f724 100644 --- a/docs/recipes/build/remove-fake.md +++ b/docs/recipes/build/remove-fake.md @@ -1,7 +1,7 @@ # How do I remove the use of FAKE? [FAKE](https://fake.build/) is a tool for build automation. The standard SAFE template comes with a [ready-made build project](../../template-safe-commands.md) at the root of the solution that provides support for many common SAFE tasks. -If you would prefer not to use FAKE, you can of course simply ignore it, but this recipes shows how to completely remove it from your repository. It is important to note that having removed FAKE, you will have to follow a more manual approach to each of these processes. This recipe will only include instructions on how to run the application after removing FAKE. +If you would prefer not to use FAKE, you can of course simply ignore it, but this recipe shows how to completely remove it from your repository. It is important to note that having removed FAKE, you will have to follow a more manual approach to each of these processes. This recipe will only include instructions on how to run the application after removing FAKE. > Note that the minimal template does not use FAKE by default, and **this recipe only applies to the standard template**. diff --git a/docs/recipes/client-server/server-errors-on-client.md b/docs/recipes/client-server/server-errors-on-client.md index 3d557029c..584e70780 100644 --- a/docs/recipes/client-server/server-errors-on-client.md +++ b/docs/recipes/client-server/server-errors-on-client.md @@ -4,7 +4,7 @@ SAFE Stack makes it easy to catch and handle exceptions raised by the server on --- #### 1. Update the Model -Update the model to store the error details that we receive from the server. Find the `Model` type in `src/Client/Index.fs` and add it the following `Errors` field: +Update the model to store the error details that we receive from the server. Find the `Model` type in `src/Client/Index.fs` and add the following `Errors` field to it: ```fsharp type Model = @@ -78,4 +78,4 @@ addTodo = } ``` -and when you try to add a todo then you will see the error message from the server. \ No newline at end of file +and when you try to add a todo then you will see the error message from the server. diff --git a/docs/recipes/client-server/share-code.md b/docs/recipes/client-server/share-code.md index b4969f3ca..2f589a8e2 100644 --- a/docs/recipes/client-server/share-code.md +++ b/docs/recipes/client-server/share-code.md @@ -28,8 +28,8 @@ let customerIsValid customer = If at any point you realise you need to use both the `Customer` type and the `customerIsValid` function both in the Client and the Server, all you need to do is to move both of them to `Shared` project. You can either put them in the `Shared.fs` file, or create your own file in the Shared project (eg. `Customer.fs`). After this, you will be able to use both the `Customer` type and the `customerIsValid` function in both the Client and the Server. ## Serialization -SAFE comes out of the box with [Fable.Remoting] or [Thoth] for serialization. These will handle transport of data seamlessly for you. +SAFE comes out of the box with [Fable.Remoting] or [Thoth] for serialization. These will handle the transport of data seamlessly for you. ## Considerations -> Be careful not to place code in `Shared.fs` that depends on a Client or Server-specific dependency. If your code depends on `Fable` for example, in most cases it will not be suitable to place it in Shared, since it can only be used in Client. Similarly, if your types rely on .NET specific types in e.g. the framework class library (FCL), beware. Fable [has built-in mappings](https://fable.io/docs/dotnet/compatibility.html) for popular .NET types e.g. `System.DateTime` and `System.Math`, but you will have to write your own mappers otherwise. \ No newline at end of file +> Be careful not to place code in `Shared.fs` that depends on a Client or Server-specific dependency. If your code depends on `Fable` for example, in most cases it will not be suitable to place it in Shared, since it can only be used in Client. Similarly, if your types rely on .NET specific types in e.g. the framework class library (FCL), beware. Fable [has built-in mappings](https://fable.io/docs/dotnet/compatibility.html) for popular .NET types e.g. `System.DateTime` and `System.Math`, but you will have to write your own mappers otherwise. diff --git a/docs/recipes/developing-and-testing/debug-safe-app.md b/docs/recipes/developing-and-testing/debug-safe-app.md index efed6b4c8..35c972036 100644 --- a/docs/recipes/developing-and-testing/debug-safe-app.md +++ b/docs/recipes/developing-and-testing/debug-safe-app.md @@ -1,7 +1,7 @@ # How do I debug a SAFE app? ## I'm using Visual Studio -In order to debug Server code from Visual Studio, we need set the correct URLs in the project's debug properties. +In order to debug Server code from Visual Studio, we need to set the correct URLs in the project's debug properties. ### Debugging the Server @@ -61,7 +61,7 @@ However, we can still debug it via the magic of source mapping. If you are using The exact instructions will depend on your browser, but essentially it simply involves: * Opening the Developer tools panel (usually by hitting F12). -* Finding the F# file you want to add breakpoints to in the source of the website. +* Find the F# file you want to add breakpoints to in the source of the website. * Add breakpoints to it in your browser inspector. ## I'm using VS Code @@ -89,10 +89,10 @@ The server is now running. You can use the bar at the top of your screen to paus ### Debug the Client 1. Start the Client by running `dotnet fable watch -o output -s --run npx vite` from `/src/Client/`. -2. Open the Command Palettek using `ctrl+shift+p` and run `Debug: Open Link`. +2. Open the Command Palette using `ctrl+shift+p` and run `Debug: Open Link`. 3. When prompted for a url, type `http://localhost:8080/`. This will launch a browser which is pointed at the URL and connect the debugger to it. 4. You can now set breakpoints in your F# code by opening files via the "Loaded Scrips" tab in the debugger; setting breakpoints in files opened from disk does NOT work. > If you find that your breakpoints aren't being hit, try stopping the Client, disconnecting the debugger and re-launching them both. -> To find out more about the VS Code debugger, see [here](https://code.visualstudio.com/docs/editor/debugging). \ No newline at end of file +> To find out more about the VS Code debugger, see [here](https://code.visualstudio.com/docs/editor/debugging). diff --git a/docs/recipes/javascript/import-js-module.md b/docs/recipes/javascript/import-js-module.md index 11451f772..caf8f2e43 100644 --- a/docs/recipes/javascript/import-js-module.md +++ b/docs/recipes/javascript/import-js-module.md @@ -24,7 +24,7 @@ let foo = importDefault "module-name" // F# #### Testing the import -To ensure that the import was successful you can console log the value and you should see the value in the browsers console window which you can get to by right-clicking and selecting the Inspect Element. +To ensure that the import was successful, you can console log the value and you should see the value in the browser's console window, which you can get to by right-clicking and selecting the Inspect Element. ```fsharp Browser.Dom.console.log("imported value", foo) ``` @@ -42,7 +42,7 @@ import React from "react" #### Setup -In some cases components can use the named export syntax. In the below case "module-name" has an object/function/class that is called `bar`. By referncing it below it is brought into the current scope. +In some cases components can use the named export syntax. In the below case "module-name" has an object/function/class that is called `bar`. By referencing it below, it is brought into the current scope. For example, if the module below contained something like: ```javascript export const bar (x,y) => x + y @@ -56,7 +56,7 @@ let bar = import "bar" "module-name" // F# ``` #### Testing the import -To ensure that the import was successful you can console log the value and you should see the value in the browsers console window which you can get to by right-clicking and selecting the Inspect Element. +To ensure that the import was successful, you can console log the value and you should see the value in the browser's console window, which you can get to by right-clicking and selecting the Inspect Element. ```fsharp Browser.Dom.console.log("imported value", bar) ``` @@ -69,7 +69,7 @@ import { useState } from "react" ``` ### Entire module contents -In rare cases you may have to import an entire module's contents and provide an alias in the below case we named it myModule. You can now use dot notation to access anything that is exported from module-name. For example, if the module being imported below includes an export to a function `doAllTheAmazingThings()` you could access it like: +In rare cases you may have to import an entire module's contents and provide an alias. In the below case we named it myModule. You can now use dot notation to access anything that is exported from module-name. For example, if the module being imported below includes an export to a function `doAllTheAmazingThings()` you could access it like: ```javascript myModule.doAllTheAmazingThings() ``` @@ -82,7 +82,7 @@ let myModule = importAll "module-name" // F# #### Testing the import -To ensure that the import was successful you can console log the value and you should see the value in the browsers console window which you can get to by right-clicking and selecting the Inspect Element. +To ensure that the import was successful, you can console log the value and you should see the value in the browser's console window, which you can get to by right-clicking and selecting the Inspect Element. ```fsharp Browser.Dom.console.log("imported value", myModule) ``` diff --git a/docs/recipes/javascript/third-party-react-package.md b/docs/recipes/javascript/third-party-react-package.md index fed547bed..5e2a4f131 100644 --- a/docs/recipes/javascript/third-party-react-package.md +++ b/docs/recipes/javascript/third-party-react-package.md @@ -1,4 +1,4 @@ -To use a third-party React library in a SAFE application, you need to write an F# wrapper around it. There are two ways for doing this - using [Feliz](https://zaid-ajaj.github.io/Feliz/) or using [Fable.React](https://www.nuget.org/packages/Fable.React/). +To use a third-party React library in a SAFE application, you need to write an F# wrapper around it. There are two ways to do this - using [Feliz](https://zaid-ajaj.github.io/Feliz/) or using [Fable.React](https://www.nuget.org/packages/Fable.React/). ## Prerequisites @@ -30,7 +30,7 @@ Feliz.Interop.reactApi.createElement (importDefault "react-d3-speedometer", crea import ReactSpeedometer from "react-d3-speedometer" ``` -The reason for using `importDefault` is the documentation for the component uses a default export "ReactSpeedometer". Please find a list of common import statetments at the end of this recipe +The reason for using `importDefault` is the documentation for the component uses a default export "ReactSpeedometer". Please find a list of common import statements at the end of this recipe As a quick check to ensure that the library is being imported and we have no typos you can `console.log` the following at the top within the view function @@ -112,9 +112,9 @@ With all these in place, you can use the React element in your client like so: open ReactSpeedometer reactSpeedometer [ - Prop.Value 10 // Since Value is already decalred in HTMLAttr you can use Prop.Value to tell the F# compiler its of type Prop and not HTMLAttr + Prop.Value 10 // Since Value is already decalred in HTMLAttr you can use Prop.Value to tell the F# compiler it is of type Prop and not HTMLAttr MaxValue 100 MinValue 0 StartColor "red" ] -``` \ No newline at end of file +``` diff --git a/docs/recipes/package-management/migrate-to-nuget.md b/docs/recipes/package-management/migrate-to-nuget.md index c708bccd8..fb1eafdb1 100644 --- a/docs/recipes/package-management/migrate-to-nuget.md +++ b/docs/recipes/package-management/migrate-to-nuget.md @@ -49,7 +49,7 @@ Azure.Core (1.24) #### 4. Remove remaining paket files -Once you have added all of your dependencies to the relevant `.fsproj` files, you can remove the folowing files and folders from your solution. +Once you have added all of your dependencies to the relevant `.fsproj` files, you can remove the following files and folders from your solution. **Files:** * `paket.lock` @@ -69,4 +69,4 @@ Alternatively, run ```bash dotnet tool uninstall paket ``` -at the root of your solution. \ No newline at end of file +at the root of your solution. diff --git a/docs/recipes/patterns/add-dependency-injection.md b/docs/recipes/patterns/add-dependency-injection.md index b33cf2539..31f823347 100644 --- a/docs/recipes/patterns/add-dependency-injection.md +++ b/docs/recipes/patterns/add-dependency-injection.md @@ -23,7 +23,7 @@ ++ service_config (fun services -> services.AddSingleton()) ``` - > [This section](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0#lifetime-and-registration-options) of the official ASP .NET Core article explain the distinction between different lifetime registrations, such as Singleton and Transient. + > [This section](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0#lifetime-and-registration-options) of the official ASP .NET Core article explains the distinction between different lifetime registrations, such as Singleton and Transient. 3. Ensure that your Fable Remoting API can access the `HttpContext` type by using the `fromContext` builder function. ```diff @@ -51,4 +51,4 @@ ## Further Reading * [Official documentation on DI in ASP .NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0) -* [Archived example PR to update the SAFE Template Todo App to use DI](https://github.com/SAFE-Stack/SAFE-template/pull/466/files) \ No newline at end of file +* [Archived example PR to update the SAFE Template Todo App to use DI](https://github.com/SAFE-Stack/SAFE-template/pull/466/files) diff --git a/docs/recipes/storage/use-sqlprovider-ssdt.md b/docs/recipes/storage/use-sqlprovider-ssdt.md index 645bd1c8a..8c928fabd 100644 --- a/docs/recipes/storage/use-sqlprovider-ssdt.md +++ b/docs/recipes/storage/use-sqlprovider-ssdt.md @@ -108,7 +108,7 @@ type DB = UseOptionTypes = Common.NullableColumnType.OPTION > -//TO RELOAD SCHEMA: 1) uncomment the line below; 2) save; 3) recomment; 4) save again and wait. +//TO RELOAD SCHEMA: 1) uncomment the line below; 2) save; 3) recommend; 4) save again and wait. //DB.GetDataContext().``Design Time Commands``.ClearDatabaseSchemaCache let createContext (connectionString: string) = @@ -136,7 +136,7 @@ let getTodos (db: DB.dataContext) = let addTodo (db: DB.dataContext) (todo: Shared.Todo) = async { - let t = db.Dbo.Todos.Create() + let t = db.Dbo.Todos.Create() // or even better, use strongly typed .``Create(fields)``(...) t.Id <- todo.Id t.Description <- todo.Description t.IsDone <- false @@ -237,4 +237,4 @@ let SsdtPath = __SOURCE_DIRECTORY__ + @"/../../ssdt/SafeTodoDB/bin/Release/SafeT #endif ``` -NOTE: This assumes that your SSDT .sqlproj will be built in Release mode (you can build it manually, or use a FAKE build script to handle this). \ No newline at end of file +NOTE: This assumes that your SSDT .sqlproj will be built in Release mode (you can build it manually, or use a FAKE build script to handle this). diff --git a/docs/recipes/template.md b/docs/recipes/template.md index 20e39d624..df548c15d 100644 --- a/docs/recipes/template.md +++ b/docs/recipes/template.md @@ -13,11 +13,11 @@ Follow the following pattern and headings and the guide below. --- # How Do I < insert task here >? -Start by writing a short introduction of a few sentences. Explain what the recipe is about, and problems it solves. Which technologies does it utilise, and what are the alternatives etc.? +Start by writing a short introduction of a few sentences. Explain what the recipe is about, and the problems it solves. Which technologies does it utilise, and what are the alternatives etc.? Remember to link the first instance of any technology to the appropriate docs elsewhere within this site, or to the homepage of the technology (or both!). ### Step-by-step Guide -Write clear instructions on how to get to the desired outcome. The step-by-step instructions should be clear, short, easy to understand with possibly a use case and an example at the end if suitable. +Write clear instructions on how to get to the desired outcome. The step-by-step instructions should be clear, short, easy to understand, with possibly a use case and an example at the end if suitable. If you have a step in this section that is relevant to some other recipe we have here in the docs, such as adding a package to a SAFE app, link it to that relevant page. diff --git a/docs/recipes/ui/add-routing-with-separate-models.md b/docs/recipes/ui/add-routing-with-separate-models.md index 5198e06c2..26f14a507 100644 --- a/docs/recipes/ui/add-routing-with-separate-models.md +++ b/docs/recipes/ui/add-routing-with-separate-models.md @@ -1,4 +1,4 @@ -# How do I add routing to a SAFE app with separate model for every page? +# How do I add routing to a SAFE app with a separate model for every page? If your application has multiple separate components, there is no need to have one big, complex model that manages all the state for all components. In this recipe we separate the information of the todo list out of the main `Model`, and give the todo list application its own route. We also add a "Page not found" page. @@ -309,4 +309,4 @@ The routing should work now. Try navigating to [localhost:8080](http://localhost !!! info "# sign" You might be surprised to see the hash sign as part of the URL. It enables React to react to URL changes without a full page refresh. - There are ways to omit this, but getting this to work properly is outside of the scope of this recipe. \ No newline at end of file + There are ways to omit this, but getting this to work properly is outside of the scope of this recipe. diff --git a/docs/recipes/ui/add-routing.md b/docs/recipes/ui/add-routing.md index 693e91f0c..9c7ec4355 100644 --- a/docs/recipes/ui/add-routing.md +++ b/docs/recipes/ui/add-routing.md @@ -1,6 +1,6 @@ # How do I add routing to a SAFE app with a shared model for all pages? -When building larger apps, you probably want different pages to be accessible through different URLs. In this recipe, we show you how to add routes to different pages to an application, including adding a "page not found" page that is displayed when an unknown URL is entered. +When building larger apps, you probably want different pages to be accessible through different URLs. In this recipe, we show you how to add routes to different pages of an application, including adding a "page not found" page that is displayed when an unknown URL is entered. In this recipe we use the simplest approach to storing states for multiple pages, by creating a single state for the full app. A potential benefit of this approach is that the state of a page is not lost when navigating away from it. You will see how that works at the end of the recipe. @@ -198,4 +198,4 @@ To see how the state is maintained even when navigating away from the page, type ## 10. Adding more pages -Now that you have set up the routing, adding more pages is simple: add a new case to the `Page` type; add a route for this page in the `parseUrl` function; add a function that takes a model and dispatcher to generate your new page, and add a new case to the pattern match inside the `view` function to display the new case. \ No newline at end of file +Now that you have set up the routing, adding more pages is simple: add a new case to the `Page` type; add a route for this page in the `parseUrl` function; add a function that takes a model and dispatcher to generate your new page, and add a new case to the pattern match inside the `view` function to display the new case. diff --git a/docs/recipes/ui/add-style.md b/docs/recipes/ui/add-style.md index 308bbf094..12129ffa3 100644 --- a/docs/recipes/ui/add-style.md +++ b/docs/recipes/ui/add-style.md @@ -18,7 +18,7 @@ The cleanest way to add your own stylesheet is to create a new file e.g. `src/Cl ``` ## Method B: Import without `index.css` -In order for Vite to know that there are styles to be bundled, you must import them into your app. By default this is already configured for `index.css` but if you don't have it set up, not to worry! Follow these steps: +In order for Vite to know that there are styles to be bundled, you must import them into your app. By default this is already configured for `index.css`, but if you don't have it set up, don't worry! Follow these steps: 1. Create your custom css file in `src/Client`, e.g. `custom-style.css` 1. Direct Fable to emit an import for your style file. @@ -29,4 +29,4 @@ In order for Vite to know that there are styles to be bundled, you must import t ``` ## There you have it! -You can now style your app by writing to the `custom-style.css` file. \ No newline at end of file +You can now style your app by writing to the `custom-style.css` file. diff --git a/docs/recipes/ui/add-tailwind.md b/docs/recipes/ui/add-tailwind.md index bf42f9619..b18496b37 100644 --- a/docs/recipes/ui/add-tailwind.md +++ b/docs/recipes/ui/add-tailwind.md @@ -4,7 +4,7 @@ As of SAFE version 5 (released in December 2023) it is included in the template by default so it can be used straight away. -If you are are using the minimal template or if you are upgrading from an old version of SAFE, continue reading for installation instructions. +If you are using the minimal template or if you are upgrading from an old version of SAFE, continue reading for installation instructions. 1. [Add a stylesheet](https://safe-stack.github.io/docs/recipes/ui/add-style/) to the project @@ -56,4 +56,4 @@ If you are are using the minimal template or if you are upgrading from an old ve prop.classes [ "text-red-200" ] prop.text todo.Description ] - ``` \ No newline at end of file + ``` diff --git a/docs/recipes/ui/routing-with-elmish.md b/docs/recipes/ui/routing-with-elmish.md index 3f3a82a26..bf215bf74 100644 --- a/docs/recipes/ui/routing-with-elmish.md +++ b/docs/recipes/ui/routing-with-elmish.md @@ -182,7 +182,7 @@ type Model = ``` ## 6. Initializing the application -Create a function that initializes the app based on an url +Create a function that initializes the app based on a url ```fsharp title="Index.fs" let initFromUrl url = @@ -286,4 +286,4 @@ The routing should work now. Try navigating to [localhost:8080](http://localhost !!! info "# sign" You might be surprised to see the hash sign as part of the URL. It enables React to react to URL changes without a full page refresh. - There are ways to omit this, but getting this to work properly is outside of the scope of this recipe. \ No newline at end of file + There are ways to omit this, but getting this to work properly is outside of the scope of this recipe. diff --git a/docs/safe-from-scratch.md b/docs/safe-from-scratch.md index d16a1246f..11ddf1fbb 100644 --- a/docs/safe-from-scratch.md +++ b/docs/safe-from-scratch.md @@ -24,7 +24,7 @@ dotnet new global.json ``` ## 2. Creating client & server -Now that we have basic core tools installed, we can go about creating a basic F# server and client and get them communicating with one another. +Now that we have basic core tools installed, we can go about creating a basic F# server and client and get them to communicate with one another. ### 2.1 [Create a basic server application](https://github.com/CompositionalIT/safe-from-scratch/commit/90b4fcb6ef948886bb872b3b24d3e5a9d21ecdc0) * Create a folder e.g. `server`. @@ -59,14 +59,14 @@ Now that we have a running HTTP server and the ability to create JS from F#, we ```bash npm install vite ``` -> Vite is a multi-purpose tool used to aid development and packaging of JavaScript applications. +> Vite is a multi-purpose tool used to aid the development and packaging of JavaScript applications. * You can now launch the application. ```bash dotnet fable watch -o output -s --run npx vite ``` This command tells Fable to compile all F# into the `output` folder and then launches Vite, which acts as a local development web server. -* You should see output in your terminal similar to this: +* You should see the output in your terminal similar to this: ![](img/safe-from-scratch-1.png) @@ -96,14 +96,14 @@ Now that we have a (very) basic F# client/server app, we'll now add support for * Add the **react** and **react-dom** packages to your NPM dependencies. * Add the **@vitejs/plugin-react** and **remotedev** packages to your NPM dev dependencies. -* Add react to the list of plugins in your vite config. +* Add react to the list of plugins in your Vite config. ### 3.2 [Add F# React support](https://github.com/CompositionalIT/safe-from-scratch/commit/1409820300e2b09db11003c077ff3e2c82f6d9d2) Now that we have React added to our application, we can add the appropriate F# libraries such as Feliz to start to use React in a typesafe, F#-friendly manner. * Add the **Feliz** NuGet package to the Client project. * Remove the `