Skip to content

Commit

Permalink
✨ Static Site Generation
Browse files Browse the repository at this point in the history
The website uses BlazeKit's SSG capabilities
  • Loading branch information
koeeenig committed Jan 14, 2024
1 parent 842f777 commit 3d28fa2
Show file tree
Hide file tree
Showing 64 changed files with 9,493 additions and 4 deletions.
7 changes: 7 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": []
}
30 changes: 30 additions & 0 deletions BlazeKit.sln
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazeKit.Reactivity", "src\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazeKit.Routing.TestApp", "tests\BlazeKit.Routing.TestApp\BlazeKit.Routing.TestApp.csproj", "{196C36B5-D580-4C2D-BDFF-AFD530255F2E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{98E8084D-CE3D-429D-97B4-A7E41EDF1431}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazeKit.Static", "src\BlazeKit.Static\BlazeKit.Static.csproj", "{B28BBCD3-5A90-447E-8BAD-D78D7517AAD7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazeKit.Site.Static", "src\BlazeKit.Site.Static\BlazeKit.Site.Static.csproj", "{FB0F7358-8500-46DF-B213-3E8980F70856}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazeKit.Website", "src\BlazeKit.Website\BlazeKit.Website.csproj", "{AB38EF74-D766-4A77-A7F2-EA8B74AEA495}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazeKit.Website.Islands", "src\BlazeKit.Website.Islands\BlazeKit.Website.Islands.csproj", "{1C224EA5-8DE8-46E4-A0BE-20AEDB8ACB1D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -77,6 +87,22 @@ Global
{196C36B5-D580-4C2D-BDFF-AFD530255F2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{196C36B5-D580-4C2D-BDFF-AFD530255F2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{196C36B5-D580-4C2D-BDFF-AFD530255F2E}.Release|Any CPU.Build.0 = Release|Any CPU
{B28BBCD3-5A90-447E-8BAD-D78D7517AAD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B28BBCD3-5A90-447E-8BAD-D78D7517AAD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B28BBCD3-5A90-447E-8BAD-D78D7517AAD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B28BBCD3-5A90-447E-8BAD-D78D7517AAD7}.Release|Any CPU.Build.0 = Release|Any CPU
{FB0F7358-8500-46DF-B213-3E8980F70856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB0F7358-8500-46DF-B213-3E8980F70856}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB0F7358-8500-46DF-B213-3E8980F70856}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB0F7358-8500-46DF-B213-3E8980F70856}.Release|Any CPU.Build.0 = Release|Any CPU
{AB38EF74-D766-4A77-A7F2-EA8B74AEA495}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AB38EF74-D766-4A77-A7F2-EA8B74AEA495}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AB38EF74-D766-4A77-A7F2-EA8B74AEA495}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AB38EF74-D766-4A77-A7F2-EA8B74AEA495}.Release|Any CPU.Build.0 = Release|Any CPU
{1C224EA5-8DE8-46E4-A0BE-20AEDB8ACB1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1C224EA5-8DE8-46E4-A0BE-20AEDB8ACB1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C224EA5-8DE8-46E4-A0BE-20AEDB8ACB1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C224EA5-8DE8-46E4-A0BE-20AEDB8ACB1D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -86,6 +112,10 @@ Global
{D466841A-00F1-4E37-BEB3-F7DEAF7EABB1} = {952B525D-297C-4F66-85E2-967C9982771F}
{4D98C97D-CFCC-4473-BFD8-B28DA3199EF6} = {952B525D-297C-4F66-85E2-967C9982771F}
{196C36B5-D580-4C2D-BDFF-AFD530255F2E} = {952B525D-297C-4F66-85E2-967C9982771F}
{B28BBCD3-5A90-447E-8BAD-D78D7517AAD7} = {98E8084D-CE3D-429D-97B4-A7E41EDF1431}
{FB0F7358-8500-46DF-B213-3E8980F70856} = {98E8084D-CE3D-429D-97B4-A7E41EDF1431}
{AB38EF74-D766-4A77-A7F2-EA8B74AEA495} = {98E8084D-CE3D-429D-97B4-A7E41EDF1431}
{1C224EA5-8DE8-46E4-A0BE-20AEDB8ACB1D} = {98E8084D-CE3D-429D-97B4-A7E41EDF1431}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8F0560BE-EE70-4DE3-8ACA-590A2B56C96A}
Expand Down
9 changes: 9 additions & 0 deletions src/BlazeKit.Hydration/BlazeKit.Hydration.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
84 changes: 84 additions & 0 deletions src/BlazeKit.Hydration/DataHydrationContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Text.Json;

