build(site): [SITE-07] reproduce complete Monaco contribution assembly

This commit is contained in:
Harvey Zhao committed 2026-09-15 11:29:17 +08:00
1 parent 0cfdf4a7e3
commit 23ac6f7128
26 files changed
+3812 -6

No files matched your search

+1
View File
@@ -42,3 +42,4 @@ refactor/baselines/site-vendor/monaco-core-origins/** -text whitespace=-trailing
docs/licenses/monaco-editor/core-origins/** -text whitespace=-trailing-space,cr-at-eol
refactor/baselines/site-vendor/monaco-core-build/** -text whitespace=-trailing-space,cr-at-eol
refactor/baselines/monaco-core-locales.json text eol=lf
refactor/baselines/site-vendor/monaco-contributions/** -text whitespace=-trailing-space,cr-at-eol
+2 -1
View File
@@ -170,7 +170,8 @@
"verify:monaco-mode-sources": "node scripts/site-vendor/monaco/reproduce-modes.ts",
"verify:monaco-basic-sources": "node scripts/site-vendor/monaco/reproduce-basic.ts",
"verify:monaco-core-origins": "node scripts/site-vendor/monaco/reproduce-core-origins.ts",
"verify:monaco-core-build": "node scripts/site-vendor/monaco/reproduce-core-build.ts"
"verify:monaco-core-build": "node scripts/site-vendor/monaco/reproduce-core-build.ts",
"verify:monaco-contributions": "node scripts/site-vendor/monaco/reproduce-contributions.ts"
},
"browserslist": "last 1 Chrome version",
"devDependencies": {
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,761 @@
{
"schemaVersion": 1,
"task": "SITE-07",
"status": "checkpoint-incomplete",
"recordedAt": "2026-09-15T03:28:21.144Z",
"baseCommit": "0cfdf4a7e3292d6f2006c9d4e734b90afc771755",
"node": "v24.21.0",
"yarn": "1.22.22",
"fingerprints": {
"refactor/baselines/monaco-contributions-provenance.json": "62fdffdd4d7174d005de0a9a8ca86a5a7f5507ec975dbdb145971f15b4b8790d",
"scripts/site-vendor/monaco/contributions.ts": "2bd178e8060d671979cbf29275ce4b9aa444f6ada67e0dfdcf7886bb82531b5a",
"scripts/site-vendor/monaco/reproduce-contributions.ts": "ef7134a78a45308139e20ed3ef90db7dd488564a10bb3a1f9281ab01271d8e6b",
"test/monaco-provenance.test.js": "35f9a9d36193580cac0d64619b58675196b9217c361ecca0d8d71667afe3c7d5",
"test/browser/editor-modes.spec.js": "4cd0b14c18d244c7121e5354b8feceaa07831cf60b34a7017a4d5927538a8582",
"test/browser/editor-basic-languages.spec.js": "f3f594819f0ff2506a719542c4d699673d2865a84d621059c2577469662aa290",
"docs/assets/js/vs/editor/editor.main.js": "05a50ed50bb013b6e7a82d25686186aa7bdff8b8209c7badab00e046b5e99618"
},
"reproduction": {
"result": {
"archives": 6,
"gitSources": 102,
"groups": [
{
"name": "monaco-typescript",
"modules": 3
},
{
"name": "monaco-css",
"modules": 2
},
{
"name": "monaco-json",
"modules": 2
},
{
"name": "monaco-html",
"modules": 2
},
{
"name": "monaco-languages",
"modules": 79
}
],
"modules": 88,
"outputs": [
{
"type": "dev",
"bytes": 7070291,
"sha256": "07b20188585825310d08702b1172a349b21edb5b3ff566beae77aa2dede6183f",
"exactAssembly": true
},
{
"type": "min",
"bytes": 2792306,
"sha256": "05a50ed50bb013b6e7a82d25686186aa7bdff8b8209c7badab00e046b5e99618",
"exactAssembly": true
}
],
"fullUpstreamTypecheck": false,
"coreTypeScriptAndCssRebuilt": false
},
"offline": {
"exitCode": 0,
"logSha256": "004775a6c2542157e0ece55383ec457412427f3897ffa1b969ea6da201a67035"
},
"online": {
"exitCode": 0,
"logSha256": "004775a6c2542157e0ece55383ec457412427f3897ffa1b969ea6da201a67035"
}
},
"unit": {
"passed": 23,
"failed": 0,
"logSha256": "a73af771c578d713b5fbcda0c0a416d298bb12e6849ac3c7c970ece8b6b91473"
},
"types": {
"exitCode": 0,
"logSha256": "4fd31ba351a5624c86a0b8992f70a9d5a501f4c2709aae0e211cc50d9952c446"
},
"lint": {
"exitCode": 0
},
"browser": {
"command": "yarn test:browser test/browser/editor-modes.spec.js test/browser/editor-basic-languages.spec.js --workers=1",
"reportSha256": "8e9bb5c0ef29871ca7b2ba7e4126e0dd7c3bf13bdb2f96373960bf80db545a03",
"stats": {
"startTime": "2026-09-15T03:22:10.323Z",
"duration": 21751.57,
"expected": 6,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"errors": [],
"tests": [
{
"title": "docs Monaco preserves all upstream basic-language tokenization cases",
"project": "chromium",
"status": "passed",
"browser": "153.0.8010.12",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"checks": [
{
"name": "monaco-basic-results",
"value": {
"cases": 2523,
"suites": 81,
"languages": [
"abap",
"apex",
"azcli",
"bat",
"bicep",
"cameligo",
"clojure",
"coffeescript",
"cpp",
"csharp",
"csp",
"css",
"dart",
"dockerfile",
"ecl",
"elixir",
"flow9",
"fsharp",
"go",
"graphql",
"handlebars",
"hcl",
"html",
"javascript",
"java",
"julia",
"kotlin",
"less",
"lexon",
"liquid",
"lua",
"m3",
"markdown",
"mips",
"msdax",
"mysql",
"objective-c",
"pascal",
"pascaligo",
"perl",
"sql",
"php",
"pla",
"postiats",
"powerquery",
"powershell",
"proto",
"pug",
"python",
"qsharp",
"r",
"razor",
"redis",
"restructuredtext",
"ruby",
"rust",
"sb",
"scala",
"scheme",
"scss",
"shell",
"sol",
"aes",
"sparql",
"st",
"swift",
"systemverilog",
"tcl",
"twig",
"typescript",
"vb",
"xml",
"yaml",
"ini",
"pgsql",
"redshift"
],
"failures": []
}
}
]
},
{
"title": "docs Monaco preserves all upstream basic-language tokenization cases",
"project": "firefox",
"status": "passed",
"browser": "155.0",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"checks": [
{
"name": "monaco-basic-results",
"value": {
"cases": 2523,
"suites": 81,
"languages": [
"abap",
"apex",
"azcli",
"bat",
"bicep",
"cameligo",
"clojure",
"coffeescript",
"cpp",
"csharp",
"csp",
"css",
"dart",
"dockerfile",
"ecl",
"elixir",
"flow9",
"fsharp",
"go",
"graphql",
"handlebars",
"hcl",
"html",
"javascript",
"java",
"julia",
"kotlin",
"less",
"lexon",
"liquid",
"lua",
"m3",
"markdown",
"mips",
"msdax",
"mysql",
"objective-c",
"pascal",
"pascaligo",
"perl",
"sql",
"php",
"pla",
"postiats",
"powerquery",
"powershell",
"proto",
"pug",
"python",
"qsharp",
"r",
"razor",
"redis",
"restructuredtext",
"ruby",
"rust",
"sb",
"scala",
"scheme",
"scss",
"shell",
"sol",
"aes",
"sparql",
"st",
"swift",
"systemverilog",
"tcl",
"twig",
"typescript",
"vb",
"xml",
"yaml",
"ini",
"pgsql",
"redshift"
],
"failures": []
}
}
]
},
{
"title": "docs Monaco preserves all upstream basic-language tokenization cases",
"project": "webkit",
"status": "passed",
"browser": "26.6",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"checks": [
{
"name": "monaco-basic-results",
"value": {
"cases": 2523,
"suites": 81,
"languages": [
"abap",
"apex",
"azcli",
"bat",
"bicep",
"cameligo",
"clojure",
"coffeescript",
"cpp",
"csharp",
"csp",
"css",
"dart",
"dockerfile",
"ecl",
"elixir",
"flow9",
"fsharp",
"go",
"graphql",
"handlebars",
"hcl",
"html",
"javascript",
"java",
"julia",
"kotlin",
"less",
"lexon",
"liquid",
"lua",
"m3",
"markdown",
"mips",
"msdax",
"mysql",
"objective-c",
"pascal",
"pascaligo",
"perl",
"sql",
"php",
"pla",
"postiats",
"powerquery",
"powershell",
"proto",
"pug",
"python",
"qsharp",
"r",
"razor",
"redis",
"restructuredtext",
"ruby",
"rust",
"sb",
"scala",
"scheme",
"scss",
"shell",
"sol",
"aes",
"sparql",
"st",
"swift",
"systemverilog",
"tcl",
"twig",
"typescript",
"vb",
"xml",
"yaml",
"ini",
"pgsql",
"redshift"
],
"failures": []
}
}
]
},
{
"title": "docs Monaco mode registrations update editor diagnostics and formatting across languages",
"project": "chromium",
"status": "passed",
"browser": "153.0.8010.12",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"checks": [
{
"name": "monaco-mode-results",
"value": {
"evidence": [
{
"language": "css",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-css",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-css",
"_fsPath": null
},
"owner": "css",
"code": "css-propertyvalueexpected",
"severity": 8,
"message": "property value expected",
"source": "css",
"startLineNumber": 1,
"startColumn": 12,
"endLineNumber": 1,
"endColumn": 13
}
]
},
{
"language": "json",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-json",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-json",
"_fsPath": null
},
"owner": "json",
"code": "516",
"severity": 8,
"message": "Value expected",
"source": "json",
"startLineNumber": 1,
"startColumn": 10,
"endLineNumber": 1,
"endColumn": 11
}
]
},
{
"language": "json",
"formatted": "{\r\n \"value\": 1\r\n}"
},
{
"language": "typescript",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-typescript",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-typescript",
"_fsPath": null
},
"owner": "typescript",
"code": "2322",
"severity": 8,
"message": "Type 'string' is not assignable to type 'number'.",
"startLineNumber": 1,
"startColumn": 7,
"endLineNumber": 1,
"endColumn": 12,
"relatedInformation": [],
"tags": []
}
]
},
{
"language": "html",
"formatted": "<div>\r\n <p>hello</p>\r\n <p>world</p>\r\n</div>"
}
],
"loadedModes": [
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/css/cssMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/json/jsonMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/typescript/tsMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/html/htmlMode.js",
"status": 200
}
]
}
}
]
},
{
"title": "docs Monaco mode registrations update editor diagnostics and formatting across languages",
"project": "firefox",
"status": "passed",
"browser": "155.0",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"checks": [
{
"name": "monaco-mode-results",
"value": {
"evidence": [
{
"language": "css",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-css",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-css",
"_fsPath": null
},
"owner": "css",
"code": "css-propertyvalueexpected",
"severity": 8,
"message": "property value expected",
"source": "css",
"startLineNumber": 1,
"startColumn": 12,
"endLineNumber": 1,
"endColumn": 13
}
]
},
{
"language": "json",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-json",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-json",
"_fsPath": null
},
"owner": "json",
"code": "516",
"severity": 8,
"message": "Value expected",
"source": "json",
"startLineNumber": 1,
"startColumn": 10,
"endLineNumber": 1,
"endColumn": 11
}
]
},
{
"language": "json",
"formatted": "{\r\n \"value\": 1\r\n}"
},
{
"language": "typescript",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-typescript",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-typescript",
"_fsPath": null
},
"owner": "typescript",
"code": "2322",
"severity": 8,
"message": "Type 'string' is not assignable to type 'number'.",
"startLineNumber": 1,
"startColumn": 7,
"endLineNumber": 1,
"endColumn": 12,
"relatedInformation": [],
"tags": []
}
]
},
{
"language": "html",
"formatted": "<div>\r\n <p>hello</p>\r\n <p>world</p>\r\n</div>"
}
],
"loadedModes": [
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/css/cssMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/json/jsonMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/typescript/tsMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/html/htmlMode.js",
"status": 200
}
]
}
}
]
},
{
"title": "docs Monaco mode registrations update editor diagnostics and formatting across languages",
"project": "webkit",
"status": "passed",
"browser": "26.6",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"checks": [
{
"name": "monaco-mode-results",
"value": {
"evidence": [
{
"language": "css",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-css",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-css",
"_fsPath": null
},
"owner": "css",
"code": "css-propertyvalueexpected",
"severity": 8,
"message": "property value expected",
"source": "css",
"startLineNumber": 1,
"startColumn": 12,
"endLineNumber": 1,
"endColumn": 13
}
]
},
{
"language": "json",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-json",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-json",
"_fsPath": null
},
"owner": "json",
"code": "516",
"severity": 8,
"message": "Value expected",
"source": "json",
"startLineNumber": 1,
"startColumn": 10,
"endLineNumber": 1,
"endColumn": 11
}
]
},
{
"language": "json",
"formatted": "{\n \"value\": 1\n}"
},
{
"language": "typescript",
"markers": [
{
"resource": {
"scheme": "file",
"authority": "",
"path": "/mode-probe-typescript",
"query": "",
"fragment": "",
"_formatted": "file:///mode-probe-typescript",
"_fsPath": null
},
"owner": "typescript",
"code": "2322",
"severity": 8,
"message": "Type 'string' is not assignable to type 'number'.",
"startLineNumber": 1,
"startColumn": 7,
"endLineNumber": 1,
"endColumn": 12,
"relatedInformation": [],
"tags": []
}
]
},
{
"language": "html",
"formatted": "<div>\n <p>hello</p>\n <p>world</p>\n</div>"
}
],
"loadedModes": [
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/css/cssMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/json/jsonMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/typescript/tsMode.js",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/language/html/htmlMode.js",
"status": 200
}
]
}
}
]
}
]
},
"limitations": [
"Module emission and complete editor assembly reproduce exactly; this is not a full upstream semantic typecheck.",
"The original core TypeScript pipeline, CSS and remaining embedded origins remain open.",
"Runtime assets and notice delivery are unchanged; no physical device, external SDK, remote CI or publication evidence is claimed.",
"Thumbnail default compatibility policy remains pending."
]
}
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 - present Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,972 @@
const gulp = require('gulp');
/**
* @typedef { { src:string; 'npm/dev':string; 'npm/min':string; built:string; releaseDev:string; releaseMin:string; } } ICorePaths
* @typedef { { src:string; dev:string; min:string; esm: string; } } IPluginPaths
* @typedef { { name:string; contrib:string; modulePrefix:string; rootPath:string; paths:IPluginPaths } } IPlugin
* @typedef { { METADATA: {CORE:{paths:ICorePaths}; PLUGINS:IPlugin[];} } } IMetadata
* @type { IMetadata }
*/
const metadata = require('./monaco-editor/metadata');
const es = require('event-stream');
const path = require('path');
const fs = require('fs');
const rimraf = require('rimraf');
const cp = require('child_process');
const yaserver = require('yaserver');
const http = require('http');
const CleanCSS = require('clean-css');
const uncss = require('uncss');
const File = require('vinyl');
const ts = require('typescript');
const WEBSITE_GENERATED_PATH = path.join(__dirname, 'monaco-editor/website/playground/new-samples');
const MONACO_EDITOR_VERSION = (function () {
const packageJsonPath = path.join(__dirname, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
const version = packageJson.version;
if (!/\d+\.\d+\.\d+/.test(version)) {
console.log('unrecognized package.json version: ' + version);
process.exit(0);
}
return version;
})();
async function _execute(task) {
// Always invoke as if it were a callback task
return new Promise((resolve, reject) => {
if (task.length === 1) {
// this is a calback task
task((err) => {
if (err) {
return reject(err);
}
resolve();
});
return;
}
const taskResult = task();
if (typeof taskResult === 'undefined') {
// this is a sync task
resolve();
return;
}
if (typeof taskResult.then === 'function') {
// this is a promise returning task
taskResult.then(resolve, reject);
return;
}
// this is a stream returning task
taskResult.on('end', (_) => resolve());
taskResult.on('error', (err) => reject(err));
});
}
function taskSeries(...tasks) {
return async () => {
for (let i = 0; i < tasks.length; i++) {
await _execute(tasks[i]);
}
};
}
const cleanReleaseTask = function (cb) {
rimraf('release', { maxBusyTries: 1 }, cb);
};
gulp.task(
'release',
taskSeries(cleanReleaseTask, function () {
return es.merge(
// dev folder
releaseOne('dev'),
// min folder
releaseOne('min'),
// esm folder
ESM_release(),
// package.json
gulp
.src('package.json')
.pipe(
es.through(function (data) {
var json = JSON.parse(data.contents.toString());
json.private = false;
data.contents = Buffer.from(JSON.stringify(json, null, ' '));
this.emit('data', data);
})
)
.pipe(gulp.dest('release')),
gulp.src('CHANGELOG.md').pipe(gulp.dest('release')),
// min-maps folder
gulp.src('node_modules/monaco-editor-core/min-maps/**/*').pipe(gulp.dest('release/min-maps')),
// other files
gulp
.src([
'node_modules/monaco-editor-core/LICENSE',
'node_modules/monaco-editor-core/monaco.d.ts',
'node_modules/monaco-editor-core/ThirdPartyNotices.txt',
'README.md'
])
.pipe(addPluginDTS())
.pipe(addPluginThirdPartyNotices())
.pipe(gulp.dest('release'))
);
})
);
/**
* Release to `dev` or `min`.
* @param {'dev'|'min'} type
* @returns {NodeJS.ReadWriteStream}
*/
function releaseOne(type) {
return es.merge(
gulp
.src('node_modules/monaco-editor-core/' + type + '/**/*')
.pipe(addPluginContribs(type))
.pipe(gulp.dest('release/' + type)),
pluginStreams(type, 'release/' + type + '/')
);
}
/**
* Release plugins to `dev` or `min`.
* @param {'dev'|'min'} type
* @param {string} destinationPath
* @returns {NodeJS.ReadWriteStream}
*/
function pluginStreams(type, destinationPath) {
return es.merge(
metadata.METADATA.PLUGINS.map(function (plugin) {
return pluginStream(plugin, type, destinationPath);
})
);
}
/**
* Release a plugin to `dev` or `min`.
* @param {IPlugin} plugin
* @param {'dev'|'min'} type
* @param {string} destinationPath
* @returns {NodeJS.ReadWriteStream}
*/
function pluginStream(plugin, type, destinationPath) {
const pluginPath = path.join(plugin.rootPath, plugin.paths[type]); // dev or min
const contribPath =
path.join(pluginPath, plugin.contrib.substr(plugin.modulePrefix.length)) + '.js';
return gulp
.src([pluginPath + '/**/*', '!' + contribPath])
.pipe(
es.through(
/**
* @param {File} data
*/
function (data) {
if (!/_\.contribution/.test(data.path)) {
this.emit('data', data);
return;
}
let contents = data.contents.toString();
contents = contents.replace(
'define(["require", "exports"],',
'define(["require", "exports", "vs/editor/editor.api"],'
);
data.contents = Buffer.from(contents);
this.emit('data', data);
}
)
)
.pipe(gulp.dest(destinationPath + plugin.modulePrefix));
}
/**
* Edit editor.main.js:
* - rename the AMD module 'vs/editor/editor.main' to 'vs/editor/edcore.main'
* - append monaco.contribution modules from plugins
* - append new AMD module 'vs/editor/editor.main' that stiches things together
*
* @param {'dev'|'min'} type
* @returns {NodeJS.ReadWriteStream}
*/
function addPluginContribs(type) {
return es.through(
/**
* @param {File} data
*/
function (data) {
if (!/editor\.main\.js$/.test(data.path)) {
this.emit('data', data);
return;
}
let contents = data.contents.toString();
// Rename the AMD module 'vs/editor/editor.main' to 'vs/editor/edcore.main'
contents = contents.replace(/"vs\/editor\/editor\.main\"/, '"vs/editor/edcore.main"');
/** @type {string[]} */
let extraContent = [];
/** @type {string[]} */
let allPluginsModuleIds = [];
metadata.METADATA.PLUGINS.forEach(function (plugin) {
allPluginsModuleIds.push(plugin.contrib);
const pluginPath = path.join(plugin.rootPath, plugin.paths[type]); // dev or min
const contribPath =
path.join(__dirname, pluginPath, plugin.contrib.substr(plugin.modulePrefix.length)) +
'.js';
let contribContents = fs.readFileSync(contribPath).toString();
contribContents = contribContents.replace(
/define\((['"][a-z\/\-]+\/fillers\/monaco-editor-core['"]),\[\],/,
"define($1,['vs/editor/editor.api'],"
);
extraContent.push(contribContents);
});
extraContent.push(
`define("vs/editor/editor.main", ["vs/editor/edcore.main","${allPluginsModuleIds.join(
'","'
)}"], function(api) { return api; });`
);
let insertIndex = contents.lastIndexOf('//# sourceMappingURL=');
if (insertIndex === -1) {
insertIndex = contents.length;
}
contents =
contents.substring(0, insertIndex) +
'\n' +
extraContent.join('\n') +
'\n' +
contents.substring(insertIndex);
data.contents = Buffer.from(contents);
this.emit('data', data);
}
);
}
/**
* @returns {NodeJS.ReadWriteStream}
*/
function ESM_release() {
return es.merge(
gulp
.src([
'node_modules/monaco-editor-core/esm/**/*',
// we will create our own editor.api.d.ts which also contains the plugins API
'!node_modules/monaco-editor-core/esm/vs/editor/editor.api.d.ts'
])
.pipe(ESM_addImportSuffix())
.pipe(ESM_addPluginContribs('release/esm'))
.pipe(gulp.dest('release/esm')),
ESM_pluginStreams('release/esm/')
);
}
/**
* Release plugins to `esm`.
* @param {string} destinationPath
* @returns {NodeJS.ReadWriteStream}
*/
function ESM_pluginStreams(destinationPath) {
return es.merge(
metadata.METADATA.PLUGINS.map(function (plugin) {
return ESM_pluginStream(plugin, destinationPath);
})
);
}
/**
* Release a plugin to `esm`.
* Adds a dependency to 'vs/editor/editor.api' in contrib files in order for `monaco` to be defined.
* Rewrites imports for 'monaco-editor-core/**'
* @param {IPlugin} plugin
* @param {string} destinationPath
* @returns {NodeJS.ReadWriteStream}
*/
function ESM_pluginStream(plugin, destinationPath) {
const DESTINATION = path.join(__dirname, destinationPath);
const pluginPath = path.join(plugin.rootPath, plugin.paths['esm']);
return gulp
.src([pluginPath + '/**/*'])
.pipe(
es.through(
/**
* @param {File} data
*/
function (data) {
if (!/(\.js$)|(\.ts$)/.test(data.path)) {
this.emit('data', data);
return;
}
let contents = data.contents.toString();
const info = ts.preProcessFile(contents);
for (let i = info.importedFiles.length - 1; i >= 0; i--) {
let importText = info.importedFiles[i].fileName;
const pos = info.importedFiles[i].pos;
const end = info.importedFiles[i].end;
if (!/(^\.\/)|(^\.\.\/)/.test(importText)) {
// non-relative import
if (!/^monaco-editor-core/.test(importText)) {
console.error(
`Non-relative import for unknown module: ${importText} in ${data.path}`
);
process.exit(0);
}
if (importText === 'monaco-editor-core') {
importText = 'monaco-editor-core/esm/vs/editor/editor.api';
}
const myFileDestPath = path.join(DESTINATION, plugin.modulePrefix, data.relative);
const importFilePath = path.join(
DESTINATION,
importText.substr('monaco-editor-core/esm/'.length)
);
let relativePath = path
.relative(path.dirname(myFileDestPath), importFilePath)
.replace(/\\/g, '/');
if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) {
relativePath = './' + relativePath;
}
contents =
contents.substring(0, pos + 1) + relativePath + contents.substring(end + 1);
}
}
contents = contents.replace(/\/\/# sourceMappingURL=.*((\r?\n)|$)/g, '');
data.contents = Buffer.from(contents);
this.emit('data', data);
}
)
)
.pipe(
es.through(
/**
* @param {File} data
*/
function (data) {
if (!/monaco\.contribution\.js$/.test(data.path)) {
this.emit('data', data);
return;
}
const myFileDestPath = path.join(DESTINATION, plugin.modulePrefix, data.relative);
const apiFilePath = path.join(DESTINATION, 'vs/editor/editor.api');
let relativePath = path
.relative(path.dirname(myFileDestPath), apiFilePath)
.replace(/\\/g, '/');
if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) {
relativePath = './' + relativePath;
}
let contents = data.contents.toString();
contents = `import '${relativePath}';\n` + contents;
data.contents = Buffer.from(contents);
this.emit('data', data);
}
)
)
.pipe(ESM_addImportSuffix())
.pipe(gulp.dest(destinationPath + plugin.modulePrefix));
}
/**
* Adds `.js` to all import statements.
* @returns {NodeJS.ReadWriteStream}
*/
function ESM_addImportSuffix() {
return es.through(
/**
* @param {File} data
*/
function (data) {
if (!/\.js$/.test(data.path)) {
this.emit('data', data);
return;
}
let contents = data.contents.toString();
const info = ts.preProcessFile(contents);
for (let i = info.importedFiles.length - 1; i >= 0; i--) {
const importText = info.importedFiles[i].fileName;
const pos = info.importedFiles[i].pos;
const end = info.importedFiles[i].end;
if (/\.css$/.test(importText)) {
continue;
}
contents =
contents.substring(0, pos + 1) + importText + '.js' + contents.substring(end + 1);
}
data.contents = Buffer.from(contents);
this.emit('data', data);
}
);
}
/**
* - Rename esm/vs/editor/editor.main.js to esm/vs/editor/edcore.main.js
* - Create esm/vs/editor/editor.main.js that that stiches things together
* @param {string} dest
* @returns {NodeJS.ReadWriteStream}
*/
function ESM_addPluginContribs(dest) {
const DESTINATION = path.join(__dirname, dest);
return es.through(
/**
* @param {File} data
*/
function (data) {
if (!/editor\.main\.js$/.test(data.path)) {
this.emit('data', data);
return;
}
this.emit(
'data',
new File({
path: data.path.replace(/editor\.main/, 'edcore.main'),
base: data.base,
contents: data.contents
})
);
const mainFileDestPath = path.join(DESTINATION, 'vs/editor/editor.main.js');
/** @type {string[]} */
let mainFileImports = [];
metadata.METADATA.PLUGINS.forEach(function (plugin) {
const contribDestPath = path.join(DESTINATION, plugin.contrib);
let relativePath = path
.relative(path.dirname(mainFileDestPath), contribDestPath)
.replace(/\\/g, '/');
if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) {
relativePath = './' + relativePath;
}
mainFileImports.push(relativePath);
});
const mainFileContents =
mainFileImports.map((name) => `import '${name}';`).join('\n') +
`\n\nexport * from './edcore.main';`;
this.emit(
'data',
new File({
path: data.path,
base: data.base,
contents: Buffer.from(mainFileContents)
})
);
}
);
}
/**
* Edit monaco.d.ts:
* - append monaco.d.ts from plugins
* @returns {NodeJS.ReadWriteStream}
*/
function addPluginDTS() {
return es.through(
/**
* @param {File} data
*/
function (data) {
if (!/monaco\.d\.ts$/.test(data.path)) {
this.emit('data', data);
return;
}
let contents = data.contents.toString();
/** @type {string[]} */
const extraContent = [];
metadata.METADATA.PLUGINS.forEach(function (plugin) {
const dtsPath = path.join(plugin.rootPath, './monaco.d.ts');
try {
let plugindts = fs.readFileSync(dtsPath).toString();
plugindts = plugindts.replace(/\/\/\/ <reference.*\n/m, '');
extraContent.push(plugindts);
} catch (err) {
return;
}
});
contents =
[
'/*!-----------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * Type definitions for monaco-editor',
' * Released under the MIT license',
'*-----------------------------------------------------------*/'
].join('\n') +
'\n' +
contents +
'\n' +
extraContent.join('\n');
// Ensure consistent indentation and line endings
contents = cleanFile(contents);
data.contents = Buffer.from(contents);
this.emit(
'data',
new File({
path: path.join(path.dirname(data.path), 'esm/vs/editor/editor.api.d.ts'),
base: data.base,
contents: Buffer.from(toExternalDTS(contents))
})
);
fs.writeFileSync('monaco-editor/website/playground/monaco.d.ts.txt', contents);
fs.writeFileSync('monaco-editor/typedoc/monaco.d.ts', contents);
this.emit('data', data);
}
);
}
/**
* Transforms a .d.ts which uses internal modules (namespaces) to one which is usable with external modules
* This function is duplicated in the `vscode` repo.
* @param {string} contents
* @returns string
*/
function toExternalDTS(contents) {
let lines = contents.split(/\r\n|\r|\n/);
let killNextCloseCurlyBrace = false;
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
if (killNextCloseCurlyBrace) {
if ('}' === line) {
lines[i] = '';
killNextCloseCurlyBrace = false;
continue;
}
if (line.indexOf(' ') === 0) {
lines[i] = line.substr(4);
} else if (line.charAt(0) === '\t') {
lines[i] = line.substr(1);
}
continue;
}
if ('declare namespace monaco {' === line) {
lines[i] = '';
killNextCloseCurlyBrace = true;
continue;
}
if (line.indexOf('declare namespace monaco.') === 0) {
lines[i] = line.replace('declare namespace monaco.', 'export namespace ');
}
if (line.indexOf('declare let MonacoEnvironment') === 0) {
lines[i] = `declare global {\n let MonacoEnvironment: Environment | undefined;\n}`;
}
if (line.indexOf(' MonacoEnvironment?') === 0) {
lines[i] = ` MonacoEnvironment?: Environment | undefined;`;
}
}
return lines.join('\n').replace(/\n\n\n+/g, '\n\n');
}
/**
* Normalize line endings and ensure consistent 4 spaces indentation
* @param {string} contents
* @returns {string}
*/
function cleanFile(contents) {
return contents
.split(/\r\n|\r|\n/)
.map(function (line) {
const m = line.match(/^(\t+)/);
if (!m) {
return line;
}
const tabsCount = m[1].length;
let newIndent = '';
for (let i = 0; i < 4 * tabsCount; i++) {
newIndent += ' ';
}
return newIndent + line.substring(tabsCount);
})
.join('\n');
}
/**
* Edit ThirdPartyNotices.txt:
* - append ThirdPartyNotices.txt from plugins
* @returns {NodeJS.ReadWriteStream}
*/
function addPluginThirdPartyNotices() {
return es.through(
/**
* @param {File} data
*/
function (data) {
if (!/ThirdPartyNotices\.txt$/.test(data.path)) {
this.emit('data', data);
return;
}
let contents = data.contents.toString();
console.log('ADDING ThirdPartyNotices from ./ThirdPartyNotices.txt');
let thirdPartyNoticeContent = fs
.readFileSync(path.join(__dirname, 'ThirdPartyNotices.txt'))
.toString();
thirdPartyNoticeContent = thirdPartyNoticeContent.split('\n').slice(8).join('\n');
contents += '\n' + thirdPartyNoticeContent;
data.contents = Buffer.from(contents);
this.emit('data', data);
}
);
}
// --- website
const cleanWebsiteTask = function (cb) {
rimraf('../monaco-editor-website', { maxBusyTries: 1 }, cb);
};
const buildWebsiteTask = taskSeries(cleanWebsiteTask, function () {
function replaceWithRelativeResource(dataPath, contents, regex, callback) {
return contents.replace(regex, function (_, m0) {
var filePath = path.join(path.dirname(dataPath), m0);
return callback(m0, fs.readFileSync(filePath));
});
}
var waiting = 0;
var done = false;
return es
.merge(
gulp
.src(['website/**/*'], { dot: true })
.pipe(
es.through(
function (data) {
if (!data.contents || !/\.(html)$/.test(data.path) || /new-samples/.test(data.path)) {
return this.emit('data', data);
}
var contents = data.contents.toString();
contents = contents.replace(/\.\.\/release\/dev/g, 'node_modules/monaco-editor/min');
contents = contents.replace(/{{version}}/g, MONACO_EDITOR_VERSION);
contents = contents.replace(/{{year}}/g, new Date().getFullYear());
// Preload xhr contents
contents = replaceWithRelativeResource(
data.path,
contents,
/<pre data-preload="([^"]+)".*/g,
function (m0, fileContents) {
return (
'<pre data-preload="' +
m0 +
'" style="display:none">' +
fileContents
.toString('utf8')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;') +
'</pre>'
);
}
);
// Inline fork.png
contents = replaceWithRelativeResource(
data.path,
contents,
/src="(\.\/fork.png)"/g,
function (m0, fileContents) {
return 'src="data:image/png;base64,' + fileContents.toString('base64') + '"';
}
);
var allCSS = '';
var tmpcontents = replaceWithRelativeResource(
data.path,
contents,
/<link data-inline="yes-please" href="([^"]+)".*/g,
function (m0, fileContents) {
allCSS += fileContents.toString('utf8');
return '';
}
);
tmpcontents = tmpcontents.replace(/<script.*/g, '');
tmpcontents = tmpcontents.replace(/<link.*/g, '');
waiting++;
uncss(
tmpcontents,
{
raw: allCSS,
ignore: [/\.alert\b/, /\.alert-error\b/, /\.playground-page\b/]
},
function (err, output) {
waiting--;
if (!err) {
output = new CleanCSS().minify(output).styles;
var isFirst = true;
contents = contents.replace(
/<link data-inline="yes-please" href="([^"]+)".*/g,
function (_, m0) {
if (isFirst) {
isFirst = false;
return '<style>' + output + '</style>';
}
return '';
}
);
}
// Inline javascript
contents = replaceWithRelativeResource(
data.path,
contents,
/<script data-inline="yes-please" src="([^"]+)".*/g,
function (m0, fileContents) {
return '<script>' + fileContents.toString('utf8') + '</script>';
}
);
data.contents = Buffer.from(contents.split(/\r\n|\r|\n/).join('\n'));
this.emit('data', data);
if (done && waiting === 0) {
this.emit('end');
}
}.bind(this)
);
},
function () {
done = true;
if (waiting === 0) {
this.emit('end');
}
}
)
)
.pipe(gulp.dest('../monaco-editor-website'))
)
.pipe(
es.through(
function (data) {
this.emit('data', data);
},
function () {
// temporarily create package.json so that npm install doesn't bark
fs.writeFileSync('../monaco-editor-website/package.json', '{}');
fs.writeFileSync('../monaco-editor-website/.nojekyll', '');
cp.execSync('npm install monaco-editor', {
cwd: path.join(__dirname, '../monaco-editor-website')
});
fs.unlinkSync('../monaco-editor-website/package.json');
this.emit('end');
}
)
);
});
gulp.task('build-website', buildWebsiteTask);
gulp.task('prepare-website-branch', async function () {
cp.execSync('git init', {
cwd: path.join(__dirname, '../monaco-editor-website')
});
let remoteUrl = cp.execSync('git remote get-url origin');
let committerUserName = cp.execSync("git log --format='%an' -1");
let committerEmail = cp.execSync("git log --format='%ae' -1");
cp.execSync(`git config user.name ${committerUserName}`, {
cwd: path.join(__dirname, '../monaco-editor-website')
});
cp.execSync(`git config user.email ${committerEmail}`, {
cwd: path.join(__dirname, '../monaco-editor-website')
});
cp.execSync(`git remote add origin ${remoteUrl}`, {
cwd: path.join(__dirname, '../monaco-editor-website')
});
cp.execSync('git checkout -b gh-pages', {
cwd: path.join(__dirname, '../monaco-editor-website')
});
cp.execSync('git add .', {
cwd: path.join(__dirname, '../monaco-editor-website')
});
cp.execSync('git commit -m "Publish website"', {
cwd: path.join(__dirname, '../monaco-editor-website')
});
console.log('RUN monaco-editor-website>git push origin gh-pages --force');
});
const generateTestSamplesTask = function () {
var sampleNames = fs.readdirSync(path.join(__dirname, 'monaco-editor/test/samples'));
var samples = sampleNames.map(function (sampleName) {
var samplePath = path.join(__dirname, 'monaco-editor/test/samples', sampleName);
var sampleContent = fs.readFileSync(samplePath).toString();
return {
name: sampleName,
content: sampleContent
};
});
var prefix =
'//This is a generated file via gulp generate-test-samples\ndefine([], function() { return';
var suffix = '; });';
fs.writeFileSync(
path.join(__dirname, 'monaco-editor/test/samples-all.generated.js'),
prefix + JSON.stringify(samples, null, '\t') + suffix
);
var PLAY_SAMPLES = require(path.join(WEBSITE_GENERATED_PATH, 'all.js')).PLAY_SAMPLES;
var locations = [];
for (var i = 0; i < PLAY_SAMPLES.length; i++) {
var sample = PLAY_SAMPLES[i];
var sampleId = sample.id;
var samplePath = path.join(WEBSITE_GENERATED_PATH, sample.path);
var html = fs.readFileSync(path.join(samplePath, 'sample.html'));
var js = fs.readFileSync(path.join(samplePath, 'sample.js'));
var css = fs.readFileSync(path.join(samplePath, 'sample.css'));
var result = [
'<!DOCTYPE html>',
'<!-- THIS IS A GENERATED FILE VIA gulp generate-test-samples -->',
'<html>',
'<head>',
' <base href="..">',
' <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />',
'</head>',
'<body>',
'<style>',
'/*----------------------------------------SAMPLE CSS START*/',
'',
css,
'',
'/*----------------------------------------SAMPLE CSS END*/',
'</style>',
'<a class="loading-opts" href="playground.generated/index.html">[&lt;&lt; BACK]</a> <br/>',
'THIS IS A GENERATED FILE VIA gulp generate-test-samples',
'',
'<div id="bar" style="margin-bottom: 6px;"></div>',
'',
'<div style="clear:both"></div>',
'<div id="outer-container" style="width:800px;height:450px;border: 1px solid grey">',
'<!-- ----------------------------------------SAMPLE HTML START-->',
'',
html,
'',
'<!-- ----------------------------------------SAMPLE HTML END-->',
'</div>',
'<div style="clear:both"></div>',
'',
'<script src="../metadata.js"></script>',
'<script src="dev-setup.js"></script>',
'<script>',
'loadEditor(function() {',
'/*----------------------------------------SAMPLE JS START*/',
'',
js,
'',
'/*----------------------------------------SAMPLE JS END*/',
'});',
'</script>',
'</body>',
'</html>'
];
fs.writeFileSync(
path.join(__dirname, 'monaco-editor/test/playground.generated/' + sampleId + '.html'),
result.join('\n')
);
locations.push({
path: sampleId + '.html',
name: sample.chapter + ' &gt; ' + sample.name
});
}
var index = [
'<!DOCTYPE html>',
'<!-- THIS IS A GENERATED FILE VIA gulp generate-test-samples -->',
'<html>',
'<head>',
' <base href="..">',
'</head>',
'<body>',
'<a class="loading-opts" href="index.html">[&lt;&lt; BACK]</a><br/>',
'THIS IS A GENERATED FILE VIA gulp generate-test-samples<br/><br/>',
locations
.map(function (location) {
return (
'<a class="loading-opts" href="playground.generated/' +
location.path +
'">' +
location.name +
'</a>'
);
})
.join('<br/>\n'),
'<script src="../metadata.js"></script>',
'<script src="dev-setup.js"></script>',
'</body>',
'</html>'
];
fs.writeFileSync(
path.join(__dirname, 'monaco-editor/test/playground.generated/index.html'),
index.join('\n')
);
};
function createSimpleServer(rootDir, port) {
yaserver
.createServer({
rootDir: rootDir
})
.then((staticServer) => {
const server = http.createServer((request, response) => {
return staticServer.handle(request, response);
});
server.listen(port, '127.0.0.1', () => {
console.log(`Running at http://127.0.0.1:${port}`);
});
});
}
gulp.task('generate-test-samples', taskSeries(generateTestSamplesTask));
gulp.task(
'simpleserver',
taskSeries(generateTestSamplesTask, function () {
const SERVER_ROOT = path.normalize(path.join(__dirname, '../'));
createSimpleServer(SERVER_ROOT, 8080);
createSimpleServer(SERVER_ROOT, 8088);
})
);
@@ -0,0 +1,86 @@
const requirejs = require('requirejs');
const path = require('path');
const fs = require('fs');
const Terser = require('terser');
const helpers = require('monaco-plugin-helpers');
const REPO_ROOT = path.resolve(__dirname, '..', '..');
const sha1 = helpers.getGitVersion(REPO_ROOT);
const semver = require('../../package.json').version;
const headerVersion = semver + '(' + sha1 + ')';
const BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * monaco-css version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/Microsoft/monaco-css/blob/master/LICENSE.md',
' *-----------------------------------------------------------------------------*/',
''
].join('\n');
bundleOne('monaco.contribution');
bundleOne('cssMode', ['vs/language/css/monaco.contribution']);
bundleOne('cssWorker');
function bundleOne(moduleId, exclude) {
requirejs.optimize(
{
baseUrl: 'out/amd/',
name: 'vs/language/css/' + moduleId,
out: 'release/dev/' + moduleId + '.js',
exclude: exclude,
paths: {
'vs/language/css': REPO_ROOT + '/monaco-css/out/amd',
'vs/language/css/fillers/monaco-editor-core':
REPO_ROOT + '/monaco-css/out/amd/fillers/monaco-editor-core-amd'
},
optimize: 'none',
packages: [
{
name: 'vscode-css-languageservice',
location: path.join(REPO_ROOT, 'node_modules/vscode-css-languageservice/lib/umd'),
main: 'cssLanguageService'
},
{
name: 'vscode-languageserver-types',
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-types/lib/umd'),
main: 'main'
},
{
name: 'vscode-languageserver-textdocument',
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-textdocument/lib/umd'),
main: 'main'
},
{
name: 'vscode-uri',
location: path.join(REPO_ROOT, 'node_modules/vscode-uri/lib/umd'),
main: 'index'
},
{
name: 'vscode-nls',
location: path.join(REPO_ROOT, 'monaco-css/out/amd/fillers'),
main: 'vscode-nls'
}
]
},
async function (buildResponse) {
const devFilePath = path.join(REPO_ROOT, 'monaco-css/release/dev/' + moduleId + '.js');
const minFilePath = path.join(REPO_ROOT, 'monaco-css/release/min/' + moduleId + '.js');
const fileContents = fs.readFileSync(devFilePath).toString();
console.log();
console.log(`Minifying ${devFilePath}...`);
const result = await Terser.minify(fileContents, {
output: {
comments: 'some'
}
});
console.log(`Done minifying ${devFilePath}.`);
try {
fs.mkdirSync(path.join(REPO_ROOT, 'monaco-css/release/min'));
} catch (err) {}
fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code);
}
);
}
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"module": "amd",
"moduleResolution": "node",
"outDir": "../out/amd",
"declaration": true,
"target": "es5",
"lib": ["dom", "es5", "es2015.collection", "es2015.promise", "es2015.iterable"]
}
}
@@ -0,0 +1,82 @@
(function () {
var METADATA = {
CORE: {
paths: {
src: '/vscode/out/vs',
'npm/dev': 'node_modules/monaco-editor-core/dev/vs',
'npm/min': 'node_modules/monaco-editor-core/min/vs',
built: '/vscode/out-monaco-editor-core/min/vs',
releaseDev: 'release/dev/vs',
releaseMin: 'release/min/vs'
}
},
PLUGINS: [
{
name: 'monaco-typescript',
contrib: 'vs/language/typescript/monaco.contribution',
modulePrefix: 'vs/language/typescript',
rootPath: './monaco-typescript',
paths: {
// use ./ to indicate it is relative to the `rootPath`
dev: './release/dev',
min: './release/min',
esm: './release/esm'
}
},
{
name: 'monaco-css',
contrib: 'vs/language/css/monaco.contribution',
modulePrefix: 'vs/language/css',
rootPath: './monaco-css',
paths: {
// use ./ to indicate it is relative to the `rootPath`
dev: './release/dev',
min: './release/min',
esm: './release/esm'
}
},
{
name: 'monaco-json',
contrib: 'vs/language/json/monaco.contribution',
modulePrefix: 'vs/language/json',
rootPath: './monaco-json',
paths: {
// use ./ to indicate it is relative to the `rootPath`
dev: './release/dev',
min: './release/min',
esm: './release/esm'
}
},
{
name: 'monaco-html',
contrib: 'vs/language/html/monaco.contribution',
modulePrefix: 'vs/language/html',
rootPath: './monaco-html',
paths: {
// use ./ to indicate it is relative to the `rootPath`
dev: './release/dev',
min: './release/min',
esm: './release/esm'
}
},
{
name: 'monaco-languages',
contrib: 'vs/basic-languages/monaco.contribution',
modulePrefix: 'vs/basic-languages',
rootPath: './monaco-languages',
paths: {
// use ./ to indicate it is relative to the `rootPath`
dev: './release/dev',
min: './release/min',
esm: './release/esm'
}
}
]
};
if (typeof exports !== 'undefined') {
exports.METADATA = METADATA;
} else {
self.METADATA = METADATA;
}
})();
@@ -0,0 +1,86 @@
const requirejs = require('requirejs');
const path = require('path');
const fs = require('fs');
const terser = require('terser');
const helpers = require('monaco-plugin-helpers');
const REPO_ROOT = path.resolve(__dirname, '..', '..');
const sha1 = helpers.getGitVersion(REPO_ROOT);
const semver = require('../../package.json').version;
const headerVersion = semver + '(' + sha1 + ')';
const BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * monaco-html version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/Microsoft/monaco-html/blob/master/LICENSE.md',
' *-----------------------------------------------------------------------------*/',
''
].join('\n');
bundleOne('monaco.contribution');
bundleOne('htmlMode', ['vs/language/html/monaco.contribution']);
bundleOne('htmlWorker');
function bundleOne(moduleId, exclude) {
requirejs.optimize(
{
baseUrl: 'out/amd/',
name: 'vs/language/html/' + moduleId,
out: 'release/dev/' + moduleId + '.js',
exclude: exclude,
paths: {
'vs/language/html': REPO_ROOT + '/monaco-html/out/amd',
'vs/language/html/fillers/monaco-editor-core':
REPO_ROOT + '/monaco-html/out/amd/fillers/monaco-editor-core-amd'
},
optimize: 'none',
packages: [
{
name: 'vscode-html-languageservice',
location: path.join(REPO_ROOT, 'node_modules/vscode-html-languageservice/lib/umd'),
main: 'htmlLanguageService'
},
{
name: 'vscode-languageserver-types',
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-types/lib/umd'),
main: 'main'
},
{
name: 'vscode-languageserver-textdocument',
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-textdocument/lib/umd'),
main: 'main'
},
{
name: 'vscode-uri',
location: path.join(REPO_ROOT, 'node_modules/vscode-uri/lib/umd'),
main: 'index'
},
{
name: 'vscode-nls',
location: path.join(REPO_ROOT, 'monaco-html/out/amd/fillers'),
main: 'vscode-nls'
}
]
},
async function (buildResponse) {
const devFilePath = path.join(REPO_ROOT, 'monaco-html/release/dev/' + moduleId + '.js');
const minFilePath = path.join(REPO_ROOT, 'monaco-html/release/min/' + moduleId + '.js');
const fileContents = fs.readFileSync(devFilePath).toString();
console.log();
console.log(`Minifying ${devFilePath}...`);
const result = await terser.minify(fileContents, {
output: {
comments: 'some'
}
});
console.log(`Done minifying ${devFilePath}.`);
try {
fs.mkdirSync(path.join(REPO_ROOT, 'monaco-html/release/min'));
} catch (err) {}
fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code);
}
);
}
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"module": "amd",
"moduleResolution": "node",
"outDir": "../out/amd",
"declaration": true,
"target": "es5",
"lib": ["dom", "es5", "es2015.collection", "es2015.promise", "es2015.iterable"]
}
}
@@ -0,0 +1,90 @@
const requirejs = require('requirejs');
const path = require('path');
const fs = require('fs');
const Terser = require('terser');
const helpers = require('monaco-plugin-helpers');
const REPO_ROOT = path.resolve(__dirname, '..', '..');
const sha1 = helpers.getGitVersion(REPO_ROOT);
const semver = require('../../package.json').version;
const headerVersion = semver + '(' + sha1 + ')';
const BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * monaco-json version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/Microsoft/monaco-json/blob/master/LICENSE.md',
' *-----------------------------------------------------------------------------*/',
''
].join('\n');
bundleOne('monaco.contribution');
bundleOne('jsonMode', ['vs/language/json/monaco.contribution']);
bundleOne('jsonWorker');
function bundleOne(moduleId, exclude) {
requirejs.optimize(
{
baseUrl: 'out/amd/',
name: 'vs/language/json/' + moduleId,
out: 'release/dev/' + moduleId + '.js',
exclude: exclude,
paths: {
'vs/language/json': REPO_ROOT + '/monaco-json/out/amd',
'vs/language/json/fillers/monaco-editor-core':
REPO_ROOT + '/monaco-json/out/amd/fillers/monaco-editor-core-amd'
},
optimize: 'none',
packages: [
{
name: 'vscode-json-languageservice',
location: path.join(REPO_ROOT, 'node_modules/vscode-json-languageservice/lib/umd'),
main: 'jsonLanguageService'
},
{
name: 'vscode-languageserver-types',
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-types/lib/umd'),
main: 'main'
},
{
name: 'vscode-languageserver-textdocument',
location: path.join(REPO_ROOT, 'node_modules/vscode-languageserver-textdocument/lib/umd'),
main: 'main'
},
{
name: 'jsonc-parser',
location: path.join(REPO_ROOT, 'node_modules/jsonc-parser/lib/umd'),
main: 'main'
},
{
name: 'vscode-uri',
location: path.join(REPO_ROOT, 'node_modules/vscode-uri/lib/umd'),
main: 'index'
},
{
name: 'vscode-nls',
location: path.join(REPO_ROOT, 'monaco-json/out/amd/fillers'),
main: 'vscode-nls'
}
]
},
async function (buildResponse) {
const devFilePath = path.join(REPO_ROOT, 'monaco-json/release/dev/' + moduleId + '.js');
const minFilePath = path.join(REPO_ROOT, 'monaco-json/release/min/' + moduleId + '.js');
const fileContents = fs.readFileSync(devFilePath).toString();
console.log(`Minifying ${devFilePath}...`);
const result = await Terser.minify(fileContents, {
output: {
comments: 'some'
}
});
console.log(`Done minifying ${devFilePath}.`);
try {
fs.mkdirSync(path.join(REPO_ROOT, 'monaco-json/release/min'));
} catch (err) {}
fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code);
}
);
}
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"module": "amd",
"moduleResolution": "node",
"outDir": "../out/amd",
"declaration": true,
"target": "es5",
"lib": ["dom", "es5", "es2015.collection", "es2015.promise", "es2015.iterable"]
}
}
@@ -0,0 +1,72 @@
const requirejs = require('requirejs');
const path = require('path');
const fs = require('fs');
const terser = require('terser');
const glob = require('glob');
const helpers = require('monaco-plugin-helpers');
const REPO_ROOT = path.resolve(__dirname, '..', '..');
const sha1 = helpers.getGitVersion(REPO_ROOT);
const semver = require('../../package.json').version;
const headerVersion = semver + '(' + sha1 + ')';
const BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * monaco-languages version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md',
' *-----------------------------------------------------------------------------*/',
''
].join('\n');
bundleOne('monaco.contribution');
glob('out/amd/*/*.contribution.js', { cwd: path.dirname(__dirname) }, function (err, files) {
if (err) {
console.log(err);
return;
}
files.forEach(function (file) {
file = file.replace(/\.contribution\.js$/, '');
file = file.replace(/out[/\\]amd[/\\]/, '');
bundleOne(file, ['vs/basic-languages/monaco.contribution']);
});
});
function bundleOne(moduleId, exclude) {
requirejs.optimize(
{
baseUrl: 'out/amd/',
name: 'vs/basic-languages/' + moduleId,
out: 'release/dev/' + moduleId + '.js',
exclude: exclude,
paths: {
'vs/basic-languages': REPO_ROOT + '/monaco-languages/out/amd',
'vs/basic-languages/fillers/monaco-editor-core':
REPO_ROOT + '/monaco-languages/out/amd/fillers/monaco-editor-core-amd'
},
optimize: 'none'
},
async function (buildResponse) {
const devFilePath = path.join(REPO_ROOT, 'monaco-languages/release/dev/' + moduleId + '.js');
const minFilePath = path.join(REPO_ROOT, 'monaco-languages/release/min/' + moduleId + '.js');
const fileContents = fs.readFileSync(devFilePath).toString();
console.log();
console.log(`Minifying ${devFilePath}...`);
const result = await terser.minify(fileContents, {
output: {
comments: 'some'
}
});
console.log(`Done minifying ${devFilePath}.`);
try {
fs.mkdirSync(path.join(REPO_ROOT, 'monaco-languages/release/min'));
} catch (err) {}
try {
fs.mkdirSync(path.dirname(minFilePath));
} catch (err) {}
fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code);
}
);
}
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"module": "amd",
"moduleResolution": "node",
"outDir": "../out/amd",
"target": "es5",
"strict": true,
"lib": ["dom", "es5", "es2015.collection", "es2015.iterable", "es2015.promise"]
}
}
@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const requirejs = require('requirejs');
const path = require('path');
const fs = require('fs');
const terser = require('terser');
const helpers = require('monaco-plugin-helpers');
const REPO_ROOT = path.resolve(__dirname, '..', '..');
const sha1 = helpers.getGitVersion(REPO_ROOT);
const semver = require('../../package.json').version;
const headerVersion = semver + '(' + sha1 + ')';
const BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * monaco-typescript version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/Microsoft/monaco-typescript/blob/master/LICENSE.md',
' *-----------------------------------------------------------------------------*/',
''
].join('\n');
bundleOne('monaco.contribution');
bundleOne('tsMode', ['vs/language/typescript/monaco.contribution']);
bundleOne('tsWorker');
function bundleOne(moduleId, exclude) {
requirejs.optimize(
{
baseUrl: 'out/amd/',
name: 'vs/language/typescript/' + moduleId,
out: 'release/dev/' + moduleId + '.js',
exclude: exclude,
paths: {
'vs/language/typescript': REPO_ROOT + '/monaco-typescript/out/amd',
'vs/language/typescript/fillers/monaco-editor-core':
REPO_ROOT + '/monaco-typescript/out/amd/fillers/monaco-editor-core-amd'
},
optimize: 'none'
},
async function (buildResponse) {
const devFilePath = path.join(REPO_ROOT, 'monaco-typescript/release/dev/' + moduleId + '.js');
const minFilePath = path.join(REPO_ROOT, 'monaco-typescript/release/min/' + moduleId + '.js');
const fileContents = fs.readFileSync(devFilePath).toString();
console.log();
console.log(`Minifying ${devFilePath}...`);
const result = await terser.minify(fileContents, {
output: {
comments: 'some'
}
});
console.log(`Done minifying ${devFilePath}.`);
try {
fs.mkdirSync(path.join(REPO_ROOT, 'monaco-typescript/release/min'));
} catch (err) {}
fs.writeFileSync(minFilePath, BUNDLED_FILE_HEADER + result.code);
}
);
}
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "amd",
"moduleResolution": "node",
"outDir": "../out/amd",
"declaration": true,
"target": "es5",
"lib": ["dom", "es5", "es2015.collection", "es2015.iterable", "es2015.promise"],
"strict": true
}
}
@@ -0,0 +1,49 @@
# SITE-07 Monaco 注册贡献模块与完整入口装配
修改前 HEAD:0cfdf4a7e3292d6f2006c9d4e734b90afc771755。
## 实现与兼容边界
补齐此前 core 压缩核验留下的贡献模块装配缺口。六个 npm 归档及固定 Monaco
Git 归档提供构建工具、核心产物和源码;102 个 Git 成员逐项验证 blob/SHA-256,
其中 88 个 TS 模块分为 TypeScript 3、CSS 2、JSON 2、HTML 2、基础语言 79。
冻结原 metadata、gulp 装配配方、五组配置/打包配方和许可;原 lock 复用既有副本。
新增 `yarn verify:monaco-contributions [--fetch]`。使用 TypeScript 4.4.4 原
ES5/AMD/strict 配置发射源码,RequireJS 2.3.6 负责命名、排序及顶层 strict
处理,Terser 5.9.0 负责分组压缩。原锁中 Terser 的 source-map 是嵌套 0.7.3,
不是顶层 0.6.1;验证器按真实依赖位置核对。五个独立 filler 使用 program 发射,
避免 isolated transpilation 自动给已有 define 调用再包一层 AMD。
遵循原发布步骤:压缩后给 filler 加核心 API 依赖,核心入口改名,按固定顺序
追加五组模块和返回核心 API 的别名,然后恢复原 source-map 位置与换行。完整
开发版 7,070,291 字节和压缩版 2,792,306 字节均精确复现;压缩版同时匹配
站点现有文件。不手改运行资产,不改公开 API、worker/脚本 URL、锁文件或版本。
`contributions.ts` 用 AST 静态读取 metadata,检查重复/动态值、顺序和模块归属。
core 与贡献 bundle 不能套用同一 AMD 清单规则:core 含嵌套匿名 UMD,入口经
字符串表索引 719 注册;验证器确认唯一入口及真实 define 调用,保留完整字节
比较。每次构建使用新的忽略目录,缺少输入不能被旧缓存产物掩盖。
## 验证与问题处理
23/23 单元、严格 docs-tools 类型和 lint 通过。新增负例覆盖缺少/重排分组、
动态/重复 metadata、错误归属、缺失/重复/已注入依赖、伪入口字符串和错误表
索引。原脚本探针发现 strict 转换、filler 双层包装及 core 表索引差异,均修正
为上游实际流程,未把检查器问题记为运行时缺陷或通过忽略字节差异绕过。
三引擎浏览器回归 6/6:每个引擎运行全部 2,511 上游和 12 补充分词用例,
验证 76 个语言 URL,并执行真实 CSS/JSON/TS 诊断更新、HTML/JSON 格式化和
模型清理。联网和离线的完整源码/装配复验、执行环境、内容指纹与浏览器报告
见[验证记录](../baselines/monaco-contributions-validation.json)。
## 后续与回退
维护入口:[Monaco README](../../scripts/site-vendor/monaco/README.md)。这证明
注册模块源码发射和最终装配等价,不证明完整原始 VS Code TS 工程编译或上游
语义类型检查。CSS、其余内嵌来源及 SITE-07 其他交付边界继续开放;本批没有
改变站点 notice 内容,不重复声称设备、SDK 或远端 CI 验收。
SITE-07 doing / VENDOR-06 open,仍为 199/265;Thumbnail 默认策略待答。
可单独回退新增重现脚本、基线、单元用例与维护记录;独立本地提交,无推送、
部署或发布。
+1 -1
View File
@@ -653,7 +653,7 @@
- SITE-AI-DOCS-01: [记录](changes/2026-09-14-SITE-AI-DOCS-01-documentation-pipeline.md) [记录](baselines/documentation-pipeline-validation.json)
- SITE-BUILD-01: [记录](changes/2026-09-14-SITE-BUILD-01-staged-builds.md) [记录](baselines/site-build-validation.json)
- SITE-03: [记录](changes/2026-09-14-SITE-03-desktop-editor.md) [记录](baselines/site-editor-validation.json)
- SITE-07: [记录](site-inventory.md) [记录](baselines/site-provenance.json) [记录](changes/2026-09-14-SITE-07-vendor-notices.md) [记录](baselines/site-notices-checkpoint.json) [记录](baselines/site-codicons-provenance.json) [记录](baselines/site-codicons-validation.json) [记录](changes/2026-09-15-SITE-07-codicons.md) [记录](baselines/vconsole-notices-provenance.json) [记录](baselines/vconsole-notices-validation.json) [记录](changes/2026-09-15-SITE-07-vconsole-notices.md) [记录](console-modernization.md) [记录](baselines/site-console-inventory.json) [记录](baselines/site-console-validation.json) [记录](changes/2026-09-15-SITE-07-console-baseline.md) [记录](baselines/console-feed-provenance.json) [记录](changes/2026-09-15-SITE-07-console-feed-source.md) [记录](baselines/console-commonjs-provenance.json) [记录](changes/2026-09-15-SITE-07-console-commonjs.md) [记录](baselines/console-esm-provenance.json) [记录](changes/2026-09-15-SITE-07-console-esm.md) [记录](baselines/console-embedded-notices.json) [记录](baselines/console-notices-validation.json) [记录](changes/2026-09-15-SITE-07-console-notices.md) [记录](baselines/console-embedded-sources.json) [记录](baselines/console-embedded-validation.json) [记录](changes/2026-09-15-SITE-07-console-embedded-sources.md) [记录](baselines/console-derived-attribution.json) [记录](baselines/console-derived-validation.json) [记录](changes/2026-09-15-SITE-07-console-derived-attribution.md) [记录](baselines/console-stackoverflow-provenance.json) [记录](baselines/console-stackoverflow-validation.json) [记录](changes/2026-09-15-SITE-07-console-stackoverflow.md) [记录](baselines/console-shallowequal-validation.json) [记录](changes/2026-09-15-SITE-07-console-shallowequal.md) [记录](console-notice-review.md) [记录](baselines/console-notice-review-validation.json) [记录](changes/2026-09-15-SITE-07-console-notice-review.md) [记录](baselines/monaco-typescript-provenance.json) [记录](baselines/monaco-typescript-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-typescript.md) [记录](baselines/monaco-languages-provenance.json) [记录](baselines/monaco-languages-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-languages.md) [记录](baselines/monaco-language-notices.json) [记录](baselines/monaco-language-notices-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-language-notices.md) [记录](baselines/monaco-modes-provenance.json) [记录](baselines/monaco-modes-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-modes.md) [记录](baselines/monaco-basic-provenance.json) [记录](baselines/monaco-basic-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-basic.md) [记录](baselines/monaco-core-origins-provenance.json) [记录](baselines/monaco-core-origins-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-core-origins.md) [记录](baselines/monaco-core-build-provenance.json) [记录](baselines/monaco-core-build-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-core-build.md)
- SITE-07: [记录](site-inventory.md) [记录](baselines/site-provenance.json) [记录](changes/2026-09-14-SITE-07-vendor-notices.md) [记录](baselines/site-notices-checkpoint.json) [记录](baselines/site-codicons-provenance.json) [记录](baselines/site-codicons-validation.json) [记录](changes/2026-09-15-SITE-07-codicons.md) [记录](baselines/vconsole-notices-provenance.json) [记录](baselines/vconsole-notices-validation.json) [记录](changes/2026-09-15-SITE-07-vconsole-notices.md) [记录](console-modernization.md) [记录](baselines/site-console-inventory.json) [记录](baselines/site-console-validation.json) [记录](changes/2026-09-15-SITE-07-console-baseline.md) [记录](baselines/console-feed-provenance.json) [记录](changes/2026-09-15-SITE-07-console-feed-source.md) [记录](baselines/console-commonjs-provenance.json) [记录](changes/2026-09-15-SITE-07-console-commonjs.md) [记录](baselines/console-esm-provenance.json) [记录](changes/2026-09-15-SITE-07-console-esm.md) [记录](baselines/console-embedded-notices.json) [记录](baselines/console-notices-validation.json) [记录](changes/2026-09-15-SITE-07-console-notices.md) [记录](baselines/console-embedded-sources.json) [记录](baselines/console-embedded-validation.json) [记录](changes/2026-09-15-SITE-07-console-embedded-sources.md) [记录](baselines/console-derived-attribution.json) [记录](baselines/console-derived-validation.json) [记录](changes/2026-09-15-SITE-07-console-derived-attribution.md) [记录](baselines/console-stackoverflow-provenance.json) [记录](baselines/console-stackoverflow-validation.json) [记录](changes/2026-09-15-SITE-07-console-stackoverflow.md) [记录](baselines/console-shallowequal-validation.json) [记录](changes/2026-09-15-SITE-07-console-shallowequal.md) [记录](console-notice-review.md) [记录](baselines/console-notice-review-validation.json) [记录](changes/2026-09-15-SITE-07-console-notice-review.md) [记录](baselines/monaco-typescript-provenance.json) [记录](baselines/monaco-typescript-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-typescript.md) [记录](baselines/monaco-languages-provenance.json) [记录](baselines/monaco-languages-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-languages.md) [记录](baselines/monaco-language-notices.json) [记录](baselines/monaco-language-notices-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-language-notices.md) [记录](baselines/monaco-modes-provenance.json) [记录](baselines/monaco-modes-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-modes.md) [记录](baselines/monaco-basic-provenance.json) [记录](baselines/monaco-basic-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-basic.md) [记录](baselines/monaco-core-origins-provenance.json) [记录](baselines/monaco-core-origins-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-core-origins.md) [记录](baselines/monaco-core-build-provenance.json) [记录](baselines/monaco-core-build-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-core-build.md) [记录](baselines/monaco-contributions-provenance.json) [记录](baselines/monaco-contributions-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-contributions.md)
- EX-01: [记录](changes/2026-09-14-EX-01-react-consumer.md) [记录](baselines/react-consumer-validation.json) [记录](scripts/react-consumer.mjs)
- EX-02: [记录](changes/2026-09-14-EX-02-vue-consumer.md) [记录](baselines/vue-consumer-validation.json) [记录](scripts/vue-consumer.mjs)
- MOD-01: [记录](changes/2026-09-15-MOD-01-bun-evaluation.md) [记录](baselines/bun-install-validation.json) [记录](bun-evaluation.md)
+10
View File
@@ -1,5 +1,15 @@
# 进度与证据
## SITE-07 Monaco 完整贡献模块装配
六个 npm 归档和 102 个 Git 成员支撑 88 个 TS 注册模块,原 RequireJS/Terser
流程及发布配方精确复现完整 dev/min editor.main,站点运行字节不变。23/23
单元、严格类型/lint、联网/离线复验和三引擎语言模式/分词回归 6/6 通过。
见[记录](changes/2026-09-15-SITE-07-monaco-contributions.md)。SITE-07/VENDOR-06 仍开放,199/265;
后续 CSS 与其余内嵌来源,完整原始 core TS 编译未被本批替代。Thumbnail 默认
兼容策略仍待答。
## SITE-07 Monaco 核心压缩与编辑操作
固定 esbuild 0.12.6 精确复现 13 个 core JS 产物,loader 源码/版本头及 css/nls
+5 -2
View File
@@ -652,11 +652,14 @@
"refactor/changes/2026-09-15-SITE-07-monaco-core-origins.md",
"refactor/baselines/monaco-core-build-provenance.json",
"refactor/baselines/monaco-core-build-validation.json",
"refactor/changes/2026-09-15-SITE-07-monaco-core-build.md"
"refactor/changes/2026-09-15-SITE-07-monaco-core-build.md",
"refactor/baselines/monaco-contributions-provenance.json",
"refactor/baselines/monaco-contributions-validation.json",
"refactor/changes/2026-09-15-SITE-07-monaco-contributions.md"
],
"compatibleResolution": "Keep separate from owned TS migration. Verify source/version/diff and license notices before replacement; preserve API/CSS/worker URLs and run owning package tests.",
"closureCriteria": "固定上游版本/内容差异、完整组件许可与分发 notices,兼容测试通过;仅当前上游许可证名称不足以关闭。",
"workspaceState": "Monaco 0.30.1 remains byte-pinned (the known CSS newline-only difference is preserved). Codicons 0.0.26 and the actual TypeScript 4.4.4 worker have original notice supplements. CSS/HTML/JSON workers, four mode bundles and all 76 basic-language bundles reproduce exactly; 2511 upstream plus 12 supplemental tokenizer cases pass in three engines. DOMPurify 2.3.1 and marked 3.0.2 now have exact npm-to-Git/source-map/development adaptations; the missing DOMPurify terms and original/legacy marked licenses are delivered. All 85 site notices and attribution links pass three-engine HTTP/mobile playback checks; Markdown formatting/filtering/hook cleanup also passes. Latest unit 18/18, strict type/lint and four-archive/seven-Git offline/network checks pass. The complete core compiler pipeline, other core/loader/localization and further embedded origins remain open; this is not an exhaustive sanitizer audit or release approval. All 13 core JavaScript minified outputs now reproduce with locked esbuild 0.12.6 on Windows x64; loader source/header and css/nls sources match fixed Git. Two maps inventory 584 prepared-source entries with 549 different names. Ten-locale editing/find checks pass 30/30 and core diff-worker checks pass 3/3; unit 20/20, type/lint and offline/network checks pass. Complete original TS compilation, appended contributions, CSS and remaining embedded origins are not claimed complete."
"workspaceState": "Monaco 0.30.1 remains byte-pinned, including its known CSS newline-only difference. Codicons 0.0.26, TypeScript 4.4.4 and language-service licenses are delivered. Three service workers, four modes and 76 basic-language bundles reproduce exactly; all 13 core JS minified outputs reproduce with locked esbuild 0.12.6 on Windows x64. Loader/header and css/nls source origins match fixed Git; two maps inventory 584 prepared-source entries and 549 different names. DOMPurify 2.3.1 and marked 3.0.2 have exact source adaptations and complete notice supplements; 85 site notices were verified by HTTP/mobile browser checks. Latest contribution check verifies six archives and 102 Git members, emits 88 TS registration modules and uses RequireJS 2.3.6/Terser 5.9.0 to reproduce the entire dev/min editor assembly, including core string-table entry, dependency injection, order, alias and map trailer. Latest units 23/23, strict type/lint and offline/network source checks pass; modes/tokenizer regressions pass 6/6 across three engines with 2523 token cases per engine. Earlier ten-locale editing/find 30/30 and diff-worker 3/3 evidence remains recorded separately. Original core TS compilation, CSS reproduction and remaining embedded-origin review stay open; this is not release approval or an exhaustive sanitizer audit."
},
{
"id": "VENDOR-07",
+4 -1
View File
@@ -4534,7 +4534,10 @@
"changes/2026-09-15-SITE-07-monaco-core-origins.md",
"baselines/monaco-core-build-provenance.json",
"baselines/monaco-core-build-validation.json",
"changes/2026-09-15-SITE-07-monaco-core-build.md"
"changes/2026-09-15-SITE-07-monaco-core-build.md",
"baselines/monaco-contributions-provenance.json",
"baselines/monaco-contributions-validation.json",
"changes/2026-09-15-SITE-07-monaco-contributions.md"
]
},
{
+41 -1
View File
@@ -236,4 +236,44 @@ Remaining origin review can start from the map inventory: `dom.ts` references Wi
marked names a Stack Overflow snippet; `path.ts` identifies Node 14.16.0 while the
older supplied notice links a different commit. Reference comments alone do not
establish copied expression or a missing license; inspect actual boundaries and
original terms before deciding. Core registration assembly and CSS remain separate.
original terms before deciding. Registration assembly is covered below; CSS remains
separate.
## Contribution registration assembly
`reproduce-contributions.ts` closes the appended-registration gap left by the
core minifier check. Six fixed npm archives and the fixed Monaco Git archive supply
TypeScript 4.4.4, RequireJS 2.3.6, Terser 5.9.0 and its nested source-map 0.7.3.
The top-level lock also contains source-map 0.6.1; it is not Terser's dependency.
Nothing is installed in the workspace and no historical install script runs.
The record covers 102 Git members, including 88 TypeScript module inputs and the
original metadata, release recipe, five bundler recipes/configurations and license.
Modules are emitted with the original ES5/AMD/strict settings. Standalone fillers
use program emission: isolated transpilation would incorrectly wrap their existing
AMD calls in another module. RequireJS itself names and orders definitions and
removes standalone strict directives. Each run uses a fresh ignored directory so
missing modules cannot resolve from stale outputs. This proves output equivalence,
not a full upstream semantic TypeScript check.
`contributions.ts` parses the fixed metadata statically and verifies the five-group
order: TypeScript (3 modules), CSS (2), JSON (2), HTML (2), basic languages (79).
After Terser minification and version headers, the release recipe injects the core
API dependency into each filler, renames the original editor entry, appends groups
and an API-returning alias, and preserves the exact source-map trailer. Both full
development and minified editor files must match their npm archive bytes; the
minified file must also match the unchanged site asset. Core's nested anonymous UMD
definitions are allowed; the unique renamed entry must be used by an AMD call,
including the archived string-table lookup (index 719 in both core builds).
```sh
yarn verify:monaco-contributions --fetch
yarn verify:monaco-contributions
yarn test:browser test/browser/editor-modes.spec.js test/browser/editor-basic-languages.spec.js --workers=1
```
Unit counterexamples cover missing/reordered groups, computed/duplicate metadata,
foreign modules, missing/repeated/already-injected dependencies and false entry
strings. Browser regressions exercise real diagnostics/formatting and all pinned
tokenizer cases. This does not close original core TS compilation, CSS reproduction,
remaining embedded-origin review, or the broader SITE-07 release requirements.
@@ -0,0 +1,98 @@
import assert from 'node:assert/strict'
import ts from 'typescript'
import { moduleIds } from './languages.ts'
export interface Contribution { name: string, contrib: string, modulePrefix: string }
// Read the fixed metadata without running its browser/CommonJS wrapper.
export function readContributionMetadata(source: string): Contribution[] {
const ast = ts.createSourceFile('metadata.js', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
const declarations: ts.VariableDeclaration[] = []
const visit = (node: ts.Node) => {
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === 'METADATA')
declarations.push(node)
ts.forEachChild(node, visit)
}
visit(ast)
assert.equal(declarations.length, 1, 'Expected one METADATA declaration')
function property(object: ts.Node | undefined, key: string): ts.Expression {
assert(object && ts.isObjectLiteralExpression(object), 'Expected metadata object')
const values = object.properties.filter(item => ts.isPropertyAssignment(item) && (ts.isIdentifier(item.name) || ts.isStringLiteral(item.name)) && item.name.text === key)
assert.equal(values.length, 1, `Expected one metadata property: ${key}`)
return (values[0] as ts.PropertyAssignment).initializer
}
function literal(object: ts.Node, key: string): string {
const value = property(object, key)
assert(ts.isStringLiteral(value), 'Expected literal metadata value')
return value.text
}
const plugins = property(declarations[0]!.initializer, 'PLUGINS')
assert(ts.isArrayLiteralExpression(plugins), 'Expected plugin array')
const result = plugins.elements.map((plugin) => {
const name = literal(plugin, 'name')
const contrib = literal(plugin, 'contrib')
const modulePrefix = literal(plugin, 'modulePrefix')
assert.equal(literal(plugin, 'rootPath'), `./${name}`)
assert.equal(contrib, `${modulePrefix}/monaco.contribution`)
for (const type of ['dev', 'min'])
assert.equal(literal(property(plugin, 'paths'), type), `./release/${type}`)
return { name, contrib, modulePrefix }
})
assert.deepEqual(result.map(item => item.name), ['monaco-typescript', 'monaco-css', 'monaco-json', 'monaco-html', 'monaco-languages'], 'Contribution order changed')
assert.deepEqual(result.map(item => item.modulePrefix), ['vs/language/typescript', 'vs/language/css', 'vs/language/json', 'vs/language/html', 'vs/basic-languages'], 'Contribution prefix changed')
return result
}
// The upstream release injects this dependency AFTER per-plugin minification.
export function injectCoreDependency(source: string, prefix: string): string {
const pattern = /define\((['"][a-z/\-]+\/fillers\/monaco-editor-core['"]),\[\],/g
const matches = [...source.matchAll(pattern)]
assert.equal(matches.length, 1, 'Expected one empty core filler dependency')
assert.equal(matches[0]![1]!.slice(1, -1), `${prefix}/fillers/monaco-editor-core`, 'Wrong core filler')
return source.replace(pattern, 'define($1,[\'vs/editor/editor.api\'],')
}
export function assembleContributions(core: string, groups: (Contribution & { source: string })[]): string {
assert.deepEqual(groups.map(item => item.modulePrefix), ['vs/language/typescript', 'vs/language/css', 'vs/language/json', 'vs/language/html', 'vs/basic-languages'], 'Contribution order changed')
const entry = '"vs/editor/editor.main"'
assert.equal(core.split(entry).length, 2, 'Expected unique quoted core entry')
// Core contains nested UMD calls and addresses AMD names through a string table.
// Verify the entry's actual define() use, not merely a matching literal string.
let entries = 0
const tables = new Map<string, number>()
const ast = ts.createSourceFile('core.js', core, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
const findTables = (node: ts.Node) => {
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isArrayLiteralExpression(node.initializer)) {
const index = node.initializer.elements.findIndex(item => ts.isStringLiteral(item) && item.text === 'vs/editor/editor.main')
if (index >= 0) {
assert(node.initializer.elements.every(item => ts.isStringLiteral(item)), 'Expected literal core module table')
tables.set(node.name.text, index)
}
}
ts.forEachChild(node, findTables)
}
findTables(ast)
const visit = (node: ts.Node) => {
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'define') {
const name = node.arguments[0]
if (name && ts.isStringLiteral(name) && name.text === 'vs/editor/editor.main')
entries++
if (name && ts.isElementAccessExpression(name) && ts.isIdentifier(name.expression) && ts.isNumericLiteral(name.argumentExpression)
&& tables.get(name.expression.text) === Number(name.argumentExpression.text)) { entries++ }
}
ts.forEachChild(node, visit)
}
visit(ast)
assert.equal(entries, 1, 'Missing core AMD entry')
core = core.replace(entry, '"vs/editor/edcore.main"')
const extra = groups.map((group) => {
assert.equal(group.contrib, `${group.modulePrefix}/monaco.contribution`, 'Wrong contribution entry')
const ids = moduleIds(group.source)
assert(ids.includes(group.contrib) && ids.every(id => id.startsWith(`${group.modulePrefix}/`)), 'Wrong contribution ownership')
return injectCoreDependency(group.source, group.modulePrefix)
})
extra.push(`define("vs/editor/editor.main", ["vs/editor/edcore.main","${groups.map(group => group.contrib).join('","')}"], function(api) { return api; });`)
const map = core.lastIndexOf('//# sourceMappingURL=')
const offset = map === -1 ? core.length : map
return `${core.slice(0, offset)}\n${extra.join('\n')}\n${core.slice(offset)}`
}
@@ -0,0 +1,134 @@
import type { Archive, Member } from './archives.ts'
import type { Contribution } from './contributions.ts'
import assert from 'node:assert/strict'
import { Buffer } from 'node:buffer'
import { execFileSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import fs from 'node:fs'
import { createRequire } from 'node:module'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import { ArchiveCache, hash } from './archives.ts'
import { emitAmd } from './compiler.ts'
import { assembleContributions, readContributionMetadata } from './contributions.ts'
import { moduleIds } from './languages.ts'
interface Provenance {
commit: string
archives: Archive[]
compilerMembers: Member[]
sourceArchive: { url: string, file: string, bytes: number, sha256: string }
members: { path: string, member: string, sha256: string, gitBlobSha: string, frozen?: string }[]
modules: { id: string, path: string }[]
groups: Contribution[]
files: { type: 'dev' | 'min', core: Member, editor: Member }[]
target: { path: string, sha256: string }
}
const root = fileURLToPath(new URL('../../../', import.meta.url))
assert(process.argv.slice(2).every(arg => arg === '--fetch'), 'Use reproduce-contributions.ts [--fetch]')
assert.equal(process.version, `v${fs.readFileSync(path.join(root, '.node-version'), 'utf8').trim()}`, 'Use canonical Node')
const record: Provenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/monaco-contributions-provenance.json'), 'utf8'))
const cache = new ArchiveCache(root, path.join(root, 'refactor/.cache/monaco-review'))
const online = process.argv.includes('--fetch')
await cache.verify(record.archives, [], online)
assert.equal(record.sourceArchive.file, 'monaco-editor-source.tgz')
assert.equal(record.sourceArchive.url, `https://codeload.github.com/microsoft/monaco-editor/tar.gz/${record.commit}`)
const archiveFile = path.join(cache.directory, record.sourceArchive.file)
let archive: Buffer
if (online) {
const response = await fetch(record.sourceArchive.url, { signal: AbortSignal.timeout(60000) })
assert(response.ok, `Repository archive HTTP ${response.status}`)
archive = Buffer.from(await response.arrayBuffer())
}
else {
archive = fs.readFileSync(archiveFile)
}
assert.equal(archive.length, record.sourceArchive.bytes)
assert.equal(hash(archive), record.sourceArchive.sha256, 'Repository archive changed')
if (online)
fs.writeFileSync(archiveFile, archive)
const sources = new Map<string, string>()
for (const member of record.members) {
assert.equal(member.member, `monaco-editor-${record.commit}/${member.path}`)
assert(!member.path.split('/').includes('..'), 'Invalid repository member')
const bytes = execFileSync('tar', ['-xOzf', archiveFile, member.member], { maxBuffer: 20 * 1024 * 1024 })
assert.equal(hash(bytes), member.sha256, `Source changed: ${member.path}`)
assert.equal(createHash('sha1').update(`blob ${bytes.length}\0`).update(bytes).digest('hex'), member.gitBlobSha, 'Git blob changed')
if (member.frozen)
assert.deepEqual(fs.readFileSync(path.join(root, member.frozen)), bytes, 'Frozen recipe changed')
assert(!sources.has(member.path), 'Duplicate source')
sources.set(member.path, bytes.toString('utf8'))
}
function read(file: string): string {
const source = sources.get(file)
assert(source !== undefined, `Unverified source: ${file}`)
return source
}
const groups = readContributionMetadata(read('monaco-editor/metadata.js'))
assert.deepEqual(groups, record.groups)
const lock = JSON.parse(read('package-lock.json')).dependencies
for (const name of ['typescript', 'terser', 'source-map', 'requirejs']) {
const item = record.archives.find(item => item.name === name)
const dependency = name === 'source-map' ? lock.terser.dependencies[name] : lock[name]
assert(item && item.version === dependency.version && item.integrity === dependency.integrity, `Wrong historical tool: ${name}`)
cache.extractCompiler(item)
}
for (const member of record.compilerMembers)
cache.member(member)
const require = createRequire(path.join(cache.directory, 'compiler/entry.cjs'))
const ts = require('typescript') as typeof import('typescript')
const terser = require('terser') as { minify: (source: string, options: { output: { comments: string } }) => Promise<{ code: string }> }
assert.equal(ts.version, '4.4.4')
assert.equal(require('terser/package.json').version, '5.9.0')
assert.equal(require('requirejs/package.json').version, '2.3.6')
assert.equal(record.modules.length, 88)
assert.equal(new Set(record.modules.map(module => module.id)).size, 88)
// Isolate output from earlier runs, so a missing source cannot resolve from cache.
const work = fs.mkdtempSync(path.join(cache.directory, 'contributions-'))
const built: (Contribution & { dev: string, min: string, modules: number })[] = []
for (const group of groups) {
const config = JSON.parse(read(`${group.name}/src/tsconfig.json`)).compilerOptions
assert.equal(config.module, 'amd')
assert.equal(config.target, 'es5')
const modules = record.modules.filter(module => module.id.startsWith(`${group.modulePrefix}/`))
for (const module of modules) {
const relative = module.id.slice(group.modulePrefix.length + 1)
assert(/^[\w./-]+$/.test(relative) && !relative.split('/').includes('..'), 'Invalid AMD input')
assert.equal(module.path, `${group.name}/src/${relative.replace(/fillers\/monaco-editor-core$/, 'fillers/monaco-editor-core-amd')}.ts`)
const source = read(module.path)
// transpileModule wraps non-module scripts in AMD. Compile the five standalone
// fillers as programs to preserve their single define([], ...) registration.
const emitted = relative === 'fillers/monaco-editor-core'
? emitAmd(ts, source, config.lib, config.strict)
: ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES5, module: ts.ModuleKind.AMD, newLine: ts.NewLineKind.LineFeed, strict: config.strict } }).outputText
const file = path.join(work, `${module.id}.js`)
fs.mkdirSync(path.dirname(file), { recursive: true })
fs.writeFileSync(file, emitted)
}
const output = path.join(work, `${group.name}.js`)
execFileSync(process.execPath, [require.resolve('requirejs/bin/r.js'), '-o', `baseUrl=${work}`, `name=${group.contrib}`, `out=${output}`, 'optimize=none'], { maxBuffer: 10 * 1024 * 1024 })
const dev = fs.readFileSync(output, 'utf8')
assert.deepEqual(moduleIds(dev), modules.map(module => module.id), 'Contribution source coverage/order changed')
const header = `/*!-----------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * ${group.name} version: 0.30.1(${record.commit})\n * Released under the MIT license\n * https://github.com/Microsoft/${group.name}/blob/master/LICENSE.md\n *-----------------------------------------------------------------------------*/\n`
const min = header + (await terser.minify(dev, { output: { comments: 'some' } })).code
built.push({ ...group, dev, min, modules: modules.length })
}
assert.equal(built.reduce((total, group) => total + group.modules, 0), record.modules.length)
assert.deepEqual(record.files.map(file => file.type), ['dev', 'min'])
const outputs = record.files.map((file) => {
const assembled = assembleContributions(cache.member(file.core).toString('utf8'), built.map(group => ({ ...group, source: group[file.type] })))
const expected = cache.member(file.editor)
assert(Buffer.from(assembled).equals(expected), `Complete ${file.type} editor assembly differs`)
if (file.type === 'min') {
const target = fs.readFileSync(path.join(root, record.target.path))
assert.equal(hash(target), record.target.sha256, 'Shipped editor changed')
assert.deepEqual(target, expected, 'Shipped editor differs from archive')
}
return { type: file.type, bytes: expected.length, sha256: hash(expected), exactAssembly: true }
})
console.log(JSON.stringify({ archives: record.archives.length, gitSources: sources.size, groups: built.map(group => ({ name: group.name, modules: group.modules })), modules: record.modules.length, outputs, fullUpstreamTypecheck: false, coreTypeScriptAndCssRebuilt: false }))
+44
View File
@@ -4,11 +4,55 @@ import test from 'node:test'
import ts from 'typescript'
import { extractBasicFixtures } from '../scripts/site-vendor/monaco/basic-fixtures.ts'
import { emitAmd } from '../scripts/site-vendor/monaco/compiler.ts'
import { assembleContributions, injectCoreDependency, readContributionMetadata } from '../scripts/site-vendor/monaco/contributions.ts'
import { inventoryCoreMap, readCoreNls, restoreCoreMapComment } from '../scripts/site-vendor/monaco/core-build.ts'
import { adaptCoreOrigin } from '../scripts/site-vendor/monaco/core-origins.ts'
import { aliasModule, moduleIds, nameModule, verifyWorkerSources } from '../scripts/site-vendor/monaco/languages.ts'
import { browserTypeScript, verifyTypeScriptSource } from '../scripts/site-vendor/monaco/typescript.ts'
const contributionGroups = ['typescript', 'css', 'json', 'html', 'languages'].map((name) => {
const modulePrefix = name === 'languages' ? 'vs/basic-languages' : `vs/language/${name}`
return { name: `monaco-${name}`, modulePrefix, contrib: `${modulePrefix}/monaco.contribution` }
})
test('Contribution metadata preserves upstream ordering and rejects computed or duplicate values', () => {
const plugins = contributionGroups.map(group => ({ ...group, rootPath: `./${group.name}`, paths: { dev: './release/dev', min: './release/min' } }))
const metadata = list => `var METADATA = ${JSON.stringify({ PLUGINS: list })};`
assert.deepEqual(readContributionMetadata(metadata(plugins)), contributionGroups)
assert.throws(() => readContributionMetadata(metadata([...plugins].reverse())), /order changed/)
assert.throws(() => readContributionMetadata(metadata(plugins).replace('"monaco-css"', 'getName()')), /literal metadata/)
assert.throws(() => readContributionMetadata(metadata(plugins).replace('"name":"monaco-css"', '"name":"monaco-css","name":"monaco-css"')), /one metadata property/)
})
test('Contribution assembly retains the entry alias, dependency order and exact map boundary', () => {
const core = 'define("vs/editor/editor.main",[],function(){return {};});\n'
const map = '//# sourceMappingURL=../../../min-maps/vs/editor/editor.main.js.map'
const groups = contributionGroups.map(group => ({ ...group, source: `define('${group.modulePrefix}/fillers/monaco-editor-core',[],function(){return self.monaco;});\ndefine('${group.contrib}',[],function(){});` }))
const output = assembleContributions(core + map, groups)
assert(output.startsWith(core.replace('editor.main', 'edcore.main')))
assert(output.endsWith(`function(api) { return api; });\n${map}`))
assert.deepEqual(moduleIds(output), ['vs/editor/edcore.main', ...groups.flatMap(group => [`${group.modulePrefix}/fillers/monaco-editor-core`, group.contrib]), 'vs/editor/editor.main'])
assert.equal(output.split('[\'vs/editor/editor.api\']').length, 6)
assert.equal(assembleContributions(core, groups), output.slice(0, -map.length))
assert.throws(() => assembleContributions(core + core, groups), /unique quoted core entry/)
assert.throws(() => assembleContributions('const name = "vs/editor/editor.main";', groups), /Missing core AMD entry/)
assert(assembleContributions(`define([],function(){});\n${core}`, groups).startsWith('define([],function(){});'))
const indexed = 'var names = ["other","vs/editor/editor.main"];define(names[1 /* entry */],[],function(){});'
assert(assembleContributions(indexed, groups).startsWith(indexed.replace('editor.main', 'edcore.main')))
assert.throws(() => assembleContributions(indexed.replace('names[1', 'names[0'), groups), /Missing core AMD entry/)
assert.throws(() => assembleContributions(core, groups.slice(1)), /order changed/)
assert.throws(() => assembleContributions(core, groups.map((group, index) => index ? group : { ...group, source: `${group.source}\ndefine('unrelated',[],function(){});` })), /ownership/)
})
test('Core dependency injection rejects omissions, duplicates and already patched filler modules', () => {
const prefix = 'vs/basic-languages'
const source = `define('${prefix}/fillers/monaco-editor-core',[],function(){return self.monaco;});`
assert.throws(() => injectCoreDependency('', prefix), /one empty core filler/)
assert.throws(() => injectCoreDependency(source + source, prefix), /one empty core filler/)
assert.throws(() => injectCoreDependency(source, 'vs/language/css'), /Wrong core filler/)
assert.throws(() => injectCoreDependency(injectCoreDependency(source, prefix), prefix), /one empty core filler/)
})
test('Core NLS extraction accepts historical trailing commas without evaluating expressions', () => {
assert.deepEqual(readCoreNls('define("nls", { "find": ["Find", "查找",], });'), { id: 'nls', messages: { find: ['Find', '查找'] } })
assert.throws(() => readCoreNls('define("nls", { "find": [translate()], });'), /literal translation text/)