mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2025-12-20 06:51:55 +00:00
Improved lighthouse performance score through inlining CSS
This commit is contained in:
1
src/CriticalCSS/Home.AccessDenied.css
Normal file
1
src/CriticalCSS/Home.AccessDenied.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Home.Assets.css
Normal file
1
src/CriticalCSS/Home.Assets.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Home.Groups.css
Normal file
1
src/CriticalCSS/Home.Groups.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Home.Index.css
Normal file
1
src/CriticalCSS/Home.Index.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Home.Inventory.css
Normal file
1
src/CriticalCSS/Home.Inventory.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Home.Locations.css
Normal file
1
src/CriticalCSS/Home.Locations.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Home.Login.css
Normal file
1
src/CriticalCSS/Home.Login.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Home.Users.css
Normal file
1
src/CriticalCSS/Home.Users.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Settings.Admin.css
Normal file
1
src/CriticalCSS/Settings.Admin.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/Settings.User.css
Normal file
1
src/CriticalCSS/Settings.User.css
Normal file
File diff suppressed because one or more lines are too long
1
src/CriticalCSS/_Layout.css
Normal file
1
src/CriticalCSS/_Layout.css
Normal file
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.Models;
|
||||||
using Berufsschule_HAM.HealthChecks;
|
using Berufsschule_HAM.HealthChecks;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.AspNetCore.ResponseCompression;
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Bind options
|
// Bind options
|
||||||
@@ -50,6 +51,8 @@ builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationSc
|
|||||||
builder.Services.AddResponseCompression(options =>
|
builder.Services.AddResponseCompression(options =>
|
||||||
{
|
{
|
||||||
options.EnableForHttps = true;
|
options.EnableForHttps = true;
|
||||||
|
options.Providers.Add<GzipCompressionProvider>();
|
||||||
|
options.Providers.Add<BrotliCompressionProvider>();
|
||||||
options.MimeTypes =
|
options.MimeTypes =
|
||||||
[
|
[
|
||||||
"text/plain",
|
"text/plain",
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
<form id="updateSettings" style="margin-bottom: 4rem !important" method="post" asp-controller="Settings" asp-action="Admin">
|
<form id="updateSettings" style="margin-bottom: 4rem !important" method="post" asp-controller="Settings" asp-action="Admin">
|
||||||
<div class="row g-3">
|
|
||||||
<h4 class="fw-bold">@T["General settings"]</h4>
|
<h4 class="fw-bold">@T["General settings"]</h4>
|
||||||
|
<div class="row g-3">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label" for="updateHashAlgorithm">@T["Default hash algorithm"]</label>
|
<label class="form-label" for="updateHashAlgorithm">@T["Default hash algorithm"]</label>
|
||||||
<select type="text" name="DefaultHashAlgorithm" id="updateHashAlgorithm" class="form-control">
|
<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" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - Berufsschule_HAM</title>
|
<title>@ViewData["Title"] - Berufsschule_HAM</title>
|
||||||
<script type="importmap"></script>
|
<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"
|
<link rel="stylesheet"
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
|
href="~/lib/bootstrap/dist/css/bootstrap.min.css"
|
||||||
crossorigin="anonymous">
|
media="print"
|
||||||
|
onload="this.media='all'">
|
||||||
@* <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" /> *@
|
@* <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'">
|
<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>
|
<noscript><link fetchpriority="high" rel="stylesheet" href="~/css/site.css"></noscript>
|
||||||
<link rel="stylesheet" href="~/Berufsschule_HAM.styles.css" asp-append-version="true" />
|
|
||||||
<script>
|
<script>
|
||||||
window.appTranslations = {
|
window.appTranslations = {
|
||||||
selectLocation: '@T["Select location"]',
|
selectLocation: '@T["Select location"]',
|
||||||
@@ -38,7 +50,7 @@
|
|||||||
</script>
|
</script>
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar bg border-bottom box-shadow mb-3">
|
<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">
|
<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>
|
<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"
|
<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;
|
white-space: nowrap;
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skip-link {
|
|
||||||
position: fixed;
|
|
||||||
left: -10000px;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
.skip-link:focus {
|
|
||||||
left: 10px;
|
|
||||||
top: 10px;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|||||||
145
src/critical.js
Normal file
145
src/critical.js
Normal file
@@ -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 {
|
h4.fw-bold, h4.card-title {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table button {
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-link:focus {
|
||||||
|
left: 10px;
|
||||||
|
top: 10px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user