path-treeify 1.1.0 → 1.2.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
@@ -8,8 +8,8 @@
8
8
  </div>
9
9
 
10
10
  <div align="left">
11
- <a href="https://github.com/isaaxite/path-treeify">
12
- <img alt="GitHub package.json dynamic" src="https://img.shields.io/github/package-json/version/isaaxite/path-treeify?logo=github">
11
+ <a href="https://www.npmjs.com/package/path-treeify">
12
+ <img alt="NPM Version" src="https://img.shields.io/npm/v/path-treeify">
13
13
  </a>
14
14
  <a href="https://nodejs.org">
15
15
  <img alt="node" src="https://img.shields.io/node/v/path-treeify">
@@ -39,7 +39,8 @@
39
39
  - 🔗 Each node carries a `parent` circular reference for upward traversal
40
40
  - 📍 Each node exposes a `getPath()` method to retrieve its own paths directly
41
41
  - 🔍 Optional `filter` callback to include/exclude directories during scanning
42
- - ⚡ `build()` method scans the entire `base` directory with zero configuration
42
+ - ⚡ `build()` scans the entire `base` directory with zero configuration
43
+ - 🎛️ `buildBy()` accepts either a directory name array or a filter function
43
44
  - 📦 Ships as both ESM (`index.mjs`) and CJS (`index.cjs`) with full TypeScript types
44
45
  - 🚫 Zero runtime dependencies
45
46
 
@@ -70,8 +71,11 @@ import { PathTreeify } from 'path-treeify';
70
71
 
71
72
  const treeify = new PathTreeify({ base: '/your/project/root' });
72
73
 
73
- // Scan specific directories
74
- const tree = treeify.buildByDirNames(['src', 'tests']);
74
+ // Scan specific directories by name
75
+ const tree = treeify.buildBy(['src', 'tests']);
76
+
77
+ // Scan with a filter function over all top-level directories
78
+ const filtered = treeify.buildBy(name => !name.startsWith('.') && name !== 'node_modules');
75
79
 
76
80
  // Or scan everything under base at once
77
81
  const fullTree = treeify.build();
@@ -88,7 +92,7 @@ Creates a new instance.
88
92
  | Option | Type | Required | Description |
89
93
  |----------|-------------------------------|----------|----------------------------------------------------|
90
94
  | `base` | `string` | ✅ | Absolute path to the root directory to scan from |
91
- | `filter` | `FilterFunction` (see below) | ❌ | Called for every directory found during traversal |
95
+ | `filter` | `FilterFunction` (see below) | ❌ | Called for every directory found during deep traversal |
92
96
 
93
97
  `base` must exist and be a directory, otherwise the constructor throws.
94
98
 
@@ -96,6 +100,8 @@ Creates a new instance.
96
100
 
97
101
  ### `FilterFunction`
98
102
 
103
+ Used as the `filter` option in the constructor. Applied recursively during deep traversal of the tree.
104
+
99
105
  ```ts
100
106
  type FilterFunction = (params: {
101
107
  name: string; // directory name (leaf segment)
@@ -105,7 +111,7 @@ type FilterFunction = (params: {
105
111
 
106
112
  Return `true` to **include** the directory and recurse into it; `false` to **skip** it.
107
113
 
108
- **Example — skip hidden directories and `node_modules`:**
114
+ **Example — skip hidden directories and `node_modules` at every level:**
109
115
 
110
116
  ```ts
