summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/_process
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-02-14 17:38:45 +0100
committerGitHub <noreply@github.com>2023-02-14 17:38:45 +0100
commitd47147fb6ad229b1c039aff9d0959b6e281f4df5 (patch)
tree6e9e790f2b9bc71b5f0c9c7e64b95cae31579d58 /ext/node/polyfills/_process
parent1d00bbe47e2ca14e2d2151518e02b2324461a065 (diff)
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is transpiled and snapshotted during the build process. During the first pass a minimal amount of work was done to create the snapshot, a lot of code in "ext/node" depends on presence of "Deno" global. This code will be gradually fixed in the follow up PRs to migrate it to import relevant APIs from "internal:" modules. Currently the code from snapshot is not used in any way, and all Node/npm compatibility still uses code from "https://deno.land/std/node" (or from the location specified by "DENO_NODE_COMPAT_URL"). This will also be handled in a follow up PRs. --------- Co-authored-by: crowlkats <crowlkats@toaxl.com> Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com> Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
Diffstat (limited to 'ext/node/polyfills/_process')
-rw-r--r--ext/node/polyfills/_process/exiting.ts4
-rw-r--r--ext/node/polyfills/_process/process.ts129
-rw-r--r--ext/node/polyfills/_process/stdio.mjs7
-rw-r--r--ext/node/polyfills/_process/streams.mjs246
4 files changed, 386 insertions, 0 deletions
diff --git a/ext/node/polyfills/_process/exiting.ts b/ext/node/polyfills/_process/exiting.ts
new file mode 100644
index 000000000..8cc37eef8
--- /dev/null
+++ b/ext/node/polyfills/_process/exiting.ts
@@ -0,0 +1,4 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// deno-lint-ignore prefer-const
+export let _exiting = false;
diff --git a/ext/node/polyfills/_process/process.ts b/ext/node/polyfills/_process/process.ts
new file mode 100644
index 000000000..7ba44e431
--- /dev/null
+++ b/ext/node/polyfills/_process/process.ts
@@ -0,0 +1,129 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
+
+// The following are all the process APIs that don't depend on the stream module
+// They have to be split this way to prevent a circular dependency
+
+import { build } from "internal:runtime/js/01_build.js";
+import { nextTick as _nextTick } from "internal:deno_node/polyfills/_next_tick.ts";
+import { _exiting } from "internal:deno_node/polyfills/_process/exiting.ts";
+
+/** Returns the operating system CPU architecture for which the Deno binary was compiled */
+export function arch(): string {
+ if (build.arch == "x86_64") {
+ return "x64";
+ } else if (build.arch == "aarch64") {
+ return "arm64";
+ } else {
+ throw Error("unreachable");
+ }
+}
+
+/** 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_nexttick_callback_args */
+export const nextTick = _nextTick;
+
+/** Wrapper of Deno.env.get, which doesn't throw type error when
+ * the env name has "=" or "\0" in it. */
+function denoEnvGet(name: string) {
+ const perm =
+ Deno.permissions.querySync?.({ name: "env", variable: name }).state ??
+ "granted"; // for Deno Deploy
+ // Returns undefined if the env permission is unavailable
+ if (perm !== "granted") {
+ return undefined;
+ }
+ try {
+ return Deno.env.get(name);
+ } catch (e) {
+ if (e instanceof TypeError) {
+ return undefined;
+ }
+ throw e;
+ }
+}
+
+const OBJECT_PROTO_PROP_NAMES = Object.getOwnPropertyNames(Object.prototype);
+/**
+ * https://nodejs.org/api/process.html#process_process_env
+ * Requires env permissions
+ */
+export const env: InstanceType<ObjectConstructor> & Record<string, string> =
+ new Proxy(Object(), {
+ get: (target, prop) => {
+ if (typeof prop === "symbol") {
+ return target[prop];
+ }
+
+ const envValue = denoEnvGet(prop);
+
+ if (envValue) {
+ return envValue;
+ }
+
+ if (OBJECT_PROTO_PROP_NAMES.includes(prop)) {
+ return target[prop];
+ }
+
+ return envValue;
+ },
+ ownKeys: () => Reflect.ownKeys(Deno.env.toObject()),
+ getOwnPropertyDescriptor: (_target, name) => {
+ const value = denoEnvGet(String(name));
+ if (value) {
+ return {
+ enumerable: true,
+ configurable: true,
+ value,
+ };
+ }
+ },
+ set(_target, prop, value) {
+ Deno.env.set(String(prop), String(value));
+ return true; // success
+ },
+ has: (_target, prop) => typeof denoEnvGet(String(prop)) === "string",
+ });
+
+/**
+ * https://nodejs.org/api/process.html#process_process_version
+ *
+ * This value is hard coded to latest stable release of Node, as
+ * some packages are checking it for compatibility. Previously
+ * it pointed to Deno version, but that led to incompability
+ * with some packages.
+ */
+export const version = "v18.12.1";
+
+/**
+ * https://nodejs.org/api/process.html#process_process_versions
+ *
+ * This value is hard coded to latest stable release of Node, as
+ * some packages are checking it for compatibility. Previously
+ * it contained only output of `Deno.version`, but that led to incompability
+ * with some packages. Value of `v8` field is still taken from `Deno.version`.
+ */
+export const versions = {
+ node: "18.12.1",
+ uv: "1.43.0",
+ zlib: "1.2.11",
+ brotli: "1.0.9",
+ ares: "1.18.1",
+ modules: "108",
+ nghttp2: "1.47.0",
+ napi: "8",
+ llhttp: "6.0.10",
+ openssl: "3.0.7+quic",
+ cldr: "41.0",
+ icu: "71.1",
+ tz: "2022b",
+ unicode: "14.0",
+ ngtcp2: "0.8.1",
+ nghttp3: "0.7.0",
+ ...Deno.version,
+};
diff --git a/ext/node/polyfills/_process/stdio.mjs b/ext/node/polyfills/_process/stdio.mjs
new file mode 100644
index 000000000..4e0173dfa
--- /dev/null
+++ b/ext/node/polyfills/_process/stdio.mjs
@@ -0,0 +1,7 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
+
+// Lazily initializes the actual stdio objects.
+// This trick is necessary for avoiding circular dependencies between
+// stream and process modules.
+export const stdio = {};
diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs
new file mode 100644
index 000000000..30811e673
--- /dev/null
+++ b/ext/node/polyfills/_process/streams.mjs
@@ -0,0 +1,246 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license.
+
+import { Buffer } from "internal:deno_node/polyfills/buffer.ts";
+import {
+ clearLine,
+ clearScreenDown,
+ cursorTo,
+ moveCursor,
+} from "internal:deno_node/polyfills/internal/readline/callbacks.mjs";
+import { Duplex, Readable, Writable } from "internal:deno_node/polyfills/stream.ts";
+import { stdio } from "internal:deno_node/polyfills/_process/stdio.mjs";
+import { fs as fsConstants } from "internal:deno_node/polyfills/internal_binding/constants.ts";
+
+// https://github.com/nodejs/node/blob/00738314828074243c9a52a228ab4c68b04259ef/lib/internal/bootstrap/switches/is_main_thread.js#L41
+function createWritableStdioStream(writer, name) {
+ const stream = new Writable({
+ write(buf, enc, cb) {
+ if (!writer) {
+ this.destroy(
+ new Error(`Deno.${name} is not available in this environment`),
+ );
+ return;
+ }
+ writer.writeSync(buf instanceof Uint8Array ? buf : Buffer.from(buf, enc));
+ cb();
+ },
+ destroy(err, cb) {
+ cb(err);
+ this._undestroy();
+ if (!this._writableState.emitClose) {
+ nextTick(() => this.emit("close"));
+ }
+ },
+ });
+ stream.fd = writer?.rid ?? -1;
+ stream.destroySoon = stream.destroy;
+ stream._isStdio = true;
+ stream.once("close", () => writer?.close());
+ Object.defineProperties(stream, {
+ columns: {
+ enumerable: true,
+ configurable: true,
+ get: () =>
+ Deno.isatty?.(writer?.rid) ? Deno.consoleSize?.().columns : undefined,
+ },
+ rows: {
+ enumerable: true,
+ configurable: true,
+ get: () =>
+ Deno.isatty?.(writer?.rid) ? Deno.consoleSize?.().rows : undefined,
+ },
+ isTTY: {
+ enumerable: true,
+ configurable: true,
+ get: () => Deno.isatty?.(writer?.rid),
+ },
+ getWindowSize: {
+ enumerable: true,
+ configurable: true,
+ value: () =>
+ Deno.isatty?.(writer?.rid)
+ ? Object.values(Deno.consoleSize?.())
+ : undefined,
+ },
+ });
+
+ if (Deno.isatty?.(writer?.rid)) {
+ // These belong on tty.WriteStream(), but the TTY streams currently have
+ // following problems:
+ // 1. Using them here introduces a circular dependency.
+ // 2. Creating a net.Socket() from a fd is not currently supported.
+ stream.cursorTo = function (x, y, callback) {
+ return cursorTo(this, x, y, callback);
+ };
+
+ stream.moveCursor = function (dx, dy, callback) {
+ return moveCursor(this, dx, dy, callback);
+ };
+
+ stream.clearLine = function (dir, callback) {
+ return clearLine(this, dir, callback);
+ };
+
+ stream.clearScreenDown = function (callback) {
+ return clearScreenDown(this, callback);
+ };
+ }
+
+ return stream;
+}
+
+/** https://nodejs.org/api/process.html#process_process_stderr */
+export const stderr = stdio.stderr = createWritableStdioStream(
+ Deno.stderr,
+ "stderr",
+);
+
+/** https://nodejs.org/api/process.html#process_process_stdout */
+export const stdout = stdio.stdout = createWritableStdioStream(
+ Deno.stdout,
+ "stdout",
+);
+
+// TODO(PolarETech): This function should be replaced by
+// `guessHandleType()` in "../internal_binding/util.ts".
+// https://github.com/nodejs/node/blob/v18.12.1/src/node_util.cc#L257
+function _guessStdinType(fd) {
+ if (typeof fd !== "number" || fd < 0) return "UNKNOWN";
+ if (Deno.isatty?.(fd)) return "TTY";
+
+ try {
+ const fileInfo = Deno.fstatSync?.(fd);
+
+ // https://github.com/nodejs/node/blob/v18.12.1/deps/uv/src/unix/tty.c#L333
+ if (Deno.build.os !== "windows") {
+ switch (fileInfo.mode & fsConstants.S_IFMT) {
+ case fsConstants.S_IFREG:
+ case fsConstants.S_IFCHR:
+ return "FILE";
+ case fsConstants.S_IFIFO:
+ return "PIPE";
+ case fsConstants.S_IFSOCK:
+ // TODO(PolarETech): Need a better way to identify "TCP".
+ // Currently, unable to exclude UDP.
+ return "TCP";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ // https://github.com/nodejs/node/blob/v18.12.1/deps/uv/src/win/handle.c#L31
+ if (fileInfo.isFile) {
+ // TODO(PolarETech): Need a better way to identify a piped stdin on Windows.
+ // On Windows, `Deno.fstatSync(rid).isFile` returns true even for a piped stdin.
+ // Therefore, a piped stdin cannot be distinguished from a file by this property.
+ // The mtime, atime, and birthtime of the file are "2339-01-01T00:00:00.000Z",
+ // so use the property as a workaround.
+ if (fileInfo.birthtime.valueOf() === 11644473600000) return "PIPE";
+ return "FILE";
+ }
+ } catch (e) {
+ // TODO(PolarETech): Need a better way to identify a character file on Windows.
+ // "EISDIR" error occurs when stdin is "null" on Windows,
+ // so use the error as a workaround.
+ if (Deno.build.os === "windows" && e.code === "EISDIR") return "FILE";
+ }
+
+ return "UNKNOWN";
+}
+
+const _read = function (size) {
+ const p = Buffer.alloc(size || 16 * 1024);
+ Deno.stdin?.read(p).then((length) => {
+ this.push(length === null ? null : p.slice(0, length));
+ }, (error) => {
+ this.destroy(error);
+ });
+};
+
+/** https://nodejs.org/api/process.html#process_process_stdin */
+// https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L189
+export const stdin = stdio.stdin = (() => {
+ const fd = Deno.stdin?.rid;
+ let _stdin;
+ const stdinType = _guessStdinType(fd);
+
+ switch (stdinType) {
+ case "FILE": {
+ // Since `fs.ReadStream` cannot be imported before process initialization,
+ // use `Readable` instead.
+ // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L200
+ // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/fs/streams.js#L148
+ _stdin = new Readable({
+ highWaterMark: 64 * 1024,
+ autoDestroy: false,
+ read: _read,
+ });
+ break;
+ }
+ case "TTY":
+ case "PIPE":
+ case "TCP": {
+ // TODO(PolarETech):
+ // For TTY, `new Duplex()` should be replaced `new tty.ReadStream()` if possible.
+ // There are two problems that need to be resolved.
+ // 1. Using them here introduces a circular dependency.
+ // 2. Creating a tty.ReadStream() is not currently supported.
+ // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L194
+ // https://github.com/nodejs/node/blob/v18.12.1/lib/tty.js#L47
+
+ // For PIPE and TCP, `new Duplex()` should be replaced `new net.Socket()` if possible.
+ // There are two problems that need to be resolved.
+ // 1. Using them here introduces a circular dependency.
+ // 2. Creating a net.Socket() from a fd is not currently supported.
+ // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L206
+ // https://github.com/nodejs/node/blob/v18.12.1/lib/net.js#L329
+ _stdin = new Duplex({
+ readable: stdinType === "TTY" ? undefined : true,
+ writable: stdinType === "TTY" ? undefined : false,
+ readableHighWaterMark: stdinType === "TTY" ? 0 : undefined,
+ allowHalfOpen: false,
+ emitClose: false,
+ autoDestroy: true,
+ decodeStrings: false,
+ read: _read,
+ });
+
+ if (stdinType !== "TTY") {
+ // Make sure the stdin can't be `.end()`-ed
+ _stdin._writableState.ended = true;
+ }
+ break;
+ }
+ default: {
+ // Provide a dummy contentless input for e.g. non-console
+ // Windows applications.
+ _stdin = new Readable({ read() {} });
+ _stdin.push(null);
+ }
+ }
+
+ return _stdin;
+})();
+stdin.on("close", () => Deno.stdin?.close());
+stdin.fd = Deno.stdin?.rid ?? -1;
+Object.defineProperty(stdin, "isTTY", {
+ enumerable: true,
+ configurable: true,
+ get() {
+ return Deno.isatty?.(Deno.stdin.rid);
+ },
+});
+stdin._isRawMode = false;
+stdin.setRawMode = (enable) => {
+ Deno.stdin?.setRaw?.(enable);
+ stdin._isRawMode = enable;
+ return stdin;
+};
+Object.defineProperty(stdin, "isRaw", {
+ enumerable: true,
+ configurable: true,
+ get() {
+ return stdin._isRawMode;
+ },
+});