rrdir 14.2.2 → 14.2.4

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/README.md CHANGED
@@ -8,13 +8,14 @@ This module is able to read any path including ones that contain invalid UTF-8 s
8
8
  | Benchmark | rrdir | fdir |
9
9
  |---|---|---|
10
10
  | async | 56ms | 56ms |
11
- | sync | 155ms | 174ms |
12
- | async + exclude | 43ms | |
13
- | sync + exclude | 118ms | |
14
- | async iterator | 77ms | |
15
- | async iterator + exclude | 68ms | |
16
-
17
- Results for 122K entries (111K files, 11K dirs), Node.js on macOS. rrdir returns richer entries (path + directory + symlink) while fdir returns only paths. Run with `make bench`.
11
+ | sync | 150ms | 173ms |
12
+ | async + glob | 64ms | 62ms |
13
+ | sync + glob | 171ms | 194ms |
14
+ | async + exclude | 49ms | 36ms |
15
+ | sync + exclude | 118ms | 112ms |
16
+ | async iterator | 78ms | — |
17
+
18
+ Results for 122K entries (111K files, 11K dirs), Node.js on macOS. rrdir returns richer entries (path + directory + symlink) while fdir returns only paths. fdir uses [picomatch](https://github.com/micromatch/picomatch) for glob matching, rrdir has a built-in glob matcher. Run with `make bench`.
18
19
 
19
20
  ## Usage
20
21
  ```console
package/dist/index.d.ts CHANGED
@@ -1,16 +1,20 @@
1
1
  import { Stats } from "node:fs";
2
2
 
3
3
  //#region index.d.ts
4
+ /** The internal encoding used for path operations. */
4
5
  type Encoding = "utf8" | "buffer";
6
+ /** A directory path, either as a string or a Buffer for raw byte paths. */
5
7
  type Dir = string | Buffer;
8
+ /** Options for `rrdir`, `rrdirAsync`, and `rrdirSync`. */
6
9
  type RRDirOpts = {
7
- strict?: boolean;
8
- stats?: boolean;
9
- followSymlinks?: boolean;
10
- include?: Array<string>;
11
- exclude?: Array<string>;
10
+ /** Whether to throw immediately when reading an entry fails. Default: `false`. */strict?: boolean; /** Whether to include `entry.stats`. Will reduce performance. Default: `false`. */
11
+ stats?: boolean; /** Whether to follow symlinks for both recursion and `stat` calls. Default: `false`. */
12
+ followSymlinks?: boolean; /** Path globs to include, e.g. `["**.map"]`. Default: `undefined`. */
13
+ include?: Array<string>; /** Path globs to exclude, e.g. `["**.js"]`. Default: `undefined`. */
14
+ exclude?: Array<string>; /** Whether `include` and `exclude` match case-insensitively. Default: `false`. */
12
15
  insensitive?: boolean;
13
16
  };
17
+ /** A directory entry returned by `rrdir`, `rrdirAsync`, and `rrdirSync`. */
14
18
  type Entry<T = Dir> = {
15
19
  /** The path to the entry, will be relative if `dir` is given relative. If `dir` is a `Uint8Array`, this will be too. Always present. */path: T; /** Boolean indicating whether the entry is a directory. `undefined` on error. */
16
20
  directory?: boolean; /** Boolean indicating whether the entry is a symbolic link. `undefined` on error. */
@@ -18,8 +22,11 @@ type Entry<T = Dir> = {
18
22
  stats?: Stats; /** Any error encountered while reading this entry. `undefined` on success. */
19
23
  err?: Error;
20
24
  };
25
+ /** Recursively read a directory via async iterator. Memory usage is `O(1)`. */
21
26
  declare function rrdir<T extends Dir>(dir: T, opts?: RRDirOpts): AsyncGenerator<Entry<T>>;
27
+ /** Recursively read a directory, returning all entries as an array. Memory usage is `O(n)`. */
22
28
  declare function rrdirAsync<T extends Dir>(dir: T, opts?: RRDirOpts): Promise<Array<Entry<T>>>;
29
+ /** Synchronously recursively read a directory, returning all entries as an array. Memory usage is `O(n)`. */
23
30
  declare function rrdirSync<T extends Dir>(dir: T, opts?: RRDirOpts): Array<Entry<T>>;
24
31
  //#endregion
25
32
  export { Dir, Encoding, Entry, RRDirOpts, rrdir, rrdirAsync, rrdirSync };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{lstat as e,readdir as t,stat as n}from"node:fs/promises";import{lstatSync as r,readdirSync as i,statSync as a}from"node:fs";import{isAbsolute as o,resolve as s,sep as c}from"node:path";const l=new TextEncoder,u=l.encode.bind(l),d=new TextDecoder,f=d.decode.bind(d),p=u(c);function m({name:e},t,n){if(n===`buffer`){if(t===`.`)return e;let n=t,r=e,i=new Uint8Array(n.length+p.length+r.length);return i.set(n,0),i.set(p,n.length),i.set(r,n.length+p.length),i}else return t===`.`?e:String(t)+c+String(e)}function h(e,t,n,r,i){let a={path:e,directory:r?r.isDirectory():t,symlink:r?r.isSymbolicLink():n};return i&&(a.stats=r),a}function g(e,t){e=e.replace(/\\/g,`/`);let n=e.endsWith(`/**`),r=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`).replace(/\*\*/g,`__DOUBLESTAR__`).replace(/\*/g,`[^/]*`).replace(/__DOUBLESTAR__/g,`.*`);return n?(r=r.slice(0,-3),r=`^${r}(?:/.*)?$`):r=`^${r}$`,new RegExp(r,t?`i`:``)}function _(e,t){if(!e?.length)return null;let n=e.map(e=>g(e,t)),r=s(`.`)+c;return e=>{let t=o(e)?e:r+e,i=c===`\\`?t.replace(/\\/g,`/`):t;return n.some(e=>e.test(i))}}function v(e,t){typeof e==`string`&&/[/\\]$/.test(e)&&(e=e.substring(0,e.length-1));let n=e instanceof Uint8Array?`buffer`:`utf8`,r=t.insensitive||!1,i=_(t.include||[],r),a=_(t.exclude||[],r);return{dir:e,internalOpts:{includeMatcher:i,excludeMatcher:a,hasMatcher:!!(a||i),encoding:n,followSymlinks:!!t.followSymlinks,needStats:!!t.stats,strict:!!t.strict,readdirOpts:{encoding:n,withFileTypes:!0}}}}function y(e,t){return t===`buffer`?f(e):e}async function*b(r,i={}){let a=v(r,i),{includeMatcher:o,excludeMatcher:s,hasMatcher:c,encoding:l,followSymlinks:u,needStats:d,strict:f,readdirOpts:p}=a.internalOpts;r=a.dir;let g=[r];for(;g.length>0;){let r=await Promise.all(g.map(e=>t(e,p).then(t=>({dir:e,dirents:t,err:void 0}),t=>({dir:e,dirents:void 0,err:t})))),i=[];for(let{dir:t,dirents:a,err:p}of r){if(p){if(f)throw p;yield{path:t,err:p};continue}for(let r of a){let a=m(r,t,l),p=!0;if(c){let e=y(a,l);if(s?.(e))continue;p=!o||o(e)}let g=r.isDirectory(),_=r.isSymbolicLink(),v=u&&_,b;if(p){if(d||v)try{b=await(u?n:e)(a)}catch(e){if(f)throw e;yield{path:a,err:e}}yield h(a,g,_,b,d)}let x=g;if(v){if(!b)try{b=await n(a)}catch{}x=!!b?.isDirectory()}x&&i.push(a)}}g=i}}async function x(e,t={}){let n=v(e,t),r=[];return await S(n.dir,n.internalOpts,r),r}async function S(r,i,a){let{includeMatcher:o,excludeMatcher:s,hasMatcher:c,encoding:l,followSymlinks:u,needStats:d,strict:f,readdirOpts:p}=i,g=[];try{g=await t(r,p)}catch(e){if(f)throw e;a.push({path:r,err:e})}if(!g.length)return;let _=[];for(let t of g){let i=m(t,r,l),p=!0;if(c){let e=y(i,l);if(s?.(e))continue;p=!o||o(e)}let g=t.isDirectory(),v=t.isSymbolicLink(),b=u&&v,x;if(p){if(d||b)try{x=await(u?n:e)(i)}catch(e){if(f)throw e;a.push({path:i,err:e})}a.push(h(i,g,v,x,d))}let S=g;if(b){if(!x)try{x=await n(i)}catch{}S=!!x?.isDirectory()}S&&_.push(i)}_.length&&await Promise.all(_.map(e=>S(e,i,a)))}function C(e,t={}){let n=v(e,t),r=[];return w(n.dir,n.internalOpts,r),r}function w(e,t,n){let{includeMatcher:o,excludeMatcher:s,hasMatcher:c,encoding:l,followSymlinks:u,needStats:d,strict:f,readdirOpts:p}=t,g=[];try{g=i(e,p)}catch(t){if(f)throw t;n.push({path:e,err:t})}if(g.length)for(let i of g){let p=m(i,e,l),g=!0;if(c){let e=y(p,l);if(s?.(e))continue;g=!o||o(e)}let _=i.isDirectory(),v=i.isSymbolicLink(),b=u&&v,x;if(g){if(d||b)try{x=(u?a:r)(p)}catch(e){if(f)throw e;n.push({path:p,err:e})}n.push(h(p,_,v,x,d))}let S=_;if(b){if(!x)try{x=a(p)}catch{}S=!!x?.isDirectory()}S&&w(p,t,n)}}export{b as rrdir,x as rrdirAsync,C as rrdirSync};
