diff --git a/channel.ts b/channel.ts index d5d0ac0f1..5ae9f22fc 100644 --- a/channel.ts +++ b/channel.ts @@ -13,7 +13,8 @@ import { IEnvironmentService } from "vs/platform/environment/common/environment" import { IExtensionDescription, ExtensionIdentifier } from "vs/platform/extensions/common/extensions"; import { FileDeleteOptions, FileOverwriteOptions, FileType, IStat, IWatchOptions, FileOpenOptions } from "vs/platform/files/common/files"; import { ILogService } from "vs/platform/log/common/log"; -import { IProductService } from "vs/platform/product/common/product"; +import pkg from "vs/platform/product/node/package"; +import product from "vs/platform/product/node/product"; import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment"; import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints"; import { DiskFileSystemProvider } from "vs/workbench/services/files/node/diskFileSystemProvider"; @@ -200,14 +201,13 @@ export class ExtensionEnvironmentChannel implements IServerChannel { private async scanExtensions(locale: string): Promise { const root = getPathFromAmdModule(require, ""); - const product = require.__$__nodeRequire(path.resolve(root, "../package.json")) as IProductService; const translations = {}; // TODO: translations // TODO: there is also this.environment.extensionDevelopmentLocationURI to look into. const scanBuiltin = async (): Promise => { const input = new ExtensionScannerInput( - product.version, product.commit, locale, !!process.env.VSCODE_DEV, + pkg.version, product.commit, locale, !!process.env.VSCODE_DEV, path.resolve(root, "../extensions"), true, false, @@ -220,7 +220,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel { const scanInstalled = async (): Promise => { const input = new ExtensionScannerInput( - product.version, product.commit, locale, !!process.env.VSCODE_DEV, + pkg.version, product.commit, locale, !!process.env.VSCODE_DEV, this.environment.extensionsPath, false, true, translations, ); return ExtensionScanner.scanExtensions(input, this.log); diff --git a/cli.ts b/cli.ts new file mode 100644 index 000000000..cdd68e59a --- /dev/null +++ b/cli.ts @@ -0,0 +1,65 @@ +import * as os from "os"; +import { validatePaths } from "vs/code/node/paths"; +import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper"; +import { ParsedArgs } from "vs/platform/environment/common/environment"; +import { buildHelpMessage, buildVersionMessage } from "vs/platform/environment/node/argv"; +import product from "vs/platform/product/node/product"; +import pkg from "vs/platform/product/node/package"; +import { MainServer, WebviewServer } from "vs/server/server"; +import "vs/server/tar"; + +interface IMainCli { + main: (argv: ParsedArgs) => Promise; +} + +const main = async (): Promise => { + const args = validatePaths(parseMainProcessArgv(process.argv)); + + if (!product.extensionsGallery) { + product.extensionsGallery = { + serviceUrl: process.env.SERVICE_URL || "https://v1.extapi.coder.com", + itemUrl: process.env.ITEM_URL || "", + controlUrl: "", + recommendationsUrl: "", + }; + } + + if (args.help) { + const executable = `${product.applicationName}${os.platform() === "win32" ? ".exe" : ""}`; + return console.log(buildHelpMessage(product.nameLong, executable, pkg.version)); + } + + if (args.version) { + return console.log(buildVersionMessage(pkg.version, product.commit)); + } + + const shouldSpawnCliProcess = (): boolean => { + return !!args["install-source"] + || !!args["list-extensions"] + || !!args["install-extension"] + || !!args["uninstall-extension"] + || !!args["locate-extension"] + || !!args["telemetry"]; + }; + + if (shouldSpawnCliProcess()) { + const cli = await new Promise((c, e) => require(["vs/code/node/cliProcessMain"], c, e)); + await cli.main(args); + // There is some WriteStream instance keeping it open so force an exit. + return process.exit(0); + } + + const webviewServer = new WebviewServer(); + const server = new MainServer(webviewServer, args); + // The main server inserts webview server address to the root HTML, so we'll + // need to wait for it to listen otherwise the address will be null. + await webviewServer.listen(8444); + await server.listen(8443); + console.log(`Main server serving ${server.address}`); + console.log(`Webview server serving ${webviewServer.address}`); +}; + +main().catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/entry.ts b/entry.ts deleted file mode 100644 index 5aa0548a8..000000000 --- a/entry.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { MainServer, WebviewServer } from "./server"; - -const webviewServer = new WebviewServer(); -const server = new MainServer(webviewServer); -webviewServer.listen(8444).then(async () => { - await server.listen(8443); - console.log(`Main server serving ${server.address}`); - console.log(`Webview server serving ${webviewServer.address}`); -}).catch((error) => { - console.error(error); - process.exit(1); -}); diff --git a/main.js b/main.js index 6bdb6c032..765f41812 100644 --- a/main.js +++ b/main.js @@ -1 +1 @@ -require("../../bootstrap-amd").load("vs/server/entry"); +require("../../bootstrap-amd").load("vs/server/cli"); diff --git a/package.json b/package.json index 6e9f55ed5..6af0cb63a 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,13 @@ "build": "echo TODO && exit 1" }, "devDependencies": { + "@types/tar-stream": "^1.6.1", "nodemon": "^1.19.1" + }, + "dependencies": { + "tar-stream": "^2.1.0" + }, + "resolutions": { + "@types/node": "^10.12.12" } } diff --git a/server.ts b/server.ts index 6e24f74d2..2d4f96995 100644 --- a/server.ts +++ b/server.ts @@ -5,16 +5,13 @@ import * as path from "path"; import * as util from "util"; import * as url from "url"; -import { getPathFromAmdModule } from "vs/base/common/amd"; import { Emitter } from "vs/base/common/event"; import { sanitizeFilePath } from "vs/base/common/extpath"; import { getMediaMime } from "vs/base/common/mime"; import { extname } from "vs/base/common/path"; import { UriComponents, URI } from "vs/base/common/uri"; import { IPCServer, ClientConnectionEvent } from "vs/base/parts/ipc/common/ipc"; -import { validatePaths } from "vs/code/node/paths"; import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner"; -import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper"; import { IEnvironmentService, ParsedArgs } from "vs/platform/environment/common/environment"; import { EnvironmentService } from "vs/platform/environment/node/environmentService"; import { InstantiationService } from "vs/platform/instantiation/common/instantiationService"; @@ -23,6 +20,7 @@ import { getLogLevel, ILogService } from "vs/platform/log/common/log"; import { LogLevelSetterChannel } from "vs/platform/log/common/logIpc"; import { SpdLogService } from "vs/platform/log/node/spdlogService"; import { IProductConfiguration } from "vs/platform/product/common/product"; +import product from "vs/platform/product/node/product"; import { ConnectionType, ConnectionTypeRequest } from "vs/platform/remote/common/remoteAgentConnection"; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/platform/remote/common/remoteAgentFileSystemChannel"; import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService"; @@ -123,7 +121,7 @@ export class MainServer extends Server { private readonly services = new ServiceCollection(); - public constructor(private readonly webviewServer: WebviewServer) { + public constructor(private readonly webviewServer: WebviewServer, args: ParsedArgs) { super(); this.server.on("upgrade", async (request, socket) => { @@ -134,10 +132,6 @@ export class MainServer extends Server { protocol.dispose(error); } }); - } - - public async listen(port: number): Promise { - const args = validatePaths(parseMainProcessArgv(process.argv)); const environmentService = new EnvironmentService(args, process.execPath); this.services.set(IEnvironmentService, environmentService); @@ -163,8 +157,6 @@ export class MainServer extends Server { new ExtensionEnvironmentChannel(environmentService, logService), ); }); - - await super.listen(port); } protected async handleRequest( @@ -203,9 +195,7 @@ export class MainServer extends Server { REMOTE_USER_DATA_URI: transformer.transformOutgoing( (this.services.get(IEnvironmentService) as EnvironmentService).webUserDataHome, ), - PRODUCT_CONFIGURATION: require.__$__nodeRequire( - path.resolve(getPathFromAmdModule(require, ""), "../product.json"), - ), + PRODUCT_CONFIGURATION: product, CONNECTION_AUTH_TOKEN: "", }; diff --git a/tar.ts b/tar.ts index b7aafb20e..b513e145a 100644 --- a/tar.ts +++ b/tar.ts @@ -1,10 +1,5 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - import * as nls from "vs/nls"; -import * as vszip from "vszip"; +import * as vszip from "vs/base/node/zip"; import * as fs from "fs"; import * as path from "path"; import * as tarStream from "tar-stream"; @@ -12,6 +7,10 @@ import { promisify } from "util"; import { CancellationToken } from "vs/base/common/cancellation"; import { mkdirp } from "vs/base/node/pfs"; +// We will be overriding these, so keep a reference to the original. +const vszipExtract = vszip.extract; +const vszipBuffer = vszip.buffer; + export interface IExtractOptions { overwrite?: boolean; @@ -29,8 +28,8 @@ export interface IFile { } /** - * Override the standard VS Code behavior for zipping - * extensions to use the TAR format instead of ZIP. + * Override the standard VS Code behavior for zipping extensions to use the TAR + * format instead of ZIP. */ export const zip = (tarPath: string, files: IFile[]): Promise => { return new Promise((c, e): void => { @@ -63,10 +62,9 @@ export const zip = (tarPath: string, files: IFile[]): Promise => { }; /** - * Override the standard VS Code behavior for extracting - * archives, to first attempt to process the archive as a TAR - * and then fallback on the original implementation, for processing - * ZIPs. + * Override the standard VS Code behavior for extracting archives to first + * attempt to process the archive as a TAR and then fall back to the original + * implementation for processing ZIPs. */ export const extract = (archivePath: string, extractPath: string, options: IExtractOptions = {}, token: CancellationToken): Promise => { return new Promise((c, e): void => { @@ -76,15 +74,15 @@ export const extract = (archivePath: string, extractPath: string, options: IExtr return; } - vszip.extract(archivePath, extractPath, options, token).then(c).catch(e); + vszipExtract(archivePath, extractPath, options, token).then(c).catch(e); }); }); }; /** - * Override the standard VS Code behavior for buffering - * archives, to first process the Buffer as a TAR and then - * fallback on the original implementation, for processing ZIPs. + * Override the standard VS Code behavior for buffering archives to first + * process the Buffer as a TAR and then fall back to the original + * implementation for processing ZIPs. */ export const buffer = (targetPath: string, filePath: string): Promise => { return new Promise((c, e): void => { @@ -104,16 +102,16 @@ export const buffer = (targetPath: string, filePath: string): Promise => return; } - vszip.buffer(targetPath, filePath).then(c).catch(e); + vszipBuffer(targetPath, filePath).then(c).catch(e); }); }); }; /** - * Override the standard VS Code behavior for extracting assets - * from archive Buffers to use the TAR format instead of ZIP. + * Override the standard VS Code behavior for extracting assets from archive + * Buffers to use the TAR format instead of ZIP. */ -export const extractAssets = (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise => { +const extractAssets = (tarPath: string, match: RegExp, callback: (path: string, data: Buffer) => void): Promise => { return new Promise(async (c, e): Promise => { try { const buffer = await promisify(fs.readFile)(tarPath); @@ -217,3 +215,9 @@ const extractTar = (tarPath: string, targetPath: string, options: IExtractOption } }); }; + +// Override original functionality so we can use tar instead of zip. +const target = vszip as typeof vszip; +target.zip = zip; +target.extract = extract; +target.buffer = buffer; diff --git a/yarn.lock b/yarn.lock index 7d6793b41..c172bb317 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,18 @@ # yarn lockfile v1 +"@types/node@*", "@types/node@^10.12.12": + version "10.14.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.10.tgz#e491484c6060af8d461e12ec81c0da8a3282b8de" + integrity sha512-V8wj+w2YMNvGuhgl/MA5fmTxgjmVHVoasfIaxMMZJV6Y8Kk+Ydpi1z2whoShDCJ2BuNVoqH/h1hrygnBxkrw/Q== + +"@types/tar-stream@^1.6.1": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-1.6.1.tgz#67d759068ff781d976cad978893bb7a334ec8809" + integrity sha512-pYCDOPuRE+4tXFk1rSMYiuI+kSrXiJ4av1bboQbkcEBA2rqwEWfIn9kdMSH+5nYu58WksHuxwx+7kVbtg0Le7w== + dependencies: + "@types/node" "*" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -110,6 +122,13 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== +bl@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" + integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A== + dependencies: + readable-stream "^3.0.1" + boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -376,6 +395,13 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +end-of-stream@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + dependencies: + once "^1.4.0" + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -458,6 +484,11 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -1120,7 +1151,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -once@^1.3.0: +once@^1.3.0, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -1238,6 +1269,15 @@ readable-stream@^2.0.2, readable-stream@^2.0.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.0.1, readable-stream@^3.1.1: + version "3.4.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" + integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" @@ -1466,6 +1506,13 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== + dependencies: + safe-buffer "~5.1.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -1504,6 +1551,17 @@ supports-color@^5.2.0, supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +tar-stream@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" + integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw== + dependencies: + bl "^3.0.0" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + tar@^4: version "4.4.8" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" @@ -1636,7 +1694,7 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=