Skip to content

Commit 6e809b6

Browse files
committed
Handle when VS Code fails to load
This is mostly for development where VS Code might not have finished compiling yet.
1 parent 7c6fe56 commit 6e809b6

File tree

9 files changed

+205
-183
lines changed

9 files changed

+205
-183
lines changed

scripts/build.ts

+5-12
Original file line numberDiff line numberDiff line change
@@ -246,18 +246,11 @@ class Builder {
246246
await this.task("copying vs code into build directory", async () => {
247247
await fs.mkdirp(vscodeBuildPath)
248248
await Promise.all([
249-
(async (): Promise<void> => {
250-
await fs.move(
251-
path.join(this.vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`),
252-
path.join(vscodeBuildPath, "out")
253-
)
254-
await fs.remove(path.join(vscodeBuildPath, "out/vs/server/browser/workbench.html"))
255-
await fs.move(
256-
path.join(vscodeBuildPath, "out/vs/server/browser/workbench-build.html"),
257-
path.join(vscodeBuildPath, "out/vs/server/browser/workbench.html")
258-
)
259-
})(),
260-
await fs.copy(path.join(this.vscodeSourcePath, ".build/extensions"), path.join(vscodeBuildPath, "extensions")),
249+
fs.move(
250+
path.join(this.vscodeSourcePath, `out-vscode${process.env.MINIFY ? "-min" : ""}`),
251+
path.join(vscodeBuildPath, "out")
252+
),
253+
fs.copy(path.join(this.vscodeSourcePath, ".build/extensions"), path.join(vscodeBuildPath, "extensions")),
261254
])
262255
})
263256

scripts/vscode.patch

+5-164
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ index d0f6e6b18a..1966fd297d 100644
418418
-
419419
diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts
420420
new file mode 100644
421-
index 0000000000..8e7c5af184
421+
index 0000000000..3a62205b38
422422
--- /dev/null
423423
+++ b/src/vs/server/browser/client.ts
424424
@@ -0,0 +1,162 @@
@@ -532,12 +532,12 @@ index 0000000000..8e7c5af184
532532
+ if (!window.isSecureContext) {
533533
+ (services.get(INotificationService) as INotificationService).notify({
534534
+ severity: Severity.Warning,
535-
+ message: "code-server is being accessed over an insecure domain. Some functionality may not work as expected.",
535+
+ message: 'code-server is being accessed over an insecure domain. Some functionality may not work as expected.',
536536
+ actions: {
537537
+ primary: [{
538-
+ id: "understand",
539-
+ label: "I understand",
540-
+ tooltip: "",
538+
+ id: 'understand',
539+
+ label: 'I understand',
540+
+ tooltip: '',
541541
+ class: undefined,
542542
+ enabled: true,
543543
+ checked: true,
@@ -679,165 +679,6 @@ index 0000000000..0d2e93edae
679679
+ this.disposed = true;
680680
+ }
681681
+}
682-
diff --git a/src/vs/server/browser/workbench-build.html b/src/vs/server/browser/workbench-build.html
683-
new file mode 100644
684-
index 0000000000..50f48cd74c
685-
--- /dev/null
686-
+++ b/src/vs/server/browser/workbench-build.html
687-
@@ -0,0 +1,92 @@
688-
+<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
689-
+<!DOCTYPE html>
690-
+<html>
691-
+ <head>
692-
+ <meta charset="utf-8" />
693-
+
694-
+ <!-- Disable pinch zooming -->
695-
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
696-
+
697-
+ <!-- Workbench Configuration -->
698-
+ <meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}">
699-
+
700-
+ <!-- Workarounds/Hacks (remote user data uri) -->
701-
+ <meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
702-
+ <!-- NOTE@coder: Added the commit for use in caching, the product for the
703-
+ extensions gallery URL, and nls for language support. -->
704-
+ <meta id="vscode-remote-commit" data-settings="{{COMMIT}}">
705-
+ <meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}">
706-
+ <meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
707-
+
708-
+ <!-- Workbench Icon/Manifest/CSS -->
709-
+ <link rel="icon" href="../static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
710-
+ <link rel="manifest" href="../static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
711-
+ <link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css">
712-
+ <link rel="apple-touch-icon" href="./static-{{COMMIT}}/src/browser/media/code-server.png" />
713-
+ <meta name="apple-mobile-web-app-capable" content="yes">
714-
+
715-
+ <!-- Prefetch to avoid waterfall -->
716-
+ <link rel="prefetch" href="./static-{{COMMIT}}/node_modules/semver-umd/lib/semver-umd.js">
717-
+ </head>
718-
+
719-
+ <body aria-label="">
720-
+ </body>
721-
+
722-
+ <!-- Startup (do not modify order of script tags!) -->
723-
+ <!-- NOTE:coder: Modified to work against the current path and use the commit for caching. -->
724-
+ <script>
725-
+ // NOTE: Changes to inline scripts require update of content security policy
726-
+ const basePath = window.location.pathname.replace(/\/+$/, '');
727-
+ const base = window.location.origin + basePath;
728-
+ const el = document.getElementById('vscode-remote-commit');
729-
+ const commit = el ? el.getAttribute('data-settings') : "";
730-
+ const staticBase = base + '/static-' + commit;
731-
+ let nlsConfig;
732-
+ try {
733-
+ nlsConfig = JSON.parse(document.getElementById('vscode-remote-nls-configuration').getAttribute('data-settings'));
734-
+ if (nlsConfig._resolvedLanguagePackCoreLocation) {
735-
+ const bundles = Object.create(null);
736-
+ nlsConfig.loadBundle = (bundle, language, cb) => {
737-
+ let result = bundles[bundle];
738-
+ if (result) {
739-
+ return cb(undefined, result);
740-
+ }
741-
+ // FIXME: Only works if path separators are /.
742-
+ const path = nlsConfig._resolvedLanguagePackCoreLocation
743-
+ + '/' + bundle.replace(/\//g, '!') + '.nls.json';
744-
+ fetch(`${base}/resource/?path=${encodeURIComponent(path)}`)
745-
+ .then((response) => response.json())
746-
+ .then((json) => {
747-
+ bundles[bundle] = json;
748-
+ cb(undefined, json);
749-
+ })
750-
+ .catch(cb);
751-
+ };
752-
+ }
753-
+ } catch (error) { /* Probably fine. */ }
754-
+ self.require = {
755-
+ baseUrl: `${staticBase}/out`,
756-
+ paths: {
757-
+ 'vscode-textmate': `${staticBase}/node_modules/vscode-textmate/release/main`,
758-
+ 'onigasm-umd': `${staticBase}/node_modules/onigasm-umd/release/main`,
759-
+ 'xterm': `${staticBase}/node_modules/xterm/lib/xterm.js`,
760-
+ 'xterm-addon-search': `${staticBase}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
761-
+ 'xterm-addon-web-links': `${staticBase}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
762-
+ 'xterm-addon-webgl': `${staticBase}/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
763-
+ 'semver-umd': `${staticBase}/node_modules/semver-umd/lib/semver-umd.js`,
764-
+ },
765-
+ 'vs/nls': nlsConfig,
766-
+ };
767-
+ </script>
768-
+ <script src="./static-{{COMMIT}}/out/vs/loader.js"></script>
769-
+ <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.nls.js"></script>
770-
+ <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.js"></script>
771-
+ <!-- TODO@coder: This errors with multiple anonymous define calls (one is
772-
+ workbench.js and one is semver-umd.js). For now use the same method found in
773-
+ workbench-dev.html. Appears related to the timing of the script load events. -->
774-
+ <!-- <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.js"></script> -->
775-
+ <script>
776-
+ // NOTE: Changes to inline scripts require update of content security policy
777-
+ require(['vs/code/browser/workbench/workbench'], function() {});
778-
+ </script>
779-
+</html>
780-
diff --git a/src/vs/server/browser/workbench.html b/src/vs/server/browser/workbench.html
781-
new file mode 100644
782-
index 0000000000..47d76f388b
783-
--- /dev/null
784-
+++ b/src/vs/server/browser/workbench.html
785-
@@ -0,0 +1,55 @@
786-
+<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
787-
+<!DOCTYPE html>
788-
+<html>
789-
+ <head>
790-
+ <meta charset="utf-8" />
791-
+
792-
+ <!-- Disable pinch zooming -->
793-
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
794-
+
795-
+ <!-- Workbench Configuration -->
796-
+ <meta id="vscode-workbench-web-configuration" data-settings="{{WORKBENCH_WEB_CONFIGURATION}}">
797-
+
798-
+ <!-- Workarounds/Hacks (remote user data uri) -->
799-
+ <meta id="vscode-remote-user-data-uri" data-settings="{{REMOTE_USER_DATA_URI}}">
800-
+ <!-- NOTE@coder: Added the commit for use in caching, the product for the
801-
+ extensions gallery URL, and nls for language support. -->
802-
+ <meta id="vscode-remote-commit" data-settings="{{COMMIT}}">
803-
+ <meta id="vscode-remote-product-configuration" data-settings="{{PRODUCT_CONFIGURATION}}">
804-
+ <meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
805-
+
806-
+ <!-- Workbench Icon/Manifest/CSS -->
807-
+ <link rel="icon" href="../static/src/browser/media/favicon.ico" type="image/x-icon" />
808-
+ <link rel="manifest" href="../static/src/browser/media/manifest.json" crossorigin="use-credentials">
809-
+ <link rel="apple-touch-icon" href="./static/src/browser/media/code-server.png" />
810-
+ <meta name="apple-mobile-web-app-capable" content="yes">
811-
+ </head>
812-
+
813-
+ <body aria-label="">
814-
+ </body>
815-
+
816-
+ <!-- Startup (do not modify order of script tags!) -->
817-
+ <script>
818-
+ const basePath = window.location.pathname.replace(/\/+$/, '');
819-
+ const base = window.location.origin + basePath;
820-
+ const el = document.getElementById('vscode-remote-commit');
821-
+ const commit = el ? el.getAttribute('data-settings') : "";
822-
+ const staticBase = base + '/static-' + commit;
823-
+ self.require = {
824-
+ baseUrl: `${staticBase}/out`,
825-
+ paths: {
826-
+ 'vscode-textmate': `${staticBase}/node_modules/vscode-textmate/release/main`,
827-
+ 'onigasm-umd': `${staticBase}/node_modules/onigasm-umd/release/main`,
828-
+ 'xterm': `${staticBase}/node_modules/xterm/lib/xterm.js`,
829-
+ 'xterm-addon-search': `${staticBase}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
830-
+ 'xterm-addon-web-links': `${staticBase}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
831-
+ 'xterm-addon-webgl': `${staticBase}/node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js`,
832-
+ 'semver-umd': `${staticBase}/node_modules/semver-umd/lib/semver-umd.js`,
833-
+ },
834-
+ };
835-
+ </script>
836-
+ <script src="./static/out/vs/loader.js"></script>
837-
+ <script>
838-
+ require(['vs/code/browser/workbench/workbench'], function() {});
839-
+ </script>
840-
+</html>
841682
diff --git a/src/vs/server/browser/worker.ts b/src/vs/server/browser/worker.ts
842683
new file mode 100644
843684
index 0000000000..0ba93cc070

src/node/app/server.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class MainHttpProvider extends HttpProvider {
1919
): Promise<HttpResponse | undefined> {
2020
if (base === "/static") {
2121
const response = await this.getResource(this.rootPath, requestPath)
22-
if (this.options.commit && this.options.commit !== "development") {
22+
if (!this.isDev) {
2323
response.cache = true
2424
}
2525
return response
@@ -41,7 +41,7 @@ export class MainHttpProvider extends HttpProvider {
4141

4242
const response = await this.getUtf8Resource(this.rootPath, "src/browser/index.html")
4343
response.content = response.content
44-
.replace(/{{COMMIT}}/g, this.options.commit || "development")
44+
.replace(/{{COMMIT}}/g, this.options.commit)
4545
.replace(/"{{OPTIONS}}"/g, `'${JSON.stringify(options)}'`)
4646
.replace(/{{COMPONENT}}/g, ReactDOMServer.renderToString(<App options={options} />))
4747
return response

src/node/entry.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const main = async (args: Args = {}): Promise<void> => {
2323
const auth = args.auth || AuthType.Password
2424
const originalPassword = auth === AuthType.Password && (process.env.PASSWORD || (await generatePassword()))
2525

26-
let commit = "development"
26+
let commit: string | undefined
2727
try {
2828
commit = require("../../package.json").commit
2929
} catch (error) {
@@ -36,7 +36,7 @@ const main = async (args: Args = {}): Promise<void> => {
3636
basePath: args["base-path"],
3737
cert: args.cert,
3838
certKey: args["cert-key"],
39-
commit,
39+
commit: commit || "development",
4040
host: args.host || (args.auth === AuthType.Password && typeof args.cert !== "undefined" ? "0.0.0.0" : "localhost"),
4141
password: originalPassword ? hash(originalPassword) : undefined,
4242
port: typeof args.port !== "undefined" ? parseInt(args.port, 10) : 8080,

src/node/http.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export interface HttpProviderOptions {
112112
readonly base: string
113113
readonly auth: AuthType
114114
readonly password?: string
115-
readonly commit?: string
115+
readonly commit: string
116116
}
117117

118118
/**
@@ -150,6 +150,10 @@ export abstract class HttpProvider {
150150
request: http.IncomingMessage
151151
): Promise<HttpResponse | undefined>
152152

153+
protected get isDev(): boolean {
154+
return this.options.commit === "development"
155+
}
156+
153157
/**
154158
* Return the specified path with the base path prepended.
155159
*/

src/node/vscode/error.html

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
6+
<!-- <meta http-equiv="Content-Security-Policy" content="font-src 'self'; connect-src 'self'; default-src ws: wss:; style-src 'self'; script-src 'self' 'unsafe-inline'; manifest-src 'self'; img-src 'self' data:;"> -->
7+
<title>code-server</title>
8+
<link rel="icon" href="./static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
9+
<link rel="manifest" href="./static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
10+
<link rel="apple-touch-icon" href="./static-{{COMMIT}}/src/browser/media/code-server.png" />
11+
<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans&display=swap" rel="stylesheet" />
12+
<meta id="coder-options" data-settings="{{OPTIONS}}">
13+
</head>
14+
<body>
15+
<div id="root" style="color:#f4f4f4;padding:20px;max-width:700px;">
16+
{{ERROR}}
17+
</div>
18+
<script>
19+
if (parent) {
20+
parent.postMessage({ event: "loaded" }, window.location.origin);
21+
}
22+
</script>
23+
</body>
24+
</html>

src/node/vscode/server.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,11 @@ export class VscodeHttpProvider extends HttpProvider {
128128
if (!this.authenticated(request)) {
129129
return { redirect: "/login" }
130130
}
131-
return this.getRoot(request, query)
131+
try {
132+
return await this.getRoot(request, query)
133+
} catch (error) {
134+
return this.getErrorRoot(error)
135+
}
132136
case "/static": {
133137
switch (requestPath) {
134138
case "/out/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js": {
@@ -171,7 +175,7 @@ export class VscodeHttpProvider extends HttpProvider {
171175
private async getRoot(request: http.IncomingMessage, query: querystring.ParsedUrlQuery): Promise<HttpResponse> {
172176
const settings = await this.settings.read()
173177
const [response, options] = await Promise.all([
174-
this.getUtf8Resource(this.serverRootPath, "browser/workbench.html"),
178+
await this.getUtf8Resource(this.rootPath, `src/node/vscode/workbench${!this.isDev ? "-build" : ""}.html`),
175179
this.initialize({
176180
args: this.args,
177181
query,
@@ -201,4 +205,13 @@ export class VscodeHttpProvider extends HttpProvider {
201205
.replace(`"{{NLS_CONFIGURATION}}"`, `'${JSON.stringify(options.nlsConfiguration)}'`),
202206
}
203207
}
208+
209+
private async getErrorRoot(error: Error): Promise<HttpResponse> {
210+
const response = await this.getUtf8Resource(this.rootPath, "src/node/vscode/error.html")
211+
const message = `VS Code failed to load. ${
212+
this.isDev ? "It might not have finished compiling (check for 'Finished compilation' in the output)." : ""
213+
} <br><br>${error}`
214+
response.content = response.content.replace(/{{COMMIT}}/g, this.options.commit).replace(/{{ERROR}}/g, message)
215+
return response
216+
}
204217
}

0 commit comments

Comments
 (0)