Neue services
This commit is contained in:
206
scripts/prerender-routes.ts
Normal file
206
scripts/prerender-routes.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { blogPostData, locationData, serviceData } from '../src/data/seoData';
|
||||
|
||||
type RouteMeta = {
|
||||
route: string;
|
||||
title: string;
|
||||
description: string;
|
||||
canonicalUrl: string;
|
||||
keywords?: string[];
|
||||
schema?: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const DIST_DIR = path.resolve(process.cwd(), 'dist');
|
||||
const BASE_URL = 'https://bayareait.services';
|
||||
const DEFAULT_OG_IMAGE = `${BASE_URL}/logo.svg`;
|
||||
|
||||
const staticRoutes: RouteMeta[] = [
|
||||
{
|
||||
route: '/',
|
||||
title: 'IT Service & IT Support for Businesses in Corpus Christi, TX',
|
||||
description:
|
||||
'Reliable IT support and IT services for businesses in Corpus Christi, TX. Fast response, outsourced IT support and help desk solutions.',
|
||||
canonicalUrl: `${BASE_URL}/`,
|
||||
keywords: ['IT Service', 'IT Support', 'Corpus Christi', 'IT Help Desk'],
|
||||
schema: {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'ITService',
|
||||
name: 'Bay Area IT',
|
||||
url: BASE_URL,
|
||||
telephone: '+1-361-765-8400',
|
||||
areaServed: ['Corpus Christi', 'Portland', 'Rockport', 'Aransas Pass', 'Kingsville'],
|
||||
},
|
||||
},
|
||||
{
|
||||
route: '/about',
|
||||
title: 'About Bay Area IT | Local IT Support in Corpus Christi',
|
||||
description:
|
||||
'Learn about Bay Area IT, a local IT partner serving Corpus Christi and the Coastal Bend with practical support, reliable service, and over 25 years of experience.',
|
||||
canonicalUrl: `${BASE_URL}/about`,
|
||||
schema: {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
name: 'Bay Area IT',
|
||||
url: BASE_URL,
|
||||
},
|
||||
},
|
||||
{
|
||||
route: '/services',
|
||||
title: 'IT Services | Bay Area IT Support, Email, Networking and Web',
|
||||
description:
|
||||
'Explore Bay Area IT services for Corpus Christi businesses, including help desk support, business email, networking, hardware, web design, and day-to-day IT support.',
|
||||
canonicalUrl: `${BASE_URL}/services`,
|
||||
},
|
||||
{
|
||||
route: '/blog',
|
||||
title: 'Blog | Bay Area IT Insights for Corpus Christi Businesses',
|
||||
description:
|
||||
'Read practical IT guidance for Corpus Christi and Coastal Bend businesses, from managed IT support and costs to business email and local service coverage.',
|
||||
canonicalUrl: `${BASE_URL}/blog`,
|
||||
},
|
||||
{
|
||||
route: '/contact',
|
||||
title: 'Contact Bay Area IT | Free IT Assessment in Corpus Christi',
|
||||
description:
|
||||
'Talk to Bay Area IT about managed IT support, help desk coverage, business email, networking, and technology support across Corpus Christi and the Coastal Bend.',
|
||||
canonicalUrl: `${BASE_URL}/contact`,
|
||||
},
|
||||
{
|
||||
route: '/locations',
|
||||
title: 'IT Support Service Areas - Corpus Christi & Coastal Bend, TX',
|
||||
description:
|
||||
'Bay Area IT provides IT support and IT services throughout the Coastal Bend. View all cities we serve in the Corpus Christi area.',
|
||||
canonicalUrl: `${BASE_URL}/locations`,
|
||||
},
|
||||
{
|
||||
route: '/privacy-policy',
|
||||
title: 'Privacy Policy | Bay Area IT',
|
||||
description:
|
||||
'Read how Bay Area IT collects, uses, and protects information submitted through this website.',
|
||||
canonicalUrl: `${BASE_URL}/privacy-policy`,
|
||||
},
|
||||
{
|
||||
route: '/terms-of-service',
|
||||
title: 'Terms of Service | Bay Area IT',
|
||||
description:
|
||||
'Review the Bay Area IT terms covering use of this website and our IT support services.',
|
||||
canonicalUrl: `${BASE_URL}/terms-of-service`,
|
||||
},
|
||||
];
|
||||
|
||||
const dynamicRoutes: RouteMeta[] = [
|
||||
...locationData.map((item) => ({
|
||||
route: `/${item.slug}`,
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
canonicalUrl: `${BASE_URL}/${item.slug}`,
|
||||
keywords: item.keywords,
|
||||
schema: {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'LocalBusiness',
|
||||
name: 'Bay Area IT',
|
||||
url: `${BASE_URL}/${item.slug}`,
|
||||
areaServed: item.city,
|
||||
},
|
||||
})),
|
||||
...serviceData.map((item) => ({
|
||||
route: `/${item.slug}`,
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
canonicalUrl: `${BASE_URL}/${item.slug}`,
|
||||
keywords: item.keywords,
|
||||
schema: {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Service',
|
||||
name: item.h1,
|
||||
provider: {
|
||||
'@type': 'Organization',
|
||||
name: 'Bay Area IT',
|
||||
},
|
||||
url: `${BASE_URL}/${item.slug}`,
|
||||
},
|
||||
})),
|
||||
...blogPostData
|
||||
.filter((item) => !item.redirect)
|
||||
.map((item) => ({
|
||||
route: `/${item.slug}`,
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
canonicalUrl: `${BASE_URL}/${item.slug}`,
|
||||
keywords: item.keywords,
|
||||
schema: {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BlogPosting',
|
||||
headline: item.h1,
|
||||
description: item.description,
|
||||
url: `${BASE_URL}/${item.slug}`,
|
||||
publisher: {
|
||||
'@type': 'Organization',
|
||||
name: 'Bay Area IT',
|
||||
},
|
||||
},
|
||||
})),
|
||||
];
|
||||
|
||||
function escapeHtml(value: string) {
|
||||
return value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
function buildHead(meta: RouteMeta) {
|
||||
const keywords = meta.keywords?.length
|
||||
? `\n <meta name="keywords" content="${escapeHtml(meta.keywords.join(', '))}" />`
|
||||
: '';
|
||||
const schema = meta.schema
|
||||
? `\n <script type="application/ld+json">${JSON.stringify(meta.schema)}</script>`
|
||||
: '';
|
||||
|
||||
return ` <title>${escapeHtml(meta.title)}</title>
|
||||
<meta name="description" content="${escapeHtml(meta.description)}" />${keywords}
|
||||
<link rel="canonical" href="${meta.canonicalUrl}" />
|
||||
<meta property="og:title" content="${escapeHtml(meta.title)}" />
|
||||
<meta property="og:description" content="${escapeHtml(meta.description)}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="Bay Area IT" />
|
||||
<meta property="og:url" content="${meta.canonicalUrl}" />
|
||||
<meta property="og:image" content="${DEFAULT_OG_IMAGE}" />
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:title" content="${escapeHtml(meta.title)}" />
|
||||
<meta name="twitter:description" content="${escapeHtml(meta.description)}" />${schema}`;
|
||||
}
|
||||
|
||||
function injectHead(template: string, meta: RouteMeta) {
|
||||
const withoutTitle = template.replace(/<title>[\s\S]*?<\/title>/i, '');
|
||||
return withoutTitle.replace('</head>', `${buildHead(meta)}\n </head>`);
|
||||
}
|
||||
|
||||
async function writeRouteHtml(template: string, meta: RouteMeta) {
|
||||
const html = injectHead(template, meta);
|
||||
const cleanRoute = meta.route === '/' ? '' : meta.route.replace(/^\/+/, '');
|
||||
const filePath =
|
||||
meta.route === '/'
|
||||
? path.join(DIST_DIR, 'index.html')
|
||||
: path.join(DIST_DIR, cleanRoute, 'index.html');
|
||||
|
||||
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
||||
await fs.writeFile(filePath, html, 'utf8');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const templatePath = path.join(DIST_DIR, 'index.html');
|
||||
const template = await fs.readFile(templatePath, 'utf8');
|
||||
const allRoutes = [...staticRoutes, ...dynamicRoutes];
|
||||
|
||||
await Promise.all(allRoutes.map((meta) => writeRouteHtml(template, meta)));
|
||||
console.log(`Prerendered ${allRoutes.length} route HTML files.`);
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('Failed to prerender route HTML files.');
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user