@crashbytes/dendro 1.0.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/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +109 -0
- package/README.md +385 -0
- package/bin/cb-tree.js +63 -0
- package/bin/cli.js +78 -0
- package/examples/advanced-usage.js +59 -0
- package/examples/basic-usage.js +14 -0
- package/index.js +296 -0
- package/package.json +29 -0
- package/setup-and-test.sh +37 -0
- package/test/test.js +52 -0
- package/test.js +57 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to dendro will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-10-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of dendro ๐ณ
|
|
12
|
+
- Beautiful directory tree visualization with Unicode box-drawing characters
|
|
13
|
+
- Smart file type icons for 30+ file types
|
|
14
|
+
- CLI with flexible options (depth control, filtering, etc.)
|
|
15
|
+
- Programmatic API for Node.js integration
|
|
16
|
+
- Statistics display (file and directory counts)
|
|
17
|
+
- Smart default exclusions (node_modules, .git, etc.)
|
|
18
|
+
- Regex-based pattern exclusion
|
|
19
|
+
- Hidden file support with `-a` flag
|
|
20
|
+
- Full path display option
|
|
21
|
+
- Plain text mode (no icons)
|
|
22
|
+
- Comprehensive documentation and examples
|
|
23
|
+
|
|
24
|
+
### Features
|
|
25
|
+
- ๐ Directory visualization
|
|
26
|
+
- ๐จ File type icons
|
|
27
|
+
- โก Fast directory traversal
|
|
28
|
+
- ๐ฏ Pattern-based filtering
|
|
29
|
+
- ๐ Built-in statistics
|
|
30
|
+
- ๐ง Both CLI and API
|
|
31
|
+
|
|
32
|
+
[1.0.0]: https://github.com/CrashBytes/dendron/releases/tag/v1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CrashBytes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/QUICKSTART.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Quick Start Guide - dendro ๐ณ
|
|
2
|
+
|
|
3
|
+
## What is dendro?
|
|
4
|
+
|
|
5
|
+
**dendro** (ฮดฮญฮฝฮดฯฮฟ) is Greek for "tree" - a beautiful CLI tool to visualize directory structures with file type icons.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd ~/github/crashbytes-tree
|
|
11
|
+
npm install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Testing Methods
|
|
15
|
+
|
|
16
|
+
### Method 1: Run the test suite
|
|
17
|
+
```bash
|
|
18
|
+
npm test
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Method 2: Run CLI directly (no installation)
|
|
22
|
+
```bash
|
|
23
|
+
# Test on current directory
|
|
24
|
+
node bin/cli.js
|
|
25
|
+
|
|
26
|
+
# Test on crashbytes blog
|
|
27
|
+
node bin/cli.js ~/github/crashbytes
|
|
28
|
+
|
|
29
|
+
# Test with depth limit
|
|
30
|
+
node bin/cli.js ~/github/crashbytes -d 2
|
|
31
|
+
|
|
32
|
+
# Show all options
|
|
33
|
+
node bin/cli.js --help
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Method 3: Install globally (recommended)
|
|
37
|
+
```bash
|
|
38
|
+
npm link
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then use anywhere:
|
|
42
|
+
```bash
|
|
43
|
+
dendro
|
|
44
|
+
dendro ~/github/crashbytes -d 3
|
|
45
|
+
dendro . -a
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Method 4: Use with npx (after publishing)
|
|
49
|
+
```bash
|
|
50
|
+
npx @crashbytes/dendro
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Common Test Commands
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Show 2 levels of current directory
|
|
57
|
+
node bin/cli.js -d 2
|
|
58
|
+
|
|
59
|
+
# Show all files including hidden
|
|
60
|
+
node bin/cli.js -a
|
|
61
|
+
|
|
62
|
+
# Exclude patterns
|
|
63
|
+
node bin/cli.js -e "test" "*.log"
|
|
64
|
+
|
|
65
|
+
# Show without icons
|
|
66
|
+
node bin/cli.js --no-icons
|
|
67
|
+
|
|
68
|
+
# Show with full paths
|
|
69
|
+
node bin/cli.js -p
|
|
70
|
+
|
|
71
|
+
# Visualize the dendro project itself
|
|
72
|
+
node bin/cli.js . -d 2
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Programmatic Usage
|
|
76
|
+
|
|
77
|
+
Create a test file:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
// my-test.js
|
|
81
|
+
const { buildTree, renderTree, getTreeStats } = require('@crashbytes/dendro');
|
|
82
|
+
|
|
83
|
+
const tree = buildTree('.', { maxDepth: 2 });
|
|
84
|
+
const output = renderTree(tree, { showIcons: true });
|
|
85
|
+
console.log(output);
|
|
86
|
+
|
|
87
|
+
const stats = getTreeStats(tree);
|
|
88
|
+
console.log(`\n${stats.directories} dirs, ${stats.files} files`);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Run it:
|
|
92
|
+
```bash
|
|
93
|
+
node my-test.js
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Publishing to NPM
|
|
97
|
+
|
|
98
|
+
When ready:
|
|
99
|
+
```bash
|
|
100
|
+
npm login
|
|
101
|
+
npm publish
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Why "dendro"?
|
|
105
|
+
|
|
106
|
+
- **ฮดฮญฮฝฮดฯฮฟ** (dendro) = Greek for "tree"
|
|
107
|
+
- Short, memorable, professional
|
|
108
|
+
- Used in science (dendrology, dendrochronology)
|
|
109
|
+
- Perfect for visualizing tree structures!
|
package/README.md
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# dendro ๐ณ
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@crashbytes/dendro)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://github.com/CrashBytes/dendron/issues)
|
|
6
|
+
[](https://github.com/CrashBytes/dendron/stargazers)
|
|
7
|
+
|
|
8
|
+
**dendro** (ฮดฮญฮฝฮดฯฮฟ) - from the Greek word for "tree"
|
|
9
|
+
|
|
10
|
+
A beautiful, fast directory tree visualization CLI with intuitive file type icons.
|
|
11
|
+
|
|
12
|
+
๐ **[GitHub Repository](https://github.com/CrashBytes/dendron)** | ๐ฆ **[npm Package](https://www.npmjs.com/package/@crashbytes/dendro)**
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Why "dendro"?
|
|
17
|
+
|
|
18
|
+
The name **dendro** comes from the Greek word **ฮดฮญฮฝฮดฯฮฟ** (pronounced "DEN-droh"), meaning "tree". It's:
|
|
19
|
+
|
|
20
|
+
- **Memorable** - Short, unique, and easy to remember
|
|
21
|
+
- **Meaningful** - Directly relates to tree structures and hierarchies
|
|
22
|
+
- **Professional** - Used in scientific contexts (dendrology, dendrochronology)
|
|
23
|
+
- **Global** - Recognizable across languages due to its Greek root
|
|
24
|
+
- **Concise** - Just 6 letters, quick to type
|
|
25
|
+
|
|
26
|
+
When you visualize directory structures, you're essentially mapping a treeโand that's exactly what dendro does, with style.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- ๐ณ Beautiful tree visualization with Unicode box-drawing characters
|
|
33
|
+
- ๐จ **Smart file type icons** for instant visual recognition
|
|
34
|
+
- โก Lightning fast - optimized directory traversal
|
|
35
|
+
- ๐ฏ Flexible filtering with regex pattern support
|
|
36
|
+
- ๐ Built-in statistics (file counts, directory counts)
|
|
37
|
+
- ๐ง Both CLI and programmatic API
|
|
38
|
+
- ๐ญ Smart defaults (auto-excludes node_modules, .git, etc.)
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
### Via npm (Recommended)
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g @crashbytes/dendro
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Via npx (No Installation)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
npx @crashbytes/dendro
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### From Source (GitHub)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/CrashBytes/dendron.git
|
|
58
|
+
cd dendron
|
|
59
|
+
npm install
|
|
60
|
+
npm link
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Quick Start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Visualize current directory
|
|
67
|
+
dendro
|
|
68
|
+
|
|
69
|
+
# Visualize specific directory
|
|
70
|
+
dendro /path/to/project
|
|
71
|
+
|
|
72
|
+
# Limit depth to 3 levels
|
|
73
|
+
dendro ~/projects -d 3
|
|
74
|
+
|
|
75
|
+
# Show all files including hidden
|
|
76
|
+
dendro -a
|
|
77
|
+
|
|
78
|
+
# Show help
|
|
79
|
+
dendro --help
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage
|
|
83
|
+
|
|
84
|
+
### Command Line Options
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
Usage: dendro [path] [options]
|
|
88
|
+
|
|
89
|
+
Arguments:
|
|
90
|
+
path Directory path to display (default: ".")
|
|
91
|
+
|
|
92
|
+
Options:
|
|
93
|
+
-V, --version Output version number
|
|
94
|
+
-d, --max-depth <number> Maximum depth to traverse
|
|
95
|
+
-a, --all Show hidden files and directories
|
|
96
|
+
--no-icons Disable file type icons
|
|
97
|
+
-p, --show-paths Show full paths
|
|
98
|
+
-e, --exclude <patterns> Patterns to exclude (regex)
|
|
99
|
+
--no-stats Hide statistics summary
|
|
100
|
+
-h, --help Display help
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Examples
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Show only 2 levels deep
|
|
107
|
+
dendro -d 2
|
|
108
|
+
|
|
109
|
+
# Show all files including hidden ones
|
|
110
|
+
dendro -a
|
|
111
|
+
|
|
112
|
+
# Exclude specific patterns (node_modules, test directories)
|
|
113
|
+
dendro -e "node_modules" "test" "__pycache__"
|
|
114
|
+
|
|
115
|
+
# Show full file paths
|
|
116
|
+
dendro -p
|
|
117
|
+
|
|
118
|
+
# Plain text output (no icons)
|
|
119
|
+
dendro --no-icons
|
|
120
|
+
|
|
121
|
+
# Combine options
|
|
122
|
+
dendro ~/my-project -d 3 -a -e "*.log" "dist"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## File Type Icons
|
|
126
|
+
|
|
127
|
+
dendro automatically detects and displays appropriate icons for common file types:
|
|
128
|
+
|
|
129
|
+
| Icon | File Types |
|
|
130
|
+
|------|-----------|
|
|
131
|
+
| ๐ | Directories |
|
|
132
|
+
| ๐ | JavaScript (.js, .jsx, .mjs, .cjs) |
|
|
133
|
+
| ๐ | TypeScript (.ts, .tsx) |
|
|
134
|
+
| ๐ | JSON (.json) |
|
|
135
|
+
| ๐ | Markdown (.md, .mdx) |
|
|
136
|
+
| ๐จ | Stylesheets (.css, .scss, .sass, .less) |
|
|
137
|
+
| ๐ | HTML (.html, .htm) |
|
|
138
|
+
| ๐ผ๏ธ | Images (.png, .jpg, .gif, .svg, .webp) |
|
|
139
|
+
| ๐ฌ | Videos (.mp4, .avi, .mov, .mkv) |
|
|
140
|
+
| ๐ต | Audio (.mp3, .wav, .ogg, .m4a) |
|
|
141
|
+
| ๐ | PDFs (.pdf) |
|
|
142
|
+
| ๐๏ธ | Archives (.zip, .tar, .gz, .rar, .7z) |
|
|
143
|
+
| ๐๏ธ | Databases (.db, .sqlite, .sql) |
|
|
144
|
+
| โ๏ธ | Config files (.yaml, .yml, .toml, .ini) |
|
|
145
|
+
| ๐ | Lock files (package-lock.json, yarn.lock) |
|
|
146
|
+
| ๐ฆ | Git files (.gitignore, .gitattributes) |
|
|
147
|
+
| ๐ | Other text files |
|
|
148
|
+
|
|
149
|
+
## Programmatic API
|
|
150
|
+
|
|
151
|
+
Use dendro in your Node.js projects:
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
const { buildTree, renderTree, getTreeStats } = require('@crashbytes/dendro');
|
|
155
|
+
|
|
156
|
+
// Build a tree structure
|
|
157
|
+
const tree = buildTree('/path/to/directory', {
|
|
158
|
+
maxDepth: 3,
|
|
159
|
+
showHidden: false,
|
|
160
|
+
excludePatterns: [/node_modules/, /\.git/]
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Render as text
|
|
164
|
+
const output = renderTree(tree, {
|
|
165
|
+
showIcons: true,
|
|
166
|
+
showPaths: false
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
console.log(output);
|
|
170
|
+
|
|
171
|
+
// Get statistics
|
|
172
|
+
const stats = getTreeStats(tree);
|
|
173
|
+
console.log(`${stats.directories} directories, ${stats.files} files`);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### API Reference
|
|
177
|
+
|
|
178
|
+
#### `buildTree(dirPath, options)`
|
|
179
|
+
|
|
180
|
+
Builds a tree data structure from a directory.
|
|
181
|
+
|
|
182
|
+
**Parameters:**
|
|
183
|
+
- `dirPath` (string) - Path to directory
|
|
184
|
+
- `options` (object)
|
|
185
|
+
- `maxDepth` (number) - Maximum depth to traverse (default: Infinity)
|
|
186
|
+
- `showHidden` (boolean) - Include hidden files (default: false)
|
|
187
|
+
- `excludePatterns` (RegExp[]) - Patterns to exclude (default: [])
|
|
188
|
+
|
|
189
|
+
**Returns:** Tree object with structure:
|
|
190
|
+
```javascript
|
|
191
|
+
{
|
|
192
|
+
name: string,
|
|
193
|
+
type: 'file' | 'directory',
|
|
194
|
+
icon: string,
|
|
195
|
+
path: string,
|
|
196
|
+
children?: TreeNode[]
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `renderTree(tree, options)`
|
|
201
|
+
|
|
202
|
+
Renders a tree structure as formatted text.
|
|
203
|
+
|
|
204
|
+
**Parameters:**
|
|
205
|
+
- `tree` (object) - Tree structure from buildTree
|
|
206
|
+
- `options` (object)
|
|
207
|
+
- `showIcons` (boolean) - Display file type icons (default: true)
|
|
208
|
+
- `showPaths` (boolean) - Display full paths (default: false)
|
|
209
|
+
- `prefix` (string) - Internal use for recursion
|
|
210
|
+
- `isLast` (boolean) - Internal use for recursion
|
|
211
|
+
|
|
212
|
+
**Returns:** Formatted string representation
|
|
213
|
+
|
|
214
|
+
#### `getTreeStats(tree)`
|
|
215
|
+
|
|
216
|
+
Calculates statistics for a tree.
|
|
217
|
+
|
|
218
|
+
**Returns:** Object with `{ files: number, directories: number }`
|
|
219
|
+
|
|
220
|
+
## Default Exclusions
|
|
221
|
+
|
|
222
|
+
By default (without `-a` flag), dendro excludes:
|
|
223
|
+
- Hidden files/directories (starting with `.`)
|
|
224
|
+
- `node_modules`
|
|
225
|
+
- `.git`
|
|
226
|
+
- `.DS_Store`
|
|
227
|
+
- `dist`
|
|
228
|
+
- `build`
|
|
229
|
+
- `coverage`
|
|
230
|
+
|
|
231
|
+
Override with `-a` or use `-e` to add custom exclusions.
|
|
232
|
+
|
|
233
|
+
## Advanced Usage
|
|
234
|
+
|
|
235
|
+
### Custom Filtering
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
const { buildTree, renderTree } = require('@crashbytes/dendro');
|
|
239
|
+
|
|
240
|
+
// Build tree
|
|
241
|
+
const tree = buildTree('.', { maxDepth: 3 });
|
|
242
|
+
|
|
243
|
+
// Filter to show only JavaScript files
|
|
244
|
+
function filterJS(node) {
|
|
245
|
+
if (node.type === 'file') {
|
|
246
|
+
return /\.(js|jsx|ts|tsx)$/.test(node.name) ? node : null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (node.children) {
|
|
250
|
+
const filtered = node.children.map(filterJS).filter(Boolean);
|
|
251
|
+
return filtered.length > 0 ? { ...node, children: filtered } : null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const jsOnly = filterJS(tree);
|
|
258
|
+
console.log(renderTree(jsOnly));
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Integration with Build Tools
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
// In your build script
|
|
265
|
+
const { buildTree, getTreeStats } = require('@crashbytes/dendro');
|
|
266
|
+
|
|
267
|
+
const tree = buildTree('./dist');
|
|
268
|
+
const stats = getTreeStats(tree);
|
|
269
|
+
|
|
270
|
+
console.log(`Build output: ${stats.files} files in ${stats.directories} directories`);
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Examples
|
|
274
|
+
|
|
275
|
+
See the [`/examples`](https://github.com/CrashBytes/dendron/tree/main/examples) directory for more usage examples:
|
|
276
|
+
- `basic-usage.js` - Simple tree visualization
|
|
277
|
+
- `advanced-usage.js` - Custom filtering and statistics
|
|
278
|
+
|
|
279
|
+
## Contributing
|
|
280
|
+
|
|
281
|
+
We welcome contributions! Here's how you can help:
|
|
282
|
+
|
|
283
|
+
1. **Fork the repository** on [GitHub](https://github.com/CrashBytes/dendron)
|
|
284
|
+
2. **Clone your fork**
|
|
285
|
+
```bash
|
|
286
|
+
git clone https://github.com/YOUR-USERNAME/dendron.git
|
|
287
|
+
cd dendron
|
|
288
|
+
npm install
|
|
289
|
+
```
|
|
290
|
+
3. **Create a feature branch**
|
|
291
|
+
```bash
|
|
292
|
+
git checkout -b feature/amazing-feature
|
|
293
|
+
```
|
|
294
|
+
4. **Make your changes** and test them
|
|
295
|
+
```bash
|
|
296
|
+
npm test
|
|
297
|
+
node bin/cli.js
|
|
298
|
+
```
|
|
299
|
+
5. **Commit your changes**
|
|
300
|
+
```bash
|
|
301
|
+
git commit -m "Add amazing feature"
|
|
302
|
+
```
|
|
303
|
+
6. **Push to your fork**
|
|
304
|
+
```bash
|
|
305
|
+
git push origin feature/amazing-feature
|
|
306
|
+
```
|
|
307
|
+
7. **Open a Pull Request** on GitHub
|
|
308
|
+
|
|
309
|
+
### Areas for Contribution
|
|
310
|
+
|
|
311
|
+
- ๐จ Add more file type icons
|
|
312
|
+
- โก Performance optimizations
|
|
313
|
+
- ๐ Documentation improvements
|
|
314
|
+
- ๐ Bug fixes
|
|
315
|
+
- โจ New features
|
|
316
|
+
|
|
317
|
+
## Bug Reports & Feature Requests
|
|
318
|
+
|
|
319
|
+
Found a bug or have a feature request? Please open an issue on [GitHub Issues](https://github.com/CrashBytes/dendron/issues).
|
|
320
|
+
|
|
321
|
+
When reporting bugs, please include:
|
|
322
|
+
- Your OS and Node.js version
|
|
323
|
+
- Steps to reproduce
|
|
324
|
+
- Expected vs actual behavior
|
|
325
|
+
- Screenshots if applicable
|
|
326
|
+
|
|
327
|
+
## Development
|
|
328
|
+
|
|
329
|
+
### Running Tests
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
npm test
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Local Development
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# Clone the repository
|
|
339
|
+
git clone https://github.com/CrashBytes/dendron.git
|
|
340
|
+
cd dendron
|
|
341
|
+
|
|
342
|
+
# Install dependencies
|
|
343
|
+
npm install
|
|
344
|
+
|
|
345
|
+
# Link for local testing
|
|
346
|
+
npm link
|
|
347
|
+
|
|
348
|
+
# Make changes and test
|
|
349
|
+
dendro ~/test-directory
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Changelog
|
|
353
|
+
|
|
354
|
+
See [CHANGELOG.md](https://github.com/CrashBytes/dendron/blob/main/CHANGELOG.md) for version history.
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
MIT License - see [LICENSE](https://github.com/CrashBytes/dendron/blob/main/LICENSE) file for details.
|
|
359
|
+
|
|
360
|
+
## Author
|
|
361
|
+
|
|
362
|
+
**CrashBytes**
|
|
363
|
+
- Website: [crashbytes.com](https://crashbytes.com)
|
|
364
|
+
- GitHub: [@CrashBytes](https://github.com/CrashBytes)
|
|
365
|
+
- Twitter: [@crashbytes](https://twitter.com/crashbytes)
|
|
366
|
+
|
|
367
|
+
## Acknowledgments
|
|
368
|
+
|
|
369
|
+
Built with โค๏ธ for developers who love beautiful CLIs.
|
|
370
|
+
|
|
371
|
+
Special thanks to all [contributors](https://github.com/CrashBytes/dendron/graphs/contributors) who help make dendro better!
|
|
372
|
+
|
|
373
|
+
---
|
|
374
|
+
|
|
375
|
+
## Support
|
|
376
|
+
|
|
377
|
+
If you find dendro useful, please:
|
|
378
|
+
- โญ Star the project on [GitHub](https://github.com/CrashBytes/dendron)
|
|
379
|
+
- ๐ฆ Share it on social media
|
|
380
|
+
- ๐ Write a blog post about it
|
|
381
|
+
- ๐ฌ Tell your developer friends
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
*dendro - Because every great project starts with understanding its structure* ๐ณ
|
package/bin/cb-tree.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const dirTree = require('../index');
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
const options = {
|
|
8
|
+
all: false,
|
|
9
|
+
depth: Infinity,
|
|
10
|
+
dirsOnly: false,
|
|
11
|
+
exclude: []
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
let targetPath = process.cwd();
|
|
15
|
+
|
|
16
|
+
// Parse arguments
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const arg = args[i];
|
|
19
|
+
|
|
20
|
+
if (arg === '-a' || arg === '--all') {
|
|
21
|
+
options.all = true;
|
|
22
|
+
} else if (arg === '-d' || arg === '--dirs-only') {
|
|
23
|
+
options.dirsOnly = true;
|
|
24
|
+
} else if (arg === '-L' || arg === '--level') {
|
|
25
|
+
options.depth = parseInt(args[++i]);
|
|
26
|
+
} else if (arg === '-I' || arg === '--exclude') {
|
|
27
|
+
options.exclude.push(args[++i]);
|
|
28
|
+
} else if (arg === '-h' || arg === '--help') {
|
|
29
|
+
showHelp();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
} else if (!arg.startsWith('-')) {
|
|
32
|
+
targetPath = path.resolve(arg);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function showHelp() {
|
|
37
|
+
console.log(`
|
|
38
|
+
crashbytes-tree - A beautiful directory tree with file type icons
|
|
39
|
+
|
|
40
|
+
Usage: cb-tree [options] [path]
|
|
41
|
+
|
|
42
|
+
Options:
|
|
43
|
+
-a, --all Show hidden files (starting with .)
|
|
44
|
+
-d, --dirs-only List directories only
|
|
45
|
+
-L, --level <n> Maximum depth of directory tree
|
|
46
|
+
-I, --exclude <pattern> Exclude files/directories matching pattern
|
|
47
|
+
-h, --help Show this help message
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
cb-tree # Show tree of current directory
|
|
51
|
+
cb-tree /path/to/dir # Show tree of specific directory
|
|
52
|
+
cb-tree -L 2 # Show tree with max depth of 2
|
|
53
|
+
cb-tree -a -L 3 # Show tree including hidden files, max depth 3
|
|
54
|
+
cb-tree -d # Show directories only
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
console.log(dirTree(targetPath, options));
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(`Error: ${error.message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const { buildTree, renderTree, getTreeStats } = require('../index');
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('dendro')
|
|
12
|
+
.description('Display directory tree structure with beautiful icons')
|
|
13
|
+
.version('1.0.0')
|
|
14
|
+
.argument('[path]', 'Directory path to display', '.')
|
|
15
|
+
.option('-d, --max-depth <number>', 'Maximum depth to traverse', parseInt)
|
|
16
|
+
.option('-a, --all', 'Show hidden files and directories', false)
|
|
17
|
+
.option('--no-icons', 'Disable file type icons')
|
|
18
|
+
.option('-p, --show-paths', 'Show full paths', false)
|
|
19
|
+
.option('-e, --exclude <patterns...>', 'Patterns to exclude (regex)')
|
|
20
|
+
.option('--no-stats', 'Hide statistics summary')
|
|
21
|
+
.action((dirPath, options) => {
|
|
22
|
+
try {
|
|
23
|
+
const fullPath = path.resolve(dirPath);
|
|
24
|
+
|
|
25
|
+
console.log(chalk.bold.cyan(`\n๐ณ Directory Tree: ${fullPath}\n`));
|
|
26
|
+
|
|
27
|
+
// Build exclude patterns
|
|
28
|
+
const excludePatterns = options.exclude
|
|
29
|
+
? options.exclude.map(p => new RegExp(p))
|
|
30
|
+
: [];
|
|
31
|
+
|
|
32
|
+
// Common excludes by default
|
|
33
|
+
if (!options.all) {
|
|
34
|
+
excludePatterns.push(
|
|
35
|
+
/^node_modules$/,
|
|
36
|
+
/^\.git$/,
|
|
37
|
+
/^\.DS_Store$/,
|
|
38
|
+
/^dist$/,
|
|
39
|
+
/^build$/,
|
|
40
|
+
/^coverage$/
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Build the tree
|
|
45
|
+
const tree = buildTree(fullPath, {
|
|
46
|
+
maxDepth: options.maxDepth || Infinity,
|
|
47
|
+
showHidden: options.all,
|
|
48
|
+
excludePatterns
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!tree) {
|
|
52
|
+
console.log(chalk.red('Error: Could not read directory'));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Render the tree
|
|
57
|
+
const output = renderTree(tree, {
|
|
58
|
+
showIcons: options.icons,
|
|
59
|
+
showPaths: options.showPaths
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
console.log(output);
|
|
63
|
+
|
|
64
|
+
// Show statistics
|
|
65
|
+
if (options.stats) {
|
|
66
|
+
const stats = getTreeStats(tree);
|
|
67
|
+
console.log(chalk.dim(`\n${stats.directories} directories, ${stats.files} files`));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log(); // Empty line at the end
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
program.parse();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const { buildTree, renderTree, getTreeStats } = require('../index');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
|
|
4
|
+
// Advanced usage with custom options
|
|
5
|
+
console.log(chalk.bold.cyan('Advanced Directory Tree Example\n'));
|
|
6
|
+
|
|
7
|
+
const tree = buildTree('.', {
|
|
8
|
+
maxDepth: 3,
|
|
9
|
+
showHidden: true,
|
|
10
|
+
excludePatterns: [
|
|
11
|
+
/node_modules/,
|
|
12
|
+
/\.git/,
|
|
13
|
+
/dist/,
|
|
14
|
+
/coverage/,
|
|
15
|
+
/\.log$/
|
|
16
|
+
]
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Render with custom options
|
|
20
|
+
const output = renderTree(tree, {
|
|
21
|
+
showIcons: true,
|
|
22
|
+
showPaths: false
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
console.log(output);
|
|
26
|
+
|
|
27
|
+
// Get and display statistics
|
|
28
|
+
const stats = getTreeStats(tree);
|
|
29
|
+
console.log(chalk.dim(`\n๐ Statistics:`));
|
|
30
|
+
console.log(chalk.dim(` Directories: ${stats.directories}`));
|
|
31
|
+
console.log(chalk.dim(` Files: ${stats.files}`));
|
|
32
|
+
console.log(chalk.dim(` Total items: ${stats.directories + stats.files}`));
|
|
33
|
+
|
|
34
|
+
// Custom filtering example
|
|
35
|
+
console.log(chalk.bold.yellow('\n\n๐ JavaScript files only:\n'));
|
|
36
|
+
|
|
37
|
+
function filterJavaScriptFiles(node) {
|
|
38
|
+
if (node.type === 'file') {
|
|
39
|
+
const ext = node.name.split('.').pop();
|
|
40
|
+
return ['js', 'jsx', 'ts', 'tsx'].includes(ext) ? node : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (node.children) {
|
|
44
|
+
const filteredChildren = node.children
|
|
45
|
+
.map(filterJavaScriptFiles)
|
|
46
|
+
.filter(Boolean);
|
|
47
|
+
|
|
48
|
+
if (filteredChildren.length > 0) {
|
|
49
|
+
return { ...node, children: filteredChildren };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const jsTree = filterJavaScriptFiles(tree);
|
|
57
|
+
if (jsTree) {
|
|
58
|
+
console.log(renderTree(jsTree, { showIcons: true }));
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const { buildTree, renderTree } = require('../index');
|
|
2
|
+
|
|
3
|
+
// Basic usage example
|
|
4
|
+
const tree = buildTree('.', {
|
|
5
|
+
maxDepth: 2,
|
|
6
|
+
showHidden: false
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const output = renderTree(tree, {
|
|
10
|
+
showIcons: true,
|
|
11
|
+
showPaths: false
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
console.log(output);
|
package/index.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
// Icon mappings for different file types and directories
|
|
5
|
+
const icons = {
|
|
6
|
+
// Directory
|
|
7
|
+
directory: '๐',
|
|
8
|
+
directoryOpen: '๐',
|
|
9
|
+
|
|
10
|
+
// Common file types
|
|
11
|
+
javascript: '๐',
|
|
12
|
+
typescript: '๐',
|
|
13
|
+
json: '๐',
|
|
14
|
+
markdown: '๐',
|
|
15
|
+
text: '๐',
|
|
16
|
+
image: '๐ผ๏ธ',
|
|
17
|
+
video: '๐ฌ',
|
|
18
|
+
audio: '๐ต',
|
|
19
|
+
pdf: '๐',
|
|
20
|
+
zip: '๐๏ธ',
|
|
21
|
+
executable: 'โ๏ธ',
|
|
22
|
+
config: 'โ๏ธ',
|
|
23
|
+
css: '๐จ',
|
|
24
|
+
html: '๐',
|
|
25
|
+
database: '๐๏ธ',
|
|
26
|
+
lock: '๐',
|
|
27
|
+
git: '๐ฆ',
|
|
28
|
+
|
|
29
|
+
// Default
|
|
30
|
+
default: '๐'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// File extension to icon mapping
|
|
34
|
+
const extensionMap = {
|
|
35
|
+
// JavaScript/TypeScript
|
|
36
|
+
'.js': icons.javascript,
|
|
37
|
+
'.jsx': icons.javascript,
|
|
38
|
+
'.ts': icons.typescript,
|
|
39
|
+
'.tsx': icons.typescript,
|
|
40
|
+
'.mjs': icons.javascript,
|
|
41
|
+
'.cjs': icons.javascript,
|
|
42
|
+
|
|
43
|
+
// Data formats
|
|
44
|
+
'.json': icons.json,
|
|
45
|
+
'.yaml': icons.config,
|
|
46
|
+
'.yml': icons.config,
|
|
47
|
+
'.xml': icons.config,
|
|
48
|
+
'.toml': icons.config,
|
|
49
|
+
'.ini': icons.config,
|
|
50
|
+
|
|
51
|
+
// Markdown/Documentation
|
|
52
|
+
'.md': icons.markdown,
|
|
53
|
+
'.mdx': icons.markdown,
|
|
54
|
+
'.txt': icons.text,
|
|
55
|
+
'.pdf': icons.pdf,
|
|
56
|
+
|
|
57
|
+
// Web
|
|
58
|
+
'.html': icons.html,
|
|
59
|
+
'.htm': icons.html,
|
|
60
|
+
'.css': icons.css,
|
|
61
|
+
'.scss': icons.css,
|
|
62
|
+
'.sass': icons.css,
|
|
63
|
+
'.less': icons.css,
|
|
64
|
+
|
|
65
|
+
// Images
|
|
66
|
+
'.png': icons.image,
|
|
67
|
+
'.jpg': icons.image,
|
|
68
|
+
'.jpeg': icons.image,
|
|
69
|
+
'.gif': icons.image,
|
|
70
|
+
'.svg': icons.image,
|
|
71
|
+
'.webp': icons.image,
|
|
72
|
+
'.ico': icons.image,
|
|
73
|
+
'.bmp': icons.image,
|
|
74
|
+
|
|
75
|
+
// Video
|
|
76
|
+
'.mp4': icons.video,
|
|
77
|
+
'.avi': icons.video,
|
|
78
|
+
'.mov': icons.video,
|
|
79
|
+
'.mkv': icons.video,
|
|
80
|
+
'.webm': icons.video,
|
|
81
|
+
|
|
82
|
+
// Audio
|
|
83
|
+
'.mp3': icons.audio,
|
|
84
|
+
'.wav': icons.audio,
|
|
85
|
+
'.ogg': icons.audio,
|
|
86
|
+
'.m4a': icons.audio,
|
|
87
|
+
'.flac': icons.audio,
|
|
88
|
+
|
|
89
|
+
// Archives
|
|
90
|
+
'.zip': icons.zip,
|
|
91
|
+
'.tar': icons.zip,
|
|
92
|
+
'.gz': icons.zip,
|
|
93
|
+
'.rar': icons.zip,
|
|
94
|
+
'.7z': icons.zip,
|
|
95
|
+
|
|
96
|
+
// Databases
|
|
97
|
+
'.db': icons.database,
|
|
98
|
+
'.sqlite': icons.database,
|
|
99
|
+
'.sql': icons.database,
|
|
100
|
+
|
|
101
|
+
// Executables
|
|
102
|
+
'.exe': icons.executable,
|
|
103
|
+
'.sh': icons.executable,
|
|
104
|
+
'.bat': icons.executable,
|
|
105
|
+
'.cmd': icons.executable,
|
|
106
|
+
|
|
107
|
+
// Lock files
|
|
108
|
+
'.lock': icons.lock,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Special filename mappings
|
|
112
|
+
const filenameMap = {
|
|
113
|
+
'.gitignore': icons.git,
|
|
114
|
+
'.gitattributes': icons.git,
|
|
115
|
+
'.gitmodules': icons.git,
|
|
116
|
+
'package.json': icons.json,
|
|
117
|
+
'package-lock.json': icons.lock,
|
|
118
|
+
'yarn.lock': icons.lock,
|
|
119
|
+
'pnpm-lock.yaml': icons.lock,
|
|
120
|
+
'.env': icons.config,
|
|
121
|
+
'.env.local': icons.config,
|
|
122
|
+
'.env.development': icons.config,
|
|
123
|
+
'.env.production': icons.config,
|
|
124
|
+
'Dockerfile': icons.config,
|
|
125
|
+
'docker-compose.yml': icons.config,
|
|
126
|
+
'README.md': icons.markdown,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get the appropriate icon for a file or directory
|
|
131
|
+
*/
|
|
132
|
+
function getIcon(filename, isDirectory) {
|
|
133
|
+
if (isDirectory) {
|
|
134
|
+
return icons.directory;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check special filenames first
|
|
138
|
+
if (filenameMap[filename]) {
|
|
139
|
+
return filenameMap[filename];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check by extension
|
|
143
|
+
const ext = path.extname(filename).toLowerCase();
|
|
144
|
+
if (extensionMap[ext]) {
|
|
145
|
+
return extensionMap[ext];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return icons.default;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Build directory tree structure
|
|
153
|
+
*/
|
|
154
|
+
function buildTree(dirPath, options = {}) {
|
|
155
|
+
const {
|
|
156
|
+
maxDepth = Infinity,
|
|
157
|
+
currentDepth = 0,
|
|
158
|
+
showHidden = false,
|
|
159
|
+
excludePatterns = []
|
|
160
|
+
} = options;
|
|
161
|
+
|
|
162
|
+
if (currentDepth >= maxDepth) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const stats = fs.statSync(dirPath);
|
|
168
|
+
const name = path.basename(dirPath);
|
|
169
|
+
|
|
170
|
+
// Check if should exclude
|
|
171
|
+
if (!showHidden && name.startsWith('.')) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (const pattern of excludePatterns) {
|
|
176
|
+
if (name.match(pattern)) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!stats.isDirectory()) {
|
|
182
|
+
return {
|
|
183
|
+
name,
|
|
184
|
+
type: 'file',
|
|
185
|
+
icon: getIcon(name, false),
|
|
186
|
+
path: dirPath
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// It's a directory
|
|
191
|
+
const children = [];
|
|
192
|
+
const entries = fs.readdirSync(dirPath);
|
|
193
|
+
|
|
194
|
+
for (const entry of entries) {
|
|
195
|
+
const childPath = path.join(dirPath, entry);
|
|
196
|
+
const childTree = buildTree(childPath, {
|
|
197
|
+
...options,
|
|
198
|
+
currentDepth: currentDepth + 1
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (childTree) {
|
|
202
|
+
children.push(childTree);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Sort: directories first, then files, alphabetically
|
|
207
|
+
children.sort((a, b) => {
|
|
208
|
+
if (a.type !== b.type) {
|
|
209
|
+
return a.type === 'directory' ? -1 : 1;
|
|
210
|
+
}
|
|
211
|
+
return a.name.localeCompare(b.name);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
name,
|
|
216
|
+
type: 'directory',
|
|
217
|
+
icon: getIcon(name, true),
|
|
218
|
+
path: dirPath,
|
|
219
|
+
children
|
|
220
|
+
};
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error(`Error reading ${dirPath}:`, error.message);
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Render the tree structure as text
|
|
229
|
+
*/
|
|
230
|
+
function renderTree(tree, options = {}) {
|
|
231
|
+
const {
|
|
232
|
+
prefix = '',
|
|
233
|
+
isLast = true,
|
|
234
|
+
showIcons = true,
|
|
235
|
+
showPaths = false
|
|
236
|
+
} = options;
|
|
237
|
+
|
|
238
|
+
if (!tree) return '';
|
|
239
|
+
|
|
240
|
+
const lines = [];
|
|
241
|
+
const connector = isLast ? 'โโโ ' : 'โโโ ';
|
|
242
|
+
const icon = showIcons ? `${tree.icon} ` : '';
|
|
243
|
+
const pathInfo = showPaths ? ` (${tree.path})` : '';
|
|
244
|
+
|
|
245
|
+
lines.push(`${prefix}${connector}${icon}${tree.name}${pathInfo}`);
|
|
246
|
+
|
|
247
|
+
if (tree.children && tree.children.length > 0) {
|
|
248
|
+
const newPrefix = prefix + (isLast ? ' ' : 'โ ');
|
|
249
|
+
|
|
250
|
+
tree.children.forEach((child, index) => {
|
|
251
|
+
const childIsLast = index === tree.children.length - 1;
|
|
252
|
+
lines.push(renderTree(child, {
|
|
253
|
+
...options,
|
|
254
|
+
prefix: newPrefix,
|
|
255
|
+
isLast: childIsLast
|
|
256
|
+
}));
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return lines.join('\n');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Get statistics about the tree
|
|
265
|
+
*/
|
|
266
|
+
function getTreeStats(tree) {
|
|
267
|
+
if (!tree) {
|
|
268
|
+
return { files: 0, directories: 0 };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
let files = 0;
|
|
272
|
+
let directories = 0;
|
|
273
|
+
|
|
274
|
+
if (tree.type === 'file') {
|
|
275
|
+
files = 1;
|
|
276
|
+
} else if (tree.type === 'directory') {
|
|
277
|
+
directories = 1;
|
|
278
|
+
if (tree.children) {
|
|
279
|
+
tree.children.forEach(child => {
|
|
280
|
+
const childStats = getTreeStats(child);
|
|
281
|
+
files += childStats.files;
|
|
282
|
+
directories += childStats.directories;
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return { files, directories };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
module.exports = {
|
|
291
|
+
buildTree,
|
|
292
|
+
renderTree,
|
|
293
|
+
getTreeStats,
|
|
294
|
+
getIcon,
|
|
295
|
+
icons
|
|
296
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crashbytes/dendro",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A beautiful directory tree visualization CLI with file type icons - dendro (ฮดฮญฮฝฮดฯฮฟ) means 'tree' in Greek",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dendro": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node test.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"directory",
|
|
14
|
+
"tree",
|
|
15
|
+
"cli",
|
|
16
|
+
"file-tree",
|
|
17
|
+
"icons",
|
|
18
|
+
"visualization"
|
|
19
|
+
],
|
|
20
|
+
"author": "CrashBytes",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"commander": "^11.1.0",
|
|
24
|
+
"chalk": "^4.1.2"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=14.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
echo "๐ณ Setting up dendro package..."
|
|
4
|
+
echo ""
|
|
5
|
+
|
|
6
|
+
# Navigate to the package directory
|
|
7
|
+
cd "$(dirname "$0")"
|
|
8
|
+
|
|
9
|
+
echo "๐ฆ Installing dependencies..."
|
|
10
|
+
npm install
|
|
11
|
+
|
|
12
|
+
echo ""
|
|
13
|
+
echo "โ
Installation complete!"
|
|
14
|
+
echo ""
|
|
15
|
+
echo "๐งช Running tests..."
|
|
16
|
+
echo ""
|
|
17
|
+
node test.js
|
|
18
|
+
|
|
19
|
+
echo ""
|
|
20
|
+
echo "๐ Available commands:"
|
|
21
|
+
echo ""
|
|
22
|
+
echo " 1. Test locally:"
|
|
23
|
+
echo " node bin/cli.js"
|
|
24
|
+
echo ""
|
|
25
|
+
echo " 2. Test on a specific directory:"
|
|
26
|
+
echo " node bin/cli.js ~/github/crashbytes -d 2"
|
|
27
|
+
echo ""
|
|
28
|
+
echo " 3. Install globally (link):"
|
|
29
|
+
echo " npm link"
|
|
30
|
+
echo " Then run: dendro"
|
|
31
|
+
echo ""
|
|
32
|
+
echo " 4. Show help:"
|
|
33
|
+
echo " node bin/cli.js --help"
|
|
34
|
+
echo ""
|
|
35
|
+
echo " 5. Visualize the dendro project itself:"
|
|
36
|
+
echo " node bin/cli.js . -d 2"
|
|
37
|
+
echo ""
|
package/test/test.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const { buildTree, treeToString, getIcon } = require('../index');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
console.log('๐งช Running crashbytes-tree tests...\n');
|
|
5
|
+
|
|
6
|
+
// Test 1: Icon recognition
|
|
7
|
+
console.log('Test 1: Icon Recognition');
|
|
8
|
+
console.log('------------------------');
|
|
9
|
+
console.log('Folder icon:', getIcon('my-folder', true));
|
|
10
|
+
console.log('JavaScript icon:', getIcon('app.js', false));
|
|
11
|
+
console.log('JSON icon:', getIcon('package.json', false));
|
|
12
|
+
console.log('Markdown icon:', getIcon('README.md', false));
|
|
13
|
+
console.log('node_modules icon:', getIcon('node_modules', true));
|
|
14
|
+
console.log('โ
Icon recognition test passed\n');
|
|
15
|
+
|
|
16
|
+
// Test 2: Build tree structure
|
|
17
|
+
console.log('Test 2: Build Tree Structure');
|
|
18
|
+
console.log('------------------------');
|
|
19
|
+
try {
|
|
20
|
+
const tree = buildTree(path.join(__dirname, '..'), {
|
|
21
|
+
maxDepth: 1,
|
|
22
|
+
showHidden: false,
|
|
23
|
+
ignorePatterns: ['node_modules', '.git']
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
console.log('Root name:', tree.name);
|
|
27
|
+
console.log('Root type:', tree.type);
|
|
28
|
+
console.log('Root icon:', tree.icon);
|
|
29
|
+
console.log('Children count:', tree.children ? tree.children.length : 0);
|
|
30
|
+
console.log('โ
Build tree test passed\n');
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error('โ Build tree test failed:', error.message);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Test 3: Tree to string conversion
|
|
36
|
+
console.log('Test 3: Tree to String');
|
|
37
|
+
console.log('------------------------');
|
|
38
|
+
try {
|
|
39
|
+
const tree = buildTree(path.join(__dirname, '..'), {
|
|
40
|
+
maxDepth: 2,
|
|
41
|
+
showHidden: false,
|
|
42
|
+
ignorePatterns: ['node_modules', '.git', 'test']
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const treeString = treeToString(tree);
|
|
46
|
+
console.log(treeString);
|
|
47
|
+
console.log('โ
Tree to string test passed\n');
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error('โ Tree to string test failed:', error.message);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log('โ
All tests completed!');
|
package/test.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { buildTree, renderTree, getTreeStats, getIcon } = require('./index');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
console.log('Running tests for dendro...\n');
|
|
5
|
+
|
|
6
|
+
// Test 1: Icon selection
|
|
7
|
+
console.log('Test 1: Icon Selection');
|
|
8
|
+
console.log('JavaScript file:', getIcon('app.js', false));
|
|
9
|
+
console.log('TypeScript file:', getIcon('component.tsx', false));
|
|
10
|
+
console.log('JSON file:', getIcon('package.json', false));
|
|
11
|
+
console.log('Markdown file:', getIcon('README.md', false));
|
|
12
|
+
console.log('Directory:', getIcon('src', true));
|
|
13
|
+
console.log('โ Icon selection test passed\n');
|
|
14
|
+
|
|
15
|
+
// Test 2: Build tree for current directory (limited depth)
|
|
16
|
+
console.log('Test 2: Build Tree');
|
|
17
|
+
const testPath = path.resolve('.');
|
|
18
|
+
const tree = buildTree(testPath, {
|
|
19
|
+
maxDepth: 2,
|
|
20
|
+
showHidden: false,
|
|
21
|
+
excludePatterns: [/node_modules/, /\.git/]
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (tree && tree.type === 'directory') {
|
|
25
|
+
console.log('โ Tree built successfully');
|
|
26
|
+
console.log(` Root: ${tree.name}`);
|
|
27
|
+
console.log(` Children: ${tree.children ? tree.children.length : 0}`);
|
|
28
|
+
} else {
|
|
29
|
+
console.log('โ Tree build failed');
|
|
30
|
+
}
|
|
31
|
+
console.log();
|
|
32
|
+
|
|
33
|
+
// Test 3: Render tree
|
|
34
|
+
console.log('Test 3: Render Tree (first 2 levels)');
|
|
35
|
+
const rendered = renderTree(tree, {
|
|
36
|
+
showIcons: true,
|
|
37
|
+
showPaths: false
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (rendered && rendered.length > 0) {
|
|
41
|
+
console.log('โ Tree rendered successfully');
|
|
42
|
+
console.log('\nPreview:');
|
|
43
|
+
console.log(rendered.split('\n').slice(0, 15).join('\n'));
|
|
44
|
+
console.log('...');
|
|
45
|
+
} else {
|
|
46
|
+
console.log('โ Tree render failed');
|
|
47
|
+
}
|
|
48
|
+
console.log();
|
|
49
|
+
|
|
50
|
+
// Test 4: Statistics
|
|
51
|
+
console.log('Test 4: Statistics');
|
|
52
|
+
const stats = getTreeStats(tree);
|
|
53
|
+
console.log(`Files: ${stats.files}`);
|
|
54
|
+
console.log(`Directories: ${stats.directories}`);
|
|
55
|
+
console.log('โ Statistics test passed\n');
|
|
56
|
+
|
|
57
|
+
console.log('All tests completed!');
|