1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-22 06:09:25 -05:00
denoland-deno/ext/node/polyfills/_process/streams.mjs
Bartek Iwańczuk d47147fb6a
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>
2023-02-14 17:38:45 +01:00

246 lines
8 KiB
JavaScript

// 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;
},
});