-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
209 lines (192 loc) · 6.75 KB
/
index.js
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
const fs = require('fs');
const path = require('path');
const babel = require("@babel/core");
const postcss = require("postcss");
const htmlparser2 = require("htmlparser2");
const marked = require("marked");
const {OpenAI} = require('openai');
const dotenv = require('dotenv');
dotenv.config();
const babelConfig = {
presets: [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
],
plugins: [
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods",
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-optional-chaining"
]
};
const OA_API_KEY = process.env.OPENAI_API_KEY;
const RATE_LIMIT_WINDOW_MS = 1000; // Adjust this based on actual rate limits
let lastApiCallTime = 0;
console.log(`OpenAI API key: ${OA_API_KEY}`);
async function summarizeFileOpenAI(filename, file) {
const openai = new OpenAI({apiKey: OA_API_KEY});
const currentTime = Date.now();
if (currentTime - lastApiCallTime < RATE_LIMIT_WINDOW_MS) {
await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_WINDOW_MS - (currentTime - lastApiCallTime)));
}
lastApiCallTime = Date.now();
try{
const completion = await openai.chat.completions.create({
messages: [{ role: "system", content: "You are a helpful repo assistant. Be concise but insightful." }, { role: "user", content: `Summarize the code for this file (${filename}) and its purpose. What functions and UI elements are written here? : ${file}. Respond in less than 250 words.` }],
model: "gpt-4",
});
const summary = completion.choices[0].message.content;
console.log(summary);
return summary;
} catch (error) {
console.error(`Error summarizing file ${filename} using OpenAI: ${error}`);
return `Error summarizing file: ${error.message}`;
}
}
async function callDummyAPI(filename, sourceCode) {
console.log('Summarizing file: ', filename)
const summary = await summarizeFileOpenAI(filename, sourceCode);
return Promise.resolve(`Summary for ${filename} : ${summary}`);
}
async function generateAST(directory) {
const files = getFiles(directory);
const asts = [];
for (const file of files) {
const code = fs.readFileSync(file, 'utf8');
let ast, summary;
try {
if (file.endsWith('.js') || file.endsWith('.jsx') || file.endsWith('.ts') || file.endsWith('.tsx')) {
ast = await babel.parseAsync(code, {
...babelConfig,
filename: file
});
} else if (file.endsWith('.json')) {
ast = parseJSON(file, code);
} else if (file.endsWith('.html')) {
ast = htmlparser2.parseDocument(code);
} else if (file.endsWith('.css')) {
ast = postcss.parse(code);
} else if (file.endsWith('.md')) {
const html = marked(code);
ast = htmlparser2.parseDocument(html);
} else {
console.log(`Skipping unsupported file type: ${file}`);
continue; // Skip unsupported files
}
} catch (error) {
console.error(`Error processing ${file}: ${error}`);
asts.push({
file: file,
type: getFileType(file),
error: error.message,
sourceCode: code
});
continue; // Continue processing the next file on error
}
try {
// Ensure that the summary is awaited before pushing to asts
summary = await callDummyAPI(file, code);
} catch (error) {
console.error(`Error generating summary for ${file}: ${error}`);
summary = `Error generating summary: ${error.message}`;
}
// Push the result to the asts array, ensuring that summary is properly handled
asts.push({
file: file,
type: getFileType(file),
ast: ast,
summary: summary, // Directly use the resolved summary
sourceCode: code
});
}
return asts;
}
function parseJSON(file, code) {
try {
const parsed = JSON.parse(code);
const ast = deriveSchema(parsed);
return { type: "object", properties: ast };
} catch (error) {
console.error(`Error parsing JSON in file ${file}: ${error}`);
throw error; // Rethrow to be caught by the calling function
}
}
function deriveSchema(jsonObject) {
const getType = (value) => {
if (Array.isArray(value)) {
return 'array';
} else if (value === null) {
return 'null';
} else {
return typeof value;
}
};
const schema = {};
for (const key in jsonObject) {
const value = jsonObject[key];
schema[key] = { type: getType(value) };
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
schema[key].properties = deriveSchema(value);
}
}
return schema;
}
function getFiles(dir, files_ = []) {
const files = fs.readdirSync(dir);
for (const i in files) {
const name = path.join(dir, files[i]);
if (fs.statSync(name).isDirectory()) {
if (files[i] !== 'node_modules') {
getFiles(name, files_); // Recurse into subdirectories, excluding node_modules
}
} else {
// Check if the file is package-lock.json and skip it
if (!name.endsWith('package-lock.json')) {
files_.push(name);
}
}
}
return files_;
}
function getFileType(file) {
const ext = path.extname(file).toLowerCase();
switch (ext) {
case '.js':
case '.jsx':
case '.ts':
case '.tsx':
return "JavaScript/TypeScript";
case '.json':
return "JSON";
case '.html':
return "HTML";
case '.css':
return "CSS";
case '.md':
return "Markdown";
default:
return "Unknown";
}
}
function stringifySafe(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return '[Circular]'; // Duplicate reference found, replace key
}
seen.add(value);
}
return value;
});
}
// Example usage
const YOUR_DIRECTORY = './test';
generateAST(YOUR_DIRECTORY)
.then(asts => {
fs.writeFileSync('asts.json', stringifySafe(asts), 'utf8');
console.log("ASTs and DOMs have been written to asts.json");
})
.catch(err => console.error(err));