From 6a8bdf6b16a289998d7f2961bf153316fee502dd Mon Sep 17 00:00:00 2001 From: "tao.chen" <93983997+taochen-ct@users.noreply.github.com> Date: Thu, 20 Aug 2026 20:11:06 +0800 Subject: [PATCH] fix: monaco-editor offline --- frontend/.gitignore | 4 +++ frontend/app/lib/monacoSetup.client.ts | 34 +++++++++++++++----------- frontend/package.json | 6 ++++- frontend/public/monaco/.gitkeep | 0 4 files changed, 29 insertions(+), 15 deletions(-) create mode 100644 frontend/public/monaco/.gitkeep diff --git a/frontend/.gitignore b/frontend/.gitignore index 039ee62..271afdb 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -5,3 +5,7 @@ # React Router /.react-router/ /build/ + +# monaco-editor 预构建 min/vs(由 scripts/copy-monaco.mjs 在 postinstall 时 +# 从 node_modules/monaco-editor/min/vs 复制生成,不要提交) +/public/monaco/vs/ diff --git a/frontend/app/lib/monacoSetup.client.ts b/frontend/app/lib/monacoSetup.client.ts index 4f6e721..bd7f93f 100644 --- a/frontend/app/lib/monacoSetup.client.ts +++ b/frontend/app/lib/monacoSetup.client.ts @@ -1,21 +1,27 @@ // Monaco 本地加载配置 // -// @monaco-editor/react 默认从 jsDelivr CDN 异步加载 monaco-editor 的核心 -// 脚本与 Worker(见 @monaco-editor/loader 默认 paths: -// https://cdn.jsdelivr.net/npm/monaco-editor@.../min/vs)。 -// 离线 / 内网环境下 CDN 不可达时, 初始化失败,控制台报 -// "Monaco initialization: error",编辑器永远停在 loading 状态 -// (Python 编辑与预览都依赖 ,因此全部打不开)。 +// 离线 / 内网环境下,@monaco-editor/react 默认从 jsDelivr CDN 加载 monaco +// (见 @monaco-editor/loader 默认 paths: jsdelivr 的 monaco-editor min/vs), +// CDN 不可达时控制台报 "Monaco initialization: error", 永远停在 +// 内部 loading 状态(Python 编辑与预览都依赖 ,全部打不开)。 // -// 这里把本地 node_modules 中的 monaco-editor 交给 Vite 打包,并在任何 -// 挂载前通过 loader.config 注入,彻底断开 CDN 依赖。 -// 必须在 root.tsx 顶部 import,保证先于编辑器渲染执行。 +// 早期尝试:import * as monaco from "monaco-editor" 让 Vite 把 monaco ESM 打 +// 进 client bundle + loader.config({ monaco })。但 monaco ESM 内部用 +// new URL('./xxx/webWorkerBootstrap.js?esm', import.meta.url) +// 这种深嵌套相对路径 bootstrap worker,Vite 8 / Rolldown 不会把它们改写成 +// 产物的 hash asset URL,运行时 fallback 成 blob URL,浏览器解析相对 +// specifier 直接抛 "Failed to resolve module specifier ...webWorkerBootstrap.js"。 +// 此外手动 import '...editor.worker?worker' 也会被 Rolldown 报 +// "failed to resolve import"。这是 Vite/Rolldown 与 monaco ESM 的兼容坑。 // -// 注意:不配置独立 worker(monaco 会自动 fallback 到主线程,离线可用, -// 仅有一条无害的 console warning)。Vite 8 / Rolldown 对 `?worker` 后缀 -// 解析不兼容,强行配置反而会打挂构建。 +// 最终方案:用 monaco 自带的预构建 `min/vs`(IIFE/AMD 分布),通过 +// loader.config({ paths }) 自托管到 /monaco/vs。预构建版自带 worker +// 加载(路径相对于 vs 根),完全不依赖 bundler 的 ?worker 行为,行为与 +// 原 CDN 模式一一对应,自包含离线。min/vs 由 postinstall 脚本从 +// node_modules/monaco-editor/min/vs 复制到 frontend/public/monaco/vs。 +// +// 必须用 .client 后缀:SPA 构建的 server 预渲染阶段不会执行该模块。 import { loader } from "@monaco-editor/react"; -import * as monaco from "monaco-editor"; -loader.config({ monaco }); +loader.config({ paths: { vs: "/monaco/vs" } }); diff --git a/frontend/package.json b/frontend/package.json index 7e91070..ebd9617 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,7 +6,11 @@ "build": "react-router build", "dev": "react-router dev", "start": "react-router-serve ./build/server/index.js", - "typecheck": "react-router typegen && tsc" + "typecheck": "react-router typegen && tsc", + "copy:monaco": "node scripts/copy-monaco.mjs", + "postinstall": "node scripts/copy-monaco.mjs", + "predev": "node scripts/copy-monaco.mjs", + "prebuild": "node scripts/copy-monaco.mjs" }, "dependencies": { "@monaco-editor/react": "^4.7.0", diff --git a/frontend/public/monaco/.gitkeep b/frontend/public/monaco/.gitkeep new file mode 100644 index 0000000..e69de29