build(site): [SITE-07] reproduce Monaco CSS sources and verify styled editor

This commit is contained in:
Harvey Zhao committed 2026-09-15 11:55:22 +08:00
1 parent 23ac6f7128
commit d2b2f76db8
23 files changed
+16271 -5

No files matched your search

+1
View File
@@ -43,3 +43,4 @@ docs/licenses/monaco-editor/core-origins/** -text whitespace=-trailing-space,cr-
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
refactor/baselines/site-vendor/monaco-css-build/** -text whitespace=-trailing-space,cr-at-eol
+2 -1
View File
@@ -171,7 +171,8 @@
"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-contributions": "node scripts/site-vendor/monaco/reproduce-contributions.ts"
"verify:monaco-contributions": "node scripts/site-vendor/monaco/reproduce-contributions.ts",
"verify:monaco-css": "node scripts/site-vendor/monaco/reproduce-css.ts"
},
"browserslist": "last 1 Chrome version",
"devDependencies": {
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,265 @@
{
"schemaVersion": 1,
"task": "SITE-07",
"status": "checkpoint-incomplete",
"recordedAt": "2026-09-15T03:54:24.439Z",
"baseCommit": "23ac6f71284547a5e4dbf6cdbf0966f6cef49c87",
"node": "v24.21.0",
"yarn": "1.22.22",
"fingerprints": {
"refactor/baselines/monaco-css-build-provenance.json": "9842dab17ddc7ffedfedb3dbb3c16e8d2ec9fcade5a5cd42ac2ce124cb2fa3ab",
"scripts/site-vendor/monaco/css.ts": "b287c31c3d52a88e46f8937ee465907d28573615d59e1f731a69fc5439e2bf3e",
"scripts/site-vendor/monaco/reproduce-css.ts": "ce5f077886b90b83d482044e8b5b77d2779bc106d6e4537855b2fd2374f45fd2",
"test/monaco-provenance.test.js": "132c76e2daf2a9369b5331014fa05062cb77bb284b5615d067f03e5c03131657",
"test/browser/editor-styles.spec.js": "13deeb38582a9bfda94ed10d661c8302224f5b77dcbbcdff63aefa6e35404f11",
"docs/assets/js/vs/editor/editor.main.css": "165d5ada907097527448677d0d0f0213578fe5cfaffcb245d7229862e849783a"
},
"reproduction": {
"result": {
"gitSources": 81,
"styles": 68,
"inlinedImages": [
"src/vs/editor/contrib/colorPicker/images/opacity-background.png",
"src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-light.svg",
"src/vs/editor/standalone/browser/iPadShowKeyboard/keyboard-dark.svg"
],
"fonts": [
"vs/base/browser/ui/codicons/codicon/codicon.ttf"
],
"dependencySelectors": 153,
"normalizedSeparators": 70,
"developmentBytes": 114942,
"minifiedBytes": 72052,
"siteBytes": 72057,
"exactDevelopment": true,
"exactMinification": true,
"preservedSiteCrlf": 5,
"compiler": {
"cssnano": "4.1.11",
"postcss": "7.0.35"
},
"originalCoreTypeScriptCompiled": false
},
"offline": {
"exitCode": 0,
"logSha256": "8ac42cf42c91a65214244292da8a65813ce58664ab2a5ee225d5b67eac65c2b3"
},
"online": {
"exitCode": 0,
"logSha256": "ed86a59cef3c1c314ff6f9d82a7c8bf3d4adeb41b6608521d70a92c17e994ce7"
},
"earlierFailure": {
"code": "ENOTFOUND",
"host": "codeload.github.com",
"logSha256": "a8dd9a7d79f947e364226fef4b48444e1900a51cdf3bd2bce89bc2cfcb26ae33",
"resolvedBy": "Retry of the same verified archive request"
}
},
"unit": {
"passed": 25,
"failed": 0,
"logSha256": "1ddccc2a6b48f578f34a75170853c779a76da71b529d3a4e854b9ba33024a3ea"
},
"types": {
"exitCode": 0,
"logSha256": "71642f85fdc68059b2c4f81737d70eb55c3f56f5a7a76523e672764bef7a5034"
},
"lint": {
"exitCode": 0
},
"browser": {
"command": "yarn test:browser test/browser/editor-styles.spec.js --workers=1",
"reportSha256": "473741dd39d4296600b66b3e4fdec65ae1e6c48143362d97c608609a88e1eeba",
"stats": {
"startTime": "2026-09-15T03:50:07.778Z",
"duration": 7879.217,
"expected": 3,
"skipped": 0,
"unexpected": 0,
"flaky": 0
},
"errors": [],
"tests": [
{
"title": "docs Monaco styles retain layout, themes and loaded codicon glyphs",
"project": "chromium",
"status": "passed",
"browser": "153.0.8010.12",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"styles": {
"themes": [
{
"theme": "vs-dark",
"color": "rgb(30, 30, 30)"
},
{
"theme": "vs",
"color": "rgb(255, 255, 254)"
}
],
"font": {
"loaded": [
{
"family": "codicon",
"status": "loaded"
}
],
"family": "codicon",
"content": "\"\""
},
"hostBox": {
"x": 8,
"y": 389,
"width": 640,
"height": 320
},
"findBox": {
"x": 201,
"y": 353.1777648925781,
"width": 419,
"height": 33
},
"assets": [
{
"url": "http://127.0.0.1:8084/assets/js/vs/editor/editor.main.css",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/base/browser/ui/codicons/codicon/codicon.ttf",
"status": 200
}
],
"resized": {
"width": 420,
"height": 240
}
}
},
{
"title": "docs Monaco styles retain layout, themes and loaded codicon glyphs",
"project": "firefox",
"status": "passed",
"browser": "155.0",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"styles": {
"themes": [
{
"theme": "vs-dark",
"color": "rgb(30, 30, 30)"
},
{
"theme": "vs",
"color": "rgb(255, 255, 254)"
}
],
"font": {
"loaded": [
{
"family": "codicon",
"status": "loaded"
}
],
"family": "codicon",
"content": "\"\""
},
"hostBox": {
"x": 8,
"y": 390,
"width": 640,
"height": 320
},
"findBox": {
"x": 201,
"y": 357.7498779296875,
"width": 419,
"height": 33
},
"assets": [
{
"url": "http://127.0.0.1:8084/assets/js/vs/editor/editor.main.css",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/base/browser/ui/codicons/codicon/codicon.ttf",
"status": 200
}
],
"resized": {
"width": 420,
"height": 240
}
}
},
{
"title": "docs Monaco styles retain layout, themes and loaded codicon glyphs",
"project": "webkit",
"status": "passed",
"browser": "26.6",
"platform": "win32",
"errors": [],
"consoleErrors": [],
"failedRequests": [],
"styles": {
"themes": [
{
"theme": "vs-dark",
"color": "rgb(30, 30, 30)"
},
{
"theme": "vs",
"color": "rgb(255, 255, 254)"
}
],
"font": {
"loaded": [
{
"family": "codicon",
"status": "loaded"
}
],
"family": "codicon",
"content": "\"\""
},
"hostBox": {
"x": 8,
"y": 392,
"width": 640,
"height": 320
},
"findBox": {
"x": 201,
"y": 375.0150146484375,
"width": 419,
"height": 33
},
"assets": [
{
"url": "http://127.0.0.1:8084/assets/js/vs/editor/editor.main.css",
"status": 200
},
{
"url": "http://127.0.0.1:8084/assets/js/vs/base/browser/ui/codicons/codicon/codicon.ttf",
"status": 200
}
],
"resized": {
"width": 420,
"height": 240
}
}
}
]
},
"limitations": [
"The original CSS plugin emits 70 CRLF separators. Conversion to the observed LF-only npm development file is explicit; the exact original publication stage responsible remains unidentified.",
"Original core TypeScript compilation and remaining embedded-origin review are not completed by CSS reproduction.",
"Runtime CSS, font files and notice delivery remain unchanged. No physical device, external SDK, remote CI or publication evidence is claimed.",
"Thumbnail default compatibility policy remains pending."
]
}
@@ -0,0 +1,11 @@
* text=auto
LICENSE.txt eol=crlf
ThirdPartyNotices.txt eol=crlf
*.bat eol=crlf
*.cmd eol=crlf
*.ps1 eol=lf
*.sh eol=lf
*.rtf -text
**/*.json linguist-language=jsonc
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015 - 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,481 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const gulp = require('gulp');
const path = require('path');
const util = require('./lib/util');
const task = require('./lib/task');
const common = require('./lib/optimize');
const es = require('event-stream');
const File = require('vinyl');
const i18n = require('./lib/i18n');
const standalone = require('./lib/standalone');
const cp = require('child_process');
const compilation = require('./lib/compilation');
const monacoapi = require('./lib/monaco-api');
const fs = require('fs');
let root = path.dirname(__dirname);
let sha1 = util.getVersion(root);
let semver = require('./monaco/package.json').version;
let headerVersion = semver + '(' + sha1 + ')';
// Build
let editorEntryPoints = [
{
name: 'vs/editor/editor.main',
include: [],
exclude: ['vs/css', 'vs/nls'],
prepend: ['out-editor-build/vs/css.js', 'out-editor-build/vs/nls.js'],
},
{
name: 'vs/base/common/worker/simpleWorker',
include: ['vs/editor/common/services/editorSimpleWorker'],
prepend: ['vs/loader.js'],
append: ['vs/base/worker/workerMain'],
dest: 'vs/base/worker/workerMain.js'
}
];
let editorResources = [
'out-editor-build/vs/base/browser/ui/codicons/**/*.ttf'
];
let BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * Version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/microsoft/vscode/blob/main/LICENSE.txt',
' *-----------------------------------------------------------*/',
''
].join('\n');
const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []);
const extractEditorSrcTask = task.define('extract-editor-src', () => {
const apiusages = monacoapi.execute().usageContent;
const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString();
standalone.extractEditor({
sourcesRoot: path.join(root, 'src'),
entryPoints: [
'vs/editor/editor.main',
'vs/editor/editor.worker',
'vs/base/worker/workerMain',
],
inlineEntryPoints: [
apiusages,
extrausages
],
shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers
importIgnorePattern: /(^vs\/css!)/,
destRoot: path.join(root, 'out-editor-src'),
redirects: []
});
});
const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true));
const optimizeEditorAMDTask = task.define('optimize-editor-amd', common.optimizeTask({
src: 'out-editor-build',
entryPoints: editorEntryPoints,
resources: editorResources,
loaderConfig: {
paths: {
'vs': 'out-editor-build/vs',
'vs/css': 'out-editor-build/vs/css.build',
'vs/nls': 'out-editor-build/vs/nls.build',
'vscode': 'empty:'
}
},
bundleLoader: false,
header: BUNDLED_FILE_HEADER,
bundleInfo: true,
out: 'out-editor',
languages: languages
}));
const minifyEditorAMDTask = task.define('minify-editor-amd', common.minifyTask('out-editor'));
const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => {
standalone.createESMSourcesAndResources2({
srcFolder: './out-editor-src',
outFolder: './out-editor-esm',
outResourcesFolder: './out-monaco-editor-core/esm',
ignores: [
'inlineEntryPoint:0.ts',
'inlineEntryPoint:1.ts',
'vs/loader.js',
'vs/nls.ts',
'vs/nls.build.js',
'vs/nls.d.ts',
'vs/css.js',
'vs/css.build.js',
'vs/css.d.ts',
'vs/base/worker/workerMain.ts',
],
renames: {
'vs/nls.mock.ts': 'vs/nls.ts'
}
});
});
const compileEditorESMTask = task.define('compile-editor-esm', () => {
const KEEP_PREV_ANALYSIS = false;
const FAIL_ON_PURPOSE = false;
console.log(`Launching the TS compiler at ${path.join(__dirname, '../out-editor-esm')}...`);
let result;
if (process.platform === 'win32') {
result = cp.spawnSync(`..\\node_modules\\.bin\\tsc.cmd`, {
cwd: path.join(__dirname, '../out-editor-esm')
});
} else {
result = cp.spawnSync(`node`, [`../node_modules/.bin/tsc`], {
cwd: path.join(__dirname, '../out-editor-esm')
});
}
console.log(result.stdout.toString());
console.log(result.stderr.toString());
if (FAIL_ON_PURPOSE || result.status !== 0) {
console.log(`The TS Compilation failed, preparing analysis folder...`);
const destPath = path.join(__dirname, '../../vscode-monaco-editor-esm-analysis');
const keepPrevAnalysis = (KEEP_PREV_ANALYSIS && fs.existsSync(destPath));
const cleanDestPath = (keepPrevAnalysis ? Promise.resolve() : util.rimraf(destPath)());
return cleanDestPath.then(() => {
// build a list of files to copy
const files = util.rreddir(path.join(__dirname, '../out-editor-esm'));
if (!keepPrevAnalysis) {
fs.mkdirSync(destPath);
// initialize a new repository
cp.spawnSync(`git`, [`init`], {
cwd: destPath
});
// copy files from src
for (const file of files) {
const srcFilePath = path.join(__dirname, '../src', file);
const dstFilePath = path.join(destPath, file);
if (fs.existsSync(srcFilePath)) {
util.ensureDir(path.dirname(dstFilePath));
const contents = fs.readFileSync(srcFilePath).toString().replace(/\r\n|\r|\n/g, '\n');
fs.writeFileSync(dstFilePath, contents);
}
}
// create an initial commit to diff against
cp.spawnSync(`git`, [`add`, `.`], {
cwd: destPath
});
// create the commit
cp.spawnSync(`git`, [`commit`, `-m`, `"original sources"`, `--no-gpg-sign`], {
cwd: destPath
});
}
// copy files from tree shaken src
for (const file of files) {
const srcFilePath = path.join(__dirname, '../out-editor-src', file);
const dstFilePath = path.join(destPath, file);
if (fs.existsSync(srcFilePath)) {
util.ensureDir(path.dirname(dstFilePath));
const contents = fs.readFileSync(srcFilePath).toString().replace(/\r\n|\r|\n/g, '\n');
fs.writeFileSync(dstFilePath, contents);
}
}
console.log(`Open in VS Code the folder at '${destPath}' and you can analyze the compilation error`);
throw new Error('Standalone Editor compilation failed. If this is the build machine, simply launch `yarn run gulp editor-distro` on your machine to further analyze the compilation problem.');
});
}
});
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('\tMonacoEnvironment?') === 0) {
lines[i] = ` MonacoEnvironment?: Environment | undefined;`;
}
}
return lines.join('\n').replace(/\n\n\n+/g, '\n\n');
}
function filterStream(testFunc) {
return es.through(function (data) {
if (!testFunc(data.relative)) {
return;
}
this.emit('data', data);
});
}
const finalEditorResourcesTask = task.define('final-editor-resources', () => {
return es.merge(
// other assets
es.merge(
gulp.src('build/monaco/LICENSE'),
gulp.src('build/monaco/ThirdPartyNotices.txt'),
gulp.src('src/vs/monaco.d.ts')
).pipe(gulp.dest('out-monaco-editor-core')),
// place the .d.ts in the esm folder
gulp.src('src/vs/monaco.d.ts')
.pipe(es.through(function (data) {
this.emit('data', new File({
path: data.path.replace(/monaco\.d\.ts/, 'editor.api.d.ts'),
base: data.base,
contents: Buffer.from(toExternalDTS(data.contents.toString()))
}));
}))
.pipe(gulp.dest('out-monaco-editor-core/esm/vs/editor')),
// package.json
gulp.src('build/monaco/package.json')
.pipe(es.through(function (data) {
let json = JSON.parse(data.contents.toString());
json.private = false;
data.contents = Buffer.from(JSON.stringify(json, null, ' '));
this.emit('data', data);
}))
.pipe(gulp.dest('out-monaco-editor-core')),
// version.txt
gulp.src('build/monaco/version.txt')
.pipe(es.through(function (data) {
data.contents = Buffer.from(`monaco-editor-core: https://github.com/microsoft/vscode/tree/${sha1}`);
this.emit('data', data);
}))
.pipe(gulp.dest('out-monaco-editor-core')),
// README.md
gulp.src('build/monaco/README-npm.md')
.pipe(es.through(function (data) {
this.emit('data', new File({
path: data.path.replace(/README-npm\.md/, 'README.md'),
base: data.base,
contents: data.contents
}));
}))
.pipe(gulp.dest('out-monaco-editor-core')),
// dev folder
es.merge(
gulp.src('out-editor/**/*')
).pipe(gulp.dest('out-monaco-editor-core/dev')),
// min folder
es.merge(
gulp.src('out-editor-min/**/*')
).pipe(filterStream(function (path) {
// no map files
return !/(\.js\.map$)|(nls\.metadata\.json$)|(bundleInfo\.json$)/.test(path);
})).pipe(es.through(function (data) {
// tweak the sourceMappingURL
if (!/\.js$/.test(data.path)) {
this.emit('data', data);
return;
}
let relativePathToMap = path.relative(path.join(data.relative), path.join('min-maps', data.relative + '.map'));
let strContents = data.contents.toString();
let newStr = '//# sourceMappingURL=' + relativePathToMap.replace(/\\/g, '/');
strContents = strContents.replace(/\/\/# sourceMappingURL=[^ ]+$/, newStr);
data.contents = Buffer.from(strContents);
this.emit('data', data);
})).pipe(gulp.dest('out-monaco-editor-core/min')),
// min-maps folder
es.merge(
gulp.src('out-editor-min/**/*')
).pipe(filterStream(function (path) {
// no map files
return /\.js\.map$/.test(path);
})).pipe(gulp.dest('out-monaco-editor-core/min-maps'))
);
});
gulp.task('extract-editor-src',
task.series(
util.rimraf('out-editor-src'),
extractEditorSrcTask
)
);
gulp.task('editor-distro',
task.series(
task.parallel(
util.rimraf('out-editor-src'),
util.rimraf('out-editor-build'),
util.rimraf('out-editor-esm'),
util.rimraf('out-monaco-editor-core'),
util.rimraf('out-editor'),
util.rimraf('out-editor-min')
),
extractEditorSrcTask,
task.parallel(
task.series(
compileEditorAMDTask,
optimizeEditorAMDTask,
minifyEditorAMDTask
),
task.series(
createESMSourcesAndResourcesTask,
compileEditorESMTask
)
),
finalEditorResourcesTask
)
);
const bundleEditorESMTask = task.define('editor-esm-bundle-webpack', () => {
const webpack = require('webpack');
const webpackGulp = require('webpack-stream');
const result = es.through();
const webpackConfigPath = path.join(root, 'build/monaco/monaco.webpack.config.js');
const webpackConfig = {
...require(webpackConfigPath),
...{ mode: 'production' }
};
const webpackDone = (err, stats) => {
if (err) {
result.emit('error', err);
return;
}
const { compilation } = stats;
if (compilation.errors.length > 0) {
result.emit('error', compilation.errors.join('\n'));
}
if (compilation.warnings.length > 0) {
result.emit('data', compilation.warnings.join('\n'));
}
};
return webpackGulp(webpackConfig, webpack, webpackDone)
.pipe(gulp.dest('out-editor-esm-bundle'));
});
gulp.task('editor-esm-bundle',
task.series(
task.parallel(
util.rimraf('out-editor-src'),
util.rimraf('out-editor-esm'),
util.rimraf('out-monaco-editor-core'),
util.rimraf('out-editor-esm-bundle'),
),
extractEditorSrcTask,
createESMSourcesAndResourcesTask,
compileEditorESMTask,
bundleEditorESMTask,
)
);
gulp.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);
}));
//#region monaco type checking
function createTscCompileTask(watch) {
return () => {
const createReporter = require('./lib/reporter').createReporter;
return new Promise((resolve, reject) => {
const args = ['./node_modules/.bin/tsc', '-p', './src/tsconfig.monaco.json', '--noEmit'];
if (watch) {
args.push('-w');
}
const child = cp.spawn(`node`, args, {
cwd: path.join(__dirname, '..'),
// stdio: [null, 'pipe', 'inherit']
});
let errors = [];
let reporter = createReporter('monaco');
let report;
// eslint-disable-next-line no-control-regex
let magic = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings
child.stdout.on('data', data => {
let str = String(data);
str = str.replace(magic, '').trim();
if (str.indexOf('Starting compilation') >= 0 || str.indexOf('File change detected') >= 0) {
errors.length = 0;
report = reporter.end(false);
} else if (str.indexOf('Compilation complete') >= 0) {
report.end();
} else if (str) {
let match = /(.*\(\d+,\d+\): )(.*: )(.*)/.exec(str);
if (match) {
// trying to massage the message so that it matches the gulp-tsb error messages
// e.g. src/vs/base/common/strings.ts(663,5): error TS2322: Type '1234' is not assignable to type 'string'.
let fullpath = path.join(root, match[1]);
let message = match[3];
reporter(fullpath + message);
} else {
reporter(str);
}
}
});
child.on('exit', resolve);
child.on('error', reject);
});
};
}
const monacoTypecheckWatchTask = task.define('monaco-typecheck-watch', createTscCompileTask(true));
exports.monacoTypecheckWatchTask = monacoTypecheckWatchTask;
const monacoTypecheckTask = task.define('monaco-typecheck', createTscCompileTask(false));
exports.monacoTypecheckTask = monacoTypecheckTask;
//#endregion
@@ -0,0 +1,464 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.bundle = void 0;
const fs = require("fs");
const path = require("path");
const vm = require("vm");
/**
* Bundle `entryPoints` given config `config`.
*/
function bundle(entryPoints, config, callback) {
const entryPointsMap = {};
entryPoints.forEach((module) => {
entryPointsMap[module.name] = module;
});
const allMentionedModulesMap = {};
entryPoints.forEach((module) => {
allMentionedModulesMap[module.name] = true;
(module.include || []).forEach(function (includedModule) {
allMentionedModulesMap[includedModule] = true;
});
(module.exclude || []).forEach(function (excludedModule) {
allMentionedModulesMap[excludedModule] = true;
});
});
const code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js'));
const r = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});');
const loaderModule = { exports: {} };
r.call({}, require, loaderModule, loaderModule.exports);
const loader = loaderModule.exports;
config.isBuild = true;
config.paths = config.paths || {};
if (!config.paths['vs/nls']) {
config.paths['vs/nls'] = 'out-build/vs/nls.build';
}
if (!config.paths['vs/css']) {
config.paths['vs/css'] = 'out-build/vs/css.build';
}
loader.config(config);
loader(['require'], (localRequire) => {
const resolvePath = (path) => {
const r = localRequire.toUrl(path);
if (!/\.js/.test(r)) {
return r + '.js';
}
return r;
};
for (const moduleId in entryPointsMap) {
const entryPoint = entryPointsMap[moduleId];
if (entryPoint.append) {
entryPoint.append = entryPoint.append.map(resolvePath);
}
if (entryPoint.prepend) {
entryPoint.prepend = entryPoint.prepend.map(resolvePath);
}
}
});
loader(Object.keys(allMentionedModulesMap), () => {
const modules = loader.getBuildInfo();
const partialResult = emitEntryPoints(modules, entryPointsMap);
const cssInlinedResources = loader('vs/css').getInlinedResources();
callback(null, {
files: partialResult.files,
cssInlinedResources: cssInlinedResources,
bundleData: partialResult.bundleData
});
}, (err) => callback(err, null));
}
exports.bundle = bundle;
function emitEntryPoints(modules, entryPoints) {
const modulesMap = {};
modules.forEach((m) => {
modulesMap[m.id] = m;
});
const modulesGraph = {};
modules.forEach((m) => {
modulesGraph[m.id] = m.dependencies;
});
const sortedModules = topologicalSort(modulesGraph);
let result = [];
const usedPlugins = {};
const bundleData = {
graph: modulesGraph,
bundles: {}
};
Object.keys(entryPoints).forEach((moduleToBundle) => {
const info = entryPoints[moduleToBundle];
const rootNodes = [moduleToBundle].concat(info.include || []);
const allDependencies = visit(rootNodes, modulesGraph);
const excludes = ['require', 'exports', 'module'].concat(info.exclude || []);
excludes.forEach((excludeRoot) => {
const allExcludes = visit([excludeRoot], modulesGraph);
Object.keys(allExcludes).forEach((exclude) => {
delete allDependencies[exclude];
});
});
const includedModules = sortedModules.filter((module) => {
return allDependencies[module];
});
bundleData.bundles[moduleToBundle] = includedModules;
const res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend || [], info.append || [], info.dest);
result = result.concat(res.files);
for (const pluginName in res.usedPlugins) {
usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName];
}
});
Object.keys(usedPlugins).forEach((pluginName) => {
const plugin = usedPlugins[pluginName];
if (typeof plugin.finishBuild === 'function') {
const write = (filename, contents) => {
result.push({
dest: filename,
sources: [{
path: null,
contents: contents
}]
});
};
plugin.finishBuild(write);
}
});
return {
// TODO@TS 2.1.2
files: extractStrings(removeDuplicateTSBoilerplate(result)),
bundleData: bundleData
};
}
function extractStrings(destFiles) {
const parseDefineCall = (moduleMatch, depsMatch) => {
const module = moduleMatch.replace(/^"|"$/g, '');
let deps = depsMatch.split(',');
deps = deps.map((dep) => {
dep = dep.trim();
dep = dep.replace(/^"|"$/g, '');
dep = dep.replace(/^'|'$/g, '');
let prefix = null;
let _path = null;
const pieces = dep.split('!');
if (pieces.length > 1) {
prefix = pieces[0] + '!';
_path = pieces[1];
}
else {
prefix = '';
_path = pieces[0];
}
if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) {
const res = path.join(path.dirname(module), _path).replace(/\\/g, '/');
return prefix + res;
}
return prefix + _path;
});
return {
module: module,
deps: deps
};
};
destFiles.forEach((destFile) => {
if (!/\.js$/.test(destFile.dest)) {
return;
}
if (/\.nls\.js$/.test(destFile.dest)) {
return;
}
// Do one pass to record the usage counts for each module id
const useCounts = {};
destFile.sources.forEach((source) => {
const matches = source.contents.match(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/);
if (!matches) {
return;
}
const defineCall = parseDefineCall(matches[1], matches[2]);
useCounts[defineCall.module] = (useCounts[defineCall.module] || 0) + 1;
defineCall.deps.forEach((dep) => {
useCounts[dep] = (useCounts[dep] || 0) + 1;
});
});
const sortedByUseModules = Object.keys(useCounts);
sortedByUseModules.sort((a, b) => {
return useCounts[b] - useCounts[a];
});
const replacementMap = {};
sortedByUseModules.forEach((module, index) => {
replacementMap[module] = index;
});
destFile.sources.forEach((source) => {
source.contents = source.contents.replace(/define\(("[^"]+"),\s*\[(((, )?("|')[^"']+("|'))+)\]/, (_, moduleMatch, depsMatch) => {
const defineCall = parseDefineCall(moduleMatch, depsMatch);
return `define(__m[${replacementMap[defineCall.module]}/*${defineCall.module}*/], __M([${defineCall.deps.map(dep => replacementMap[dep] + '/*' + dep + '*/').join(',')}])`;
});
});
destFile.sources.unshift({
path: null,
contents: [
'(function() {',
`var __m = ${JSON.stringify(sortedByUseModules)};`,
`var __M = function(deps) {`,
` var result = [];`,
` for (var i = 0, len = deps.length; i < len; i++) {`,
` result[i] = __m[deps[i]];`,
` }`,
` return result;`,
`};`
].join('\n')
});
destFile.sources.push({
path: null,
contents: '}).call(this);'
});
});
return destFiles;
}
function removeDuplicateTSBoilerplate(destFiles) {
// Taken from typescript compiler => emitFiles
const BOILERPLATE = [
{ start: /^var __extends/, end: /^}\)\(\);$/ },
{ start: /^var __assign/, end: /^};$/ },
{ start: /^var __decorate/, end: /^};$/ },
{ start: /^var __metadata/, end: /^};$/ },
{ start: /^var __param/, end: /^};$/ },
{ start: /^var __awaiter/, end: /^};$/ },
{ start: /^var __generator/, end: /^};$/ },
];
destFiles.forEach((destFile) => {
const SEEN_BOILERPLATE = [];
destFile.sources.forEach((source) => {
const lines = source.contents.split(/\r\n|\n|\r/);
const newLines = [];
let IS_REMOVING_BOILERPLATE = false, END_BOILERPLATE;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (IS_REMOVING_BOILERPLATE) {
newLines.push('');
if (END_BOILERPLATE.test(line)) {
IS_REMOVING_BOILERPLATE = false;
}
}
else {
for (let j = 0; j < BOILERPLATE.length; j++) {
const boilerplate = BOILERPLATE[j];
if (boilerplate.start.test(line)) {
if (SEEN_BOILERPLATE[j]) {
IS_REMOVING_BOILERPLATE = true;
END_BOILERPLATE = boilerplate.end;
}
else {
SEEN_BOILERPLATE[j] = true;
}
}
}
if (IS_REMOVING_BOILERPLATE) {
newLines.push('');
}
else {
newLines.push(line);
}
}
}
source.contents = newLines.join('\n');
});
});
return destFiles;
}
function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, append, dest) {
if (!dest) {
dest = entryPoint + '.js';
}
const mainResult = {
sources: [],
dest: dest
}, results = [mainResult];
const usedPlugins = {};
const getLoaderPlugin = (pluginName) => {
if (!usedPlugins[pluginName]) {
usedPlugins[pluginName] = modulesMap[pluginName].exports;
}
return usedPlugins[pluginName];
};
includedModules.forEach((c) => {
const bangIndex = c.indexOf('!');
if (bangIndex >= 0) {
const pluginName = c.substr(0, bangIndex);
const plugin = getLoaderPlugin(pluginName);
mainResult.sources.push(emitPlugin(entryPoint, plugin, pluginName, c.substr(bangIndex + 1)));
return;
}
const module = modulesMap[c];
if (module.path === 'empty:') {
return;
}
const contents = readFileAndRemoveBOM(module.path);
if (module.shim) {
mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents));
}
else {
mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents));
}
});
Object.keys(usedPlugins).forEach((pluginName) => {
const plugin = usedPlugins[pluginName];
if (typeof plugin.writeFile === 'function') {
const req = (() => {
throw new Error('no-no!');
});
req.toUrl = something => something;
const write = (filename, contents) => {
results.push({
dest: filename,
sources: [{
path: null,
contents: contents
}]
});
};
plugin.writeFile(pluginName, entryPoint, req, write, {});
}
});
const toIFile = (path) => {
const contents = readFileAndRemoveBOM(path);
return {
path: path,
contents: contents
};
};
const toPrepend = (prepend || []).map(toIFile);
const toAppend = (append || []).map(toIFile);
mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend);
return {
files: results,
usedPlugins: usedPlugins
};
}
function readFileAndRemoveBOM(path) {
const BOM_CHAR_CODE = 65279;
let contents = fs.readFileSync(path, 'utf8');
// Remove BOM
if (contents.charCodeAt(0) === BOM_CHAR_CODE) {
contents = contents.substring(1);
}
return contents;
}
function emitPlugin(entryPoint, plugin, pluginName, moduleName) {
let result = '';
if (typeof plugin.write === 'function') {
const write = ((what) => {
result += what;
});
write.getEntryPoint = () => {
return entryPoint;
};
write.asModule = (moduleId, code) => {
code = code.replace(/^define\(/, 'define("' + moduleId + '",');
result += code;
};
plugin.write(pluginName, moduleName, write);
}
return {
path: null,
contents: result
};
}
function emitNamedModule(moduleId, defineCallPosition, path, contents) {
// `defineCallPosition` is the position in code: |define()
const defineCallOffset = positionToOffset(contents, defineCallPosition.line, defineCallPosition.col);
// `parensOffset` is the position in code: define|()
const parensOffset = contents.indexOf('(', defineCallOffset);
const insertStr = '"' + moduleId + '", ';
return {
path: path,
contents: contents.substr(0, parensOffset + 1) + insertStr + contents.substr(parensOffset + 1)
};
}
function emitShimmedModule(moduleId, myDeps, factory, path, contents) {
const strDeps = (myDeps.length > 0 ? '"' + myDeps.join('", "') + '"' : '');
const strDefine = 'define("' + moduleId + '", [' + strDeps + '], ' + factory + ');';
return {
path: path,
contents: contents + '\n;\n' + strDefine
};
}
/**
* Convert a position (line:col) to (offset) in string `str`
*/
function positionToOffset(str, desiredLine, desiredCol) {
if (desiredLine === 1) {
return desiredCol - 1;
}
let line = 1;
let lastNewLineOffset = -1;
do {
if (desiredLine === line) {
return lastNewLineOffset + 1 + desiredCol - 1;
}
lastNewLineOffset = str.indexOf('\n', lastNewLineOffset + 1);
line++;
} while (lastNewLineOffset >= 0);
return -1;
}
/**
* Return a set of reachable nodes in `graph` starting from `rootNodes`
*/
function visit(rootNodes, graph) {
const result = {};
const queue = rootNodes;
rootNodes.forEach((node) => {
result[node] = true;
});
while (queue.length > 0) {
const el = queue.shift();
const myEdges = graph[el] || [];
myEdges.forEach((toNode) => {
if (!result[toNode]) {
result[toNode] = true;
queue.push(toNode);
}
});
}
return result;
}
/**
* Perform a topological sort on `graph`
*/
function topologicalSort(graph) {
const allNodes = {}, outgoingEdgeCount = {}, inverseEdges = {};
Object.keys(graph).forEach((fromNode) => {
allNodes[fromNode] = true;
outgoingEdgeCount[fromNode] = graph[fromNode].length;
graph[fromNode].forEach((toNode) => {
allNodes[toNode] = true;
outgoingEdgeCount[toNode] = outgoingEdgeCount[toNode] || 0;
inverseEdges[toNode] = inverseEdges[toNode] || [];
inverseEdges[toNode].push(fromNode);
});
});
// https://en.wikipedia.org/wiki/Topological_sorting
const S = [], L = [];
Object.keys(allNodes).forEach((node) => {
if (outgoingEdgeCount[node] === 0) {
delete outgoingEdgeCount[node];
S.push(node);
}
});
while (S.length > 0) {
// Ensure the exact same order all the time with the same inputs
S.sort();
const n = S.shift();
L.push(n);
const myInverseEdges = inverseEdges[n] || [];
myInverseEdges.forEach((m) => {
outgoingEdgeCount[m]--;
if (outgoingEdgeCount[m] === 0) {
delete outgoingEdgeCount[m];
S.push(m);
}
});
}
if (Object.keys(outgoingEdgeCount).length > 0) {
throw new Error('Cannot do topological sort on cyclic graph, remaining nodes: ' + Object.keys(outgoingEdgeCount));
}
return L;
}
@@ -0,0 +1,302 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as es from 'event-stream';
import * as gulp from 'gulp';
import * as concat from 'gulp-concat';
import * as filter from 'gulp-filter';
import * as fancyLog from 'fancy-log';
import * as ansiColors from 'ansi-colors';
import * as path from 'path';
import * as pump from 'pump';
import * as VinylFile from 'vinyl';
import * as bundle from './bundle';
import { Language, processNlsFiles } from './i18n';
import { createStatsStream } from './stats';
import * as util from './util';
const REPO_ROOT_PATH = path.join(__dirname, '../..');
function log(prefix: string, message: string): void {
fancyLog(ansiColors.cyan('[' + prefix + ']'), message);
}
export function loaderConfig() {
const result: any = {
paths: {
'vs': 'out-build/vs',
'vscode': 'empty:'
},
amdModulesPattern: /^vs\//
};
result['vs/css'] = { inlineResources: true };
return result;
}
const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i;
function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, externalLoaderInfo?: any): NodeJS.ReadWriteStream {
let sources = [
`${src}/vs/loader.js`
];
if (bundleLoader) {
sources = sources.concat([
`${src}/vs/css.js`,
`${src}/vs/nls.js`
]);
}
let isFirst = true;
return (
gulp
.src(sources, { base: `${src}` })
.pipe(es.through(function (data) {
if (isFirst) {
isFirst = false;
this.emit('data', new VinylFile({
path: 'fake',
base: '.',
contents: Buffer.from(bundledFileHeader)
}));
this.emit('data', data);
} else {
this.emit('data', data);
}
}, function () {
if (externalLoaderInfo !== undefined) {
this.emit('data', new VinylFile({
path: 'fake2',
base: '.',
contents: Buffer.from(`require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)});`)
}));
}
this.emit('end');
}))
.pipe(concat('vs/loader.js'))
);
}
function toConcatStream(src: string, bundledFileHeader: string, sources: bundle.IFile[], dest: string, fileContentMapper: (contents: string, path: string) => string): NodeJS.ReadWriteStream {
const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest);
// If a bundle ends up including in any of the sources our copyright, then
// insert a fake source at the beginning of each bundle with our copyright
let containsOurCopyright = false;
for (let i = 0, len = sources.length; i < len; i++) {
const fileContents = sources[i].contents;
if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) {
containsOurCopyright = true;
break;
}
}
if (containsOurCopyright) {
sources.unshift({
path: null,
contents: bundledFileHeader
});
}
const treatedSources = sources.map(function (source) {
const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : '';
const base = source.path ? root + `/${src}` : '.';
const path = source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake';
const contents = source.path ? fileContentMapper(source.contents, path) : source.contents;
return new VinylFile({
path: path,
base: base,
contents: Buffer.from(contents)
});
});
return es.readArray(treatedSources)
.pipe(useSourcemaps ? util.loadSourcemaps() : es.through())
.pipe(concat(dest))
.pipe(createStatsStream(dest));
}
function toBundleStream(src: string, bundledFileHeader: string, bundles: bundle.IConcatFile[], fileContentMapper: (contents: string, path: string) => string): NodeJS.ReadWriteStream {
return es.merge(bundles.map(function (bundle) {
return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest, fileContentMapper);
}));
}
export interface IOptimizeTaskOpts {
/**
* The folder to read files from.
*/
src: string;
/**
* (for AMD files, will get bundled and get Copyright treatment)
*/
entryPoints: bundle.IEntryPoint[];
/**
* (svg, etc.)
*/
resources: string[];
loaderConfig: any;
/**
* Additional info we append to the end of the loader
*/
externalLoaderInfo?: any;
/**
* (true by default - append css and nls to loader)
*/
bundleLoader?: boolean;
/**
* (basically the Copyright treatment)
*/
header?: string;
/**
* (emit bundleInfo.json file)
*/
bundleInfo: boolean;
/**
* (out folder name)
*/
out: string;
/**
* (out folder name)
*/
languages?: Language[];
/**
* File contents interceptor
* @param contents The contens of the file
* @param path The absolute file path, always using `/`, even on Windows
*/
fileContentMapper?: (contents: string, path: string) => string;
}
const DEFAULT_FILE_HEADER = [
'/*!--------------------------------------------------------',
' * Copyright (C) Microsoft Corporation. All rights reserved.',
' *--------------------------------------------------------*/'
].join('\n');
export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream {
const src = opts.src;
const entryPoints = opts.entryPoints;
const resources = opts.resources;
const loaderConfig = opts.loaderConfig;
const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER;
const bundleLoader = (typeof opts.bundleLoader === 'undefined' ? true : opts.bundleLoader);
const out = opts.out;
const fileContentMapper = opts.fileContentMapper || ((contents: string, _path: string) => contents);
return function () {
const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps');
const bundlesStream = es.through(); // this stream will contain the bundled files
const resourcesStream = es.through(); // this stream will contain the resources
const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json
bundle.bundle(entryPoints, loaderConfig, function (err, result) {
if (err || !result) { return bundlesStream.emit('error', JSON.stringify(err)); }
toBundleStream(src, bundledFileHeader, result.files, fileContentMapper).pipe(bundlesStream);
// Remove css inlined resources
const filteredResources = resources.slice();
result.cssInlinedResources.forEach(function (resource) {
if (process.env['VSCODE_BUILD_VERBOSE']) {
log('optimizer', 'excluding inlined: ' + resource);
}
filteredResources.push('!' + resource);
});
gulp.src(filteredResources, { base: `${src}`, allowEmpty: true }).pipe(resourcesStream);
const bundleInfoArray: VinylFile[] = [];
if (opts.bundleInfo) {
bundleInfoArray.push(new VinylFile({
path: 'bundleInfo.json',
base: '.',
contents: Buffer.from(JSON.stringify(result.bundleData, null, '\t'))
}));
}
es.readArray(bundleInfoArray).pipe(bundleInfoStream);
});
const result = es.merge(
loader(src, bundledFileHeader, bundleLoader, opts.externalLoaderInfo),
bundlesStream,
resourcesStream,
bundleInfoStream
);
return result
.pipe(sourcemaps.write('./', {
sourceRoot: undefined,
addComment: true,
includeContent: true
}))
.pipe(opts.languages && opts.languages.length ? processNlsFiles({
fileHeader: bundledFileHeader,
languages: opts.languages
}) : es.through())
.pipe(gulp.dest(out));
};
}
export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => void {
const esbuild = require('esbuild') as typeof import('esbuild');
const sourceMappingURL = sourceMapBaseUrl ? ((f: any) => `${sourceMapBaseUrl}/${f.relative}.map`) : undefined;
return cb => {
const cssnano = require('cssnano') as typeof import('cssnano');
const postcss = require('gulp-postcss') as typeof import('gulp-postcss');
const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps');
const jsFilter = filter('**/*.js', { restore: true });
const cssFilter = filter('**/*.css', { restore: true });
pump(
gulp.src([src + '/**', '!' + src + '/**/*.map']),
jsFilter,
sourcemaps.init({ loadMaps: true }),
es.map((f: any, cb) => {
esbuild.build({
entryPoints: [f.path],
minify: true,
sourcemap: 'external',
outdir: '.',
platform: 'node',
target: ['esnext'],
write: false
}).then(res => {
const jsFile = res.outputFiles.find(f => /\.js$/.test(f.path))!;
const sourceMapFile = res.outputFiles.find(f => /\.js\.map$/.test(f.path))!;
f.contents = Buffer.from(jsFile.contents);
f.sourceMap = JSON.parse(sourceMapFile.text);
cb(undefined, f);
}, cb);
}),
jsFilter.restore,
cssFilter,
postcss([cssnano({ preset: 'default' })]),
cssFilter.restore,
(<any>sourcemaps).mapSources((sourcePath: string) => {
if (sourcePath === 'bootstrap-fork.js') {
return 'bootstrap-fork.orig.js';
}
return sourcePath;
}),
sourcemaps.write('./', {
sourceMappingURL,
sourceRoot: undefined,
includeContent: true,
addComment: true
} as any),
gulp.dest(src + '-min'),
(err: any) => cb(err));
};
}
@@ -0,0 +1,372 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fs from 'fs';
import * as path from 'path';
import * as tss from './treeshaking';
const REPO_ROOT = path.join(__dirname, '../../');
const SRC_DIR = path.join(REPO_ROOT, 'src');
let dirCache: { [dir: string]: boolean; } = {};
function writeFile(filePath: string, contents: Buffer | string): void {
function ensureDirs(dirPath: string): void {
if (dirCache[dirPath]) {
return;
}
dirCache[dirPath] = true;
ensureDirs(path.dirname(dirPath));
if (fs.existsSync(dirPath)) {
return;
}
fs.mkdirSync(dirPath);
}
ensureDirs(path.dirname(filePath));
fs.writeFileSync(filePath, contents);
}
export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: string }): void {
const ts = require('typescript') as typeof import('typescript');
const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString());
let compilerOptions: { [key: string]: any };
if (tsConfig.extends) {
compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions);
delete tsConfig.extends;
} else {
compilerOptions = tsConfig.compilerOptions;
}
tsConfig.compilerOptions = compilerOptions;
compilerOptions.noEmit = false;
compilerOptions.noUnusedLocals = false;
compilerOptions.preserveConstEnums = false;
compilerOptions.declaration = false;
compilerOptions.moduleResolution = ts.ModuleResolutionKind.Classic;
options.compilerOptions = compilerOptions;
console.log(`Running tree shaker with shakeLevel ${tss.toStringShakeLevel(options.shakeLevel)}`);
// Take the extra included .d.ts files from `tsconfig.monaco.json`
options.typings = (<string[]>tsConfig.include).filter(includedFile => /\.d\.ts$/.test(includedFile));
// Add extra .d.ts files from `node_modules/@types/`
if (Array.isArray(options.compilerOptions?.types)) {
options.compilerOptions.types.forEach((type: string) => {
options.typings.push(`../node_modules/@types/${type}/index.d.ts`);
});
}
let result = tss.shake(options);
for (let fileName in result) {
if (result.hasOwnProperty(fileName)) {
writeFile(path.join(options.destRoot, fileName), result[fileName]);
}
}
let copied: { [fileName: string]: boolean; } = {};
const copyFile = (fileName: string) => {
if (copied[fileName]) {
return;
}
copied[fileName] = true;
const srcPath = path.join(options.sourcesRoot, fileName);
const dstPath = path.join(options.destRoot, fileName);
writeFile(dstPath, fs.readFileSync(srcPath));
};
const writeOutputFile = (fileName: string, contents: string | Buffer) => {
writeFile(path.join(options.destRoot, fileName), contents);
};
for (let fileName in result) {
if (result.hasOwnProperty(fileName)) {
const fileContents = result[fileName];
const info = ts.preProcessFile(fileContents);
for (let i = info.importedFiles.length - 1; i >= 0; i--) {
const importedFileName = info.importedFiles[i].fileName;
let importedFilePath: string;
if (/^vs\/css!/.test(importedFileName)) {
importedFilePath = importedFileName.substr('vs/css!'.length) + '.css';
} else {
importedFilePath = importedFileName;
}
if (/(^\.\/)|(^\.\.\/)/.test(importedFilePath)) {
importedFilePath = path.join(path.dirname(fileName), importedFilePath);
}
if (/\.css$/.test(importedFilePath)) {
transportCSS(importedFilePath, copyFile, writeOutputFile);
} else {
if (fs.existsSync(path.join(options.sourcesRoot, importedFilePath + '.js'))) {
copyFile(importedFilePath + '.js');
}
}
}
}
}
delete tsConfig.compilerOptions.moduleResolution;
writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t'));
[
'vs/css.build.js',
'vs/css.d.ts',
'vs/css.js',
'vs/loader.js',
'vs/nls.build.js',
'vs/nls.d.ts',
'vs/nls.js',
'vs/nls.mock.ts',
].forEach(copyFile);
}
export interface IOptions2 {
srcFolder: string;
outFolder: string;
outResourcesFolder: string;
ignores: string[];
renames: { [filename: string]: string; };
}
export function createESMSourcesAndResources2(options: IOptions2): void {
const ts = require('typescript') as typeof import('typescript');
const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder);
const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder);
const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder);
const getDestAbsoluteFilePath = (file: string): string => {
let dest = options.renames[file.replace(/\\/g, '/')] || file;
if (dest === 'tsconfig.json') {
return path.join(OUT_FOLDER, `tsconfig.json`);
}
if (/\.ts$/.test(dest)) {
return path.join(OUT_FOLDER, dest);
}
return path.join(OUT_RESOURCES_FOLDER, dest);
};
const allFiles = walkDirRecursive(SRC_FOLDER);
for (const file of allFiles) {
if (options.ignores.indexOf(file.replace(/\\/g, '/')) >= 0) {
continue;
}
if (file === 'tsconfig.json') {
const tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString());
tsConfig.compilerOptions.module = 'es6';
tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/');
write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t'));
continue;
}
if (/\.d\.ts$/.test(file) || /\.css$/.test(file) || /\.js$/.test(file) || /\.ttf$/.test(file)) {
// Transport the files directly
write(getDestAbsoluteFilePath(file), fs.readFileSync(path.join(SRC_FOLDER, file)));
continue;
}
if (/\.ts$/.test(file)) {
// Transform the .ts file
let fileContents = fs.readFileSync(path.join(SRC_FOLDER, file)).toString();
const info = ts.preProcessFile(fileContents);
for (let i = info.importedFiles.length - 1; i >= 0; i--) {
const importedFilename = info.importedFiles[i].fileName;
const pos = info.importedFiles[i].pos;
const end = info.importedFiles[i].end;
let importedFilepath: string;
if (/^vs\/css!/.test(importedFilename)) {
importedFilepath = importedFilename.substr('vs/css!'.length) + '.css';
} else {
importedFilepath = importedFilename;
}
if (/(^\.\/)|(^\.\.\/)/.test(importedFilepath)) {
importedFilepath = path.join(path.dirname(file), importedFilepath);
}
let relativePath: string;
if (importedFilepath === path.dirname(file).replace(/\\/g, '/')) {
relativePath = '../' + path.basename(path.dirname(file));
} else if (importedFilepath === path.dirname(path.dirname(file)).replace(/\\/g, '/')) {
relativePath = '../../' + path.basename(path.dirname(path.dirname(file)));
} else {
relativePath = path.relative(path.dirname(file), importedFilepath);
}
relativePath = relativePath.replace(/\\/g, '/');
if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) {
relativePath = './' + relativePath;
}
fileContents = (
fileContents.substring(0, pos + 1)
+ relativePath
+ fileContents.substring(end + 1)
);
}
fileContents = fileContents.replace(/import ([a-zA-z0-9]+) = require\(('[^']+')\);/g, function (_, m1, m2) {
return `import * as ${m1} from ${m2};`;
});
write(getDestAbsoluteFilePath(file), fileContents);
continue;
}
console.log(`UNKNOWN FILE: ${file}`);
}
function walkDirRecursive(dir: string): string[] {
if (dir.charAt(dir.length - 1) !== '/' || dir.charAt(dir.length - 1) !== '\\') {
dir += '/';
}
let result: string[] = [];
_walkDirRecursive(dir, result, dir.length);
return result;
}
function _walkDirRecursive(dir: string, result: string[], trimPos: number): void {
const files = fs.readdirSync(dir);
for (let i = 0; i < files.length; i++) {
const file = path.join(dir, files[i]);
if (fs.statSync(file).isDirectory()) {
_walkDirRecursive(file, result, trimPos);
} else {
result.push(file.substr(trimPos));
}
}
}
function write(absoluteFilePath: string, contents: string | Buffer): void {
if (/(\.ts$)|(\.js$)/.test(absoluteFilePath)) {
contents = toggleComments(contents.toString());
}
writeFile(absoluteFilePath, contents);
function toggleComments(fileContents: string): string {
let lines = fileContents.split(/\r\n|\r|\n/);
let mode = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (mode === 0) {
if (/\/\/ ESM-comment-begin/.test(line)) {
mode = 1;
continue;
}
if (/\/\/ ESM-uncomment-begin/.test(line)) {
mode = 2;
continue;
}
continue;
}
if (mode === 1) {
if (/\/\/ ESM-comment-end/.test(line)) {
mode = 0;
continue;
}
lines[i] = '// ' + line;
continue;
}
if (mode === 2) {
if (/\/\/ ESM-uncomment-end/.test(line)) {
mode = 0;
continue;
}
lines[i] = line.replace(/^(\s*)\/\/ ?/, function (_, indent) {
return indent;
});
}
}
return lines.join('\n');
}
}
}
function transportCSS(module: string, enqueue: (module: string) => void, write: (path: string, contents: string | Buffer) => void): boolean {
if (!/\.css/.test(module)) {
return false;
}
const filename = path.join(SRC_DIR, module);
const fileContents = fs.readFileSync(filename).toString();
const inlineResources = 'base64'; // see https://github.com/microsoft/monaco-editor/issues/148
const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64');
write(module, newContents);
return true;
function _rewriteOrInlineUrls(contents: string, forceBase64: boolean): string {
return _replaceURL(contents, (url) => {
const fontMatch = url.match(/^(.*).ttf\?(.*)$/);
if (fontMatch) {
const relativeFontPath = `${fontMatch[1]}.ttf`; // trim the query parameter
const fontPath = path.join(path.dirname(module), relativeFontPath);
enqueue(fontPath);
return relativeFontPath;
}
const imagePath = path.join(path.dirname(module), url);
const fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath));
const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png';
let DATA = ';base64,' + fileContents.toString('base64');
if (!forceBase64 && /\.svg$/.test(url)) {
// .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris
let newText = fileContents.toString()
.replace(/"/g, '\'')
.replace(/</g, '%3C')
.replace(/>/g, '%3E')
.replace(/&/g, '%26')
.replace(/#/g, '%23')
.replace(/\s+/g, ' ');
let encodedData = ',' + newText;
if (encodedData.length < DATA.length) {
DATA = encodedData;
}
}
return '"data:' + MIME + DATA + '"';
});
}
function _replaceURL(contents: string, replacer: (url: string) => string): string {
// Use ")" as the terminator as quotes are oftentimes not used at all
return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => {
let url = matches[0];
// Eliminate starting quotes (the initial whitespace is not captured)
if (url.charAt(0) === '"' || url.charAt(0) === '\'') {
url = url.substring(1);
}
// The ending whitespace is captured
while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) {
url = url.substring(0, url.length - 1);
}
// Eliminate ending quotes
if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') {
url = url.substring(0, url.length - 1);
}
if (!_startsWith(url, 'data:') && !_startsWith(url, 'http://') && !_startsWith(url, 'https://')) {
url = replacer(url);
}
return 'url(' + url + ')';
});
}
function _startsWith(haystack: string, needle: string): boolean {
return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle;
}
}
@@ -0,0 +1,234 @@
{
"name": "code-oss-dev",
"version": "1.62.0",
"distro": "68b7698bb44bb140dfd35df9825ea6825edf87af",
"author": {
"name": "Microsoft Corporation"
},
"license": "MIT",
"main": "./out/main",
"private": true,
"scripts": {
"test": "mocha",
"test-browser": "node test/unit/browser/index.js",
"preinstall": "node build/npm/preinstall.js",
"postinstall": "node build/npm/postinstall.js",
"compile": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile",
"watch": "npm-run-all -lp watch-client watch-extensions",
"watchd": "deemon yarn watch",
"watch-webd": "deemon yarn watch-web",
"kill-watchd": "deemon --kill yarn watch",
"kill-watch-webd": "deemon --kill yarn watch-web",
"restart-watchd": "deemon --restart yarn watch",
"restart-watch-webd": "deemon --restart yarn watch-web",
"watch-client": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-client",
"watch-clientd": "deemon yarn watch-client",
"kill-watch-clientd": "deemon --kill yarn watch-client",
"watch-extensions": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-extensions watch-extension-media",
"watch-extensionsd": "deemon yarn watch-extensions",
"kill-watch-extensionsd": "deemon --kill yarn watch-extensions",
"mocha": "mocha test/unit/node/all.js --delay",
"precommit": "node build/hygiene.js",
"gulp": "node --max_old_space_size=8192 ./node_modules/gulp/bin/gulp.js",
"electron": "node build/lib/electron",
"7z": "7z",
"update-grammars": "node build/npm/update-all-grammars.js",
"update-localization-extension": "node build/npm/update-localization-extension.js",
"smoketest": "cd test/smoke && yarn compile && node test/index.js",
"smoketest-no-compile": "cd test/smoke && node test/index.js",
"download-builtin-extensions": "node build/lib/builtInExtensions.js",
"download-builtin-extensions-cg": "node build/lib/builtInExtensionsCG.js",
"monaco-compile-check": "tsc -p src/tsconfig.monaco.json --noEmit",
"tsec-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.tsec.json",
"vscode-dts-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.vscode-dts.json && node node_modules/tsec/bin/tsec -p src/tsconfig.vscode-proposed-dts.json",
"valid-layers-check": "node build/lib/layersChecker.js",
"update-distro": "node build/npm/update-distro.js",
"web": "node resources/web/code-web.js",
"compile-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-web",
"watch-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js watch-web",
"eslint": "node build/eslint",
"playwright-install": "node build/azure-pipelines/common/installPlaywright.js",
"compile-build": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-build",
"compile-extensions-build": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js compile-extensions-build",
"minify-vscode": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode",
"minify-vscode-reh": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode-reh",
"minify-vscode-reh-web": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode-reh-web",
"hygiene": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js hygiene",
"core-ci": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js core-ci",
"extensions-ci": "node --max_old_space_size=4095 ./node_modules/gulp/bin/gulp.js extensions-ci"
},
"dependencies": {
"@microsoft/applicationinsights-web": "^2.6.4",
"@parcel/watcher": "2.0.0",
"@vscode/sqlite3": "4.0.12",
"@vscode/vscode-languagedetection": "1.0.21",
"applicationinsights": "1.0.8",
"chokidar": "3.5.1",
"graceful-fs": "4.2.6",
"http-proxy-agent": "^2.1.0",
"https-proxy-agent": "^2.2.3",
"iconv-lite-umd": "0.6.8",
"jschardet": "3.0.0",
"keytar": "7.2.0",
"minimist": "^1.2.5",
"native-is-elevated": "0.4.3",
"native-keymap": "3.0.1",
"native-watchdog": "1.3.0",
"node-pty": "0.11.0-beta7",
"spdlog": "^0.13.0",
"sudo-prompt": "9.2.1",
"tas-client-umd": "0.1.4",
"v8-inspect-profiler": "^0.0.22",
"vscode-nsfw": "2.1.8",
"vscode-oniguruma": "1.5.1",
"vscode-proxy-agent": "^0.11.0",
"vscode-regexpp": "^3.1.0",
"vscode-ripgrep": "^1.12.1",
"vscode-textmate": "5.4.1",
"xterm": "4.15.0-beta.10",
"xterm-addon-search": "0.9.0-beta.5",
"xterm-addon-serialize": "0.7.0-beta.2",
"xterm-addon-unicode11": "0.3.0",
"xterm-addon-webgl": "0.12.0-beta.15",
"xterm-headless": "4.15.0-beta.10",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},
"devDependencies": {
"7zip": "0.0.6",
"@types/applicationinsights": "0.20.0",
"@types/chokidar": "2.1.3",
"@types/cookie": "^0.3.3",
"@types/copy-webpack-plugin": "^6.0.3",
"@types/cssnano": "^4.0.0",
"@types/debug": "4.1.5",
"@types/graceful-fs": "4.1.2",
"@types/gulp-postcss": "^8.0.0",
"@types/http-proxy-agent": "^2.0.1",
"@types/keytar": "^4.4.0",
"@types/minimist": "^1.2.1",
"@types/mocha": "^8.2.0",
"@types/node": "14.x",
"@types/sinon": "^10.0.2",
"@types/sinon-test": "^2.4.2",
"@types/trusted-types": "^1.0.6",
"@types/vscode-windows-registry": "^1.0.0",
"@types/webpack": "^4.41.25",
"@types/wicg-file-system-access": "^2020.9.2",
"@types/windows-foreground-love": "^0.3.0",
"@types/windows-mutex": "^0.4.0",
"@types/windows-process-tree": "^0.2.0",
"@types/winreg": "^1.2.30",
"@types/yauzl": "^2.9.1",
"@types/yazl": "^2.4.2",
"@typescript-eslint/eslint-plugin": "3.2.0",
"@typescript-eslint/parser": "^3.3.0",
"ansi-colors": "^3.2.3",
"asar": "^3.0.3",
"chromium-pickle-js": "^0.2.0",
"copy-webpack-plugin": "^6.0.3",
"cson-parser": "^1.3.3",
"css-loader": "^3.2.0",
"cssnano": "^4.1.11",
"debounce": "^1.0.0",
"deemon": "^1.4.0",
"electron": "13.5.1",
"eslint": "6.8.0",
"eslint-plugin-header": "3.1.1",
"eslint-plugin-jsdoc": "^19.1.0",
"event-stream": "3.3.4",
"fancy-log": "^1.3.3",
"fast-plist": "0.1.2",
"file-loader": "^4.2.0",
"glob": "^5.0.13",
"gulp": "^4.0.0",
"gulp-atom-electron": "1.32.0",
"gulp-azure-storage": "^0.11.1",
"gulp-bom": "^3.0.0",
"gulp-buffer": "0.0.2",
"gulp-concat": "^2.6.1",
"gulp-eslint": "^5.0.0",
"gulp-filter": "^5.1.0",
"gulp-flatmap": "^1.0.2",
"gulp-gunzip": "^1.0.0",
"gulp-gzip": "^1.4.2",
"gulp-json-editor": "^2.5.0",
"gulp-plumber": "^1.2.0",
"gulp-postcss": "^9.0.0",
"gulp-remote-retry-src": "^0.6.0",
"gulp-rename": "^1.2.0",
"gulp-replace": "^0.5.4",
"gulp-shell": "^0.6.5",
"gulp-sourcemaps": "^3.0.0",
"gulp-tsb": "4.0.6",
"gulp-untar": "^0.0.7",
"gulp-vinyl-zip": "^2.1.2",
"husky": "^0.13.1",
"innosetup": "6.0.5",
"is": "^3.1.0",
"istanbul-lib-coverage": "^3.0.0",
"istanbul-lib-instrument": "^4.0.0",
"istanbul-lib-report": "^3.0.0",
"istanbul-lib-source-maps": "^4.0.0",
"istanbul-reports": "^3.0.0",
"jsdom-no-contextify": "^3.1.0",
"lazy.js": "^0.4.2",
"merge-options": "^1.0.1",
"mime": "^1.4.1",
"minimatch": "^3.0.4",
"minimist": "^1.2.5",
"mkdirp": "^1.0.4",
"mocha": "^8.2.1",
"mocha-junit-reporter": "^2.0.0",
"mocha-multi-reporters": "^1.5.1",
"npm-run-all": "^4.1.5",
"opn": "^6.0.0",
"optimist": "0.3.5",
"p-all": "^1.0.0",
"path-browserify": "^1.0.1",
"playwright": "1.15.0",
"pump": "^1.0.1",
"queue": "3.0.6",
"rcedit": "^1.1.0",
"request": "^2.85.0",
"rimraf": "^2.2.8",
"sinon": "^11.1.1",
"sinon-test": "^3.1.0",
"source-map": "0.6.1",
"source-map-support": "^0.3.2",
"style-loader": "^1.0.0",
"ts-loader": "^9.2.3",
"tsec": "0.1.4",
"typescript": "^4.5.0-dev.20211021",
"typescript-formatter": "7.1.0",
"underscore": "^1.12.1",
"util": "^0.12.4",
"vinyl": "^2.0.0",
"vinyl-fs": "^3.0.0",
"vscode-debugprotocol": "1.48.0",
"vscode-nls-dev": "^3.3.1",
"vscode-telemetry-extractor": "^1.8.0",
"webpack": "^5.42.0",
"webpack-cli": "^4.7.2",
"webpack-stream": "^6.1.2",
"xml2js": "^0.4.17",
"yaserver": "^0.2.0"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/vscode.git"
},
"bugs": {
"url": "https://github.com/microsoft/vscode/issues"
},
"optionalDependencies": {
"vscode-windows-registry": "1.0.3",
"windows-foreground-love": "0.4.0",
"windows-mutex": "0.4.1",
"windows-process-tree": "^0.3.2"
},
"resolutions": {
"elliptic": "^6.5.3",
"nwmatcher": "^1.4.4"
}
}
@@ -0,0 +1,362 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------------------------
*---------------------------------------------------------------------------------------------
*---------------------------------------------------------------------------------------------
*---------------------------------------------------------------------------------------------
*---------------------------------------------------------------------------------------------
* Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/
*---------------------------------------------------------------------------------------------
*---------------------------------------------------------------------------------------------
*---------------------------------------------------------------------------------------------
*---------------------------------------------------------------------------------------------
*--------------------------------------------------------------------------------------------*/
'use strict';
var _cssPluginGlobal = this;
var CSSBuildLoaderPlugin;
(function (CSSBuildLoaderPlugin) {
var global = (_cssPluginGlobal || {});
/**
* Known issue:
* - In IE there is no way to know if the CSS file loaded successfully or not.
*/
var BrowserCSSLoader = /** @class */ (function () {
function BrowserCSSLoader() {
this._pendingLoads = 0;
}
BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) {
var unbind = function () {
linkNode.removeEventListener('load', loadEventListener);
linkNode.removeEventListener('error', errorEventListener);
};
var loadEventListener = function (e) {
unbind();
callback();
};
var errorEventListener = function (e) {
unbind();
errorback(e);
};
linkNode.addEventListener('load', loadEventListener);
linkNode.addEventListener('error', errorEventListener);
};
BrowserCSSLoader.prototype._onLoad = function (name, callback) {
this._pendingLoads--;
callback();
};
BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) {
this._pendingLoads--;
errorback(err);
};
BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) {
this._pendingLoads++;
var head = document.head || document.getElementsByTagName('head')[0];
var other = head.getElementsByTagName('link') || head.getElementsByTagName('script');
if (other.length > 0) {
head.insertBefore(linkNode, other[other.length - 1]);
}
else {
head.appendChild(linkNode);
}
};
BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) {
var _this = this;
var linkNode = document.createElement('link');
linkNode.setAttribute('rel', 'stylesheet');
linkNode.setAttribute('type', 'text/css');
linkNode.setAttribute('data-name', name);
var callback = function () { return _this._onLoad(name, externalCallback); };
var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); };
this.attachListeners(name, linkNode, callback, errorback);
linkNode.setAttribute('href', cssUrl);
return linkNode;
};
BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) {
var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link');
for (i = 0, len = links.length; i < len; i++) {
nameAttr = links[i].getAttribute('data-name');
hrefAttr = links[i].getAttribute('href');
if (nameAttr === name || hrefAttr === cssUrl) {
return true;
}
}
return false;
};
BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) {
if (this._linkTagExists(name, cssUrl)) {
externalCallback();
return;
}
var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback);
this._insertLinkNode(linkNode);
};
return BrowserCSSLoader;
}());
var NodeCSSLoader = /** @class */ (function () {
function NodeCSSLoader() {
this.fs = require.nodeRequire('fs');
}
NodeCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) {
var contents = this.fs.readFileSync(cssUrl, 'utf8');
// Remove BOM
if (contents.charCodeAt(0) === NodeCSSLoader.BOM_CHAR_CODE) {
contents = contents.substring(1);
}
externalCallback(contents);
};
NodeCSSLoader.BOM_CHAR_CODE = 65279;
return NodeCSSLoader;
}());
// ------------------------------ Finally, the plugin
var CSSPlugin = /** @class */ (function () {
function CSSPlugin(cssLoader) {
this.cssLoader = cssLoader;
}
CSSPlugin.prototype.load = function (name, req, load, config) {
config = config || {};
var myConfig = config['vs/css'] || {};
global.inlineResources = myConfig.inlineResources;
global.inlineResourcesLimit = myConfig.inlineResourcesLimit || 5000;
var cssUrl = req.toUrl(name + '.css');
this.cssLoader.load(name, cssUrl, function (contents) {
// Contents has the CSS file contents if we are in a build
if (config.isBuild) {
CSSPlugin.BUILD_MAP[name] = contents;
CSSPlugin.BUILD_PATH_MAP[name] = cssUrl;
}
load({});
}, function (err) {
if (typeof load.error === 'function') {
load.error('Could not find ' + cssUrl + ' or it was empty');
}
});
};
CSSPlugin.prototype.write = function (pluginName, moduleName, write) {
// getEntryPoint is a Monaco extension to r.js
var entryPoint = write.getEntryPoint();
// r.js destroys the context of this plugin between calling 'write' and 'writeFile'
// so the only option at this point is to leak the data to a global
global.cssPluginEntryPoints = global.cssPluginEntryPoints || {};
global.cssPluginEntryPoints[entryPoint] = global.cssPluginEntryPoints[entryPoint] || [];
global.cssPluginEntryPoints[entryPoint].push({
moduleName: moduleName,
contents: CSSPlugin.BUILD_MAP[moduleName],
fsPath: CSSPlugin.BUILD_PATH_MAP[moduleName],
});
write.asModule(pluginName + '!' + moduleName, 'define([\'vs/css!' + entryPoint + '\'], {});');
};
CSSPlugin.prototype.writeFile = function (pluginName, moduleName, req, write, config) {
if (global.cssPluginEntryPoints && global.cssPluginEntryPoints.hasOwnProperty(moduleName)) {
var fileName = req.toUrl(moduleName + '.css');
var contents = [
'/*---------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' *--------------------------------------------------------*/'
], entries = global.cssPluginEntryPoints[moduleName];
for (var i = 0; i < entries.length; i++) {
if (global.inlineResources) {
contents.push(Utilities.rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, global.inlineResources === 'base64', global.inlineResourcesLimit));
}
else {
contents.push(Utilities.rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents));
}
}
write(fileName, contents.join('\r\n'));
}
};
CSSPlugin.prototype.getInlinedResources = function () {
return global.cssInlinedResources || [];
};
CSSPlugin.BUILD_MAP = {};
CSSPlugin.BUILD_PATH_MAP = {};
return CSSPlugin;
}());
CSSBuildLoaderPlugin.CSSPlugin = CSSPlugin;
var Utilities = /** @class */ (function () {
function Utilities() {
}
Utilities.startsWith = function (haystack, needle) {
return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle;
};
/**
* Find the path of a file.
*/
Utilities.pathOf = function (filename) {
var lastSlash = filename.lastIndexOf('/');
if (lastSlash !== -1) {
return filename.substr(0, lastSlash + 1);
}
else {
return '';
}
};
/**
* A conceptual a + b for paths.
* Takes into account if `a` contains a protocol.
* Also normalizes the result: e.g.: a/b/ + ../c => a/c
*/
Utilities.joinPaths = function (a, b) {
function findSlashIndexAfterPrefix(haystack, prefix) {
if (Utilities.startsWith(haystack, prefix)) {
return Math.max(prefix.length, haystack.indexOf('/', prefix.length));
}
return 0;
}
var aPathStartIndex = 0;
aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//');
aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://');
aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://');
function pushPiece(pieces, piece) {
if (piece === './') {
// Ignore
return;
}
if (piece === '../') {
var prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null);
if (prevPiece && prevPiece === '/') {
// Ignore
return;
}
if (prevPiece && prevPiece !== '../') {
// Pop
pieces.pop();
return;
}
}
// Push
pieces.push(piece);
}
function push(pieces, path) {
while (path.length > 0) {
var slashIndex = path.indexOf('/');
var piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path);
path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : '');
pushPiece(pieces, piece);
}
}
var pieces = [];
push(pieces, a.substr(aPathStartIndex));
if (b.length > 0 && b.charAt(0) === '/') {
pieces = [];
}
push(pieces, b);
return a.substring(0, aPathStartIndex) + pieces.join('');
};
Utilities.commonPrefix = function (str1, str2) {
var len = Math.min(str1.length, str2.length);
for (var i = 0; i < len; i++) {
if (str1.charCodeAt(i) !== str2.charCodeAt(i)) {
break;
}
}
return str1.substring(0, i);
};
Utilities.commonFolderPrefix = function (fromPath, toPath) {
var prefix = Utilities.commonPrefix(fromPath, toPath);
var slashIndex = prefix.lastIndexOf('/');
if (slashIndex === -1) {
return '';
}
return prefix.substring(0, slashIndex + 1);
};
Utilities.relativePath = function (fromPath, toPath) {
if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) {
return toPath;
}
// Ignore common folder prefix
var prefix = Utilities.commonFolderPrefix(fromPath, toPath);
fromPath = fromPath.substr(prefix.length);
toPath = toPath.substr(prefix.length);
var upCount = fromPath.split('/').length;
var result = '';
for (var i = 1; i < upCount; i++) {
result += '../';
}
return result + toPath;
};
Utilities._replaceURL = function (contents, replacer) {
// Use ")" as the terminator as quotes are oftentimes not used at all
return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, function (_) {
var matches = [];
for (var _i = 1; _i < arguments.length; _i++) {
matches[_i - 1] = arguments[_i];
}
var url = matches[0];
// Eliminate starting quotes (the initial whitespace is not captured)
if (url.charAt(0) === '"' || url.charAt(0) === '\'') {
url = url.substring(1);
}
// The ending whitespace is captured
while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) {
url = url.substring(0, url.length - 1);
}
// Eliminate ending quotes
if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') {
url = url.substring(0, url.length - 1);
}
if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) {
url = replacer(url);
}
return 'url(' + url + ')';
});
};
Utilities.rewriteUrls = function (originalFile, newFile, contents) {
return this._replaceURL(contents, function (url) {
var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url);
return Utilities.relativePath(newFile, absoluteUrl);
});
};
Utilities.rewriteOrInlineUrls = function (originalFileFSPath, originalFile, newFile, contents, forceBase64, inlineByteLimit) {
var fs = require.nodeRequire('fs');
var path = require.nodeRequire('path');
return this._replaceURL(contents, function (url) {
if (/\.(svg|png)$/.test(url)) {
var fsPath = path.join(path.dirname(originalFileFSPath), url);
var fileContents = fs.readFileSync(fsPath);
if (fileContents.length < inlineByteLimit) {
global.cssInlinedResources = global.cssInlinedResources || [];
var normalizedFSPath = fsPath.replace(/\\/g, '/');
if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) {
// console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES');
}
global.cssInlinedResources.push(normalizedFSPath);
var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png';
var DATA = ';base64,' + fileContents.toString('base64');
if (!forceBase64 && /\.svg$/.test(url)) {
// .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris
var newText = fileContents.toString()
.replace(/"/g, '\'')
.replace(/</g, '%3C')
.replace(/>/g, '%3E')
.replace(/&/g, '%26')
.replace(/#/g, '%23')
.replace(/\s+/g, ' ');
var encodedData = ',' + newText;
if (encodedData.length < DATA.length) {
DATA = encodedData;
}
}
return '"data:' + MIME + DATA + '"';
}
}
var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url);
return Utilities.relativePath(newFile, absoluteUrl);
});
};
return Utilities;
}());
CSSBuildLoaderPlugin.Utilities = Utilities;
(function () {
var cssLoader = null;
var isElectron = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions['electron'] !== 'undefined');
if (typeof process !== 'undefined' && process.versions && !!process.versions.node && !isElectron) {
cssLoader = new NodeCSSLoader();
}
else {
cssLoader = new BrowserCSSLoader();
}
define('vs/css', new CSSPlugin(cssLoader));
})();
})(CSSBuildLoaderPlugin || (CSSBuildLoaderPlugin = {}));
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,49 @@
# SITE-07 Monaco CSS 来源、压缩与样式回归
修改前 HEAD:23ac6f71284547a5e4dbf6cdbf0966f6cef49c87。
## 源码与结构
新增 css.ts 和 reproduce-css.ts,分开处理注册顺序、原始 CSS 运输/拼接配方与
归档/锁/压缩验证。68 份样式从固定 VS Code commit 恢复,连同 3 个内联图片、
字体、配方、锁和许可共 81 个 Git 成员验证 blob/SHA-256。原始 transportCSS
函数由 AST 定位后在受限读取环境执行;每份准备样式均与 core ESM 产物相等。
64 份原文不变,另外四份涉及字体查询参数、URL 引号及 PNG/SVG 内联。字体复制
字节也与归档一致。此处 VM 是隔离辅助,不作为安全沙箱保证。
按实际 AMD 注册次序拼接,不能用模块名表的排列替代。原 CSS 插件生成的 70 个
CRLF 分隔符转换为发布包实际 LF,再加原版本头,完整开发文件 114,942 字节精确
匹配。这项发布换行归一化的具体原流水线位置尚未确认,因此记录为有证据的
字节转换,不声称未经改造地重跑了整个上游发布命令。
## 工具链与兼容
cssnano 4.1.11 / PostCSS 7.0.35 按原 preset: default 压缩后,完整 72,052 字节
产物匹配。复用 Yarn Classic 1.22.22:在新的忽略目录中按原始 lock 冻结安装,
禁用安装脚本,锁内容保持一致;153 个依赖选择器、143 个版本均有原 registry
integrity。没有改变项目依赖或根 yarn.lock。默认命令使用离线缓存,--fetch
重新取回源归档并允许安装访问 registry。
最初隔离工具继承父项目 Chrome-only Browserslist,导致 transparent 被压成
initial。固定历史默认目标后恢复全部字节,显式拒绝环境覆盖。历史 caniuse
数据过期提示不通过更新数据消除。原 archive 的 LICENSE.txt 按 .gitattributes
使用 CRLF,Git blob 为 LF;仅对该成员的 Git 身份检查转换,原许可全文保留。
站点 CSS 为 72,057 字节,精确保留现有五个头部 CRLF 差异;不改变运行代码、
CSS/DOM hooks、worker 或资源 URL,没有扩大消费者最低浏览器要求。
## 验证、后续与回退
25/25 单元、严格 docs-tools 类型及 lint 通过;来源/完整开发文件/压缩文件的
离线与联网复验通过。联网第一次遇到 codeload.github.com DNS ENOTFOUND,保留
失败记录后重试。三引擎样式回归 3/3:明暗主题、布局、查找面板边界、字体载入
与真实 codicon 字形、CSS/font HTTP、缩放及模型销毁。初始测试误把浅色背景
写成纯白,核实原 colorRegistry 的 #fffffe 后修正预期,未改生产颜色。
详细环境/产物/报告指纹见[验证记录](../baselines/monaco-css-build-validation.json),
维护入口见[Monaco README](../../scripts/site-vendor/monaco/README.md)。本批未改变
站点 notices;完整原始 core TS 编译、其余内嵌来源和 SITE-07 其他许可交付范围
继续开放,未以浏览器样式测试替代设备、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) [记录](baselines/monaco-contributions-provenance.json) [记录](baselines/monaco-contributions-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-contributions.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) [记录](baselines/monaco-css-build-provenance.json) [记录](baselines/monaco-css-build-validation.json) [记录](changes/2026-09-15-SITE-07-monaco-css.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)
+11
View File
@@ -1,5 +1,16 @@
# 进度与证据
## SITE-07 Monaco CSS 来源与样式回归
68 份样式、3 个内联图片和字体按原配方恢复,81 个 Git 成员验证;显式记录
70 个生成 CRLF 到发布 LF 的转换后,完整开发 CSS 匹配。历史 cssnano/PostCSS
及冻结 Yarn 安装复现完整压缩 CSS,站点五个头部 CRLF 保留。25/25 单元、
严格类型/lint、联网重试/离线复验和三引擎主题/字体/布局回归 3/3 通过。
见[记录](changes/2026-09-15-SITE-07-monaco-css.md)。该换行归一化的原发布步骤
尚未定位;其余内嵌来源与完整原始 core TS 编译仍开放。SITE-07/VENDOR-06
保持未完结,199/265;Thumbnail 默认兼容策略仍待答。
## SITE-07 Monaco 完整贡献模块装配
六个 npm 归档和 102 个 Git 成员支撑 88 个 TS 注册模块,原 RequireJS/Terser
+5 -2
View File
@@ -655,11 +655,14 @@
"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"
"refactor/changes/2026-09-15-SITE-07-monaco-contributions.md",
"refactor/baselines/monaco-css-build-provenance.json",
"refactor/baselines/monaco-css-build-validation.json",
"refactor/changes/2026-09-15-SITE-07-monaco-css.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, 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."
"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. CSS now reproduces from 68 styles, three embedded images and the copied font, with 81 verified Git members. The archived transport/plugin recipes match every prepared ESM stylesheet; explicit normalization of 70 generated CRLF separators reproduces the entire development CSS. cssnano 4.1.11/PostCSS 7.0.35 reproduce complete minified CSS using isolated frozen Yarn installs (153 selectors/143 versions) and historical browser defaults. The site retains its five header CRLF differences. The exact upstream stage that normalized the 70 development separators is still unidentified. Latest units 25/25, strict type/lint and offline/network checks pass; new three-engine style/theme/font/resize regressions pass 3/3. Original core TS compilation and remaining embedded-origin review stay open; this is not release approval or an exhaustive sanitizer audit."
},
{
"id": "VENDOR-07",
+4 -1
View File
@@ -4537,7 +4537,10 @@
"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"
"changes/2026-09-15-SITE-07-monaco-contributions.md",
"baselines/monaco-css-build-provenance.json",
"baselines/monaco-css-build-validation.json",
"changes/2026-09-15-SITE-07-monaco-css.md"
]
},
{
+48
View File
@@ -277,3 +277,51 @@ 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.
## CSS sources and historical compression
`reproduce-css.ts` separately covers all 68 core stylesheets. The AMD string table
does not give their assembly order: `coreStyleOrder` follows actual registrations.
81 fixed VS Code Git members include styles, three inline images, the codicon font,
original transport/build recipes, package/lock and license. Original `transportCSS`
is extracted syntactically from the verified recipe and executed with reads limited
to verified inputs. It removes the font query, normalizes URL quotes and embeds the
images. Every prepared result equals the corresponding core ESM archive member;
the copied font also matches. This harness does not run the full VS Code build.
The original CSS plugin rewrites paths and joins 70 separators with CRLF. The npm
development CSS is LF-only. That publication normalization is explicit and counted;
its exact upstream pipeline stage has not been established. After the fixed release
header and this documented transformation, the whole 114,942-byte development file
matches. This is source/output equivalence with an observed newline adaptation,
not an assertion that the original publishing command was reproduced unchanged.
Historical cssnano 4.1.11 and PostCSS 7.0.35 reproduce the complete 72,052-byte
minified file. Each run creates a fresh ignored tool directory, writes the fixed
historical lock, installs with Yarn 1.22.22 `--frozen-lockfile --ignore-scripts`,
and checks that the lock remains identical. The recorded closure has 153 selectors
and 143 package versions, all with registry integrity. `--fetch` downloads/verifies
source archives and permits registry access; the default uses cached archives and
an offline frozen install. No project dependency or root lock changes are needed.
The private manifest explicitly selects historical Browserslist defaults to prevent
the parent project's `last 1 Chrome version` target leaking into the archived build.
That leakage changes `transparent` to `initial`. Ambient BROWSERSLIST/config/env
overrides are rejected. Do not update historical caniuse data to silence its age
warning: these inputs exist only to reproduce the pinned asset. VS Code's archive
also obeys `LICENSE.txt eol=crlf`; only that member is converted to LF for its Git
blob check, while its frozen license bytes stay intact.
The site's existing 72,057-byte CSS differs solely by five CRLF header newlines;
the verifier requires that exact transformation and leaves the asset unchanged.
```sh
yarn verify:monaco-css --fetch
yarn verify:monaco-css
yarn test:browser test/browser/editor-styles.spec.js --workers=1
```
Browser checks cover light/dark theme colors, layout and resize, find-widget bounds,
successful CSS/font requests, loaded codicon glyphs and model disposal in all three
engines. The pinned light theme is intentionally #fffffe. Full original core TS
compilation and further embedded-origin review remain separate open work.
+105
View File
@@ -0,0 +1,105 @@
import type { Buffer } from 'node:buffer'
import assert from 'node:assert/strict'
import path from 'node:path'
import vm from 'node:vm'
import ts from 'typescript'
export function coreStyleOrder(source: string): string[] {
const ast = ts.createSourceFile('core.js', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS)
const tables = new Map<string, string[]>()
const visitTables = (node: ts.Node) => {
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer && ts.isArrayLiteralExpression(node.initializer)
&& node.initializer.elements.every(item => ts.isStringLiteral(item))) { tables.set(node.name.text, node.initializer.elements.map(item => (item as ts.StringLiteral).text)) }
ts.forEachChild(node, visitTables)
}
visitTables(ast)
const styles: string[] = []
const visitCalls = (node: ts.Node) => {
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === 'define') {
const name = node.arguments[0]
if (name && ts.isElementAccessExpression(name) && ts.isIdentifier(name.expression) && ts.isNumericLiteral(name.argumentExpression)) {
const id = tables.get(name.expression.text)?.[Number(name.argumentExpression.text)]
if (id?.startsWith('vs/css!') && id !== 'vs/css!vs/editor/editor.main')
styles.push(id.slice('vs/css!'.length))
}
}
ts.forEachChild(node, visitCalls)
}
visitCalls(ast)
assert(styles.length && new Set(styles).size === styles.length, 'Missing or repeated core CSS modules')
return styles
}
interface PreparedStyle { id: string, contents: string, moduleName: string, fsPath: string }
interface CssPlugin {
writeFile: (plugin: string, entry: string, request: { toUrl: (name: string) => string }, write: (file: string, source: string) => void, config: object) => void
}
// Execute only the pinned transport function and CSS plugin, with reads restricted
// to the caller's verified source map. VM is an isolation aid, not a security sandbox.
export function prepareCoreStyles(transportRecipe: string, cssRecipe: string, ids: string[], read: (file: string) => Buffer) {
const ast = ts.createSourceFile('standalone.ts', transportRecipe, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
const functions = ast.statements.filter(node => ts.isFunctionDeclaration(node) && node.name?.text === 'transportCSS')
assert.equal(functions.length, 1, 'Expected one transportCSS function')
const code = ts.transpileModule(functions[0]!.getText(ast), { compilerOptions: { target: ts.ScriptTarget.ES2020, module: ts.ModuleKind.None } }).outputText
const assets = new Set<string>()
const context = vm.createContext({
SRC_DIR: '/source',
path: path.posix,
fs: { readFileSync: (file: string) => {
assert(file.startsWith('/source/') && !file.split('/').includes('..'), 'CSS source escaped verified inputs')
const source = `src/${file.slice('/source/'.length)}`
assets.add(source)
return read(source)
} },
})
vm.runInContext(code, context, { timeout: 1000 })
const transport = context.transportCSS as (module: string, enqueue: (font: string) => void, write: (file: string, contents: string) => void) => boolean
const fonts = new Set<string>()
const prepared: PreparedStyle[] = ids.map((id) => {
assert(/^vs\/[\w./-]+$/.test(id) && !id.split('/').includes('..'), 'Invalid CSS module')
let contents: string | undefined
assert(transport(`${id}.css`, font => fonts.add(font), (file, source) => {
assert.equal(file, `${id}.css`)
assert.equal(contents, undefined, 'Repeated CSS output')
contents = source
}), 'CSS transport rejected input')
assert(typeof contents === 'string', 'Missing transported CSS')
return { id, contents, moduleName: id, fsPath: `/source/${id}.css` }
})
let plugin: CssPlugin | undefined
const build = vm.createContext({
process: { versions: { node: '24' } },
require: { nodeRequire: (name: string) => {
if (name === 'path')
return path.posix
assert.equal(name, 'fs', 'Unexpected CSS build import')
return {
readFileSync: () => { throw new Error('Unprepared CSS resource') },
}
} },
define: (name: string, value: CssPlugin) => {
assert.equal(name, 'vs/css')
assert.equal(plugin, undefined)
plugin = value
},
inlineResources: true,
inlineResourcesLimit: 5000,
cssPluginEntryPoints: { 'vs/editor/editor.main': prepared },
})
vm.runInContext(cssRecipe, build, { timeout: 1000 })
assert(plugin, 'Missing CSS build plugin')
let joined: string | undefined
plugin.writeFile('vs/css', 'vs/editor/editor.main', { toUrl: name => name }, (file, source) => {
assert.equal(file, 'vs/editor/editor.main.css')
assert.equal(joined, undefined)
joined = source
}, {})
assert(typeof joined === 'string', 'Missing CSS bundle')
return { prepared, assets: [...assets], fonts: [...fonts], joined, normalizedSeparators: (joined.match(/\r\n/g) || []).length }
}
export function coreCssHeader(commit: string): string {
assert(/^[a-f0-9]{40}$/.test(commit))
return `/*!-----------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Version: 0.30.1(${commit})\n * Released under the MIT license\n * https://github.com/microsoft/vscode/blob/main/LICENSE.txt\n *-----------------------------------------------------------*/\n`
}
+141
View File
@@ -0,0 +1,141 @@
import type { Archive, Member } from './archives.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 { coreCssHeader, coreStyleOrder, prepareCoreStyles } from './css.ts'
interface Selector { selector: string, name: string, version: string, resolved: string, integrity: string }
interface LockedDependency {
version: string
resolved?: string
integrity?: string
dependencies?: Record<string, string>
optionalDependencies?: Record<string, string>
}
interface Provenance {
commit: string
archives: Archive[]
sourceArchive: { url: string, file: string, bytes: number, sha256: string }
members: { path: string, member: string, sha256: string, gitBlobSha: string, gitNormalizeCRLF?: boolean, frozen?: string }[]
tooling: { manifest: object, selectors: Selector[] }
coreJavaScript: Member
styles: { id: string, prepared: Member }[]
fonts: string[]
normalizedSeparators: number
development: Member
minified: Member
editor: Member
target: { path: string, sha256: string, crlf: number }
}
const root = fileURLToPath(new URL('../../../', import.meta.url))
assert(process.argv.slice(2).every(arg => arg === '--fetch'), 'Use yarn verify:monaco-css [--fetch]')
assert.equal(process.version, `v${fs.readFileSync(path.join(root, '.node-version'), 'utf8').trim()}`, 'Use canonical Node')
const yarn = process.env.npm_execpath
assert(yarn && process.env.npm_config_user_agent?.split(' ')[0] === 'yarn/1.22.22', 'Use Yarn Classic 1.22.22')
assert.equal(execFileSync(process.execPath, [yarn, '--version'], { encoding: 'utf8' }).trim(), '1.22.22')
for (const variable of ['BROWSERSLIST', 'BROWSERSLIST_CONFIG', 'BROWSERSLIST_ENV'])
assert(!process.env[variable], `Historical CSS target must not inherit ${variable}`)
const record: Provenance = JSON.parse(fs.readFileSync(path.join(root, 'refactor/baselines/monaco-css-build-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, 'vscode-source.tgz')
assert.equal(record.sourceArchive.url, `https://codeload.github.com/microsoft/vscode/tar.gz/${record.commit}`)
const archivePath = 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, `VS Code archive HTTP ${response.status}`)
archive = Buffer.from(await response.arrayBuffer())
}
else {
archive = fs.readFileSync(archivePath)
}
assert.equal(archive.length, record.sourceArchive.bytes)
assert.equal(hash(archive), record.sourceArchive.sha256, 'VS Code archive changed')
if (online)
fs.writeFileSync(archivePath, archive)
const sources = new Map<string, Buffer>()
for (const member of record.members) {
assert.equal(member.member, `vscode-${record.commit}/${member.path}`)
assert(!member.path.split('/').includes('..'), 'Invalid VS Code source')
const bytes = execFileSync('tar', ['-xOzf', archivePath, member.member], { maxBuffer: 5 * 1024 * 1024 })
assert.equal(hash(bytes), member.sha256, `Source changed: ${member.path}`)
assert.equal(Boolean(member.gitNormalizeCRLF), member.path === 'LICENSE.txt', 'Unexpected Git line-ending adaptation')
const gitBytes = member.gitNormalizeCRLF ? Buffer.from(bytes.toString('utf8').replace(/\r\n/g, '\n')) : bytes
assert.equal(createHash('sha1').update(`blob ${gitBytes.length}\0`).update(gitBytes).digest('hex'), member.gitBlobSha, 'Git blob changed')
if (member.frozen)
assert.deepEqual(fs.readFileSync(path.join(root, member.frozen)), bytes, 'Frozen CSS recipe changed')
assert(!sources.has(member.path), 'Duplicate CSS source')
sources.set(member.path, bytes)
}
function read(file: string): Buffer {
const source = sources.get(file)
assert(source, `Unverified CSS source: ${file}`)
return source
}
const ids = coreStyleOrder(cache.member(record.coreJavaScript).toString('utf8'))
assert(read('.gitattributes').toString('utf8').includes('LICENSE.txt eol=crlf'), 'Unexplained archive license line endings')
assert.deepEqual(ids, record.styles.map(style => style.id))
assert.equal(ids.length, 68)
const styles = prepareCoreStyles(read('build/lib/standalone.ts').toString('utf8'), read('src/vs/css.build.js').toString('utf8'), ids, read)
for (const [index, style] of styles.prepared.entries())
assert(Buffer.from(style.contents).equals(cache.member(record.styles[index]!.prepared)), `Prepared CSS differs: ${style.id}`)
assert.deepEqual(styles.fonts, record.fonts)
for (const font of styles.fonts)
assert.deepEqual(read(`src/${font}`), cache.read('monaco-editor-core-0.30.1', `package/esm/${font}`), 'Copied font differs')
assert.equal(styles.normalizedSeparators, record.normalizedSeparators)
assert.equal(styles.normalizedSeparators, 70)
// The archived dev CSS is LF-only; the fixed plugin joins 70 separators with CRLF.
// Record this observed publication normalization instead of ignoring whitespace.
const development = `${coreCssHeader(record.commit)}\n${styles.joined}`.replace(/\r\n/g, '\n')
assert(Buffer.from(development).equals(cache.member(record.development)), 'Complete CSS development bundle differs')
// The existing Yarn lock parser has no declarations; keep its used boundary local.
const lockfile = createRequire(import.meta.url)('@yarnpkg/lockfile') as { parse: (source: string) => { type: string, object: Record<string, LockedDependency> } }
const parsed = lockfile.parse(read('yarn.lock').toString('utf8'))
assert.equal(parsed.type, 'success')
const closure = new Map<string, Selector>()
function visit(selector: string) {
if (closure.has(selector))
return
const dependency = parsed.object[selector]
assert(dependency?.resolved && dependency.integrity, `Unpinned historical CSS dependency: ${selector}`)
closure.set(selector, { selector, name: selector.slice(0, selector.lastIndexOf('@')), version: dependency.version, resolved: dependency.resolved, integrity: dependency.integrity })
for (const [name, range] of Object.entries({ ...dependency.dependencies, ...dependency.optionalDependencies }))
visit(`${name}@${range}`)
}
visit('cssnano@^4.1.11')
assert.deepEqual([...closure.values()], record.tooling.selectors)
assert.equal(JSON.parse(read('package.json').toString('utf8')).devDependencies.cssnano, '^4.1.11')
const manifest = { name: 'monaco-css-historical-tools', private: true, browserslist: ['defaults'], dependencies: { cssnano: '^4.1.11' } }
assert.deepEqual(manifest, record.tooling.manifest)
const compiler = fs.mkdtempSync(path.join(cache.directory, 'css-build-'))
fs.writeFileSync(path.join(compiler, 'package.json'), `${JSON.stringify(manifest, null, 2)}\n`)
fs.writeFileSync(path.join(compiler, 'yarn.lock'), read('yarn.lock'))
const install = execFileSync(process.execPath, [yarn, 'install', '--frozen-lockfile', '--ignore-scripts', '--non-interactive', ...online ? [] : ['--offline']], { cwd: compiler, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 })
fs.writeFileSync(path.join(cache.directory, 'css-install-last.log'), install)
assert.deepEqual(fs.readFileSync(path.join(compiler, 'yarn.lock')), read('yarn.lock'), 'Frozen historical lock changed')
const require = createRequire(path.join(compiler, 'entry.cjs'))
const cssnanoPath = require.resolve('cssnano/package.json')
const cssRequire = createRequire(cssnanoPath)
assert.equal(require(cssnanoPath).version, '4.1.11')
assert.equal(cssRequire('postcss/package.json').version, '7.0.35')
const minified: { css: string } = await cssRequire('postcss')([require('cssnano')({ preset: 'default' })]).process(development, { from: path.join(compiler, 'editor.main.css'), map: false })
assert(Buffer.from(minified.css).equals(cache.member(record.minified)), 'Complete CSS minification differs')
assert.deepEqual(cache.member(record.minified), cache.member(record.editor), 'Editor CSS archive differs')
const target = fs.readFileSync(path.join(root, record.target.path))
assert.equal(hash(target), record.target.sha256, 'Site CSS changed')
assert.equal((target.toString('utf8').match(/\r\n/g) || []).length, record.target.crlf)
assert.equal(record.target.crlf, 5)
assert(Buffer.from(minified.css.replace(/\n/g, '\r\n')).equals(target), 'Site CSS differs beyond five header newlines')
console.log(JSON.stringify({ gitSources: sources.size, styles: ids.length, inlinedImages: styles.assets.filter(file => !file.endsWith('.css')), fonts: styles.fonts, dependencySelectors: closure.size, normalizedSeparators: styles.normalizedSeparators, developmentBytes: Buffer.byteLength(development), minifiedBytes: Buffer.byteLength(minified.css), siteBytes: target.length, exactDevelopment: true, exactMinification: true, preservedSiteCrlf: 5, compiler: { cssnano: '4.1.11', postcss: '7.0.35' }, originalCoreTypeScriptCompiled: false }))
+68
View File
@@ -0,0 +1,68 @@
import { expect, test } from './fixtures.js'
test('docs Monaco styles retain layout, themes and loaded codicon glyphs', async ({ page }, testInfo) => {
const assets = []
page.on('response', (response) => {
if (/\/(?:editor\.main\.css|codicon\.ttf)$/.test(response.url()))
assets.push({ url: response.url(), status: response.status() })
})
await page.goto('/test/player.html?core=candidate&chapter=published')
await page.addScriptTag({ url: '/assets/js/vs/loader.js' })
await page.evaluate(async () => {
window.require.config({ paths: { vs: '/assets/js/vs' } })
await new Promise((resolve, reject) => window.require(['vs/editor/editor.main'], resolve, reject))
const host = document.createElement('div')
host.style.cssText = 'width:640px;height:320px'
document.body.appendChild(host)
const editor = window.monaco.editor.create(host, { value: 'first\nsecond\nfirst', language: 'plaintext', theme: 'vs-dark', minimap: { enabled: false } })
window.styleProbe = { host, editor, model: editor.getModel() }
})
const editor = page.locator('.monaco-editor').first()
const evidence = []
try {
await expect(editor).toBeVisible()
await expect(editor).toHaveCSS('position', 'relative')
// The pinned colorRegistry deliberately uses #fffffe for the light theme.
for (const [theme, color] of [['vs-dark', 'rgb(30, 30, 30)'], ['vs', 'rgb(255, 255, 254)']]) {
await page.evaluate(theme => window.monaco.editor.setTheme(theme), theme)
await expect(editor).toHaveCSS('background-color', color)
evidence.push({ theme, color })
}
await page.evaluate(() => window.styleProbe.editor.getAction('actions.find').run())
const find = page.locator('.find-widget')
await expect(find).toBeVisible()
const font = await page.evaluate(async () => {
const loaded = await document.fonts.load('16px codicon')
const icon = window.styleProbe.host.querySelector('.find-widget .codicon')
const style = getComputedStyle(icon, '::before')
return { loaded: loaded.map(font => ({ family: font.family, status: font.status })), family: style.fontFamily, content: style.content }
})
expect(font.loaded.length).toBeGreaterThan(0)
expect(font.loaded.every(item => item.status === 'loaded')).toBe(true)
expect(font.family).toContain('codicon')
expect(['none', 'normal', '""']).not.toContain(font.content)
const hostBox = await editor.boundingBox()
const findBox = await find.boundingBox()
expect(findBox.x).toBeGreaterThanOrEqual(hostBox.x)
expect(findBox.x + findBox.width).toBeLessThanOrEqual(hostBox.x + hostBox.width + 1)
await page.evaluate(() => {
window.styleProbe.host.style.cssText = 'width:420px;height:240px'
window.styleProbe.editor.layout()
})
await expect(editor).toHaveCSS('width', '420px')
await expect(editor).toHaveCSS('height', '240px')
expect(assets.filter(asset => asset.url.endsWith('/editor.main.css'))).toHaveLength(1)
expect(assets.some(asset => asset.url.endsWith('/codicon.ttf'))).toBe(true)
expect(assets.every(asset => asset.status === 200)).toBe(true)
await testInfo.attach('monaco-style-results', { contentType: 'application/json', body: JSON.stringify({ themes: evidence, font, hostBox, findBox, assets, resized: { width: 420, height: 240 } }) })
}
finally {
await page.evaluate(() => {
const { host, editor, model } = window.styleProbe
editor.dispose()
model.dispose()
host.remove()
})
}
expect(await page.evaluate(() => window.monaco.editor.getModels().length)).toBe(0)
})
+18
View File
@@ -7,6 +7,7 @@ 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 { coreStyleOrder, prepareCoreStyles } from '../scripts/site-vendor/monaco/css.ts'
import { aliasModule, moduleIds, nameModule, verifyWorkerSources } from '../scripts/site-vendor/monaco/languages.ts'
import { browserTypeScript, verifyTypeScriptSource } from '../scripts/site-vendor/monaco/typescript.ts'
@@ -15,6 +16,23 @@ const contributionGroups = ['typescript', 'css', 'json', 'html', 'languages'].ma
return { name: `monaco-${name}`, modulePrefix, contrib: `${modulePrefix}/monaco.contribution` }
})
test('Core CSS inventory follows registration order instead of the module-name table order', () => {
const source = 'var names = ["vs/css!vs/editor/editor.main","vs/css!vs/a","vs/css!vs/b"]; define(names[2],[],{}); define(names[1],[],{});'
assert.deepEqual(coreStyleOrder(source), ['vs/b', 'vs/a'])
assert.throws(() => coreStyleOrder(`${source} define(names[2],[],{});`), /repeated core CSS/)
assert.throws(() => coreStyleOrder('define([],{});'), /Missing/)
})
test('CSS source transport cannot silently fall back to unverified or missing inputs', () => {
const unexpectedRead = () => {
throw new Error('unexpected read')
}
assert.throws(() => prepareCoreStyles('', '', ['vs/a'], unexpectedRead), /one transportCSS/)
const recipe = 'function transportCSS(module: string, enqueue: Function, write: Function) { fs.readFileSync("/outside/a.css"); return true; }'
assert.throws(() => prepareCoreStyles(recipe, '', ['vs/a'], unexpectedRead), /escaped verified inputs/)
assert.throws(() => prepareCoreStyles('function transportCSS() { return true; }', '', ['vs/a'], unexpectedRead), /Missing transported CSS/)
})
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 })};`