117 lines
3.2 KiB
TypeScript
117 lines
3.2 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import matter from 'gray-matter';
|
|
import { unified } from 'unified';
|
|
import remarkParse from 'remark-parse';
|
|
import remarkMath from 'remark-math';
|
|
import remarkGfm from 'remark-gfm';
|
|
import remarkRehype from 'remark-rehype';
|
|
import rehypeKatex from 'rehype-katex';
|
|
import rehypeStringify from 'rehype-stringify';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
const CONTENT_DIR = path.resolve(__dirname, '../content');
|
|
const OUTPUT_DIR = path.resolve(__dirname, '../public/content');
|
|
|
|
interface ContentMeta {
|
|
slug: string;
|
|
title: string;
|
|
description: string;
|
|
date: string;
|
|
tags: string[];
|
|
order?: number;
|
|
cover?: string;
|
|
}
|
|
|
|
const processor = unified()
|
|
.use(remarkParse)
|
|
.use(remarkGfm)
|
|
.use(remarkMath)
|
|
.use(remarkRehype)
|
|
.use(rehypeKatex)
|
|
.use(rehypeStringify);
|
|
|
|
async function compileMarkdown(content: string): Promise<string> {
|
|
const result = await processor.process(content);
|
|
return String(result);
|
|
}
|
|
|
|
async function processContentType(type: 'docs' | 'blog') {
|
|
const typeDir = path.join(CONTENT_DIR, type);
|
|
const manifest: Record<string, ContentMeta[]> = {};
|
|
|
|
for (const lang of ['en', 'zh']) {
|
|
const langDir = path.join(typeDir, lang);
|
|
if (!fs.existsSync(langDir)) {
|
|
manifest[lang] = [];
|
|
continue;
|
|
}
|
|
|
|
const files = fs.readdirSync(langDir).filter(f => f.endsWith('.md'));
|
|
const entries: ContentMeta[] = [];
|
|
|
|
for (const file of files) {
|
|
const filePath = path.join(langDir, file);
|
|
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
const { data, content } = matter(raw);
|
|
|
|
const meta: ContentMeta = {
|
|
slug: data.slug || file.replace(/\.md$/, ''),
|
|
title: data.title || '',
|
|
description: data.description || '',
|
|
date: data.date ? String(data.date) : '',
|
|
tags: data.tags || [],
|
|
order: data.order,
|
|
cover: data.cover,
|
|
};
|
|
|
|
entries.push(meta);
|
|
|
|
// Compile and write individual content file
|
|
const html = await compileMarkdown(content);
|
|
const outputPath = path.join(OUTPUT_DIR, type, lang, `${meta.slug}.json`);
|
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
fs.writeFileSync(outputPath, JSON.stringify({ meta, html }, null, 2));
|
|
|
|
console.log(` ${lang}/${meta.slug}`);
|
|
}
|
|
|
|
// Sort: docs by order, blog by date descending
|
|
if (type === 'docs') {
|
|
entries.sort((a, b) => (a.order ?? 999) - (b.order ?? 999));
|
|
} else {
|
|
entries.sort((a, b) => b.date.localeCompare(a.date));
|
|
}
|
|
|
|
manifest[lang] = entries;
|
|
}
|
|
|
|
// Write manifest
|
|
const manifestPath = path.join(OUTPUT_DIR, `${type}-manifest.json`);
|
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
console.log(` -> ${type}-manifest.json`);
|
|
}
|
|
|
|
async function main() {
|
|
console.log('Building content...');
|
|
|
|
// Clean output
|
|
if (fs.existsSync(OUTPUT_DIR)) {
|
|
fs.rmSync(OUTPUT_DIR, { recursive: true });
|
|
}
|
|
|
|
await processContentType('docs');
|
|
await processContentType('blog');
|
|
|
|
console.log('Content build complete!');
|
|
}
|
|
|
|
main().catch(err => {
|
|
console.error('Content build failed:', err);
|
|
process.exit(1);
|
|
});
|