diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index f07d722199a..85b9b2e547b 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -28,10 +28,11 @@ const fs = require('fs'); const glob = require('glob'); const { compileBuildTask } = require('./gulpfile.compile'); const { compileExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions'); -const { vscodeWebEntryPoints, vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } = require('./gulpfile.vscode.web'); +const { vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } = require('./gulpfile.vscode.web'); const cp = require('child_process'); const log = require('fancy-log'); const { isESM } = require('./lib/esm'); +const buildfile = require('../src/buildfile'); const REPO_ROOT = path.dirname(__dirname); const commit = getVersion(REPO_ROOT); @@ -87,7 +88,11 @@ const serverResources = [ ...serverResourceExcludes ]; -const serverWithWebResourceIncludes = [ +const serverWithWebResourceIncludes = isESM() ? [ + ...serverResourceIncludes, + 'out-build/vs/code/browser/workbench/*.html', + ...vscodeWebResourceIncludes +] : [ ...serverResourceIncludes, ...vscodeWebResourceIncludes ]; @@ -126,14 +131,35 @@ const serverEntryPoints = [ } ]; +const webEntryPoints = isESM() ? [ + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerOutputLinks, + buildfile.workerBackgroundTokenization, + buildfile.keyboardMaps, + buildfile.codeWeb +].flat() : [ + buildfile.entrypoint('vs/workbench/workbench.web.main'), + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.keyboardMaps, + buildfile.workbenchWeb() +].flat(); + const serverWithWebEntryPoints = [ // Include all of server ...serverEntryPoints, - // Include workbench web - ...vscodeWebEntryPoints -]; + // Include all of web + ...webEntryPoints, +].flat(); const commonJSEntryPoints = [ 'out-build/server-main.js', diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 609b0d60b0a..59dbdc438bd 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -38,7 +38,18 @@ const glob = promisify(require('glob')); const rcedit = promisify(require('rcedit')); // Build -const vscodeEntryPoints = [ +const vscodeEntryPoints = isESM() ? [ + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerProfileAnalysis, + buildfile.workerOutputLinks, + buildfile.workerBackgroundTokenization, + buildfile.workbenchDesktop(), + buildfile.code +].flat() : [ buildfile.entrypoint('vs/workbench/workbench.desktop.main'), buildfile.base, buildfile.workerExtensionHost, @@ -46,11 +57,62 @@ const vscodeEntryPoints = [ buildfile.workerLanguageDetection, buildfile.workerLocalFileSearch, buildfile.workerProfileAnalysis, - buildfile.workbenchDesktop, + buildfile.workbenchDesktop(), buildfile.code ].flat(); -const vscodeResources = [ +const vscodeResourceIncludes = isESM() ? [ + + // NLS + 'out-build/nls.messages.json', + 'out-build/nls.keys.json', + + // Workbench + 'out-build/vs/code/electron-sandbox/workbench/workbench.esm.html', + + // Electron Preload + 'out-build/vs/base/parts/sandbox/electron-sandbox/preload.js', + 'out-build/vs/base/parts/sandbox/electron-sandbox/preload-aux.js', + + // Node Scripts + 'out-build/vs/base/node/{terminateProcess.sh,cpuUsage.sh,ps.sh}', + + // Touchbar + 'out-build/vs/workbench/browser/parts/editor/media/*.png', + 'out-build/vs/workbench/contrib/debug/browser/media/*.png', + + // External Terminal + 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', + + // Terminal shell integration + 'out-build/vs/workbench/contrib/terminal/browser/media/fish_xdg_data/fish/vendor_conf.d/*.fish', + 'out-build/vs/workbench/contrib/terminal/browser/media/*.ps1', + 'out-build/vs/workbench/contrib/terminal/browser/media/*.psm1', + 'out-build/vs/workbench/contrib/terminal/browser/media/*.sh', + 'out-build/vs/workbench/contrib/terminal/browser/media/*.zsh', + + // Accessibility Signals + 'out-build/vs/platform/accessibilitySignal/browser/media/*.mp3', + + // Welcome + 'out-build/vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.{svg,png}', + + // Extensions + 'out-build/vs/workbench/contrib/extensions/browser/media/{theme-icon.png,language-icon.svg}', + 'out-build/vs/workbench/services/extensionManagement/common/media/*.{svg,png}', + + // Webview + 'out-build/vs/workbench/contrib/webview/browser/pre/*.{js,html}', + + // Extension Host Worker + 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.esm.html', + + // Process Explorer + 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.esm.html', + + // Issue Reporter + 'out-build/vs/workbench/contrib/issue/electron-sandbox/issueReporter.esm.html' +] : [ 'out-build/nls.messages.json', 'out-build/nls.keys.json', 'out-build/vs/**/*.{svg,png,html,jpg,mp3}', @@ -78,6 +140,21 @@ const vscodeResources = [ '!**/test/**' ]; +const vscodeResources = [ + + // Includes + ...vscodeResourceIncludes, + + // Excludes + '!out-build/vs/code/browser/**', + '!out-build/vs/editor/standalone/**', + '!out-build/vs/code/**/*-dev.html', + '!out-build/vs/code/**/*-dev.esm.html', + '!out-build/vs/workbench/contrib/issue/**/*-dev.html', + '!out-build/vs/workbench/contrib/issue/**/*-dev.esm.html', + '!**/test/**' +]; + // Do not change the order of these files! They will // be inlined into the target window file in this order // and they depend on each other in this way. @@ -215,7 +292,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op 'vs/workbench/workbench.desktop.main.js', 'vs/workbench/workbench.desktop.main.css', 'vs/workbench/api/node/extensionHostProcess.js', - 'vs/code/electron-sandbox/workbench/workbench.html', + isESM() ? 'vs/code/electron-sandbox/workbench/workbench.esm.html' : 'vs/code/electron-sandbox/workbench/workbench.html', 'vs/code/electron-sandbox/workbench/workbench.js' ]); @@ -244,6 +321,11 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op version += '-' + quality; } + if (isESM() && quality !== 'exploration') { + // TODO@esm remove this safeguard + throw new Error('Refuse to build ESM on quality other than exploration'); + } + const name = product.nameShort; const packageJsonUpdates = { name, version, ...(isESM(`Setting 'type: module' and 'main: out/main.js' in top level package.json`) ? { type: 'module', main: 'out/main.js' } : {}) }; // TODO@esm this should be configured in the top level package.json diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 190dac79372..40340567c69 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -21,6 +21,7 @@ const vfs = require('vinyl-fs'); const packageJson = require('../package.json'); const { compileBuildTask } = require('./gulpfile.compile'); const extensions = require('./lib/extensions'); +const { isESM } = require('./lib/esm'); const REPO_ROOT = path.dirname(__dirname); const BUILD_ROOT = path.dirname(REPO_ROOT); @@ -30,7 +31,27 @@ const commit = getVersion(REPO_ROOT); const quality = product.quality; const version = (quality && quality !== 'stable') ? `${packageJson.version}-${quality}` : packageJson.version; -const vscodeWebResourceIncludes = [ +const vscodeWebResourceIncludes = isESM() ? [ + + // NLS + 'out-build/nls.messages.js', + + // Accessibility Signals + 'out-build/vs/platform/accessibilitySignal/browser/media/*.mp3', + + // Welcome + 'out-build/vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.{svg,png}', + + // Extensions + 'out-build/vs/workbench/contrib/extensions/browser/media/{theme-icon.png,language-icon.svg}', + 'out-build/vs/workbench/services/extensionManagement/common/media/*.{svg,png}', + + // Webview + 'out-build/vs/workbench/contrib/webview/browser/pre/*.{js,html}', + + // Extension Host Worker + 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.esm.html', +]: [ // Workbench 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,jpg,mp3}', @@ -47,7 +68,6 @@ const vscodeWebResourceIncludes = [ // Extension Worker 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html', - 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.esm.html', // Web node paths (needed for integration tests) 'out-build/vs/webPackagePaths.js', @@ -70,7 +90,17 @@ const vscodeWebResources = [ const buildfile = require('../src/buildfile'); -const vscodeWebEntryPoints = [ +const vscodeWebEntryPoints = isESM() ? [ + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerOutputLinks, + buildfile.workerBackgroundTokenization, + buildfile.keyboardMaps, + buildfile.workbenchWeb() +].flat() : [ buildfile.entrypoint('vs/workbench/workbench.web.main'), buildfile.base, buildfile.workerExtensionHost, @@ -78,9 +108,8 @@ const vscodeWebEntryPoints = [ buildfile.workerLanguageDetection, buildfile.workerLocalFileSearch, buildfile.keyboardMaps, - buildfile.workbenchWeb + buildfile.workbenchWeb() ].flat(); -exports.vscodeWebEntryPoints = vscodeWebEntryPoints; /** * @param {object} product The parsed product.json file contents diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 615f760877b..627b9966700 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -55,9 +55,6 @@ function bundle(entryPoints, config, callback) { }; 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); } @@ -105,7 +102,7 @@ function emitEntryPoints(modules, entryPoints) { return allDependencies[module]; }); bundleData.bundles[moduleToBundle] = includedModules; - const res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend || [], info.append || [], info.dest); + const res = emitEntryPoint(modulesMap, modulesGraph, moduleToBundle, includedModules, info.prepend || [], info.dest); result = result.concat(res.files); for (const pluginName in res.usedPlugins) { usedPlugins[pluginName] = usedPlugins[pluginName] || res.usedPlugins[pluginName]; @@ -278,7 +275,7 @@ function removeDuplicateTSBoilerplate(source, SEEN_BOILERPLATE = []) { } return newLines.join('\n'); } -function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, append, dest) { +function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, dest) { if (!dest) { dest = entryPoint + '.js'; } @@ -352,8 +349,7 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, }; }; const toPrepend = (prepend || []).map(toIFile); - const toAppend = (append || []).map(toIFile); - mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend); + mainResult.sources = toPrepend.concat(mainResult.sources); return { files: results, usedPlugins: usedPlugins diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index 36fea87c41c..6e3f96a5062 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -52,7 +52,6 @@ export interface IEntryPoint { include?: string[]; exclude?: string[]; prepend?: IExtraFile[]; - append?: IExtraFile[]; dest?: string; target?: 'amd' | 'esm'; } @@ -158,9 +157,6 @@ export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callba }; 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); } @@ -224,7 +220,6 @@ function emitEntryPoints(modules: IBuildModuleInfo[], entryPoints: IEntryPointMa moduleToBundle, includedModules, info.prepend || [], - info.append || [], info.dest ); @@ -430,7 +425,6 @@ function emitEntryPoint( entryPoint: string, includedModules: string[], prepend: IExtraFile[], - append: IExtraFile[], dest: string | undefined ): IEmitEntryPointResult { if (!dest) { @@ -516,9 +510,8 @@ function emitEntryPoint( }; const toPrepend = (prepend || []).map(toIFile); - const toAppend = (append || []).map(toIFile); - mainResult.sources = toPrepend.concat(mainResult.sources).concat(toAppend); + mainResult.sources = toPrepend.concat(mainResult.sources); return { files: results, diff --git a/build/lib/esm.js b/build/lib/esm.js index cda0f22034c..db2683445a2 100644 --- a/build/lib/esm.js +++ b/build/lib/esm.js @@ -28,9 +28,9 @@ function setESM(enabled) { } function isESM(logWarning) { try { - const res = fs.readFileSync(esmMarkerFile, 'utf8') === 'true'; + const res = (typeof process.env.VSCODE_BUILD_ESM === 'string' && process.env.VSCODE_BUILD_ESM.toLowerCase() === 'true') || (fs.readFileSync(esmMarkerFile, 'utf8') === 'true'); if (res && logWarning) { - console.warn(`ESM: ${logWarning}`); + console.warn(`[esm] ${logWarning}`); } return res; } diff --git a/build/lib/esm.ts b/build/lib/esm.ts index fd811ca782d..2fc5215e284 100644 --- a/build/lib/esm.ts +++ b/build/lib/esm.ts @@ -29,9 +29,9 @@ export function setESM(enabled: boolean) { export function isESM(logWarning?: string): boolean { try { - const res = fs.readFileSync(esmMarkerFile, 'utf8') === 'true'; + const res = (typeof process.env.VSCODE_BUILD_ESM === 'string' && process.env.VSCODE_BUILD_ESM.toLowerCase() === 'true') || (fs.readFileSync(esmMarkerFile, 'utf8') === 'true'); if (res && logWarning) { - console.warn(`ESM: ${logWarning}`); + console.warn(`[esm] ${logWarning}`); } return res; } catch (error) { diff --git a/build/lib/i18n.js b/build/lib/i18n.js index a837cbc4ac0..6964616291b 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -316,7 +316,7 @@ globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), function processNlsFiles(opts) { return (0, event_stream_1.through)(function (file) { const fileName = path.basename(file.path); - if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles + if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles (TODO@esm this file is not created anymore, pick another) try { const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); if (NLSKeysFormat.is(json)) { diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 28f8cc993e6..cd7e522ad36 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -387,7 +387,7 @@ globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), export function processNlsFiles(opts: { out: string; fileHeader: string; languages: Language[] }): ThroughStream { return through(function (this: ThroughStream, file: File) { const fileName = path.basename(file.path); - if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles + if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles (TODO@esm this file is not created anymore, pick another) try { const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); if (NLSKeysFormat.is(json)) { diff --git a/build/lib/mangle/index.js b/build/lib/mangle/index.js index 10e1aeb0d5a..c000f44ef40 100644 --- a/build/lib/mangle/index.js +++ b/build/lib/mangle/index.js @@ -14,6 +14,7 @@ const ts = require("typescript"); const url_1 = require("url"); const workerpool = require("workerpool"); const staticLanguageServiceHost_1 = require("./staticLanguageServiceHost"); +const esm_1 = require("../esm"); const buildfile = require('../../../src/buildfile'); class ShortIdent { prefix; @@ -247,35 +248,51 @@ function isNameTakenInFile(node, name) { } return false; } -const skippedExportMangledFiles = [ - // Build - 'css.build', - // Monaco - 'editorCommon', - 'editorOptions', - 'editorZoom', - 'standaloneEditor', - 'standaloneEnums', - 'standaloneLanguages', - // Generated - 'extensionsApiProposals', - // Module passed around as type - 'pfs', - // entry points - ...[ - buildfile.entrypoint('vs/server/node/server.main', []), - buildfile.entrypoint('vs/workbench/workbench.desktop.main', []), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, - buildfile.workbenchDesktop, - buildfile.workbenchWeb, - buildfile.code - ].flat().map(x => x.name), -]; +const skippedExportMangledFiles = function () { + return [ + // Build + 'css.build', + // Monaco + 'editorCommon', + 'editorOptions', + 'editorZoom', + 'standaloneEditor', + 'standaloneEnums', + 'standaloneLanguages', + // Generated + 'extensionsApiProposals', + // Module passed around as type + 'pfs', + // entry points + ...(0, esm_1.isESM)() ? [ + buildfile.entrypoint('vs/server/node/server.main'), + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerProfileAnalysis, + buildfile.workerOutputLinks, + buildfile.workerBackgroundTokenization, + buildfile.workbenchDesktop(), + buildfile.workbenchWeb(), + buildfile.code, + buildfile.codeWeb + ].flat().map(x => x.name) : [ + buildfile.entrypoint('vs/server/node/server.main'), + buildfile.entrypoint('vs/workbench/workbench.desktop.main'), + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerProfileAnalysis, + buildfile.workbenchDesktop(), + buildfile.workbenchWeb(), + buildfile.code + ].flat().map(x => x.name), + ]; +}; const skippedExportMangledProjects = [ // Test projects 'vscode-api-tests', @@ -519,7 +536,7 @@ class Mangler { for (const data of this.allExportedSymbols.values()) { if (data.fileName.endsWith('.d.ts') || skippedExportMangledProjects.some(proj => data.fileName.includes(proj)) - || skippedExportMangledFiles.some(file => data.fileName.endsWith(file + '.ts'))) { + || skippedExportMangledFiles().some(file => data.fileName.endsWith(file + '.ts'))) { continue; } if (!data.shouldMangle(data.replacementName)) { diff --git a/build/lib/mangle/index.ts b/build/lib/mangle/index.ts index a148c4dd637..14ec11bc471 100644 --- a/build/lib/mangle/index.ts +++ b/build/lib/mangle/index.ts @@ -12,6 +12,7 @@ import * as ts from 'typescript'; import { pathToFileURL } from 'url'; import * as workerpool from 'workerpool'; import { StaticLanguageServiceHost } from './staticLanguageServiceHost'; +import { isESM } from '../esm'; const buildfile = require('../../../src/buildfile'); class ShortIdent { @@ -279,40 +280,55 @@ function isNameTakenInFile(node: ts.Node, name: string): boolean { return false; } +const skippedExportMangledFiles = function () { // using a function() to ensure late isESM() check + return [ + // Build + 'css.build', -const skippedExportMangledFiles = [ - // Build - 'css.build', + // Monaco + 'editorCommon', + 'editorOptions', + 'editorZoom', + 'standaloneEditor', + 'standaloneEnums', + 'standaloneLanguages', - // Monaco - 'editorCommon', - 'editorOptions', - 'editorZoom', - 'standaloneEditor', - 'standaloneEnums', - 'standaloneLanguages', + // Generated + 'extensionsApiProposals', - // Generated - 'extensionsApiProposals', + // Module passed around as type + 'pfs', - // Module passed around as type - 'pfs', - - // entry points - ...[ - buildfile.entrypoint('vs/server/node/server.main', []), - buildfile.entrypoint('vs/workbench/workbench.desktop.main', []), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, - buildfile.workbenchDesktop, - buildfile.workbenchWeb, - buildfile.code - ].flat().map(x => x.name), -]; + // entry points + ...isESM() ? [ + buildfile.entrypoint('vs/server/node/server.main'), + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerProfileAnalysis, + buildfile.workerOutputLinks, + buildfile.workerBackgroundTokenization, + buildfile.workbenchDesktop(), + buildfile.workbenchWeb(), + buildfile.code, + buildfile.codeWeb + ].flat().map(x => x.name) : [ + buildfile.entrypoint('vs/server/node/server.main'), + buildfile.entrypoint('vs/workbench/workbench.desktop.main'), + buildfile.base, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerProfileAnalysis, + buildfile.workbenchDesktop(), + buildfile.workbenchWeb(), + buildfile.code + ].flat().map(x => x.name), + ]; +}; const skippedExportMangledProjects = [ // Test projects @@ -609,7 +625,7 @@ export class Mangler { for (const data of this.allExportedSymbols.values()) { if (data.fileName.endsWith('.d.ts') || skippedExportMangledProjects.some(proj => data.fileName.includes(proj)) - || skippedExportMangledFiles.some(file => data.fileName.endsWith(file + '.ts')) + || skippedExportMangledFiles().some(file => data.fileName.endsWith(file + '.ts')) ) { continue; } diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 3c91a8ef7e4..3a2206ffdc0 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -15,6 +15,7 @@ const filter = require("gulp-filter"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); const path = require("path"); +const fs = require("fs"); const pump = require("pump"); const VinylFile = require("vinyl"); const bundle = require("./bundle"); @@ -22,6 +23,8 @@ const i18n_1 = require("./i18n"); const stats_1 = require("./stats"); const util = require("./util"); const postcss_1 = require("./postcss"); +const esbuild = require("esbuild"); +const sourcemaps = require("gulp-sourcemaps"); const esm_1 = require("./esm"); const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix, message) { @@ -154,7 +157,6 @@ function optimizeAMDTask(opts) { const loaderConfig = opts.loaderConfig; const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; const fileContentMapper = opts.fileContentMapper || ((contents, _path) => contents); - const sourcemaps = require('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 @@ -196,40 +198,29 @@ function optimizeAMDTask(opts) { }) : es.through()); } function optimizeESMTask(opts, cjsOpts) { - // TODO@esm honor IEntryPoint#prepred/append (unused?) - const esbuild = require('esbuild'); - const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; - const sourcemaps = require('gulp-sourcemaps'); const resourcesStream = es.through(); // this stream will contain the resources const bundlesStream = es.through(); // this stream will contain the bundled files - const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json const entryPoints = opts.entryPoints.filter(d => d.target !== 'amd'); if (cjsOpts) { cjsOpts.entryPoints.forEach(entryPoint => entryPoints.push({ name: path.parse(entryPoint).name })); } - // TODO@esm remove hardcoded entry point and support `dest` of `IEntryPoint` or clean that up - entryPoints.push({ name: 'vs/base/worker/workerMain' }); + entryPoints.push({ name: 'vs/base/worker/workerMain' }); // TODO@esm remove hardcoded entry point when workers are cleaned up const allMentionedModules = new Set(); for (const entryPoint of entryPoints) { allMentionedModules.add(entryPoint.name); entryPoint.include?.forEach(allMentionedModules.add, allMentionedModules); entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules); } - // TODO@esm remove this from the bundle files - allMentionedModules.delete('vs/css'); + allMentionedModules.delete('vs/css'); // TODO@esm remove this when vs/css is removed const bundleAsync = async () => { - let bundleData; const files = []; const tasks = []; for (const entryPoint of entryPoints) { - const t1 = performance.now(); - console.log(`[bundle] STARTING '${entryPoint.name}'...`); + console.log(`[bundle] '${entryPoint.name}'`); // support for 'dest' via esbuild#in/out const dest = entryPoint.dest?.replace(/\.[^/.]+$/, '') ?? entryPoint.name; - // const dest = entryPoint.name; // boilerplate massage const banner = { js: '' }; - const fs = await Promise.resolve().then(() => require('node:fs')); const tslibPath = path.join(require.resolve('tslib'), '../tslib.es6.js'); banner.js += await fs.promises.readFile(tslibPath, 'utf-8'); const boilerplateTrimmer = { @@ -251,20 +242,20 @@ function optimizeESMTask(opts, cjsOpts) { } } const task = esbuild.build({ - logLevel: 'silent', bundle: true, external: entryPoint.exclude, packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages platform: 'neutral', // makes esm format: 'esm', plugins: [boilerplateTrimmer], - target: ['es2023'], + target: ['es2022'], loader: { '.ttf': 'file', '.svg': 'file', '.png': 'file', '.sh': 'file', }, + assetNames: 'media/[name]', // moves media assets into a sub-folder "media" banner, entryPoints: [ { @@ -276,25 +267,6 @@ function optimizeESMTask(opts, cjsOpts) { write: false, // enables res.outputFiles metafile: true, // enables res.metafile }).then(res => { - console.log(`[bundle] DONE for '${entryPoint.name}' (${Math.round(performance.now() - t1)}ms)`); - if (opts.bundleInfo) { - // TODO@esm validate that bundleData is correct - bundleData ??= { graph: {}, bundles: {} }; - function pathToModule(path) { - return path - .replace(new RegExp(`^${opts.src}\\/`), '') - .replace(/\.js$/, ''); - } - for (const [path, value] of Object.entries(res.metafile.outputs)) { - const entryModule = pathToModule(path); - const inputModules = Object.keys(value.inputs).map(pathToModule); - bundleData.bundles[entryModule] = inputModules; - } - for (const [input, value] of Object.entries(res.metafile.inputs)) { - const dependencies = value.imports.map(i => pathToModule(i.path)); - bundleData.graph[pathToModule(input)] = dependencies; - } - } for (const file of res.outputFiles) { let contents = file.contents; if (file.path.endsWith('.js')) { @@ -320,25 +292,15 @@ function optimizeESMTask(opts, cjsOpts) { tasks.push(task); } await Promise.all(tasks); - return { files, bundleData }; + return { files }; }; bundleAsync().then((output) => { // bundle output (JS, CSS, SVG...) es.readArray(output.files).pipe(bundlesStream); - // bundeInfo.json - const bundleInfoArray = []; - if (typeof output.bundleData === 'object') { - bundleInfoArray.push(new VinylFile({ - path: 'bundleInfo.json', - base: '.', - contents: Buffer.from(JSON.stringify(output.bundleData, null, '\t')) - })); - } - es.readArray(bundleInfoArray).pipe(bundleInfoStream); // forward all resources gulp.src(opts.resources, { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream); }); - const result = es.merge(bundlesStream, resourcesStream, bundleInfoStream); + const result = es.merge(bundlesStream, resourcesStream); return result .pipe(sourcemaps.write('./', { sourceRoot: undefined, @@ -347,12 +309,11 @@ function optimizeESMTask(opts, cjsOpts) { })) .pipe(opts.languages && opts.languages.length ? (0, i18n_1.processNlsFiles)({ out: opts.src, - fileHeader: bundledFileHeader, + fileHeader: opts.header || DEFAULT_FILE_HEADER, languages: opts.languages }) : es.through()); } function optimizeCommonJSTask(opts) { - const esbuild = require('esbuild'); const src = opts.src; const entryPoints = opts.entryPoints; return gulp.src(entryPoints, { base: `${src}`, allowEmpty: true }) @@ -400,11 +361,9 @@ function optimizeTask(opts) { }; } function minifyTask(src, sourceMapBaseUrl) { - const esbuild = require('esbuild'); const sourceMappingURL = sourceMapBaseUrl ? ((f) => `${sourceMapBaseUrl}/${f.relative}.map`) : undefined; return cb => { const cssnano = require('cssnano'); - const sourcemaps = require('gulp-sourcemaps'); const svgmin = require('gulp-svgmin'); const jsFilter = filter('**/*.js', { restore: true }); const cssFilter = filter('**/*.css', { restore: true }); @@ -416,7 +375,7 @@ function minifyTask(src, sourceMapBaseUrl) { sourcemap: 'external', outdir: '.', platform: 'node', - target: ['esnext'], + target: ['es2022'], write: false }).then(res => { const jsFile = res.outputFiles.find(f => /\.js$/.test(f.path)); diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index ad3fc19682b..e70f2de9261 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -10,6 +10,7 @@ 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 fs from 'fs'; import * as pump from 'pump'; import * as VinylFile from 'vinyl'; import * as bundle from './bundle'; @@ -17,7 +18,8 @@ import { Language, processNlsFiles } from './i18n'; import { createStatsStream } from './stats'; import * as util from './util'; import { gulpPostcss } from './postcss'; -import type { Plugin } from 'esbuild'; +import * as esbuild from 'esbuild'; +import * as sourcemaps from 'gulp-sourcemaps'; import { isESM } from './esm'; const REPO_ROOT_PATH = path.join(__dirname, '../..'); @@ -221,8 +223,6 @@ function optimizeAMDTask(opts: IOptimizeAMDTaskOpts): NodeJS.ReadWriteStream { const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; const fileContentMapper = opts.fileContentMapper || ((contents: string, _path: string) => contents); - 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 @@ -274,24 +274,15 @@ function optimizeAMDTask(opts: IOptimizeAMDTaskOpts): NodeJS.ReadWriteStream { } function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJSTaskOpts): NodeJS.ReadWriteStream { - // TODO@esm honor IEntryPoint#prepred/append (unused?) - - const esbuild = require('esbuild') as typeof import('esbuild'); - - const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; - const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); - const resourcesStream = es.through(); // this stream will contain the resources const bundlesStream = es.through(); // this stream will contain the bundled files - const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json const entryPoints = opts.entryPoints.filter(d => d.target !== 'amd'); if (cjsOpts) { cjsOpts.entryPoints.forEach(entryPoint => entryPoints.push({ name: path.parse(entryPoint).name })); } - // TODO@esm remove hardcoded entry point and support `dest` of `IEntryPoint` or clean that up - entryPoints.push({ name: 'vs/base/worker/workerMain' }); + entryPoints.push({ name: 'vs/base/worker/workerMain' }); // TODO@esm remove hardcoded entry point when workers are cleaned up const allMentionedModules = new Set(); for (const entryPoint of entryPoints) { @@ -300,31 +291,26 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules); } - // TODO@esm remove this from the bundle files - allMentionedModules.delete('vs/css'); + allMentionedModules.delete('vs/css'); // TODO@esm remove this when vs/css is removed const bundleAsync = async () => { - let bundleData: bundle.IBundleData | undefined; const files: VinylFile[] = []; const tasks: Promise[] = []; for (const entryPoint of entryPoints) { - const t1 = performance.now(); - console.log(`[bundle] STARTING '${entryPoint.name}'...`); + console.log(`[bundle] '${entryPoint.name}'`); // support for 'dest' via esbuild#in/out const dest = entryPoint.dest?.replace(/\.[^/.]+$/, '') ?? entryPoint.name; - // const dest = entryPoint.name; // boilerplate massage const banner = { js: '' }; - const fs = await import('node:fs'); const tslibPath = path.join(require.resolve('tslib'), '../tslib.es6.js'); banner.js += await fs.promises.readFile(tslibPath, 'utf-8'); - const boilerplateTrimmer: Plugin = { + const boilerplateTrimmer: esbuild.Plugin = { name: 'boilerplate-trimmer', setup(build) { build.onLoad({ filter: /\.js$/ }, async args => { @@ -344,22 +330,21 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS } } - const task = esbuild.build({ - logLevel: 'silent', bundle: true, external: entryPoint.exclude, packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages platform: 'neutral', // makes esm format: 'esm', plugins: [boilerplateTrimmer], - target: ['es2023'], + target: ['es2022'], loader: { '.ttf': 'file', '.svg': 'file', '.png': 'file', '.sh': 'file', }, + assetNames: 'media/[name]', // moves media assets into a sub-folder "media" banner, entryPoints: [ { @@ -372,30 +357,6 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS metafile: true, // enables res.metafile }).then(res => { - console.log(`[bundle] DONE for '${entryPoint.name}' (${Math.round(performance.now() - t1)}ms)`); - - if (opts.bundleInfo) { - // TODO@esm validate that bundleData is correct - bundleData ??= { graph: {}, bundles: {} }; - - function pathToModule(path: string) { - return path - .replace(new RegExp(`^${opts.src}\\/`), '') - .replace(/\.js$/, ''); - } - - for (const [path, value] of Object.entries(res.metafile.outputs)) { - const entryModule = pathToModule(path); - const inputModules = Object.keys(value.inputs).map(pathToModule); - bundleData.bundles[entryModule] = inputModules; - } - - for (const [input, value] of Object.entries(res.metafile.inputs)) { - const dependencies = value.imports.map(i => pathToModule(i.path)); - bundleData.graph[pathToModule(input)] = dependencies; - } - } - for (const file of res.outputFiles) { let contents = file.contents; @@ -427,7 +388,7 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS } await Promise.all(tasks); - return { files, bundleData }; + return { files }; }; bundleAsync().then((output) => { @@ -435,25 +396,13 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS // bundle output (JS, CSS, SVG...) es.readArray(output.files).pipe(bundlesStream); - // bundeInfo.json - const bundleInfoArray: VinylFile[] = []; - if (typeof output.bundleData === 'object') { - bundleInfoArray.push(new VinylFile({ - path: 'bundleInfo.json', - base: '.', - contents: Buffer.from(JSON.stringify(output.bundleData, null, '\t')) - })); - } - es.readArray(bundleInfoArray).pipe(bundleInfoStream); - // forward all resources gulp.src(opts.resources, { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream); }); const result = es.merge( bundlesStream, - resourcesStream, - bundleInfoStream + resourcesStream ); return result @@ -464,7 +413,7 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS })) .pipe(opts.languages && opts.languages.length ? processNlsFiles({ out: opts.src, - fileHeader: bundledFileHeader, + fileHeader: opts.header || DEFAULT_FILE_HEADER, languages: opts.languages }) : es.through()); } @@ -489,8 +438,6 @@ export interface IOptimizeCommonJSTaskOpts { } function optimizeCommonJSTask(opts: IOptimizeCommonJSTaskOpts): NodeJS.ReadWriteStream { - const esbuild = require('esbuild') as typeof import('esbuild'); - const src = opts.src; const entryPoints = opts.entryPoints; @@ -578,12 +525,10 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr } 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 sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); const svgmin = require('gulp-svgmin') as typeof import('gulp-svgmin'); const jsFilter = filter('**/*.js', { restore: true }); @@ -601,7 +546,7 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => sourcemap: 'external', outdir: '.', platform: 'node', - target: ['esnext'], + target: ['es2022'], write: false }).then(res => { const jsFile = res.outputFiles.find(f => /\.js$/.test(f.path))!; diff --git a/src/buildfile.js b/src/buildfile.js index d29ae17a40f..f3341b51c66 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +const { isESM } = require('../build/lib/esm'); + /** * @param {string} name * @param {string[]=} exclude @@ -66,22 +68,37 @@ exports.workerNotebook = createEditorWorkerModuleDescription('vs/workbench/contr exports.workerLanguageDetection = createEditorWorkerModuleDescription('vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker'); exports.workerLocalFileSearch = createEditorWorkerModuleDescription('vs/workbench/services/search/worker/localFileSearch'); exports.workerProfileAnalysis = createEditorWorkerModuleDescription('vs/platform/profiling/electron-sandbox/profileAnalysisWorker'); +exports.workerOutputLinks = createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer', true); +exports.workerBackgroundTokenization = createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker', true); -exports.workbenchDesktop = [ - ...createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer', true), - ...createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker', true), - createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), - createModuleDescription('vs/platform/files/node/watcher/watcherMain'), - createModuleDescription('vs/platform/terminal/node/ptyHostMain'), - createModuleDescription('vs/workbench/api/node/extensionHostProcess'), - createModuleDescription('vs/workbench/contrib/issue/electron-sandbox/issueReporterMain'), -]; +exports.workbenchDesktop = function () { + return isESM() ? [ + createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), + createModuleDescription('vs/platform/files/node/watcher/watcherMain'), + createModuleDescription('vs/platform/terminal/node/ptyHostMain'), + createModuleDescription('vs/workbench/api/node/extensionHostProcess'), + createModuleDescription('vs/workbench/contrib/issue/electron-sandbox/issueReporterMain'), + createModuleDescription('vs/workbench/workbench.desktop.main') + ] : [ + ...createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer', true), + ...createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker', true), + createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), + createModuleDescription('vs/platform/files/node/watcher/watcherMain'), + createModuleDescription('vs/platform/terminal/node/ptyHostMain'), + createModuleDescription('vs/workbench/api/node/extensionHostProcess'), + createModuleDescription('vs/workbench/contrib/issue/electron-sandbox/issueReporterMain'), + ]; +}; -exports.workbenchWeb = [ - ...createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer', true), - ...createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker', true), - createModuleDescription('vs/code/browser/workbench/workbench', ['vs/workbench/workbench.web.main']) -]; +exports.workbenchWeb = function () { + return isESM() ? [ + createModuleDescription('vs/workbench/workbench.web.main') + ] : [ + ...createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer', true), + ...createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker', true), + createModuleDescription('vs/code/browser/workbench/workbench', ['vs/workbench/workbench.web.main']) + ]; +}; exports.keyboardMaps = [ createModuleDescription('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux'), @@ -97,4 +114,8 @@ exports.code = [ createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain') ]; +exports.codeWeb = [ + createModuleDescription('vs/code/browser/workbench/workbench') +]; + exports.entrypoint = createModuleDescription; diff --git a/src/vs/code/browser/workbench/workbench.esm.html b/src/vs/code/browser/workbench/workbench.esm.html index 38c03e6a321..77881982735 100644 --- a/src/vs/code/browser/workbench/workbench.esm.html +++ b/src/vs/code/browser/workbench/workbench.esm.html @@ -25,7 +25,7 @@ - + diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index db947d6c029..58e39456485 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -117,8 +117,7 @@ export async function main(argv: string[]): Promise { // Extensions Management else if (shouldSpawnCliProcess(args)) { - - const cli = await import('vs/code/node/cliProcessMain'); + const cli = await import(['vs', 'code', 'node', 'cliProcessMain'].join('/') /* TODO@esm workaround to prevent esbuild from inlining this */); await cli.main(args); return;