feat: add Open Graph image generation and SEO metadata
- Introduced `PageSeo` component for managing page metadata for social sharing. - Created Open Graph image templates using SVG and static fonts for better visual representation. - Implemented service alert management with functions to check active alerts and format route numbers. - Added utility functions for generating and watching alert validity. - Developed a script to generate Open Graph images dynamically based on page content. - Included tests for service alert functionalities to ensure correctness and reliability.
This commit is contained in:
65
scripts/generate-og-images.mjs
Normal file
65
scripts/generate-og-images.mjs
Normal file
@@ -0,0 +1,65 @@
|
||||
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { ImageResponse } from 'next/og.js';
|
||||
import { ogImageSize, socialLocales, socialPages } from '../lib/social.mjs';
|
||||
import { createOgImage } from '../lib/og-template.mjs';
|
||||
|
||||
const projectRoot = new URL('../', import.meta.url);
|
||||
const outputDirectory = new URL('public/images/og/', projectRoot);
|
||||
|
||||
async function svgAsset(path, color) {
|
||||
const svg = (await readFile(new URL(path, projectRoot), 'utf8'))
|
||||
.replaceAll('currentColor', color)
|
||||
.replace(/<svg\b([^>]*)>/, (tag, attributes) =>
|
||||
/\sfill=/.test(attributes) ? tag : `<svg${attributes} fill="${color}">`
|
||||
);
|
||||
return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`;
|
||||
}
|
||||
|
||||
async function font(name, file, weight) {
|
||||
return {
|
||||
name,
|
||||
data: await readFile(new URL(`assets/fonts/og/${file}`, projectRoot)),
|
||||
weight,
|
||||
style: 'normal',
|
||||
};
|
||||
}
|
||||
|
||||
const fonts = await Promise.all([
|
||||
font('Manrope', 'Manrope-SemiBold.ttf', 600),
|
||||
font('Manrope', 'Manrope-ExtraBold.ttf', 800),
|
||||
font('Caveat', 'Caveat-Bold.ttf', 700),
|
||||
]);
|
||||
const [logo, star5, star6, thinStar, shield, note, love, background] =
|
||||
await Promise.all([
|
||||
readFile(new URL('assets/images/logo-alt.png', projectRoot)).then(
|
||||
(data) => `data:image/png;base64,${data.toString('base64')}`
|
||||
),
|
||||
svgAsset('assets/images/thick-round-star-5.svg', '#9d7ba0'),
|
||||
svgAsset('assets/images/thick-round-star-6.svg', '#5780cc'),
|
||||
svgAsset('assets/images/thin-star-big.svg', '#0058ff'),
|
||||
svgAsset('assets/icons/shield.svg', '#1d4fac'),
|
||||
svgAsset('assets/icons/note.svg', '#715573'),
|
||||
svgAsset('assets/icons/love.svg', '#ff003d'),
|
||||
svgAsset('assets/images/og-background.svg', '#f7f9fd'),
|
||||
]);
|
||||
const assets = { logo, star5, star6, thinStar, shield, note, love, background };
|
||||
|
||||
await mkdir(outputDirectory, { recursive: true });
|
||||
|
||||
for (const [id, page] of Object.entries(socialPages)) {
|
||||
for (const locale of socialLocales) {
|
||||
const response = new ImageResponse(createOgImage(page, locale, assets), {
|
||||
...ogImageSize,
|
||||
fonts,
|
||||
});
|
||||
const png = Buffer.from(await response.arrayBuffer());
|
||||
const destination = new URL(`${id}-${locale}.png`, outputDirectory);
|
||||
const temporary = new URL(`${id}-${locale}.png.tmp`, outputDirectory);
|
||||
await writeFile(temporary, png);
|
||||
await rename(temporary, destination);
|
||||
console.log(
|
||||
`Generated ${fileURLToPath(destination)} (${png.length} bytes)`
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user