flatten-tool 1.6.1 → 1.6.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.
- package/README.md +13 -0
- package/index.ts +55 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
A CLI utility to flatten directory structures, with perfect GitHub Flavored Markdown compatibility.
|
|
6
6
|
|
|
7
|
+
[](https://youtu.be/LCbSoK0Mkjk)
|
|
8
|
+
*Watch the YouTube video for an example of why you might need to flatten project files into a single document for AI discussions and code reviews.*
|
|
9
|
+
|
|
7
10
|
[](https://asciinema.org/a/ThswNC1vrdlK0wdD)
|
|
8
11
|
|
|
9
12
|
## Installation
|
|
@@ -140,6 +143,16 @@ This project uses Bun for runtime, TypeScript for type safety, and follows the g
|
|
|
140
143
|
|
|
141
144
|
## Changelog
|
|
142
145
|
|
|
146
|
+
### v1.6.3
|
|
147
|
+
- Enhanced safety: Automatically exclude common binary file extensions (PNG, JPEG, PDF, archives, executables, etc.) when merging to Markdown to prevent corruption.
|
|
148
|
+
- Added `--ignore` CLI option: Allow additional glob patterns to ignore (e.g., `*.log`, `temp/**`).
|
|
149
|
+
- Minor clean-ups: Improved variable naming and code consistency.
|
|
150
|
+
- Added YouTube video link in README demonstrating use case for AI discussions.
|
|
151
|
+
|
|
152
|
+
### v1.6.2
|
|
153
|
+
- Updated AGENTS.md with revised coding guidelines.
|
|
154
|
+
- Added '..' links in subdirectory file trees for navigation to parent directories.
|
|
155
|
+
|
|
143
156
|
### v1.6.1
|
|
144
157
|
- Added instructions for running flatten-tool directly with npx and bunx.
|
|
145
158
|
|
package/index.ts
CHANGED
|
@@ -67,20 +67,28 @@ export async function flattenDirectory(
|
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
// Patterns explicitly ignored (in addition to .gitignore when enabled)
|
|
71
|
+
const extraIgnores = ignorePatterns.map(pattern => `!${pattern}`);
|
|
71
72
|
|
|
72
|
-
const
|
|
73
|
+
const defaultIgnores = ['.git'];
|
|
73
74
|
if (!flattenToDirectory) {
|
|
74
|
-
|
|
75
|
+
// Common binary/media extensions that would corrupt UTF-8 text output
|
|
76
|
+
const binaryExts = [
|
|
77
|
+
'gif', 'png', 'jpg', 'jpeg', 'webp', 'svg', 'bmp', 'ico',
|
|
78
|
+
'pdf', 'zip', 'tar', 'gz', 'xz', '7z',
|
|
79
|
+
'mp3', 'mp4', 'webm', 'ogg', 'wav',
|
|
80
|
+
'exe', 'dll', 'so', 'dylib', 'bin'
|
|
81
|
+
];
|
|
82
|
+
defaultIgnores.push(`**/*.{${binaryExts.join(',')}}`);
|
|
75
83
|
}
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
const files = await globby(['**', ...extraIgnores], {
|
|
78
86
|
cwd: absSource,
|
|
79
87
|
gitignore: respectGitignore,
|
|
80
88
|
absolute: true,
|
|
81
89
|
dot: true,
|
|
82
90
|
onlyFiles: true,
|
|
83
|
-
ignore:
|
|
91
|
+
ignore: defaultIgnores,
|
|
84
92
|
});
|
|
85
93
|
|
|
86
94
|
if (!flattenToDirectory) {
|
|
@@ -147,6 +155,8 @@ export async function flattenDirectory(
|
|
|
147
155
|
}
|
|
148
156
|
}
|
|
149
157
|
|
|
158
|
+
sections.push({ path: '', headerText: 'Project File Tree' });
|
|
159
|
+
|
|
150
160
|
collectSections(treeObj, '');
|
|
151
161
|
|
|
152
162
|
const anchorMap = new Map<string, string>();
|
|
@@ -161,10 +171,21 @@ export async function flattenDirectory(
|
|
|
161
171
|
node: any,
|
|
162
172
|
depth: number = 0,
|
|
163
173
|
prefix: string = '',
|
|
164
|
-
anchorMap: Map<string, string
|
|
174
|
+
anchorMap: Map<string, string>,
|
|
175
|
+
parentPath: string | null // null for global root (no ..)
|
|
165
176
|
): string {
|
|
166
177
|
let result = '';
|
|
167
178
|
const indent = ' '.repeat(depth);
|
|
179
|
+
|
|
180
|
+
// Add .. if we have a parent
|
|
181
|
+
if (parentPath !== null) {
|
|
182
|
+
const parentAnchor = anchorMap.get(parentPath) ?? '';
|
|
183
|
+
result += `${indent}- [..](#${parentAnchor})\n`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Determine indentation for direct children
|
|
187
|
+
const entryIndent = ' '.repeat(depth);
|
|
188
|
+
|
|
168
189
|
const entries: [string, any][] = Object.entries(node);
|
|
169
190
|
|
|
170
191
|
entries.sort(([a], [b]) => {
|
|
@@ -180,17 +201,27 @@ export async function flattenDirectory(
|
|
|
180
201
|
const display = isDir ? name + '/' : name;
|
|
181
202
|
const pathHere = prefix ? `${prefix}/${name}` : name;
|
|
182
203
|
const anchor = anchorMap.get(pathHere) ?? '';
|
|
183
|
-
|
|
204
|
+
|
|
205
|
+
result += `${entryIndent}- [${display}](#${anchor})\n`;
|
|
206
|
+
|
|
184
207
|
if (isDir) {
|
|
185
|
-
|
|
208
|
+
// Recurse with increased depth; child's parent is current prefix
|
|
209
|
+
result += renderMarkdownTree(
|
|
210
|
+
value,
|
|
211
|
+
depth + 1,
|
|
212
|
+
pathHere,
|
|
213
|
+
anchorMap,
|
|
214
|
+
prefix
|
|
215
|
+
);
|
|
186
216
|
}
|
|
187
217
|
}
|
|
218
|
+
|
|
188
219
|
return result;
|
|
189
220
|
}
|
|
190
221
|
|
|
191
222
|
// Render global tree with correct anchors
|
|
192
|
-
let treeMarkdown = "# Project File Tree\n\n
|
|
193
|
-
treeMarkdown += renderMarkdownTree(treeObj,
|
|
223
|
+
let treeMarkdown = "# Project File Tree\n\n";
|
|
224
|
+
treeMarkdown += renderMarkdownTree(treeObj, 0, '', anchorMap, null);
|
|
194
225
|
treeMarkdown += "\n\n";
|
|
195
226
|
|
|
196
227
|
const writeStream = createWriteStream(absTarget);
|
|
@@ -230,8 +261,12 @@ export async function flattenDirectory(
|
|
|
230
261
|
if (currentPath) {
|
|
231
262
|
writeStream.write(`# ${currentPath}\n\n`);
|
|
232
263
|
writeStream.write(`File Tree\n\n`);
|
|
233
|
-
|
|
234
|
-
|
|
264
|
+
|
|
265
|
+
const parentPath = currentPath.includes('/')
|
|
266
|
+
? currentPath.slice(0, currentPath.lastIndexOf('/'))
|
|
267
|
+
: '';
|
|
268
|
+
|
|
269
|
+
writeStream.write(renderMarkdownTree(node, 0, currentPath, anchorMap, parentPath));
|
|
235
270
|
writeStream.write('\n');
|
|
236
271
|
}
|
|
237
272
|
|
|
@@ -334,13 +369,20 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
334
369
|
type: 'boolean',
|
|
335
370
|
default: false,
|
|
336
371
|
})
|
|
372
|
+
.option('ignore', {
|
|
373
|
+
alias: 'i',
|
|
374
|
+
type: 'string',
|
|
375
|
+
array: true,
|
|
376
|
+
describe: 'Additional glob patterns to ignore (e.g. "*.log" "temp/**")',
|
|
377
|
+
default: [],
|
|
378
|
+
})
|
|
337
379
|
}, async (argv) => {
|
|
338
380
|
let source = argv.source as string; // now always defined (default '.')
|
|
339
381
|
let target = argv.target as string; // may be undefined
|
|
340
382
|
|
|
341
383
|
const move: boolean = argv.move as boolean;
|
|
342
384
|
const overwrite: boolean = argv.overwrite as boolean;
|
|
343
|
-
const ignorePatterns: string[] = argv.ignore as string[]
|
|
385
|
+
const ignorePatterns: string[] = (argv.ignore as string[] | undefined) ?? [];
|
|
344
386
|
const respectGitignore: boolean = argv.gitignore as boolean;
|
|
345
387
|
const flattenToDirectory: boolean = argv.directory as boolean;
|
|
346
388
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flatten-tool",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
4
4
|
"description": "CLI tool to flatten directory structures: merge files into a single Markdown file (default) or copy/move to a flat directory with escaped filenames. Respects .gitignore, supports move/overwrite, and more.",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"type": "module",
|