make-folder-txt 1.4.4 → 2.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/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
Perfect for sharing your codebase with **AI tools**, **teammates**, or **code reviewers** — without zipping files or giving repo access.
|
|
13
13
|
|
|
14
|
-
[Installation](#-installation) · [Usage](#-usage) · [Help](#-get-help) · [Output Format](#-output-format) · [What Gets Skipped](#-what-gets-skipped) · [Contributing](#-contributing)
|
|
14
|
+
[Installation](#-installation) · [Usage](#-usage) · [Help](#-get-help) · [Copy to Clipboard](#-copy-to-clipboard) · [Force Include Everything](#-force-include-everything) · [Shell Autocompletion](#-shell-autocompletion) · [Output Format](#-output-format) · [What Gets Skipped](#-what-gets-skipped) · [Contributing](#-contributing)
|
|
15
15
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
@@ -23,6 +23,9 @@ Ever needed to share your entire project with ChatGPT, Claude, or a teammate —
|
|
|
23
23
|
|
|
24
24
|
- ✅ Run it from any project directory — no arguments needed
|
|
25
25
|
- ✅ Built-in help system with `--help` flag
|
|
26
|
+
- ✅ Shell autocompletion with `--install-completion`
|
|
27
|
+
- ✅ Copy to clipboard with `--copy` flag
|
|
28
|
+
- ✅ Force include everything with `--force` flag
|
|
26
29
|
- ✅ Generates a clean folder tree + every file's content
|
|
27
30
|
- ✅ `.txtignore` support (works like `.gitignore`)
|
|
28
31
|
- ✅ Automatically skips `node_modules`, binaries, and junk files
|
|
@@ -61,13 +64,62 @@ make-folder-txt --version # Show version info
|
|
|
61
64
|
make-folder-txt -v # Short version of version
|
|
62
65
|
```
|
|
63
66
|
|
|
67
|
+
### 📋 Copy to Clipboard
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
make-folder-txt --copy # Generate output and copy to clipboard
|
|
71
|
+
make-folder-txt --copy --ignore-folder node_modules # Copy filtered output
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The `--copy` flag automatically copies the generated output to your system clipboard, making it easy to paste directly into AI tools, emails, or documents. Works on Windows, macOS, and Linux (requires `xclip` or `xsel` on Linux).
|
|
75
|
+
|
|
76
|
+
### 🔥 Force Include Everything
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
make-folder-txt --force # Include everything (overrides all ignore patterns)
|
|
80
|
+
make-folder-txt --force --copy # Include everything and copy to clipboard
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `--force` flag overrides all ignore patterns and includes:
|
|
84
|
+
- `node_modules` and other ignored folders
|
|
85
|
+
- Binary files (images, executables, etc.)
|
|
86
|
+
- Large files (no 500 KB limit)
|
|
87
|
+
- Files in `.txtignore`
|
|
88
|
+
- System files and other normally skipped content
|
|
89
|
+
|
|
90
|
+
Use this when you need a complete, unfiltered dump of your entire project.
|
|
91
|
+
|
|
92
|
+
### ⚡ Shell Autocompletion
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
make-folder-txt --install-completion # Install bash/zsh autocompletion
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
After installation, you'll get intelligent tab completion:
|
|
99
|
+
- **Flag completion**: `make-folder-txt --<TAB>` shows all available flags
|
|
100
|
+
- **Folder completion**: `make-folder-txt --ignore-folder <TAB>` shows folders
|
|
101
|
+
- **File completion**: `make-folder-txt --ignore-file <TAB>` shows files
|
|
102
|
+
|
|
103
|
+
**Example usage:**
|
|
104
|
+
```bash
|
|
105
|
+
$ make-folder-txt --ignore-folder b<TAB>
|
|
106
|
+
# → completes to "bin/" if bin folder exists
|
|
107
|
+
|
|
108
|
+
$ make-folder-txt --ignore-file p<TAB>
|
|
109
|
+
# → completes to "package.json" if package.json exists
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The completion automatically detects your shell (bash/zsh) and installs the appropriate scripts. Restart your terminal after installation.
|
|
113
|
+
|
|
64
114
|
Ignore specific folders/files by name:
|
|
65
115
|
|
|
66
116
|
```bash
|
|
67
117
|
make-folder-txt --ignore-folder examples extensions docs
|
|
118
|
+
make-folder-txt -ifo examples extensions docs # shorthand
|
|
68
119
|
make-folder-txt --ignore-folder examples extensions "docs and explaination"
|
|
69
120
|
make-folder-txt --ignore-folder examples extensions docs --ignore-file LICENSE
|
|
70
121
|
make-folder-txt --ignore-file .env .env.local secrets.txt
|
|
122
|
+
make-folder-txt -ifi .env .env.local secrets.txt # shorthand
|
|
71
123
|
```
|
|
72
124
|
|
|
73
125
|
Use a `.txtignore` file (works like `.gitignore`):
|
|
@@ -93,7 +145,9 @@ Include only specific folders/files by name (everything else is ignored):
|
|
|
93
145
|
|
|
94
146
|
```bash
|
|
95
147
|
make-folder-txt --only-folder src docs
|
|
148
|
+
make-folder-txt -ofo src docs # shorthand
|
|
96
149
|
make-folder-txt --only-file package.json README.md
|
|
150
|
+
make-folder-txt -ofi package.json README.md # shorthand
|
|
97
151
|
make-folder-txt --only-folder src --only-file package.json
|
|
98
152
|
```
|
|
99
153
|
|
package/bin/make-folder-txt.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const { version } = require("../package.json");
|
|
6
|
+
const { execSync } = require("child_process");
|
|
6
7
|
|
|
7
8
|
// ── config ────────────────────────────────────────────────────────────────────
|
|
8
9
|
const IGNORE_DIRS = new Set(["node_modules", ".git", ".next", "dist", "build", ".cache"]);
|
|
@@ -37,6 +38,34 @@ function readTxtIgnore(rootDir) {
|
|
|
37
38
|
return ignorePatterns;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
function copyToClipboard(text) {
|
|
42
|
+
try {
|
|
43
|
+
if (process.platform === 'win32') {
|
|
44
|
+
// Windows
|
|
45
|
+
execSync(`echo ${JSON.stringify(text).replace(/"/g, '""')} | clip`, { stdio: 'ignore' });
|
|
46
|
+
} else if (process.platform === 'darwin') {
|
|
47
|
+
// macOS
|
|
48
|
+
execSync(`echo ${JSON.stringify(text)} | pbcopy`, { stdio: 'ignore' });
|
|
49
|
+
} else {
|
|
50
|
+
// Linux (requires xclip or xsel)
|
|
51
|
+
try {
|
|
52
|
+
execSync(`echo ${JSON.stringify(text)} | xclip -selection clipboard`, { stdio: 'ignore' });
|
|
53
|
+
} catch {
|
|
54
|
+
try {
|
|
55
|
+
execSync(`echo ${JSON.stringify(text)} | xsel --clipboard --input`, { stdio: 'ignore' });
|
|
56
|
+
} catch {
|
|
57
|
+
console.warn('⚠️ Could not copy to clipboard. Install xclip or xsel on Linux.');
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
console.warn('⚠️ Could not copy to clipboard:', err.message);
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
40
69
|
function collectFiles(
|
|
41
70
|
dir,
|
|
42
71
|
rootDir,
|
|
@@ -54,6 +83,7 @@ function collectFiles(
|
|
|
54
83
|
hasOnlyFilters = false,
|
|
55
84
|
rootName = "",
|
|
56
85
|
txtIgnore = new Set(),
|
|
86
|
+
force = false,
|
|
57
87
|
} = options;
|
|
58
88
|
|
|
59
89
|
let entries;
|
|
@@ -74,7 +104,7 @@ function collectFiles(
|
|
|
74
104
|
const childIndent = indent + (isLast ? " " : "│ ");
|
|
75
105
|
|
|
76
106
|
if (entry.isDirectory()) {
|
|
77
|
-
if (ignoreDirs.has(entry.name)) {
|
|
107
|
+
if (!force && ignoreDirs.has(entry.name)) {
|
|
78
108
|
if (!hasOnlyFilters) {
|
|
79
109
|
lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
|
|
80
110
|
}
|
|
@@ -84,8 +114,8 @@ function collectFiles(
|
|
|
84
114
|
// Get relative path for .txtignore pattern matching
|
|
85
115
|
const relPathForIgnore = path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
|
|
86
116
|
|
|
87
|
-
// Check against .txtignore patterns (both dirname and relative path)
|
|
88
|
-
if (txtIgnore.has(entry.name) || txtIgnore.has(`${entry.name}/`) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`${relPathForIgnore}/`) || txtIgnore.has(`/${relPathForIgnore}/`)) {
|
|
117
|
+
// Check against .txtignore patterns (both dirname and relative path) unless force is enabled
|
|
118
|
+
if (!force && (txtIgnore.has(entry.name) || txtIgnore.has(`${entry.name}/`) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`${relPathForIgnore}/`) || txtIgnore.has(`/${relPathForIgnore}/`))) {
|
|
89
119
|
if (!hasOnlyFilters) {
|
|
90
120
|
lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
|
|
91
121
|
}
|
|
@@ -111,6 +141,7 @@ function collectFiles(
|
|
|
111
141
|
hasOnlyFilters,
|
|
112
142
|
rootName,
|
|
113
143
|
txtIgnore,
|
|
144
|
+
force,
|
|
114
145
|
},
|
|
115
146
|
);
|
|
116
147
|
|
|
@@ -123,18 +154,18 @@ function collectFiles(
|
|
|
123
154
|
filePaths.push(...child.filePaths);
|
|
124
155
|
}
|
|
125
156
|
} else {
|
|
126
|
-
if (ignoreFiles.has(entry.name)) return;
|
|
157
|
+
if (!force && ignoreFiles.has(entry.name)) return;
|
|
127
158
|
|
|
128
159
|
// Get relative path for .txtignore pattern matching
|
|
129
160
|
const relPathForIgnore = path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
|
|
130
161
|
|
|
131
|
-
// Check against .txtignore patterns (both filename and relative path)
|
|
132
|
-
if (txtIgnore.has(entry.name) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`/${relPathForIgnore}`)) {
|
|
162
|
+
// Check against .txtignore patterns (both filename and relative path) unless force is enabled
|
|
163
|
+
if (!force && (txtIgnore.has(entry.name) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`/${relPathForIgnore}`))) {
|
|
133
164
|
return;
|
|
134
165
|
}
|
|
135
166
|
|
|
136
|
-
// Ignore .txt files that match the folder name (e.g., foldername.txt)
|
|
137
|
-
if (entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
|
|
167
|
+
// Ignore .txt files that match the folder name (e.g., foldername.txt) unless force is enabled
|
|
168
|
+
if (!force && entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
|
|
138
169
|
|
|
139
170
|
const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder || onlyFiles.has(entry.name);
|
|
140
171
|
if (!shouldIncludeFile) return;
|
|
@@ -148,12 +179,12 @@ function collectFiles(
|
|
|
148
179
|
return { lines, filePaths, hasIncluded: filePaths.length > 0 || lines.length > 0 };
|
|
149
180
|
}
|
|
150
181
|
|
|
151
|
-
function readContent(absPath) {
|
|
182
|
+
function readContent(absPath, force = false) {
|
|
152
183
|
const ext = path.extname(absPath).toLowerCase();
|
|
153
|
-
if (BINARY_EXTS.has(ext)) return "[binary / skipped]";
|
|
184
|
+
if (!force && BINARY_EXTS.has(ext)) return "[binary / skipped]";
|
|
154
185
|
try {
|
|
155
186
|
const stat = fs.statSync(absPath);
|
|
156
|
-
if (stat.size > 500 * 1024) {
|
|
187
|
+
if (!force && stat.size > 500 * 1024) {
|
|
157
188
|
return `[file too large: ${(stat.size / 1024).toFixed(1)} KB – skipped]`;
|
|
158
189
|
}
|
|
159
190
|
return fs.readFileSync(absPath, "utf8");
|
|
@@ -166,6 +197,68 @@ function readContent(absPath) {
|
|
|
166
197
|
|
|
167
198
|
const args = process.argv.slice(2);
|
|
168
199
|
|
|
200
|
+
if (args.includes("--install-completion")) {
|
|
201
|
+
const { execSync } = require('child_process');
|
|
202
|
+
const path = require('path');
|
|
203
|
+
const os = require('os');
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
const homeDir = os.homedir();
|
|
207
|
+
const shell = process.env.SHELL || '';
|
|
208
|
+
|
|
209
|
+
if (shell.includes('zsh')) {
|
|
210
|
+
// Install for zsh
|
|
211
|
+
const zshrc = path.join(homeDir, '.zshrc');
|
|
212
|
+
const completionDir = path.join(homeDir, '.zsh', 'completions');
|
|
213
|
+
|
|
214
|
+
// Create completions directory if it doesn't exist
|
|
215
|
+
try {
|
|
216
|
+
execSync(`mkdir -p "${completionDir}"`, { stdio: 'ignore' });
|
|
217
|
+
} catch (e) {}
|
|
218
|
+
|
|
219
|
+
// Copy completion file
|
|
220
|
+
const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.zsh');
|
|
221
|
+
execSync(`cp "${completionPath}" "${completionDir}/_make-folder-txt"`, { stdio: 'ignore' });
|
|
222
|
+
|
|
223
|
+
// Add to .zshrc if not already there
|
|
224
|
+
try {
|
|
225
|
+
const zshrcContent = fs.readFileSync(zshrc, 'utf8');
|
|
226
|
+
if (!zshrcContent.includes('fpath+=~/.zsh/completions')) {
|
|
227
|
+
fs.appendFileSync(zshrc, '\n# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
|
|
228
|
+
}
|
|
229
|
+
} catch (e) {
|
|
230
|
+
// .zshrc doesn't exist, create it
|
|
231
|
+
fs.writeFileSync(zshrc, '# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
console.log('✅ Zsh completion installed! Restart your terminal or run: source ~/.zshrc');
|
|
235
|
+
|
|
236
|
+
} else {
|
|
237
|
+
// Install for bash
|
|
238
|
+
const bashrc = path.join(homeDir, '.bashrc');
|
|
239
|
+
const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.bash');
|
|
240
|
+
|
|
241
|
+
// Add to .bashrc if not already there
|
|
242
|
+
try {
|
|
243
|
+
const bashrcContent = fs.readFileSync(bashrc, 'utf8');
|
|
244
|
+
if (!bashrcContent.includes('make-folder-txt-completion.bash')) {
|
|
245
|
+
fs.appendFileSync(bashrc, `\n# make-folder-txt completion\nsource "${completionPath}"\n`);
|
|
246
|
+
}
|
|
247
|
+
} catch (e) {
|
|
248
|
+
// .bashrc doesn't exist, create it
|
|
249
|
+
fs.writeFileSync(bashrc, `# make-folder-txt completion\nsource "${completionPath}"\n`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
console.log('✅ Bash completion installed! Restart your terminal or run: source ~/.bashrc');
|
|
253
|
+
}
|
|
254
|
+
} catch (err) {
|
|
255
|
+
console.error('❌ Failed to install completion:', err.message);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
|
|
169
262
|
if (args.includes("-v") || args.includes("--version")) {
|
|
170
263
|
console.log(`v${version}`);
|
|
171
264
|
console.log("Built by Muhammad Saad Amin");
|
|
@@ -181,19 +274,29 @@ Dump an entire project folder into a single readable .txt file.
|
|
|
181
274
|
make-folder-txt [options]
|
|
182
275
|
|
|
183
276
|
\x1b[33mOPTIONS\x1b[0m
|
|
184
|
-
--ignore-folder <names...> Ignore specific folders by name
|
|
185
|
-
--ignore-file <names...> Ignore specific files by name
|
|
186
|
-
--only-folder <names...> Include only specific folders
|
|
187
|
-
--only-file <names...> Include only specific files
|
|
188
|
-
--
|
|
189
|
-
--
|
|
277
|
+
--ignore-folder, -ifo <names...> Ignore specific folders by name
|
|
278
|
+
--ignore-file, -ifi <names...> Ignore specific files by name
|
|
279
|
+
--only-folder, -ofo <names...> Include only specific folders
|
|
280
|
+
--only-file, -ofi <names...> Include only specific files
|
|
281
|
+
--copy Copy output to clipboard
|
|
282
|
+
--force Include everything (overrides all ignore patterns)
|
|
283
|
+
--install-completion Install shell autocompletion (bash/zsh)
|
|
284
|
+
--help, -h Show this help message
|
|
285
|
+
--version, -v Show version information
|
|
190
286
|
|
|
191
287
|
\x1b[33mEXAMPLES\x1b[0m
|
|
192
288
|
make-folder-txt
|
|
289
|
+
make-folder-txt --copy
|
|
290
|
+
make-folder-txt --force
|
|
291
|
+
make-folder-txt --install-completion
|
|
193
292
|
make-folder-txt --ignore-folder node_modules dist
|
|
293
|
+
make-folder-txt -ifo node_modules dist
|
|
194
294
|
make-folder-txt --ignore-file .env .env.local
|
|
295
|
+
make-folder-txt -ifi .env .env.local
|
|
195
296
|
make-folder-txt --only-folder src docs
|
|
297
|
+
make-folder-txt -ofo src docs
|
|
196
298
|
make-folder-txt --only-file package.json README.md
|
|
299
|
+
make-folder-txt -ofi package.json README.md
|
|
197
300
|
|
|
198
301
|
\x1b[33m.TXTIGNORE FILE\x1b[0m
|
|
199
302
|
Create a .txtignore file in your project root to specify files/folders to ignore.
|
|
@@ -214,11 +317,23 @@ const ignoreFiles = new Set(IGNORE_FILES);
|
|
|
214
317
|
const onlyFolders = new Set();
|
|
215
318
|
const onlyFiles = new Set();
|
|
216
319
|
let outputArg = null;
|
|
320
|
+
let copyToClipboardFlag = false;
|
|
321
|
+
let forceFlag = false;
|
|
217
322
|
|
|
218
323
|
for (let i = 0; i < args.length; i += 1) {
|
|
219
324
|
const arg = args[i];
|
|
220
325
|
|
|
221
|
-
if (arg === "--
|
|
326
|
+
if (arg === "--copy") {
|
|
327
|
+
copyToClipboardFlag = true;
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (arg === "--force") {
|
|
332
|
+
forceFlag = true;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (arg === "--ignore-folder" || arg === "-ifo") {
|
|
222
337
|
let consumed = 0;
|
|
223
338
|
while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
|
224
339
|
ignoreDirs.add(args[i + 1]);
|
|
@@ -232,8 +347,10 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
232
347
|
continue;
|
|
233
348
|
}
|
|
234
349
|
|
|
235
|
-
if (arg.startsWith("--ignore-folder=")) {
|
|
236
|
-
const value = arg.
|
|
350
|
+
if (arg.startsWith("--ignore-folder=") || arg.startsWith("-ifo=")) {
|
|
351
|
+
const value = arg.startsWith("--ignore-folder=")
|
|
352
|
+
? arg.slice("--ignore-folder=".length)
|
|
353
|
+
: arg.slice("-ifo=".length);
|
|
237
354
|
if (!value) {
|
|
238
355
|
console.error("Error: --ignore-folder requires a folder name.");
|
|
239
356
|
process.exit(1);
|
|
@@ -242,7 +359,7 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
242
359
|
continue;
|
|
243
360
|
}
|
|
244
361
|
|
|
245
|
-
if (arg === "--ignore-file") {
|
|
362
|
+
if (arg === "--ignore-file" || arg === "-ifi") {
|
|
246
363
|
let consumed = 0;
|
|
247
364
|
while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
|
248
365
|
ignoreFiles.add(args[i + 1]);
|
|
@@ -256,8 +373,10 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
256
373
|
continue;
|
|
257
374
|
}
|
|
258
375
|
|
|
259
|
-
if (arg.startsWith("--ignore-file=")) {
|
|
260
|
-
const value = arg.
|
|
376
|
+
if (arg.startsWith("--ignore-file=") || arg.startsWith("-ifi=")) {
|
|
377
|
+
const value = arg.startsWith("--ignore-file=")
|
|
378
|
+
? arg.slice("--ignore-file=".length)
|
|
379
|
+
: arg.slice("-ifi=".length);
|
|
261
380
|
if (!value) {
|
|
262
381
|
console.error("Error: --ignore-file requires a file name.");
|
|
263
382
|
process.exit(1);
|
|
@@ -266,7 +385,7 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
266
385
|
continue;
|
|
267
386
|
}
|
|
268
387
|
|
|
269
|
-
if (arg === "--only-folder") {
|
|
388
|
+
if (arg === "--only-folder" || arg === "-ofo") {
|
|
270
389
|
let consumed = 0;
|
|
271
390
|
while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
|
272
391
|
onlyFolders.add(args[i + 1]);
|
|
@@ -280,8 +399,10 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
280
399
|
continue;
|
|
281
400
|
}
|
|
282
401
|
|
|
283
|
-
if (arg.startsWith("--only-folder=")) {
|
|
284
|
-
const value = arg.
|
|
402
|
+
if (arg.startsWith("--only-folder=") || arg.startsWith("-ofo=")) {
|
|
403
|
+
const value = arg.startsWith("--only-folder=")
|
|
404
|
+
? arg.slice("--only-folder=".length)
|
|
405
|
+
: arg.slice("-ofo=".length);
|
|
285
406
|
if (!value) {
|
|
286
407
|
console.error("Error: --only-folder requires a folder name.");
|
|
287
408
|
process.exit(1);
|
|
@@ -290,7 +411,7 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
290
411
|
continue;
|
|
291
412
|
}
|
|
292
413
|
|
|
293
|
-
if (arg === "--only-file") {
|
|
414
|
+
if (arg === "--only-file" || arg === "-ofi") {
|
|
294
415
|
let consumed = 0;
|
|
295
416
|
while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
|
296
417
|
onlyFiles.add(args[i + 1]);
|
|
@@ -304,8 +425,10 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
304
425
|
continue;
|
|
305
426
|
}
|
|
306
427
|
|
|
307
|
-
if (arg.startsWith("--only-file=")) {
|
|
308
|
-
const value = arg.
|
|
428
|
+
if (arg.startsWith("--only-file=") || arg.startsWith("-ofi=")) {
|
|
429
|
+
const value = arg.startsWith("--only-file=")
|
|
430
|
+
? arg.slice("--only-file=".length)
|
|
431
|
+
: arg.slice("-ofi=".length);
|
|
309
432
|
if (!value) {
|
|
310
433
|
console.error("Error: --only-file requires a file name.");
|
|
311
434
|
process.exit(1);
|
|
@@ -348,7 +471,7 @@ const { lines: treeLines, filePaths } = collectFiles(
|
|
|
348
471
|
ignoreFiles,
|
|
349
472
|
onlyFolders,
|
|
350
473
|
onlyFiles,
|
|
351
|
-
{ hasOnlyFilters, rootName, txtIgnore },
|
|
474
|
+
{ hasOnlyFilters, rootName, txtIgnore, force: forceFlag },
|
|
352
475
|
);
|
|
353
476
|
|
|
354
477
|
// ── build output ──────────────────────────────────────────────────────────────
|
|
@@ -380,7 +503,7 @@ filePaths.forEach(({ abs, rel }) => {
|
|
|
380
503
|
out.push(subDivider);
|
|
381
504
|
out.push(`FILE: ${rel}`);
|
|
382
505
|
out.push(subDivider);
|
|
383
|
-
out.push(readContent(abs));
|
|
506
|
+
out.push(readContent(abs, forceFlag));
|
|
384
507
|
});
|
|
385
508
|
|
|
386
509
|
out.push("");
|
|
@@ -394,4 +517,14 @@ const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
|
|
|
394
517
|
console.log(`✅ Done!`);
|
|
395
518
|
console.log(`📄 Output : ${outputFile}`);
|
|
396
519
|
console.log(`📊 Size : ${sizeKB} KB`);
|
|
397
|
-
console.log(`🗂️ Files : ${filePaths.length}
|
|
520
|
+
console.log(`🗂️ Files : ${filePaths.length}`);
|
|
521
|
+
|
|
522
|
+
if (copyToClipboardFlag) {
|
|
523
|
+
const content = fs.readFileSync(outputFile, 'utf8');
|
|
524
|
+
const success = copyToClipboard(content);
|
|
525
|
+
if (success) {
|
|
526
|
+
console.log(`📋 Copied to clipboard!`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
console.log('');
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# make-folder-txt bash completion
|
|
3
|
+
|
|
4
|
+
_make_folder_txt_completion() {
|
|
5
|
+
local cur prev opts
|
|
6
|
+
COMPREPLY=()
|
|
7
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
8
|
+
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
9
|
+
|
|
10
|
+
opts="--ignore-folder -ifo --ignore-file -ifi --only-folder -ofo --only-file -ofi --copy --force --help --version -h -v"
|
|
11
|
+
|
|
12
|
+
case "${prev}" in
|
|
13
|
+
--ignore-folder|-ifo)
|
|
14
|
+
# Complete with folder names in current directory
|
|
15
|
+
local folders=$(ls -d */ 2>/dev/null | sed 's|/||g')
|
|
16
|
+
COMPREPLY=( $(compgen -W "${folders}" -- ${cur}) )
|
|
17
|
+
return 0
|
|
18
|
+
;;
|
|
19
|
+
--ignore-file|-ifi|--only-file|-ofi)
|
|
20
|
+
# Complete with file names in current directory
|
|
21
|
+
local files=$(ls -p 2>/dev/null | grep -v /)
|
|
22
|
+
COMPREPLY=( $(compgen -W "${files}" -- ${cur}) )
|
|
23
|
+
return 0
|
|
24
|
+
;;
|
|
25
|
+
--only-folder|-ofo)
|
|
26
|
+
# Complete with folder names in current directory
|
|
27
|
+
local folders=$(ls -d */ 2>/dev/null | sed 's|/||g')
|
|
28
|
+
COMPREPLY=( $(compgen -W "${folders}" -- ${cur}) )
|
|
29
|
+
return 0
|
|
30
|
+
;;
|
|
31
|
+
*)
|
|
32
|
+
;;
|
|
33
|
+
esac
|
|
34
|
+
|
|
35
|
+
if [[ ${cur} == -* ]]; then
|
|
36
|
+
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
|
37
|
+
return 0
|
|
38
|
+
fi
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
complete -F _make_folder_txt_completion make-folder-txt
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#compdef make-folder-txt
|
|
2
|
+
# make-folder-txt zsh completion
|
|
3
|
+
|
|
4
|
+
_make_folder_txt() {
|
|
5
|
+
local -a arguments
|
|
6
|
+
arguments=(
|
|
7
|
+
'--ignore-folder[Ignore specific folders by name]:folder:_directories'
|
|
8
|
+
'-ifo[Ignore specific folders by name]:folder:_directories'
|
|
9
|
+
'--ignore-file[Ignore specific files by name]:file:_files'
|
|
10
|
+
'-ifi[Ignore specific files by name]:file:_files'
|
|
11
|
+
'--only-folder[Include only specific folders]:folder:_directories'
|
|
12
|
+
'-ofo[Include only specific folders]:folder:_directories'
|
|
13
|
+
'--only-file[Include only specific files]:file:_files'
|
|
14
|
+
'-ofi[Include only specific files]:file:_files'
|
|
15
|
+
'--copy[Copy output to clipboard]'
|
|
16
|
+
'--force[Include everything (overrides all ignore patterns)]'
|
|
17
|
+
'--help[Show help message]'
|
|
18
|
+
'--version[Show version information]'
|
|
19
|
+
'-h[Show help message]'
|
|
20
|
+
'-v[Show version information]'
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
_arguments -s -S $arguments && return 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
_make_folder_txt "$@"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "make-folder-txt",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.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": {
|