1
+ import{lstat as e,readdir as t,stat as n}from"node:fs/promises";import{lstatSync as r,readdirSync as i,statSync as a}from"node:fs";import{isAbsolute as o,resolve as s,sep as c}from"node:path";const l=new TextEncoder,u=l.encode.bind(l),d=new TextDecoder,f=d.decode.bind(d),p=u(c);function m({name:e},t,n){if(n===`buffer`){if(t===`.`)return e;let n=t,r=e,i=new Uint8Array(n.length+p.length+r.length);return i.set(n,0),i.set(p,n.length),i.set(r,n.length+p.length),i}else return t===`.`?e:t+c+e}function h(e,t,n,r,i){let a=r?r.isDirectory():t,o=r?r.isSymbolicLink():n;return i?{path:e,directory:a,symlink:o,stats:r}:{path:e,directory:a,symlink:o}}function g(e,t){e=e.replace(/\\/g,`/`);let n=e.endsWith(`/**`),r=e.replace(/[.+?^${}()|[\]\\]/g,`\\$&`).replace(/\*\*/g,`__DOUBLESTAR__`).replace(/\*/g,`[^/]*`).replace(/__DOUBLESTAR__/g,`.*`);return n?(r=r.slice(0,-3),r=`^${r}(?:/.*)?$`):r=`^${r}$`,new RegExp(r,t?`i`:``)}function _(e,t){if(!e?.length)return null;let n=e.map(e=>g(e,t)),r=s(`.`)+c;return e=>{let t=o(e)?e:r+e,i=c===`\\`?t.replace(/\\/g,`/`):t;return n.some(e=>e.test(i))}}function v(e,t){typeof e==`string`&&/[/\\]$/.test(e)&&(e=e.substring(0,e.length-1));let n=e instanceof Uint8Array?`buffer`:`utf8`,r=t.insensitive||!1,i=_(t.include||[],r),a=_(t.exclude||[],r);return{dir:e,internalOpts:{includeMatcher:i,excludeMatcher:a,hasMatcher:!!(a||i),encoding:n,followSymlinks:!!t.followSymlinks,needStats:!!t.stats,strict:!!t.strict,readdirOpts:{encoding:n,withFileTypes:!0}}}}function y(e,t){return t===`buffer`?f(e):e}async function*b(r,i={}){let a=v(r,i),{includeMatcher:o,excludeMatcher:s,hasMatcher:c,encoding:l,followSymlinks:u,needStats:d,strict:f,readdirOpts:p}=a.internalOpts;r=a.dir;let g=[r];for(;g.length>0;){let r=await Promise.all(g.map(e=>t(e,p).then(t=>({dir:e,dirents:t,err:void 0}),t=>({dir:e,dirents:void 0,err:t})))),i=[];for(let{dir:t,dirents:a,err:p}of r){if(p){if(f)throw p;yield{path:t,err:p};continue}for(let r of a){let a=m(r,t,l),p=!0;if(c){let e=y(a,l);if(s!==null&&s(e))continue;p=o===null||o(e)}let g=r.isDirectory(),_=r.isSymbolicLink(),v=u&&_,b;if(p){if(d||v)try{b=await(u?n:e)(a)}catch(e){if(f)throw e;yield{path:a,err:e}}yield h(a,g,_,b,d)}let x=g;if(v){if(!b)try{b=await n(a)}catch{}x=!!b?.isDirectory()}x&&i.push(a)}}g=i}}async function x(e,t={}){let n=v(e,t),r=[];return await S(n.dir,n.internalOpts,r),r}async function S(r,i,a){let{includeMatcher:o,excludeMatcher:s,hasMatcher:c,encoding:l,followSymlinks:u,needStats:d,strict:f,readdirOpts:p}=i,g=[];try{g=await t(r,p)}catch(e){if(f)throw e;a.push({path:r,err:e})}if(!g.length)return;let _=[];for(let t of g){let i=m(t,r,l),p=!0;if(c){let e=y(i,l);if(s!==null&&s(e))continue;p=o===null||o(e)}let g=t.isDirectory(),v=t.isSymbolicLink(),b=u&&v,x;if(p){if(d||b)try{x=await(u?n:e)(i)}catch(e){if(f)throw e;a.push({path:i,err:e})}a.push(h(i,g,v,x,d))}let S=g;if(b){if(!x)try{x=await n(i)}catch{}S=!!x?.isDirectory()}S&&_.push(i)}_.length&&await Promise.all(_.map(e=>S(e,i,a)))}function C(e,t={}){let n=v(e,t),r=[];return w(n.dir,n.internalOpts,r),r}function w(e,t,n){let{includeMatcher:o,excludeMatcher:s,hasMatcher:c,encoding:l,followSymlinks:u,needStats:d,strict:f,readdirOpts:p}=t,g=[e];for(;g.length>0;){let e=g.pop(),t=[];try{t=i(e,p)}catch(t){if(f)throw t;n.push({path:e,err:t});continue}if(t.length)for(let i of t){let t=m(i,e,l),p=!0;if(c){let e=y(t,l);if(s!==null&&s(e))continue;p=o===null||o(e)}let _=i.isDirectory(),v=i.isSymbolicLink(),b=u&&v,x;if(p){if(d||b)try{x=(u?a:r)(t)}catch(e){if(f)throw e;n.push({path:t,err:e})}n.push(h(t,_,v,x,d))}let S=_;if(b){if(!x)try{x=a(t)}catch{}S=!!x?.isDirectory()}S&&g.push(t)}}}export{b as rrdir,x as rrdirAsync,C as rrdirSync};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rrdir",
3
- "version": "14.2.2",
3
+ "version": "14.2.4",
4
4
  "description": "Recursive directory reader with a delightful API",
