Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fs/unstable): add statSync and lstatSync #6300

Merged
merged 1 commit into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions _tools/check_docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 5 additions & 2 deletions fs/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
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");

Check warning on line 28 in fs/_utils.ts

View check run for this annotation

Codecov / codecov/patch

fs/_utils.ts#L26-L28

Added lines #L26 - L28 were not covered by tests
}
3 changes: 2 additions & 1 deletion fs/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
52 changes: 44 additions & 8 deletions fs/unstable_lstat.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,68 @@
// 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";
* const fileInfo = await lstat("README.md");
* 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<FileInfo> {
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);
}
}

Check warning on line 35 in fs/unstable_lstat.ts

View check run for this annotation

Codecov / codecov/patch

fs/unstable_lstat.ts#L31-L35

Added lines #L31 - L35 were not covered by tests
}

/**
* 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));

Check warning on line 65 in fs/unstable_lstat.ts

View check run for this annotation

Codecov / codecov/patch

fs/unstable_lstat.ts#L64-L65

Added lines #L64 - L65 were not covered by tests
} catch (error) {
throw mapError(error);
}
Expand Down
50 changes: 34 additions & 16 deletions fs/unstable_lstat_test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
50 changes: 42 additions & 8 deletions fs/unstable_stat.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,67 @@
// 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";
* const fileInfo = await stat("README.md");
* 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<FileInfo> {
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);
}
}

Check warning on line 36 in fs/unstable_stat.ts

View check run for this annotation

Codecov / codecov/patch

fs/unstable_stat.ts#L32-L36

Added lines #L32 - L36 were not covered by tests
}

/**
* 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));

Check warning on line 64 in fs/unstable_stat.ts

View check run for this annotation

Codecov / codecov/patch

fs/unstable_stat.ts#L63-L64

Added lines #L63 - L64 were not covered by tests
} catch (error) {
throw mapError(error);
}
Expand Down
34 changes: 24 additions & 10 deletions fs/unstable_stat_test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
8 changes: 8 additions & 0 deletions fs/unstable_types.ts
Original file line number Diff line number Diff line change
@@ -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`. */
Expand Down
Loading