mirror of https://github.com/coder/code-server.git
Use server-main.js to load VS Code
It looks like the bootstrap files are now bundled so we can no longer require them. We could make them included again, but maybe it is better to go through the main entrypoint anyway because it includes some nls stuff which is maybe necessary. This also fixes what looks like a bug where we could create two servers if two requests came in. I am not sure what the practical consequences of that would be, but it will no longer do that.
This commit is contained in:
parent
3140312fe1
commit
a78c0db5d5
|
@ -272,3 +272,18 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
||||||
embedderIdentifier: 'server-distro',
|
embedderIdentifier: 'server-distro',
|
||||||
extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? {
|
extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? {
|
||||||
...this._productService.extensionsGallery,
|
...this._productService.extensionsGallery,
|
||||||
|
Index: code-server/lib/vscode/src/server-main.js
|
||||||
|
===================================================================
|
||||||
|
--- code-server.orig/lib/vscode/src/server-main.js
|
||||||
|
+++ code-server/lib/vscode/src/server-main.js
|
||||||
|
@@ -336,4 +336,9 @@ function prompt(question) {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
-start();
|
||||||
|
+async function loadCodeWithNls() {
|
||||||
|
+ const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname });
|
||||||
|
+ return loadCode(nlsConfiguration);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+module.exports.loadCodeWithNls = loadCodeWithNls;
|
||||||
|
|
|
@ -837,11 +837,6 @@ export interface CodeArgs extends UserProvidedCodeArgs {
|
||||||
log?: string[]
|
log?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Types for ../../lib/vscode/src/vs/server/node/server.main.ts:65.
|
|
||||||
*/
|
|
||||||
export type SpawnCodeCli = (args: CodeArgs) => Promise<void>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert our arguments to equivalent VS Code server arguments.
|
* Convert our arguments to equivalent VS Code server arguments.
|
||||||
* Does not add any extra arguments.
|
* Does not add any extra arguments.
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { field, logger } from "@coder/logger"
|
import { field, logger } from "@coder/logger"
|
||||||
import http from "http"
|
import http from "http"
|
||||||
|
import * as path from "path"
|
||||||
import { Disposable } from "../common/emitter"
|
import { Disposable } from "../common/emitter"
|
||||||
import { plural } from "../common/util"
|
import { plural } from "../common/util"
|
||||||
import { createApp, ensureAddress } from "./app"
|
import { createApp, ensureAddress } from "./app"
|
||||||
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
|
import { AuthType, DefaultedArgs, Feature, toCodeArgs, UserProvidedArgs } from "./cli"
|
||||||
import { commit, version } from "./constants"
|
import { commit, version, vsRootPath } from "./constants"
|
||||||
import { register } from "./routes"
|
import { register } from "./routes"
|
||||||
import { isDirectory, loadAMDModule, open } from "./util"
|
import { VSCodeModule } from "./routes/vscode"
|
||||||
|
import { isDirectory, open } from "./util"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the user passed an extension-related VS Code flag.
|
* Return true if the user passed an extension-related VS Code flag.
|
||||||
|
@ -46,12 +48,10 @@ export interface OpenCommandPipeArgs {
|
||||||
*/
|
*/
|
||||||
export const runCodeCli = async (args: DefaultedArgs): Promise<void> => {
|
export const runCodeCli = async (args: DefaultedArgs): Promise<void> => {
|
||||||
logger.debug("Running Code CLI")
|
logger.debug("Running Code CLI")
|
||||||
|
|
||||||
// See ../../lib/vscode/src/vs/server/node/server.main.ts:65.
|
|
||||||
const spawnCli = await loadAMDModule<SpawnCodeCli>("vs/server/node/server.main", "spawnCli")
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await spawnCli(await toCodeArgs(args))
|
const mod = require(path.join(vsRootPath, "out/server-main")) as VSCodeModule
|
||||||
|
const serverModule = await mod.loadCodeWithNls()
|
||||||
|
await serverModule.spawnCli(await toCodeArgs(args))
|
||||||
// Rather than have the caller handle errors and exit, spawnCli will exit
|
// Rather than have the caller handle errors and exit, spawnCli will exit
|
||||||
// itself. Additionally, it does this on a timeout set to 0. So, try
|
// itself. Additionally, it does this on a timeout set to 0. So, try
|
||||||
// waiting for VS Code to exit before giving up and doing it ourselves.
|
// waiting for VS Code to exit before giving up and doing it ourselves.
|
||||||
|
|
|
@ -8,10 +8,10 @@ import * as path from "path"
|
||||||
import { WebsocketRequest } from "../../../typings/pluginapi"
|
import { WebsocketRequest } from "../../../typings/pluginapi"
|
||||||
import { logError } from "../../common/util"
|
import { logError } from "../../common/util"
|
||||||
import { CodeArgs, toCodeArgs } from "../cli"
|
import { CodeArgs, toCodeArgs } from "../cli"
|
||||||
import { isDevMode } from "../constants"
|
import { isDevMode, vsRootPath } from "../constants"
|
||||||
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http"
|
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http"
|
||||||
import { SocketProxyProvider } from "../socket"
|
import { SocketProxyProvider } from "../socket"
|
||||||
import { isFile, loadAMDModule } from "../util"
|
import { isFile } from "../util"
|
||||||
import { Router as WsRouter } from "../wsRouter"
|
import { Router as WsRouter } from "../wsRouter"
|
||||||
|
|
||||||
export const router = express.Router()
|
export const router = express.Router()
|
||||||
|
@ -31,11 +31,46 @@ export interface IVSCodeServerAPI {
|
||||||
dispose(): void
|
dispose(): void
|
||||||
}
|
}
|
||||||
|
|
||||||
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
|
/**
|
||||||
export type CreateServer = (address: string | net.AddressInfo | null, args: CodeArgs) => Promise<IVSCodeServerAPI>
|
* VS Code's CLI entrypoint (../../../lib/vscode/src/server-main.js).
|
||||||
|
*
|
||||||
|
* Normally VS Code will run `node server-main.js` which starts either the web
|
||||||
|
* server or the CLI (for installing extensions, etc) but we patch it so we can
|
||||||
|
* `require` it and call its functions directly in order to integrate with our
|
||||||
|
* web server.
|
||||||
|
*/
|
||||||
|
export type VSCodeModule = {
|
||||||
|
// See ../../../lib/vscode/src/server-main.js:339.
|
||||||
|
loadCodeWithNls(): {
|
||||||
|
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
|
||||||
|
createServer(address: string | net.AddressInfo | null, args: CodeArgs): Promise<IVSCodeServerAPI>
|
||||||
|
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:65.
|
||||||
|
spawnCli(args: CodeArgs): Promise<void>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The VS Code server is dynamically loaded in when a request is made to this
|
/**
|
||||||
// router by `ensureCodeServerLoaded`.
|
* Load then create the VS Code server.
|
||||||
|
*/
|
||||||
|
async function loadVSCode(req: express.Request): Promise<IVSCodeServerAPI> {
|
||||||
|
const mod = require(path.join(vsRootPath, "out/server-main")) as VSCodeModule
|
||||||
|
const serverModule = await mod.loadCodeWithNls()
|
||||||
|
return serverModule.createServer(null, {
|
||||||
|
...(await toCodeArgs(req.args)),
|
||||||
|
"accept-server-license-terms": true,
|
||||||
|
// This seems to be used to make the connection token flags optional (when
|
||||||
|
// set to 1.63) but we have always included them.
|
||||||
|
compatibility: "1.64",
|
||||||
|
"without-connection-token": true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// To prevent loading the module more than once at a time. We also have the
|
||||||
|
// resolved value so you do not need to `await` everywhere.
|
||||||
|
let vscodeServerPromise: Promise<IVSCodeServerAPI> | undefined
|
||||||
|
|
||||||
|
// The resolved value from the dynamically loaded VS Code server. Do not use
|
||||||
|
// without first calling and awaiting `ensureCodeServerLoaded`.
|
||||||
let vscodeServer: IVSCodeServerAPI | undefined
|
let vscodeServer: IVSCodeServerAPI | undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,21 +84,21 @@ export const ensureVSCodeLoaded = async (
|
||||||
if (vscodeServer) {
|
if (vscodeServer) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
|
if (!vscodeServerPromise) {
|
||||||
const createVSServer = await loadAMDModule<CreateServer>("vs/server/node/server.main", "createServer")
|
vscodeServerPromise = loadVSCode(req)
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
vscodeServer = await createVSServer(null, {
|
vscodeServer = await vscodeServerPromise
|
||||||
...(await toCodeArgs(req.args)),
|
|
||||||
"accept-server-license-terms": true,
|
|
||||||
// This seems to be used to make the connection token flags optional (when
|
|
||||||
// set to 1.63) but we have always included them.
|
|
||||||
compatibility: "1.64",
|
|
||||||
"without-connection-token": true,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
vscodeServerPromise = undefined // Unset so we can try again.
|
||||||
logError(logger, "CodeServerRouteWrapper", error)
|
logError(logger, "CodeServerRouteWrapper", error)
|
||||||
if (isDevMode) {
|
if (isDevMode) {
|
||||||
return next(new Error((error instanceof Error ? error.message : error) + " (VS Code may still be compiling)"))
|
return next(
|
||||||
|
new Error(
|
||||||
|
(error instanceof Error ? error.message : error) +
|
||||||
|
" (Have you applied the patches? If so, VS Code may still be compiling)",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return next(error)
|
return next(error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import * as path from "path"
|
||||||
import safeCompare from "safe-compare"
|
import safeCompare from "safe-compare"
|
||||||
import * as util from "util"
|
import * as util from "util"
|
||||||
import xdgBasedir from "xdg-basedir"
|
import xdgBasedir from "xdg-basedir"
|
||||||
import { vsRootPath } from "./constants"
|
|
||||||
|
|
||||||
export interface Paths {
|
export interface Paths {
|
||||||
data: string
|
data: string
|
||||||
|
@ -503,31 +502,6 @@ export function isNodeJSErrnoException(error: unknown): error is NodeJS.ErrnoExc
|
||||||
// TODO: Replace with proper templating system.
|
// TODO: Replace with proper templating system.
|
||||||
export const escapeJSON = (value: cp.Serializable) => JSON.stringify(value).replace(/"/g, """)
|
export const escapeJSON = (value: cp.Serializable) => JSON.stringify(value).replace(/"/g, """)
|
||||||
|
|
||||||
type AMDModule<T> = { [exportName: string]: T }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads AMD module, typically from a compiled VSCode bundle.
|
|
||||||
*
|
|
||||||
* @deprecated This should be gradually phased out as code-server migrates to lib/vscode
|
|
||||||
* @param amdPath Path to module relative to lib/vscode
|
|
||||||
* @param exportName Given name of export in the file
|
|
||||||
*/
|
|
||||||
export const loadAMDModule = async <T>(amdPath: string, exportName: string): Promise<T> => {
|
|
||||||
// Set default remote native node modules path, if unset
|
|
||||||
process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"] =
|
|
||||||
process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"] || path.join(vsRootPath, "remote", "node_modules")
|
|
||||||
|
|
||||||
require(path.join(vsRootPath, "out/bootstrap-node")).injectNodeModuleLookupPath(
|
|
||||||
process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"],
|
|
||||||
)
|
|
||||||
|
|
||||||
const module = await new Promise<AMDModule<T>>((resolve, reject) => {
|
|
||||||
require(path.join(vsRootPath, "out/bootstrap-amd")).load(amdPath, resolve, reject)
|
|
||||||
})
|
|
||||||
|
|
||||||
return module[exportName] as T
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Split a string on the first equals. The result will always be an array with
|
* Split a string on the first equals. The result will always be an array with
|
||||||
* two items regardless of how many equals there are. The second item will be
|
* two items regardless of how many equals there are. The second item will be
|
||||||
|
|
Loading…
Reference in New Issue