首页
/ VS Code 仓库中 monaco-editor-core 发布全流程:从 gulp editor-distro 到 npm publish

VS Code 仓库中 monaco-editor-core 发布全流程:从 gulp editor-distro 到 npm publish

2026-09-05 21:01:54作者:袁立春Spencer

本文以 VS Code 仓库中 build/monaco/README.md 记载的 monaco-editor-core 发布流程为主体,结合 build/gulpfile.editor.tsbuild/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.ts is now automatically generated when running gulp 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 行把 MarkerTagMarkerSeverityCancellationTokenSourceURIPositionRangeSelection 等基础类型抽入 monaco 命名空间;
  • #includeAll(module;替换规则): 排除名单:抽取模块的全部顶层声明,并支持 旧名=>新名 的替换指令与按名排除。例如 monaco.d.ts.recipe 第 89 行#includeAll(vs/editor/standalone/browser/standaloneEditor.js;languages.Token=>Token) 把独立编辑器 API 整体拉入 monaco.editor 命名空间;
  • 命名空间骨架:recipe 本身手写声明了 monacomonaco.editormonaco.languagesmonaco.worker 四个命名空间的框架(含 EnvironmentIDisposableIEventEmitter 等手写接口),#include 的声明被填充进这些骨架中;
  • 版本校验:recipe 文件末尾的 //dtsv=3 与运行时常量 dtsv(见 monaco-api.ts 第 15 行)必须一致,否则拒绝生成并提示需要重启 gulp watch——这是防止旧配方与新生成器混用的保护机制。

生成过程中还有几个值得注意的清洗规则(getMassagedTopLevelDeclarationText):带 @internalprivate 标记的成员会被整体剔除,export default 被改写为普通 exportconst 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-distrobuild/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()usageContentbuild/monaco/monaco.usage.recipe 作为“使用引用”注入。后者的作用正如文件头注释所写——“adding references to various symbols which should not be removed via tree shaking”,例如通过 a = editorAPI.CancellationTokenSource; 这类语句保住宿根引用(ServiceIdentifier.typeSyncDescriptor0.ctor 等运行时才能发现的依赖);
  • 额外拷贝vs/base/browser/dompurify/dompurify.jsvs/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.jsonmodule: ./esm/vs/editor/editor.main.js 声明的路径。

阶段三:finalEditorResourcesTask —— 组装发布目录

build/gulpfile.editor.ts 第 131 行,用 es.merge 并行组装以下资源到 out-monaco-editor-core/

  1. LICENSE 与第三方法律文件:原样拷贝 build/monaco/LICENSEbuild/monaco/ThirdPartyNotices.txt
  2. 类型入口转换:把 src/vs/monaco.d.tstoExternalDTS 函数改写后另存为 esm/vs/editor/editor.api.d.ts。该函数的作用是“外部化”:把 declare namespace monaco { ... } 的外层包裹剥掉,declare namespace monaco.editor 等变为 export namespace editor,并把 declare var MonacoEnvironment 改写为 declare global 形式——使同一份声明文件既能作为仓库内部的全局声明使用,又能作为 npm 包的对外的模块类型定义;
  3. 改写 package.json:基于 build/monaco/package.json 做三处动态修改——private 置为 false;从 src/vs/base/common/marked/cgmanifest.jsonsrc/vs/base/browser/dompurify/cgmanifest.json 读取精确版本号,注入 dependencies 字段(markeddompurify,读取失败会直接抛错终止构建,保证依赖版本一定可溯源);
  4. version.txt:写入第 4.1 节所述的 HEAD SHA1 溯源链接;
  5. 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.jsonprivate: false、正确的 dependencies)、类型文件、README、LICENSE 全部就位,发布动作本身只是把整个目录推送到 npm 注册表。

6. 完整操作清单(继承原文档四步流程)

步骤 操作 关键点 / 源码依据
1 生成 monaco.d.ts 运行 gulp watch 时自动生成,无需手动维护;由 build/lib/monaco-api.tsbuild/monaco/monaco.d.ts.recipe 抽取
2 升版 修改 build/monaco/package.jsonversion 字段;版本号会进入产物文件头 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 读取失败、模块或类型名在源码中不存在)都会显式报错终止,不会产出半成品包。

登录后查看全文
热门项目推荐
相关项目推荐