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 +12 -3
- package/bin/make-folder-txt.js +149 -19
- package/package.json +1 -1
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
|
|
57
|
-
make-folder-txt --ignore-
|
|
58
|
-
make-folder-txt --ignore-folder
|
|
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
|
---
|
package/bin/make-folder-txt.js
CHANGED
|
@@ -19,12 +19,28 @@ const BINARY_EXTS = new Set([
|
|
|
19
19
|
|
|
20
20
|
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
21
21
|
|
|
22
|
-
function collectFiles(
|
|
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
|
-
|
|
58
|
+
if (!hasOnlyFilters) {
|
|
59
|
+
lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
|
|
60
|
+
}
|
|
43
61
|
return;
|
|
44
62
|
}
|
|
45
|
-
|
|
46
|
-
|
|
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"
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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
|
|
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": {
|