Skip to content

Commit 6fb5e44

Browse files
Merge pull request #208 from TimeWarpEngineering/Cramer/2025-01-20/UpdatePackages
Cramer/2025 01 20/update packages
2 parents c8edfc0 + d32c4a9 commit 6fb5e44

File tree

20 files changed

+206
-113
lines changed

20 files changed

+206
-113
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# CSS Bundle Hash Management in Blazor with Tailwind
2+
3+
## Overview
4+
When using Blazor components like FluentUI and QuickGrid alongside Tailwind CSS, you may encounter build issues related to CSS bundle hashing. This document explains the issue and provides solutions.
5+
6+
## The Problem
7+
Modern Blazor component libraries use content hashing for their CSS bundles to enable cache busting. These hashes are generated based on the content of the CSS files and appear in the bundle filenames, e.g.:
8+
- `Microsoft.FluentUI.AspNetCore.Components.{hash}.bundle.scp.css`
9+
- `Microsoft.AspNetCore.Components.QuickGrid.{hash}.bundle.scp.css`
10+
11+
The challenge arises when Tailwind needs to process these files during build time, as the hash values can change with package updates.
12+
13+
## Current Solution
14+
The current solution involves creating dummy CSS files with the correct hash values to satisfy the build process:
15+
16+
```xml
17+
<Target Name="CreateDummyFluentUICSS" BeforeTargets="Build">
18+
<ItemGroup>
19+
<DummyCSSFile Include="$(ProjectDir)obj\css\_content\Microsoft.FluentUI.AspNetCore.Components\Microsoft.FluentUI.AspNetCore.Components.{hash}.bundle.scp.css" />
20+
</ItemGroup>
21+
<MakeDir Directories="$(ProjectDir)obj\css\_content\Microsoft.FluentUI.AspNetCore.Components" />
22+
<WriteLinesToFile File="@(DummyCSSFile)" Lines="/* Dummy file to satisfy build requirement */" Overwrite="true" />
23+
</Target>
24+
25+
<Target Name="CreateDummyQuickGridCSS" BeforeTargets="Build">
26+
<ItemGroup>
27+
<DummyQuickGridCSSFile Include="$(ProjectDir)obj\css\_content\Microsoft.AspNetCore.Components.QuickGrid\Microsoft.AspNetCore.Components.QuickGrid.{hash}.bundle.scp.css" />
28+
</ItemGroup>
29+
<MakeDir Directories="$(ProjectDir)obj\css\_content\Microsoft.AspNetCore.Components.QuickGrid" />
30+
<WriteLinesToFile File="@(DummyQuickGridCSSFile)" Lines="/* Dummy file to satisfy build requirement */" Overwrite="true" />
31+
</Target>
32+
```
33+
34+
## What to Do When Updating Packages
35+
When updating FluentUI, QuickGrid, or other component packages that use CSS bundles:
36+
37+
1. Build the project after the update
38+
2. Watch for build errors mentioning missing CSS bundles
39+
3. Note the new hash values from the error messages
40+
4. Update the corresponding hash values in your CreateDummy targets
41+
5. Rebuild the project
42+
43+
Example error message to look for:
44+
```
45+
Error: Failed to find '_content/Microsoft.AspNetCore.Components.QuickGrid/Microsoft.AspNetCore.Components.QuickGrid.{hash}.bundle.scp.css'
46+
```
47+
48+
## Alternative Solutions
49+
50+
### 1. Runtime CSS Loading
51+
Move component CSS loading to runtime by adding links in index.html:
52+
```html
53+
<head>
54+
<link href="/_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css" rel="stylesheet" />
55+
<link href="/_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css" rel="stylesheet" />
56+
<link href="/_content/Microsoft.AspNetCore.Components.QuickGrid/Microsoft.AspNetCore.Components.QuickGrid.bundle.scp.css" rel="stylesheet" />
57+
</head>
58+
```
59+
60+
### 2. Exclude Bundle Processing
61+
Configure Tailwind to ignore all scoped CSS bundles:
62+
```javascript
63+
// tailwind.config.js
64+
module.exports = {
65+
content: [
66+
// ... other content ...
67+
"!**/*.bundle.scp.css",
68+
],
69+
}
70+
```
71+
72+
## Recommendations
73+
1. Document current hash values in a comment within the csproj file
74+
2. Add a reminder comment about checking hash values when updating packages
75+
3. Consider implementing one of the alternative solutions for longer-term maintainability
76+
77+
## Future Considerations
78+
The need for hash management might be eliminated in future versions of the component libraries or Tailwind CSS. Keep an eye on:
79+
- Updates to FluentUI and QuickGrid that might change how CSS is bundled
80+
- Tailwind CSS updates that might better handle dynamic imports
81+
- Blazor's CSS isolation and bundling strategies

