mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 15:49:44 -05:00
feat(std/fs/node): adding some functions (#7921)
This commit is contained in:
parent
f75bd89aff
commit
5bed06fb94
17 changed files with 1403 additions and 0 deletions
59
std/node/_fs/_fs_lstat.ts
Normal file
59
std/node/_fs/_fs_lstat.ts
Normal file
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
BigIntStats,
|
||||
CFISBIS,
|
||||
statCallback,
|
||||
statCallbackBigInt,
|
||||
statOptions,
|
||||
Stats,
|
||||
} from "./_fs_stat.ts";
|
||||
|
||||
export function lstat(path: string | URL, callback: statCallback): void;
|
||||
export function lstat(
|
||||
path: string | URL,
|
||||
options: { bigint: false },
|
||||
callback: statCallback,
|
||||
): void;
|
||||
export function lstat(
|
||||
path: string | URL,
|
||||
options: { bigint: true },
|
||||
callback: statCallbackBigInt,
|
||||
): void;
|
||||
export function lstat(
|
||||
path: string | URL,
|
||||
optionsOrCallback: statCallback | statCallbackBigInt | statOptions,
|
||||
maybeCallback?: statCallback | statCallbackBigInt,
|
||||
) {
|
||||
const callback =
|
||||
(typeof optionsOrCallback === "function"
|
||||
? optionsOrCallback
|
||||
: maybeCallback) as (
|
||||
err: Error | undefined,
|
||||
stat: BigIntStats | Stats,
|
||||
) => void;
|
||||
const options = typeof optionsOrCallback === "object"
|
||||
? optionsOrCallback
|
||||
: { bigint: false };
|
||||
|
||||
if (!callback) throw new Error("No callback function supplied");
|
||||
|
||||
Deno.lstat(path)
|
||||
.then((stat) => callback(undefined, CFISBIS(stat, options.bigint)))
|
||||
.catch((err) => callback(err, err));
|
||||
}
|
||||
|
||||
export function lstatSync(path: string | URL): Stats;
|
||||
export function lstatSync(
|
||||
path: string | URL,
|
||||
options: { bigint: false },
|
||||
): Stats;
|
||||
export function lstatSync(
|
||||
path: string | URL,
|
||||
options: { bigint: true },
|
||||
): BigIntStats;
|
||||
export function lstatSync(
|
||||
path: string | URL,
|
||||
options?: statOptions,
|
||||
): Stats | BigIntStats {
|
||||
const origin = Deno.lstatSync(path);
|
||||
return CFISBIS(origin, options?.bigint || false);
|
||||
}
|
56
std/node/_fs/_fs_lstat_test.ts
Normal file
56
std/node/_fs/_fs_lstat_test.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import { lstat, lstatSync } from "./_fs_lstat.ts";
|
||||
import { fail } from "../../testing/asserts.ts";
|
||||
import { assertStats, assertStatsBigInt } from "./_fs_stat_test.ts";
|
||||
import type { BigIntStats, Stats } from "./_fs_stat.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: get a file Stats (lstat)",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
await new Promise<Stats>((resolve, reject) => {
|
||||
lstat(file, (err, stat) => {
|
||||
if (err) reject(err);
|
||||
resolve(stat);
|
||||
});
|
||||
})
|
||||
.then((stat) => {
|
||||
assertStats(stat, Deno.lstatSync(file));
|
||||
})
|
||||
.catch(() => fail())
|
||||
.finally(() => {
|
||||
Deno.removeSync(file);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: get a file Stats (lstat)",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
assertStats(lstatSync(file), Deno.lstatSync(file));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: get a file BigInt Stats (lstat)",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
await new Promise<BigIntStats>((resolve, reject) => {
|
||||
lstat(file, { bigint: true }, (err, stat) => {
|
||||
if (err) reject(err);
|
||||
resolve(stat);
|
||||
});
|
||||
})
|
||||
.then((stat) => assertStatsBigInt(stat, Deno.lstatSync(file)))
|
||||
.catch(() => fail())
|
||||
.finally(() => Deno.removeSync(file));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: BigInt Stats (lstat)",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
assertStatsBigInt(lstatSync(file, { bigint: true }), Deno.lstatSync(file));
|
||||
},
|
||||
});
|
103
std/node/_fs/_fs_open.ts
Normal file
103
std/node/_fs/_fs_open.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
import { existsSync } from "../../fs/mod.ts";
|
||||
import { fromFileUrl } from "../path.ts";
|
||||
import { getOpenOptions } from "./_fs_common.ts";
|
||||
|
||||
type openFlags =
|
||||
| "a"
|
||||
| "ax"
|
||||
| "a+"
|
||||
| "ax+"
|
||||
| "as"
|
||||
| "as+"
|
||||
| "r"
|
||||
| "r+"
|
||||
| "rs+"
|
||||
| "w"
|
||||
| "wx"
|
||||
| "w+"
|
||||
| "wx+";
|
||||
|
||||
type openCallback = (err: Error | undefined, fd: number) => void;
|
||||
|
||||
function convertFlagAndModeToOptions(
|
||||
flag?: openFlags,
|
||||
mode?: number,
|
||||
): Deno.OpenOptions | undefined {
|
||||
if (!flag && !mode) return undefined;
|
||||
if (!flag && mode) return { mode };
|
||||
return { ...getOpenOptions(flag), mode };
|
||||
}
|
||||
|
||||
export function open(path: string | URL, callback: openCallback): void;
|
||||
export function open(
|
||||
path: string | URL,
|
||||
flags: openFlags,
|
||||
callback: openCallback,
|
||||
): void;
|
||||
export function open(
|
||||
path: string | URL,
|
||||
flags: openFlags,
|
||||
mode: number,
|
||||
callback: openCallback,
|
||||
): void;
|
||||
export function open(
|
||||
path: string | URL,
|
||||
flagsOrCallback: openCallback | openFlags,
|
||||
callbackOrMode?: openCallback | number,
|
||||
maybeCallback?: openCallback,
|
||||
) {
|
||||
const flags = typeof flagsOrCallback === "string"
|
||||
? flagsOrCallback
|
||||
: undefined;
|
||||
const callback = typeof flagsOrCallback === "function"
|
||||
? flagsOrCallback
|
||||
: typeof callbackOrMode === "function"
|
||||
? callbackOrMode
|
||||
: maybeCallback;
|
||||
const mode = typeof callbackOrMode === "number" ? callbackOrMode : undefined;
|
||||
path = path instanceof URL ? fromFileUrl(path) : path;
|
||||
|
||||
if (!callback) throw new Error("No callback function supplied");
|
||||
|
||||
if (["ax", "ax+", "wx", "wx+"].includes(flags || "") && existsSync(path)) {
|
||||
const err = new Error(`EEXIST: file already exists, open '${path}'`);
|
||||
callback(err, 0);
|
||||
} else {
|
||||
if (flags === "as" || flags === "as+") {
|
||||
try {
|
||||
const res = openSync(path, flags, mode);
|
||||
callback(undefined, res);
|
||||
} catch (error) {
|
||||
callback(error, error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Deno.open(path, convertFlagAndModeToOptions(flags, mode))
|
||||
.then((file) => callback(undefined, file.rid))
|
||||
.catch((err) => callback(err, err));
|
||||
}
|
||||
}
|
||||
|
||||
export function openSync(path: string | URL): number;
|
||||
export function openSync(path: string | URL, flags?: openFlags): number;
|
||||
export function openSync(path: string | URL, mode?: number): number;
|
||||
export function openSync(
|
||||
path: string | URL,
|
||||
flags?: openFlags,
|
||||
mode?: number,
|
||||
): number;
|
||||
export function openSync(
|
||||
path: string | URL,
|
||||
flagsOrMode?: openFlags | number,
|
||||
maybeMode?: number,
|
||||
) {
|
||||
const flags = typeof flagsOrMode === "string" ? flagsOrMode : undefined;
|
||||
const mode = typeof flagsOrMode === "number" ? flagsOrMode : maybeMode;
|
||||
path = path instanceof URL ? fromFileUrl(path) : path;
|
||||
|
||||
if (["ax", "ax+", "wx", "wx+"].includes(flags || "") && existsSync(path)) {
|
||||
throw new Error(`EEXIST: file already exists, open '${path}'`);
|
||||
}
|
||||
|
||||
return Deno.openSync(path, convertFlagAndModeToOptions(flags, mode)).rid;
|
||||
}
|
209
std/node/_fs/_fs_open_test.ts
Normal file
209
std/node/_fs/_fs_open_test.ts
Normal file
|
@ -0,0 +1,209 @@
|
|||
import {
|
||||
assert,
|
||||
assertEquals,
|
||||
assertThrows,
|
||||
fail,
|
||||
} from "../../testing/asserts.ts";
|
||||
import { open, openSync } from "./_fs_open.ts";
|
||||
import { join, parse } from "../../path/mod.ts";
|
||||
import { existsSync } from "../../fs/mod.ts";
|
||||
import { closeSync } from "./_fs_close.ts";
|
||||
|
||||
const temp_dir = parse(Deno.makeTempFileSync()).dir;
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: open file",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
let fd1: number;
|
||||
await new Promise<number>((resolve, reject) => {
|
||||
open(file, (err, fd) => {
|
||||
if (err) reject(err);
|
||||
resolve(fd);
|
||||
});
|
||||
})
|
||||
.then((fd) => {
|
||||
fd1 = fd;
|
||||
assert(Deno.resources()[fd], `${fd}`);
|
||||
})
|
||||
.catch(() => fail())
|
||||
.finally(() => closeSync(fd1));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: open file",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
const fd = openSync(file);
|
||||
assert(Deno.resources()[fd]);
|
||||
closeSync(fd);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'a'",
|
||||
fn() {
|
||||
const file = join(temp_dir, "some_random_file");
|
||||
const fd = openSync(file, "a");
|
||||
assertEquals(typeof fd, "number");
|
||||
assertEquals(existsSync(file), true);
|
||||
assert(Deno.resources()[fd]);
|
||||
closeSync(fd);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'ax'",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
assertThrows(
|
||||
() => {
|
||||
openSync(file, "ax");
|
||||
},
|
||||
Error,
|
||||
`EEXIST: file already exists, open '${file}'`,
|
||||
);
|
||||
Deno.removeSync(file);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'a+'",
|
||||
fn() {
|
||||
const file = join(temp_dir, "some_random_file2");
|
||||
const fd = openSync(file, "a+");
|
||||
assertEquals(typeof fd, "number");
|
||||
assertEquals(existsSync(file), true);
|
||||
closeSync(fd);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'ax+'",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
assertThrows(
|
||||
() => {
|
||||
openSync(file, "ax+");
|
||||
},
|
||||
Error,
|
||||
`EEXIST: file already exists, open '${file}'`,
|
||||
);
|
||||
Deno.removeSync(file);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'as'",
|
||||
fn() {
|
||||
const file = join(temp_dir, "some_random_file10");
|
||||
const fd = openSync(file, "as");
|
||||
assertEquals(existsSync(file), true);
|
||||
assertEquals(typeof fd, "number");
|
||||
closeSync(fd);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'as+'",
|
||||
fn() {
|
||||
const file = join(temp_dir, "some_random_file10");
|
||||
const fd = openSync(file, "as+");
|
||||
assertEquals(existsSync(file), true);
|
||||
assertEquals(typeof fd, "number");
|
||||
closeSync(fd);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'r'",
|
||||
fn() {
|
||||
const file = join(temp_dir, "some_random_file3");
|
||||
assertThrows(() => {
|
||||
openSync(file, "r");
|
||||
}, Error);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'r+'",
|
||||
fn() {
|
||||
const file = join(temp_dir, "some_random_file4");
|
||||
assertThrows(() => {
|
||||
openSync(file, "r+");
|
||||
}, Error);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'w'",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
Deno.writeTextFileSync(file, "hi there");
|
||||
const fd = openSync(file, "w");
|
||||
assertEquals(typeof fd, "number");
|
||||
assertEquals(Deno.readTextFileSync(file), "");
|
||||
closeSync(fd);
|
||||
|
||||
const file2 = join(temp_dir, "some_random_file5");
|
||||
const fd2 = openSync(file2, "w");
|
||||
assertEquals(typeof fd2, "number");
|
||||
assertEquals(existsSync(file2), true);
|
||||
closeSync(fd2);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'wx'",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
Deno.writeTextFileSync(file, "hi there");
|
||||
const fd = openSync(file, "w");
|
||||
assertEquals(typeof fd, "number");
|
||||
assertEquals(Deno.readTextFileSync(file), "");
|
||||
closeSync(fd);
|
||||
|
||||
const file2 = Deno.makeTempFileSync();
|
||||
assertThrows(
|
||||
() => {
|
||||
openSync(file2, "wx");
|
||||
},
|
||||
Error,
|
||||
`EEXIST: file already exists, open '${file2}'`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'w+'",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
Deno.writeTextFileSync(file, "hi there");
|
||||
const fd = openSync(file, "w+");
|
||||
assertEquals(typeof fd, "number");
|
||||
assertEquals(Deno.readTextFileSync(file), "");
|
||||
closeSync(fd);
|
||||
|
||||
const file2 = join(temp_dir, "some_random_file6");
|
||||
const fd2 = openSync(file2, "w+");
|
||||
assertEquals(typeof fd2, "number");
|
||||
assertEquals(existsSync(file2), true);
|
||||
closeSync(fd2);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "open with flag 'wx+'",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
assertThrows(
|
||||
() => {
|
||||
openSync(file, "wx+");
|
||||
},
|
||||
Error,
|
||||
`EEXIST: file already exists, open '${file}'`,
|
||||
);
|
||||
Deno.removeSync(file);
|
||||
},
|
||||
});
|
117
std/node/_fs/_fs_readdir.ts
Normal file
117
std/node/_fs/_fs_readdir.ts
Normal file
|
@ -0,0 +1,117 @@
|
|||
import { asyncIterableToCallback } from "./_fs_watch.ts";
|
||||
import Dirent from "./_fs_dirent.ts";
|
||||
import { fromFileUrl } from "../path.ts";
|
||||
|
||||
function toDirent(val: Deno.DirEntry): Dirent {
|
||||
return new Dirent(val);
|
||||
}
|
||||
|
||||
type readDirOptions = {
|
||||
encoding?: string;
|
||||
withFileTypes?: boolean;
|
||||
};
|
||||
|
||||
type readDirCallback = (err: Error | undefined, files: string[]) => void;
|
||||
|
||||
type readDirCallbackDirent = (err: Error | undefined, files: Dirent[]) => void;
|
||||
|
||||
type readDirBoth = (
|
||||
err: Error | undefined,
|
||||
files: string[] | Dirent[] | Array<string | Dirent>,
|
||||
) => void;
|
||||
|
||||
export function readdir(
|
||||
path: string | URL,
|
||||
options: { withFileTypes?: false; encoding?: string },
|
||||
callback: readDirCallback,
|
||||
): void;
|
||||
export function readdir(
|
||||
path: string | URL,
|
||||
options: { withFileTypes: true; encoding?: string },
|
||||
callback: readDirCallbackDirent,
|
||||
): void;
|
||||
export function readdir(path: string | URL, callback: readDirCallback): void;
|
||||
export function readdir(
|
||||
path: string | URL,
|
||||
optionsOrCallback: readDirOptions | readDirCallback | readDirCallbackDirent,
|
||||
maybeCallback?: readDirCallback | readDirCallbackDirent,
|
||||
) {
|
||||
const callback =
|
||||
(typeof optionsOrCallback === "function"
|
||||
? optionsOrCallback
|
||||
: maybeCallback) as readDirBoth | undefined;
|
||||
const options = typeof optionsOrCallback === "object"
|
||||
? optionsOrCallback
|
||||
: null;
|
||||
const result: Array<string | Dirent> = [];
|
||||
path = path instanceof URL ? fromFileUrl(path) : path;
|
||||
|
||||
if (!callback) throw new Error("No callback function supplied");
|
||||
|
||||
if (options?.encoding) {
|
||||
try {
|
||||
new TextDecoder(options.encoding);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`TypeError [ERR_INVALID_OPT_VALUE_ENCODING]: The value "${options.encoding}" is invalid for option "encoding"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
asyncIterableToCallback(Deno.readDir(path), (val, done) => {
|
||||
if (typeof path !== "string") return;
|
||||
if (done) {
|
||||
callback(undefined, result);
|
||||
return;
|
||||
}
|
||||
if (options?.withFileTypes) {
|
||||
result.push(toDirent(val));
|
||||
} else result.push(decode(val.name));
|
||||
});
|
||||
} catch (error) {
|
||||
callback(error, result);
|
||||
}
|
||||
}
|
||||
|
||||
function decode(str: string, encoding?: string): string {
|
||||
if (!encoding) return str;
|
||||
else {
|
||||
const decoder = new TextDecoder(encoding);
|
||||
const encoder = new TextEncoder();
|
||||
return decoder.decode(encoder.encode(str));
|
||||
}
|
||||
}
|
||||
|
||||
export function readdirSync(
|
||||
path: string | URL,
|
||||
options: { withFileTypes: true; encoding?: string },
|
||||
): Dirent[];
|
||||
export function readdirSync(
|
||||
path: string | URL,
|
||||
options?: { withFileTypes?: false; encoding?: string },
|
||||
): string[];
|
||||
export function readdirSync(
|
||||
path: string | URL,
|
||||
options?: readDirOptions,
|
||||
): Array<string | Dirent> {
|
||||
const result = [];
|
||||
path = path instanceof URL ? fromFileUrl(path) : path;
|
||||
|
||||
if (options?.encoding) {
|
||||
try {
|
||||
new TextDecoder(options.encoding);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`TypeError [ERR_INVALID_OPT_VALUE_ENCODING]: The value "${options.encoding}" is invalid for option "encoding"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const file of Deno.readDirSync(path)) {
|
||||
if (options?.withFileTypes) {
|
||||
result.push(toDirent(file));
|
||||
} else result.push(decode(file.name));
|
||||
}
|
||||
return result;
|
||||
}
|
71
std/node/_fs/_fs_readdir_test.ts
Normal file
71
std/node/_fs/_fs_readdir_test.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { assertEquals, assertNotEquals, fail } from "../../testing/asserts.ts";
|
||||
import { readdir, readdirSync } from "./_fs_readdir.ts";
|
||||
import { join } from "../../path/mod.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: reading empty directory",
|
||||
async fn() {
|
||||
const dir = Deno.makeTempDirSync();
|
||||
await new Promise<string[]>((resolve, reject) => {
|
||||
readdir(dir, (err, files) => {
|
||||
if (err) reject(err);
|
||||
resolve(files);
|
||||
});
|
||||
})
|
||||
.then((files) => assertEquals(files, []))
|
||||
.catch(() => fail())
|
||||
.finally(() => Deno.removeSync(dir));
|
||||
},
|
||||
});
|
||||
|
||||
function assertEqualsArrayAnyOrder<T>(actual: T[], expected: T[]) {
|
||||
assertEquals(actual.length, expected.length);
|
||||
for (const item of expected) {
|
||||
const index = actual.indexOf(item);
|
||||
assertNotEquals(index, -1);
|
||||
expected = expected.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: reading non-empty directory",
|
||||
async fn() {
|
||||
const dir = Deno.makeTempDirSync();
|
||||
Deno.writeTextFileSync(join(dir, "file1.txt"), "hi");
|
||||
Deno.writeTextFileSync(join(dir, "file2.txt"), "hi");
|
||||
Deno.mkdirSync(join(dir, "some_dir"));
|
||||
await new Promise<string[]>((resolve, reject) => {
|
||||
readdir(dir, (err, files) => {
|
||||
if (err) reject(err);
|
||||
resolve(files);
|
||||
});
|
||||
})
|
||||
.then((files) =>
|
||||
assertEqualsArrayAnyOrder(files, ["file1.txt", "some_dir", "file2.txt"])
|
||||
)
|
||||
.catch(() => fail())
|
||||
.finally(() => Deno.removeSync(dir, { recursive: true }));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: reading empty the directory",
|
||||
fn() {
|
||||
const dir = Deno.makeTempDirSync();
|
||||
assertEquals(readdirSync(dir), []);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: reading non-empty directory",
|
||||
fn() {
|
||||
const dir = Deno.makeTempDirSync();
|
||||
Deno.writeTextFileSync(join(dir, "file1.txt"), "hi");
|
||||
Deno.writeTextFileSync(join(dir, "file2.txt"), "hi");
|
||||
Deno.mkdirSync(join(dir, "some_dir"));
|
||||
assertEqualsArrayAnyOrder(
|
||||
readdirSync(dir),
|
||||
["file1.txt", "some_dir", "file2.txt"],
|
||||
);
|
||||
},
|
||||
});
|
23
std/node/_fs/_fs_rename.ts
Normal file
23
std/node/_fs/_fs_rename.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { fromFileUrl } from "../path.ts";
|
||||
|
||||
export function rename(
|
||||
oldPath: string | URL,
|
||||
newPath: string | URL,
|
||||
callback: (err?: Error) => void,
|
||||
) {
|
||||
oldPath = oldPath instanceof URL ? fromFileUrl(oldPath) : oldPath;
|
||||
newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath;
|
||||
|
||||
if (!callback) throw new Error("No callback function supplied");
|
||||
|
||||
Deno.rename(oldPath, newPath)
|
||||
.then((_) => callback())
|
||||
.catch(callback);
|
||||
}
|
||||
|
||||
export function renameSync(oldPath: string | URL, newPath: string | URL) {
|
||||
oldPath = oldPath instanceof URL ? fromFileUrl(oldPath) : oldPath;
|
||||
newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath;
|
||||
|
||||
Deno.renameSync(oldPath, newPath);
|
||||
}
|
38
std/node/_fs/_fs_rename_test.ts
Normal file
38
std/node/_fs/_fs_rename_test.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { assertEquals, fail } from "../../testing/asserts.ts";
|
||||
import { rename, renameSync } from "./_fs_rename.ts";
|
||||
import { existsSync } from "../../fs/mod.ts";
|
||||
import { join, parse } from "../../path/mod.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: renaming a file",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
const newPath = join(parse(file).dir, `${parse(file).base}_renamed`);
|
||||
await new Promise((resolve, reject) => {
|
||||
rename(file, newPath, (err) => {
|
||||
if (err) reject(err);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
assertEquals(existsSync(newPath), true);
|
||||
assertEquals(existsSync(file), false);
|
||||
})
|
||||
.catch(() => fail())
|
||||
.finally(() => {
|
||||
if (existsSync(file)) Deno.removeSync(file);
|
||||
if (existsSync(newPath)) Deno.removeSync(newPath);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: renaming a file",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
const newPath = join(parse(file).dir, `${parse(file).base}_renamed`);
|
||||
renameSync(file, newPath);
|
||||
assertEquals(existsSync(newPath), true);
|
||||
assertEquals(existsSync(file), false);
|
||||
},
|
||||
});
|
36
std/node/_fs/_fs_rmdir.ts
Normal file
36
std/node/_fs/_fs_rmdir.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
type rmdirOptions = {
|
||||
maxRetries?: number;
|
||||
recursive?: boolean;
|
||||
retryDelay?: number;
|
||||
};
|
||||
|
||||
type rmdirCallback = (err?: Error) => void;
|
||||
|
||||
export function rmdir(path: string | URL, callback: rmdirCallback): void;
|
||||
export function rmdir(
|
||||
path: string | URL,
|
||||
options: rmdirOptions,
|
||||
callback: rmdirCallback,
|
||||
): void;
|
||||
export function rmdir(
|
||||
path: string | URL,
|
||||
optionsOrCallback: rmdirOptions | rmdirCallback,
|
||||
maybeCallback?: rmdirCallback,
|
||||
) {
|
||||
const callback = typeof optionsOrCallback === "function"
|
||||
? optionsOrCallback
|
||||
: maybeCallback;
|
||||
const options = typeof optionsOrCallback === "object"
|
||||
? optionsOrCallback
|
||||
: undefined;
|
||||
|
||||
if (!callback) throw new Error("No callback function supplied");
|
||||
|
||||
Deno.remove(path, { recursive: options?.recursive })
|
||||
.then((_) => callback())
|
||||
.catch(callback);
|
||||
}
|
||||
|
||||
export function rmdirSync(path: string | URL, options?: rmdirOptions) {
|
||||
Deno.removeSync(path, { recursive: options?.recursive });
|
||||
}
|
88
std/node/_fs/_fs_rmdir_test.ts
Normal file
88
std/node/_fs/_fs_rmdir_test.ts
Normal file
|
@ -0,0 +1,88 @@
|
|||
import { assertEquals, fail } from "../../testing/asserts.ts";
|
||||
import { rmdir, rmdirSync } from "./_fs_rmdir.ts";
|
||||
import { closeSync } from "./_fs_close.ts";
|
||||
import { existsSync } from "../../fs/mod.ts";
|
||||
import { join } from "../../path/mod.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: removing empty folder",
|
||||
async fn() {
|
||||
const dir = Deno.makeTempDirSync();
|
||||
await new Promise((resolve, reject) => {
|
||||
rmdir(dir, (err) => {
|
||||
if (err) reject(err);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.then(() => assertEquals(existsSync(dir), false))
|
||||
.catch(() => fail())
|
||||
.finally(() => {
|
||||
if (existsSync(dir)) Deno.removeSync(dir);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: removing empty folder",
|
||||
fn() {
|
||||
const dir = Deno.makeTempDirSync();
|
||||
rmdirSync(dir);
|
||||
assertEquals(existsSync(dir), false);
|
||||
},
|
||||
});
|
||||
|
||||
function closeRes(before: Deno.ResourceMap, after: Deno.ResourceMap) {
|
||||
for (const key in after) {
|
||||
if (!before[key]) {
|
||||
try {
|
||||
closeSync(Number(key));
|
||||
} catch (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: removing non-empty folder",
|
||||
async fn() {
|
||||
const rBefore = Deno.resources();
|
||||
const dir = Deno.makeTempDirSync();
|
||||
Deno.createSync(join(dir, "file1.txt"));
|
||||
Deno.createSync(join(dir, "file2.txt"));
|
||||
Deno.mkdirSync(join(dir, "some_dir"));
|
||||
Deno.createSync(join(dir, "some_dir", "file.txt"));
|
||||
await new Promise((resolve, reject) => {
|
||||
rmdir(dir, { recursive: true }, (err) => {
|
||||
if (err) reject(err);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.then(() => assertEquals(existsSync(dir), false))
|
||||
.catch(() => fail())
|
||||
.finally(() => {
|
||||
if (existsSync(dir)) Deno.removeSync(dir, { recursive: true });
|
||||
const rAfter = Deno.resources();
|
||||
closeRes(rBefore, rAfter);
|
||||
});
|
||||
},
|
||||
ignore: Deno.build.os === "windows",
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: removing non-empty folder",
|
||||
fn() {
|
||||
const rBefore = Deno.resources();
|
||||
const dir = Deno.makeTempDirSync();
|
||||
Deno.createSync(join(dir, "file1.txt"));
|
||||
Deno.createSync(join(dir, "file2.txt"));
|
||||
Deno.mkdirSync(join(dir, "some_dir"));
|
||||
Deno.createSync(join(dir, "some_dir", "file.txt"));
|
||||
rmdirSync(dir, { recursive: true });
|
||||
assertEquals(existsSync(dir), false);
|
||||
// closing resources
|
||||
const rAfter = Deno.resources();
|
||||
closeRes(rBefore, rAfter);
|
||||
},
|
||||
ignore: Deno.build.os === "windows",
|
||||
});
|
289
std/node/_fs/_fs_stat.ts
Normal file
289
std/node/_fs/_fs_stat.ts
Normal file
|
@ -0,0 +1,289 @@
|
|||
export type statOptions = {
|
||||
bigint: boolean;
|
||||
};
|
||||
|
||||
export type Stats = {
|
||||
/** ID of the device containing the file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
dev: number | null;
|
||||
/** Inode number.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
ino: number | null;
|
||||
/** **UNSTABLE**: Match behavior with Go on Windows for `mode`.
|
||||
*
|
||||
* The underlying raw `st_mode` bits that contain the standard Unix
|
||||
* permissions for this file/directory. */
|
||||
mode: number | null;
|
||||
/** Number of hard links pointing to this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
nlink: number | null;
|
||||
/** User ID of the owner of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
uid: number | null;
|
||||
/** Group ID of the owner of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
gid: number | null;
|
||||
/** Device ID of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
rdev: number | null;
|
||||
/** The size of the file, in bytes. */
|
||||
size: number;
|
||||
/** Blocksize for filesystem I/O.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
blksize: number | null;
|
||||
/** Number of blocks allocated to the file, in 512-byte units.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
blocks: number | null;
|
||||
/** The last modification time of the file. This corresponds to the `mtime`
|
||||
* field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This
|
||||
* may not be available on all platforms. */
|
||||
mtime: Date | null;
|
||||
/** The last access time of the file. This corresponds to the `atime`
|
||||
* field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
|
||||
* be available on all platforms. */
|
||||
atime: Date | null;
|
||||
/** The creation time of the file. This corresponds to the `birthtime`
|
||||
* field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may
|
||||
* not be available on all platforms. */
|
||||
birthtime: Date | null;
|
||||
/** change time */
|
||||
ctime: Date | null;
|
||||
/** atime in milliseconds */
|
||||
atimeMs: number | null;
|
||||
/** atime in milliseconds */
|
||||
mtimeMs: number | null;
|
||||
/** atime in milliseconds */
|
||||
ctimeMs: number | null;
|
||||
/** atime in milliseconds */
|
||||
birthtimeMs: number | null;
|
||||
isBlockDevice: () => boolean;
|
||||
isCharacterDevice: () => boolean;
|
||||
isDirectory: () => boolean;
|
||||
isFIFO: () => boolean;
|
||||
isFile: () => boolean;
|
||||
isSocket: () => boolean;
|
||||
isSymbolicLink: () => boolean;
|
||||
};
|
||||
|
||||
export type BigIntStats = {
|
||||
/** ID of the device containing the file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
dev: BigInt | null;
|
||||
/** Inode number.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
ino: BigInt | null;
|
||||
/** **UNSTABLE**: Match behavior with Go on Windows for `mode`.
|
||||
*
|
||||
* The underlying raw `st_mode` bits that contain the standard Unix
|
||||
* permissions for this file/directory. */
|
||||
mode: BigInt | null;
|
||||
/** Number of hard links pointing to this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
nlink: BigInt | null;
|
||||
/** User ID of the owner of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
uid: BigInt | null;
|
||||
/** Group ID of the owner of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
gid: BigInt | null;
|
||||
/** Device ID of this file.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
rdev: BigInt | null;
|
||||
/** The size of the file, in bytes. */
|
||||
size: BigInt;
|
||||
/** Blocksize for filesystem I/O.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
blksize: BigInt | null;
|
||||
/** Number of blocks allocated to the file, in 512-byte units.
|
||||
*
|
||||
* _Linux/Mac OS only._ */
|
||||
blocks: BigInt | null;
|
||||
/** The last modification time of the file. This corresponds to the `mtime`
|
||||
* field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This
|
||||
* may not be available on all platforms. */
|
||||
mtime: Date | null;
|
||||
/** The last access time of the file. This corresponds to the `atime`
|
||||
* field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not
|
||||
* be available on all platforms. */
|
||||
atime: Date | null;
|
||||
/** The creation time of the file. This corresponds to the `birthtime`
|
||||
* field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may
|
||||
* not be available on all platforms. */
|
||||
birthtime: Date | null;
|
||||
/** change time */
|
||||
ctime: Date | null;
|
||||
/** atime in milliseconds */
|
||||
atimeMs: BigInt | null;
|
||||
/** atime in milliseconds */
|
||||
mtimeMs: BigInt | null;
|
||||
/** atime in milliseconds */
|
||||
ctimeMs: BigInt | null;
|
||||
/** atime in nanoseconds */
|
||||
birthtimeMs: BigInt | null;
|
||||
/** atime in nanoseconds */
|
||||
atimeNs: BigInt | null;
|
||||
/** atime in nanoseconds */
|
||||
mtimeNs: BigInt | null;
|
||||
/** atime in nanoseconds */
|
||||
ctimeNs: BigInt | null;
|
||||
/** atime in nanoseconds */
|
||||
birthtimeNs: BigInt | null;
|
||||
isBlockDevice: () => boolean;
|
||||
isCharacterDevice: () => boolean;
|
||||
isDirectory: () => boolean;
|
||||
isFIFO: () => boolean;
|
||||
isFile: () => boolean;
|
||||
isSocket: () => boolean;
|
||||
isSymbolicLink: () => boolean;
|
||||
};
|
||||
|
||||
export function convertFileInfoToStats(origin: Deno.FileInfo): Stats {
|
||||
return {
|
||||
dev: origin.dev,
|
||||
ino: origin.ino,
|
||||
mode: origin.mode,
|
||||
nlink: origin.nlink,
|
||||
uid: origin.uid,
|
||||
gid: origin.gid,
|
||||
rdev: origin.rdev,
|
||||
size: origin.size,
|
||||
blksize: origin.blksize,
|
||||
blocks: origin.blocks,
|
||||
mtime: origin.mtime,
|
||||
atime: origin.atime,
|
||||
birthtime: origin.birthtime,
|
||||
mtimeMs: origin.mtime?.getTime() || null,
|
||||
atimeMs: origin.atime?.getTime() || null,
|
||||
birthtimeMs: origin.birthtime?.getTime() || null,
|
||||
isFile: () => origin.isFile,
|
||||
isDirectory: () => origin.isDirectory,
|
||||
isSymbolicLink: () => origin.isSymlink,
|
||||
// not sure about those
|
||||
isBlockDevice: () => false,
|
||||
isFIFO: () => false,
|
||||
isCharacterDevice: () => false,
|
||||
isSocket: () => false,
|
||||
ctime: origin.mtime,
|
||||
ctimeMs: origin.mtime?.getTime() || null,
|
||||
};
|
||||
}
|
||||
|
||||
function to_BigInt(number?: number | null) {
|
||||
if (number === null || number === undefined) return null;
|
||||
return BigInt(number);
|
||||
}
|
||||
|
||||
export function convertFileInfoToBigIntStats(
|
||||
origin: Deno.FileInfo,
|
||||
): BigIntStats {
|
||||
return {
|
||||
dev: to_BigInt(origin.dev),
|
||||
ino: to_BigInt(origin.ino),
|
||||
mode: to_BigInt(origin.mode),
|
||||
nlink: to_BigInt(origin.nlink),
|
||||
uid: to_BigInt(origin.uid),
|
||||
gid: to_BigInt(origin.gid),
|
||||
rdev: to_BigInt(origin.rdev),
|
||||
size: to_BigInt(origin.size) || 0n,
|
||||
blksize: to_BigInt(origin.blksize),
|
||||
blocks: to_BigInt(origin.blocks),
|
||||
mtime: origin.mtime,
|
||||
atime: origin.atime,
|
||||
birthtime: origin.birthtime,
|
||||
mtimeMs: origin.mtime ? BigInt(origin.mtime.getTime()) : null,
|
||||
atimeMs: origin.atime ? BigInt(origin.atime.getTime()) : null,
|
||||
birthtimeMs: origin.birthtime ? BigInt(origin.birthtime.getTime()) : null,
|
||||
mtimeNs: origin.mtime ? BigInt(origin.mtime.getTime()) * 1000000n : null,
|
||||
atimeNs: origin.atime ? BigInt(origin.atime.getTime()) * 1000000n : null,
|
||||
birthtimeNs: origin.birthtime
|
||||
? BigInt(origin.birthtime.getTime()) * 1000000n
|
||||
: null,
|
||||
isFile: () => origin.isFile,
|
||||
isDirectory: () => origin.isDirectory,
|
||||
isSymbolicLink: () => origin.isSymlink,
|
||||
// not sure about those
|
||||
isBlockDevice: () => false,
|
||||
isFIFO: () => false,
|
||||
isCharacterDevice: () => false,
|
||||
isSocket: () => false,
|
||||
ctime: origin.mtime,
|
||||
ctimeMs: origin.mtime ? BigInt(origin.mtime.getTime()) : null,
|
||||
ctimeNs: origin.mtime ? BigInt(origin.mtime.getTime()) * 1000000n : null,
|
||||
};
|
||||
}
|
||||
|
||||
// shortcut for Convert File Info to Stats or BigIntStats
|
||||
export function CFISBIS(fileInfo: Deno.FileInfo, bigInt: boolean) {
|
||||
if (bigInt) return convertFileInfoToBigIntStats(fileInfo);
|
||||
return convertFileInfoToStats(fileInfo);
|
||||
}
|
||||
|
||||
export type statCallbackBigInt = (
|
||||
err: Error | undefined,
|
||||
stat: BigIntStats,
|
||||
) => void;
|
||||
|
||||
export type statCallback = (err: Error | undefined, stat: Stats) => void;
|
||||
|
||||
export function stat(path: string | URL, callback: statCallback): void;
|
||||
export function stat(
|
||||
path: string | URL,
|
||||
options: { bigint: false },
|
||||
callback: statCallback,
|
||||
): void;
|
||||
export function stat(
|
||||
path: string | URL,
|
||||
options: { bigint: true },
|
||||
callback: statCallbackBigInt,
|
||||
): void;
|
||||
export function stat(
|
||||
path: string | URL,
|
||||
optionsOrCallback: statCallback | statCallbackBigInt | statOptions,
|
||||
maybeCallback?: statCallback | statCallbackBigInt,
|
||||
) {
|
||||
const callback =
|
||||
(typeof optionsOrCallback === "function"
|
||||
? optionsOrCallback
|
||||
: maybeCallback) as (
|
||||
err: Error | undefined,
|
||||
stat: BigIntStats | Stats,
|
||||
) => void;
|
||||
const options = typeof optionsOrCallback === "object"
|
||||
? optionsOrCallback
|
||||
: { bigint: false };
|
||||
|
||||
if (!callback) throw new Error("No callback function supplied");
|
||||
|
||||
Deno.stat(path)
|
||||
.then((stat) => callback(undefined, CFISBIS(stat, options.bigint)))
|
||||
.catch((err) => callback(err, err));
|
||||
}
|
||||
|
||||
export function statSync(path: string | URL): Stats;
|
||||
export function statSync(path: string | URL, options: { bigint: false }): Stats;
|
||||
export function statSync(
|
||||
path: string | URL,
|
||||
options: { bigint: true },
|
||||
): BigIntStats;
|
||||
export function statSync(
|
||||
path: string | URL,
|
||||
options: statOptions = { bigint: false },
|
||||
): Stats | BigIntStats {
|
||||
const origin = Deno.statSync(path);
|
||||
return CFISBIS(origin, options.bigint);
|
||||
}
|
107
std/node/_fs/_fs_stat_test.ts
Normal file
107
std/node/_fs/_fs_stat_test.ts
Normal file
|
@ -0,0 +1,107 @@
|
|||
import { BigIntStats, stat, Stats, statSync } from "./_fs_stat.ts";
|
||||
import { assertEquals, fail } from "../../testing/asserts.ts";
|
||||
|
||||
export function assertStats(actual: Stats, expected: Deno.FileInfo) {
|
||||
assertEquals(actual.dev, expected.dev);
|
||||
assertEquals(actual.gid, expected.gid);
|
||||
assertEquals(actual.size, expected.size);
|
||||
assertEquals(actual.blksize, expected.blksize);
|
||||
assertEquals(actual.blocks, expected.blocks);
|
||||
assertEquals(actual.ino, expected.ino);
|
||||
assertEquals(actual.gid, expected.gid);
|
||||
assertEquals(actual.mode, expected.mode);
|
||||
assertEquals(actual.nlink, expected.nlink);
|
||||
assertEquals(actual.rdev, expected.rdev);
|
||||
assertEquals(actual.uid, expected.uid);
|
||||
assertEquals(actual.atime?.getTime(), expected.atime?.getTime());
|
||||
assertEquals(actual.mtime?.getTime(), expected.mtime?.getTime());
|
||||
assertEquals(actual.birthtime?.getTime(), expected.birthtime?.getTime());
|
||||
assertEquals(actual.atimeMs, expected.atime?.getTime());
|
||||
assertEquals(actual.mtimeMs, expected.mtime?.getTime());
|
||||
assertEquals(actual.birthtimeMs, expected.birthtime?.getTime());
|
||||
assertEquals(actual.isFile(), expected.isFile);
|
||||
assertEquals(actual.isDirectory(), expected.isDirectory);
|
||||
assertEquals(actual.isSymbolicLink(), expected.isSymlink);
|
||||
}
|
||||
|
||||
function to_BigInt(num?: number | null) {
|
||||
if (num === undefined || num === null) return null;
|
||||
return BigInt(num);
|
||||
}
|
||||
|
||||
export function assertStatsBigInt(
|
||||
actual: BigIntStats,
|
||||
expected: Deno.FileInfo,
|
||||
) {
|
||||
assertEquals(actual.dev, to_BigInt(expected.dev));
|
||||
assertEquals(actual.gid, to_BigInt(expected.gid));
|
||||
assertEquals(actual.size, to_BigInt(expected.size));
|
||||
assertEquals(actual.blksize, to_BigInt(expected.blksize));
|
||||
assertEquals(actual.blocks, to_BigInt(expected.blocks));
|
||||
assertEquals(actual.ino, to_BigInt(expected.ino));
|
||||
assertEquals(actual.gid, to_BigInt(expected.gid));
|
||||
assertEquals(actual.mode, to_BigInt(expected.mode));
|
||||
assertEquals(actual.nlink, to_BigInt(expected.nlink));
|
||||
assertEquals(actual.rdev, to_BigInt(expected.rdev));
|
||||
assertEquals(actual.uid, to_BigInt(expected.uid));
|
||||
assertEquals(actual.atime?.getTime(), expected.atime?.getTime());
|
||||
assertEquals(actual.mtime?.getTime(), expected.mtime?.getTime());
|
||||
assertEquals(actual.birthtime?.getTime(), expected.birthtime?.getTime());
|
||||
assertEquals(Number(actual.atimeMs), expected.atime?.getTime());
|
||||
assertEquals(Number(actual.mtimeMs), expected.mtime?.getTime());
|
||||
assertEquals(Number(actual.birthtimeMs), expected.birthtime?.getTime());
|
||||
assertEquals(actual.atimeNs === null, actual.atime === null);
|
||||
assertEquals(actual.mtimeNs === null, actual.mtime === null);
|
||||
assertEquals(actual.birthtimeNs === null, actual.birthtime === null);
|
||||
assertEquals(actual.isFile(), expected.isFile);
|
||||
assertEquals(actual.isDirectory(), expected.isDirectory);
|
||||
assertEquals(actual.isSymbolicLink(), expected.isSymlink);
|
||||
}
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: get a file Stats",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
await new Promise<Stats>((resolve, reject) => {
|
||||
stat(file, (err, stat) => {
|
||||
if (err) reject(err);
|
||||
resolve(stat);
|
||||
});
|
||||
})
|
||||
.then((stat) => assertStats(stat, Deno.statSync(file)))
|
||||
.catch(() => fail())
|
||||
.finally(() => Deno.removeSync(file));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: get a file Stats",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
assertStats(statSync(file), Deno.statSync(file));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: get a file BigInt Stats",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
await new Promise<BigIntStats>((resolve, reject) => {
|
||||
stat(file, { bigint: true }, (err, stat) => {
|
||||
if (err) reject(err);
|
||||
resolve(stat);
|
||||
});
|
||||
})
|
||||
.then((stat) => assertStatsBigInt(stat, Deno.statSync(file)))
|
||||
.catch(() => fail())
|
||||
.finally(() => Deno.removeSync(file));
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: get a file BigInt Stats",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
assertStatsBigInt(statSync(file, { bigint: true }), Deno.statSync(file));
|
||||
},
|
||||
});
|
10
std/node/_fs/_fs_unlink.ts
Normal file
10
std/node/_fs/_fs_unlink.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export function unlink(path: string | URL, callback: (err?: Error) => void) {
|
||||
if (!callback) throw new Error("No callback function supplied");
|
||||
Deno.remove(path)
|
||||
.then((_) => callback())
|
||||
.catch(callback);
|
||||
}
|
||||
|
||||
export function unlinkSync(path: string | URL) {
|
||||
Deno.removeSync(path);
|
||||
}
|
30
std/node/_fs/_fs_unlink_test.ts
Normal file
30
std/node/_fs/_fs_unlink_test.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { assertEquals, fail } from "../../testing/asserts.ts";
|
||||
import { existsSync } from "../../fs/mod.ts";
|
||||
import { unlink, unlinkSync } from "./_fs_unlink.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "ASYNC: deleting a file",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
await new Promise((resolve, reject) => {
|
||||
unlink(file, (err) => {
|
||||
if (err) reject(err);
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.then(() => assertEquals(existsSync(file), false))
|
||||
.catch(() => fail())
|
||||
.finally(() => {
|
||||
if (existsSync(file)) Deno.removeSync(file);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "SYNC: Test deleting a file",
|
||||
fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
unlinkSync(file);
|
||||
assertEquals(existsSync(file), false);
|
||||
},
|
||||
});
|
111
std/node/_fs/_fs_watch.ts
Normal file
111
std/node/_fs/_fs_watch.ts
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { fromFileUrl } from "../path.ts";
|
||||
import { EventEmitter } from "../events.ts";
|
||||
import { notImplemented } from "../_utils.ts";
|
||||
|
||||
export function asyncIterableIteratorToCallback<T>(
|
||||
iterator: AsyncIterableIterator<T>,
|
||||
callback: (val: T, done?: boolean) => void,
|
||||
) {
|
||||
function next() {
|
||||
iterator.next().then((obj) => {
|
||||
if (obj.done) {
|
||||
callback(obj.value, true);
|
||||
return;
|
||||
}
|
||||
callback(obj.value);
|
||||
next();
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
export function asyncIterableToCallback<T>(
|
||||
iter: AsyncIterable<T>,
|
||||
callback: (val: T, done?: boolean) => void,
|
||||
) {
|
||||
const iterator = iter[Symbol.asyncIterator]();
|
||||
function next() {
|
||||
iterator.next().then((obj) => {
|
||||
if (obj.done) {
|
||||
callback(obj.value, true);
|
||||
return;
|
||||
}
|
||||
callback(obj.value);
|
||||
next();
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
type watchOptions = {
|
||||
persistent?: boolean;
|
||||
recursive?: boolean;
|
||||
encoding?: string;
|
||||
};
|
||||
|
||||
type watchListener = (eventType: string, filename: string) => void;
|
||||
|
||||
export function watch(
|
||||
filename: string | URL,
|
||||
options: watchOptions,
|
||||
listener: watchListener,
|
||||
): FSWatcher;
|
||||
export function watch(
|
||||
filename: string | URL,
|
||||
listener: watchListener,
|
||||
): FSWatcher;
|
||||
export function watch(
|
||||
filename: string | URL,
|
||||
options: watchOptions,
|
||||
): FSWatcher;
|
||||
export function watch(filename: string | URL): FSWatcher;
|
||||
export function watch(
|
||||
filename: string | URL,
|
||||
optionsOrListener?: watchOptions | watchListener,
|
||||
optionsOrListener2?: watchOptions | watchListener,
|
||||
) {
|
||||
const listener = typeof optionsOrListener === "function"
|
||||
? optionsOrListener
|
||||
: typeof optionsOrListener2 === "function"
|
||||
? optionsOrListener2
|
||||
: undefined;
|
||||
const options = typeof optionsOrListener === "object"
|
||||
? optionsOrListener
|
||||
: typeof optionsOrListener2 === "object"
|
||||
? optionsOrListener2
|
||||
: undefined;
|
||||
filename = filename instanceof URL ? fromFileUrl(filename) : filename;
|
||||
|
||||
const iterator = Deno.watchFs(filename, {
|
||||
recursive: options?.recursive || false,
|
||||
});
|
||||
|
||||
if (!listener) throw new Error("No callback function supplied");
|
||||
|
||||
const fsWatcher = new FSWatcher(() => {
|
||||
if (iterator.return) iterator.return();
|
||||
});
|
||||
|
||||
fsWatcher.on("change", listener);
|
||||
|
||||
asyncIterableIteratorToCallback<Deno.FsEvent>(iterator, (val, done) => {
|
||||
if (done) return;
|
||||
fsWatcher.emit("change", val.kind, val.paths[0]);
|
||||
});
|
||||
|
||||
return fsWatcher;
|
||||
}
|
||||
|
||||
class FSWatcher extends EventEmitter {
|
||||
close: () => void;
|
||||
constructor(closer: () => void) {
|
||||
super();
|
||||
this.close = closer;
|
||||
}
|
||||
ref() {
|
||||
notImplemented("FSWatcher.ref() is not implemented");
|
||||
}
|
||||
unref() {
|
||||
notImplemented("FSWatcher.unref() is not implemented");
|
||||
}
|
||||
}
|
32
std/node/_fs/_fs_watch_test.ts
Normal file
32
std/node/_fs/_fs_watch_test.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { watch } from "./_fs_watch.ts";
|
||||
import { assertEquals, fail } from "../../testing/asserts.ts";
|
||||
|
||||
function wait(time: number) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, time);
|
||||
});
|
||||
}
|
||||
|
||||
Deno.test({
|
||||
name: "watching a file",
|
||||
async fn() {
|
||||
const file = Deno.makeTempFileSync();
|
||||
const result: Array<[string, string]> = [];
|
||||
await new Promise((resolve) => {
|
||||
const watcher = watch(
|
||||
file,
|
||||
(eventType, filename) => result.push([eventType, filename]),
|
||||
);
|
||||
wait(100)
|
||||
.then(() => Deno.writeTextFileSync(file, "something"))
|
||||
.then(() => wait(100))
|
||||
.then(() => watcher.close())
|
||||
.then(() => wait(100))
|
||||
.then(resolve);
|
||||
})
|
||||
.then(() => {
|
||||
assertEquals(result.length >= 1, true);
|
||||
})
|
||||
.catch(() => fail());
|
||||
},
|
||||
});
|
|
@ -11,6 +11,15 @@ import { exists, existsSync } from "./_fs/_fs_exists.ts";
|
|||
import { mkdir, mkdirSync } from "./_fs/_fs_mkdir.ts";
|
||||
import { copyFile, copyFileSync } from "./_fs/_fs_copy.ts";
|
||||
import { writeFile, writeFileSync } from "./_fs/_fs_writeFile.ts";
|
||||
import { readdir, readdirSync } from "./_fs/_fs_readdir.ts";
|
||||
import { rename, renameSync } from "./_fs/_fs_rename.ts";
|
||||
import { rmdir, rmdirSync } from "./_fs/_fs_rmdir.ts";
|
||||
import { unlink, unlinkSync } from "./_fs/_fs_unlink.ts";
|
||||
import { watch } from "./_fs/_fs_watch.ts";
|
||||
import { open, openSync } from "./_fs/_fs_open.ts";
|
||||
import { stat, statSync } from "./_fs/_fs_stat.ts";
|
||||
import { lstat, lstatSync } from "./_fs/_fs_lstat.ts";
|
||||
|
||||
import * as promises from "./_fs/promises/mod.ts";
|
||||
|
||||
export {
|
||||
|
@ -29,13 +38,28 @@ export {
|
|||
copyFileSync,
|
||||
exists,
|
||||
existsSync,
|
||||
lstat,
|
||||
lstatSync,
|
||||
mkdir,
|
||||
mkdirSync,
|
||||
open,
|
||||
openSync,
|
||||
promises,
|
||||
readdir,
|
||||
readdirSync,
|
||||
readFile,
|
||||
readFileSync,
|
||||
readlink,
|
||||
readlinkSync,
|
||||
rename,
|
||||
renameSync,
|
||||
rmdir,
|
||||
rmdirSync,
|
||||
stat,
|
||||
statSync,
|
||||
unlink,
|
||||
unlinkSync,
|
||||
watch,
|
||||
writeFile,
|
||||
writeFileSync,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue