博客秒级排序升级与代码质量清理记录

起因:同一分钟内的排序混乱

npm run new 快速连续创建文章时,发现一个隐患:原文件名格式 YY-MM-DD-HH-MM.md 只精确到分钟,同一分钟内创建的两篇文章排序顺序不确定。排序逻辑散布在前端工具 src/utils/sortPosts.ts、文章创建脚本 tools/scripts/new-post.js 和 Admin 后台 tools/admin/server.js 三处,需要同步升级。

文件名格式升级到秒级

新格式 YY-MM-DD-HH-MM-SS.md 在原有基础上追加秒字段。解析器用可选匹配组处理向后兼容,旧文件无需改动:

// src/utils/sortPosts.ts
export function getTimestampFromFilename(id: string): number {
  const filename = id.split('/').pop() || id;
  const match = filename.match(/^(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})(?:-(\d{2}))?/);

  if (!match) return 0;

  const [, yy, month, day, hour, minute, second] = match;
  const year = 2000 + parseInt(yy, 10);

  return new Date(
    year,
    parseInt(month, 10) - 1,
    parseInt(day, 10),
    parseInt(hour, 10),
    parseInt(minute, 10),
    parseInt(second || '0', 10)  // 秒默认为 00,旧格式自动兼容
  ).getTime();
}

(?:-(\d{2}))? 让秒成为可选项,second || '0' 处理旧格式的降级。Admin 后台的同名函数逻辑完全一致,文章创建脚本则在生成文件名时补上秒字段。

代码质量:从 119 个错误到 40 个

趁着改排序逻辑,顺手跑了一遍 ESLint 和 TypeScript 检查,结果是 8 个 ESLint 警告和 119 个 TypeScript 错误。修完这轮后,ESLint 清零,TypeScript 降到约 40 个。

全局类型声明解决了不少重复错误。项目里 Mermaid、KaTeX、ECharts 都通过 CDN 动态加载,挂在 window 上,TypeScript 不认识这些属性,导致 30 多个重复报错。在 src/env.d.ts 里统一声明 Window 接口扩展后,这批错误一次性消失:

/// <reference types="astro/client" />

interface Window {
  mermaid?: {
    initialize: (config: Record<string, unknown>) => void;
    render: (id: string, code: string) => Promise<{ svg: string }>;
  };
  katex?: {
    renderToString: (formula: string, options: { throwOnError?: boolean; displayMode?: boolean }) => string;
  };
  echarts?: {
    init: (container: HTMLElement) => EChartsInstance;
  };
}

ESLint 警告主要是两类。一是箭头函数参数缺括号,项目配置了 arrow-parens: alwayspost => post.id 要改成 (post) => post.id,涉及五六个页面文件。二是未使用的变量,catch (e) 里的 e 没用到,改成 catch (_e) 或加 eslint-disable-next-line 注释;未使用的导入直接删掉。

TypeScript 类型错误里比较典型的是泛型约束问题。sortPostsByTime 原来写成 <T extends CollectionEntry<'blog'>>(posts: T[]): T[],泛型约束反而让编译器无法确认 a.data 的类型,改成直接用 CollectionEntry<'blog'>[] 就解决了。另一类是 getCollection 回调参数的隐式 any,加上 ({data}: CollectionEntry<'blog'>) 类型注解即可。

剩余 40 个错误主要集中在 Mermaid 和 BlogGalaxy 组件里,ECharts 的事件参数类型需要更细致的定义,还有一些 DOM 操作缺少 null 检查,留待后续处理,不影响运行。

几点收获

向后兼容的重点在于”可选而非强制”,正则里一个 (?:...)? 就能让新旧格式共存,不需要迁移脚本。全局类型声明文件也应该集中管理,把散落在各组件里的 declare global 统一到 src/env.d.ts,既消除了重复,也让类型定义有了明确的归属。大规模修复时先清 ESLint、再处理 TypeScript 的顺序是对的,语法层面干净之后,类型错误的信噪比会高很多,更容易判断哪些值得修、哪些可以暂缓。