A repo to explore using FSharp.Compiler.Portacode for hot-reloading Giraffe Views
- Install the
giraffe-reload
global tooldotnet tool install -g Giraffe.HotReload.Cli::*
- Invoke the
giraffe-reload
tool in watch-mode on your projectgiraffe-reload --watch --webhook:http://localhost:5000/update path/to/project.fsproj
- If your project runs on another IP or port, change it as appropriate.
- The
/update
route is default right now, but can be configured - You can run the tool from your project directory and it'll discover your fsproj as well.
- open the
Giraffe.HotReload
namespace and use the newUseGiraffeWithHotReload
extension method onIApplicationBuilder
. You'll likely want to#if
def this around theDEBUG
define, so that you don't allow hot-reloading in production. - Use this extension method instead of the normal
UseGiraffe
one. Check the sample project for an example usage. - Launch your project as usual, likely via
dotnet run
- Make edits to your giraffe handler
- Give your webroot folder and all files a name in your fsproj and tell msbuild it will be watchable
<ItemGroup>
<WebRoot Include="$(MSBuildProjectDirectory)/wwwroot/**/*" />
<Watch Include="@(WebRoot)" />
</ItemGroup>
- Tell msbuild to copy these files on builds in your fsproj
<Content Include="@(WebRoot)">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- Make an msbuild task for copying these files in your fsproj
<Target Name="CopyWebroot">
<Copy
SourceFiles="@(WebRoot)"
DestinationFiles="@(WebRoot->'$(OutDir)\wwwroot\%(RecursiveDir)%(Filename)%(Extension)')"
/>
</Target>
- Teach ASP.NET Core where your webroot is
let webroot = "wwwroot"
WebHostBuilder()
(...omitted...)
.UseWebRoot(webroot)
(...omitted...)
- Start a
dotnet watch
task with this target. Everytime you save, msbuild will now copy this files to the output
$ dotnet watch msbuild /t:CopyWebroot
By default this will watch your webroot folder. If you want to include additional folders, you can add them to StaticFileProviders
in the Settings.
The current settings that can be configured are listed below.
type Settings = {
/// The route where the hot reload tool should post.
UpdateRoute : string
/// The route for the websocket that will refresh the browser.
WebsocketRefreshRoute : string
/// The name of the Giraffe HttpHandler member that will be searched for
WebAppMemberName : string
/// Static file providers for anything not under webroot
StaticFileProviders : IFileProvider list
}
with
static member Default = {
UpdateRoute = "/update"
WebsocketRefreshRoute = "/ws"
WebAppMemberName = "webApp"
StaticFileProviders = []
}
You can pass these settings to the UseGiraffeWithHotReload
as a second argument.
let settings = { Settings.Default with UpdateRoute = "/PleaseSendCodeHere" }
app.UseGiraffeWithHotReload(webApp,settings)
The code looks for either a static HttpHandler value or a HttpHandler-generating-function called webApp
(or another name that you provide in Settings) in your main application code.
If the value webApp: HttpHandler
(or another name that you provide in Settings) is found, that value is passed into the HotReload middleware immediately.
If a member of the form webApp: 'dep1 -> ... -> 'depN -> HttpHandler
(or another name that you provide in Settings) is found, the parameters are resolved from the HttpContext.RequestServices
service locator on your behalf, passed into the function to get the HttpHandler
, and then that value is passed into the HotReload middleware.
Log messages for both of these traversal paths will be written to the ASP.Net Core Logging Infrastructure under the Giraffe.HotReload.LiveUpdate.HotReloadGiraffeMiddleware
logger name,
Check the samples/ReloadSample/Program.fs
file for an example of a function-generating webApp
.
WARNING: If your function includes generic parameters it will not work at this time.
The new middleware exposes a websocket-friendly connection at localhost:5000/ws
, and if you include a simple script like in your root page template every page in your app will support hot-refresh.
The important part is the onmessage
handler, where the page is refreshed when a message is sent.
var socket = new WebSocket('ws://localhost:5000/ws');
socket.onopen = function(event) {
console.log('Connection opened');
}
socket.onmessage = function(event) {
console.log(event.data);
window.location.reload();
return false;
}
socket.onclose = function(event) {
console.log("connection closed");
}
socket.onerror = function(error) {
console.log("error", error);
}
To test the current set up:
cd
to thesamples/ReloadSample
directory- run the
Giraffe.HotReload.Cli
project from that working directory to generate the project optionsdotnet run --project ../../src/Giraffe.HotReload.Cli/Giraffe.HotReload.Cli.fsproj -- --eval @out.args
- you may need to ensure that file exists first
- run the
Giraffe.HotReload.Cli
project from that working directory in watch modedotnet run --project ../../src/Giraffe.HotReload.Cli/Giraffe.HotReload.Cli.fsproj -- --watch --webhook:http://localhost:5000/update @out.args
- run the
ReloadSample
projectdotnet run
- make changes to the
ReloadSample
project
MacOS/Linux | Windows |
---|---|
Stable | Prerelease |
---|---|
Make sure the following requirements are installed in your system:
- dotnet SDK 2.0 or higher
- Mono if you're on Linux or macOS.
> build.cmd // on windows
$ ./build.sh // on unix
CONFIGURATION
will set the configuration of the dotnet commands. If not set it will default to Release.CONFIGURATION=Debug ./build.sh
will result in things likedotnet build -c Debug
GITHUB_TOKEN
will be used to upload release notes and nuget packages to github.- Be sure to set this before releasing
The WatchTests
target will use dotnet-watch to watch for changes in your lib or tests and re-run your tests on all TargetFrameworks
./build.sh WatchTests
git add .
git commit -m "Scaffold"
git remote add origin origin https://github.com/user/MyCoolNewLib.git
git push -u origin master
paket config add-token "https://www.nuget.org" 4003d786-cc37-4004-bfdf-c4f3e8ef9b3a
-
- You can then set the
GITHUB_TOKEN
to upload release notes and artifacts to github - Otherwise it will fallback to username/password
- You can then set the
-
Then update the
RELEASE_NOTES.md
with a new version, date, and release notes ReleaseNotesHelper
#### 0.2.0 - 2017-04-20
* FEATURE: Does cool stuff!
* BUGFIX: Fixes that silly oversight
- You can then use the
Release
target. This will:- make a commit bumping the version:
Bump version to 0.2.0
and add the release notes to the commit - publish the package to nuget
- push a git tag
- make a commit bumping the version:
./build.sh Release
To format code run the following target
./build.sh FormatCode
This uses Fantomas to do code formatting. Please report code formatting bugs to that repository.