@wtasnorg/node-lib 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEV_CHECKLIST.md +0 -1
- package/README.md +1 -0
- package/docs/README.md +1 -0
- package/docs/docs.json +1001 -6
- package/docs/functions/createFindDirectories.md +38 -0
- package/docs/functions/hello.md +1 -1
- package/docs/functions/pojo.md +3 -3
- package/docs/globals.md +6 -0
- package/docs/interfaces/FileSystemDependencies.md +51 -0
- package/docs/interfaces/FindDirectoriesOptions.md +41 -0
- package/eslint.config.js +47 -0
- package/package.json +9 -3
- package/src/find.d.ts +25 -0
- package/src/find.js +62 -0
- package/src/find.test.d.ts +2 -0
- package/src/find.test.js +123 -0
- package/src/find.test.ts +148 -0
- package/src/find.ts +99 -0
- package/src/index.d.ts +4 -1
- package/src/index.js +2 -1
- package/src/index.ts +9 -1
- package/src/pojo.d.ts +2 -2
- package/src/pojo.js +1 -1
- package/src/pojo.ts +2 -2
- package/test_report +26 -0
- package/typedoc.json +6 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[**@wtasnorg/node-lib**](../README.md)
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
5
|
+
[@wtasnorg/node-lib](../globals.md) / createFindDirectories
|
|
6
|
+
|
|
7
|
+
# Function: createFindDirectories()
|
|
8
|
+
|
|
9
|
+
> **createFindDirectories**(`deps`): (`root`, `options`) => `Promise`\<`string`[]\>
|
|
10
|
+
|
|
11
|
+
Defined in: [find.ts:19](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L19)
|
|
12
|
+
|
|
13
|
+
Factory that produces an async findDirectories function with
|
|
14
|
+
injected filesystem dependencies for full testability.
|
|
15
|
+
|
|
16
|
+
## Parameters
|
|
17
|
+
|
|
18
|
+
### deps
|
|
19
|
+
|
|
20
|
+
[`FileSystemDependencies`](../interfaces/FileSystemDependencies.md)
|
|
21
|
+
|
|
22
|
+
## Returns
|
|
23
|
+
|
|
24
|
+
> (`root`, `options`): `Promise`\<`string`[]\>
|
|
25
|
+
|
|
26
|
+
### Parameters
|
|
27
|
+
|
|
28
|
+
#### root
|
|
29
|
+
|
|
30
|
+
`string`
|
|
31
|
+
|
|
32
|
+
#### options
|
|
33
|
+
|
|
34
|
+
[`FindDirectoriesOptions`](../interfaces/FindDirectoriesOptions.md) = `{}`
|
|
35
|
+
|
|
36
|
+
### Returns
|
|
37
|
+
|
|
38
|
+
`Promise`\<`string`[]\>
|
package/docs/functions/hello.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
> **hello**(): `Promise`\<`string`\>
|
|
10
10
|
|
|
11
|
-
Defined in: [hello.ts:6](https://github.com/wtasg/node-lib/blob/
|
|
11
|
+
Defined in: [hello.ts:6](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/hello.ts#L6)
|
|
12
12
|
|
|
13
13
|
A sample function that should work to test if lib is installed correctly.
|
|
14
14
|
|
package/docs/functions/pojo.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
# Function: pojo()
|
|
8
8
|
|
|
9
|
-
> **pojo**\<`T`\>(`instance`): `Record`\<`string`, `
|
|
9
|
+
> **pojo**\<`T`\>(`instance`): `Record`\<`string`, `unknown`\>
|
|
10
10
|
|
|
11
|
-
Defined in: [pojo.ts:10](https://github.com/wtasg/node-lib/blob/
|
|
11
|
+
Defined in: [pojo.ts:10](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/pojo.ts#L10)
|
|
12
12
|
|
|
13
13
|
Convert a class instance into a plain JavaScript object.
|
|
14
14
|
Copies only the instance's own enumerable data properties
|
|
@@ -30,6 +30,6 @@ A class instance to convert.
|
|
|
30
30
|
|
|
31
31
|
## Returns
|
|
32
32
|
|
|
33
|
-
`Record`\<`string`, `
|
|
33
|
+
`Record`\<`string`, `unknown`\>
|
|
34
34
|
|
|
35
35
|
A plain JavaScript object containing only data fields.
|
package/docs/globals.md
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
# @wtasnorg/node-lib
|
|
6
6
|
|
|
7
|
+
## Interfaces
|
|
8
|
+
|
|
9
|
+
- [FileSystemDependencies](interfaces/FileSystemDependencies.md)
|
|
10
|
+
- [FindDirectoriesOptions](interfaces/FindDirectoriesOptions.md)
|
|
11
|
+
|
|
7
12
|
## Functions
|
|
8
13
|
|
|
14
|
+
- [createFindDirectories](functions/createFindDirectories.md)
|
|
9
15
|
- [hello](functions/hello.md)
|
|
10
16
|
- [pojo](functions/pojo.md)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[**@wtasnorg/node-lib**](../README.md)
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
5
|
+
[@wtasnorg/node-lib](../globals.md) / FileSystemDependencies
|
|
6
|
+
|
|
7
|
+
# Interface: FileSystemDependencies
|
|
8
|
+
|
|
9
|
+
Defined in: [find.ts:3](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L3)
|
|
10
|
+
|
|
11
|
+
## Properties
|
|
12
|
+
|
|
13
|
+
### readdir()
|
|
14
|
+
|
|
15
|
+
> **readdir**: (`path`, `opts`) => `Promise`\<`object`[]\>
|
|
16
|
+
|
|
17
|
+
Defined in: [find.ts:4](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L4)
|
|
18
|
+
|
|
19
|
+
#### Parameters
|
|
20
|
+
|
|
21
|
+
##### path
|
|
22
|
+
|
|
23
|
+
`string`
|
|
24
|
+
|
|
25
|
+
##### opts
|
|
26
|
+
|
|
27
|
+
###### withFileTypes
|
|
28
|
+
|
|
29
|
+
`true`
|
|
30
|
+
|
|
31
|
+
#### Returns
|
|
32
|
+
|
|
33
|
+
`Promise`\<`object`[]\>
|
|
34
|
+
|
|
35
|
+
***
|
|
36
|
+
|
|
37
|
+
### stat()
|
|
38
|
+
|
|
39
|
+
> **stat**: (`path`) => `Promise`\<\{ `isDirectory`: `boolean`; \}\>
|
|
40
|
+
|
|
41
|
+
Defined in: [find.ts:5](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L5)
|
|
42
|
+
|
|
43
|
+
#### Parameters
|
|
44
|
+
|
|
45
|
+
##### path
|
|
46
|
+
|
|
47
|
+
`string`
|
|
48
|
+
|
|
49
|
+
#### Returns
|
|
50
|
+
|
|
51
|
+
`Promise`\<\{ `isDirectory`: `boolean`; \}\>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[**@wtasnorg/node-lib**](../README.md)
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
5
|
+
[@wtasnorg/node-lib](../globals.md) / FindDirectoriesOptions
|
|
6
|
+
|
|
7
|
+
# Interface: FindDirectoriesOptions
|
|
8
|
+
|
|
9
|
+
Defined in: [find.ts:8](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L8)
|
|
10
|
+
|
|
11
|
+
## Properties
|
|
12
|
+
|
|
13
|
+
### allowlist?
|
|
14
|
+
|
|
15
|
+
> `optional` **allowlist**: `string`[] \| (`absPath`, `name`) => `boolean`
|
|
16
|
+
|
|
17
|
+
Defined in: [find.ts:11](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L11)
|
|
18
|
+
|
|
19
|
+
***
|
|
20
|
+
|
|
21
|
+
### blocklist?
|
|
22
|
+
|
|
23
|
+
> `optional` **blocklist**: `string`[] \| (`absPath`, `name`) => `boolean`
|
|
24
|
+
|
|
25
|
+
Defined in: [find.ts:12](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L12)
|
|
26
|
+
|
|
27
|
+
***
|
|
28
|
+
|
|
29
|
+
### followSymlinks?
|
|
30
|
+
|
|
31
|
+
> `optional` **followSymlinks**: `boolean`
|
|
32
|
+
|
|
33
|
+
Defined in: [find.ts:10](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L10)
|
|
34
|
+
|
|
35
|
+
***
|
|
36
|
+
|
|
37
|
+
### maxDepth?
|
|
38
|
+
|
|
39
|
+
> `optional` **maxDepth**: `number`
|
|
40
|
+
|
|
41
|
+
Defined in: [find.ts:9](https://github.com/wtasg/node-lib/blob/5ccb6028429af225c9ab29e1b6007075025c601f/src/find.ts#L9)
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import eslint from "@eslint/js";
|
|
4
|
+
import { defineConfig } from "eslint/config";
|
|
5
|
+
import tseslint from "typescript-eslint";
|
|
6
|
+
import prettier from "eslint-config-prettier";
|
|
7
|
+
import globals from "globals";
|
|
8
|
+
import stylistic from "@stylistic/eslint-plugin";
|
|
9
|
+
|
|
10
|
+
export default defineConfig([
|
|
11
|
+
eslint.configs.recommended,
|
|
12
|
+
...tseslint.configs.recommended,
|
|
13
|
+
...tseslint.configs.strict,
|
|
14
|
+
...tseslint.configs.stylistic,
|
|
15
|
+
prettier,
|
|
16
|
+
|
|
17
|
+
{
|
|
18
|
+
languageOptions: {
|
|
19
|
+
globals: {
|
|
20
|
+
...globals.node,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
{
|
|
26
|
+
plugins: {
|
|
27
|
+
"@stylistic": stylistic
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
rules: {
|
|
33
|
+
"@typescript-eslint/no-unused-vars": [
|
|
34
|
+
"warn",
|
|
35
|
+
{
|
|
36
|
+
argsIgnorePattern: "^_",
|
|
37
|
+
varsIgnorePattern: "^_",
|
|
38
|
+
caughtErrorsIgnorePattern: "^_"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
semi: ["error", "always"],
|
|
43
|
+
// "no-unused-vars": ["error", { varsIgnorePattern: "^[A-Z_]" }],
|
|
44
|
+
"@stylistic/quotes": ["error", "double"]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wtasnorg/node-lib",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "node library",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -27,9 +27,15 @@
|
|
|
27
27
|
"url": "git+https://github.com/wtasg/node-lib.git"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
+
"@eslint/js": "^9.39.1",
|
|
31
|
+
"@stylistic/eslint-plugin": "^5.6.1",
|
|
30
32
|
"@types/node": "^24.10.1",
|
|
31
|
-
"
|
|
33
|
+
"eslint": "^9.39.1",
|
|
34
|
+
"eslint-config-prettier": "^10.1.8",
|
|
35
|
+
"globals": "^16.5.0",
|
|
36
|
+
"typedoc": "^0.28.15",
|
|
32
37
|
"typedoc-plugin-markdown": "^4.9.0",
|
|
33
|
-
"typescript": "^5.9.3"
|
|
38
|
+
"typescript": "^5.9.3",
|
|
39
|
+
"typescript-eslint": "^8.48.1"
|
|
34
40
|
}
|
|
35
41
|
}
|
package/src/find.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface FileSystemDependencies {
|
|
2
|
+
readdir: (path: string, opts: {
|
|
3
|
+
withFileTypes: true;
|
|
4
|
+
}) => Promise<{
|
|
5
|
+
name: string;
|
|
6
|
+
isDirectory(): boolean;
|
|
7
|
+
}[]>;
|
|
8
|
+
stat: (path: string) => Promise<{
|
|
9
|
+
isDirectory(): boolean;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
interface FindDirectoriesOptions {
|
|
13
|
+
maxDepth?: number;
|
|
14
|
+
followSymlinks?: boolean;
|
|
15
|
+
allowlist?: string[] | ((absPath: string, name: string) => boolean);
|
|
16
|
+
blocklist?: string[] | ((absPath: string, name: string) => boolean);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Factory that produces an async findDirectories function with
|
|
20
|
+
* injected filesystem dependencies for full testability.
|
|
21
|
+
*/
|
|
22
|
+
declare function createFindDirectories(deps: FileSystemDependencies): (root: string, options?: FindDirectoriesOptions) => Promise<string[]>;
|
|
23
|
+
export { createFindDirectories, };
|
|
24
|
+
export type { FileSystemDependencies, FindDirectoriesOptions, };
|
|
25
|
+
//# sourceMappingURL=find.d.ts.map
|
package/src/find.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { resolve, join } from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* Factory that produces an async findDirectories function with
|
|
4
|
+
* injected filesystem dependencies for full testability.
|
|
5
|
+
*/
|
|
6
|
+
function createFindDirectories(deps) {
|
|
7
|
+
const { readdir } = deps;
|
|
8
|
+
return async function findDirectories(root, options = {}) {
|
|
9
|
+
const { maxDepth = 1, allowlist, blocklist } = options;
|
|
10
|
+
const absRoot = resolve(root);
|
|
11
|
+
const results = [];
|
|
12
|
+
function isAllowed(absPath, name) {
|
|
13
|
+
if (allowlist) {
|
|
14
|
+
if (Array.isArray(allowlist)) {
|
|
15
|
+
if (!allowlist.includes(name))
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
else if (!allowlist(absPath, name)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function isBlocked(absPath, name) {
|
|
25
|
+
if (blocklist) {
|
|
26
|
+
if (Array.isArray(blocklist)) {
|
|
27
|
+
if (blocklist.includes(name))
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
else if (blocklist(absPath, name)) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
async function walk(currentPath, depth) {
|
|
37
|
+
if (depth > maxDepth)
|
|
38
|
+
return;
|
|
39
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
const childPath = resolve(join(currentPath, entry.name));
|
|
42
|
+
const isDirectory = entry.isDirectory();
|
|
43
|
+
if (!isDirectory)
|
|
44
|
+
continue;
|
|
45
|
+
if (isBlocked(childPath, entry.name))
|
|
46
|
+
continue;
|
|
47
|
+
if (!isAllowed(childPath, entry.name))
|
|
48
|
+
continue;
|
|
49
|
+
results.push(childPath);
|
|
50
|
+
if (depth < maxDepth) {
|
|
51
|
+
await walk(childPath, depth + 1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// match find(1): root is always included
|
|
56
|
+
results.push(absRoot);
|
|
57
|
+
await walk(absRoot, 1);
|
|
58
|
+
return results;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export { createFindDirectories, };
|
|
62
|
+
//# sourceMappingURL=find.js.map
|
package/src/find.test.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { createFindDirectories } from "./find.js";
|
|
4
|
+
// Helper to build Dirent-like objects
|
|
5
|
+
function dir(name) {
|
|
6
|
+
return { name, isDirectory: () => true };
|
|
7
|
+
}
|
|
8
|
+
function file(name) {
|
|
9
|
+
return { name, isDirectory: () => false };
|
|
10
|
+
}
|
|
11
|
+
test("find: findDirectories success", async () => {
|
|
12
|
+
// Minimal mock FS: /root contains one directory "a" and one file "b"
|
|
13
|
+
const mockFS = {
|
|
14
|
+
readdir: async (path) => {
|
|
15
|
+
if (path === "/root") {
|
|
16
|
+
return [
|
|
17
|
+
{ name: "a", isDirectory: () => true },
|
|
18
|
+
{ name: "b", isDirectory: () => false },
|
|
19
|
+
];
|
|
20
|
+
}
|
|
21
|
+
return [];
|
|
22
|
+
},
|
|
23
|
+
stat: async (path) => ({
|
|
24
|
+
isDirectory: () => path === "/root" || path === "/root/a"
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
const findDirectories = createFindDirectories(mockFS);
|
|
28
|
+
const result = await findDirectories("/root", { maxDepth: 1 });
|
|
29
|
+
assert.deepEqual(result, [
|
|
30
|
+
"/root",
|
|
31
|
+
"/root/a"
|
|
32
|
+
]);
|
|
33
|
+
});
|
|
34
|
+
test("find: findDirectories symlink is NOT treated as a directory", async () => {
|
|
35
|
+
const mockFS = {
|
|
36
|
+
readdir: async (path) => {
|
|
37
|
+
if (path === "/root") {
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
name: "linkToA",
|
|
41
|
+
isDirectory: () => false
|
|
42
|
+
}
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
},
|
|
47
|
+
stat: async (path) => ({
|
|
48
|
+
isDirectory: () => path === "/root" // only root is a directory
|
|
49
|
+
}),
|
|
50
|
+
readlink: async () => {
|
|
51
|
+
throw new Error("readlink should not be called when symlink support is removed");
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const findDirectories = createFindDirectories(mockFS);
|
|
55
|
+
const result = await findDirectories("/root", { maxDepth: 1 });
|
|
56
|
+
assert.deepEqual(result, [
|
|
57
|
+
"/root"
|
|
58
|
+
]);
|
|
59
|
+
});
|
|
60
|
+
test("find: findDirectories at depth=2", async () => {
|
|
61
|
+
const deps = {
|
|
62
|
+
readdir: async (path) => {
|
|
63
|
+
switch (path) {
|
|
64
|
+
case "/root":
|
|
65
|
+
return [dir("a"), dir("b"), file("ignore.txt")];
|
|
66
|
+
case "/root/a":
|
|
67
|
+
return [dir("a1")];
|
|
68
|
+
case "/root/b":
|
|
69
|
+
return [dir("b1")];
|
|
70
|
+
default:
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
stat: async () => ({
|
|
75
|
+
isDirectory: () => false
|
|
76
|
+
})
|
|
77
|
+
};
|
|
78
|
+
const find = createFindDirectories(deps);
|
|
79
|
+
const result = await find("/root", { maxDepth: 2 });
|
|
80
|
+
assert.deepEqual(result.sort(), [
|
|
81
|
+
"/root",
|
|
82
|
+
"/root/a",
|
|
83
|
+
"/root/b",
|
|
84
|
+
"/root/a/a1",
|
|
85
|
+
"/root/b/b1"
|
|
86
|
+
].sort());
|
|
87
|
+
});
|
|
88
|
+
test("find: findDirectories at depth=3", async () => {
|
|
89
|
+
const deps = {
|
|
90
|
+
readdir: async (path) => {
|
|
91
|
+
switch (path) {
|
|
92
|
+
case "/root":
|
|
93
|
+
return [dir("a"), dir("b")];
|
|
94
|
+
case "/root/a":
|
|
95
|
+
return [dir("a1")];
|
|
96
|
+
case "/root/b":
|
|
97
|
+
return [dir("b1")];
|
|
98
|
+
case "/root/a/a1":
|
|
99
|
+
case "/root/b/b1":
|
|
100
|
+
return [dir("c")];
|
|
101
|
+
case "/root/b/b1/c":
|
|
102
|
+
return [dir("d"), file("fd")];
|
|
103
|
+
default:
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
stat: async () => ({
|
|
108
|
+
isDirectory: () => false
|
|
109
|
+
})
|
|
110
|
+
};
|
|
111
|
+
const find = createFindDirectories(deps);
|
|
112
|
+
const result = await find("/root", { maxDepth: 3 });
|
|
113
|
+
assert.deepEqual(result.sort(), [
|
|
114
|
+
"/root",
|
|
115
|
+
"/root/a",
|
|
116
|
+
"/root/b",
|
|
117
|
+
"/root/a/a1",
|
|
118
|
+
"/root/a/a1/c",
|
|
119
|
+
"/root/b/b1",
|
|
120
|
+
"/root/b/b1/c",
|
|
121
|
+
].sort());
|
|
122
|
+
});
|
|
123
|
+
//# sourceMappingURL=find.test.js.map
|
package/src/find.test.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
|
|
4
|
+
import { createFindDirectories } from "./find.js";
|
|
5
|
+
|
|
6
|
+
import type { FileSystemDependencies } from "./find.js";
|
|
7
|
+
|
|
8
|
+
// Helper to build Dirent-like objects
|
|
9
|
+
function dir(name: string): { name: string; isDirectory(): boolean } {
|
|
10
|
+
return { name, isDirectory: () => true };
|
|
11
|
+
}
|
|
12
|
+
function file(name: string): { name: string; isDirectory(): boolean } {
|
|
13
|
+
return { name, isDirectory: () => false };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test("find: findDirectories success", async () => {
|
|
17
|
+
|
|
18
|
+
// Minimal mock FS: /root contains one directory "a" and one file "b"
|
|
19
|
+
const mockFS = {
|
|
20
|
+
readdir: async (path: string) => {
|
|
21
|
+
if (path === "/root") {
|
|
22
|
+
return [
|
|
23
|
+
{ name: "a", isDirectory: () => true },
|
|
24
|
+
{ name: "b", isDirectory: () => false },
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
return [];
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
stat: async (path: string) => ({
|
|
31
|
+
isDirectory: () => path === "/root" || path === "/root/a"
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const findDirectories = createFindDirectories(mockFS);
|
|
36
|
+
|
|
37
|
+
const result = await findDirectories("/root", { maxDepth: 1 });
|
|
38
|
+
|
|
39
|
+
assert.deepEqual(result, [
|
|
40
|
+
"/root",
|
|
41
|
+
"/root/a"
|
|
42
|
+
]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("find: findDirectories symlink is NOT treated as a directory", async () => {
|
|
46
|
+
|
|
47
|
+
const mockFS = {
|
|
48
|
+
readdir: async (path: string) => {
|
|
49
|
+
if (path === "/root") {
|
|
50
|
+
return [
|
|
51
|
+
{
|
|
52
|
+
name: "linkToA",
|
|
53
|
+
isDirectory: () => false
|
|
54
|
+
}
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
stat: async (path: string) => ({
|
|
61
|
+
isDirectory: () => path === "/root" // only root is a directory
|
|
62
|
+
}),
|
|
63
|
+
|
|
64
|
+
readlink: async () => {
|
|
65
|
+
throw new Error("readlink should not be called when symlink support is removed");
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const findDirectories = createFindDirectories(mockFS);
|
|
70
|
+
|
|
71
|
+
const result = await findDirectories("/root", { maxDepth: 1 });
|
|
72
|
+
|
|
73
|
+
assert.deepEqual(result, [
|
|
74
|
+
"/root"
|
|
75
|
+
]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
test("find: findDirectories at depth=2", async () => {
|
|
80
|
+
const deps: FileSystemDependencies = {
|
|
81
|
+
readdir: async (path: string): Promise<{ name: string; isDirectory(): boolean }[]> => {
|
|
82
|
+
switch (path) {
|
|
83
|
+
case "/root":
|
|
84
|
+
return [dir("a"), dir("b"), file("ignore.txt")];
|
|
85
|
+
case "/root/a":
|
|
86
|
+
return [dir("a1")];
|
|
87
|
+
case "/root/b":
|
|
88
|
+
return [dir("b1")];
|
|
89
|
+
default:
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
stat: async () => ({
|
|
94
|
+
isDirectory: () => false
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const find = createFindDirectories(deps);
|
|
99
|
+
|
|
100
|
+
const result = await find("/root", { maxDepth: 2 });
|
|
101
|
+
|
|
102
|
+
assert.deepEqual(result.sort(), [
|
|
103
|
+
"/root",
|
|
104
|
+
"/root/a",
|
|
105
|
+
"/root/b",
|
|
106
|
+
"/root/a/a1",
|
|
107
|
+
"/root/b/b1"
|
|
108
|
+
].sort());
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("find: findDirectories at depth=3", async () => {
|
|
112
|
+
const deps: FileSystemDependencies = {
|
|
113
|
+
readdir: async (path: string): Promise<{ name: string; isDirectory(): boolean }[]> => {
|
|
114
|
+
switch (path) {
|
|
115
|
+
case "/root":
|
|
116
|
+
return [dir("a"), dir("b")];
|
|
117
|
+
case "/root/a":
|
|
118
|
+
return [dir("a1")];
|
|
119
|
+
case "/root/b":
|
|
120
|
+
return [dir("b1")];
|
|
121
|
+
case "/root/a/a1":
|
|
122
|
+
case "/root/b/b1":
|
|
123
|
+
return [dir("c")];
|
|
124
|
+
case "/root/b/b1/c":
|
|
125
|
+
return [dir("d"), file("fd")];
|
|
126
|
+
default:
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
stat: async () => ({
|
|
131
|
+
isDirectory: () => false
|
|
132
|
+
})
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const find = createFindDirectories(deps);
|
|
136
|
+
|
|
137
|
+
const result = await find("/root", { maxDepth: 3 });
|
|
138
|
+
|
|
139
|
+
assert.deepEqual(result.sort(), [
|
|
140
|
+
"/root",
|
|
141
|
+
"/root/a",
|
|
142
|
+
"/root/b",
|
|
143
|
+
"/root/a/a1",
|
|
144
|
+
"/root/a/a1/c",
|
|
145
|
+
"/root/b/b1",
|
|
146
|
+
"/root/b/b1/c",
|
|
147
|
+
].sort());
|
|
148
|
+
});
|