path-treeify 1.3.0-beta.d47c4d9 → 1.4.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/README.md CHANGED
@@ -51,10 +51,12 @@
51
51
  - 🔗 Each node carries a `parent` circular reference for upward traversal
52
52
  - 📍 Each node exposes a `getPath()` method to retrieve its own paths directly
53
53
  - 🏷️ Each node has a `type` field — `PathTreeNodeKind.Dir` or `PathTreeNodeKind.File`
54
+ - 📏 Each node has a `depth` field indicating its distance from the root
54
55
  - 👁️ `fileVisible` option includes files as leaf nodes alongside directories
55
56
  - 🔍 Optional `filter` callback applied at **every depth**, including top-level entries
56
57
  - ⚡ `build()` scans the entire `base` directory with zero configuration
57
58
  - 🎛️ `buildBy()` accepts either a path segment array or a top-level filter function
59
+ - 🗃️ `usePathCache` option caches `getPath()` results per node for repeated access
58
60
  - 📦 Ships as both ESM (`index.mjs`) and CJS (`index.cjs`) with full TypeScript types
59
61
  - 🚫 Zero runtime dependencies
60
62
 
@@ -94,12 +96,12 @@ const treeifyWithFiles = new PathTreeify({
94
96
  });
95
97
  const fullTree = treeifyWithFiles.build();
96
98
 
97
- // Check node type
99
+ // Check node type and depth
98
100
  for (const child of tree.children) {
99
101
  if (child.type === PathTreeNodeKind.Dir) {
100
- console.log('dir:', child.value);
102
+ console.log(`dir (depth ${child.depth}):`, child.value);
101
103
  } else if (child.type === PathTreeNodeKind.File) {
102
- console.log('file:', child.value);
104
+ console.log(`file (depth ${child.depth}):`, child.value);
103
105
  }
104
106
  }
105
107
  ```
@@ -112,11 +114,12 @@ for (const child of tree.children) {
112
114
 
113
115
  Creates a new instance.
114
116
 
115
- | Option | Type | Required | Description |
116
- |---------------|------------------------------|----------|--------------------------------------------------------------------------|
117
- | `base` | `string` | ✅ | Absolute path to the root directory to scan from |
118
- | `filter` | `FilterFunction` (see below) | ❌ | Applied at **every depth** — top-level entries included |
119
- | `fileVisible` | `boolean` | ❌ | When `true`, files are included as leaf nodes. Defaults to `false` |
117
+ | Option | Type | Required | Description |
118
+ |----------------|------------------------------|----------|-------------------------------------------------------------------------------------|
119
+ | `base` | `string` | ✅ | Absolute path to the root directory to scan from |
120
+ | `filter` | `FilterFunction` (see below) | ❌ | Applied at **every depth** — top-level entries included |
121
+ | `fileVisible` | `boolean` | ❌ | When `true`, files are included as leaf nodes. Defaults to `false` |
122
+ | `usePathCache` | `boolean` | ❌ | When `true`, `getPath()` results are cached on each node after the first call |
120
123
 
121
124
  `base` must exist and be a directory, otherwise the constructor throws.
122
125
 
@@ -190,6 +193,7 @@ const tree = treeify.buildBy(name => name !== 'node_modules' && !name.startsWith
190
193
 
191
194
  ```ts
