diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 8621211..311a24d 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1215,8 +1215,11 @@ bun run check # 一键运行 schema:check + typecheck + lint + test | `typescript-eslint` stylistic-type-checked | TypeScript 风格规则(命名规范、语法选择等) | | `eslint-plugin-perfectionist` recommended-natural | 导入语句和命名导出自动排序 | | `eslint-plugin-import` | 导入路径验证、循环依赖检测、重复导入合并 | +| `no-restricted-syntax` | 禁止 `src/server/` 运行时代码直接使用 `console.*` | | `eslint-plugin-prettier` recommended + `eslint-config-prettier` | 将 Prettier 格式集成为 ESLint 规则,禁用冲突规则 | +后端运行时代码的 `console.*` 检查使用中文定制提示:`后端运行时代码禁止直接使用 console.*;请通过注入的 Logger 实例输出日志,配置加载失败前使用 createConsoleFallback()。`。`src/server/logger.ts` 是唯一例外,用于封装 `ConsoleFallbackLogger`。 + ### 测试代码 ESLint 规范 测试代码与业务代码使用相同的 ESLint 规则集,应优先通过类型化 helper、类型化 mock、显式 no-op 和受控断言模式满足已启用的类型感知规则,最小化 `eslint-disable` 的使用。具体约定: diff --git a/eslint.config.js b/eslint.config.js index 2099c00..294234f 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,6 +6,9 @@ import reactHooks from "eslint-plugin-react-hooks"; import reactRefresh from "eslint-plugin-react-refresh"; import tseslint from "typescript-eslint"; +const noDirectConsoleMessage = + "后端运行时代码禁止直接使用 console.*;请通过注入的 Logger 实例输出日志,配置加载失败前使用 createConsoleFallback()。"; + export default tseslint.config( { ignores: [ @@ -53,6 +56,19 @@ export default tseslint.config( "no-undef": "off", }, }, + { + files: ["src/server/**/*.ts"], + ignores: ["src/server/logger.ts"], + rules: { + "no-restricted-syntax": [ + "error", + { + message: noDirectConsoleMessage, + selector: "MemberExpression[object.name='console']", + }, + ], + }, + }, { files: ["eslint.config.js"], rules: { diff --git a/src/server/bootstrap.ts b/src/server/bootstrap.ts index b2999e4..18e27c6 100644 --- a/src/server/bootstrap.ts +++ b/src/server/bootstrap.ts @@ -9,7 +9,7 @@ import type { StaticAssets } from "./static"; import { loadConfig, type ResolvedConfig } from "./checker/config-loader"; import { ProbeEngine } from "./checker/engine"; import { ProbeStore } from "./checker/store"; -import { createRuntimeLogger } from "./logger"; +import { createConsoleFallback, createRuntimeLogger } from "./logger"; import { startServer } from "./server"; export interface BootstrapDependencies { @@ -59,7 +59,13 @@ export async function bootstrap(options: BootstrapOptions, dependencies: Bootstr process.on(signal, handler); }); const exit = dependencies.exit ?? ((code: number) => process.exit(code)); - const logError = dependencies.logError ?? console.error; + const logError = + dependencies.logError ?? + ((...data: unknown[]) => { + createConsoleFallback().fatal( + data.map((item) => (item instanceof Error ? item.message : String(item))).join(" "), + ); + }); let store: ProbeStore | undefined; let engine: BootstrapEngine | undefined; diff --git a/src/server/dev.ts b/src/server/dev.ts index 10ac5cc..4e0b97b 100644 --- a/src/server/dev.ts +++ b/src/server/dev.ts @@ -1,5 +1,6 @@ import { bootstrap } from "./bootstrap"; import { readRuntimeConfig } from "./config"; +import { createConsoleFallback } from "./logger"; import { readAppVersion } from "./version"; async function main() { @@ -9,6 +10,6 @@ async function main() { } void main().catch((error) => { - console.error("启动失败:", error instanceof Error ? error.message : error); + createConsoleFallback().fatal(`启动失败: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); }); diff --git a/src/server/main.ts b/src/server/main.ts index d2999a8..2c7748a 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -1,5 +1,6 @@ import { bootstrap } from "./bootstrap"; import { readRuntimeConfig } from "./config"; +import { createConsoleFallback } from "./logger"; import { readAppVersion } from "./version"; async function main() { @@ -9,6 +10,6 @@ async function main() { } void main().catch((error) => { - console.error("启动失败:", error instanceof Error ? error.message : error); + createConsoleFallback().fatal(`启动失败: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); });