make-folder-txt 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,9 +53,18 @@ That's it. A `my-project.txt` file will be created in the same directory.
53
53
  Ignore specific folders/files by name:
54
54
 
55
55
  ```bash
56
- make-folder-txt --ignore-folder logs
57
- make-folder-txt --ignore-file .env
58
- make-folder-txt --ignore-folder dist --ignore-folder coverage --ignore-file secrets.txt
56
+ make-folder-txt --ignore-folder examples extensions docs
57
+ make-folder-txt --ignore-folder examples extensions "docs and explaination"
58
+ make-folder-txt --ignore-folder examples extensions docs --ignore-file LICENSE
59
+ make-folder-txt --ignore-file .env .env.local secrets.txt
60
+ ```
61
+
62
+ Include only specific folders/files by name (everything else is ignored):
63
+
64
+ ```bash
65
+ make-folder-txt --only-folder src docs
66
+ make-folder-txt --only-file package.json README.md
67
+ make-folder-txt --only-folder src --only-file package.json
59
68
  ```
60
69
 
61
70
  ---
@@ -19,12 +19,28 @@ const BINARY_EXTS = new Set([
19
19
 
20
20
  // ── helpers ───────────────────────────────────────────────────────────────────
21
21
 
22
- function collectFiles(dir, rootDir, ignoreDirs, ignoreFiles, indent = "", lines = [], filePaths = []) {
22
+ function collectFiles(
23
+ dir,
24
+ rootDir,
25
+ ignoreDirs,
26
+ ignoreFiles,
27
+ onlyFolders,
28
+ onlyFiles,
29
+ options = {},
30
+ ) {
31
+ const {
32
+ indent = "",
33
+ lines = [],
34
+ filePaths = [],
35
+ inSelectedFolder = false,
36
+ hasOnlyFilters = false,
37
+ } = options;
38
+
23
39
  let entries;
24
40
  try {
25
41
  entries = fs.readdirSync(dir, { withFileTypes: true });
26
42
  } catch {
27
- return { lines, filePaths };
43
+ return { lines, filePaths, hasIncluded: false };
28
44
  }
29
45
 
30
46
  entries.sort((a, b) => {
@@ -39,20 +55,53 @@ function collectFiles(dir, rootDir, ignoreDirs, ignoreFiles, indent = "", lines
39
55
 
40
56
  if (entry.isDirectory()) {
41
57
  if (ignoreDirs.has(entry.name)) {
42
- lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
58
+ if (!hasOnlyFilters) {
59
+ lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
60
+ }
43
61
  return;
44
62
  }
45
- lines.push(`${indent}${connector}${entry.name}/`);
46
- collectFiles(path.join(dir, entry.name), rootDir, ignoreDirs, ignoreFiles, childIndent, lines, filePaths);
63
+
64
+ const childPath = path.join(dir, entry.name);
65
+ const childInSelectedFolder = inSelectedFolder || onlyFolders.has(entry.name);
66
+ const childLines = [];
67
+ const childFiles = [];
68
+ const child = collectFiles(
69
+ childPath,
70
+ rootDir,
71
+ ignoreDirs,
72
+ ignoreFiles,
73
+ onlyFolders,
74
+ onlyFiles,
75
+ {
76
+ indent: childIndent,
77
+ lines: childLines,
78
+ filePaths: childFiles,
79
+ inSelectedFolder: childInSelectedFolder,
80
+ hasOnlyFilters,
81
+ },
82
+ );
83
+
84
+ const explicitlySelectedFolder = hasOnlyFilters && onlyFolders.has(entry.name);
85
+ const shouldIncludeDir = !hasOnlyFilters || child.hasIncluded || explicitlySelectedFolder;
86
+
87
+ if (shouldIncludeDir) {
88
+ lines.push(`${indent}${connector}${entry.name}/`);
89
+ lines.push(...child.lines);
90
+ filePaths.push(...child.filePaths);
91
+ }
47
92
  } else {
48
93
  if (ignoreFiles.has(entry.name)) return;
94
+
95
+ const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder || onlyFiles.has(entry.name);
96
+ if (!shouldIncludeFile) return;
97
+
49
98
  lines.push(`${indent}${connector}${entry.name}`);
50
99
  const relPath = "/" + path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
51
100
  filePaths.push({ abs: path.join(dir, entry.name), rel: relPath });
52
101
  }
53
102
  });
54
103
 
55
- return { lines, filePaths };
104
+ return { lines, filePaths, hasIncluded: filePaths.length > 0 || lines.length > 0 };
56
105
  }
57
106
 
58
107
  function readContent(absPath) {
@@ -81,37 +130,109 @@ if (args.includes("-v") || args.includes("--version")) {
81
130
 
82
131
  const ignoreDirs = new Set(IGNORE_DIRS);
83
132
  const ignoreFiles = new Set(IGNORE_FILES);
133
+ const onlyFolders = new Set();
134
+ const onlyFiles = new Set();
84
135
  let outputArg = null;
85
136
 
86
137
  for (let i = 0; i < args.length; i += 1) {
87
138
  const arg = args[i];
88
139
 
89
- if (arg === "--ignore-folder" || arg.startsWith("--ignore-folder=")) {
90
- const value = arg.startsWith("--ignore-folder=")
91
- ? arg.slice("--ignore-folder=".length)
92
- : args[i + 1];
93
- if (!value || value.startsWith("-")) {
140
+ if (arg === "--ignore-folder") {
141
+ let consumed = 0;
142
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
143
+ ignoreDirs.add(args[i + 1]);
144
+ i += 1;
145
+ consumed += 1;
146
+ }
147
+ if (consumed === 0) {
148
+ console.error("Error: --ignore-folder requires at least one folder name.");
149
+ process.exit(1);
150
+ }
151
+ continue;
152
+ }
153
+
154
+ if (arg.startsWith("--ignore-folder=")) {
155
+ const value = arg.slice("--ignore-folder=".length);
156
+ if (!value) {
94
157
  console.error("Error: --ignore-folder requires a folder name.");
95
158
  process.exit(1);
96
159
  }
97
- if (!arg.includes("=")) i += 1;
98
160
  ignoreDirs.add(value);
99
161
  continue;
100
162
  }
101
163
 
102
- if (arg === "--ignore-file" || arg.startsWith("--ignore-file=")) {
103
- const value = arg.startsWith("--ignore-file=")
104
- ? arg.slice("--ignore-file=".length)
105
- : args[i + 1];
106
- if (!value || value.startsWith("-")) {
164
+ if (arg === "--ignore-file") {
165
+ let consumed = 0;
166
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
167
+ ignoreFiles.add(args[i + 1]);
168
+ i += 1;
169
+ consumed += 1;
170
+ }
171
+ if (consumed === 0) {
172
+ console.error("Error: --ignore-file requires at least one file name.");
173
+ process.exit(1);
174
+ }
175
+ continue;
176
+ }
177
+
178
+ if (arg.startsWith("--ignore-file=")) {
179
+ const value = arg.slice("--ignore-file=".length);
180
+ if (!value) {
107
181
  console.error("Error: --ignore-file requires a file name.");
108
182
  process.exit(1);
109
183
  }
110
- if (!arg.includes("=")) i += 1;
111
184
  ignoreFiles.add(value);
112
185
  continue;
113
186
  }
114
187
 
188
+ if (arg === "--only-folder") {
189
+ let consumed = 0;
190
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
191
+ onlyFolders.add(args[i + 1]);
192
+ i += 1;
193
+ consumed += 1;
194
+ }
195
+ if (consumed === 0) {
196
+ console.error("Error: --only-folder requires at least one folder name.");
197
+ process.exit(1);
198
+ }
199
+ continue;
200
+ }
201
+
202
+ if (arg.startsWith("--only-folder=")) {
203
+ const value = arg.slice("--only-folder=".length);
204
+ if (!value) {
205
+ console.error("Error: --only-folder requires a folder name.");
206
+ process.exit(1);
207
+ }
208
+ onlyFolders.add(value);
209
+ continue;
210
+ }
211
+
212
+ if (arg === "--only-file") {
213
+ let consumed = 0;
214
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
215
+ onlyFiles.add(args[i + 1]);
216
+ i += 1;
217
+ consumed += 1;
218
+ }
219
+ if (consumed === 0) {
220
+ console.error("Error: --only-file requires at least one file name.");
221
+ process.exit(1);
222
+ }
223
+ continue;
224
+ }
225
+
226
+ if (arg.startsWith("--only-file=")) {
227
+ const value = arg.slice("--only-file=".length);
228
+ if (!value) {
229
+ console.error("Error: --only-file requires a file name.");
230
+ process.exit(1);
231
+ }
232
+ onlyFiles.add(value);
233
+ continue;
234
+ }
235
+
115
236
  if (arg.startsWith("-")) {
116
237
  console.error(`Error: Unknown option "${arg}".`);
117
238
  process.exit(1);
@@ -136,7 +257,16 @@ const outputFile = outputArg
136
257
 
137
258
  console.log(`\n📂 Scanning: ${folderPath}`);
138
259
 
139
- const { lines: treeLines, filePaths } = collectFiles(folderPath, folderPath, ignoreDirs, ignoreFiles);
260
+ const hasOnlyFilters = onlyFolders.size > 0 || onlyFiles.size > 0;
261
+ const { lines: treeLines, filePaths } = collectFiles(
262
+ folderPath,
263
+ folderPath,
264
+ ignoreDirs,
265
+ ignoreFiles,
266
+ onlyFolders,
267
+ onlyFiles,
268
+ { hasOnlyFilters },
269
+ );
140
270
 
141
271
  // ── build output ──────────────────────────────────────────────────────────────
142
272
  const out = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-folder-txt",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
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": {