refactor: only index md files

This commit is contained in:
hypercross 2026-02-26 14:14:26 +08:00
parent 34c2f9e346
commit c6889ac65d
2 changed files with 58 additions and 54 deletions

View File

@ -1,9 +1,9 @@
import type { ServeCommandHandler } from '../types.js';
import { createServer, Server, IncomingMessage, ServerResponse } from 'http';
import { readdirSync, statSync, readFileSync, existsSync } from 'fs';
import { createReadStream } from 'fs';
import { join, resolve, extname, sep, relative } from 'path';
import { watch } from 'chokidar';
import type { ServeCommandHandler } from "../types.js";
import { createServer, Server, IncomingMessage, ServerResponse } from "http";
import { readdirSync, statSync, readFileSync, existsSync } from "fs";
import { createReadStream } from "fs";
import { join, resolve, extname, sep, relative } from "path";
import { watch } from "chokidar";
interface ContentIndex {
[path: string]: string;
@ -13,19 +13,19 @@ interface ContentIndex {
* MIME
*/
const MIME_TYPES: Record<string, string> = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.md': 'text/markdown',
'.csv': 'text/csv',
'.woff': 'font/woff',
'.woff2': 'font/woff2',
".html": "text/html",
".css": "text/css",
".js": "application/javascript",
".json": "application/json",
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".svg": "image/svg+xml",
".md": "text/markdown",
".csv": "text/csv",
".woff": "font/woff",
".woff2": "font/woff2",
};
/**
@ -33,11 +33,11 @@ const MIME_TYPES: Record<string, string> = {
*/
function getMimeType(filePath: string): string {
const ext = extname(filePath).toLowerCase();
return MIME_TYPES[ext] || 'application/octet-stream';
return MIME_TYPES[ext] || "application/octet-stream";
}
/**
* .md .csv
* .md
*/
function scanDirectory(dir: string): ContentIndex {
const index: ContentIndex = {};
@ -46,18 +46,18 @@ function scanDirectory(dir: string): ContentIndex {
const entries = readdirSync(currentPath);
for (const entry of entries) {
if (entry.startsWith('.') || entry === 'node_modules') continue;
if (entry.startsWith(".") || entry === "node_modules") continue;
const fullPath = join(currentPath, entry);
const relPath = relativePath ? join(relativePath, entry) : entry;
const normalizedRelPath = '/' + relPath.split(sep).join('/');
const normalizedRelPath = "/" + relPath.split(sep).join("/");
const stats = statSync(fullPath);
if (stats.isDirectory()) {
scan(fullPath, relPath);
} else if (entry.endsWith('.md') || entry.endsWith('.csv')) {
} else if (entry.endsWith(".md")) {
try {
const content = readFileSync(fullPath, 'utf-8');
const content = readFileSync(fullPath, "utf-8");
index[normalizedRelPath] = content;
} catch (e) {
console.error(`读取文件失败:${fullPath}`, e);
@ -66,7 +66,7 @@ function scanDirectory(dir: string): ContentIndex {
}
}
scan(dir, '');
scan(dir, "");
return index;
}
@ -75,8 +75,8 @@ function scanDirectory(dir: string): ContentIndex {
*/
function sendFile(res: ServerResponse, filePath: string) {
res.writeHead(200, {
'Content-Type': getMimeType(filePath),
'Access-Control-Allow-Origin': '*',
"Content-Type": getMimeType(filePath),
"Access-Control-Allow-Origin": "*",
});
createReadStream(filePath).pipe(res);
}
@ -85,8 +85,8 @@ function sendFile(res: ServerResponse, filePath: string) {
* 404
*/
function send404(res: ServerResponse) {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not Found');
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("Not Found");
}
/**
@ -94,8 +94,8 @@ function send404(res: ServerResponse) {
*/
function sendJson(res: ServerResponse, data: unknown) {
res.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
});
res.end(JSON.stringify(data, null, 2));
}
@ -104,7 +104,11 @@ function sendJson(res: ServerResponse, data: unknown) {
*
* @returns true
*/
function tryServeStatic(res: ServerResponse, filePath: string, dir: string): boolean {
function tryServeStatic(
res: ServerResponse,
filePath: string,
dir: string,
): boolean {
const fullPath = join(dir, filePath);
if (!existsSync(fullPath)) {
@ -129,17 +133,17 @@ function createRequestHandler(
getIndex: () => ContentIndex,
) {
return (req: IncomingMessage, res: ServerResponse) => {
const url = req.url || '/';
const filePath = decodeURIComponent(url.split('?')[0]);
const url = req.url || "/";
const filePath = decodeURIComponent(url.split("?")[0]);
// 1. 处理 /__CONTENT_INDEX.json
if (filePath === '/__CONTENT_INDEX.json') {
if (filePath === "/__CONTENT_INDEX.json") {
sendJson(res, getIndex());
return;
}
// 2. 处理 /static/ 目录(从 dist/web
if (filePath.startsWith('/static/')) {
if (filePath.startsWith("/static/")) {
if (tryServeStatic(res, filePath, distDir)) {
return;
}
@ -154,7 +158,7 @@ function createRequestHandler(
}
// 4. 处理 SPA 路由:返回 index.html
if (tryServeStatic(res, 'index.html', distDir)) {
if (tryServeStatic(res, "index.html", distDir)) {
return;
}
@ -168,16 +172,16 @@ function createRequestHandler(
*/
export const serveCommand: ServeCommandHandler = async (dir, options) => {
const contentDir = resolve(dir);
const distDir = resolve(process.cwd(), 'dist/web');
const distDir = resolve(process.cwd(), "dist/web");
let contentIndex: ContentIndex = {};
// 扫描内容目录生成索引
console.log('扫描内容目录...');
console.log("扫描内容目录...");
contentIndex = scanDirectory(contentDir);
console.log(`已索引 ${Object.keys(contentIndex).length} 个文件`);
// 监听文件变化
console.log('监听文件变化...');
console.log("监听文件变化...");
const watcher = watch(contentDir, {
ignored: /(^|[\/\\])\../,
persistent: true,
@ -185,11 +189,11 @@ export const serveCommand: ServeCommandHandler = async (dir, options) => {
});
watcher
.on('add', (path) => {
if (path.endsWith('.md') || path.endsWith('.csv')) {
.on("add", (path) => {
if (path.endsWith(".md")) {
try {
const content = readFileSync(path, 'utf-8');
const relPath = '/' + relative(contentDir, path).split(sep).join('/');
const content = readFileSync(path, "utf-8");
const relPath = "/" + relative(contentDir, path).split(sep).join("/");
contentIndex[relPath] = content;
console.log(`[新增] ${relPath}`);
} catch (e) {
@ -197,11 +201,11 @@ export const serveCommand: ServeCommandHandler = async (dir, options) => {
}
}
})
.on('change', (path) => {
if (path.endsWith('.md') || path.endsWith('.csv')) {
.on("change", (path) => {
if (path.endsWith(".md")) {
try {
const content = readFileSync(path, 'utf-8');
const relPath = '/' + relative(contentDir, path).split(sep).join('/');
const content = readFileSync(path, "utf-8");
const relPath = "/" + relative(contentDir, path).split(sep).join("/");
contentIndex[relPath] = content;
console.log(`[更新] ${relPath}`);
} catch (e) {
@ -209,9 +213,9 @@ export const serveCommand: ServeCommandHandler = async (dir, options) => {
}
}
})
.on('unlink', (path) => {
if (path.endsWith('.md') || path.endsWith('.csv')) {
const relPath = '/' + relative(contentDir, path).split(sep).join('/');
.on("unlink", (path) => {
if (path.endsWith(".md")) {
const relPath = "/" + relative(contentDir, path).split(sep).join("/");
delete contentIndex[relPath];
console.log(`[删除] ${relPath}`);
}

View File

@ -23,7 +23,7 @@ async function loadDevIndex(): Promise<void> {
if (typeof import.meta !== "undefined" && import.meta.glob) {
try {
// @ts-ignore - 只加载 .csv 和 .md 文件
const modules = import.meta.glob("../../content/**/*.{csv,md}", {
const modules = import.meta.glob("../../content/**/*.md", {
as: "raw",
eager: true,
});