Archived
1
0

Getting the client to run (#12)

* Clean up workbench and integrate initialization data

* Uncomment Electron fill

* Run server & client together

* Clean up Electron fill & patch

* Bind fs methods

This makes them usable with the promise form:
`promisify(access)(...)`.

* Add space between tag and title to browser logger

* Add typescript dep to server and default __dirname for path

* Serve web files from server

* Adjust some dev options

* Rework workbench a bit to use a class and catch unexpected errors

* No mkdirs for now, fix util fill, use bash with exec

* More fills, make general client abstract

* More fills

* Fix cp.exec

* Fix require calls in fs fill being aliased

* Create data and storage dir

* Implement fs.watch

Using exec for now.

* Implement storage database fill

* Fix os export and homedir

* Add comment to use navigator.sendBeacon

* Fix fs callbacks (some args are optional)

* Make sure data directory exists when passing it back

* Update patch

* Target es5

* More fills

* Add APIs required for bootstrap-fork to function (#15)

* Add bootstrap-fork execution

* Add createConnection

* Bundle bootstrap-fork into cli

* Remove .node directory created from spdlog

* Fix npm start

* Remove unnecessary comment

* Add webpack-hot-middleware if CLI env is not set

* Add restarting to shared process

* Fix starting with yarn
This commit is contained in:
Asher
2019-01-18 15:46:40 -06:00
committed by Kyle Carberry
parent 05899b5edf
commit 72bf4547d4
80 changed files with 5183 additions and 9697 deletions

View File

@ -1,6 +1,6 @@
import * as cp from "child_process";
import { Client } from "../client";
import { useBuffer } from "./util";
import { useBuffer } from "../../common/util";
export class CP {
@ -13,19 +13,21 @@ export class CP {
options?: { encoding?: BufferEncoding | string | "buffer" | null } & cp.ExecOptions | null | ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void),
callback?: ((error: Error | null, stdout: string, stderr: string) => void) | ((error: Error | null, stdout: Buffer, stderr: Buffer) => void),
): cp.ChildProcess => {
const process = this.client.spawn(command);
// TODO: Probably should add an `exec` instead of using `spawn`, especially
// since bash might not be available.
const childProcess = this.client.spawn("bash", ["-c", command.replace(/"/g, "\\\"")]);
let stdout = "";
process.stdout.on("data", (data) => {
childProcess.stdout.on("data", (data) => {
stdout += data.toString();
});
let stderr = "";
process.stderr.on("data", (data) => {
childProcess.stderr.on("data", (data) => {
stderr += data.toString();
});
process.on("exit", (exitCode) => {
childProcess.on("exit", (exitCode) => {
const error = exitCode !== 0 ? new Error(stderr) : null;
if (typeof options === "function") {
callback = options;
@ -39,15 +41,15 @@ export class CP {
});
// @ts-ignore
return process;
return childProcess;
}
public fork(modulePath: string): cp.ChildProcess {
public fork = (modulePath: string, args?: ReadonlyArray<string> | cp.ForkOptions, options?: cp.ForkOptions): cp.ChildProcess => {
//@ts-ignore
return this.client.fork(modulePath);
return this.client.fork(modulePath, args, options);
}
public spawn(command: string, args?: ReadonlyArray<string> | cp.SpawnOptions, _options?: cp.SpawnOptions): cp.ChildProcess {
public spawn = (command: string, args?: ReadonlyArray<string> | cp.SpawnOptions, options?: cp.SpawnOptions): cp.ChildProcess => {
// TODO: fix this ignore. Should check for args or options here
//@ts-ignore
return this.client.spawn(command, args, options);

View File

@ -1,9 +1,20 @@
import { exec, ChildProcess } from "child_process";
import { EventEmitter } from "events";
import * as fs from "fs";
import { IEncodingOptions, IEncodingOptionsCallback, escapePath, useBuffer } from "../../common/util";
import { Client } from "../client";
// Use this to get around Webpack inserting our fills.
// TODO: is there a better way?
declare var _require: typeof require;
/**
* Implements the native fs module
* Doesn't use `implements typeof import("fs")` to remove need for __promisify__ impls
*
* TODO: For now we can't use async in the evaluate calls because they get
* transpiled to TypeScript's helpers. tslib is included but we also need to set
* _this somehow which the __awaiter helper uses.
*/
export class FS {
@ -11,34 +22,46 @@ export class FS {
private readonly client: Client,
) { }
public access(path: fs.PathLike, mode: number | undefined, callback: (err: NodeJS.ErrnoException) => void): void {
public access = (path: fs.PathLike, mode: number | undefined | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof mode === "function") {
callback = mode;
mode = undefined;
}
this.client.evaluate((path, mode) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.access)(path, mode);
}, path, mode).then(() => {
callback(undefined!);
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public appendFile(file: fs.PathLike | number, data: any, options: { encoding?: string | null, mode?: string | number, flag?: string } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
// tslint:disable-next-line no-any
public appendFile = (file: fs.PathLike | number, data: any, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof options === "function") {
callback = options;
options = undefined;
}
this.client.evaluate((path, data, options) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.appendFile)(path, data, options);
}, file, data, options).then(() => {
callback(undefined!);
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public chmod(path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void {
public chmod = (path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((path, mode) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.chmod)(path, mode);
}, path, mode).then(() => {
callback(undefined!);
@ -47,10 +70,11 @@ export class FS {
});
}
public chown(path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void {
public chown = (path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((path, uid, gid) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.chown)(path, uid, gid);
}, path, uid, gid).then(() => {
callback(undefined!);
@ -59,10 +83,11 @@ export class FS {
});
}
public close(fd: number, callback: (err: NodeJS.ErrnoException) => void): void {
public close = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((fd) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.close)(fd);
}, fd).then(() => {
callback(undefined!);
@ -71,26 +96,31 @@ export class FS {
});
}
public copyFile(src: fs.PathLike, dest: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
this.client.evaluate((src, dest) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
return util.promisify(fs.copyFile)(src, dest);
}, src, dest).then(() => {
callback(undefined!);
public copyFile = (src: fs.PathLike, dest: fs.PathLike, flags: number | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof flags === "function") {
callback = flags;
}
this.client.evaluate((src, dest, flags) => {
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.copyFile)(src, dest, flags);
}, src, dest, typeof flags !== "function" ? flags : undefined).then(() => {
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public createWriteStream(): void {
public createWriteStream = (): void => {
throw new Error("not implemented");
}
public exists(path: fs.PathLike, callback: (exists: boolean) => void): void {
public exists = (path: fs.PathLike, callback: (exists: boolean) => void): void => {
this.client.evaluate((path) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.exists)(path);
}, path).then((r) => {
callback(r);
@ -99,10 +129,11 @@ export class FS {
});
}
public fchmod(fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void {
public fchmod = (fd: number, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((fd, mode) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.fchmod)(fd, mode);
}, fd, mode).then(() => {
callback(undefined!);
@ -111,10 +142,11 @@ export class FS {
});
}
public fchown(fd: number, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void {
public fchown = (fd: number, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((fd, uid, gid) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.fchown)(fd, uid, gid);
}, fd, uid, gid).then(() => {
callback(undefined!);
@ -123,10 +155,11 @@ export class FS {
});
}
public fdatasync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void {
public fdatasync = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((fd) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.fdatasync)(fd);
}, fd).then(() => {
callback(undefined!);
@ -135,21 +168,23 @@ export class FS {
});
}
public fstat(fd: number, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void {
this.client.evaluate(async (fd) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const stats = await util.promisify(fs.fstat)(fd);
return {
...stats,
_isBlockDevice: stats.isBlockDevice(),
_isCharacterDevice: stats.isCharacterDevice(),
_isDirectory: stats.isDirectory(),
_isFIFO: stats.isFIFO(),
_isFile: stats.isFile(),
_isSocket: stats.isSocket(),
_isSymbolicLink: stats.isSymbolicLink(),
};
public fstat = (fd: number, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
this.client.evaluate((fd) => {
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.fstat)(fd).then((stats) => {
return {
...stats,
_isBlockDevice: stats.isBlockDevice(),
_isCharacterDevice: stats.isCharacterDevice(),
_isDirectory: stats.isDirectory(),
_isFIFO: stats.isFIFO(),
_isFile: stats.isFile(),
_isSocket: stats.isSocket(),
_isSymbolicLink: stats.isSymbolicLink(),
};
});
}, fd).then((stats) => {
callback(undefined!, Stats.fromObject(stats));
}).catch((ex) => {
@ -157,10 +192,11 @@ export class FS {
});
}
public fsync(fd: number, callback: (err: NodeJS.ErrnoException) => void): void {
public fsync = (fd: number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((fd) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.fsync)(fd);
}, fd).then(() => {
callback(undefined!);
@ -169,22 +205,28 @@ export class FS {
});
}
public ftruncate(fd: number, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
public ftruncate = (fd: number, len: number | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof len === "function") {
callback = len;
len = undefined;
}
this.client.evaluate((fd, len) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.ftruncate)(fd, len);
}, fd, len).then(() => {
callback(undefined!);
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public futimes(fd: number, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void {
public futimes = (fd: number, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((fd, atime, mtime) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.futimes)(fd, atime, mtime);
}, fd, atime, mtime).then(() => {
callback(undefined!);
@ -193,10 +235,11 @@ export class FS {
});
}
public lchmod(path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void {
public lchmod = (path: fs.PathLike, mode: string | number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((path, mode) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.lchmod)(path, mode);
}, path, mode).then(() => {
callback(undefined!);
@ -205,10 +248,11 @@ export class FS {
});
}
public lchown(path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void {
public lchown = (path: fs.PathLike, uid: number, gid: number, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((path, uid, gid) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.lchown)(path, uid, gid);
}, path, uid, gid).then(() => {
callback(undefined!);
@ -217,10 +261,11 @@ export class FS {
});
}
public link(existingPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
public link = (existingPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((existingPath, newPath) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.link)(existingPath, newPath);
}, existingPath, newPath).then(() => {
callback(undefined!);
@ -229,21 +274,23 @@ export class FS {
});
}
public lstat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void {
this.client.evaluate(async (path) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const stats = await util.promisify(fs.lstat)(path);
return {
...stats,
_isBlockDevice: stats.isBlockDevice(),
_isCharacterDevice: stats.isCharacterDevice(),
_isDirectory: stats.isDirectory(),
_isFIFO: stats.isFIFO(),
_isFile: stats.isFile(),
_isSocket: stats.isSocket(),
_isSymbolicLink: stats.isSymbolicLink(),
};
public lstat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
this.client.evaluate((path) => {
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.lstat)(path).then((stats) => {
return {
...stats,
_isBlockDevice: stats.isBlockDevice(),
_isCharacterDevice: stats.isCharacterDevice(),
_isDirectory: stats.isDirectory(),
_isFIFO: stats.isFIFO(),
_isFile: stats.isFile(),
_isSocket: stats.isSocket(),
_isSymbolicLink: stats.isSymbolicLink(),
};
});
}, path).then((stats) => {
callback(undefined!, Stats.fromObject(stats));
}).catch((ex) => {
@ -251,54 +298,70 @@ export class FS {
});
}
public mkdir(path: fs.PathLike, mode: number | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
public mkdir = (path: fs.PathLike, mode: number | string | fs.MakeDirectoryOptions | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof mode === "function") {
callback = mode;
mode = undefined;
}
this.client.evaluate((path, mode) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.mkdir)(path, mode);
}, path, mode).then(() => {
callback(undefined!);
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public mkdtemp(prefix: string, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, folder: string) => void): void {
public mkdtemp = (prefix: string, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, folder: string | Buffer) => void): void => {
if (typeof options === "function") {
callback = options;
options = undefined;
}
this.client.evaluate((prefix, options) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.mkdtemp)(prefix, options);
}, prefix, options).then((folder) => {
callback(undefined!, folder);
callback!(undefined!, folder);
}).catch((ex) => {
callback(ex, undefined!);
callback!(ex, undefined!);
});
}
public open(path: fs.PathLike, flags: string | number, mode: string | number | undefined | null, callback: (err: NodeJS.ErrnoException, fd: number) => void): void {
public open = (path: fs.PathLike, flags: string | number, mode: string | number | undefined | null | ((err: NodeJS.ErrnoException, fd: number) => void), callback?: (err: NodeJS.ErrnoException, fd: number) => void): void => {
if (typeof mode === "function") {
callback = mode;
mode = undefined;
}
this.client.evaluate((path, flags, mode) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.open)(path, flags, mode);
}, path, flags, mode).then((fd) => {
callback(undefined!, fd);
callback!(undefined!, fd);
}).catch((ex) => {
callback(ex, undefined!);
callback!(ex, undefined!);
});
}
public read<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number, length: number, position: number | null, callback: (err: NodeJS.ErrnoException, bytesRead: number, buffer: TBuffer) => void): void {
this.client.evaluate(async (fd, bufferLength, length, position) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
public read = <TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number, length: number, position: number | null, callback: (err: NodeJS.ErrnoException, bytesRead: number, buffer: TBuffer) => void): void => {
this.client.evaluate((fd, length, position) => {
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
const buffer = new Buffer(length);
const resp = await util.promisify(fs.read)(fd, buffer, 0, length, position);
return {
bytesRead: resp.bytesRead,
content: buffer.toString("utf8"),
};
}, fd, buffer.byteLength, length, position).then((resp) => {
return util.promisify(fs.read)(fd, buffer, 0, length, position).then((resp) => {
return {
bytesRead: resp.bytesRead,
content: buffer.toString("utf8"),
};
}):
}, fd, length, position).then((resp) => {
const newBuf = Buffer.from(resp.content, "utf8");
buffer.set(newBuf, offset);
callback(undefined!, resp.bytesRead, newBuf as TBuffer);
@ -307,60 +370,80 @@ export class FS {
});
}
public readFile(path: fs.PathLike | number, options: string | { encoding?: string | null | undefined; flag?: string | undefined; } | null | undefined, callback: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void {
this.client.evaluate(async (path, options) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const value = await util.promisify(fs.readFile)(path, options);
public readFile = (path: fs.PathLike | number, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, data: string | Buffer) => void): void => {
if (typeof options === "function") {
callback = options;
options = undefined;
}
this.client.evaluate((path, options) => {
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return value.toString();
return util.promisify(fs.readFile)(path, options).then((value) => value.toString());
}, path, options).then((buffer) => {
callback(undefined!, buffer);
callback!(undefined!, buffer);
}).catch((ex) => {
callback(ex, undefined!);
callback!(ex, undefined!);
});
}
public readdir(path: fs.PathLike, options: { encoding: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, files: string[]) => void): void {
public readdir = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, files: Buffer[] | fs.Dirent[] | string[]) => void): void => {
if (typeof options === "function") {
callback = options;
options = undefined;
}
// TODO: options can also take `withFileTypes` but the types aren't working.
this.client.evaluate((path, options) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.readdir)(path, options);
}, path, options).then((files) => {
callback(undefined!, files);
callback!(undefined!, files);
}).catch((ex) => {
callback(ex, undefined!);
callback!(ex, undefined!);
});
}
public readlink(path: fs.PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, linkString: string) => void): void {
public readlink = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, linkString: string | Buffer) => void): void => {
if (typeof options === "function") {
callback = options;
options = undefined;
}
this.client.evaluate((path, options) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.readlink)(path, options);
}, path, options).then((linkString) => {
callback(undefined!, linkString);
callback!(undefined!, linkString);
}).catch((ex) => {
callback(ex, undefined!);
callback!(ex, undefined!);
});
}
public realpath(path: fs.PathLike, options: { encoding?: BufferEncoding | null } | BufferEncoding | undefined | null, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => void): void {
public realpath = (path: fs.PathLike, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException, resolvedPath: string | Buffer) => void): void => {
if (typeof options === "function") {
callback = options;
options = undefined;
}
this.client.evaluate((path, options) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.realpath)(path, options);
}, path, options).then((resolvedPath) => {
callback(undefined!, resolvedPath);
callback!(undefined!, resolvedPath);
}).catch((ex) => {
callback(ex, undefined!);
callback!(ex, undefined!);
});
}
public rename(oldPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
public rename = (oldPath: fs.PathLike, newPath: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((oldPath, newPath) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.rename)(oldPath, newPath);
}, oldPath, newPath).then(() => {
callback(undefined!);
@ -369,10 +452,11 @@ export class FS {
});
}
public rmdir(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
public rmdir = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((path) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.rmdir)(path);
}, path).then(() => {
callback(undefined!);
@ -381,21 +465,23 @@ export class FS {
});
}
public stat(path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void {
this.client.evaluate(async (path) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const stats = await util.promisify(fs.stat)(path);
return {
...stats,
_isBlockDevice: stats.isBlockDevice(),
_isCharacterDevice: stats.isCharacterDevice(),
_isDirectory: stats.isDirectory(),
_isFIFO: stats.isFIFO(),
_isFile: stats.isFile(),
_isSocket: stats.isSocket(),
_isSymbolicLink: stats.isSymbolicLink(),
};
public stat = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException, stats: fs.Stats) => void): void => {
this.client.evaluate((path) => {
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.stat)(path).then((stats) => {
return {
...stats,
_isBlockDevice: stats.isBlockDevice(),
_isCharacterDevice: stats.isCharacterDevice(),
_isDirectory: stats.isDirectory(),
_isFIFO: stats.isFIFO(),
_isFile: stats.isFile(),
_isSocket: stats.isSocket(),
_isSymbolicLink: stats.isSymbolicLink(),
};
});
}, path).then((stats) => {
callback(undefined!, Stats.fromObject(stats));
}).catch((ex) => {
@ -403,34 +489,45 @@ export class FS {
});
}
public symlink(target: fs.PathLike, path: fs.PathLike, type: fs.symlink.Type | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
public symlink = (target: fs.PathLike, path: fs.PathLike, type: fs.symlink.Type | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof type === "function") {
callback = type;
type = undefined;
}
this.client.evaluate((target, path, type) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.symlink)(target, path, type);
}, target, path, type).then(() => {
callback(undefined!);
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public truncate(path: fs.PathLike, len: number | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
public truncate = (path: fs.PathLike, len: number | undefined | null | ((err: NodeJS.ErrnoException) => void), callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof len === "function") {
callback = len;
len = undefined;
}
this.client.evaluate((path, len) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.truncate)(path, len);
}, path, len).then(() => {
callback(undefined!);
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public unlink(path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void {
public unlink = (path: fs.PathLike, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((path) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.unlink)(path);
}, path).then(() => {
callback(undefined!);
@ -439,10 +536,11 @@ export class FS {
});
}
public utimes(path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void {
public utimes = (path: fs.PathLike, atime: string | number | Date, mtime: string | number | Date, callback: (err: NodeJS.ErrnoException) => void): void => {
this.client.evaluate((path, atime, mtime) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.utimes)(path, atime, mtime);
}, path, atime, mtime).then(() => {
callback(undefined!);
@ -451,38 +549,112 @@ export class FS {
});
}
public write<TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined, length: number | undefined, position: number | undefined, callback: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void {
this.client.evaluate(async (fd, buffer, offset, length, position) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const resp = await util.promisify(fs.write)(fd, Buffer.from(buffer, "utf8"), offset, length, position);
public write = <TBuffer extends Buffer | Uint8Array>(fd: number, buffer: TBuffer, offset: number | undefined, length: number | undefined, position: number | undefined | ((err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void), callback?: (err: NodeJS.ErrnoException, written: number, buffer: TBuffer) => void): void => {
if (typeof position === "function") {
callback = position;
position = undefined;
}
this.client.evaluate((fd, buffer, offset, length, position) => {
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return {
bytesWritten: resp.bytesWritten,
content: resp.buffer.toString("utf8"),
}
return util.promisify(fs.write)(fd, Buffer.from(buffer, "utf8"), offset, length, position).then((resp) => {
return {
bytesWritten: resp.bytesWritten,
content: resp.buffer.toString("utf8"),
};
});
}, fd, buffer.toString(), offset, length, position).then((r) => {
callback(undefined!, r.bytesWritten, Buffer.from(r.content, "utf8") as TBuffer);
callback!(undefined!, r.bytesWritten, Buffer.from(r.content, "utf8") as TBuffer);
}).catch((ex) => {
callback(ex, undefined!, undefined!);
callback!(ex, undefined!, undefined!);
});
}
public writeFile(path: fs.PathLike | number, data: any, options: { encoding?: string | null; mode?: number | string; flag?: string; } | string | undefined | null, callback: (err: NodeJS.ErrnoException) => void): void {
// tslint:disable-next-line no-any
public writeFile = (path: fs.PathLike | number, data: any, options: IEncodingOptionsCallback, callback?: (err: NodeJS.ErrnoException) => void): void => {
if (typeof options === "function") {
callback = options;
options = undefined;
}
this.client.evaluate((path, data, options) => {
const fs = require("fs") as typeof import("fs");
const util = require("util") as typeof import("util");
const fs = _require("fs") as typeof import("fs");
const util = _require("util") as typeof import("util");
return util.promisify(fs.writeFile)(path, data, options);
}, path, data, options).then(() => {
callback(undefined!);
callback!(undefined!);
}).catch((ex) => {
callback(ex);
callback!(ex);
});
}
public watch = (filename: fs.PathLike, options: IEncodingOptions, listener?: ((event: string, filename: string) => void) | ((event: string, filename: Buffer) => void)): fs.FSWatcher => {
// TODO: can we modify `evaluate` for long-running processes like watch?
// Especially since inotifywait might not be available.
const buffer = new NewlineInputBuffer((msg): void => {
msg = msg.trim();
const index = msg.lastIndexOf(":");
const events = msg.substring(index + 1).split(",");
const baseFilename = msg.substring(0, index).split("/").pop();
events.forEach((event) => {
switch (event) {
// Rename is emitted when a file appears or disappears in the directory.
case "CREATE":
case "DELETE":
case "MOVED_FROM":
case "MOVED_TO":
watcher.emit("rename", baseFilename);
break;
case "CLOSE_WRITE":
watcher.emit("change", baseFilename);
break;
}
});
});
// TODO: `exec` is undefined for some reason.
const process = exec(`inotifywait ${escapePath(filename.toString())} -m --format "%w%f:%e"`);
process.on("exit", (exitCode) => {
watcher.emit("error", new Error(`process terminated unexpectedly with code ${exitCode}`));
});
process.stdout.on("data", (data) => {
buffer.push(data);
});
const watcher = new Watcher(process);
if (listener) {
const l = listener;
watcher.on("change", (filename) => {
// @ts-ignore not sure how to make this work.
l("change", useBuffer(options) ? Buffer.from(filename) : filename);
});
watcher.on("rename", (filename) => {
// @ts-ignore not sure how to make this work.
l("rename", useBuffer(options) ? Buffer.from(filename) : filename);
});
}
return watcher;
}
}
class Watcher extends EventEmitter implements fs.FSWatcher {
public constructor(private readonly process: ChildProcess) {
super();
}
public close(): void {
this.process.kill();
}
}
class Stats implements fs.Stats {
// tslint:disable-next-line no-any
public static fromObject(object: any): Stats {
return new Stats(object);
}
@ -575,3 +747,39 @@ class Stats implements fs.Stats {
}
}
/**
* Class for safely taking input and turning it into separate messages.
* Assumes that messages are split by newlines.
*/
export class NewlineInputBuffer {
private callback: (msg: string) => void;
private buffer: string | undefined;
public constructor(callback: (msg: string) => void) {
this.callback = callback;
}
/**
* Add data to be buffered.
*/
public push(data: string | Uint8Array): void {
let input = typeof data === "string" ? data : data.toString();
if (this.buffer) {
input = this.buffer + input;
this.buffer = undefined;
}
const lines = input.split("\n");
const length = lines.length - 1;
const lastLine = lines[length];
if (lastLine.length > 0) {
this.buffer = lastLine;
}
lines.pop(); // This is either the line we buffered or an empty string.
for (let i = 0; i < length; ++i) {
this.callback(lines[i]);
}
}
}

View File

@ -1,4 +1,5 @@
import * as net from "net";
import { Client } from '../client';
type NodeNet = typeof net;
@ -7,6 +8,10 @@ type NodeNet = typeof net;
*/
export class Net implements NodeNet {
public constructor(
private readonly client: Client,
) {}
public get Socket(): typeof net.Socket {
throw new Error("not implemented");
}
@ -19,8 +24,9 @@ export class Net implements NodeNet {
throw new Error("not implemented");
}
public createConnection(): net.Socket {
throw new Error("not implemented");
public createConnection(...args: any[]): net.Socket {
//@ts-ignore
return this.client.createConnection(...args) as net.Socket;
}
public isIP(_input: string): number {

View File

@ -1,8 +0,0 @@
/**
* Return true if the options specify to use a Buffer instead of string.
*/
export const useBuffer = (options: { encoding?: string | null } | string | undefined | null | Function): boolean => {
return options === "buffer"
|| (!!options && typeof options !== "string" && typeof options !== "function"
&& (options.encoding === "buffer" || options.encoding === null));
};