-
Notifications
You must be signed in to change notification settings - Fork 0
/
concatenate_project.ts
227 lines (203 loc) · 6.75 KB
/
concatenate_project.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/env deno run --allow-read --allow-write
/**
* ------------------------------------------------------------------
* Script Name: concatenate_project.ts
* Description: Concatenates relevant project documentation and
* code files into a single file for AI review.
* For .ts files, extracts header comments unless specified
* to include full content based on inclusion list.
* Usage: deno run --allow-read --allow-write concatenate_project.ts
* ------------------------------------------------------------------
*/
import { walk } from "https://deno.land/[email protected]/fs/walk.ts";
// Set the output file name
const OUTPUT_FILE = "concatenate_project.txt";
// Remove the output file if it already exists to avoid appending
try {
await Deno.remove(OUTPUT_FILE);
console.log(`Existing ${OUTPUT_FILE} removed.`);
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
// File does not exist, no action needed
} else {
console.error(`Error removing ${OUTPUT_FILE}:`, error);
Deno.exit(1);
}
}
// Define directories to exclude
const EXCLUDE_DIRS = [
"node_modules",
".git",
".vscode",
"dist",
"build",
"cache",
"src/notebooks",
"markdown_example",
"markdown_private",
"markdown_test",
"markdown",
"tests",
];
// Define file extensions to include
const INCLUDE_EXTENSIONS = ["js", "ts", "json", "md", "ipynb", "txt"];
// Define file names to exclude
const EXCLUDE_FILES = [
OUTPUT_FILE,
"package-lock.json",
".env",
"convert-js-to-ts.js",
"lock.json",
"notes.txt",
"concatenate_project.ts",
"concatenate_project.sh",
];
// Define list of .ts files or glob patterns to include full content
const INCLUDE_FULL_TS_FILES = ["./src/config.ts"]; // Add paths or glob patterns as needed
/**
* Checks if a file should be excluded based on EXCLUDE_FILES.
* @param filePath - The path of the file to check.
* @returns `true` if the file should be excluded, otherwise `false`.
*/
function shouldExcludeFile(filePath: string): boolean {
const fileName = filePath.split("/").pop() || "";
return EXCLUDE_FILES.includes(fileName);
}
/**
* Determines if a .ts file should include its full content based on INCLUDE_FULL_TS_FILES.
* @param filePath - The path of the .ts file to check.
* @returns `true` if the file should include full content, otherwise `false`.
*/
function shouldIncludeFullTs(filePath: string): boolean {
// Normalize paths for comparison
const normalizedPath = filePath.replace(/\\/g, "/");
return INCLUDE_FULL_TS_FILES.some((pattern) => {
// Simple glob pattern matching (exact match or wildcard)
if (pattern.endsWith("/*")) {
const dir = pattern.slice(0, -2);
return normalizedPath.startsWith(dir + "/");
}
return normalizedPath === pattern;
});
}
/**
* Extracts header comments from a .ts file.
* Header comments include block comments at the top and single-line comments until the first non-comment line.
* @param filePath - The path of the .ts file.
* @returns The extracted header comments as a string.
*/
async function extractTsHeaderComments(filePath: string): Promise<string> {
const file = await Deno.open(filePath, { read: true });
const decoder = new TextDecoder("utf-8");
const reader = file.readable.getReader();
let inBlockComment = false;
let headerComments = "";
let done = false;
while (!done) {
const { value, done: readerDone } = await reader.read();
if (readerDone) {
break;
}
const lines = decoder.decode(value).split("\n");
for (const line of lines) {
if (!inBlockComment) {
if (/^\s*\/\*/.test(line)) {
inBlockComment = true;
headerComments += line + "\n";
if (/\*\//.test(line)) {
inBlockComment = false;
}
} else if (/^\s*\/\//.test(line)) {
headerComments += line + "\n";
} else if (headerComments.trim() === "") {
// Skip leading non-comment lines
done = true;
break;
} else {
// First non-comment line after comments
done = true;
break;
}
} else {
headerComments += line + "\n";
if (/\*\//.test(line)) {
inBlockComment = false;
}
}
}
}
file.close();
return headerComments.trim();
}
/**
* Appends content to the output file with headers and footers.
* @param content - The content to append.
*/
async function appendToOutput(content: string) {
await Deno.writeTextFile(OUTPUT_FILE, content, { append: true });
}
console.log("Starting concatenation...");
// Initialize the output file
await Deno.writeTextFile(OUTPUT_FILE, "");
// Traverse the project directory
for await (const entry of walk(".", {
skip: EXCLUDE_DIRS.map((dir) => new RegExp(`^${dir}(/|$)`)),
includeFiles: true,
includeDirs: false,
followSymlinks: false,
})) {
const filePath = `./${entry.path}`;
const extension = entry.name.split(".").pop()?.toLowerCase();
// Skip if extension is not in INCLUDE_EXTENSIONS
if (!extension || !INCLUDE_EXTENSIONS.includes(extension)) {
continue;
}
// Skip excluded files
if (shouldExcludeFile(filePath)) {
console.log(`Skipping ${filePath} (matches exclusion pattern)`);
continue;
} else {
console.log(`Processing ${filePath}`);
}
// Add a header with the file name
const header = `===== File: ${filePath} =====\n\n`;
await appendToOutput(header);
if (extension === "ts") {
if (shouldIncludeFullTs(filePath)) {
// Append the entire .ts file content
try {
const content = await Deno.readTextFile(filePath);
await appendToOutput(content);
} catch (error) {
console.error(`Error reading ${filePath}:`, error);
await appendToOutput("// Error reading file.\n");
}
} else {
// Extract and append only header comments for .ts files
try {
const headerComments = await extractTsHeaderComments(filePath);
if (headerComments) {
await appendToOutput(headerComments);
} else {
await appendToOutput("// No header comments found.\n");
}
} catch (error) {
console.error(`Error extracting comments from ${filePath}:`, error);
await appendToOutput("// Error extracting header comments.\n");
}
}
} else {
// Append the entire file content for other file types
try {
const content = await Deno.readTextFile(filePath);
await appendToOutput(content);
} catch (error) {
console.error(`Error reading ${filePath}:`, error);
await appendToOutput("// Error reading file.\n");
}
}
// Add a footer for separation
const footer = `\n===========================\n\n`;
await appendToOutput(footer);
}
console.log(`Concatenation complete. Output file created: ${OUTPUT_FILE}`);