192
195
  interface PathTreeNode {
196
+ depth: number; // distance from root; root is 0, its children are 1, etc.
193
197
  parent: PathTreeNode | null; // null only on the synthetic root
194
198
  value: string; // entry name for this node
195
199
  children: PathTreeNode[]; // empty for file nodes
@@ -199,6 +203,8 @@ interface PathTreeNode {
199
203
  }
200
204
  ```
201
205
 
206
+ When `usePathCache: true` is set on the `PathTreeify` instance, the result of `getPath()` is cached on the node after the first call — subsequent calls return the same object reference without recomputing the parent chain.
207
+
202
208
  > ⚠️ **Circular references** — `parent` points back up the tree. Use `JSON.stringify` replacers or a library like `flatted` if you need to serialize the result.
203
209
 
204
210
  ---
@@ -266,7 +272,7 @@ const tree = treeify.buildBy(name => name !== 'node_modules' && !name.startsWith
266
272
  function printPaths(node) {
267
273
  for (const child of node.children) {
268
274
  const { absolute } = child.getPath();
269
- console.log(`[${child.type}] ${absolute}`);
275
+ console.log(`[${child.type}] depth=${child.depth} ${absolute}`);
270
276
  printPaths(child);
271
277
  }
272
278
  }
@@ -274,6 +280,22 @@ function printPaths(node) {
274
280
  printPaths(tree);
275
281
  ```
276
282
 
283
+ ### Cache `getPath()` results for repeated traversal
284
+
285
+ ```ts
286
+ const treeify = new PathTreeify({
287
+ base: '/your/project',
288
+ usePathCache: true,
289
+ });
290
+ const tree = treeify.build();
291
+
292
+ // First call walks the parent chain and caches the result
293
+ const pathA = tree.children[0].getPath();
294
+ // Subsequent calls return the cached object directly
295
+ const pathB = tree.children[0].getPath();
296
+ console.log(pathA === pathB); // true
297
+ ```
298
+
277
299
  ### CommonJS usage
278
300
 
279
301
  ```js
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t=require("fs"),i=require("path");class r{static isValid(e){try{return t.accessSync(e,t.constants.F_OK),!0}catch{return!1}}static isDirectory(e){try{return t.statSync(e).isDirectory()}catch{return!1}}static isFile(e){try{return t.statSync(e).isFile()}catch{return!1}}}exports.PathTreeNodeKind=void 0,(e=exports.PathTreeNodeKind||(exports.PathTreeNodeKind={})).Dir="dir",e.File="file",e.Unknown="unknown";class s{constructor(e){this.parent=null,this.value="",this.children=[],this.type=exports.PathTreeNodeKind.Unknown,this.base=e}getPath(){let e="",t=this;for(;t.parent;)e=e?`${t.value}${i.sep}${e}`:t.value,t=t.parent;return{relative:e,absolute:i.resolve(this.base,e)}}}exports.PathTreeify=class{applyFilter(e,t){return!(!this.fileVisible&&!r.isDirectory(e))&&(!this.userFilter||this.userFilter({name:t,dirPath:i.dirname(e)}))}constructor({filter:e,base:t,fileVisible:i}){if(this.fileVisible=!1,"boolean"==typeof i&&i&&(this.fileVisible=i),void 0!==e&&(this.validateFilter(e),this.userFilter=e),!t||!r.isValid(t))throw new Error(`${t} is not a valid path!`);if(!r.isDirectory(t))throw new Error(`${t} is not a dirPath!`);this.base=t}validateFilter(e){if("function"!=typeof e)throw new TypeError("filter must be a function")}initNode(){return new s(this.base)}buildChildren(e,s){const n=[],o=t.readdirSync(e);for(const t of o){const o=i.join(e,t);if(!this.applyFilter(o,t))continue;const l=this.initNode();l.value=t,l.parent=s,n.push(l),this.fileVisible&&r.isFile(o)?l.type=exports.PathTreeNodeKind.File:(l.type=exports.PathTreeNodeKind.Dir,l.children=this.buildChildren(o,l))}return n}checkRelativePaths(e){if(!Array.isArray(e))throw new Error("Expected array, got "+typeof e);for(let t=0;t<e.length;t++){const s=e[t];if("string"!=typeof s)throw new Error(`Item at index ${t} is not a string, got ${typeof s}`);const n=i.resolve(this.base,s);if(!r.isValid(n))throw new Error(`Path does not exist or is not accessible: ${n} (from relative path: ${s})`);if(!(r.isDirectory(n)||this.fileVisible&&r.isFile(n)))throw new Error(`Path is not a directory: ${n} (from relative path: ${s})`)}}formatSegments(e){return e.map(e=>e.split(/[/\\]/).filter(Boolean).join(i.sep)).filter(Boolean)}getAllEntriesUnderBase(){return t.readdirSync(this.base).filter(e=>{const t=i.resolve(this.base,e);return this.applyFilter(t,e)})}buildBySegments(e){const t=this.initNode(),s=this.formatSegments(e);this.checkRelativePaths(s);for(const e of s){const s=i.resolve(this.base,e),n=this.initNode();n.value=e,n.parent=t,t.children.push(n),this.fileVisible&&r.isFile(s)?n.type=exports.PathTreeNodeKind.File:(n.type=exports.PathTreeNodeKind.Dir,n.children=this.buildChildren(s,n))}return t}buildByFilter(e){const t=this.getAllEntriesUnderBase();return this.buildBySegments(t.filter(e))}buildBy(e){if(Array.isArray(e))return this.buildBySegments(e);if("function"==typeof e)return this.buildByFilter(e);throw new TypeError("buildBy: expected an array of strings or a filter function, but received "+typeof e)}build(){const e=this.getAllEntriesUnderBase();return this.buildBySegments(e)}};
1
+ "use strict";var e,t=require("fs"),i=require("path");class r{static isValid(e){try{return t.accessSync(e,t.constants.F_OK),!0}catch{return!1}}static isDirectory(e){try{return t.statSync(e).isDirectory()}catch{return!1}}static isFile(e){try{return t.statSync(e).isFile()}catch{return!1}}}exports.PathTreeNodeKind=void 0,(e=exports.PathTreeNodeKind||(exports.PathTreeNodeKind={})).Dir="dir",e.File="file",e.Unknown="unknown";class s{constructor(e){this.usePathCache=!1,this.base=e.base,"boolean"==typeof e.usePathCache&&(this.usePathCache=e.usePathCache)}getPath(){let e=this;const t=()=>{let t="",r=this;for(;r.parent;)t=t?`${r.value}${i.sep}${t}`:r.value,r=r.parent;return{relative:t,absolute:i.resolve(e.base,t)}};return e.usePathCache?(e._pathCache||(e._pathCache=t()),e._pathCache):t()}}exports.PathTreeify=class{constructor(e){this.fileVisible=!1;const{filter:t,base:i,fileVisible:n,usePathCache:a}=e;if("boolean"==typeof n&&n&&(this.fileVisible=n),void 0!==t&&(this.validateFilter(t),this.userFilter=t),!i||!r.isValid(i))throw new Error(`${i} is not a valid path!`);if(!r.isDirectory(i))throw new Error(`${i} is not a dirPath!`);this.base=i,this.pathTreeNodeShared=new s({base:i,usePathCache:a})}applyFilter(e,t){return!(!this.fileVisible&&!r.isDirectory(e))&&(!this.userFilter||this.userFilter({name:t,dirPath:i.dirname(e)}))}validateFilter(e){if("function"!=typeof e)throw new TypeError("filter must be a function")}initNode(){const e=Object.create(this.pathTreeNodeShared),t=Object.create(e);return t.parent=null,t.value="",t.children=[],t.type=exports.PathTreeNodeKind.Unknown,t.depth=-1,t}buildChildren(e,s,n){const a=[],o=n||t.readdirSync(e),h=s.depth+1;for(const t of o){const n=i.join(e,t);if(!this.applyFilter(n,t))continue;const o=this.initNode();o.depth=h,o.value=t,o.parent=s,a.push(o),this.fileVisible&&r.isFile(n)?o.type=exports.PathTreeNodeKind.File:(o.type=exports.PathTreeNodeKind.Dir,o.children=this.buildChildren(n,o))}return a}checkRelativePaths(e){for(let t=0;t<e.length;t++){const s=e[t];if("string"!=typeof s)throw new Error(`Item at index ${t} is not a string, got ${typeof s}`);const n=i.resolve(this.base,s);if(!r.isValid(n))throw new Error(`Path does not exist or is not accessible: ${n} (from relative path: ${s})`);if(!(r.isDirectory(n)||this.fileVisible&&r.isFile(n)))throw new Error(`Path is not a directory: ${n} (from relative path: ${s})`)}}formatSegments(e){return e.map(e=>e.split(/[/\\]/).filter(Boolean).join(i.sep)).filter(Boolean)}getAllEntriesUnderBase(){return t.readdirSync(this.base).filter(e=>{const t=i.resolve(this.base,e);return this.applyFilter(t,e)})}buildBySegments(e){const t=this.initNode();return t.depth=0,t.children=this.buildChildren(this.base,t,e),t}buildByFilter(e){const t=this.getAllEntriesUnderBase();return this.buildBySegments(t.filter(e))}buildBy(e){if(Array.isArray(e)){const t=this.formatSegments(e);return this.checkRelativePaths(t),this.buildBySegments(t)}if("function"==typeof e)return this.buildByFilter(e);throw new TypeError("buildBy: expected an array of strings or a filter function, but received "+typeof e)}build(){const e=this.getAllEntriesUnderBase();return this.buildBySegments(e)}};
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{readdirSync as t,accessSync as i,constants as e,statSync as r}from"fs";import{dirname as s,join as n,resolve as l,sep as o}from"path";class a{static isValid(t){try{return i(t,e.F_OK),!0}catch{return!1}}static isDirectory(t){try{return r(t).isDirectory()}catch{return!1}}static isFile(t){try{return r(t).isFile()}catch{return!1}}}var h;!function(t){t.Dir="dir",t.File="file",t.Unknown="unknown"}(h||(h={}));class c{constructor(t){this.parent=null,this.value="",this.children=[],this.type=h.Unknown,this.base=t}getPath(){let t="",i=this;for(;i.parent;)t=t?`${i.value}${o}${t}`:i.value,i=i.parent;return{relative:t,absolute:l(this.base,t)}}}class u{applyFilter(t,i){return!(!this.fileVisible&&!a.isDirectory(t))&&(!this.userFilter||this.userFilter({name:i,dirPath:s(t)}))}constructor({filter:t,base:i,fileVisible:e}){if(this.fileVisible=!1,"boolean"==typeof e&&e&&(this.fileVisible=e),void 0!==t&&(this.validateFilter(t),this.userFilter=t),!i||!a.isValid(i))throw new Error(`${i} is not a valid path!`);if(!a.isDirectory(i))throw new Error(`${i} is not a dirPath!`);this.base=i}validateFilter(t){if("function"!=typeof t)throw new TypeError("filter must be a function")}initNode(){return new c(this.base)}buildChildren(i,e){const r=[],s=t(i);for(const t of s){const s=n(i,t);if(!this.applyFilter(s,t))continue;const l=this.initNode();l.value=t,l.parent=e,r.push(l),this.fileVisible&&a.isFile(s)?l.type=h.File:(l.type=h.Dir,l.children=this.buildChildren(s,l))}return r}checkRelativePaths(t){if(!Array.isArray(t))throw new Error("Expected array, got "+typeof t);for(let i=0;i<t.length;i++){const e=t[i];if("string"!=typeof e)throw new Error(`Item at index ${i} is not a string, got ${typeof e}`);const r=l(this.base,e);if(!a.isValid(r))throw new Error(`Path does not exist or is not accessible: ${r} (from relative path: ${e})`);if(!(a.isDirectory(r)||this.fileVisible&&a.isFile(r)))throw new Error(`Path is not a directory: ${r} (from relative path: ${e})`)}}formatSegments(t){return t.map(t=>t.split(/[/\\]/).filter(Boolean).join(o)).filter(Boolean)}getAllEntriesUnderBase(){return t(this.base).filter(t=>{const i=l(this.base,t);return this.applyFilter(i,t)})}buildBySegments(t){const i=this.initNode(),e=this.formatSegments(t);this.checkRelativePaths(e);for(const t of e){const e=l(this.base,t),r=this.initNode();r.value=t,r.parent=i,i.children.push(r),this.fileVisible&&a.isFile(e)?r.type=h.File:(r.type=h.Dir,r.children=this.buildChildren(e,r))}return i}buildByFilter(t){const i=this.getAllEntriesUnderBase();return this.buildBySegments(i.filter(t))}buildBy(t){if(Array.isArray(t))return this.buildBySegments(t);if("function"==typeof t)return this.buildByFilter(t);throw new TypeError("buildBy: expected an array of strings or a filter function, but received "+typeof t)}build(){const t=this.getAllEntriesUnderBase();return this.buildBySegments(t)}}export{h as PathTreeNodeKind,u as PathTreeify};
1
+ import{readdirSync as t,accessSync as e,constants as i,statSync as r}from"fs";import{dirname as s,join as n,resolve as a,sep as h}from"path";class l{static isValid(t){try{return e(t,i.F_OK),!0}catch{return!1}}static isDirectory(t){try{return r(t).isDirectory()}catch{return!1}}static isFile(t){try{return r(t).isFile()}catch{return!1}}}var o;!function(t){t.Dir="dir",t.File="file",t.Unknown="unknown"}(o||(o={}));class c{constructor(t){this.usePathCache=!1,this.base=t.base,"boolean"==typeof t.usePathCache&&(this.usePathCache=t.usePathCache)}getPath(){let t=this;const e=()=>{let e="",i=this;for(;i.parent;)e=e?`${i.value}${h}${e}`:i.value,i=i.parent;return{relative:e,absolute:a(t.base,e)}};return t.usePathCache?(t._pathCache||(t._pathCache=e()),t._pathCache):e()}}class u{constructor(t){this.fileVisible=!1;const{filter:e,base:i,fileVisible:r,usePathCache:s}=t;if("boolean"==typeof r&&r&&(this.fileVisible=r),void 0!==e&&(this.validateFilter(e),this.userFilter=e),!i||!l.isValid(i))throw new Error(`${i} is not a valid path!`);if(!l.isDirectory(i))throw new Error(`${i} is not a dirPath!`);this.base=i,this.pathTreeNodeShared=new c({base:i,usePathCache:s})}applyFilter(t,e){return!(!this.fileVisible&&!l.isDirectory(t))&&(!this.userFilter||this.userFilter({name:e,dirPath:s(t)}))}validateFilter(t){if("function"!=typeof t)throw new TypeError("filter must be a function")}initNode(){const t=Object.create(this.pathTreeNodeShared),e=Object.create(t);return e.parent=null,e.value="",e.children=[],e.type=o.Unknown,e.depth=-1,e}buildChildren(e,i,r){const s=[],a=r||t(e),h=i.depth+1;for(const t of a){const r=n(e,t);if(!this.applyFilter(r,t))continue;const a=this.initNode();a.depth=h,a.value=t,a.parent=i,s.push(a),this.fileVisible&&l.isFile(r)?a.type=o.File:(a.type=o.Dir,a.children=this.buildChildren(r,a))}return s}checkRelativePaths(t){for(let e=0;e<t.length;e++){const i=t[e];if("string"!=typeof i)throw new Error(`Item at index ${e} is not a string, got ${typeof i}`);const r=a(this.base,i);if(!l.isValid(r))throw new Error(`Path does not exist or is not accessible: ${r} (from relative path: ${i})`);if(!(l.isDirectory(r)||this.fileVisible&&l.isFile(r)))throw new Error(`Path is not a directory: ${r} (from relative path: ${i})`)}}formatSegments(t){return t.map(t=>t.split(/[/\\]/).filter(Boolean).join(h)).filter(Boolean)}getAllEntriesUnderBase(){return t(this.base).filter(t=>{const e=a(this.base,t);return this.applyFilter(e,t)})}buildBySegments(t){const e=this.initNode();return e.depth=0,e.children=this.buildChildren(this.base,e,t),e}buildByFilter(t){const e=this.getAllEntriesUnderBase();return this.buildBySegments(e.filter(t))}buildBy(t){if(Array.isArray(t)){const e=this.formatSegments(t);return this.checkRelativePaths(e),this.buildBySegments(e)}if("function"==typeof t)return this.buildByFilter(t);throw new TypeError("buildBy: expected an array of strings or a filter function, but received "+typeof t)}build(){const t=this.getAllEntriesUnderBase();return this.buildBySegments(t)}}export{o as PathTreeNodeKind,u as PathTreeify};
@@ -0,0 +1 @@
1
+ export {};
@@ -11,6 +11,13 @@ interface PathTreeifyProps {
11
11
  filter?: FilterFunction;
12
12
  /** When true, files are included as leaf nodes alongside directories. Defaults to false */
13
13
  fileVisible?: boolean;
14
+ /**
15
+ * When true, the result of {@link PathTreeNode.getPath} is memoised on each node
16
+ * after the first call. Subsequent calls return the same object reference without
17
+ * re-walking the parent chain. Useful when nodes are accessed repeatedly.
18
+ * Defaults to false.
19
+ */
20
+ usePathCache?: boolean;
14
21
  }
15
22
  /** Classification of a node in the path tree */
16
23
  export declare enum PathTreeNodeKind {
@@ -19,11 +26,28 @@ export declare enum PathTreeNodeKind {
19
26
  /** Assigned before the node's type has been resolved */
20
27
  Unknown = "unknown"
21
28
  }
29
+ /**
30
+ * Public interface for a node in the path tree.
31
+ * Consumers receive this type; the internal implementation class is not exported.
32
+ */
22
33
  export interface PathTreeNode {
34
+ /** Distance from the root node; root itself is 0, its direct children are 1, and so on */
35
+ depth: number;
36
+ /** Reference to the parent node; null for the root node */
23
37
  parent: PathTreeNode | null;
38
+ /** The entry name of this node (not a full path) */
24
39
  value: string;
40
+ /** Child nodes; non-empty only for directory nodes */
25
41
  children: PathTreeNode[];
42
+ /** Whether this node is a directory, a file, or not yet resolved */
26
43
  type: PathTreeNodeKind;
44
+ /**
45
+ * Walks up the parent chain to compute this node's relative and absolute paths.
46
+ * When the owning {@link PathTreeify} instance was created with `usePathCache: true`,
47
+ * the result is memoised after the first call and the same object is returned on
48
+ * every subsequent call.
49
+ * @returns `relative` — path from the tree root; `absolute` — fully resolved path on disk
50
+ */
27
51
  getPath(): {
28
52
  relative: string;
29
53
  absolute: string;
@@ -33,6 +57,12 @@ export interface PathTreeNode {
33
57
  export declare class PathTreeify {
34
58
  /** The root directory to scan */
35
59
  private base;
60
+ /**
61
+ * Shared prototype instance for nodes produced by this builder.
62
+ * All nodes created via {@link initNode} inherit `base` and `getPath` from this object,
63
+ * avoiding per-node storage of the base path string.
64
+ */
65
+ private pathTreeNodeShared;
36
66
  /**
37
67
  * Optional user-supplied filter. When set, every entry must pass this predicate
38
68
  * in addition to the built-in visibility check.
@@ -40,6 +70,7 @@ export declare class PathTreeify {
40
70
  private userFilter?;
41
71
  /** When true, files are included as leaf nodes during traversal. Defaults to false */
42
72
  private fileVisible;
73
+ constructor(props: Partial<PathTreeifyProps>);
43
74
  /**
44
75
  * Determines whether a given entry should be included in the tree.
45
76
  * - If {@link fileVisible} is false, non-directory entries are always excluded.
@@ -48,27 +79,39 @@ export declare class PathTreeify {
48
79
  * @param name - Entry name (filename or directory name)
49
80
  */
50
81
  private applyFilter;
51
- constructor({ filter, base, fileVisible }: Partial<PathTreeifyProps>);
52
82
  /**
53
83
  * Asserts that the provided value is a callable {@link FilterFunction}.
54
84
  * Throws a TypeError if the check fails.
55
85
  */
56
86
  private validateFilter;
57
- /** Creates a new unattached {@link PathTreeNode} */
87
+ /**
88
+ * Creates a new unattached {@link PathTreeNode}.
89
+ * The node is created with a two-layer prototype chain:
90
+ * `node → cache → pathTreeNodeShared`. The intermediate `cache` layer is a
91
+ * per-node object that holds `_pathCache` when `usePathCache` is enabled,
92
+ * keeping the cached value isolated to each node while still inheriting `base`
93
+ * and `getPath` from `pathTreeNodeShared`.
94
+ * `depth` is initialised to `-1` and must be set by the caller.
95
+ */
58
96
  private initNode;
59
97
  /**
60
98
  * Recursively reads {@link dirPath} and builds child nodes for each entry that
61
99
  * passes {@link applyFilter}. Directories are traversed depth-first;
62
100
  * files (when {@link fileVisible} is true) become leaf nodes.
63
- * @param dirPath - Absolute path of the directory to read
64
- * @param parent - The parent node to attach child nodes to
101
+ *
102
+ * @param dirPath - Absolute path of the directory to read
103
+ * @param parent - The parent node to attach child nodes to
104
+ * @param segments - Optional explicit list of entry names to use instead of reading the
105
+ * directory from disk; used by {@link buildBySegments} to skip a
106
+ * redundant `readdirSync` when the segment list is already known
65
107
  */
66
108
  private buildChildren;
67
109
  /**
68
110
  * Validates that every entry in {@link relativeSegments} refers to an accessible
69
111
  * path under {@link base}. When {@link fileVisible} is false, each path must be
70
112
  * a directory; when true, regular files are also accepted.
71
- * @param relativeSegments - Relative path strings to validate
113
+ * @param relativeSegments - Relative path strings to validate; assumed to be a string
114
+ * array (callers are responsible for type safety at the boundary)
72
115
  */
73
116
  private checkRelativePaths;
74
117
  /**
@@ -84,21 +127,23 @@ export declare class PathTreeify {
84
127
  */
85
128
  private getAllEntriesUnderBase;
86
129
  /**
87
- * Builds a subtree containing only the entries identified by {@link segments}.
88
- * Paths are normalised via {@link formatSegments} and validated before use.
89
- * @param segments - Relative paths to include as top-level nodes
130
+ * Builds a subtree whose top-depth children correspond to {@link segments}.
131
+ * The root node is created at depth 0; children are built by delegating to
132
+ * {@link buildChildren}, passing {@link segments} directly to avoid a redundant
133
+ * `readdirSync` of the base directory.
134
+ * @param segments - Normalised and validated relative path segments
90
135
  */
91
136
  private buildBySegments;
92
137
  /**
93
- * Builds a subtree from top-level entries whose names satisfy {@link filter}.
94
- * Note: this predicate only affects top-level selection, not recursive traversal.
138
+ * Builds a subtree from top-depth entries whose names satisfy {@link filter}.
139
+ * Note: this predicate only affects top-depth selection, not recursive traversal.
95
140
  * For recursive filtering use the `filter` constructor option.
96
- * @param filter - Predicate applied to each top-level entry name
141
+ * @param filter - Predicate applied to each top-depth entry name
97
142
  */
98
143
  private buildByFilter;
99
144
  /** Overload: build the tree from an explicit list of relative path segments */
100
145
  buildBy(segments: string[]): PathTreeNode;
101
- /** Overload: build the tree from a predicate applied to top-level entry names */
146
+ /** Overload: build the tree from a predicate applied to top-depth entry names */
102
147
  buildBy(filter: (segment: string) => boolean): PathTreeNode;
103
148
  /** Builds a full tree from all immediate entries under the base path */
104
149
  build(): PathTreeNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "path-treeify",
3
- "version": "1.3.0-beta.d47c4d9",
3
+ "version": "1.4.0",
4
4
  "description": "Convert a path or an array of paths into a tree-structured JavaScript object, where each node has a `parent` property that holds a circular reference to its parent node.",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",