@khatastroffik/treefolder 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Loïs Bégué
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/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # Khatastroffik "Treefolder" aka "tfold"
2
+
3
+ A **node.js CLI utility** generating a **tree representation** (treeview) of a **folder structure** including its contained files and subfolders.
4
+
5
+ ![GitHub package.json version](https://img.shields.io/github/package-json/v/khatastroffik/treefolder?style=flat&labelColor=darkblue&color=black) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/khatastroffik/treefolder?style=flat&labelColor=darkblue&color=black) ![GitHub License](https://img.shields.io/github/license/khatastroffik/treefolder?labelColor=darkblue&color=black&style=flat) ![GitHub package.json dev/peer/optional dependency version](https://img.shields.io/github/package-json/dependency-version/khatastroffik/treefolder/dev/typescript?style=flat&labelColor=darkgreen&color=black) ![GitHub package.json dev/peer/optional dependency version](https://img.shields.io/github/package-json/dependency-version/khatastroffik/treefolder/dev/eslint?style=flat&labelColor=darkgreen&color=black) ![GitHub package.json dev/peer/optional dependency version](https://img.shields.io/github/package-json/dependency-version/khatastroffik/treefolder/dev/jest?style=flat&labelColor=darkgreen&color=black)
6
+
7
+ ![tfold](https://raw.githubusercontent.com/khatastroffik/treefolder/41846124e0e66c899d1ca0e5acf826eb727e4f68/docs/tfold.jpg)
8
+
9
+ ## Installation
10
+
11
+ ### prerequisite
12
+
13
+ This CLI tool requires `node.js` or a compatible JavaScript runtime environment to execute successfully. `npm`, `pnpm` or `yarn` may be required as well, in case you'd like to install the tool manually and/or to modify the source code.
14
+
15
+ ### Install and use "treefolder" as a globally available CLI tool
16
+
17
+ This is the recommanded and easiest approach.
18
+
19
+ 1. Install using `pnpm` (see below), `yarn` or `npm`:
20
+
21
+ ```ini
22
+ pnpm add --global @khatastroffik/treefolder
23
+ ```
24
+
25
+ 1. start using the CLI tool from any directory:
26
+
27
+ ```ini
28
+ # show treeview of the current directory
29
+ tfold
30
+ # or show treeview of any directory
31
+ tfold <some-path-to-be-used-as-tree-root>
32
+ ```
33
+
34
+ Note: you may configure the tool using some **options** as needed. See the [Features](#features) list.
35
+
36
+ ## Features
37
+
38
+ 1. [Auto "root folder"](#auto-root-folder)
39
+ 1. [Auto "ignore paths"](#auto-ignore-paths)
40
+ 1. [Sort leaves "folders first"](#sort-leaves-folders-first)
41
+ 1. [Styled output](#styled-output)
42
+ 1. [Version information](#version-information)
43
+ 1. [Verbose output](#verbose-output)
44
+
45
+ ### Auto "root folder"
46
+
47
+ If not specified as a command line argument, the "root folder" of the tree is set to the current working directory aka `.`.
48
+
49
+ ### Auto "ignore paths"
50
+
51
+ This tool is intentionally ignoring i.e. filtering out a few folder or file items by default and won't proceed with such ignored items i.e. those won't be visible in the resulting treeview.
52
+
53
+ The following paths (within the root folder) are ignored by default: `node_modules` , `dist`, `build`, `.git`, `.husky\_`, `logs`, `.angular` and `coverage`.
54
+
55
+ Notes:
56
+
57
+ - _Glob-syntax_ is not supported at the moment. The ignored paths are matching real folder paths, starting within i.e. resolved into the root folder.
58
+ - This filtering cannot be disabled or modified at the moment. Feel free to edit the ignored paths/items directly in the source code of the tool, when suitable.
59
+
60
+ ### Sort leaves "folders first"
61
+
62
+ By default, the list i.e. the leaves of the treeview are sorted following the _"folders first" principle_: All the folders are listed first, then all the files. All items are alphabetically sorted. This apply at any depth within the tree structure.
63
+
64
+ This behavior can be _disabled_ using the command line argument `--unsorted`. In this case, all items are sorted the way the operating system does e.g. sorted by their names, regardless of their type (directory or file).
65
+
66
+ ### Styled output
67
+
68
+ The treeview i.e. tree representation can be generated and displayed using 4 different styles: **`none`** (default), **`black`**, **`wireframe`** or **`colored`**.
69
+ The style can be defined using the command line argument `--style`or `-s` like so: `--style <name of the style>`.
70
+
71
+ &rarr; Examples of styled output can be found in the additional documentation: [Styled output examples](docs/readme.md#styled-output-examples)
72
+
73
+ Note: some environments i.e. shells may not display the symbols (which are represented using specific unicode code points like `U+1F5BF` or `U+1F5C1`) properly. Try to change the _font_ used in the shell in order to display the correct unicode symbols.
74
+
75
+ ### Version information
76
+
77
+ Use the command line argument `--version` or `-v` to display the version information about the tool.
78
+
79
+ ### Verbose output
80
+
81
+ When the flag i.e. command line argument `--verbose` is defined/set, then a few more information will be displayed together with the tree representation.
82
+
83
+ ## Development
84
+
85
+ ### Manually install as a globally available CLI tool `tfold`
86
+
87
+ 1. Clone (e.g. using `git`) or download this repository locally in order to make this tool available in your environment.
88
+
89
+ ```ini
90
+ gh repo clone khatastroffik/treefolder
91
+ # or
92
+ git clone https://github.com/khatastroffik/treefolder.git
93
+ ```
94
+
95
+ 1. Navigate to the repo folder:
96
+
97
+ ```ini
98
+ cd treefolder
99
+ ```
100
+
101
+ 1. Install the (dev-) dependencies using your favorite package manager and transpile the TypeScript source code into a JavaScript module.
102
+
103
+ ```ini
104
+ # first install the development dependencies
105
+ pnpm install
106
+ # then transpile the source code to javascript
107
+ pnpm build
108
+ ```
109
+
110
+ 1. Use your favorite package manager to install the tool globally. For example:
111
+
112
+ ```ini
113
+ npm install -g
114
+ # or (YES, TWICE IN A ROW!)
115
+ pnpm link --global
116
+ pnpm link --global
117
+ ```
118
+
119
+ Note: _pnpm link_ lacks of refinements (!) as of v10.x, but this seems to work well, despite the warnings...
120
+
121
+ 1. Use the CLI tool alias `tfold`. E.g.:
122
+
123
+ ```ini
124
+ # in any directory/folder simply run
125
+ tfold
126
+ # you may use additional arguments too, in order to configure the output
127
+ tfold --verbose -s colored /c/DEV/a-folder-to-be-scanned
128
+ # or
129
+ tfold c:\\DEV\\another-folder --unsorted --style wireframe
130
+ ```
131
+
132
+ ### Running the source code
133
+
134
+ Within the local clone/copy of the treefolder repository, you may:
135
+
136
+ #### &rarr; Run the **typescript** code directly (development)
137
+
138
+ ```ini
139
+ pnpm tsx src\index.ts
140
+ # or
141
+ pnpm tsx src\index.ts [root-path] [options]
142
+ # or run and watch for code changes
143
+ pnpm dev
144
+ ```
145
+
146
+ or
147
+
148
+ #### &rarr; Build and run the **javascript** code (development)
149
+
150
+ ```ini
151
+ # at least once
152
+ pnpm build
153
+ # then
154
+ pnpm start
155
+ # or
156
+ node .
157
+ # or
158
+ node . ..\..\some-folder
159
+ # or
160
+ node dist\index.js ..\..\some-folder
161
+ # or
162
+ node dist\index.js --style=colored --unsorted --verbose c:\test\some-other-folder
163
+
164
+ ```
165
+
166
+ ---
167
+
168
+ ```text
169
+ ┌─────────────────────────────────────────────┐
170
+ | |
171
+ ╭━┳━╭━╭━╮╮
172
+ ┃┈┈┈┣▄╋▄┫
173
+ ┃┈┃┈╰━╰━━━━━━╮ "K11K"
174
+ ╰┳╯┈┈┈┈┈┈┈┈ ◢█◣ a very pragmatic dog
175
+ ┃┈┈┈┈┈┈┈┈┈┈████
176
+ ┃┈┈┈┈┈┈┈┈┈┈◥█◤
177
+ ┃┈┈┈┈╭━┳━━━━╯
178
+ ┣━━━━━━┫
179
+ | |
180
+ └──────────── made by khatastroffik ──────────┘
181
+ ```
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ #! /usr/bin/env node
2
+ "use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(e,o,n,t){void 0===t&&(t=n);var r=Object.getOwnPropertyDescriptor(o,n);r&&!("get"in r?!o.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return o[n]}}),Object.defineProperty(e,t,r)}:function(e,o,n,t){void 0===t&&(t=n),e[t]=o[n]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,o){Object.defineProperty(e,"default",{enumerable:!0,value:o})}:function(e,o){e.default=o}),__importStar=this&&this.__importStar||function(){var e=function(o){return e=Object.getOwnPropertyNames||function(e){var o=[];for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(o[o.length]=n);return o},e(o)};return function(o){if(o&&o.__esModule)return o;var n={};if(null!=o)for(var t=e(o),r=0;r<t.length;r++)"default"!==t[r]&&__createBinding(n,o,t[r]);return __setModuleDefault(n,o),n}}(),__importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(exports,"__esModule",{value:!0});const node_fs_1=__importDefault(require("node:fs")),node_path_1=__importDefault(require("node:path")),node_process_1=require("node:process"),node_util_1=__importStar(require("node:util")),package_json_1=require("../package.json"),readdirPromise=node_util_1.default.promisify(node_fs_1.default.readdir),space=" ";var Style;!function(e){e.none="none",e.wireframe="wireframe",e.black="black",e.colored="colored"}(Style||(Style={}));const symbolStyles={none:{closed:"",open:"",file:""},wireframe:{closed:"🗀",open:"🗁",file:"🗋"},black:{closed:"🖿",open:"🖿",file:"🗎"},colored:{closed:"📁",open:"📂",file:"📄"}},parseArgsOptionsConfig={style:{type:"string",default:"none",short:"s"},verbose:{type:"boolean",default:!1},unsorted:{type:"boolean",default:!1,short:"u"},version:{type:"boolean",default:!1,short:"v"}},config={style:Style.none,symbols:symbolStyles.none,root:".",ignores:["node_modules","dist","build",".git",".husky\\_","logs",".angular","coverage"],verbose:!1,unsorted:!1};let folderCount=0,fileCount=0,ignoredCount=0,cliArgs={};function compareFolderContentItems(e,o){return e.isDirectory()?o.isFile()?-1:e.name.localeCompare(o.name):o.isDirectory()?1:e.name.localeCompare(o.name)}function isIgnored(e,o){const n=config.ignores.includes(node_path_1.default.join(e,o));return n&&ignoredCount++,n}async function getFolderContent(e){const o=(await readdirPromise(e,{withFileTypes:!0})).filter(o=>!isIgnored(e,o.name));return config.unsorted?o:o.sort(compareFolderContentItems)}async function buildTree(e,o,n,t){const r=await getFolderContent(e),i=r.length<1?config.symbols.closed:config.symbols.open;let s=`${o+(n?"":t?`└─${i} `:`├─${i} `)}${n&&config.symbols.open?`${i} ${node_path_1.default.basename(e)}`:node_path_1.default.basename(e)}\n`;const l=n?"":t?" ":"│ ",a=r.length-1;for(let n=0;n<r.length;n++){const t=r[n];if(t.isDirectory()&&(s+=await buildTree(node_path_1.default.join(e,t.name),o+l,!1,n===a)),t.isFile()){fileCount++;s+=`${o+l+(n===a?`└─${config.symbols.file} `:`├─${config.symbols.file} `)+t.name}\n`}}return folderCount++,s}async function configure(){let e;try{e=(0,node_util_1.parseArgs)({options:parseArgsOptionsConfig,allowPositionals:!0,strict:!0})}catch(e){const o=e;console.error(`[${(0,node_util_1.styleText)("red",o.name)}]: ${o.message}`),(0,node_process_1.exit)(1)}const{values:o,positionals:n}=e;o.version&&(await version(),(0,node_process_1.exit)(0)),config.style=Style[o.style]??config.style,config.symbols=symbolStyles[config.style],config.root=node_path_1.default.resolve(n.length>0?n[0]:config.root),config.ignores=config.ignores.map(e=>node_path_1.default.join(config.root,e)),config.unsorted=Boolean(o.unsorted),config.verbose=Boolean(o.verbose),cliArgs={...o,root:n[0]??"n/a"}}function t(e){return(0,node_util_1.styleText)("number"==typeof e?"yellow":"blueBright",`${e}`)}async function version(){console.info(t(`\n Treefolder aka "tfold"\n Version ${package_json_1.version}\n \n https://npmjs.com/@khatastroffik/treefolder\n\n ┌─────────────────────────────────────────────┐\n | |\n ╭━┳━╭━╭━╮╮\n ┃┈┈┈┣▄╋▄┫\n ┃┈┃┈╰━╰━━━━━━╮ "K11K"\n ╰┳╯┈┈┈┈┈┈┈┈ ◢█◣ a very pragmatic dog\n ┃┈┈┈┈┈┈┈┈┈┈████\n ┃┈┈┈┈┈┈┈┈┈┈◥█◤\n ┃┈┈┈┈╭━┳━━━━╯\n ┣━━━━━━┫\n | |\n └──────────── made by khatastroffik ──────────┘\n `))}async function main(){await configure(),console.info(await buildTree(config.root,"",!0,!0)),config.verbose&&(console.info(t("\nCommand line arguments:")),console.info(cliArgs),console.info(t("\nTreefolder configuration:")),console.info(config,"\n"),console.info(t(`Scanned ${t(folderCount)} folders and ${t(fileCount)} files.`)),console.info(t(`Filtered out ${t(ignoredCount)} items (folders or files).`)))}main();
package/docs/readme.md ADDED
@@ -0,0 +1,116 @@
1
+ # Additional documentation
2
+
3
+ ## Styled output examples
4
+
5
+ ### "none" Style &rarr; `--style=none`
6
+
7
+ This is the **default** style and may be ommited
8
+
9
+ ```shell
10
+ treefolder
11
+ ├─ .husky
12
+ │ └─ pre-commit
13
+ ├─ .vscode
14
+ │ └─ settings.json
15
+ ├─ docs
16
+ │ └─ readme.md
17
+ ├─ src
18
+ │ ├─ testfolder
19
+ │ └─ index.ts
20
+ ├─ .gitignore
21
+ ├─ eslint.config.mjs
22
+ ├─ jest.config.js
23
+ ├─ package.json
24
+ ├─ pnpm-lock.yaml
25
+ ├─ pnpm-workspace.yaml
26
+ ├─ README.md
27
+ └─ tsconfig.json
28
+ ```
29
+
30
+ ### "colored" Style &rarr; `--style=colored`
31
+
32
+ ```shell
33
+ 📂 treefolder
34
+ ├─📂 .husky
35
+ │ └─📄 pre-commit
36
+ ├─📂 .vscode
37
+ │ └─📄 settings.json
38
+ ├─📂 docs
39
+ │ └─📄 readme.md
40
+ ├─📂 src
41
+ │ ├─📁 testfolder
42
+ │ └─📄 index.ts
43
+ ├─📄 .gitignore
44
+ ├─📄 eslint.config.mjs
45
+ ├─📄 jest.config.js
46
+ ├─📄 package.json
47
+ ├─📄 pnpm-lock.yaml
48
+ ├─📄 pnpm-workspace.yaml
49
+ ├─📄 README.md
50
+ └─📄 tsconfig.json
51
+ ```
52
+
53
+ ### "black" Style &rarr; `--style=black`
54
+
55
+ ```shell
56
+ 🖿 treefolder
57
+ ├─🖿 .husky
58
+ │ └─🗎 pre-commit
59
+ ├─🖿 .vscode
60
+ │ └─🗎 settings.json
61
+ ├─🖿 docs
62
+ │ └─🗎 readme.md
63
+ ├─🖿 src
64
+ │ ├─🖿 testfolder
65
+ │ └─🗎 index.ts
66
+ ├─🗎 .gitignore
67
+ ├─🗎 eslint.config.mjs
68
+ ├─🗎 jest.config.js
69
+ ├─🗎 package.json
70
+ ├─🗎 pnpm-lock.yaml
71
+ ├─🗎 pnpm-workspace.yaml
72
+ ├─🗎 README.md
73
+ └─🗎 tsconfig.json
74
+ ```
75
+
76
+ ### "wireframe" Style &rarr; `--style=wireframe`
77
+
78
+ ```shell
79
+ 🗁 treefolder
80
+ ├─🗁 .husky
81
+ │ └─🗋 pre-commit
82
+ ├─🗁 .vscode
83
+ │ └─🗋 settings.json
84
+ ├─🗁 docs
85
+ │ └─🗋 readme.md
86
+ ├─🗁 src
87
+ │ ├─🗀 testfolder
88
+ │ └─🗋 index.ts
89
+ ├─🗋 .gitignore
90
+ ├─🗋 eslint.config.mjs
91
+ ├─🗋 jest.config.js
92
+ ├─🗋 package.json
93
+ ├─🗋 pnpm-lock.yaml
94
+ ├─🗋 pnpm-workspace.yaml
95
+ ├─🗋 README.md
96
+ └─🗋 tsconfig.json
97
+ ```
98
+
99
+ ## Sources &amp; stories
100
+
101
+ ### From trees and leaves
102
+
103
+ - <https://willcarh.art/blog/how-to-print-file-trees-on-the-command-line>
104
+ - <https://stackoverflow.com/questions/41472161/fs-readdir-ignore-directories>
105
+ - <https://www.geeksforgeeks.org/node-js/node-js-fs-readdirsync-method/>
106
+ - <https://www.npmjs.com/package/recursive-readdir>
107
+
108
+ ### From Glob patterns and .gitignore
109
+
110
+ - <https://github.com/isaacs/node-glob>
111
+ - <https://github.com/micromatch/picomatch>
112
+ - <https://github.com/sindresorhus/globby>
113
+
114
+ ### Other topics
115
+
116
+ - https://github.com/absolute-version/commit-and-tag-version
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@khatastroffik/treefolder",
3
+ "version": "1.0.0",
4
+ "devEngines": {
5
+ "packageManager": {
6
+ "name": "pnpm",
7
+ "version": "10.28.2"
8
+ }
9
+ },
10
+ "description": "A node.js CLI utility generating a tree representation (treeview) of a folder structure including its contained files and subfolders.",
11
+ "author": "khatastroffik",
12
+ "license": "MIT",
13
+ "repository": {
14
+ "url": "https://github.com/khatastroffik/treefolder.git",
15
+ "type": "git"
16
+ },
17
+ "bugs": {
18
+ "url": "https://github.com/khatastroffik/treefolder/issues"
19
+ },
20
+ "keywords": [
21
+ "treefolder",
22
+ "tfold",
23
+ "treeview",
24
+ "tree",
25
+ "node.js",
26
+ "khatastroffik",
27
+ "directory",
28
+ "folder",
29
+ "structure",
30
+ "CLI",
31
+ "readdir"
32
+ ],
33
+ "main": "dist/index.js",
34
+ "bin": {
35
+ "tfold": "./dist/index.js",
36
+ "treefolder": "./dist/index.js"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "docs/readme.md"
41
+ ],
42
+ "publishConfig": {
43
+ "registry": "https://registry.npmjs.org/",
44
+ "access": "public"
45
+ },
46
+ "devDependencies": {
47
+ "@antfu/eslint-config": "^6.7.3",
48
+ "@eslint/js": "^9.39.2",
49
+ "@eslint/json": "^0.14.0",
50
+ "@eslint/markdown": "^7.5.1",
51
+ "@types/jest": "^30.0.0",
52
+ "@types/node": "^25.0.3",
53
+ "eslint": "^9.39.2",
54
+ "eslint-plugin-format": "^1.1.0",
55
+ "eslint-plugin-jest": "^29.12.1",
56
+ "globals": "^17.0.0",
57
+ "husky": "^9.1.7",
58
+ "jest": "^30.2.0",
59
+ "lint-staged": "^16.2.7",
60
+ "rimraf": "^6.1.2",
61
+ "terser": "^5.46.0",
62
+ "ts-jest": "^29.4.6",
63
+ "tsx": "^4.21.0",
64
+ "typescript": "^5.9.3",
65
+ "typescript-eslint": "^8.51.0"
66
+ },
67
+ "lint-staged": {
68
+ "*.ts": "pnpm lintix"
69
+ },
70
+ "scripts": {
71
+ "test": "jest --passWithNoTests",
72
+ "lint": "eslint .",
73
+ "lintix": "eslint . --fix",
74
+ "clean": "rimraf ./dist",
75
+ "dev": "tsx watch src/index.ts",
76
+ "typecheck": "tsc --noEmit",
77
+ "build": "pnpm clean && tsc",
78
+ "build2prod": "pnpm build && terser dist/index.js -o dist/index.js -c -m",
79
+ "start": "node .",
80
+ "pushtags": "git push --follow-tags origin main",
81
+ "try-commit": "git hook run pre-commit",
82
+ "tsx": "tsx",
83
+ "release": "pnpm dlx commit-and-tag-version",
84
+ "release:test": "pnpm dlx commit-and-tag-version --dry-run",
85
+ "pub": "pnpm pushtags && pnpm publish",
86
+ "pub:test": "pnpm publish --dry-run --force --no-git-checks"
87
+ }
88
+ }