feat: rename project to TTTK
- Update package name and CLI binary to 'tttk' - Revise documentation to reflect new name - Add default serve behavior when no subcommand is given - Add directory existence check in serve command - Refactor inline comments to Chinese
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
import type { CompileCommandHandler } from '../types.js';
|
||||
import type { CompileCommandHandler } from "../types.js";
|
||||
|
||||
export const compileCommand: CompileCommandHandler = async (dir, options) => {
|
||||
console.log(`开始编译...`);
|
||||
console.log(`目录:${dir}`);
|
||||
console.log("开始编译...");
|
||||
console.log(`源目录:${dir}`);
|
||||
console.log(`输出目录:${options.output}`);
|
||||
|
||||
|
||||
// TODO: 实现编译逻辑
|
||||
// 1. 扫描目录下的所有 .md 文件
|
||||
// 2. 解析 markdown 并生成路由
|
||||
// 3. 打包为带 hash 路由的单个 HTML 入口
|
||||
|
||||
console.log('编译完成!');
|
||||
|
||||
console.log("编译完成!");
|
||||
};
|
||||
|
||||
+28
-24
@@ -22,14 +22,14 @@ interface ContentIndex {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 CLI 脚本文件所在目录路径(用于定位 dist 文件夹)
|
||||
* 获取 CLI 脚本文件所在目录路径(用于定位 dist 目录)
|
||||
*/
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const distDir = resolve(__dirname, "..", "..", "..", "dist", "web");
|
||||
|
||||
/**
|
||||
* MIME 类型映射
|
||||
* MIME 类型映射表
|
||||
*/
|
||||
const MIME_TYPES: Record<string, string> = {
|
||||
".html": "text/html",
|
||||
@@ -48,7 +48,7 @@ const MIME_TYPES: Record<string, string> = {
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取文件扩展名对应的 MIME 类型
|
||||
* 根据文件扩展名获取对应的 MIME 类型
|
||||
*/
|
||||
function getMimeType(filePath: string): string {
|
||||
const ext = extname(filePath).toLowerCase();
|
||||
@@ -56,7 +56,7 @@ function getMimeType(filePath: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the best network IP for display (prefer LAN IPv4, fallback to localhost).
|
||||
* 获取最佳网络 IP 用于显示(优先局域网 IPv4,回退到 localhost)
|
||||
*/
|
||||
function getBestIP(): string {
|
||||
const interfaces = networkInterfaces();
|
||||
@@ -85,7 +85,7 @@ function getBestIP(): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the <title> from an SVG string, or null if absent.
|
||||
* 从 SVG 字符串中提取 <title> 内容,无则返回 null
|
||||
*/
|
||||
function extractSvgTitle(svg: string): string | null {
|
||||
const m = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(svg);
|
||||
@@ -93,17 +93,15 @@ function extractSvgTitle(svg: string): string | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a human-readable label from a filename.
|
||||
* "character-sheet" -> "Character Sheet"
|
||||
* 从文件名生成可读标签
|
||||
* 如 "character-sheet" -> "Character Sheet"
|
||||
*/
|
||||
function labelFromId(id: string): string {
|
||||
return id
|
||||
.replace(/[-_]/g, " ")
|
||||
.replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
return id.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描目录内的 .md 文件,生成索引
|
||||
* 扫描目录内的 .md 等文件,生成内容索引与块数据
|
||||
*/
|
||||
export function scanDirectory(dir: string): {
|
||||
index: ContentIndex;
|
||||
@@ -170,7 +168,7 @@ export function scanDirectory(dir: string): {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送文件响应
|
||||
* 发送文件响应(流式传输)
|
||||
*/
|
||||
function sendFile(res: ServerResponse, filePath: string) {
|
||||
res.writeHead(200, {
|
||||
@@ -181,7 +179,7 @@ function sendFile(res: ServerResponse, filePath: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 404 响应
|
||||
* 发送 404 未找到响应
|
||||
*/
|
||||
function send404(res: ServerResponse) {
|
||||
res.writeHead(404, { "Content-Type": "text/plain" });
|
||||
@@ -189,7 +187,7 @@ function send404(res: ServerResponse) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 JSON 响应
|
||||
* 发送 JSON 格式的响应
|
||||
*/
|
||||
function sendJson(res: ServerResponse, data: unknown) {
|
||||
res.writeHead(200, {
|
||||
@@ -201,7 +199,7 @@ function sendJson(res: ServerResponse, data: unknown) {
|
||||
|
||||
/**
|
||||
* 尝试提供静态文件
|
||||
* @returns 如果文件存在并已成功发送则返回 true
|
||||
* @returns 文件存在并成功发送时返回 true
|
||||
*/
|
||||
function tryServeStatic(
|
||||
res: ServerResponse,
|
||||
@@ -224,7 +222,7 @@ function tryServeStatic(
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建请求处理器
|
||||
* 创建 HTTP 请求处理器
|
||||
*/
|
||||
function createRequestHandler(
|
||||
contentDir: string,
|
||||
@@ -274,7 +272,7 @@ function createRequestHandler(
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容服务器接口
|
||||
* 内容服务器实例接口
|
||||
*/
|
||||
export interface ContentServer {
|
||||
/**
|
||||
@@ -300,7 +298,7 @@ export interface ContentServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建内容服务器
|
||||
* 创建内容服务器(HTTP + WebSocket + 文件监听)
|
||||
*/
|
||||
export function createContentServer(
|
||||
contentDir: string,
|
||||
@@ -324,7 +322,7 @@ export function createContentServer(
|
||||
statSheets: [],
|
||||
};
|
||||
|
||||
/** Re-scan completions from current content index and collected blocks */
|
||||
/** 从当前内容索引和已收集的块重新扫描补全数据 */
|
||||
function recomputeCompletions(): void {
|
||||
completionsIndex = scanCompletions(contentIndex, collectedBlocks);
|
||||
console.log(
|
||||
@@ -333,7 +331,7 @@ export function createContentServer(
|
||||
}
|
||||
|
||||
// 扫描内容目录生成索引
|
||||
console.log("扫描内容目录...");
|
||||
console.log("正在扫描内容目录...");
|
||||
const scanResult = scanDirectory(contentDir);
|
||||
contentIndex = scanResult.index;
|
||||
collectedBlocks = scanResult.blocks;
|
||||
@@ -341,7 +339,7 @@ export function createContentServer(
|
||||
recomputeCompletions();
|
||||
|
||||
// 监听文件变化
|
||||
console.log("监听文件变化...");
|
||||
console.log("正在监听文件变化...");
|
||||
const watcher = watch(contentDir, {
|
||||
ignored: /(^|[\/\\])\./,
|
||||
persistent: true,
|
||||
@@ -431,7 +429,7 @@ export function createContentServer(
|
||||
// ---- Journal / MQTT broker ----
|
||||
let journal: Awaited<ReturnType<typeof createJournalServer>> | null = null;
|
||||
|
||||
// 创建 HTTP 服务器 BEFORE journal so we can pass it in
|
||||
// 在 journal 之前创建 HTTP 服务器,以便传入给 journal
|
||||
const handleRequest = createRequestHandler(
|
||||
contentDir,
|
||||
distPath,
|
||||
@@ -454,7 +452,7 @@ export function createContentServer(
|
||||
}
|
||||
console.log(`内容目录:${contentDir}`);
|
||||
console.log(`静态资源目录:${distPath}`);
|
||||
console.log(`Journal 连接:ws://${bestIP}:${port}\n`);
|
||||
console.log(`日志连接:ws://${bestIP}:${port}\n`);
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -463,7 +461,7 @@ export function createContentServer(
|
||||
index: contentIndex,
|
||||
completions: completionsIndex,
|
||||
close() {
|
||||
console.log("关闭内容服务器...");
|
||||
console.log("正在关闭内容服务器...");
|
||||
server.close();
|
||||
watcher.close();
|
||||
if (journal) journal.close();
|
||||
@@ -476,6 +474,12 @@ export function createContentServer(
|
||||
*/
|
||||
export const serveCommand: ServeCommandHandler = async (dir, options) => {
|
||||
const contentDir = resolve(dir);
|
||||
|
||||
if (!existsSync(contentDir)) {
|
||||
console.error(`错误:目录不存在:${contentDir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const port = parseInt(options.port, 10);
|
||||
const host = options.host || "0.0.0.0";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user