@ankurhalder/codebase-snapshot 2.0.0 → 2.1.1
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 +2399 -372
- package/dist/{chunk-RGDVLQTT.js → chunk-SUOJL5EX.js} +2247 -355
- package/dist/cli.cjs +2417 -397
- package/dist/cli.d.cts +9 -1
- package/dist/cli.d.ts +9 -1
- package/dist/cli.js +160 -31
- package/dist/index.cjs +2260 -354
- 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 +150 -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,6 +1055,47 @@ var NEXTJS_KINDS = [
|
|
|
370
1055
|
}
|
|
371
1056
|
];
|
|
372
1057
|
|
|
1058
|
+
// src/presets/node.ts
|
|
1059
|
+
var NODE_LAYERS = [
|
|
1060
|
+
{ name: "core", match: (p) => p.startsWith("src/core/") || p.startsWith("core/") },
|
|
1061
|
+
{ name: "protocols", match: (p) => p.startsWith("src/protocols/") || p.startsWith("protocols/") },
|
|
1062
|
+
{ name: "providers", match: (p) => p.startsWith("src/providers/") || p.startsWith("providers/") },
|
|
1063
|
+
{ name: "security", match: (p) => p.startsWith("src/security/") || p.startsWith("security/") },
|
|
1064
|
+
{ name: "db", match: (p) => p.startsWith("src/db/") || p.startsWith("db/") || p.startsWith("prisma/") },
|
|
1065
|
+
{ name: "api", match: (p) => p.startsWith("src/api/") || p.startsWith("api/") || p.startsWith("routes/") },
|
|
1066
|
+
{ name: "services", match: (p) => p.startsWith("src/services/") || p.startsWith("services/") },
|
|
1067
|
+
{ name: "controllers", match: (p) => p.startsWith("src/controllers/") || p.startsWith("controllers/") },
|
|
1068
|
+
{ name: "scripts", match: (p) => p.startsWith("scripts/") },
|
|
1069
|
+
{ name: "docs", match: (p) => p.startsWith("docs/") },
|
|
1070
|
+
{
|
|
1071
|
+
name: "config",
|
|
1072
|
+
match: (p) => p.startsWith(".github/") || p.startsWith(".husky/") || p.startsWith(".") || p.includes("config")
|
|
1073
|
+
},
|
|
1074
|
+
{ name: "root", match: (p) => !p.includes("/") }
|
|
1075
|
+
];
|
|
1076
|
+
var NODE_KINDS = [
|
|
1077
|
+
{ kind: "test", match: (p) => /\.(test|spec)\.[jt]sx?$/.test(p.toLowerCase()) },
|
|
1078
|
+
{ kind: "route", match: (p) => p.includes("/routes/") || p.includes("route.") || p.includes("/api/") },
|
|
1079
|
+
{ kind: "controller", match: (p) => p.includes("controller") },
|
|
1080
|
+
{ kind: "service", match: (p) => p.includes("service") },
|
|
1081
|
+
{ kind: "provider", match: (p) => p.includes("provider") },
|
|
1082
|
+
{ kind: "model", match: (p) => p.includes("model") || p.includes("entity") },
|
|
1083
|
+
{ kind: "schema", match: (p) => p.includes("schema") || p.includes("zod") || p.includes("dto") },
|
|
1084
|
+
{ kind: "type", match: (p) => p.endsWith(".d.ts") || p.includes("types.") || p.includes("type.") },
|
|
1085
|
+
{ kind: "security", match: (p) => p.includes("auth") || p.includes("security") || p.includes("guard") },
|
|
1086
|
+
{ kind: "db", match: (p) => p.includes("db") || p.includes("database") || p.includes("migration") },
|
|
1087
|
+
{ kind: "data", match: (p) => p.endsWith(".json") || p.endsWith(".csv") },
|
|
1088
|
+
{ kind: "doc", match: (p) => p.endsWith(".md") || p.endsWith(".mdx") },
|
|
1089
|
+
{ kind: "script", match: (p) => p.startsWith("scripts/") || p.endsWith(".sh") || p.endsWith(".ps1") },
|
|
1090
|
+
{
|
|
1091
|
+
kind: "config",
|
|
1092
|
+
match: (p) => {
|
|
1093
|
+
const base = p.split("/").pop() || "";
|
|
1094
|
+
return ["package.json", "tsconfig.json", "Dockerfile"].includes(base) || base.startsWith(".");
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
];
|
|
1098
|
+
|
|
373
1099
|
// src/presets/vite.ts
|
|
374
1100
|
var VITE_LAYERS = [
|
|
375
1101
|
{ name: "pages", match: (p) => p.startsWith("src/pages/") || p.startsWith("src/views/") },
|
|
@@ -417,44 +1143,419 @@ var VITE_KINDS = [
|
|
|
417
1143
|
}
|
|
418
1144
|
];
|
|
419
1145
|
|
|
420
|
-
// src/presets/
|
|
421
|
-
var
|
|
422
|
-
|
|
423
|
-
{
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
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
|
+
},
|
|
432
1294
|
{
|
|
433
|
-
name: "
|
|
434
|
-
|
|
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"
|
|
435
1302
|
},
|
|
436
|
-
{
|
|
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
|
+
}
|
|
437
1354
|
];
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
{ kind: "provider", match: (p) => p.includes("provider") },
|
|
444
|
-
{ kind: "model", match: (p) => p.includes("model") || p.includes("entity") },
|
|
445
|
-
{ kind: "schema", match: (p) => p.includes("schema") || p.includes("zod") || p.includes("dto") },
|
|
446
|
-
{ kind: "type", match: (p) => p.endsWith(".d.ts") || p.includes("types.") || p.includes("type.") },
|
|
447
|
-
{ kind: "security", match: (p) => p.includes("auth") || p.includes("security") || p.includes("guard") },
|
|
448
|
-
{ kind: "db", match: (p) => p.includes("db") || p.includes("database") || p.includes("migration") },
|
|
449
|
-
{ kind: "data", match: (p) => p.endsWith(".json") || p.endsWith(".csv") },
|
|
450
|
-
{ kind: "doc", match: (p) => p.endsWith(".md") || p.endsWith(".mdx") },
|
|
451
|
-
{ kind: "script", match: (p) => p.startsWith("scripts/") || p.endsWith(".sh") || p.endsWith(".ps1") },
|
|
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 = [
|
|
452
1360
|
{
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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"
|
|
458
1559
|
}
|
|
459
1560
|
];
|
|
460
1561
|
|
|
@@ -500,57 +1601,118 @@ 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
|
-
const
|
|
1710
|
+
const reported = childProcess__namespace.execSync("git rev-parse --abbrev-ref HEAD", {
|
|
550
1711
|
cwd,
|
|
551
1712
|
stdio: ["ignore", "pipe", "ignore"],
|
|
552
1713
|
encoding: "utf8"
|
|
553
|
-
}).trim()
|
|
1714
|
+
}).trim();
|
|
1715
|
+
const branch = !reported || reported === "HEAD" ? fallbackBranch : reported;
|
|
554
1716
|
const commit = childProcess__namespace.execSync("git rev-parse HEAD", {
|
|
555
1717
|
cwd,
|
|
556
1718
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -678,10 +1840,10 @@ function matchesExclude(relativePath, pattern) {
|
|
|
678
1840
|
return posixPath === cleaned || posixPath.startsWith(`${cleaned}/`) || posixPath.includes(`/${cleaned}/`) || posixPath.endsWith(`/${cleaned}`);
|
|
679
1841
|
}
|
|
680
1842
|
function readGitignoreMatcher(dir) {
|
|
681
|
-
const gitignorePath =
|
|
1843
|
+
const gitignorePath = path10__namespace.join(dir, ".gitignore");
|
|
682
1844
|
let raw;
|
|
683
1845
|
try {
|
|
684
|
-
raw =
|
|
1846
|
+
raw = fs9__namespace.readFileSync(gitignorePath, "utf8");
|
|
685
1847
|
} catch {
|
|
686
1848
|
return () => false;
|
|
687
1849
|
}
|
|
@@ -784,7 +1946,7 @@ function walkDirectory(dir, baseDir, visited) {
|
|
|
784
1946
|
const results = { files: [], skipped: [] };
|
|
785
1947
|
let realDir;
|
|
786
1948
|
try {
|
|
787
|
-
realDir =
|
|
1949
|
+
realDir = fs9__namespace.realpathSync(dir);
|
|
788
1950
|
} catch {
|
|
789
1951
|
realDir = dir;
|
|
790
1952
|
}
|
|
@@ -793,21 +1955,21 @@ function walkDirectory(dir, baseDir, visited) {
|
|
|
793
1955
|
const ignoredByGitignore = dir === baseDir ? readGitignoreMatcher(baseDir) : () => false;
|
|
794
1956
|
let entries;
|
|
795
1957
|
try {
|
|
796
|
-
entries =
|
|
1958
|
+
entries = fs9__namespace.readdirSync(dir, { withFileTypes: true });
|
|
797
1959
|
} catch {
|
|
798
1960
|
results.skipped.push({ path: dir, reason: "read-error", detail: "directory unreadable" });
|
|
799
1961
|
return results;
|
|
800
1962
|
}
|
|
801
1963
|
for (const entry of entries) {
|
|
802
1964
|
if (DEFAULT_IGNORED_DIRS.includes(entry.name)) continue;
|
|
803
|
-
const fullPath =
|
|
804
|
-
const relPath =
|
|
1965
|
+
const fullPath = path10__namespace.join(dir, entry.name);
|
|
1966
|
+
const relPath = path10__namespace.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
805
1967
|
if (ignoredByGitignore(relPath)) continue;
|
|
806
1968
|
let isDirectory = entry.isDirectory();
|
|
807
1969
|
let isFile = entry.isFile();
|
|
808
1970
|
if (entry.isSymbolicLink()) {
|
|
809
1971
|
try {
|
|
810
|
-
const stats =
|
|
1972
|
+
const stats = fs9__namespace.statSync(fullPath);
|
|
811
1973
|
isDirectory = stats.isDirectory();
|
|
812
1974
|
isFile = stats.isFile();
|
|
813
1975
|
if (!isDirectory && !isFile) {
|
|
@@ -1072,6 +2234,8 @@ function normalizeResolvedConfig(config) {
|
|
|
1072
2234
|
config.includeMonolith === true
|
|
1073
2235
|
),
|
|
1074
2236
|
include: resolveInclude(config.include),
|
|
2237
|
+
preset: typeof config.preset === "string" ? config.preset : "generic",
|
|
2238
|
+
presetExclude: Array.isArray(config.presetExclude) ? config.presetExclude : [],
|
|
1075
2239
|
layersMode: config.layersMode === "replace" ? "replace" : "merge",
|
|
1076
2240
|
budgets: config.budgets && typeof config.budgets === "object" ? config.budgets : { default: config.maxPartBytes, byLayer: {} },
|
|
1077
2241
|
hooks: resolveHooks(config.hooks),
|
|
@@ -1119,18 +2283,22 @@ function parseJsonish(raw, file) {
|
|
|
1119
2283
|
}
|
|
1120
2284
|
async function loadConfigFileDetailed(cwd) {
|
|
1121
2285
|
const warnings = [];
|
|
2286
|
+
const errors = [];
|
|
1122
2287
|
for (const filename of CONFIG_FILENAMES) {
|
|
1123
|
-
const fullPath =
|
|
1124
|
-
if (!
|
|
2288
|
+
const fullPath = path10__namespace.join(cwd, filename);
|
|
2289
|
+
if (!fs9__namespace.existsSync(fullPath)) continue;
|
|
1125
2290
|
if (filename.endsWith(".json") || filename === ".snapshotrc") {
|
|
1126
2291
|
try {
|
|
1127
2292
|
return {
|
|
1128
|
-
config: parseJsonish(
|
|
2293
|
+
config: parseJsonish(fs9__namespace.readFileSync(fullPath, "utf8"), fullPath),
|
|
1129
2294
|
file: fullPath,
|
|
1130
|
-
warnings
|
|
2295
|
+
warnings,
|
|
2296
|
+
errors
|
|
1131
2297
|
};
|
|
1132
2298
|
} catch (err) {
|
|
1133
|
-
|
|
2299
|
+
const message = err.message;
|
|
2300
|
+
warnings.push(message);
|
|
2301
|
+
errors.push(message);
|
|
1134
2302
|
continue;
|
|
1135
2303
|
}
|
|
1136
2304
|
}
|
|
@@ -1144,28 +2312,37 @@ async function loadConfigFileDetailed(cwd) {
|
|
|
1144
2312
|
const imported = await import(url.pathToFileURL(fullPath).href);
|
|
1145
2313
|
const config = imported.default ?? imported;
|
|
1146
2314
|
if (!config || typeof config !== "object") {
|
|
1147
|
-
|
|
2315
|
+
const message = `${fullPath} does not export a configuration object.`;
|
|
2316
|
+
warnings.push(message);
|
|
2317
|
+
errors.push(message);
|
|
1148
2318
|
continue;
|
|
1149
2319
|
}
|
|
1150
|
-
return { config, file: fullPath, warnings };
|
|
2320
|
+
return { config, file: fullPath, warnings, errors };
|
|
1151
2321
|
} catch (err) {
|
|
1152
|
-
|
|
2322
|
+
const message = `Could not load ${fullPath}: ${err.message}`;
|
|
2323
|
+
warnings.push(message);
|
|
2324
|
+
errors.push(message);
|
|
1153
2325
|
continue;
|
|
1154
2326
|
}
|
|
1155
2327
|
}
|
|
1156
|
-
const pkgJsonPath =
|
|
1157
|
-
if (
|
|
2328
|
+
const pkgJsonPath = path10__namespace.join(cwd, "package.json");
|
|
2329
|
+
if (fs9__namespace.existsSync(pkgJsonPath)) {
|
|
1158
2330
|
try {
|
|
1159
|
-
const parsed = parseJsonish(
|
|
2331
|
+
const parsed = parseJsonish(fs9__namespace.readFileSync(pkgJsonPath, "utf8"), pkgJsonPath);
|
|
1160
2332
|
const snapshotKey = parsed.snapshot;
|
|
1161
2333
|
if (snapshotKey && typeof snapshotKey === "object") {
|
|
1162
|
-
return { config: snapshotKey, file: pkgJsonPath, warnings };
|
|
2334
|
+
return { config: snapshotKey, file: pkgJsonPath, warnings, errors };
|
|
2335
|
+
}
|
|
2336
|
+
if (snapshotKey !== void 0) {
|
|
2337
|
+
warnings.push(`${pkgJsonPath}: the "snapshot" key must be an object; it was ignored.`);
|
|
1163
2338
|
}
|
|
1164
2339
|
} catch (err) {
|
|
1165
|
-
|
|
2340
|
+
const message = err.message;
|
|
2341
|
+
warnings.push(message);
|
|
2342
|
+
errors.push(message);
|
|
1166
2343
|
}
|
|
1167
2344
|
}
|
|
1168
|
-
return { config: null, file: null, warnings };
|
|
2345
|
+
return { config: null, file: null, warnings, errors };
|
|
1169
2346
|
}
|
|
1170
2347
|
async function loadConfigFile(cwd) {
|
|
1171
2348
|
const loaded = await loadConfigFileDetailed(cwd);
|
|
@@ -1264,7 +2441,9 @@ function boundedBytes(value, fallback, minimum, label) {
|
|
|
1264
2441
|
`${label} must be a byte count such as 120000, "120k" or "1.5mb" (received ${describeBytes(value)}).`
|
|
1265
2442
|
);
|
|
1266
2443
|
}
|
|
1267
|
-
if (parsed <= 0)
|
|
2444
|
+
if (parsed <= 0) {
|
|
2445
|
+
throw new Error(`${label} must be a positive byte count (received ${describeBytes(value)}).`);
|
|
2446
|
+
}
|
|
1268
2447
|
if (parsed < minimum) {
|
|
1269
2448
|
throw new Error(`${label} must be at least ${minimum} bytes (received ${describeBytes(value)}).`);
|
|
1270
2449
|
}
|
|
@@ -1304,12 +2483,12 @@ function resolveBudgets(budgets, fallback) {
|
|
|
1304
2483
|
if (typeof budgets.byLayer !== "object" || budgets.byLayer === null) {
|
|
1305
2484
|
throw new Error("`budgets.byLayer` must be an object of layer name to byte size.");
|
|
1306
2485
|
}
|
|
1307
|
-
for (const [
|
|
1308
|
-
byLayer[
|
|
2486
|
+
for (const [layer2, value] of Object.entries(budgets.byLayer)) {
|
|
2487
|
+
byLayer[layer2] = boundedBytes(
|
|
1309
2488
|
value,
|
|
1310
2489
|
fallback,
|
|
1311
2490
|
MIN_MAX_PART_BYTES,
|
|
1312
|
-
`budgets.byLayer["${
|
|
2491
|
+
`budgets.byLayer["${layer2}"]`
|
|
1313
2492
|
);
|
|
1314
2493
|
}
|
|
1315
2494
|
}
|
|
@@ -1330,18 +2509,22 @@ function resolveHooks(hooks) {
|
|
|
1330
2509
|
}
|
|
1331
2510
|
function resolveCandidateCwd(userConfig, cliOptions, baseDir) {
|
|
1332
2511
|
const candidate = cliOptions?.cwd || userConfig?.cwd || baseDir || process.cwd();
|
|
1333
|
-
const resolved =
|
|
1334
|
-
if (!
|
|
2512
|
+
const resolved = path10__namespace.resolve(candidate);
|
|
2513
|
+
if (!fs9__namespace.existsSync(resolved)) {
|
|
1335
2514
|
throw new Error(`Working directory does not exist: ${resolved}`);
|
|
1336
2515
|
}
|
|
1337
|
-
if (!
|
|
2516
|
+
if (!fs9__namespace.statSync(resolved).isDirectory()) {
|
|
1338
2517
|
throw new Error(`Working directory is not a directory: ${resolved}`);
|
|
1339
2518
|
}
|
|
1340
2519
|
return resolved;
|
|
1341
2520
|
}
|
|
1342
2521
|
function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
1343
2522
|
const cwd = resolveCandidateCwd(userConfig ?? null, cliOptions, baseDir);
|
|
1344
|
-
const
|
|
2523
|
+
const forcedPreset = userConfig?.preset;
|
|
2524
|
+
if (forcedPreset !== void 0 && !getPreset(forcedPreset)) {
|
|
2525
|
+
throw new Error(unknownPresetMessage(forcedPreset));
|
|
2526
|
+
}
|
|
2527
|
+
const preset = detectPreset(cwd, forcedPreset);
|
|
1345
2528
|
const layersMode = resolveLayersMode(userConfig?.layersMode);
|
|
1346
2529
|
const layers = userConfig?.layers && userConfig.layers.length > 0 ? layersMode === "replace" ? userConfig.layers : [...userConfig.layers, ...preset.layers] : preset.layers;
|
|
1347
2530
|
const kinds = userConfig?.kinds && userConfig.kinds.length > 0 ? layersMode === "replace" ? userConfig.kinds : [...userConfig.kinds, ...preset.kinds] : preset.kinds;
|
|
@@ -1377,6 +2560,8 @@ function resolveConfig(userConfig, cliOptions, baseDir) {
|
|
|
1377
2560
|
),
|
|
1378
2561
|
includeSensitiveFiles: cliOptions?.includeSensitiveFiles ?? userConfig?.includeSensitiveFiles ?? false,
|
|
1379
2562
|
include: resolveInclude(userConfig?.include),
|
|
2563
|
+
preset: preset.presetName,
|
|
2564
|
+
presetExclude: [...preset.exclude],
|
|
1380
2565
|
artifacts,
|
|
1381
2566
|
layersMode,
|
|
1382
2567
|
budgets,
|
|
@@ -1411,8 +2596,8 @@ function matchRule(filePath, matcher) {
|
|
|
1411
2596
|
return false;
|
|
1412
2597
|
}
|
|
1413
2598
|
function classifyLayer(filePath, layers) {
|
|
1414
|
-
for (const
|
|
1415
|
-
if (matchRule(filePath,
|
|
2599
|
+
for (const layer2 of layers) {
|
|
2600
|
+
if (matchRule(filePath, layer2.match)) return layer2.name;
|
|
1416
2601
|
}
|
|
1417
2602
|
return "other";
|
|
1418
2603
|
}
|
|
@@ -1493,11 +2678,11 @@ function generateAuditReport(manifest, secretFindings) {
|
|
|
1493
2678
|
"| :--- | :--- | :--- |"
|
|
1494
2679
|
);
|
|
1495
2680
|
const totalFiles = manifest.counts.totalFiles || 1;
|
|
1496
|
-
for (const [
|
|
2681
|
+
for (const [layer2, count] of Object.entries(manifest.layers).sort(
|
|
1497
2682
|
(a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)
|
|
1498
2683
|
)) {
|
|
1499
2684
|
const pct = (count / totalFiles * 100).toFixed(1);
|
|
1500
|
-
lines.push(`| \`${
|
|
2685
|
+
lines.push(`| \`${layer2}\` | ${count} | ${pct}% |`);
|
|
1501
2686
|
}
|
|
1502
2687
|
lines.push("");
|
|
1503
2688
|
lines.push(
|
|
@@ -1605,21 +2790,21 @@ function partitionFiles(files, maxPartBytes, contentFingerprint) {
|
|
|
1605
2790
|
section.byteSize = Buffer.byteLength(section.text, "utf8");
|
|
1606
2791
|
continue;
|
|
1607
2792
|
}
|
|
1608
|
-
const
|
|
1609
|
-
const target = Math.max(0, budget - Buffer.byteLength(
|
|
2793
|
+
const marker2 = "\n... [part truncated: cannot fit budget] ...\n";
|
|
2794
|
+
const target = Math.max(0, budget - Buffer.byteLength(marker2, "utf8"));
|
|
1610
2795
|
const clipped = truncateToBytes(rendered[oversized].markdown, target);
|
|
1611
|
-
rendered[oversized] = rehashPart(rendered[oversized], clipped.content +
|
|
2796
|
+
rendered[oversized] = rehashPart(rendered[oversized], clipped.content + marker2);
|
|
1612
2797
|
return rendered;
|
|
1613
2798
|
}
|
|
1614
2799
|
return enforceBudget(renderGroups(groups, contentFingerprint), budget);
|
|
1615
2800
|
}
|
|
1616
2801
|
function enforceBudget(parts, budget) {
|
|
1617
|
-
const
|
|
1618
|
-
const target = Math.max(0, budget - Buffer.byteLength(
|
|
2802
|
+
const marker2 = "\n... [part truncated: cannot fit budget] ...\n";
|
|
2803
|
+
const target = Math.max(0, budget - Buffer.byteLength(marker2, "utf8"));
|
|
1619
2804
|
return parts.map((part) => {
|
|
1620
2805
|
if (part.metadata.byteSize <= budget) return part;
|
|
1621
2806
|
const clipped = truncateToBytes(part.markdown, target);
|
|
1622
|
-
return rehashPart(part, clipped.content +
|
|
2807
|
+
return rehashPart(part, clipped.content + marker2);
|
|
1623
2808
|
});
|
|
1624
2809
|
}
|
|
1625
2810
|
function renumberParts(parts, contentFingerprint) {
|
|
@@ -1627,10 +2812,10 @@ function renumberParts(parts, contentFingerprint) {
|
|
|
1627
2812
|
if (total === 0) return [];
|
|
1628
2813
|
const zeroPad = (index) => String(index).padStart(Math.max(3, String(total).length), "0");
|
|
1629
2814
|
const nameFor = (position) => `part-${zeroPad(position)}.md`;
|
|
1630
|
-
const
|
|
2815
|
+
const marker2 = "## Contents\n";
|
|
1631
2816
|
return parts.map((part, index) => {
|
|
1632
|
-
const markerIndex = part.markdown.indexOf(
|
|
1633
|
-
const body = markerIndex === -1 ? "" : part.markdown.slice(markerIndex +
|
|
2817
|
+
const markerIndex = part.markdown.indexOf(marker2);
|
|
2818
|
+
const body = markerIndex === -1 ? "" : part.markdown.slice(markerIndex + marker2.length);
|
|
1634
2819
|
const included = part.metadata.includedFiles;
|
|
1635
2820
|
if (markerIndex === -1) {
|
|
1636
2821
|
throw new Error(`${part.filename} has no "## Contents" section to re-number.`);
|
|
@@ -1688,9 +2873,9 @@ function topEntries(counts, limit) {
|
|
|
1688
2873
|
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
2874
|
}
|
|
1690
2875
|
function prepareSection(file, sectionBudget) {
|
|
1691
|
-
let
|
|
1692
|
-
let byteSize = Buffer.byteLength(
|
|
1693
|
-
if (byteSize <= sectionBudget) return { file, text, byteSize };
|
|
2876
|
+
let text2 = renderFileSection(file);
|
|
2877
|
+
let byteSize = Buffer.byteLength(text2, "utf8");
|
|
2878
|
+
if (byteSize <= sectionBudget) return { file, text: text2, byteSize };
|
|
1694
2879
|
const shrinkBody = () => {
|
|
1695
2880
|
const content = file.content ?? "";
|
|
1696
2881
|
const overhead = byteSize - Buffer.byteLength(content, "utf8");
|
|
@@ -1699,24 +2884,24 @@ function prepareSection(file, sectionBudget) {
|
|
|
1699
2884
|
const truncated = truncateToBytes(content, allowed);
|
|
1700
2885
|
if (!truncated.isTruncated) return;
|
|
1701
2886
|
applyTruncatedContent(file, truncated.content);
|
|
1702
|
-
|
|
1703
|
-
byteSize = Buffer.byteLength(
|
|
2887
|
+
text2 = renderFileSection(file);
|
|
2888
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
1704
2889
|
};
|
|
1705
2890
|
shrinkBody();
|
|
1706
2891
|
if (byteSize > sectionBudget && file.summary) {
|
|
1707
2892
|
file.summary = void 0;
|
|
1708
2893
|
file.summaryOmitted = true;
|
|
1709
|
-
|
|
1710
|
-
byteSize = Buffer.byteLength(
|
|
2894
|
+
text2 = renderFileSection(file);
|
|
2895
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
1711
2896
|
shrinkBody();
|
|
1712
2897
|
}
|
|
1713
2898
|
if (byteSize > sectionBudget) {
|
|
1714
2899
|
const target = sectionBudget - Buffer.byteLength(SECTION_TRUNCATION_NOTICE, "utf8");
|
|
1715
|
-
const truncated = truncateToBytes(
|
|
1716
|
-
|
|
1717
|
-
byteSize = Buffer.byteLength(
|
|
2900
|
+
const truncated = truncateToBytes(text2, Math.max(0, target));
|
|
2901
|
+
text2 = truncated.content + SECTION_TRUNCATION_NOTICE;
|
|
2902
|
+
byteSize = Buffer.byteLength(text2, "utf8");
|
|
1718
2903
|
}
|
|
1719
|
-
return { file, text, byteSize };
|
|
2904
|
+
return { file, text: text2, byteSize };
|
|
1720
2905
|
}
|
|
1721
2906
|
function applyTruncatedContent(file, content) {
|
|
1722
2907
|
file.content = content;
|
|
@@ -1838,6 +3023,7 @@ function renderFileSection(file) {
|
|
|
1838
3023
|
`- Source Lines: ${file.originalLines}`,
|
|
1839
3024
|
`- Included Lines: ${file.includedLines}`,
|
|
1840
3025
|
`- SHA-256: ${file.hashSha256}`,
|
|
3026
|
+
...file.summaryQuality ? [`- Summary Quality: ${file.summaryQuality}`] : [],
|
|
1841
3027
|
...renderSummary(file.summary),
|
|
1842
3028
|
...file.summaryOmitted ? ["- Summary: (omitted: exceeds the part budget)"] : [],
|
|
1843
3029
|
"",
|
|
@@ -2085,7 +3271,7 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
2085
3271
|
".so"
|
|
2086
3272
|
]);
|
|
2087
3273
|
function isBinaryExtension(filePath) {
|
|
2088
|
-
const ext =
|
|
3274
|
+
const ext = path10__namespace.extname(filePath).toLowerCase();
|
|
2089
3275
|
return BINARY_EXTENSIONS.has(ext);
|
|
2090
3276
|
}
|
|
2091
3277
|
function isBinaryBuffer(buffer) {
|
|
@@ -2099,9 +3285,9 @@ function isBinaryFile(absolutePath) {
|
|
|
2099
3285
|
if (isBinaryExtension(absolutePath)) return true;
|
|
2100
3286
|
let fd;
|
|
2101
3287
|
try {
|
|
2102
|
-
fd =
|
|
3288
|
+
fd = fs9__namespace.openSync(absolutePath, "r");
|
|
2103
3289
|
const buffer = Buffer.alloc(8192);
|
|
2104
|
-
const bytesRead =
|
|
3290
|
+
const bytesRead = fs9__namespace.readSync(fd, buffer, 0, 8192, 0);
|
|
2105
3291
|
if (bytesRead === 0) return false;
|
|
2106
3292
|
return isBinaryBuffer(buffer.subarray(0, bytesRead));
|
|
2107
3293
|
} catch {
|
|
@@ -2109,7 +3295,7 @@ function isBinaryFile(absolutePath) {
|
|
|
2109
3295
|
} finally {
|
|
2110
3296
|
if (fd !== void 0) {
|
|
2111
3297
|
try {
|
|
2112
|
-
|
|
3298
|
+
fs9__namespace.closeSync(fd);
|
|
2113
3299
|
} catch {
|
|
2114
3300
|
}
|
|
2115
3301
|
}
|
|
@@ -2169,10 +3355,10 @@ var LANGUAGE_BY_BASENAME = {
|
|
|
2169
3355
|
};
|
|
2170
3356
|
function detectLanguage(filePath) {
|
|
2171
3357
|
const normalized = filePath.replace(/\\/g, "/");
|
|
2172
|
-
const base =
|
|
3358
|
+
const base = path10__namespace.posix.basename(normalized);
|
|
2173
3359
|
if (LANGUAGE_BY_BASENAME[base]) return LANGUAGE_BY_BASENAME[base];
|
|
2174
3360
|
if (base === ".env" || base.startsWith(".env.")) return "dotenv";
|
|
2175
|
-
const ext =
|
|
3361
|
+
const ext = path10__namespace.posix.extname(base).toLowerCase();
|
|
2176
3362
|
return LANGUAGE_BY_EXTENSION[ext] ?? "text";
|
|
2177
3363
|
}
|
|
2178
3364
|
function emptyDrift() {
|
|
@@ -2191,8 +3377,8 @@ function sha256Hex(buffer) {
|
|
|
2191
3377
|
function validateSnapshot(configInput) {
|
|
2192
3378
|
assertUsableConfigInput(configInput);
|
|
2193
3379
|
const config = isResolvedConfig(configInput) ? normalizeResolvedConfig(configInput) : resolveConfig(configInput);
|
|
2194
|
-
const manifestPath =
|
|
2195
|
-
if (!
|
|
3380
|
+
const manifestPath = path10__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
3381
|
+
if (!fs9__namespace.existsSync(manifestPath)) {
|
|
2196
3382
|
return {
|
|
2197
3383
|
valid: false,
|
|
2198
3384
|
manifestPresent: false,
|
|
@@ -2203,7 +3389,7 @@ function validateSnapshot(configInput) {
|
|
|
2203
3389
|
}
|
|
2204
3390
|
let manifest;
|
|
2205
3391
|
try {
|
|
2206
|
-
manifest = JSON.parse(
|
|
3392
|
+
manifest = JSON.parse(fs9__namespace.readFileSync(manifestPath, "utf8"));
|
|
2207
3393
|
} catch (err) {
|
|
2208
3394
|
return {
|
|
2209
3395
|
valid: false,
|
|
@@ -2226,7 +3412,7 @@ function validateSnapshot(configInput) {
|
|
|
2226
3412
|
}
|
|
2227
3413
|
const drift = emptyDrift();
|
|
2228
3414
|
const warnings = [];
|
|
2229
|
-
const snapshotDir =
|
|
3415
|
+
const snapshotDir = path10__namespace.resolve(config.cwd, config.outDir);
|
|
2230
3416
|
const manifestFileMap = /* @__PURE__ */ new Map();
|
|
2231
3417
|
for (const file of manifest.files) {
|
|
2232
3418
|
if (file && typeof file.relativePath === "string") {
|
|
@@ -2238,7 +3424,7 @@ function validateSnapshot(configInput) {
|
|
|
2238
3424
|
);
|
|
2239
3425
|
const collection = collectFiles(config.cwd, {
|
|
2240
3426
|
includePatterns: config.include,
|
|
2241
|
-
excludePatterns: config.excludePatterns,
|
|
3427
|
+
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
2242
3428
|
includeSensitiveFiles: config.includeSensitiveFiles
|
|
2243
3429
|
});
|
|
2244
3430
|
for (const diskFile of collection.files) {
|
|
@@ -2250,7 +3436,7 @@ function validateSnapshot(configInput) {
|
|
|
2250
3436
|
}
|
|
2251
3437
|
let diskHash;
|
|
2252
3438
|
try {
|
|
2253
|
-
diskHash = sha256Hex(
|
|
3439
|
+
diskHash = sha256Hex(fs9__namespace.readFileSync(path10__namespace.join(config.cwd, diskFile)));
|
|
2254
3440
|
} catch {
|
|
2255
3441
|
drift.missingOnDisk.push(diskFile);
|
|
2256
3442
|
continue;
|
|
@@ -2260,10 +3446,11 @@ function validateSnapshot(configInput) {
|
|
|
2260
3446
|
}
|
|
2261
3447
|
}
|
|
2262
3448
|
for (const snapshotFile of manifestFileMap.keys()) {
|
|
2263
|
-
if (!
|
|
3449
|
+
if (!fs9__namespace.existsSync(path10__namespace.join(config.cwd, snapshotFile))) {
|
|
2264
3450
|
drift.missingOnDisk.push(snapshotFile);
|
|
2265
3451
|
}
|
|
2266
3452
|
}
|
|
3453
|
+
drift.missingOnDisk = [...new Set(drift.missingOnDisk)];
|
|
2267
3454
|
const maxEffectiveBudget = Math.max(
|
|
2268
3455
|
config.maxPartBytes,
|
|
2269
3456
|
config.budgets.default,
|
|
@@ -2274,10 +3461,10 @@ function validateSnapshot(configInput) {
|
|
|
2274
3461
|
if (!part || typeof part.filename !== "string") continue;
|
|
2275
3462
|
partNames.add(part.filename);
|
|
2276
3463
|
if (!config.artifacts.parts) continue;
|
|
2277
|
-
const partPath =
|
|
3464
|
+
const partPath = path10__namespace.join(snapshotDir, "parts", part.filename);
|
|
2278
3465
|
let buffer;
|
|
2279
3466
|
try {
|
|
2280
|
-
buffer =
|
|
3467
|
+
buffer = fs9__namespace.readFileSync(partPath);
|
|
2281
3468
|
} catch {
|
|
2282
3469
|
drift.partIssues.push({ part: part.filename, issue: "missing from snapshot/parts" });
|
|
2283
3470
|
continue;
|
|
@@ -2364,6 +3551,11 @@ function validateSnapshot(configInput) {
|
|
|
2364
3551
|
}
|
|
2365
3552
|
}
|
|
2366
3553
|
}
|
|
3554
|
+
if (recorded?.preset !== void 0 && recorded.preset !== config.preset) {
|
|
3555
|
+
drift.configMismatches.push(
|
|
3556
|
+
`preset: manifest ${recorded.preset} vs current ${config.preset}`
|
|
3557
|
+
);
|
|
3558
|
+
}
|
|
2367
3559
|
if (recorded?.layersMode !== void 0 && recorded.layersMode !== config.layersMode) {
|
|
2368
3560
|
drift.configMismatches.push(
|
|
2369
3561
|
`layersMode: manifest ${recorded.layersMode} vs current ${config.layersMode}`
|
|
@@ -2381,10 +3573,10 @@ function validateSnapshot(configInput) {
|
|
|
2381
3573
|
...Object.keys(recordedByLayer),
|
|
2382
3574
|
...Object.keys(config.budgets.byLayer)
|
|
2383
3575
|
]);
|
|
2384
|
-
for (const
|
|
2385
|
-
if (recordedByLayer[
|
|
3576
|
+
for (const layer2 of Array.from(layerNames).sort()) {
|
|
3577
|
+
if (recordedByLayer[layer2] !== config.budgets.byLayer[layer2]) {
|
|
2386
3578
|
drift.configMismatches.push(
|
|
2387
|
-
`budgets.byLayer.${
|
|
3579
|
+
`budgets.byLayer.${layer2}: manifest ${recordedByLayer[layer2]} vs current ${config.budgets.byLayer[layer2]}`
|
|
2388
3580
|
);
|
|
2389
3581
|
}
|
|
2390
3582
|
}
|
|
@@ -2467,9 +3659,9 @@ function validateSnapshot(configInput) {
|
|
|
2467
3659
|
["full-codebase.md", manifest.artifacts?.fullMarkdown]
|
|
2468
3660
|
]) {
|
|
2469
3661
|
if (!artifact) continue;
|
|
2470
|
-
const artifactPath =
|
|
3662
|
+
const artifactPath = path10__namespace.join(snapshotDir, key);
|
|
2471
3663
|
try {
|
|
2472
|
-
const buffer =
|
|
3664
|
+
const buffer = fs9__namespace.readFileSync(artifactPath);
|
|
2473
3665
|
if (sha256Hex(buffer) !== artifact.sha256 || buffer.length !== artifact.byteSize) {
|
|
2474
3666
|
drift.partIssues.push({ part: key, issue: "artifact does not match its recorded digest" });
|
|
2475
3667
|
}
|
|
@@ -2525,35 +3717,35 @@ function describeMatcher(matcher) {
|
|
|
2525
3717
|
return String(matcher);
|
|
2526
3718
|
}
|
|
2527
3719
|
function normalizeTarget(cwd, target) {
|
|
2528
|
-
const absolute =
|
|
2529
|
-
const relative3 =
|
|
2530
|
-
if (relative3.startsWith("..") ||
|
|
2531
|
-
return relative3.split(
|
|
3720
|
+
const absolute = path10__namespace.resolve(cwd, target);
|
|
3721
|
+
const relative3 = path10__namespace.relative(cwd, absolute);
|
|
3722
|
+
if (relative3.startsWith("..") || path10__namespace.isAbsolute(relative3)) return target;
|
|
3723
|
+
return relative3.split(path10__namespace.sep).join("/");
|
|
2532
3724
|
}
|
|
2533
3725
|
function findRuleMatch(rules, relativePath) {
|
|
2534
3726
|
return rules.find(
|
|
2535
3727
|
(rule) => matchRule(relativePath, rule.match)
|
|
2536
3728
|
);
|
|
2537
3729
|
}
|
|
2538
|
-
function
|
|
2539
|
-
const manifestPath =
|
|
3730
|
+
function readManifest2(config) {
|
|
3731
|
+
const manifestPath = path10__namespace.resolve(config.cwd, config.outDir, "manifest.json");
|
|
2540
3732
|
try {
|
|
2541
|
-
return JSON.parse(
|
|
3733
|
+
return JSON.parse(fs9__namespace.readFileSync(manifestPath, "utf8"));
|
|
2542
3734
|
} catch {
|
|
2543
3735
|
return null;
|
|
2544
3736
|
}
|
|
2545
3737
|
}
|
|
2546
3738
|
function explainPath(config, target) {
|
|
2547
3739
|
const relative3 = normalizeTarget(config.cwd, target);
|
|
2548
|
-
const absolute =
|
|
3740
|
+
const absolute = path10__namespace.join(config.cwd, relative3);
|
|
2549
3741
|
const result = {
|
|
2550
3742
|
path: relative3,
|
|
2551
|
-
exists:
|
|
3743
|
+
exists: fs9__namespace.existsSync(absolute),
|
|
2552
3744
|
included: false
|
|
2553
3745
|
};
|
|
2554
3746
|
const collection = collectFiles(config.cwd, {
|
|
2555
3747
|
includePatterns: config.include,
|
|
2556
|
-
excludePatterns: config.excludePatterns,
|
|
3748
|
+
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
2557
3749
|
includeSensitiveFiles: config.includeSensitiveFiles
|
|
2558
3750
|
});
|
|
2559
3751
|
if (!collection.files.includes(relative3)) {
|
|
@@ -2578,16 +3770,16 @@ function explainPath(config, target) {
|
|
|
2578
3770
|
if (layerRule) result.layerMatch = describeMatcher(layerRule.match);
|
|
2579
3771
|
const kindRule = findRuleMatch(config.kinds, relative3);
|
|
2580
3772
|
if (kindRule) result.kindMatch = describeMatcher(kindRule.match);
|
|
2581
|
-
if (
|
|
2582
|
-
const buffer =
|
|
2583
|
-
const
|
|
3773
|
+
if (fs9__namespace.existsSync(absolute) && fs9__namespace.statSync(absolute).isFile()) {
|
|
3774
|
+
const buffer = fs9__namespace.readFileSync(absolute);
|
|
3775
|
+
const text2 = buffer.toString("utf8");
|
|
2584
3776
|
result.bytes = buffer.length;
|
|
2585
|
-
result.lines = countLines(
|
|
2586
|
-
const { findings } = scanAndRedactSecrets(
|
|
3777
|
+
result.lines = countLines(text2);
|
|
3778
|
+
const { findings } = scanAndRedactSecrets(text2, config.secretRules, relative3);
|
|
2587
3779
|
result.secretCount = findings.length;
|
|
2588
3780
|
result.secrets = findings.slice(0, 20).map((finding) => ({ rule: finding.rule, line: finding.line }));
|
|
2589
3781
|
}
|
|
2590
|
-
const manifest =
|
|
3782
|
+
const manifest = readManifest2(config);
|
|
2591
3783
|
const entry = manifest?.files.find((file) => file.relativePath === relative3);
|
|
2592
3784
|
result.part = entry?.part ?? null;
|
|
2593
3785
|
result.inclusionMode = entry?.inclusionMode;
|
|
@@ -2670,10 +3862,16 @@ function compareManifests(before, after) {
|
|
|
2670
3862
|
};
|
|
2671
3863
|
}
|
|
2672
3864
|
function loadManifest(target) {
|
|
2673
|
-
|
|
3865
|
+
let isDirectory;
|
|
3866
|
+
try {
|
|
3867
|
+
isDirectory = fs9__namespace.statSync(target).isDirectory();
|
|
3868
|
+
} catch {
|
|
3869
|
+
throw new Error(`No snapshot found at ${target}: the path does not exist.`);
|
|
3870
|
+
}
|
|
3871
|
+
const manifestPath = isDirectory ? path10__namespace.join(target, "manifest.json") : target;
|
|
2674
3872
|
let raw;
|
|
2675
3873
|
try {
|
|
2676
|
-
raw =
|
|
3874
|
+
raw = fs9__namespace.readFileSync(manifestPath, "utf8");
|
|
2677
3875
|
} catch {
|
|
2678
3876
|
throw new Error(`Could not read a manifest at ${manifestPath}.`);
|
|
2679
3877
|
}
|
|
@@ -2686,6 +3884,9 @@ function loadManifest(target) {
|
|
|
2686
3884
|
if (!parsed || !Array.isArray(parsed.files)) {
|
|
2687
3885
|
throw new Error(`${manifestPath} is not a snapshot manifest (no "files" array).`);
|
|
2688
3886
|
}
|
|
3887
|
+
if (!parsed.counts || typeof parsed.counts !== "object") {
|
|
3888
|
+
throw new Error(`${manifestPath} is not a snapshot manifest (no "counts" object).`);
|
|
3889
|
+
}
|
|
2689
3890
|
return parsed;
|
|
2690
3891
|
}
|
|
2691
3892
|
function renderDiffText(diff) {
|
|
@@ -2709,14 +3910,659 @@ function renderDiffText(diff) {
|
|
|
2709
3910
|
return lines.join("\n");
|
|
2710
3911
|
}
|
|
2711
3912
|
|
|
3913
|
+
// src/wizard/config-template.ts
|
|
3914
|
+
var DEFAULT_ANSWERS = {
|
|
3915
|
+
preset: "generic",
|
|
3916
|
+
maxPartBytes: "300k",
|
|
3917
|
+
largeFileBytes: "200k",
|
|
3918
|
+
include: [],
|
|
3919
|
+
excludePatterns: [],
|
|
3920
|
+
artifacts: {
|
|
3921
|
+
readme: true,
|
|
3922
|
+
manifest: true,
|
|
3923
|
+
parts: true,
|
|
3924
|
+
navigation: true,
|
|
3925
|
+
projectTree: true,
|
|
3926
|
+
audit: true,
|
|
3927
|
+
monolith: false
|
|
3928
|
+
},
|
|
3929
|
+
includeSensitiveFiles: false,
|
|
3930
|
+
outDir: "snapshot"
|
|
3931
|
+
};
|
|
3932
|
+
function list(values) {
|
|
3933
|
+
if (values.length === 0) return "[]";
|
|
3934
|
+
return `[${values.map((value) => JSON.stringify(value)).join(", ")}]`;
|
|
3935
|
+
}
|
|
3936
|
+
function size(value) {
|
|
3937
|
+
return typeof value === "string" ? JSON.stringify(value) : String(value);
|
|
3938
|
+
}
|
|
3939
|
+
var PRESET_NAMES = [
|
|
3940
|
+
"nextjs \xB7 react-vite \xB7 vue-vite \xB7 nuxt \xB7 sveltekit \xB7 angular \xB7 astro",
|
|
3941
|
+
"remix \xB7 solid \xB7 nest \xB7 node-express \xB7 node-lib",
|
|
3942
|
+
"python-django \xB7 python-fastapi \xB7 python-lib \xB7 go \xB7 rust",
|
|
3943
|
+
"java-spring \xB7 dotnet \xB7 php-laravel \xB7 ruby-rails",
|
|
3944
|
+
"monorepo \xB7 docs \xB7 infra \xB7 generic"
|
|
3945
|
+
];
|
|
3946
|
+
function renderConfig(answers) {
|
|
3947
|
+
const { artifacts } = answers;
|
|
3948
|
+
return `import { defineConfig } from "@ankurhalder/codebase-snapshot";
|
|
3949
|
+
|
|
3950
|
+
export default defineConfig({
|
|
3951
|
+
// \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
|
|
3952
|
+
// Which project shape to use. Detected from the repo when omitted.
|
|
3953
|
+
// ${PRESET_NAMES.join("\n // ")}
|
|
3954
|
+
preset: ${JSON.stringify(answers.preset)},
|
|
3955
|
+
|
|
3956
|
+
// Recorded in the manifest when git cannot report a branch.
|
|
3957
|
+
branch: "main",
|
|
3958
|
+
|
|
3959
|
+
// \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
|
|
3960
|
+
// Where the snapshot is written.
|
|
3961
|
+
outDir: ${JSON.stringify(answers.outDir)},
|
|
3962
|
+
// Scratch directory; the new snapshot is renamed into place atomically.
|
|
3963
|
+
tmpDir: "snapshot.tmp",
|
|
3964
|
+
|
|
3965
|
+
// \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
|
|
3966
|
+
// Hard ceiling per part. "300k", 307200 and "1.5mb" are all accepted.
|
|
3967
|
+
maxPartBytes: ${size(answers.maxPartBytes)},
|
|
3968
|
+
// Files above this size get a head/tail preview instead of the full body.
|
|
3969
|
+
largeFileBytes: ${size(answers.largeFileBytes)},
|
|
3970
|
+
previewHeadLines: 80,
|
|
3971
|
+
previewTailLines: 40,
|
|
3972
|
+
// Different ceilings per layer. Unlisted layers use \`default\`.
|
|
3973
|
+
budgets: {
|
|
3974
|
+
default: ${size(answers.maxPartBytes)},
|
|
3975
|
+
byLayer: {},
|
|
3976
|
+
},
|
|
3977
|
+
|
|
3978
|
+
// \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
|
|
3979
|
+
// Allow-list. Empty means "everything that is not ignored".
|
|
3980
|
+
// Example: ["src/**", "packages/*/src/**"]
|
|
3981
|
+
include: ${list(answers.include)},
|
|
3982
|
+
// Globs or path segments to drop. Example: ["**/*.test.ts", "docs"]
|
|
3983
|
+
excludePatterns: ${list(answers.excludePatterns)},
|
|
3984
|
+
|
|
3985
|
+
// \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
|
|
3986
|
+
// "merge" = the preset's rules plus yours (yours win). "replace" = yours only.
|
|
3987
|
+
layersMode: "merge",
|
|
3988
|
+
// [{ name: "billing", match: "src/features/billing/**" }]
|
|
3989
|
+
layers: [],
|
|
3990
|
+
// [{ kind: "route", match: "src/app/api/**" }]
|
|
3991
|
+
kinds: [],
|
|
3992
|
+
|
|
3993
|
+
// \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
|
|
3994
|
+
// .env, *.pem, .npmrc, .ssh/ and friends are skipped unless this is true.
|
|
3995
|
+
includeSensitiveFiles: ${answers.includeSensitiveFiles},
|
|
3996
|
+
secrets: {
|
|
3997
|
+
// [{ rule: "internal-token", pattern: /CORP-[A-Z0-9]{32}/ }]
|
|
3998
|
+
customPatterns: [],
|
|
3999
|
+
// Set true to drop the 21 built-in rules.
|
|
4000
|
+
disableDefaultPatterns: false,
|
|
4001
|
+
},
|
|
4002
|
+
|
|
4003
|
+
// \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
|
|
4004
|
+
artifacts: {
|
|
4005
|
+
readme: ${artifacts.readme}, // README.md
|
|
4006
|
+
manifest: ${artifacts.manifest}, // manifest.json \u2014 required by validate and report
|
|
4007
|
+
parts: ${artifacts.parts}, // parts/part-XXX.md
|
|
4008
|
+
navigation: ${artifacts.navigation}, // reports/navigation.md
|
|
4009
|
+
projectTree: ${artifacts.projectTree}, // reports/project-tree.md
|
|
4010
|
+
audit: ${artifacts.audit}, // reports/audit.md
|
|
4011
|
+
monolith: ${artifacts.monolith}, // full-codebase.json + .md (roughly doubles output)
|
|
4012
|
+
},
|
|
4013
|
+
// Skip the monolith above this size. null = no limit.
|
|
4014
|
+
maxMonolithBytes: null,
|
|
4015
|
+
|
|
4016
|
+
// \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
|
|
4017
|
+
// afterGenerate: (manifest) => { /* write your own artifact */ }
|
|
4018
|
+
hooks: {},
|
|
4019
|
+
});
|
|
4020
|
+
`;
|
|
4021
|
+
}
|
|
4022
|
+
function renderMinimalConfig(answers) {
|
|
4023
|
+
const lines = [];
|
|
4024
|
+
if (answers.preset !== DEFAULT_ANSWERS.preset) lines.push(` preset: ${JSON.stringify(answers.preset)},`);
|
|
4025
|
+
if (answers.maxPartBytes !== DEFAULT_ANSWERS.maxPartBytes) {
|
|
4026
|
+
lines.push(` maxPartBytes: ${size(answers.maxPartBytes)},`);
|
|
4027
|
+
}
|
|
4028
|
+
if (answers.largeFileBytes !== DEFAULT_ANSWERS.largeFileBytes) {
|
|
4029
|
+
lines.push(` largeFileBytes: ${size(answers.largeFileBytes)},`);
|
|
4030
|
+
}
|
|
4031
|
+
if (answers.include.length > 0) lines.push(` include: ${list(answers.include)},`);
|
|
4032
|
+
if (answers.excludePatterns.length > 0) {
|
|
4033
|
+
lines.push(` excludePatterns: ${list(answers.excludePatterns)},`);
|
|
4034
|
+
}
|
|
4035
|
+
if (answers.includeSensitiveFiles) lines.push(" includeSensitiveFiles: true,");
|
|
4036
|
+
if (answers.outDir !== DEFAULT_ANSWERS.outDir) lines.push(` outDir: ${JSON.stringify(answers.outDir)},`);
|
|
4037
|
+
const changed = Object.keys(answers.artifacts).filter(
|
|
4038
|
+
(name) => answers.artifacts[name] !== DEFAULT_ANSWERS.artifacts[name]
|
|
4039
|
+
);
|
|
4040
|
+
if (changed.length > 0) {
|
|
4041
|
+
const entries = changed.map((name) => ` ${name}: ${answers.artifacts[name]}`).join(",\n");
|
|
4042
|
+
lines.push(` artifacts: {
|
|
4043
|
+
${entries},
|
|
4044
|
+
},`);
|
|
4045
|
+
}
|
|
4046
|
+
if (lines.length === 0) {
|
|
4047
|
+
return `import { defineConfig } from "@ankurhalder/codebase-snapshot";
|
|
4048
|
+
|
|
4049
|
+
export default defineConfig({});
|
|
4050
|
+
`;
|
|
4051
|
+
}
|
|
4052
|
+
return `import { defineConfig } from "@ankurhalder/codebase-snapshot";
|
|
4053
|
+
|
|
4054
|
+
export default defineConfig({
|
|
4055
|
+
${lines.join("\n")}
|
|
4056
|
+
});
|
|
4057
|
+
`;
|
|
4058
|
+
}
|
|
4059
|
+
var PromptCancelled = class extends Error {
|
|
4060
|
+
constructor() {
|
|
4061
|
+
super("Prompt cancelled");
|
|
4062
|
+
this.name = "PromptCancelled";
|
|
4063
|
+
}
|
|
4064
|
+
};
|
|
4065
|
+
function defaultIO() {
|
|
4066
|
+
return { input: process.stdin, output: process.stdout };
|
|
4067
|
+
}
|
|
4068
|
+
function isInteractive(io) {
|
|
4069
|
+
return Boolean(io.input.isTTY && io.output.isTTY);
|
|
4070
|
+
}
|
|
4071
|
+
function moveSelection(current, delta, count) {
|
|
4072
|
+
if (count <= 0) return 0;
|
|
4073
|
+
return ((current + delta) % count + count) % count;
|
|
4074
|
+
}
|
|
4075
|
+
function toggleIndex(selected, index) {
|
|
4076
|
+
const next = new Set(selected);
|
|
4077
|
+
if (next.has(index)) next.delete(index);
|
|
4078
|
+
else next.add(index);
|
|
4079
|
+
return next;
|
|
4080
|
+
}
|
|
4081
|
+
function marker(active) {
|
|
4082
|
+
return active ? "\u276F" : " ";
|
|
4083
|
+
}
|
|
4084
|
+
function choiceLine(choice, active) {
|
|
4085
|
+
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
4086
|
+
return ` ${marker(active)} ${choice.label}${hint}`;
|
|
4087
|
+
}
|
|
4088
|
+
function renderSelect(message, choices, index) {
|
|
4089
|
+
const lines = [`\u25C6 ${message}`, ""];
|
|
4090
|
+
choices.forEach((choice, position) => lines.push(choiceLine(choice, position === index)));
|
|
4091
|
+
lines.push("", " \u2191\u2193 move \xB7 enter select");
|
|
4092
|
+
return lines.join("\n");
|
|
4093
|
+
}
|
|
4094
|
+
function renderMultiSelect(message, choices, index, selected) {
|
|
4095
|
+
const lines = [`\u25C6 ${message}`, ""];
|
|
4096
|
+
choices.forEach((choice, position) => {
|
|
4097
|
+
const box = selected.has(position) ? "\u25FC" : "\u25FB";
|
|
4098
|
+
const hint = choice.hint ? ` ${choice.hint}` : "";
|
|
4099
|
+
lines.push(` ${marker(position === index)} ${box} ${choice.label}${hint}`);
|
|
4100
|
+
});
|
|
4101
|
+
lines.push("", " \u2191\u2193 move \xB7 space toggle \xB7 enter confirm");
|
|
4102
|
+
return lines.join("\n");
|
|
4103
|
+
}
|
|
4104
|
+
function renderConfirm(message, value) {
|
|
4105
|
+
return [
|
|
4106
|
+
`\u25C6 ${message}`,
|
|
4107
|
+
"",
|
|
4108
|
+
` ${marker(value)} Yes`,
|
|
4109
|
+
` ${marker(!value)} No`,
|
|
4110
|
+
"",
|
|
4111
|
+
" \u2190\u2192 or y/n \xB7 enter confirm"
|
|
4112
|
+
].join("\n");
|
|
4113
|
+
}
|
|
4114
|
+
function renderText(message, value, error) {
|
|
4115
|
+
const lines = [`\u25C6 ${message}`, "", ` ${value || "\u258F"}`];
|
|
4116
|
+
if (error) lines.push("", ` \u2716 ${error}`);
|
|
4117
|
+
return lines.join("\n");
|
|
4118
|
+
}
|
|
4119
|
+
function keyReader(input) {
|
|
4120
|
+
const queue = [];
|
|
4121
|
+
const waiters = [];
|
|
4122
|
+
const wasRaw = input.isRaw;
|
|
4123
|
+
const onKey = (_text, key) => {
|
|
4124
|
+
const waiter = waiters.shift();
|
|
4125
|
+
if (waiter) waiter(key);
|
|
4126
|
+
else queue.push(key);
|
|
4127
|
+
};
|
|
4128
|
+
readline__namespace.emitKeypressEvents(input);
|
|
4129
|
+
if (input.isTTY) input.setRawMode(true);
|
|
4130
|
+
input.resume();
|
|
4131
|
+
input.on("keypress", onKey);
|
|
4132
|
+
return {
|
|
4133
|
+
next() {
|
|
4134
|
+
const queued = queue.shift();
|
|
4135
|
+
if (queued) return Promise.resolve(queued);
|
|
4136
|
+
return new Promise((resolve5) => waiters.push(resolve5));
|
|
4137
|
+
},
|
|
4138
|
+
close() {
|
|
4139
|
+
input.off("keypress", onKey);
|
|
4140
|
+
if (input.isTTY) input.setRawMode(wasRaw ?? false);
|
|
4141
|
+
input.pause();
|
|
4142
|
+
}
|
|
4143
|
+
};
|
|
4144
|
+
}
|
|
4145
|
+
var Frame = class {
|
|
4146
|
+
constructor(output) {
|
|
4147
|
+
this.output = output;
|
|
4148
|
+
}
|
|
4149
|
+
output;
|
|
4150
|
+
drawn = 0;
|
|
4151
|
+
draw(text2) {
|
|
4152
|
+
this.erase();
|
|
4153
|
+
this.output.write(`${text2}
|
|
4154
|
+
`);
|
|
4155
|
+
this.drawn = text2.split("\n").length;
|
|
4156
|
+
}
|
|
4157
|
+
erase() {
|
|
4158
|
+
if (this.drawn > 0) this.output.write(`\x1B[${this.drawn}A\x1B[0J`);
|
|
4159
|
+
this.drawn = 0;
|
|
4160
|
+
}
|
|
4161
|
+
};
|
|
4162
|
+
function isCancel(key) {
|
|
4163
|
+
return key.ctrl === true && key.name === "c" || key.ctrl === true && key.name === "d";
|
|
4164
|
+
}
|
|
4165
|
+
async function select(io, message, choices, options = {}) {
|
|
4166
|
+
if (choices.length === 0) throw new Error("select() requires at least one choice.");
|
|
4167
|
+
const fallbackIndex = Math.max(
|
|
4168
|
+
0,
|
|
4169
|
+
choices.findIndex((choice) => choice.value === options.defaultValue)
|
|
4170
|
+
);
|
|
4171
|
+
if (!isInteractive(io)) {
|
|
4172
|
+
io.output.write(`${renderSelect(message, choices, fallbackIndex)}
|
|
4173
|
+
`);
|
|
4174
|
+
return choices[fallbackIndex].value;
|
|
4175
|
+
}
|
|
4176
|
+
const frame = new Frame(io.output);
|
|
4177
|
+
const reader = keyReader(io.input);
|
|
4178
|
+
let index = fallbackIndex;
|
|
4179
|
+
try {
|
|
4180
|
+
for (; ; ) {
|
|
4181
|
+
frame.draw(renderSelect(message, choices, index));
|
|
4182
|
+
const key = await reader.next();
|
|
4183
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4184
|
+
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
4185
|
+
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
4186
|
+
else if (key.name === "return" || key.name === "enter") {
|
|
4187
|
+
frame.erase();
|
|
4188
|
+
return choices[index].value;
|
|
4189
|
+
} else if (key.name === "j") index = moveSelection(index, 1, choices.length);
|
|
4190
|
+
else if (key.name === "k") index = moveSelection(index, -1, choices.length);
|
|
4191
|
+
}
|
|
4192
|
+
} finally {
|
|
4193
|
+
reader.close();
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
async function multiSelect(io, message, choices, options = {}) {
|
|
4197
|
+
const selected = /* @__PURE__ */ new Set();
|
|
4198
|
+
choices.forEach((choice, position) => {
|
|
4199
|
+
if (options.defaults?.includes(choice.value)) selected.add(position);
|
|
4200
|
+
});
|
|
4201
|
+
const result = () => choices.filter((_, position) => selected.has(position)).map((choice) => choice.value);
|
|
4202
|
+
if (!isInteractive(io)) {
|
|
4203
|
+
io.output.write(`${renderMultiSelect(message, choices, 0, selected)}
|
|
4204
|
+
`);
|
|
4205
|
+
return result();
|
|
4206
|
+
}
|
|
4207
|
+
const frame = new Frame(io.output);
|
|
4208
|
+
const reader = keyReader(io.input);
|
|
4209
|
+
let index = 0;
|
|
4210
|
+
try {
|
|
4211
|
+
for (; ; ) {
|
|
4212
|
+
frame.draw(renderMultiSelect(message, choices, index, selected));
|
|
4213
|
+
const key = await reader.next();
|
|
4214
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4215
|
+
if (key.name === "up") index = moveSelection(index, -1, choices.length);
|
|
4216
|
+
else if (key.name === "down") index = moveSelection(index, 1, choices.length);
|
|
4217
|
+
else if (key.name === "space") {
|
|
4218
|
+
const next = toggleIndex(selected, index);
|
|
4219
|
+
selected.clear();
|
|
4220
|
+
for (const position of next) selected.add(position);
|
|
4221
|
+
} else if (key.name === "a") {
|
|
4222
|
+
if (selected.size === choices.length) selected.clear();
|
|
4223
|
+
else choices.forEach((_, position) => selected.add(position));
|
|
4224
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
4225
|
+
frame.erase();
|
|
4226
|
+
return result();
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
} finally {
|
|
4230
|
+
reader.close();
|
|
4231
|
+
}
|
|
4232
|
+
}
|
|
4233
|
+
async function confirm(io, message, options = {}) {
|
|
4234
|
+
let value = options.defaultValue ?? true;
|
|
4235
|
+
if (!isInteractive(io)) {
|
|
4236
|
+
io.output.write(`${renderConfirm(message, value)}
|
|
4237
|
+
`);
|
|
4238
|
+
return value;
|
|
4239
|
+
}
|
|
4240
|
+
const frame = new Frame(io.output);
|
|
4241
|
+
const reader = keyReader(io.input);
|
|
4242
|
+
try {
|
|
4243
|
+
for (; ; ) {
|
|
4244
|
+
frame.draw(renderConfirm(message, value));
|
|
4245
|
+
const key = await reader.next();
|
|
4246
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4247
|
+
if (key.name === "up" || key.name === "left") value = true;
|
|
4248
|
+
else if (key.name === "down" || key.name === "right") value = false;
|
|
4249
|
+
else if (key.name === "y") value = true;
|
|
4250
|
+
else if (key.name === "n") value = false;
|
|
4251
|
+
else if (key.name === "return" || key.name === "enter") {
|
|
4252
|
+
frame.erase();
|
|
4253
|
+
return value;
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
} finally {
|
|
4257
|
+
reader.close();
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
async function text(io, message, options = {}) {
|
|
4261
|
+
const fallback = options.defaultValue ?? "";
|
|
4262
|
+
if (!isInteractive(io)) {
|
|
4263
|
+
io.output.write(`${renderText(message, fallback)}
|
|
4264
|
+
`);
|
|
4265
|
+
return fallback;
|
|
4266
|
+
}
|
|
4267
|
+
const frame = new Frame(io.output);
|
|
4268
|
+
const reader = keyReader(io.input);
|
|
4269
|
+
let value = fallback;
|
|
4270
|
+
let error;
|
|
4271
|
+
try {
|
|
4272
|
+
for (; ; ) {
|
|
4273
|
+
frame.draw(renderText(message, value, error));
|
|
4274
|
+
const key = await reader.next();
|
|
4275
|
+
if (isCancel(key)) throw new PromptCancelled();
|
|
4276
|
+
error = void 0;
|
|
4277
|
+
if (key.name === "return" || key.name === "enter") {
|
|
4278
|
+
const problem = options.validate ? options.validate(value) : null;
|
|
4279
|
+
if (problem) {
|
|
4280
|
+
error = problem;
|
|
4281
|
+
continue;
|
|
4282
|
+
}
|
|
4283
|
+
frame.erase();
|
|
4284
|
+
return value;
|
|
4285
|
+
}
|
|
4286
|
+
if (key.name === "backspace") {
|
|
4287
|
+
value = value.slice(0, -1);
|
|
4288
|
+
continue;
|
|
4289
|
+
}
|
|
4290
|
+
if (key.name === "escape") {
|
|
4291
|
+
value = "";
|
|
4292
|
+
continue;
|
|
4293
|
+
}
|
|
4294
|
+
if (_isPrintable(key)) value += _keyText(key);
|
|
4295
|
+
}
|
|
4296
|
+
} finally {
|
|
4297
|
+
reader.close();
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
function _isPrintable(key) {
|
|
4301
|
+
return typeof key.sequence === "string" && key.sequence.length === 1 && key.sequence >= " ";
|
|
4302
|
+
}
|
|
4303
|
+
function _keyText(key) {
|
|
4304
|
+
return key.sequence ?? "";
|
|
4305
|
+
}
|
|
4306
|
+
|
|
4307
|
+
// src/wizard/init.ts
|
|
4308
|
+
var CONFIG_CANDIDATES = [
|
|
4309
|
+
"snapshot.config.ts",
|
|
4310
|
+
"snapshot.config.js",
|
|
4311
|
+
"snapshot.config.mjs",
|
|
4312
|
+
"snapshot.config.cjs",
|
|
4313
|
+
"snapshot.config.json",
|
|
4314
|
+
".snapshotrc.json",
|
|
4315
|
+
".snapshotrc"
|
|
4316
|
+
];
|
|
4317
|
+
var SIZE_CHOICES = [
|
|
4318
|
+
{ value: "60k", label: "Small", hint: "60 KB \xB7 ~15k tokens \xB7 tight context windows" },
|
|
4319
|
+
{ value: "120k", label: "Medium", hint: "120 KB \xB7 ~30k tokens \xB7 recommended" },
|
|
4320
|
+
{ value: "300k", label: "Large", hint: "300 KB \xB7 ~75k tokens \xB7 the default" },
|
|
4321
|
+
{ value: "custom", label: "Custom\u2026", hint: "type a size like 150k or 1.5mb" }
|
|
4322
|
+
];
|
|
4323
|
+
function existingConfig(cwd) {
|
|
4324
|
+
for (const name of CONFIG_CANDIDATES) {
|
|
4325
|
+
if (fs9__namespace.existsSync(path10__namespace.join(cwd, name))) return name;
|
|
4326
|
+
}
|
|
4327
|
+
return null;
|
|
4328
|
+
}
|
|
4329
|
+
function artifactAnswers(options) {
|
|
4330
|
+
const artifacts = { ...DEFAULT_ANSWERS.artifacts };
|
|
4331
|
+
for (const name of options.skipArtifacts ?? []) {
|
|
4332
|
+
if (ARTIFACT_NAMES.includes(name)) {
|
|
4333
|
+
artifacts[name] = false;
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4336
|
+
if (options.onlyArtifacts && options.onlyArtifacts.length > 0) {
|
|
4337
|
+
const keep = new Set(options.onlyArtifacts);
|
|
4338
|
+
for (const name of ARTIFACT_NAMES) artifacts[name] = keep.has(name);
|
|
4339
|
+
}
|
|
4340
|
+
return artifacts;
|
|
4341
|
+
}
|
|
4342
|
+
function topLevelDirs(cwd) {
|
|
4343
|
+
try {
|
|
4344
|
+
return fs9__namespace.readdirSync(cwd, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules").map((entry) => entry.name).sort();
|
|
4345
|
+
} catch {
|
|
4346
|
+
return [];
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
function addScripts(cwd, notes) {
|
|
4350
|
+
const packagePath = path10__namespace.join(cwd, "package.json");
|
|
4351
|
+
if (!fs9__namespace.existsSync(packagePath)) {
|
|
4352
|
+
notes.push("No package.json found, so npm scripts were not added.");
|
|
4353
|
+
return [];
|
|
4354
|
+
}
|
|
4355
|
+
let parsed;
|
|
4356
|
+
try {
|
|
4357
|
+
parsed = JSON.parse(fs9__namespace.readFileSync(packagePath, "utf8"));
|
|
4358
|
+
} catch {
|
|
4359
|
+
notes.push("Could not parse package.json; npm scripts were not added.");
|
|
4360
|
+
return [];
|
|
4361
|
+
}
|
|
4362
|
+
const scripts = parsed.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {};
|
|
4363
|
+
const added = [];
|
|
4364
|
+
if (!scripts.snapshot) {
|
|
4365
|
+
scripts.snapshot = "codebase-snapshot generate";
|
|
4366
|
+
added.push("snapshot");
|
|
4367
|
+
}
|
|
4368
|
+
if (!scripts["snapshot:validate"]) {
|
|
4369
|
+
scripts["snapshot:validate"] = "codebase-snapshot validate";
|
|
4370
|
+
added.push("snapshot:validate");
|
|
4371
|
+
}
|
|
4372
|
+
if (added.length === 0) return [];
|
|
4373
|
+
parsed.scripts = scripts;
|
|
4374
|
+
fs9__namespace.writeFileSync(packagePath, `${JSON.stringify(parsed, null, 2)}
|
|
4375
|
+
`, "utf8");
|
|
4376
|
+
return added;
|
|
4377
|
+
}
|
|
4378
|
+
function addGitignore(cwd) {
|
|
4379
|
+
const ignorePath = path10__namespace.join(cwd, ".gitignore");
|
|
4380
|
+
const existing = fs9__namespace.existsSync(ignorePath) ? fs9__namespace.readFileSync(ignorePath, "utf8") : "";
|
|
4381
|
+
const wanted = ["snapshot/", "snapshot.tmp/"];
|
|
4382
|
+
const added = wanted.filter((entry) => !existing.split(/\r?\n/).includes(entry));
|
|
4383
|
+
if (added.length === 0) return [];
|
|
4384
|
+
const prefix = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
4385
|
+
fs9__namespace.writeFileSync(ignorePath, `${existing}${prefix}${added.join("\n")}
|
|
4386
|
+
`, "utf8");
|
|
4387
|
+
return added;
|
|
4388
|
+
}
|
|
4389
|
+
function resolvePresetChoice(answers, presetName) {
|
|
4390
|
+
return {
|
|
4391
|
+
value: presetName,
|
|
4392
|
+
label: presetName,
|
|
4393
|
+
hint: presetName === answers.preset ? "detected" : void 0
|
|
4394
|
+
};
|
|
4395
|
+
}
|
|
4396
|
+
async function runInit(options) {
|
|
4397
|
+
const io = options.io ?? defaultIO();
|
|
4398
|
+
const detected = detectPreset(options.cwd, options.preset);
|
|
4399
|
+
const notes = [];
|
|
4400
|
+
const answers = {
|
|
4401
|
+
preset: detected.presetName,
|
|
4402
|
+
maxPartBytes: options.maxPartBytes ?? detected.recommendedBudget ?? DEFAULT_ANSWERS.maxPartBytes,
|
|
4403
|
+
largeFileBytes: DEFAULT_ANSWERS.largeFileBytes,
|
|
4404
|
+
include: options.include ?? [],
|
|
4405
|
+
excludePatterns: options.exclude ?? [],
|
|
4406
|
+
artifacts: artifactAnswers(options),
|
|
4407
|
+
includeSensitiveFiles: options.includeSensitiveFiles ?? DEFAULT_ANSWERS.includeSensitiveFiles,
|
|
4408
|
+
outDir: options.outDir ?? DEFAULT_ANSWERS.outDir
|
|
4409
|
+
};
|
|
4410
|
+
const interactive = isInteractive(io) && options.yes !== true;
|
|
4411
|
+
if (interactive) {
|
|
4412
|
+
io.output.write("\n @ankurhalder/codebase-snapshot\n");
|
|
4413
|
+
io.output.write(" Turn this repository into an LLM-ready, redacted, byte-bounded snapshot.\n\n");
|
|
4414
|
+
io.output.write(` Detected ${detected.presetName}
|
|
4415
|
+
`);
|
|
4416
|
+
io.output.write(` Directory ${options.cwd}
|
|
4417
|
+
|
|
4418
|
+
`);
|
|
4419
|
+
const useDetected = await confirm(io, `Set up for ${detected.presetName}?`, {
|
|
4420
|
+
defaultValue: true
|
|
4421
|
+
});
|
|
4422
|
+
if (!useDetected) {
|
|
4423
|
+
answers.preset = await select(
|
|
4424
|
+
io,
|
|
4425
|
+
"What kind of project is this?",
|
|
4426
|
+
listPresetNames().map((name) => resolvePresetChoice(answers, name))
|
|
4427
|
+
);
|
|
4428
|
+
const chosen = detectPreset(options.cwd, answers.preset);
|
|
4429
|
+
answers.preset = chosen.presetName;
|
|
4430
|
+
}
|
|
4431
|
+
const scope = await select(
|
|
4432
|
+
io,
|
|
4433
|
+
"What should go into the snapshot?",
|
|
4434
|
+
[
|
|
4435
|
+
{ value: "all", label: "Everything the scanner finds", hint: "recommended \xB7 respects .gitignore" },
|
|
4436
|
+
{ value: "folders", label: "Only specific folders\u2026" },
|
|
4437
|
+
{ value: "no-tests", label: "Everything, but skip tests" },
|
|
4438
|
+
{ value: "custom", label: "Let me write glob patterns" }
|
|
4439
|
+
],
|
|
4440
|
+
{ defaultValue: "all" }
|
|
4441
|
+
);
|
|
4442
|
+
if (scope === "folders") {
|
|
4443
|
+
const dirs = topLevelDirs(options.cwd).slice(0, 40);
|
|
4444
|
+
if (dirs.length === 0) {
|
|
4445
|
+
notes.push("No top-level directories found; the snapshot will include everything.");
|
|
4446
|
+
} else {
|
|
4447
|
+
const chosen = await multiSelect(
|
|
4448
|
+
io,
|
|
4449
|
+
"Select folders",
|
|
4450
|
+
dirs.map((name) => ({ value: name, label: `${name}/` })),
|
|
4451
|
+
{ defaults: dirs }
|
|
4452
|
+
);
|
|
4453
|
+
answers.include = chosen.map((name) => `${name}/**`);
|
|
4454
|
+
}
|
|
4455
|
+
} else if (scope === "no-tests") {
|
|
4456
|
+
answers.excludePatterns = [
|
|
4457
|
+
...answers.excludePatterns,
|
|
4458
|
+
"**/*.test.*",
|
|
4459
|
+
"**/*.spec.*",
|
|
4460
|
+
"tests",
|
|
4461
|
+
"test"
|
|
4462
|
+
];
|
|
4463
|
+
} else if (scope === "custom") {
|
|
4464
|
+
const raw = await text(io, "Glob patterns to include (comma separated)", {
|
|
4465
|
+
defaultValue: ""
|
|
4466
|
+
});
|
|
4467
|
+
answers.include = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
4468
|
+
}
|
|
4469
|
+
const sizeChoice = await select(io, "How large should each part be?", SIZE_CHOICES, {
|
|
4470
|
+
defaultValue: "120k"
|
|
4471
|
+
});
|
|
4472
|
+
if (sizeChoice === "custom") {
|
|
4473
|
+
const raw = await text(io, "Part size (e.g. 150k, 1.5mb, 900000)", {
|
|
4474
|
+
defaultValue: String(answers.maxPartBytes),
|
|
4475
|
+
validate: (value) => parseByteSize(value) === void 0 ? "Not a valid size." : null
|
|
4476
|
+
});
|
|
4477
|
+
answers.maxPartBytes = parseByteSize(raw) ?? answers.maxPartBytes;
|
|
4478
|
+
} else {
|
|
4479
|
+
answers.maxPartBytes = sizeChoice;
|
|
4480
|
+
}
|
|
4481
|
+
const artifactChoices = [
|
|
4482
|
+
{ value: "readme", label: "README.md", hint: "orientation page" },
|
|
4483
|
+
{ value: "manifest", label: "manifest.json", hint: "required by validate and report" },
|
|
4484
|
+
{ value: "parts", label: "parts/", hint: "the code payload" },
|
|
4485
|
+
{ value: "navigation", label: "reports/navigation.md", hint: "file \u2192 part index" },
|
|
4486
|
+
{ value: "projectTree", label: "reports/project-tree.md", hint: "ASCII tree" },
|
|
4487
|
+
{ value: "audit", label: "reports/audit.md", hint: "security and health findings" },
|
|
4488
|
+
{ value: "monolith", label: "full-codebase.json + .md", hint: "roughly doubles output" }
|
|
4489
|
+
];
|
|
4490
|
+
const presetEnabled = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]);
|
|
4491
|
+
const selectedArtifacts = await multiSelect(
|
|
4492
|
+
io,
|
|
4493
|
+
"Which artifacts should be generated?",
|
|
4494
|
+
artifactChoices,
|
|
4495
|
+
{ defaults: presetEnabled }
|
|
4496
|
+
);
|
|
4497
|
+
for (const name of ARTIFACT_NAMES) answers.artifacts[name] = selectedArtifacts.includes(name);
|
|
4498
|
+
const enabledCount = ARTIFACT_NAMES.filter((name) => answers.artifacts[name]).length;
|
|
4499
|
+
answers.includeSensitiveFiles = await confirm(
|
|
4500
|
+
io,
|
|
4501
|
+
"Include credential files (.env, *.pem, .npmrc, .ssh/)?",
|
|
4502
|
+
{ defaultValue: false }
|
|
4503
|
+
);
|
|
4504
|
+
io.output.write("\n Review\n\n");
|
|
4505
|
+
io.output.write(` project ${answers.preset}
|
|
4506
|
+
`);
|
|
4507
|
+
io.output.write(` include ${answers.include.length ? answers.include.join(", ") : "everything"}
|
|
4508
|
+
`);
|
|
4509
|
+
io.output.write(` part size ${String(answers.maxPartBytes)}
|
|
4510
|
+
`);
|
|
4511
|
+
io.output.write(` artifacts ${enabledCount} of ${ARTIFACT_NAMES.length}
|
|
4512
|
+
`);
|
|
4513
|
+
io.output.write(` credentials ${answers.includeSensitiveFiles ? "included" : "skipped"}
|
|
4514
|
+
|
|
4515
|
+
`);
|
|
4516
|
+
io.output.write(" Will write snapshot.config with your answers, plus every other setting\n");
|
|
4517
|
+
io.output.write(" at its default \u2014 so nothing is hidden behind the docs.\n\n");
|
|
4518
|
+
const proceed = await confirm(io, "Write this and continue?", { defaultValue: true });
|
|
4519
|
+
if (!proceed) throw new PromptCancelled();
|
|
4520
|
+
}
|
|
4521
|
+
const configText = options.minimal ? renderMinimalConfig(answers) : renderConfig(answers);
|
|
4522
|
+
const configName = supportsTypeScriptImports() ? "snapshot.config.ts" : "snapshot.config.mjs";
|
|
4523
|
+
const configPath = path10__namespace.join(options.cwd, configName);
|
|
4524
|
+
if (options.dryRun) {
|
|
4525
|
+
return {
|
|
4526
|
+
configPath,
|
|
4527
|
+
configText,
|
|
4528
|
+
written: false,
|
|
4529
|
+
preset: answers.preset,
|
|
4530
|
+
scripts: [],
|
|
4531
|
+
gitignoreEntries: [],
|
|
4532
|
+
notes
|
|
4533
|
+
};
|
|
4534
|
+
}
|
|
4535
|
+
const present = existingConfig(options.cwd);
|
|
4536
|
+
if (present && present !== configName && !options.force) {
|
|
4537
|
+
throw new Error(
|
|
4538
|
+
`A configuration already exists (${present}). Re-run with --force to replace it.`
|
|
4539
|
+
);
|
|
4540
|
+
}
|
|
4541
|
+
if (fs9__namespace.existsSync(configPath) && !options.force) {
|
|
4542
|
+
throw new Error(`${configName} already exists. Re-run with --force to replace it.`);
|
|
4543
|
+
}
|
|
4544
|
+
fs9__namespace.writeFileSync(configPath, configText, "utf8");
|
|
4545
|
+
const scripts = addScripts(options.cwd, notes);
|
|
4546
|
+
const gitignoreEntries = addGitignore(options.cwd);
|
|
4547
|
+
return {
|
|
4548
|
+
configPath,
|
|
4549
|
+
configText,
|
|
4550
|
+
written: true,
|
|
4551
|
+
preset: answers.preset,
|
|
4552
|
+
scripts,
|
|
4553
|
+
gitignoreEntries,
|
|
4554
|
+
notes
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
|
|
2712
4558
|
// src/index.ts
|
|
2713
4559
|
var GENERATOR_NAME = "@ankurhalder/codebase-snapshot";
|
|
2714
|
-
var GENERATOR_VERSION = "2.
|
|
4560
|
+
var GENERATOR_VERSION = "2.1.1";
|
|
2715
4561
|
var SCHEMA_VERSION = "3.0.0";
|
|
2716
4562
|
function sha256Hex2(input) {
|
|
2717
4563
|
return crypto__namespace.createHash("sha256").update(input).digest("hex");
|
|
2718
4564
|
}
|
|
2719
|
-
var
|
|
4565
|
+
var TS_EXTENSIONS = [
|
|
2720
4566
|
".ts",
|
|
2721
4567
|
".mts",
|
|
2722
4568
|
".cts",
|
|
@@ -2724,24 +4570,21 @@ var SUMMARY_EXTENSIONS = [
|
|
|
2724
4570
|
".js",
|
|
2725
4571
|
".mjs",
|
|
2726
4572
|
".cjs",
|
|
2727
|
-
".jsx"
|
|
2728
|
-
".json",
|
|
2729
|
-
".jsonc",
|
|
2730
|
-
".md",
|
|
2731
|
-
".mdx"
|
|
4573
|
+
".jsx"
|
|
2732
4574
|
];
|
|
2733
4575
|
function summarizeFile(relativePath, redactedContent) {
|
|
2734
4576
|
const lower = relativePath.toLowerCase();
|
|
2735
|
-
if (!SUMMARY_EXTENSIONS.some((extension) => lower.endsWith(extension))) {
|
|
2736
|
-
return void 0;
|
|
2737
|
-
}
|
|
2738
4577
|
if (lower.endsWith(".json") || lower.endsWith(".jsonc")) {
|
|
2739
|
-
return summarizeJson(redactedContent);
|
|
4578
|
+
return { summary: summarizeJson(redactedContent), quality: "structural" };
|
|
2740
4579
|
}
|
|
2741
4580
|
if (lower.endsWith(".md") || lower.endsWith(".mdx")) {
|
|
2742
|
-
return summarizeMarkdown(redactedContent);
|
|
4581
|
+
return { summary: summarizeMarkdown(redactedContent), quality: "structural" };
|
|
4582
|
+
}
|
|
4583
|
+
if (TS_EXTENSIONS.some((extension) => lower.endsWith(extension))) {
|
|
4584
|
+
return { summary: summarizeTypeScriptSource(relativePath, redactedContent), quality: "ast" };
|
|
2743
4585
|
}
|
|
2744
|
-
|
|
4586
|
+
const profiled = summarizeWithProfile(relativePath, redactedContent);
|
|
4587
|
+
return profiled ? { summary: profiled.summary, quality: profiled.quality } : void 0;
|
|
2745
4588
|
}
|
|
2746
4589
|
function isSourceSummary(summary) {
|
|
2747
4590
|
return Boolean(summary && typeof summary === "object" && "imports" in summary);
|
|
@@ -2767,8 +4610,8 @@ var IMPORT_CANDIDATE_SUFFIXES = [
|
|
|
2767
4610
|
];
|
|
2768
4611
|
function resolveSpecifier(fromFile, specifier, knownFiles) {
|
|
2769
4612
|
if (!RELATIVE_SPECIFIER.test(specifier)) return null;
|
|
2770
|
-
const baseDir =
|
|
2771
|
-
const joined =
|
|
4613
|
+
const baseDir = path10__namespace.posix.dirname(fromFile);
|
|
4614
|
+
const joined = path10__namespace.posix.normalize(path10__namespace.posix.join(baseDir, specifier));
|
|
2772
4615
|
const candidate = joined.replace(/\.(?:js|mjs|cjs|jsx)$/, "");
|
|
2773
4616
|
for (const suffix of IMPORT_CANDIDATE_SUFFIXES) {
|
|
2774
4617
|
const withSuffix = `${joined}${suffix}`;
|
|
@@ -2860,11 +4703,11 @@ function verifyInMemory(files, parts, maxPartBytes) {
|
|
|
2860
4703
|
return errors;
|
|
2861
4704
|
}
|
|
2862
4705
|
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}${
|
|
4706
|
+
const cwd = path10__namespace.resolve(config.cwd);
|
|
4707
|
+
const outDir = path10__namespace.resolve(cwd, config.outDir);
|
|
4708
|
+
const tmpDir = path10__namespace.resolve(cwd, config.tmpDir);
|
|
4709
|
+
const root = path10__namespace.parse(cwd).root;
|
|
4710
|
+
const contains = (parent, child) => child === parent || child.startsWith(`${parent}${path10__namespace.sep}`);
|
|
2868
4711
|
const reject = (reason) => {
|
|
2869
4712
|
throw new Error(
|
|
2870
4713
|
`Refusing to generate: ${reason}. outDir=${outDir} tmpDir=${tmpDir} cwd=${cwd}`
|
|
@@ -2883,26 +4726,66 @@ function resolveSafeOutputDirs(config) {
|
|
|
2883
4726
|
if (contains(tmpDir, outDir)) reject("outDir is inside tmpDir");
|
|
2884
4727
|
return { outDir, tmpDir };
|
|
2885
4728
|
}
|
|
4729
|
+
var PLACEHOLDER_ENTRIES = /* @__PURE__ */ new Set([".gitkeep", ".gitignore", ".DS_Store"]);
|
|
4730
|
+
var OWNERSHIP_MARKER = ".codebase-snapshot";
|
|
4731
|
+
function writeOwnershipMarker(dir) {
|
|
4732
|
+
const marker2 = {
|
|
4733
|
+
generator: GENERATOR_NAME,
|
|
4734
|
+
version: GENERATOR_VERSION,
|
|
4735
|
+
schemaVersion: SCHEMA_VERSION
|
|
4736
|
+
};
|
|
4737
|
+
fs9__namespace.writeFileSync(path10__namespace.join(dir, OWNERSHIP_MARKER), `${JSON.stringify(marker2, null, 2)}
|
|
4738
|
+
`, "utf8");
|
|
4739
|
+
}
|
|
4740
|
+
function isOwnOutput(dir) {
|
|
4741
|
+
if (fs9__namespace.existsSync(path10__namespace.join(dir, OWNERSHIP_MARKER))) return true;
|
|
4742
|
+
const manifestPath = path10__namespace.join(dir, "manifest.json");
|
|
4743
|
+
if (!fs9__namespace.existsSync(manifestPath)) return false;
|
|
4744
|
+
try {
|
|
4745
|
+
const parsed = JSON.parse(fs9__namespace.readFileSync(manifestPath, "utf8"));
|
|
4746
|
+
const generator = parsed.generator;
|
|
4747
|
+
return typeof parsed.schemaVersion === "string" && Array.isArray(parsed.files) && typeof generator === "object" && generator !== null && generator.name === GENERATOR_NAME;
|
|
4748
|
+
} catch {
|
|
4749
|
+
return false;
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
function assertReplaceable(target, label) {
|
|
4753
|
+
if (!fs9__namespace.existsSync(target)) return;
|
|
4754
|
+
if (!fs9__namespace.statSync(target).isDirectory()) {
|
|
4755
|
+
throw new Error(
|
|
4756
|
+
`Refusing to write into ${label} ${target}: it exists but is not a directory.`
|
|
4757
|
+
);
|
|
4758
|
+
}
|
|
4759
|
+
const entries = fs9__namespace.readdirSync(target);
|
|
4760
|
+
const meaningful = entries.filter((name) => !PLACEHOLDER_ENTRIES.has(name));
|
|
4761
|
+
if (meaningful.length === 0) return;
|
|
4762
|
+
if (isOwnOutput(target)) return;
|
|
4763
|
+
const preview = meaningful.slice(0, 5).join(", ");
|
|
4764
|
+
const more = meaningful.length > 5 ? `, and ${meaningful.length - 5} more` : "";
|
|
4765
|
+
throw new Error(
|
|
4766
|
+
`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.`
|
|
4767
|
+
);
|
|
4768
|
+
}
|
|
2886
4769
|
function swapIntoPlace(tmpDir, outDir) {
|
|
2887
4770
|
const backupDir = `${outDir}.backup-${process.pid}-${Date.now()}`;
|
|
2888
|
-
const hadPrevious =
|
|
2889
|
-
if (hadPrevious)
|
|
4771
|
+
const hadPrevious = fs9__namespace.existsSync(outDir);
|
|
4772
|
+
if (hadPrevious) fs9__namespace.renameSync(outDir, backupDir);
|
|
2890
4773
|
try {
|
|
2891
4774
|
try {
|
|
2892
|
-
|
|
4775
|
+
fs9__namespace.renameSync(tmpDir, outDir);
|
|
2893
4776
|
} catch (err) {
|
|
2894
4777
|
if (err.code === "EXDEV") {
|
|
2895
|
-
|
|
2896
|
-
|
|
4778
|
+
fs9__namespace.cpSync(tmpDir, outDir, { recursive: true });
|
|
4779
|
+
fs9__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
2897
4780
|
} else {
|
|
2898
4781
|
throw err;
|
|
2899
4782
|
}
|
|
2900
4783
|
}
|
|
2901
|
-
if (hadPrevious)
|
|
4784
|
+
if (hadPrevious) fs9__namespace.rmSync(backupDir, { recursive: true, force: true });
|
|
2902
4785
|
} catch (err) {
|
|
2903
|
-
if (hadPrevious &&
|
|
2904
|
-
if (
|
|
2905
|
-
|
|
4786
|
+
if (hadPrevious && fs9__namespace.existsSync(backupDir)) {
|
|
4787
|
+
if (fs9__namespace.existsSync(outDir)) fs9__namespace.rmSync(outDir, { recursive: true, force: true });
|
|
4788
|
+
fs9__namespace.renameSync(backupDir, outDir);
|
|
2906
4789
|
}
|
|
2907
4790
|
throw err;
|
|
2908
4791
|
}
|
|
@@ -2918,7 +4801,9 @@ function artifactWarnings(artifacts) {
|
|
|
2918
4801
|
);
|
|
2919
4802
|
}
|
|
2920
4803
|
if (ARTIFACT_NAMES.every((name) => artifacts[name] === false)) {
|
|
2921
|
-
messages.push(
|
|
4804
|
+
messages.push(
|
|
4805
|
+
"every artifact is disabled: only the .codebase-snapshot ownership marker will be written."
|
|
4806
|
+
);
|
|
2922
4807
|
}
|
|
2923
4808
|
return messages;
|
|
2924
4809
|
}
|
|
@@ -2966,7 +4851,7 @@ function generateSnapshot(configInput, options) {
|
|
|
2966
4851
|
const gitInfo = getGitInfo(config.cwd, config.branch);
|
|
2967
4852
|
const collection = collectFiles(config.cwd, {
|
|
2968
4853
|
includePatterns: config.include,
|
|
2969
|
-
excludePatterns: config.excludePatterns,
|
|
4854
|
+
excludePatterns: [...config.excludePatterns, ...config.presetExclude],
|
|
2970
4855
|
includeSensitiveFiles: config.includeSensitiveFiles
|
|
2971
4856
|
});
|
|
2972
4857
|
const files = [];
|
|
@@ -2979,10 +4864,10 @@ function generateSnapshot(configInput, options) {
|
|
|
2979
4864
|
let totalSizeBytes = 0;
|
|
2980
4865
|
let largeFileCount = 0;
|
|
2981
4866
|
for (const relativePath of collection.files) {
|
|
2982
|
-
const absolutePath =
|
|
4867
|
+
const absolutePath = path10__namespace.join(config.cwd, relativePath);
|
|
2983
4868
|
let stats;
|
|
2984
4869
|
try {
|
|
2985
|
-
stats =
|
|
4870
|
+
stats = fs9__namespace.statSync(absolutePath);
|
|
2986
4871
|
if (!stats.isFile()) {
|
|
2987
4872
|
skipped.push({ path: relativePath, reason: "not-file" });
|
|
2988
4873
|
continue;
|
|
@@ -2997,7 +4882,7 @@ function generateSnapshot(configInput, options) {
|
|
|
2997
4882
|
}
|
|
2998
4883
|
let buffer;
|
|
2999
4884
|
try {
|
|
3000
|
-
buffer =
|
|
4885
|
+
buffer = fs9__namespace.readFileSync(absolutePath);
|
|
3001
4886
|
} catch {
|
|
3002
4887
|
skipped.push({ path: relativePath, reason: "read-error" });
|
|
3003
4888
|
continue;
|
|
@@ -3007,10 +4892,10 @@ function generateSnapshot(configInput, options) {
|
|
|
3007
4892
|
const lineCount = countLines(rawContent);
|
|
3008
4893
|
totalLines += lineCount;
|
|
3009
4894
|
totalSizeBytes += stats.size;
|
|
3010
|
-
const
|
|
4895
|
+
const layer2 = classifyLayer(relativePath, config.layers);
|
|
3011
4896
|
const kind = classifyKind(relativePath, config.kinds);
|
|
3012
4897
|
const language = detectLanguage(relativePath);
|
|
3013
|
-
bump(layerCounts,
|
|
4898
|
+
bump(layerCounts, layer2);
|
|
3014
4899
|
bump(kindCounts, kind);
|
|
3015
4900
|
const { redacted, findings } = scanAndRedactSecrets(
|
|
3016
4901
|
rawContent,
|
|
@@ -3034,8 +4919,11 @@ function generateSnapshot(configInput, options) {
|
|
|
3034
4919
|
includedLines = preview.includedLines;
|
|
3035
4920
|
}
|
|
3036
4921
|
let summary;
|
|
4922
|
+
let summaryQuality;
|
|
3037
4923
|
try {
|
|
3038
|
-
|
|
4924
|
+
const outcome = summarizeFile(relativePath, redacted);
|
|
4925
|
+
summary = outcome?.summary;
|
|
4926
|
+
summaryQuality = outcome?.quality;
|
|
3039
4927
|
if (isSourceSummary(summary) && summary.parseErrors) {
|
|
3040
4928
|
warnings.push(
|
|
3041
4929
|
`Parse errors in ${relativePath} (${summary.parseErrors}); its summary is incomplete.`
|
|
@@ -3052,7 +4940,7 @@ function generateSnapshot(configInput, options) {
|
|
|
3052
4940
|
lines: lineCount,
|
|
3053
4941
|
hashSha256,
|
|
3054
4942
|
part: "",
|
|
3055
|
-
layer,
|
|
4943
|
+
layer: layer2,
|
|
3056
4944
|
kind,
|
|
3057
4945
|
language,
|
|
3058
4946
|
inclusionMode: isPreviewTruncated ? "preview" : "full",
|
|
@@ -3062,6 +4950,7 @@ function generateSnapshot(configInput, options) {
|
|
|
3062
4950
|
includedLines,
|
|
3063
4951
|
includedBytes: Buffer.byteLength(finalContent, "utf8"),
|
|
3064
4952
|
summary,
|
|
4953
|
+
summaryQuality,
|
|
3065
4954
|
content: finalContent
|
|
3066
4955
|
});
|
|
3067
4956
|
}
|
|
@@ -3102,10 +4991,11 @@ function generateSnapshot(configInput, options) {
|
|
|
3102
4991
|
includeSensitiveFiles: config.includeSensitiveFiles,
|
|
3103
4992
|
artifacts: { ...config.artifacts },
|
|
3104
4993
|
include: [...config.include],
|
|
4994
|
+
preset: config.preset,
|
|
3105
4995
|
layersMode: config.layersMode,
|
|
3106
4996
|
budgets: { default: config.budgets.default, byLayer: { ...config.budgets.byLayer } },
|
|
3107
4997
|
excludePatterns: [...config.excludePatterns],
|
|
3108
|
-
layers: config.layers.map((
|
|
4998
|
+
layers: config.layers.map((layer2) => layer2.name),
|
|
3109
4999
|
kinds: config.kinds.map((kind) => kind.kind)
|
|
3110
5000
|
},
|
|
3111
5001
|
counts: {
|
|
@@ -3192,45 +5082,48 @@ function generateSnapshot(configInput, options) {
|
|
|
3192
5082
|
return manifest;
|
|
3193
5083
|
}
|
|
3194
5084
|
const { outDir, tmpDir } = resolveSafeOutputDirs(config);
|
|
5085
|
+
assertReplaceable(tmpDir, "tmpDir");
|
|
5086
|
+
assertReplaceable(outDir, "outDir");
|
|
3195
5087
|
const { artifacts } = config;
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
const
|
|
3200
|
-
|
|
5088
|
+
fs9__namespace.rmSync(tmpDir, { recursive: true, force: true });
|
|
5089
|
+
fs9__namespace.mkdirSync(tmpDir, { recursive: true });
|
|
5090
|
+
writeOwnershipMarker(tmpDir);
|
|
5091
|
+
const partsDir = path10__namespace.join(tmpDir, "parts");
|
|
5092
|
+
const reportsDir = path10__namespace.join(tmpDir, "reports");
|
|
5093
|
+
if (artifacts.parts) fs9__namespace.mkdirSync(partsDir, { recursive: true });
|
|
3201
5094
|
if (artifacts.navigation || artifacts.projectTree || artifacts.audit) {
|
|
3202
|
-
|
|
5095
|
+
fs9__namespace.mkdirSync(reportsDir, { recursive: true });
|
|
3203
5096
|
}
|
|
3204
5097
|
if (artifacts.parts) {
|
|
3205
5098
|
for (const part of parts) {
|
|
3206
|
-
|
|
5099
|
+
fs9__namespace.writeFileSync(path10__namespace.join(partsDir, part.filename), part.markdown, "utf8");
|
|
3207
5100
|
}
|
|
3208
5101
|
}
|
|
3209
5102
|
if (artifacts.projectTree) {
|
|
3210
5103
|
const projectTree = generateProjectTree(files.map((file) => file.relativePath));
|
|
3211
|
-
|
|
5104
|
+
fs9__namespace.writeFileSync(path10__namespace.join(reportsDir, "project-tree.md"), projectTree, "utf8");
|
|
3212
5105
|
}
|
|
3213
5106
|
if (artifacts.navigation) {
|
|
3214
|
-
|
|
5107
|
+
fs9__namespace.writeFileSync(path10__namespace.join(reportsDir, "navigation.md"), generateNavigation(manifest), "utf8");
|
|
3215
5108
|
}
|
|
3216
5109
|
if (artifacts.audit) {
|
|
3217
|
-
|
|
3218
|
-
|
|
5110
|
+
fs9__namespace.writeFileSync(
|
|
5111
|
+
path10__namespace.join(reportsDir, "audit.md"),
|
|
3219
5112
|
generateAuditReport(manifest, secretFindings),
|
|
3220
5113
|
"utf8"
|
|
3221
5114
|
);
|
|
3222
5115
|
}
|
|
3223
5116
|
if (artifacts.readme) {
|
|
3224
|
-
|
|
5117
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "README.md"), generateReadme(manifest), "utf8");
|
|
3225
5118
|
}
|
|
3226
5119
|
if (artifacts.manifest) {
|
|
3227
|
-
|
|
5120
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
3228
5121
|
}
|
|
3229
5122
|
if (monolithJson !== null) {
|
|
3230
|
-
|
|
5123
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "full-codebase.json"), monolithJson, "utf8");
|
|
3231
5124
|
}
|
|
3232
5125
|
if (monolithMarkdown !== null) {
|
|
3233
|
-
|
|
5126
|
+
fs9__namespace.writeFileSync(path10__namespace.join(tmpDir, "full-codebase.md"), monolithMarkdown, "utf8");
|
|
3234
5127
|
}
|
|
3235
5128
|
swapIntoPlace(tmpDir, outDir);
|
|
3236
5129
|
runAfterGenerate(config, manifest);
|
|
@@ -3241,12 +5134,17 @@ async function generateSnapshotAsync(configInput, options) {
|
|
|
3241
5134
|
}
|
|
3242
5135
|
|
|
3243
5136
|
exports.ARTIFACT_NAMES = ARTIFACT_NAMES;
|
|
5137
|
+
exports.DEFAULT_ANSWERS = DEFAULT_ANSWERS;
|
|
3244
5138
|
exports.DEFAULT_SECRET_PATTERNS = DEFAULT_SECRET_PATTERNS;
|
|
3245
5139
|
exports.GENERATOR_NAME = GENERATOR_NAME;
|
|
3246
5140
|
exports.GENERATOR_VERSION = GENERATOR_VERSION;
|
|
5141
|
+
exports.LANGUAGE_PROFILES = LANGUAGE_PROFILES;
|
|
3247
5142
|
exports.MIN_MAX_PART_BYTES = MIN_MAX_PART_BYTES;
|
|
5143
|
+
exports.PRESETS = PRESETS;
|
|
5144
|
+
exports.PromptCancelled = PromptCancelled;
|
|
3248
5145
|
exports.SCHEMA_VERSION = SCHEMA_VERSION;
|
|
3249
5146
|
exports.anchorForPath = anchorForPath;
|
|
5147
|
+
exports.assertReplaceable = assertReplaceable;
|
|
3250
5148
|
exports.assertUsableConfigInput = assertUsableConfigInput;
|
|
3251
5149
|
exports.collectFiles = collectFiles;
|
|
3252
5150
|
exports.compareManifests = compareManifests;
|
|
@@ -3259,12 +5157,15 @@ exports.fenceForContent = fenceForContent;
|
|
|
3259
5157
|
exports.generateProjectTree = generateProjectTree;
|
|
3260
5158
|
exports.generateSnapshot = generateSnapshot;
|
|
3261
5159
|
exports.generateSnapshotAsync = generateSnapshotAsync;
|
|
5160
|
+
exports.getPreset = getPreset;
|
|
3262
5161
|
exports.globToRegExp = globToRegExp;
|
|
3263
5162
|
exports.isBinaryBuffer = isBinaryBuffer;
|
|
3264
5163
|
exports.isBinaryExtension = isBinaryExtension;
|
|
3265
5164
|
exports.isBinaryFile = isBinaryFile;
|
|
5165
|
+
exports.isInteractive = isInteractive;
|
|
3266
5166
|
exports.isResolvedConfig = isResolvedConfig;
|
|
3267
5167
|
exports.isSensitivePath = isSensitivePath;
|
|
5168
|
+
exports.listPresetNames = listPresetNames;
|
|
3268
5169
|
exports.loadConfigFile = loadConfigFile;
|
|
3269
5170
|
exports.loadConfigFileDetailed = loadConfigFileDetailed;
|
|
3270
5171
|
exports.loadManifest = loadManifest;
|
|
@@ -3274,16 +5175,21 @@ exports.matchesExclude = matchesExclude;
|
|
|
3274
5175
|
exports.normalizeResolvedConfig = normalizeResolvedConfig;
|
|
3275
5176
|
exports.parseByteSize = parseByteSize;
|
|
3276
5177
|
exports.partitionFiles = partitionFiles;
|
|
5178
|
+
exports.profileForPath = profileForPath;
|
|
5179
|
+
exports.renderConfig = renderConfig;
|
|
3277
5180
|
exports.renderDiffText = renderDiffText;
|
|
3278
5181
|
exports.renderExplainText = renderExplainText;
|
|
5182
|
+
exports.renderMinimalConfig = renderMinimalConfig;
|
|
3279
5183
|
exports.renumberParts = renumberParts;
|
|
3280
5184
|
exports.resolveConfig = resolveConfig;
|
|
3281
5185
|
exports.resolveSafeOutputDirs = resolveSafeOutputDirs;
|
|
5186
|
+
exports.runInit = runInit;
|
|
3282
5187
|
exports.scanAndRedactSecrets = scanAndRedactSecrets;
|
|
3283
5188
|
exports.stripJsonComments = stripJsonComments;
|
|
3284
5189
|
exports.summarizeJson = summarizeJson;
|
|
3285
5190
|
exports.summarizeMarkdown = summarizeMarkdown;
|
|
3286
5191
|
exports.summarizeTypeScriptSource = summarizeTypeScriptSource;
|
|
5192
|
+
exports.summarizeWithProfile = summarizeWithProfile;
|
|
3287
5193
|
exports.supportsTypeScriptImports = supportsTypeScriptImports;
|
|
3288
5194
|
exports.truncatePreview = truncatePreview;
|
|
3289
5195
|
exports.truncateToBytes = truncateToBytes;
|