summaryrefslogtreecommitdiff
path: root/std/node
diff options
context:
space:
mode:
Diffstat (limited to 'std/node')
-rw-r--r--std/node/README.md7
-rw-r--r--std/node/_utils.ts35
-rw-r--r--std/node/fs.ts68
-rw-r--r--std/node/fs_test.ts47
-rw-r--r--std/node/testdata/hello.txt1
-rw-r--r--std/node/util.ts47
-rw-r--r--std/node/util_test.ts122
7 files changed, 327 insertions, 0 deletions
diff --git a/std/node/README.md b/std/node/README.md
new file mode 100644
index 000000000..6d363a11d
--- /dev/null
+++ b/std/node/README.md
@@ -0,0 +1,7 @@
+# Deno Node compatibility
+
+This module is meant to have a compatibility layer for the
+[nodeJS standard library](https://nodejs.org/docs/latest-v12.x/api/).
+
+**Warning** : Any function of this module should not be referred anywhere in the
+deno standard library as it's a compatiblity module.
diff --git a/std/node/_utils.ts b/std/node/_utils.ts
new file mode 100644
index 000000000..f0808e82b
--- /dev/null
+++ b/std/node/_utils.ts
@@ -0,0 +1,35 @@
+export function notImplemented(msg?: string): never {
+ const message = msg ? `Not implemented: ${msg}` : "Not implemented";
+ throw new Error(message);
+}
+
+// API helpers
+
+export type MaybeNull<T> = T | null;
+export type MaybeDefined<T> = T | undefined;
+export type MaybeEmpty<T> = T | null | undefined;
+
+export function intoCallbackAPI<T>(
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
+ func: (...args: any[]) => Promise<T>,
+ cb: MaybeEmpty<(err: MaybeNull<Error>, value: MaybeEmpty<T>) => void>,
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
+ ...args: any[]
+): void {
+ func(...args)
+ .then(value => cb && cb(null, value))
+ .catch(err => cb && cb(err, null));
+}
+
+export function intoCallbackAPIWithIntercept<T1, T2>(
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
+ func: (...args: any[]) => Promise<T1>,
+ interceptor: (v: T1) => T2,
+ cb: MaybeEmpty<(err: MaybeNull<Error>, value: MaybeEmpty<T2>) => void>,
+ /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
+ ...args: any[]
+): void {
+ func(...args)
+ .then(value => cb && cb(null, interceptor(value)))
+ .catch(err => cb && cb(err, null));
+}
diff --git a/std/node/fs.ts b/std/node/fs.ts
new file mode 100644
index 000000000..01b9e2398
--- /dev/null
+++ b/std/node/fs.ts
@@ -0,0 +1,68 @@
+import { notImplemented, intoCallbackAPIWithIntercept } from "./_utils.ts";
+const { readFile: denoReadFile, readFileSync: denoReadFileSync } = Deno;
+
+type ReadFileCallback = (err: Error | null, data: string | Uint8Array) => void;
+
+interface ReadFileOptions {
+ encoding?: string | null;
+ flag?: string;
+}
+
+function getEncoding(
+ optOrCallback?: ReadFileOptions | ReadFileCallback
+): string | null {
+ if (!optOrCallback || typeof optOrCallback === "function") {
+ return null;
+ } else {
+ if (optOrCallback.encoding) {
+ if (
+ optOrCallback.encoding === "utf8" ||
+ optOrCallback.encoding === "utf-8"
+ ) {
+ return "utf8";
+ } else {
+ notImplemented();
+ }
+ }
+ return null;
+ }
+}
+
+function maybeDecode(
+ data: Uint8Array,
+ encoding: string | null
+): string | Uint8Array {
+ if (encoding === "utf8") {
+ return new TextDecoder().decode(data);
+ }
+ return data;
+}
+
+export function readFile(
+ path: string,
+ optOrCallback: ReadFileCallback | ReadFileOptions,
+ callback?: ReadFileCallback
+): void {
+ let cb: ReadFileCallback | undefined;
+ if (typeof optOrCallback === "function") {
+ cb = optOrCallback;
+ } else {
+ cb = callback;
+ }
+
+ const encoding = getEncoding(optOrCallback);
+
+ intoCallbackAPIWithIntercept<Uint8Array, string | Uint8Array>(
+ denoReadFile,
+ (data: Uint8Array): string | Uint8Array => maybeDecode(data, encoding),
+ cb,
+ path
+ );
+}
+
+export function readFileSync(
+ path: string,
+ opt?: ReadFileOptions
+): string | Uint8Array {
+ return maybeDecode(denoReadFileSync(path), getEncoding(opt));
+}
diff --git a/std/node/fs_test.ts b/std/node/fs_test.ts
new file mode 100644
index 000000000..81b9594f1
--- /dev/null
+++ b/std/node/fs_test.ts
@@ -0,0 +1,47 @@
+import { readFile, readFileSync } from "./fs.ts";
+import { test } from "../testing/mod.ts";
+import * as path from "../path/mod.ts";
+import { assertEquals, assert } from "../testing/asserts.ts";
+
+const testData = path.resolve(path.join("node", "testdata", "hello.txt"));
+
+// Need to convert to promises, otherwise test() won't report error correctly.
+test(async function readFileSuccess() {
+ const data = await new Promise((res, rej) => {
+ readFile(testData, (err, data) => {
+ if (err) {
+ rej(err);
+ }
+ res(data);
+ });
+ });
+
+ assert(data instanceof Uint8Array);
+ assertEquals(new TextDecoder().decode(data as Uint8Array), "hello world");
+});
+
+test(async function readFileEncodeUtf8Success() {
+ const data = await new Promise((res, rej) => {
+ readFile(testData, { encoding: "utf8" }, (err, data) => {
+ if (err) {
+ rej(err);
+ }
+ res(data);
+ });
+ });
+
+ assertEquals(typeof data, "string");
+ assertEquals(data as string, "hello world");
+});
+
+test(function readFileSyncSuccess() {
+ const data = readFileSync(testData);
+ assert(data instanceof Uint8Array);
+ assertEquals(new TextDecoder().decode(data as Uint8Array), "hello world");
+});
+
+test(function readFileEncodeUtf8Success() {
+ const data = readFileSync(testData, { encoding: "utf8" });
+ assertEquals(typeof data, "string");
+ assertEquals(data as string, "hello world");
+});
diff --git a/std/node/testdata/hello.txt b/std/node/testdata/hello.txt
new file mode 100644
index 000000000..95d09f2b1
--- /dev/null
+++ b/std/node/testdata/hello.txt
@@ -0,0 +1 @@
+hello world \ No newline at end of file
diff --git a/std/node/util.ts b/std/node/util.ts
new file mode 100644
index 000000000..d0187e616
--- /dev/null
+++ b/std/node/util.ts
@@ -0,0 +1,47 @@
+export function isArray(value: unknown): boolean {
+ return Array.isArray(value);
+}
+
+export function isBoolean(value: unknown): boolean {
+ return typeof value === "boolean" || value instanceof Boolean;
+}
+
+export function isNull(value: unknown): boolean {
+ return value === null;
+}
+
+export function isNullOrUndefined(value: unknown): boolean {
+ return value === null || value === undefined;
+}
+
+export function isNumber(value: unknown): boolean {
+ return typeof value === "number" || value instanceof Number;
+}
+
+export function isString(value: unknown): boolean {
+ return typeof value === "string" || value instanceof String;
+}
+
+export function isSymbol(value: unknown): boolean {
+ return typeof value === "symbol";
+}
+
+export function isUndefined(value: unknown): boolean {
+ return value === undefined;
+}
+
+export function isObject(value: unknown): boolean {
+ return value !== null && typeof value === "object";
+}
+
+export function isError(e: unknown): boolean {
+ return e instanceof Error;
+}
+
+export function isFunction(value: unknown): boolean {
+ return typeof value === "function";
+}
+
+export function isRegExp(value: unknown): boolean {
+ return value instanceof RegExp;
+}
diff --git a/std/node/util_test.ts b/std/node/util_test.ts
new file mode 100644
index 000000000..19512bedb
--- /dev/null
+++ b/std/node/util_test.ts
@@ -0,0 +1,122 @@
+import { test } from "../testing/mod.ts";
+import { assert } from "../testing/asserts.ts";
+import * as util from "./util.ts";
+
+test({
+ name: "[util] isBoolean",
+ fn() {
+ assert(util.isBoolean(true));
+ assert(util.isBoolean(new Boolean()));
+ assert(util.isBoolean(new Boolean(true)));
+ assert(util.isBoolean(false));
+ assert(!util.isBoolean("deno"));
+ assert(!util.isBoolean("true"));
+ }
+});
+
+test({
+ name: "[util] isNull",
+ fn() {
+ let n;
+ assert(util.isNull(null));
+ assert(!util.isNull(n));
+ assert(!util.isNull(0));
+ assert(!util.isNull({}));
+ }
+});
+
+test({
+ name: "[util] isNullOrUndefined",
+ fn() {
+ let n;
+ assert(util.isNullOrUndefined(null));
+ assert(util.isNullOrUndefined(n));
+ assert(!util.isNullOrUndefined({}));
+ assert(!util.isNullOrUndefined("undefined"));
+ }
+});
+
+test({
+ name: "[util] isNumber",
+ fn() {
+ assert(util.isNumber(666));
+ assert(util.isNumber(new Number(666)));
+ assert(!util.isNumber("999"));
+ assert(!util.isNumber(null));
+ }
+});
+
+test({
+ name: "[util] isString",
+ fn() {
+ assert(util.isString("deno"));
+ assert(util.isString(new String("DIO")));
+ assert(!util.isString(1337));
+ }
+});
+
+test({
+ name: "[util] isSymbol",
+ fn() {}
+});
+
+test({
+ name: "[util] isUndefined",
+ fn() {
+ let t;
+ assert(util.isUndefined(t));
+ assert(!util.isUndefined("undefined"));
+ assert(!util.isUndefined({}));
+ }
+});
+
+test({
+ name: "[util] isObject",
+ fn() {
+ const dio = { stand: "Za Warudo" };
+ assert(util.isObject(dio));
+ assert(util.isObject(new RegExp(/Toki Wo Tomare/)));
+ assert(!util.isObject("Jotaro"));
+ }
+});
+
+test({
+ name: "[util] isError",
+ fn() {
+ const java = new Error();
+ const nodejs = new TypeError();
+ const deno = "Future";
+ assert(util.isError(java));
+ assert(util.isError(nodejs));
+ assert(!util.isError(deno));
+ }
+});
+
+test({
+ name: "[util] isFunction",
+ fn() {
+ const f = function(): void {};
+ assert(util.isFunction(f));
+ assert(!util.isFunction({}));
+ assert(!util.isFunction(new RegExp(/f/)));
+ }
+});
+
+test({
+ name: "[util] isRegExp",
+ fn() {
+ assert(util.isRegExp(new RegExp(/f/)));
+ assert(util.isRegExp(/fuManchu/));
+ assert(!util.isRegExp({ evil: "eye" }));
+ assert(!util.isRegExp(null));
+ }
+});
+
+test({
+ name: "[util] isArray",
+ fn() {
+ assert(util.isArray([]));
+ assert(!util.isArray({ yaNo: "array" }));
+ assert(!util.isArray(null));
+ }
+});