New website for F# Software Foundation
Website is generated by Fornax.
Modifications are done by pull request, which are built automatically and the preview content is published to an Azure Storage Account with any pull requests going to a subdirectory for the pull request number to help contributors test their changes.
To contribute, first check the projects for static content to migrate. Move the note to "In progress", convert it to an issue, and assign it to yourself so others are aware you are working on it.
- Fork and clone this repository
- Install the .NET Core 3.1 SDK.
- Run
dotnet tool restore
- this will download the static site generatorfornax
. - Edit the website content locally. If using an editor such as VS Code with Ionide or Rider, you'll have syntax highlighting and typechecking as you author the content.
- To build the site locally, use
dotnet fornax build
. - To run the site on a local server, use
dotnet fornax watch
and then browse to http://localhost:8080 to see the content. - When your works is ready to publish, commit the changes and create a pull request from your fork.
Once your work is merged, please close the issue. If the work is too large and you'd like to split it up, add an additional note card with the balance of work.
Fornax is a static site generator, so it generates (checks notes) strings. There are Loaders
that can read data from various sources, put that into SiteContents
and then Generators
that output strings from the SiteContents
record.
Fornax includes a full Domain Specific Language (DSL) for generating HTML markup in a type safe, composable way, but ultimately, a Generator
will call HtmlElement.ToString
and convert it all to a string. Anything else that can create a string can be used as well, including reading a static file of HTML markup created with some other editor. If you have an existing string of HTML content, use the !!
operator to convert it to an HtmlElement
so it can be composed with other markup using the DSL.
For a practical example, suppose you have a layout that defines the site's overall navigation elements, headers, and footers that you want to remain consistent throughout the site. Within those navigation elements, you want each page to have different content. To achieve this, you'll need a generator for the layout that each page can reference so they \render their own content then inject it into the layout's generator that will generate the frame around that content. The result is that each page has its own internal content that is wrapped by a standard layout.
In fact, in index.fsx
, you will see it does exactly this:
- It loads
layout.fsx
, which loads each of the loader modules. It's only loaded once and then cached for each page that calls it. - The loader modules that
layout.fsx
loads each add their content toSiteContent
. For instancegloballoader.fsx
adds aSiteInfo
record with the title and description to theSiteContent
that is available further in the pipeline. - The
generate'
function builds all the markup for this page and the result is anHtmlElement
which is passed toLayout.layout
. It has access to data loaded into `SiteContent by the loaders. - In
layout.fsx
, thelayout
function wraps the content for that page with the headers, navigation elements, and footers, then returns anHtmlElement
. - The
generate
function at the bottom passes theSiteContents
togenerate'
to build thisHtmlElement
(this page's content wrapped in the navigation layout) which is then passed toLayout.render
which callsHtmlElement.ToString
to generate the HTML content for the page. - In
config.fsx
, theconfig
is defined with all theGenerators[]
that are used for the site. It includesindex.fsx
to generateindex.html
.
From a the top down, the pipeline for Fornax looks like this:
- Read
config.fsx
to find generator scripts. - Load each generator script file from the config.
- Each generator script references
layout.fsx
. - The
layout.fsx
module loads the various loaders to populateSiteContent
. - The
generate
function is called on each generator script. - In each script's
generate
function a. Thegenerate'
function generates content. a. The content is passed tolayout
function inlayout.fsx
a. The content wrapped in a layout is passed toLayout.render
which finally converts it all to a string of HTML. - The string of HTML is written to the file specified in
config.fsx
for that generator.
In some cases, it's necessary to use Fornax's F# DSL to generate content, such as data driven content or the overall layout to wrap content. This gives a great deal of flexibility, but also adds complexity since contributors must understand F#, the Fornax HTML DSL, and the website's CSS styles. Whenever the content is very static and follows a standard format, simple markdown can be used for the content.
The postloader.fsx
provides an example as a way to load content from markdown files. Some minimal metadata can be provided at the top of each markdown file to use in content generation before rendering HTML from the markdown. If people will frequently be contributing static content, please consider supporting loading this content from markdown files.
Thank you for your contributions, and please reach out if you have any questions or suggestions!