From 6cbb38937207abfa7f87e05b4f519c23163a4c42 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Tue, 24 Dec 2024 17:56:02 +0900 Subject: [PATCH] feat(fs/unstable): add statSync and lstatSync --- _tools/check_docs.ts | 3 +++ fs/_utils.ts | 7 ++++-- fs/deno.json | 3 ++- fs/unstable_lstat.ts | 52 +++++++++++++++++++++++++++++++++------ fs/unstable_lstat_test.ts | 50 +++++++++++++++++++++++++------------ fs/unstable_stat.ts | 50 +++++++++++++++++++++++++++++++------ fs/unstable_stat_test.ts | 34 +++++++++++++++++-------- fs/unstable_types.ts | 8 ++++++ 8 files changed, 162 insertions(+), 45 deletions(-) diff --git a/_tools/check_docs.ts b/_tools/check_docs.ts index 98bfed3a244b..3ad76d6948fc 100644 --- a/_tools/check_docs.ts +++ b/_tools/check_docs.ts @@ -62,6 +62,9 @@ const ENTRY_POINTS = [ "../front_matter/mod.ts", "../front_matter/unstable_yaml.ts", "../fs/mod.ts", + "../fs/unstable_lstat.ts", + "../fs/unstable_stat.ts", + "../fs/unstable_types.ts", "../html/mod.ts", "../html/unstable_is_valid_custom_element_name.ts", "../http/mod.ts", diff --git a/fs/_utils.ts b/fs/_utils.ts index 2ac664f79cd2..fb7253979fed 100644 --- a/fs/_utils.ts +++ b/fs/_utils.ts @@ -21,6 +21,9 @@ function checkWindows(): boolean { return false; } -export function getNodeFsPromises() { - return (globalThis as any).process.getBuiltinModule("node:fs/promises"); +/** + * @returns The Node.js `fs` module. + */ +export function getNodeFs() { + return (globalThis as any).process.getBuiltinModule("node:fs"); } diff --git a/fs/deno.json b/fs/deno.json index e5d02d641dc8..f76825bc0f6e 100644 --- a/fs/deno.json +++ b/fs/deno.json @@ -13,8 +13,9 @@ "./exists": "./exists.ts", "./expand-glob": "./expand_glob.ts", "./move": "./move.ts", - "./unstable-stat": "./unstable_stat.ts", "./unstable-lstat": "./unstable_lstat.ts", + "./unstable-stat": "./unstable_stat.ts", + "./unstable-types": "./unstable_types.ts", "./walk": "./walk.ts" } } diff --git a/fs/unstable_lstat.ts b/fs/unstable_lstat.ts index 080821556929..cc3f29e9d90a 100644 --- a/fs/unstable_lstat.ts +++ b/fs/unstable_lstat.ts @@ -1,12 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { getNodeFsPromises, isDeno } from "./_utils.ts"; +import { getNodeFs, isDeno } from "./_utils.ts"; import { mapError } from "./_map_error.ts"; import { toFileInfo } from "./_to_file_info.ts"; import type { FileInfo } from "./unstable_types.ts"; -/** Resolves to a {@linkcode FileInfo} for the specified `path`. If `path` is a symlink, information for the symlink will be returned instead of what it points to. +/** + * Resolves to a {@linkcode FileInfo} for the specified `path`. If `path` is a symlink, information for the symlink will be returned instead of what it points to. * + * Requires `allow-read` permission in Deno. + * + * @example Usage * ```ts * import { assert } from "@std/assert"; * import { lstat } from "@std/fs/unstable-lstat"; @@ -14,19 +18,51 @@ import type { FileInfo } from "./unstable_types.ts"; * assert(fileInfo.isFile); * ``` * - * Requires `allow-read` permission. - * * @tags allow-read - * @category File System + * + * @param path The path to the file or directory. + * @returns A promise that resolves to a {@linkcode FileInfo} for the specified `path`. */ export async function lstat(path: string | URL): Promise { if (isDeno) { return Deno.lstat(path); } else { - const fsPromises = getNodeFsPromises(); try { - const stat = await fsPromises.lstat(path); - return toFileInfo(stat); + return toFileInfo(await getNodeFs().promises.lstat(path)); + } catch (error) { + throw mapError(error); + } + } +} + +/** + * Synchronously returns a {@linkcode FileInfo} for the specified + * `path`. If `path` is a symlink, information for the symlink will be + * returned instead of what it points to. + * + * Requires `allow-read` permission in Deno. + * + * @example Usage + * + * ```ts + * import { assert } from "@std/assert"; + * import { lstatSync } from "@std/fs/unstable-lstat"; + * + * const fileInfo = lstatSync("README.md"); + * assert(fileInfo.isFile); + * ``` + * + * @tags allow-read + * + * @param path The path to the file or directory. + * @returns A {@linkcode FileInfo} for the specified `path`. + */ +export function lstatSync(path: string | URL): FileInfo { + if (isDeno) { + return Deno.lstatSync(path); + } else { + try { + return toFileInfo(getNodeFs().lstatSync(path)); } catch (error) { throw mapError(error); } diff --git a/fs/unstable_lstat_test.ts b/fs/unstable_lstat_test.ts index 2e9fbc5ec0a7..78d06fec84da 100644 --- a/fs/unstable_lstat_test.ts +++ b/fs/unstable_lstat_test.ts @@ -1,30 +1,48 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert, assertRejects } from "@std/assert"; -import { lstat } from "./unstable_lstat.ts"; +import { assert, assertRejects, assertThrows } from "@std/assert"; +import { lstat, lstatSync } from "./unstable_lstat.ts"; import { NotFound } from "./unstable_errors.js"; -Deno.test("lstat() returns FileInfo for a file", async () => { - const fileInfo = await lstat("README.md"); - - assert(fileInfo.isFile); +Deno.test("lstat() and lstatSync() return FileInfo for a file", async () => { + { + const fileInfo = await lstat("README.md"); + assert(fileInfo.isFile); + } + { + const fileInfo = lstatSync("README.md"); + assert(fileInfo.isFile); + } }); -Deno.test("lstat() does not follow symlinks", async () => { - const linkFile = `${import.meta.dirname}/testdata/0-link`; - const fileInfo = await lstat(linkFile); - - assert(fileInfo.isSymlink); +Deno.test("lstat() and lstatSync() do not follow symlinks", async () => { + const linkFile = new URL("testdata/0-link", import.meta.url); + { + const fileInfo = await lstat(linkFile); + assert(fileInfo.isSymlink); + } + { + const fileInfo = lstatSync(linkFile); + assert(fileInfo.isSymlink); + } }); -Deno.test("lstat() returns FileInfo for a directory", async () => { - const fileInfo = await lstat("fs"); - - assert(fileInfo.isDirectory); +Deno.test("lstat() and lstatSync() return FileInfo for a directory", async () => { + { + const fileInfo = await lstat("fs"); + assert(fileInfo.isDirectory); + } + { + const fileInfo = lstatSync("fs"); + assert(fileInfo.isDirectory); + } }); -Deno.test("lstat() rejects with NotFound for a non-existent file", async () => { +Deno.test("lstat() and lstatSync() throw with NotFound for a non-existent file", async () => { await assertRejects(async () => { await lstat("non_existent_file"); }, NotFound); + assertThrows(() => { + lstatSync("non_existent_file"); + }, NotFound); }); diff --git a/fs/unstable_stat.ts b/fs/unstable_stat.ts index c8d46ba5c57e..e91bc0142add 100644 --- a/fs/unstable_stat.ts +++ b/fs/unstable_stat.ts @@ -1,13 +1,17 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { getNodeFsPromises, isDeno } from "./_utils.ts"; +import { getNodeFs, isDeno } from "./_utils.ts"; import { mapError } from "./_map_error.ts"; import { toFileInfo } from "./_to_file_info.ts"; import type { FileInfo } from "./unstable_types.ts"; -/** Resolves to a {@linkcode FileInfo} for the specified `path`. Will +/** + * Resolves to a {@linkcode FileInfo} for the specified `path`. Will * always follow symlinks. * + * Requires `allow-read` permission in Deno. + * + * @example Usage * ```ts * import { assert } from "@std/assert"; * import { stat } from "@std/fs/unstable-stat"; @@ -15,19 +19,49 @@ import type { FileInfo } from "./unstable_types.ts"; * assert(fileInfo.isFile); * ``` * - * Requires `allow-read` permission. - * * @tags allow-read - * @category File System + * + * @param path The path to the file or directory. + * @returns A promise that resolves to a {@linkcode FileInfo} for the specified `path`. */ export async function stat(path: string | URL): Promise { if (isDeno) { return Deno.stat(path); } else { - const fsPromises = getNodeFsPromises(); try { - const stat = await fsPromises.stat(path); - return toFileInfo(stat); + return toFileInfo(await getNodeFs().promises.stat(path)); + } catch (error) { + throw mapError(error); + } + } +} + +/** + * Synchronously returns a {@linkcode FileInfo} for the specified + * `path`. Will always follow symlinks. + * + * Requires `allow-read` permission in Deno. + * + * @example Usage + * ```ts + * import { assert } from "@std/assert"; + * import { statSync } from "@std/fs/unstable-stat"; + * + * const fileInfo = statSync("README.md"); + * assert(fileInfo.isFile); + * ``` + * + * @tags allow-read + * + * @param path The path to the file or directory. + * @returns A {@linkcode FileInfo} for the specified `path`. + */ +export function statSync(path: string | URL): FileInfo { + if (isDeno) { + return Deno.statSync(path); + } else { + try { + return toFileInfo(getNodeFs().statSync(path)); } catch (error) { throw mapError(error); } diff --git a/fs/unstable_stat_test.ts b/fs/unstable_stat_test.ts index da9fb454288c..08c88f5ea811 100644 --- a/fs/unstable_stat_test.ts +++ b/fs/unstable_stat_test.ts @@ -1,23 +1,37 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert, assertRejects } from "@std/assert"; -import { stat } from "./unstable_stat.ts"; +import { assert, assertRejects, assertThrows } from "@std/assert"; +import { stat, statSync } from "./unstable_stat.ts"; import { NotFound } from "./unstable_errors.js"; -Deno.test("stat() returns FileInfo for a file", async () => { - const fileInfo = await stat("README.md"); +Deno.test("stat() and statSync() return FileInfo for a file", async () => { + { + const fileInfo = await stat("README.md"); + assert(fileInfo.isFile); + } - assert(fileInfo.isFile); + { + const fileInfo = statSync("README.md"); + assert(fileInfo.isFile); + } }); -Deno.test("stat() returns FileInfo for a directory", async () => { - const fileInfo = await stat("fs"); - - assert(fileInfo.isDirectory); +Deno.test("stat() and statSync() return FileInfo for a directory", async () => { + { + const fileInfo = await stat("fs"); + assert(fileInfo.isDirectory); + } + { + const fileInfo = statSync("fs"); + assert(fileInfo.isDirectory); + } }); -Deno.test("stat() rejects with NotFound for a non-existent file", async () => { +Deno.test("stat() and statSync() throw with NotFound for a non-existent file", async () => { await assertRejects(async () => { await stat("non_existent_file"); }, NotFound); + assertThrows(() => { + statSync("non_existent_file"); + }, NotFound); }); diff --git a/fs/unstable_types.ts b/fs/unstable_types.ts index bde3f14fac33..5851445ff2b9 100644 --- a/fs/unstable_types.ts +++ b/fs/unstable_types.ts @@ -1,5 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +/** + * Provides information about a file and is returned by + * {@linkcode stat}, {@linkcode lstat}, {@linkcode statSync}, + * and {@linkcode lstatSync} or from calling `stat()` and `statSync()` + * on an {@linkcode FsFile} instance. + * + * @category File System + */ export interface FileInfo { /** True if this is info for a regular file. Mutually exclusive to * `FileInfo.isDirectory` and `FileInfo.isSymlink`. */