namespace BlazeKit.Hydration;
public class DataHydrationContext
{
private bool initalized = false;
public DataHydrationContext(Func<Task<string>> loadData = null)
{
_hydrationData = new Dictionary<string, object>();
this.loadData = loadData;
}

private Dictionary<string, object> _hydrationData;
private readonly Func<Task<string>> loadData;

public void Add(string key, object value)
{
if(_hydrationData.ContainsKey(key)) {
_hydrationData[key] = value;
} else {
_hydrationData.Add(key, value);
}
}

public async Task<T> GetAsync<T>(string key, T defaultValue)
{
var result = defaultValue;
if(OperatingSystem.IsBrowser()) {
await LoadData();
}

if(_hydrationData.TryGetValue(key, out var tmpValue)) {
var deserialzed = JsonSerializer.Deserialize<T>(((JsonElement)tmpValue).GetRawText());
result = deserialzed;
}
return result;
}

public bool TryGet<T>(string key, out T value)
{
if(OperatingSystem.IsBrowser() && !initalized) {
LoadData();
}
value = default;
if(_hydrationData.TryGetValue(key, out var tmpValue)) {
// check if tmpValue is of type T
if(tmpValue is T) {
value = (T)tmpValue;
return true;
} else {
value = default;
return false;
}
} else {
return false;
}
}

public string Serialized() {
return System.Text.Json.JsonSerializer.Serialize(_hydrationData);
}

private async Task<bool> LoadData() {
// check if initalized if not hydrate data from dom
Console.WriteLine("Loading page data");
var data = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string,object>>(await loadData());
foreach(var item in data) {
if(_hydrationData.ContainsKey(item.Key)) {
_hydrationData[item.Key] = item.Value;
} else {
_hydrationData.Add(item.Key, item.Value);
}
}
// if(!initalized) {
// var data = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string,object>>(await loadData());
// foreach(var item in data) {
// _hydrationData.Add(item.Key, item.Value);
// }
// initalized = true;
// }

return true;
}
}
35 changes: 35 additions & 0 deletions src/BlazeKit.Site.Static/BlazeKit.Site.Static.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>.blazekit</CompilerGeneratedFilesOutputPath>
<!-- <PublishDir>.blazekit/build/tmp</PublishDir> -->
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.1" />
<!-- <ProjectReference Include="..\BlazeKit.Site\BlazeKit.Site.csproj" PrivateAssets="all" /> -->
<ProjectReference Include="..\BlazeKit.Static\BlazeKit.Static.csproj" />
<ProjectReference Include="..\BlazeKit.Website\BlazeKit.Website.csproj" />
<!-- <ProjectReference Include="..\BlazeKit\BlazeKit.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> -->
</ItemGroup>

<Target Name="CustomActionsAfterPublish" AfterTargets="AfterPublish">
<PropertyGroup>
<SourceFolder>$(PublishDir)wwwroot</SourceFolder>
<DestinationFolder>$(PublishDir)..\ssg</DestinationFolder>
</PropertyGroup>
<ItemGroup>
<FilesToCopy Include="$([MSBuild]::EnsureTrailingSlash('$(SourceFolder)'))**\*.*"/>
</ItemGroup>
<MakeDir Directories="$(PublishDir)..\ssg" />
<Message Text="Copying files from $(SourceFolder) to $(DestinationFolder)" Importance="high" />
<Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$([MSBuild]::EnsureTrailingSlash('$(DestinationFolder)'))%(RecursiveDir)"/>

<Exec Command="$(PublishDir)$(AssemblyName).$(OutputType) ssg" />
</Target>

</Project>
41 changes: 41 additions & 0 deletions src/BlazeKit.Site.Static/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// See https://aka.ms/new-console-template for more information

using BlazeKit.Website;

if (args.Count() > 0 && args[0] == "ssg") {
Console.WriteLine("Building Static Site");
new BlazeKit.Static.StaticSiteGenerator(
".blazekit/build/ssg",
".blazekit/build/tmp/wwwroot",
typeof(BlazeKit.Website.Index).Assembly
).Build();
Console.WriteLine("Static Site Built");
return;
}

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorComponents().AddInteractiveWebAssemblyComponents();

var app = builder.Build();
app.UseRouting();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
} else {
// app.UseWebAssemblyDebugging();
}
// app.UseBlazorFrameworkFiles();

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<BlazeKit.Website.Index>().AddInteractiveWebAssemblyRenderMode();