TimeWarp.Architecture/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
<LangVersion>latest</LangVersion>
2020
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
2121
<Nullable>disable</Nullable>
22-
<TargetFramework>net8.0</TargetFramework>
22+
<TargetFramework>net9.0</TargetFramework>
2323
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
2424
</PropertyGroup>
2525

TimeWarp.Architecture/Directory.Packages.props

Lines changed: 85 additions & 85 deletions
Large diffs are not rendered by default.

TimeWarp.Architecture/Source/Analyzers/TimeWarp.Architecture.Analyzers/TimeWarp.Architecture.Analyzers.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414
<PrivateAssets>all</PrivateAssets>
1515
</PackageReference>
1616
<PackageReference Include="Microsoft.CodeAnalysis.Common" />
17-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
17+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
1818
</ItemGroup>
1919
</Project>

TimeWarp.Architecture/Source/Common/Common.Server/CommonServerModule.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,9 @@ string swaggerApiTitle
8787
);
8888
}
8989

90-
private static void ConfigureAzureAppConfig(IConfigurationManager aConfigurationManager)
90+
private static void ConfigureAzureAppConfig(IConfigurationManager configurationManager)
9191
{
92-
string? connectionString = aConfigurationManager.GetConnectionString("AppConfig");
92+
string? connectionString = configurationManager.GetConnectionString("AppConfig");
9393
if (string.IsNullOrEmpty(connectionString))
9494
{
9595
Console.WriteLine("No AppConfig ConnectionString");
@@ -98,18 +98,18 @@ private static void ConfigureAzureAppConfig(IConfigurationManager aConfiguration
9898

9999
Console.WriteLine($"connectionString: {connectionString}");
100100

101-
aConfigurationManager.AddAzureAppConfiguration
101+
configurationManager.AddAzureAppConfiguration
102102
(
103-
aAzureAppConfigurationOptions =>
104-
aAzureAppConfigurationOptions
103+
azureAppConfigurationOptions =>
104+
azureAppConfigurationOptions
105105
.Connect(connectionString)
106106
.UseFeatureFlags()
107107
.ConfigureRefresh
108108
(
109-
aAzureAppConfigurationRefreshOptions =>
110-
aAzureAppConfigurationRefreshOptions
109+
azureAppConfigurationRefreshOptions =>
110+
azureAppConfigurationRefreshOptions
111111
.Register("Sentinel", refreshAll: true)
112-
.SetCacheExpiration(TimeSpan.FromMinutes(5))
112+
.SetRefreshInterval(TimeSpan.FromMinutes(5))
113113
)
114114
.ConfigureKeyVault
115115
(
@@ -119,7 +119,7 @@ private static void ConfigureAzureAppConfig(IConfigurationManager aConfiguration
119119
optional: false
120120
);
121121

122-
string? testValue = aConfigurationManager.GetValue<string>("TestValue");
122+
string? testValue = configurationManager.GetValue<string>("TestValue");
123123
Console.WriteLine($"App Config value TestValue: {testValue}");
124124
}
125125

TimeWarp.Architecture/Source/ContainerApps/Aspire/Aspire.AppHost/Aspire.AppHost.csproj

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
2-
2+
<Sdk Name="Aspire.AppHost.Sdk" Version="9.0.0" />
33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
65
<ImplicitUsings>enable</ImplicitUsings>
76
<Nullable>enable</Nullable>
87
<IsAspireHost>true</IsAspireHost>

TimeWarp.Architecture/Source/ContainerApps/Aspire/Aspire.ServiceDefaults/Aspire.ServiceDefaults.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net8.0</TargetFramework>
54
<ImplicitUsings>enable</ImplicitUsings>
65
<Nullable>enable</Nullable>
76
<IsAspireSharedProject>true</IsAspireSharedProject>

TimeWarp.Architecture/Source/ContainerApps/Web/Web.Server/Components/App.razor

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ window.passwordless = new Client({ apiKey: "timewarp:public:b00cdd667db547de90de
4848
</script>
4949

5050
<script src="_framework/blazor.web.js"></script>
51-
<script src="js/downloadFile.js"></script>
5251

5352
@*TODO: Cramer review PWA block*@
5453
<!--#if PWA -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@namespace TimeWarp.Architecture.Components
2+
3+
<FluentDialogProvider />
4+
<FluentKeyCodeProvider />
5+
<FluentMenuProvider />
6+
<FluentMessageBarProvider />
7+
<FluentToastProvider />
8+
<FluentTooltipProvider />

TimeWarp.Architecture/Source/ContainerApps/Web/Web.Spa/Components/Layouts/MainLayout.razor

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
protected const string HeadingHeight = "52px";
66
}
77
@Body
8+
9+
<FluentUIRequiredFeatures />

0 commit comments

Comments
 (0)