Skip to content

Commit 4b9aa6e

Browse files
committed
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.
1 parent ff6dbb2 commit 4b9aa6e

File tree

5 files changed

+75
-56
lines changed

5 files changed

+75
-56
lines changed

patches/integration.diff

+15
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,18 @@ Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
272272
embedderIdentifier: 'server-distro',
273273
extensionsGallery: this._webExtensionResourceUrlTemplate && this._productService.extensionsGallery ? {
274274
...this._productService.extensionsGallery,
275+
Index: code-server/lib/vscode/src/server-main.js
276+
===================================================================
277+
--- code-server.orig/lib/vscode/src/server-main.js
278+
+++ code-server/lib/vscode/src/server-main.js
279+
@@ -336,4 +336,9 @@ function prompt(question) {
280+
});
281+
}
282+
283+
-start();
284+
+async function loadCodeWithNls() {
285+
+ const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname });
286+
+ return loadCode(nlsConfiguration);
287+
+}
288+
+
289+
+module.exports.loadCodeWithNls = loadCodeWithNls;

src/node/cli.ts

-5
Original file line numberDiff line numberDiff line change
@@ -837,11 +837,6 @@ export interface CodeArgs extends UserProvidedCodeArgs {
837837
log?: string[]
838838
}
839839

840-
/**
841-
* Types for ../../lib/vscode/src/vs/server/node/server.main.ts:65.
842-
*/
843-
export type SpawnCodeCli = (args: CodeArgs) => Promise<void>
844-
845840
/**
846841
* Convert our arguments to equivalent VS Code server arguments.
847842
* Does not add any extra arguments.

src/node/main.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { field, logger } from "@coder/logger"
22
import http from "http"
3+
import * as path from "path"
34
import { Disposable } from "../common/emitter"
45
import { plural } from "../common/util"
56
import { createApp, ensureAddress } from "./app"
6-
import { AuthType, DefaultedArgs, Feature, SpawnCodeCli, toCodeArgs, UserProvidedArgs } from "./cli"
7-
import { commit, version } from "./constants"
7+
import { AuthType, DefaultedArgs, Feature, toCodeArgs, UserProvidedArgs } from "./cli"
8+
import { commit, version, vsRootPath } from "./constants"
89
import { register } from "./routes"
9-
import { isDirectory, loadAMDModule, open } from "./util"
10+
import { VSCodeModule } from "./routes/vscode"
11+
import { isDirectory, open } from "./util"
1012

1113
/**
1214
* Return true if the user passed an extension-related VS Code flag.
@@ -46,12 +48,10 @@ export interface OpenCommandPipeArgs {
4648
*/
4749
export const runCodeCli = async (args: DefaultedArgs): Promise<void> => {
4850
logger.debug("Running Code CLI")
49-
50-
// See ../../lib/vscode/src/vs/server/node/server.main.ts:65.
51-
const spawnCli = await loadAMDModule<SpawnCodeCli>("vs/server/node/server.main", "spawnCli")
52-
5351
try {
54-
await spawnCli(await toCodeArgs(args))
52+
const mod = require(path.join(vsRootPath, "out/server-main")) as VSCodeModule
53+
const serverModule = await mod.loadCodeWithNls()
54+
await serverModule.spawnCli(await toCodeArgs(args))
5555
// Rather than have the caller handle errors and exit, spawnCli will exit
5656
// itself. Additionally, it does this on a timeout set to 0. So, try
5757
// waiting for VS Code to exit before giving up and doing it ourselves.

src/node/routes/vscode.ts

+52-17
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import * as path from "path"
88
import { WebsocketRequest } from "../../../typings/pluginapi"
99
import { logError } from "../../common/util"
1010
import { CodeArgs, toCodeArgs } from "../cli"
11-
import { isDevMode } from "../constants"
11+
import { isDevMode, vsRootPath } from "../constants"
1212
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http"
1313
import { SocketProxyProvider } from "../socket"
14-
import { isFile, loadAMDModule } from "../util"
14+
import { isFile } from "../util"
1515
import { Router as WsRouter } from "../wsRouter"
1616

1717
export const router = express.Router()
@@ -31,11 +31,46 @@ export interface IVSCodeServerAPI {
3131
dispose(): void
3232
}
3333

34-
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
35-
export type CreateServer = (address: string | net.AddressInfo | null, args: CodeArgs) => Promise<IVSCodeServerAPI>
34+
/**
35+
* VS Code's CLI entrypoint (../../../lib/vscode/src/server-main.js).
36+
*
37+
* Normally VS Code will run `node server-main.js` which starts either the web
38+
* server or the CLI (for installing extensions, etc) but we patch it so we can
39+
* `require` it and call its functions directly in order to integrate with our
40+
* web server.
41+
*/
42+
export type VSCodeModule = {
43+
// See ../../../lib/vscode/src/server-main.js:339.
44+
loadCodeWithNls(): {
45+
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
46+
createServer(address: string | net.AddressInfo | null, args: CodeArgs): Promise<IVSCodeServerAPI>
47+
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:65.
48+
spawnCli(args: CodeArgs): Promise<void>
49+
}
50+
}
51+
52+
/**
53+
* Load then create the VS Code server.
54+
*/
55+
async function loadVSCode(req: express.Request): Promise<IVSCodeServerAPI> {
56+
const mod = require(path.join(vsRootPath, "out/server-main")) as VSCodeModule
57+
const serverModule = await mod.loadCodeWithNls()
58+
return serverModule.createServer(null, {
59+
...(await toCodeArgs(req.args)),
60+
"accept-server-license-terms": true,
61+
// This seems to be used to make the connection token flags optional (when
62+
// set to 1.63) but we have always included them.
63+
compatibility: "1.64",
64+
"without-connection-token": true,
65+
})
66+
}
3667

37-
// The VS Code server is dynamically loaded in when a request is made to this
38-
// router by `ensureCodeServerLoaded`.
68+
// To prevent loading the module more than once at a time. We also have the
69+
// resolved value so you do not need to `await` everywhere.
70+
let vscodeServerPromise: Promise<IVSCodeServerAPI> | undefined
71+
72+
// The resolved value from the dynamically loaded VS Code server. Do not use
73+
// without first calling and awaiting `ensureCodeServerLoaded`.
3974
let vscodeServer: IVSCodeServerAPI | undefined
4075

4176
/**
@@ -49,21 +84,21 @@ export const ensureVSCodeLoaded = async (
4984
if (vscodeServer) {
5085
return next()
5186
}
52-
// See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
53-
const createVSServer = await loadAMDModule<CreateServer>("vs/server/node/server.main", "createServer")
87+
if (!vscodeServerPromise) {
88+
vscodeServerPromise = loadVSCode(req)
89+
}
5490
try {
55-
vscodeServer = await createVSServer(null, {
56-
...(await toCodeArgs(req.args)),
57-
"accept-server-license-terms": true,
58-
// This seems to be used to make the connection token flags optional (when
59-
// set to 1.63) but we have always included them.
60-
compatibility: "1.64",
61-
"without-connection-token": true,
62-
})
91+
vscodeServer = await vscodeServerPromise
6392
} catch (error) {
93+
vscodeServerPromise = undefined // Unset so we can try again.
6494
logError(logger, "CodeServerRouteWrapper", error)
6595
if (isDevMode) {
66-
return next(new Error((error instanceof Error ? error.message : error) + " (VS Code may still be compiling)"))
96+
return next(
97+
new Error(
98+
(error instanceof Error ? error.message : error) +
99+
" (Have you applied the patches? If so, VS Code may still be compiling)",
100+
),
101+
)
67102
}
68103
return next(error)
69104
}

src/node/util.ts

-26
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import * as path from "path"
99
import safeCompare from "safe-compare"
1010
import * as util from "util"
1111
import xdgBasedir from "xdg-basedir"
12-
import { vsRootPath } from "./constants"
1312

1413
export interface Paths {
1514
data: string
@@ -503,31 +502,6 @@ export function isNodeJSErrnoException(error: unknown): error is NodeJS.ErrnoExc
503502
// TODO: Replace with proper templating system.
504503
export const escapeJSON = (value: cp.Serializable) => JSON.stringify(value).replace(/"/g, "&quot;")
505504

506-
type AMDModule<T> = { [exportName: string]: T }
507-
508-
/**
509-
* Loads AMD module, typically from a compiled VSCode bundle.
510-
*
511-
* @deprecated This should be gradually phased out as code-server migrates to lib/vscode
512-
* @param amdPath Path to module relative to lib/vscode
513-
* @param exportName Given name of export in the file
514-
*/
515-
export const loadAMDModule = async <T>(amdPath: string, exportName: string): Promise<T> => {
516-
// Set default remote native node modules path, if unset
517-
process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"] =
518-
process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"] || path.join(vsRootPath, "remote", "node_modules")
519-
520-
require(path.join(vsRootPath, "out/bootstrap-node")).injectNodeModuleLookupPath(
521-
process.env["VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH"],
522-
)
523-
524-
const module = await new Promise<AMDModule<T>>((resolve, reject) => {
525-
require(path.join(vsRootPath, "out/bootstrap-amd")).load(amdPath, resolve, reject)
526-
})
527-
528-
return module[exportName] as T
529-
}
530-
531505
/**
532506
* Split a string on the first equals. The result will always be an array with
533507
* two items regardless of how many equals there are. The second item will be

0 commit comments

Comments
 (0)