Monaco Editor 支持

Nasti 内置 monacoEditorPlugin,为 Monaco Editor (VS Code 使用的代码编辑器)提供一站式集成:预打包 Web Worker、注入 MonacoEnvironment、生产构建自动产出 Worker 资源。接口与 vite-plugin-monaco-editor 基本对齐,可无痛迁移。

它解决了什么?
  1. Monaco 的 Worker 是独立入口,必须单独打包。手动配置极易出错;
  2. monaco-editor 包含 2000+ 源文件。按 ESM 逐文件交给 dev server 会触发大量并发文件读取,在 macOS 等低 ulimit 环境下 HMR 时抛出 EMFILE(too many open files)。本插件把 Worker 预打包成单文件缓存, 并显式把 monaco-editor 目录从 watcher 中剔除作为双保险。

安装

npm install monaco-editor
npm install -D @nasti-toolchain/nasti

最小配置

// nasti.config.ts
import { defineConfig, monacoEditorPlugin } from '@nasti-toolchain/nasti'

export default defineConfig({
  plugins: [monacoEditorPlugin()],
})

在代码里直接 import * as monaco from 'monaco-editor',Worker 会自动从 /monacoeditorwork/<label>.worker.js 加载,不需要任何手工 new Worker(new URL(...)) 胶水代码。

使用示例

// src/main.ts
import * as monaco from 'monaco-editor'

monaco.editor.create(document.getElementById('editor')!, {
  value: 'function hi() {\n  console.log("hello monaco")\n}',
  language: 'typescript',
  theme: 'vs-dark',
  automaticLayout: true,
})

选项

选项类型默认值说明
languageWorkers MonacoEditorLanguageWorker[] ['editorWorkerService','css','html','json','typescript'] 启用的内置语言 Worker 列表
customWorkers { label, entry }[] [] 自定义 Worker,例如 monaco-graphql
publicPath string 'monacoeditorwork' Worker 在 URL 上的公共前缀;可填 CDN 绝对 URL
globalAPI boolean false 是否将 Monaco API 暴露到 window.monaco
forceBuildCDN boolean false publicPath 是 CDN 时仍强制本地产出 Worker 文件
customDistPath (root, outDir, base) => string 自定义生产构建 Worker 产物目录(返回绝对路径)

裁剪语言 Worker

只用到 JSON 和 TypeScript?只打包这两个,减小产物体积:

monacoEditorPlugin({
  languageWorkers: ['editorWorkerService', 'json', 'typescript'],
})
注意: editorWorkerService 是 Monaco 的基础 Worker, 任何情况下都应保留。SCSS / Less 共用 css,Handlebars 共用 html,无需单独声明。

自定义 Worker

集成 GraphQL、YAML 等第三方 Worker:

monacoEditorPlugin({
  customWorkers: [
    { label: 'graphql', entry: 'monaco-graphql/esm/graphql.worker' },
    { label: 'yaml',    entry: 'monaco-yaml/yaml.worker' },
  ],
})

CDN 部署

将 Worker 放到 CDN 上,publicPath 传入绝对 URL:

monacoEditorPlugin({
  publicPath: 'https://cdn.example.com/monacoeditorwork',
  forceBuildCDN: true, // 仍然生成本地产物,便于自行上传 CDN
})

跨域 Worker 会被浏览器拒绝直接加载,插件注入的运行时会自动用 Blob + importScripts 包一层规避同源策略。

globalAPI

兼容旧 API 的 window.monaco 访问方式:

monacoEditorPlugin({ globalAPI: true })

// 然后在任意地方:
window.monaco.editor.getModels()

工作原理

  1. 插件启动:以 monaco-editor/esm/vs/.../xxx.worker 为入口,用 Rolldown 预打包为 单个 IIFE 文件,缓存到 node_modules/.nasti/monaco/<版本hash>/<label>.worker.js, 并发请求做去重。
  2. Dev 中间件:拦截 GET /<publicPath>/<label>.worker.js,直接从缓存磁盘流式返回, 不走 transform 管线 ⇒ 没有并发 fd 风暴。
  3. HTML 注入:在 <head> 头部注入一段内联脚本, 设置 self.MonacoEnvironment.getWorkerUrl 指向缓存 URL。
  4. 生产构建buildStart 阶段把缓存产物拷到 outDir/<publicPath>/ 下,随主包一起部署。
  5. EMFILE 防御configureServer 阶段显式 watcher.unwatch(node_modules/monaco-editor),防止符号链接、嵌套 node_modules 等路径绕过默认忽略规则。

与 Vite 插件迁移差异

vite-plugin-monaco-editor@nasti-toolchain/nasti
import monacoEditorPlugin from 'vite-plugin-monaco-editor' import { monacoEditorPlugin } from '@nasti-toolchain/nasti'
默认命名导出具名导出
Worker 打包使用 esbuildWorker 打包使用 Rolldown
缓存目录 node_modules/.monaco 缓存目录 node_modules/.nasti/monaco/<版本hash>(版本切换自动失效)

排障

HMR 时仍然报 EMFILE

确认插件已注册并查看启动日志是否有 [nasti:monaco-editor] worker build failed。如果是 macOS,临时 ulimit -n 10240 可缓解;若依赖链中还有其它大包(如 ionicons@mui/icons-material),也建议类似地预打包。

Worker 404

访问 /monacoeditorwork/editorWorkerService.worker.js 看是否能返回。 若 404:monaco-editor 未安装;若 500:看服务器日志里的打包错误。

跨框架集成

React(@monaco-editor/react)、Vue(@guolao/vue-monaco-editor) 等封装库底层仍使用 monaco-editor,无需额外配置。