5
5
  "author": "silverwind <me@silverwind.io>",
6
6
  "repository": "silverwind/rrdir",
@@ -17,20 +17,20 @@
17
17
  "node": ">=22"
18
18
  },
19
19
  "devDependencies": {
20
- "@types/node": "25.4.0",
21
- "@typescript/native-preview": "7.0.0-dev.20260312.1",
20
+ "@types/node": "25.5.0",
21
+ "@typescript/native-preview": "7.0.0-dev.20260317.1",
22
22
  "eslint": "9.39.4",
23
- "eslint-config-silverwind": "124.0.7",
23
+ "eslint-config-silverwind": "125.0.3",
24
24
  "fdir": "6.5.0",
25
25
  "jest-extended": "7.0.0",
26
- "tsdown": "0.21.2",
27
- "tsdown-config-silverwind": "2.0.1",
26
+ "tsdown": "0.21.4",
27
+ "tsdown-config-silverwind": "2.0.2",
28
28
  "typescript": "5.9.3",
29
- "typescript-config-silverwind": "15.0.0",
29
+ "typescript-config-silverwind": "16.0.0",
30
30
  "updates": "17.9.1",
31
- "updates-config-silverwind": "1.0.3",
31
+ "updates-config-silverwind": "1.0.4",
32
32
  "versions": "14.2.1",
33
- "vitest": "4.0.18",
34
- "vitest-config-silverwind": "10.6.3"
33
+ "vitest": "4.1.0",
34
+ "vitest-config-silverwind": "10.6.5"
35
35
  }
36
36
  }