Prepare Code for integration with code-server 1. We already have the arguments so allow passing them in. There is also a slight change in a few directories to preserve the directory structure we have been using and to not override passed-in arguments. 2. Modify the entry point to allow importing the code, instead of just running the server immediately. 3. Modify the terminal environment to filter out code-server environment variables. 4. Add the code-server version to the help dialog. 5. Add ready events for use in an iframe. 6. Add our icons and remove the existing ones. 7. Use our own manifest. Index: code-server/lib/vscode/src/vs/server/node/server.main.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/server.main.ts +++ code-server/lib/vscode/src/vs/server/node/server.main.ts @@ -12,7 +12,7 @@ import { createServer as doCreateServer, import { parseArgs, ErrorReporter } from '../../platform/environment/node/argv.js'; import { join, dirname } from '../../base/common/path.js'; import { performance } from 'perf_hooks'; -import { serverOptions } from './serverEnvironmentService.js'; +import { serverOptions, ServerParsedArgs } from './serverEnvironmentService.js'; import product from '../../platform/product/common/product.js'; import * as perf from '../../base/common/performance.js'; @@ -34,38 +34,47 @@ const errorReporter: ErrorReporter = { } }; -const args = parseArgs(process.argv.slice(2), serverOptions, errorReporter); +function parse(): ServerParsedArgs { + return parseArgs(process.argv.slice(2), serverOptions, errorReporter); +} -const REMOTE_DATA_FOLDER = args['server-data-dir'] || process.env['VSCODE_AGENT_FOLDER'] || join(os.homedir(), product.serverDataFolderName || '.vscode-remote'); -const USER_DATA_PATH = join(REMOTE_DATA_FOLDER, 'data'); -const APP_SETTINGS_HOME = join(USER_DATA_PATH, 'User'); -const GLOBAL_STORAGE_HOME = join(APP_SETTINGS_HOME, 'globalStorage'); -const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History'); -const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine'); -args['user-data-dir'] = USER_DATA_PATH; -const APP_ROOT = dirname(FileAccess.asFileUri('').fsPath); -const BUILTIN_EXTENSIONS_FOLDER_PATH = join(APP_ROOT, 'extensions'); -args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH; -args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions'); - -[REMOTE_DATA_FOLDER, args['extensions-dir'], USER_DATA_PATH, APP_SETTINGS_HOME, MACHINE_SETTINGS_HOME, GLOBAL_STORAGE_HOME, LOCAL_HISTORY_HOME].forEach(f => { - try { - if (!fs.existsSync(f)) { - fs.mkdirSync(f, { mode: 0o700 }); - } - } catch (err) { console.error(err); } -}); +function createDirs(args: ServerParsedArgs): string { + const REMOTE_DATA_FOLDER = args['server-data-dir'] || args['user-data-dir'] || process.env['VSCODE_AGENT_FOLDER'] || join(os.homedir(), product.serverDataFolderName || '.vscode-remote'); + const USER_DATA_PATH = args['user-data-dir'] || join(REMOTE_DATA_FOLDER, 'data'); + const APP_SETTINGS_HOME = join(USER_DATA_PATH, 'User'); + const GLOBAL_STORAGE_HOME = join(APP_SETTINGS_HOME, 'globalStorage'); + const LOCAL_HISTORY_HOME = join(APP_SETTINGS_HOME, 'History'); + const MACHINE_SETTINGS_HOME = join(USER_DATA_PATH, 'Machine'); + args['user-data-dir'] = USER_DATA_PATH; + const APP_ROOT = dirname(FileAccess.asFileUri('').fsPath); + const BUILTIN_EXTENSIONS_FOLDER_PATH = args['builtin-extensions-dir'] || join(APP_ROOT, 'extensions'); + args['builtin-extensions-dir'] = BUILTIN_EXTENSIONS_FOLDER_PATH; + args['extensions-dir'] = args['extensions-dir'] || join(REMOTE_DATA_FOLDER, 'extensions'); + + [REMOTE_DATA_FOLDER, args['extensions-dir'], USER_DATA_PATH, APP_SETTINGS_HOME, MACHINE_SETTINGS_HOME, GLOBAL_STORAGE_HOME, LOCAL_HISTORY_HOME].forEach(f => { + try { + if (!fs.existsSync(f)) { + fs.mkdirSync(f, { mode: 0o700 }); + } + } catch (err) { console.error(err); } + }); + return REMOTE_DATA_FOLDER; +} /** * invoked by server-main.js */ -export function spawnCli() { - runCli(args, REMOTE_DATA_FOLDER, serverOptions); +function spawnCli(args = parse()): Promise { + return runCli(args, createDirs(args), serverOptions); } /** * invoked by server-main.js */ -export function createServer(address: string | net.AddressInfo | null): Promise { - return doCreateServer(address, args, REMOTE_DATA_FOLDER); +function createServer(address: string | net.AddressInfo | null, args = parse()): Promise { + return doCreateServer(address, args, createDirs(args)); } + +// The aliases prevent the names getting mangled during minification which would +// make it difficult to import. +export { spawnCli as spawnCli, createServer as createServer }; Index: code-server/lib/vscode/src/vs/base/common/processes.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/processes.ts +++ code-server/lib/vscode/src/vs/base/common/processes.ts @@ -111,6 +111,8 @@ export function sanitizeProcessEnvironme /^VSCODE_(?!(PORTABLE|SHELL_LOGIN|ENV_REPLACE|ENV_APPEND|ENV_PREPEND)).+$/, /^SNAP(|_.*)$/, /^GDK_PIXBUF_.+$/, + /^CODE_SERVER_.+$/, + /^CS_.+$/, ]; const envKeys = Object.keys(env); envKeys Index: code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts +++ code-server/lib/vscode/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts @@ -77,8 +77,11 @@ export class BrowserDialogHandler extend async about(): Promise { const detailString = (useAgo: boolean): string => { - return localize('aboutDetail', - "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", + return localize('aboutCodeServerDetail', + "code-server: {0}", + this.productService.codeServerVersion ? `v${this.productService.codeServerVersion}` : 'Unknown' + ) + '\n' + localize('aboutDetail', + "Code: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", this.productService.version || 'Unknown', this.productService.commit || 'Unknown', this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown', Index: code-server/lib/vscode/src/vs/workbench/browser/client.ts =================================================================== --- /dev/null +++ code-server/lib/vscode/src/vs/workbench/browser/client.ts @@ -0,0 +1,46 @@ +import { Disposable } from "../../base/common/lifecycle.js"; + +export class CodeServerClient extends Disposable { + constructor ( + ) { + super(); + } + + async startup(): Promise { + // Emit ready events + const event = new CustomEvent('ide-ready'); + window.dispatchEvent(event); + + if (parent) { + // Tell the parent loading has completed. + parent.postMessage({ event: 'loaded' }, '*'); + + // Proxy or stop proxing events as requested by the parent. + const listeners = new Map void>(); + + window.addEventListener('message', parentEvent => { + const eventName = parentEvent.data.bind || parentEvent.data.unbind; + if (eventName) { + const oldListener = listeners.get(eventName); + if (oldListener) { + document.removeEventListener(eventName, oldListener); + } + } + + if (parentEvent.data.bind && parentEvent.data.prop) { + const listener = (event: Event) => { + parent?.postMessage( + { + event: parentEvent.data.event, + [parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event], + }, + window.location.origin, + ); + }; + listeners.set(parentEvent.data.bind, listener); + document.addEventListener(parentEvent.data.bind, listener); + } + }); + } + } +} Index: code-server/lib/vscode/src/vs/workbench/browser/web.main.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/workbench/browser/web.main.ts +++ code-server/lib/vscode/src/vs/workbench/browser/web.main.ts @@ -64,6 +64,7 @@ import { IOpenerService } from '../../pl import { mixin, safeStringify } from '../../base/common/objects.js'; import { IndexedDB } from '../../base/browser/indexedDB.js'; import { WebFileSystemAccess } from '../../platform/files/browser/webFileSystemAccess.js'; +import { CodeServerClient } from '../../workbench/browser/client.js'; import { ITelemetryService } from '../../platform/telemetry/common/telemetry.js'; import { IProgressService } from '../../platform/progress/common/progress.js'; import { DelayedLogChannel } from '../services/output/common/delayedLogChannel.js'; @@ -131,6 +132,9 @@ export class BrowserMain extends Disposa // Startup const instantiationService = workbench.startup(); + const codeServerClient = this._register(instantiationService.createInstance(CodeServerClient)); + await codeServerClient.startup(); + // Window this._register(instantiationService.createInstance(BrowserWindow)); Index: code-server/lib/vscode/src/vs/base/common/product.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/base/common/product.ts +++ code-server/lib/vscode/src/vs/base/common/product.ts @@ -55,6 +55,8 @@ export type ExtensionVirtualWorkspaceSup }; export interface IProductConfiguration { + readonly codeServerVersion?: string + readonly version: string; readonly date?: string; readonly quality?: string; Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html =================================================================== --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench-dev.html @@ -11,7 +11,8 @@ - + + @@ -26,8 +27,9 @@ - - + + + Index: code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html =================================================================== --- code-server.orig/lib/vscode/src/vs/code/browser/workbench/workbench.html +++ code-server/lib/vscode/src/vs/code/browser/workbench/workbench.html @@ -11,7 +11,8 @@ - + + @@ -23,8 +24,9 @@ - - + + + Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts =================================================================== --- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts +++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts @@ -308,6 +308,7 @@ export class WebClientServer { } : undefined; const productConfiguration = { + codeServerVersion: this._productService.codeServerVersion, embedderIdentifier: 'server-distro', extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? { ...this._productService.extensionsGallery, Index: code-server/lib/vscode/src/server-main.ts =================================================================== --- code-server.orig/lib/vscode/src/server-main.ts +++ code-server/lib/vscode/src/server-main.ts @@ -25,6 +25,9 @@ const __dirname = path.dirname(fileURLTo perf.mark('code/server/start'); (globalThis as any).vscodeServerStartTime = performance.now(); +// This is not indented to make the diff less noisy. We need to move this out +// of the top-level so it will not run immediately and we can control the start. +async function start() { // Do a quick parse to determine if a server or the cli needs to be started const parsedArgs = minimist(process.argv.slice(2), { boolean: ['start-server', 'list-extensions', 'print-ip-address', 'help', 'version', 'accept-server-license-terms', 'update-extensions'], @@ -153,6 +156,7 @@ if (shouldSpawnCli) { } }); } +} function sanitizeStringArg(val: any): string | undefined { if (Array.isArray(val)) { // if an argument is passed multiple times, minimist creates an array @@ -283,3 +287,22 @@ function prompt(question: string): Promi }); }); } + +async function loadCodeWithNls() { + const nlsConfiguration = await resolveNLSConfiguration({ + userLocale: 'en', + osLocale: 'en', + commit: product.commit, + userDataPath: '', + nlsMetadataPath: __dirname, + }); + return loadCode(nlsConfiguration); +} + +// This alias prevents the name getting mangled during minification which would +// make it difficult to import. +export { loadCodeWithNls as loadCodeWithNls }; + +if (!process.env.CODE_SERVER_PARENT_PID) { + start(); +}