Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/public/og-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions website/assets/og-image.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"dev": "rspress dev",
"build": "rspress build",
"render:og-image": "node scripts/render-og-image.cjs",
"preview": "rspress preview",
"type-check": "tsc --noEmit -p tsconfig.json",
"check:links": "node ../scripts/validation/check-site-links.cjs",
Expand Down
48 changes: 41 additions & 7 deletions website/rspress.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ import type {
UserConfig,
} from '@rspress/core';
import { pluginSitemap } from '@rspress/plugin-sitemap';
import {
applyBuiltPageMeta,
docsSiteOrigin,
localeDescriptions,
siteBase,
socialHeadEntries,
} from './social-meta';

type MarkdownAstNode = {
type?: string;
Expand Down Expand Up @@ -386,7 +393,6 @@ const isExternalLink = (link: string) => /^https?:\/\//.test(link);
const repositoryRoot = path.join(import.meta.dirname, '..');
const docsRoot = path.join(repositoryRoot, 'docs');
const githubRepositoryUrl = 'https://github.com/devcodex-labs/monSQLize';
const siteBase = '/monSQLize/';

const isExternalOrSiteLink = (url: string) =>
/^(?:[a-z][a-z0-9+.-]*:|\/\/|#|\/)/i.test(url);
Expand Down Expand Up @@ -535,32 +541,60 @@ const rewriteGeneratedLanguageSwitchLinks: RspressPlugin = {
name: 'rewrite-generated-language-switch-links',
afterBuild(config) {
const builtSiteDir = path.resolve(import.meta.dirname, config.outDir || 'dist');
const htmlFiles = collectHtmlFiles(builtSiteDir);
let descriptionCorrected = 0;
let socialAdded = 0;
const correctedPages: string[] = [];
const socialPages: string[] = [];

collectHtmlFiles(builtSiteDir).forEach(htmlFile => {
htmlFiles.forEach(htmlFile => {
const originalContent = fs.readFileSync(htmlFile, 'utf8');
let rewrittenContent = originalContent;

generatedLanguageSwitchHrefAliases.forEach((target, source) => {
rewrittenContent = rewrittenContent.split(source).join(target);
});

const relativePath = path.relative(builtSiteDir, htmlFile);
const meta = applyBuiltPageMeta(rewrittenContent, relativePath);
if (meta.descriptionCorrected) {
descriptionCorrected += 1;
correctedPages.push(relativePath);
}
if (meta.socialAdded) {
socialAdded += 1;
socialPages.push(relativePath);
}
rewrittenContent = meta.html;

if (rewrittenContent !== originalContent) {
fs.writeFileSync(htmlFile, rewrittenContent);
}
});

console.log(`[docs-meta] pages=${htmlFiles.length} description-corrections=${descriptionCorrected} social-tags-added=${socialAdded}`);
if (correctedPages.length > 0) {
console.log(`[docs-meta] corrected descriptions: ${correctedPages.join(', ')}`);
}
if (socialPages.length > 0) {
console.log(`[docs-meta] inserted social tags: ${socialPages.join(', ')}`);
}
}
};

const config: UserConfig = {
root: path.join(import.meta.dirname, '..', 'docs'),
themeDir: path.join(import.meta.dirname, 'theme'),
base: siteBase,
siteOrigin: docsSiteOrigin,
lang: 'en',
title: 'monSQLize',
icon: '/favicon.svg',
globalStyles: path.join(import.meta.dirname, 'styles', 'home.css'),
description: 'A database-native TypeScript production data runtime layer with MongoDB stable today, plus cache, pools, transactions, models, sync, and observability.',
// Locale descriptions are the fallback. A root description would override them
// for every page that has no extracted summary, including Chinese pages.
outDir: 'dist',
head: socialHeadEntries(),
builderConfig: {
performance: {
buildCache: false,
Expand All @@ -584,13 +618,13 @@ const config: UserConfig = {
lang: 'en',
label: 'English',
title: 'monSQLize',
description: 'A database-native TypeScript production data runtime layer.'
description: localeDescriptions.en
},
{
lang: 'zh',
label: '简体中文',
title: 'monSQLize',
description: '数据库原生 TypeScript 生产数据运行时增强层。'
description: localeDescriptions.zh
}
],
plugins: [
Expand Down Expand Up @@ -618,7 +652,7 @@ const config: UserConfig = {
lang: 'en',
label: 'English',
title: 'monSQLize',
description: 'A database-native TypeScript production data runtime layer.',
description: localeDescriptions.en,
nav: englishNav,
sidebar: {
'/': englishSidebar
Expand All @@ -628,7 +662,7 @@ const config: UserConfig = {
lang: 'zh',
label: '简体中文',
title: 'monSQLize',
description: '数据库原生 TypeScript 生产数据运行时增强层。',
description: localeDescriptions.zh,
nav: chineseNav,
sidebar: {
'/zh/': chineseSidebar
Expand Down
3 changes: 3 additions & 0 deletions website/scripts/check-browser.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const contentTypes = {
'.js': 'text/javascript; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.webp': 'image/webp',
'.woff2': 'font/woff2',
};
Expand Down
72 changes: 72 additions & 0 deletions website/scripts/render-og-image.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env node
'use strict';

/**
* Render the docs social card.
*
* Source: website/assets/og-image.svg
* Output: docs/public/og-image.png (1200x630, committed, copied into the site build)
*
* Requires rsvg-convert (librsvg2-bin) and optipng. The SVG uses Inter, with
* Liberation Sans as a fallback. Re-run with: npm run render:og-image
*/

const fs = require('node:fs');
const path = require('node:path');
const { spawnSync } = require('node:child_process');

const websiteRoot = path.resolve(__dirname, '..');
const svgPath = path.join(websiteRoot, 'assets', 'og-image.svg');
const pngPath = path.resolve(websiteRoot, '..', 'docs', 'public', 'og-image.png');
const width = 1200;
const height = 630;
const maxBytes = 300 * 1024;

function run(command, args) {
const result = spawnSync(command, args, { stdio: 'inherit' });
if (result.error) {
console.error(`[og-image] failed to run ${command}: ${result.error.message}`);
process.exit(1);
}
if (result.status !== 0) {
process.exit(result.status ?? 1);
}
}

function readPngSize(filePath) {
const header = Buffer.alloc(24);
const fd = fs.openSync(filePath, 'r');
try {
fs.readSync(fd, header, 0, 24, 0);
} finally {
fs.closeSync(fd);
}
if (header.toString('ascii', 1, 4) !== 'PNG') {
throw new Error(`${filePath} is not a PNG`);
}
return {
width: header.readUInt32BE(16),
height: header.readUInt32BE(20),
};
}

run('rsvg-convert', [
'--width', String(width),
'--height', String(height),
'--output', pngPath,
svgPath,
]);
run('optipng', ['-o7', '-strip', 'all', '-quiet', pngPath]);

const size = readPngSize(pngPath);
const bytes = fs.statSync(pngPath).size;
if (size.width !== width || size.height !== height) {
console.error(`[og-image] expected ${width}x${height}, got ${size.width}x${size.height}`);
process.exit(1);
}
if (bytes > maxBytes) {
console.error(`[og-image] ${bytes} bytes exceeds ${maxBytes} bytes`);
process.exit(1);
}

console.log(`[og-image] wrote ${path.relative(path.resolve(websiteRoot, '..'), pngPath)} ${size.width}x${size.height} ${bytes} bytes`);
Loading
Loading