tiny-readdir 2.2.0 → 2.3.0

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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /* IMPORT */
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import { isFunction } from './utils.js';
4
+ import { isFunction, makeCounterPromise } from './utils.js';
5
5
  /* MAIN */
6
6
  const readdir = (rootPath, options) => {
7
7
  const followSymlinks = options?.followSymlinks ?? false;
@@ -11,46 +11,56 @@ const readdir = (rootPath, options) => {
11
11
  const isIgnored = isFunction(ignore) ? ignore : (targetPath) => ignore.test(targetPath);
12
12
  const signal = options?.signal ?? { aborted: false };
13
13
  const directories = [];
14
+ const directoriesNames = new Set();
14
15
  const files = [];
16
+ const filesNames = new Set();
15
17
  const symlinks = [];
18
+ const symlinksNames = new Set();
16
19
  const map = {};
17
20
  const visited = new Set();
18
- const resultEmpty = { directories: [], files: [], symlinks: [], map: {} };
19
- const result = { directories, files, symlinks, map };
21
+ const resultEmpty = { directories: [], directoriesNames: new Set(), files: [], filesNames: new Set(), symlinks: [], symlinksNames: new Set(), map: {} };
22
+ const result = { directories, directoriesNames, files, filesNames, symlinks, symlinksNames, map };
23
+ const { promise, increment, decrement } = makeCounterPromise();
20
24
  let foundPaths = 0;
21
- const handleDirectory = (dirmap, subPath, depth) => {
25
+ const handleDirectory = (dirmap, subPath, name, depth) => {
22
26
  if (visited.has(subPath))
23
27
  return;
24
28
  if (foundPaths >= maxPaths)
25
29
  return;
26
30
  foundPaths += 1;
27
31
  dirmap.directories.push(subPath);
32
+ dirmap.directoriesNames.add(name);
28
33
  directories.push(subPath);
34
+ directoriesNames.add(name);
29
35
  visited.add(subPath);
30
36
  if (depth >= maxDepth)
31
37
  return;
32
38
  if (foundPaths >= maxPaths)
33
39
  return;
34
- return populateResultFromPath(subPath, depth + 1);
40
+ populateResultFromPath(subPath, depth + 1);
35
41
  };
36
- const handleFile = (dirmap, subPath) => {
42
+ const handleFile = (dirmap, subPath, name) => {
37
43
  if (visited.has(subPath))
38
44
  return;
39
45
  if (foundPaths >= maxPaths)
40
46
  return;
41
47
  foundPaths += 1;
42
48
  dirmap.files.push(subPath);
49
+ dirmap.filesNames.add(name);
43
50
  files.push(subPath);
51
+ filesNames.add(name);
44
52
  visited.add(subPath);
45
53
  };
46
- const handleSymlink = (dirmap, subPath, depth) => {
54
+ const handleSymlink = (dirmap, subPath, name, depth) => {
47
55
  if (visited.has(subPath))
48
56
  return;
49
57
  if (foundPaths >= maxPaths)
50
58
  return;
51
59
  foundPaths += 1;
52
60
  dirmap.symlinks.push(subPath);
61
+ dirmap.symlinksNames.add(name);
53
62
  symlinks.push(subPath);
63
+ symlinksNames.add(name);
54
64
  visited.add(subPath);
55
65
  if (!followSymlinks)
56
66
  return;
@@ -58,73 +68,90 @@ const readdir = (rootPath, options) => {
58
68
  return;
59
69
  if (foundPaths >= maxPaths)
60
70
  return;
61
- return populateResultFromSymlink(subPath, depth + 1);
71
+ populateResultFromSymlink(subPath, depth + 1);
62
72
  };
63
- const handleStat = (dirmap, rootPath, stat, depth) => {
73
+ const handleStat = (dirmap, rootPath, name, stat, depth) => {
64
74
  if (signal.aborted)
65
75
  return;
66
76
  if (isIgnored(rootPath))
67
77
  return;
68
78
  if (stat.isDirectory()) {
69
- return handleDirectory(dirmap, rootPath, depth);
79
+ handleDirectory(dirmap, rootPath, name, depth);
70
80
  }
71
81
  else if (stat.isFile()) {
72
- return handleFile(dirmap, rootPath);
82
+ handleFile(dirmap, rootPath, name);
73
83
  }
74
84
  else if (stat.isSymbolicLink()) {
75
- return handleSymlink(dirmap, rootPath, depth);
85
+ handleSymlink(dirmap, rootPath, name, depth);
76
86
  }
77
87
  };
78
88
  const handleDirent = (dirmap, rootPath, dirent, depth) => {
79
89
  if (signal.aborted)
80
90
  return;
81
91
  const separator = (rootPath === path.sep) ? '' : path.sep;
82
- const subPath = `${rootPath}${separator}${dirent.name}`;
92
+ const name = dirent.name;
93
+ const subPath = `${rootPath}${separator}${name}`;
83
94
  if (isIgnored(subPath))
84
95
  return;
85
96
  if (dirent.isDirectory()) {
86
- return handleDirectory(dirmap, subPath, depth);
97
+ handleDirectory(dirmap, subPath, name, depth);
87
98
  }
88
99
  else if (dirent.isFile()) {
89
- return handleFile(dirmap, subPath);
100
+ handleFile(dirmap, subPath, name);
90
101
  }
91
102
  else if (dirent.isSymbolicLink()) {
92
- return handleSymlink(dirmap, subPath, depth);
103
+ handleSymlink(dirmap, subPath, name, depth);
93
104
  }
94
105
  };
95
106
  const handleDirents = (dirmap, rootPath, dirents, depth) => {
96
- return Promise.all(dirents.map((dirent) => {
97
- return handleDirent(dirmap, rootPath, dirent, depth);
98
- }));
107
+ for (let i = 0, l = dirents.length; i < l; i++) {
108
+ handleDirent(dirmap, rootPath, dirents[i], depth);
109
+ }
99
110
  };
100
- const populateResultFromPath = async (rootPath, depth) => {
111
+ const populateResultFromPath = (rootPath, depth) => {
101
112
  if (signal.aborted)
102
113
  return;
103
114
  if (depth > maxDepth)
104
115
  return;
105
116
  if (foundPaths >= maxPaths)
106
117
  return;
107
- const dirents = await fs.promises.readdir(rootPath, { withFileTypes: true }).catch(() => []);
108
- if (signal.aborted)
109
- return;
110
- const dirmap = map[rootPath] = { directories: [], files: [], symlinks: [] };
111
- if (!dirents.length)
112
- return;
113
- await handleDirents(dirmap, rootPath, dirents, depth);
118
+ increment();
119
+ fs.readdir(rootPath, { withFileTypes: true }, (error, dirents) => {
120
+ if (error)
121
+ return decrement();
122
+ if (signal.aborted)
123
+ return decrement();
124
+ if (!dirents.length)
125
+ return decrement();
126
+ const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set(), files: [], filesNames: new Set(), symlinks: [], symlinksNames: new Set() };
127
+ handleDirents(dirmap, rootPath, dirents, depth);
128
+ decrement();
129
+ });
114
130
  };
115
131
  const populateResultFromSymlink = async (rootPath, depth) => {
116
- try {
117
- const realPath = await fs.promises.realpath(rootPath);
118
- const stat = await fs.promises.stat(realPath);
119
- const dirmap = map[rootPath] = { directories: [], files: [], symlinks: [] };
120
- await handleStat(dirmap, realPath, stat, depth);
121
- }
122
- catch { }
132
+ increment();
133
+ fs.realpath(rootPath, (error, realPath) => {
134
+ if (error)
135
+ return decrement();
136
+ if (signal.aborted)
137
+ return decrement();
138
+ fs.stat(realPath, async (error, stat) => {
139
+ if (error)
140
+ return decrement();
141
+ if (signal.aborted)
142
+ return decrement();
143
+ const name = path.basename(realPath);
144
+ const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set(), files: [], filesNames: new Set(), symlinks: [], symlinksNames: new Set() };
145
+ handleStat(dirmap, realPath, name, stat, depth);
146
+ decrement();
147
+ });
148
+ });
123
149
  };
124
150
  const getResult = async (rootPath, depth = 1) => {
125
151
  rootPath = path.normalize(rootPath);
126
152
  visited.add(rootPath);
127
- await populateResultFromPath(rootPath, depth);
153
+ populateResultFromPath(rootPath, depth);
154
+ await promise;
128
155
  if (signal.aborted)
129
156
  return resultEmpty;
130
157
  return result;
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- type Promisable<T> = Promise<T> | T;
1
+ type Callback = () => void;
2
2
  type Options = {
3
3
  depth?: number;
4
4
  limit?: number;
@@ -10,8 +10,11 @@ type Options = {
10
10
  };
11
11
  type ResultDirectory = {
12
12
  directories: string[];
13
+ directoriesNames: Set<string>;
13
14
  files: string[];
15
+ filesNames: Set<string>;
14
16
  symlinks: string[];
17
+ symlinksNames: Set<string>;
15
18
  };
16
19
  type ResultDirectories = {
17
20
  [path: string]: ResultDirectory;
@@ -19,4 +22,4 @@ type ResultDirectories = {
19
22
  type Result = ResultDirectory & {
20
23
  map: ResultDirectories;
21
24
  };
22
- export type { Promisable, Options, ResultDirectory, ResultDirectories, Result };
25
+ export type { Callback, Options, ResultDirectory, ResultDirectories, Result };
package/dist/utils.d.ts CHANGED
@@ -1,2 +1,8 @@
1
+ import type { Callback } from './types';
1
2
  declare const isFunction: (value: unknown) => value is Function;
2
- export { isFunction };
3
+ declare const makeCounterPromise: () => {
4
+ promise: Promise<void>;
5
+ increment: Callback;
6
+ decrement: Callback;
7
+ };
8
+ export { isFunction, makeCounterPromise };
package/dist/utils.js CHANGED
@@ -1,6 +1,22 @@
1
+ /* IMPORT */
2
+ import makeNakedPromise from 'promise-make-naked';
1
3
  /* MAIN */
2
4
  const isFunction = (value) => {
3
5
  return (typeof value === 'function');
4
6
  };
7
+ const makeCounterPromise = () => {
8
+ const { promise, resolve } = makeNakedPromise();
9
+ let counter = 0;
10
+ const increment = () => {
11
+ counter += 1;
12
+ };
13
+ const decrement = () => {
14
+ counter -= 1;
15
+ if (counter)
16
+ return;
17
+ resolve();
18
+ };
19
+ return { promise, increment, decrement };
20
+ };
5
21
  /* EXPORT */
6
- export { isFunction };
22
+ export { isFunction, makeCounterPromise };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tiny-readdir",
3
3
  "repository": "github:fabiospampinato/tiny-readdir",
4
4
  "description": "A simple promisified recursive readdir function.",
5
- "version": "2.2.0",
5
+ "version": "2.3.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "exports": "./dist/index.js",
@@ -11,10 +11,9 @@
11
11
  "clean": "tsex clean",
12
12
  "compile": "tsex compile",
13
13
  "compile:watch": "tsex compile --watch",
14
- "test": "npm run test:native && npm run test:yielding",
15
- "test:native": "fava '**/native.js'",
16
- "test:yielding": "node test/yielding.js",
17
- "prepublishOnly": "npm run clean && npm run compile && npm run test"
14
+ "test": "tsex test",
15
+ "test:watch": "tsex test --watch",
16
+ "prepublishOnly": "tsex prepare"
18
17
  },
19
18
  "keywords": [
20
19
  "readdir",
@@ -23,10 +22,13 @@
23
22
  "simple",
24
23
  "tiny"
25
24
  ],
25
+ "dependencies": {
26
+ "promise-make-naked": "^2.1.1"
27
+ },
26
28
  "devDependencies": {
27
- "@types/node": "^18.11.9",
28
- "fava": "^0.0.7",
29
- "tsex": "^1.1.3",
30
- "typescript": "^4.9.3"
29
+ "@types/node": "^20.4.8",
30
+ "fava": "^0.2.1",
31
+ "tsex": "^3.0.1",
32
+ "typescript": "^5.1.6"
31
33
  }
32
34
  }
package/readme.md CHANGED
@@ -27,6 +27,10 @@ console.log ( result.directories ); // => Array of absolute paths pointing to di
27
27
  console.log ( result.files ); // => Array of absolute paths pointing to files
28
28
  console.log ( result.symlinks ); // => Array of absolute paths pointing to symlinks
29
29
 
30
+ console.log ( result.directoriesNames ); // => Set of directories names found
31
+ console.log ( result.filesNames ); // => Set of files name found
32
+ console.log ( result.symlinksNames ); // => Set of symlinks names found
33
+
30
34
  setTimeout ( () => aborter.abort (), 10000 ); // Aborting if it's going to take longer than 10s
31
35
  ```
32
36
 
package/src/index.ts CHANGED
@@ -3,8 +3,8 @@
3
3
 
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
- import {isFunction} from './utils';
7
- import type {Promisable, Options, ResultDirectory, ResultDirectories, Result} from './types';
6
+ import {isFunction, makeCounterPromise} from './utils';
7
+ import type {Options, ResultDirectory, ResultDirectories, Result} from './types';
8
8
 
9
9
  /* MAIN */
10
10
 
@@ -17,16 +17,20 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
17
17
  const isIgnored = isFunction ( ignore ) ? ignore : ( targetPath: string ) => ignore.test ( targetPath );
18
18
  const signal = options?.signal ?? { aborted: false };
19
19
  const directories: string[] = [];
20
+ const directoriesNames: Set<string> = new Set ();
20
21
  const files: string[] = [];
22
+ const filesNames: Set<string> = new Set ();
21
23
  const symlinks: string[] = [];
24
+ const symlinksNames: Set<string> = new Set ();
22
25
  const map: ResultDirectories = {};
23
26
  const visited = new Set<string> ();
24
- const resultEmpty: Result = { directories: [], files: [], symlinks: [], map: {} };
25
- const result: Result = { directories, files, symlinks, map };
27
+ const resultEmpty: Result = { directories: [], directoriesNames: new Set (), files: [], filesNames: new Set (), symlinks: [], symlinksNames: new Set (), map: {} };
28
+ const result: Result = { directories, directoriesNames, files, filesNames, symlinks, symlinksNames, map };
29
+ const {promise, increment, decrement} = makeCounterPromise ();
26
30
 
27
31
  let foundPaths = 0;
28
32
 
29
- const handleDirectory = ( dirmap: ResultDirectory, subPath: string, depth: number ): Promisable<void> => {
33
+ const handleDirectory = ( dirmap: ResultDirectory, subPath: string, name: string, depth: number ): void => {
30
34
 
31
35
  if ( visited.has ( subPath ) ) return;
32
36
 
@@ -34,18 +38,20 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
34
38
 
35
39
  foundPaths += 1;
36
40
  dirmap.directories.push ( subPath );
41
+ dirmap.directoriesNames.add ( name );
37
42
  directories.push ( subPath );
43
+ directoriesNames.add ( name );
38
44
  visited.add ( subPath );
39
45
 
40
46
  if ( depth >= maxDepth ) return;
41
47
 
42
48
  if ( foundPaths >= maxPaths ) return;
43
49
 
44
- return populateResultFromPath ( subPath, depth + 1 );
50
+ populateResultFromPath ( subPath, depth + 1 );
45
51
 
46
52
  };
47
53
 
48
- const handleFile = ( dirmap: ResultDirectory, subPath: string ): void => {
54
+ const handleFile = ( dirmap: ResultDirectory, subPath: string, name: string ): void => {
49
55
 
50
56
  if ( visited.has ( subPath ) ) return;
51
57
 
@@ -53,12 +59,14 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
53
59
 
54
60
  foundPaths += 1;
55
61
  dirmap.files.push ( subPath );
62
+ dirmap.filesNames.add ( name );
56
63
  files.push ( subPath );
64
+ filesNames.add ( name );
57
65
  visited.add ( subPath );
58
66
 
59
67
  };
60
68
 
61
- const handleSymlink = ( dirmap: ResultDirectory, subPath: string, depth: number ): Promisable<void> => {
69
+ const handleSymlink = ( dirmap: ResultDirectory, subPath: string, name: string, depth: number ): void => {
62
70
 
63
71
  if ( visited.has ( subPath ) ) return;
64
72
 
@@ -66,7 +74,9 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
66
74
 
67
75
  foundPaths += 1;
68
76
  dirmap.symlinks.push ( subPath );
77
+ dirmap.symlinksNames.add ( name );
69
78
  symlinks.push ( subPath );
79
+ symlinksNames.add ( name );
70
80
  visited.add ( subPath );
71
81
 
72
82
  if ( !followSymlinks ) return;
@@ -75,11 +85,11 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
75
85
 
76
86
  if ( foundPaths >= maxPaths ) return;
77
87
 
78
- return populateResultFromSymlink ( subPath, depth + 1 );
88
+ populateResultFromSymlink ( subPath, depth + 1 );
79
89
 
80
90
  };
81
91
 
82
- const handleStat = ( dirmap: ResultDirectory, rootPath: string, stat: fs.Stats, depth: number ): Promisable<void> => {
92
+ const handleStat = ( dirmap: ResultDirectory, rootPath: string, name: string, stat: fs.Stats, depth: number ): void => {
83
93
 
84
94
  if ( signal.aborted ) return;
85
95
 
@@ -87,56 +97,57 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
87
97
 
88
98
  if ( stat.isDirectory () ) {
89
99
 
90
- return handleDirectory ( dirmap, rootPath, depth );
100
+ handleDirectory ( dirmap, rootPath, name, depth );
91
101
 
92
102
  } else if ( stat.isFile () ) {
93
103
 
94
- return handleFile ( dirmap, rootPath );
104
+ handleFile ( dirmap, rootPath, name );
95
105
 
96
106
  } else if ( stat.isSymbolicLink () ) {
97
107
 
98
- return handleSymlink ( dirmap, rootPath, depth );
108
+ handleSymlink ( dirmap, rootPath, name, depth );
99
109
 
100
110
  }
101
111
 
102
112
  };
103
113
 
104
- const handleDirent = ( dirmap: ResultDirectory, rootPath: string, dirent: fs.Dirent, depth: number ): Promisable<void> => {
114
+ const handleDirent = ( dirmap: ResultDirectory, rootPath: string, dirent: fs.Dirent, depth: number ): void => {
105
115
 
106
116
  if ( signal.aborted ) return;
107
117
 
108
118
  const separator = ( rootPath === path.sep ) ? '' : path.sep;
109
- const subPath = `${rootPath}${separator}${dirent.name}`;
119
+ const name = dirent.name;
120
+ const subPath = `${rootPath}${separator}${name}`;
110
121
 
111
122
  if ( isIgnored ( subPath ) ) return;
112
123
 
113
124
  if ( dirent.isDirectory () ) {
114
125
 
115
- return handleDirectory ( dirmap, subPath, depth );
126
+ handleDirectory ( dirmap, subPath, name, depth );
116
127
 
117
128
  } else if ( dirent.isFile () ) {
118
129
 
119
- return handleFile ( dirmap, subPath );
130
+ handleFile ( dirmap, subPath, name );
120
131
 
121
132
  } else if ( dirent.isSymbolicLink () ) {
122
133
 
123
- return handleSymlink ( dirmap, subPath, depth );
134
+ handleSymlink ( dirmap, subPath, name, depth );
124
135
 
125
136
  }
126
137
 
127
138
  };
128
139
 
129
- const handleDirents = ( dirmap: ResultDirectory, rootPath: string, dirents: fs.Dirent[], depth: number ): Promise<void[]> => {
140
+ const handleDirents = ( dirmap: ResultDirectory, rootPath: string, dirents: fs.Dirent[], depth: number ): void => {
130
141
 
131
- return Promise.all ( dirents.map ( ( dirent ): Promisable<void> => {
142
+ for ( let i = 0, l = dirents.length; i < l; i++ ) {
132
143
 
133
- return handleDirent ( dirmap, rootPath, dirent, depth );
144
+ handleDirent ( dirmap, rootPath, dirents[i], depth );
134
145
 
135
- }));
146
+ }
136
147
 
137
148
  };
138
149
 
139
- const populateResultFromPath = async ( rootPath: string, depth: number ): Promise<void> => {
150
+ const populateResultFromPath = ( rootPath: string, depth: number ): void => {
140
151
 
141
152
  if ( signal.aborted ) return;
142
153
 
@@ -144,29 +155,52 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
144
155
 
145
156
  if ( foundPaths >= maxPaths ) return;
146
157
 
147
- const dirents = await fs.promises.readdir ( rootPath, { withFileTypes: true } ).catch ( () => [] );
158
+ increment ();
148
159
 
149
- if ( signal.aborted ) return;
160
+ fs.readdir ( rootPath, { withFileTypes: true }, ( error, dirents ) => {
161
+
162
+ if ( error ) return decrement ();
163
+
164
+ if ( signal.aborted ) return decrement ();
150
165
 
151
- const dirmap = map[rootPath] = { directories: [], files: [], symlinks: [] };
166
+ if ( !dirents.length ) return decrement ();
152
167
 
153
- if ( !dirents.length ) return;
168
+ const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set (), files: [], filesNames: new Set (), symlinks: [], symlinksNames: new Set () };
154
169
 
155
- await handleDirents ( dirmap, rootPath, dirents, depth );
170
+ handleDirents ( dirmap, rootPath, dirents, depth );
171
+
172
+ decrement ();
173
+
174
+ });
156
175
 
157
176
  };
158
177
 
159
178
  const populateResultFromSymlink = async ( rootPath: string, depth: number ): Promise<void> => {
160
179
 
161
- try {
180
+ increment ();
181
+
182
+ fs.realpath ( rootPath, ( error, realPath ) => {
183
+
184
+ if ( error ) return decrement ();
162
185
 
163
- const realPath = await fs.promises.realpath ( rootPath );
164
- const stat = await fs.promises.stat ( realPath );
165
- const dirmap = map[rootPath] = { directories: [], files: [], symlinks: [] };
186
+ if ( signal.aborted ) return decrement ();
166
187
 
167
- await handleStat ( dirmap, realPath, stat, depth );
188
+ fs.stat ( realPath, async ( error, stat ) => {
168
189
 
169
- } catch {}
190
+ if ( error ) return decrement ();
191
+
192
+ if ( signal.aborted ) return decrement ();
193
+
194
+ const name = path.basename ( realPath );
195
+ const dirmap = map[rootPath] = { directories: [], directoriesNames: new Set (), files: [], filesNames: new Set (), symlinks: [], symlinksNames: new Set () };
196
+
197
+ handleStat ( dirmap, realPath, name, stat, depth );
198
+
199
+ decrement ();
200
+
201
+ });
202
+
203
+ });
170
204
 
171
205
  };
172
206
 
@@ -176,7 +210,9 @@ const readdir = ( rootPath: string, options?: Options ): Promise<Result> => {
176
210
 
177
211
  visited.add ( rootPath );
178
212
 
179
- await populateResultFromPath ( rootPath, depth );
213
+ populateResultFromPath ( rootPath, depth );
214
+
215
+ await promise;
180
216
 
181
217
  if ( signal.aborted ) return resultEmpty;
182
218
 
package/src/types.ts CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /* HELPERS */
3
3
 
4
- type Promisable<T> = Promise<T> | T;
4
+ type Callback = () => void;
5
5
 
6
6
  /* MAIN */
7
7
 
@@ -15,8 +15,11 @@ type Options = {
15
15
 
16
16
  type ResultDirectory = {
17
17
  directories: string[],
18
+ directoriesNames: Set<string>,
18
19
  files: string[],
19
- symlinks: string[]
20
+ filesNames: Set<string>,
21
+ symlinks: string[],
22
+ symlinksNames: Set<string>
20
23
  };
21
24
 
22
25
  type ResultDirectories = {
@@ -29,4 +32,4 @@ type Result = ResultDirectory & {
29
32
 
30
33
  /* EXPORT */
31
34
 
32
- export type {Promisable, Options, ResultDirectory, ResultDirectories, Result};
35
+ export type {Callback, Options, ResultDirectory, ResultDirectories, Result};
package/src/utils.ts CHANGED
@@ -1,4 +1,9 @@
1
1
 
2
+ /* IMPORT */
3
+
4
+ import makeNakedPromise from 'promise-make-naked';
5
+ import type {Callback} from './types';
6
+
2
7
  /* MAIN */
3
8
 
4
9
  const isFunction = ( value: unknown ): value is Function => {
@@ -7,6 +12,32 @@ const isFunction = ( value: unknown ): value is Function => {
7
12
 
8
13
  };
9
14
 
15
+ const makeCounterPromise = (): { promise: Promise<void>, increment: Callback, decrement: Callback } => {
16
+
17
+ const {promise, resolve} = makeNakedPromise<void> ();
18
+
19
+ let counter = 0;
20
+
21
+ const increment = (): void => {
22
+
23
+ counter += 1;
24
+
25
+ };
26
+
27
+ const decrement = (): void => {
28
+
29
+ counter -= 1;
30
+
31
+ if ( counter ) return;
32
+
33
+ resolve ();
34
+
35
+ };
36
+
37
+ return { promise, increment, decrement };
38
+
39
+ };
40
+
10
41
  /* EXPORT */
11
42
 
12
- export {isFunction};
43
+ export {isFunction, makeCounterPromise};
@@ -6,6 +6,10 @@ import fs from 'node:fs';
6
6
  import path from 'node:path';
7
7
  import readdir from '../dist/index.js';
8
8
 
9
+ /* HELPERS */
10
+
11
+ const toBasename = filePath => path.basename ( filePath );
12
+
9
13
  /* MAIN */
10
14
 
11
15
  describe ( 'Tiny Readdir', it => {
@@ -41,43 +45,67 @@ describe ( 'Tiny Readdir', it => {
41
45
 
42
46
  const expected = {
43
47
  directories: [folder1Path, folder2Path, folder1DeepPath, root2Path],
48
+ directoriesNames: new Set ( [folder1Path, folder2Path, folder1DeepPath, root2Path].map ( toBasename ) ),
44
49
  files: [file1aPath, file1bPath, file2Path, fileDeep1Path],
50
+ filesNames: new Set ( [file1aPath, file1bPath, file2Path, fileDeep1Path].map ( toBasename ) ),
45
51
  symlinks: [symlink1FromPath, symlink2FromPath],
52
+ symlinksNames: new Set ( [symlink1FromPath, symlink2FromPath].map ( toBasename ) ),
46
53
  map: {
47
54
  [root1Path]: {
48
55
  directories: [folder1Path, folder2Path],
56
+ directoriesNames: new Set ( [folder1Path, folder2Path].map ( toBasename ) ),
49
57
  files: [],
50
- symlinks: [symlink1FromPath]
58
+ filesNames: new Set (),
59
+ symlinks: [symlink1FromPath],
60
+ symlinksNames: new Set ( [symlink1FromPath].map ( toBasename ) )
51
61
  },
52
62
  [root2Path]: {
53
63
  directories: [],
64
+ directoriesNames: new Set (),
54
65
  files: [],
55
- symlinks: [symlink2FromPath]
66
+ filesNames: new Set (),
67
+ symlinks: [symlink2FromPath],
68
+ symlinksNames: new Set ( [symlink2FromPath].map ( toBasename ) )
56
69
  },
57
70
  [folder1Path]: {
58
71
  directories: [folder1DeepPath],
72
+ directoriesNames: new Set ( [folder1DeepPath].map ( toBasename ) ),
59
73
  files: [file1aPath, file1bPath],
60
- symlinks: []
74
+ filesNames: new Set ( [file1aPath, file1bPath].map ( toBasename ) ),
75
+ symlinks: [],
76
+ symlinksNames: new Set ()
61
77
  },
62
78
  [folder2Path]: {
63
79
  directories: [],
80
+ directoriesNames: new Set (),
64
81
  files: [file2Path],
65
- symlinks: []
82
+ filesNames: new Set ( [file2Path].map ( toBasename ) ),
83
+ symlinks: [],
84
+ symlinksNames: new Set ()
66
85
  },
67
86
  [folder1DeepPath]: {
68
87
  directories: [],
88
+ directoriesNames: new Set (),
69
89
  files: [fileDeep1Path],
70
- symlinks: []
90
+ filesNames: new Set ( [fileDeep1Path].map ( toBasename ) ),
91
+ symlinks: [],
92
+ symlinksNames: new Set ()
71
93
  },
72
94
  [symlink1FromPath]: {
73
95
  directories: [root2Path],
96
+ directoriesNames: new Set ( [root2Path].map ( toBasename ) ),
74
97
  files: [],
75
- symlinks: []
98
+ filesNames: new Set (),
99
+ symlinks: [],
100
+ symlinksNames: new Set ()
76
101
  },
77
102
  [symlink2FromPath]: {
78
103
  directories: [],
104
+ directoriesNames: new Set (),
79
105
  files: [],
80
- symlinks: []
106
+ filesNames: new Set (),
107
+ symlinks: [],
108
+ symlinksNames: new Set ()
81
109
  }
82
110
  }
83
111
  };
@@ -128,23 +156,35 @@ describe ( 'Tiny Readdir', it => {
128
156
 
129
157
  const expected = {
130
158
  directories: [folder1Path, folder2Path],
159
+ directoriesNames: new Set ( [folder1Path, folder2Path].map ( toBasename ) ),
131
160
  files: [],
161
+ filesNames: new Set (),
132
162
  symlinks: [symlink1FromPath],
163
+ symlinksNames: new Set ( [symlink1FromPath].map ( toBasename ) ),
133
164
  map: {
134
165
  [root1Path]: {
135
166
  directories: [folder1Path, folder2Path],
167
+ directoriesNames: new Set ( [folder1Path, folder2Path].map ( toBasename ) ),
136
168
  files: [],
137
- symlinks: [symlink1FromPath]
169
+ filesNames: new Set (),
170
+ symlinks: [symlink1FromPath],
171
+ symlinksNames: new Set ( [symlink1FromPath].map ( toBasename ) )
138
172
  },
139
173
  [folder1Path]: {
140
174
  directories: [],
175
+ directoriesNames: new Set (),
141
176
  files: [],
142
- symlinks: []
177
+ filesNames: new Set (),
178
+ symlinks: [],
179
+ symlinksNames: new Set ()
143
180
  },
144
181
  [folder2Path]: {
145
182
  directories: [],
183
+ directoriesNames: new Set (),
146
184
  files: [],
147
- symlinks: []
185
+ filesNames: new Set (),
186
+ symlinks: [],
187
+ symlinksNames: new Set ()
148
188
  }
149
189
  }
150
190
  };
@@ -164,4 +204,38 @@ describe ( 'Tiny Readdir', it => {
164
204
 
165
205
  });
166
206
 
207
+ it ( 'does not freeze the main thread', async t => {
208
+
209
+ return new Promise ( resolve => {
210
+
211
+ let count = 0;
212
+ let start = Date.now ();
213
+
214
+ const aborter = new AbortController ();
215
+ const signal = aborter.signal;
216
+
217
+ const intervalId = setInterval ( () => {
218
+ count += 1;
219
+ console.log ( 'tick', count );
220
+ if ( count !== 100 ) return;
221
+ clearInterval ( intervalId );
222
+ const end = Date.now ();
223
+ const elapsed = end - start;
224
+ console.log ( 'elapsed', elapsed );
225
+ console.log ( elapsed );
226
+ if ( elapsed > 1500 ) {
227
+ t.fail ();
228
+ } else {
229
+ t.pass ();
230
+ }
231
+ aborter.abort ();
232
+ resolve ();
233
+ }, 10 );
234
+
235
+ readdir ( '/', { signal } );
236
+
237
+ });
238
+
239
+ });
240
+
167
241
  });
package/test/yielding.js DELETED
@@ -1,35 +0,0 @@
1
-
2
- /* IMPORT */
3
-
4
- import readdir from '../dist/index.js';
5
-
6
- /* MAIN */
7
-
8
- const main = async () => {
9
-
10
- let count = 0;
11
- let start = Date.now ();
12
-
13
- setInterval ( () => {
14
- count += 1;
15
- console.log ( 'tick', count );
16
- if ( count < 100 ) return;
17
- const end = Date.now ();
18
- const elapsed = end - start;
19
- console.log ( 'elapsed', elapsed );
20
- if ( elapsed > 1500 ) {
21
- process.exit ( 1 ); // Fail
22
- } else {
23
- process.exit ( 0 ); // Success
24
- }
25
- }, 10 );
26
-
27
- await readdir ( '/' );
28
-
29
- process.exit ( 1 ); // Fail
30
-
31
- };
32
-
33
- /* RUNNING */
34
-
35
- await main ();