Skip to content

Commit

Permalink
Add find()
Browse files Browse the repository at this point in the history
  • Loading branch information
Hexagon committed Mar 28, 2024
1 parent e86c3a6 commit 1ecce9c
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 16 deletions.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ will cover most scenarios, this library focuses on the file system operations.
Example:

```
import { exists } from "@cross/fs/stat";
import { exists, find } from "@cross/fs/stat";
// Check if my/file exists
console.log(await exists("my/file"));
// -> true / false
// false
// Search for package.json recursively, starting from parent folder
console.log(await find("../", (path) => path.endsWith("package.json")));
// ["/home/.../package.json","/home/.../.../package.json"]
```

Methods:
Expand All @@ -34,12 +39,13 @@ Methods:
| --------- | ---- | ---- | --- | ------------------- |
| stat | X | X | X | runtime native |
| lstat | X | X | X | node:fs/promises |
| exists | X | X | X | custom native |
| isDir | X | X | X | custom native |
| isFile | X | X | X | custom native |
| isSymlink | X | X | X | custom native |
| size | X | X | X | custom native |
| diskusage | X | X | X | custom native |
| exists | X | X | X | custom |
| isDir | X | X | X | custom |
| isFile | X | X | X | custom |
| isSymlink | X | X | X | custom |
| size | X | X | X | custom |
| find | X | X | X | custom |
| diskusage | X | X | X | custom |

### Io

Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cross/fs",
"version": "0.0.5",
"version": "0.0.6",
"exports": {
".": "./mod.ts",
"./stat": "./src/stat/mod.ts",
Expand Down
67 changes: 67 additions & 0 deletions src/stat/find.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { readdir } from "node:fs/promises";
import { stat, StatResult } from "./mod.ts";
import { join, resolve } from "@std/path";

/**
* Recursively finds files and directories within a specified path, optionally applying advanced filtering.
*
* @param inPath - The starting directory path for the search.
* @param fileFilter - A filter function that takes the absolute file/directory path and its
* stat result as arguments. The function should return `true` if the item should be included
* in the results and `false` otherwise.
* @param recursive - Whether to search subdirectories recursively (default: `true`).
* @returns A `Promise` resolving to an array of absolute paths matching the filter criteria.
*
* **Examples:**
*
* **Simple Usage (Filename filtering):**
* ```typescript
* console.log(await find("../", (path) => path.endsWith("package.json")));
* ```
*
* **Advanced Usage (Filtering based on file type, size, etc.):**
* ```typescript
* console.log(await find("./documents", (path, stat) =>
* stat.isFile() && stat.size > 1024 * 1024 // Find files larger than 1MB
* ));
* ```
*/
export async function find(
inPath: string,
fileFilter: (path: string, stat: StatResult) => boolean,
recursive: boolean = true,
): Promise<string[]> {
const statSelf = await stat(inPath);
const resolvedPath = resolve(inPath);
if (statSelf.isFile || statSelf.isSymlink) {
if (!fileFilter || fileFilter(resolvedPath, statSelf)) {
return [resolvedPath]; // Return the file path directly
} else {
return []; // File doesn't match the filter
}
}
if (statSelf.isDirectory) {
// Include the directory itself if it passes the filter
let results: string[] = [];
if (fileFilter(resolvedPath, statSelf)) {
results.push(resolvedPath);
}
const files = await readdir(resolvedPath, { withFileTypes: true });
const paths: Promise<string[]>[] = files.map((file) => {
const path = join(resolvedPath, file.name);
return find(path, fileFilter, recursive);
});
const promiseResults = await Promise.allSettled(paths);
results = results.concat(
promiseResults
.filter((result): result is PromiseFulfilledResult<string[]> =>
result.status === "fulfilled"
)
.map((result) => result.value)
.flat(),
);
return results;
}

return []; // Not a directory or file
}
1 change: 1 addition & 0 deletions src/stat/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,4 @@ export { lstat } from "node:fs/promises";
export * from "./is.ts";
export * from "./exists.ts";
export * from "./size.ts";
export * from "./find.ts";
29 changes: 22 additions & 7 deletions src/stat/size.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { readdir, stat } from "node:fs/promises";
import { readdir } from "node:fs/promises";
import { stat } from "./mod.ts";
import { join } from "@std/path";

/**
* Calculates the actual disk usage of a file or directory (considering file system block sizes).
*
* @param inPath - The path to the file or directory.
* @param recursive - If `true`, calculates disk usage recursively for directories. Defaults to `false`.
* @returns The total disk usage in bytes.
*/
export async function diskusage(
inPath: string,
recursive?: boolean,
): Promise<number> {
const statSelf = await stat(inPath);
const blockSize = statSelf.blksize;
const actualSize = Math.max(blockSize, statSelf.size);
if (statSelf.isFile()) {
const actualSize = Math.max(blockSize!, statSelf.size);
if (statSelf.isFile) {
return actualSize;
}
if (statSelf.isDirectory()) {
if (statSelf.isDirectory) {
const files = await readdir(inPath, { withFileTypes: true });
const paths: Promise<number>[] = files.map((file) => {
const path = join(inPath, file.name);
return size(path, recursive);
return diskusage(path, recursive);
});
const results = await Promise.allSettled(paths);
const sizes = results
Expand All @@ -27,15 +35,22 @@ export async function diskusage(
return actualSize;
}

/**
* Calculates the total size of a file or directory.
*
* @param inPath - The path to the file or directory.
* @param recursive - If `true`, calculates size recursively for directories. Defaults to `false`.
* @returns The total size in bytes.
*/
export async function size(
inPath: string,
recursive?: boolean,
): Promise<number> {
const statSelf = await stat(inPath);
if (statSelf.isFile()) {
if (statSelf.isFile) {
return statSelf.size;
}
if (statSelf.isDirectory()) {
if (statSelf.isDirectory) {
const files = await readdir(inPath, { withFileTypes: true });
const paths: Promise<number>[] = files.map((file) => {
const path = join(inPath, file.name);
Expand Down

0 comments on commit 1ecce9c

Please sign in to comment.