A repo to explore using FSharp.Compiler.Portacode for hot-reloading Giraffe Views
- Install the
giraffe-reloadglobal tooldotnet tool install -g Giraffe.HotReload.Cli::*
- Invoke the
giraffe-reloadtool 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
/updateroute 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.HotReloadnamespace and use the newUseGiraffeWithHotReloadextension method onIApplicationBuilder. You'll likely want to#ifdef this around theDEBUGdefine, so that you don't allow hot-reloading in production. - Use this extension method instead of the normal
UseGiraffeone. 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 watchtask with this target. Everytime you save, msbuild will now copy this files to the output
$ dotnet watch msbuild /t:CopyWebrootBy 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:
cdto thesamples/ReloadSampledirectory- run the
Giraffe.HotReload.Cliproject 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.Cliproject 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
ReloadSampleprojectdotnet run
- make changes to the
ReloadSampleproject
| 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
CONFIGURATIONwill set the configuration of the dotnet commands. If not set it will default to Release.CONFIGURATION=Debug ./build.shwill result in things likedotnet build -c Debug
GITHUB_TOKENwill 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_TOKENto upload release notes and artifacts to github - Otherwise it will fallback to username/password
- You can then set the
-
Then update the
RELEASE_NOTES.mdwith 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
Releasetarget. This will:- make a commit bumping the version:
Bump version to 0.2.0and 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.