Compare commits
51 Commits
1.903-vsc1
...
1.1156-vsc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf63bbd003 | ||
|
|
02f62882b8 | ||
|
|
362715bbeb | ||
|
|
ec70ea6994 | ||
|
|
04adf14146 | ||
|
|
406ec0ba71 | ||
|
|
a2ad3d4ff4 | ||
|
|
4a29cd1664 | ||
|
|
0462a93f11 | ||
|
|
db39eacfa1 | ||
|
|
c020cd2f2c | ||
|
|
81bbfa7fbe | ||
|
|
aa1474b675 | ||
|
|
8256252967 | ||
|
|
07342bbee7 | ||
|
|
72152f74ab | ||
|
|
420ca76f54 | ||
|
|
31503fc853 | ||
|
|
cf399ef6ac | ||
|
|
bb5836ec61 | ||
|
|
f36235e03f | ||
|
|
6ef1628acb | ||
|
|
ab8f8a0a22 | ||
|
|
cdb900aca8 | ||
|
|
1622fd4152 | ||
|
|
6c972e855f | ||
|
|
e332882a88 | ||
|
|
d0142e2536 | ||
|
|
e8c8fba91d | ||
|
|
01a63a7241 | ||
|
|
a2e0638c6a | ||
|
|
4e62f938a9 | ||
|
|
4c5bb83fc1 | ||
|
|
a3ac4567e3 | ||
|
|
58cf109a83 | ||
|
|
fab45dedcd | ||
|
|
446573809c | ||
|
|
5ad9398b01 | ||
|
|
bcdbd90197 | ||
|
|
0de7247868 | ||
|
|
c9f91e77cd | ||
|
|
30b8565e2d | ||
|
|
6b887dcc9c | ||
|
|
41c7d98b7b | ||
|
|
b055a26dc3 | ||
|
|
2bc6e1a457 | ||
|
|
e61ea796c6 | ||
|
|
d073622629 | ||
|
|
5f40ebb845 | ||
|
|
c56e2797cc | ||
|
|
cdc5b55a9d |
15
.travis.yml
15
.travis.yml
@@ -1,13 +1,21 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 10.15.1
|
||||
env:
|
||||
- VSCODE_VERSION="1.33.1" MAJOR_VERSION="1" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER-vsc$VSCODE_VERSION"
|
||||
services:
|
||||
- docker
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
env:
|
||||
- VSCODE_VERSION="1.33.1" MAJOR_VERSION="1" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER-vsc$VSCODE_VERSION" TARGET="centos"
|
||||
- os: linux
|
||||
dist: trusty
|
||||
env:
|
||||
- VSCODE_VERSION="1.33.1" MAJOR_VERSION="1" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER-vsc$VSCODE_VERSION" TARGET="alpine"
|
||||
- os: osx
|
||||
env:
|
||||
- VSCODE_VERSION="1.33.1" MAJOR_VERSION="1" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER-vsc$VSCODE_VERSION"
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install libxkbfile-dev
|
||||
libsecret-1-dev; fi
|
||||
@@ -19,7 +27,6 @@ before_deploy:
|
||||
- git config --local user.name "$USER_NAME"
|
||||
- git config --local user.email "$USER_EMAIL"
|
||||
- git tag "$VERSION" "$TRAVIS_COMMIT"
|
||||
- yarn task package "$VERSION"
|
||||
deploy:
|
||||
provider: releases
|
||||
file_glob: true
|
||||
@@ -34,7 +41,7 @@ deploy:
|
||||
- release/*.tar.gz
|
||||
- release/*.zip
|
||||
on:
|
||||
repo: codercom/code-server
|
||||
repo: cdr/code-server
|
||||
branch: master
|
||||
cache:
|
||||
yarn: true
|
||||
|
||||
@@ -39,13 +39,12 @@ RUN adduser --gecos '' --disabled-password coder && \
|
||||
|
||||
USER coder
|
||||
# We create first instead of just using WORKDIR as when WORKDIR creates, the user is root.
|
||||
RUN mkdir -p /home/coder/project && \
|
||||
chmod g+rw /home/coder/project;
|
||||
RUN mkdir -p /home/coder/project
|
||||
|
||||
WORKDIR /home/coder/project
|
||||
|
||||
# This assures we have a volume mounted even if the user forgot to do bind mount.
|
||||
# XXX: Workaround for GH-459 and for OpenShift compatibility.
|
||||
# So that they do not lose their data if they delete the container.
|
||||
VOLUME [ "/home/coder/project" ]
|
||||
|
||||
COPY --from=0 /src/packages/server/cli-linux-x64 /usr/local/bin/code-server
|
||||
|
||||
14
README.md
14
README.md
@@ -1,8 +1,8 @@
|
||||
# code-server
|
||||
|
||||
[](https://github.com/codercom/code-server/issues)
|
||||
[](https://github.com/codercom/code-server/releases/latest)
|
||||
[](https://github.com/codercom/code-server/blob/master/LICENSE)
|
||||
[](https://github.com/cdr/code-server/issues)
|
||||
[](https://github.com/cdr/code-server/releases/latest)
|
||||
[](https://github.com/cdr/code-server/blob/master/LICENSE)
|
||||
[](https://discord.gg/zxSwN8Z)
|
||||
|
||||
`code-server` is [VS Code](https://github.com/Microsoft/vscode) running on a remote server, accessible through the browser.
|
||||
@@ -23,13 +23,17 @@ docker run -it -p 127.0.0.1:8443:8443 -v "${PWD}:/home/coder/project" codercom/c
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Run over SSH
|
||||
|
||||
Use [sshcode](https://github.com/codercom/sshcode) for a simple setup.
|
||||
|
||||
### Docker
|
||||
|
||||
See docker oneliner mentioned above. Dockerfile is at [/Dockerfile](/Dockerfile).
|
||||
|
||||
### Binaries
|
||||
|
||||
1. [Download a binary](https://github.com/codercom/code-server/releases) (Linux and OS X supported. Windows coming soon)
|
||||
1. [Download a binary](https://github.com/cdr/code-server/releases) (Linux and OS X supported. Windows coming soon)
|
||||
2. Start the binary with the project directory as the first argument
|
||||
|
||||
```
|
||||
@@ -42,7 +46,7 @@ See docker oneliner mentioned above. Dockerfile is at [/Dockerfile](/Dockerfile)
|
||||
|
||||
For detailed instructions and troubleshooting, see the [self-hosted quick start guide](doc/self-hosted/index.md).
|
||||
|
||||
Quickstart guides for [Google Cloud](doc/admin/install/google_cloud.md), [AWS](doc/admin/install/aws.md), and [Digital Ocean](doc/admin/install/digitalocean.md).
|
||||
Quickstart guides for [Google Cloud](doc/admin/install/google_cloud.md), [AWS](doc/admin/install/aws.md), and [DigitalOcean](doc/admin/install/digitalocean.md).
|
||||
|
||||
How to [secure your setup](/doc/security/ssl.md).
|
||||
|
||||
|
||||
42
build/platform.ts
Normal file
42
build/platform.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Script that detects platform name and arch.
|
||||
* Cannot use os.platform() as that won't detect libc version
|
||||
*/
|
||||
import * as cp from "child_process";
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
|
||||
enum Lib {
|
||||
GLIBC,
|
||||
MUSL,
|
||||
}
|
||||
|
||||
const CLIB: Lib | undefined = ((): Lib | undefined => {
|
||||
if (os.platform() !== "linux") {
|
||||
return;
|
||||
}
|
||||
const glibc = cp.spawnSync("getconf", ["GNU_LIBC_VERSION"]);
|
||||
if (glibc.status === 0) {
|
||||
return Lib.GLIBC;
|
||||
}
|
||||
|
||||
const ldd = cp.spawnSync("ldd", ["--version"]);
|
||||
if (ldd.stdout && ldd.stdout.indexOf("musl") !== -1) {
|
||||
return Lib.MUSL;
|
||||
}
|
||||
|
||||
const muslFile = fs.readdirSync("/lib").find((value) => value.startsWith("libc.musl"));
|
||||
if (muslFile) {
|
||||
return Lib.MUSL;
|
||||
}
|
||||
|
||||
return Lib.GLIBC;
|
||||
})();
|
||||
|
||||
export const platform = (): NodeJS.Platform | "musl" => {
|
||||
if (CLIB === Lib.MUSL) {
|
||||
return "musl";
|
||||
}
|
||||
|
||||
return os.platform();
|
||||
};
|
||||
@@ -1,7 +1,9 @@
|
||||
import { register, run } from "@coder/runner";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import * as fs from "fs";
|
||||
import * as fse from "fs-extra";
|
||||
import * as os from "os";
|
||||
import { platform } from "./platform";
|
||||
import * as path from "path";
|
||||
import * as zlib from "zlib";
|
||||
import * as https from "https";
|
||||
@@ -16,6 +18,13 @@ const vscodeVersion = process.env.VSCODE_VERSION || "1.33.1";
|
||||
const vsSourceUrl = `https://codesrv-ci.cdr.sh/vstar-${vscodeVersion}.tar.gz`;
|
||||
|
||||
const buildServerBinary = register("build:server:binary", async (runner) => {
|
||||
logger.info("Building with environment", field("env", {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
VERSION: process.env.VERSION,
|
||||
OSTYPE: process.env.OSTYPE,
|
||||
TARGET: process.env.TARGET,
|
||||
}));
|
||||
|
||||
await ensureInstalled();
|
||||
await Promise.all([
|
||||
buildBootstrapFork(),
|
||||
@@ -180,12 +189,12 @@ register("package", async (runner, releaseTag) => {
|
||||
|
||||
const releasePath = path.resolve(__dirname, "../release");
|
||||
|
||||
const archiveName = `code-server${releaseTag}-${os.platform()}-${os.arch()}`;
|
||||
const archiveName = `code-server${releaseTag}-${platform()}-${os.arch()}`;
|
||||
const archiveDir = path.join(releasePath, archiveName);
|
||||
fse.removeSync(archiveDir);
|
||||
fse.mkdirpSync(archiveDir);
|
||||
|
||||
const binaryPath = path.join(__dirname, `../packages/server/cli-${os.platform()}-${os.arch()}`);
|
||||
const binaryPath = path.join(__dirname, `../packages/server/cli-${platform()}-${os.arch()}`);
|
||||
const binaryDestination = path.join(archiveDir, "code-server");
|
||||
fse.copySync(binaryPath, binaryDestination);
|
||||
fs.chmodSync(binaryDestination, "755");
|
||||
|
||||
@@ -26,7 +26,7 @@ metadata:
|
||||
provisioner: kubernetes.io/aws-ebs
|
||||
parameters:
|
||||
type: gp2
|
||||
fsType: ext4
|
||||
fsType: ext4
|
||||
---
|
||||
kind: PersistentVolumeClaim
|
||||
apiVersion: v1
|
||||
@@ -71,4 +71,4 @@ spec:
|
||||
- name: code-server-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: code-store
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This tutorial shows you how to deploy `code-server` on an EC2 AWS instance.
|
||||
|
||||
If you're just starting out, we recommend [installing code-server locally](../../self-hosted/index.md). It takes only a few minutes and lets you try out all of the features. You can also try out the IDE on a container hosted [by Coder](http://coder.com/signup)
|
||||
If you're just starting out, we recommend [installing code-server locally](../../self-hosted/index.md). It takes only a few minutes and lets you try out all of the features.
|
||||
|
||||
---
|
||||
|
||||
@@ -11,9 +11,9 @@ If you're just starting out, we recommend [installing code-server locally](../..
|
||||
### Use the AWS wizard
|
||||
|
||||
- Click **Launch Instance** from your [EC2 dashboard](https://console.aws.amazon.com/ec2/v2/home).
|
||||
- Select the Ubuntu Server 16.04 LTS (HVM), SSD Volume Type (`ami-0f9cf087c1f27d9b1)` at this time of writing)
|
||||
- Select the Ubuntu Server 18.04 LTS (HVM), SSD Volume Type
|
||||
- Select an appropriate instance size (we recommend t2.medium/large, depending on team size and number of repositories/languages enabled), then **Next: Configure Instance Details**
|
||||
- Select **Next: ...** until you get to the **Configure Security Group** page, then add the default **HTTP** rule (port range "80", source "0.0.0.0/0, ::/0")
|
||||
- Select **Next: ...** until you get to the **Configure Security Group** page, then add a **Custom TCP Rule** rule with port range set to `8443` and source set to "Anywhere"
|
||||
> Rules with source of 0.0.0.0/0 allow all IP addresses to access your instance. We recommend setting [security group rules](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-network-security.html?icmpid=docs_ec2_console) to allow access from known IP addresses only.
|
||||
- Click **Launch**
|
||||
- You will be prompted to create a key pair
|
||||
@@ -35,11 +35,11 @@ If you're just starting out, we recommend [installing code-server locally](../..
|
||||
- At this point it is time to download the `code-server` binary. We will of course want the linux version.
|
||||
- Find the latest Linux release from this URL:
|
||||
```
|
||||
https://github.com/codercom/code-server/releases/latest
|
||||
https://github.com/cdr/code-server/releases/latest
|
||||
```
|
||||
- Replace {version} in the following command with the version found on the releases page and run it (or just copy the download URL from the releases page):
|
||||
```
|
||||
wget https://github.com/codercom/code-server/releases/download/{version}/code-server-{version}-linux-x64.tar.gz
|
||||
wget https://github.com/cdr/code-server/releases/download/{version}/code-server-{version}-linux-x64.tar.gz
|
||||
```
|
||||
- Extract the downloaded tar.gz file with this command, for example:
|
||||
```
|
||||
@@ -56,14 +56,11 @@ If you're just starting out, we recommend [installing code-server locally](../..
|
||||
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../../security/ssl.md)
|
||||
- Finally, run
|
||||
```
|
||||
sudo ./code-server -p 80
|
||||
./code-server
|
||||
```
|
||||
- When you visit the public IP for your AWS instance, you will be greeted with this page. Code-server is using a self-signed SSL certificate for easy setup. To proceed to the IDE, click **"Advanced"**<img src ="../../assets/chrome_warning.png">
|
||||
- Then click **"proceed anyway"**<img src="../../assets/chrome_confirm.png">
|
||||
- Open your browser and visit `https://$public_ip:8443/` (where `$public_ip` is your AWS instance's public IP address). You will be greeted with a page similar to the following screenshot. Code-server is using a self-signed SSL certificate for easy setup. In Chrome/Chromium, click **"Advanced"** then click **"proceed anyway"**. In Firefox, click **Advanced**, then **Add Exception**, then finally **Confirm Security Exception**.<img src ="../../assets/chrome_warning.png">
|
||||
|
||||
> For instructions on how to keep the server running after you end your SSH session please checkout [how to use systemd](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) to start linux based services if they are killed
|
||||
|
||||
> The `-p 80` flag is necessary in order to make the IDE accessible from the public IP of your instance (also available from the description in the instances page.
|
||||
|
||||
---
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/codercom/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This tutorial shows you how to deploy `code-server` to a single node running on DigitalOcean.
|
||||
|
||||
If you're just starting out, we recommend [installing code-server locally](../../self-hosted/index.md). It takes only a few minutes and lets you try out all of the features. You can also try out the IDE on a container hosted [by Coder](http://coder.com/signup)
|
||||
If you're just starting out, we recommend [installing code-server locally](../../self-hosted/index.md). It takes only a few minutes and lets you try out all of the features.
|
||||
|
||||
---
|
||||
|
||||
@@ -15,14 +15,14 @@ If you're just starting out, we recommend [installing code-server locally](../..
|
||||
- Launch your instance
|
||||
- Open a terminal on your computer and SSH into your instance
|
||||
> example: ssh root@203.0.113.0
|
||||
- Once in the SSH session, visit code-server [releases page](https://github.com/codercom/code-server/releases/) and copy the link to the download for the latest linux release
|
||||
- Once in the SSH session, visit code-server [releases page](https://github.com/cdr/code-server/releases/) and copy the link to the download for the latest linux release
|
||||
- Find the latest Linux release from this URL:
|
||||
```
|
||||
https://github.com/codercom/code-server/releases/latest
|
||||
https://github.com/cdr/code-server/releases/latest
|
||||
```
|
||||
- Replace {version} in the following command with the version found on the releases page and run it (or just copy the download URL from the releases page):
|
||||
```
|
||||
wget https://github.com/codercom/code-server/releases/download/{version}/code-server-{version}-linux-x64.tar.gz
|
||||
wget https://github.com/cdr/code-server/releases/download/{version}/code-server-{version}-linux-x64.tar.gz
|
||||
```
|
||||
- Extract the downloaded tar.gz file with this command, for example:
|
||||
```
|
||||
@@ -39,11 +39,10 @@ If you're just starting out, we recommend [installing code-server locally](../..
|
||||
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../../security/ssl.md)
|
||||
- Finally start the code-server
|
||||
```
|
||||
sudo ./code-server -p 80
|
||||
./code-server
|
||||
```
|
||||
> For instructions on how to keep the server running after you end your SSH session please checkout [how to use systemd](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) to start linux based services if they are killed
|
||||
- When you visit the public IP for your Digital Ocean instance, you will be greeted with this page. Code-server is using a self-signed SSL certificate for easy setup. To proceed to the IDE, click **"Advanced"**<img src ="../../assets/chrome_warning.png">
|
||||
- Then click **"proceed anyway"**<img src="../../assets/chrome_confirm.png">
|
||||
- Open your browser and visit `https://$public_ip:8443/` (where `$public_ip` is your Digital Ocean instance's public IP address). You will be greeted with a page similar to the following screenshot. Code-server is using a self-signed SSL certificate for easy setup. In Chrome/Chromium, click **"Advanced"** then click **"proceed anyway"**. In Firefox, click **Advanced**, then **Add Exception**, then finally **Confirm Security Exception**.<img src ="../../assets/chrome_warning.png">
|
||||
|
||||
---
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/codercom/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
This tutorial shows you how to deploy `code-server` to a single node running on Google Cloud.
|
||||
|
||||
If you're just starting out, we recommend [installing code-server locally](../../self-hosted/index.md). It takes only a few minutes and lets you try out all of the features. You can also try out the IDE on a container hosted [by Coder](http://coder.com/signup)
|
||||
If you're just starting out, we recommend [installing code-server locally](../../self-hosted/index.md). It takes only a few minutes and lets you try out all of the features.
|
||||
|
||||
---
|
||||
|
||||
@@ -12,9 +12,10 @@ If you're just starting out, we recommend [installing code-server locally](../..
|
||||
- [Open your Google Cloud console](https://console.cloud.google.com/compute/instances) to create a new VM instance and click **Create Instance**
|
||||
- Choose an appropriate machine type (we recommend 2 vCPU and 7.5 GB RAM, more depending on team size and number of repositories/languages enabled)
|
||||
- Choose Ubuntu 16.04 LTS as your boot disk
|
||||
- Check the boxes for **Allow HTTP traffic** and **Allow HTTPS traffic** in the **Firewall** section
|
||||
- Expand the "Management, security, disks, networking, sole tenancy" section, go to the "Networking" tab, then under network tags add "code-server"
|
||||
- Create your VM, and **take note** of its public IP address.
|
||||
- Copy the link to download the latest Linux binary from our [releases page](https://github.com/codercom/code-server/releases)
|
||||
- Visit "VPC network" in the console and go to "Firewall rules". Create a new firewall rule called "http-8443". Under "Target tags" add "code-server", and under "Protocols and ports" tick "Specified protocols and ports" and "tcp". Beside "tcp", add "8443", then create the rule.
|
||||
- Copy the link to download the latest Linux binary from our [releases page](https://github.com/cdr/code-server/releases)
|
||||
|
||||
---
|
||||
|
||||
@@ -27,12 +28,12 @@ gcloud compute ssh --zone [region] [instance name]
|
||||
|
||||
- Find the latest Linux release from this URL:
|
||||
```
|
||||
https://github.com/codercom/code-server/releases/latest
|
||||
https://github.com/cdr/code-server/releases/latest
|
||||
```
|
||||
|
||||
- Replace {version} in the following command with the version found on the releases page and run it (or just copy the download URL from the releases page):
|
||||
```
|
||||
wget https://github.com/codercom/code-server/releases/download/{version}/code-server-{version}-linux-x64.tar.gz
|
||||
wget https://github.com/cdr/code-server/releases/download/{version}/code-server-{version}-linux-x64.tar.gz
|
||||
```
|
||||
|
||||
- Extract the downloaded tar.gz file with this command, for example:
|
||||
@@ -50,22 +51,16 @@ cd code-server-{version}-linux-x64
|
||||
chmod +x code-server
|
||||
```
|
||||
|
||||
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../security/ssl.md)
|
||||
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../../security/ssl.md)
|
||||
|
||||
- Start the code-server
|
||||
```
|
||||
sudo ./code-server -p 80
|
||||
```
|
||||
```
|
||||
./code-server
|
||||
```
|
||||
- Open your browser and visit `https://$public_ip:8443/` (where `$public_ip` is your Compute Engine instance's public IP address). You will be greeted with a page similar to the following screenshot. Code-server is using a self-signed SSL certificate for easy setup. In Chrome/Chromium, click **"Advanced"** then click **"proceed anyway"**. In Firefox, click **Advanced**, then **Add Exception**, then finally **Confirm Security Exception**.<img src ="../../assets/chrome_warning.png">
|
||||
|
||||
> For instructions on how to keep the server running after you end your SSH session please checkout [how to use systemd](https://www.linode.com/docs/quick-answers/linux/start-service-at-boot/) to start linux based services if they are killed
|
||||
|
||||
- Access code-server from the public IP of your Google Cloud instance we noted earlier in your browser.
|
||||
> example: 32.32.32.234
|
||||
|
||||
- You will be greeted with this page. Code-server is using a self-signed SSL certificate for easy setup. To proceed to the IDE, click **"Advanced"**<img src ="../../assets/chrome_warning.png">
|
||||
|
||||
- Then click **"proceed anyway"**<img src="../../assets/chrome_confirm.png">
|
||||
|
||||
---
|
||||
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/codercom/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
@@ -4,7 +4,7 @@
|
||||
|
||||
## Quickstart Guide
|
||||
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/codercom/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
|
||||
|
||||
This document pertains to Coder specific implementations of VS Code. For documentation on how to use VS Code itself, please refer to the official [documentation for VS Code](https://code.visualstudio.com/docs)
|
||||
|
||||
@@ -17,7 +17,7 @@ It takes just a few minutes to get your own self-hosted server running. If you'v
|
||||
-->
|
||||
|
||||
|
||||
1. Visit [the releases](https://github.com/codercom/code-server/releases) page and download the latest cli for your operating system
|
||||
1. Visit [the releases](https://github.com/cdr/code-server/releases) page and download the latest cli for your operating system
|
||||
2. Double click the executable to run in the current directory
|
||||
3. Copy the password that appears in the cli<img src="../assets/cli.png">
|
||||
4. In your browser navigate to `localhost:8443`
|
||||
@@ -25,8 +25,7 @@ It takes just a few minutes to get your own self-hosted server running. If you'v
|
||||
> NOTE: Be careful with your password as sharing it will grant those users access to your server's file system
|
||||
|
||||
### Things To Know
|
||||
- When you visit the IP for your code-server, you will be greeted with this page. Code-server is using a self-signed SSL certificate for easy setup. To proceed to the IDE, click **"Advanced"**<img src ="../assets/chrome_warning.png">
|
||||
- Then click **"proceed anyway"**<img src="../assets/chrome_confirm.png">
|
||||
- When you visit the IP for your code-server instance, you will be greeted with a page similar to the following screenshot. Code-server is using a self-signed SSL certificate for easy setup. In Chrome/Chromium, click **"Advanced"** then click **"proceed anyway"**. In Firefox, click **Advanced**, then **Add Exception**, then finally **Confirm Security Exception**.<img src ="../../assets/chrome_warning.png">
|
||||
|
||||
## Usage
|
||||
<pre class="pre-wrap"><code>code-server<span class="virtual-br"></span> --help</code></pre>
|
||||
@@ -56,7 +55,7 @@ Options:
|
||||
```
|
||||
|
||||
### Data Directory
|
||||
Use `code-server -d (path/to/directory)` or `code-server --data-dir=(path/to/directory)`, excluding the parentheses to specify the root folder that VS Code will start in.
|
||||
Use `code-server -d (path/to/directory)` or `code-server --user-data-dir=(path/to/directory)`, excluding the parentheses to specify the root folder that VS Code will start in.
|
||||
|
||||
### Host
|
||||
By default, code-server will use `0.0.0.0` as its address. This can be changed by using `code-server -h` or `code-server --host=` followed by the address you want to use.
|
||||
@@ -94,7 +93,7 @@ Options:
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Apache Reverse Proxy
|
||||
Example of https virtualhost configuration for Apache as a reverse proxy. Please also pass --allow-http on code-server startup to allow the proxy to connect.
|
||||
```
|
||||
@@ -106,7 +105,7 @@ Options:
|
||||
RewriteRule /(.*) ws://localhost:8443/$1 [P,L]
|
||||
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
|
||||
RewriteRule /(.*) http://localhost:8443/$1 [P,L]
|
||||
|
||||
|
||||
ProxyRequests off
|
||||
|
||||
RequestHeader set X-Forwarded-Proto https
|
||||
@@ -118,6 +117,6 @@ Options:
|
||||
</VirtualHost>
|
||||
```
|
||||
*Important:* For more details about Apache reverse proxy configuration checkout the [documentation](https://httpd.apache.org/docs/current/mod/mod_proxy.html) - especially the [Securing your Server](https://httpd.apache.org/docs/current/mod/mod_proxy.html#access) section
|
||||
|
||||
|
||||
### Help
|
||||
Use `code-server --help` to view the usage for the CLI. This is also shown at the beginning of this section.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@coder/code-server",
|
||||
"repository": "https://github.com/codercom/code-server",
|
||||
"repository": "https://github.com/cdr/code-server",
|
||||
"author": "Coder",
|
||||
"license": "MIT",
|
||||
"description": "Run VS Code remotely.",
|
||||
|
||||
9
packages/ide-api/api.d.ts
vendored
9
packages/ide-api/api.d.ts
vendored
@@ -1,5 +1,10 @@
|
||||
// tslint:disable no-any
|
||||
|
||||
import { ITerminalService } from "vs/workbench/contrib/terminal/common/terminal";
|
||||
import { IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
|
||||
export interface EvalHelper { }
|
||||
interface ActiveEvalEmitter {
|
||||
removeAllListeners(event?: string): void;
|
||||
@@ -144,11 +149,15 @@ declare namespace ide {
|
||||
export const client: {};
|
||||
|
||||
export const workbench: {
|
||||
readonly action: Action,
|
||||
readonly syncActionDescriptor: SyncActionDescriptor,
|
||||
readonly statusbarService: IStatusbarService;
|
||||
readonly actionsRegistry: IWorkbenchActionRegistry;
|
||||
readonly notificationService: INotificationService;
|
||||
readonly storageService: IStorageService;
|
||||
readonly menuRegistry: IMenuRegistry;
|
||||
readonly commandRegistry: ICommandRegistry;
|
||||
readonly terminalService: ITerminalService;
|
||||
|
||||
onFileCreate(cb: (path: string) => void): void;
|
||||
onFileMove(cb: (path: string, target: string) => void): void;
|
||||
|
||||
@@ -171,8 +171,10 @@ const newCreateElement = <K extends keyof HTMLElementTagNameMap>(tagName: K): HT
|
||||
document.createElement = newCreateElement;
|
||||
|
||||
class Clipboard {
|
||||
public has(): boolean {
|
||||
return false;
|
||||
private readonly buffers = new Map<string, Buffer>();
|
||||
|
||||
public has(format: string): boolean {
|
||||
return this.buffers.has(format);
|
||||
}
|
||||
|
||||
public readFindText(): string {
|
||||
@@ -190,6 +192,14 @@ class Clipboard {
|
||||
public readText(): Promise<string> {
|
||||
return clipboard.readText();
|
||||
}
|
||||
|
||||
public writeBuffer(format: string, buffer: Buffer): void {
|
||||
this.buffers.set(format, buffer);
|
||||
}
|
||||
|
||||
public readBuffer(format: string): Buffer | undefined {
|
||||
return this.buffers.get(format);
|
||||
}
|
||||
}
|
||||
|
||||
class Shell {
|
||||
@@ -368,14 +378,31 @@ class BrowserWindow extends EventEmitter {
|
||||
|
||||
public setFullScreen(fullscreen: boolean): void {
|
||||
if (fullscreen) {
|
||||
document.documentElement.requestFullscreen();
|
||||
document.documentElement.requestFullscreen().catch((error) => {
|
||||
logger.error(error.message);
|
||||
});
|
||||
} else {
|
||||
document.exitFullscreen();
|
||||
document.exitFullscreen().catch((error) => {
|
||||
logger.error(error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public isFullScreen(): boolean {
|
||||
return document.fullscreenEnabled;
|
||||
// TypeScript doesn't recognize this property.
|
||||
// tslint:disable no-any
|
||||
if (typeof (window as any)["fullScreen"] !== "undefined") {
|
||||
return (window as any)["fullScreen"];
|
||||
}
|
||||
// tslint:enable no-any
|
||||
|
||||
try {
|
||||
return window.matchMedia("(display-mode: fullscreen)").matches;
|
||||
} catch (error) {
|
||||
logger.error(error.message);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public isFocused(): boolean {
|
||||
|
||||
@@ -35,6 +35,10 @@ class OS {
|
||||
default: this._platform = "linux"; break;
|
||||
}
|
||||
process.platform = this._platform;
|
||||
process.env = {};
|
||||
data.env.forEach((v, k) => {
|
||||
process.env[k] = v;
|
||||
});
|
||||
}
|
||||
|
||||
public release(): string {
|
||||
|
||||
47
packages/protocol/README.md
Normal file
47
packages/protocol/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Protocol
|
||||
|
||||
This module provides a way for the browser to run Node modules like `fs`, `net`,
|
||||
etc.
|
||||
|
||||
## Internals
|
||||
|
||||
### Server-side proxies
|
||||
The server-side proxies are regular classes that call native Node functions. The
|
||||
only thing special about them is that they must return promises and they must
|
||||
return serializable values.
|
||||
|
||||
The only exception to the promise rule are event-related methods such as
|
||||
`onEvent` and `onDone` (these are synchronous). The server will simply
|
||||
immediately bind and push all events it can to the client. It doesn't wait for
|
||||
the client to start listening. This prevents issues with the server not
|
||||
receiving the client's request to start listening in time.
|
||||
|
||||
However, there is a way to specify events that should not bind immediately and
|
||||
should wait for the client to request it, because some events (like `data` on a
|
||||
stream) cannot be bound immediately (because doing so changes how the stream
|
||||
behaves).
|
||||
|
||||
### Client-side proxies
|
||||
Client-side proxies are `Proxy` instances. They simply make remote calls for any
|
||||
method you call on it. The only exception is for events. Each client proxy has a
|
||||
local emitter which it uses in place of a remote call (this allows the call to
|
||||
be completed synchronously on the client). Then when an event is received from
|
||||
the server, it gets emitted on that local emitter.
|
||||
|
||||
When an event is listened to, the proxy also notifies the server so it can start
|
||||
listening in case it isn't already (see the `data` example above). This only
|
||||
works for events that only fire after they are bound.
|
||||
|
||||
### Client-side fills
|
||||
The client-side fills implement the Node API and make calls to the server-side
|
||||
proxies using the client-side proxies.
|
||||
|
||||
When a proxy returns a proxy (for example `fs.createWriteStream`), that proxy is
|
||||
a promise (since communicating with the server is asynchronous). We have to
|
||||
return the fill from `fs.createWriteStream` synchronously, so that means the
|
||||
fill has to contain a proxy promise. To eliminate the need for calling `then`
|
||||
and to keep the code looking clean every time you use the proxy, the proxy is
|
||||
itself wrapped in another proxy which just calls the method after a `then`. This
|
||||
works since all the methods return promises (aside from the event methods, but
|
||||
those are not used by the fills directly—they are only used internally to
|
||||
forward events to the fill if it is an event emitter).
|
||||
@@ -14,5 +14,8 @@
|
||||
"rimraf": "^2.6.3",
|
||||
"text-encoding": "^0.7.0",
|
||||
"ts-protoc-gen": "^0.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"gen": "./scripts/generate_proto.sh"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
protoc --plugin="protoc-gen-ts=./node_modules/.bin/protoc-gen-ts" --js_out="import_style=commonjs,binary:./src/proto" --ts_out="./src/proto" ./src/proto/*.proto --proto_path="./src/proto"
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
protoc --plugin="protoc-gen-ts=./node_modules/.bin/protoc-gen-ts" --js_out="import_style=commonjs,binary:./src/proto" --ts_out="./src/proto" ./src/proto/*.proto --proto_path="./src/proto"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { promisify } from "util";
|
||||
import { Emitter } from "@coder/events";
|
||||
import { logger, field } from "@coder/logger";
|
||||
import { ReadWriteConnection, InitData, SharedProcessData } from "../common/connection";
|
||||
import { Module, ServerProxy } from "../common/proxy";
|
||||
import { ClientServerProxy, Module, ServerProxy } from "../common/proxy";
|
||||
import { argumentToProto, protoToArgument, moduleToProto, protoToModule, protoToOperatingSystem } from "../common/util";
|
||||
import { Argument, Ping, ServerMessage, ClientMessage, Method, Event, Callback } from "../proto";
|
||||
import { FsModule, ChildProcessModule, NetModule, NodePtyModule, SpdlogModule, TrashModule } from "./modules";
|
||||
@@ -224,7 +224,11 @@ export class Client {
|
||||
field("method", method),
|
||||
]);
|
||||
|
||||
proxyMessage.setArgsList(args.map((a) => argumentToProto(a, storeCallback)));
|
||||
proxyMessage.setArgsList(args.map((a) => argumentToProto<ClientServerProxy>(
|
||||
a,
|
||||
storeCallback,
|
||||
(p) => p.proxyId,
|
||||
)));
|
||||
|
||||
const clientMessage = new ClientMessage();
|
||||
clientMessage.setMethod(message);
|
||||
@@ -274,6 +278,9 @@ export class Client {
|
||||
shell: init.getShell(),
|
||||
extensionsDirectory: init.getExtensionsDirectory(),
|
||||
builtInExtensionsDirectory: init.getBuiltinExtensionsDir(),
|
||||
extraExtensionDirectories: init.getExtraExtensionDirectoriesList(),
|
||||
extraBuiltinExtensionDirectories: init.getExtraBuiltinExtensionDirectoriesList(),
|
||||
env: init.getEnvMap(),
|
||||
};
|
||||
this.initDataEmitter.emit(this._initData);
|
||||
break;
|
||||
@@ -429,7 +436,7 @@ export class Client {
|
||||
/**
|
||||
* Return a proxy that makes remote calls.
|
||||
*/
|
||||
private createProxy<T>(proxyId: number | Module, promise: Promise<any> = Promise.resolve()): T {
|
||||
private createProxy<T extends ClientServerProxy>(proxyId: number | Module, promise: Promise<any> = Promise.resolve()): T {
|
||||
logger.trace(() => [
|
||||
"creating proxy",
|
||||
field("proxyId", proxyId),
|
||||
@@ -449,7 +456,7 @@ export class Client {
|
||||
cb(event.event, ...event.args);
|
||||
});
|
||||
},
|
||||
}, {
|
||||
} as ClientServerProxy, {
|
||||
get: (target: any, name: string): any => {
|
||||
// When resolving a promise with a proxy, it will check for "then".
|
||||
if (name === "then") {
|
||||
|
||||
@@ -2,13 +2,22 @@ import * as cp from "child_process";
|
||||
import * as net from "net";
|
||||
import * as stream from "stream";
|
||||
import { callbackify } from "util";
|
||||
import { ClientProxy } from "../../common/proxy";
|
||||
import { ChildProcessModuleProxy, ChildProcessProxy, ChildProcessProxies } from "../../node/modules/child_process";
|
||||
import { Readable, Writable } from "./stream";
|
||||
import { ClientProxy, ClientServerProxy } from "../../common/proxy";
|
||||
import { ChildProcessModuleProxy, ChildProcessProxy } from "../../node/modules/child_process";
|
||||
import { ClientWritableProxy, ClientReadableProxy, Readable, Writable } from "./stream";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.ChildProcess {
|
||||
export interface ClientChildProcessProxy extends ChildProcessProxy, ClientServerProxy<cp.ChildProcess> {}
|
||||
|
||||
export interface ClientChildProcessProxies {
|
||||
childProcess: ClientChildProcessProxy;
|
||||
stdin?: ClientWritableProxy | null;
|
||||
stdout?: ClientReadableProxy | null;
|
||||
stderr?: ClientReadableProxy | null;
|
||||
}
|
||||
|
||||
export class ChildProcess extends ClientProxy<ClientChildProcessProxy> implements cp.ChildProcess {
|
||||
public readonly stdin: stream.Writable;
|
||||
public readonly stdout: stream.Readable;
|
||||
public readonly stderr: stream.Readable;
|
||||
@@ -18,7 +27,7 @@ export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.C
|
||||
private _killed: boolean = false;
|
||||
private _pid = -1;
|
||||
|
||||
public constructor(proxyPromises: Promise<ChildProcessProxies>) {
|
||||
public constructor(proxyPromises: Promise<ClientChildProcessProxies>) {
|
||||
super(proxyPromises.then((p) => p.childProcess));
|
||||
this.stdin = new Writable(proxyPromises.then((p) => p.stdin!));
|
||||
this.stdout = new Readable(proxyPromises.then((p) => p.stdout!));
|
||||
@@ -99,8 +108,14 @@ export class ChildProcess extends ClientProxy<ChildProcessProxy> implements cp.C
|
||||
}
|
||||
}
|
||||
|
||||
interface ClientChildProcessModuleProxy extends ChildProcessModuleProxy, ClientServerProxy {
|
||||
exec(command: string, options?: { encoding?: string | null } & cp.ExecOptions | null, callback?: ((error: cp.ExecException | null, stdin: string | Buffer, stdout: string | Buffer) => void)): Promise<ClientChildProcessProxies>;
|
||||
fork(modulePath: string, args?: string[], options?: cp.ForkOptions): Promise<ClientChildProcessProxies>;
|
||||
spawn(command: string, args?: string[], options?: cp.SpawnOptions): Promise<ClientChildProcessProxies>;
|
||||
}
|
||||
|
||||
export class ChildProcessModule {
|
||||
public constructor(private readonly proxy: ChildProcessModuleProxy) {}
|
||||
public constructor(private readonly proxy: ClientChildProcessModuleProxy) {}
|
||||
|
||||
public exec = (
|
||||
command: string,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import * as fs from "fs";
|
||||
import { callbackify } from "util";
|
||||
import { ClientProxy, Batch } from "../../common/proxy";
|
||||
import { Batch, ClientProxy, ClientServerProxy } from "../../common/proxy";
|
||||
import { IEncodingOptions, IEncodingOptionsCallback } from "../../common/util";
|
||||
import { FsModuleProxy, Stats as IStats, WatcherProxy, WriteStreamProxy } from "../../node/modules/fs";
|
||||
import { Writable } from "./stream";
|
||||
import { FsModuleProxy, ReadStreamProxy, Stats as IStats, WatcherProxy, WriteStreamProxy } from "../../node/modules/fs";
|
||||
import { Readable, Writable } from "./stream";
|
||||
|
||||
// tslint:disable no-any
|
||||
// tslint:disable completed-docs
|
||||
// tslint:disable completed-docs no-any
|
||||
|
||||
class StatBatch extends Batch<IStats, { path: fs.PathLike }> {
|
||||
public constructor(private readonly proxy: FsModuleProxy) {
|
||||
@@ -38,7 +37,9 @@ class ReaddirBatch extends Batch<Buffer[] | fs.Dirent[] | string[], { path: fs.P
|
||||
}
|
||||
}
|
||||
|
||||
class Watcher extends ClientProxy<WatcherProxy> implements fs.FSWatcher {
|
||||
interface ClientWatcherProxy extends WatcherProxy, ClientServerProxy<fs.FSWatcher> {}
|
||||
|
||||
class Watcher extends ClientProxy<ClientWatcherProxy> implements fs.FSWatcher {
|
||||
public close(): void {
|
||||
this.catch(this.proxy.close());
|
||||
}
|
||||
@@ -48,7 +49,25 @@ class Watcher extends ClientProxy<WatcherProxy> implements fs.FSWatcher {
|
||||
}
|
||||
}
|
||||
|
||||
class WriteStream extends Writable<WriteStreamProxy> implements fs.WriteStream {
|
||||
interface ClientReadStreamProxy extends ReadStreamProxy, ClientServerProxy<fs.ReadStream> {}
|
||||
|
||||
class ReadStream extends Readable<ClientReadStreamProxy> implements fs.ReadStream {
|
||||
public get bytesRead(): number {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public get path(): string | Buffer {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.catch(this.proxy.close());
|
||||
}
|
||||
}
|
||||
|
||||
interface ClientWriteStreamProxy extends WriteStreamProxy, ClientServerProxy<fs.WriteStream> {}
|
||||
|
||||
class WriteStream extends Writable<ClientWriteStreamProxy> implements fs.WriteStream {
|
||||
public get bytesWritten(): number {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
@@ -62,12 +81,18 @@ class WriteStream extends Writable<WriteStreamProxy> implements fs.WriteStream {
|
||||
}
|
||||
}
|
||||
|
||||
interface ClientFsModuleProxy extends FsModuleProxy, ClientServerProxy {
|
||||
createReadStream(path: fs.PathLike, options?: any): Promise<ClientReadStreamProxy>;
|
||||
createWriteStream(path: fs.PathLike, options?: any): Promise<ClientWriteStreamProxy>;
|
||||
watch(filename: fs.PathLike, options?: IEncodingOptions): Promise<ClientWatcherProxy>;
|
||||
}
|
||||
|
||||
export class FsModule {
|
||||
private readonly statBatch: StatBatch;
|
||||
private readonly lstatBatch: LstatBatch;
|
||||
private readonly readdirBatch: ReaddirBatch;
|
||||
|
||||
public constructor(private readonly proxy: FsModuleProxy) {
|
||||
public constructor(private readonly proxy: ClientFsModuleProxy) {
|
||||
this.statBatch = new StatBatch(this.proxy);
|
||||
this.lstatBatch = new LstatBatch(this.proxy);
|
||||
this.readdirBatch = new ReaddirBatch(this.proxy);
|
||||
@@ -110,6 +135,10 @@ export class FsModule {
|
||||
);
|
||||
}
|
||||
|
||||
public createReadStream = (path: fs.PathLike, options?: any): fs.ReadStream => {
|
||||
return new ReadStream(this.proxy.createReadStream(path, options));
|
||||
}
|
||||
|
||||
public createWriteStream = (path: fs.PathLike, options?: any): fs.WriteStream => {
|
||||
return new WriteStream(this.proxy.createWriteStream(path, options));
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import * as net from "net";
|
||||
import { callbackify } from "util";
|
||||
import { ClientProxy } from "../../common/proxy";
|
||||
import { ClientProxy, ClientServerProxy } from "../../common/proxy";
|
||||
import { NetModuleProxy, NetServerProxy, NetSocketProxy } from "../../node/modules/net";
|
||||
import { Duplex } from "./stream";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
export class Socket extends Duplex<NetSocketProxy> implements net.Socket {
|
||||
interface ClientNetSocketProxy extends NetSocketProxy, ClientServerProxy<net.Socket> {}
|
||||
|
||||
export class Socket extends Duplex<ClientNetSocketProxy> implements net.Socket {
|
||||
private _connecting: boolean = false;
|
||||
private _destroyed: boolean = false;
|
||||
|
||||
public constructor(proxyPromise: Promise<NetSocketProxy> | NetSocketProxy, connecting?: boolean) {
|
||||
public constructor(proxyPromise: Promise<ClientNetSocketProxy> | ClientNetSocketProxy, connecting?: boolean) {
|
||||
super(proxyPromise);
|
||||
if (connecting) {
|
||||
this._connecting = connecting;
|
||||
@@ -126,12 +128,16 @@ export class Socket extends Duplex<NetSocketProxy> implements net.Socket {
|
||||
}
|
||||
}
|
||||
|
||||
export class Server extends ClientProxy<NetServerProxy> implements net.Server {
|
||||
interface ClientNetServerProxy extends NetServerProxy, ClientServerProxy<net.Server> {
|
||||
onConnection(cb: (proxy: ClientNetSocketProxy) => void): Promise<void>;
|
||||
}
|
||||
|
||||
export class Server extends ClientProxy<ClientNetServerProxy> implements net.Server {
|
||||
private socketId = 0;
|
||||
private readonly sockets = new Map<number, net.Socket>();
|
||||
private _listening: boolean = false;
|
||||
|
||||
public constructor(proxyPromise: Promise<NetServerProxy> | NetServerProxy) {
|
||||
public constructor(proxyPromise: Promise<ClientNetServerProxy> | ClientNetServerProxy) {
|
||||
super(proxyPromise);
|
||||
|
||||
this.catch(this.proxy.onConnection((socketProxy) => {
|
||||
@@ -208,11 +214,17 @@ export class Server extends ClientProxy<NetServerProxy> implements net.Server {
|
||||
|
||||
type NodeNet = typeof net;
|
||||
|
||||
interface ClientNetModuleProxy extends NetModuleProxy, ClientServerProxy {
|
||||
createSocket(options?: net.SocketConstructorOpts): Promise<ClientNetSocketProxy>;
|
||||
createConnection(target: string | number | net.NetConnectOpts, host?: string): Promise<ClientNetSocketProxy>;
|
||||
createServer(options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean }): Promise<ClientNetServerProxy>;
|
||||
}
|
||||
|
||||
export class NetModule implements NodeNet {
|
||||
public readonly Socket: typeof net.Socket;
|
||||
public readonly Server: typeof net.Server;
|
||||
|
||||
public constructor(private readonly proxy: NetModuleProxy) {
|
||||
public constructor(private readonly proxy: ClientNetModuleProxy) {
|
||||
// @ts-ignore this is because Socket is missing things from the Stream
|
||||
// namespace but I'm unsure how best to provide them (finished,
|
||||
// finished.__promisify__, pipeline, and some others) or if it even matters.
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import * as pty from "node-pty";
|
||||
import { ClientProxy } from "../../common/proxy";
|
||||
import { ClientProxy, ClientServerProxy } from "../../common/proxy";
|
||||
import { NodePtyModuleProxy, NodePtyProcessProxy } from "../../node/modules/node-pty";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements pty.IPty {
|
||||
interface ClientNodePtyProcessProxy extends NodePtyProcessProxy, ClientServerProxy {}
|
||||
|
||||
export class NodePtyProcess extends ClientProxy<ClientNodePtyProcessProxy> implements pty.IPty {
|
||||
private _pid = -1;
|
||||
private _process = "";
|
||||
private lastCols: number | undefined;
|
||||
private lastRows: number | undefined;
|
||||
|
||||
public constructor(
|
||||
private readonly moduleProxy: NodePtyModuleProxy,
|
||||
private readonly moduleProxy: ClientNodePtyModuleProxy,
|
||||
private readonly file: string,
|
||||
private readonly args: string[] | string,
|
||||
private readonly options: pty.IPtyForkOptions,
|
||||
@@ -18,10 +22,12 @@ export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements
|
||||
this.on("process", (process) => this._process = process);
|
||||
}
|
||||
|
||||
protected initialize(proxyPromise: Promise<NodePtyProcessProxy>): void {
|
||||
super.initialize(proxyPromise);
|
||||
protected initialize(proxyPromise: Promise<ClientNodePtyProcessProxy>): ClientNodePtyProcessProxy {
|
||||
const proxy = super.initialize(proxyPromise);
|
||||
this.catch(this.proxy.getPid().then((p) => this._pid = p));
|
||||
this.catch(this.proxy.getProcess().then((p) => this._process = p));
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
public get pid(): number {
|
||||
@@ -33,6 +39,9 @@ export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements
|
||||
}
|
||||
|
||||
public resize(columns: number, rows: number): void {
|
||||
this.lastCols = columns;
|
||||
this.lastRows = rows;
|
||||
|
||||
this.catch(this.proxy.resize(columns, rows));
|
||||
}
|
||||
|
||||
@@ -47,14 +56,22 @@ export class NodePtyProcess extends ClientProxy<NodePtyProcessProxy> implements
|
||||
protected handleDisconnect(): void {
|
||||
this._process += " (disconnected)";
|
||||
this.emit("data", "\r\n\nLost connection...\r\n\n");
|
||||
this.initialize(this.moduleProxy.spawn(this.file, this.args, this.options));
|
||||
this.initialize(this.moduleProxy.spawn(this.file, this.args, {
|
||||
...this.options,
|
||||
cols: this.lastCols || this.options.cols,
|
||||
rows: this.lastRows || this.options.rows,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
type NodePty = typeof pty;
|
||||
|
||||
interface ClientNodePtyModuleProxy extends NodePtyModuleProxy, ClientServerProxy {
|
||||
spawn(file: string, args: string[] | string, options: pty.IPtyForkOptions): Promise<ClientNodePtyProcessProxy>;
|
||||
}
|
||||
|
||||
export class NodePtyModule implements NodePty {
|
||||
public constructor(private readonly proxy: NodePtyModuleProxy) {}
|
||||
public constructor(private readonly proxy: ClientNodePtyModuleProxy) {}
|
||||
|
||||
public spawn = (file: string, args: string[] | string, options: pty.IPtyForkOptions): pty.IPty => {
|
||||
return new NodePtyProcess(this.proxy, file, args, options);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import * as spdlog from "spdlog";
|
||||
import { ClientProxy } from "../../common/proxy";
|
||||
import { ClientProxy, ClientServerProxy } from "../../common/proxy";
|
||||
import { RotatingLoggerProxy, SpdlogModuleProxy } from "../../node/modules/spdlog";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
class RotatingLogger extends ClientProxy<RotatingLoggerProxy> implements spdlog.RotatingLogger {
|
||||
interface ClientRotatingLoggerProxy extends RotatingLoggerProxy, ClientServerProxy {}
|
||||
|
||||
class RotatingLogger extends ClientProxy<ClientRotatingLoggerProxy> implements spdlog.RotatingLogger {
|
||||
public constructor(
|
||||
private readonly moduleProxy: SpdlogModuleProxy,
|
||||
private readonly moduleProxy: ClientSpdlogModuleProxy,
|
||||
private readonly name: string,
|
||||
private readonly filename: string,
|
||||
private readonly filesize: number,
|
||||
@@ -31,10 +33,14 @@ class RotatingLogger extends ClientProxy<RotatingLoggerProxy> implements spdlog.
|
||||
}
|
||||
}
|
||||
|
||||
interface ClientSpdlogModuleProxy extends SpdlogModuleProxy, ClientServerProxy {
|
||||
createLogger(name: string, filePath: string, fileSize: number, fileCount: number): Promise<ClientRotatingLoggerProxy>;
|
||||
}
|
||||
|
||||
export class SpdlogModule {
|
||||
public readonly RotatingLogger: typeof spdlog.RotatingLogger;
|
||||
|
||||
public constructor(private readonly proxy: SpdlogModuleProxy) {
|
||||
public constructor(private readonly proxy: ClientSpdlogModuleProxy) {
|
||||
this.RotatingLogger = class extends RotatingLogger {
|
||||
public constructor(name: string, filename: string, filesize: number, filecount: number) {
|
||||
super(proxy, name, filename, filesize, filecount);
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import * as stream from "stream";
|
||||
import { callbackify } from "util";
|
||||
import { ClientProxy } from "../../common/proxy";
|
||||
import { DuplexProxy, IReadableProxy, WritableProxy } from "../../node/modules/stream";
|
||||
import { ClientProxy, ClientServerProxy } from "../../common/proxy";
|
||||
import { isPromise } from "../../common/util";
|
||||
import { DuplexProxy, ReadableProxy, WritableProxy } from "../../node/modules/stream";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
// tslint:disable completed-docs no-any
|
||||
|
||||
export class Writable<T extends WritableProxy = WritableProxy> extends ClientProxy<T> implements stream.Writable {
|
||||
export interface ClientWritableProxy extends WritableProxy, ClientServerProxy<stream.Writable> {}
|
||||
|
||||
export class Writable<T extends ClientWritableProxy = ClientWritableProxy> extends ClientProxy<T> implements stream.Writable {
|
||||
public get writable(): boolean {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
@@ -50,7 +53,6 @@ export class Writable<T extends WritableProxy = WritableProxy> extends ClientPro
|
||||
return this.catch(this.proxy.setDefaultEncoding(encoding));
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public write(chunk: any, encoding?: string | ((error?: Error | null) => void), callback?: (error?: Error | null) => void): boolean {
|
||||
if (typeof encoding === "function") {
|
||||
callback = encoding;
|
||||
@@ -65,7 +67,6 @@ export class Writable<T extends WritableProxy = WritableProxy> extends ClientPro
|
||||
return true; // Always true since we can't get this synchronously.
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public end(data?: any | (() => void), encoding?: string | (() => void), callback?: (() => void)): void {
|
||||
if (typeof data === "function") {
|
||||
callback = data;
|
||||
@@ -88,7 +89,9 @@ export class Writable<T extends WritableProxy = WritableProxy> extends ClientPro
|
||||
}
|
||||
}
|
||||
|
||||
export class Readable<T extends IReadableProxy = IReadableProxy> extends ClientProxy<T> implements stream.Readable {
|
||||
export interface ClientReadableProxy extends ReadableProxy, ClientServerProxy<stream.Readable> {}
|
||||
|
||||
export class Readable<T extends ClientReadableProxy = ClientReadableProxy> extends ClientProxy<T> implements stream.Readable {
|
||||
public get readable(): boolean {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
@@ -141,11 +144,20 @@ export class Readable<T extends IReadableProxy = IReadableProxy> extends ClientP
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public pipe<T>(): T {
|
||||
throw new Error("not implemented");
|
||||
public pipe<P extends NodeJS.WritableStream>(destination: P, options?: { end?: boolean }): P {
|
||||
const writableProxy = (destination as any as Writable).proxyPromise;
|
||||
if (!writableProxy) {
|
||||
throw new Error("can only pipe stream proxies");
|
||||
}
|
||||
this.catch(
|
||||
isPromise(writableProxy)
|
||||
? writableProxy.then((p) => this.proxy.pipe(p, options))
|
||||
: this.proxy.pipe(writableProxy, options),
|
||||
);
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public [Symbol.asyncIterator](): AsyncIterableIterator<any> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
@@ -164,7 +176,9 @@ export class Readable<T extends IReadableProxy = IReadableProxy> extends ClientP
|
||||
}
|
||||
}
|
||||
|
||||
export class Duplex<T extends DuplexProxy = DuplexProxy> extends Writable<T> implements stream.Duplex, stream.Readable {
|
||||
export interface ClientDuplexProxy extends DuplexProxy, ClientServerProxy<stream.Duplex> {}
|
||||
|
||||
export class Duplex<T extends ClientDuplexProxy = ClientDuplexProxy> extends Writable<T> implements stream.Duplex, stream.Readable {
|
||||
private readonly _readable: Readable;
|
||||
|
||||
public constructor(proxyPromise: Promise<T> | T) {
|
||||
@@ -228,7 +242,6 @@ export class Duplex<T extends DuplexProxy = DuplexProxy> extends Writable<T> imp
|
||||
this._readable.unshift();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public [Symbol.asyncIterator](): AsyncIterableIterator<any> {
|
||||
return this._readable[Symbol.asyncIterator]();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import * as trash from "trash";
|
||||
import { ClientServerProxy } from "../../common/proxy";
|
||||
import { TrashModuleProxy } from "../../node/modules/trash";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
interface ClientTrashModuleProxy extends TrashModuleProxy, ClientServerProxy {}
|
||||
|
||||
export class TrashModule {
|
||||
public constructor(private readonly proxy: TrashModuleProxy) {}
|
||||
public constructor(private readonly proxy: ClientTrashModuleProxy) {}
|
||||
|
||||
public trash = (path: string, options?: trash.Options): Promise<void> => {
|
||||
return this.proxy.trash(path, options);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import * as jspb from "google-protobuf";
|
||||
|
||||
export interface SendableConnection {
|
||||
send(data: Buffer | Uint8Array): void;
|
||||
}
|
||||
@@ -25,6 +27,9 @@ export interface InitData {
|
||||
readonly shell: string;
|
||||
readonly extensionsDirectory: string;
|
||||
readonly builtInExtensionsDirectory: string;
|
||||
readonly extraExtensionDirectories: string[];
|
||||
readonly extraBuiltinExtensionDirectories: string[];
|
||||
readonly env: jspb.Map<string, string>;
|
||||
}
|
||||
|
||||
export interface SharedProcessData {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { EventEmitter } from "events";
|
||||
import { isPromise } from "./util";
|
||||
import { isPromise, EventCallback } from "./util";
|
||||
|
||||
// tslint:disable no-any
|
||||
|
||||
/**
|
||||
* Allow using a proxy like it's returned synchronously. This only works because
|
||||
* all proxy methods return promises.
|
||||
* all proxy methods must return promises.
|
||||
*/
|
||||
const unpromisify = <T extends ServerProxy>(proxyPromise: Promise<T>): T => {
|
||||
const unpromisify = <T extends ClientServerProxy>(proxyPromise: Promise<T>): T => {
|
||||
return new Proxy({}, {
|
||||
get: (target: any, name: string): any => {
|
||||
if (typeof target[name] === "undefined") {
|
||||
@@ -24,23 +24,23 @@ const unpromisify = <T extends ServerProxy>(proxyPromise: Promise<T>): T => {
|
||||
};
|
||||
|
||||
/**
|
||||
* Client-side emitter that just forwards proxy events to its own emitter.
|
||||
* It also turns a promisified proxy into a non-promisified proxy so we don't
|
||||
* need a bunch of `then` calls everywhere.
|
||||
* Client-side emitter that just forwards server proxy events to its own
|
||||
* emitter. It also turns a promisified server proxy into a non-promisified
|
||||
* proxy so we don't need a bunch of `then` calls everywhere.
|
||||
*/
|
||||
export abstract class ClientProxy<T extends ServerProxy> extends EventEmitter {
|
||||
private _proxy: T | undefined;
|
||||
export abstract class ClientProxy<T extends ClientServerProxy> extends EventEmitter {
|
||||
private _proxy: T;
|
||||
|
||||
/**
|
||||
* You can specify not to bind events in order to avoid emitting twice for
|
||||
* duplex streams.
|
||||
*/
|
||||
public constructor(
|
||||
proxyPromise: Promise<T> | T,
|
||||
private _proxyPromise: Promise<T> | T,
|
||||
private readonly bindEvents: boolean = true,
|
||||
) {
|
||||
super();
|
||||
this.initialize(proxyPromise);
|
||||
this._proxy = this.initialize(this._proxyPromise);
|
||||
if (this.bindEvents) {
|
||||
this.on("disconnected", (error) => {
|
||||
try {
|
||||
@@ -64,11 +64,34 @@ export abstract class ClientProxy<T extends ServerProxy> extends EventEmitter {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected get proxy(): T {
|
||||
if (!this._proxy) {
|
||||
throw new Error("not initialized");
|
||||
}
|
||||
/**
|
||||
* Bind the event locally and ensure the event is bound on the server.
|
||||
*/
|
||||
public addListener(event: string, listener: (...args: any[]) => void): this {
|
||||
this.catch(this.proxy.bindDelayedEvent(event));
|
||||
|
||||
return super.on(event, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for `addListener`.
|
||||
*/
|
||||
public on(event: string, listener: (...args: any[]) => void): this {
|
||||
return this.addListener(event, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Original promise for the server proxy. Can be used to be passed as an
|
||||
* argument.
|
||||
*/
|
||||
public get proxyPromise(): Promise<T> | T {
|
||||
return this._proxyPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server proxy.
|
||||
*/
|
||||
protected get proxy(): T {
|
||||
return this._proxy;
|
||||
}
|
||||
|
||||
@@ -76,13 +99,18 @@ export abstract class ClientProxy<T extends ServerProxy> extends EventEmitter {
|
||||
* Initialize the proxy by unpromisifying if necessary and binding to its
|
||||
* events.
|
||||
*/
|
||||
protected initialize(proxyPromise: Promise<T> | T): void {
|
||||
this._proxy = isPromise(proxyPromise) ? unpromisify(proxyPromise) : proxyPromise;
|
||||
protected initialize(proxyPromise: Promise<T> | T): T {
|
||||
this._proxyPromise = proxyPromise;
|
||||
this._proxy = isPromise(this._proxyPromise)
|
||||
? unpromisify(this._proxyPromise)
|
||||
: this._proxyPromise;
|
||||
if (this.bindEvents) {
|
||||
this.catch(this.proxy.onEvent((event, ...args): void => {
|
||||
this.proxy.onEvent((event, ...args): void => {
|
||||
this.emit(event, ...args);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
return this._proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,34 +130,107 @@ export abstract class ClientProxy<T extends ServerProxy> extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
export interface ServerProxyOptions<T> {
|
||||
/**
|
||||
* The events to bind immediately.
|
||||
*/
|
||||
bindEvents: string[];
|
||||
/**
|
||||
* Events that signal the proxy is done.
|
||||
*/
|
||||
doneEvents: string[];
|
||||
/**
|
||||
* Events that should only be bound when asked
|
||||
*/
|
||||
delayedEvents?: string[];
|
||||
/**
|
||||
* Whatever is emitting events (stream, child process, etc).
|
||||
*/
|
||||
instance: T;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy to the actual instance on the server. Every method must only accept
|
||||
* serializable arguments and must return promises with serializable values. If
|
||||
* a proxy itself has proxies on creation (like how ChildProcess has stdin),
|
||||
* The actual proxy instance on the server. Every method must only accept
|
||||
* serializable arguments and must return promises with serializable values.
|
||||
*
|
||||
* If a proxy itself has proxies on creation (like how ChildProcess has stdin),
|
||||
* then it should return all of those at once, otherwise you will miss events
|
||||
* from those child proxies and fail to dispose them properly.
|
||||
*
|
||||
* Events listeners are added client-side (since all events automatically
|
||||
* forward to the client), so onDone and onEvent do not need to be asynchronous.
|
||||
*/
|
||||
export interface ServerProxy {
|
||||
export abstract class ServerProxy<T extends EventEmitter = EventEmitter> {
|
||||
public readonly instance: T;
|
||||
|
||||
private readonly callbacks = <EventCallback[]>[];
|
||||
|
||||
public constructor(private readonly options: ServerProxyOptions<T>) {
|
||||
this.instance = options.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose the proxy.
|
||||
*/
|
||||
dispose(): Promise<void>;
|
||||
public async dispose(): Promise<void> {
|
||||
this.instance.removeAllListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used instead of an event to force it to be implemented since there
|
||||
* would be no guarantee the implementation would remember to emit the event.
|
||||
*/
|
||||
onDone(cb: () => void): Promise<void>;
|
||||
public onDone(cb: () => void): void {
|
||||
this.options.doneEvents.forEach((event) => {
|
||||
this.instance.on(event, cb);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind an event that will not fire without first binding it and shouldn't be
|
||||
* bound immediately.
|
||||
|
||||
* For example, binding to `data` switches a stream to flowing mode, so we
|
||||
* don't want to do it until we're asked. Otherwise something like `pipe`
|
||||
* won't work because potentially some or all of the data will already have
|
||||
* been flushed out.
|
||||
*/
|
||||
public async bindDelayedEvent(event: string): Promise<void> {
|
||||
if (this.options.delayedEvents
|
||||
&& this.options.delayedEvents.includes(event)
|
||||
&& !this.options.bindEvents.includes(event)) {
|
||||
this.options.bindEvents.push(event);
|
||||
this.callbacks.forEach((cb) => {
|
||||
this.instance.on(event, (...args: any[]) => cb(event, ...args));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to all possible events. On the client, this is to reduce boilerplate
|
||||
* that would just be a bunch of error-prone forwarding of each individual
|
||||
* event from the proxy to its own emitter. It also fixes a timing issue
|
||||
* because we just always send all events from the server, so we never miss
|
||||
* any due to listening too late.
|
||||
* event from the proxy to its own emitter.
|
||||
*
|
||||
* It also fixes a timing issue because we just always send all events from
|
||||
* the server, so we never miss any due to listening too late.
|
||||
*
|
||||
* This cannot be async because then we can bind to the events too late.
|
||||
*/
|
||||
// tslint:disable-next-line no-any
|
||||
onEvent(cb: (event: string, ...args: any[]) => void): Promise<void>;
|
||||
public onEvent(cb: EventCallback): void {
|
||||
this.callbacks.push(cb);
|
||||
this.options.bindEvents.forEach((event) => {
|
||||
this.instance.on(event, (...args: any[]) => cb(event, ...args));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A server-side proxy stored on the client. The proxy ID only exists on the
|
||||
* client-side version of the server proxy. The event listeners are handled by
|
||||
* the client and the remaining methods are proxied to the server.
|
||||
*/
|
||||
export interface ClientServerProxy<T extends EventEmitter = EventEmitter> extends ServerProxy<T> {
|
||||
proxyId: number | Module;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Argument, Module as ProtoModule, WorkingInit } from "../proto";
|
||||
import { OperatingSystem } from "../common/connection";
|
||||
import { Module, ServerProxy } from "./proxy";
|
||||
import { ClientServerProxy, Module, ServerProxy } from "./proxy";
|
||||
|
||||
// tslint:disable no-any
|
||||
|
||||
@@ -19,6 +19,8 @@ export const escapePath = (path: string): string => {
|
||||
return `'${path.replace(/'/g, "'\\''")}'`;
|
||||
};
|
||||
|
||||
export type EventCallback = (event: string, ...args: any[]) => void;
|
||||
|
||||
export type IEncodingOptions = {
|
||||
encoding?: BufferEncoding | null;
|
||||
flag?: string;
|
||||
@@ -34,15 +36,26 @@ export type IEncodingOptionsCallback = IEncodingOptions | ((err: NodeJS.ErrnoExc
|
||||
* If sending a function is possible, provide `storeFunction`.
|
||||
* If sending a proxy is possible, provide `storeProxy`.
|
||||
*/
|
||||
export const argumentToProto = (
|
||||
export const argumentToProto = <P = ClientServerProxy | ServerProxy>(
|
||||
value: any,
|
||||
storeFunction?: (fn: () => void) => number,
|
||||
storeProxy?: (proxy: ServerProxy) => number,
|
||||
storeProxy?: (proxy: P) => number | Module,
|
||||
): Argument => {
|
||||
const convert = (currentValue: any): Argument => {
|
||||
const message = new Argument();
|
||||
|
||||
if (currentValue instanceof Error
|
||||
if (isProxy<P>(currentValue)) {
|
||||
if (!storeProxy) {
|
||||
throw new Error("no way to serialize proxy");
|
||||
}
|
||||
const arg = new Argument.ProxyValue();
|
||||
const id = storeProxy(currentValue);
|
||||
if (typeof id === "string") {
|
||||
throw new Error("unable to serialize module proxy");
|
||||
}
|
||||
arg.setId(id);
|
||||
message.setProxy(arg);
|
||||
} else if (currentValue instanceof Error
|
||||
|| (currentValue && typeof currentValue.message !== "undefined"
|
||||
&& typeof currentValue.stack !== "undefined")) {
|
||||
const arg = new Argument.ErrorValue();
|
||||
@@ -58,13 +71,6 @@ export const argumentToProto = (
|
||||
const arg = new Argument.ArrayValue();
|
||||
arg.setDataList(currentValue.map(convert));
|
||||
message.setArray(arg);
|
||||
} else if (isProxy(currentValue)) {
|
||||
if (!storeProxy) {
|
||||
throw new Error("no way to serialize proxy");
|
||||
}
|
||||
const arg = new Argument.ProxyValue();
|
||||
arg.setId(storeProxy(currentValue));
|
||||
message.setProxy(arg);
|
||||
} else if (currentValue instanceof Date
|
||||
|| (currentValue && typeof currentValue.getTime === "function")) {
|
||||
const arg = new Argument.DateValue();
|
||||
@@ -218,7 +224,7 @@ export const platformToProto = (platform: NodeJS.Platform): WorkingInit.Operatin
|
||||
}
|
||||
};
|
||||
|
||||
export const isProxy = (value: any): value is ServerProxy => {
|
||||
export const isProxy = <P = ClientServerProxy | ServerProxy>(value: any): value is P => {
|
||||
return value && typeof value === "object" && typeof value.onEvent === "function";
|
||||
};
|
||||
|
||||
@@ -230,8 +236,11 @@ export const isPromise = (value: any): value is Promise<any> => {
|
||||
* When spawning VS Code tries to preserve the environment but since it's in
|
||||
* the browser, it doesn't work.
|
||||
*/
|
||||
export const preserveEnv = (options?: { env?: NodeJS.ProcessEnv } | null): void => {
|
||||
if (options && options.env) {
|
||||
options.env = { ...process.env, ...options.env };
|
||||
}
|
||||
export const withEnv = <T extends { env?: NodeJS.ProcessEnv }>(options?: T): T | undefined => {
|
||||
return options && options.env ? {
|
||||
...options,
|
||||
env: {
|
||||
...process.env, ...options.env,
|
||||
},
|
||||
} : options;
|
||||
};
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
import * as cp from "child_process";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { preserveEnv } from "../../common/util";
|
||||
import { withEnv } from "../../common/util";
|
||||
import { WritableProxy, ReadableProxy } from "./stream";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
export type ForkProvider = (modulePath: string, args?: string[], options?: cp.ForkOptions) => cp.ChildProcess;
|
||||
|
||||
export class ChildProcessProxy implements ServerProxy {
|
||||
public constructor(private readonly process: cp.ChildProcess) {}
|
||||
export class ChildProcessProxy extends ServerProxy<cp.ChildProcess> {
|
||||
public constructor(instance: cp.ChildProcess) {
|
||||
super({
|
||||
bindEvents: ["close", "disconnect", "error", "exit", "message"],
|
||||
doneEvents: ["close"],
|
||||
instance,
|
||||
});
|
||||
}
|
||||
|
||||
public async kill(signal?: string): Promise<void> {
|
||||
this.process.kill(signal);
|
||||
this.instance.kill(signal);
|
||||
}
|
||||
|
||||
public async disconnect(): Promise<void> {
|
||||
this.process.disconnect();
|
||||
this.instance.disconnect();
|
||||
}
|
||||
|
||||
public async ref(): Promise<void> {
|
||||
this.process.ref();
|
||||
this.instance.ref();
|
||||
}
|
||||
|
||||
public async unref(): Promise<void> {
|
||||
this.process.unref();
|
||||
this.instance.unref();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async send(message: any): Promise<void> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
this.process.send(message, (error) => {
|
||||
this.instance.send(message, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@@ -40,25 +46,13 @@ export class ChildProcessProxy implements ServerProxy {
|
||||
}
|
||||
|
||||
public async getPid(): Promise<number> {
|
||||
return this.process.pid;
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.process.on("close", cb);
|
||||
return this.instance.pid;
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.process.kill();
|
||||
setTimeout(() => this.process.kill("SIGKILL"), 5000); // Double tap.
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.process.on("close", (code, signal) => cb("close", code, signal));
|
||||
this.process.on("disconnect", () => cb("disconnect"));
|
||||
this.process.on("error", (error) => cb("error", error));
|
||||
this.process.on("exit", (exitCode, signal) => cb("exit", exitCode, signal));
|
||||
this.process.on("message", (message) => cb("message", message));
|
||||
this.instance.kill();
|
||||
setTimeout(() => this.instance.kill("SIGKILL"), 5000); // Double tap.
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,29 +71,25 @@ export class ChildProcessModuleProxy {
|
||||
options?: { encoding?: string | null } & cp.ExecOptions | null,
|
||||
callback?: ((error: cp.ExecException | null, stdin: string | Buffer, stdout: string | Buffer) => void),
|
||||
): Promise<ChildProcessProxies> {
|
||||
preserveEnv(options);
|
||||
|
||||
return this.returnProxies(cp.exec(command, options, callback));
|
||||
return this.returnProxies(cp.exec(command, options && withEnv(options), callback));
|
||||
}
|
||||
|
||||
public async fork(modulePath: string, args?: string[], options?: cp.ForkOptions): Promise<ChildProcessProxies> {
|
||||
preserveEnv(options);
|
||||
|
||||
return this.returnProxies((this.forkProvider || cp.fork)(modulePath, args, options));
|
||||
return this.returnProxies((this.forkProvider || cp.fork)(modulePath, args, withEnv(options)));
|
||||
}
|
||||
|
||||
public async spawn(command: string, args?: string[], options?: cp.SpawnOptions): Promise<ChildProcessProxies> {
|
||||
preserveEnv(options);
|
||||
|
||||
return this.returnProxies(cp.spawn(command, args, options));
|
||||
return this.returnProxies(cp.spawn(command, args, withEnv(options)));
|
||||
}
|
||||
|
||||
private returnProxies(process: cp.ChildProcess): ChildProcessProxies {
|
||||
return {
|
||||
childProcess: new ChildProcessProxy(process),
|
||||
stdin: process.stdin && new WritableProxy(process.stdin),
|
||||
stdout: process.stdout && new ReadableProxy(process.stdout),
|
||||
stderr: process.stderr && new ReadableProxy(process.stderr),
|
||||
// Child processes streams appear to immediately flow so we need to bind
|
||||
// to the data event right away.
|
||||
stdout: process.stdout && new ReadableProxy(process.stdout, ["data"]),
|
||||
stderr: process.stderr && new ReadableProxy(process.stderr, ["data"]),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import * as fs from "fs";
|
||||
import { promisify } from "util";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { IEncodingOptions } from "../../common/util";
|
||||
import { WritableProxy } from "./stream";
|
||||
import { ReadableProxy, WritableProxy } from "./stream";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
// tslint:disable completed-docs no-any
|
||||
|
||||
/**
|
||||
* A serializable version of fs.Stats.
|
||||
@@ -37,45 +37,52 @@ export interface Stats {
|
||||
_isSocket: boolean;
|
||||
}
|
||||
|
||||
export class WriteStreamProxy extends WritableProxy<fs.WriteStream> {
|
||||
export class ReadStreamProxy extends ReadableProxy<fs.ReadStream> {
|
||||
public constructor(stream: fs.ReadStream) {
|
||||
super(stream, ["open"]);
|
||||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
this.stream.close();
|
||||
this.instance.close();
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.instance.close();
|
||||
await super.dispose();
|
||||
this.stream.close();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
await super.onEvent(cb);
|
||||
this.stream.on("open", (fd) => cb("open", fd));
|
||||
}
|
||||
}
|
||||
|
||||
export class WatcherProxy implements ServerProxy {
|
||||
public constructor(private readonly watcher: fs.FSWatcher) {}
|
||||
export class WriteStreamProxy extends WritableProxy<fs.WriteStream> {
|
||||
public constructor(stream: fs.WriteStream) {
|
||||
super(stream, ["open"]);
|
||||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
this.watcher.close();
|
||||
this.instance.close();
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.watcher.close();
|
||||
this.watcher.removeAllListeners();
|
||||
this.instance.close();
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class WatcherProxy extends ServerProxy<fs.FSWatcher> {
|
||||
public constructor(watcher: fs.FSWatcher) {
|
||||
super({
|
||||
bindEvents: ["change", "close", "error"],
|
||||
doneEvents: ["close", "error"],
|
||||
instance: watcher,
|
||||
});
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.watcher.on("close", cb);
|
||||
this.watcher.on("error", cb);
|
||||
public async close(): Promise<void> {
|
||||
this.instance.close();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.watcher.on("change", (event, filename) => cb("change", event, filename));
|
||||
this.watcher.on("close", () => cb("close"));
|
||||
this.watcher.on("error", (error) => cb("error", error));
|
||||
public async dispose(): Promise<void> {
|
||||
this.instance.close();
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +91,6 @@ export class FsModuleProxy {
|
||||
return promisify(fs.access)(path, mode);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public appendFile(file: fs.PathLike | number, data: any, options?: fs.WriteFileOptions): Promise<void> {
|
||||
return promisify(fs.appendFile)(file, data, options);
|
||||
}
|
||||
@@ -105,7 +111,10 @@ export class FsModuleProxy {
|
||||
return promisify(fs.copyFile)(src, dest, flags);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async createReadStream(path: fs.PathLike, options?: any): Promise<ReadStreamProxy> {
|
||||
return new ReadStreamProxy(fs.createReadStream(path, options));
|
||||
}
|
||||
|
||||
public async createWriteStream(path: fs.PathLike, options?: any): Promise<WriteStreamProxy> {
|
||||
return new WriteStreamProxy(fs.createWriteStream(path, options));
|
||||
}
|
||||
@@ -236,7 +245,6 @@ export class FsModuleProxy {
|
||||
return promisify(fs.write)(fd, buffer, offset, length, position);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public writeFile (path: fs.PathLike | number, data: any, options: IEncodingOptions): Promise<void> {
|
||||
return promisify(fs.writeFile)(path, data, options);
|
||||
}
|
||||
|
||||
@@ -2,78 +2,65 @@ import * as net from "net";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { DuplexProxy } from "./stream";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
// tslint:disable completed-docs no-any
|
||||
|
||||
export class NetSocketProxy extends DuplexProxy<net.Socket> {
|
||||
public constructor(socket: net.Socket) {
|
||||
super(socket, ["connect", "lookup", "timeout"]);
|
||||
}
|
||||
|
||||
public async connect(options: number | string | net.SocketConnectOpts, host?: string): Promise<void> {
|
||||
this.stream.connect(options as any, host as any); // tslint:disable-line no-any this works fine
|
||||
this.instance.connect(options as any, host as any);
|
||||
}
|
||||
|
||||
public async unref(): Promise<void> {
|
||||
this.stream.unref();
|
||||
this.instance.unref();
|
||||
}
|
||||
|
||||
public async ref(): Promise<void> {
|
||||
this.stream.ref();
|
||||
this.instance.ref();
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.stream.removeAllListeners();
|
||||
this.stream.end();
|
||||
this.stream.destroy();
|
||||
this.stream.unref();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.stream.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
await super.onEvent(cb);
|
||||
this.stream.on("connect", () => cb("connect"));
|
||||
this.stream.on("lookup", (error, address, family, host) => cb("lookup", error, address, family, host));
|
||||
this.stream.on("timeout", () => cb("timeout"));
|
||||
this.instance.end();
|
||||
this.instance.destroy();
|
||||
this.instance.unref();
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class NetServerProxy implements ServerProxy {
|
||||
public constructor(private readonly server: net.Server) {}
|
||||
export class NetServerProxy extends ServerProxy<net.Server> {
|
||||
public constructor(instance: net.Server) {
|
||||
super({
|
||||
bindEvents: ["close", "error", "listening"],
|
||||
doneEvents: ["close"],
|
||||
instance,
|
||||
});
|
||||
}
|
||||
|
||||
public async listen(handle?: net.ListenOptions | number | string, hostname?: string | number, backlog?: number): Promise<void> {
|
||||
this.server.listen(handle, hostname as any, backlog as any); // tslint:disable-line no-any this is fine
|
||||
this.instance.listen(handle, hostname as any, backlog as any);
|
||||
}
|
||||
|
||||
public async ref(): Promise<void> {
|
||||
this.server.ref();
|
||||
this.instance.ref();
|
||||
}
|
||||
|
||||
public async unref(): Promise<void> {
|
||||
this.server.unref();
|
||||
this.instance.unref();
|
||||
}
|
||||
|
||||
public async close(): Promise<void> {
|
||||
this.server.close();
|
||||
this.instance.close();
|
||||
}
|
||||
|
||||
public async onConnection(cb: (proxy: NetSocketProxy) => void): Promise<void> {
|
||||
this.server.on("connection", (socket) => cb(new NetSocketProxy(socket)));
|
||||
this.instance.on("connection", (socket) => cb(new NetSocketProxy(socket)));
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.server.close();
|
||||
this.server.removeAllListeners();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.server.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.server.on("close", () => cb("close"));
|
||||
this.server.on("error", (error) => cb("error", error));
|
||||
this.server.on("listening", () => cb("listening"));
|
||||
this.instance.close();
|
||||
this.instance.removeAllListeners();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +70,7 @@ export class NetModuleProxy {
|
||||
}
|
||||
|
||||
public async createConnection(target: string | number | net.NetConnectOpts, host?: string): Promise<NetSocketProxy> {
|
||||
return new NetSocketProxy(net.createConnection(target as any, host)); // tslint:disable-line no-any defeat stubborness
|
||||
return new NetSocketProxy(net.createConnection(target as any, host));
|
||||
}
|
||||
|
||||
public async createServer(options?: { allowHalfOpen?: boolean, pauseOnConnect?: boolean }): Promise<NetServerProxy> {
|
||||
|
||||
@@ -2,25 +2,32 @@
|
||||
import { EventEmitter } from "events";
|
||||
import * as pty from "node-pty";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
import { preserveEnv } from "../../common/util";
|
||||
import { withEnv } from "../../common/util";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
/**
|
||||
* Server-side IPty proxy.
|
||||
*/
|
||||
export class NodePtyProcessProxy implements ServerProxy {
|
||||
private readonly emitter = new EventEmitter();
|
||||
|
||||
export class NodePtyProcessProxy extends ServerProxy {
|
||||
public constructor(private readonly process: pty.IPty) {
|
||||
super({
|
||||
bindEvents: ["process", "data", "exit"],
|
||||
doneEvents: ["exit"],
|
||||
instance: new EventEmitter(),
|
||||
});
|
||||
|
||||
this.process.on("data", (data) => this.instance.emit("data", data));
|
||||
this.process.on("exit", (exitCode, signal) => this.instance.emit("exit", exitCode, signal));
|
||||
|
||||
let name = process.process;
|
||||
setTimeout(() => { // Need to wait for the caller to listen to the event.
|
||||
this.emitter.emit("process", name);
|
||||
this.instance.emit("process", name);
|
||||
}, 1);
|
||||
const timer = setInterval(() => {
|
||||
if (process.process !== name) {
|
||||
name = process.process;
|
||||
this.emitter.emit("process", name);
|
||||
this.instance.emit("process", name);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
@@ -47,21 +54,10 @@ export class NodePtyProcessProxy implements ServerProxy {
|
||||
this.process.write(data);
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.process.on("exit", cb);
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.process.kill();
|
||||
setTimeout(() => this.process.kill("SIGKILL"), 5000); // Double tap.
|
||||
this.emitter.removeAllListeners();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.emitter.on("process", (process) => cb("process", process));
|
||||
this.process.on("data", (data) => cb("data", data));
|
||||
this.process.on("exit", (exitCode, signal) => cb("exit", exitCode, signal));
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +66,6 @@ export class NodePtyProcessProxy implements ServerProxy {
|
||||
*/
|
||||
export class NodePtyModuleProxy {
|
||||
public async spawn(file: string, args: string[] | string, options: pty.IPtyForkOptions): Promise<NodePtyProcessProxy> {
|
||||
preserveEnv(options);
|
||||
|
||||
return new NodePtyProcessProxy(require("node-pty").spawn(file, args, options));
|
||||
return new NodePtyProcessProxy(require("node-pty").spawn(file, args, withEnv(options)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,14 @@ import { ServerProxy } from "../../common/proxy";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
|
||||
export class RotatingLoggerProxy implements ServerProxy {
|
||||
private readonly emitter = new EventEmitter();
|
||||
|
||||
public constructor(private readonly logger: spdlog.RotatingLogger) {}
|
||||
export class RotatingLoggerProxy extends ServerProxy<EventEmitter> {
|
||||
public constructor(private readonly logger: spdlog.RotatingLogger) {
|
||||
super({
|
||||
bindEvents: [],
|
||||
doneEvents: ["dispose"],
|
||||
instance: new EventEmitter(),
|
||||
});
|
||||
}
|
||||
|
||||
public async trace (message: string): Promise<void> { this.logger.trace(message); }
|
||||
public async debug (message: string): Promise<void> { this.logger.debug(message); }
|
||||
@@ -21,19 +25,10 @@ export class RotatingLoggerProxy implements ServerProxy {
|
||||
public async flush (): Promise<void> { this.logger.flush(); }
|
||||
public async drop (): Promise<void> { this.logger.drop(); }
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.emitter.on("dispose", cb);
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
await this.flush();
|
||||
this.emitter.emit("dispose");
|
||||
this.emitter.removeAllListeners();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(_cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
// No events.
|
||||
this.instance.emit("dispose");
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +1,38 @@
|
||||
import { EventEmitter } from "events";
|
||||
import * as stream from "stream";
|
||||
import { ServerProxy } from "../../common/proxy";
|
||||
|
||||
// tslint:disable completed-docs
|
||||
// tslint:disable completed-docs no-any
|
||||
|
||||
export class WritableProxy<T extends stream.Writable = stream.Writable> implements ServerProxy {
|
||||
public constructor(protected readonly stream: T) {}
|
||||
|
||||
public async destroy(): Promise<void> {
|
||||
this.stream.destroy();
|
||||
export class WritableProxy<T extends stream.Writable = stream.Writable> extends ServerProxy<T> {
|
||||
public constructor(instance: T, bindEvents: string[] = [], delayedEvents?: string[]) {
|
||||
super({
|
||||
bindEvents: ["close", "drain", "error", "finish"].concat(bindEvents),
|
||||
doneEvents: ["close"],
|
||||
delayedEvents,
|
||||
instance,
|
||||
});
|
||||
}
|
||||
|
||||
public async destroy(): Promise<void> {
|
||||
this.instance.destroy();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async end(data?: any, encoding?: string): Promise<void> {
|
||||
return new Promise((resolve): void => {
|
||||
this.stream.end(data, encoding, () => {
|
||||
this.instance.end(data, encoding, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async setDefaultEncoding(encoding: string): Promise<void> {
|
||||
this.stream.setDefaultEncoding(encoding);
|
||||
this.instance.setDefaultEncoding(encoding);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async write(data: any, encoding?: string): Promise<void> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
this.stream.write(data, encoding, (error) => {
|
||||
this.instance.write(data, encoding, (error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@@ -37,22 +43,8 @@ export class WritableProxy<T extends stream.Writable = stream.Writable> implemen
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.stream.end();
|
||||
this.stream.removeAllListeners();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.stream.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
// Sockets have an extra argument on "close".
|
||||
// tslint:disable-next-line no-any
|
||||
this.stream.on("close", (...args: any[]) => cb("close", ...args));
|
||||
this.stream.on("drain", () => cb("drain"));
|
||||
this.stream.on("error", (error) => cb("error", error));
|
||||
this.stream.on("finish", () => cb("finish"));
|
||||
this.instance.end();
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,50 +52,58 @@ export class WritableProxy<T extends stream.Writable = stream.Writable> implemen
|
||||
* This noise is because we can't do multiple extends and we also can't seem to
|
||||
* do `extends WritableProxy<T> implement ReadableProxy<T>` (for `DuplexProxy`).
|
||||
*/
|
||||
export interface IReadableProxy extends ServerProxy {
|
||||
destroy(): Promise<void>;
|
||||
export interface IReadableProxy<T extends EventEmitter> extends ServerProxy<T> {
|
||||
pipe<P extends WritableProxy>(destination: P, options?: { end?: boolean; }): Promise<void>;
|
||||
setEncoding(encoding: string): Promise<void>;
|
||||
dispose(): Promise<void>;
|
||||
onDone(cb: () => void): Promise<void>;
|
||||
}
|
||||
|
||||
export class ReadableProxy<T extends stream.Readable = stream.Readable> implements IReadableProxy {
|
||||
public constructor(protected readonly stream: T) {}
|
||||
export class ReadableProxy<T extends stream.Readable = stream.Readable> extends ServerProxy<T> implements IReadableProxy<T> {
|
||||
public constructor(instance: T, bindEvents: string[] = []) {
|
||||
super({
|
||||
bindEvents: ["close", "end", "error"].concat(bindEvents),
|
||||
doneEvents: ["close"],
|
||||
delayedEvents: ["data"],
|
||||
instance,
|
||||
});
|
||||
}
|
||||
|
||||
public async pipe<P extends WritableProxy>(destination: P, options?: { end?: boolean; }): Promise<void> {
|
||||
this.instance.pipe(destination.instance, options);
|
||||
// `pipe` switches the stream to flowing mode and makes data start emitting.
|
||||
await this.bindDelayedEvent("data");
|
||||
}
|
||||
|
||||
public async destroy(): Promise<void> {
|
||||
this.stream.destroy();
|
||||
this.instance.destroy();
|
||||
}
|
||||
|
||||
public async setEncoding(encoding: string): Promise<void> {
|
||||
this.stream.setEncoding(encoding);
|
||||
this.instance.setEncoding(encoding);
|
||||
}
|
||||
|
||||
public async dispose(): Promise<void> {
|
||||
this.stream.destroy();
|
||||
}
|
||||
|
||||
public async onDone(cb: () => void): Promise<void> {
|
||||
this.stream.on("close", cb);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
this.stream.on("close", () => cb("close"));
|
||||
this.stream.on("data", (chunk) => cb("data", chunk));
|
||||
this.stream.on("end", () => cb("end"));
|
||||
this.stream.on("error", (error) => cb("error", error));
|
||||
this.instance.destroy();
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class DuplexProxy<T extends stream.Duplex = stream.Duplex> extends WritableProxy<T> implements IReadableProxy {
|
||||
export class DuplexProxy<T extends stream.Duplex = stream.Duplex> extends WritableProxy<T> implements IReadableProxy<T> {
|
||||
public constructor(stream: T, bindEvents: string[] = []) {
|
||||
super(stream, ["end"].concat(bindEvents), ["data"]);
|
||||
}
|
||||
|
||||
public async pipe<P extends WritableProxy>(destination: P, options?: { end?: boolean; }): Promise<void> {
|
||||
this.instance.pipe(destination.instance, options);
|
||||
// `pipe` switches the stream to flowing mode and makes data start emitting.
|
||||
await this.bindDelayedEvent("data");
|
||||
}
|
||||
|
||||
public async setEncoding(encoding: string): Promise<void> {
|
||||
this.stream.setEncoding(encoding);
|
||||
this.instance.setEncoding(encoding);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-any
|
||||
public async onEvent(cb: (event: string, ...args: any[]) => void): Promise<void> {
|
||||
await super.onEvent(cb);
|
||||
this.stream.on("data", (chunk) => cb("data", chunk));
|
||||
this.stream.on("end", () => cb("end"));
|
||||
public async dispose(): Promise<void> {
|
||||
this.instance.destroy();
|
||||
await super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ export interface ServerOptions {
|
||||
readonly cacheDirectory: string;
|
||||
readonly builtInExtensionsDirectory: string;
|
||||
readonly extensionsDirectory: string;
|
||||
readonly extraExtensionDirectories?: string[];
|
||||
readonly extraBuiltinExtensionDirectories?: string[];
|
||||
readonly fork?: ForkProvider;
|
||||
}
|
||||
|
||||
@@ -99,6 +101,13 @@ export class Server {
|
||||
initMsg.setTmpDirectory(os.tmpdir());
|
||||
initMsg.setOperatingSystem(platformToProto(os.platform()));
|
||||
initMsg.setShell(os.userInfo().shell || global.process.env.SHELL || "");
|
||||
initMsg.setExtraExtensionDirectoriesList(this.options.extraExtensionDirectories || []);
|
||||
initMsg.setExtraBuiltinExtensionDirectoriesList(this.options.extraBuiltinExtensionDirectories || []);
|
||||
|
||||
for (let key in process.env) {
|
||||
initMsg.getEnvMap().set(key, process.env[key] as string);
|
||||
}
|
||||
|
||||
const srvMsg = new ServerMessage();
|
||||
srvMsg.setInit(initMsg);
|
||||
connection.send(srvMsg.serializeBinary());
|
||||
@@ -136,6 +145,7 @@ export class Server {
|
||||
const args = proxyMessage.getArgsList().map((a) => protoToArgument(
|
||||
a,
|
||||
(id, args) => this.sendCallback(proxyId, id, args),
|
||||
(id) => this.getProxy(id).instance,
|
||||
));
|
||||
|
||||
logger.trace(() => [
|
||||
@@ -241,9 +251,7 @@ export class Server {
|
||||
this.proxies.set(proxyId, { instance });
|
||||
|
||||
if (isProxy(instance)) {
|
||||
instance.onEvent((event, ...args) => this.sendEvent(proxyId, event, ...args)).catch((error) => {
|
||||
logger.error(error.message);
|
||||
});
|
||||
instance.onEvent((event, ...args) => this.sendEvent(proxyId, event, ...args));
|
||||
instance.onDone(() => {
|
||||
// It might have finished because we disposed it due to a disconnect.
|
||||
if (!this.disconnected) {
|
||||
@@ -255,8 +263,6 @@ export class Server {
|
||||
this.removeProxy(proxyId);
|
||||
}, this.responseTimeout);
|
||||
}
|
||||
}).catch((error) => {
|
||||
logger.error(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -42,4 +42,8 @@ message WorkingInit {
|
||||
string shell = 6;
|
||||
string builtin_extensions_dir = 7;
|
||||
string extensions_directory = 8;
|
||||
repeated string extra_extension_directories = 9;
|
||||
repeated string extra_builtin_extension_directories = 10;
|
||||
|
||||
map<string, string> env = 11;
|
||||
}
|
||||
|
||||
15
packages/protocol/src/proto/client_pb.d.ts
vendored
15
packages/protocol/src/proto/client_pb.d.ts
vendored
@@ -135,6 +135,18 @@ export class WorkingInit extends jspb.Message {
|
||||
getExtensionsDirectory(): string;
|
||||
setExtensionsDirectory(value: string): void;
|
||||
|
||||
clearExtraExtensionDirectoriesList(): void;
|
||||
getExtraExtensionDirectoriesList(): Array<string>;
|
||||
setExtraExtensionDirectoriesList(value: Array<string>): void;
|
||||
addExtraExtensionDirectories(value: string, index?: number): string;
|
||||
|
||||
clearExtraBuiltinExtensionDirectoriesList(): void;
|
||||
getExtraBuiltinExtensionDirectoriesList(): Array<string>;
|
||||
setExtraBuiltinExtensionDirectoriesList(value: Array<string>): void;
|
||||
addExtraBuiltinExtensionDirectories(value: string, index?: number): string;
|
||||
|
||||
getEnvMap(): jspb.Map<string, string>;
|
||||
clearEnvMap(): void;
|
||||
serializeBinary(): Uint8Array;
|
||||
toObject(includeInstance?: boolean): WorkingInit.AsObject;
|
||||
static toObject(includeInstance: boolean, msg: WorkingInit): WorkingInit.AsObject;
|
||||
@@ -155,6 +167,9 @@ export namespace WorkingInit {
|
||||
shell: string,
|
||||
builtinExtensionsDir: string,
|
||||
extensionsDirectory: string,
|
||||
extraExtensionDirectoriesList: Array<string>,
|
||||
extraBuiltinExtensionDirectoriesList: Array<string>,
|
||||
envMap: Array<[string, string]>,
|
||||
}
|
||||
|
||||
export enum OperatingSystem {
|
||||
|
||||
@@ -72,7 +72,7 @@ if (goog.DEBUG && !COMPILED) {
|
||||
* @constructor
|
||||
*/
|
||||
proto.WorkingInit = function(opt_data) {
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, null, null);
|
||||
jspb.Message.initialize(this, opt_data, 0, -1, proto.WorkingInit.repeatedFields_, null);
|
||||
};
|
||||
goog.inherits(proto.WorkingInit, jspb.Message);
|
||||
if (goog.DEBUG && !COMPILED) {
|
||||
@@ -137,7 +137,7 @@ proto.ClientMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.ClientMessage.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
method: (f = msg.getMethod()) && node_pb.Method.toObject(includeInstance, f),
|
||||
ping: (f = msg.getPing()) && node_pb.Ping.toObject(includeInstance, f)
|
||||
};
|
||||
@@ -360,7 +360,7 @@ proto.ServerMessage.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.ServerMessage.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
fail: (f = msg.getFail()) && node_pb.Method.Fail.toObject(includeInstance, f),
|
||||
success: (f = msg.getSuccess()) && node_pb.Method.Success.toObject(includeInstance, f),
|
||||
event: (f = msg.getEvent()) && node_pb.Event.toObject(includeInstance, f),
|
||||
@@ -759,6 +759,13 @@ proto.ServerMessage.prototype.hasSharedProcessActive = function() {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* List of repeated fields within this message type.
|
||||
* @private {!Array<number>}
|
||||
* @const
|
||||
*/
|
||||
proto.WorkingInit.repeatedFields_ = [9,10];
|
||||
|
||||
|
||||
|
||||
if (jspb.Message.GENERATE_TO_OBJECT) {
|
||||
@@ -787,7 +794,7 @@ proto.WorkingInit.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.WorkingInit.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
homeDirectory: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
tmpDirectory: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
dataDirectory: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
@@ -795,7 +802,10 @@ proto.WorkingInit.toObject = function(includeInstance, msg) {
|
||||
operatingSystem: jspb.Message.getFieldWithDefault(msg, 5, 0),
|
||||
shell: jspb.Message.getFieldWithDefault(msg, 6, ""),
|
||||
builtinExtensionsDir: jspb.Message.getFieldWithDefault(msg, 7, ""),
|
||||
extensionsDirectory: jspb.Message.getFieldWithDefault(msg, 8, "")
|
||||
extensionsDirectory: jspb.Message.getFieldWithDefault(msg, 8, ""),
|
||||
extraExtensionDirectoriesList: jspb.Message.getRepeatedField(msg, 9),
|
||||
extraBuiltinExtensionDirectoriesList: jspb.Message.getRepeatedField(msg, 10),
|
||||
envMap: (f = msg.getEnvMap()) ? f.toObject(includeInstance, undefined) : []
|
||||
};
|
||||
|
||||
if (includeInstance) {
|
||||
@@ -864,6 +874,20 @@ proto.WorkingInit.deserializeBinaryFromReader = function(msg, reader) {
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.setExtensionsDirectory(value);
|
||||
break;
|
||||
case 9:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addExtraExtensionDirectories(value);
|
||||
break;
|
||||
case 10:
|
||||
var value = /** @type {string} */ (reader.readString());
|
||||
msg.addExtraBuiltinExtensionDirectories(value);
|
||||
break;
|
||||
case 11:
|
||||
var value = msg.getEnvMap();
|
||||
reader.readMessage(value, function(message, reader) {
|
||||
jspb.Map.deserializeBinary(message, reader, jspb.BinaryReader.prototype.readString, jspb.BinaryReader.prototype.readString, null, "");
|
||||
});
|
||||
break;
|
||||
default:
|
||||
reader.skipField();
|
||||
break;
|
||||
@@ -949,6 +973,24 @@ proto.WorkingInit.serializeBinaryToWriter = function(message, writer) {
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getExtraExtensionDirectoriesList();
|
||||
if (f.length > 0) {
|
||||
writer.writeRepeatedString(
|
||||
9,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getExtraBuiltinExtensionDirectoriesList();
|
||||
if (f.length > 0) {
|
||||
writer.writeRepeatedString(
|
||||
10,
|
||||
f
|
||||
);
|
||||
}
|
||||
f = message.getEnvMap(true);
|
||||
if (f && f.getLength() > 0) {
|
||||
f.serializeBinary(11, writer, jspb.BinaryWriter.prototype.writeString, jspb.BinaryWriter.prototype.writeString);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1081,4 +1123,89 @@ proto.WorkingInit.prototype.setExtensionsDirectory = function(value) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* repeated string extra_extension_directories = 9;
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
proto.WorkingInit.prototype.getExtraExtensionDirectoriesList = function() {
|
||||
return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 9));
|
||||
};
|
||||
|
||||
|
||||
/** @param {!Array<string>} value */
|
||||
proto.WorkingInit.prototype.setExtraExtensionDirectoriesList = function(value) {
|
||||
jspb.Message.setField(this, 9, value || []);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @param {number=} opt_index
|
||||
*/
|
||||
proto.WorkingInit.prototype.addExtraExtensionDirectories = function(value, opt_index) {
|
||||
jspb.Message.addToRepeatedField(this, 9, value, opt_index);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the list making it empty but non-null.
|
||||
*/
|
||||
proto.WorkingInit.prototype.clearExtraExtensionDirectoriesList = function() {
|
||||
this.setExtraExtensionDirectoriesList([]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* repeated string extra_builtin_extension_directories = 10;
|
||||
* @return {!Array<string>}
|
||||
*/
|
||||
proto.WorkingInit.prototype.getExtraBuiltinExtensionDirectoriesList = function() {
|
||||
return /** @type {!Array<string>} */ (jspb.Message.getRepeatedField(this, 10));
|
||||
};
|
||||
|
||||
|
||||
/** @param {!Array<string>} value */
|
||||
proto.WorkingInit.prototype.setExtraBuiltinExtensionDirectoriesList = function(value) {
|
||||
jspb.Message.setField(this, 10, value || []);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @param {number=} opt_index
|
||||
*/
|
||||
proto.WorkingInit.prototype.addExtraBuiltinExtensionDirectories = function(value, opt_index) {
|
||||
jspb.Message.addToRepeatedField(this, 10, value, opt_index);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the list making it empty but non-null.
|
||||
*/
|
||||
proto.WorkingInit.prototype.clearExtraBuiltinExtensionDirectoriesList = function() {
|
||||
this.setExtraBuiltinExtensionDirectoriesList([]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* map<string, string> env = 11;
|
||||
* @param {boolean=} opt_noLazyCreate Do not create the map if
|
||||
* empty, instead returning `undefined`
|
||||
* @return {!jspb.Map<string,string>}
|
||||
*/
|
||||
proto.WorkingInit.prototype.getEnvMap = function(opt_noLazyCreate) {
|
||||
return /** @type {!jspb.Map<string,string>} */ (
|
||||
jspb.Message.getMapField(this, 11, opt_noLazyCreate,
|
||||
null));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears values from the map. The map will be non-null.
|
||||
*/
|
||||
proto.WorkingInit.prototype.clearEnvMap = function() {
|
||||
this.getEnvMap().clear();
|
||||
};
|
||||
|
||||
|
||||
goog.object.extend(exports, proto);
|
||||
|
||||
@@ -583,7 +583,7 @@ proto.Argument.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
error: (f = msg.getError()) && proto.Argument.ErrorValue.toObject(includeInstance, f),
|
||||
buffer: (f = msg.getBuffer()) && proto.Argument.BufferValue.toObject(includeInstance, f),
|
||||
object: (f = msg.getObject()) && proto.Argument.ObjectValue.toObject(includeInstance, f),
|
||||
@@ -843,7 +843,7 @@ proto.Argument.ErrorValue.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.ErrorValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
message: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
stack: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
code: jspb.Message.getFieldWithDefault(msg, 3, "")
|
||||
@@ -1022,7 +1022,7 @@ proto.Argument.BufferValue.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.BufferValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
data: msg.getData_asB64()
|
||||
};
|
||||
|
||||
@@ -1171,7 +1171,7 @@ proto.Argument.ObjectValue.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.ObjectValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
dataMap: (f = msg.getDataMap()) ? f.toObject(includeInstance, proto.Argument.toObject) : []
|
||||
};
|
||||
|
||||
@@ -1308,7 +1308,7 @@ proto.Argument.ArrayValue.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.ArrayValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
dataList: jspb.Message.toObjectList(msg.getDataList(),
|
||||
proto.Argument.toObject, includeInstance)
|
||||
};
|
||||
@@ -1455,7 +1455,7 @@ proto.Argument.ProxyValue.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.ProxyValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
id: jspb.Message.getFieldWithDefault(msg, 1, 0)
|
||||
};
|
||||
|
||||
@@ -1580,7 +1580,7 @@ proto.Argument.FunctionValue.prototype.toObject = function(opt_includeInstance)
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.FunctionValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
id: jspb.Message.getFieldWithDefault(msg, 1, 0)
|
||||
};
|
||||
|
||||
@@ -1705,7 +1705,7 @@ proto.Argument.NullValue.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.NullValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
|
||||
};
|
||||
|
||||
@@ -1804,7 +1804,7 @@ proto.Argument.UndefinedValue.prototype.toObject = function(opt_includeInstance)
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.UndefinedValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
|
||||
};
|
||||
|
||||
@@ -1903,7 +1903,7 @@ proto.Argument.DateValue.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Argument.DateValue.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
date: jspb.Message.getFieldWithDefault(msg, 1, "")
|
||||
};
|
||||
|
||||
@@ -2449,7 +2449,7 @@ proto.Method.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Method.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
namedProxy: (f = msg.getNamedProxy()) && proto.Method.Named.toObject(includeInstance, f),
|
||||
numberedProxy: (f = msg.getNumberedProxy()) && proto.Method.Numbered.toObject(includeInstance, f)
|
||||
};
|
||||
@@ -2582,7 +2582,7 @@ proto.Method.Named.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Method.Named.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
id: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
module: jspb.Message.getFieldWithDefault(msg, 2, 0),
|
||||
method: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
@@ -2817,7 +2817,7 @@ proto.Method.Numbered.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Method.Numbered.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
id: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
proxyId: jspb.Message.getFieldWithDefault(msg, 2, 0),
|
||||
method: jspb.Message.getFieldWithDefault(msg, 3, ""),
|
||||
@@ -3045,7 +3045,7 @@ proto.Method.Fail.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Method.Fail.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
id: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
response: (f = msg.getResponse()) && proto.Argument.toObject(includeInstance, f)
|
||||
};
|
||||
@@ -3217,7 +3217,7 @@ proto.Method.Success.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Method.Success.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
id: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
response: (f = msg.getResponse()) && proto.Argument.toObject(includeInstance, f)
|
||||
};
|
||||
@@ -3481,7 +3481,7 @@ proto.Callback.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Callback.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
namedCallback: (f = msg.getNamedCallback()) && proto.Callback.Named.toObject(includeInstance, f),
|
||||
numberedCallback: (f = msg.getNumberedCallback()) && proto.Callback.Numbered.toObject(includeInstance, f)
|
||||
};
|
||||
@@ -3614,7 +3614,7 @@ proto.Callback.Named.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Callback.Named.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
module: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
callbackId: jspb.Message.getFieldWithDefault(msg, 2, 0),
|
||||
argsList: jspb.Message.toObjectList(msg.getArgsList(),
|
||||
@@ -3822,7 +3822,7 @@ proto.Callback.Numbered.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Callback.Numbered.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
proxyId: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
callbackId: jspb.Message.getFieldWithDefault(msg, 2, 0),
|
||||
argsList: jspb.Message.toObjectList(msg.getArgsList(),
|
||||
@@ -4115,7 +4115,7 @@ proto.Event.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Event.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
namedEvent: (f = msg.getNamedEvent()) && proto.Event.Named.toObject(includeInstance, f),
|
||||
numberedEvent: (f = msg.getNumberedEvent()) && proto.Event.Numbered.toObject(includeInstance, f)
|
||||
};
|
||||
@@ -4248,7 +4248,7 @@ proto.Event.Named.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Event.Named.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
module: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
event: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
argsList: jspb.Message.toObjectList(msg.getArgsList(),
|
||||
@@ -4456,7 +4456,7 @@ proto.Event.Numbered.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Event.Numbered.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
proxyId: jspb.Message.getFieldWithDefault(msg, 1, 0),
|
||||
event: jspb.Message.getFieldWithDefault(msg, 2, ""),
|
||||
argsList: jspb.Message.toObjectList(msg.getArgsList(),
|
||||
@@ -4723,7 +4723,7 @@ proto.Ping.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Ping.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
|
||||
};
|
||||
|
||||
@@ -4822,7 +4822,7 @@ proto.Pong.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.Pong.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ proto.SharedProcessActive.prototype.toObject = function(opt_includeInstance) {
|
||||
* @suppress {unusedLocalVariables} f is only used for nested messages
|
||||
*/
|
||||
proto.SharedProcessActive.toObject = function(includeInstance, msg) {
|
||||
var obj = {
|
||||
var f, obj = {
|
||||
socketPath: jspb.Message.getFieldWithDefault(msg, 1, ""),
|
||||
logPath: jspb.Message.getFieldWithDefault(msg, 2, "")
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ describe("child_process", () => {
|
||||
const cp = client.modules[Module.ChildProcess];
|
||||
|
||||
const getStdout = async (proc: ChildProcess): Promise<string> => {
|
||||
return new Promise((r): Readable => proc.stdout!.on("data", r))
|
||||
return new Promise((r): Readable => proc.stdout!.once("data", r))
|
||||
.then((s) => s.toString());
|
||||
};
|
||||
|
||||
|
||||
@@ -70,12 +70,12 @@ describe("fs", () => {
|
||||
describe("chown", () => {
|
||||
it("should chown existing file", async () => {
|
||||
const file = await helper.createTmpFile();
|
||||
await expect(util.promisify(fs.chown)(file, 1, 1))
|
||||
await expect(util.promisify(nativeFs.chown)(file, 1000, 1000))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("should fail to chown nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.chown)(helper.tmpFile(), 1, 1))
|
||||
await expect(util.promisify(fs.chown)(helper.tmpFile(), 1000, 1000))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
@@ -131,6 +131,42 @@ describe("fs", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("createReadStream", () => {
|
||||
it("should read a file", async () => {
|
||||
const file = helper.tmpFile();
|
||||
const content = "foobar";
|
||||
await util.promisify(nativeFs.writeFile)(file, content);
|
||||
|
||||
const reader = fs.createReadStream(file);
|
||||
|
||||
await expect(new Promise((resolve, reject): void => {
|
||||
let data = "";
|
||||
reader.once("error", reject);
|
||||
reader.once("end", () => resolve(data));
|
||||
reader.on("data", (d) => data += d.toString());
|
||||
})).resolves.toBe(content);
|
||||
});
|
||||
|
||||
it("should pipe to a writable stream", async () => {
|
||||
const source = helper.tmpFile();
|
||||
const content = "foo";
|
||||
await util.promisify(nativeFs.writeFile)(source, content);
|
||||
|
||||
const destination = helper.tmpFile();
|
||||
const reader = fs.createReadStream(source);
|
||||
const writer = fs.createWriteStream(destination);
|
||||
|
||||
await new Promise((resolve, reject): void => {
|
||||
reader.once("error", reject);
|
||||
writer.once("error", reject);
|
||||
writer.once("close", resolve);
|
||||
reader.pipe(writer);
|
||||
});
|
||||
|
||||
await expect(util.promisify(nativeFs.readFile)(destination, "utf8")).resolves.toBe(content);
|
||||
});
|
||||
});
|
||||
|
||||
describe("exists", () => {
|
||||
it("should output file exists", async () => {
|
||||
await expect(util.promisify(fs.exists)(__filename))
|
||||
@@ -162,13 +198,13 @@ describe("fs", () => {
|
||||
it("should fchown existing file", async () => {
|
||||
const file = await helper.createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "r");
|
||||
await expect(util.promisify(fs.fchown)(fd, 1, 1))
|
||||
await expect(util.promisify(fs.fchown)(fd, 1000, 1000))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
|
||||
it("should fail to fchown nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.fchown)(99999, 1, 1))
|
||||
await expect(util.promisify(fs.fchown)(99999, 1000, 1000))
|
||||
.rejects.toThrow("EBADF");
|
||||
});
|
||||
});
|
||||
@@ -239,7 +275,7 @@ describe("fs", () => {
|
||||
it("should futimes existing file", async () => {
|
||||
const file = await helper.createTmpFile();
|
||||
const fd = await util.promisify(nativeFs.open)(file, "w");
|
||||
await expect(util.promisify(fs.futimes)(fd, 1, 1))
|
||||
await expect(util.promisify(fs.futimes)(fd, 1000, 1000))
|
||||
.resolves.toBeUndefined();
|
||||
await util.promisify(nativeFs.close)(fd);
|
||||
});
|
||||
@@ -275,14 +311,13 @@ describe("fs", () => {
|
||||
describe("lchown", () => {
|
||||
it("should lchown existing file", async () => {
|
||||
const file = await helper.createTmpFile();
|
||||
await expect(util.promisify(fs.lchown)(file, 1, 1))
|
||||
await expect(util.promisify(fs.lchown)(file, 1000, 1000))
|
||||
.resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
// TODO: Doesn't fail on my system?
|
||||
it("should fail to lchown nonexistent file", async () => {
|
||||
await expect(util.promisify(fs.lchown)(helper.tmpFile(), 1, 1))
|
||||
.resolves.toBeUndefined();
|
||||
await expect(util.promisify(fs.lchown)(helper.tmpFile(), 1000, 1000))
|
||||
.rejects.toThrow("ENOENT");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -586,7 +621,10 @@ describe("fs", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispose", () => {
|
||||
client.dispose();
|
||||
it("should dispose", (done) => {
|
||||
setTimeout(() => {
|
||||
client.dispose();
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,9 +3,11 @@ import { createClient } from "./helpers";
|
||||
describe("Server", () => {
|
||||
const dataDirectory = "/tmp/example";
|
||||
const workingDirectory = "/working/dir";
|
||||
const extensionsDirectory = "/tmp/example";
|
||||
const builtInExtensionsDirectory = "/tmp/example";
|
||||
const cacheDirectory = "/tmp/cache";
|
||||
const client = createClient({
|
||||
extensionsDirectory,
|
||||
builtInExtensionsDirectory,
|
||||
cacheDirectory,
|
||||
dataDirectory,
|
||||
|
||||
@@ -27,9 +27,10 @@ describe("spdlog", () => {
|
||||
.toContain("critical");
|
||||
});
|
||||
|
||||
it("should dispose", () => {
|
||||
it("should dispose", (done) => {
|
||||
setTimeout(() => {
|
||||
client.dispose();
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,8 @@ import * as util from "util";
|
||||
import { Module } from "../src/common/proxy";
|
||||
import { createClient, Helper } from "./helpers";
|
||||
|
||||
// tslint:disable deprecation to use fs.exists
|
||||
|
||||
describe("trash", () => {
|
||||
const client = createClient();
|
||||
const trash = client.modules[Module.Trash];
|
||||
@@ -18,9 +20,10 @@ describe("trash", () => {
|
||||
expect(await util.promisify(fs.exists)(file)).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should dispose", () => {
|
||||
it("should dispose", (done) => {
|
||||
setTimeout(() => {
|
||||
client.dispose();
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,11 +2,13 @@ import { Binary } from "@coder/nbin";
|
||||
import * as fs from "fs";
|
||||
import * as os from "os";
|
||||
import * as path from "path";
|
||||
import { platform } from "../../../build/platform";
|
||||
|
||||
const target = `${os.platform()}-${os.arch()}`;
|
||||
const target = `${platform()}-${os.arch()}`;
|
||||
const rootDir = path.join(__dirname, "..");
|
||||
const bin = new Binary({
|
||||
mainFile: path.join(rootDir, "out", "cli.js"),
|
||||
target: platform() === "darwin" ? "darwin" : platform() === "musl" ? "alpine" : "linux",
|
||||
});
|
||||
bin.writeFiles(path.join(rootDir, "build", "**"));
|
||||
bin.writeFiles(path.join(rootDir, "out", "**"));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { field, logger } from "@coder/logger";
|
||||
import { ServerMessage, SharedProcessActive } from "@coder/protocol/src/proto";
|
||||
import { withEnv } from "@coder/protocol";
|
||||
import { ChildProcess, fork, ForkOptions } from "child_process";
|
||||
import { randomFillSync } from "crypto";
|
||||
import * as fs from "fs";
|
||||
@@ -15,21 +16,28 @@ import opn = require("opn");
|
||||
|
||||
import * as commander from "commander";
|
||||
|
||||
const collect = <T>(value: T, previous: T[]): T[] => {
|
||||
return previous.concat(value);
|
||||
};
|
||||
|
||||
commander.version(process.env.VERSION || "development")
|
||||
.name("code-server")
|
||||
.description("Run VS Code on a remote server.")
|
||||
.option("--cert <value>")
|
||||
.option("--cert-key <value>")
|
||||
.option("-e, --extensions-dir <dir>", "Set the root path for extensions.")
|
||||
.option("-d --user-data-dir <dir>", " Specifies the directory that user data is kept in, useful when running as root.")
|
||||
.option("-e, --extensions-dir <dir>", "Override the main default path for user extensions.")
|
||||
.option("--extra-extensions-dir [dir]", "Path to an extra user extension directory (repeatable).", collect, [])
|
||||
.option("--extra-builtin-extensions-dir [dir]", "Path to an extra built-in extension directory (repeatable).", collect, [])
|
||||
.option("-d --user-data-dir <dir>", "Specifies the directory that user data is kept in, useful when running as root.")
|
||||
.option("--data-dir <value>", "DEPRECATED: Use '--user-data-dir' instead. Customize where user-data is stored.")
|
||||
.option("-h, --host <value>", "Customize the hostname.", "0.0.0.0")
|
||||
.option("-o, --open", "Open in the browser on startup.", false)
|
||||
.option("-p, --port <number>", "Port to bind on.", parseInt(process.env.PORT!, 10) || 8443)
|
||||
.option("-N, --no-auth", "Start without requiring authentication.", undefined)
|
||||
.option("-N, --no-auth", "Start without requiring authentication.", false)
|
||||
.option("-H, --allow-http", "Allow http connections.", false)
|
||||
.option("-P, --password <value>", "DEPRECATED: Use the PASSWORD environment variable instead. Specify a password for authentication.")
|
||||
.option("--disable-telemetry", "Disables ALL telemetry.", false)
|
||||
.option("--socket <value>", "Listen on a UNIX socket. Host and port will be ignored when set.")
|
||||
.option("--install-extension <value>", "Install an extension by its ID.")
|
||||
.option("--bootstrap-fork <name>", "Used for development. Never set.")
|
||||
.option("--extra-args <args>", "Used for development. Never set.")
|
||||
@@ -57,12 +65,15 @@ const bold = (text: string | number): string | number => {
|
||||
|
||||
readonly userDataDir?: string;
|
||||
readonly extensionsDir?: string;
|
||||
readonly extraExtensionsDir?: string[];
|
||||
readonly extraBuiltinExtensionsDir?: string[];
|
||||
|
||||
readonly dataDir?: string;
|
||||
readonly password?: string;
|
||||
readonly open?: boolean;
|
||||
readonly cert?: string;
|
||||
readonly certKey?: string;
|
||||
readonly socket?: string;
|
||||
|
||||
readonly installExtension?: string;
|
||||
|
||||
@@ -81,6 +92,9 @@ const bold = (text: string | number): string | number => {
|
||||
|
||||
const dataDir = path.resolve(options.userDataDir || options.dataDir || path.join(dataHome, "code-server"));
|
||||
const extensionsDir = options.extensionsDir ? path.resolve(options.extensionsDir) : path.resolve(dataDir, "extensions");
|
||||
const builtInExtensionsDir = path.resolve(buildDir || path.join(__dirname, ".."), "build/extensions");
|
||||
const extraExtensionDirs = options.extraExtensionsDir ? options.extraExtensionsDir.map((p) => path.resolve(p)) : [];
|
||||
const extraBuiltinExtensionDirs = options.extraBuiltinExtensionsDir ? options.extraBuiltinExtensionsDir.map((p) => path.resolve(p)) : [];
|
||||
const workingDir = path.resolve(args[0] || process.cwd());
|
||||
const dependenciesDir = path.join(os.tmpdir(), "code-server/dependencies");
|
||||
|
||||
@@ -98,6 +112,8 @@ const bold = (text: string | number): string | number => {
|
||||
fse.mkdirp(extensionsDir),
|
||||
fse.mkdirp(workingDir),
|
||||
fse.mkdirp(dependenciesDir),
|
||||
...extraExtensionDirs.map((p) => fse.mkdirp(p)),
|
||||
...extraBuiltinExtensionDirs.map((p) => fse.mkdirp(p)),
|
||||
]);
|
||||
|
||||
const unpackExecutable = (binaryName: string): void => {
|
||||
@@ -113,7 +129,6 @@ const bold = (text: string | number): string | number => {
|
||||
// tslint:disable-next-line no-any
|
||||
(<any>global).RIPGREP_LOCATION = path.join(dependenciesDir, "rg");
|
||||
|
||||
const builtInExtensionsDir = path.resolve(buildDir || path.join(__dirname, ".."), "build/extensions");
|
||||
if (options.bootstrapFork) {
|
||||
const modulePath = options.bootstrapFork;
|
||||
if (!modulePath) {
|
||||
@@ -177,12 +192,7 @@ const bold = (text: string | number): string | number => {
|
||||
"--builtin-extensions-dir", builtInExtensionsDir,
|
||||
"--extensions-dir", extensionsDir,
|
||||
"--install-extension", options.installExtension,
|
||||
], {
|
||||
env: {
|
||||
VSCODE_ALLOW_IO: "true",
|
||||
VSCODE_LOGS: process.env.VSCODE_LOGS,
|
||||
},
|
||||
}, dataDir);
|
||||
], withEnv({ env: { VSCODE_ALLOW_IO: "true" } }), dataDir);
|
||||
|
||||
fork.stdout.on("data", (d: Buffer) => d.toString().split("\n").forEach((l) => logger.info(l)));
|
||||
fork.stderr.on("data", (d: Buffer) => d.toString().split("\n").forEach((l) => logger.error(l)));
|
||||
@@ -192,9 +202,9 @@ const bold = (text: string | number): string | number => {
|
||||
}
|
||||
|
||||
// TODO: fill in appropriate doc url
|
||||
logger.info("Additional documentation: http://github.com/codercom/code-server");
|
||||
logger.info("Additional documentation: http://github.com/cdr/code-server");
|
||||
logger.info("Initializing", field("data-dir", dataDir), field("extensions-dir", extensionsDir), field("working-dir", workingDir), field("log-dir", logDir));
|
||||
const sharedProcess = new SharedProcess(dataDir, extensionsDir, builtInExtensionsDir);
|
||||
const sharedProcess = new SharedProcess(dataDir, extensionsDir, builtInExtensionsDir, extraExtensionDirs, extraBuiltinExtensionDirs);
|
||||
const sendSharedProcessReady = (socket: WebSocket): void => {
|
||||
const active = new SharedProcessActive();
|
||||
active.setSocketPath(sharedProcess.socketPath);
|
||||
@@ -214,6 +224,7 @@ const bold = (text: string | number): string | number => {
|
||||
}
|
||||
|
||||
let password = options.password || process.env.PASSWORD;
|
||||
const usingCustomPassword = !!password;
|
||||
if (!password) {
|
||||
// Generate a random password with a length of 24.
|
||||
const buffer = Buffer.alloc(12);
|
||||
@@ -248,6 +259,8 @@ const bold = (text: string | number): string | number => {
|
||||
serverOptions: {
|
||||
extensionsDirectory: extensionsDir,
|
||||
builtInExtensionsDirectory: builtInExtensionsDir,
|
||||
extraExtensionDirectories: extraExtensionDirs,
|
||||
extraBuiltinExtensionDirectories: extraBuiltinExtensionDirs,
|
||||
dataDirectory: dataDir,
|
||||
workingDirectory: workingDir,
|
||||
cacheDirectory: cacheHome,
|
||||
@@ -267,7 +280,11 @@ const bold = (text: string | number): string | number => {
|
||||
});
|
||||
|
||||
logger.info("Starting webserver...", field("host", options.host), field("port", options.port));
|
||||
app.server.listen(options.port, options.host);
|
||||
if (options.socket) {
|
||||
app.server.listen(options.socket);
|
||||
} else {
|
||||
app.server.listen(options.port, options.host);
|
||||
}
|
||||
let clientId = 1;
|
||||
app.wss.on("connection", (ws, req) => {
|
||||
const id = clientId++;
|
||||
@@ -284,24 +301,33 @@ const bold = (text: string | number): string | number => {
|
||||
});
|
||||
app.wss.on("error", (err: NodeJS.ErrnoException) => {
|
||||
if (err.code === "EADDRINUSE") {
|
||||
logger.error(`Port ${bold(options.port)} is in use. Please free up port ${options.port} or specify a different port with the -p flag`);
|
||||
if (options.socket) {
|
||||
logger.error(`Socket ${bold(options.socket)} is in use. Please specify a different socket.`);
|
||||
} else {
|
||||
logger.error(`Port ${bold(options.port)} is in use. Please free up port ${options.port} or specify a different port with the -p flag`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
if (!options.certKey && !options.cert) {
|
||||
logger.warn("No certificate specified. \u001B[1mThis could be insecure.");
|
||||
// TODO: fill in appropriate doc url
|
||||
logger.warn("Documentation on securing your setup: https://github.com/codercom/code-server/blob/master/doc/security/ssl.md");
|
||||
logger.warn("Documentation on securing your setup: https://github.com/cdr/code-server/blob/master/doc/security/ssl.md");
|
||||
}
|
||||
|
||||
if (!options.noAuth) {
|
||||
logger.info(" ");
|
||||
logger.info(`Password:\u001B[1m ${password}`);
|
||||
logger.info(usingCustomPassword ? "Using custom password." : `Password:\u001B[1m ${password}`);
|
||||
} else {
|
||||
logger.warn(" ");
|
||||
logger.warn("Launched without authentication.");
|
||||
}
|
||||
if (options.disableTelemetry) {
|
||||
logger.info("Telemetry is disabled");
|
||||
}
|
||||
|
||||
const url = `http://localhost:${options.port}/`;
|
||||
const protocol = options.allowHttp ? "http" : "https";
|
||||
const url = `${protocol}://localhost:${options.port}/`;
|
||||
logger.info(" ");
|
||||
logger.info("Started (click the link below to open):");
|
||||
logger.info(url);
|
||||
|
||||
@@ -133,7 +133,7 @@ export const createApp = async (options: CreateAppOptions): Promise<{
|
||||
});
|
||||
});
|
||||
|
||||
const server = httpolyglot.createServer(options.bypassAuth ? {} : options.httpsOptions || certs, app) as http.Server;
|
||||
const server = httpolyglot.createServer(options.allowHttp ? {} : options.httpsOptions || certs, app) as http.Server;
|
||||
const wss = new ws.Server({ server });
|
||||
|
||||
wss.shouldHandle = (req): boolean => {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ParsedArgs } from "vs/platform/environment/common/environment";
|
||||
import { Emitter } from "@coder/events/src";
|
||||
import { retry } from "@coder/ide/src/retry";
|
||||
import { logger, field, Level } from "@coder/logger";
|
||||
import { withEnv } from "@coder/protocol";
|
||||
|
||||
export enum SharedProcessState {
|
||||
Stopped,
|
||||
@@ -38,6 +39,8 @@ export class SharedProcess {
|
||||
private readonly userDataDir: string,
|
||||
private readonly extensionsDir: string,
|
||||
private readonly builtInExtensionsDir: string,
|
||||
private readonly extraExtensionDirs: string[],
|
||||
private readonly extraBuiltinExtensionDirs: string[],
|
||||
) {
|
||||
this.retry.run();
|
||||
}
|
||||
@@ -88,13 +91,10 @@ export class SharedProcess {
|
||||
this.activeProcess.kill();
|
||||
}
|
||||
|
||||
const activeProcess = forkModule("vs/code/electron-browser/sharedProcess/sharedProcessMain", [], {
|
||||
env: {
|
||||
VSCODE_ALLOW_IO: "true",
|
||||
VSCODE_LOGS: process.env.VSCODE_LOGS,
|
||||
DISABLE_TELEMETRY: process.env.DISABLE_TELEMETRY,
|
||||
},
|
||||
}, this.userDataDir);
|
||||
const activeProcess = forkModule(
|
||||
"vs/code/electron-browser/sharedProcess/sharedProcessMain", [],
|
||||
withEnv({ env: { VSCODE_ALLOW_IO: "true" } }), this.userDataDir,
|
||||
);
|
||||
this.activeProcess = activeProcess;
|
||||
|
||||
await new Promise((resolve, reject): void => {
|
||||
@@ -136,6 +136,8 @@ export class SharedProcess {
|
||||
"builtin-extensions-dir": this.builtInExtensionsDir,
|
||||
"user-data-dir": this.userDataDir,
|
||||
"extensions-dir": this.extensionsDir,
|
||||
"extra-extension-dirs": this.extraExtensionDirs,
|
||||
"extra-builtin-extension-dirs": this.extraBuiltinExtensionDirs,
|
||||
},
|
||||
logLevel: this.logger.level,
|
||||
sharedIPCHandle: this.socketPath,
|
||||
|
||||
@@ -6,13 +6,17 @@ import { IStatusbarService, StatusbarAlignment } from "vs/platform/statusbar/com
|
||||
import * as paths from "./fill/paths";
|
||||
import product from "./fill/product";
|
||||
import "./vscode.scss";
|
||||
import { MenuId, MenuRegistry } from "vs/platform/actions/common/actions";
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { CommandsRegistry } from "vs/platform/commands/common/commands";
|
||||
import { IFileService, FileOperation } from "vs/platform/files/common/files";
|
||||
import { ITextFileService } from "vs/workbench/services/textfile/common/textfiles";
|
||||
import { IModelService } from "vs/editor/common/services/modelService";
|
||||
import { ITerminalService } from "vs/workbench/contrib/terminal/common/terminal";
|
||||
import { IStorageService } from "vs/platform/storage/common/storage";
|
||||
|
||||
// NOTE: shouldn't import anything from VS Code here or anything that will
|
||||
// depend on a synchronous fill like `os`.
|
||||
|
||||
@@ -33,12 +37,14 @@ class VSClient extends IdeClient {
|
||||
window.ide = {
|
||||
client: ideClientInstance,
|
||||
workbench: {
|
||||
action: Action,
|
||||
syncActionDescriptor: SyncActionDescriptor,
|
||||
commandRegistry: CommandsRegistry,
|
||||
// tslint:disable-next-line:no-any
|
||||
menuRegistry: MenuRegistry as any,
|
||||
// tslint:disable-next-line:no-any
|
||||
statusbarService: getService<IStatusbarService>(IStatusbarService) as any,
|
||||
actionsRegistry: Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions),
|
||||
menuRegistry: MenuRegistry,
|
||||
statusbarService: getService<IStatusbarService>(IStatusbarService),
|
||||
notificationService: getService<INotificationService>(INotificationService),
|
||||
terminalService: getService<ITerminalService>(ITerminalService),
|
||||
storageService: {
|
||||
save: (): Promise<void> => {
|
||||
// tslint:disable-next-line:no-any
|
||||
|
||||
@@ -79,9 +79,7 @@
|
||||
.dialog-entry {
|
||||
cursor: pointer;
|
||||
font-size: 1.02em;
|
||||
padding: 0px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
padding: 0px 8px;
|
||||
|
||||
.dialog-entry-info {
|
||||
display: flex;
|
||||
@@ -94,6 +92,14 @@
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.dialog-entry-size {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dialog-entry-mtime {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--list-hover-background);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,15 @@ export type DialogOptions = OpenDialogOptions | SaveDialogOptions;
|
||||
|
||||
export const showOpenDialog = (options: OpenDialogOptions): Promise<string> => {
|
||||
return new Promise<string>((resolve, reject): void => {
|
||||
const dialog = new Dialog(DialogType.Open, options);
|
||||
// Make the default to show hidden files and directories since there is no
|
||||
// other way to make them visible in the dialogs currently.
|
||||
const dialog = new Dialog(DialogType.Open, typeof options.properties.showHiddenFiles === "undefined" ? {
|
||||
...options,
|
||||
properties: {
|
||||
...options.properties,
|
||||
showHiddenFiles: true,
|
||||
},
|
||||
} : options);
|
||||
dialog.onSelect((e) => {
|
||||
dialog.dispose();
|
||||
resolve(e);
|
||||
@@ -307,7 +315,6 @@ class Dialog {
|
||||
}
|
||||
buttonsNode.appendChild(confirmBtn);
|
||||
this.root.appendChild(buttonsNode);
|
||||
this.entryList.layout();
|
||||
|
||||
this.path = options.defaultPath || "/";
|
||||
}
|
||||
@@ -383,6 +390,8 @@ class Dialog {
|
||||
return true;
|
||||
});
|
||||
|
||||
this.entryList.layout();
|
||||
|
||||
this.entryList.setChildren(null, items.map((i: DialogEntry): ITreeElement<DialogEntry> => ({ element: i })));
|
||||
this.entryList.domFocus();
|
||||
this.entryList.setFocus([null]);
|
||||
@@ -404,7 +413,7 @@ class Dialog {
|
||||
*/
|
||||
private async list(directory: string): Promise<ReadonlyArray<DialogEntry>> {
|
||||
const paths = (await util.promisify(fs.readdir)(directory)).sort();
|
||||
const stats = await Promise.all(paths.map(p => util.promisify(fs.stat)(path.join(directory, p))));
|
||||
const stats = await Promise.all(paths.map(p => util.promisify(fs.lstat)(path.join(directory, p))));
|
||||
|
||||
return stats.map((stat, index): DialogEntry => ({
|
||||
fullPath: path.join(directory, paths[index]),
|
||||
@@ -480,7 +489,7 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
|
||||
start: 0,
|
||||
end: node.filterData.length,
|
||||
}] : []);
|
||||
templateData.size.innerText = node.element.size.toString();
|
||||
templateData.size.innerText = !node.element.isDirectory ? this.humanReadableSize(node.element.size) : "";
|
||||
templateData.lastModified.innerText = node.element.lastModified;
|
||||
|
||||
// We know this exists because we created the template.
|
||||
@@ -498,4 +507,15 @@ class DialogEntryRenderer implements ITreeRenderer<DialogEntry, string, DialogEn
|
||||
public disposeTemplate(_templateData: DialogEntryData): void {
|
||||
// throw new Error("Method not implemented.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a positive size in bytes, return a string that is more readable for
|
||||
* humans.
|
||||
*/
|
||||
private humanReadableSize(bytes: number): string {
|
||||
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
const i = Math.min(Math.floor(bytes && Math.log(bytes) / Math.log(1000)), units.length - 1);
|
||||
|
||||
return (bytes / Math.pow(1000, i)).toFixed(2) + " " + units[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,18 @@ export class EnvironmentService extends environment.EnvironmentService {
|
||||
public get extensionsPath(): string {
|
||||
return paths.getExtensionsDirectory();
|
||||
}
|
||||
|
||||
public get builtinExtensionsPath(): string {
|
||||
return paths.getBuiltInExtensionsDirectory();
|
||||
}
|
||||
|
||||
public get extraExtensionPaths(): string[] {
|
||||
return paths.getExtraExtensionDirectories();
|
||||
}
|
||||
|
||||
public get extraBuiltinExtensionPaths(): string[] {
|
||||
return paths.getExtraBuiltinExtensionDirectories();
|
||||
}
|
||||
}
|
||||
|
||||
const target = environment as typeof environment;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { OpenProcessExplorer } from "vs/workbench/contrib/issue/electron-browser
|
||||
import { ToggleDevToolsAction } from "vs/workbench/electron-browser/actions/developerActions";
|
||||
import { OpenPrivacyStatementUrlAction, OpenRequestFeatureUrlAction, OpenTwitterUrlAction } from "vs/workbench/electron-browser/actions/helpActions";
|
||||
import { CloseCurrentWindowAction, NewWindowAction, ShowAboutDialogAction } from "vs/workbench/electron-browser/actions/windowActions";
|
||||
import { REVEAL_IN_OS_COMMAND_ID } from "vs/workbench/contrib/files/browser/fileCommands";
|
||||
|
||||
const toSkip = [
|
||||
ToggleDevToolsAction.ID,
|
||||
@@ -16,6 +17,7 @@ const toSkip = [
|
||||
NewWindowAction.ID,
|
||||
CloseCurrentWindowAction.ID,
|
||||
CloseWorkspaceAction.ID,
|
||||
REVEAL_IN_OS_COMMAND_ID,
|
||||
|
||||
// Unfortunately referenced as a string
|
||||
"update.showCurrentReleaseNotes",
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { InitData, SharedProcessData } from "@coder/protocol";
|
||||
|
||||
/**
|
||||
* Provides paths.
|
||||
*/
|
||||
class Paths {
|
||||
private _appData: string | undefined;
|
||||
private _defaultUserData: string | undefined;
|
||||
@@ -7,6 +10,8 @@ class Paths {
|
||||
private _extensionsDirectory: string | undefined;
|
||||
private _builtInExtensionsDirectory: string | undefined;
|
||||
private _workingDirectory: string | undefined;
|
||||
private _extraExtensionDirectories: string[] | undefined;
|
||||
private _extraBuiltinExtensionDirectories: string[] | undefined;
|
||||
|
||||
public get appData(): string {
|
||||
if (typeof this._appData === "undefined") {
|
||||
@@ -48,6 +53,22 @@ class Paths {
|
||||
return this._builtInExtensionsDirectory;
|
||||
}
|
||||
|
||||
public get extraExtensionDirectories(): string[] {
|
||||
if (!this._extraExtensionDirectories) {
|
||||
throw new Error("trying to access extra extension directories before they have been set");
|
||||
}
|
||||
|
||||
return this._extraExtensionDirectories;
|
||||
}
|
||||
|
||||
public get extraBuiltinExtensionDirectories(): string[] {
|
||||
if (!this._extraBuiltinExtensionDirectories) {
|
||||
throw new Error("trying to access extra builtin extension directories before they have been set");
|
||||
}
|
||||
|
||||
return this._extraBuiltinExtensionDirectories;
|
||||
}
|
||||
|
||||
public get workingDirectory(): string {
|
||||
if (!this._workingDirectory) {
|
||||
throw new Error("trying to access working directory before it has been set");
|
||||
@@ -56,6 +77,9 @@ class Paths {
|
||||
return this._workingDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize paths using the provided data.
|
||||
*/
|
||||
public initialize(data: InitData, sharedData: SharedProcessData): void {
|
||||
process.env.VSCODE_LOGS = sharedData.logPath;
|
||||
this._appData = data.dataDirectory;
|
||||
@@ -64,6 +88,8 @@ class Paths {
|
||||
this._extensionsDirectory = data.extensionsDirectory;
|
||||
this._builtInExtensionsDirectory = data.builtInExtensionsDirectory;
|
||||
this._workingDirectory = data.workingDirectory;
|
||||
this._extraExtensionDirectories = data.extraExtensionDirectories;
|
||||
this._extraBuiltinExtensionDirectories = data.extraBuiltinExtensionDirectories;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,4 +99,6 @@ export const getDefaultUserDataPath = (): string => _paths.defaultUserData;
|
||||
export const getWorkingDirectory = (): string => _paths.workingDirectory;
|
||||
export const getExtensionsDirectory = (): string => _paths.extensionsDirectory;
|
||||
export const getBuiltInExtensionsDirectory = (): string => _paths.builtInExtensionsDirectory;
|
||||
export const getExtraExtensionDirectories = (): string[] => _paths.extraExtensionDirectories;
|
||||
export const getExtraBuiltinExtensionDirectories = (): string[] => _paths.extraBuiltinExtensionDirectories;
|
||||
export const getSocketPath = (): string => _paths.socketPath;
|
||||
|
||||
@@ -11,7 +11,7 @@ class Product implements IProductConfiguration {
|
||||
public introductoryVideosUrl = "https://code.visualstudio.com/docs/getstarted/introvideos";
|
||||
public tipsAndTricksUrl = "https://code.visualstudio.com/docs/getstarted/tips-and-tricks";
|
||||
public twitterUrl = "https://twitter.com/code";
|
||||
public licenseUrl = "https://github.com/codercom/code-server/blob/master/LICENSE";
|
||||
public licenseUrl = "https://github.com/cdr/code-server/blob/master/LICENSE";
|
||||
public aiConfig = process.env.DISABLE_TELEMETRY ? undefined! : {
|
||||
// Only needed so vscode can see that content exists for this value.
|
||||
// We override the application insights module.
|
||||
@@ -28,12 +28,17 @@ class Product implements IProductConfiguration {
|
||||
return this._dataFolderName;
|
||||
}
|
||||
|
||||
public extensionsGallery = {
|
||||
serviceUrl: global && global.process && global.process.env.SERVICE_URL
|
||||
|| process.env.SERVICE_URL
|
||||
|| "https://v1.extapi.coder.com",
|
||||
// tslint:disable-next-line:no-any
|
||||
} as any;
|
||||
// tslint:disable-next-line:no-any
|
||||
public extensionsGallery: any = {
|
||||
get serviceUrl(): string {
|
||||
return process.env.SERVICE_URL || "https://v1.extapi.coder.com";
|
||||
},
|
||||
|
||||
get itemUrl(): string {
|
||||
return process.env.ITEM_URL || "";
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
public extensionExecutionEnvironments = {
|
||||
"wayou.vscode-todo-highlight": "worker",
|
||||
|
||||
@@ -2,6 +2,17 @@ import * as vscodeTextmate from "../../../../lib/vscode/node_modules/vscode-text
|
||||
|
||||
const target = vscodeTextmate as typeof vscodeTextmate;
|
||||
|
||||
const ctx = (require as any).context("../../../../lib/extensions", true, /.*\.tmLanguage.json$/);
|
||||
// Maps grammar scope to loaded grammar
|
||||
const scopeToGrammar = {} as any;
|
||||
|
||||
ctx.keys().forEach((key: string) => {
|
||||
const value = ctx(key);
|
||||
if (value.scopeName) {
|
||||
scopeToGrammar[value.scopeName] = value;
|
||||
}
|
||||
});
|
||||
|
||||
target.Registry = class Registry extends vscodeTextmate.Registry {
|
||||
public constructor(opts: vscodeTextmate.RegistryOptions) {
|
||||
super({
|
||||
@@ -21,6 +32,13 @@ target.Registry = class Registry extends vscodeTextmate.Registry {
|
||||
}).catch(reason => rej(reason));
|
||||
});
|
||||
},
|
||||
loadGrammar: async (scopeName: string) => {
|
||||
if (scopeToGrammar[scopeName]) {
|
||||
return scopeToGrammar[scopeName];
|
||||
}
|
||||
|
||||
return opts.loadGrammar(scopeName);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,51 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -euxo pipefail
|
||||
|
||||
yarn task build:server:binary
|
||||
# Build using a Docker container using the specified image and version.
|
||||
function docker_build() {
|
||||
local image="${1}" ; shift
|
||||
local version="${1}" ; shift
|
||||
|
||||
local containerId
|
||||
containerId=$(docker create --network=host --rm -it -v "$(pwd)"/.cache:/src/.cache "${image}")
|
||||
docker start "${containerId}"
|
||||
docker exec "${containerId}" mkdir -p /src
|
||||
|
||||
function docker_exec() {
|
||||
docker exec "${containerId}" bash -c "$@"
|
||||
}
|
||||
|
||||
docker cp ./. "${containerId}":/src
|
||||
docker_exec "cd /src && yarn"
|
||||
docker_exec "cd /src && npm rebuild"
|
||||
docker_exec "cd /src && NODE_ENV=production VERSION=${version} yarn task build:server:binary"
|
||||
docker_exec "cd /src && yarn task package ${version}"
|
||||
docker cp "${containerId}":/src/release/. ./release/
|
||||
|
||||
docker stop "${containerId}"
|
||||
}
|
||||
|
||||
function main() {
|
||||
local version=${VERSION:-}
|
||||
local ostype=${OSTYPE:-}
|
||||
|
||||
if [[ -z "${version}" ]] ; then
|
||||
>&2 echo "Must set VERSION environment variable"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "${ostype}" == "darwin"* ]]; then
|
||||
NODE_ENV=production VERSION="${version}" yarn task build:server:binary
|
||||
yarn task package "${version}"
|
||||
else
|
||||
local image
|
||||
if [[ "$TARGET" == "alpine" ]]; then
|
||||
image="codercom/nbin-alpine"
|
||||
else
|
||||
image="codercom/nbin-centos"
|
||||
fi
|
||||
docker_build "${image}" "${version}"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@@ -21,3 +21,15 @@ Object.defineProperty(fs.read, util.promisify.custom, {
|
||||
global.requestAnimationFrame = (cb) => {
|
||||
setTimeout(cb, 0);
|
||||
};
|
||||
|
||||
// lchmod might not be available. Jest runs graceful-fs which makes this a no-op
|
||||
// when it doesn't exist but that doesn't seem to always run when running
|
||||
// multiple tests (or maybe it gets undone after a test).
|
||||
if (!fs.lchmod) {
|
||||
fs.lchmod = function (path, mode, cb) {
|
||||
if (cb) {
|
||||
process.nextTick(cb);
|
||||
}
|
||||
};
|
||||
fs.lchmodSync = function () {};
|
||||
}
|
||||
|
||||
@@ -129,9 +129,12 @@ index f91ca2b..ef6fce9 100644
|
||||
- const isMac = platform.isMacintosh;
|
||||
+ const isMac = browser.isMacintosh;
|
||||
diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts
|
||||
index f08c996..f9de58c 100644
|
||||
index f08c996..7db13fa 100644
|
||||
--- a/src/vs/code/electron-browser/issue/issueReporterMain.ts
|
||||
+++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts
|
||||
@@ -296 +296 @@ export class IssueReporter extends Disposable {
|
||||
- const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath];
|
||||
+ const piiPaths = [this.environmentService.appRoot, this.environmentService.extensionsPath, ...this.environmentService.extraExtensionPaths];
|
||||
@@ -425 +425 @@ export class IssueReporter extends Disposable {
|
||||
- const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey;
|
||||
+ const cmdOrCtrlKey = browser.isMacintosh ? e.metaKey : e.ctrlKey;
|
||||
@@ -146,25 +149,33 @@ index e0ff793..885de12 100644
|
||||
- const cmdOrCtrlKey = platform.isMacintosh ? e.metaKey : e.ctrlKey;
|
||||
+ const cmdOrCtrlKey = browser.isMacintosh ? e.metaKey : e.ctrlKey;
|
||||
diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
|
||||
index 6fd8249..04c0933 100644
|
||||
index 6fd8249..6ae6b11 100644
|
||||
--- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
|
||||
+++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
|
||||
@@ -50,0 +51,2 @@ import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiatio
|
||||
+import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
|
||||
+import { mkdirp } from 'vs/base/node/pfs';
|
||||
@@ -93,0 +96,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
@@ -93,0 +96,10 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
+ Promise.all<boolean | undefined>([ // Copied from src/vs/code/electron-main/main.ts
|
||||
+ environmentService.extensionsPath,
|
||||
+ environmentService.nodeCachedDataDir,
|
||||
+ environmentService.logsPath,
|
||||
+ environmentService.globalStorageHome,
|
||||
+ environmentService.workspaceStorageHome,
|
||||
+ environmentService.backupHome
|
||||
+ environmentService.backupHome,
|
||||
+ ...environmentService.extraExtensionPaths,
|
||||
+ ...environmentService.extraBuiltinExtensionPaths,
|
||||
+ ].map((path): undefined | Promise<boolean> => path ? mkdirp(path) : undefined));
|
||||
@@ -119,0 +130,2 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
@@ -119,0 +132,2 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
+ const backupMainService = instantiationService.createInstance(BackupMainService) as BackupMainService;
|
||||
+ backupMainService.initialize().catch(console.error);
|
||||
@@ -223,0 +236 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise<vo
|
||||
@@ -124 +138 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
- const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
|
||||
+ const { appRoot, extensionsPath, extraExtensionPaths, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService;
|
||||
@@ -138 +152 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
|
||||
- piiPaths: [appRoot, extensionsPath]
|
||||
+ piiPaths: [appRoot, extensionsPath, ...extraExtensionPaths]
|
||||
@@ -223,0 +238 @@ async function handshake(configuration: ISharedProcessConfiguration): Promise<vo
|
||||
+startup({ machineId: "1" });
|
||||
diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts
|
||||
index 1f8b17a..2a875f9 100644
|
||||
@@ -175,7 +186,7 @@ index 1f8b17a..2a875f9 100644
|
||||
+ await cli.main(args);
|
||||
+ return; // Always just do this for now.
|
||||
diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts
|
||||
index f97a692..0206957 100644
|
||||
index f97a692..8059a67 100644
|
||||
--- a/src/vs/editor/browser/config/configuration.ts
|
||||
+++ b/src/vs/editor/browser/config/configuration.ts
|
||||
@@ -10 +9,0 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -187,9 +198,6 @@ index f97a692..0206957 100644
|
||||
@@ -367 +366 @@ export class Configuration extends CommonEditorConfiguration {
|
||||
- if (platform.isMacintosh) {
|
||||
+ if (browser.isMacintosh) {
|
||||
@@ -378 +377 @@ export class Configuration extends CommonEditorConfiguration {
|
||||
- emptySelectionClipboard: browser.isWebKit || browser.isFirefox,
|
||||
+ emptySelectionClipboard: false, // browser.isWebKit || browser.isFirefox,
|
||||
diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts
|
||||
index b3b4472..f888d63 100644
|
||||
--- a/src/vs/editor/browser/controller/mouseHandler.ts
|
||||
@@ -250,32 +258,55 @@ index c69ea3f..b8d87f7 100644
|
||||
-const GOLDEN_LINE_HEIGHT_RATIO = platform.isMacintosh ? 1.5 : 1.35;
|
||||
+const GOLDEN_LINE_HEIGHT_RATIO = browser.isMacintosh ? 1.5 : 1.35;
|
||||
diff --git a/src/vs/editor/contrib/clipboard/clipboard.ts b/src/vs/editor/contrib/clipboard/clipboard.ts
|
||||
index 990be3a..8a326c6 100644
|
||||
index 990be3a..18ae0d5 100644
|
||||
--- a/src/vs/editor/contrib/clipboard/clipboard.ts
|
||||
+++ b/src/vs/editor/contrib/clipboard/clipboard.ts
|
||||
@@ -29 +29,2 @@ const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE)
|
||||
@@ -18,0 +19 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
|
||||
+import { clipboard } from 'electron';
|
||||
@@ -29 +30,2 @@ const supportsCopyWithSyntaxHighlighting = (supportsCopy && !browser.isEdgeOrIE)
|
||||
-const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste')));
|
||||
+// const supportsPaste = (platform.isNative || (!browser.isChrome && document.queryCommandSupported('paste')));
|
||||
+const supportsPaste = true;
|
||||
@@ -176,0 +178 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
@@ -71 +73 @@ class ExecCommandCutAction extends ExecCommandAction {
|
||||
- kbOpts = null;
|
||||
+ // kbOpts = null;
|
||||
@@ -119 +121 @@ class ExecCommandCopyAction extends ExecCommandAction {
|
||||
- kbOpts = null;
|
||||
+ // kbOpts = null;
|
||||
@@ -174 +176 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
- kbOpts = null;
|
||||
+ // kbOpts = null;
|
||||
@@ -176,0 +179 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
+ const { workbench } = require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench');
|
||||
@@ -181 +183 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
@@ -181 +184 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
- precondition: EditorContextKeys.writable,
|
||||
+ precondition: (require('vs/platform/contextkey/common/contextkey') as typeof import('vs/platform/contextkey/common/contextkey')).ContextKeyExpr.and(EditorContextKeys.writable, workbench.clipboardContextKey),
|
||||
@@ -191 +193,2 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
@@ -191 +194,2 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
- order: 3
|
||||
+ order: 3,
|
||||
+ when: workbench.clipboardContextKey,
|
||||
@@ -194,0 +198,14 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
@@ -194,0 +199,26 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
+
|
||||
+ public async run(accessor, editor: ICodeEditor): Promise<void> {
|
||||
+ if (editor instanceof (require('vs/editor/browser/widget/codeEditorWidget') as typeof import('vs/editor/browser/widget/codeEditorWidget')).CodeEditorWidget) {
|
||||
+ try {
|
||||
+ editor.trigger('', (require('vs/editor/common/editorCommon') as typeof import ('vs/editor/common/editorCommon')).Handler.Paste, {
|
||||
+ text: await (require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench')).workbench.clipboardText,
|
||||
+ editor.focus();
|
||||
+ const textInput = document.activeElement! as HTMLTextAreaElement;
|
||||
+ const dataTransfer = new DataTransfer();
|
||||
+ const value = await clipboard.readText();
|
||||
+ dataTransfer.setData("text/plain", value);
|
||||
+ const pasteEvent = new ClipboardEvent("paste", {
|
||||
+ clipboardData: dataTransfer,
|
||||
+ });
|
||||
+ textInput.dispatchEvent(pasteEvent);
|
||||
+ } catch (ex) {
|
||||
+ super.run(accessor, editor);
|
||||
+ try {
|
||||
+ editor.trigger('', (require('vs/editor/common/editorCommon') as typeof import ('vs/editor/common/editorCommon')).Handler.Paste, {
|
||||
+ text: await (require('vs/../../../../packages/vscode/src/workbench') as typeof import ('vs/../../../../packages/vscode/src/workbench')).workbench.clipboardText,
|
||||
+ });
|
||||
+ } catch (ex) {
|
||||
+ super.run(accessor, editor);
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ super.run(accessor, editor);
|
||||
@@ -363,6 +394,81 @@ index 9952574..908a9ae 100644
|
||||
@@ -9 +9 @@ import { URI } from 'vs/base/common/uri';
|
||||
-import { isMacintosh } from 'vs/base/common/platform';
|
||||
+import { isMacintosh } from 'vs/base/browser/browser';
|
||||
diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts
|
||||
index eb1873c..dbbacd0 100644
|
||||
--- a/src/vs/platform/environment/common/environment.ts
|
||||
+++ b/src/vs/platform/environment/common/environment.ts
|
||||
@@ -120,0 +121,2 @@ export interface IEnvironmentService {
|
||||
+ extraExtensionPaths: string[];
|
||||
+ extraBuiltinExtensionPaths: string[];
|
||||
diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts
|
||||
index 43866f8..e69b513 100644
|
||||
--- a/src/vs/platform/environment/node/environmentService.ts
|
||||
+++ b/src/vs/platform/environment/node/environmentService.ts
|
||||
@@ -172,0 +173,8 @@ export class EnvironmentService implements IEnvironmentService {
|
||||
+ @memoize
|
||||
+ get extraExtensionPaths(): string[] {
|
||||
+ return this._args['extra-extension-dirs'] || [];
|
||||
+ }
|
||||
+ @memoize
|
||||
+ get extraBuiltinExtensionPaths(): string[] {
|
||||
+ return this._args['extra-builtin-extension-dirs'] || [];
|
||||
+ }
|
||||
diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
|
||||
index c897029..f84d9b6 100644
|
||||
--- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts
|
||||
+++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
|
||||
@@ -733,5 +733,9 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
- const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System)
|
||||
- .then(result => {
|
||||
- this.logService.info('Scanned system extensions:', result.length);
|
||||
- return result;
|
||||
- });
|
||||
+ const systemExtensionsPromise = Promise.all([
|
||||
+ this.scanExtensions(this.systemExtensionsPath, ExtensionType.System),
|
||||
+ ...this.environmentService.extraBuiltinExtensionPaths
|
||||
+ .map((path) => this.scanExtensions(path, ExtensionType.System))
|
||||
+ ]).then((results) => {
|
||||
+ const result = results.reduce((flat, current) => flat.concat(current), []);
|
||||
+ this.logService.info('Scanned system extensions:', result.length);
|
||||
+ return result;
|
||||
+ });
|
||||
@@ -761 +765 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
- return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)])
|
||||
+ return Promise.all([this.getUninstalledExtensions(), this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User)])
|
||||
@@ -772,0 +777,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
+ private scanAllUserExtensions(folderName: string, type: ExtensionType): Promise<ILocalExtension[]> {
|
||||
+ return Promise.all([
|
||||
+ this.scanExtensions(folderName, type),
|
||||
+ ...this.environmentService.extraExtensionPaths.map((p) => this.scanExtensions(p, ExtensionType.User))
|
||||
+ ]).then((results) => results.reduce((flat, current) => flat.concat(current), []));
|
||||
+ }
|
||||
+
|
||||
@@ -805 +816 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
- .then(uninstalled => this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
|
||||
+ .then(uninstalled => this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
|
||||
@@ -814 +825 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
- return this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
|
||||
+ return this.scanAllUserExtensions(this.extensionsPath, ExtensionType.User) // All user extensions
|
||||
diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts
|
||||
index 9845da1..567c195 100644
|
||||
--- a/src/vs/platform/storage/node/storageMainService.ts
|
||||
+++ b/src/vs/platform/storage/node/storageMainService.ts
|
||||
@@ -169 +169,6 @@ export class StorageMainService extends Disposable implements IStorageMainServic
|
||||
- return readdir(this.environmentService.extensionsPath).then(extensions => {
|
||||
+ return Promise.all([
|
||||
+ this.environmentService.extensionsPath,
|
||||
+ ...this.environmentService.extraExtensionPaths,
|
||||
+ ].map((p) => readdir(p)))
|
||||
+ .then((results) => results.reduce((flat, current) => flat.concat(current), []))
|
||||
+ .then(extensions => {
|
||||
diff --git a/src/vs/platform/telemetry/electron-browser/telemetryService.ts b/src/vs/platform/telemetry/electron-browser/telemetryService.ts
|
||||
index 31d0309..5b166af 100644
|
||||
--- a/src/vs/platform/telemetry/electron-browser/telemetryService.ts
|
||||
+++ b/src/vs/platform/telemetry/electron-browser/telemetryService.ts
|
||||
@@ -42 +42 @@ export class TelemetryService extends Disposable implements ITelemetryService {
|
||||
- piiPaths: [environmentService.appRoot, environmentService.extensionsPath]
|
||||
+ piiPaths: [environmentService.appRoot, environmentService.extensionsPath, ...environmentService.extraExtensionPaths]
|
||||
diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts
|
||||
index cbc55b3..9d27c01 100644
|
||||
--- a/src/vs/platform/windows/common/windows.ts
|
||||
@@ -807,7 +913,7 @@ index 780147c..2e8c9af 100644
|
||||
- if (platform.isMacintosh) {
|
||||
+ if (browser.isMacintosh) {
|
||||
diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js
|
||||
index 74fc798..03728d0 100644
|
||||
index 74fc798..0b6b5eb 100644
|
||||
--- a/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js
|
||||
+++ b/src/vs/workbench/contrib/webview/electron-browser/webview-pre.js
|
||||
@@ -10 +10,19 @@
|
||||
@@ -846,6 +952,11 @@ index 74fc798..03728d0 100644
|
||||
+ // supportFetchAPI: true,
|
||||
+ // corsEnabled: true
|
||||
+ // });
|
||||
@@ -328 +346,3 @@
|
||||
- newFrame.contentWindow.focus();
|
||||
+ if (document.hasFocus()) {
|
||||
+ newFrame.contentWindow.focus();
|
||||
+ }
|
||||
diff --git a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts
|
||||
index 484ff86..f3f57cb 100644
|
||||
--- a/src/vs/workbench/contrib/webview/electron-browser/webview.contribution.ts
|
||||
@@ -974,11 +1085,30 @@ index 75f0026..2e94683 100644
|
||||
- if (!isMacintosh && getTitleBarStyle(configurationService, environmentService) === 'custom') {
|
||||
+ if (!(isNative && isMacintosh) && getTitleBarStyle(configurationService, environmentService) === 'custom') {
|
||||
diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
|
||||
index 059f821..2dde675 100644
|
||||
index 059f821..b19f292 100644
|
||||
--- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
|
||||
+++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts
|
||||
@@ -32,0 +33 @@ function getSystemExtensionsRoot(): string {
|
||||
+ return (require('vs/../../../../packages/vscode/src/fill/paths') as typeof import ('vs/../../../../packages/vscode/src/fill/paths')).getBuiltInExtensionsDirectory();
|
||||
@@ -191,2 +192,3 @@ export class CachedExtensionScanner {
|
||||
- const folderStat = await pfs.stat(input.absoluteFolderPath);
|
||||
- input.mtime = folderStat.mtime.getTime();
|
||||
+ const folderStats = await Promise.all([pfs.stat(input.absoluteFolderPath), ...input.extraFolderPaths.map((p) => pfs.stat(p))]);
|
||||
+ input.mtime = folderStats[0].mtime.getTime();
|
||||
+ input.extraMtimes = folderStats.slice(1).map((s) => s.mtime.getTime());
|
||||
@@ -259 +261 @@ export class CachedExtensionScanner {
|
||||
- new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations),
|
||||
+ new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations, environmentService.extraBuiltinExtensionPaths),
|
||||
@@ -290 +292 @@ export class CachedExtensionScanner {
|
||||
- new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations),
|
||||
+ new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations, environmentService.extraExtensionPaths),
|
||||
diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts
|
||||
index 9133b7e..8c801b7 100644
|
||||
--- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts
|
||||
+++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts
|
||||
@@ -461 +461 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
- if (errorMessage === this._lastExtensionHostError) {
|
||||
+ if (errorMessage === this._lastExtensionHostError || errorMessage === "disconnected") {
|
||||
diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
|
||||
index b337206..0477464 100644
|
||||
--- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts
|
||||
@@ -996,6 +1126,54 @@ index 838a9c7..2308cee 100644
|
||||
@@ -192 +192 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise<IRenderer
|
||||
- process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
|
||||
+ // process.kill(initData.parentPid, 0); // throws an exception if the main process doesn't exist anymore.
|
||||
diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts
|
||||
index 6e2179d..e6f38c9 100644
|
||||
--- a/src/vs/workbench/services/extensions/node/extensionPoints.ts
|
||||
+++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts
|
||||
@@ -445,0 +446 @@ export class ExtensionScannerInput {
|
||||
+ public extraMtimes: number[] = [];
|
||||
@@ -455 +456,2 @@ export class ExtensionScannerInput {
|
||||
- public readonly tanslations: Translations
|
||||
+ public readonly tanslations: Translations,
|
||||
+ public readonly extraFolderPaths: string[] = [],
|
||||
@@ -469,0 +472,16 @@ export class ExtensionScannerInput {
|
||||
+ // Allow extra folder paths in any order. Doesn't account for duplicates though.
|
||||
+ const eq = (a: string[] = [], b: string[] = [], atimes: number[] = [], btimes: number[] = []): boolean => {
|
||||
+ if (a.length !== b.length || atimes.length !== btimes.length) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ for (let i = 0; i < a.length; ++i) {
|
||||
+ const index = b.indexOf(a[i]);
|
||||
+ if (index === -1) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ if (atimes[i] !== btimes[index]) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ return true;
|
||||
+ };
|
||||
@@ -479,0 +498 @@ export class ExtensionScannerInput {
|
||||
+ && eq(a.extraFolderPaths, b.extraFolderPaths, a.extraMtimes, b.extraMtimes)
|
||||
@@ -530 +549 @@ export class ExtensionScanner {
|
||||
- * Scan a list of extensions defined in `absoluteFolderPath`
|
||||
+ * Scan a list of extensions defined in `absoluteFolderPath` and `extraFolderPaths`
|
||||
@@ -532 +551 @@ export class ExtensionScanner {
|
||||
- public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolver: IExtensionResolver | null = null): Promise<IExtensionDescription[]> {
|
||||
+ public static async scanExtensions(input: ExtensionScannerInput, log: ILog, resolvers: IExtensionResolver | IExtensionResolver[] | null = null): Promise<IExtensionDescription[]> {
|
||||
@@ -533,0 +553 @@ export class ExtensionScanner {
|
||||
+ const extraFolderPaths = input.extraFolderPaths;
|
||||
@@ -537,2 +557,4 @@ export class ExtensionScanner {
|
||||
- if (!resolver) {
|
||||
- resolver = new DefaultExtensionResolver(absoluteFolderPath);
|
||||
+ if (!resolvers) {
|
||||
+ resolvers = [absoluteFolderPath, ...extraFolderPaths].map((p) => new DefaultExtensionResolver(p));
|
||||
+ } else if (!Array.isArray(resolvers)) {
|
||||
+ resolvers = [resolvers];
|
||||
@@ -552 +574,2 @@ export class ExtensionScanner {
|
||||
- let refs = await resolver.resolveExtensions();
|
||||
+ let refs = await Promise.all(resolvers.map((resolver) => resolver.resolveExtensions()))
|
||||
+ .then((results) => results.reduce((flat, current) => flat.concat(current), []));
|
||||
diff --git a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts b/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
|
||||
index 33d3697..af71b01 100644
|
||||
--- a/src/vs/workbench/services/files/node/watcher/nsfw/watcherService.ts
|
||||
|
||||
@@ -90,8 +90,6 @@ module.exports = (options = {}) => ({
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
"process.env.NODE_ENV": `"${environment}"`,
|
||||
"process.env.LOG_LEVEL": `"${process.env.LOG_LEVEL || ""}"`,
|
||||
"process.env.SERVICE_URL": `"${process.env.SERVICE_URL || ""}"`,
|
||||
"process.env.VERSION": `"${process.env.VERSION || ""}"`,
|
||||
}),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user