Implemented css bundling
This commit is contained in:
28
Moonlight.ApiServer/Helpers/BundleAssetFileProvider.cs
Normal file
28
Moonlight.ApiServer/Helpers/BundleAssetFileProvider.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders.Physical;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
namespace Moonlight.ApiServer.Helpers;
|
||||
|
||||
public class BundleAssetFileProvider : IFileProvider
|
||||
{
|
||||
public IDirectoryContents GetDirectoryContents(string subpath)
|
||||
=> NotFoundDirectoryContents.Singleton;
|
||||
|
||||
public IFileInfo GetFileInfo(string subpath)
|
||||
{
|
||||
if(subpath != "/css/bundle.css")
|
||||
return new NotFoundFileInfo(subpath);
|
||||
|
||||
var physicalPath = PathBuilder.File("storage", "tmp", "bundle.css");
|
||||
|
||||
if(!File.Exists(physicalPath))
|
||||
return new NotFoundFileInfo(subpath);
|
||||
|
||||
return new PhysicalFileInfo(new FileInfo(physicalPath));
|
||||
}
|
||||
|
||||
public IChangeToken Watch(string filter)
|
||||
=> NullChangeToken.Singleton;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ExCSS" Version="4.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.10"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
132
Moonlight.ApiServer/Services/BundleService.cs
Normal file
132
Moonlight.ApiServer/Services/BundleService.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using ExCSS;
|
||||
using Microsoft.Extensions.Hosting.Internal;
|
||||
using MoonCore.Helpers;
|
||||
|
||||
namespace Moonlight.ApiServer.Services;
|
||||
|
||||
public class BundleService : IHostedService
|
||||
{
|
||||
private readonly ILogger<BundleService> Logger;
|
||||
private readonly List<string> CssFiles = new();
|
||||
|
||||
private readonly AssetService AssetService;
|
||||
private readonly IWebHostEnvironment HostEnvironment;
|
||||
|
||||
public BundleService(ILogger<BundleService> logger, PluginService pluginService, IWebHostEnvironment hostEnvironment, AssetService assetService)
|
||||
{
|
||||
Logger = logger;
|
||||
HostEnvironment = hostEnvironment;
|
||||
AssetService = assetService;
|
||||
}
|
||||
|
||||
public async Task Bundle(CancellationToken cancellationToken)
|
||||
{
|
||||
Logger.LogInformation("Collecting css files...");
|
||||
|
||||
var fi = HostEnvironment.WebRootFileProvider.GetFileInfo("css/core.min.css");
|
||||
|
||||
CssFiles.Add(fi.PhysicalPath!);
|
||||
CssFiles.AddRange(AssetService.GetCssAssets().Select(webPath =>
|
||||
{
|
||||
var fii = HostEnvironment.WebRootFileProvider.GetFileInfo(webPath.TrimStart('/'));
|
||||
|
||||
return fii.PhysicalPath!;
|
||||
}));
|
||||
|
||||
Logger.LogInformation("Bundling css files...");
|
||||
|
||||
var content = "";
|
||||
|
||||
if (CssFiles.Count > 0)
|
||||
{
|
||||
var cssFileContents = new List<string>();
|
||||
|
||||
foreach (var cssFile in CssFiles)
|
||||
{
|
||||
var fileContent = await File.ReadAllTextAsync(cssFile, cancellationToken);
|
||||
cssFileContents.Add(fileContent);
|
||||
}
|
||||
|
||||
content = cssFileContents[0];
|
||||
|
||||
if (cssFileContents.Count > 1)
|
||||
{
|
||||
var styleSheets = new List<Stylesheet>();
|
||||
|
||||
var parser = new StylesheetParser();
|
||||
|
||||
foreach (var fileContent in cssFileContents)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sh = await parser.ParseAsync(fileContent, cancellationToken);
|
||||
styleSheets.Add(sh);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogWarning("An error occured while parsing css file: {e}", e);
|
||||
}
|
||||
}
|
||||
|
||||
var mainStylesheet = styleSheets.First();
|
||||
|
||||
foreach (var stylesheet in styleSheets.Skip(1))
|
||||
{
|
||||
// Style
|
||||
foreach (var styleRule in stylesheet.StyleRules)
|
||||
{
|
||||
if (mainStylesheet.StyleRules.Any(x => x.Selector.Text == styleRule.Selector.Text))
|
||||
continue;
|
||||
|
||||
content += styleRule.ToCss() + "\n";
|
||||
}
|
||||
|
||||
// Container
|
||||
foreach (var containerRule in stylesheet.ContainerRules)
|
||||
{
|
||||
if (mainStylesheet.ContainerRules.Any(x => x.ConditionText == containerRule.ConditionText))
|
||||
continue;
|
||||
|
||||
content += containerRule.ToCss() + "\n";
|
||||
}
|
||||
|
||||
// Import Rule
|
||||
foreach (var importRule in stylesheet.ImportRules)
|
||||
{
|
||||
if (mainStylesheet.ImportRules.Any(x => x.Text == importRule.Text))
|
||||
continue;
|
||||
|
||||
content += importRule.ToCss() + "\n";
|
||||
}
|
||||
|
||||
// Media Rules
|
||||
foreach (var mediaRule in stylesheet.MediaRules)
|
||||
content += mediaRule.StylesheetText.Text + "\n";
|
||||
|
||||
// Page Rules
|
||||
foreach (var pageRule in stylesheet.PageRules)
|
||||
{
|
||||
if (mainStylesheet.PageRules.Any(x => x.SelectorText == pageRule.SelectorText))
|
||||
continue;
|
||||
|
||||
content += pageRule.ToCss() + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(PathBuilder.Dir("storage", "tmp"));
|
||||
await File.WriteAllTextAsync(PathBuilder.File("storage", "tmp", "bundle.css"), content, cancellationToken);
|
||||
|
||||
Logger.LogInformation("Successfully built css bundle");
|
||||
}
|
||||
|
||||
public void AddCssFile(string file) => CssFiles.Add(file);
|
||||
|
||||
//
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
=> Bundle(cancellationToken);
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
=> Task.CompletedTask;
|
||||
}
|
||||
@@ -79,6 +79,7 @@ public class Startup
|
||||
await RegisterCaching();
|
||||
await HookPluginBuild();
|
||||
await HandleConfigureArguments();
|
||||
await RegisterPluginAssets();
|
||||
|
||||
await BuildWebApplication();
|
||||
|
||||
@@ -341,8 +342,21 @@ public class Startup
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task RegisterPluginAssets()
|
||||
{
|
||||
WebApplicationBuilder.Services.AddHostedService(sp => sp.GetRequiredService<BundleService>());
|
||||
WebApplicationBuilder.Services.AddSingleton<BundleService>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task UsePluginAssets()
|
||||
{
|
||||
WebApplication.UseStaticFiles(new StaticFileOptions()
|
||||
{
|
||||
FileProvider = new BundleAssetFileProvider()
|
||||
});
|
||||
|
||||
WebApplication.UseStaticFiles(new StaticFileOptions()
|
||||
{
|
||||
FileProvider = new PluginAssetFileProvider(PluginService)
|
||||
|
||||
Reference in New Issue
Block a user