111
117
  const treeify = new PathTreeify({
@@ -118,7 +124,7 @@ const treeify = new PathTreeify({
118
124
 
119
125
  ### `build(): PathTreeNode`
120
126
 
121
- Scans **all** subdirectories directly under `base` and returns a synthetic root `PathTreeNode`. This is the zero-configuration alternative to `buildByDirNames`.
127
+ Scans **all** subdirectories directly under `base` and returns a synthetic root `PathTreeNode`. This is the zero-configuration shorthand.
122
128
 
123
129
  ```ts
124
130
  const tree = treeify.build();
@@ -126,17 +132,26 @@ const tree = treeify.build();
126
132
 
127
133
  ---
128
134
 
129
- ### `buildByDirNames(dirNames: string[]): PathTreeNode`
135
+ ### `buildBy(dirNames: string[]): PathTreeNode`
130
136
 
131
- Scans the given relative directory names (resolved against `base`) and returns a synthetic root `PathTreeNode` whose `children` are the top-level nodes you requested.
137
+ Builds a tree from the given list of directory names (relative to `base`).
132
138
 
133
139
  ```ts
134
- const root = treeify.buildByDirNames(['src', 'docs']);
140
+ const tree = treeify.buildBy(['src', 'docs', 'tests']);
135
141
  ```
136
142
 
137
- - Each element must be a valid, accessible directory relative to `base`.
138
143
  - Leading and trailing slashes are stripped automatically.
139
- - Throws if any path does not exist or is not a directory.
144
+ - Throws if any name does not resolve to a valid directory under `base`.
145
+
146
+ ### `buildBy(filter: (dirName: string) => boolean): PathTreeNode`
147
+
148
+ Collects all top-level subdirectories under `base`, applies the given filter function, then builds a tree from the matching names.
149
+
150
+ ```ts
151
+ const tree = treeify.buildBy(name => name !== 'node_modules' && !name.startsWith('.'));
152
+ ```
153
+
154
+ > Note: this `filter` operates only on the **top-level** directory names under `base`. For filtering at every depth, pass a `filter` to the constructor instead.
140
155
 
141
156
  ---
142
157
 
@@ -145,17 +160,14 @@ const root = treeify.buildByDirNames(['src', 'docs']);
145
160
  Walks a node's `parent` chain to reconstruct its full path. Equivalent to calling `node.getPath()` directly.
146
161
 
147
162
  ```ts
148
- const srcNode = root.children[0];
149
- const { relative, absolute } = treeify.getPathBy(srcNode);
150
- // relative → 'src'
151
- // absolute → '/your/project/src'
163
+ const { relative, absolute } = treeify.getPathBy(node);
152
164
  ```
153
165
 
154
166
  ---
155
167
 
156
168
  ### `PathTreeNode`
157
169
 
158
- `PathTreeNode` is now a **class** with its own `getPath()` method, so you can retrieve a node's path without passing it back to the `PathTreeify` instance.
170
+ `PathTreeNode` is a **class** with its own `getPath()` method, so you can retrieve a node's path without passing it back to the `PathTreeify` instance.
159
171
 
160
172
  ```ts
161
173
  class PathTreeNode {
@@ -180,18 +192,30 @@ class PathTreeNode {
180
192
  ```ts
181
193
  import { PathTreeify } from 'path-treeify';
182
194
 
183
- const treeify = new PathTreeify({
184
- base: '/your/project',
185
- filter: ({ name }) => name !== 'node_modules' && !name.startsWith('.'),
186
- });
187
-
195
+ const treeify = new PathTreeify({ base: '/your/project' });
188
196
  const tree = treeify.build();
189
197
  ```
190
198
 
191
199
  ### Scan specific directories
192
200
 
193
201
  ```ts
194
- const tree = treeify.buildByDirNames(['src', 'tests', 'docs']);
202
+ const tree = treeify.buildBy(['src', 'tests', 'docs']);
203
+ ```
204
+
205
+ ### Filter top-level directories
206
+
207
+ ```ts
208
+ const tree = treeify.buildBy(name => name !== 'node_modules' && !name.startsWith('.'));
209
+ ```
210
+
211
+ ### Filter at every depth via constructor
212
+
213
+ ```ts
214
+ const treeify = new PathTreeify({
215
+ base: '/your/project',
216
+ filter: ({ name }) => name !== 'node_modules' && !name.startsWith('.'),
217
+ });
218
+ const tree = treeify.build();
195
219
  ```
196
220
 
197
221
  ### Retrieve paths via `node.getPath()`
package/dist/index.cjs CHANGED
@@ -1,159 +1 @@
1
- 'use strict';
2
-
3
- var fs = require('fs');
4
- var path = require('path');
5
-
6
- class PathValidator {
7
- static isValid(path) {
8
- try {
9
- fs.accessSync(path, fs.constants.F_OK);
10
- return true;
11
- }
12
- catch {
13
- return false;
14
- }
15
- }
16
- static isDirectory(path) {
17
- try {
18
- return fs.statSync(path).isDirectory();
19
- }
20
- catch {
21
- return false;
22
- }
23
- }
24
- }
25
- class PathTreeNode {
26
- constructor(base) {
27
- this.parent = null;
28
- this.value = '';
29
- this.children = [];
30
- this.base = base;
31
- }
32
- getPath() {
33
- let relative = '';
34
- let current = this;
35
- while (current.parent) {
36
- relative = relative
37
- ? `${current.value}${path.sep}${relative}`
38
- : current.value;
39
- current = current.parent;
40
- }
41
- return { relative, absolute: path.resolve(this.base, relative) };
42
- }
43
- }
44
- class PathTreeify {
45
- constructor({ filter, base }) {
46
- if (typeof filter !== 'undefined') {
47
- this.validateFilter(filter);
48
- this.filter = filter;
49
- }
50
- if (!base || !PathValidator.isValid(base)) {
51
- throw new Error(`${base} is not a valid path!`);
52
- }
53
- if (!PathValidator.isDirectory(base)) {
54
- throw new Error(`${base} is not a dirPath!`);
55
- }
56
- this.base = base;
57
- }
58
- validateFilter(filter) {
59
- if (typeof filter !== 'function') {
60
- throw new TypeError('filter must be a function');
61
- }
62
- if (filter.length !== 1) {
63
- throw new TypeError('filter must accept exactly one parameter');
64
- }
65
- try {
66
- const testResult = filter({ name: 'test', postPath: '/test' });
67
- if (typeof testResult !== 'boolean') {
68
- throw new TypeError('filter must return a boolean');
69
- }
70
- }
71
- catch (error) {
72
- throw new TypeError('filter function threw an error during test: ' + error);
73
- }
74
- }
75
- initNode(parent = null) {
76
- const node = new PathTreeNode(this.base);
77
- if (parent) {
78
- node.parent = parent;
79
- }
80
- return node;
81
- }
82
- buildChildren(dirPath, parent) {
83
- const names = fs.readdirSync(dirPath);
84
- const children = [];
85
- for (const name of names) {
86
- const subPath = path.join(dirPath, name);
87
- if (!fs.statSync(subPath).isDirectory()) {
88
- continue;
89
- }
90
- if (this.filter && !this.filter({ dirPath, name })) {
91
- continue;
92
- }
93
- const node = this.initNode();
94
- node.value = name;
95
- node.parent = parent;
96
- node.children = this.buildChildren(subPath, node);
97
- children.push(node);
98
- }
99
- return children;
100
- }
101
- checkRelativePaths(relativeDirNames) {
102
- if (!Array.isArray(relativeDirNames)) {
103
- throw new Error(`Expected array, got ${typeof relativeDirNames}`);
104
- }
105
- for (let i = 0; i < relativeDirNames.length; i++) {
106
- const it = relativeDirNames[i];
107
- if (typeof it !== 'string') {
108
- throw new Error(`Item at index ${i} is not a string, got ${typeof it}`);
109
- }
110
- const absPath = path.resolve(this.base, it);
111
- if (!PathValidator.isValid(absPath)) {
112
- throw new Error(`Path does not exist or is not accessible: ${absPath} (from relative path: ${it})`);
113
- }
114
- if (!PathValidator.isDirectory(absPath)) {
115
- throw new Error(`Path is not a directory: ${absPath} (from relative path: ${it})`);
116
- }
117
- }
118
- }
119
- formatDirnames(dirNames) {
120
- return dirNames.map(dir => {
121
- // Remove leading and trailing slashes
122
- return dir.replace(/^\/+|\/+$/g, '');
123
- }).filter(dir => dir !== ''); // Optional: filter empty strings
124
- }
125
- getPathBy(node) {
126
- let relative = '';
127
- let current = node;
128
- while (current.parent) {
129
- relative = relative
130
- ? `${current.value}${path.sep}${relative}`
131
- : current.value;
132
- current = current.parent;
133
- }
134
- return { relative, absolute: path.resolve(this.base, relative) };
135
- }
136
- buildByDirNames(dirNames) {
137
- const root = this.initNode();
138
- this.checkRelativePaths(dirNames);
139
- const dirNameArr = this.formatDirnames(dirNames);
140
- for (const dirName of dirNameArr) {
141
- const node = this.initNode();
142
- node.value = dirName;
143
- node.parent = root;
144
- node.children = this.buildChildren(path.resolve(this.base, dirName), node);
145
- root.children.push(node);
146
- }
147
- return root;
148
- }
149
- build() {
150
- const dirNameArr = fs.readdirSync(this.base).filter(name => {
151
- const abs = path.resolve(this.base, name);
152
- return PathValidator.isDirectory(abs);
153
- });
154
- return this.buildByDirNames(dirNameArr);
155
- }
156
- }
157
-
158
- exports.PathTreeify = PathTreeify;
159
- //# sourceMappingURL=index.cjs.map
1
+ "use strict";var t=require("fs"),r=require("path");class e{static isValid(r){try{return t.accessSync(r,t.constants.F_OK),!0}catch{return!1}}static isDirectory(r){try{return t.statSync(r).isDirectory()}catch{return!1}}}class i{constructor(t){this.parent=null,this.value="",this.children=[],this.base=t}getPath(){let t="",e=this;for(;e.parent;)t=t?`${e.value}${r.sep}${t}`:e.value,e=e.parent;return{relative:t,absolute:r.resolve(this.base,t)}}}exports.PathTreeify=class{constructor({filter:t,base:r}){if(void 0!==t&&(this.validateFilter(t),this.filter=t),!r||!e.isValid(r))throw new Error(`${r} is not a valid path!`);if(!e.isDirectory(r))throw new Error(`${r} is not a dirPath!`);this.base=r}validateFilter(t){if("function"!=typeof t)throw new TypeError("filter must be a function");if(1!==t.length)throw new TypeError("filter must accept exactly one parameter");try{if("boolean"!=typeof t({name:"test",postPath:"/test"}))throw new TypeError("filter must return a boolean")}catch(t){throw new TypeError("filter function threw an error during test: "+t)}}initNode(t=null){const r=new i(this.base);return t&&(r.parent=t),r}buildChildren(e,i){const s=t.readdirSync(e),a=[];for(const n of s){const s=r.join(e,n);if(!t.statSync(s).isDirectory())continue;if(this.filter&&!this.filter({dirPath:e,name:n}))continue;const o=this.initNode();o.value=n,o.parent=i,o.children=this.buildChildren(s,o),a.push(o)}return a}checkRelativePaths(t){if(!Array.isArray(t))throw new Error("Expected array, got "+typeof t);for(let i=0;i<t.length;i++){const s=t[i];if("string"!=typeof s)throw new Error(`Item at index ${i} is not a string, got ${typeof s}`);const a=r.resolve(this.base,s);if(!e.isValid(a))throw new Error(`Path does not exist or is not accessible: ${a} (from relative path: ${s})`);if(!e.isDirectory(a))throw new Error(`Path is not a directory: ${a} (from relative path: ${s})`)}}formatDirnames(t){return t.map(t=>t.replace(/^\/+|\/+$/g,"")).filter(t=>""!==t)}getAllDirNamesUnderBase(){return t.readdirSync(this.base).filter(t=>{const i=r.resolve(this.base,t);return e.isDirectory(i)})}buildByDirNames(t){const e=this.initNode();this.checkRelativePaths(t);const i=this.formatDirnames(t);for(const t of i){const i=this.initNode();i.value=t,i.parent=e,i.children=this.buildChildren(r.resolve(this.base,t),i),e.children.push(i)}return e}buildByFilter(t){const r=this.getAllDirNamesUnderBase();return this.buildByDirNames(r.filter(t))}getPathBy(t){let e="",i=t;for(;i.parent;)e=e?`${i.value}${r.sep}${e}`:i.value,i=i.parent;return{relative:e,absolute:r.resolve(this.base,e)}}buildBy(t){if(Array.isArray(t))return this.buildByDirNames(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.getAllDirNamesUnderBase();return this.buildByDirNames(t)}};
package/dist/index.mjs CHANGED
@@ -1,157 +1 @@
1
- import { readdirSync, statSync, accessSync, constants } from 'fs';
2
- import { join, resolve, sep } from 'path';
3
-
4
- class PathValidator {
5
- static isValid(path) {
6
- try {
7
- accessSync(path, constants.F_OK);
8
- return true;
9
- }
10
- catch {
11
- return false;
12
- }
13
- }
14
- static isDirectory(path) {
15
- try {
16
- return statSync(path).isDirectory();
17
- }
18
- catch {
19
- return false;
20
- }
21
- }
22
- }
23
- class PathTreeNode {
24
- constructor(base) {
25
- this.parent = null;
26
- this.value = '';
27
- this.children = [];
28
- this.base = base;
29
- }
30
- getPath() {
31
- let relative = '';
32
- let current = this;
33
- while (current.parent) {
34
- relative = relative
35
- ? `${current.value}${sep}${relative}`
36
- : current.value;
37
- current = current.parent;
38
- }
39
- return { relative, absolute: resolve(this.base, relative) };
40
- }
41
- }
42
- class PathTreeify {
43
- constructor({ filter, base }) {
44
- if (typeof filter !== 'undefined') {
45
- this.validateFilter(filter);
46
- this.filter = filter;
47
- }
48
- if (!base || !PathValidator.isValid(base)) {
49
- throw new Error(`${base} is not a valid path!`);
50
- }
51
- if (!PathValidator.isDirectory(base)) {
52
- throw new Error(`${base} is not a dirPath!`);
53
- }
54
- this.base = base;
55
- }
56
- validateFilter(filter) {
57
- if (typeof filter !== 'function') {
58
- throw new TypeError('filter must be a function');
59
- }
60
- if (filter.length !== 1) {
61
- throw new TypeError('filter must accept exactly one parameter');
62
- }
63
- try {
64
- const testResult = filter({ name: 'test', postPath: '/test' });
65
- if (typeof testResult !== 'boolean') {
66
- throw new TypeError('filter must return a boolean');
67
- }
68
- }
69
- catch (error) {
70
- throw new TypeError('filter function threw an error during test: ' + error);
71
- }
72
- }
73
- initNode(parent = null) {
74
- const node = new PathTreeNode(this.base);
75
- if (parent) {
76
- node.parent = parent;
77
- }
78
- return node;
79
- }
80
- buildChildren(dirPath, parent) {
81
- const names = readdirSync(dirPath);
82
- const children = [];
83
- for (const name of names) {
84
- const subPath = join(dirPath, name);
85
- if (!statSync(subPath).isDirectory()) {
86
- continue;
87
- }
88
- if (this.filter && !this.filter({ dirPath, name })) {
89
- continue;
90
- }
91
- const node = this.initNode();
92
- node.value = name;
93
- node.parent = parent;
94
- node.children = this.buildChildren(subPath, node);
95
- children.push(node);
96
- }
97
- return children;
98
- }
99
- checkRelativePaths(relativeDirNames) {
100
- if (!Array.isArray(relativeDirNames)) {
101
- throw new Error(`Expected array, got ${typeof relativeDirNames}`);
102
- }
103
- for (let i = 0; i < relativeDirNames.length; i++) {
104
- const it = relativeDirNames[i];
105
- if (typeof it !== 'string') {
106
- throw new Error(`Item at index ${i} is not a string, got ${typeof it}`);
107
- }
108
- const absPath = resolve(this.base, it);
109
- if (!PathValidator.isValid(absPath)) {
110
- throw new Error(`Path does not exist or is not accessible: ${absPath} (from relative path: ${it})`);
111
- }
112
- if (!PathValidator.isDirectory(absPath)) {
113
- throw new Error(`Path is not a directory: ${absPath} (from relative path: ${it})`);
114
- }
115
- }
116
- }
117
- formatDirnames(dirNames) {
118
- return dirNames.map(dir => {
119
- // Remove leading and trailing slashes
120
- return dir.replace(/^\/+|\/+$/g, '');
121
- }).filter(dir => dir !== ''); // Optional: filter empty strings
122
- }
123
- getPathBy(node) {
124
- let relative = '';
125
- let current = node;
126
- while (current.parent) {
127
- relative = relative
128
- ? `${current.value}${sep}${relative}`
129
- : current.value;
130
- current = current.parent;
131
- }
132
- return { relative, absolute: resolve(this.base, relative) };
133
- }
134
- buildByDirNames(dirNames) {
135
- const root = this.initNode();
136
- this.checkRelativePaths(dirNames);
137
- const dirNameArr = this.formatDirnames(dirNames);
138
- for (const dirName of dirNameArr) {
139
- const node = this.initNode();
140
- node.value = dirName;
141
- node.parent = root;
142
- node.children = this.buildChildren(resolve(this.base, dirName), node);
143
- root.children.push(node);
144
- }
145
- return root;
146
- }
147
- build() {
148
- const dirNameArr = readdirSync(this.base).filter(name => {
149
- const abs = resolve(this.base, name);
150
- return PathValidator.isDirectory(abs);
151
- });
152
- return this.buildByDirNames(dirNameArr);
153
- }
154
- }
155
-
156
- export { PathTreeify };
157
- //# sourceMappingURL=index.mjs.map
1
+ import{readdirSync as t,statSync as r,accessSync as e,constants as i}from"fs";import{join as s,resolve as a,sep as n}from"path";class o{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}}}class l{constructor(t){this.parent=null,this.value="",this.children=[],this.base=t}getPath(){let t="",r=this;for(;r.parent;)t=t?`${r.value}${n}${t}`:r.value,r=r.parent;return{relative:t,absolute:a(this.base,t)}}}class h{constructor({filter:t,base:r}){if(void 0!==t&&(this.validateFilter(t),this.filter=t),!r||!o.isValid(r))throw new Error(`${r} is not a valid path!`);if(!o.isDirectory(r))throw new Error(`${r} is not a dirPath!`);this.base=r}validateFilter(t){if("function"!=typeof t)throw new TypeError("filter must be a function");if(1!==t.length)throw new TypeError("filter must accept exactly one parameter");try{if("boolean"!=typeof t({name:"test",postPath:"/test"}))throw new TypeError("filter must return a boolean")}catch(t){throw new TypeError("filter function threw an error during test: "+t)}}initNode(t=null){const r=new l(this.base);return t&&(r.parent=t),r}buildChildren(e,i){const a=t(e),n=[];for(const t of a){const a=s(e,t);if(!r(a).isDirectory())continue;if(this.filter&&!this.filter({dirPath:e,name:t}))continue;const o=this.initNode();o.value=t,o.parent=i,o.children=this.buildChildren(a,o),n.push(o)}return n}checkRelativePaths(t){if(!Array.isArray(t))throw new Error("Expected array, got "+typeof t);for(let r=0;r<t.length;r++){const e=t[r];if("string"!=typeof e)throw new Error(`Item at index ${r} is not a string, got ${typeof e}`);const i=a(this.base,e);if(!o.isValid(i))throw new Error(`Path does not exist or is not accessible: ${i} (from relative path: ${e})`);if(!o.isDirectory(i))throw new Error(`Path is not a directory: ${i} (from relative path: ${e})`)}}formatDirnames(t){return t.map(t=>t.replace(/^\/+|\/+$/g,"")).filter(t=>""!==t)}getAllDirNamesUnderBase(){return t(this.base).filter(t=>{const r=a(this.base,t);return o.isDirectory(r)})}buildByDirNames(t){const r=this.initNode();this.checkRelativePaths(t);const e=this.formatDirnames(t);for(const t of e){const e=this.initNode();e.value=t,e.parent=r,e.children=this.buildChildren(a(this.base,t),e),r.children.push(e)}return r}buildByFilter(t){const r=this.getAllDirNamesUnderBase();return this.buildByDirNames(r.filter(t))}getPathBy(t){let r="",e=t;for(;e.parent;)r=r?`${e.value}${n}${r}`:e.value,e=e.parent;return{relative:r,absolute:a(this.base,r)}}buildBy(t){if(Array.isArray(t))return this.buildByDirNames(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.getAllDirNamesUnderBase();return this.buildByDirNames(t)}}export{h as PathTreeify};
@@ -1,36 +1,93 @@
1
+ /** A filter function that determines whether a directory should be included in the tree */
1
2
  type FilterFunction = (params: {
2
3
  name: string;
3
4
  dirPath: string;
4
5
  }) => boolean;
6
+ /** Constructor options for PathTreeify */
5
7
  interface PathTreeifyProps {
6
8
  base: string;
7
9
  filter?: FilterFunction;
8
10
  }
11
+ /** Represents a single node (directory) in the path tree */
9
12
  declare class PathTreeNode {
13
+ /** The root base path used to resolve absolute paths */
10
14
  private base;
15
+ /** Reference to the parent node; null for the root node */
11
16
  parent: PathTreeNode | null;
17
+ /** The directory name of this node (not a full path) */
12
18
  value: string;
19
+ /** Child nodes representing subdirectories */
13
20
  children: PathTreeNode[];
14
21
  constructor(base: string);
22
+ /**
23
+ * Walks up the parent chain to compute this node's relative and absolute paths.
24
+ * @returns An object containing the relative path from base and the absolute path
25
+ */
15
26
  getPath(): {
16
27
  relative: string;
17
28
  absolute: string;
18
29
  };
19
30
  }
31
+ /** Builds a tree of directory nodes rooted at a given base path */
20
32
  export declare class PathTreeify {
33
+ /** The root directory to scan */
21
34
  private base;
35
+ /** Optional filter applied to each directory during traversal */
22
36
  private filter?;
23
37
  constructor({ filter, base }: Partial<PathTreeifyProps>);
38
+ /**
39
+ * Validates that the provided filter is a function, accepts one parameter,
40
+ * and returns a boolean. Throws a TypeError if any condition is violated.
41
+ */
24
42
  private validateFilter;
43
+ /**
44
+ * Creates and optionally attaches a new PathTreeNode to a parent.
45
+ * @param parent - The parent node to attach to, or null for the root
46
+ */
25
47
  private initNode;
48
+ /**
49
+ * Recursively reads a directory and builds child nodes for each subdirectory.
50
+ * Applies the instance-level filter if one is set.
51
+ * @param dirPath - Absolute path of the directory to read
52
+ * @param parent - The parent node to attach children to
53
+ */
26
54
  private buildChildren;
55
+ /**
56
+ * Validates that each entry in the array is a string pointing to
57
+ * an accessible directory relative to the base path.
58
+ * @param relativeDirNames - Array of relative directory path strings to validate
59
+ */
27
60
  private checkRelativePaths;
61
+ /**
62
+ * Strips leading and trailing slashes from each directory name
63
+ * and removes any resulting empty strings.
64
+ */
28
65
  private formatDirnames;
66
+ /** Returns the names of all immediate subdirectories under the base path */
67
+ private getAllDirNamesUnderBase;
68
+ /**
69
+ * Builds a tree rooted at base, containing only the specified subdirectories.
70
+ * @param dirNames - Relative directory names to include as top-level nodes
71
+ */
72
+ private buildByDirNames;
73
+ /**
74
+ * Builds a tree using only the subdirectories under base that pass the given filter.
75
+ * @param filter - A predicate applied to each top-level directory name
76
+ */
77
+ private buildByFilter;
78
+ /**
79
+ * Computes the relative and absolute paths for a given node
80
+ * by walking up the parent chain.
81
+ */
29
82
  getPathBy(node: PathTreeNode): {
30
83
  relative: string;
31
84
  absolute: string;
32
85
  };
33
- buildByDirNames(dirNames: string[]): PathTreeNode;
86
+ /** Overload: build the tree from an explicit list of relative directory names */
87
+ buildBy(dirNames: string[]): PathTreeNode;
88
+ /** Overload: build the tree from a predicate applied to top-level directory names */
89
+ buildBy(filter: (dirName: string) => boolean): PathTreeNode;
90
+ /** Builds a full tree from all immediate subdirectories under the base path */
34
91
  build(): PathTreeNode;
35
92
  }
36
93
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "path-treeify",
3
- "version": "1.1.0",
3
+ "version": "1.2.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",