mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2026-05-06 11:32:19 +00:00
Improved lighthouse performance score through inlining CSS
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ using Berufsschule_HAM.Services;
|
||||
using Berufsschule_HAM.Models;
|
||||
using Berufsschule_HAM.HealthChecks;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Bind options
|
||||
@@ -50,6 +51,8 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc
|
||||
builder.Services.AddResponseCompression(options =>
|
||||
{
|
||||
options.EnableForHttps = true;
|
||||
options.Providers.Add<GzipCompressionProvider>();
|
||||
options.Providers.Add<BrotliCompressionProvider>();
|
||||
options.MimeTypes =
|
||||
[
|
||||
"text/plain",
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
|
||||
<form id="updateSettings" style="margin-bottom: 4rem !important" method="post" asp-controller="Settings" asp-action="Admin">
|
||||
<h4 class="fw-bold">@T["General settings"]</h4>
|
||||
<div class="row g-3">
|
||||
<h4 class="fw-bold">@T["General settings"]</h4>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="updateHashAlgorithm">@T["Default hash algorithm"]</label>
|
||||
<select type="text" name="DefaultHashAlgorithm" id="updateHashAlgorithm" class="form-control">
|
||||
|
||||
@@ -12,14 +12,26 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - Berufsschule_HAM</title>
|
||||
<script type="importmap"></script>
|
||||
@* <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-append-version="true"/> *@
|
||||
<style>
|
||||
@if (Context.Request.Path.Value is not null)
|
||||
{
|
||||
string path = System.IO.Path.Combine("CriticalCSS", Context.Request.Path.Value.Trim('/').Replace("/", ".") + ".css");
|
||||
if (File.Exists(path))
|
||||
{
|
||||
@Html.Raw(File.ReadAllText(path));
|
||||
}
|
||||
} else {
|
||||
@Html.Raw(File.ReadAllText("CriticalCSS/_Layout.css"));
|
||||
}
|
||||
</style>
|
||||
<link rel="preload" href="~/lib/bootstrap/dist/css/bootstrap.min.css" as="style"/>
|
||||
<link rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
|
||||
crossorigin="anonymous">
|
||||
href="~/lib/bootstrap/dist/css/bootstrap.min.css"
|
||||
media="print"
|
||||
onload="this.media='all'">
|
||||
@* <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> *@
|
||||
<link rel="preload" href="~/css/site.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||
<noscript><link rel="stylesheet" href="~/css/site.css"></noscript>
|
||||
<link rel="stylesheet" href="~/Berufsschule_HAM.styles.css" asp-append-version="true" />
|
||||
<noscript><link fetchpriority="high" rel="stylesheet" href="~/css/site.css"></noscript>
|
||||
<script>
|
||||
window.appTranslations = {
|
||||
selectLocation: '@T["Select location"]',
|
||||
@@ -38,7 +50,7 @@
|
||||
</script>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar bg border-bottom box-shadow mb-3">
|
||||
<a href="#main-content" class="skip-link btn btn-primary">@T["Jump to content"]</a>
|
||||
<a href="#main-content" class="skip-link btn btn-primary" style="position: fixed; left: -10000px; z-index: 1000;">@T["Jump to content"]</a>
|
||||
<div class="container-fluid">
|
||||
<a class="" asp-area="" asp-controller="Home" asp-action="Index"><img fetchpriority="high" src="/HAM_Banner_xs.png" alt="Logo" width="123" height="40" style="width: 123px; height: 40px;"></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||
|
||||
@@ -46,14 +46,3 @@ button.accept-policy {
|
||||
white-space: nowrap;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
.skip-link {
|
||||
position: fixed;
|
||||
left: -10000px;
|
||||
z-index: 1000;
|
||||
}
|
||||
.skip-link:focus {
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
import { generate } from 'critical';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import puppeteer from 'puppeteer';
|
||||
|
||||
const browser = await puppeteer.launch();
|
||||
const page = await browser.newPage();
|
||||
|
||||
// Login
|
||||
await page.goto('http://localhost:5275/Home/Login');
|
||||
await page.type('#username', 'admin');
|
||||
await page.type('#password', 'Test1234.');
|
||||
await page.click('button[type=submit]');
|
||||
await page.waitForNavigation();
|
||||
|
||||
// Extract cookies
|
||||
const cookies = await page.cookies();
|
||||
await browser.close();
|
||||
|
||||
async function generateCriticalCSSForViews() {
|
||||
const viewsDir = 'Views';
|
||||
|
||||
// Helper function to get all .cshtml files recursively
|
||||
function getAllCshtmlFiles(dir) {
|
||||
let results = [];
|
||||
const list = fs.readdirSync(dir);
|
||||
|
||||
list.forEach(file => {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
|
||||
if (stat && stat.isDirectory()) {
|
||||
// Recursively get files from subdirectories
|
||||
results = results.concat(getAllCshtmlFiles(filePath));
|
||||
} else if (file.endsWith('.cshtml')) {
|
||||
results.push(filePath);
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Helper function to convert file path to URL path
|
||||
function filePathToUrlPath(filePath) {
|
||||
// Remove 'Views/' prefix
|
||||
let relativePath = filePath.replace(/^Views[\/\\]/, '');
|
||||
|
||||
// Remove .cshtml extension
|
||||
relativePath = relativePath.replace(/\.cshtml$/, '');
|
||||
|
||||
// Convert to URL format (replace \ with / and capitalize first letter)
|
||||
const urlPath = relativePath
|
||||
.split(/[\/\\]/)
|
||||
.map((segment, index) =>
|
||||
index === 0 ? segment : segment.charAt(0).toUpperCase() + segment.slice(1)
|
||||
)
|
||||
.join('/');
|
||||
|
||||
// Handle the case where we have a single file (like Index.cshtml)
|
||||
if (relativePath.includes('/')) {
|
||||
// Convert to URL path format: Views/Home/Index.cshtml -> /Home/Index
|
||||
return '/' + relativePath.replace(/\\/g, '/').replace(/\.cshtml$/, '');
|
||||
} else {
|
||||
// For files directly in Views folder (like Views/Index.cshtml)
|
||||
return '/' + relativePath.replace(/\.cshtml$/, '');
|
||||
}
|
||||
}
|
||||
|
||||
// Get all .cshtml files
|
||||
const cshtmlFiles = getAllCshtmlFiles(viewsDir);
|
||||
|
||||
// Create CriticalCSS directory if it doesn't exist
|
||||
const criticalCssDir = 'CriticalCSS';
|
||||
if (!fs.existsSync(criticalCssDir)) {
|
||||
fs.mkdirSync(criticalCssDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Process each file
|
||||
for (const file of cshtmlFiles) {
|
||||
try {
|
||||
const urlPath = filePathToUrlPath(file);
|
||||
|
||||
// Generate critical CSS
|
||||
await generate({
|
||||
src: `http://localhost:5275${urlPath}`,
|
||||
inline: false,
|
||||
width: 1920,
|
||||
height: 1080,
|
||||
penthouse: {
|
||||
customHeaders: {
|
||||
cookie: cookies.map(c => `${c.name}=${c.value}`).join('; ')
|
||||
},
|
||||
forceInclude: [
|
||||
'[data-bs-theme="dark"]',
|
||||
'[data-bs-theme="dark"] *',
|
||||
'body.dark',
|
||||
'.navbar-dark',
|
||||
'.navbar',
|
||||
'.navbar-collapse',
|
||||
'.dropdown',
|
||||
'.dropdown-toggle',
|
||||
'.dropdown-toggle::after',
|
||||
'.dropdown-item',
|
||||
'.dropdown-menu',
|
||||
'.dropdown-menu-end',
|
||||
'.dropdown-menu.show',
|
||||
'.me-2',
|
||||
'.align-items-center',
|
||||
'.d-flex',
|
||||
'.position-fixed', // print batch
|
||||
'.bottom-0',
|
||||
'.start-0',
|
||||
'.m-4',
|
||||
'.row', // elements
|
||||
'.g-3',
|
||||
'.col-md-3',
|
||||
'.text-center',
|
||||
'.mb-3',
|
||||
'.mt-3',
|
||||
'.py-4',
|
||||
'.text-center',
|
||||
'h2',
|
||||
'.form-control',
|
||||
'.btn',
|
||||
'.btn-secondary',
|
||||
'.modal',
|
||||
]
|
||||
},
|
||||
target: {
|
||||
css: path.join(criticalCssDir, urlPath.replace(/\//g, '.').replace(/^\./, '') + '.css')
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`Critical CSS generated for: ${urlPath}`);
|
||||
} catch (err) {
|
||||
console.error(`Error processing ${file}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('All critical CSS files generated!');
|
||||
}
|
||||
|
||||
// Run the function
|
||||
generateCriticalCSSForViews().catch(console.error);
|
||||
@@ -72,3 +72,13 @@ h3.modal-title {
|
||||
h4.fw-bold, h4.card-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.table button {
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
outline: none;
|
||||
}
|
||||
Reference in New Issue
Block a user