mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
Refactor build process: remove unused entry points and update resource patterns for desktop build
This commit is contained in:
207
build/next/compare-builds.cjs
Normal file
207
build/next/compare-builds.cjs
Normal file
@@ -0,0 +1,207 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function walk(dir, base) {
|
||||
let results = [];
|
||||
try {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
const rel = path.join(base, e.name);
|
||||
if (e.isDirectory()) {
|
||||
results = results.concat(walk(path.join(dir, e.name), rel));
|
||||
} else {
|
||||
results.push(rel);
|
||||
}
|
||||
}
|
||||
} catch (err) { /* skip */ }
|
||||
return results;
|
||||
}
|
||||
|
||||
const oldRoot = '/Users/jrieken/Code/vscode/Visual Studio Code - Insiders-OLD.app/Contents/Resources/app';
|
||||
const newRoot = '/Users/jrieken/Code/vscode/Visual Studio Code - Insiders-NEW.app/Contents/Resources/app';
|
||||
|
||||
const oldFiles = new Set(walk(oldRoot, ''));
|
||||
const newFiles = new Set(walk(newRoot, ''));
|
||||
|
||||
const onlyOld = [...oldFiles].filter(f => !newFiles.has(f)).sort();
|
||||
const onlyNew = [...newFiles].filter(f => !oldFiles.has(f)).sort();
|
||||
|
||||
// Group by top-level directory
|
||||
function groupByTopDir(files) {
|
||||
const groups = {};
|
||||
for (const f of files) {
|
||||
const parts = f.split(path.sep);
|
||||
const topDir = parts.length > 1 ? parts[0] : '(root)';
|
||||
if (!groups[topDir]) { groups[topDir] = []; }
|
||||
groups[topDir].push(f);
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
console.log('OLD total files:', oldFiles.size);
|
||||
console.log('NEW total files:', newFiles.size);
|
||||
console.log('');
|
||||
|
||||
console.log('============================================');
|
||||
console.log('FILES ONLY IN OLD (missing from NEW):', onlyOld.length);
|
||||
console.log('============================================');
|
||||
const oldGroups = groupByTopDir(onlyOld);
|
||||
for (const [dir, files] of Object.entries(oldGroups).sort()) {
|
||||
console.log(`\n [${dir}] (${files.length} files)`);
|
||||
for (const f of files.slice(0, 50)) {
|
||||
console.log(` - ${f}`);
|
||||
}
|
||||
if (files.length > 50) { console.log(` ... and ${files.length - 50} more`); }
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('============================================');
|
||||
console.log('FILES ONLY IN NEW (extra in NEW):', onlyNew.length);
|
||||
console.log('============================================');
|
||||
const newGroups = groupByTopDir(onlyNew);
|
||||
for (const [dir, files] of Object.entries(newGroups).sort()) {
|
||||
console.log(`\n [${dir}] (${files.length} files)`);
|
||||
for (const f of files.slice(0, 50)) {
|
||||
console.log(` - ${f}`);
|
||||
}
|
||||
if (files.length > 50) { console.log(` ... and ${files.length - 50} more`); }
|
||||
}
|
||||
|
||||
// Check JS files with significant size differences
|
||||
console.log('');
|
||||
console.log('============================================');
|
||||
console.log('.js files with >2x size difference');
|
||||
console.log('============================================');
|
||||
const common = [...oldFiles].filter(f => newFiles.has(f) && f.endsWith('.js'));
|
||||
const sizeDiffs = [];
|
||||
for (const f of common) {
|
||||
try {
|
||||
const oldSize = fs.statSync(path.join(oldRoot, f)).size;
|
||||
const newSize = fs.statSync(path.join(newRoot, f)).size;
|
||||
if (oldSize === 0 || newSize === 0) { continue; }
|
||||
const ratio = newSize / oldSize;
|
||||
if (ratio < 0.5 || ratio > 2.0) {
|
||||
sizeDiffs.push({ file: f, oldSize, newSize, ratio });
|
||||
}
|
||||
} catch (e) { /* skip */ }
|
||||
}
|
||||
sizeDiffs.sort((a, b) => a.ratio - b.ratio);
|
||||
if (sizeDiffs.length === 0) {
|
||||
console.log(' None found.');
|
||||
} else {
|
||||
for (const d of sizeDiffs) {
|
||||
const arrow = d.ratio < 1 ? 'SMALLER' : 'LARGER';
|
||||
console.log(` ${d.file}: OLD=${d.oldSize} NEW=${d.newSize} (${d.ratio.toFixed(2)}x, ${arrow})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for empty JS files in NEW that aren't empty in OLD
|
||||
console.log('');
|
||||
console.log('============================================');
|
||||
console.log('.js files that became empty in NEW');
|
||||
console.log('============================================');
|
||||
let emptyCount = 0;
|
||||
for (const f of common) {
|
||||
if (!f.endsWith('.js')) { continue; }
|
||||
try {
|
||||
const oldSize = fs.statSync(path.join(oldRoot, f)).size;
|
||||
const newSize = fs.statSync(path.join(newRoot, f)).size;
|
||||
if (oldSize > 0 && newSize === 0) {
|
||||
console.log(` ${f}: was ${oldSize} bytes, now 0`);
|
||||
emptyCount++;
|
||||
}
|
||||
} catch (e) { /* skip */ }
|
||||
}
|
||||
if (emptyCount === 0) { console.log(' None found.'); }
|
||||
|
||||
// Size comparison by top-level directory within out/
|
||||
console.log('');
|
||||
console.log('============================================');
|
||||
console.log('Size comparison by area (out/vs/*)');
|
||||
console.log('============================================');
|
||||
|
||||
function dirSize(dir) {
|
||||
let total = 0;
|
||||
try {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
const full = path.join(dir, e.name);
|
||||
if (e.isDirectory()) {
|
||||
total += dirSize(full);
|
||||
} else {
|
||||
total += fs.statSync(full).size;
|
||||
}
|
||||
}
|
||||
} catch (e) { /* skip */ }
|
||||
return total;
|
||||
}
|
||||
|
||||
const oldOut = path.join(oldRoot, 'out');
|
||||
const newOut = path.join(newRoot, 'out');
|
||||
|
||||
// Top-level out/ size
|
||||
const oldTotal = dirSize(oldOut);
|
||||
const newTotal = dirSize(newOut);
|
||||
console.log(` TOTAL out/: OLD=${(oldTotal / 1024 / 1024).toFixed(1)}MB NEW=${(newTotal / 1024 / 1024).toFixed(1)}MB diff=+${((newTotal - oldTotal) / 1024 / 1024).toFixed(1)}MB`);
|
||||
|
||||
const areas = ['vs/base', 'vs/code', 'vs/editor', 'vs/platform', 'vs/workbench'];
|
||||
for (const area of areas) {
|
||||
const oldSize = dirSize(path.join(oldOut, area));
|
||||
const newSize = dirSize(path.join(newOut, area));
|
||||
const diff = newSize - oldSize;
|
||||
const sign = diff >= 0 ? '+' : '';
|
||||
console.log(` ${area}: OLD=${(oldSize / 1024 / 1024).toFixed(1)}MB NEW=${(newSize / 1024 / 1024).toFixed(1)}MB diff=${sign}${(diff / 1024 / 1024).toFixed(1)}MB`);
|
||||
}
|
||||
|
||||
// Detailed breakdown of extra files in NEW
|
||||
console.log('');
|
||||
console.log('============================================');
|
||||
console.log('EXTRA FILES IN NEW - DETAILED BREAKDOWN');
|
||||
console.log('============================================');
|
||||
const extra = [...newFiles].filter(f => !oldFiles.has(f)).sort();
|
||||
|
||||
const byExt = {};
|
||||
for (const f of extra) {
|
||||
const ext = path.extname(f) || '(no ext)';
|
||||
if (!byExt[ext]) { byExt[ext] = []; }
|
||||
byExt[ext].push(f);
|
||||
}
|
||||
console.log('\nBy extension:');
|
||||
for (const [ext, files] of Object.entries(byExt).sort((a, b) => b[1].length - a[1].length)) {
|
||||
console.log(` ${ext}: ${files.length} files`);
|
||||
}
|
||||
|
||||
// List all JS files
|
||||
console.log('\n--- Extra .js files ---');
|
||||
(byExt['.js'] || []).forEach(f => console.log(` ${f}`));
|
||||
|
||||
// List all HTML files
|
||||
console.log('\n--- Extra .html files ---');
|
||||
(byExt['.html'] || []).forEach(f => console.log(` ${f}`));
|
||||
|
||||
// List other non-CSS files
|
||||
console.log('\n--- Extra non-CSS/non-JS/non-HTML files ---');
|
||||
for (const f of extra) {
|
||||
const ext = path.extname(f);
|
||||
if (ext !== '.css' && ext !== '.js' && ext !== '.html') {
|
||||
console.log(` ${f}`);
|
||||
}
|
||||
}
|
||||
|
||||
// List CSS files grouped by area
|
||||
console.log('\n--- Extra .css files by area ---');
|
||||
const cssFiles = byExt['.css'] || [];
|
||||
const cssAreas = {};
|
||||
for (const f of cssFiles) {
|
||||
const parts = f.split(path.sep);
|
||||
const area = parts.length > 3 ? parts.slice(0, 3).join('/') : parts.slice(0, 2).join('/');
|
||||
if (!cssAreas[area]) { cssAreas[area] = []; }
|
||||
cssAreas[area].push(f);
|
||||
}
|
||||
for (const [area, files] of Object.entries(cssAreas).sort()) {
|
||||
console.log(` [${area}] (${files.length} files)`);
|
||||
}
|
||||
@@ -134,7 +134,6 @@ function getEntryPointsForTarget(target: BuildTarget): string[] {
|
||||
...desktopWorkerEntryPoints,
|
||||
...desktopEntryPoints,
|
||||
...codeEntryPoints,
|
||||
...webEntryPoints, // Desktop also includes web for remote development
|
||||
...keyboardMapEntryPoints,
|
||||
];
|
||||
case 'server':
|
||||
@@ -184,9 +183,7 @@ function getCssBundleEntryPointsForTarget(target: BuildTarget): Set<string> {
|
||||
case 'desktop':
|
||||
return new Set([
|
||||
'vs/workbench/workbench.desktop.main',
|
||||
'vs/workbench/workbench.web.main.internal',
|
||||
'vs/code/electron-browser/workbench/workbench',
|
||||
'vs/code/browser/workbench/workbench',
|
||||
]);
|
||||
case 'server':
|
||||
return new Set(); // Server has no UI
|
||||
@@ -230,9 +227,6 @@ const desktopResourcePatterns = [
|
||||
// HTML
|
||||
'vs/code/electron-browser/workbench/workbench.html',
|
||||
'vs/code/electron-browser/workbench/workbench-dev.html',
|
||||
'vs/code/browser/workbench/workbench.html',
|
||||
'vs/code/browser/workbench/workbench-dev.html',
|
||||
'vs/code/browser/workbench/callback.html',
|
||||
'vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html',
|
||||
'vs/workbench/contrib/webview/browser/pre/*.html',
|
||||
|
||||
@@ -241,11 +235,11 @@ const desktopResourcePatterns = [
|
||||
|
||||
// Shell scripts
|
||||
'vs/base/node/*.sh',
|
||||
'vs/workbench/contrib/terminal/common/scripts/**/*.sh',
|
||||
'vs/workbench/contrib/terminal/common/scripts/**/*.ps1',
|
||||
'vs/workbench/contrib/terminal/common/scripts/**/*.psm1',
|
||||
'vs/workbench/contrib/terminal/common/scripts/**/*.fish',
|
||||
'vs/workbench/contrib/terminal/common/scripts/**/*.zsh',
|
||||
'vs/workbench/contrib/terminal/common/scripts/*.sh',
|
||||
'vs/workbench/contrib/terminal/common/scripts/*.ps1',
|
||||
'vs/workbench/contrib/terminal/common/scripts/*.psm1',
|
||||
'vs/workbench/contrib/terminal/common/scripts/*.fish',
|
||||
'vs/workbench/contrib/terminal/common/scripts/*.zsh',
|
||||
'vs/workbench/contrib/externalTerminal/**/*.scpt',
|
||||
|
||||
// Media - audio
|
||||
@@ -254,8 +248,7 @@ const desktopResourcePatterns = [
|
||||
// Media - images
|
||||
'vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.svg',
|
||||
'vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.png',
|
||||
'vs/workbench/contrib/extensions/browser/media/*.svg',
|
||||
'vs/workbench/contrib/extensions/browser/media/*.png',
|
||||
'vs/workbench/contrib/extensions/browser/media/{theme-icon.png,language-icon.svg}',
|
||||
'vs/workbench/services/extensionManagement/common/media/*.svg',
|
||||
'vs/workbench/services/extensionManagement/common/media/*.png',
|
||||
'vs/workbench/browser/parts/editor/media/*.png',
|
||||
@@ -460,6 +453,7 @@ async function copyFile(srcPath: string, destPath: string): Promise<void> {
|
||||
const desktopStandaloneFiles = [
|
||||
'vs/base/parts/sandbox/electron-browser/preload.ts',
|
||||
'vs/base/parts/sandbox/electron-browser/preload-aux.ts',
|
||||
'vs/platform/browserView/electron-browser/preload-browserView.ts',
|
||||
];
|
||||
|
||||
async function compileStandaloneFiles(outDir: string, doMinify: boolean, target: BuildTarget): Promise<void> {
|
||||
@@ -558,11 +552,15 @@ async function copyResources(outDir: string, target: BuildTarget, excludeDevFile
|
||||
}
|
||||
}
|
||||
|
||||
// Copy CSS files
|
||||
// Copy CSS files (only for development/transpile builds, not production bundles
|
||||
// where CSS is already bundled into combined files like workbench.desktop.main.css)
|
||||
if (!excludeDevFiles) {
|
||||
const cssCount = await copyCssFiles(outDir, excludeTests);
|
||||
copied += cssCount;
|
||||
|
||||
console.log(`[resources] Copied ${copied} files (${cssCount} CSS)`);
|
||||
} else {
|
||||
console.log(`[resources] Copied ${copied} files (CSS skipped - bundled)`);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
||||
@@ -108,6 +108,66 @@ npm run gulp vscode-reh-web-darwin-arm64-min
|
||||
|
||||
---
|
||||
|
||||
## Build Comparison: OLD (gulp-tsb) vs NEW (esbuild) — Desktop Build
|
||||
|
||||
### Summary
|
||||
|
||||
| Metric | OLD | NEW | Delta |
|
||||
|--------|-----|-----|-------|
|
||||
| Total files in `out/` | 3993 | 4301 | +309 extra, 1 missing |
|
||||
| Total size of `out/` | 25.8 MB | 64.6 MB | +38.8 MB (2.5×) |
|
||||
| `workbench.desktop.main.js` | 13.0 MB | 15.5 MB | +2.5 MB |
|
||||
|
||||
### 1 Missing File (in OLD, not in NEW)
|
||||
|
||||
| File | Why Missing | Fix |
|
||||
|------|-------------|-----|
|
||||
| `out/vs/platform/browserView/electron-browser/preload-browserView.js` | Not listed in `desktopStandaloneFiles` in index.ts. Only `preload.ts` and `preload-aux.ts` are compiled as standalone files. | **Add** `'vs/platform/browserView/electron-browser/preload-browserView.ts'` to the `desktopStandaloneFiles` array in `index.ts`. |
|
||||
|
||||
### 309 Extra Files (in NEW, not in OLD) — Breakdown
|
||||
|
||||
| Category | Count | Explanation |
|
||||
|----------|-------|-------------|
|
||||
| **CSS files** | 291 | `copyCssFiles()` copies ALL `.css` from `src/` to the output. The old bundler inlines CSS into the main `.css` bundle (e.g., `workbench.desktop.main.css`) and never ships individual CSS files. These individual files ARE needed at runtime because the new ESM system uses `import './foo.css'` resolved by an import map. |
|
||||
| **Vendor JS files** | 3 | `dompurify.js`, `marked.js`, `semver.js` — listed in `commonResourcePatterns`. The old bundler inlines these into the main bundle. The new system keeps them as separate files because they're plain JS (not TS). They're needed. |
|
||||
| **Web workbench bundle** | 1 | `vs/code/browser/workbench/workbench.js` (15.4 MB). This is the web workbench entry point bundle. It should NOT be in a desktop build — the old build explicitly excludes `out-build/vs/code/browser/**`. The `desktopResourcePatterns` in index.ts includes `vs/code/browser/workbench/*.html` and `callback.html` which is correct, but the actual bundle gets written by the esbuild desktop bundle step because the desktop entry points include web entry points. |
|
||||
| **Web workbench internal** | 1 | `vs/workbench/workbench.web.main.internal.js` (15.4 MB). Similar: shouldn't ship in a desktop build. It's output by the esbuild bundler. |
|
||||
| **Keyboard layout contributions** | 3 | `layout.contribution.{darwin,linux,win}.js` — the old bundler inlines these into the main bundle. These are new separate files from the esbuild bundler. |
|
||||
| **NLS files** | 2 | `nls.messages.js` (new) and `nls.metadata.json` (new). The old build has `nls.messages.json` and `nls.keys.json` but not a `.js` version or metadata. The `.js` version is produced by the NLS plugin. |
|
||||
| **HTML files** | 2 | `vs/code/browser/workbench/workbench.html` and `callback.html` — correctly listed in `desktopResourcePatterns` (these are needed for desktop's built-in web server). |
|
||||
| **SVG loading spinners** | 3 | `loading-dark.svg`, `loading-hc.svg`, `loading.svg` in `vs/workbench/contrib/extensions/browser/media/`. The old build only copies `theme-icon.png` and `language-icon.svg` from that folder; the new build's `desktopResourcePatterns` uses `*.svg` which is broader. |
|
||||
| **codicon.ttf (duplicate)** | 1 | At `vs/base/browser/ui/codicons/codicon/codicon.ttf`. The old build copies this to `out/media/codicon.ttf` only. The new build has BOTH: the copy in `out/media/` (from esbuild's `file` loader) AND the original path (from `commonResourcePatterns`). Duplicate. |
|
||||
| **PSReadLine.psm1** | 1 | `vs/workbench/contrib/terminal/common/scripts/psreadline/PSReadLine.psm1` — the old build uses `*.psm1` in `terminal/common/scripts/` (non-recursive?). The new build uses `**/*.psm1` (recursive), picking up this subdirectory file. Check if it's needed. |
|
||||
| **date file** | 1 | `out/date` — build timestamp, produced by the new build's `bundle()` function. The old build doesn't write this; it reads `package.json.date` instead. |
|
||||
|
||||
### Size Increase Breakdown by Area
|
||||
|
||||
| Area | OLD | NEW | Delta | Why |
|
||||
|------|-----|-----|-------|-----|
|
||||
| `vs/code` | 1.5 MB | 17.4 MB | +15.9 MB | Web workbench bundle (15.4 MB) shouldn't be in desktop build |
|
||||
| `vs/workbench` | 18.9 MB | 38.7 MB | +19.8 MB | `workbench.web.main.internal.js` (15.4 MB) + unmangled desktop bundle (+2.5 MB) + individual CSS files (~1 MB) |
|
||||
| `vs/base` | 0 MB | 0.4 MB | +0.4 MB | Individual CSS files + vendor JS |
|
||||
| `vs/editor` | 0.3 MB | 0.5 MB | +0.1 MB | Individual CSS files |
|
||||
| `vs/platform` | 1.7 MB | 1.9 MB | +0.2 MB | Individual CSS files |
|
||||
|
||||
### JS Files with >2× Size Change
|
||||
|
||||
| File | OLD | NEW | Ratio | Reason |
|
||||
|------|-----|-----|-------|--------|
|
||||
| `vs/workbench/contrib/webview/browser/pre/service-worker.js` | 7 KB | 15 KB | 2.2× | Not minified / includes more inlined code |
|
||||
| `vs/code/electron-browser/workbench/workbench.js` | 10 KB | 28 KB | 2.75× | OLD is minified to 6 lines; NEW is 380 lines (not compressed, includes tslib banner) |
|
||||
|
||||
### Action Items
|
||||
|
||||
1. **[CRITICAL] Missing `preload-browserView.ts`** — Add to `desktopStandaloneFiles` in index.ts. Without it, BrowserView (used for Simple Browser) may fail.
|
||||
2. **[SIZE] Web bundles in desktop build** — `workbench.web.main.internal.js` and `vs/code/browser/workbench/workbench.js` together add ~31 MB. These are written by the esbuild bundler and not filtered out. Consider: either don't bundle web entry points for the desktop target, or ensure the packaging step excludes them (currently `packageTask` takes `out-vscode-min/**` without filtering).
|
||||
3. **[SIZE] No mangling** — The desktop main bundle is 2.5 MB larger due to no property mangling. Known open item.
|
||||
4. **[MINOR] Duplicate codicon.ttf** — Exists at both `out/media/codicon.ttf` (from esbuild `file` loader) and `out/vs/base/browser/ui/codicons/codicon/codicon.ttf` (from `commonResourcePatterns`). Consider removing from `commonResourcePatterns` if it's already handled by the loader.
|
||||
5. **[MINOR] Extra SVGs** — `desktopResourcePatterns` uses `*.svg` for extensions media but old build only ships `language-icon.svg`. The loading spinners may be unused in the desktop build.
|
||||
6. **[MINOR] Extra PSReadLine.psm1** from recursive glob — verify if needed.
|
||||
|
||||
---
|
||||
|
||||
## Self-hosting Setup
|
||||
|
||||
The default `VS Code - Build` task now runs three parallel watchers:
|
||||
|
||||
Reference in New Issue
Block a user