make-folder-txt 1.1.1 → 1.1.3

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.
@@ -34,6 +34,7 @@ function collectFiles(
34
34
  filePaths = [],
35
35
  inSelectedFolder = false,
36
36
  hasOnlyFilters = false,
37
+ rootName = "",
37
38
  } = options;
38
39
 
39
40
  let entries;
@@ -78,6 +79,7 @@ function collectFiles(
78
79
  filePaths: childFiles,
79
80
  inSelectedFolder: childInSelectedFolder,
80
81
  hasOnlyFilters,
82
+ rootName,
81
83
  },
82
84
  );
83
85
 
@@ -92,6 +94,9 @@ function collectFiles(
92
94
  } else {
93
95
  if (ignoreFiles.has(entry.name)) return;
94
96
 
97
+ // Ignore .txt files that match the folder name (e.g., foldername.txt)
98
+ if (entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
99
+
95
100
  const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder || onlyFiles.has(entry.name);
96
101
  if (!shouldIncludeFile) return;
97
102
 
@@ -247,7 +252,6 @@ for (let i = 0; i < args.length; i += 1) {
247
252
  process.exit(1);
248
253
  }
249
254
 
250
- const folderPath = process.cwd();
251
255
 
252
256
  const rootName = path.basename(folderPath);
253
257
 
@@ -265,7 +269,7 @@ const { lines: treeLines, filePaths } = collectFiles(
265
269
  ignoreFiles,
266
270
  onlyFolders,
267
271
  onlyFiles,
268
- { hasOnlyFilters },
272
+ { hasOnlyFilters, rootName },
269
273
  );
270
274
 
271
275
  // ── build output ──────────────────────────────────────────────────────────────
@@ -0,0 +1,604 @@
1
+ ================================================================================
2
+ START OF FOLDER: make-folder-txt
3
+ ================================================================================
4
+
5
+ ================================================================================
6
+ PROJECT STRUCTURE
7
+ ================================================================================
8
+ Root: C:\Programming\make-folder-txt
9
+
10
+ make-folder-txt/
11
+ ├── .git/ [skipped]
12
+ ├── bin/
13
+ │ └── make-folder-txt.js
14
+ ├── LICENSE
15
+ ├── package.json
16
+ └── README.md
17
+
18
+ Total files: 4
19
+
20
+ ================================================================================
21
+ FILE CONTENTS
22
+ ================================================================================
23
+
24
+ --------------------------------------------------------------------------------
25
+ FILE: /bin/make-folder-txt.js
26
+ --------------------------------------------------------------------------------
27
+ #!/usr/bin/env node
28
+
29
+ const fs = require("fs");
30
+ const path = require("path");
31
+ const { version } = require("../package.json");
32
+
33
+ // ── config ────────────────────────────────────────────────────────────────────
34
+ const IGNORE_DIRS = new Set(["node_modules", ".git", ".next", "dist", "build", ".cache"]);
35
+ const IGNORE_FILES = new Set([".DS_Store", "Thumbs.db", "desktop.ini"]);
36
+
37
+ const BINARY_EXTS = new Set([
38
+ ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".svg", ".webp",
39
+ ".pdf", ".zip", ".tar", ".gz", ".rar", ".7z",
40
+ ".exe", ".dll", ".so", ".dylib", ".bin",
41
+ ".mp3", ".mp4", ".wav", ".avi", ".mov",
42
+ ".woff", ".woff2", ".ttf", ".eot", ".otf",
43
+ ".lock",
44
+ ]);
45
+
46
+ // ── helpers ───────────────────────────────────────────────────────────────────
47
+
48
+ function collectFiles(
49
+ dir,
50
+ rootDir,
51
+ ignoreDirs,
52
+ ignoreFiles,
53
+ onlyFolders,
54
+ onlyFiles,
55
+ options = {},
56
+ ) {
57
+ const {
58
+ indent = "",
59
+ lines = [],
60
+ filePaths = [],
61
+ inSelectedFolder = false,
62
+ hasOnlyFilters = false,
63
+ } = options;
64
+
65
+ let entries;
66
+ try {
67
+ entries = fs.readdirSync(dir, { withFileTypes: true });
68
+ } catch {
69
+ return { lines, filePaths, hasIncluded: false };
70
+ }
71
+
72
+ entries.sort((a, b) => {
73
+ if (a.isDirectory() === b.isDirectory()) return a.name.localeCompare(b.name);
74
+ return a.isDirectory() ? -1 : 1;
75
+ });
76
+
77
+ entries.forEach((entry, idx) => {
78
+ const isLast = idx === entries.length - 1;
79
+ const connector = isLast ? "└── " : "├── ";
80
+ const childIndent = indent + (isLast ? " " : "│ ");
81
+
82
+ if (entry.isDirectory()) {
83
+ if (ignoreDirs.has(entry.name)) {
84
+ if (!hasOnlyFilters) {
85
+ lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
86
+ }
87
+ return;
88
+ }
89
+
90
+ const childPath = path.join(dir, entry.name);
91
+ const childInSelectedFolder = inSelectedFolder || onlyFolders.has(entry.name);
92
+ const childLines = [];
93
+ const childFiles = [];
94
+ const child = collectFiles(
95
+ childPath,
96
+ rootDir,
97
+ ignoreDirs,
98
+ ignoreFiles,
99
+ onlyFolders,
100
+ onlyFiles,
101
+ {
102
+ indent: childIndent,
103
+ lines: childLines,
104
+ filePaths: childFiles,
105
+ inSelectedFolder: childInSelectedFolder,
106
+ hasOnlyFilters,
107
+ },
108
+ );
109
+
110
+ const explicitlySelectedFolder = hasOnlyFilters && onlyFolders.has(entry.name);
111
+ const shouldIncludeDir = !hasOnlyFilters || child.hasIncluded || explicitlySelectedFolder;
112
+
113
+ if (shouldIncludeDir) {
114
+ lines.push(`${indent}${connector}${entry.name}/`);
115
+ lines.push(...child.lines);
116
+ filePaths.push(...child.filePaths);
117
+ }
118
+ } else {
119
+ if (ignoreFiles.has(entry.name)) return;
120
+
121
+ const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder || onlyFiles.has(entry.name);
122
+ if (!shouldIncludeFile) return;
123
+
124
+ lines.push(`${indent}${connector}${entry.name}`);
125
+ const relPath = "/" + path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
126
+ filePaths.push({ abs: path.join(dir, entry.name), rel: relPath });
127
+ }
128
+ });
129
+
130
+ return { lines, filePaths, hasIncluded: filePaths.length > 0 || lines.length > 0 };
131
+ }
132
+
133
+ function readContent(absPath) {
134
+ const ext = path.extname(absPath).toLowerCase();
135
+ if (BINARY_EXTS.has(ext)) return "[binary / skipped]";
136
+ try {
137
+ const stat = fs.statSync(absPath);
138
+ if (stat.size > 500 * 1024) {
139
+ return `[file too large: ${(stat.size / 1024).toFixed(1)} KB – skipped]`;
140
+ }
141
+ return fs.readFileSync(absPath, "utf8");
142
+ } catch (err) {
143
+ return `[could not read file: ${err.message}]`;
144
+ }
145
+ }
146
+
147
+ // ── main ──────────────────────────────────────────────────────────────────────
148
+
149
+ const args = process.argv.slice(2);
150
+
151
+ if (args.includes("-v") || args.includes("--version")) {
152
+ console.log(`v${version}`);
153
+ console.log("Built by Muhammad Saad Amin");
154
+ process.exit(0);
155
+ }
156
+
157
+ const ignoreDirs = new Set(IGNORE_DIRS);
158
+ const ignoreFiles = new Set(IGNORE_FILES);
159
+ const onlyFolders = new Set();
160
+ const onlyFiles = new Set();
161
+ let outputArg = null;
162
+
163
+ for (let i = 0; i < args.length; i += 1) {
164
+ const arg = args[i];
165
+
166
+ if (arg === "--ignore-folder") {
167
+ let consumed = 0;
168
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
169
+ ignoreDirs.add(args[i + 1]);
170
+ i += 1;
171
+ consumed += 1;
172
+ }
173
+ if (consumed === 0) {
174
+ console.error("Error: --ignore-folder requires at least one folder name.");
175
+ process.exit(1);
176
+ }
177
+ continue;
178
+ }
179
+
180
+ if (arg.startsWith("--ignore-folder=")) {
181
+ const value = arg.slice("--ignore-folder=".length);
182
+ if (!value) {
183
+ console.error("Error: --ignore-folder requires a folder name.");
184
+ process.exit(1);
185
+ }
186
+ ignoreDirs.add(value);
187
+ continue;
188
+ }
189
+
190
+ if (arg === "--ignore-file") {
191
+ let consumed = 0;
192
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
193
+ ignoreFiles.add(args[i + 1]);
194
+ i += 1;
195
+ consumed += 1;
196
+ }
197
+ if (consumed === 0) {
198
+ console.error("Error: --ignore-file requires at least one file name.");
199
+ process.exit(1);
200
+ }
201
+ continue;
202
+ }
203
+
204
+ if (arg.startsWith("--ignore-file=")) {
205
+ const value = arg.slice("--ignore-file=".length);
206
+ if (!value) {
207
+ console.error("Error: --ignore-file requires a file name.");
208
+ process.exit(1);
209
+ }
210
+ ignoreFiles.add(value);
211
+ continue;
212
+ }
213
+
214
+ if (arg === "--only-folder") {
215
+ let consumed = 0;
216
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
217
+ onlyFolders.add(args[i + 1]);
218
+ i += 1;
219
+ consumed += 1;
220
+ }
221
+ if (consumed === 0) {
222
+ console.error("Error: --only-folder requires at least one folder name.");
223
+ process.exit(1);
224
+ }
225
+ continue;
226
+ }
227
+
228
+ if (arg.startsWith("--only-folder=")) {
229
+ const value = arg.slice("--only-folder=".length);
230
+ if (!value) {
231
+ console.error("Error: --only-folder requires a folder name.");
232
+ process.exit(1);
233
+ }
234
+ onlyFolders.add(value);
235
+ continue;
236
+ }
237
+
238
+ if (arg === "--only-file") {
239
+ let consumed = 0;
240
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
241
+ onlyFiles.add(args[i + 1]);
242
+ i += 1;
243
+ consumed += 1;
244
+ }
245
+ if (consumed === 0) {
246
+ console.error("Error: --only-file requires at least one file name.");
247
+ process.exit(1);
248
+ }
249
+ continue;
250
+ }
251
+
252
+ if (arg.startsWith("--only-file=")) {
253
+ const value = arg.slice("--only-file=".length);
254
+ if (!value) {
255
+ console.error("Error: --only-file requires a file name.");
256
+ process.exit(1);
257
+ }
258
+ onlyFiles.add(value);
259
+ continue;
260
+ }
261
+
262
+ if (arg.startsWith("-")) {
263
+ console.error(`Error: Unknown option "${arg}".`);
264
+ process.exit(1);
265
+ }
266
+
267
+ if (!outputArg) {
268
+ outputArg = arg;
269
+ continue;
270
+ }
271
+
272
+ console.error(`Error: Unexpected argument "${arg}".`);
273
+ process.exit(1);
274
+ }
275
+
276
+ const folderPath = process.cwd();
277
+ console.log(`Folder path: ${folderPath}`);;
278
+ const rootName = path.basename(folderPath);
279
+
280
+ const outputFile = outputArg
281
+ ? path.resolve(outputArg)
282
+ : path.join(process.cwd(), `${rootName}.txt`);
283
+
284
+ console.log(`\n📂 Scanning: ${folderPath}`);
285
+
286
+ const hasOnlyFilters = onlyFolders.size > 0 || onlyFiles.size > 0;
287
+ const { lines: treeLines, filePaths } = collectFiles(
288
+ folderPath,
289
+ folderPath,
290
+ ignoreDirs,
291
+ ignoreFiles,
292
+ onlyFolders,
293
+ onlyFiles,
294
+ { hasOnlyFilters },
295
+ );
296
+
297
+ // ── build output ──────────────────────────────────────────────────────────────
298
+ const out = [];
299
+ const divider = "=".repeat(80);
300
+ const subDivider = "-".repeat(80);
301
+
302
+ out.push(divider);
303
+ out.push(`START OF FOLDER: ${rootName}`);
304
+ out.push(divider);
305
+ out.push("");
306
+
307
+ out.push(divider);
308
+ out.push("PROJECT STRUCTURE");
309
+ out.push(divider);
310
+ out.push(`Root: ${folderPath}\n`);
311
+ out.push(`${rootName}/`);
312
+ treeLines.forEach(l => out.push(l));
313
+ out.push("");
314
+ out.push(`Total files: ${filePaths.length}`);
315
+ out.push("");
316
+
317
+ out.push(divider);
318
+ out.push("FILE CONTENTS");
319
+ out.push(divider);
320
+
321
+ filePaths.forEach(({ abs, rel }) => {
322
+ out.push("");
323
+ out.push(subDivider);
324
+ out.push(`FILE: ${rel}`);
325
+ out.push(subDivider);
326
+ out.push(readContent(abs));
327
+ });
328
+
329
+ out.push("");
330
+ out.push(divider);
331
+ out.push(`END OF FOLDER: ${rootName}`);
332
+ out.push(divider);
333
+
334
+ fs.writeFileSync(outputFile, out.join("\n"), "utf8");
335
+
336
+ const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
337
+ console.log(`✅ Done!`);
338
+ console.log(`📄 Output : ${outputFile}`);
339
+ console.log(`📊 Size : ${sizeKB} KB`);
340
+ console.log(`🗂️ Files : ${filePaths.length}\n`);
341
+
342
+
343
+ --------------------------------------------------------------------------------
344
+ FILE: /LICENSE
345
+ --------------------------------------------------------------------------------
346
+ MIT License
347
+
348
+ Copyright (c) 2026 Muhammad Saad Amin @SENODROOM
349
+
350
+ Permission is hereby granted, free of charge, to any person obtaining a copy
351
+ of this software and associated documentation files (the "Software"), to deal
352
+ in the Software without restriction, including without limitation the rights
353
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
354
+ copies of the Software, and to permit persons to whom the Software is
355
+ furnished to do so, subject to the following conditions:
356
+
357
+ The above copyright notice and this permission notice shall be included in all
358
+ copies or substantial portions of the Software.
359
+
360
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
361
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
362
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
363
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
364
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
365
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
366
+ SOFTWARE.
367
+
368
+
369
+ --------------------------------------------------------------------------------
370
+ FILE: /package.json
371
+ --------------------------------------------------------------------------------
372
+ {
373
+ "name": "make-folder-txt",
374
+ "version": "1.1.2",
375
+ "description": "Generate a single .txt file containing the full folder structure and file contents of any project, ignoring node_modules and other junk.",
376
+ "main": "bin/make-folder-txt.js",
377
+ "bin": {
378
+ "make-folder-txt": "bin/make-folder-txt.js"
379
+ },
380
+ "scripts": {
381
+ "test": "echo \"No tests yet\" && exit 0"
382
+ },
383
+ "keywords": [
384
+ "folder",
385
+ "dump",
386
+ "project",
387
+ "structure",
388
+ "txt",
389
+ "cli",
390
+ "export"
391
+ ],
392
+ "author": "Muhammad Saad Amin",
393
+ "license": "MIT",
394
+ "engines": {
395
+ "node": ">=14.0.0"
396
+ }
397
+ }
398
+
399
+
400
+ --------------------------------------------------------------------------------
401
+ FILE: /README.md
402
+ --------------------------------------------------------------------------------
403
+ <div align="center">
404
+
405
+ # 📁 make-folder-txt
406
+
407
+ **Instantly dump your entire project into a single, readable `.txt` file.**
408
+
409
+ [![npm version](https://img.shields.io/npm/v/make-folder-txt?color=crimson&style=flat-square)](https://www.npmjs.com/package/make-folder-txt)
410
+ [![npm downloads](https://img.shields.io/npm/dm/make-folder-txt?color=orange&style=flat-square)](https://www.npmjs.com/package/make-folder-txt)
411
+ [![license](https://img.shields.io/npm/l/make-folder-txt?color=blue&style=flat-square)](./LICENSE)
412
+ [![node](https://img.shields.io/node/v/make-folder-txt?color=green&style=flat-square)](https://nodejs.org)
413
+
414
+ Perfect for sharing your codebase with **AI tools**, **teammates**, or **code reviewers** — without zipping files or giving repo access.
415
+
416
+ [Installation](#-installation) · [Usage](#-usage) · [Output Format](#-output-format) · [What Gets Skipped](#-what-gets-skipped) · [Contributing](#-contributing)
417
+
418
+ </div>
419
+
420
+ ---
421
+
422
+ ## ✨ Why make-folder-txt?
423
+
424
+ Ever needed to share your entire project with ChatGPT, Claude, or a teammate — but copy-pasting every file one by one is painful? **make-folder-txt** solves that in one command.
425
+
426
+ - ✅ Run it from any project directory — no arguments needed
427
+ - ✅ Generates a clean folder tree + every file's content
428
+ - ✅ Automatically skips `node_modules`, binaries, and junk files
429
+ - ✅ Zero dependencies — pure Node.js
430
+ - ✅ Works on Windows, macOS, and Linux
431
+
432
+ ---
433
+
434
+ ## 📦 Installation
435
+
436
+ Install globally once, use anywhere:
437
+
438
+ ```bash
439
+ npm install -g make-folder-txt
440
+ ```
441
+
442
+ ---
443
+
444
+ ## 🚀 Usage
445
+
446
+ Navigate into your project folder and run:
447
+
448
+ ```bash
449
+ cd my-project
450
+ make-folder-txt
451
+ ```
452
+
453
+ That's it. A `my-project.txt` file will be created in the same directory.
454
+
455
+ Ignore specific folders/files by name:
456
+
457
+ ```bash
458
+ make-folder-txt --ignore-folder examples extensions docs
459
+ make-folder-txt --ignore-folder examples extensions "docs and explaination"
460
+ make-folder-txt --ignore-folder examples extensions docs --ignore-file LICENSE
461
+ make-folder-txt --ignore-file .env .env.local secrets.txt
462
+ ```
463
+
464
+ Include only specific folders/files by name (everything else is ignored):
465
+
466
+ ```bash
467
+ make-folder-txt --only-folder src docs
468
+ make-folder-txt --only-file package.json README.md
469
+ make-folder-txt --only-folder src --only-file package.json
470
+ ```
471
+
472
+ ---
473
+
474
+ ## 🎯 Real World Examples
475
+
476
+ **Sharing with an AI tool (ChatGPT, Claude, etc.):**
477
+
478
+ ```bash
479
+ cd "C:\Web Development\my-app\backend"
480
+ make-folder-txt
481
+ # → backend.txt created, ready to paste into any AI chat
482
+ ```
483
+
484
+ **On macOS / Linux:**
485
+
486
+ ```bash
487
+ cd /home/user/projects/my-app
488
+ make-folder-txt
489
+ # → my-app.txt created
490
+ ```
491
+
492
+ ---
493
+
494
+ ## 📄 Output Format
495
+
496
+ The generated `.txt` file is structured in two clear sections:
497
+
498
+ ```
499
+ ================================================================================
500
+ START OF FOLDER: my-project
501
+ ================================================================================
502
+
503
+ ================================================================================
504
+ PROJECT STRUCTURE
505
+ ================================================================================
506
+ Root: C:\Web Development\my-project
507
+
508
+ my-project/
509
+ ├── src/
510
+ │ ├── controllers/
511
+ │ │ └── userController.js
512
+ │ ├── models/
513
+ │ │ └── User.js
514
+ │ └── index.js
515
+ ├── node_modules/ [skipped]
516
+ ├── package.json
517
+ └── README.md
518
+
519
+ Total files: 5
520
+
521
+ ================================================================================
522
+ FILE CONTENTS
523
+ ================================================================================
524
+
525
+ --------------------------------------------------------------------------------
526
+ FILE: /src/index.js
527
+ --------------------------------------------------------------------------------
528
+ const express = require('express');
529
+ ...
530
+
531
+ --------------------------------------------------------------------------------
532
+ FILE: /package.json
533
+ --------------------------------------------------------------------------------
534
+ {
535
+ "name": "my-project",
536
+ ...
537
+ }
538
+
539
+ ================================================================================
540
+ END OF FOLDER: my-project
541
+ ================================================================================
542
+ ```
543
+
544
+ ---
545
+
546
+ ## 🚫 What Gets Skipped
547
+
548
+ The tool is smart about what it ignores so your output stays clean and readable.
549
+
550
+ | Category | Details |
551
+ | --------------- | -------------------------------------------------------------- |
552
+ | 📁 Folders | `node_modules`, `.git`, `.next`, `dist`, `build`, `.cache` |
553
+ | 🖼️ Binary files | Images (`.png`, `.jpg`, `.gif`...), fonts, videos, executables |
554
+ | 📦 Archives | `.zip`, `.tar`, `.gz`, `.rar`, `.7z` |
555
+ | 🔤 Font files | `.woff`, `.woff2`, `.ttf`, `.eot`, `.otf` |
556
+ | 📋 Lock files | `package-lock.json`, `yarn.lock` |
557
+ | 📏 Large files | Any file over **500 KB** |
558
+ | 🗑️ System files | `.DS_Store`, `Thumbs.db`, `desktop.ini` |
559
+
560
+ Binary and skipped files are noted in the output as `[binary / skipped]` so you always know what was omitted.
561
+
562
+ ---
563
+
564
+ ## 🛠️ Requirements
565
+
566
+ - **Node.js** v14.0.0 or higher
567
+ - No other dependencies
568
+
569
+ ---
570
+
571
+ ## 🤝 Contributing
572
+
573
+ Contributions, issues, and feature requests are welcome!
574
+
575
+ 1. Fork the repository
576
+ 2. Create your feature branch: `git checkout -b feature/my-feature`
577
+ 3. Commit your changes: `git commit -m 'Add my feature'`
578
+ 4. Push to the branch: `git push origin feature/my-feature`
579
+ 5. Open a Pull Request
580
+
581
+ ---
582
+
583
+ ## 👤 Author
584
+
585
+ **Muhammad Saad Amin**
586
+
587
+ ---
588
+
589
+ ## 📝 License
590
+
591
+ This project is licensed under the **MIT License** — feel free to use it in personal and commercial projects.
592
+
593
+ ---
594
+
595
+ <div align="center">
596
+
597
+ If this tool saved you time, consider giving it a ⭐ on npm!
598
+
599
+ </div>
600
+
601
+
602
+ ================================================================================
603
+ END OF FOLDER: make-folder-txt
604
+ ================================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-folder-txt",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Generate a single .txt file containing the full folder structure and file contents of any project, ignoring node_modules and other junk.",
5
5
  "main": "bin/make-folder-txt.js",
6
6
  "bin": {