refactor: only index md files
This commit is contained in:
parent
34c2f9e346
commit
c6889ac65d
|
|
@ -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,9 +104,13 @@ 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)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -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}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue