From 35c240eeed33ebfe671c70f6bcad9947915f0148 Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Fri, 7 Jun 2024 15:54:50 +1200 Subject: [PATCH] docs(fs): improve documentation (#4788) --- _tools/check_docs.ts | 1 + fs/_get_file_info_type.ts | 7 +- fs/_is_same_path.ts | 11 +- fs/_is_subdir.ts | 12 +- fs/_to_path_string.ts | 7 +- fs/copy.ts | 40 +- fs/empty_dir.ts | 33 +- fs/ensure_dir.ts | 38 +- fs/ensure_file.ts | 34 +- fs/ensure_link.ts | 30 +- fs/ensure_symlink.ts | 39 +- fs/eol.ts | 14 +- fs/exists.ts | 50 ++- fs/expand_glob.ts | 190 ++++++++-- fs/mod.ts | 5 +- fs/move.ts | 61 ++- fs/walk.ts | 758 ++++++++++++++++++++++++++++++++++++-- 17 files changed, 1162 insertions(+), 168 deletions(-) diff --git a/_tools/check_docs.ts b/_tools/check_docs.ts index b5e70eeb1..30bbec93e 100644 --- a/_tools/check_docs.ts +++ b/_tools/check_docs.ts @@ -43,6 +43,7 @@ const ENTRY_POINTS = [ "../fmt/duration.ts", "../fmt/printf.ts", "../front_matter/mod.ts", + "../fs/mod.ts", "../html/mod.ts", "../http/mod.ts", "../internal/mod.ts", diff --git a/fs/_get_file_info_type.ts b/fs/_get_file_info_type.ts index 54bebbf47..90235bb3a 100644 --- a/fs/_get_file_info_type.ts +++ b/fs/_get_file_info_type.ts @@ -6,8 +6,11 @@ export type PathType = "file" | "dir" | "symlink"; /** * Get a human readable file type string. * - * @param fileInfo A FileInfo describes a file and is returned by `stat`, - * `lstat` + * @param file File information, as returned by {@linkcode Deno.stat} or + * {@linkcode Deno.lstat}. + * + * @returns The file type as a string, or `undefined` if the file type is + * unknown. */ export function getFileInfoType(fileInfo: Deno.FileInfo): PathType | undefined { return fileInfo.isFile diff --git a/fs/_is_same_path.ts b/fs/_is_same_path.ts index c2bb9607c..23029ae53 100644 --- a/fs/_is_same_path.ts +++ b/fs/_is_same_path.ts @@ -5,14 +5,17 @@ import { resolve } from "@std/path/resolve"; import { toPathString } from "./_to_path_string.ts"; /** - * Test whether `src` and `dest` resolve to the same location - * @param src src file path - * @param dest dest file path + * Checks if two paths are the same. + * + * @param src Source file path as a string or URL. + * @param dest Destination file path as a string or URL. + * + * @returns `true` if the paths are the same, `false` otherwise. */ export function isSamePath( src: string | URL, dest: string | URL, -): boolean | void { +): boolean { src = toPathString(src); dest = toPathString(dest); diff --git a/fs/_is_subdir.ts b/fs/_is_subdir.ts index 2083dc7a2..fa2383a9a 100644 --- a/fs/_is_subdir.ts +++ b/fs/_is_subdir.ts @@ -5,10 +5,14 @@ import { SEPARATOR } from "@std/path/constants"; import { toPathString } from "./_to_path_string.ts"; /** - * Test whether or not `dest` is a sub-directory of `src` - * @param src src file path - * @param dest dest file path - * @param sep path separator + * Checks whether `src` is a sub-directory of `dest`. + * + * @param src Source file path as a string or URL. + * @param dest Destination file path as a string or URL. + * @param sep Path separator. Defaults to `\\` for Windows and `/` for other + * platforms. + * + * @returns `true` if `src` is a sub-directory of `dest`, `false` otherwise. */ export function isSubdir( src: string | URL, diff --git a/fs/_to_path_string.ts b/fs/_to_path_string.ts index 5616ebfdc..36b15098e 100644 --- a/fs/_to_path_string.ts +++ b/fs/_to_path_string.ts @@ -4,8 +4,11 @@ import { fromFileUrl } from "@std/path/from-file-url"; /** - * Convert a URL or string to a path - * @param pathUrl A URL or string to be converted + * Convert a URL or string to a path. + * + * @param pathUrl A URL or string to be converted. + * + * @returns The path as a string. */ export function toPathString( pathUrl: string | URL, diff --git a/fs/copy.ts b/fs/copy.ts index 0072f1923..b4d7b19dc 100644 --- a/fs/copy.ts +++ b/fs/copy.ts @@ -255,22 +255,24 @@ function copyDirSync( } /** - * Asynchronously copy a file or directory. The directory can have contents. - * Like `cp -r`. + * Asynchronously copy a file or directory (along with its contents), like + * {@linkcode https://www.ibm.com/docs/en/aix/7.3?topic=c-cp-command#cp__cp_flagr | cp -r}. * - * If `src` is a directory it will copy everything inside of this directory, - * not the entire directory itself. If `src` is a file, `dest` cannot be a - * directory. + * Both `src` and `dest` must both be a file or directory. * - * Requires the `--allow-read` and `--allow-write` flag. + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param src The source file/directory path as a string or URL. * @param dest The destination file/directory path as a string or URL. * @param options Options for copying. + * * @returns A promise that resolves once the copy operation completes. * * @example Basic usage - * ```ts + * ```ts no-eval * import { copy } from "@std/fs/copy"; * * await copy("./foo", "./bar"); @@ -280,7 +282,7 @@ function copyDirSync( * overwriting. * * @example Overwriting files/directories - * ```ts + * ```ts no-eval * import { copy } from "@std/fs/copy"; * * await copy("./foo", "./bar", { overwrite: true }); @@ -290,7 +292,7 @@ function copyDirSync( * any existing files or directories. * * @example Preserving timestamps - * ```ts + * ```ts no-eval * import { copy } from "@std/fs/copy"; * * await copy("./foo", "./bar", { preserveTimestamps: true }); @@ -329,22 +331,24 @@ export async function copy( } /** - * Synchronously copy a file or directory. The directory can have contents. - * Like `cp -r`. + * Synchronously copy a file or directory (along with its contents), like + * {@linkcode https://www.ibm.com/docs/en/aix/7.3?topic=c-cp-command#cp__cp_flagr | cp -r}. * - * If `src` is a directory it will copy everything inside of this directory, - * not the entire directory itself. If `src` is a file, `dest` cannot be a - * directory. + * Both `src` and `dest` must both be a file or directory. * - * Requires the `--allow-read` and `--allow-write` flag. + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param src The source file/directory path as a string or URL. * @param dest The destination file/directory path as a string or URL. * @param options Options for copying. + * * @returns A void value that returns once the copy operation completes. * * @example Basic usage - * ```ts + * ```ts no-eval * import { copySync } from "@std/fs/copy"; * * copySync("./foo", "./bar"); @@ -354,7 +358,7 @@ export async function copy( * overwriting. * * @example Overwriting files/directories - * ```ts + * ```ts no-eval * import { copySync } from "@std/fs/copy"; * * copySync("./foo", "./bar", { overwrite: true }); @@ -364,7 +368,7 @@ export async function copy( * any existing files or directories. * * @example Preserving timestamps - * ```ts + * ```ts no-eval * import { copySync } from "@std/fs/copy"; * * copySync("./foo", "./bar", { preserveTimestamps: true }); diff --git a/fs/empty_dir.ts b/fs/empty_dir.ts index ec012105e..d36dc373a 100644 --- a/fs/empty_dir.ts +++ b/fs/empty_dir.ts @@ -3,17 +3,22 @@ import { join } from "@std/path/join"; import { toPathString } from "./_to_path_string.ts"; /** - * Asynchronously ensures that a directory is empty deletes the directory - * contents it is not empty. If the directory does not exist, it is created. - * The directory itself is not deleted. + * Asynchronously ensures that a directory is empty. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the directory does not exist, it is created. The directory itself is not + * deleted. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param dir The path of the directory to empty, as a string or URL. + * * @returns A void promise that resolves once the directory is empty. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { emptyDir } from "@std/fs/empty-dir"; * * await emptyDir("./foo"); @@ -41,16 +46,22 @@ export async function emptyDir(dir: string | URL) { /** * Synchronously ensures that a directory is empty deletes the directory - * contents it is not empty. If the directory does not exist, it is created. - * The directory itself is not deleted. + * contents it is not empty. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the directory does not exist, it is created. The directory itself is not + * deleted. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param dir The path of the directory to empty, as a string or URL. + * * @returns A void value that returns once the directory is empty. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { emptyDirSync } from "@std/fs/empty-dir"; * * emptyDirSync("./foo"); diff --git a/fs/ensure_dir.ts b/fs/ensure_dir.ts index 237e3c44d..e8b75881e 100644 --- a/fs/ensure_dir.ts +++ b/fs/ensure_dir.ts @@ -2,16 +2,23 @@ import { getFileInfoType } from "./_get_file_info_type.ts"; /** - * Asynchronously ensures that the directory exists. If the directory structure - * does not exist, it is created. Like `mkdir -p`. + * Asynchronously ensures that the directory exists, like + * {@linkcode https://www.ibm.com/docs/en/aix/7.3?topic=m-mkdir-command#mkdir__row-d3e133766 | mkdir -p}. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the directory already exists, this function does nothing. If the directory + * does not exist, it is created. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param dir The path of the directory to ensure, as a string or URL. + * * @returns A promise that resolves once the directory exists. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { ensureDir } from "@std/fs/ensure-dir"; * * await ensureDir("./bar"); @@ -55,19 +62,26 @@ export async function ensureDir(dir: string | URL) { } /** - * Synchronously ensures that the directory exists. If the directory structure - * does not exist, it is created. Like `mkdir -p`. + * Synchronously ensures that the directory exists, like + * {@linkcode https://www.ibm.com/docs/en/aix/7.3?topic=m-mkdir-command#mkdir__row-d3e133766 | mkdir -p}. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the directory already exists, this function does nothing. If the directory + * does not exist, it is created. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param dir The path of the directory to ensure, as a string or URL. + * * @returns A void value that returns once the directory exists. * - * @example - * ```ts - * import { ensureDir } from "@std/fs/ensure-dir"; + * @example Usage + * ```ts no-eval + * import { ensureDirSync } from "@std/fs/ensure-dir"; * - * await ensureDir("./bar"); + * ensureDirSync("./bar"); * ``` */ export function ensureDirSync(dir: string | URL) { diff --git a/fs/ensure_file.ts b/fs/ensure_file.ts index 4f986745e..615e92875 100644 --- a/fs/ensure_file.ts +++ b/fs/ensure_file.ts @@ -5,17 +5,22 @@ import { getFileInfoType } from "./_get_file_info_type.ts"; import { toPathString } from "./_to_path_string.ts"; /** - * Asynchronously ensures that the file exists. If the file that is requested to - * be created is in directories that do not exist, these directories are created. - * If the file already exists, it is not modified. + * Asynchronously ensures that the file exists. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the file already exists, this function does nothing. If the parent + * directories for the file do not exist, they are created. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param filePath The path of the file to ensure, as a string or URL. + * * @returns A void promise that resolves once the file exists. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { ensureFile } from "@std/fs/ensure-file"; * * await ensureFile("./folder/targetFile.dat"); @@ -45,17 +50,22 @@ export async function ensureFile(filePath: string | URL): Promise { } /** - * Synchronously ensures that the file exists. If the file that is requested to - * be created is in directories that do not exist, these directories are created. - * If the file already exists, it is not modified. + * Synchronously ensures that the file exists. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the file already exists, this function does nothing. If the parent + * directories for the file do not exist, they are created. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param filePath The path of the file to ensure, as a string or URL. + * * @returns A void value that returns once the file exists. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { ensureFileSync } from "@std/fs/ensure-file"; * * ensureFileSync("./folder/targetFile.dat"); diff --git a/fs/ensure_link.ts b/fs/ensure_link.ts index 8ca453f2f..0fc63ad6b 100644 --- a/fs/ensure_link.ts +++ b/fs/ensure_link.ts @@ -4,16 +4,23 @@ import { ensureDir, ensureDirSync } from "./ensure_dir.ts"; import { toPathString } from "./_to_path_string.ts"; /** - * Asynchronously ensures that the hard link exists. If the directory structure - * does not exist, it is created. + * Asynchronously ensures that the hard link exists. + * + * If the parent directories for the hard link do not exist, they are created. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param src The source file path as a string or URL. Directory hard links are * not allowed. * @param dest The destination link path as a string or URL. + * * @returns A void promise that resolves once the hard link exists. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { ensureLink } from "@std/fs/ensure-link"; * * await ensureLink("./folder/targetFile.dat", "./folder/targetFile.link.dat"); @@ -27,16 +34,23 @@ export async function ensureLink(src: string | URL, dest: string | URL) { } /** - * Synchronously ensures that the hard link exists. If the directory structure - * does not exist, it is created. + * Synchronously ensures that the hard link exists. + * + * If the parent directories for the hard link do not exist, they are created. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param src The source file path as a string or URL. Directory hard links are * not allowed. * @param dest The destination link path as a string or URL. + * * @returns A void value that returns once the hard link exists. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { ensureLinkSync } from "@std/fs/ensure-link"; * * ensureLinkSync("./folder/targetFile.dat", "./folder/targetFile.link.dat"); diff --git a/fs/ensure_symlink.ts b/fs/ensure_symlink.ts index 859c4b5c5..ecde54974 100644 --- a/fs/ensure_symlink.ts +++ b/fs/ensure_symlink.ts @@ -17,19 +17,25 @@ function resolveSymlinkTarget(target: string | URL, linkName: string | URL) { } /** - * Asynchronously ensures that the link exists, and points to a valid file. If - * the directory structure does not exist, it is created. If the link already - * exists, it is not modified but error is thrown if it is not point to the - * given target. + * Asynchronously ensures that the link exists, and points to a valid file. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the parent directories for the link do not exist, they are created. If the + * link already exists, and it is not modified, this function does nothing. If + * the link already exists, and it does not point to the given target, an error + * is thrown. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param target The source file path as a string or URL. * @param linkName The destination link path as a string or URL. + * * @returns A void promise that resolves once the link exists. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { ensureSymlink } from "@std/fs/ensure-symlink"; * * await ensureSymlink("./folder/targetFile.dat", "./folder/targetFile.link.dat"); @@ -75,19 +81,24 @@ export async function ensureSymlink( } /** - * Synchronously ensures that the link exists, and points to a valid file. If - * the directory structure does not exist, it is created. If the link already - * exists, it is not modified but error is thrown if it is not point to the - * given target. + * Synchronously ensures that the link exists, and points to a valid file. * - * Requires the `--allow-read` and `--allow-write` flag. + * If the parent directories for the link do not exist, they are created. If the + * link already exists, and it is not modified, this function does nothing. If + * the link already exists, and it does not point to the given target, an error + * is thrown. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param target The source file path as a string or URL. * @param linkName The destination link path as a string or URL. * @returns A void value that returns once the link exists. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { ensureSymlinkSync } from "@std/fs/ensure-symlink"; * * ensureSymlinkSync("./folder/targetFile.dat", "./folder/targetFile.link.dat"); diff --git a/fs/eol.ts b/fs/eol.ts index 701359efb..630505ce3 100644 --- a/fs/eol.ts +++ b/fs/eol.ts @@ -9,8 +9,8 @@ export const CRLF = "\r\n" as const; /** * End-of-line character evaluated for the current platform. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { EOL } from "@std/fs/eol"; * * EOL; // "\n" on POSIX platforms and "\r\n" on Windows @@ -25,10 +25,11 @@ const regDetect = /(?:\r?\n)/g; * character is detected, `null` is returned. * * @param content The input string to detect EOL characters. + * * @returns The detected EOL character(s) or `null` if no EOL character is detected. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { detect } from "@std/fs/eol"; * * detect("deno\r\nis not\r\nnode"); // "\r\n" @@ -52,10 +53,11 @@ export function detect(content: string): typeof EOL | null { * * @param content The input string to normalize. * @param eol The EOL character(s) to normalize the input string to. + * * @returns The input string normalized to the targeted EOL. * - * @example - * ```ts + * @example Usage + * ```ts no-eval * import { LF, format } from "@std/fs/eol"; * * const CRLFinput = "deno\r\nis not\r\nnode"; diff --git a/fs/exists.ts b/fs/exists.ts index 81bd8babc..d8a52503e 100644 --- a/fs/exists.ts +++ b/fs/exists.ts @@ -33,15 +33,22 @@ export interface ExistsOptions { * file operation directly. This function is not recommended for this use case. * See the recommended method below. * - * @see https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use + * @see {@link https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use} for + * more information on the time-of-check to time-of-use bug. + * + * Requires `--allow-read` and `--allow-sys` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param path The path to the file or directory, as a string or URL. * @param options Additional options for the check. + * * @returns A promise that resolves with `true` if the path exists, `false` * otherwise. * * @example Recommended method - * ```ts + * ```ts no-eval * // Notice no use of exists * try { * await Deno.remove("./foo", { recursive: true }); @@ -54,10 +61,10 @@ export interface ExistsOptions { * ``` * * Notice that `exists()` is not used in the above example. Doing so avoids a - * possible race condition. See the above section for details. + * possible race condition. See the above note for details. * * @example Basic usage - * ```ts + * ```ts no-eval * import { exists } from "@std/fs/exists"; * * await exists("./exists"); // true @@ -65,7 +72,7 @@ export interface ExistsOptions { * ``` * * @example Check if a path is readable - * ```ts + * ```ts no-eval * import { exists } from "@std/fs/exists"; * * await exists("./readable", { isReadable: true }); // true @@ -73,7 +80,7 @@ export interface ExistsOptions { * ``` * * @example Check if a path is a directory - * ```ts + * ```ts no-eval * import { exists } from "@std/fs/exists"; * * await exists("./directory", { isDirectory: true }); // true @@ -81,7 +88,7 @@ export interface ExistsOptions { * ``` * * @example Check if a path is a file - * ```ts + * ```ts no-eval * import { exists } from "@std/fs/exists"; * * await exists("./file", { isFile: true }); // true @@ -89,7 +96,7 @@ export interface ExistsOptions { * ``` * * @example Check if a path is a readable directory - * ```ts + * ```ts no-eval * import { exists } from "@std/fs/exists"; * * await exists("./readable_directory", { isReadable: true, isDirectory: true }); // true @@ -97,7 +104,7 @@ export interface ExistsOptions { * ``` * * @example Check if a path is a readable file - * ```ts + * ```ts no-eval * import { exists } from "@std/fs/exists"; * * await exists("./readable_file", { isReadable: true, isFile: true }); // true @@ -164,14 +171,21 @@ export async function exists( * file operation directly. This function is not recommended for this use case. * See the recommended method below. * - * @see https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use + * @see {@link https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use} for + * more information on the time-of-check to time-of-use bug. + * + * Requires `--allow-read` and `--allow-sys` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param path The path to the file or directory, as a string or URL. * @param options Additional options for the check. + * * @returns `true` if the path exists, `false` otherwise. * * @example Recommended method - * ```ts + * ```ts no-eval * // Notice no use of exists * try { * Deno.removeSync("./foo", { recursive: true }); @@ -184,10 +198,10 @@ export async function exists( * ``` * * Notice that `existsSync()` is not used in the above example. Doing so avoids - * a possible race condition. See the above section for details. + * a possible race condition. See the above note for details. * * @example Basic usage - * ```ts + * ```ts no-eval * import { existsSync } from "@std/fs/exists"; * * existsSync("./exists"); // true @@ -195,7 +209,7 @@ export async function exists( * ``` * * @example Check if a path is readable - * ```ts + * ```ts no-eval * import { existsSync } from "@std/fs/exists"; * * existsSync("./readable", { isReadable: true }); // true @@ -203,7 +217,7 @@ export async function exists( * ``` * * @example Check if a path is a directory - * ```ts + * ```ts no-eval * import { existsSync } from "@std/fs/exists"; * * existsSync("./directory", { isDirectory: true }); // true @@ -211,7 +225,7 @@ export async function exists( * ``` * * @example Check if a path is a file - * ```ts + * ```ts no-eval * import { existsSync } from "@std/fs/exists"; * * existsSync("./file", { isFile: true }); // true @@ -219,7 +233,7 @@ export async function exists( * ``` * * @example Check if a path is a readable directory - * ```ts + * ```ts no-eval * import { existsSync } from "@std/fs/exists"; * * existsSync("./readable_directory", { isReadable: true, isDirectory: true }); // true @@ -227,7 +241,7 @@ export async function exists( * ``` * * @example Check if a path is a readable file - * ```ts + * ```ts no-eval * import { existsSync } from "@std/fs/exists"; * * existsSync("./readable_file", { isReadable: true, isFile: true }); // true diff --git a/fs/expand_glob.ts b/fs/expand_glob.ts index c5bd647a9..6e995268b 100644 --- a/fs/expand_glob.ts +++ b/fs/expand_glob.ts @@ -80,14 +80,20 @@ function comparePath(a: WalkEntry, b: WalkEntry): number { /** * Returns an async iterator that yields each file path matching the given glob - * pattern. The file paths are relative to the provided `root` directory. - * If `root` is not provided, the current working directory is used. - * The `root` directory is not included in the yielded file paths. + * pattern. * - * Requires the `--allow-read` flag. + * The file paths are absolute paths. If `root` is not provided, the current + * working directory is used. The `root` directory is not included in the + * yielded file paths. + * + * Requires `--allow-read` permission. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param glob The glob pattern to expand. * @param options Additional options for the expansion. + * * @returns An async iterator that yields each walk entry matching the glob * pattern. * @@ -100,31 +106,163 @@ function comparePath(a: WalkEntry, b: WalkEntry): number { * └── foo.ts * ``` * - * ```ts + * ```ts no-eval * // script.ts * import { expandGlob } from "@std/fs/expand-glob"; * - * const entries = []; - * for await (const entry of expandGlob("*.ts")) { - * entries.push(entry); - * } + * await Array.fromAsync(expandGlob("*.ts")); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false, + * // }, + * // { + * // path: "/Users/user/folder/foo.ts", + * // name: "foo.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false, + * // }, + * // ] + * ``` * - * entries[0]!.path; // "/Users/user/folder/script.ts" - * entries[0]!.name; // "script.ts" - * entries[0]!.isFile; // false - * entries[0]!.isDirectory; // true - * entries[0]!.isSymlink; // false + * @example Define root directory * - * entries[1]!.path; // "/Users/user/folder/foo.ts" - * entries[1]!.name; // "foo.ts" - * entries[1]!.isFile; // true - * entries[1]!.isDirectory; // false - * entries[1]!.isSymlink; // false + * Setting the `root` option to `/folder` will expand the glob pattern from the + * `/folder` directory. + * + * File structure: + * ``` + * folder + * ├── subdir + * │ └── bar.ts + * ├── script.ts + * └── foo.ts + * ``` + * + * ```ts no-eval + * // script.ts + * import { expandGlob } from "@std/fs/expand-glob"; + * + * await Array.fromAsync(expandGlob("*.ts", { root: "./subdir" })); + * // [ + * // { + * // path: "/Users/user/folder/subdir/bar.ts", + * // name: "bar.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false, + * // }, + * // ] + * ``` + * + * @example Exclude files + * + * Setting the `exclude` option to `["foo.ts"]` will exclude the `foo.ts` file + * from the expansion. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo.ts + * ``` + * + * ```ts no-eval + * // script.ts + * import { expandGlob } from "@std/fs/expand-glob"; + * + * await Array.fromAsync(expandGlob("*.ts", { exclude: ["foo.ts"] })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "true.ts", + * // isFile: false, + * // isDirectory: false, + * // isSymlink: false, + * // }, + * // ] + * ``` + * + * @example Exclude directories + * + * Setting the `includeDirs` option to `false` will exclude directories from the + * expansion. + * + * File structure: + * ``` + * folder + * ├── subdir + * │ └── bar.ts + * ├── script.ts + * └── foo.ts + * ``` + * + * ```ts no-eval + * // script.ts + * import { expandGlob } from "@std/fs/expand-glob"; + * + * await Array.fromAsync(expandGlob("*", { includeDirs: false })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false, + * // }, + * // { + * // path: "/Users/user/folder/foo.ts", + * // name: "foo.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false, + * // }, + * // ] + * ``` + * + * @example Follow symbolic links + * + * Setting the `followSymlinks` option to `true` will follow symbolic links. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── link.ts -> script.ts (symbolic link) + * ``` + * + * ```ts no-eval + * // script.ts + * import { expandGlob } from "@std/fs/expand-glob"; + * + * await Array.fromAsync(expandGlob("*.ts", { followSymlinks: true })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false, + * // }, + * // { + * // path: "/Users/user/folder/symlink", + * // name: "symlink", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: true, + * // }, + * // ] * ``` */ export async function* expandGlob( glob: string | URL, - { + options: ExpandGlobOptions = {}, +): AsyncIterableIterator { + let { root, exclude = [], includeDirs = true, @@ -133,8 +271,8 @@ export async function* expandGlob( caseInsensitive, followSymlinks, canonicalize, - }: ExpandGlobOptions = {}, -): AsyncIterableIterator { + } = options; + const { segments, isAbsolute: isGlobAbsolute, @@ -247,11 +385,15 @@ export async function* expandGlob( * * Requires the `--allow-read` flag. * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. + * * @param glob The glob pattern to expand. * @param options Additional options for the expansion. + * * @returns An iterator that yields each walk entry matching the glob pattern. * - * @example Basic usage + * @example Usage * * File structure: * ``` @@ -260,7 +402,7 @@ export async function* expandGlob( * └── foo.ts * ``` * - * ```ts + * ```ts no-eval * // script.ts * import { expandGlobSync } from "@std/fs/expand-glob"; * diff --git a/fs/mod.ts b/fs/mod.ts index da46758ec..4b996a1ab 100644 --- a/fs/mod.ts +++ b/fs/mod.ts @@ -3,12 +3,15 @@ /** * Helpers for working with the filesystem. * - * ```ts + * ```ts no-eval * import { ensureFile, copy, ensureDir, move } from "@std/fs"; * * await ensureFile("example.txt"); + * * await copy("example.txt", "example_copy.txt"); + * * await ensureDir("subdir"); + * * await move("example_copy.txt", "subdir/example_copy.txt"); * ``` * diff --git a/fs/move.ts b/fs/move.ts index 97d6261bb..0555e2b71 100644 --- a/fs/move.ts +++ b/fs/move.ts @@ -5,11 +5,36 @@ import { isSamePath } from "./_is_same_path.ts"; const EXISTS_ERROR = new Deno.errors.AlreadyExists("dest already exists."); /** - * Error thrown in {@linkcode move} or {@linkcode moveSync} when the - * destination is a subdirectory of the source. + * Error thrown in {@linkcode move} or {@linkcode moveSync} when the destination + * is a subdirectory of the source. + * + * @example Usage + * ```ts no-eval + * import { move, SubdirectoryMoveError } from "@std/fs/move"; + * + * try { + * await move("./foo", "./foo/bar"); + * } catch (error) { + * if (error instanceof SubdirectoryMoveError) { + * console.error(error.message); + * } + * } + * ``` */ export class SubdirectoryMoveError extends Error { - /** Constructs a new instance. */ + /** + * Constructs a new instance. + * + * @param src The source file or directory as a string or URL. + * @param dest The destination file or directory as a string or URL. + * + * @example Usage + * ```ts no-eval + * import { SubdirectoryMoveError } from "@std/fs/move"; + * + * throw new SubdirectoryMoveError("./foo", "./foo/bar"); + * ``` + */ constructor(src: string | URL, dest: string | URL) { super( `Cannot move '${src}' to a subdirectory of itself, '${dest}'.`, @@ -29,15 +54,24 @@ export interface MoveOptions { } /** - * Asynchronously moves a file or directory. + * Asynchronously moves a file or directory (along with its contents). + * + * If `src` is a sub-directory of `dest`, a {@linkcode SubdirectoryMoveError} + * will be thrown. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param src The source file or directory as a string or URL. * @param dest The destination file or directory as a string or URL. * @param options Options for the move operation. + * * @returns A void promise that resolves once the operation completes. * * @example Basic usage - * ```ts + * ```ts no-eval * import { move } from "@std/fs/move"; * * await move("./foo", "./bar"); @@ -47,7 +81,7 @@ export interface MoveOptions { * overwriting. * * @example Overwriting - * ```ts + * ```ts no-eval * import { move } from "@std/fs/move"; * * await move("./foo", "./bar", { overwrite: true }); @@ -92,15 +126,24 @@ export async function move( } /** - * Synchronously moves a file or directory. + * Synchronously moves a file or directory (along with its contents). + * + * If `src` is a sub-directory of `dest`, a {@linkcode SubdirectoryMoveError} + * will be thrown. + * + * Requires `--allow-read` and `--allow-write` permissions. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. * * @param src The source file or directory as a string or URL. * @param dest The destination file or directory as a string or URL. * @param options Options for the move operation. + * * @returns A void value that returns once the operation completes. * * @example Basic usage - * ```ts + * ```ts no-eval * import { moveSync } from "@std/fs/move"; * * moveSync("./foo", "./bar"); @@ -110,7 +153,7 @@ export async function move( * overwriting. * * @example Overwriting - * ```ts + * ```ts no-eval * import { moveSync } from "@std/fs/move"; * * moveSync("./foo", "./bar", { overwrite: true }); diff --git a/fs/walk.ts b/fs/walk.ts index 2f420f38d..fd59c9ee6 100644 --- a/fs/walk.ts +++ b/fs/walk.ts @@ -11,12 +11,53 @@ import { type WalkEntry, } from "./_create_walk_entry.ts"; -/** Error thrown in {@linkcode walk} or {@linkcode walkSync} during iteration. */ +/** + * Error thrown in {@linkcode walk} or {@linkcode walkSync} during iteration. + * + * @example Usage + * ```ts no-eval + * import { walk, WalkError } from "@std/fs/walk"; + * + * try { + * for await (const entry of walk("./non_existent_root")) { + * console.log(entry.path); + * } + * } catch (error) { + * if (error instanceof WalkError) { + * console.error(error.message); + * } + * } + * ``` + */ export class WalkError extends Error { - /** File path of the root that's being walked. */ + /** + * File path of the root that's being walked. + * + * @example Usage + * ```ts + * import { WalkError } from "@std/fs/walk"; + * import { assertEquals } from "@std/assert/assert-equals"; + * + * const error = new WalkError("error message", "./foo"); + * + * assertEquals(error.root, "./foo"); + * ``` + */ root: string; - /** Constructs a new instance. */ + /** + * Constructs a new instance. + * + * @param cause The cause of the error. + * @param root The root directory that's being walked. + * + * @example Usage + * ```ts no-eval + * import { WalkError } from "@std/fs/walk"; + * + * throw new WalkError("error message", "./foo"); + * ``` + */ constructor(cause: unknown, root: string) { super( `${cause instanceof Error ? cause.message : cause} for path "${root}"`, @@ -121,9 +162,18 @@ export type { WalkEntry }; * Recursively walks through a directory and yields information about each file * and directory encountered. * + * The file paths are absolute paths. The root directory is included in the + * yielded entries. + * + * Requires `--allow-read` permission. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. + * * @param root The root directory to start the walk from, as a string or URL. * @param options The options for the walk. - * @returns An async iterable iterator that yields `WalkEntry` objects. + * + * @returns An async iterable iterator that yields the walk entry objects. * * @example Basic usage * @@ -134,30 +184,339 @@ export type { WalkEntry }; * └── foo.ts * ``` * - * ```ts + * ```ts no-eval * import { walk } from "@std/fs/walk"; * - * const entries = []; - * for await (const entry of walk(".")) { - * entries.push(entry); - * } + * await Array.fromAsync(walk(".")); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/foo.ts", + * // name: "foo.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` * - * entries[0]!.path; // "folder" - * entries[0]!.name; // "folder" - * entries[0]!.isFile; // false - * entries[0]!.isDirectory; // true - * entries[0]!.isSymlink; // false + * @example Maximum file depth * - * entries[1]!.path; // "folder/script.ts" - * entries[1]!.name; // "script.ts" - * entries[1]!.isFile; // true - * entries[1]!.isDirectory; // false - * entries[1]!.isSymlink; // false + * Setting the `maxDepth` option to `1` will only include the root directory and + * its immediate children. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo + * └── bar.ts + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { maxDepth: 1 })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/foo", + * // name: "foo", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Exclude files + * + * Setting the `includeFiles` option to `false` will exclude files. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { includeFiles: false })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/foo", + * // name: "foo", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false, + * // }, + * // ] + * ``` + * + * @example Exclude directories + * + * Setting the `includeDirs` option to `false` will exclude directories. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { includeDirs: false })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Exclude symbolic links + * + * Setting the `includeSymlinks` option to `false` will exclude symbolic links. + * + * File structure: + * ``` + * folder + * ├── script.ts + * ├── foo + * └── link -> script.ts (symbolic link) + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { includeSymlinks: false })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Follow symbolic links + * + * Setting the `followSymlinks` option to `true` will follow symbolic links, + * affecting the `path` property of the walk entry. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── link -> script.ts (symbolic link) + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { followSymlinks: true })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "link", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: true + * // }, + * // ] + * ``` + * + * @example Canonicalize symbolic links + * + * Setting the `canonicalize` option to `false` will canonicalize the path of + * the followed symbolic link. Meaning, the `path` property of the walk entry + * will be the path of the symbolic link itself. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── link -> script.ts (symbolic link) + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { followSymlinks: true, canonicalize: true })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/link", + * // name: "link", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: true + * // }, + * // ] + * ``` + * + * @example Filter by file extensions + * + * Setting the `exts` option to `[".ts"]` will only include entries with the + * `.ts` file extension. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo.js + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { exts: [".ts"] })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Filter by regular expressions + * + * Setting the `match` option to `[/.s/]` will only include entries with the + * letter `s` in their name. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── README.md + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { match: [/s/] })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Exclude by regular expressions + * + * Setting the `skip` option to `[/.s/]` will exclude entries with the letter + * `s` in their name. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── README.md + * ``` + * + * ```ts no-eval + * import { walk } from "@std/fs/walk"; + * + * await Array.fromAsync(walk(".", { skip: [/s/] })); + * // [ + * // { + * // path: "/Users/user/folder/README.md", + * // name: "README.md", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] * ``` */ export async function* walk( root: string | URL, - { + options: WalkOptions = {}, +): AsyncIterableIterator { + const { maxDepth = Infinity, includeFiles = true, includeDirs = true, @@ -167,8 +526,8 @@ export async function* walk( exts = undefined, match = undefined, skip = undefined, - }: WalkOptions = {}, -): AsyncIterableIterator { + } = options; + if (maxDepth < 0) { return; } @@ -222,7 +581,360 @@ export async function* walk( } } -/** Same as {@linkcode walk} but uses synchronous ops */ +/** + * Recursively walks through a directory and yields information about each file + * and directory encountered. + * + * The file paths are absolute paths. The root directory is included in the + * yielded entries. + * + * Requires `--allow-read` permission. + * + * @see {@link https://docs.deno.com/runtime/manual/basics/permissions#file-system-access} + * for more information on Deno's permissions system. + * + * @param root The root directory to start the walk from, as a string or URL. + * @param options The options for the walk. + * + * @returns A synchronous iterable iterator that yields the walk entry objects. + * + * @example Basic usage + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo.ts + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".")); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/foo.ts", + * // name: "foo.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Maximum file depth + * + * Setting the `maxDepth` option to `1` will only include the root directory and + * its immediate children. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo + * └── bar.ts + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { maxDepth: 1 })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/foo", + * // name: "foo", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Exclude files + * + * Setting the `includeFiles` option to `false` will exclude files. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { includeFiles: false })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/foo", + * // name: "foo", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false, + * // }, + * // ] + * ``` + * + * @example Exclude directories + * + * Setting the `includeDirs` option to `false` will exclude directories. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { includeDirs: false })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Exclude symbolic links + * + * Setting the `includeSymlinks` option to `false` will exclude symbolic links. + * + * File structure: + * ``` + * folder + * ├── script.ts + * ├── foo + * └── link -> script.ts (symbolic link) + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { includeSymlinks: false })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Follow symbolic links + * + * Setting the `followSymlinks` option to `true` will follow symbolic links, + * affecting the `path` property of the walk entry. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── link -> script.ts (symbolic link) + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { followSymlinks: true })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "link", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: true + * // }, + * // ] + * ``` + * + * @example Canonicalize symbolic links + * + * Setting the `canonicalize` option to `false` will canonicalize the path of + * the followed symbolic link. Meaning, the `path` property of the walk entry + * will be the path of the symbolic link itself. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── link -> script.ts (symbolic link) + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { followSymlinks: true, canonicalize: true })); + * // [ + * // { + * // path: "/Users/user/folder", + * // name: "folder", + * // isFile: false, + * // isDirectory: true, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // { + * // path: "/Users/user/folder/link", + * // name: "link", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: true + * // }, + * // ] + * ``` + * + * @example Filter by file extensions + * + * Setting the `exts` option to `[".ts"]` will only include entries with the + * `.ts` file extension. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── foo.js + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { exts: [".ts"] })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Filter by regular expressions + * + * Setting the `match` option to `[/.s/]` will only include entries with the + * letter `s` in their name. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── README.md + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { match: [/s/] })); + * // [ + * // { + * // path: "/Users/user/folder/script.ts", + * // name: "script.ts", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + * + * @example Exclude by regular expressions + * + * Setting the `skip` option to `[/.s/]` will exclude entries with the letter + * `s` in their name. + * + * File structure: + * ``` + * folder + * ├── script.ts + * └── README.md + * ``` + * + * ```ts no-eval + * import { walkSync } from "@std/fs/walk"; + * + * Array.from(walkSync(".", { skip: [/s/] })); + * // [ + * // { + * // path: "/Users/user/folder/README.md", + * // name: "README.md", + * // isFile: true, + * // isDirectory: false, + * // isSymlink: false + * // }, + * // ] + * ``` + */ export function* walkSync( root: string | URL, {