refactor: standardize HTML and JavaScript formatting; improve readability
This commit is contained in:
parent
bdbce1c3ad
commit
1fbc500c19
42
index.html
42
index.html
|
|
@ -1,14 +1,22 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<!-- Primary Meta Tags -->
|
<!-- Primary Meta Tags -->
|
||||||
<title>Sascha Bach | Freelance Software Developer – Accessible Web Development</title>
|
<title>
|
||||||
<meta name="description" content="Freelance software developer in Germany specializing in accessible web development. I build inclusive websites compliant with the Accessibility Act using React, TypeScript, and modern frameworks." />
|
Sascha Bach | Freelance Software Developer – Accessible Web Development
|
||||||
|
</title>
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Freelance software developer in Germany specializing in accessible web development. I build inclusive websites compliant with the Accessibility Act using React, TypeScript, and modern frameworks."
|
||||||
|
/>
|
||||||
<meta name="author" content="Sascha Bach" />
|
<meta name="author" content="Sascha Bach" />
|
||||||
<meta name="keywords" content="freelance software developer, accessible web development, barrierefreie Webentwicklung, React developer, TypeScript, web accessibility, BFSG, Germany" />
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="freelance software developer, accessible web development, barrierefreie Webentwicklung, React developer, TypeScript, web accessibility, BFSG, Germany"
|
||||||
|
/>
|
||||||
<link rel="canonical" href="https://sascha-bach.de/" />
|
<link rel="canonical" href="https://sascha-bach.de/" />
|
||||||
|
|
||||||
<!-- Hreflang for multi-language -->
|
<!-- Hreflang for multi-language -->
|
||||||
|
|
@ -19,16 +27,28 @@
|
||||||
<!-- Open Graph / Facebook -->
|
<!-- Open Graph / Facebook -->
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:url" content="https://sascha-bach.de/" />
|
<meta property="og:url" content="https://sascha-bach.de/" />
|
||||||
<meta property="og:title" content="Sascha Bach | Freelance Software Developer" />
|
<meta
|
||||||
<meta property="og:description" content="Freelance software developer in Germany specializing in accessible web development. Building inclusive websites compliant with the Accessibility Act." />
|
property="og:title"
|
||||||
|
content="Sascha Bach | Freelance Software Developer"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Freelance software developer in Germany specializing in accessible web development. Building inclusive websites compliant with the Accessibility Act."
|
||||||
|
/>
|
||||||
<meta property="og:locale" content="en_US" />
|
<meta property="og:locale" content="en_US" />
|
||||||
<meta property="og:locale:alternate" content="de_DE" />
|
<meta property="og:locale:alternate" content="de_DE" />
|
||||||
<meta property="og:site_name" content="Sascha Bach Portfolio" />
|
<meta property="og:site_name" content="Sascha Bach Portfolio" />
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary" />
|
||||||
<meta name="twitter:title" content="Sascha Bach | Freelance Software Developer" />
|
<meta
|
||||||
<meta name="twitter:description" content="Freelance software developer in Germany specializing in accessible web development. Building inclusive websites compliant with the Accessibility Act." />
|
name="twitter:title"
|
||||||
|
content="Sascha Bach | Freelance Software Developer"
|
||||||
|
/>
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="Freelance software developer in Germany specializing in accessible web development. Building inclusive websites compliant with the Accessibility Act."
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- JSON-LD Structured Data -->
|
<!-- JSON-LD Structured Data -->
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
|
|
@ -77,7 +97,11 @@
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<noscript>
|
<noscript>
|
||||||
<h1>Sascha Bach – Freelance Software Developer</h1>
|
<h1>Sascha Bach – Freelance Software Developer</h1>
|
||||||
<p>Freelance software developer in Germany specializing in accessible web development. Building inclusive websites compliant with the Accessibility Act using React, TypeScript, and modern frameworks.</p>
|
<p>
|
||||||
|
Freelance software developer in Germany specializing in accessible web
|
||||||
|
development. Building inclusive websites compliant with the
|
||||||
|
Accessibility Act using React, TypeScript, and modern frameworks.
|
||||||
|
</p>
|
||||||
<p>Contact: freelancer [at] sascha-bach.de</p>
|
<p>Contact: freelancer [at] sascha-bach.de</p>
|
||||||
<p>Visit with JavaScript enabled for the full experience.</p>
|
<p>Visit with JavaScript enabled for the full experience.</p>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
|
||||||
90
prerender.js
90
prerender.js
|
|
@ -6,17 +6,17 @@
|
||||||
* Usage: node prerender.js (run after `vite build`)
|
* Usage: node prerender.js (run after `vite build`)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createServer } from 'http';
|
import { createServer } from 'http'
|
||||||
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs'
|
||||||
import { join, dirname } from 'path';
|
import { join, dirname } from 'path'
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url'
|
||||||
import puppeteer from 'puppeteer';
|
import puppeteer from 'puppeteer'
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||||
const DIST = join(__dirname, 'dist');
|
const DIST = join(__dirname, 'dist')
|
||||||
const PORT = 4173;
|
const PORT = 4173
|
||||||
|
|
||||||
const ROUTES = ['/', '/technical', '/imprint', '/privacy-policy'];
|
const ROUTES = ['/', '/technical', '/imprint', '/privacy-policy']
|
||||||
|
|
||||||
/** Minimal static file server that falls back to index.html (SPA). */
|
/** Minimal static file server that falls back to index.html (SPA). */
|
||||||
function startServer() {
|
function startServer() {
|
||||||
|
|
@ -31,71 +31,71 @@ function startServer() {
|
||||||
'.pdf': 'application/pdf',
|
'.pdf': 'application/pdf',
|
||||||
'.woff2': 'font/woff2',
|
'.woff2': 'font/woff2',
|
||||||
'.woff': 'font/woff',
|
'.woff': 'font/woff',
|
||||||
};
|
}
|
||||||
|
|
||||||
const server = createServer((req, res) => {
|
const server = createServer((req, res) => {
|
||||||
let filePath = join(DIST, req.url === '/' ? '/index.html' : req.url);
|
let filePath = join(DIST, req.url === '/' ? '/index.html' : req.url)
|
||||||
|
|
||||||
if (!existsSync(filePath) || !filePath.includes('.')) {
|
if (!existsSync(filePath) || !filePath.includes('.')) {
|
||||||
filePath = join(DIST, 'index.html');
|
filePath = join(DIST, 'index.html')
|
||||||
}
|
}
|
||||||
|
|
||||||
const ext = '.' + filePath.split('.').pop();
|
const ext = '.' + filePath.split('.').pop()
|
||||||
const contentType = mimeTypes[ext] || 'application/octet-stream';
|
const contentType = mimeTypes[ext] || 'application/octet-stream'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(filePath);
|
const content = readFileSync(filePath)
|
||||||
res.writeHead(200, { 'Content-Type': contentType });
|
res.writeHead(200, { 'Content-Type': contentType })
|
||||||
res.end(content);
|
res.end(content)
|
||||||
} catch {
|
} catch {
|
||||||
res.writeHead(404);
|
res.writeHead(404)
|
||||||
res.end('Not found');
|
res.end('Not found')
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log(` Server running on http://localhost:${PORT}`);
|
console.log(` Server running on http://localhost:${PORT}`)
|
||||||
resolve(server);
|
resolve(server)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function prerender() {
|
async function prerender() {
|
||||||
console.log('\n🔍 Prerendering routes...\n');
|
console.log('\n🔍 Prerendering routes...\n')
|
||||||
|
|
||||||
const server = await startServer();
|
const server = await startServer()
|
||||||
const browser = await puppeteer.launch({ headless: true });
|
const browser = await puppeteer.launch({ headless: true })
|
||||||
|
|
||||||
for (const route of ROUTES) {
|
for (const route of ROUTES) {
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage()
|
||||||
const url = `http://localhost:${PORT}${route}`;
|
const url = `http://localhost:${PORT}${route}`
|
||||||
|
|
||||||
console.log(` Rendering ${route} ...`);
|
console.log(` Rendering ${route} ...`)
|
||||||
await page.goto(url, { waitUntil: 'networkidle0', timeout: 15000 });
|
await page.goto(url, { waitUntil: 'networkidle0', timeout: 15000 })
|
||||||
|
|
||||||
// Wait a bit for React to finish any async rendering
|
// Wait a bit for React to finish any async rendering
|
||||||
await new Promise((r) => setTimeout(r, 1500));
|
await new Promise((r) => setTimeout(r, 1500))
|
||||||
|
|
||||||
const html = await page.content();
|
const html = await page.content()
|
||||||
await page.close();
|
await page.close()
|
||||||
|
|
||||||
// Write to dist/<route>/index.html
|
// Write to dist/<route>/index.html
|
||||||
const outDir = route === '/' ? DIST : join(DIST, route);
|
const outDir = route === '/' ? DIST : join(DIST, route)
|
||||||
if (!existsSync(outDir)) {
|
if (!existsSync(outDir)) {
|
||||||
mkdirSync(outDir, { recursive: true });
|
mkdirSync(outDir, { recursive: true })
|
||||||
}
|
}
|
||||||
const outFile = join(outDir, 'index.html');
|
const outFile = join(outDir, 'index.html')
|
||||||
writeFileSync(outFile, html, 'utf-8');
|
writeFileSync(outFile, html, 'utf-8')
|
||||||
console.log(` ✓ Saved ${outFile.replace(DIST, 'dist')}`);
|
console.log(` ✓ Saved ${outFile.replace(DIST, 'dist')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await browser.close();
|
await browser.close()
|
||||||
server.close();
|
server.close()
|
||||||
console.log('\n✅ Prerendering complete!\n');
|
console.log('\n✅ Prerendering complete!\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
prerender().catch((err) => {
|
prerender().catch((err) => {
|
||||||
console.error('Prerendering failed:', err);
|
console.error('Prerendering failed:', err)
|
||||||
process.exit(1);
|
process.exit(1)
|
||||||
});
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue