oh-my-muse/docs/memorys/2026-05-24-muse-studio脚手架与基础设施搭建.md
zizi 317dfd4d13 feat(muse-studio): 搭建 P2 用户端工作台
完成 Muse Studio React SPA 基座和六个用户端功能域,接入 MSW Mock、Tiptap 写作台、知识库资料管理、智能体槽位预检绑定、市场授权安装和个人中心用量权益视图。
2026-05-25 10:15:28 +08:00

3.2 KiB
Raw Blame History

muse-studio 脚手架与核心基础设施搭建留痕

任务背景

在根目录从零初始化前端用户端子项目 muse-studioVite + React + TS 结构并完成路由、统一客户端、MSW 2.0 Mock、IndexedDB 及 Zustand 状态库等基础设施建设。

关键工程实践与避坑指南

1. TypeScript 6.0 严格模式兼容

(1) erasableSyntaxOnly 限制

  • 现象当启用该规则时TypeScript 6.0 禁用了 parameter properties 语法(如在 constructor 参数中直接写 public code: string)。
  • 避坑:声明异常类或常规类时,需采用标准 ES ES6 属性定义:
    export class ApiError extends Error {
      public code: string;
      public status: number;
      constructor(code: string, message: string, status: number) {
        super(message);
        this.code = code;
        this.status = status;
      }
    }
    

(2) exactOptionalPropertyTypes 限制

  • 现象:在对象字面量中如果显式将可选属性设为 undefined 会报错。
  • 避坑:构造请求参数(如 RequestInit 的 body)时,避免传入 body: undefined。应采用动态属性扩展或先判断再赋值:
    const options: RequestInit = { method: 'POST' };
    if (data !== undefined) {
      options.body = JSON.stringify(data);
    }
    

(3) baseUrl 被废弃

  • 现象TypeScript 6.0+ 将逐渐废弃 baseUrl,编译时会报 TS5101 错误。
  • 避坑:从 tsconfig.app.json 中直接移除 "baseUrl"Vite + TS 依然能够通过相对位置正确解析 "paths"(别名映射)。

2. pnpm 包管理构建脚本受阻

  • 现象:在新版 pnpm (v10/v11) 中,若存在未显式授权的构建脚本(如 esbuild, msw),任何 pnpm 命令在运行依赖完整性检查时都会抛出 [ERR_PNPM_IGNORED_BUILDS] 并强制中断。
  • 解决方案
    1. 本地开发:在子项目根目录下创建 .npmrc 并添加以下白名单配置:
      only-built-dependencies[]=esbuild
      only-built-dependencies[]=msw
      
    2. CI 或自动化编译:如果依然受到全局白名单阻碍,可使用 --ignore-scripts 绕过依赖构建脚本检查:
      pnpm install --ignore-scripts
      

3. MSW 2.0 模拟 SSE (Server-Sent Events)

  • 实现MSW 2.0 支持使用 ReadableStream 模拟 SSE 流式推送。在 Mock 处理器中可通过 TextEncoder 写入符合 EventSource 格式的缓冲行,并在 headers 中指定 text/event-stream
    http.get('/api/ai/stream', () => {
      const encoder = new TextEncoder();
      const stream = new ReadableStream({
        async start(controller) {
          controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: 'chunk', content: '数据' })}\n\n`));
          controller.close();
        }
      });
      return new HttpResponse(stream, {
        headers: { 'Content-Type': 'text/event-stream' }
      });
    })
    

4. Zustand 4 严格模式类型推断

  • 避坑:在 Zustand Store 中,为了避免隐式 any 类型报错,建议统一使用柯里化方法传入泛型定义:
    import create from 'zustand';
    export const useUIStore = create<UIState>()((set) => ({ ... }));