VS Code 仓库中 monaco-editor-core 发布全流程:从 gulp editor-distro 到 npm publish
本文以 VS Code 仓库中 build/monaco/README.md 记载的 monaco-editor-core 发布流程为主体,结合 build/gulpfile.editor.ts 与 build/lib/monaco-api.ts 的源码实现,完整拆解“生成类型声明 → 升版 → 构建分发产物 → 发布 npm 包”四个步骤的每一步背后到底发生了什么,帮助读者不仅能按文档操作,还能理解每个产物文件的生成原理与前置约束。
1. 发布对象:monaco-editor-core 是什么
发布流程针对的包由 build/monaco/package.json 定义:
{
"name": "monaco-editor-core",
"private": true,
"version": "0.0.0",
"description": "A browser based code editor",
"typings": "./esm/vs/editor/editor.api.d.ts",
"module": "./esm/vs/editor/editor.main.js",
"license": "MIT"
}
两个关键字段决定了后续流程:
private: true:仓库内的这份 manifest 本身不会被直接发布,构建脚本会在生成发布目录时把它改写为false(见第 4 节);typings/module:指向./esm/vs/editor/下的产物,说明该包以 ESM 形态交付,类型入口是editor.api.d.ts而非裸monaco.d.ts。
配套的 build/monaco/README-npm.md 说明了该 npm 包定位:它是 monaco-editor npm 模块的构建块(building block),仅包含来自 VS Code 仓库的核心编辑器能力;除非要独立开发可分发的语言支持,一般应直接消费包含语言支持的完整 monaco-editor 包。该 README 会在构建时被重命名并放入发布目录(见第 4 节)。
2. 第一步:生成 monaco.d.ts(gulp watch 自动完成)
原发布文档的第一步写着:
The
monaco.d.tsis now automatically generated when runninggulp watch
这意味着开发者不再需要手动维护 src/vs/monaco.d.ts——只要编辑器 API 的源码变了,watch 模式下类型声明会自动重新生成。源码层面的对应实现是 build/gulpfile.editor.ts 中注册的 monacodts 任务:
task.task('monacodts', task.define('monacodts', () => {
const result = monacoapi.execute();
fs.writeFileSync(result.filePath, result.content);
fs.writeFileSync(path.join(root, 'src/vs/editor/common/standalone/standaloneEnums.ts'), result.enums);
return Promise.resolve(true);
}));
它做两件事:把生成的声明文件写入 src/vs/monaco.d.ts,并把收集到的枚举写入 src/vs/editor/common/standalone/standaloneEnums.ts。
2.1 生成机制:recipe 驱动的声明抽取
生成逻辑位于 build/lib/monaco-api.ts,核心是按 build/monaco/monaco.d.ts.recipe 这份“配方”文件逐行处理:
#include(module): Type1, Type2:从指定模块的编译声明输出中,按名字抽取指定的顶层类型。例如 recipe 第 73~85 行把MarkerTag、MarkerSeverity、CancellationTokenSource、URI、Position、Range、Selection等基础类型抽入monaco命名空间;#includeAll(module;替换规则): 排除名单:抽取模块的全部顶层声明,并支持旧名=>新名的替换指令与按名排除。例如 monaco.d.ts.recipe 第 89 行 用#includeAll(vs/editor/standalone/browser/standaloneEditor.js;languages.Token=>Token)把独立编辑器 API 整体拉入monaco.editor命名空间;- 命名空间骨架:recipe 本身手写声明了
monaco、monaco.editor、monaco.languages、monaco.worker四个命名空间的框架(含Environment、IDisposable、IEvent、Emitter等手写接口),#include的声明被填充进这些骨架中; - 版本校验:recipe 文件末尾的
//dtsv=3与运行时常量dtsv(见 monaco-api.ts 第 15 行)必须一致,否则拒绝生成并提示需要重启gulp watch——这是防止旧配方与新生成器混用的保护机制。
生成过程中还有几个值得注意的清洗规则(getMassagedTopLevelDeclarationText):带 @internal 或 private 标记的成员会被整体剔除,export default 被改写为普通 export,const enum 被降级为普通 enum,双引号统一替换为单引号——也就是说 API 作者用 @internal 注释即可控制符号是否进入公开类型面。
3. 第二步:Bump version
原发布文档要求“increase version in build/monaco/package.json”。这个版本号不是孤立的,build/gulpfile.editor.ts 在文件头部就直接消费了它:
import monacoPackage from './monaco/package.json' with { type: 'json' };
const sha1 = getVersion(root);
const semver = monacoPackage.version;
const headerVersion = semver + '(' + sha1 + ')';
semver + '(' + sha1 + ')' 会拼进每个产物文件的文件头(BUNDLED_FILE_HEADER),即发布出去的所有 JS 文件头部都形如 Version: 0.49.x(abc1234)。这与下一步“先推送再构建”的强约束直接相关:发布包内嵌了仓库 HEAD 的 SHA1,用户据此可定位到确切的源码快照。
4. 第三步:生成 npm 内容(gulp editor-distro)
4.1 前置条件:改动必须已提交并推送
原文档特别强调:
Be sure to have all changes committed and pushed to the remote(the generated files contain the HEAD sha and that should be available on the remote)
原因在于 finalEditorResourcesTask 生成的 version.txt 内容是这样写死的:
monaco-editor-core: https://github.com/microsoft/vscode/tree/${sha1}
sha1 来自 getVersion(root),即本地仓库的 HEAD 提交哈希。如果该提交只存在于本地、还没推送到远端,发布包里的溯源链接就会 404。因此“pushed to the remote”是硬性前提,而非流程建议。
4.2 任务链:editor-distro 的四个阶段
editor-distro 在 build/gulpfile.editor.ts 第 214 行 注册,是一条串行管线:
task.task('editor-distro',
task.series(
task.parallel(
util.rimraf('out-editor-src'),
util.rimraf('out-monaco-editor-core'),
),
extractEditorSrcTask,
compileEditorESMTask,
finalEditorResourcesTask
)
);
阶段一:extractEditorSrcTask —— 从 VS Code 源码中“抽出”编辑器子集
任务定义见 build/gulpfile.editor.ts 第 37 行,关键参数:
- 入口点:
vs/editor/editor.main.ts(编辑器主入口)、vs/editor/editor.worker.start.ts(Web Worker 入口)、vs/editor/common/services/editorWebWorkerMain.ts; - 树摇:
shakeLevel: 2(注释标明级别含义0-Files, 1-InnerFile, 2-ClassMembers),从入口出发做到类成员级别的未用代码剔除; - inlineEntryPoints:把第 2 节
monacoapi.execute()的usageContent与 build/monaco/monaco.usage.recipe 作为“使用引用”注入。后者的作用正如文件头注释所写——“adding references to various symbols which should not be removed via tree shaking”,例如通过a = editorAPI.CancellationTokenSource;这类语句保住宿根引用(ServiceIdentifier.type、SyncDescriptor0.ctor等运行时才能发现的依赖); - 额外拷贝:
vs/base/browser/dompurify/dompurify.js、vs/base/common/marked/marked.js等第三方文件随源码一并抽出; - 输出:抽取结果落在
out-editor-src/,并预先声明 TS 输出目录指向../out-monaco-editor-core/esm/vs。
阶段二:compileEditorESMTask —— 编译为 ESM 产物
见 build/gulpfile.editor.ts 第 71 行:以 build: true 全量编译 out-editor-src,经 i18n.processNlsFiles 处理本地化文件(把 NLS 字符串按语言展开),同时注入第 3 节描述的版本文件头,最终写入 out-monaco-editor-core/esm/——正好对应 package.json 中 module: ./esm/vs/editor/editor.main.js 声明的路径。
阶段三:finalEditorResourcesTask —— 组装发布目录
见 build/gulpfile.editor.ts 第 131 行,用 es.merge 并行组装以下资源到 out-monaco-editor-core/:
- LICENSE 与第三方法律文件:原样拷贝 build/monaco/LICENSE 与 build/monaco/ThirdPartyNotices.txt;
- 类型入口转换:把
src/vs/monaco.d.ts经toExternalDTS函数改写后另存为esm/vs/editor/editor.api.d.ts。该函数的作用是“外部化”:把declare namespace monaco { ... }的外层包裹剥掉,declare namespace monaco.editor等变为export namespace editor,并把declare var MonacoEnvironment改写为declare global形式——使同一份声明文件既能作为仓库内部的全局声明使用,又能作为 npm 包的对外的模块类型定义; - 改写 package.json:基于 build/monaco/package.json 做三处动态修改——
private置为false;从src/vs/base/common/marked/cgmanifest.json与src/vs/base/browser/dompurify/cgmanifest.json读取精确版本号,注入dependencies字段(marked与dompurify,读取失败会直接抛错终止构建,保证依赖版本一定可溯源); - version.txt:写入第 4.1 节所述的 HEAD SHA1 溯源链接;
- README 替换:把 build/monaco/README-npm.md 重命名为
README.md放入发布目录——即 npm 页面展示的说明来自 npm 版 README,而不是本 README 这份发布操作手册。
至此 out-monaco-editor-core/ 就是一份自包含、可直接发布的 npm 包目录。
5. 第四步:Publish
原发布文档的最后两步:
cd out-monaco-editor-core
npm publish
由于第 4.2 节阶段三已经把 package.json(private: false、正确的 dependencies)、类型文件、README、LICENSE 全部就位,发布动作本身只是把整个目录推送到 npm 注册表。
6. 完整操作清单(继承原文档四步流程)
| 步骤 | 操作 | 关键点 / 源码依据 |
|---|---|---|
| 1 | 生成 monaco.d.ts | 运行 gulp watch 时自动生成,无需手动维护;由 build/lib/monaco-api.ts 按 build/monaco/monaco.d.ts.recipe 抽取 |
| 2 | 升版 | 修改 build/monaco/package.json 的 version 字段;版本号会进入产物文件头 Version: x.y.z(sha) |
| 3 | 生成 npm 内容 | 先 commit 并 push 全部改动(产物内嵌 HEAD SHA1,必须在远端可访问),再运行 gulp editor-distro,产出 out-monaco-editor-core/ |
| 4 | 发布 | cd out-monaco-editor-core && npm publish |
需要注意的适用前提:以上流程针对 VS Code 主仓库内直接构建 monaco-editor-core 的方式(monaco 类型检查另有独立的 gulp monaco-typecheck 任务,基于 src/tsconfig.monaco.json 执行,见 build/gulpfile.editor.ts 第 233 行 区域)。整个管线是纯构建脚本编排,任何一步失败(recipe 版本不匹配、cgmanifest 读取失败、模块或类型名在源码中不存在)都会显式报错终止,不会产出半成品包。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00