summaryrefslogtreecommitdiff
path: root/ext/node
diff options
context:
space:
mode:
authorLuca Casonato <hello@lcas.dev>2024-09-05 09:22:52 +0200
committerGitHub <noreply@github.com>2024-09-05 09:22:52 +0200
commit49e3ee010c7d4423fbab89bf12749235e156c9be (patch)
tree92185d6cf32a94ae4b241a23a64309bec3f92e59 /ext/node
parent17b5e98b822dc23407a0292dcf61e624fbf2a4b1 (diff)
feat(ext/node): add abort helpers, process & streams fix (#25262)
This commit adds: - `addAbortListener` in `node:events` - `aborted` in `node:util` - `execPath` and `execvArgs` named export from `node:process` - `getDefaultHighWaterMark` from `node:stream` The `execPath` is very hacky - because module namespaces can not have real getters, `execPath` is an object with a `toString()` method that on call returns the actual `execPath`, and replaces the `execPath` binding with the string. This is done so that we don't require the `execPath` permission on startup.
Diffstat (limited to 'ext/node')
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/polyfills/_events.mjs2
-rw-r--r--ext/node/polyfills/events.ts1
-rw-r--r--ext/node/polyfills/internal/events/abort_listener.mjs44
-rw-r--r--ext/node/polyfills/process.ts25
-rw-r--r--ext/node/polyfills/stream.ts4
-rw-r--r--ext/node/polyfills/util.ts32
7 files changed, 100 insertions, 9 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 17fd7ab5a..39b06e9fd 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -502,6 +502,7 @@ deno_core::extension!(deno_node,
"internal/error_codes.ts",
"internal/errors.ts",
"internal/event_target.mjs",
+ "internal/events/abort_listener.mjs",
"internal/fixed_queue.ts",
"internal/fs/streams.mjs",
"internal/fs/utils.mjs",
diff --git a/ext/node/polyfills/_events.mjs b/ext/node/polyfills/_events.mjs
index 12b0935e6..ce7a8ebf2 100644
--- a/ext/node/polyfills/_events.mjs
+++ b/ext/node/polyfills/_events.mjs
@@ -47,6 +47,8 @@ import {
import { spliceOne } from "ext:deno_node/_utils.ts";
import { nextTick } from "ext:deno_node/_process/process.ts";
+export { addAbortListener } from "./internal/events/abort_listener.mjs";
+
const kCapture = Symbol("kCapture");
const kErrorMonitor = Symbol("events.errorMonitor");
const kMaxEventTargetListeners = Symbol("events.maxEventTargetListeners");
diff --git a/ext/node/polyfills/events.ts b/ext/node/polyfills/events.ts
index 3b7381410..78f3d8768 100644
--- a/ext/node/polyfills/events.ts
+++ b/ext/node/polyfills/events.ts
@@ -1,6 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// @deno-types="./_events.d.ts"
export {
+ addAbortListener,
captureRejectionSymbol,
default,
defaultMaxListeners,
diff --git a/ext/node/polyfills/internal/events/abort_listener.mjs b/ext/node/polyfills/internal/events/abort_listener.mjs
new file mode 100644
index 000000000..f1430489f
--- /dev/null
+++ b/ext/node/polyfills/internal/events/abort_listener.mjs
@@ -0,0 +1,44 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
+
+import { primordials } from "ext:deno_node/internal/test/binding.ts";
+const { queueMicrotask } = primordials;
+import { SymbolDispose } from "ext:deno_web/00_infra.js";
+import * as abortSignal from "ext:deno_web/03_abort_signal.js";
+import { validateAbortSignal, validateFunction } from "../validators.mjs";
+import { codes } from "../errors.ts";
+const { ERR_INVALID_ARG_TYPE } = codes;
+
+/**
+ * @param {AbortSignal} signal
+ * @param {EventListener} listener
+ * @returns {Disposable}
+ */
+function addAbortListener(signal, listener) {
+ if (signal === undefined) {
+ throw new ERR_INVALID_ARG_TYPE("signal", "AbortSignal", signal);
+ }
+ validateAbortSignal(signal, "signal");
+ validateFunction(listener, "listener");
+
+ let removeEventListener;
+ if (signal.aborted) {
+ queueMicrotask(() => listener());
+ } else {
+ signal[abortSignal.add](() => {
+ removeEventListener?.();
+ listener();
+ });
+ removeEventListener = () => {
+ signal[abortSignal.remove](listener);
+ };
+ }
+ return {
+ __proto__: null,
+ [SymbolDispose]() {
+ removeEventListener?.();
+ },
+ };
+}
+
+export { addAbortListener };
diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts
index 2130087fb..f09d7ceed 100644
--- a/ext/node/polyfills/process.ts
+++ b/ext/node/polyfills/process.ts
@@ -82,6 +82,8 @@ export const argv: string[] = ["", ""];
// And retains any value as long as it's nullish or number-ish.
let ProcessExitCode: undefined | null | string | number;
+export const execArgv: string[] = [];
+
/** https://nodejs.org/api/process.html#process_process_exit_code */
export const exit = (code?: number | string) => {
if (code || code === 0) {
@@ -337,7 +339,20 @@ function uncaughtExceptionHandler(err: any, origin: string) {
process.emit("uncaughtException", err, origin);
}
-let execPath: string | null = null;
+export let execPath: string = Object.freeze({
+ __proto__: String.prototype,
+ toString() {
+ execPath = Deno.execPath();
+ return execPath;
+ },
+ get length() {
+ return this.toString().length;
+ },
+ [Symbol.for("Deno.customInspect")](inspect, options) {
+ return inspect(this.toString(), options);
+ },
+ // deno-lint-ignore no-explicit-any
+}) as any as string;
// The process class needs to be an ES5 class because it can be instantiated
// in Node without the `new` keyword. It's not a true class in Node. Popular
@@ -425,7 +440,7 @@ Process.prototype.cwd = cwd;
Process.prototype.env = env;
/** https://nodejs.org/api/process.html#process_process_execargv */
-Process.prototype.execArgv = [];
+Process.prototype.execArgv = execArgv;
/** https://nodejs.org/api/process.html#process_process_exit_code */
Process.prototype.exit = exit;
@@ -704,11 +719,7 @@ Process.prototype._eval = undefined;
Object.defineProperty(Process.prototype, "execPath", {
get() {
- if (execPath) {
- return execPath;
- }
- execPath = Deno.execPath();
- return execPath;
+ return String(execPath);
},
set(path: string) {
execPath = path;
diff --git a/ext/node/polyfills/stream.ts b/ext/node/polyfills/stream.ts
index 42f83fa28..618e5ba11 100644
--- a/ext/node/polyfills/stream.ts
+++ b/ext/node/polyfills/stream.ts
@@ -19,6 +19,9 @@ import {
Transform,
Writable,
} from "ext:deno_node/_stream.mjs";
+import {
+ getDefaultHighWaterMark,
+} from "ext:deno_node/internal/streams/state.mjs";
export {
_isUint8Array,
@@ -26,6 +29,7 @@ export {
addAbortSignal,
Duplex,
finished,
+ getDefaultHighWaterMark,
PassThrough,
pipeline,
Readable,
diff --git a/ext/node/polyfills/util.ts b/ext/node/polyfills/util.ts
index cb4e6498a..c94d0f14b 100644
--- a/ext/node/polyfills/util.ts
+++ b/ext/node/polyfills/util.ts
@@ -25,9 +25,13 @@ const {
StringPrototypeIsWellFormed,
StringPrototypePadStart,
StringPrototypeToWellFormed,
+ PromiseResolve,
} = primordials;
-import { promisify } from "ext:deno_node/internal/util.mjs";
+import {
+ createDeferredPromise,
+ promisify,
+} from "ext:deno_node/internal/util.mjs";
import { callbackify } from "ext:deno_node/_util/_util_callbackify.js";
import { debuglog } from "ext:deno_node/internal/util/debuglog.ts";
import {
@@ -41,8 +45,13 @@ import types from "node:util/types";
import { Buffer } from "node:buffer";
import { isDeepStrictEqual } from "ext:deno_node/internal/util/comparisons.ts";
import process from "node:process";
-import { validateString } from "ext:deno_node/internal/validators.mjs";
+import {
+ validateAbortSignal,
+ validateString,
+} from "ext:deno_node/internal/validators.mjs";
import { parseArgs } from "ext:deno_node/internal/util/parse_args/parse_args.js";
+import * as abortSignal from "ext:deno_web/03_abort_signal.js";
+import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
export {
callbackify,
@@ -288,6 +297,24 @@ export function deprecate(fn: any, msg: string, code?: any) {
return deprecated;
}
+// deno-lint-ignore require-await
+export async function aborted(
+ signal: AbortSignal,
+ // deno-lint-ignore no-explicit-any
+ _resource: any,
+): Promise<void> {
+ if (signal === undefined) {
+ throw new ERR_INVALID_ARG_TYPE("signal", "AbortSignal", signal);
+ }
+ validateAbortSignal(signal, "signal");
+ if (signal.aborted) {
+ return PromiseResolve();
+ }
+ const abortPromise = createDeferredPromise();
+ signal[abortSignal.add](abortPromise.resolve);
+ return abortPromise.promise;
+}
+
export { getSystemErrorName, isDeepStrictEqual };
export default {
@@ -311,6 +338,7 @@ export default {
isBuffer,
_extend,
getSystemErrorName,
+ aborted,
deprecate,
callbackify,
parseArgs,