@ankurhalder/codebase-snapshot 2.0.0 → 2.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 +42 -0
- package/dist/bin.js +2330 -367
- package/dist/{chunk-RGDVLQTT.js → chunk-QEIOXPMU.js} +2242 -366
- package/dist/cli.cjs +2346 -390
- package/dist/cli.d.cts +9 -1
- package/dist/cli.d.ts +9 -1
- package/dist/cli.js +108 -27
- package/dist/index.cjs +2255 -365
- package/dist/index.d.cts +183 -6
- package/dist/index.d.ts +183 -6
- package/dist/index.js +1 -1
- package/dist/{types-S-5eRuRc.d.cts → types-CJqkePM4.d.cts} +20 -1
- package/dist/{types-S-5eRuRc.d.ts → types-CJqkePM4.d.ts} +20 -1
- package/docs/configuration.md +97 -1
- package/docs/install.md +136 -0
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var crypto = require('crypto');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var fs9 = require('fs');
|
|
5
|
+
var path10 = require('path');
|
|
6
6
|
var ts = require('typescript');
|
|
7
7
|
var url = require('url');
|
|
8
8
|
var childProcess = require('child_process');
|
|
9
|
+
var readline = require('readline');
|
|
9
10
|
|
|
10
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
12
|
|
|
@@ -28,135 +29,498 @@ function _interopNamespace(e) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
|
|
31
|
-
var
|
|
32
|
-
var
|
|
32
|
+
var fs9__namespace = /*#__PURE__*/_interopNamespace(fs9);
|
|
33
|
+
var path10__namespace = /*#__PURE__*/_interopNamespace(path10);
|
|
33
34
|
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
34
35
|
var childProcess__namespace = /*#__PURE__*/_interopNamespace(childProcess);
|
|
36
|
+
var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
|
|
35
37
|
|
|
36
38
|
// src/index.ts
|
|
37
39
|
|
|
38
|
-
// src/ast/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
40
|
+
// src/ast/profiles/shared.ts
|
|
41
|
+
var MAX_SUMMARY_CHARS = 1e6;
|
|
42
|
+
function emptySummary() {
|
|
43
|
+
return {
|
|
44
|
+
directives: [],
|
|
45
|
+
imports: [],
|
|
46
|
+
exports: [],
|
|
47
|
+
reExports: [],
|
|
48
|
+
functions: [],
|
|
49
|
+
classes: [],
|
|
50
|
+
interfaces: [],
|
|
51
|
+
types: [],
|
|
52
|
+
enums: [],
|
|
53
|
+
variables: [],
|
|
54
|
+
reactComponents: [],
|
|
55
|
+
hooks: [],
|
|
56
|
+
routeHandlers: []
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function dedupeSorted(values) {
|
|
60
|
+
return Array.from(new Set(values.filter(Boolean))).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
61
|
+
}
|
|
62
|
+
function finalizeSummary(summary) {
|
|
63
|
+
return {
|
|
64
|
+
directives: dedupeSorted(summary.directives),
|
|
65
|
+
imports: dedupeSorted(summary.imports),
|
|
66
|
+
exports: dedupeSorted(summary.exports),
|
|
67
|
+
reExports: dedupeSorted(summary.reExports),
|
|
68
|
+
functions: dedupeSorted(summary.functions),
|
|
69
|
+
classes: dedupeSorted(summary.classes),
|
|
70
|
+
interfaces: dedupeSorted(summary.interfaces),
|
|
71
|
+
types: dedupeSorted(summary.types),
|
|
72
|
+
enums: dedupeSorted(summary.enums),
|
|
73
|
+
variables: dedupeSorted(summary.variables),
|
|
74
|
+
reactComponents: dedupeSorted(summary.reactComponents),
|
|
75
|
+
hooks: dedupeSorted(summary.hooks),
|
|
76
|
+
routeHandlers: dedupeSorted(summary.routeHandlers)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function eachLine(content, visit) {
|
|
80
|
+
const text2 = content.length > MAX_SUMMARY_CHARS ? content.slice(0, MAX_SUMMARY_CHARS) : content;
|
|
81
|
+
for (const line of text2.split(/\r?\n/)) visit(line);
|
|
82
|
+
}
|
|
83
|
+
function isTopLevel(line) {
|
|
84
|
+
return !/^[\s]/.test(line);
|
|
85
|
+
}
|
|
86
|
+
function isPublicName(name) {
|
|
87
|
+
return name.length > 0 && !name.startsWith("_") && /^[A-Za-z]/.test(name);
|
|
88
|
+
}
|
|
89
|
+
function isCapitalizedName(name) {
|
|
90
|
+
return /^[A-Z]/.test(name);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/ast/profiles/cfamily.ts
|
|
94
|
+
var INCLUDE = /^#\s*include\s*[<"]([^>"]+)[>"]/;
|
|
95
|
+
var DEFINE = /^#\s*define\s+([A-Za-z_]\w*)/;
|
|
96
|
+
var USING = /^using\s+(?:namespace\s+)?([\w:]+)\s*;/;
|
|
97
|
+
var TYPE_DECL = /^(?:typedef\s+)?(?:struct|union|class|enum)\s+([A-Za-z_]\w*)/;
|
|
98
|
+
var TYPEDEF = /^typedef\b[^;{}]*?\b([A-Za-z_]\w*)\s*;/;
|
|
99
|
+
var FUNCTION = /^[A-Za-z_][\w \t*&:<>,]*?\b([A-Za-z_]\w*)\s*\([^;]*\)\s*\{?\s*$/;
|
|
100
|
+
var NOT_A_FUNCTION = /^(?:if|for|while|switch|return|else|do|case|sizeof|catch|throw)\b/;
|
|
101
|
+
var cfamilyProfile = {
|
|
102
|
+
id: "cfamily",
|
|
103
|
+
extensions: [".c", ".h", ".cc", ".cpp", ".cxx", ".hpp", ".hxx", ".m", ".mm"],
|
|
104
|
+
summarize(_path, content) {
|
|
105
|
+
const summary = emptySummary();
|
|
106
|
+
eachLine(content, (line) => {
|
|
107
|
+
const trimmed = line.trim();
|
|
108
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*")) {
|
|
109
|
+
return;
|
|
52
110
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
inBlockComment = false;
|
|
58
|
-
i += 1;
|
|
111
|
+
const included = INCLUDE.exec(trimmed);
|
|
112
|
+
if (included) {
|
|
113
|
+
summary.imports.push(included[1]);
|
|
114
|
+
return;
|
|
59
115
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
116
|
+
const used = USING.exec(trimmed);
|
|
117
|
+
if (used) {
|
|
118
|
+
summary.imports.push(used[1]);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const macro = DEFINE.exec(trimmed);
|
|
122
|
+
if (macro) {
|
|
123
|
+
summary.variables.push(macro[1]);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (!isTopLevel(line)) return;
|
|
127
|
+
const declared = TYPE_DECL.exec(line);
|
|
128
|
+
if (declared) {
|
|
129
|
+
const kind = /^typedef\s+enum/.test(line) || /^enum\b/.test(trimmed) ? "enum" : "type";
|
|
130
|
+
if (kind === "enum") summary.enums.push(declared[1]);
|
|
131
|
+
else summary.classes.push(declared[1]);
|
|
132
|
+
summary.exports.push(declared[1]);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const aliased = TYPEDEF.exec(line);
|
|
136
|
+
if (aliased) {
|
|
137
|
+
summary.types.push(aliased[1]);
|
|
138
|
+
summary.exports.push(aliased[1]);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (NOT_A_FUNCTION.test(trimmed)) return;
|
|
142
|
+
const fn = FUNCTION.exec(line);
|
|
143
|
+
if (fn) {
|
|
144
|
+
summary.functions.push(fn[1]);
|
|
145
|
+
summary.exports.push(fn[1]);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/ast/profiles/dotnet.ts
|
|
153
|
+
var USING2 = /^using\s+([\w.]+)\s*;/;
|
|
154
|
+
var TYPE_DECL2 = /^\s*(?:public\s+|internal\s+|private\s+|protected\s+|sealed\s+|abstract\s+|static\s+|partial\s+|readonly\s+)*(class|interface|struct|record|enum)\s+([A-Za-z_]\w*)/;
|
|
155
|
+
var METHOD = /^\s*(?:public|private|protected|internal)\s+(?:static\s+)?(?:async\s+)?(?:virtual\s+|override\s+|sealed\s+)?[\w<>[\],?]+\s+([A-Za-z_]\w*)\s*[<(]/;
|
|
156
|
+
var dotnetProfile = {
|
|
157
|
+
id: "dotnet",
|
|
158
|
+
extensions: [".cs"],
|
|
159
|
+
summarize(_path, content) {
|
|
160
|
+
const summary = emptySummary();
|
|
161
|
+
eachLine(content, (line) => {
|
|
162
|
+
const trimmed = line.trim();
|
|
163
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("/*") || trimmed.startsWith("*")) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const used = USING2.exec(trimmed);
|
|
167
|
+
if (used) {
|
|
168
|
+
summary.imports.push(used[1]);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const declaration = TYPE_DECL2.exec(line);
|
|
172
|
+
if (declaration) {
|
|
173
|
+
const kind = declaration[1];
|
|
174
|
+
const name = declaration[2];
|
|
175
|
+
if (kind === "interface") summary.interfaces.push(name);
|
|
176
|
+
else if (kind === "enum") summary.enums.push(name);
|
|
177
|
+
else summary.classes.push(name);
|
|
178
|
+
if (/^\s*public\b/.test(line)) summary.exports.push(name);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const method = METHOD.exec(line);
|
|
182
|
+
if (method) {
|
|
183
|
+
summary.functions.push(method[1]);
|
|
184
|
+
if (/^\s*public\b/.test(line)) summary.exports.push(method[1]);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/ast/profiles/go.ts
|
|
192
|
+
var FUNC = /^func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/;
|
|
193
|
+
var TYPE = /^type\s+([A-Za-z_]\w*)\s+(.*)$/;
|
|
194
|
+
var VAR_CONST = /^(?:var|const)\s+([A-Za-z_]\w*)/;
|
|
195
|
+
var IMPORT_SINGLE = /^import\s+(?:[\w.]+\s+)?"([^"]+)"/;
|
|
196
|
+
var IMPORT_OPEN = /^import\s*\(/;
|
|
197
|
+
var IMPORT_ENTRY = /^\s*(?:[\w.]+\s+)?"([^"]+)"/;
|
|
198
|
+
var goProfile = {
|
|
199
|
+
id: "go",
|
|
200
|
+
extensions: [".go"],
|
|
201
|
+
summarize(_path, content) {
|
|
202
|
+
const summary = emptySummary();
|
|
203
|
+
let inImportBlock = false;
|
|
204
|
+
eachLine(content, (line) => {
|
|
205
|
+
const trimmed = line.trim();
|
|
206
|
+
if (!trimmed || trimmed.startsWith("//")) return;
|
|
207
|
+
if (inImportBlock) {
|
|
208
|
+
if (trimmed.startsWith(")")) {
|
|
209
|
+
inImportBlock = false;
|
|
210
|
+
return;
|
|
68
211
|
}
|
|
69
|
-
|
|
70
|
-
|
|
212
|
+
const entry = IMPORT_ENTRY.exec(line);
|
|
213
|
+
if (entry) summary.imports.push(entry[1]);
|
|
214
|
+
return;
|
|
71
215
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
216
|
+
if (IMPORT_OPEN.test(trimmed)) {
|
|
217
|
+
inImportBlock = true;
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const single = IMPORT_SINGLE.exec(trimmed);
|
|
221
|
+
if (single) {
|
|
222
|
+
summary.imports.push(single[1]);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (!isTopLevel(line)) return;
|
|
226
|
+
const method = FUNC.exec(line);
|
|
227
|
+
if (method) {
|
|
228
|
+
const name = method[1];
|
|
229
|
+
summary.functions.push(name);
|
|
230
|
+
if (isCapitalizedName(name)) summary.exports.push(name);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const declared = TYPE.exec(line);
|
|
234
|
+
if (declared) {
|
|
235
|
+
const name = declared[1];
|
|
236
|
+
const body = declared[2].trim();
|
|
237
|
+
if (body.startsWith("struct")) summary.classes.push(name);
|
|
238
|
+
else if (body.startsWith("interface")) summary.interfaces.push(name);
|
|
239
|
+
else summary.types.push(name);
|
|
240
|
+
if (isCapitalizedName(name)) summary.exports.push(name);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const value = VAR_CONST.exec(line);
|
|
244
|
+
if (value) {
|
|
245
|
+
summary.variables.push(value[1]);
|
|
246
|
+
if (isCapitalizedName(value[1])) summary.exports.push(value[1]);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
90
250
|
}
|
|
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
|
-
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// src/ast/profiles/jvm.ts
|
|
254
|
+
var IMPORT = /^import\s+(?:static\s+)?([\w.]+?)(?:\.\*)?;?\s*$/;
|
|
255
|
+
var TYPE_DECL3 = /^\s*(?:public\s+|private\s+|protected\s+|internal\s+)?(?:final\s+|abstract\s+|open\s+|sealed\s+|data\s+|static\s+|partial\s+)*(class|interface|enum|record|object|trait)\s+(?:class\s+)?([A-Za-z_]\w*)/;
|
|
256
|
+
var KOTLIN_FUN = /^\s*(?:public\s+|private\s+|protected\s+|internal\s+|override\s+|suspend\s+|inline\s+|open\s+|operator\s+)*fun\s+(?:<[^>]*>\s*)?(?:[\w.]+\.)?([A-Za-z_]\w*)/;
|
|
257
|
+
var SCALA_DEF = /^\s*def\s+([A-Za-z_]\w*)/;
|
|
258
|
+
var JAVA_METHOD = /^\s+(?:public|private|protected)\s+(?:static\s+)?(?:final\s+)?(?:synchronized\s+)?(?:abstract\s+)?[\w<>[\],.?]+\s+([A-Za-z_]\w*)\s*\(/;
|
|
259
|
+
var jvmProfile = {
|
|
260
|
+
id: "jvm",
|
|
261
|
+
extensions: [".java", ".kt", ".kts", ".scala"],
|
|
262
|
+
summarize(path11, content) {
|
|
263
|
+
const summary = emptySummary();
|
|
264
|
+
const lower = path11.toLowerCase();
|
|
265
|
+
const isJava = lower.endsWith(".java");
|
|
266
|
+
const isKotlin = lower.endsWith(".kt") || lower.endsWith(".kts");
|
|
267
|
+
eachLine(content, (line) => {
|
|
268
|
+
const trimmed = line.trim();
|
|
269
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
const imported = IMPORT.exec(trimmed);
|
|
273
|
+
if (imported) {
|
|
274
|
+
summary.imports.push(imported[1]);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const declaration = TYPE_DECL3.exec(line);
|
|
278
|
+
if (declaration) {
|
|
279
|
+
const kind = declaration[1];
|
|
280
|
+
const name = declaration[2];
|
|
281
|
+
if (kind === "interface" || kind === "trait") summary.interfaces.push(name);
|
|
282
|
+
else if (kind === "enum") summary.enums.push(name);
|
|
283
|
+
else summary.classes.push(name);
|
|
284
|
+
const hidden = /^\s*(?:private|protected|internal)\b/.test(line);
|
|
285
|
+
if (isJava ? /^\s*public\b/.test(line) : !hidden) summary.exports.push(name);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const kotlinFunction = KOTLIN_FUN.exec(line);
|
|
289
|
+
if (kotlinFunction && isKotlin) {
|
|
290
|
+
summary.functions.push(kotlinFunction[1]);
|
|
291
|
+
const hidden = /^\s*(?:private|protected|internal)\b/.test(line);
|
|
292
|
+
if (!hidden) summary.exports.push(kotlinFunction[1]);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const scalaFunction = SCALA_DEF.exec(line);
|
|
296
|
+
if (scalaFunction && !isJava && !isKotlin) {
|
|
297
|
+
summary.functions.push(scalaFunction[1]);
|
|
298
|
+
const hidden = /^\s*(?:private|protected)\b/.test(line);
|
|
299
|
+
if (!hidden) summary.exports.push(scalaFunction[1]);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (isJava) {
|
|
303
|
+
const method = JAVA_METHOD.exec(line);
|
|
304
|
+
if (method) {
|
|
305
|
+
summary.functions.push(method[1]);
|
|
306
|
+
if (/^\s*public\b/.test(line)) summary.exports.push(method[1]);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
128
311
|
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
continue;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// src/ast/profiles/php.ts
|
|
315
|
+
var USE = /^use\s+([\w\\]+)\s*(?:as\s+\w+)?;/;
|
|
316
|
+
var DECLARE = /^(?:abstract\s+|final\s+|readonly\s+)*(class|interface|trait|enum)\s+([A-Za-z_]\w*)/;
|
|
317
|
+
var FUNCTION2 = /^(?:public\s+|protected\s+|private\s+|static\s+|final\s+|abstract\s+)*function\s+([A-Za-z_]\w*)/;
|
|
318
|
+
var phpProfile = {
|
|
319
|
+
id: "php",
|
|
320
|
+
extensions: [".php"],
|
|
321
|
+
summarize(_path, content) {
|
|
322
|
+
const summary = emptySummary();
|
|
323
|
+
eachLine(content, (line) => {
|
|
324
|
+
const trimmed = line.trim();
|
|
325
|
+
if (!trimmed || trimmed.startsWith("//") || trimmed.startsWith("#") || trimmed.startsWith("/*")) {
|
|
326
|
+
return;
|
|
145
327
|
}
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
328
|
+
const used = USE.exec(trimmed);
|
|
329
|
+
if (used) {
|
|
330
|
+
summary.imports.push(used[1].replace(/\\\\/g, "\\"));
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const declaration = DECLARE.exec(trimmed);
|
|
334
|
+
if (declaration) {
|
|
335
|
+
const kind = declaration[1];
|
|
336
|
+
const name = declaration[2];
|
|
337
|
+
if (kind === "interface" || kind === "trait") summary.interfaces.push(name);
|
|
338
|
+
else if (kind === "enum") summary.enums.push(name);
|
|
339
|
+
else summary.classes.push(name);
|
|
340
|
+
summary.exports.push(name);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const fn = FUNCTION2.exec(trimmed);
|
|
344
|
+
if (fn) {
|
|
345
|
+
summary.functions.push(fn[1]);
|
|
346
|
+
if (!/^private\b/.test(trimmed)) summary.exports.push(fn[1]);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
154
350
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
// src/ast/profiles/python.ts
|
|
354
|
+
var IMPORT2 = /^import\s+(.+)$/;
|
|
355
|
+
var FROM_IMPORT = /^from\s+([\w.]+)\s+import\s+(.+)$/;
|
|
356
|
+
var CLASS = /^class\s+([A-Za-z_]\w*)/;
|
|
357
|
+
var DEF = /^(?:async\s+)?def\s+([A-Za-z_]\w*)/;
|
|
358
|
+
var CONSTANT = /^([A-Z_][A-Z0-9_]*)\s*(?::[^=]+)?=/;
|
|
359
|
+
var ALL_DUNDER = /^__all__\s*=\s*\[([^\]]*)\]/;
|
|
360
|
+
var ROUTE_DECORATOR = /^\s*@[\w.]*\b(?:get|post|put|patch|delete|head|options|route)\s*\(/i;
|
|
361
|
+
var pythonProfile = {
|
|
362
|
+
id: "python",
|
|
363
|
+
extensions: [".py", ".pyi"],
|
|
364
|
+
summarize(_path, content) {
|
|
365
|
+
const summary = emptySummary();
|
|
366
|
+
let routePending = false;
|
|
367
|
+
eachLine(content, (line) => {
|
|
368
|
+
const trimmed = line.trim();
|
|
369
|
+
if (!trimmed || trimmed.startsWith("#")) return;
|
|
370
|
+
if (ROUTE_DECORATOR.test(line)) {
|
|
371
|
+
routePending = true;
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const from = FROM_IMPORT.exec(trimmed);
|
|
375
|
+
if (from) {
|
|
376
|
+
summary.imports.push(from[1]);
|
|
377
|
+
routePending = false;
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const imported = IMPORT2.exec(trimmed);
|
|
381
|
+
if (imported) {
|
|
382
|
+
for (const part of imported[1].split(",")) {
|
|
383
|
+
const name = part.trim().split(/\s+as\s+/)[0].trim();
|
|
384
|
+
if (name) summary.imports.push(name);
|
|
385
|
+
}
|
|
386
|
+
routePending = false;
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (isTopLevel(line)) {
|
|
390
|
+
const klass = CLASS.exec(line);
|
|
391
|
+
if (klass) {
|
|
392
|
+
summary.classes.push(klass[1]);
|
|
393
|
+
if (isPublicName(klass[1])) summary.exports.push(klass[1]);
|
|
394
|
+
routePending = false;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const definition = DEF.exec(line);
|
|
398
|
+
if (definition) {
|
|
399
|
+
const name = definition[1];
|
|
400
|
+
summary.functions.push(name);
|
|
401
|
+
if (routePending) {
|
|
402
|
+
summary.routeHandlers.push(name);
|
|
403
|
+
routePending = false;
|
|
404
|
+
}
|
|
405
|
+
if (isPublicName(name)) summary.exports.push(name);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const all = ALL_DUNDER.exec(trimmed);
|
|
409
|
+
if (all) {
|
|
410
|
+
for (const entry of all[1].split(",")) {
|
|
411
|
+
const name = entry.trim().replace(/['"]/g, "").trim();
|
|
412
|
+
if (name) summary.exports.push(name);
|
|
413
|
+
}
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const constant = CONSTANT.exec(line);
|
|
417
|
+
if (constant) summary.variables.push(constant[1]);
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
// src/ast/profiles/ruby.ts
|
|
425
|
+
var REQUIRE = /^require(?:_relative)?\s+["']([^"']+)["']/;
|
|
426
|
+
var CLASS_OR_MODULE = /^(?:class|module)\s+([A-Z][\w:]*)/;
|
|
427
|
+
var METHOD2 = /^def\s+(?:self\.)?([A-Za-z_]\w*[?!=]?)/;
|
|
428
|
+
var rubyProfile = {
|
|
429
|
+
id: "ruby",
|
|
430
|
+
extensions: [".rb", ".rake", ".gemspec"],
|
|
431
|
+
summarize(_path, content) {
|
|
432
|
+
const summary = emptySummary();
|
|
433
|
+
eachLine(content, (line) => {
|
|
434
|
+
const trimmed = line.trim();
|
|
435
|
+
if (!trimmed || trimmed.startsWith("#")) return;
|
|
436
|
+
const required = REQUIRE.exec(trimmed);
|
|
437
|
+
if (required) {
|
|
438
|
+
summary.imports.push(required[1]);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (!isTopLevel(line)) return;
|
|
442
|
+
const declaration = CLASS_OR_MODULE.exec(line);
|
|
443
|
+
if (declaration) {
|
|
444
|
+
summary.classes.push(declaration[1]);
|
|
445
|
+
summary.exports.push(declaration[1]);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const method = METHOD2.exec(line);
|
|
449
|
+
if (method) {
|
|
450
|
+
summary.functions.push(method[1]);
|
|
451
|
+
summary.exports.push(method[1]);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
// src/ast/profiles/rust.ts
|
|
459
|
+
var USE2 = /^use\s+([^;]+);/;
|
|
460
|
+
var FN = /^(pub(?:\([^)]*\))?\s+)?(?:const\s+)?(?:async\s+)?(?:unsafe\s+)?fn\s+([A-Za-z_]\w*)/;
|
|
461
|
+
var STRUCT = /^(pub(?:\([^)]*\))?\s+)?struct\s+([A-Za-z_]\w*)/;
|
|
462
|
+
var TRAIT = /^(pub(?:\([^)]*\))?\s+)?(?:unsafe\s+)?trait\s+([A-Za-z_]\w*)/;
|
|
463
|
+
var ENUM = /^(pub(?:\([^)]*\))?\s+)?enum\s+([A-Za-z_]\w*)/;
|
|
464
|
+
var TYPE2 = /^(pub(?:\([^)]*\))?\s+)?type\s+([A-Za-z_]\w*)/;
|
|
465
|
+
var MOD = /^(pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_]\w*)/;
|
|
466
|
+
var IMPL = /^impl(?:<[^>]*>)?\s+(?:[\w:<>]+\s+for\s+)?([A-Za-z_]\w*)/;
|
|
467
|
+
var rustProfile = {
|
|
468
|
+
id: "rust",
|
|
469
|
+
extensions: [".rs"],
|
|
470
|
+
summarize(_path, content) {
|
|
471
|
+
const summary = emptySummary();
|
|
472
|
+
eachLine(content, (line) => {
|
|
473
|
+
const trimmed = line.trim();
|
|
474
|
+
if (!trimmed || trimmed.startsWith("//")) return;
|
|
475
|
+
const used = USE2.exec(trimmed);
|
|
476
|
+
if (used) {
|
|
477
|
+
summary.imports.push(used[1].trim());
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (!isTopLevel(line)) return;
|
|
481
|
+
const isPublic = /^pub\b/.test(trimmed);
|
|
482
|
+
const fn = FN.exec(line);
|
|
483
|
+
if (fn) {
|
|
484
|
+
summary.functions.push(fn[2]);
|
|
485
|
+
if (isPublic) summary.exports.push(fn[2]);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const struct = STRUCT.exec(line);
|
|
489
|
+
if (struct) {
|
|
490
|
+
summary.classes.push(struct[2]);
|
|
491
|
+
if (isPublic) summary.exports.push(struct[2]);
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const trait = TRAIT.exec(line);
|
|
495
|
+
if (trait) {
|
|
496
|
+
summary.interfaces.push(trait[2]);
|
|
497
|
+
if (isPublic) summary.exports.push(trait[2]);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
const enumeration = ENUM.exec(line);
|
|
501
|
+
if (enumeration) {
|
|
502
|
+
summary.enums.push(enumeration[2]);
|
|
503
|
+
if (isPublic) summary.exports.push(enumeration[2]);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const alias = TYPE2.exec(line);
|
|
507
|
+
if (alias) {
|
|
508
|
+
summary.types.push(alias[2]);
|
|
509
|
+
if (isPublic) summary.exports.push(alias[2]);
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const module = MOD.exec(line);
|
|
513
|
+
if (module) {
|
|
514
|
+
summary.variables.push(module[2]);
|
|
515
|
+
if (isPublic) summary.exports.push(module[2]);
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const implementation = IMPL.exec(line);
|
|
519
|
+
if (implementation) summary.classes.push(implementation[1]);
|
|
520
|
+
});
|
|
521
|
+
return { summary: finalizeSummary(summary), quality: "heuristic" };
|
|
522
|
+
}
|
|
523
|
+
};
|
|
160
524
|
var ROUTE_HANDLER_NAMES = /* @__PURE__ */ new Set([
|
|
161
525
|
"GET",
|
|
162
526
|
"POST",
|
|
@@ -196,9 +560,9 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
196
560
|
if (parseErrors > 0) summary.parseErrors = parseErrors;
|
|
197
561
|
for (const statement of sourceFile.statements) {
|
|
198
562
|
if (ts__default.default.isExpressionStatement(statement) && ts__default.default.isStringLiteral(statement.expression)) {
|
|
199
|
-
const
|
|
200
|
-
if (
|
|
201
|
-
summary.directives.push(
|
|
563
|
+
const text2 = statement.expression.text;
|
|
564
|
+
if (text2 === "use client" || text2 === "use server") {
|
|
565
|
+
summary.directives.push(text2);
|
|
202
566
|
}
|
|
203
567
|
continue;
|
|
204
568
|
}
|
|
@@ -298,23 +662,344 @@ function summarizeTypeScriptSource(relativePath, content) {
|
|
|
298
662
|
stack.push(children[index]);
|
|
299
663
|
}
|
|
300
664
|
}
|
|
301
|
-
const
|
|
302
|
-
summary.directives =
|
|
303
|
-
summary.imports =
|
|
304
|
-
summary.exports =
|
|
305
|
-
summary.reExports =
|
|
306
|
-
summary.functions =
|
|
307
|
-
summary.classes =
|
|
308
|
-
summary.interfaces =
|
|
309
|
-
summary.types =
|
|
310
|
-
summary.enums =
|
|
311
|
-
summary.variables =
|
|
312
|
-
summary.reactComponents =
|
|
313
|
-
summary.hooks =
|
|
314
|
-
summary.routeHandlers =
|
|
665
|
+
const dedupeSorted2 = (values) => Array.from(new Set(values)).sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
666
|
+
summary.directives = dedupeSorted2(summary.directives);
|
|
667
|
+
summary.imports = dedupeSorted2(summary.imports);
|
|
668
|
+
summary.exports = dedupeSorted2(summary.exports);
|
|
669
|
+
summary.reExports = dedupeSorted2(summary.reExports);
|
|
670
|
+
summary.functions = dedupeSorted2(summary.functions);
|
|
671
|
+
summary.classes = dedupeSorted2(summary.classes);
|
|
672
|
+
summary.interfaces = dedupeSorted2(summary.interfaces);
|
|
673
|
+
summary.types = dedupeSorted2(summary.types);
|
|
674
|
+
summary.enums = dedupeSorted2(summary.enums);
|
|
675
|
+
summary.variables = dedupeSorted2(summary.variables);
|
|
676
|
+
summary.reactComponents = dedupeSorted2(summary.reactComponents);
|
|
677
|
+
summary.hooks = dedupeSorted2(summary.hooks);
|
|
678
|
+
summary.routeHandlers = dedupeSorted2(summary.routeHandlers);
|
|
315
679
|
return summary;
|
|
316
680
|
}
|
|
317
681
|
|
|
682
|
+
// src/ast/profiles/sfc.ts
|
|
683
|
+
var SCRIPT_BLOCK = /<script[^>]*>([\s\S]*?)<\/script>/gi;
|
|
684
|
+
var ASTRO_FRONTMATTER = /^\uFEFF?---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?=\r?\n|$)/;
|
|
685
|
+
function componentName(path11) {
|
|
686
|
+
const base = path11.split("/").pop() ?? path11;
|
|
687
|
+
return base.replace(/\.[^.]+$/, "");
|
|
688
|
+
}
|
|
689
|
+
var sfcProfile = {
|
|
690
|
+
id: "sfc",
|
|
691
|
+
extensions: [".vue", ".svelte", ".astro"],
|
|
692
|
+
summarize(path11, content) {
|
|
693
|
+
let script = "";
|
|
694
|
+
if (path11.toLowerCase().endsWith(".astro")) {
|
|
695
|
+
const frontmatter = ASTRO_FRONTMATTER.exec(content);
|
|
696
|
+
if (frontmatter) script += `${frontmatter[1]}
|
|
697
|
+
`;
|
|
698
|
+
}
|
|
699
|
+
for (const match of content.matchAll(SCRIPT_BLOCK)) {
|
|
700
|
+
script += `${match[1]}
|
|
701
|
+
`;
|
|
702
|
+
}
|
|
703
|
+
const name = componentName(path11);
|
|
704
|
+
if (!script.trim()) {
|
|
705
|
+
const summary2 = emptySummary();
|
|
706
|
+
if (name) {
|
|
707
|
+
summary2.exports.push(name);
|
|
708
|
+
summary2.reactComponents.push(name);
|
|
709
|
+
}
|
|
710
|
+
return { summary: finalizeSummary(summary2), quality: "heuristic" };
|
|
711
|
+
}
|
|
712
|
+
const summary = summarizeTypeScriptSource(`${name}.ts`, script);
|
|
713
|
+
summary.reactComponents.push(name);
|
|
714
|
+
summary.exports.push(name);
|
|
715
|
+
return { summary, quality: "ast" };
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
// src/ast/profiles/index.ts
|
|
720
|
+
var LANGUAGE_PROFILES = [
|
|
721
|
+
pythonProfile,
|
|
722
|
+
goProfile,
|
|
723
|
+
rustProfile,
|
|
724
|
+
jvmProfile,
|
|
725
|
+
dotnetProfile,
|
|
726
|
+
phpProfile,
|
|
727
|
+
rubyProfile,
|
|
728
|
+
cfamilyProfile,
|
|
729
|
+
sfcProfile
|
|
730
|
+
];
|
|
731
|
+
var BY_EXTENSION = /* @__PURE__ */ new Map();
|
|
732
|
+
for (const profile of LANGUAGE_PROFILES) {
|
|
733
|
+
for (const extension of profile.extensions) {
|
|
734
|
+
BY_EXTENSION.set(extension.toLowerCase(), profile);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
function profileForPath(path11) {
|
|
738
|
+
const lower = path11.toLowerCase();
|
|
739
|
+
const dot = lower.lastIndexOf(".");
|
|
740
|
+
if (dot === -1) return null;
|
|
741
|
+
return BY_EXTENSION.get(lower.slice(dot)) ?? null;
|
|
742
|
+
}
|
|
743
|
+
function summarizeWithProfile(path11, content) {
|
|
744
|
+
const profile = profileForPath(path11);
|
|
745
|
+
return profile ? profile.summarize(path11, content) : null;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// src/ast/structured.ts
|
|
749
|
+
function stripJsonComments(content) {
|
|
750
|
+
const text2 = content.replace(/^\uFEFF/, "");
|
|
751
|
+
let out = "";
|
|
752
|
+
let inString = false;
|
|
753
|
+
let inLineComment = false;
|
|
754
|
+
let inBlockComment = false;
|
|
755
|
+
for (let i = 0; i < text2.length; i++) {
|
|
756
|
+
const char = text2[i];
|
|
757
|
+
const next = text2[i + 1];
|
|
758
|
+
if (inLineComment) {
|
|
759
|
+
if (char === "\n") {
|
|
760
|
+
inLineComment = false;
|
|
761
|
+
out += char;
|
|
762
|
+
}
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (inBlockComment) {
|
|
766
|
+
if (char === "*" && next === "/") {
|
|
767
|
+
inBlockComment = false;
|
|
768
|
+
i += 1;
|
|
769
|
+
}
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (inString) {
|
|
773
|
+
out += char;
|
|
774
|
+
if (char === "\\") {
|
|
775
|
+
if (next !== void 0) {
|
|
776
|
+
out += next;
|
|
777
|
+
i += 1;
|
|
778
|
+
}
|
|
779
|
+
} else if (char === '"') {
|
|
780
|
+
inString = false;
|
|
781
|
+
}
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
if (char === '"') {
|
|
785
|
+
inString = true;
|
|
786
|
+
out += char;
|
|
787
|
+
continue;
|
|
788
|
+
}
|
|
789
|
+
if (char === "/" && next === "/") {
|
|
790
|
+
inLineComment = true;
|
|
791
|
+
i += 1;
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
if (char === "/" && next === "*") {
|
|
795
|
+
inBlockComment = true;
|
|
796
|
+
i += 1;
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
out += char;
|
|
800
|
+
}
|
|
801
|
+
return out;
|
|
802
|
+
}
|
|
803
|
+
function summarizeJson(content) {
|
|
804
|
+
try {
|
|
805
|
+
const parsed = JSON.parse(stripJsonComments(content));
|
|
806
|
+
if (Array.isArray(parsed)) {
|
|
807
|
+
const sample = parsed[0];
|
|
808
|
+
const sampleKeys = sample && typeof sample === "object" && !Array.isArray(sample) ? Object.keys(sample).slice(0, 10).sort() : [];
|
|
809
|
+
return {
|
|
810
|
+
shape: "array",
|
|
811
|
+
itemCount: parsed.length,
|
|
812
|
+
keys: [],
|
|
813
|
+
sampleKeys
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
if (parsed && typeof parsed === "object") {
|
|
817
|
+
const keys = Object.keys(parsed).sort();
|
|
818
|
+
return {
|
|
819
|
+
shape: "object",
|
|
820
|
+
itemCount: keys.length,
|
|
821
|
+
keys: keys.slice(0, 30),
|
|
822
|
+
sampleKeys: []
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
return {
|
|
826
|
+
shape: typeof parsed,
|
|
827
|
+
itemCount: null,
|
|
828
|
+
keys: [],
|
|
829
|
+
sampleKeys: []
|
|
830
|
+
};
|
|
831
|
+
} catch {
|
|
832
|
+
return {
|
|
833
|
+
shape: "invalid-json",
|
|
834
|
+
itemCount: null,
|
|
835
|
+
keys: [],
|
|
836
|
+
sampleKeys: []
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
function summarizeMarkdown(content) {
|
|
841
|
+
const headings = [];
|
|
842
|
+
const frontmatterKeys = [];
|
|
843
|
+
const lines = content.replace(/^\uFEFF/, "").split("\n");
|
|
844
|
+
let inFrontmatter = false;
|
|
845
|
+
for (let index = 0; index < lines.length; index++) {
|
|
846
|
+
const line = lines[index];
|
|
847
|
+
if (index === 0 && line.trim() === "---") {
|
|
848
|
+
inFrontmatter = true;
|
|
849
|
+
continue;
|
|
850
|
+
}
|
|
851
|
+
if (inFrontmatter) {
|
|
852
|
+
if (line.trim() === "---") {
|
|
853
|
+
inFrontmatter = false;
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
const match = /^([a-zA-Z0-9_-]+):/.exec(line);
|
|
857
|
+
if (match) frontmatterKeys.push(match[1]);
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
const headingMatch = /^(#{1,6})\s+(.+)$/.exec(line.trim());
|
|
861
|
+
if (headingMatch) {
|
|
862
|
+
headings.push(`${headingMatch[1]} ${headingMatch[2].trim()}`);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
headings: headings.slice(0, 40),
|
|
867
|
+
frontmatterKeys: Array.from(new Set(frontmatterKeys)).sort()
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
var MANIFEST_FILES = [
|
|
871
|
+
"package.json",
|
|
872
|
+
"pyproject.toml",
|
|
873
|
+
"requirements.txt",
|
|
874
|
+
"setup.py",
|
|
875
|
+
"go.mod",
|
|
876
|
+
"Cargo.toml",
|
|
877
|
+
"pom.xml",
|
|
878
|
+
"build.gradle",
|
|
879
|
+
"build.gradle.kts",
|
|
880
|
+
"composer.json",
|
|
881
|
+
"Gemfile"
|
|
882
|
+
];
|
|
883
|
+
var MANIFEST_MAX_BYTES = 1e5;
|
|
884
|
+
function readManifest(cwd, name) {
|
|
885
|
+
const full = path10__namespace.join(cwd, name);
|
|
886
|
+
try {
|
|
887
|
+
const stats = fs9__namespace.statSync(full);
|
|
888
|
+
if (!stats.isFile() || stats.size > MANIFEST_MAX_BYTES) return null;
|
|
889
|
+
return fs9__namespace.readFileSync(full, "utf8");
|
|
890
|
+
} catch {
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
function collectDependencies(manifests) {
|
|
895
|
+
const names = /* @__PURE__ */ new Set();
|
|
896
|
+
const raw = manifests["package.json"];
|
|
897
|
+
if (!raw) return names;
|
|
898
|
+
try {
|
|
899
|
+
const parsed = JSON.parse(raw);
|
|
900
|
+
for (const section of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
|
|
901
|
+
const block = parsed[section];
|
|
902
|
+
if (block && typeof block === "object") {
|
|
903
|
+
for (const name of Object.keys(block)) names.add(name);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
} catch {
|
|
907
|
+
}
|
|
908
|
+
return names;
|
|
909
|
+
}
|
|
910
|
+
function buildPresetContext(cwd) {
|
|
911
|
+
const files = /* @__PURE__ */ new Set();
|
|
912
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
913
|
+
try {
|
|
914
|
+
for (const entry of fs9__namespace.readdirSync(cwd, { withFileTypes: true })) {
|
|
915
|
+
if (entry.isDirectory()) dirs.add(entry.name);
|
|
916
|
+
else if (entry.isFile()) files.add(entry.name);
|
|
917
|
+
}
|
|
918
|
+
} catch {
|
|
919
|
+
}
|
|
920
|
+
const manifests = {};
|
|
921
|
+
for (const name of MANIFEST_FILES) {
|
|
922
|
+
const content = readManifest(cwd, name);
|
|
923
|
+
if (content !== null) manifests[name] = content;
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
cwd,
|
|
927
|
+
files,
|
|
928
|
+
dirs,
|
|
929
|
+
dependencies: collectDependencies(manifests),
|
|
930
|
+
manifests
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
function hasFile(context, name) {
|
|
934
|
+
return context.files.has(name);
|
|
935
|
+
}
|
|
936
|
+
function hasFileStartingWith(context, prefix) {
|
|
937
|
+
for (const file of context.files) {
|
|
938
|
+
if (file.startsWith(prefix)) return true;
|
|
939
|
+
}
|
|
940
|
+
return false;
|
|
941
|
+
}
|
|
942
|
+
function manifestContains(context, manifest, needle) {
|
|
943
|
+
const content = context.manifests[manifest];
|
|
944
|
+
if (!content) return false;
|
|
945
|
+
return content.toLowerCase().includes(needle.toLowerCase());
|
|
946
|
+
}
|
|
947
|
+
function hasDependency(context, ...names) {
|
|
948
|
+
return names.some((name) => context.dependencies.has(name));
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/presets/helpers.ts
|
|
952
|
+
function layer(name, ...prefixes) {
|
|
953
|
+
return {
|
|
954
|
+
name,
|
|
955
|
+
match: (filePath) => prefixes.some((prefix) => filePath.startsWith(prefix))
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
function kindOf(name, ...needles) {
|
|
959
|
+
return {
|
|
960
|
+
kind: name,
|
|
961
|
+
match: (filePath) => needles.some((needle) => filePath.includes(needle))
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
function suffixKind(name, ...suffixes) {
|
|
965
|
+
return {
|
|
966
|
+
kind: name,
|
|
967
|
+
match: (filePath) => suffixes.some((suffix) => filePath.endsWith(suffix))
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
function commonLayers() {
|
|
971
|
+
return [
|
|
972
|
+
layer("scripts", "scripts/", "tools/"),
|
|
973
|
+
layer("docs", "docs/", "documentation/"),
|
|
974
|
+
{
|
|
975
|
+
name: "config",
|
|
976
|
+
match: (filePath) => filePath.startsWith(".github/") || filePath.startsWith(".husky/") || filePath.startsWith(".vscode/") || filePath.startsWith(".") || filePath.includes("config")
|
|
977
|
+
},
|
|
978
|
+
{ name: "root", match: (filePath) => !filePath.includes("/") }
|
|
979
|
+
];
|
|
980
|
+
}
|
|
981
|
+
function commonKinds() {
|
|
982
|
+
return [
|
|
983
|
+
{
|
|
984
|
+
kind: "test",
|
|
985
|
+
match: (filePath) => {
|
|
986
|
+
const lower = filePath.toLowerCase();
|
|
987
|
+
return lower.includes(".test.") || lower.includes(".spec.") || lower.startsWith("test/") || lower.startsWith("tests/") || lower.includes("_test.") || lower.endsWith("_test.go") || lower.includes("__tests__");
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
suffixKind("doc", ".md", ".mdx", ".rst", ".txt"),
|
|
991
|
+
suffixKind("data", ".json", ".yaml", ".yml", ".csv", ".toml"),
|
|
992
|
+
{
|
|
993
|
+
kind: "script",
|
|
994
|
+
match: (filePath) => filePath.startsWith("scripts/") || /\.(sh|ps1|bat)$/i.test(filePath)
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
kind: "config",
|
|
998
|
+
match: (filePath) => filePath.startsWith(".") || filePath.includes("config") || filePath === "package.json" || filePath === "tsconfig.json"
|
|
999
|
+
}
|
|
1000
|
+
];
|
|
1001
|
+
}
|
|
1002
|
+
|
|
318
1003
|
// src/presets/nextjs.ts
|
|
319
1004
|
var NEXTJS_LAYERS = [
|
|
320
1005
|
{ name: "app", match: (p) => p.startsWith("src/app/") || p.startsWith("app/") },
|
|
@@ -370,53 +1055,6 @@ var NEXTJS_KINDS = [
|
|
|
370
1055
|
}
|
|
371
1056
|
];
|
|
372
1057
|
|
|
373
|
-
// src/presets/vite.ts
|
|
374
|
-
var VITE_LAYERS = [
|
|
375
|
-
{ name: "pages", match: (p) => p.startsWith("src/pages/") || p.startsWith("src/views/") },
|
|
376
|
-
{ name: "components", match: (p) => p.startsWith("src/components/") },
|
|
377
|
-
{ name: "hooks", match: (p) => p.startsWith("src/hooks/") },
|
|
378
|
-
{ name: "services", match: (p) => p.startsWith("src/services/") || p.startsWith("src/api/") },
|
|
379
|
-
{ name: "shared", match: (p) => p.startsWith("src/shared/") || p.startsWith("src/common/") },
|
|
380
|
-
{ name: "styles", match: (p) => p.startsWith("src/styles/") || p.startsWith("src/assets/") },
|
|
381
|
-
{ name: "scripts", match: (p) => p.startsWith("scripts/") },
|
|
382
|
-
{ name: "docs", match: (p) => p.startsWith("docs/") },
|
|
383
|
-
{
|
|
384
|
-
name: "config",
|
|
385
|
-
match: (p) => p.startsWith(".github/") || p.startsWith(".husky/") || p.startsWith(".") || p.includes("config")
|
|
386
|
-
},
|
|
387
|
-
{ name: "root", match: (p) => !p.includes("/") }
|
|
388
|
-
];
|
|
389
|
-
var VITE_KINDS = [
|
|
390
|
-
{ kind: "test", match: (p) => /\.(test|spec)\.[jt]sx?$/.test(p.toLowerCase()) },
|
|
391
|
-
{
|
|
392
|
-
kind: "component",
|
|
393
|
-
match: (p) => {
|
|
394
|
-
const base = p.split("/").pop() || "";
|
|
395
|
-
return p.includes("/components/") || base.endsWith(".tsx") || base.endsWith(".jsx") || base.endsWith(".vue");
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
{
|
|
399
|
-
kind: "hook",
|
|
400
|
-
match: (p) => {
|
|
401
|
-
const base = p.split("/").pop() || "";
|
|
402
|
-
return p.includes("/hooks/") || /^use[A-Z]/.test(base);
|
|
403
|
-
}
|
|
404
|
-
},
|
|
405
|
-
{ kind: "service", match: (p) => p.includes("service") || p.includes("api") },
|
|
406
|
-
{ kind: "type", match: (p) => p.endsWith(".d.ts") || p.includes("types") },
|
|
407
|
-
{ kind: "style", match: (p) => p.endsWith(".css") || p.endsWith(".scss") },
|
|
408
|
-
{ kind: "data", match: (p) => p.endsWith(".json") || p.endsWith(".csv") },
|
|
409
|
-
{ kind: "doc", match: (p) => p.endsWith(".md") || p.endsWith(".mdx") },
|
|
410
|
-
{ kind: "script", match: (p) => p.startsWith("scripts/") || p.endsWith(".sh") || p.endsWith(".ps1") },
|
|
411
|
-
{
|
|
412
|
-
kind: "config",
|
|
413
|
-
match: (p) => {
|
|
414
|
-
const base = p.split("/").pop() || "";
|
|
415
|
-
return ["package.json", "tsconfig.json", "vite.config.ts", "vite.config.js"].includes(base) || base.startsWith(".");
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
];
|
|
419
|
-
|
|
420
1058
|
// src/presets/node.ts
|
|
421
1059
|
var NODE_LAYERS = [
|
|
422
1060
|
{ name: "core", match: (p) => p.startsWith("src/core/") || p.startsWith("core/") },
|
|
@@ -453,11 +1091,474 @@ var NODE_KINDS = [
|
|
|
453
1091
|
kind: "config",
|
|
454
1092
|
match: (p) => {
|
|
455
1093
|
const base = p.split("/").pop() || "";
|
|
456
|
-
return ["package.json", "tsconfig.json", "Dockerfile"].includes(base) || base.startsWith(".");
|
|
1094
|
+
return ["package.json", "tsconfig.json", "Dockerfile"].includes(base) || base.startsWith(".");
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
];
|
|
1098
|
+
|
|
1099
|
+
// src/presets/vite.ts
|
|
1100
|
+
var VITE_LAYERS = [
|
|
1101
|
+
{ name: "pages", match: (p) => p.startsWith("src/pages/") || p.startsWith("src/views/") },
|
|
1102
|
+
{ name: "components", match: (p) => p.startsWith("src/components/") },
|
|
1103
|
+
{ name: "hooks", match: (p) => p.startsWith("src/hooks/") },
|
|
1104
|
+
{ name: "services", match: (p) => p.startsWith("src/services/") || p.startsWith("src/api/") },
|
|
1105
|
+
{ name: "shared", match: (p) => p.startsWith("src/shared/") || p.startsWith("src/common/") },
|
|
1106
|
+
{ name: "styles", match: (p) => p.startsWith("src/styles/") || p.startsWith("src/assets/") },
|
|
1107
|
+
{ name: "scripts", match: (p) => p.startsWith("scripts/") },
|
|
1108
|
+
{ name: "docs", match: (p) => p.startsWith("docs/") },
|
|
1109
|
+
{
|
|
1110
|
+
name: "config",
|
|
1111
|
+
match: (p) => p.startsWith(".github/") || p.startsWith(".husky/") || p.startsWith(".") || p.includes("config")
|
|
1112
|
+
},
|
|
1113
|
+
{ name: "root", match: (p) => !p.includes("/") }
|
|
1114
|
+
];
|
|
1115
|
+
var VITE_KINDS = [
|
|
1116
|
+
{ kind: "test", match: (p) => /\.(test|spec)\.[jt]sx?$/.test(p.toLowerCase()) },
|
|
1117
|
+
{
|
|
1118
|
+
kind: "component",
|
|
1119
|
+
match: (p) => {
|
|
1120
|
+
const base = p.split("/").pop() || "";
|
|
1121
|
+
return p.includes("/components/") || base.endsWith(".tsx") || base.endsWith(".jsx") || base.endsWith(".vue");
|
|
1122
|
+
}
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
kind: "hook",
|
|
1126
|
+
match: (p) => {
|
|
1127
|
+
const base = p.split("/").pop() || "";
|
|
1128
|
+
return p.includes("/hooks/") || /^use[A-Z]/.test(base);
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
{ kind: "service", match: (p) => p.includes("service") || p.includes("api") },
|
|
1132
|
+
{ kind: "type", match: (p) => p.endsWith(".d.ts") || p.includes("types") },
|
|
1133
|
+
{ kind: "style", match: (p) => p.endsWith(".css") || p.endsWith(".scss") },
|
|
1134
|
+
{ kind: "data", match: (p) => p.endsWith(".json") || p.endsWith(".csv") },
|
|
1135
|
+
{ kind: "doc", match: (p) => p.endsWith(".md") || p.endsWith(".mdx") },
|
|
1136
|
+
{ kind: "script", match: (p) => p.startsWith("scripts/") || p.endsWith(".sh") || p.endsWith(".ps1") },
|
|
1137
|
+
{
|
|
1138
|
+
kind: "config",
|
|
1139
|
+
match: (p) => {
|
|
1140
|
+
const base = p.split("/").pop() || "";
|
|
1141
|
+
return ["package.json", "tsconfig.json", "vite.config.ts", "vite.config.js"].includes(base) || base.startsWith(".");
|
|
457
1142
|
}
|
|
458
1143
|
}
|
|
459
1144
|
];
|
|
460
1145
|
|
|
1146
|
+
// src/presets/js-presets.ts
|
|
1147
|
+
var NEXT_LIKE_EXCLUDES = [".svelte-kit", ".astro", ".vite", ".output"];
|
|
1148
|
+
var JS_PRESETS = [
|
|
1149
|
+
{
|
|
1150
|
+
name: "nextjs",
|
|
1151
|
+
priority: 100,
|
|
1152
|
+
detect: (context) => hasFileStartingWith(context, "next.config") || hasDependency(context, "next"),
|
|
1153
|
+
layers: NEXTJS_LAYERS,
|
|
1154
|
+
kinds: NEXTJS_KINDS,
|
|
1155
|
+
exclude: NEXT_LIKE_EXCLUDES,
|
|
1156
|
+
recommendedBudget: "300k"
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
name: "nuxt",
|
|
1160
|
+
priority: 98,
|
|
1161
|
+
detect: (context) => hasFileStartingWith(context, "nuxt.config") || hasDependency(context, "nuxt", "nuxt3"),
|
|
1162
|
+
layers: [
|
|
1163
|
+
layer("pages", "pages/", "src/pages/"),
|
|
1164
|
+
layer("components", "components/", "src/components/"),
|
|
1165
|
+
layer("composables", "composables/", "src/composables/"),
|
|
1166
|
+
layer("server", "server/", "src/server/", "api/"),
|
|
1167
|
+
layer("stores", "stores/", "src/stores/"),
|
|
1168
|
+
layer("layouts", "layouts/", "src/layouts/"),
|
|
1169
|
+
...commonLayers()
|
|
1170
|
+
],
|
|
1171
|
+
kinds: [
|
|
1172
|
+
kindOf("api-route", "server/api/", "server/routes/"),
|
|
1173
|
+
suffixKind("component", ".vue"),
|
|
1174
|
+
kindOf("composable", "composables/"),
|
|
1175
|
+
kindOf("store", "stores/", "store/"),
|
|
1176
|
+
kindOf("layout", "layouts/"),
|
|
1177
|
+
...commonKinds()
|
|
1178
|
+
],
|
|
1179
|
+
exclude: [".nuxt", ".output"],
|
|
1180
|
+
recommendedBudget: "200k"
|
|
1181
|
+
},
|
|
1182
|
+
{
|
|
1183
|
+
name: "sveltekit",
|
|
1184
|
+
priority: 96,
|
|
1185
|
+
detect: (context) => hasFileStartingWith(context, "svelte.config") || hasDependency(context, "@sveltejs/kit", "svelte"),
|
|
1186
|
+
layers: [
|
|
1187
|
+
layer("routes", "src/routes/", "routes/"),
|
|
1188
|
+
layer("lib", "src/lib/", "lib/"),
|
|
1189
|
+
layer("components", "src/lib/components/", "src/components/"),
|
|
1190
|
+
layer("stores", "src/lib/stores/", "src/stores/"),
|
|
1191
|
+
...commonLayers()
|
|
1192
|
+
],
|
|
1193
|
+
kinds: [
|
|
1194
|
+
kindOf("route", "+page.", "+server.", "+layout."),
|
|
1195
|
+
suffixKind("component", ".svelte"),
|
|
1196
|
+
kindOf("store", "stores/"),
|
|
1197
|
+
kindOf("params", "params/"),
|
|
1198
|
+
...commonKinds()
|
|
1199
|
+
],
|
|
1200
|
+
exclude: [".svelte-kit", ".vercel"],
|
|
1201
|
+
recommendedBudget: "200k"
|
|
1202
|
+
},
|
|
1203
|
+
{
|
|
1204
|
+
name: "remix",
|
|
1205
|
+
priority: 94,
|
|
1206
|
+
detect: (context) => hasDependency(context, "@remix-run/react", "@remix-run/node", "@remix-run/dev"),
|
|
1207
|
+
layers: [
|
|
1208
|
+
layer("routes", "app/routes/", "routes/"),
|
|
1209
|
+
layer("components", "app/components/"),
|
|
1210
|
+
layer("lib", "app/lib/", "app/utils/"),
|
|
1211
|
+
...commonLayers()
|
|
1212
|
+
],
|
|
1213
|
+
kinds: [kindOf("route", "routes/"), ...commonKinds()],
|
|
1214
|
+
exclude: [".cache", "public/build"],
|
|
1215
|
+
recommendedBudget: "200k"
|
|
1216
|
+
},
|
|
1217
|
+
{
|
|
1218
|
+
name: "astro",
|
|
1219
|
+
priority: 92,
|
|
1220
|
+
detect: (context) => hasFileStartingWith(context, "astro.config") || hasDependency(context, "astro"),
|
|
1221
|
+
layers: [
|
|
1222
|
+
layer("pages", "src/pages/"),
|
|
1223
|
+
layer("layouts", "src/layouts/"),
|
|
1224
|
+
layer("components", "src/components/"),
|
|
1225
|
+
layer("content", "src/content/"),
|
|
1226
|
+
...commonLayers()
|
|
1227
|
+
],
|
|
1228
|
+
kinds: [suffixKind("page", ".astro"), kindOf("layout", "layouts/"), ...commonKinds()],
|
|
1229
|
+
exclude: [".astro", "dist"],
|
|
1230
|
+
recommendedBudget: "150k"
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
name: "angular",
|
|
1234
|
+
priority: 90,
|
|
1235
|
+
detect: (context) => hasFile(context, "angular.json") || hasDependency(context, "@angular/core"),
|
|
1236
|
+
layers: [
|
|
1237
|
+
layer("app", "src/app/"),
|
|
1238
|
+
layer("features", "src/app/features/"),
|
|
1239
|
+
layer("shared", "src/app/shared/"),
|
|
1240
|
+
layer("core", "src/app/core/"),
|
|
1241
|
+
layer("assets", "src/assets/"),
|
|
1242
|
+
...commonLayers()
|
|
1243
|
+
],
|
|
1244
|
+
kinds: [
|
|
1245
|
+
suffixKind("component", ".component.ts"),
|
|
1246
|
+
suffixKind("service", ".service.ts"),
|
|
1247
|
+
suffixKind("module", ".module.ts"),
|
|
1248
|
+
suffixKind("guard", ".guard.ts"),
|
|
1249
|
+
suffixKind("pipe", ".pipe.ts"),
|
|
1250
|
+
suffixKind("directive", ".directive.ts"),
|
|
1251
|
+
...commonKinds()
|
|
1252
|
+
],
|
|
1253
|
+
exclude: [".angular", "dist"],
|
|
1254
|
+
recommendedBudget: "200k"
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
name: "solid",
|
|
1258
|
+
priority: 88,
|
|
1259
|
+
detect: (context) => hasDependency(context, "solid-js", "solid-start"),
|
|
1260
|
+
layers: [
|
|
1261
|
+
layer("pages", "src/routes/", "src/pages/"),
|
|
1262
|
+
layer("components", "src/components/"),
|
|
1263
|
+
...commonLayers()
|
|
1264
|
+
],
|
|
1265
|
+
kinds: [kindOf("route", "routes/"), ...commonKinds()],
|
|
1266
|
+
exclude: [".solid", "dist"],
|
|
1267
|
+
recommendedBudget: "120k"
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
name: "vue-vite",
|
|
1271
|
+
priority: 86,
|
|
1272
|
+
detect: (context) => hasDependency(context, "vue") && (hasDependency(context, "vite") || hasFileStartingWith(context, "vite.config")),
|
|
1273
|
+
layers: [
|
|
1274
|
+
layer("pages", "src/pages/", "src/views/"),
|
|
1275
|
+
layer("components", "src/components/"),
|
|
1276
|
+
layer("composables", "src/composables/", "src/hooks/"),
|
|
1277
|
+
layer("stores", "src/stores/", "src/store/"),
|
|
1278
|
+
layer("services", "src/services/", "src/api/"),
|
|
1279
|
+
layer("shared", "src/shared/", "src/common/"),
|
|
1280
|
+
layer("styles", "src/styles/", "src/assets/"),
|
|
1281
|
+
...commonLayers()
|
|
1282
|
+
],
|
|
1283
|
+
kinds: [
|
|
1284
|
+
suffixKind("component", ".vue"),
|
|
1285
|
+
kindOf("composable", "composables/", "use"),
|
|
1286
|
+
kindOf("store", "stores/", "store/"),
|
|
1287
|
+
kindOf("service", "services/", "api/"),
|
|
1288
|
+
suffixKind("style", ".css", ".scss", ".sass", ".less"),
|
|
1289
|
+
...commonKinds()
|
|
1290
|
+
],
|
|
1291
|
+
exclude: [".vite", "dist"],
|
|
1292
|
+
recommendedBudget: "120k"
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
name: "react-vite",
|
|
1296
|
+
priority: 84,
|
|
1297
|
+
detect: (context) => hasDependency(context, "react") && (hasDependency(context, "vite") || hasFileStartingWith(context, "vite.config")),
|
|
1298
|
+
layers: VITE_LAYERS,
|
|
1299
|
+
kinds: VITE_KINDS,
|
|
1300
|
+
exclude: [".vite", "dist"],
|
|
1301
|
+
recommendedBudget: "120k"
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
name: "nest",
|
|
1305
|
+
priority: 80,
|
|
1306
|
+
detect: (context) => hasDependency(context, "@nestjs/core"),
|
|
1307
|
+
layers: [
|
|
1308
|
+
layer("modules", "src/modules/", "modules/"),
|
|
1309
|
+
layer("controllers", "src/controllers/", "controllers/"),
|
|
1310
|
+
layer("providers", "src/providers/", "providers/"),
|
|
1311
|
+
layer("core", "src/core/"),
|
|
1312
|
+
layer("security", "src/security/", "src/auth/"),
|
|
1313
|
+
layer("db", "src/db/", "src/database/", "prisma/"),
|
|
1314
|
+
...commonLayers()
|
|
1315
|
+
],
|
|
1316
|
+
kinds: [
|
|
1317
|
+
suffixKind("controller", ".controller.ts"),
|
|
1318
|
+
suffixKind("service", ".service.ts"),
|
|
1319
|
+
suffixKind("module", ".module.ts"),
|
|
1320
|
+
suffixKind("guard", ".guard.ts"),
|
|
1321
|
+
suffixKind("dto", ".dto.ts"),
|
|
1322
|
+
suffixKind("entity", ".entity.ts"),
|
|
1323
|
+
kindOf("migration", "migrations/"),
|
|
1324
|
+
...commonKinds()
|
|
1325
|
+
],
|
|
1326
|
+
exclude: ["dist", "node_modules"],
|
|
1327
|
+
recommendedBudget: "200k"
|
|
1328
|
+
},
|
|
1329
|
+
{
|
|
1330
|
+
name: "node-express",
|
|
1331
|
+
priority: 78,
|
|
1332
|
+
detect: (context) => hasDependency(context, "express", "fastify", "koa", "@hapi/hapi"),
|
|
1333
|
+
layers: NODE_LAYERS,
|
|
1334
|
+
kinds: NODE_KINDS,
|
|
1335
|
+
exclude: ["dist"],
|
|
1336
|
+
recommendedBudget: "200k"
|
|
1337
|
+
},
|
|
1338
|
+
{
|
|
1339
|
+
name: "node-lib",
|
|
1340
|
+
// A bare package.json is often just tooling around a non-JS project, so it
|
|
1341
|
+
// ranks below every definitive language manifest.
|
|
1342
|
+
priority: 45,
|
|
1343
|
+
detect: (context) => hasFile(context, "package.json"),
|
|
1344
|
+
layers: [
|
|
1345
|
+
layer("src", "src/"),
|
|
1346
|
+
layer("lib", "lib/"),
|
|
1347
|
+
layer("test", "test/", "tests/", "__tests__/"),
|
|
1348
|
+
...commonLayers()
|
|
1349
|
+
],
|
|
1350
|
+
kinds: [suffixKind("type", ".d.ts"), ...commonKinds()],
|
|
1351
|
+
exclude: ["dist"],
|
|
1352
|
+
recommendedBudget: "120k"
|
|
1353
|
+
}
|
|
1354
|
+
];
|
|
1355
|
+
|
|
1356
|
+
// src/presets/lang-presets.ts
|
|
1357
|
+
var anyFileEndingWith = (context, suffix) => Array.from(context.files).some((file) => file.endsWith(suffix));
|
|
1358
|
+
var DIR = (name) => `${name}/`;
|
|
1359
|
+
var LANGUAGE_PRESETS = [
|
|
1360
|
+
{
|
|
1361
|
+
name: "python-django",
|
|
1362
|
+
priority: 56,
|
|
1363
|
+
detect: (context) => hasFile(context, "manage.py") || manifestContains(context, "pyproject.toml", "django") || manifestContains(context, "requirements.txt", "django"),
|
|
1364
|
+
layers: [
|
|
1365
|
+
layer("views", DIR("views"), DIR("app/views"), DIR("apps/views")),
|
|
1366
|
+
layer("models", DIR("models"), DIR("app/models"), DIR("apps/models")),
|
|
1367
|
+
layer("serializers", DIR("serializers")),
|
|
1368
|
+
layer("urls", "urls.py"),
|
|
1369
|
+
layer("migrations", DIR("migrations")),
|
|
1370
|
+
layer("settings", "settings.py", "settings/"),
|
|
1371
|
+
layer("templates", DIR("templates")),
|
|
1372
|
+
...commonLayers()
|
|
1373
|
+
],
|
|
1374
|
+
kinds: [
|
|
1375
|
+
suffixKind("model", "models.py"),
|
|
1376
|
+
suffixKind("view", "views.py"),
|
|
1377
|
+
suffixKind("serializer", "serializers.py"),
|
|
1378
|
+
suffixKind("urlconf", "urls.py"),
|
|
1379
|
+
kindOf("migration", "migrations/"),
|
|
1380
|
+
suffixKind("settings", "settings.py"),
|
|
1381
|
+
...commonKinds()
|
|
1382
|
+
],
|
|
1383
|
+
exclude: ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache", ".tox", ".eggs", "staticfiles", "media"],
|
|
1384
|
+
recommendedBudget: "150k"
|
|
1385
|
+
},
|
|
1386
|
+
{
|
|
1387
|
+
name: "python-fastapi",
|
|
1388
|
+
priority: 54,
|
|
1389
|
+
detect: (context) => manifestContains(context, "requirements.txt", "fastapi") || manifestContains(context, "requirements.txt", "flask") || manifestContains(context, "pyproject.toml", "fastapi") || manifestContains(context, "pyproject.toml", "flask"),
|
|
1390
|
+
layers: [
|
|
1391
|
+
layer("routers", DIR("routers"), DIR("routes"), DIR("api")),
|
|
1392
|
+
layer("models", DIR("models"), DIR("db")),
|
|
1393
|
+
layer("schemas", DIR("schemas")),
|
|
1394
|
+
layer("services", DIR("services")),
|
|
1395
|
+
layer("core", DIR("core")),
|
|
1396
|
+
layer("tests", DIR("tests")),
|
|
1397
|
+
...commonLayers()
|
|
1398
|
+
],
|
|
1399
|
+
kinds: [
|
|
1400
|
+
kindOf("router", "routers/", "routes/", "api/"),
|
|
1401
|
+
kindOf("model", "models/"),
|
|
1402
|
+
kindOf("schema", "schemas/"),
|
|
1403
|
+
kindOf("service", "services/"),
|
|
1404
|
+
...commonKinds()
|
|
1405
|
+
],
|
|
1406
|
+
exclude: ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache", ".tox", ".eggs"],
|
|
1407
|
+
recommendedBudget: "150k"
|
|
1408
|
+
},
|
|
1409
|
+
{
|
|
1410
|
+
name: "python-lib",
|
|
1411
|
+
// Below node-lib: a Node repo with a stray .py file is still a Node repo.
|
|
1412
|
+
priority: 30,
|
|
1413
|
+
detect: (context) => hasFile(context, "pyproject.toml") || hasFile(context, "setup.py") || hasFile(context, "requirements.txt") || anyFileEndingWith(context, ".py"),
|
|
1414
|
+
layers: [
|
|
1415
|
+
layer("package", DIR("src"), DIR("lib")),
|
|
1416
|
+
layer("tests", DIR("tests"), DIR("test")),
|
|
1417
|
+
...commonLayers()
|
|
1418
|
+
],
|
|
1419
|
+
kinds: [suffixKind("module", ".py"), ...commonKinds()],
|
|
1420
|
+
exclude: ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache", ".tox", ".eggs"],
|
|
1421
|
+
recommendedBudget: "120k"
|
|
1422
|
+
},
|
|
1423
|
+
{
|
|
1424
|
+
name: "go",
|
|
1425
|
+
priority: 50,
|
|
1426
|
+
detect: (context) => hasFile(context, "go.mod"),
|
|
1427
|
+
layers: [
|
|
1428
|
+
layer("cmd", "cmd/"),
|
|
1429
|
+
layer("internal", "internal/"),
|
|
1430
|
+
layer("pkg", "pkg/"),
|
|
1431
|
+
layer("api", "api/", "proto/"),
|
|
1432
|
+
layer("migrations", "migrations/"),
|
|
1433
|
+
...commonLayers()
|
|
1434
|
+
],
|
|
1435
|
+
kinds: [
|
|
1436
|
+
kindOf("main", "cmd/"),
|
|
1437
|
+
kindOf("handler", "handler", "server", "http"),
|
|
1438
|
+
kindOf("service", "service"),
|
|
1439
|
+
kindOf("repository", "repository", "store", "repo"),
|
|
1440
|
+
kindOf("model", "model", "entity"),
|
|
1441
|
+
suffixKind("test", "_test.go"),
|
|
1442
|
+
...commonKinds()
|
|
1443
|
+
],
|
|
1444
|
+
exclude: ["vendor", "testdata"],
|
|
1445
|
+
recommendedBudget: "200k"
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
name: "rust",
|
|
1449
|
+
priority: 48,
|
|
1450
|
+
detect: (context) => hasFile(context, "Cargo.toml"),
|
|
1451
|
+
layers: [
|
|
1452
|
+
layer("src", "src/"),
|
|
1453
|
+
layer("crates", "crates/"),
|
|
1454
|
+
layer("bins", "src/bin/"),
|
|
1455
|
+
layer("tests", "tests/"),
|
|
1456
|
+
layer("benches", "benches/"),
|
|
1457
|
+
...commonLayers()
|
|
1458
|
+
],
|
|
1459
|
+
kinds: [suffixKind("module", ".rs"), suffixKind("test", "_test.rs"), ...commonKinds()],
|
|
1460
|
+
exclude: ["target"],
|
|
1461
|
+
recommendedBudget: "200k"
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
name: "java-spring",
|
|
1465
|
+
priority: 46,
|
|
1466
|
+
detect: (context) => hasFile(context, "pom.xml") || hasFile(context, "build.gradle") || hasFile(context, "build.gradle.kts"),
|
|
1467
|
+
layers: [
|
|
1468
|
+
layer("api", "src/main/java/"),
|
|
1469
|
+
layer("test", "src/test/java/"),
|
|
1470
|
+
layer("resources", "src/main/resources/"),
|
|
1471
|
+
layer("migrations", "src/main/resources/db/"),
|
|
1472
|
+
...commonLayers()
|
|
1473
|
+
],
|
|
1474
|
+
kinds: [
|
|
1475
|
+
kindOf("controller", "controller"),
|
|
1476
|
+
kindOf("service", "service"),
|
|
1477
|
+
kindOf("repository", "repository", "dao"),
|
|
1478
|
+
kindOf("entity", "entity", "model"),
|
|
1479
|
+
kindOf("dto", "dto", "request", "response"),
|
|
1480
|
+
kindOf("migration", "migration", "db/migration"),
|
|
1481
|
+
...commonKinds()
|
|
1482
|
+
],
|
|
1483
|
+
exclude: [".gradle", "out", "target"],
|
|
1484
|
+
recommendedBudget: "200k"
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
name: "dotnet",
|
|
1488
|
+
priority: 44,
|
|
1489
|
+
detect: (context) => anyFileEndingWith(context, ".csproj") || anyFileEndingWith(context, ".sln") || hasFile(context, "global.json"),
|
|
1490
|
+
layers: [
|
|
1491
|
+
layer("web", "src/", "Web/"),
|
|
1492
|
+
layer("domain", "Domain/", "Core/"),
|
|
1493
|
+
layer("infrastructure", "Infrastructure/", "Data/"),
|
|
1494
|
+
layer("tests", "Tests/", "test/"),
|
|
1495
|
+
...commonLayers()
|
|
1496
|
+
],
|
|
1497
|
+
kinds: [
|
|
1498
|
+
kindOf("controller", "Controller"),
|
|
1499
|
+
kindOf("service", "Service"),
|
|
1500
|
+
kindOf("repository", "Repository"),
|
|
1501
|
+
kindOf("model", "Model", "Entity"),
|
|
1502
|
+
kindOf("dto", "Dto", "Request", "Response"),
|
|
1503
|
+
suffixKind("test", "Tests.cs"),
|
|
1504
|
+
...commonKinds()
|
|
1505
|
+
],
|
|
1506
|
+
exclude: ["bin", "obj", ".vs"],
|
|
1507
|
+
recommendedBudget: "200k"
|
|
1508
|
+
},
|
|
1509
|
+
{
|
|
1510
|
+
name: "php-laravel",
|
|
1511
|
+
priority: 42,
|
|
1512
|
+
detect: (context) => hasFile(context, "artisan") || manifestContains(context, "composer.json", "laravel") || hasFile(context, "composer.json"),
|
|
1513
|
+
layers: [
|
|
1514
|
+
layer("app", "app/"),
|
|
1515
|
+
layer("routes", "routes/"),
|
|
1516
|
+
layer("http", "app/Http/"),
|
|
1517
|
+
layer("models", "app/Models/"),
|
|
1518
|
+
layer("providers", "app/Providers/"),
|
|
1519
|
+
layer("database", "database/"),
|
|
1520
|
+
layer("tests", "tests/"),
|
|
1521
|
+
...commonLayers()
|
|
1522
|
+
],
|
|
1523
|
+
kinds: [
|
|
1524
|
+
kindOf("controller", "Http/Controllers/"),
|
|
1525
|
+
kindOf("model", "Models/"),
|
|
1526
|
+
kindOf("migration", "migrations/"),
|
|
1527
|
+
kindOf("middleware", "Middleware/"),
|
|
1528
|
+
kindOf("request", "Requests/"),
|
|
1529
|
+
kindOf("provider", "Providers/"),
|
|
1530
|
+
...commonKinds()
|
|
1531
|
+
],
|
|
1532
|
+
exclude: ["vendor", "storage", "bootstrap/cache", "public/build"],
|
|
1533
|
+
recommendedBudget: "150k"
|
|
1534
|
+
},
|
|
1535
|
+
{
|
|
1536
|
+
name: "ruby-rails",
|
|
1537
|
+
priority: 40,
|
|
1538
|
+
detect: (context) => hasFile(context, "Gemfile") || hasFile(context, "config.ru") || hasFile(context, "Rakefile"),
|
|
1539
|
+
layers: [
|
|
1540
|
+
layer("app", "app/"),
|
|
1541
|
+
layer("controllers", "app/controllers/"),
|
|
1542
|
+
layer("models", "app/models/"),
|
|
1543
|
+
layer("config", "config/"),
|
|
1544
|
+
layer("db", "db/"),
|
|
1545
|
+
layer("spec", "spec/", "test/"),
|
|
1546
|
+
...commonLayers()
|
|
1547
|
+
],
|
|
1548
|
+
kinds: [
|
|
1549
|
+
kindOf("controller", "controllers/"),
|
|
1550
|
+
kindOf("model", "models/"),
|
|
1551
|
+
kindOf("migration", "migrate/"),
|
|
1552
|
+
kindOf("job", "jobs/"),
|
|
1553
|
+
kindOf("mailer", "mailers/"),
|
|
1554
|
+
suffixKind("test", "_spec.rb", "_test.rb"),
|
|
1555
|
+
...commonKinds()
|
|
1556
|
+
],
|
|
1557
|
+
exclude: [".bundle", "log", "tmp", "vendor/bundle", "public/assets"],
|
|
1558
|
+
recommendedBudget: "150k"
|
|
1559
|
+
}
|
|
1560
|
+
];
|
|
1561
|
+
|
|
461
1562
|
// src/presets/default.ts
|
|
462
1563
|
var DEFAULT_LAYERS = [
|
|
463
1564
|
{ name: "src", match: (p) => p.startsWith("src/") },
|
|
@@ -500,50 +1601,110 @@ var DEFAULT_KINDS = [
|
|
|
500
1601
|
}
|
|
501
1602
|
];
|
|
502
1603
|
|
|
503
|
-
// src/presets/
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
1604
|
+
// src/presets/shape-presets.ts
|
|
1605
|
+
var SHAPE_PRESETS = [
|
|
1606
|
+
{
|
|
1607
|
+
name: "monorepo",
|
|
1608
|
+
priority: 70,
|
|
1609
|
+
detect: (context) => hasFile(context, "pnpm-workspace.yaml") || hasFile(context, "nx.json") || hasFile(context, "turbo.json") || hasFile(context, "lerna.json") || manifestContains(context, "package.json", '"workspaces"'),
|
|
1610
|
+
layers: [
|
|
1611
|
+
layer("apps", "apps/"),
|
|
1612
|
+
layer("packages", "packages/"),
|
|
1613
|
+
layer("tooling", "tooling/", "tools/", "scripts/"),
|
|
1614
|
+
...commonLayers()
|
|
1615
|
+
],
|
|
1616
|
+
kinds: commonKinds(),
|
|
1617
|
+
exclude: [".turbo", ".nx"],
|
|
1618
|
+
recommendedBudget: "200k"
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
name: "infra",
|
|
1622
|
+
priority: 24,
|
|
1623
|
+
detect: (context) => Array.from(context.files).some((file) => file.endsWith(".tf")) || hasFile(context, "Chart.yaml") || hasFile(context, "kustomization.yaml") || hasFile(context, "docker-compose.yml") || hasFile(context, "docker-compose.yaml"),
|
|
1624
|
+
layers: [
|
|
1625
|
+
layer("modules", "modules/"),
|
|
1626
|
+
layer("environments", "env/", "environments/"),
|
|
1627
|
+
layer("manifests", "k8s/", "helm/", "charts/", "deploy/"),
|
|
1628
|
+
...commonLayers()
|
|
1629
|
+
],
|
|
1630
|
+
kinds: [
|
|
1631
|
+
{ kind: "terraform", match: (filePath) => filePath.endsWith(".tf") },
|
|
1632
|
+
{ kind: "kubernetes", match: (filePath) => /\.(ya?ml)$/i.test(filePath) && /k8s|helm|charts|deploy/.test(filePath) },
|
|
1633
|
+
...commonKinds()
|
|
1634
|
+
],
|
|
1635
|
+
exclude: [".terraform", ".terragrunt-cache"],
|
|
1636
|
+
recommendedBudget: "100k"
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
name: "docs",
|
|
1640
|
+
priority: 22,
|
|
1641
|
+
detect: (context) => {
|
|
1642
|
+
const markdown = Array.from(context.files).filter((file) => /\.mdx?$/i.test(file)).length;
|
|
1643
|
+
return markdown >= 3 && !hasFile(context, "package.json");
|
|
1644
|
+
},
|
|
1645
|
+
layers: [
|
|
1646
|
+
layer("content", "content/", "docs/"),
|
|
1647
|
+
layer("assets", "assets/", "static/", "public/"),
|
|
1648
|
+
...commonLayers()
|
|
1649
|
+
],
|
|
1650
|
+
kinds: commonKinds(),
|
|
1651
|
+
exclude: [".docusaurus", ".vitepress/dist", "site"],
|
|
1652
|
+
recommendedBudget: "100k"
|
|
1653
|
+
},
|
|
1654
|
+
{
|
|
1655
|
+
name: "generic",
|
|
1656
|
+
priority: 0,
|
|
1657
|
+
detect: () => true,
|
|
1658
|
+
layers: DEFAULT_LAYERS,
|
|
1659
|
+
kinds: DEFAULT_KINDS,
|
|
1660
|
+
exclude: [],
|
|
1661
|
+
recommendedBudget: "120k"
|
|
526
1662
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
1663
|
+
];
|
|
1664
|
+
|
|
1665
|
+
// src/presets/registry.ts
|
|
1666
|
+
function compare(a, b) {
|
|
1667
|
+
if (b.priority !== a.priority) return b.priority - a.priority;
|
|
1668
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
|
|
1669
|
+
}
|
|
1670
|
+
var PRESETS = [
|
|
1671
|
+
...JS_PRESETS,
|
|
1672
|
+
...LANGUAGE_PRESETS,
|
|
1673
|
+
...SHAPE_PRESETS
|
|
1674
|
+
].sort(compare);
|
|
1675
|
+
var BY_NAME = new Map(PRESETS.map((preset) => [preset.name, preset]));
|
|
1676
|
+
function listPresetNames() {
|
|
1677
|
+
return PRESETS.map((preset) => preset.name);
|
|
1678
|
+
}
|
|
1679
|
+
function getPreset(name) {
|
|
1680
|
+
return BY_NAME.get(name);
|
|
1681
|
+
}
|
|
1682
|
+
function unknownPresetMessage(name) {
|
|
1683
|
+
return `Unknown preset "${name}". Expected one of: ${listPresetNames().join(", ")}.`;
|
|
1684
|
+
}
|
|
1685
|
+
function detectPresetFromContext(context, forced) {
|
|
1686
|
+
if (forced !== void 0) {
|
|
1687
|
+
const preset = BY_NAME.get(forced);
|
|
1688
|
+
if (!preset) throw new Error(unknownPresetMessage(forced));
|
|
1689
|
+
return toDetected(preset);
|
|
533
1690
|
}
|
|
534
|
-
|
|
535
|
-
return
|
|
536
|
-
presetName: "node",
|
|
537
|
-
layers: NODE_LAYERS,
|
|
538
|
-
kinds: NODE_KINDS
|
|
539
|
-
};
|
|
1691
|
+
for (const preset of PRESETS) {
|
|
1692
|
+
if (preset.detect(context)) return toDetected(preset);
|
|
540
1693
|
}
|
|
1694
|
+
return toDetected(PRESETS[PRESETS.length - 1]);
|
|
1695
|
+
}
|
|
1696
|
+
function toDetected(preset) {
|
|
541
1697
|
return {
|
|
542
|
-
presetName:
|
|
543
|
-
layers:
|
|
544
|
-
kinds:
|
|
1698
|
+
presetName: preset.name,
|
|
1699
|
+
layers: preset.layers,
|
|
1700
|
+
kinds: preset.kinds,
|
|
1701
|
+
exclude: preset.exclude ? [...preset.exclude] : [],
|
|
1702
|
+
recommendedBudget: preset.recommendedBudget ?? null
|
|
545
1703
|
};
|
|
546
1704
|
}
|
|
1705
|
+
function detectPreset(cwd, forced) {
|
|
1706
|
+
return detectPresetFromContext(buildPresetContext(cwd), forced);
|
|
1707
|
+
}
|
|
547
1708
|
function getGitInfo(cwd, fallbackBranch = "main") {
|
|
548
1709
|
try {
|
|
549
1710
|
const branch = childProcess__namespace.execSync("git rev-parse --abbrev-ref HEAD", {
|
|
@@ -678,10 +1839,10 @@ function matchesExclude(relativePath, pattern) {
|
|
|
678
1839
|
return posixPath === cleaned || posixPath.startsWith(`${cleaned}/`) || posixPath.includes(`/${cleaned}/`) || posixPath.endsWith(`/${cleaned}`);
|
|
679
1840
|
}
|
|
680
1841
|
function readGitignoreMatcher(dir) {
|
|
681
|
-
const gitignorePath =
|
|
1842
|
+
const gitignorePath = path10__namespace.join(dir, ".gitignore");
|
|
682
1843
|
let raw;
|
|
683
1844
|
try {
|
|
684
|
-
raw =
|
|
1845
|
+
raw = fs9__namespace.readFileSync(gitignorePath, "utf8");
|
|
685
1846
|
} catch {
|
|
686
1847
|
return () => false;
|
|
687
1848
|
}
|
|
@@ -784,7 +1945,7 @@ function walkDirectory(dir, baseDir, visited) {
|
|
|
784
1945
|
const results = { files: [], skipped: [] };
|
|
785
1946
|
let realDir;
|
|
786
1947
|
try {
|
|
787
|
-
realDir =
|
|
1948
|
+
realDir = fs9__namespace.realpathSync(dir);
|
|
788
1949
|
} catch {
|
|
789
1950
|
realDir = dir;
|
|
790
1951
|
}
|
|
@@ -793,21 +1954,21 @@ function walkDirectory(dir, baseDir, visited) {
|
|
|
793
1954
|
const ignoredByGitignore = dir === baseDir ? readGitignoreMatcher(baseDir) : () => false;
|
|
794
1955
|
let entries;
|
|
795
1956
|
try {
|
|
796
|
-
entries =
|
|
1957
|
+
entries = fs9__namespace.readdirSync(dir, { withFileTypes: true });
|
|
797
1958
|
} catch {
|
|
798
1959
|
results.skipped.push({ path: dir, reason: "read-error", detail: "directory unreadable" });
|
|
799
1960
|
return results;
|
|
800
1961
|
}
|
|
801
1962
|
for (const entry of entries) {
|
|
802
1963
|
if (DEFAULT_IGNORED_DIRS.includes(entry.name)) continue;
|
|
803
|
-
const fullPath =
|
|
804
|
-
const relPath =
|
|
1964
|
+
const fullPath = path10__namespace.join(dir, entry.name);
|
|
1965
|
+
const relPath = path10__namespace.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
805
1966
|
if (ignoredByGitignore(relPath)) continue;
|
|
806
1967
|
let isDirectory = entry.isDirectory();
|
|
807
1968
|
let isFile = entry.isFile();
|
|
808
1969
|
if (entry.isSymbolicLink()) {
|
|
809
1970
|
try {
|
|
810
|
-
const stats =
|
|
1971
|
+
const stats = fs9__namespace.statSync(fullPath);
|
|
811
1972
|
isDirectory = stats.isDirectory();
|
|
812
1973
|
isFile = stats.isFile();
|
|
813
1974
|
if (!isDirectory && !isFile) {
|
|
@@ -1072,6 +2233,8 @@ function normalizeResolvedConfig(config) {
|
|
|
1072
2233
|
config.includeMonolith === true
|
|
1073
2234
|
),
|
|
1074
2235
|
include: resolveInclude(config.include),
|
|
2236
|
+
preset: typeof config.preset === "string" ? config.preset : "generic",
|
|
2237
|
+
presetExclude: Array.isArray(config.presetExclude) ? config.presetExclude : [],
|
|
1075
2238
|
layersMode: config.layersMode === "replace" ? "replace" : "merge",
|
|
1076
2239
|
budgets: config.budgets && typeof config.budgets === "object" ? config.budgets : { default: config.maxPartBytes, byLayer: {} },
|
|
1077
2240
|
hooks: resolveHooks(config.hooks),
|
|
@@ -1119,18 +2282,22 @@ function parseJsonish(raw, file) {
|
|
|
1119
2282
|
}
|
|
1120
2283
|
async function loadConfigFileDetailed(cwd) {
|
|
1121
2284
|
const warnings = [];
|
|
2285
|
+
const errors = [];
|
|
1122
2286
|
for (const filename of CONFIG_FILENAMES) {
|
|
1123
|
-
const fullPath =
|
|
1124
|
-
if (!
|
|
2287
|
+
const fullPath = path10__namespace.join(cwd, filename);
|
|
2288
|
+
if (!fs9__namespace.existsSync(fullPath)) continue;
|
|
1125
2289
|
if (filename.endsWith(".json") || filename === ".snapshotrc") {
|
|
1126
2290
|
try {
|
|
1127
2291
|
return {
|
|
1128
|
-
config: parseJsonish(
|
|
2292
|
+
config: parseJsonish(fs9__namespace.readFileSync(fullPath, "utf8"), fullPath),
|
|
1129
2293
|
file: fullPath,
|
|
1130
|
-
warnings
|
|
2294
|
+
warnings,
|
|
2295
|
+
errors
|
|
1131
2296
|
};
|
|
1132
2297
|
} catch (err) {
|
|
1133
|
-
|
|
2298
|
+
const message = err.message;
|
|
2299
|
+
warnings.push(message);
|
|
2300
|
+
errors.push(message);
|
|
1134
2301
|
continue;
|
|
1135
2302
|
}
|
|
1136
2303
|
}
|
|
@@ -1144,28 +2311,34 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
1144
2311
|
const imported = await import(url.pathToFileURL(fullPath).href);
|
|
1145
2312
|
const config = imported.default ?? imported;
|
|
1146
2313
|
if (!config || typeof config !== "object") {
|
|
1147
|
-
|
|
2314
|
+
const message = `${fullPath} does not export a configuration object.`;
|
|
2315
|
+
warnings.push(message);
|
|
2316
|
+
errors.push(message);
|
|
1148
2317
|
continue;
|
|
1149
2318
|
}
|
|
1150
|
-
return { config, file: fullPath, warnings };
|
|
2319
|
+
return { config, file: fullPath, warnings, errors };
|
|
1151
2320
|
} catch (err) {
|
|
1152
|
-
|
|
2321
|
+
const message = `Could not load ${fullPath}: ${err.message}`;
|
|
2322
|
+
warnings.push(message);
|
|
2323
|
+
errors.push(message);
|
|
1153
2324
|
continue;
|
|
1154
2325
|
}
|
|
1155
2326
|
}
|
|
1156
|
-
const pkgJsonPath =
|
|
1157
|
-
if (
|
|
2327
|
+
const pkgJsonPath = path10__namespace.join(cwd, "package.json");
|
|
2328
|
+
if (fs9__namespace.existsSync(pkgJsonPath)) {
|
|
1158
2329
|
try {
|
|
1159
|
-
const parsed = parseJsonish(
|
|
2330
|
+
const parsed = parseJsonish(fs9__namespace.readFileSync(pkgJsonPath, "utf8"), pkgJsonPath);
|
|
1160
2331
|
const snapshotKey = parsed.snapshot;
|
|
1161
2332
|
if (snapshotKey && typeof snapshotKey === "object") {
|
|
1162
|
-
return { config: snapshotKey, file: pkgJsonPath, warnings };
|
|
2333
|
+
return { config: snapshotKey, file: pkgJsonPath, warnings, errors };
|
|
1163
2334
|
}
|
|
1164
2335
|
} catch (err) {
|
|
1165
|
-
|
|
2336
|
+
const message = err.message;
|
|
2337
|
+
warnings.push(message);
|
|
2338
|
+
errors.push(message);
|
|
1166
2339
|
}
|
|
1167
2340
|
}
|
|
1168
|
-
return { config: null, file: null, warnings };
|
|
2341
|
+
return { config: null, file: null, warnings, errors };
|
|
1169
2342
|
}
|
|
1170
2343
|
async function loadConfigFile(cwd) {
|
|
1171
2344
|
const loaded = await loadConfigFileDetailed(cwd);
|
|
@@ -1264,7 +2437,9 @@ function boundedBytes(value, fallback, minimum, label) {
|
|
|
1264
2437
|
`${label} must be a byte count such as 120000, "120k" or "1.5mb" (received ${describeBytes(value)}).`
|
|
1265
2438
|
);
|
|
1266
2439
|
}
|
|
1267
|
-
if (parsed <= 0)
|
|
2440
|
+
if (parsed <= 0) {
|
|
2441
|
+
throw new Error(`${label} must be a positive byte count (received ${describeBytes(value)}).`);
|
|
2442
|
+
}
|
|
1268
2443
|
if (parsed < minimum) {
|
|
1269
2444
|
throw new Error(`${label} must be at least ${minimum} bytes (received ${describeBytes(value)}).`);
|
|
1270
2445
|
}
|
|
@@ -1304,12 +2479,12 @@ function resolveBudgets(budgets, fallback) {
|
|
|
1304
2479
|
if (typeof budgets.byLayer !== "object" || budgets.byLayer === null) {
|
|
1305
2480
|
throw new Error("`budgets.byLayer` must be an object of layer name to byte size.");
|
|
1306
2481
|
}
|
|
1307
|
-
for (const [
|
|
1308
|
-
byLayer[
|
|
2482
|
+
for (const [layer2, value] of Object.entries(budgets.byLayer)) {
|
|
2483
|
+
byLayer[layer2] = boundedBytes(
|
|
1309
2484
|
value,
|
|
1310
2485
|
fallback,
|
|
1311
2486
|
MIN_MAX_PART_BYTES,
|
|
1312
|
-
`budgets.byLayer["${
|
|
2487
|
+
`budgets.byLayer["${layer2}"]`
|
|
1313
2488
|
);
|
|
1314
2489
|
}
|
|
1315
2490
|
}
|
|
@@ -1330,18 +2505,22 @@ function resolveHooks(hooks) {
|
|
|
1330
2505
|
}
|
|
1331
2506
|
function resolveCandidateCwd(userConfig, cliOptions, baseDir) {
|
|
1332
2507
|
const candidate = cliOptions?.cwd || userConfig?.cwd || baseDir || process.cwd();
|
|
1333
|
-
const resolved =
|
|
1334
|
-
if (!
|
|
2508
|
+
const resolved = path10__namespace.resolve(candidate);
|
|
2509
|
+
if (!fs9__namespace.existsSync(resolved)) {
|
|
1335
2510
|
throw new Error(`Working directory does not exist: ${resolved}`);
|
|
1336
2511
|
}
|
|
1337
|
-
if (!
|
|
2512
|
+
if (!fs9__namespace.statSync(resolved).isDirectory()) {
|
|
1338
2513
|
throw new Error(`Working directory is not a directory: ${resolved}`);
|
|
1339
2514
|
}
|
|
1340
2515
|
return resolved;
|
|
1341
2516
|
}
|
|
1342
2517
|
function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
1343
2518
|
const cwd = resolveCandidateCwd(userConfig ?? null, cliOptions, baseDir);
|
|
1344
|
-
const
|
|
2519
|
+
const forcedPreset = userConfig?.preset;
|
|
2520
|
+
if (forcedPreset !== void 0 && !getPreset(forcedPreset)) {
|
|
2521
|
+
throw new Error(unknownPresetMessage(forcedPreset));
|
|
2522
|
+
}
|
|
2523
|
+
const preset = detectPreset(cwd, forcedPreset);
|
|
1345
2524
|
const layersMode = resolveLayersMode(userConfig?.layersMode);
|
|
1346
2525
|
const layers = userConfig?.layers && userConfig.layers.length > 0 ? layersMode === "replace" ? userConfig.layers : [...userConfig.layers, ...preset.layers] : preset.layers;
|
|
1347
2526
|
const kinds = userConfig?.kinds && userConfig.kinds.length > 0 ? layersMode === "replace" ? userConfig.kinds : [...userConfig.kinds, ...preset.kinds] : preset.kinds;
|
|
@@ -1377,6 +2556,8 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
1377
2556
|
),
|
|
1378
2557
|
includeSensitiveFiles: cliOptions?.includeSensitiveFiles ?? userConfig?.includeSensitiveFiles ?? false,
|
|
1379
2558
|
include: resolveInclude(userConfig?.include),
|
|
2559
|
+
preset: preset.presetName,
|
|
2560
|
+
presetExclude: [...preset.exclude],
|
|
1380
2561
|
artifacts,
|
|
1381
2562
|
layersMode,
|
|
1382
2563
|
budgets,
|
|
@@ -1411,8 +2592,8 @@ function matchRule(filePath, matcher) {
|
|
|
1411
2592
|
return false;
|
|
1412
2593
|
}
|
|
1413
2594
|
function classifyLayer(filePath, layers) {
|
|
1414
|
-
for (const
|
|
1415
|
-
if (matchRule(filePath,
|
|
2595
|
+
for (const layer2 of layers) {
|
|
2596
|
+
if (matchRule(filePath, layer2.match)) return layer2.name;
|
|
1416
2597
|
}
|
|
1417
2598
|
return "other";
|
|
1418
2599
|
}
|
|
@@ -1493,11 +2674,11 @@ function generateAuditReport(manifest, secretFindings) {
|
|
|
1493
2674
|
"| :--- | :--- | :--- |"
|
|
1494
2675
|
);
|
|
1495
2676
|
const totalFiles = manifest.counts.totalFiles || 1;
|
|
1496
|
-
for (const [
|
|
2677
|
+
for (const [layer2, count] of Object.entries(manifest.layers).sort(
|
|
1497
2678
|
(a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)
|
|
1498
2679
|
)) {
|
|
1499
2680
|
const pct = (count / totalFiles * 100).toFixed(1);
|
|
1500
|
-
lines.push(`| \`${
|
|
2681
|
+
lines.push(`| \`${layer2}\` | ${count} | ${pct}% |`);
|
|
1501
2682
|
}
|
|
1502
2683
|
lines.push("");
|
|
1503
2684
|
lines.push(
|
|
@@ -1605,21 +2786,21 @@ function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
|
1605
2786
|
section.byteSize = Buffer.byteLength(section.text, "utf8");
|
|
1606
2787
|
continue;
|
|
1607
2788
|
}
|
|
1608
|
-
const
|
|
1609
|
-
const target = Math.max(0, budget - Buffer.byteLength(
|
|
2789
|
+
const marker2 = "\n... [part truncated: cannot fit budget] ...\n";
|
|
2790
|
+
const target = Math.max(0, budget - Buffer.byteLength(marker2, "utf8"));
|
|
1610
2791
|
const clipped = truncateToBytes(rendered[oversized].markdown, target);
|
|
1611
|
-
rendered[oversized] = rehashPart(rendered[oversized], clipped.content +
|
|
2792
|
+
rendered[oversized] = rehashPart(rendered[oversized], clipped.content + marker2);
|
|
1612
2793
|
return rendered;
|
|
1613
2794
|
}
|
|
1614
2795
|
return enforceBudget(renderGroups(groups, contentFingerprint), budget);
|
|
1615
2796
|
}
|
|
1616
2797
|
function enforceBudget(parts, budget) {
|
|
1617
|
-
const
|
|
1618
|
-
const target = Math.max(0, budget - Buffer.byteLength(
|
|
2798
|
+
const marker2 = "\n... [part truncated: cannot fit budget] ...\n";
|
|
2799
|
+
const target = Math.max(0, budget - Buffer.byteLength(marker2, "utf8"));
|
|
1619
2800
|
return parts.map((part) => {
|
|
1620
2801
|
if (part.metadata.byteSize <= budget) return part;
|
|
1621
2802
|
const clipped = truncateToBytes(part.markdown, target);
|
|
1622
|
-
return rehashPart(part, clipped.content +
|
|
2803
|
+
return rehashPart(part, clipped.content + marker2);
|
|
1623
2804
|
});
|
|
1624
2805
|
}
|
|
1625
2806
|
function renumberParts(parts, contentFingerprint) {
|
|
@@ -1627,10 +2808,10 @@ function renumberParts(parts, contentFingerprint) {
|
|
|
1627
2808
|
if (total === 0) return [];
|
|
1628
2809
|
const zeroPad = (index) => String(index).padStart(Math.max(3, String(total).length), "0");
|
|
1629
2810
|
const nameFor = (position) => `part-${zeroPad(position)}.md`;
|
|
1630
|
-
const
|
|
2811
|
+
const marker2 = "## Contents\n";
|
|
1631
2812
|
return parts.map((part, index) => {
|
|
1632
|
-
const markerIndex = part.markdown.indexOf(
|
|
1633
|
-
const body = markerIndex === -1 ? "" : part.markdown.slice(markerIndex +
|
|
2813
|
+
const markerIndex = part.markdown.indexOf(marker2);
|
|
2814
|
+
const body = markerIndex === -1 ? "" : part.markdown.slice(markerIndex + marker2.length);
|
|
1634
2815
|
const included = part.metadata.includedFiles;
|
|
1635
2816
|
if (markerIndex === -1) {
|
|
1636
2817
|
throw new Error(`${part.filename} has no "## Contents" section to re-number.`);
|
|
@@ -1688,9 +2869,9 @@ function topEntries(counts, limit) {
|
|
|
1688
2869
|
return Object.entries(counts).sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)).slice(0, limit).map(([key, value]) => `${capMetadata(key)} (${value})`).join(", ");
|
|
1689
2870
|
}
|
|
1690
2871
|
function prepareSection(file, sectionBudget) {
|
|
1691
|
-
let
|
|
1692
|
-
let byteSize = Buffer.byteLength(
|
|
1693
|
-
if (byteSize <= sectionBudget) return { file, text, byteSize };
|
|
2872
|
+
let text2 = renderFileSection(file);
|
|
2873
|
+
let byteSize = Buffer.byteLength(text2, "utf8");
|
|
2874
|
+
if (byteSize <= sectionBudget) return { file, text: text2, byteSize };
|
|
1694
2875
|
const shrinkBody = () => {
|
|
1695
2876
|
const content = file.content ?? "";
|
|
1696
2877
|
const overhead = byteSize - Buffer.byteLength(content, "utf8");
|
|
@@ -1699,24 +2880,24 @@ function prepareSection(file, sectionBudget) {
|
|
|
1699
2880
|
const truncated = truncateToBytes(content, allowed);
|
|
1700
2881
|
if (!truncated.isTruncated) return;
|
|
1701
2882
|
applyTruncatedContent(file, truncated.content);
|
|
1702
|
-
|
|
1703
|
-
byteSize = Buffer.byteLength(
|
|
2883
|
+
text2 = renderFileSection(file);
|
|
2884
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
1704
2885
|
};
|
|
1705
2886
|
shrinkBody();
|
|
1706
2887
|
if (byteSize > sectionBudget && file.summary) {
|
|
1707
2888
|
file.summary = void 0;
|
|
1708
2889
|
file.summaryOmitted = true;
|
|
1709
|
-
|
|
1710
|
-
byteSize = Buffer.byteLength(
|
|
2890
|
+
text2 = renderFileSection(file);
|
|
2891
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
1711
2892
|
shrinkBody();
|
|
1712
2893
|
}
|
|
1713
2894
|
if (byteSize > sectionBudget) {
|
|
1714
2895
|
const target = sectionBudget - Buffer.byteLength(SECTION_TRUNCATION_NOTICE, "utf8");
|
|
1715
|
-
const truncated = truncateToBytes(
|
|
1716
|
-
|
|
1717
|
-
byteSize = Buffer.byteLength(
|
|
2896
|
+
const truncated = truncateToBytes(text2, Math.max(0, target));
|
|
2897
|
+
text2 = truncated.content + SECTION_TRUNCATION_NOTICE;
|
|
2898
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
1718
2899
|
}
|
|
1719
|
-
return { file, text, byteSize };
|
|
2900
|
+
return { file, text: text2, byteSize };
|
|
1720
2901
|
}
|
|
1721
2902
|
function applyTruncatedContent(file, content) {
|
|
1722
2903
|
file.content = content;
|
|
@@ -1838,6 +3019,7 @@ function renderFileSection(file) {
|
|
|
1838
3019
|
`- Source Lines: ${file.originalLines}`,
|
|
1839
3020
|
`- Included Lines: ${file.includedLines}`,
|
|
1840
3021
|
`- SHA-256: ${file.hashSha256}`,
|
|
3022
|
+
...file.summaryQuality ? [`- Summary Quality: ${file.summaryQuality}`] : [],
|
|
1841
3023
|
...renderSummary(file.summary),
|
|
1842
3024
|
...file.summaryOmitted ? ["- Summary: (omitted: exceeds the part budget)"] : [],
|
|
1843
3025
|
"",
|
|
@@ -2085,7 +3267,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
2085
3267
|
".so"
|
|
2086
3268
|
]);
|
|
2087
3269
|
function isBinaryExtension(filePath) {
|
|
2088
|
-
const ext =
|
|
3270
|
+
const ext = path10__namespace.extname(filePath).toLowerCase();
|
|
2089
3271
|
return BINARY_EXTENSIONS.has(ext);
|
|
2090
3272
|
}
|
|
2091
3273
|
function isBinaryBuffer(buffer) {
|
|
@@ -2099,9 +3281,9 @@ function isBinaryFile(absolutePath) {
|
|
|
2099
3281
|
if (isBinaryExtension(absolutePath)) return true;
|
|
2100
3282
|
let fd;
|
|
2101
3283
|
try {
|
|
2102
|
-
fd =
|
|
3284
|
+
fd = fs9__namespace.openSync(absolutePath, "r");
|
|
2103
3285
|
const buffer = Buffer.alloc(8192);
|
|
2104
|
-
const bytesRead =
|
|
3286
|
+
const bytesRead = fs9__namespace.readSync(fd, buffer, 0, 8192, 0);
|
|
2105
3287
|
if (bytesRead === 0) return false;
|
|
2106
3288
|
return isBinaryBuffer(buffer.subarray(0, bytesRead));
|
|
2107
3289
|
} catch {
|
|
@@ -2109,7 +3291,7 @@ function isBinaryFile(absolutePath) {
|
|
|
2109
3291
|
} finally {
|
|
2110
3292
|
if (fd !== void 0) {
|
|
2111
3293
|
try {
|
|
2112
|
-
|
|
3294
|
+
fs9__namespace.closeSync(fd);
|
|
2113
3295
|
} catch {
|
|
2114
3296
|
}
|
|
2115
3297
|
}
|
|
@@ -2169,10 +3351,10 @@ var LANGUAGE_BY_BASENAME = {
|
|
|
2169
3351
|
};
|
|
2170
3352
|
function detectLanguage(filePath) {
|
|
2171
3353
|
const normalized = filePath.replace(/\\/g, "/");
|
|
2172
|
-
const base =
|
|
3354
|
+
const base = path10__namespace.posix.basename(normalized);
|
|
2173
3355
|
if (LANGUAGE_BY_BASENAME[base]) return LANGUAGE_BY_BASENAME[base];
|
|
2174
3356
|
if (base === ".env" || base.startsWith(".env.")) return "dotenv";
|
|
2175
|
-
const ext =
|
|
3357
|
+
const ext = path10__namespace.posix.extname(base).toLowerCase();
|
|
2176
3358
|
return LANGUAGE_BY_EXTENSION[ext] ?? "text";
|
|
2177
3359
|
}
|
|
2178
3360
|
function emptyDrift() {
|
|
@@ -2191,8 +3373,8 @@ function sha256Hex(buffer) {
|
|
|
2191
3373
|
function validateSnapshot(configInput) {
|
|
2192
3374
|
assertUsableConfigInput(configInput);
|
|
2193
3375
|
const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput);
|
|
2194
|
-
const manifestPath =
|
|
2195
|
-
if (!
|
|
3376
|
+
const manifestPath = path10__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
3377
|
+
if (!fs9__namespace.existsSync(manifestPath)) {
|
|
2196
3378
|
return {
|
|
2197
3379
|
valid: false,
|
|
2198
3380
|
manifestPresent: false,
|
|
@@ -2203,7 +3385,7 @@ function validateSnapshot(configInput) {
|
|
|
2203
3385
|
}
|
|
2204
3386
|
let manifest;
|
|
2205
3387
|
try {
|
|
2206
|
-
manifest = JSON.parse(
|
|
3388
|
+
manifest = JSON.parse(fs9__namespace.readFileSync(manifestPath, "utf8"));
|
|
2207
3389
|
} catch (err) {
|
|
2208
3390
|
return {
|
|
2209
3391
|
valid: false,
|
|
@@ -2226,7 +3408,7 @@ function validateSnapshot(configInput) {
|
|
|
2226
3408
|
}
|
|
2227
3409
|
const drift = emptyDrift();
|
|
2228
3410
|
const warnings = [];
|
|
2229
|
-
const snapshotDir =
|
|
3411
|
+
const snapshotDir = path10__namespace.resolve(config.cwd, config.outDir);
|
|
2230
3412
|
const manifestFileMap = /* @__PURE__ */ new Map();
|
|
2231
3413
|
for (const file of manifest.files) {
|
|
2232
3414
|
if (file && typeof file.relativePath === "string") {
|
|
@@ -2238,7 +3420,7 @@ function validateSnapshot(configInput) {
|
|
|
2238
3420
|
);
|
|
2239
3421
|
const collection = collectFiles(config.cwd, {
|
|
2240
3422
|
includePatterns: config.include,
|
|
2241
|
-
excludePatterns: config.excludePatterns,
|
|
3423
|
+
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
2242
3424
|
includeSensitiveFiles: config.includeSensitiveFiles
|
|
2243
3425
|
});
|
|
2244
3426
|
for (const diskFile of collection.files) {
|
|
@@ -2250,7 +3432,7 @@ function validateSnapshot(configInput) {
|
|
|
2250
3432
|
}
|
|
2251
3433
|
let diskHash;
|
|
2252
3434
|
try {
|
|
2253
|
-
diskHash = sha256Hex(
|
|
3435
|
+
diskHash = sha256Hex(fs9__namespace.readFileSync(path10__namespace.join(config.cwd, diskFile)));
|
|
2254
3436
|
} catch {
|
|
2255
3437
|
drift.missingOnDisk.push(diskFile);
|
|
2256
3438
|
continue;
|
|
@@ -2260,7 +3442,7 @@ function validateSnapshot(configInput) {
|
|
|
2260
3442
|
}
|
|
2261
3443
|
}
|
|
2262
3444
|
for (const snapshotFile of manifestFileMap.keys()) {
|
|
2263
|
-
if (!
|
|
3445
|
+
if (!fs9__namespace.existsSync(path10__namespace.join(config.cwd, snapshotFile))) {
|
|
2264
3446
|
drift.missingOnDisk.push(snapshotFile);
|
|
2265
3447
|
}
|
|
2266
3448
|
}
|
|
@@ -2274,10 +3456,10 @@ function validateSnapshot(configInput) {
|
|
|
2274
3456
|
if (!part || typeof part.filename !== "string") continue;
|
|
2275
3457
|
partNames.add(part.filename);
|
|
2276
3458
|
if (!config.artifacts.parts) continue;
|
|
2277
|
-
const partPath =
|
|
3459
|
+
const partPath = path10__namespace.join(snapshotDir, "parts", part.filename);
|
|
2278
3460
|
let buffer;
|
|
2279
3461
|
try {
|
|
2280
|
-
buffer =
|
|
3462
|
+
buffer = fs9__namespace.readFileSync(partPath);
|
|
2281
3463
|
} catch {
|
|
2282
3464
|
drift.partIssues.push({ part: part.filename, issue: "missing from snapshot/parts" });
|
|
2283
3465
|
continue;
|
|
@@ -2364,6 +3546,11 @@ function validateSnapshot(configInput) {
|
|
|
2364
3546
|
}
|
|
2365
3547
|
}
|
|
2366
3548
|
}
|
|
3549
|
+
if (recorded?.preset !== void 0 && recorded.preset !== config.preset) {
|
|
3550
|
+
drift.configMismatches.push(
|
|
3551
|
+
`preset: manifest ${recorded.preset} vs current ${config.preset}`
|
|
3552
|
+
);
|
|
3553
|
+
}
|
|
2367
3554
|
if (recorded?.layersMode !== void 0 && recorded.layersMode !== config.layersMode) {
|
|
2368
3555
|
drift.configMismatches.push(
|
|
2369
3556
|
`layersMode: manifest ${recorded.layersMode} vs current ${config.layersMode}`
|
|
@@ -2381,10 +3568,10 @@ function validateSnapshot(configInput) {
|
|
|
2381
3568
|
...Object.keys(recordedByLayer),
|
|
2382
3569
|
...Object.keys(config.budgets.byLayer)
|
|
2383
3570
|
]);
|
|
2384
|
-
for (const
|
|
2385
|
-
if (recordedByLayer[
|
|
3571
|
+
for (const layer2 of Array.from(layerNames).sort()) {
|
|
3572
|
+
if (recordedByLayer[layer2] !== config.budgets.byLayer[layer2]) {
|
|
2386
3573
|
drift.configMismatches.push(
|
|
2387
|
-
`budgets.byLayer.${
|
|
3574
|
+
`budgets.byLayer.${layer2}: manifest ${recordedByLayer[layer2]} vs current ${config.budgets.byLayer[layer2]}`
|
|
2388
3575
|
);
|
|
2389
3576
|
}
|
|
2390
3577
|
}
|
|
@@ -2467,9 +3654,9 @@ function validateSnapshot(configInput) {
|
|
|
2467
3654
|
["full-codebase.md", manifest.artifacts?.fullMarkdown]
|
|
2468
3655
|
]) {
|
|
2469
3656
|
if (!artifact) continue;
|
|
2470
|
-
const artifactPath =
|
|
3657
|
+
const artifactPath = path10__namespace.join(snapshotDir, key);
|
|
2471
3658
|
try {
|
|
2472
|
-
const buffer =
|
|
3659
|
+
const buffer = fs9__namespace.readFileSync(artifactPath);
|
|
2473
3660
|
if (sha256Hex(buffer) !== artifact.sha256 || buffer.length !== artifact.byteSize) {
|
|
2474
3661
|
drift.partIssues.push({ part: key, issue: "artifact does not match its recorded digest" });
|
|
2475
3662
|
}
|
|
@@ -2525,35 +3712,35 @@ function describeMatcher(matcher) {
|
|
|
2525
3712
|
return String(matcher);
|
|
2526
3713
|
}
|
|
2527
3714
|
function normalizeTarget(cwd, target) {
|
|
2528
|
-
const absolute =
|
|
2529
|
-
const relative3 =
|
|
2530
|
-
if (relative3.startsWith("..") ||
|
|
2531
|
-
return relative3.split(
|
|
3715
|
+
const absolute = path10__namespace.resolve(cwd, target);
|
|
3716
|
+
const relative3 = path10__namespace.relative(cwd, absolute);
|
|
3717
|
+
if (relative3.startsWith("..") || path10__namespace.isAbsolute(relative3)) return target;
|
|
3718
|
+
return relative3.split(path10__namespace.sep).join("/");
|
|
2532
3719
|
}
|
|
2533
3720
|
function findRuleMatch(rules, relativePath) {
|
|
2534
3721
|
return rules.find(
|
|
2535
3722
|
(rule) => matchRule(relativePath, rule.match)
|
|
2536
3723
|
);
|
|
2537
3724
|
}
|
|
2538
|
-
function
|
|
2539
|
-
const manifestPath =
|
|
3725
|
+
function readManifest2(config) {
|
|
3726
|
+
const manifestPath = path10__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
2540
3727
|
try {
|
|
2541
|
-
return JSON.parse(
|
|
3728
|
+
return JSON.parse(fs9__namespace.readFileSync(manifestPath, "utf8"));
|
|
2542
3729
|
} catch {
|
|
2543
3730
|
return null;
|
|
2544
3731
|
}
|
|
2545
3732
|
}
|
|
2546
3733
|
function explainPath(config, target) {
|
|
2547
3734
|
const relative3 = normalizeTarget(config.cwd, target);
|
|
2548
|
-
const absolute =
|
|
3735
|
+
const absolute = path10__namespace.join(config.cwd, relative3);
|
|
2549
3736
|
const result = {
|
|
2550
3737
|
path: relative3,
|
|
2551
|
-
exists:
|
|
3738
|
+
exists: fs9__namespace.existsSync(absolute),
|
|
2552
3739
|
included: false
|
|
2553
3740
|
};
|
|
2554
3741
|
const collection = collectFiles(config.cwd, {
|
|
2555
3742
|
includePatterns: config.include,
|
|
2556
|
-
excludePatterns: config.excludePatterns,
|
|
3743
|
+
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
2557
3744
|
includeSensitiveFiles: config.includeSensitiveFiles
|
|
2558
3745
|
});
|
|
2559
3746
|
if (!collection.files.includes(relative3)) {
|
|
@@ -2578,16 +3765,16 @@ function explainPath(config, target) {
|
|
|
2578
3765
|
if (layerRule) result.layerMatch = describeMatcher(layerRule.match);
|
|
2579
3766
|
const kindRule = findRuleMatch(config.kinds, relative3);
|
|
2580
3767
|
if (kindRule) result.kindMatch = describeMatcher(kindRule.match);
|
|
2581
|
-
if (
|
|
2582
|
-
const buffer =
|
|
2583
|
-
const
|
|
3768
|
+
if (fs9__namespace.existsSync(absolute) && fs9__namespace.statSync(absolute).isFile()) {
|
|
3769
|
+
const buffer = fs9__namespace.readFileSync(absolute);
|
|
3770
|
+
const text2 = buffer.toString("utf8");
|
|
2584
3771
|
result.bytes = buffer.length;
|
|
2585
|
-
result.lines = countLines(
|
|
2586
|
-
const { findings } = scanAndRedactSecrets(
|
|
3772
|
+
result.lines = countLines(text2);
|
|
3773
|
+
const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative3);
|
|
2587
3774
|
result.secretCount = findings.length;
|
|
2588
3775
|
result.secrets = findings.slice(0, 20).map((finding) => ({ rule: finding.rule, line: finding.line }));
|
|
2589
3776
|
}
|
|
2590
|
-
const manifest =
|
|
3777
|
+
const manifest = readManifest2(config);
|
|
2591
3778
|
const entry = manifest?.files.find((file) => file.relativePath === relative3);
|
|
2592
3779
|
result.part = entry?.part ?? null;
|
|
2593
3780
|
result.inclusionMode = entry?.inclusionMode;
|
|
@@ -2670,10 +3857,10 @@ function compareManifests(before, after) {
|
|
|
2670
3857
|
};
|
|
2671
3858
|
}
|
|
2672
3859
|
function loadManifest(target) {
|
|
2673
|
-
const manifestPath =
|
|
3860
|
+
const manifestPath = fs9__namespace.statSync(target).isDirectory() ? path10__namespace.join(target, "manifest.json") : target;
|
|
2674
3861
|
let raw;
|
|
2675
3862
|
try {
|
|
2676
|
-
raw =
|
|
3863
|
+
raw = fs9__namespace.readFileSync(manifestPath, "utf8");
|
|
2677
3864
|
} catch {
|
|
2678
3865
|
throw new Error(`Could not read a manifest at ${manifestPath}.`);
|
|
2679
3866
|
}
|
|
@@ -2709,14 +3896,659 @@ function renderDiffText(diff) {
|
|
|
2709
3896
|
return lines.join("\n");
|
|
2710
3897
|
}
|
|
2711
3898
|
|
|
3899
|
+
// src/wizard/config-template.ts
|
|
3900
|
+
var DEFAULT_ANSWERS = {
|
|
3901
|
+
preset: "generic",
|
|
3902
|
+
maxPartBytes: "300k",
|
|
3903
|
+
largeFileBytes: "200k",
|
|
3904
|
+
include: [],
|
|
3905
|
+
excludePatterns: [],
|
|
3906
|
+
artifacts: {
|
|
3907
|
+
readme: true,
|
|
3908
|
+
manifest: true,
|
|
3909
|
+
parts: true,
|
|
3910
|
+
navigation: true,
|
|
3911
|
+
projectTree: true,
|
|
3912
|
+
audit: true,
|
|
3913
|
+
monolith: false
|
|
3914
|
+
},
|
|
3915
|
+
includeSensitiveFiles: false,
|
|
3916
|
+
outDir: "snapshot"
|
|
3917
|
+
};
|
|
3918
|
+
function list(values) {
|
|
3919
|
+
if (values.length === 0) return "[]";
|
|
3920
|
+
return `[${values.map((value) => JSON.stringify(value)).join(", ")}]`;
|
|
3921
|
+
}
|
|
3922
|
+
function size(value) {
|
|
3923
|
+
return typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
3924
|
+
}
|
|
3925
|
+
var PRESET_NAMES = [
|
|
3926
|
+
"nextjs \xB7 react-vite \xB7 vue-vite \xB7 nuxt \xB7 sveltekit \xB7 angular \xB7 astro",
|
|
3927
|
+
"remix \xB7 solid \xB7 nest \xB7 node-express \xB7 node-lib",
|
|
3928
|
+
"python-django \xB7 python-fastapi \xB7 python-lib \xB7 go \xB7 rust",
|
|
3929
|
+
"java-spring \xB7 dotnet \xB7 php-laravel \xB7 ruby-rails",
|
|
3930
|
+
"monorepo \xB7 docs \xB7 infra \xB7 generic"
|
|
3931
|
+
];
|
|
3932
|
+
function renderConfig(answers) {
|
|
3933
|
+
const { artifacts } = answers;
|
|
3934
|
+
return `import { defineConfig } from "@ankurhalder/codebase-snapshot";
|
|
3935
|
+
|
|
3936
|
+
export default defineConfig({
|
|
3937
|
+
// \u2500\u2500 Project \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3938
|
+
// Which project shape to use. Detected from the repo when omitted.
|
|
3939
|
+
// ${PRESET_NAMES.join("\n // ")}
|
|
3940
|
+
preset: ${JSON.stringify(answers.preset)},
|
|
3941
|
+
|
|
3942
|
+
// Recorded in the manifest when git cannot report a branch.
|
|
3943
|
+
branch: "main",
|
|
3944
|
+
|
|
3945
|
+
// \u2500\u2500 Output \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3946
|
+
// Where the snapshot is written.
|
|
3947
|
+
outDir: ${JSON.stringify(answers.outDir)},
|
|
3948
|
+
// Scratch directory; the new snapshot is renamed into place atomically.
|
|
3949
|
+
tmpDir: "snapshot.tmp",
|
|
3950
|
+
|
|
3951
|
+
// \u2500\u2500 Size \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3952
|
+
// Hard ceiling per part. "300k", 307200 and "1.5mb" are all accepted.
|
|
3953
|
+
maxPartBytes: ${size(answers.maxPartBytes)},
|
|
3954
|
+
// Files above this size get a head/tail preview instead of the full body.
|
|
3955
|
+
largeFileBytes: ${size(answers.largeFileBytes)},
|
|
3956
|
+
previewHeadLines: 80,
|
|
3957
|
+
previewTailLines: 40,
|
|
3958
|
+
// Different ceilings per layer. Unlisted layers use \`default\`.
|
|
3959
|
+
budgets: {
|
|
3960
|
+
default: ${size(answers.maxPartBytes)},
|
|
3961
|
+
byLayer: {},
|
|
3962
|
+
},
|
|
3963
|
+
|
|
3964
|
+
// \u2500\u2500 What goes in \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3965
|
+
// Allow-list. Empty means "everything that is not ignored".
|
|
3966
|
+
// Example: ["src/**", "packages/*/src/**"]
|
|
3967
|
+
include: ${list(answers.include)},
|
|
3968
|
+
// Globs or path segments to drop. Example: ["**/*.test.ts", "docs"]
|
|
3969
|
+
excludePatterns: ${list(answers.excludePatterns)},
|
|
3970
|
+
|
|
3971
|
+
// \u2500\u2500 Classification \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3972
|
+
// "merge" = the preset's rules plus yours (yours win). "replace" = yours only.
|
|
3973
|
+
layersMode: "merge",
|
|
3974
|
+
// [{ name: "billing", match: "src/features/billing/**" }]
|
|
3975
|
+
layers: [],
|
|
3976
|
+
// [{ kind: "route", match: "src/app/api/**" }]
|
|
3977
|
+
kinds: [],
|
|
3978
|
+
|
|
3979
|
+
// \u2500\u2500 Safety \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3980
|
+
// .env, *.pem, .npmrc, .ssh/ and friends are skipped unless this is true.
|
|
3981
|
+
includeSensitiveFiles: ${answers.includeSensitiveFiles},
|
|
3982
|
+
secrets: {
|
|
3983
|
+
// [{ rule: "internal-token", pattern: /CORP-[A-Z0-9]{32}/ }]
|
|
3984
|
+
customPatterns: [],
|
|
3985
|
+
// Set true to drop the 21 built-in rules.
|
|
3986
|
+
disableDefaultPatterns: false,
|
|
3987
|
+
},
|
|
3988
|
+
|
|
3989
|
+
// \u2500\u2500 Artifacts \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
3990
|
+
artifacts: {
|
|
3991
|
+
readme: ${artifacts.readme}, // README.md
|
|
3992
|
+
manifest: ${artifacts.manifest}, // manifest.json \u2014 required by validate and report
|
|
3993
|
+
parts: ${artifacts.parts}, // parts/part-XXX.md
|
|
3994
|
+
navigation: ${artifacts.navigation}, // reports/navigation.md
|
|
3995
|
+
projectTree: ${artifacts.projectTree}, // reports/project-tree.md
|
|
3996
|
+
audit: ${artifacts.audit}, // reports/audit.md
|
|
3997
|
+
monolith: ${artifacts.monolith}, // full-codebase.json + .md (roughly doubles output)
|
|
3998
|
+
},
|
|
3999
|
+
// Skip the monolith above this size. null = no limit.
|
|
4000
|
+
maxMonolithBytes: null,
|
|
4001
|
+
|
|
4002
|
+
// \u2500\u2500 Advanced \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4003
|
+
// afterGenerate: (manifest) => { /* write your own artifact */ }
|
|
4004
|
+
hooks: {},
|
|
4005
|
+
});
|
|
4006
|
+
`;
|
|
4007
|
+
}
|
|
4008
|
+
function renderMinimalConfig(answers) {
|
|
4009
|
+
const lines = [];
|
|
4010
|
+
if (answers.preset !== DEFAULT_ANSWERS.preset) lines.push(` preset: ${JSON.stringify(answers.preset)},`);
|
|
4011
|
+
if (answers.maxPartBytes !== DEFAULT_ANSWERS.maxPartBytes) {
|
|
4012
|
+
lines.push(` maxPartBytes: ${size(answers.maxPartBytes)},`);
|
|
4013
|
+
}
|
|
4014
|
+
if (answers.largeFileBytes !== DEFAULT_ANSWERS.largeFileBytes) {
|
|
4015
|
+
lines.push(` largeFileBytes: ${size(answers.largeFileBytes)},`);
|
|
4016
|
+
}
|
|
4017
|
+
if (answers.include.length > 0) lines.push(` include: ${list(answers.include)},`);
|
|
4018
|
+
if (answers.excludePatterns.length > 0) {
|
|
4019
|
+
lines.push(` excludePatterns: ${list(answers.excludePatterns)},`);
|
|
4020
|
+
}
|
|
4021
|
+
if (answers.includeSensitiveFiles) lines.push(" includeSensitiveFiles: true,");
|
|
4022
|
+
if (answers.outDir !== DEFAULT_ANSWERS.outDir) lines.push(` outDir: ${JSON.stringify(answers.outDir)},`);
|
|
4023
|
+
const changed = Object.keys(answers.artifacts).filter(
|
|
4024
|
+
(name) => answers.artifacts[name] !== DEFAULT_ANSWERS.artifacts[name]
|
|
4025
|
+
);
|
|
4026
|
+
if (changed.length > 0) {
|
|
4027
|
+
const entries = changed.map((name) => ` ${name}: ${answers.artifacts[name]}`).join(",\n");
|
|
4028
|
+
lines.push(` artifacts: {
|
|
4029
|
+
${entries},
|
|
4030
|
+
},`);
|
|
4031
|
+
}
|
|
4032
|
+
if (lines.length === 0) {
|
|
4033
|
+
return `import { defineConfig } from "@ankurhalder/codebase-snapshot";
|
|
4034
|
+
|
|
4035
|
+
export default defineConfig({});
|
|
4036
|
+
`;
|
|
4037
|
+
}
|
|
4038
|
+
return `import { defineConfig } from "@ankurhalder/codebase-snapshot";
|
|
4039
|
+
|
|
4040
|
+
export default defineConfig({
|
|
4041
|
+
${lines.join("\n")}
|
|
4042
|
+
});
|
|
4043
|
+
`;
|
|
4044
|
+
}
|
|
4045
|
+
var PromptCancelled = class extends Error {
|
|
4046
|
+
constructor() {
|
|
4047
|
+
super("Prompt cancelled");
|
|
4048
|
+
this.name = "PromptCancelled";
|
|
4049
|
+
}
|
|
4050
|
+
};
|
|
4051
|
+
function defaultIO() {
|
|
4052
|
+
return { input: process.stdin, output: process.stdout };
|
|
4053
|
+
}
|
|
4054
|
+
function isInteractive(io) {
|
|
4055
|
+
return Boolean(io.input.isTTY && io.output.isTTY);
|
|
4056
|
+
}
|
|
4057
|
+
function moveSelection(current, delta, count) {
|
|
4058
|
+
if (count <= 0) return 0;
|
|
4059
|
+
return ((current + delta) % count + count) % count;
|
|
4060
|
+
}
|
|
4061
|
+
function toggleIndex(selected, index) {
|
|
4062
|
+
const next = new Set(selected);
|
|
4063
|
+
if (next.has(index)) next.delete(index);
|
|
4064
|
+
else next.add(index);
|
|
4065
|
+
return next;
|
|
4066
|
+
}
|
|
4067
|
+
function marker(active) {
|
|
4068
|
+
return active ? "\u276F" : " ";
|
|
4069
|
+
}
|
|
4070
|
+
function choiceLine(choice, active) {
|
|
4071
|
+
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
4072
|
+
return ` ${marker(active)} ${choice.label}${hint}`;
|
|
4073
|
+
}
|
|
4074
|
+
function renderSelect(message, choices, index) {
|
|
4075
|
+
const lines = [`\u25C6 ${message}`, ""];
|
|
4076
|
+
choices.forEach((choice, position) => lines.push(choiceLine(choice, position === index)));
|
|
4077
|
+
lines.push("", " \u2191\u2193 move \xB7 enter select");
|
|
4078
|
+
return lines.join("\n");
|
|
4079
|
+
}
|
|
4080
|
+
function renderMultiSelect(message, choices, index, selected) {
|
|
4081
|
+
const lines = [`\u25C6 ${message}`, ""];
|
|
4082
|
+
choices.forEach((choice, position) => {
|
|
4083
|
+
const box = selected.has(position) ? "\u25FC" : "\u25FB";
|
|
4084
|
+
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
4085
|
+
lines.push(` ${marker(position === index)} ${box} ${choice.label}${hint}`);
|
|
4086
|
+
});
|
|
4087
|
+
lines.push("", " \u2191\u2193 move \xB7 space toggle \xB7 enter confirm");
|
|
4088
|
+
return lines.join("\n");
|
|
4089
|
+
}
|
|
4090
|
+
function renderConfirm(message, value) {
|
|
4091
|
+
return [
|
|
4092
|
+
`\u25C6 ${message}`,
|
|
4093
|
+
"",
|
|
4094
|
+
` ${marker(value)} Yes`,
|
|
4095
|
+
` ${marker(!value)} No`,
|
|
4096
|
+
"",
|
|
4097
|
+
" \u2190\u2192 or y/n \xB7 enter confirm"
|
|
4098
|
+
].join("\n");
|
|
4099
|
+
}
|
|
4100
|
+
function renderText(message, value, error) {
|
|
4101
|
+
const lines = [`\u25C6 ${message}`, "", ` ${value || "\u258F"}`];
|
|
4102
|
+
if (error) lines.push("", ` \u2716 ${error}`);
|
|
4103
|
+
return lines.join("\n");
|
|
4104
|
+
}
|
|
4105
|
+
function keyReader(input) {
|
|
4106
|
+
const queue = [];
|
|
4107
|
+
const waiters = [];
|
|
4108
|
+
const wasRaw = input.isRaw;
|
|
4109
|
+
const onKey = (_text, key) => {
|
|
4110
|
+
const waiter = waiters.shift();
|
|
4111
|
+
if (waiter) waiter(key);
|
|
4112
|
+
else queue.push(key);
|
|
4113
|
+
};
|
|
4114
|
+
readline__namespace.emitKeypressEvents(input);
|
|
4115
|
+
if (input.isTTY) input.setRawMode(true);
|
|
4116
|
+
input.resume();
|
|
4117
|
+
input.on("keypress", onKey);
|
|
4118
|
+
return {
|
|
4119
|
+
next() {
|
|
4120
|
+
const queued = queue.shift();
|
|
4121
|
+
if (queued) return Promise.resolve(queued);
|
|
4122
|
+
return new Promise((resolve5) => waiters.push(resolve5));
|
|
4123
|
+
},
|
|
4124
|
+
close() {
|
|
4125
|
+
input.off("keypress", onKey);
|
|
4126
|
+
if (input.isTTY) input.setRawMode(wasRaw ?? false);
|
|
4127
|
+
input.pause();
|
|
4128
|
+
}
|
|
4129
|
+
};
|
|
4130
|
+
}
|
|
4131
|
+
var Frame = class {
|
|
4132
|
+
constructor(output) {
|
|
4133
|
+
this.output = output;
|
|
4134
|
+
}
|
|
4135
|
+
output;
|
|
4136
|
+
drawn = 0;
|
|
4137
|
+
draw(text2) {
|
|
4138
|
+
this.erase();
|
|
4139
|
+
this.output.write(`${text2}
|
|
4140
|
+
`);
|
|
4141
|
+
this.drawn = text2.split("\n").length;
|
|
4142
|
+
}
|
|
4143
|
+
erase() {
|
|
4144
|
+
if (this.drawn > 0) this.output.write(`\x1B[${this.drawn}A\x1B[0J`);
|
|
4145
|
+
this.drawn = 0;
|
|
4146
|
+
}
|
|
4147
|
+
};
|
|
4148
|
+
function isCancel(key) {
|
|
4149
|
+
return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
|
|
4150
|
+
}
|
|
4151
|
+
async function select(io, message, choices, options = {}) {
|
|
4152
|
+
if (choices.length === 0) throw new Error("select() requires at least one choice.");
|
|
4153
|
+
const fallbackIndex = Math.max(
|
|
4154
|
+
0,
|
|
4155
|
+
choices.findIndex((choice) => choice.value === options.defaultValue)
|
|
4156
|
+
);
|
|
4157
|
+
if (!isInteractive(io)) {
|
|
4158
|
+
io.output.write(`${renderSelect(message, choices, fallbackIndex)}
|
|
4159
|
+
`);
|
|
4160
|
+
return choices[fallbackIndex].value;
|
|
4161
|
+
}
|
|
4162
|
+
const frame = new Frame(io.output);
|
|
4163
|
+
const reader = keyReader(io.input);
|
|
4164
|
+
let index = fallbackIndex;
|
|
4165
|
+
try {
|
|
4166
|
+
for (; ; ) {
|
|
4167
|
+
frame.draw(renderSelect(message, choices, index));
|
|
4168
|
+
const key = await reader.next();
|
|
4169
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4170
|
+
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
4171
|
+
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
4172
|
+
else if (key.name === "return" || key.name === "enter") {
|
|
4173
|
+
frame.erase();
|
|
4174
|
+
return choices[index].value;
|
|
4175
|
+
} else if (key.name === "j") index = moveSelection(index, 1, choices.length);
|
|
4176
|
+
else if (key.name === "k") index = moveSelection(index, -1, choices.length);
|
|
4177
|
+
}
|
|
4178
|
+
} finally {
|
|
4179
|
+
reader.close();
|
|
4180
|
+
}
|
|
4181
|
+
}
|
|
4182
|
+
async function multiSelect(io, message, choices, options = {}) {
|
|
4183
|
+
const selected = /* @__PURE__ */ new Set();
|
|
4184
|
+
choices.forEach((choice, position) => {
|
|
4185
|
+
if (options.defaults?.includes(choice.value)) selected.add(position);
|
|
4186
|
+
});
|
|
4187
|
+
const result = () => choices.filter((_, position) => selected.has(position)).map((choice) => choice.value);
|
|
4188
|
+
if (!isInteractive(io)) {
|
|
4189
|
+
io.output.write(`${renderMultiSelect(message, choices, 0, selected)}
|
|
4190
|
+
`);
|
|
4191
|
+
return result();
|
|
4192
|
+
}
|
|
4193
|
+
const frame = new Frame(io.output);
|
|
4194
|
+
const reader = keyReader(io.input);
|
|
4195
|
+
let index = 0;
|
|
4196
|
+
try {
|
|
4197
|
+
for (; ; ) {
|
|
4198
|
+
frame.draw(renderMultiSelect(message, choices, index, selected));
|
|
4199
|
+
const key = await reader.next();
|
|
4200
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4201
|
+
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
4202
|
+
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
4203
|
+
else if (key.name === "space") {
|
|
4204
|
+
const next = toggleIndex(selected, index);
|
|
4205
|
+
selected.clear();
|
|
4206
|
+
for (const position of next) selected.add(position);
|
|
4207
|
+
} else if (key.name === "a") {
|
|
4208
|
+
if (selected.size === choices.length) selected.clear();
|
|
4209
|
+
else choices.forEach((_, position) => selected.add(position));
|
|
4210
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
4211
|
+
frame.erase();
|
|
4212
|
+
return result();
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
} finally {
|
|
4216
|
+
reader.close();
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
async function confirm(io, message, options = {}) {
|
|
4220
|
+
let value = options.defaultValue ?? true;
|
|
4221
|
+
if (!isInteractive(io)) {
|
|
4222
|
+
io.output.write(`${renderConfirm(message, value)}
|
|
4223
|
+
`);
|
|
4224
|
+
return value;
|
|
4225
|
+
}
|
|
4226
|
+
const frame = new Frame(io.output);
|
|
4227
|
+
const reader = keyReader(io.input);
|
|
4228
|
+
try {
|
|
4229
|
+
for (; ; ) {
|
|
4230
|
+
frame.draw(renderConfirm(message, value));
|
|
4231
|
+
const key = await reader.next();
|
|
4232
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4233
|
+
if (key.name === "up" || key.name === "left") value = true;
|
|
4234
|
+
else if (key.name === "down" || key.name === "right") value = false;
|
|
4235
|
+
else if (key.name === "y") value = true;
|
|
4236
|
+
else if (key.name === "n") value = false;
|
|
4237
|
+
else if (key.name === "return" || key.name === "enter") {
|
|
4238
|
+
frame.erase();
|
|
4239
|
+
return value;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
} finally {
|
|
4243
|
+
reader.close();
|
|
4244
|
+
}
|
|
4245
|
+
}
|
|
4246
|
+
async function text(io, message, options = {}) {
|
|
4247
|
+
const fallback = options.defaultValue ?? "";
|
|
4248
|
+
if (!isInteractive(io)) {
|
|
4249
|
+
io.output.write(`${renderText(message, fallback)}
|
|
4250
|
+
`);
|
|
4251
|
+
return fallback;
|
|
4252
|
+
}
|
|
4253
|
+
const frame = new Frame(io.output);
|
|
4254
|
+
const reader = keyReader(io.input);
|
|
4255
|
+
let value = fallback;
|
|
4256
|
+
let error;
|
|
4257
|
+
try {
|
|
4258
|
+
for (; ; ) {
|
|
4259
|
+
frame.draw(renderText(message, value, error));
|
|
4260
|
+
const key = await reader.next();
|
|
4261
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4262
|
+
error = void 0;
|
|
4263
|
+
if (key.name === "return" || key.name === "enter") {
|
|
4264
|
+
const problem = options.validate ? options.validate(value) : null;
|
|
4265
|
+
if (problem) {
|
|
4266
|
+
error = problem;
|
|
4267
|
+
continue;
|
|
4268
|
+
}
|
|
4269
|
+
frame.erase();
|
|
4270
|
+
return value;
|
|
4271
|
+
}
|
|
4272
|
+
if (key.name === "backspace") {
|
|
4273
|
+
value = value.slice(0, -1);
|
|
4274
|
+
continue;
|
|
4275
|
+
}
|
|
4276
|
+
if (key.name === "escape") {
|
|
4277
|
+
value = "";
|
|
4278
|
+
continue;
|
|
4279
|
+
}
|
|
4280
|
+
if (_isPrintable(key)) value += _keyText(key);
|
|
4281
|
+
}
|
|
4282
|
+
} finally {
|
|
4283
|
+
reader.close();
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
function _isPrintable(key) {
|
|
4287
|
+
return typeof key.sequence === "string" && key.sequence.length === 1 && key.sequence >= " ";
|
|
4288
|
+
}
|
|
4289
|
+
function _keyText(key) {
|
|
4290
|
+
return key.sequence ?? "";
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
// src/wizard/init.ts
|
|
4294
|
+
var CONFIG_CANDIDATES = [
|
|
4295
|
+
"snapshot.config.ts",
|
|
4296
|
+
"snapshot.config.js",
|
|
4297
|
+
"snapshot.config.mjs",
|
|
4298
|
+
"snapshot.config.cjs",
|
|
4299
|
+
"snapshot.config.json",
|
|
4300
|
+
".snapshotrc.json",
|
|
4301
|
+
".snapshotrc"
|
|
4302
|
+
];
|
|
4303
|
+
var SIZE_CHOICES = [
|
|
4304
|
+
{ value: "60k", label: "Small", hint: "60 KB \xB7 ~15k tokens \xB7 tight context windows" },
|
|
4305
|
+
{ value: "120k", label: "Medium", hint: "120 KB \xB7 ~30k tokens \xB7 recommended" },
|
|
4306
|
+
{ value: "300k", label: "Large", hint: "300 KB \xB7 ~75k tokens \xB7 the default" },
|
|
4307
|
+
{ value: "custom", label: "Custom\u2026", hint: "type a size like 150k or 1.5mb" }
|
|
4308
|
+
];
|
|
4309
|
+
function existingConfig(cwd) {
|
|
4310
|
+
for (const name of CONFIG_CANDIDATES) {
|
|
4311
|
+
if (fs9__namespace.existsSync(path10__namespace.join(cwd, name))) return name;
|
|
4312
|
+
}
|
|
4313
|
+
return null;
|
|
4314
|
+
}
|
|
4315
|
+
function artifactAnswers(options) {
|
|
4316
|
+
const artifacts = { ...DEFAULT_ANSWERS.artifacts };
|
|
4317
|
+
for (const name of options.skipArtifacts ?? []) {
|
|
4318
|
+
if (ARTIFACT_NAMES.includes(name)) {
|
|
4319
|
+
artifacts[name] = false;
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
if (options.onlyArtifacts && options.onlyArtifacts.length > 0) {
|
|
4323
|
+
const keep = new Set(options.onlyArtifacts);
|
|
4324
|
+
for (const name of ARTIFACT_NAMES) artifacts[name] = keep.has(name);
|
|
4325
|
+
}
|
|
4326
|
+
return artifacts;
|
|
4327
|
+
}
|
|
4328
|
+
function topLevelDirs(cwd) {
|
|
4329
|
+
try {
|
|
4330
|
+
return fs9__namespace.readdirSync(cwd, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
|
|
4331
|
+
} catch {
|
|
4332
|
+
return [];
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
function addScripts(cwd, notes) {
|
|
4336
|
+
const packagePath = path10__namespace.join(cwd, "package.json");
|
|
4337
|
+
if (!fs9__namespace.existsSync(packagePath)) {
|
|
4338
|
+
notes.push("No package.json found, so npm scripts were not added.");
|
|
4339
|
+
return [];
|
|
4340
|
+
}
|
|
4341
|
+
let parsed;
|
|
4342
|
+
try {
|
|
4343
|
+
parsed = JSON.parse(fs9__namespace.readFileSync(packagePath, "utf8"));
|
|
4344
|
+
} catch {
|
|
4345
|
+
notes.push("Could not parse package.json; npm scripts were not added.");
|
|
4346
|
+
return [];
|
|
4347
|
+
}
|
|
4348
|
+
const scripts = parsed.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {};
|
|
4349
|
+
const added = [];
|
|
4350
|
+
if (!scripts.snapshot) {
|
|
4351
|
+
scripts.snapshot = "codebase-snapshot generate";
|
|
4352
|
+
added.push("snapshot");
|
|
4353
|
+
}
|
|
4354
|
+
if (!scripts["snapshot:validate"]) {
|
|
4355
|
+
scripts["snapshot:validate"] = "codebase-snapshot validate";
|
|
4356
|
+
added.push("snapshot:validate");
|
|
4357
|
+
}
|
|
4358
|
+
if (added.length === 0) return [];
|
|
4359
|
+
parsed.scripts = scripts;
|
|
4360
|
+
fs9__namespace.writeFileSync(packagePath, `${JSON.stringify(parsed, null, 2)}
|
|
4361
|
+
`, "utf8");
|
|
4362
|
+
return added;
|
|
4363
|
+
}
|
|
4364
|
+
function addGitignore(cwd) {
|
|
4365
|
+
const ignorePath = path10__namespace.join(cwd, ".gitignore");
|
|
4366
|
+
const existing = fs9__namespace.existsSync(ignorePath) ? fs9__namespace.readFileSync(ignorePath, "utf8") : "";
|
|
4367
|
+
const wanted = ["snapshot/", "snapshot.tmp/"];
|
|
4368
|
+
const added = wanted.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4369
|
+
if (added.length === 0) return [];
|
|
4370
|
+
const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
4371
|
+
fs9__namespace.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
|
|
4372
|
+
`, "utf8");
|
|
4373
|
+
return added;
|
|
4374
|
+
}
|
|
4375
|
+
function resolvePresetChoice(answers, presetName) {
|
|
4376
|
+
return {
|
|
4377
|
+
value: presetName,
|
|
4378
|
+
label: presetName,
|
|
4379
|
+
hint: presetName === answers.preset ? "detected" : void 0
|
|
4380
|
+
};
|
|
4381
|
+
}
|
|
4382
|
+
async function runInit(options) {
|
|
4383
|
+
const io = options.io ?? defaultIO();
|
|
4384
|
+
const detected = detectPreset(options.cwd, options.preset);
|
|
4385
|
+
const notes = [];
|
|
4386
|
+
const answers = {
|
|
4387
|
+
preset: detected.presetName,
|
|
4388
|
+
maxPartBytes: options.maxPartBytes ?? detected.recommendedBudget ?? DEFAULT_ANSWERS.maxPartBytes,
|
|
4389
|
+
largeFileBytes: DEFAULT_ANSWERS.largeFileBytes,
|
|
4390
|
+
include: options.include ?? [],
|
|
4391
|
+
excludePatterns: options.exclude ?? [],
|
|
4392
|
+
artifacts: artifactAnswers(options),
|
|
4393
|
+
includeSensitiveFiles: options.includeSensitiveFiles ?? DEFAULT_ANSWERS.includeSensitiveFiles,
|
|
4394
|
+
outDir: options.outDir ?? DEFAULT_ANSWERS.outDir
|
|
4395
|
+
};
|
|
4396
|
+
const interactive = isInteractive(io) && options.yes !== true;
|
|
4397
|
+
if (interactive) {
|
|
4398
|
+
io.output.write("\n @ankurhalder/codebase-snapshot\n");
|
|
4399
|
+
io.output.write(" Turn this repository into an LLM-ready, redacted, byte-bounded snapshot.\n\n");
|
|
4400
|
+
io.output.write(` Detected ${detected.presetName}
|
|
4401
|
+
`);
|
|
4402
|
+
io.output.write(` Directory ${options.cwd}
|
|
4403
|
+
|
|
4404
|
+
`);
|
|
4405
|
+
const useDetected = await confirm(io, `Set up for ${detected.presetName}?`, {
|
|
4406
|
+
defaultValue: true
|
|
4407
|
+
});
|
|
4408
|
+
if (!useDetected) {
|
|
4409
|
+
answers.preset = await select(
|
|
4410
|
+
io,
|
|
4411
|
+
"What kind of project is this?",
|
|
4412
|
+
listPresetNames().map((name) => resolvePresetChoice(answers, name))
|
|
4413
|
+
);
|
|
4414
|
+
const chosen = detectPreset(options.cwd, answers.preset);
|
|
4415
|
+
answers.preset = chosen.presetName;
|
|
4416
|
+
}
|
|
4417
|
+
const scope = await select(
|
|
4418
|
+
io,
|
|
4419
|
+
"What should go into the snapshot?",
|
|
4420
|
+
[
|
|
4421
|
+
{ value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
|
|
4422
|
+
{ value: "folders", label: "Only specific folders\u2026" },
|
|
4423
|
+
{ value: "no-tests", label: "Everything, but skip tests" },
|
|
4424
|
+
{ value: "custom", label: "Let me write glob patterns" }
|
|
4425
|
+
],
|
|
4426
|
+
{ defaultValue: "all" }
|
|
4427
|
+
);
|
|
4428
|
+
if (scope === "folders") {
|
|
4429
|
+
const dirs = topLevelDirs(options.cwd).slice(0, 40);
|
|
4430
|
+
if (dirs.length === 0) {
|
|
4431
|
+
notes.push("No top-level directories found; the snapshot will include everything.");
|
|
4432
|
+
} else {
|
|
4433
|
+
const chosen = await multiSelect(
|
|
4434
|
+
io,
|
|
4435
|
+
"Select folders",
|
|
4436
|
+
dirs.map((name) => ({ value: name, label: `${name}/` })),
|
|
4437
|
+
{ defaults: dirs }
|
|
4438
|
+
);
|
|
4439
|
+
answers.include = chosen.map((name) => `${name}/**`);
|
|
4440
|
+
}
|
|
4441
|
+
} else if (scope === "no-tests") {
|
|
4442
|
+
answers.excludePatterns = [
|
|
4443
|
+
...answers.excludePatterns,
|
|
4444
|
+
"**/*.test.*",
|
|
4445
|
+
"**/*.spec.*",
|
|
4446
|
+
"tests",
|
|
4447
|
+
"test"
|
|
4448
|
+
];
|
|
4449
|
+
} else if (scope === "custom") {
|
|
4450
|
+
const raw = await text(io, "Glob patterns to include (comma separated)", {
|
|
4451
|
+
defaultValue: ""
|
|
4452
|
+
});
|
|
4453
|
+
answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
4454
|
+
}
|
|
4455
|
+
const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
|
|
4456
|
+
defaultValue: "120k"
|
|
4457
|
+
});
|
|
4458
|
+
if (sizeChoice === "custom") {
|
|
4459
|
+
const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
|
|
4460
|
+
defaultValue: String(answers.maxPartBytes),
|
|
4461
|
+
validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
|
|
4462
|
+
});
|
|
4463
|
+
answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
|
|
4464
|
+
} else {
|
|
4465
|
+
answers.maxPartBytes = sizeChoice;
|
|
4466
|
+
}
|
|
4467
|
+
const artifactChoices = [
|
|
4468
|
+
{ value: "readme", label: "README.md", hint: "orientation page" },
|
|
4469
|
+
{ value: "manifest", label: "manifest.json", hint: "required by validate and report" },
|
|
4470
|
+
{ value: "parts", label: "parts/", hint: "the code payload" },
|
|
4471
|
+
{ value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
|
|
4472
|
+
{ value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
|
|
4473
|
+
{ value: "audit", label: "reports/audit.md", hint: "security and health findings" },
|
|
4474
|
+
{ value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
|
|
4475
|
+
];
|
|
4476
|
+
const presetEnabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
|
|
4477
|
+
const selectedArtifacts = await multiSelect(
|
|
4478
|
+
io,
|
|
4479
|
+
"Which artifacts should be generated?",
|
|
4480
|
+
artifactChoices,
|
|
4481
|
+
{ defaults: presetEnabled }
|
|
4482
|
+
);
|
|
4483
|
+
for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
|
|
4484
|
+
const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
|
|
4485
|
+
answers.includeSensitiveFiles = await confirm(
|
|
4486
|
+
io,
|
|
4487
|
+
"Include credential files (.env, *.pem, .npmrc, .ssh/)?",
|
|
4488
|
+
{ defaultValue: false }
|
|
4489
|
+
);
|
|
4490
|
+
io.output.write("\n Review\n\n");
|
|
4491
|
+
io.output.write(` project ${answers.preset}
|
|
4492
|
+
`);
|
|
4493
|
+
io.output.write(` include ${answers.include.length ? answers.include.join(", ") : "everything"}
|
|
4494
|
+
`);
|
|
4495
|
+
io.output.write(` part size ${String(answers.maxPartBytes)}
|
|
4496
|
+
`);
|
|
4497
|
+
io.output.write(` artifacts ${enabledCount} of ${ARTIFACT_NAMES.length}
|
|
4498
|
+
`);
|
|
4499
|
+
io.output.write(` credentials ${answers.includeSensitiveFiles ? "included" : "skipped"}
|
|
4500
|
+
|
|
4501
|
+
`);
|
|
4502
|
+
io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
|
|
4503
|
+
io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
|
|
4504
|
+
const proceed = await confirm(io, "Write this and continue?", { defaultValue: true });
|
|
4505
|
+
if (!proceed) throw new PromptCancelled();
|
|
4506
|
+
}
|
|
4507
|
+
const configText = options.minimal ? renderMinimalConfig(answers) : renderConfig(answers);
|
|
4508
|
+
const configName = supportsTypeScriptImports() ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
4509
|
+
const configPath = path10__namespace.join(options.cwd, configName);
|
|
4510
|
+
if (options.dryRun) {
|
|
4511
|
+
return {
|
|
4512
|
+
configPath,
|
|
4513
|
+
configText,
|
|
4514
|
+
written: false,
|
|
4515
|
+
preset: answers.preset,
|
|
4516
|
+
scripts: [],
|
|
4517
|
+
gitignoreEntries: [],
|
|
4518
|
+
notes
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
const present = existingConfig(options.cwd);
|
|
4522
|
+
if (present && present !== configName && !options.force) {
|
|
4523
|
+
throw new Error(
|
|
4524
|
+
`A configuration already exists (${present}). Re-run with --force to replace it.`
|
|
4525
|
+
);
|
|
4526
|
+
}
|
|
4527
|
+
if (fs9__namespace.existsSync(configPath) && !options.force) {
|
|
4528
|
+
throw new Error(`${configName} already exists. Re-run with --force to replace it.`);
|
|
4529
|
+
}
|
|
4530
|
+
fs9__namespace.writeFileSync(configPath, configText, "utf8");
|
|
4531
|
+
const scripts = addScripts(options.cwd, notes);
|
|
4532
|
+
const gitignoreEntries = addGitignore(options.cwd);
|
|
4533
|
+
return {
|
|
4534
|
+
configPath,
|
|
4535
|
+
configText,
|
|
4536
|
+
written: true,
|
|
4537
|
+
preset: answers.preset,
|
|
4538
|
+
scripts,
|
|
4539
|
+
gitignoreEntries,
|
|
4540
|
+
notes
|
|
4541
|
+
};
|
|
4542
|
+
}
|
|
4543
|
+
|
|
2712
4544
|
// src/index.ts
|
|
2713
4545
|
var GENERATOR_NAME = "@ankurhalder/codebase-snapshot";
|
|
2714
|
-
var GENERATOR_VERSION = "2.
|
|
4546
|
+
var GENERATOR_VERSION = "2.1.0";
|
|
2715
4547
|
var SCHEMA_VERSION = "3.0.0";
|
|
2716
4548
|
function sha256Hex2(input) {
|
|
2717
4549
|
return crypto__namespace.createHash("sha256").update(input).digest("hex");
|
|
2718
4550
|
}
|
|
2719
|
-
var
|
|
4551
|
+
var TS_EXTENSIONS = [
|
|
2720
4552
|
".ts",
|
|
2721
4553
|
".mts",
|
|
2722
4554
|
".cts",
|
|
@@ -2724,24 +4556,21 @@ var SUMMARY_EXTENSIONS = [
|
|
|
2724
4556
|
".js",
|
|
2725
4557
|
".mjs",
|
|
2726
4558
|
".cjs",
|
|
2727
|
-
".jsx"
|
|
2728
|
-
".json",
|
|
2729
|
-
".jsonc",
|
|
2730
|
-
".md",
|
|
2731
|
-
".mdx"
|
|
4559
|
+
".jsx"
|
|
2732
4560
|
];
|
|
2733
4561
|
function summarizeFile(relativePath, redactedContent) {
|
|
2734
4562
|
const lower = relativePath.toLowerCase();
|
|
2735
|
-
if (!SUMMARY_EXTENSIONS.some((extension) => lower.endsWith(extension))) {
|
|
2736
|
-
return void 0;
|
|
2737
|
-
}
|
|
2738
4563
|
if (lower.endsWith(".json") || lower.endsWith(".jsonc")) {
|
|
2739
|
-
return summarizeJson(redactedContent);
|
|
4564
|
+
return { summary: summarizeJson(redactedContent), quality: "structural" };
|
|
2740
4565
|
}
|
|
2741
4566
|
if (lower.endsWith(".md") || lower.endsWith(".mdx")) {
|
|
2742
|
-
return summarizeMarkdown(redactedContent);
|
|
4567
|
+
return { summary: summarizeMarkdown(redactedContent), quality: "structural" };
|
|
4568
|
+
}
|
|
4569
|
+
if (TS_EXTENSIONS.some((extension) => lower.endsWith(extension))) {
|
|
4570
|
+
return { summary: summarizeTypeScriptSource(relativePath, redactedContent), quality: "ast" };
|
|
2743
4571
|
}
|
|
2744
|
-
|
|
4572
|
+
const profiled = summarizeWithProfile(relativePath, redactedContent);
|
|
4573
|
+
return profiled ? { summary: profiled.summary, quality: profiled.quality } : void 0;
|
|
2745
4574
|
}
|
|
2746
4575
|
function isSourceSummary(summary) {
|
|
2747
4576
|
return Boolean(summary && typeof summary === "object" && "imports" in summary);
|
|
@@ -2767,8 +4596,8 @@ var IMPORT_CANDIDATE_SUFFIXES = [
|
|
|
2767
4596
|
];
|
|
2768
4597
|
function resolveSpecifier(fromFile, specifier, knownFiles) {
|
|
2769
4598
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
2770
|
-
const baseDir =
|
|
2771
|
-
const joined =
|
|
4599
|
+
const baseDir = path10__namespace.posix.dirname(fromFile);
|
|
4600
|
+
const joined = path10__namespace.posix.normalize(path10__namespace.posix.join(baseDir, specifier));
|
|
2772
4601
|
const candidate = joined.replace(/\.(?:js|mjs|cjs|jsx)$/, "");
|
|
2773
4602
|
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
2774
4603
|
const withSuffix = `${joined}${suffix}`;
|
|
@@ -2860,11 +4689,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
2860
4689
|
return errors;
|
|
2861
4690
|
}
|
|
2862
4691
|
function resolveSafeOutputDirs(config) {
|
|
2863
|
-
const cwd =
|
|
2864
|
-
const outDir =
|
|
2865
|
-
const tmpDir =
|
|
2866
|
-
const root =
|
|
2867
|
-
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${
|
|
4692
|
+
const cwd = path10__namespace.resolve(config.cwd);
|
|
4693
|
+
const outDir = path10__namespace.resolve(cwd, config.outDir);
|
|
4694
|
+
const tmpDir = path10__namespace.resolve(cwd, config.tmpDir);
|
|
4695
|
+
const root = path10__namespace.parse(cwd).root;
|
|
4696
|
+
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path10__namespace.sep}`);
|
|
2868
4697
|
const reject = (reason) => {
|
|
2869
4698
|
throw new Error(
|
|
2870
4699
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -2883,26 +4712,66 @@ function resolveSafeOutputDirs(config) {
|
|
|
2883
4712
|
if (contains(tmpDir, outDir)) reject("outDir is inside tmpDir");
|
|
2884
4713
|
return { outDir, tmpDir };
|
|
2885
4714
|
}
|
|
4715
|
+
var PLACEHOLDER_ENTRIES = /* @__PURE__ */ new Set([".gitkeep", ".gitignore", ".DS_Store"]);
|
|
4716
|
+
var OWNERSHIP_MARKER = ".codebase-snapshot";
|
|
4717
|
+
function writeOwnershipMarker(dir) {
|
|
4718
|
+
const marker2 = {
|
|
4719
|
+
generator: GENERATOR_NAME,
|
|
4720
|
+
version: GENERATOR_VERSION,
|
|
4721
|
+
schemaVersion: SCHEMA_VERSION
|
|
4722
|
+
};
|
|
4723
|
+
fs9__namespace.writeFileSync(path10__namespace.join(dir, OWNERSHIP_MARKER), `${JSON.stringify(marker2, null, 2)}
|
|
4724
|
+
`, "utf8");
|
|
4725
|
+
}
|
|
4726
|
+
function isOwnOutput(dir) {
|
|
4727
|
+
if (fs9__namespace.existsSync(path10__namespace.join(dir, OWNERSHIP_MARKER))) return true;
|
|
4728
|
+
const manifestPath = path10__namespace.join(dir, "manifest.json");
|
|
4729
|
+
if (!fs9__namespace.existsSync(manifestPath)) return false;
|
|
4730
|
+
try {
|
|
4731
|
+
const parsed = JSON.parse(fs9__namespace.readFileSync(manifestPath, "utf8"));
|
|
4732
|
+
const generator = parsed.generator;
|
|
4733
|
+
return typeof parsed.schemaVersion === "string" && Array.isArray(parsed.files) && typeof generator === "object" && generator !== null && generator.name === GENERATOR_NAME;
|
|
4734
|
+
} catch {
|
|
4735
|
+
return false;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
function assertReplaceable(target, label) {
|
|
4739
|
+
if (!fs9__namespace.existsSync(target)) return;
|
|
4740
|
+
if (!fs9__namespace.statSync(target).isDirectory()) {
|
|
4741
|
+
throw new Error(
|
|
4742
|
+
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
4743
|
+
);
|
|
4744
|
+
}
|
|
4745
|
+
const entries = fs9__namespace.readdirSync(target);
|
|
4746
|
+
const meaningful = entries.filter((name) => !PLACEHOLDER_ENTRIES.has(name));
|
|
4747
|
+
if (meaningful.length === 0) return;
|
|
4748
|
+
if (isOwnOutput(target)) return;
|
|
4749
|
+
const preview = meaningful.slice(0, 5).join(", ");
|
|
4750
|
+
const more = meaningful.length > 5 ? `, and ${meaningful.length - 5} more` : "";
|
|
4751
|
+
throw new Error(
|
|
4752
|
+
`Refusing to replace ${label} ${target}: it exists, is not empty, and does not look like a snapshot this generator wrote (found ${preview}${more}). Point ${label} at a new or empty directory, or delete it yourself first.`
|
|
4753
|
+
);
|
|
4754
|
+
}
|
|
2886
4755
|
function swapIntoPlace(tmpDir, outDir) {
|
|
2887
4756
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
2888
|
-
const hadPrevious =
|
|
2889
|
-
if (hadPrevious)
|
|
4757
|
+
const hadPrevious = fs9__namespace.existsSync(outDir);
|
|
4758
|
+
if (hadPrevious) fs9__namespace.renameSync(outDir, backupDir);
|
|
2890
4759
|
try {
|
|
2891
4760
|
try {
|
|
2892
|
-
|
|
4761
|
+
fs9__namespace.renameSync(tmpDir, outDir);
|
|
2893
4762
|
} catch (err) {
|
|
2894
4763
|
if (err.code === "EXDEV") {
|
|
2895
|
-
|
|
2896
|
-
|
|
4764
|
+
fs9__namespace.cpSync(tmpDir, outDir, { recursive: true });
|
|
4765
|
+
fs9__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
2897
4766
|
} else {
|
|
2898
4767
|
throw err;
|
|
2899
4768
|
}
|
|
2900
4769
|
}
|
|
2901
|
-
if (hadPrevious)
|
|
4770
|
+
if (hadPrevious) fs9__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
2902
4771
|
} catch (err) {
|
|
2903
|
-
if (hadPrevious &&
|
|
2904
|
-
if (
|
|
2905
|
-
|
|
4772
|
+
if (hadPrevious && fs9__namespace.existsSync(backupDir)) {
|
|
4773
|
+
if (fs9__namespace.existsSync(outDir)) fs9__namespace.rmSync(outDir, { recursive: true, force: true });
|
|
4774
|
+
fs9__namespace.renameSync(backupDir, outDir);
|
|
2906
4775
|
}
|
|
2907
4776
|
throw err;
|
|
2908
4777
|
}
|
|
@@ -2966,7 +4835,7 @@ function generateSnapshot(configInput, options) {
|
|
|
2966
4835
|
const gitInfo = getGitInfo(config.cwd, config.branch);
|
|
2967
4836
|
const collection = collectFiles(config.cwd, {
|
|
2968
4837
|
includePatterns: config.include,
|
|
2969
|
-
excludePatterns: config.excludePatterns,
|
|
4838
|
+
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
2970
4839
|
includeSensitiveFiles: config.includeSensitiveFiles
|
|
2971
4840
|
});
|
|
2972
4841
|
const files = [];
|
|
@@ -2979,10 +4848,10 @@ function generateSnapshot(configInput, options) {
|
|
|
2979
4848
|
let totalSizeBytes = 0;
|
|
2980
4849
|
let largeFileCount = 0;
|
|
2981
4850
|
for (const relativePath of collection.files) {
|
|
2982
|
-
const absolutePath =
|
|
4851
|
+
const absolutePath = path10__namespace.join(config.cwd, relativePath);
|
|
2983
4852
|
let stats;
|
|
2984
4853
|
try {
|
|
2985
|
-
stats =
|
|
4854
|
+
stats = fs9__namespace.statSync(absolutePath);
|
|
2986
4855
|
if (!stats.isFile()) {
|
|
2987
4856
|
skipped.push({ path: relativePath, reason: "not-file" });
|
|
2988
4857
|
continue;
|
|
@@ -2997,7 +4866,7 @@ function generateSnapshot(configInput, options) {
|
|
|
2997
4866
|
}
|
|
2998
4867
|
let buffer;
|
|
2999
4868
|
try {
|
|
3000
|
-
buffer =
|
|
4869
|
+
buffer = fs9__namespace.readFileSync(absolutePath);
|
|
3001
4870
|
} catch {
|
|
3002
4871
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
3003
4872
|
continue;
|
|
@@ -3007,10 +4876,10 @@ function generateSnapshot(configInput, options) {
|
|
|
3007
4876
|
const lineCount = countLines(rawContent);
|
|
3008
4877
|
totalLines += lineCount;
|
|
3009
4878
|
totalSizeBytes += stats.size;
|
|
3010
|
-
const
|
|
4879
|
+
const layer2 = classifyLayer(relativePath, config.layers);
|
|
3011
4880
|
const kind = classifyKind(relativePath, config.kinds);
|
|
3012
4881
|
const language = detectLanguage(relativePath);
|
|
3013
|
-
bump(layerCounts,
|
|
4882
|
+
bump(layerCounts, layer2);
|
|
3014
4883
|
bump(kindCounts, kind);
|
|
3015
4884
|
const { redacted, findings } = scanAndRedactSecrets(
|
|
3016
4885
|
rawContent,
|
|
@@ -3034,8 +4903,11 @@ function generateSnapshot(configInput, options) {
|
|
|
3034
4903
|
includedLines = preview.includedLines;
|
|
3035
4904
|
}
|
|
3036
4905
|
let summary;
|
|
4906
|
+
let summaryQuality;
|
|
3037
4907
|
try {
|
|
3038
|
-
|
|
4908
|
+
const outcome = summarizeFile(relativePath, redacted);
|
|
4909
|
+
summary = outcome?.summary;
|
|
4910
|
+
summaryQuality = outcome?.quality;
|
|
3039
4911
|
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
3040
4912
|
warnings.push(
|
|
3041
4913
|
`Parse errors in ${relativePath} (${summary.parseErrors}); its summary is incomplete.`
|
|
@@ -3052,7 +4924,7 @@ function generateSnapshot(configInput, options) {
|
|
|
3052
4924
|
lines: lineCount,
|
|
3053
4925
|
hashSha256,
|
|
3054
4926
|
part: "",
|
|
3055
|
-
layer,
|
|
4927
|
+
layer: layer2,
|
|
3056
4928
|
kind,
|
|
3057
4929
|
language,
|
|
3058
4930
|
inclusionMode: isPreviewTruncated ? "preview" : "full",
|
|
@@ -3062,6 +4934,7 @@ function generateSnapshot(configInput, options) {
|
|
|
3062
4934
|
includedLines,
|
|
3063
4935
|
includedBytes: Buffer.byteLength(finalContent, "utf8"),
|
|
3064
4936
|
summary,
|
|
4937
|
+
summaryQuality,
|
|
3065
4938
|
content: finalContent
|
|
3066
4939
|
});
|
|
3067
4940
|
}
|
|
@@ -3102,10 +4975,11 @@ function generateSnapshot(configInput, options) {
|
|
|
3102
4975
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
3103
4976
|
artifacts: { ...config.artifacts },
|
|
3104
4977
|
include: [...config.include],
|
|
4978
|
+
preset: config.preset,
|
|
3105
4979
|
layersMode: config.layersMode,
|
|
3106
4980
|
budgets: { default: config.budgets.default, byLayer: { ...config.budgets.byLayer } },
|
|
3107
4981
|
excludePatterns: [...config.excludePatterns],
|
|
3108
|
-
layers: config.layers.map((
|
|
4982
|
+
layers: config.layers.map((layer2) => layer2.name),
|
|
3109
4983
|
kinds: config.kinds.map((kind) => kind.kind)
|
|
3110
4984
|
},
|
|
3111
4985
|
counts: {
|
|
@@ -3192,45 +5066,48 @@ function generateSnapshot(configInput, options) {
|
|
|
3192
5066
|
return manifest;
|
|
3193
5067
|
}
|
|
3194
5068
|
const { outDir, tmpDir } = resolveSafeOutputDirs(config);
|
|
5069
|
+
assertReplaceable(tmpDir, "tmpDir");
|
|
5070
|
+
assertReplaceable(outDir, "outDir");
|
|
3195
5071
|
const { artifacts } = config;
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
const
|
|
3200
|
-
|
|
5072
|
+
fs9__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
5073
|
+
fs9__namespace.mkdirSync(tmpDir, { recursive: true });
|
|
5074
|
+
writeOwnershipMarker(tmpDir);
|
|
5075
|
+
const partsDir = path10__namespace.join(tmpDir, "parts");
|
|
5076
|
+
const reportsDir = path10__namespace.join(tmpDir, "reports");
|
|
5077
|
+
if (artifacts.parts) fs9__namespace.mkdirSync(partsDir, { recursive: true });
|
|
3201
5078
|
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
3202
|
-
|
|
5079
|
+
fs9__namespace.mkdirSync(reportsDir, { recursive: true });
|
|
3203
5080
|
}
|
|
3204
5081
|
if (artifacts.parts) {
|
|
3205
5082
|
for (const part of parts) {
|
|
3206
|
-
|
|
5083
|
+
fs9__namespace.writeFileSync(path10__namespace.join(partsDir, part.filename), part.markdown, "utf8");
|
|
3207
5084
|
}
|
|
3208
5085
|
}
|
|
3209
5086
|
if (artifacts.projectTree) {
|
|
3210
5087
|
const projectTree = generateProjectTree(files.map((file) => file.relativePath));
|
|
3211
|
-
|
|
5088
|
+
fs9__namespace.writeFileSync(path10__namespace.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
3212
5089
|
}
|
|
3213
5090
|
if (artifacts.navigation) {
|
|
3214
|
-
|
|
5091
|
+
fs9__namespace.writeFileSync(path10__namespace.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
3215
5092
|
}
|
|
3216
5093
|
if (artifacts.audit) {
|
|
3217
|
-
|
|
3218
|
-
|
|
5094
|
+
fs9__namespace.writeFileSync(
|
|
5095
|
+
path10__namespace.join(reportsDir, "audit.md"),
|
|
3219
5096
|
generateAuditReport(manifest, secretFindings),
|
|
3220
5097
|
"utf8"
|
|
3221
5098
|
);
|
|
3222
5099
|
}
|
|
3223
5100
|
if (artifacts.readme) {
|
|
3224
|
-
|
|
5101
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "README.md"), generateReadme(manifest), "utf8");
|
|
3225
5102
|
}
|
|
3226
5103
|
if (artifacts.manifest) {
|
|
3227
|
-
|
|
5104
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
3228
5105
|
}
|
|
3229
5106
|
if (monolithJson !== null) {
|
|
3230
|
-
|
|
5107
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "full-codebase.json"), monolithJson, "utf8");
|
|
3231
5108
|
}
|
|
3232
5109
|
if (monolithMarkdown !== null) {
|
|
3233
|
-
|
|
5110
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
3234
5111
|
}
|
|
3235
5112
|
swapIntoPlace(tmpDir, outDir);
|
|
3236
5113
|
runAfterGenerate(config, manifest);
|
|
@@ -3241,12 +5118,17 @@ async function generateSnapshotAsync(configInput, options) {
|
|
|
3241
5118
|
}
|
|
3242
5119
|
|
|
3243
5120
|
exports.ARTIFACT_NAMES = ARTIFACT_NAMES;
|
|
5121
|
+
exports.DEFAULT_ANSWERS = DEFAULT_ANSWERS;
|
|
3244
5122
|
exports.DEFAULT_SECRET_PATTERNS = DEFAULT_SECRET_PATTERNS;
|
|
3245
5123
|
exports.GENERATOR_NAME = GENERATOR_NAME;
|
|
3246
5124
|
exports.GENERATOR_VERSION = GENERATOR_VERSION;
|
|
5125
|
+
exports.LANGUAGE_PROFILES = LANGUAGE_PROFILES;
|
|
3247
5126
|
exports.MIN_MAX_PART_BYTES = MIN_MAX_PART_BYTES;
|
|
5127
|
+
exports.PRESETS = PRESETS;
|
|
5128
|
+
exports.PromptCancelled = PromptCancelled;
|
|
3248
5129
|
exports.SCHEMA_VERSION = SCHEMA_VERSION;
|
|
3249
5130
|
exports.anchorForPath = anchorForPath;
|
|
5131
|
+
exports.assertReplaceable = assertReplaceable;
|
|
3250
5132
|
exports.assertUsableConfigInput = assertUsableConfigInput;
|
|
3251
5133
|
exports.collectFiles = collectFiles;
|
|
3252
5134
|
exports.compareManifests = compareManifests;
|
|
@@ -3259,12 +5141,15 @@ exports.fenceForContent = fenceForContent;
|
|
|
3259
5141
|
exports.generateProjectTree = generateProjectTree;
|
|
3260
5142
|
exports.generateSnapshot = generateSnapshot;
|
|
3261
5143
|
exports.generateSnapshotAsync = generateSnapshotAsync;
|
|
5144
|
+
exports.getPreset = getPreset;
|
|
3262
5145
|
exports.globToRegExp = globToRegExp;
|
|
3263
5146
|
exports.isBinaryBuffer = isBinaryBuffer;
|
|
3264
5147
|
exports.isBinaryExtension = isBinaryExtension;
|
|
3265
5148
|
exports.isBinaryFile = isBinaryFile;
|
|
5149
|
+
exports.isInteractive = isInteractive;
|
|
3266
5150
|
exports.isResolvedConfig = isResolvedConfig;
|
|
3267
5151
|
exports.isSensitivePath = isSensitivePath;
|
|
5152
|
+
exports.listPresetNames = listPresetNames;
|
|
3268
5153
|
exports.loadConfigFile = loadConfigFile;
|
|
3269
5154
|
exports.loadConfigFileDetailed = loadConfigFileDetailed;
|
|
3270
5155
|
exports.loadManifest = loadManifest;
|
|
@@ -3274,16 +5159,21 @@ exports.matchesExclude = matchesExclude;
|
|
|
3274
5159
|
exports.normalizeResolvedConfig = normalizeResolvedConfig;
|
|
3275
5160
|
exports.parseByteSize = parseByteSize;
|
|
3276
5161
|
exports.partitionFiles = partitionFiles;
|
|
5162
|
+
exports.profileForPath = profileForPath;
|
|
5163
|
+
exports.renderConfig = renderConfig;
|
|
3277
5164
|
exports.renderDiffText = renderDiffText;
|
|
3278
5165
|
exports.renderExplainText = renderExplainText;
|
|
5166
|
+
exports.renderMinimalConfig = renderMinimalConfig;
|
|
3279
5167
|
exports.renumberParts = renumberParts;
|
|
3280
5168
|
exports.resolveConfig = resolveConfig;
|
|
3281
5169
|
exports.resolveSafeOutputDirs = resolveSafeOutputDirs;
|
|
5170
|
+
exports.runInit = runInit;
|
|
3282
5171
|
exports.scanAndRedactSecrets = scanAndRedactSecrets;
|
|
3283
5172
|
exports.stripJsonComments = stripJsonComments;
|
|
3284
5173
|
exports.summarizeJson = summarizeJson;
|
|
3285
5174
|
exports.summarizeMarkdown = summarizeMarkdown;
|
|
3286
5175
|
exports.summarizeTypeScriptSource = summarizeTypeScriptSource;
|
|
5176
|
+
exports.summarizeWithProfile = summarizeWithProfile;
|
|
3287
5177
|
exports.supportsTypeScriptImports = supportsTypeScriptImports;
|
|
3288
5178
|
exports.truncatePreview = truncatePreview;
|
|
3289
5179
|
exports.truncateToBytes = truncateToBytes;
|