tangled
alpha
login
or
join now
tbeseda.com
/
array-treeify
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
add chars and plain options
tbeseda.com
11 months ago
0a7bfd9c
65318035
+781
-34
8 changed files
expand all
collapse all
unified
split
README.md
biome.json
package-lock.json
package.json
src
index.test.ts
index.ts
readme-examples.test.ts
tsconfig.json
+59
-3
README.md
···
1
1
# `array-treeify`
2
2
3
3
-
**Simple ASCII trees from arrays. For your terminal and console displays.**
3
3
+
**Simple text trees from arrays using Unicode box-drawing characters. For your terminal and console displays.**
4
4
5
5
[](https://www.typescriptlang.org/)
6
6
[](https://www.npmjs.com/package/array-treeify)
···
8
8
9
9
## Overview
10
10
11
11
-
`array-treeify` transforms nested arrays into beautiful ASCII trees with proper branching characters. Perfect for CLIs, debug outputs, or anywhere you need to visualize hierarchical data.
11
11
+
`array-treeify` transforms nested arrays into text trees with proper branching characters. Perfect for CLIs, debug outputs, or anywhere you need to visualize hierarchical data.
12
12
+
13
13
+
```typescript
14
14
+
treeify([
15
15
+
'Lumon Industries',
16
16
+
[
17
17
+
'Board of Directors',
18
18
+
['Natalie (Representative)'],
19
19
+
'Departments',
20
20
+
[
21
21
+
'Macrodata Refinement (Cobel)',
22
22
+
['Milchick', 'Mark S.', ['Dylan G.', 'Irving B.', 'Helly R.']],
23
23
+
],
24
24
+
'Other Departments',
25
25
+
[
26
26
+
'Optics & Design',
27
27
+
'Wellness Center',
28
28
+
'Mammalians Nurturable',
29
29
+
'Choreography and Merriment',
30
30
+
],
31
31
+
],
32
32
+
])
33
33
+
```
34
34
+
12
35
13
36
```
14
37
Lumon Industries
···
37
60
## Usage
38
61
39
62
```typescript
40
40
-
function treeify(input: TreeInput): string
63
63
+
function treeify(input: TreeInput, options?: {
64
64
+
chars?: TreeChars, // Custom characters for the tree
65
65
+
plain?: boolean // Use plain whitespace instead of Unicode box-drawing characters
66
66
+
}): string
41
67
```
42
68
43
69
`array-treeify` accepts a simple, intuitive array structure that's easy to build and manipulate:
···
68
94
└─ Ambrose Eagan
69
95
*/
70
96
97
97
+
// Using custom characters
98
98
+
const resultCustomChars = treeify(
99
99
+
eagan,
100
100
+
{ chars: { branch: '├• ', lastBranch: '└• ', pipe: '│ ', space: ' ' },
101
101
+
})
102
102
+
/*
103
103
+
Kier Eagan
104
104
+
├• ...
105
105
+
│ ├• ...
106
106
+
│ └• Jame Eagan
107
107
+
│ └• Helena Eagan
108
108
+
└• Ambrose Eagan
109
109
+
*/
110
110
+
111
111
+
// Using plain whitespace characters
112
112
+
console.log(treeify(eagan, { plain: true }))
113
113
+
/*
114
114
+
Kier Eagan
115
115
+
...
116
116
+
...
117
117
+
Jame Eagan
118
118
+
Helena Eagan
119
119
+
Ambrose Eagan
120
120
+
*/
121
121
+
71
122
// Nested example
72
123
const orgChart = [
73
124
'Lumon Industries',
···
109
160
['root', ['child'], 'sibling', ['nephew', 'niece']] // 2 root nodes with children
110
161
['root', ['child', ['grandchild']]] // Grandchildren
111
162
```
163
163
+
164
164
+
## Options
165
165
+
166
166
+
- `chars`: Custom characters for the tree. Defaults to Unicode box-drawing characters.
167
167
+
- `plain`: When true, uses plain whitespace characters instead of Unicode box-drawing characters.
112
168
113
169
## License
114
170
+1
-1
biome.json
···
7
7
},
8
8
"files": {
9
9
"ignoreUnknown": false,
10
10
-
"ignore": []
10
10
+
"ignore": ["package.json"]
11
11
},
12
12
"formatter": {
13
13
"enabled": true,
+526
-1
package-lock.json
···
11
11
"devDependencies": {
12
12
"@biomejs/biome": "1.9.4",
13
13
"@types/node": "^22.13.14",
14
14
-
"typescript": "^5.3.3"
14
14
+
"tsx": "^4.19.3",
15
15
+
"typescript": "^5.8.2"
15
16
}
16
17
},
17
18
"node_modules/@biomejs/biome": {
···
178
179
"node": ">=14.21.3"
179
180
}
180
181
},
182
182
+
"node_modules/@esbuild/aix-ppc64": {
183
183
+
"version": "0.25.2",
184
184
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz",
185
185
+
"integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
186
186
+
"cpu": [
187
187
+
"ppc64"
188
188
+
],
189
189
+
"dev": true,
190
190
+
"license": "MIT",
191
191
+
"optional": true,
192
192
+
"os": [
193
193
+
"aix"
194
194
+
],
195
195
+
"engines": {
196
196
+
"node": ">=18"
197
197
+
}
198
198
+
},
199
199
+
"node_modules/@esbuild/android-arm": {
200
200
+
"version": "0.25.2",
201
201
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz",
202
202
+
"integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
203
203
+
"cpu": [
204
204
+
"arm"
205
205
+
],
206
206
+
"dev": true,
207
207
+
"license": "MIT",
208
208
+
"optional": true,
209
209
+
"os": [
210
210
+
"android"
211
211
+
],
212
212
+
"engines": {
213
213
+
"node": ">=18"
214
214
+
}
215
215
+
},
216
216
+
"node_modules/@esbuild/android-arm64": {
217
217
+
"version": "0.25.2",
218
218
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz",
219
219
+
"integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
220
220
+
"cpu": [
221
221
+
"arm64"
222
222
+
],
223
223
+
"dev": true,
224
224
+
"license": "MIT",
225
225
+
"optional": true,
226
226
+
"os": [
227
227
+
"android"
228
228
+
],
229
229
+
"engines": {
230
230
+
"node": ">=18"
231
231
+
}
232
232
+
},
233
233
+
"node_modules/@esbuild/android-x64": {
234
234
+
"version": "0.25.2",
235
235
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz",
236
236
+
"integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
237
237
+
"cpu": [
238
238
+
"x64"
239
239
+
],
240
240
+
"dev": true,
241
241
+
"license": "MIT",
242
242
+
"optional": true,
243
243
+
"os": [
244
244
+
"android"
245
245
+
],
246
246
+
"engines": {
247
247
+
"node": ">=18"
248
248
+
}
249
249
+
},
250
250
+
"node_modules/@esbuild/darwin-arm64": {
251
251
+
"version": "0.25.2",
252
252
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz",
253
253
+
"integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
254
254
+
"cpu": [
255
255
+
"arm64"
256
256
+
],
257
257
+
"dev": true,
258
258
+
"license": "MIT",
259
259
+
"optional": true,
260
260
+
"os": [
261
261
+
"darwin"
262
262
+
],
263
263
+
"engines": {
264
264
+
"node": ">=18"
265
265
+
}
266
266
+
},
267
267
+
"node_modules/@esbuild/darwin-x64": {
268
268
+
"version": "0.25.2",
269
269
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz",
270
270
+
"integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
271
271
+
"cpu": [
272
272
+
"x64"
273
273
+
],
274
274
+
"dev": true,
275
275
+
"license": "MIT",
276
276
+
"optional": true,
277
277
+
"os": [
278
278
+
"darwin"
279
279
+
],
280
280
+
"engines": {
281
281
+
"node": ">=18"
282
282
+
}
283
283
+
},
284
284
+
"node_modules/@esbuild/freebsd-arm64": {
285
285
+
"version": "0.25.2",
286
286
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz",
287
287
+
"integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
288
288
+
"cpu": [
289
289
+
"arm64"
290
290
+
],
291
291
+
"dev": true,
292
292
+
"license": "MIT",
293
293
+
"optional": true,
294
294
+
"os": [
295
295
+
"freebsd"
296
296
+
],
297
297
+
"engines": {
298
298
+
"node": ">=18"
299
299
+
}
300
300
+
},
301
301
+
"node_modules/@esbuild/freebsd-x64": {
302
302
+
"version": "0.25.2",
303
303
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz",
304
304
+
"integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
305
305
+
"cpu": [
306
306
+
"x64"
307
307
+
],
308
308
+
"dev": true,
309
309
+
"license": "MIT",
310
310
+
"optional": true,
311
311
+
"os": [
312
312
+
"freebsd"
313
313
+
],
314
314
+
"engines": {
315
315
+
"node": ">=18"
316
316
+
}
317
317
+
},
318
318
+
"node_modules/@esbuild/linux-arm": {
319
319
+
"version": "0.25.2",
320
320
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz",
321
321
+
"integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
322
322
+
"cpu": [
323
323
+
"arm"
324
324
+
],
325
325
+
"dev": true,
326
326
+
"license": "MIT",
327
327
+
"optional": true,
328
328
+
"os": [
329
329
+
"linux"
330
330
+
],
331
331
+
"engines": {
332
332
+
"node": ">=18"
333
333
+
}
334
334
+
},
335
335
+
"node_modules/@esbuild/linux-arm64": {
336
336
+
"version": "0.25.2",
337
337
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz",
338
338
+
"integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
339
339
+
"cpu": [
340
340
+
"arm64"
341
341
+
],
342
342
+
"dev": true,
343
343
+
"license": "MIT",
344
344
+
"optional": true,
345
345
+
"os": [
346
346
+
"linux"
347
347
+
],
348
348
+
"engines": {
349
349
+
"node": ">=18"
350
350
+
}
351
351
+
},
352
352
+
"node_modules/@esbuild/linux-ia32": {
353
353
+
"version": "0.25.2",
354
354
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz",
355
355
+
"integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
356
356
+
"cpu": [
357
357
+
"ia32"
358
358
+
],
359
359
+
"dev": true,
360
360
+
"license": "MIT",
361
361
+
"optional": true,
362
362
+
"os": [
363
363
+
"linux"
364
364
+
],
365
365
+
"engines": {
366
366
+
"node": ">=18"
367
367
+
}
368
368
+
},
369
369
+
"node_modules/@esbuild/linux-loong64": {
370
370
+
"version": "0.25.2",
371
371
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz",
372
372
+
"integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
373
373
+
"cpu": [
374
374
+
"loong64"
375
375
+
],
376
376
+
"dev": true,
377
377
+
"license": "MIT",
378
378
+
"optional": true,
379
379
+
"os": [
380
380
+
"linux"
381
381
+
],
382
382
+
"engines": {
383
383
+
"node": ">=18"
384
384
+
}
385
385
+
},
386
386
+
"node_modules/@esbuild/linux-mips64el": {
387
387
+
"version": "0.25.2",
388
388
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz",
389
389
+
"integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
390
390
+
"cpu": [
391
391
+
"mips64el"
392
392
+
],
393
393
+
"dev": true,
394
394
+
"license": "MIT",
395
395
+
"optional": true,
396
396
+
"os": [
397
397
+
"linux"
398
398
+
],
399
399
+
"engines": {
400
400
+
"node": ">=18"
401
401
+
}
402
402
+
},
403
403
+
"node_modules/@esbuild/linux-ppc64": {
404
404
+
"version": "0.25.2",
405
405
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz",
406
406
+
"integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
407
407
+
"cpu": [
408
408
+
"ppc64"
409
409
+
],
410
410
+
"dev": true,
411
411
+
"license": "MIT",
412
412
+
"optional": true,
413
413
+
"os": [
414
414
+
"linux"
415
415
+
],
416
416
+
"engines": {
417
417
+
"node": ">=18"
418
418
+
}
419
419
+
},
420
420
+
"node_modules/@esbuild/linux-riscv64": {
421
421
+
"version": "0.25.2",
422
422
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz",
423
423
+
"integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
424
424
+
"cpu": [
425
425
+
"riscv64"
426
426
+
],
427
427
+
"dev": true,
428
428
+
"license": "MIT",
429
429
+
"optional": true,
430
430
+
"os": [
431
431
+
"linux"
432
432
+
],
433
433
+
"engines": {
434
434
+
"node": ">=18"
435
435
+
}
436
436
+
},
437
437
+
"node_modules/@esbuild/linux-s390x": {
438
438
+
"version": "0.25.2",
439
439
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz",
440
440
+
"integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
441
441
+
"cpu": [
442
442
+
"s390x"
443
443
+
],
444
444
+
"dev": true,
445
445
+
"license": "MIT",
446
446
+
"optional": true,
447
447
+
"os": [
448
448
+
"linux"
449
449
+
],
450
450
+
"engines": {
451
451
+
"node": ">=18"
452
452
+
}
453
453
+
},
454
454
+
"node_modules/@esbuild/linux-x64": {
455
455
+
"version": "0.25.2",
456
456
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz",
457
457
+
"integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
458
458
+
"cpu": [
459
459
+
"x64"
460
460
+
],
461
461
+
"dev": true,
462
462
+
"license": "MIT",
463
463
+
"optional": true,
464
464
+
"os": [
465
465
+
"linux"
466
466
+
],
467
467
+
"engines": {
468
468
+
"node": ">=18"
469
469
+
}
470
470
+
},
471
471
+
"node_modules/@esbuild/netbsd-arm64": {
472
472
+
"version": "0.25.2",
473
473
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz",
474
474
+
"integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
475
475
+
"cpu": [
476
476
+
"arm64"
477
477
+
],
478
478
+
"dev": true,
479
479
+
"license": "MIT",
480
480
+
"optional": true,
481
481
+
"os": [
482
482
+
"netbsd"
483
483
+
],
484
484
+
"engines": {
485
485
+
"node": ">=18"
486
486
+
}
487
487
+
},
488
488
+
"node_modules/@esbuild/netbsd-x64": {
489
489
+
"version": "0.25.2",
490
490
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz",
491
491
+
"integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
492
492
+
"cpu": [
493
493
+
"x64"
494
494
+
],
495
495
+
"dev": true,
496
496
+
"license": "MIT",
497
497
+
"optional": true,
498
498
+
"os": [
499
499
+
"netbsd"
500
500
+
],
501
501
+
"engines": {
502
502
+
"node": ">=18"
503
503
+
}
504
504
+
},
505
505
+
"node_modules/@esbuild/openbsd-arm64": {
506
506
+
"version": "0.25.2",
507
507
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz",
508
508
+
"integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
509
509
+
"cpu": [
510
510
+
"arm64"
511
511
+
],
512
512
+
"dev": true,
513
513
+
"license": "MIT",
514
514
+
"optional": true,
515
515
+
"os": [
516
516
+
"openbsd"
517
517
+
],
518
518
+
"engines": {
519
519
+
"node": ">=18"
520
520
+
}
521
521
+
},
522
522
+
"node_modules/@esbuild/openbsd-x64": {
523
523
+
"version": "0.25.2",
524
524
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz",
525
525
+
"integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
526
526
+
"cpu": [
527
527
+
"x64"
528
528
+
],
529
529
+
"dev": true,
530
530
+
"license": "MIT",
531
531
+
"optional": true,
532
532
+
"os": [
533
533
+
"openbsd"
534
534
+
],
535
535
+
"engines": {
536
536
+
"node": ">=18"
537
537
+
}
538
538
+
},
539
539
+
"node_modules/@esbuild/sunos-x64": {
540
540
+
"version": "0.25.2",
541
541
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz",
542
542
+
"integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
543
543
+
"cpu": [
544
544
+
"x64"
545
545
+
],
546
546
+
"dev": true,
547
547
+
"license": "MIT",
548
548
+
"optional": true,
549
549
+
"os": [
550
550
+
"sunos"
551
551
+
],
552
552
+
"engines": {
553
553
+
"node": ">=18"
554
554
+
}
555
555
+
},
556
556
+
"node_modules/@esbuild/win32-arm64": {
557
557
+
"version": "0.25.2",
558
558
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz",
559
559
+
"integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
560
560
+
"cpu": [
561
561
+
"arm64"
562
562
+
],
563
563
+
"dev": true,
564
564
+
"license": "MIT",
565
565
+
"optional": true,
566
566
+
"os": [
567
567
+
"win32"
568
568
+
],
569
569
+
"engines": {
570
570
+
"node": ">=18"
571
571
+
}
572
572
+
},
573
573
+
"node_modules/@esbuild/win32-ia32": {
574
574
+
"version": "0.25.2",
575
575
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz",
576
576
+
"integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
577
577
+
"cpu": [
578
578
+
"ia32"
579
579
+
],
580
580
+
"dev": true,
581
581
+
"license": "MIT",
582
582
+
"optional": true,
583
583
+
"os": [
584
584
+
"win32"
585
585
+
],
586
586
+
"engines": {
587
587
+
"node": ">=18"
588
588
+
}
589
589
+
},
590
590
+
"node_modules/@esbuild/win32-x64": {
591
591
+
"version": "0.25.2",
592
592
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz",
593
593
+
"integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
594
594
+
"cpu": [
595
595
+
"x64"
596
596
+
],
597
597
+
"dev": true,
598
598
+
"license": "MIT",
599
599
+
"optional": true,
600
600
+
"os": [
601
601
+
"win32"
602
602
+
],
603
603
+
"engines": {
604
604
+
"node": ">=18"
605
605
+
}
606
606
+
},
181
607
"node_modules/@types/node": {
182
608
"version": "22.13.14",
183
609
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
···
186
612
"license": "MIT",
187
613
"dependencies": {
188
614
"undici-types": "~6.20.0"
615
615
+
}
616
616
+
},
617
617
+
"node_modules/esbuild": {
618
618
+
"version": "0.25.2",
619
619
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz",
620
620
+
"integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
621
621
+
"dev": true,
622
622
+
"hasInstallScript": true,
623
623
+
"license": "MIT",
624
624
+
"bin": {
625
625
+
"esbuild": "bin/esbuild"
626
626
+
},
627
627
+
"engines": {
628
628
+
"node": ">=18"
629
629
+
},
630
630
+
"optionalDependencies": {
631
631
+
"@esbuild/aix-ppc64": "0.25.2",
632
632
+
"@esbuild/android-arm": "0.25.2",
633
633
+
"@esbuild/android-arm64": "0.25.2",
634
634
+
"@esbuild/android-x64": "0.25.2",
635
635
+
"@esbuild/darwin-arm64": "0.25.2",
636
636
+
"@esbuild/darwin-x64": "0.25.2",
637
637
+
"@esbuild/freebsd-arm64": "0.25.2",
638
638
+
"@esbuild/freebsd-x64": "0.25.2",
639
639
+
"@esbuild/linux-arm": "0.25.2",
640
640
+
"@esbuild/linux-arm64": "0.25.2",
641
641
+
"@esbuild/linux-ia32": "0.25.2",
642
642
+
"@esbuild/linux-loong64": "0.25.2",
643
643
+
"@esbuild/linux-mips64el": "0.25.2",
644
644
+
"@esbuild/linux-ppc64": "0.25.2",
645
645
+
"@esbuild/linux-riscv64": "0.25.2",
646
646
+
"@esbuild/linux-s390x": "0.25.2",
647
647
+
"@esbuild/linux-x64": "0.25.2",
648
648
+
"@esbuild/netbsd-arm64": "0.25.2",
649
649
+
"@esbuild/netbsd-x64": "0.25.2",
650
650
+
"@esbuild/openbsd-arm64": "0.25.2",
651
651
+
"@esbuild/openbsd-x64": "0.25.2",
652
652
+
"@esbuild/sunos-x64": "0.25.2",
653
653
+
"@esbuild/win32-arm64": "0.25.2",
654
654
+
"@esbuild/win32-ia32": "0.25.2",
655
655
+
"@esbuild/win32-x64": "0.25.2"
656
656
+
}
657
657
+
},
658
658
+
"node_modules/fsevents": {
659
659
+
"version": "2.3.3",
660
660
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
661
661
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
662
662
+
"dev": true,
663
663
+
"hasInstallScript": true,
664
664
+
"license": "MIT",
665
665
+
"optional": true,
666
666
+
"os": [
667
667
+
"darwin"
668
668
+
],
669
669
+
"engines": {
670
670
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
671
671
+
}
672
672
+
},
673
673
+
"node_modules/get-tsconfig": {
674
674
+
"version": "4.10.0",
675
675
+
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz",
676
676
+
"integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==",
677
677
+
"dev": true,
678
678
+
"license": "MIT",
679
679
+
"dependencies": {
680
680
+
"resolve-pkg-maps": "^1.0.0"
681
681
+
},
682
682
+
"funding": {
683
683
+
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
684
684
+
}
685
685
+
},
686
686
+
"node_modules/resolve-pkg-maps": {
687
687
+
"version": "1.0.0",
688
688
+
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
689
689
+
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
690
690
+
"dev": true,
691
691
+
"license": "MIT",
692
692
+
"funding": {
693
693
+
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
694
694
+
}
695
695
+
},
696
696
+
"node_modules/tsx": {
697
697
+
"version": "4.19.3",
698
698
+
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.3.tgz",
699
699
+
"integrity": "sha512-4H8vUNGNjQ4V2EOoGw005+c+dGuPSnhpPBPHBtsZdGZBk/iJb4kguGlPWaZTZ3q5nMtFOEsY0nRDlh9PJyd6SQ==",
700
700
+
"dev": true,
701
701
+
"license": "MIT",
702
702
+
"dependencies": {
703
703
+
"esbuild": "~0.25.0",
704
704
+
"get-tsconfig": "^4.7.5"
705
705
+
},
706
706
+
"bin": {
707
707
+
"tsx": "dist/cli.mjs"
708
708
+
},
709
709
+
"engines": {
710
710
+
"node": ">=18.0.0"
711
711
+
},
712
712
+
"optionalDependencies": {
713
713
+
"fsevents": "~2.3.3"
189
714
}
190
715
},
191
716
"node_modules/typescript": {
+18
-12
package.json
···
1
1
{
2
2
"name": "array-treeify",
3
3
"version": "0.1.2",
4
4
-
"description": "",
5
5
-
"keywords": [
6
6
-
"ascii",
7
7
-
"tree",
8
8
-
"treeify"
9
9
-
],
4
4
+
"description": "Simple text tree diagrams from arrays.",
10
5
"license": "MIT",
11
6
"author": "tbeseda",
12
7
"repository": {
···
17
12
"main": "dist/index.js",
18
13
"types": "dist/index.d.ts",
19
14
"files": [
20
20
-
"dist/index.js",
21
21
-
"dist/index.d.ts"
15
15
+
"dist/"
22
16
],
23
17
"scripts": {
24
18
"lint": "biome check --write .",
25
19
"build": "rm -rf dist && tsc",
26
26
-
"pretest": "npm run build",
27
27
-
"test": "node --test",
20
20
+
"test": "tsx --test",
28
21
"posttest": "npm run lint",
29
22
"prepublishOnly": "npm run build"
30
23
},
31
24
"devDependencies": {
32
25
"@biomejs/biome": "1.9.4",
33
26
"@types/node": "^22.13.14",
34
34
-
"typescript": "^5.3.3"
35
35
-
}
27
27
+
"tsx": "^4.19.3",
28
28
+
"typescript": "^5.8.2"
29
29
+
},
30
30
+
"keywords": [
31
31
+
"ascii",
32
32
+
"unicode",
33
33
+
"tree",
34
34
+
"treeify",
35
35
+
"diagram",
36
36
+
"cli",
37
37
+
"output",
38
38
+
"pretty",
39
39
+
"pretty print",
40
40
+
"pretty print tree"
41
41
+
]
36
42
}
+63
src/index.test.ts
···
131
131
console.log(`\n${treeify(input2)}`)
132
132
console.log(`\n${treeify(input3)}`)
133
133
})
134
134
+
135
135
+
test('plain option uses whitespace instead of Unicode characters', () => {
136
136
+
const input: TreeInput = ['root', ['child1', 'child2', ['grandchild']]]
137
137
+
const expected = `root
138
138
+
child1
139
139
+
child2
140
140
+
grandchild`
141
141
+
142
142
+
const result = treeify(input, { plain: true })
143
143
+
console.log('\nPlain whitespace tree:')
144
144
+
console.log(result)
145
145
+
assert.strictEqual(result, expected)
146
146
+
})
147
147
+
148
148
+
test('custom characters can be provided', () => {
149
149
+
const input: TreeInput = ['root', ['child1', 'child2']]
150
150
+
const customChars = {
151
151
+
branch: '├• ',
152
152
+
lastBranch: '└• ',
153
153
+
pipe: '│ ',
154
154
+
space: ' ',
155
155
+
}
156
156
+
const expected = `root
157
157
+
├• child1
158
158
+
└• child2`
159
159
+
160
160
+
const result = treeify(input, { chars: customChars })
161
161
+
console.log('\nCustom character tree:')
162
162
+
console.log(result)
163
163
+
assert.strictEqual(result, expected)
164
164
+
})
165
165
+
166
166
+
test('custom characters take precedence over plain option', () => {
167
167
+
const input: TreeInput = ['root', ['child1', 'child2']]
168
168
+
const customChars = {
169
169
+
branch: '├• ',
170
170
+
lastBranch: '└• ',
171
171
+
pipe: '│ ',
172
172
+
space: ' ',
173
173
+
}
174
174
+
const expected = `root
175
175
+
├• child1
176
176
+
└• child2`
177
177
+
178
178
+
const result = treeify(input, { plain: true, chars: customChars })
179
179
+
console.log('\nCustom characters with plain option:')
180
180
+
console.log(result)
181
181
+
assert.strictEqual(result, expected)
182
182
+
})
183
183
+
184
184
+
test('first element must be a string', () => {
185
185
+
assert.throws(() => treeify([null, ['child']] as unknown as TreeInput), {
186
186
+
message: 'First element must be a string',
187
187
+
})
188
188
+
assert.throws(() => treeify([1, ['child']] as unknown as TreeInput), {
189
189
+
message: 'First element must be a string',
190
190
+
})
191
191
+
})
192
192
+
193
193
+
test('empty or invalid input returns empty string', () => {
194
194
+
assert.strictEqual(treeify([] as unknown as TreeInput), '')
195
195
+
assert.strictEqual(treeify([undefined] as unknown as TreeInput), '')
196
196
+
})
134
197
})
+48
-16
src/index.ts
···
16
16
*/
17
17
type FlexibleTreeInput = readonly (string | unknown[])[]
18
18
19
19
-
const CHARS = {
20
20
-
BRANCH: '├─ ',
21
21
-
LAST_BRANCH: '└─ ',
22
22
-
PIPE: '│ ',
23
23
-
SPACE: ' ',
19
19
+
type TreeChars = {
20
20
+
branch: string
21
21
+
lastBranch: string
22
22
+
pipe: string
23
23
+
space: string
24
24
+
}
25
25
+
26
26
+
/**
27
27
+
* @description ASCII characters used to render the tree.
28
28
+
*/
29
29
+
const DEFAULT_CHARS: TreeChars = {
30
30
+
branch: '├─ ',
31
31
+
lastBranch: '└─ ',
32
32
+
pipe: '│ ',
33
33
+
space: ' ',
34
34
+
} as const
35
35
+
const SPACE = ' ' as const
36
36
+
const EMPTY_CHARS: TreeChars = {
37
37
+
branch: SPACE.repeat(DEFAULT_CHARS.branch.length),
38
38
+
lastBranch: SPACE.repeat(DEFAULT_CHARS.lastBranch.length),
39
39
+
pipe: SPACE.repeat(DEFAULT_CHARS.pipe.length),
40
40
+
space: SPACE.repeat(DEFAULT_CHARS.space.length),
24
41
} as const
25
42
26
43
/**
27
27
-
* @description Creates an ASCII tree representation from a nested array structure.
44
44
+
* @description Creates a text tree representation from a nested array structure using Unicode box-drawing characters.
28
45
*
29
46
* The expected input format is a hierarchical structure where:
30
47
* - The first element must be a string (the root node)
···
38
55
* - `['root', ['child1', ['grandchild1', 'grandchild2']]]` creates a root with nested children
39
56
* - `['root', ['childA', ['grandchildA'], 'childB']]` creates multiple branches
40
57
*
41
41
-
* The output uses ASCII characters to visualize the tree structure.
58
58
+
* The output uses Unicode box-drawing characters to visualize the tree structure.
42
59
*
43
60
* @param list {FlexibleTreeInput} - An array representing the tree structure. First element must be a string.
44
44
-
* @returns {string} A string containing the ASCII tree representation
61
61
+
* @param options {Object} - An object containing optional configuration:
62
62
+
* - `chars` {TreeChars} - Custom characters for the tree. Defaults to Unicode box-drawing characters.
63
63
+
* - `plain` {boolean} - Whether to use plain whitespace characters instead of Unicode box-drawing characters.
64
64
+
*
65
65
+
* @returns {string} A string containing the tree representation
45
66
*
46
67
* @example
47
68
* treeify(['root', ['child1', 'child2', ['grandchild']]])
···
50
71
* // └─ child2
51
72
* // └─ grandchild
52
73
*/
53
53
-
export function treeify(list: FlexibleTreeInput): string {
74
74
+
export function treeify(
75
75
+
list: FlexibleTreeInput,
76
76
+
options?: {
77
77
+
chars?: TreeChars
78
78
+
plain?: boolean
79
79
+
},
80
80
+
): string {
54
81
if (!Array.isArray(list) || list.length === 0) return ''
55
82
if (list[0] === undefined) return ''
56
83
if (typeof list[0] !== 'string')
57
84
throw new Error('First element must be a string')
58
85
86
86
+
let chars = DEFAULT_CHARS
87
87
+
if (options?.plain) chars = EMPTY_CHARS
88
88
+
if (options?.chars) chars = options.chars
89
89
+
59
90
const result: string[] = []
60
91
61
92
result.push(list[0]) // first string is the root
···
70
101
i++
71
102
} else if (Array.isArray(node)) {
72
103
// array is the children of the previous item
73
73
-
renderTreeNodes(node, '', result)
104
104
+
renderTreeNodes(node, '', result, chars)
74
105
i++
75
106
} else {
76
107
// idk. skip it.
···
88
119
nodes: TreeNode[],
89
120
indent: string,
90
121
result: string[],
122
122
+
chars: TreeChars,
91
123
): void {
92
124
if (!Array.isArray(nodes) || nodes.length === 0) return
93
125
···
106
138
107
139
const isLast = !hasNextStringNode(nodes, childrenIndex + 1)
108
140
109
109
-
const prefix = isLast ? CHARS.LAST_BRANCH : CHARS.BRANCH
141
141
+
const prefix = isLast ? chars.lastBranch : chars.branch
110
142
result.push(indent + prefix + stringNode)
111
143
112
144
// children with increased indent
113
113
-
const childIndent = indent + (isLast ? CHARS.SPACE : CHARS.PIPE)
114
114
-
renderTreeNodes(arrayNode, childIndent, result)
145
145
+
const childIndent = indent + (isLast ? chars.space : chars.pipe)
146
146
+
renderTreeNodes(arrayNode, childIndent, result, chars)
115
147
116
148
// skip both the parent node and its children array
117
149
i += 2
118
150
} else if (typeof node === 'string') {
119
151
// string is simple. add it.
120
152
const isLast = !hasNextStringNode(nodes, i + 1)
121
121
-
const prefix = isLast ? CHARS.LAST_BRANCH : CHARS.BRANCH
153
153
+
const prefix = isLast ? chars.lastBranch : chars.branch
122
154
result.push(indent + prefix + node)
123
155
i++
124
156
} else if (Array.isArray(node)) {
125
157
// (>_>)
126
158
const isLast = i === nodes.length - 1
127
127
-
const childIndent = indent + (isLast ? CHARS.SPACE : CHARS.PIPE)
128
128
-
renderTreeNodes(node, childIndent, result)
159
159
+
const childIndent = indent + (isLast ? chars.space : chars.pipe)
160
160
+
renderTreeNodes(node, childIndent, result, chars)
129
161
i++
130
162
} else {
131
163
// (0_o)
+65
src/readme-examples.test.ts
···
3
3
import { treeify } from './index.js'
4
4
5
5
describe('readme examples', () => {
6
6
+
test('org chart example', () => {
7
7
+
const orgChart = treeify([
8
8
+
'Lumon Industries',
9
9
+
[
10
10
+
'Board of Directors',
11
11
+
['Natalie (Representative)'],
12
12
+
'Departments',
13
13
+
[
14
14
+
'Macrodata Refinement (Cobel)',
15
15
+
['Milchick', 'Mark S.', ['Dylan G.', 'Irving B.', 'Helly R.']],
16
16
+
],
17
17
+
'Other Departments',
18
18
+
[
19
19
+
'Optics & Design',
20
20
+
'Wellness Center',
21
21
+
'Mammalians Nurturable',
22
22
+
'Choreography and Merriment',
23
23
+
],
24
24
+
],
25
25
+
])
26
26
+
const expected = `Lumon Industries
27
27
+
├─ Board of Directors
28
28
+
│ └─ Natalie (Representative)
29
29
+
├─ Departments
30
30
+
│ └─ Macrodata Refinement (Cobel)
31
31
+
│ ├─ Milchick
32
32
+
│ └─ Mark S.
33
33
+
│ ├─ Dylan G.
34
34
+
│ ├─ Irving B.
35
35
+
│ └─ Helly R.
36
36
+
└─ Other Departments
37
37
+
├─ Optics & Design
38
38
+
├─ Wellness Center
39
39
+
├─ Mammalians Nurturable
40
40
+
└─ Choreography and Merriment`
41
41
+
42
42
+
console.log('\nOrg chart example:')
43
43
+
console.log(orgChart)
44
44
+
assert.strictEqual(orgChart, expected)
45
45
+
})
46
46
+
6
47
test('basic example', () => {
7
48
const eagan = [
8
49
'Kier Eagan',
···
19
60
console.log('\nBasic example:')
20
61
console.log(result)
21
62
assert.strictEqual(result, expected)
63
63
+
64
64
+
const expectedPlain = `Kier Eagan
65
65
+
...
66
66
+
...
67
67
+
Jame Eagan
68
68
+
Helena Eagan
69
69
+
Ambrose Eagan`
70
70
+
const resultPlain = treeify(eagan, { plain: true })
71
71
+
console.log('\nBasic example (plain):')
72
72
+
console.log(resultPlain)
73
73
+
assert.strictEqual(resultPlain, expectedPlain)
74
74
+
75
75
+
const expectedCustomChars = `Kier Eagan
76
76
+
├• ...
77
77
+
│ ├• ...
78
78
+
│ └• Jame Eagan
79
79
+
│ └• Helena Eagan
80
80
+
└• Ambrose Eagan`
81
81
+
const resultCustomChars = treeify(eagan, {
82
82
+
chars: { branch: '├• ', lastBranch: '└• ', pipe: '│ ', space: ' ' },
83
83
+
})
84
84
+
console.log('\nBasic example (custom chars):')
85
85
+
console.log(resultCustomChars)
86
86
+
assert.strictEqual(resultCustomChars, expectedCustomChars)
22
87
})
23
88
24
89
test('nested example', () => {
+1
-1
tsconfig.json
···
11
11
"forceConsistentCasingInFileNames": true,
12
12
"rootDir": "src"
13
13
},
14
14
-
"include": ["src"],
14
14
+
"include": ["src/index.ts"],
15
15
"exclude": ["node_modules", "dist"]
16
16
}