docs: align dashboard plan with fresh deno migration (#644)

This commit is contained in:
shenlan 2025-11-04 12:57:02 +08:00 committed by GitHub
parent 8096601118
commit d6299f8ca4

View File

@ -1,96 +1,109 @@
# `dashboard` Deno Runtime Migration Plan
# `dashboard` Fresh + Deno + Zustand Migration Plan
## 1. 背景与目标
`dashboard` 目前基于 Next.js 14在 Node.js 环境中通过 Yarn 和一系列脚本完成构建、导出与运行。【F:dashboard/package.json†L1-L38】【F:dashboard/Makefile†L1-L83】【F:dashboard/start.sh†L1-L16】
`dashboard` 目前基于 Next.js 14 App Router并通过 React 组件、CMS 模板与特性开关组合出首页等页面。【F:dashboard/app/page.tsx†L1-L26】【F:dashboard/app/layout.tsx†L1-L31】
`AppProviders` 又嵌套多层上下文语言、用户、主题、CMS 扩展),整体运行在 Node.js + React 18 环境。【F:dashboard/app/AppProviders.tsx†L1-L24】
构建/运行链路依赖 Yarn、Next CLI 与多份脚本,目标版本也在 `package.json``engines` 字段中锁定为 Node.js。【F:dashboard/package.json†L5-L19】
为支持 Deno runtime包括 Deno Deploy、本地 `deno run` 以及容器化部署),需要系统性评估并替换现有依赖 Node.js 运行时假设的部分。本规划文件旨在:
了统一使用 Deno 生态、引入 Fresh 框架以及在客户端采用 Zustand 管理全局状态,本规划旨在:
- 盘点 `dashboard` 中依赖 Node.js 专有 API / 工具链的功能;
- 给出分阶段的迁移策略,尽量保持与现有 Next.js 功能对齐;
- 拆解出可执行的子任务,支撑逐步迁移。
- 拆解现有 Next.js + Node.js 假设,以及与 Fresh/Deno 的差异;
- 设计 Fresh 项目结构、渲染路径与数据加载方案,确保 CMS、下载中心等关键能力仍然可用
- 在客户端抽取跨页面状态为共享的 Zustand store减少依赖 React Context
- 分阶段完成迁移,期间保持可回退、可验证的交付节奏。
## 2. Node.js 依赖现状分析
## 2. 现状评估
### 2.1 构建与运行命令
- `package.json` 中所有脚本均通过 `node``yarn` 启动 Next CLI / 自定义脚本。【F:dashboard/package.json†L6-L17
- 顶层 Makefile / `start.sh` 假设系统已安装 Node.js 与 Yarn并使用 `npm install` 构建产物。【F:dashboard/Makefile†L1-L83】【F:dashboard/start.sh†L1-L16
- Dockerfile 直接以 `node:20` 作为构建镜像并输出静态资源,再以 Nginx 托管。【F:dashboard/Dockerfile†L1-L12
### 2.1 App Router、上下文与页面结构
- 首页、租户空间等页面位于 `app/` 目录依赖服务器组件和动态模板注入。【F:dashboard/app/page.tsx†L1-L26
- 根布局通过 `AppProviders` 包裹语言、用户与主题 Provider后者继续依赖 Zustand store 维护会话用户数据。【F:dashboard/app/AppProviders.tsx†L1-L24】【F:dashboard/lib/userStore.tsx†L1-L132
- 邮件模块已存在独立的 `mail.store.ts`,证明项目内部对 Zustand 的使用模式可复用。【F:dashboard/app/store/mail.store.ts†L1-L46
### 2.2 配置文件与 CommonJS
- `next.config.js` 使用 CommonJS `require`/`module.exports`,并在运行时访问 `process.env`。【F:dashboard/next.config.js†L1-L64
- Tailwind 与 PostCSS 配置同样使用 CommonJS 导出。【F:dashboard/postcss.config.js†L1-L6】【F:dashboard/tailwind.config.js†L1-L14
- `vitest.config.ts` 通过 Node.js `path` 模块构建别名,需要 Node 的内置模块支持。【F:dashboard/vitest.config.ts†L1-L25
### 2.2 Node 绑定的构建链路
- 脚本命令全部调用 Next CLI、Vitest、Playwright 等 Node 工具,且通过 `node`/`tsx` 执行自定义脚本。【F:dashboard/package.json†L8-L19
- Makefile、`start.sh` 与 Dockerfile 明确依赖 Node.js、Yarn 与 npm Registry构建阶段亦输出静态导出给 Nginx 托管。【F:dashboard/Makefile†L7-L107】【F:dashboard/start.sh†L1-L19】【F:dashboard/Dockerfile†L1-L12
- 配置文件使用 CommonJS并直接引用 Node 内置模块(`path` 等),在 Deno/Fresh 环境需转换为 ESM 或动态导入。【F:dashboard/next.config.js†L6-L101】【F:dashboard/postcss.config.js†L1-L6】【F:dashboard/tailwind.config.js†L1-L24】【F:dashboard/vitest.config.ts†L1-L40
### 2.3 构建前置脚本
- `yarn prebuild` 调用 `scripts/export-slugs.ts`、`scripts/scan-md.ts`、`scripts/fetch-dl-index.ts`;脚本大量使用 `fs`、`path`、`process` 等 Node 内置模块。【F:scripts/export-slugs.ts†L1-L63】【F:scripts/scan-md.ts†L1-L58】【F:scripts/fetch-dl-index.ts†L1-L37】
- 这些脚本写入 `dashboard/public/_build/`、`public/dl-index/` 等目录,用于页面渲染数据。
### 2.3 内容构建与模板加载
- `src/templateRegistry.ts` 通过 `fs`、`path` 同步读取磁盘模板,依赖 CommonJS `require`Fresh 环境需改写为异步模块加载或构建期预处理。【F:dashboard/src/templateRegistry.ts†L1-L120】
- 预构建脚本(`export-slugs.ts`、`scan-md.ts`、`fetch-dl-index.ts`)大量使用 Node `fs/promises`、`process`,并写入 `public/_build` 与下载目录。【F:scripts/export-slugs.ts†L1-L74】【F:scripts/scan-md.ts†L1-L63】
- 下载清单在运行时同步读取 JSON 文件,同样倚赖 Node `fs`。【F:dashboard/lib/download-manifest.ts†L1-L24】
### 2.4 运行时SSR / API 路由)
- Next App Router 下的 API Route 主要调用后端 REST 服务,本身大多使用 `fetch`,对 Deno 兼容性良好;但 `lib/download-manifest.ts` 等 server-only 模块同步读取文件系统,依赖 Node `fs`/`path`。【F:dashboard/lib/download-manifest.ts†L1-L23】
- `lib/serviceConfig.ts` 与各页面/组件通过 `process.env` 读取环境变量,需要过渡到 `Deno.env.get` 或在构建阶段注入配置。【F:dashboard/lib/serviceConfig.ts†L1-L107
### 2.4 API 路由与运行时配置
- Next API Routes 运行在 `runtime = 'nodejs'` 下,返回 JSON 供前端消费Fresh 需改写为 Deno `Request`/`Response` 处理器。【F:dashboard/app/api/content-meta/route.ts†L1-L23】
- 前端运行时配置依赖 `process.env.NEXT_PUBLIC_*` 与全局变量切换环境/区域,需要在 Deno 中改造为 `Deno.env` 或构建期注入。【F:dashboard/config/runtime-client.ts†L1-L82
### 2.5 测试与开发工具
- Vitest、Testing Library 等依赖 Node.js (特别是 JSDOM)`vitest` CLI 默认使用 Node 执行。
- 本地开发流程依赖 `yarn dev` 与 Node 的 HMR 服务
### 2.5 状态管理与客户端依赖
- 用户信息等核心状态由自建 `userStore` + React Context 提供,需要在 Fresh + Zustand 方案中拆分为 store 与薄 Provider。【F:dashboard/lib/userStore.tsx†L1-L132】
- 其他页面多通过 SWR、局部状态管理数据与 Fresh 的 Islands 模式兼容,但需确保库能够通过 `npm:` 或原生 Deno 导入
## 3. 迁移策略与建议
## 3. Fresh + Deno + Zustand 目标架构
### 3.1 建立 Deno 任务与依赖管理
1. 在仓库根目录新增 `deno.json` / `deno.jsonc`,定义 `tasks` 映射(如 `deno task dev` -> `deno run -A npm:next dev`),并声明所需 `npm` 依赖,启用 Deno 的 Node 兼容层。
2. 通过 `deno vendor``deno.json` 中的 `imports`,集中管理 TypeScript 路径别名,替代现有 `tsconfig` 中的 Node 解析。
3. 调整 CI/Makefile`yarn` 调用替换为 `deno task`,保留向后兼容的回退命令(过渡期可提供双轨脚本)。
### 3.1 Fresh 项目骨架
1. 在仓库中新增 `dashboard-fresh/`(暂名),包含 `main.ts`, `fresh.gen.ts`, `routes/`, `islands/`, `static/` 等标准结构;并在 `deno.json` 中启用 `tasks`、`importMap` 指向 `npm:next` 替换项与 `npm:zustand`。【需新建】
2. 将 Next `app/` 路由映射到 Fresh `routes/`
- `app/page.tsx``routes/index.tsx`(读取 CMS 模板);
- `app/(tenant)/**``routes/(tenant)/**.tsx`,使用 Fresh `HandlerContext` 拉取数据;
- `app/api/**``routes/api/**.ts`,直接返回 `Response`
3. 在 `islands/` 中拆分需要交互的组件(如邮件、仪表盘 Widget以保持 SSR + 客户端增强的 Fresh 模式。
### 3.2 配置文件改造
1. 将 `next.config.js`、`postcss.config.js`、`tailwind.config.js` 转为原生 ESM如重命名为 `.mjs` 或使用 `export default`)。在配置内部避免 `require`,使用 `import` 并为 Deno 提供类型提示。
2. 检查 Next.js 版本,启用官方对 Deno 的实验支持(如 `experimental.runtime = 'edge'``experimental.appDir: true`),确保 App Router 能以 Edge Runtime 打包
3. 若仍需 CommonJS例如 Tailwind CLI使用 `npm:` 包由 Deno Node 兼容层执行,或迁移到 `tailwindcss@canary` 支持的 ESM 导出
### 3.2 构建与配置体系
1. 创建顶层 `deno.jsonc`,定义 `tasks``deno task dev`(调用 `deno run -A main.ts`)、`deno task build``deno run -A fresh-build.ts`)、`deno task lint/test` 等;添加 `compilerOptions``imports` 以取代现有 `tsconfig` 别名。【需新建】
2. 将 Tailwind/PostCSS 配置转为 ESM 模块,并在 Fresh 流程中通过 `deno task tw:build` 调用 `npm:tailwindcss` 生成 CSS
3. 用 Deno 版 Makefile/脚本替换 Node 依赖:所有 CI 步骤改为调用 `deno task`,同时保留 Node 构建路径直至迁移完成
### 3.3 构建脚本迁移
1. 将 `scripts/*.ts` 改写为兼容 Deno 的模块:
- 用 `Deno.readTextFile` / `Deno.writeTextFile` / `Deno.mkdir` 替代 `fs`
- 使用 `new URL('.', import.meta.url)``Deno.cwd()` 替代 `__dirname`、`process.cwd()`。
- 捕获异常时避免 `process.exit`,改用抛错或 `Deno.exit`
2. 将脚本注册到 `deno.json``tasks` 中(如 `deno task prebuild`),供 Next 构建流程调用。
3. 若继续兼容 Node 构建,可在脚本中引入运行时分支,检测 `globalThis.Deno` 后切换到相应 API。
### 3.3 内容与模板加载改造
1. 将 `templateRegistry` 拆为:
- 构建期脚本使用 `Deno.readDir``import()` 构建模板索引;
- 运行时通过 Deno `import` 动态加载模板或从预生成的 manifest 读取。
2. 重写 `export-slugs.ts`、`scan-md.ts`、`fetch-dl-index.ts` 为 Deno 脚本:
- 使用 `std/fs`、`std/path` 处理文件;
- 以 `Deno.cwd()`、`new URL(import.meta.url)` 处理路径;
- 替换 `process.exit` 为抛错或 `Deno.exit`
3. 对下载清单在构建阶段生成纯 JSON 模块(或放入 `static/` 目录Fresh 路由通过 `import` 读取,避免运行时 `fs` 依赖。
### 3.4 运行时代码适配
1. 将 `lib/download-manifest.ts` 中的 Node API 替换为 Deno 文件读取,或在构建阶段生成静态 JSON运行时仅通过 `import` 读取。
2. 整理 `process.env` 使用:
- 客户端公开变量保持 `NEXT_PUBLIC_*`,由 Next 在编译阶段注入;
- 服务器端逻辑(如 `lib/serviceConfig.ts`)改用封装的 `getEnv(name)`,内部在 Deno 环境使用 `Deno.env.get` 并提供 Node 回退,确保在多 runtime 下统一获取配置。
3. 审查 `app/api/**` 与服务器组件:确认是否使用 Node 限定模块(目前主要是 `console`、`fetch`,可直接在 Deno Edge Runtime 运行)。
### 3.4 API 层与后端集成
1. 为每个 Next API Route 创建等价的 Fresh handler保留与 `dashboard/server/**` 模块的调用关系;使用 `fetch``Request`/`Response` API确保边缘兼容。
2. 对需要认证 Cookie 的接口(如 `/api/auth/session`)设计 Fresh `middleware.ts`,将 Cookie 解析/刷新逻辑迁移到 Deno。
3. 若某些 Node-only 包(例如 `pdfjs-dist` 渲染)无法直接在 Fresh SSR 中运行,可转为客户端 Islands 或在构建阶段预渲染。
### 3.5 测试与开发体验
1. 评估是否迁移到 `deno test` + `std/testing`,或继续使用 Vitest通过 `deno run -A npm:vitest`)。
2. 若保留 Vitest需要
- 在 `deno.json``tasks` 中添加 `"test": "deno run -A npm:vitest run"`
- 确保配置文件(`vitest.config.ts`)使用 ESM 并避免 Node-only API或通过 `npm:path` 兼容模块。
3. 在文档中更新开发流程,指导使用 `deno task dev` 启动 Next 开发服务器。
### 3.5 Zustand 状态基线
1. 抽取公共 store会话用户来自 `userStore`)、运行时环境(读取 `runtime-client`)、特性开关等,迁移至 `dashboard-fresh/stores/` 下的 `createXXXStore.ts`
2. 在 Fresh `islands/` 中通过 Zustand hooks 访问共享状态,必要时封装 `<Provider>` 以兼容现有组件。
3. 邮件、下载等功能模块沿用或重构现有 Zustand 逻辑,将 `mail.store.ts` 迁移为岛组件专用 store并与新的全局 store 解耦。
### 3.6 部署与容器
1. 若目标是 Deno Deploy优先使用 Next 的静态导出(`NEXT_SHOULD_EXPORT=true`)并将静态资源托管到 Deno Deploy 静态站点;或使用 `@deno/next`(实验性)以 Edge Runtime 部署
2. 更新 Dockerfile改用 `denoland/deno:alpine`(或合适镜像)执行 `deno task build`,将 `out/` 拷贝到最终镜像,或直接在 Deno 容器内运行 Edge 兼容服务
3. 清理 Node.js 安装脚本(`start.sh` 等),提供 Deno 版本的部署脚本
### 3.6 UI 组件与路由迁移策略
1. 采用“页面优先”迁移:先将静态内容页(文档、下载列表)迁至 Fresh保持 React 组件和 CSS 基本不变;随后逐步迁移需要交互的管理面板
2. 使用 Fresh `Head`、`Layout` 功能重建 `AppProviders` 逻辑,将语言、主题 Provider 改为 Zustand + Signals 或 Fresh 中间件注入
3. CMS 模板可编译为 Preact 组件,仍放置在 `cms/` 下,通过构建脚本注入 Fresh 路由
## 4. 迁移子任务拆解
下表列出推荐的实施顺序与依赖关系:
### 3.7 开发、测试与质量保证
1. 评估保留 Vitest通过 `deno run -A npm:vitest`)还是迁移至 `deno test`,必要时编写兼容层加载 JSDOM。
2. 将 Playwright 测试改为通过 `deno run -A npm:playwright` 执行,或在迁移完成后替换为 Fresh 官方推荐方案。
3. 更新文档与脚本,指导开发者使用 `deno task dev`, `deno task test` 等命令,保留 Node 版本作为过渡 fallback。
| 阶段 | 子任务 | 关键文件 / 目录 | 说明 | 前置 |
| --- | --- | --- | --- | --- |
| 1 | 建立 Deno 项目骨架 | 仓库根目录、`docs/` | 新增 `deno.json`、`README` 更新、定义 `deno task`。 | 无 |
| 1 | 配置文件改造为 ESM | `dashboard/next.config.js`、`postcss.config.js`、`tailwind.config.js`、`vitest.config.ts` | 切换到 `export` 语法,移除 CommonJS需要验证 Next 对 ESM 配置的支持。 | 阶段1骨架 |
| 2 | 构建脚本 Deno 化 | `scripts/*.ts`、`dashboard/lib/download-manifest.ts` | 替换 `fs/path/process`,实现跨 runtime 工具模块。 | 配置 ESM |
| 2 | 环境变量适配 | `dashboard/lib/serviceConfig.ts` 及引用 | 引入 `getEnv` 工具,确保 Deno/Node 双环境可用。 | 阶段2脚本 |
| 3 | 开发/测试任务迁移 | `deno.json`、`dashboard/package.json`、`dashboard/Makefile` | 在 Deno 任务中封装 `next dev`、`vitest`;决定是否保留 `yarn`。 | 前述任务 |
| 3 | 部署脚本与容器更新 | `dashboard/Dockerfile`、`dashboard/start.sh` | 替换为 Deno 镜像/脚本,更新运维文档。 | 开发任务 |
| 4 | 可选:替换 Node-only 依赖 | 第三方 npm 包 | 评估 `pdfjs-dist`、`react-grid-layout` 等在 Deno 中的兼容性,必要时寻找替代或自行打包。 | 以上完成 |
### 3.8 部署与运维
1. 提供 Deno Deploy/Fresh 官方容器的部署示例:`FROM denoland/deno:alpine`,执行 `deno task build` + `deno task start`
2. 迁移 Terraform/Helm 等部署文件,确保环境变量通过 `deno.json``deployctl` 注入;同时更新 `start.sh`、Dockerfile保留 Node 版本作为灰度备份。
3. 在 CI 管道中引入 Deno 缓存与 `deno task lint`并调整制品静态资源、manifests输出目录。
## 4. 分阶段实施路线
| 阶段 | 目标 | 关键输出 | 前置条件 |
| --- | --- | --- | --- |
| 0 | 建立 Deno/Fresh 骨架 | `deno.json(c)`、Fresh 基础目录、`deno task dev` 可启动 Hello World | 无 |
| 1 | 共存期:内容页迁移 | Fresh routes 提供首页、下载、文档的静态渲染Node 版保留功能测试 | 阶段0 |
| 2 | API + CMS 适配 | Fresh handler 替换核心 API`templateRegistry`、构建脚本完成 Deno 化 | 阶段1 |
| 3 | Zustand 重构 | 会话、运行时、特性等全局状态迁移到共享 store并与 Fresh Islands 对接 | 阶段2 |
| 4 | 运维与部署 | Deno 容器/Deploy 工作流上线Node 版本进入维护模式 | 阶段3 |
| 5 | 收尾 & 清理 | 移除 Next.js 目录、废弃脚本,更新所有文档与脚手架 | 阶段4 |
## 5. 风险与注意事项
- **Next.js 在 Deno 的稳定性**:需关注官方支持状况,必要时考虑将 SSR 功能改造为静态导出或迁移到 Fresh/Preact 等 Deno 原生框架。
- **Node.js 兼容层性能**:使用 `deno run -A npm:...` 会拉起 Node 兼容层,需评估启动速度及部署体积。
- **第三方包可用性**:部分 npm 包依赖 Node 原生模块(如 `pdfjs-dist` 的 WASM 加载、`react-grid-layout` 的依赖链)。在 Deno 中使用 `npm:` 时需测试或手工打包。
- **CI/CD 更新**:构建流水线、版本锁定策略需同步调整,防止 Node/Deno 并存导致的依赖冲突。
- **Fresh 框架特性差异**:缺少内建的服务器组件与 App Router需要重构布局/数据获取逻辑;可通过 Islands + 服务器端 handler 模式逐步替代。【设计考量】
- **第三方依赖兼容性**:例如 `pdfjs-dist`、`react-grid-layout` 等包依赖 Node 构建工具,可能需要改用 `esm.sh`、`skypack` 或自建打包;必要时将其限制在客户端 Islands 中。【F:dashboard/package.json†L20-L38】
- **构建脚本迁移成本**:大量 Node 工具需改写为 Deno 版本需预留自动化测试验证生成文件是否一致。【F:scripts/export-slugs.ts†L1-L74】【F:scripts/scan-md.ts†L1-L63】
- **团队协作与培训**Fresh/Deno 与 Next.js 开发体验不同,需要补充文档、工作流示例,并在迁移过程中保持双运行时以降低学习曲线
---
> 本规划文件将根据实际迁移进展持续更新,请在实施过程中记录新增风险与解决方案
> 本规划会随迁移进度更新,执行团队请记录阻碍与修复方案,以便迭代 Fresh + Deno + Zustand 的最佳实践