diff --git a/astro.config.ts b/astro.config.ts index b150148..59d561f 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -1,7 +1,6 @@ import mdx from '@astrojs/mdx'; import node from '@astrojs/node'; import { defineConfig, envField } from 'astro/config'; -import arraybuffer from 'vite-plugin-arraybuffer'; import { astroImage } from './remark-plugins/images'; // Dynamic switch the site. This is hard coded. @@ -51,7 +50,6 @@ export default defineConfig({ enabled: false, }, vite: { - plugins: [arraybuffer()], // Add this for avoiding the needless import optimize in Vite. optimizeDeps: { exclude: ['@napi-rs/canvas'] }, }, diff --git a/package-lock.json b/package-lock.json index 0d047e5..a8acc90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ "@napi-rs/canvas": "^0.1.53", "@types/lodash": "^4.17.5", "@types/luxon": "^3.4.2", - "@types/node": "^20.14.2", + "@types/node": "^20.14.5", "@types/pg": "^8.11.6", "@types/qrcode-svg": "^1.1.4", "@types/unist": "^3.0.2", @@ -42,8 +42,7 @@ "rimraf": "^5.0.7", "sharp": "^0.33.4", "typescript": "^5.4.5", - "unist-util-visit": "^5.0.0", - "vite-plugin-arraybuffer": "^0.0.7" + "unist-util-visit": "^5.0.0" } }, "node_modules/@ampproject/remapping": { @@ -2741,9 +2740,9 @@ } }, "node_modules/@types/node": { - "version": "20.14.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", - "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", + "version": "20.14.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.5.tgz", + "integrity": "sha512-aoRR+fJkZT2l0aGOJhuA8frnCSoNX6W7U2mpNq63+BxBIj5BQFt8rHy627kijCmm63ijdSdwvGgpUsU6MBsZZA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -3993,9 +3992,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.803", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.803.tgz", - "integrity": "sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==", + "version": "1.4.805", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.805.tgz", + "integrity": "sha512-8W4UJwX/w9T0QSzINJckTKG6CYpAUTqsaWcWIsdud3I1FYJcMgW9QqT1/4CBff/pP/TihWh13OmiyY8neto6vw==", "license": "ISC" }, "node_modules/emmet": { @@ -8708,13 +8707,6 @@ } } }, - "node_modules/vite-plugin-arraybuffer": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/vite-plugin-arraybuffer/-/vite-plugin-arraybuffer-0.0.7.tgz", - "integrity": "sha512-c4Egxj7NUGco2Ggw9KUBToOxuc7Ws7mWm0hz/QnaT5Ph8ycC7ypMBOD31NuhPSx+wdUvgIbS1XpMvJLSdHakPA==", - "dev": true, - "license": "MIT" - }, "node_modules/vitefu": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", diff --git a/package.json b/package.json index 0ad3328..080a773 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@napi-rs/canvas": "^0.1.53", "@types/lodash": "^4.17.5", "@types/luxon": "^3.4.2", - "@types/node": "^20.14.2", + "@types/node": "^20.14.5", "@types/pg": "^8.11.6", "@types/qrcode-svg": "^1.1.4", "@types/unist": "^3.0.2", @@ -74,7 +74,6 @@ "rimraf": "^5.0.7", "sharp": "^0.33.4", "typescript": "^5.4.5", - "unist-util-visit": "^5.0.0", - "vite-plugin-arraybuffer": "^0.0.7" + "unist-util-visit": "^5.0.0" } } diff --git a/src/assets/og/logo-dark.png b/src/assets/og/logo-dark.png deleted file mode 100644 index cff93b5..0000000 Binary files a/src/assets/og/logo-dark.png and /dev/null differ diff --git a/src/assets/og/open-graph.png b/src/assets/og/open-graph.png deleted file mode 100644 index 1a1ee64..0000000 Binary files a/src/assets/og/open-graph.png and /dev/null differ diff --git a/src/env.d.ts b/src/env.d.ts index 204abbc..338ba9e 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -1,4 +1,3 @@ /// /// /// -/// diff --git a/src/helpers/images.ts b/src/helpers/images.ts index dde65d2..bf3a4d7 100644 --- a/src/helpers/images.ts +++ b/src/helpers/images.ts @@ -52,7 +52,6 @@ export const blurStyle = (image: Image) => ({ // Copied and modified https://github.com/zce/velite/blob/main/src/assets.ts export const imageMetadata = async (publicPath: string): Promise => { - // Load for sharp on demand for avoiding the resolver issues in production. const { default: sharp } = await import('sharp'); if (!publicPath.startsWith('/')) { diff --git a/src/helpers/og.ts b/src/helpers/og.ts index 8ed39ee..9a4a359 100644 --- a/src/helpers/og.ts +++ b/src/helpers/og.ts @@ -9,8 +9,19 @@ import { options } from '@/helpers/schema'; import { Canvas, GlobalFonts, Image, type SKRSContext2D } from '@napi-rs/canvas'; import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; -import font from '../assets/og/NotoSansSC-Bold.ttf?arraybuffer'; -import logoDark from '../assets/og/logo-dark.png?arraybuffer'; + +const darkLogo = ` + + + + + + + + + + +`; const getStringWidth = (text: string, fontSize: number) => { let result = 0; @@ -114,18 +125,20 @@ const fetchCover = async (cover: string): Promise => { return await readFile(coverPath); }; -export { default as defaultOpenGraph } from '../assets/og/open-graph.png?arraybuffer'; - export interface OpenGraphProps { title: string; summary: string; cover: string; } -export const drawOpenGraph = async ({ title, summary, cover }: OpenGraphProps) => { +export const defaultOpenGraph = async (): Promise => { + return await fetchCover('/images/default-cover.jpg'); +}; + +export const drawOpenGraph = async ({ title, summary, cover }: OpenGraphProps): Promise => { // Register the font if it doesn't exist if (!GlobalFonts.has('NotoSansSC-Bold')) { - const fontBuffer = Buffer.from(font); + const fontBuffer = await readFile(join(process.cwd(), 'src/assets/og/NotoSansSC-Bold.ttf')); GlobalFonts.register(fontBuffer, 'NotoSansSC-Bold'); } @@ -135,7 +148,7 @@ export const drawOpenGraph = async ({ title, summary, cover }: OpenGraphProps) = // Generate the logo image const logoImage = new Image(); - logoImage.src = Buffer.from(logoDark); + logoImage.src = Buffer.from(darkLogo, 'utf-8'); // Mark sure the summary length is small enough to fit in const description = `${summary @@ -171,5 +184,20 @@ export const drawOpenGraph = async ({ title, summary, cover }: OpenGraphProps) = ctx.restore(); - return await canvas.encode('png'); + const encodedImage = await canvas.encode('png'); + return await compressImage(encodedImage); +}; + +const compressImage = async (buf: Buffer): Promise => { + const { default: sharp } = await import('sharp'); + return await sharp(buf) + .png({ + compressionLevel: 9, + adaptiveFiltering: true, + force: true, + palette: true, + quality: 75, + progressive: true, + }) + .toBuffer(); }; diff --git a/src/pages/og/[slug].png.ts b/src/pages/og/[slug].png.ts index 40f04d2..3829d87 100644 --- a/src/pages/og/[slug].png.ts +++ b/src/pages/og/[slug].png.ts @@ -2,8 +2,8 @@ import { defaultOpenGraph, drawOpenGraph } from '@/helpers/og'; import { getPage, getPost, pages, posts } from '@/helpers/schema'; import type { APIRoute } from 'astro'; -const fallback = () => - new Response(defaultOpenGraph, { +const fallback = async () => + new Response(await defaultOpenGraph(), { headers: { 'Content-Type': 'image/png' }, }); @@ -12,7 +12,7 @@ export const prerender = true; export const GET: APIRoute = async ({ params }) => { const slug = params.slug; if (!slug) { - return fallback(); + return await fallback(); } let title: string; @@ -25,7 +25,7 @@ export const GET: APIRoute = async ({ params }) => { // Fallback to query from pages const page = getPage(slug); if (!page) { - return fallback(); + return await fallback(); } title = page.title;