summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Guerrero <stephenguerrero43@gmail.com>2021-01-25 11:30:31 -0500
committerGitHub <noreply@github.com>2021-01-25 17:30:31 +0100
commit1f8b83ba1aafd2c25c24c00eb7a117588a34b3f9 (patch)
tree5ab730abad715a1a0fd4cb3bc3ba8434ae4ab3c5
parente0eb111e3e2f76d16682559780baa3d756ed0df7 (diff)
feat(std/node): Add support for process.on("exit") (#8940)
This commit adds support for process.on("exit") by appending a listener to the unload event. Luckily, unload works pretty much the same as on("exit") since it won't schedule any additional work in the even loop either. This commit also solves an error in the Node implementation, since "process.argv" didn't contained the main module route as it was supposed to.
-rw-r--r--std/node/global.d.ts2
-rw-r--r--std/node/global.ts2
-rw-r--r--std/node/process.ts282
-rw-r--r--std/node/process_exit_test.ts19
-rw-r--r--std/node/process_test.ts71
5 files changed, 273 insertions, 103 deletions
diff --git a/std/node/global.d.ts b/std/node/global.d.ts
index 707073e2e..94baf03b7 100644
--- a/std/node/global.d.ts
+++ b/std/node/global.d.ts
@@ -1,5 +1,5 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-import { process as processModule } from "./process.ts";
+import processModule from "./process.ts";
import { Buffer as bufferModule } from "./buffer.ts";
import timers from "./timers.ts";
diff --git a/std/node/global.ts b/std/node/global.ts
index a07a892e5..550f6ddf9 100644
--- a/std/node/global.ts
+++ b/std/node/global.ts
@@ -1,6 +1,6 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
/// <reference path="./global.d.ts" />
-import { process as processModule } from "./process.ts";
+import processModule from "./process.ts";
import { Buffer as bufferModule } from "./buffer.ts";
import timers from "./timers.ts";
diff --git a/std/node/process.ts b/std/node/process.ts
index 5d6791fc4..419eb8cec 100644
--- a/std/node/process.ts
+++ b/std/node/process.ts
@@ -1,32 +1,109 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
import { notImplemented } from "./_utils.ts";
+import EventEmitter from "./events.ts";
+import { fromFileUrl } from "../path/mod.ts";
+
+const notImplementedEvents = [
+ "beforeExit",
+ "disconnect",
+ "message",
+ "multipleResolves",
+ "rejectionHandled",
+ "SIGBREAK",
+ "SIGBUS",
+ "SIGFPE",
+ "SIGHUP",
+ "SIGILL",
+ "SIGINT",
+ "SIGSEGV",
+ "SIGTERM",
+ "SIGWINCH",
+ "uncaughtException",
+ "uncaughtExceptionMonitor",
+ "unhandledRejection",
+ "warning",
+];
/** https://nodejs.org/api/process.html#process_process_arch */
export const arch = Deno.build.arch;
+function getArguments() {
+ return [Deno.execPath(), fromFileUrl(Deno.mainModule), ...Deno.args];
+}
+
+//deno-lint-ignore ban-ts-comment
+//@ts-ignore
+const _argv: {
+ [Deno.customInspect]: () => string;
+ [key: number]: string;
+} = [];
+
+Object.defineProperty(_argv, Deno.customInspect, {
+ enumerable: false,
+ configurable: false,
+ get: function () {
+ return getArguments();
+ },
+});
+
+/**
+ * https://nodejs.org/api/process.html#process_process_argv
+ * Read permissions are required in order to get the executable route
+ * */
+export const argv: Record<string, string> = new Proxy(_argv, {
+ get(target, prop) {
+ if (prop === Deno.customInspect) {
+ return target[Deno.customInspect];
+ }
+ return getArguments()[prop as number];
+ },
+ ownKeys() {
+ return Reflect.ownKeys(getArguments());
+ },
+});
+
/** https://nodejs.org/api/process.html#process_process_chdir_directory */
export const chdir = Deno.chdir;
/** https://nodejs.org/api/process.html#process_process_cwd */
export const cwd = Deno.cwd;
-/** https://nodejs.org/api/process.html#process_process_exit_code */
-export const exit = Deno.exit;
+//deno-lint-ignore ban-ts-comment
+//@ts-ignore
+const _env: {
+ [Deno.customInspect]: () => string;
+} = {};
-/** https://nodejs.org/api/process.html#process_process_pid */
-export const pid = Deno.pid;
-
-/** https://nodejs.org/api/process.html#process_process_platform */
-export const platform = Deno.build.os === "windows" ? "win32" : Deno.build.os;
+Object.defineProperty(_env, Deno.customInspect, {
+ enumerable: false,
+ configurable: false,
+ get: function () {
+ return Deno.env.toObject();
+ },
+});
-/** https://nodejs.org/api/process.html#process_process_version */
-export const version = `v${Deno.version.deno}`;
+/**
+ * https://nodejs.org/api/process.html#process_process_env
+ * Requires env permissions
+ * */
+export const env: Record<string, string> = new Proxy(_env, {
+ get(target, prop) {
+ if (prop === Deno.customInspect) {
+ return target[Deno.customInspect];
+ }
+ return Deno.env.get(String(prop));
+ },
+ ownKeys() {
+ return Reflect.ownKeys(Deno.env.toObject());
+ },
+ set(_target, prop, value) {
+ Deno.env.set(String(prop), String(value));
+ return value;
+ },
+});
-/** https://nodejs.org/api/process.html#process_process_versions */
-export const versions = {
- node: Deno.version.deno,
- ...Deno.version,
-};
+/** https://nodejs.org/api/process.html#process_process_exit_code */
+export const exit = Deno.exit;
/** https://nodejs.org/api/process.html#process_process_nexttick_callback_args */
export function nextTick(this: unknown, cb: () => void): void;
@@ -47,17 +124,103 @@ export function nextTick<T extends Array<unknown>>(
}
}
-/** https://nodejs.org/api/process.html#process_process */
-// @deprecated `import { process } from 'process'` for backwards compatibility with old deno versions
-export const process = {
- arch,
- chdir,
- cwd,
- exit,
- pid,
- platform,
- version,
- versions,
+/** https://nodejs.org/api/process.html#process_process_pid */
+export const pid = Deno.pid;
+
+/** https://nodejs.org/api/process.html#process_process_platform */
+export const platform = Deno.build.os === "windows" ? "win32" : Deno.build.os;
+
+/** https://nodejs.org/api/process.html#process_process_version */
+export const version = `v${Deno.version.deno}`;
+
+/** https://nodejs.org/api/process.html#process_process_versions */
+export const versions = {
+ node: Deno.version.deno,
+ ...Deno.version,
+};
+
+class Process extends EventEmitter {
+ constructor() {
+ super();
+
+ //This causes the exit event to be binded to the unload event
+ window.addEventListener("unload", () => {
+ //TODO(Soremwar)
+ //Get the exit code from the unload event
+ super.emit("exit", 0);
+ });
+ }
+
+ /** https://nodejs.org/api/process.html#process_process_arch */
+ arch = arch;
+
+ /**
+ * https://nodejs.org/api/process.html#process_process_argv
+ * Read permissions are required in order to get the executable route
+ * */
+ argv = argv;
+
+ /** https://nodejs.org/api/process.html#process_process_chdir_directory */
+ chdir = chdir;
+
+ /** https://nodejs.org/api/process.html#process_process_cwd */
+ cwd = cwd;
+
+ /** https://nodejs.org/api/process.html#process_process_exit_code */
+ exit = exit;
+
+ /**
+ * https://nodejs.org/api/process.html#process_process_env
+ * Requires env permissions
+ * */
+ env = env;
+
+ /** https://nodejs.org/api/process.html#process_process_nexttick_callback_args */
+ nextTick = nextTick;
+
+ /** https://nodejs.org/api/process.html#process_process_events */
+ //deno-lint-ignore ban-types
+ on(event: typeof notImplementedEvents[number], listener: Function): never;
+ on(event: "exit", listener: (code: number) => void): this;
+ //deno-lint-ignore no-explicit-any
+ on(event: string, listener: (...args: any[]) => void): this {
+ if (notImplementedEvents.includes(event)) {
+ notImplemented();
+ }
+
+ super.on(event, listener);
+
+ return this;
+ }
+
+ /** https://nodejs.org/api/process.html#process_process_pid */
+ pid = pid;
+
+ /** https://nodejs.org/api/process.html#process_process_platform */
+ platform = platform;
+
+ removeAllListeners(_event: string): never {
+ notImplemented();
+ }
+
+ removeListener(
+ event: typeof notImplementedEvents[number],
+ //deno-lint-ignore ban-types
+ listener: Function,
+ ): never;
+ removeListener(event: "exit", listener: (code: number) => void): this;
+ //deno-lint-ignore no-explicit-any
+ removeListener(event: string, listener: (...args: any[]) => void): this {
+ if (notImplementedEvents.includes(event)) {
+ notImplemented();
+ }
+
+ super.removeListener("exit", listener);
+
+ return this;
+ }
+
+ /** https://nodejs.org/api/process.html#process_process_stderr */
get stderr() {
return {
fd: Deno.stderr.rid,
@@ -79,7 +242,9 @@ export const process = {
notImplemented();
},
};
- },
+ }
+
+ /** https://nodejs.org/api/process.html#process_process_stdin */
get stdin() {
return {
fd: Deno.stdin.rid,
@@ -96,7 +261,9 @@ export const process = {
notImplemented();
},
};
- },
+ }
+
+ /** https://nodejs.org/api/process.html#process_process_stdout */
get stdout() {
return {
fd: Deno.stdout.rid,
@@ -118,49 +285,17 @@ export const process = {
notImplemented();
},
};
- },
-
- /** https://nodejs.org/api/process.html#process_process_events */
- // on is not exported by node, it is only available within process:
- // node --input-type=module -e "import { on } from 'process'; console.log(on)"
- // deno-lint-ignore ban-types
- on(_event: string, _callback: Function): void {
- // TODO(rsp): to be implemented
- notImplemented();
- },
-
- /** https://nodejs.org/api/process.html#process_process_argv */
- get argv(): string[] {
- // Getter delegates --allow-env and --allow-read until request
- // Getter also allows the export Proxy instance to function as intended
- return [Deno.execPath(), ...Deno.args];
- },
-
- /** https://nodejs.org/api/process.html#process_process_env */
- get env(): { [index: string]: string } {
- // Getter delegates --allow-env and --allow-read until request
- // Getter also allows the export Proxy instance to function as intended
- return Deno.env.toObject();
- },
- nextTick,
-};
+ }
-/**
- * https://nodejs.org/api/process.html#process_process_argv
- * @example `import { argv } from './std/node/process.ts'; console.log(argv)`
- */
-// Proxy delegates --allow-env and --allow-read to request time, even for exports
-export const argv = new Proxy(process.argv, {});
+ /** https://nodejs.org/api/process.html#process_process_version */
+ version = version;
-/**
- * https://nodejs.org/api/process.html#process_process_env
- * @example `import { env } from './std/node/process.ts'; console.log(env)`
- */
-// Proxy delegates --allow-env and --allow-read to request time, even for exports
-export const env = new Proxy(process.env, {});
+ /** https://nodejs.org/api/process.html#process_process_versions */
+ versions = versions;
+}
-// import process from './std/node/process.ts'
-export default process;
+/** https://nodejs.org/api/process.html#process_process */
+const process = new Process();
Object.defineProperty(process, Symbol.toStringTag, {
enumerable: false,
@@ -168,3 +303,16 @@ Object.defineProperty(process, Symbol.toStringTag, {
configurable: false,
value: "process",
});
+
+export const removeListener = process.removeListener;
+export const removeAllListeners = process.removeAllListeners;
+export const stderr = process.stderr;
+export const stdin = process.stdin;
+export const stdout = process.stdout;
+
+export default process;
+
+//TODO(Soremwar)
+//Remove on 1.0
+//Kept for backwars compatibility with std
+export { process };
diff --git a/std/node/process_exit_test.ts b/std/node/process_exit_test.ts
new file mode 100644
index 000000000..54c8bcc01
--- /dev/null
+++ b/std/node/process_exit_test.ts
@@ -0,0 +1,19 @@
+import "./global.ts";
+
+//deno-lint-ignore no-undef
+process.on("exit", () => {
+ console.log(1);
+});
+
+function unexpected() {
+ console.log(null);
+}
+//deno-lint-ignore no-undef
+process.on("exit", unexpected);
+//deno-lint-ignore no-undef
+process.removeListener("exit", unexpected);
+
+//deno-lint-ignore no-undef
+process.on("exit", () => {
+ console.log(2);
+});
diff --git a/std/node/process_test.ts b/std/node/process_test.ts
index 9b2c050a7..22cebf260 100644
--- a/std/node/process_test.ts
+++ b/std/node/process_test.ts
@@ -1,37 +1,12 @@
// deno-lint-ignore-file no-undef
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+import "./global.ts";
import { assert, assertEquals, assertThrows } from "../testing/asserts.ts";
+import { stripColor } from "../fmt/colors.ts";
import * as path from "../path/mod.ts";
-import * as all from "./process.ts";
-import { argv, env } from "./process.ts";
import { delay } from "../async/delay.ts";
-import "./global.ts";
-
-// NOTE: Deno.execPath() (and thus process.argv) currently requires --allow-env
-// (Also Deno.env.toObject() (and process.env) requires --allow-env but it's more obvious)
-
-Deno.test({
- name: "process exports are as they should be",
- fn() {
- // * should be the same as process, default, and globalThis.process
- // without the export aliases, and with properties that are not standalone
- const allKeys = new Set<string>(Object.keys(all));
- // without { process } for deno b/c
- allKeys.delete("process");
- // without esm default
- allKeys.delete("default");
- // with on, stdin, stderr, and stdout, which is not exported via *
- allKeys.add("on");
- allKeys.add("stdin");
- allKeys.add("stderr");
- allKeys.add("stdout");
- const allStr = Array.from(allKeys).sort().join(" ");
- assertEquals(Object.keys(all.default).sort().join(" "), allStr);
- assertEquals(Object.keys(all.process).sort().join(" "), allStr);
- assertEquals(Object.keys(process).sort().join(" "), allStr);
- },
-});
+import { env } from "./process.ts";
Deno.test({
name: "process.cwd and process.chdir success",
@@ -103,7 +78,7 @@ Deno.test({
Deno.test({
name: "process.on",
- fn() {
+ async fn() {
assertEquals(typeof process.on, "function");
assertThrows(
() => {
@@ -112,6 +87,33 @@ Deno.test({
Error,
"implemented",
);
+
+ let triggered = false;
+ process.on("exit", () => {
+ triggered = true;
+ });
+ process.emit("exit");
+ assert(triggered);
+
+ const cwd = path.dirname(path.fromFileUrl(import.meta.url));
+
+ const p = Deno.run({
+ cmd: [
+ Deno.execPath(),
+ "run",
+ "./process_exit_test.ts",
+ ],
+ cwd,
+ stdout: "piped",
+ });
+
+ const decoder = new TextDecoder();
+ const rawOutput = await p.output();
+ assertEquals(
+ stripColor(decoder.decode(rawOutput).trim()),
+ "1\n2",
+ );
+ p.close();
},
});
@@ -119,12 +121,14 @@ Deno.test({
name: "process.argv",
fn() {
assert(Array.isArray(process.argv));
- assert(Array.isArray(argv));
assert(
process.argv[0].match(/[^/\\]*deno[^/\\]*$/),
"deno included in the file name of argv[0]",
);
- // we cannot test for anything else (we see test runner arguments here)
+ assertEquals(
+ process.argv[1],
+ path.fromFileUrl(Deno.mainModule),
+ );
},
});
@@ -136,9 +140,8 @@ Deno.test({
assertEquals(typeof (process.env.HELLO), "string");
assertEquals(process.env.HELLO, "WORLD");
- // TODO(caspervonb) test the globals in a different setting (they're broken)
- // assertEquals(typeof env.HELLO, "string");
- // assertEquals(env.HELLO, "WORLD");
+ assertEquals(typeof env.HELLO, "string");
+ assertEquals(env.HELLO, "WORLD");
},
});