app.Run();
16 changes: 16 additions & 0 deletions src/BlazeKit.Static/BlazeKit.Static.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BlazeKit.Hydration\BlazeKit.Hydration.csproj" />
</ItemGroup>
</Project>
105 changes: 105 additions & 0 deletions src/BlazeKit.Static/BlazorRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.HtmlRendering;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace BlazeKit.Static;
#pragma warning disable BL0006 // Do not use RenderTree types

public sealed class BlazorRenderer : Renderer
{
private readonly Lazy<HtmlRenderer> htmlRenderer;

public BlazorRenderer(ServiceProvider serviceProvider) : this(
() =>
new HtmlRenderer(
serviceProvider,
serviceProvider.GetRequiredService<ILoggerFactory>()
),
serviceProvider
)
{ }

public BlazorRenderer(HtmlRenderer htmlRenderer,ServiceProvider serviceProvider) :this(
() => htmlRenderer,
serviceProvider
)
{ }

public BlazorRenderer(Func<HtmlRenderer> htmlRenderer,ServiceProvider serviceProvider) : base(serviceProvider, serviceProvider.GetRequiredService<ILoggerFactory>())
{
this.htmlRenderer = new Lazy<HtmlRenderer>(htmlRenderer);
}

public override Dispatcher Dispatcher => this.htmlRenderer.Value.Dispatcher;


/// <summary>
/// Renders a component T which doesn't require any parameters
/// </summary>
/// <typeparam name="T"></typeparam>
public Task<string> RenderComponent<T>() where T : IComponent
=> RenderComponent<T>(ParameterView.Empty);

/// <summary>
/// Renders a component T using the provided dictionary of parameters
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dictionary"></param>
public Task<string> RenderComponent<T>(Dictionary<string, object?> dictionary) where T : IComponent
=> RenderComponent<T>(ParameterView.FromDictionary(dictionary));

/// <summary>
/// Renders a component T using the provided parameters
/// </summary>
/// <param name="componentType"></param>
public Task<string> RenderComponent(Type componentType)
=> RenderComponent(componentType, ParameterView.Empty);

/// <summary>
/// Renders a component T using the provided dictionary of parameters
/// </summary>
/// <param name="componentType"></param>
/// <param name="dictionary"></param>
public Task<string> RenderComponent(Type componentType, Dictionary<string, object?> dictionary)
=> RenderComponent(componentType, ParameterView.FromDictionary(dictionary));

protected override void HandleException(Exception exception)
{
throw new NotImplementedException();
}

protected override Task UpdateDisplayAsync(in RenderBatch renderBatch)
{
throw new NotImplementedException();
}


private Task<string> RenderComponent<T>(ParameterView parameters) where T : IComponent
{
// Use the default dispatcher to invoke actions in the context of the
// static HTML renderer and return as a string
return htmlRenderer.Value.Dispatcher.InvokeAsync(async () =>
{
HtmlRootComponent output = await htmlRenderer.Value.RenderComponentAsync<T>(parameters);
return output.ToHtmlString();
});
}

private Task<string> RenderComponent(Type componentType, ParameterView parameters)
{
// Use the default dispatcher to invoke actions in the context of the
// static HTML renderer and return as a string
return htmlRenderer.Value.Dispatcher.InvokeAsync(async () =>
{
HtmlRootComponent output = await htmlRenderer.Value.RenderComponentAsync(componentType, parameters);
return output.ToHtmlString();
});
}


}
#pragma warning restore BL0006 // Do not use RenderTree types

16 changes: 16 additions & 0 deletions src/BlazeKit.Static/FkJsRuntime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.JSInterop;

namespace BlazeKit.Static;
public sealed class FkJsRuntime : IJSRuntime
{
public ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(string identifier, object?[]? args)
{
throw new NotSupportedException("Javascript Runtime is not valid in Static Site Generation");
}

public ValueTask<TValue> InvokeAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
{
throw new NotSupportedException("Javascript Runtime is not valid in Static Site Generation");
}
}
19 changes: 19 additions & 0 deletions src/BlazeKit.Static/FkNavigationInterception.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Components.Routing;

namespace BlazeKit.Static;

public sealed class FkNavigationInterception : INavigationInterception
{
public Task EnableNavigationInterceptionAsync()
{
return Task.CompletedTask;
}
}

public class FkScrollToLocationHash : IScrollToLocationHash
{
public Task RefreshScrollPositionForHash(string locationAbsolute)
{
return Task.CompletedTask;
}
}
Loading

0 comments on commit 3d28fa2

Please sign in to comment.