@doccov/sdk 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +397 -822
- package/dist/index.js +595 -1237
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -4,168 +4,378 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
-
var __toESM = (
|
|
8
|
-
target =
|
|
9
|
-
const to = isNodeMode || !
|
|
10
|
-
for (let key of __getOwnPropNames(
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
11
|
if (!__hasOwnProp.call(to, key))
|
|
12
12
|
__defProp(to, key, {
|
|
13
|
-
get: () =>
|
|
13
|
+
get: () => mod[key],
|
|
14
14
|
enumerable: true
|
|
15
15
|
});
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
|
-
// src/
|
|
20
|
+
// src/extract/schema/standard-schema.ts
|
|
21
|
+
import { spawn } from "node:child_process";
|
|
21
22
|
import * as fs from "node:fs";
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (typeof value !== "object" || value === null) {
|
|
23
|
+
import * as path from "node:path";
|
|
24
|
+
function isStandardJSONSchema(obj) {
|
|
25
|
+
if (typeof obj !== "object" || obj === null)
|
|
26
26
|
return false;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const standard = obj["~standard"];
|
|
30
|
-
if (typeof standard !== "object" || standard === null) {
|
|
27
|
+
const std = obj["~standard"];
|
|
28
|
+
if (typeof std !== "object" || std === null)
|
|
31
29
|
return false;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const stdObj = std;
|
|
31
|
+
if (typeof stdObj.version !== "number")
|
|
32
|
+
return false;
|
|
33
|
+
if (typeof stdObj.vendor !== "string")
|
|
35
34
|
return false;
|
|
36
|
-
}
|
|
37
35
|
const jsonSchema = stdObj.jsonSchema;
|
|
38
|
-
if (typeof jsonSchema !== "object" || jsonSchema === null)
|
|
36
|
+
if (typeof jsonSchema !== "object" || jsonSchema === null)
|
|
39
37
|
return false;
|
|
38
|
+
const jsObj = jsonSchema;
|
|
39
|
+
return typeof jsObj.output === "function";
|
|
40
|
+
}
|
|
41
|
+
var WORKER_SCRIPT = `
|
|
42
|
+
const path = require('path');
|
|
43
|
+
const { pathToFileURL } = require('url');
|
|
44
|
+
|
|
45
|
+
async function extract() {
|
|
46
|
+
// With node -e, argv is: [node, arg1, arg2, ...]
|
|
47
|
+
// (the -e script is NOT in argv)
|
|
48
|
+
const [modulePath, target] = process.argv.slice(1);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
// Import the module using dynamic import (works with ESM and CJS)
|
|
52
|
+
const absPath = path.resolve(modulePath);
|
|
53
|
+
const mod = await import(pathToFileURL(absPath).href);
|
|
54
|
+
const results = [];
|
|
55
|
+
|
|
56
|
+
// Check each export
|
|
57
|
+
for (const [name, value] of Object.entries(mod)) {
|
|
58
|
+
if (name.startsWith('_')) continue;
|
|
59
|
+
if (typeof value !== 'object' || value === null) continue;
|
|
60
|
+
|
|
61
|
+
const std = value['~standard'];
|
|
62
|
+
if (!std || typeof std !== 'object') continue;
|
|
63
|
+
if (typeof std.version !== 'number') continue;
|
|
64
|
+
if (typeof std.vendor !== 'string') continue;
|
|
65
|
+
if (!std.jsonSchema || typeof std.jsonSchema.output !== 'function') continue;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const outputSchema = std.jsonSchema.output(target);
|
|
69
|
+
const inputSchema = std.jsonSchema.input ? std.jsonSchema.input(target) : undefined;
|
|
70
|
+
|
|
71
|
+
results.push({
|
|
72
|
+
exportName: name,
|
|
73
|
+
vendor: std.vendor,
|
|
74
|
+
outputSchema,
|
|
75
|
+
inputSchema
|
|
76
|
+
});
|
|
77
|
+
} catch (e) {
|
|
78
|
+
// Skip schemas that fail to extract
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(JSON.stringify({ success: true, results }));
|
|
83
|
+
} catch (e) {
|
|
84
|
+
console.log(JSON.stringify({ success: false, error: e.message }));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
extract();
|
|
89
|
+
`;
|
|
90
|
+
function resolveCompiledPath(tsPath, baseDir) {
|
|
91
|
+
const relativePath = path.relative(baseDir, tsPath);
|
|
92
|
+
const withoutExt = relativePath.replace(/\.tsx?$/, "");
|
|
93
|
+
const candidates = [
|
|
94
|
+
path.join(baseDir, `${withoutExt}.js`),
|
|
95
|
+
path.join(baseDir, "dist", `${withoutExt.replace(/^src\//, "")}.js`),
|
|
96
|
+
path.join(baseDir, "build", `${withoutExt.replace(/^src\//, "")}.js`),
|
|
97
|
+
path.join(baseDir, "lib", `${withoutExt.replace(/^src\//, "")}.js`)
|
|
98
|
+
];
|
|
99
|
+
for (const candidate of candidates) {
|
|
100
|
+
if (fs.existsSync(candidate)) {
|
|
101
|
+
return candidate;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
async function extractStandardSchemas(compiledJsPath, options = {}) {
|
|
107
|
+
const { timeout = 1e4, target = "draft-2020-12" } = options;
|
|
108
|
+
const result = {
|
|
109
|
+
schemas: new Map,
|
|
110
|
+
errors: []
|
|
111
|
+
};
|
|
112
|
+
if (!fs.existsSync(compiledJsPath)) {
|
|
113
|
+
result.errors.push(`Compiled JS not found: ${compiledJsPath}`);
|
|
114
|
+
return result;
|
|
40
115
|
}
|
|
41
|
-
|
|
42
|
-
|
|
116
|
+
return new Promise((resolve) => {
|
|
117
|
+
const child = spawn("node", ["-e", WORKER_SCRIPT, compiledJsPath, target], {
|
|
118
|
+
timeout,
|
|
119
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
120
|
+
});
|
|
121
|
+
let stdout = "";
|
|
122
|
+
let stderr = "";
|
|
123
|
+
child.stdout.on("data", (data) => {
|
|
124
|
+
stdout += data.toString();
|
|
125
|
+
});
|
|
126
|
+
child.stderr.on("data", (data) => {
|
|
127
|
+
stderr += data.toString();
|
|
128
|
+
});
|
|
129
|
+
child.on("close", (code) => {
|
|
130
|
+
if (code !== 0) {
|
|
131
|
+
result.errors.push(`Extraction process failed: ${stderr || `exit code ${code}`}`);
|
|
132
|
+
resolve(result);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
const parsed = JSON.parse(stdout);
|
|
137
|
+
if (!parsed.success) {
|
|
138
|
+
result.errors.push(`Extraction failed: ${parsed.error}`);
|
|
139
|
+
resolve(result);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
for (const item of parsed.results) {
|
|
143
|
+
result.schemas.set(item.exportName, {
|
|
144
|
+
exportName: item.exportName,
|
|
145
|
+
vendor: item.vendor,
|
|
146
|
+
outputSchema: item.outputSchema,
|
|
147
|
+
inputSchema: item.inputSchema
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
} catch (e) {
|
|
151
|
+
result.errors.push(`Failed to parse extraction output: ${e}`);
|
|
152
|
+
}
|
|
153
|
+
resolve(result);
|
|
154
|
+
});
|
|
155
|
+
child.on("error", (err) => {
|
|
156
|
+
result.errors.push(`Subprocess error: ${err.message}`);
|
|
157
|
+
resolve(result);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
43
160
|
}
|
|
44
|
-
function
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
161
|
+
async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
|
|
162
|
+
const compiledPath = resolveCompiledPath(entryFile, baseDir);
|
|
163
|
+
if (!compiledPath) {
|
|
164
|
+
return {
|
|
165
|
+
schemas: new Map,
|
|
166
|
+
errors: [`Could not find compiled JS for ${entryFile}. Build the project first.`]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return extractStandardSchemas(compiledPath, options);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/analysis/schema-detection.ts
|
|
173
|
+
async function detectRuntimeSchemas(context) {
|
|
174
|
+
const { baseDir, entryFile } = context;
|
|
175
|
+
const compiledPath = resolveCompiledPath(entryFile, baseDir);
|
|
176
|
+
if (!compiledPath) {
|
|
177
|
+
return {
|
|
178
|
+
schemas: new Map,
|
|
179
|
+
errors: []
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const extraction = await extractStandardSchemasFromProject(entryFile, baseDir);
|
|
183
|
+
const schemas = new Map;
|
|
184
|
+
for (const [name, result] of extraction.schemas) {
|
|
185
|
+
schemas.set(name, {
|
|
186
|
+
schema: result.outputSchema,
|
|
187
|
+
vendor: result.vendor
|
|
188
|
+
});
|
|
189
|
+
}
|
|
48
190
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
version: standard.version
|
|
191
|
+
schemas,
|
|
192
|
+
errors: extraction.errors
|
|
52
193
|
};
|
|
53
194
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
195
|
+
function clearSchemaCache() {}
|
|
196
|
+
// src/extract/schema/types.ts
|
|
197
|
+
function isTypeReference(type) {
|
|
198
|
+
return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
|
|
199
|
+
}
|
|
200
|
+
function getNonNullableType(type) {
|
|
201
|
+
if (type.isUnion()) {
|
|
202
|
+
const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
|
|
203
|
+
if (nonNullable.length === 1) {
|
|
204
|
+
return nonNullable[0];
|
|
205
|
+
}
|
|
57
206
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
207
|
+
return type;
|
|
208
|
+
}
|
|
209
|
+
// src/extract/schema/adapters/zod.ts
|
|
210
|
+
var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
|
|
211
|
+
var zodAdapter = {
|
|
212
|
+
id: "zod",
|
|
213
|
+
packages: ["zod"],
|
|
214
|
+
matches(type, checker) {
|
|
215
|
+
const typeName = checker.typeToString(type);
|
|
216
|
+
return ZOD_TYPE_PATTERN.test(typeName);
|
|
217
|
+
},
|
|
218
|
+
extractOutputType(type, checker) {
|
|
219
|
+
const outputSymbol = type.getProperty("_output");
|
|
220
|
+
if (outputSymbol) {
|
|
221
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
222
|
+
}
|
|
223
|
+
const typeSymbol = type.getProperty("_type");
|
|
224
|
+
if (typeSymbol) {
|
|
225
|
+
return checker.getTypeOfSymbol(typeSymbol);
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
},
|
|
229
|
+
extractInputType(type, checker) {
|
|
230
|
+
const inputSymbol = type.getProperty("_input");
|
|
231
|
+
if (inputSymbol) {
|
|
232
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
233
|
+
}
|
|
61
234
|
return null;
|
|
62
235
|
}
|
|
63
|
-
}
|
|
64
|
-
var KNOWN_VENDORS = {
|
|
65
|
-
zod: { minVersion: "4.2.0", homepage: "https://zod.dev" },
|
|
66
|
-
arktype: { minVersion: "2.0.0", homepage: "https://arktype.io" },
|
|
67
|
-
valibot: { minVersion: "1.0.0", homepage: "https://valibot.dev" }
|
|
68
236
|
};
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
237
|
+
|
|
238
|
+
// src/extract/schema/adapters/valibot.ts
|
|
239
|
+
var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
|
|
240
|
+
var valibotAdapter = {
|
|
241
|
+
id: "valibot",
|
|
242
|
+
packages: ["valibot"],
|
|
243
|
+
matches(type, checker) {
|
|
244
|
+
const typeName = checker.typeToString(type);
|
|
245
|
+
return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
|
|
246
|
+
},
|
|
247
|
+
extractOutputType(type, checker) {
|
|
248
|
+
const typesSymbol = type.getProperty("~types");
|
|
249
|
+
if (!typesSymbol) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
253
|
+
typesType = getNonNullableType(typesType);
|
|
254
|
+
const outputSymbol = typesType.getProperty("output");
|
|
255
|
+
if (!outputSymbol) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
259
|
+
},
|
|
260
|
+
extractInputType(type, checker) {
|
|
261
|
+
const typesSymbol = type.getProperty("~types");
|
|
262
|
+
if (!typesSymbol) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
266
|
+
typesType = getNonNullableType(typesType);
|
|
267
|
+
const inputSymbol = typesType.getProperty("input");
|
|
268
|
+
if (!inputSymbol) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
76
272
|
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// src/extract/schema/adapters/typebox.ts
|
|
276
|
+
var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
|
|
277
|
+
var typeboxAdapter = {
|
|
278
|
+
id: "typebox",
|
|
279
|
+
packages: ["@sinclair/typebox"],
|
|
280
|
+
matches(type, checker) {
|
|
281
|
+
const typeName = checker.typeToString(type);
|
|
282
|
+
if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
const typeProperty = type.getProperty("type");
|
|
286
|
+
return typeProperty !== undefined;
|
|
287
|
+
},
|
|
288
|
+
extractOutputType(type, checker) {
|
|
289
|
+
const staticSymbol = type.getProperty("static");
|
|
290
|
+
if (staticSymbol) {
|
|
291
|
+
return checker.getTypeOfSymbol(staticSymbol);
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
83
294
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/extract/schema/adapters/arktype.ts
|
|
298
|
+
var ARKTYPE_TYPE_PATTERN = /^Type</;
|
|
299
|
+
var arktypeAdapter = {
|
|
300
|
+
id: "arktype",
|
|
301
|
+
packages: ["arktype"],
|
|
302
|
+
matches(type, checker) {
|
|
303
|
+
const typeName = checker.typeToString(type);
|
|
304
|
+
return ARKTYPE_TYPE_PATTERN.test(typeName);
|
|
305
|
+
},
|
|
306
|
+
extractOutputType(type, checker) {
|
|
307
|
+
if (!isTypeReference(type)) {
|
|
308
|
+
return null;
|
|
88
309
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
mod = eval("require")(modulePath);
|
|
93
|
-
} catch (err) {
|
|
94
|
-
const nodeErr = err;
|
|
95
|
-
if (nodeErr.code === "ERR_REQUIRE_ESM") {
|
|
96
|
-
const cacheBuster = currentMtime > 0 ? `?t=${currentMtime}` : "";
|
|
97
|
-
mod = await import(`${modulePath}${cacheBuster}`);
|
|
98
|
-
} else {
|
|
99
|
-
throw err;
|
|
310
|
+
const args = checker.getTypeArguments(type);
|
|
311
|
+
if (args.length < 1) {
|
|
312
|
+
return null;
|
|
100
313
|
}
|
|
314
|
+
return args[0];
|
|
315
|
+
},
|
|
316
|
+
extractInputType(type, checker) {
|
|
317
|
+
if (!isTypeReference(type)) {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
const args = checker.getTypeArguments(type);
|
|
321
|
+
if (args.length < 2) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
return args[1];
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
// src/extract/schema/registry.ts
|
|
329
|
+
var adapters = [
|
|
330
|
+
zodAdapter,
|
|
331
|
+
arktypeAdapter,
|
|
332
|
+
typeboxAdapter,
|
|
333
|
+
valibotAdapter
|
|
334
|
+
];
|
|
335
|
+
function findAdapter(type, checker) {
|
|
336
|
+
for (const adapter of adapters) {
|
|
337
|
+
if (adapter.matches(type, checker)) {
|
|
338
|
+
return adapter;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
function isSchemaType(type, checker) {
|
|
344
|
+
return findAdapter(type, checker) !== null;
|
|
345
|
+
}
|
|
346
|
+
function extractSchemaOutputType(type, checker) {
|
|
347
|
+
const adapter = findAdapter(type, checker);
|
|
348
|
+
if (!adapter) {
|
|
349
|
+
return null;
|
|
101
350
|
}
|
|
102
|
-
|
|
103
|
-
return mod;
|
|
351
|
+
return adapter.extractOutputType(type, checker);
|
|
104
352
|
}
|
|
105
|
-
|
|
353
|
+
function extractSchemaType(type, checker) {
|
|
354
|
+
const adapter = findAdapter(type, checker);
|
|
355
|
+
if (!adapter) {
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
358
|
+
const outputType = adapter.extractOutputType(type, checker);
|
|
359
|
+
if (!outputType) {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
106
362
|
const result = {
|
|
107
|
-
|
|
108
|
-
|
|
363
|
+
adapter,
|
|
364
|
+
outputType
|
|
109
365
|
};
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
result.
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
const mod2 = await loadModule(modulePath2);
|
|
117
|
-
if (!mod2 || typeof mod2 !== "object") {
|
|
118
|
-
result.errors.push("Module did not export an object");
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
const exports = mod2;
|
|
122
|
-
for (const [name, value] of Object.entries(exports)) {
|
|
123
|
-
if (name.startsWith("_") || name === "default") {
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
const schemaResult = tryExtractStandardSchema(value);
|
|
127
|
-
if (schemaResult) {
|
|
128
|
-
result.schemas.set(name, schemaResult);
|
|
129
|
-
}
|
|
366
|
+
if (adapter.extractInputType) {
|
|
367
|
+
const inputType = adapter.extractInputType(type, checker);
|
|
368
|
+
if (inputType) {
|
|
369
|
+
result.inputType = inputType;
|
|
130
370
|
}
|
|
131
|
-
if ("default" in exports && exports.default && typeof exports.default === "object") {
|
|
132
|
-
const defaultExports = exports.default;
|
|
133
|
-
for (const [name, value] of Object.entries(defaultExports)) {
|
|
134
|
-
if (name.startsWith("_"))
|
|
135
|
-
continue;
|
|
136
|
-
const schemaResult = tryExtractStandardSchema(value);
|
|
137
|
-
if (schemaResult && !result.schemas.has(name)) {
|
|
138
|
-
result.schemas.set(name, schemaResult);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
} catch (err) {
|
|
143
|
-
result.errors.push(`Runtime detection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
144
371
|
}
|
|
145
372
|
return result;
|
|
146
373
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
return entryFile;
|
|
150
|
-
}
|
|
151
|
-
const tsFile = entryFile;
|
|
152
|
-
const possiblePaths = [
|
|
153
|
-
tsFile.replace(/\.tsx?$/, ".js"),
|
|
154
|
-
tsFile.replace(/\.tsx?$/, ".mjs"),
|
|
155
|
-
tsFile.replace(/\/src\//, "/dist/").replace(/\.tsx?$/, ".js"),
|
|
156
|
-
tsFile.replace(/\/src\//, "/build/").replace(/\.tsx?$/, ".js"),
|
|
157
|
-
tsFile.replace(/\/src\//, "/lib/").replace(/\.tsx?$/, ".js")
|
|
158
|
-
];
|
|
159
|
-
for (const testPath of possiblePaths) {
|
|
160
|
-
try {
|
|
161
|
-
__require.resolve(testPath, { paths: [baseDir] });
|
|
162
|
-
return testPath;
|
|
163
|
-
} catch {}
|
|
164
|
-
}
|
|
165
|
-
return null;
|
|
374
|
+
function getRegisteredAdapters() {
|
|
375
|
+
return adapters;
|
|
166
376
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
377
|
+
function getSupportedLibraries() {
|
|
378
|
+
return adapters.flatMap((a) => a.packages);
|
|
169
379
|
}
|
|
170
380
|
// src/analysis/docs-coverage.ts
|
|
171
381
|
import {
|
|
@@ -597,7 +807,7 @@ function categorizeDrifts(drifts) {
|
|
|
597
807
|
}
|
|
598
808
|
// src/fix/jsdoc-writer.ts
|
|
599
809
|
import * as fs2 from "node:fs";
|
|
600
|
-
import * as
|
|
810
|
+
import * as path2 from "node:path";
|
|
601
811
|
|
|
602
812
|
// src/ts-module.ts
|
|
603
813
|
import * as tsNamespace from "typescript";
|
|
@@ -964,7 +1174,7 @@ async function applyEdits(edits) {
|
|
|
964
1174
|
}
|
|
965
1175
|
function createSourceFile(filePath) {
|
|
966
1176
|
const content = fs2.readFileSync(filePath, "utf-8");
|
|
967
|
-
return ts.createSourceFile(
|
|
1177
|
+
return ts.createSourceFile(path2.basename(filePath), content, ts.ScriptTarget.Latest, true, filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
968
1178
|
}
|
|
969
1179
|
// src/utils/builtin-detection.ts
|
|
970
1180
|
function isBuiltInTypeName(name) {
|
|
@@ -2162,480 +2372,6 @@ function ensureSpecCoverage(spec) {
|
|
|
2162
2372
|
}
|
|
2163
2373
|
};
|
|
2164
2374
|
}
|
|
2165
|
-
// src/quality/rules.ts
|
|
2166
|
-
var BUILTIN_TYPES = new Set([
|
|
2167
|
-
"string",
|
|
2168
|
-
"number",
|
|
2169
|
-
"boolean",
|
|
2170
|
-
"object",
|
|
2171
|
-
"any",
|
|
2172
|
-
"unknown",
|
|
2173
|
-
"void",
|
|
2174
|
-
"never",
|
|
2175
|
-
"null",
|
|
2176
|
-
"undefined",
|
|
2177
|
-
"symbol",
|
|
2178
|
-
"bigint",
|
|
2179
|
-
"Array",
|
|
2180
|
-
"Promise",
|
|
2181
|
-
"Map",
|
|
2182
|
-
"Set",
|
|
2183
|
-
"Record",
|
|
2184
|
-
"Partial",
|
|
2185
|
-
"Required",
|
|
2186
|
-
"Readonly",
|
|
2187
|
-
"Pick",
|
|
2188
|
-
"Omit",
|
|
2189
|
-
"Exclude",
|
|
2190
|
-
"Extract",
|
|
2191
|
-
"NonNullable",
|
|
2192
|
-
"ReturnType",
|
|
2193
|
-
"Parameters",
|
|
2194
|
-
"InstanceType",
|
|
2195
|
-
"ConstructorParameters",
|
|
2196
|
-
"Awaited"
|
|
2197
|
-
]);
|
|
2198
|
-
function extractTypeReferences(exp) {
|
|
2199
|
-
const refs = new Set;
|
|
2200
|
-
function collectFromSchema(schema) {
|
|
2201
|
-
if (!schema)
|
|
2202
|
-
return;
|
|
2203
|
-
if (typeof schema === "string") {
|
|
2204
|
-
if (!BUILTIN_TYPES.has(schema)) {
|
|
2205
|
-
refs.add(schema);
|
|
2206
|
-
}
|
|
2207
|
-
return;
|
|
2208
|
-
}
|
|
2209
|
-
if (typeof schema === "object") {
|
|
2210
|
-
const obj = schema;
|
|
2211
|
-
if (typeof obj.$ref === "string") {
|
|
2212
|
-
const ref = obj.$ref;
|
|
2213
|
-
const name = ref.startsWith("#/types/") ? ref.slice("#/types/".length) : ref;
|
|
2214
|
-
if (!BUILTIN_TYPES.has(name)) {
|
|
2215
|
-
refs.add(name);
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
if (typeof obj.type === "string" && !BUILTIN_TYPES.has(obj.type)) {
|
|
2219
|
-
refs.add(obj.type);
|
|
2220
|
-
}
|
|
2221
|
-
if (obj.items) {
|
|
2222
|
-
collectFromSchema(obj.items);
|
|
2223
|
-
}
|
|
2224
|
-
if (obj.properties && typeof obj.properties === "object") {
|
|
2225
|
-
for (const prop of Object.values(obj.properties)) {
|
|
2226
|
-
collectFromSchema(prop);
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
if (Array.isArray(obj.anyOf)) {
|
|
2230
|
-
for (const item of obj.anyOf) {
|
|
2231
|
-
collectFromSchema(item);
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
if (Array.isArray(obj.oneOf)) {
|
|
2235
|
-
for (const item of obj.oneOf) {
|
|
2236
|
-
collectFromSchema(item);
|
|
2237
|
-
}
|
|
2238
|
-
}
|
|
2239
|
-
if (Array.isArray(obj.allOf)) {
|
|
2240
|
-
for (const item of obj.allOf) {
|
|
2241
|
-
collectFromSchema(item);
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
if (obj.additionalProperties && typeof obj.additionalProperties === "object") {
|
|
2245
|
-
collectFromSchema(obj.additionalProperties);
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
for (const sig of exp.signatures ?? []) {
|
|
2250
|
-
for (const param of sig.parameters ?? []) {
|
|
2251
|
-
collectFromSchema(param.schema);
|
|
2252
|
-
}
|
|
2253
|
-
if (sig.returns?.schema) {
|
|
2254
|
-
collectFromSchema(sig.returns.schema);
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
for (const member of exp.members ?? []) {
|
|
2258
|
-
collectFromSchema(member.schema);
|
|
2259
|
-
for (const sig of member.signatures ?? []) {
|
|
2260
|
-
for (const param of sig.parameters ?? []) {
|
|
2261
|
-
collectFromSchema(param.schema);
|
|
2262
|
-
}
|
|
2263
|
-
if (sig.returns?.schema) {
|
|
2264
|
-
collectFromSchema(sig.returns.schema);
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
}
|
|
2268
|
-
collectFromSchema(exp.schema);
|
|
2269
|
-
if (typeof exp.type === "string" && !BUILTIN_TYPES.has(exp.type)) {
|
|
2270
|
-
refs.add(exp.type);
|
|
2271
|
-
}
|
|
2272
|
-
if (exp.extends && !BUILTIN_TYPES.has(exp.extends)) {
|
|
2273
|
-
refs.add(exp.extends);
|
|
2274
|
-
}
|
|
2275
|
-
for (const impl of exp.implements ?? []) {
|
|
2276
|
-
if (!BUILTIN_TYPES.has(impl)) {
|
|
2277
|
-
refs.add(impl);
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
return refs;
|
|
2281
|
-
}
|
|
2282
|
-
var CORE_RULES = [
|
|
2283
|
-
{
|
|
2284
|
-
id: "has-description",
|
|
2285
|
-
name: "Has Description",
|
|
2286
|
-
description: "Export has a description comment",
|
|
2287
|
-
affectsCoverage: true,
|
|
2288
|
-
defaultSeverity: "warn",
|
|
2289
|
-
check(ctx) {
|
|
2290
|
-
return Boolean(ctx.export.description?.trim());
|
|
2291
|
-
},
|
|
2292
|
-
getViolation(ctx) {
|
|
2293
|
-
return {
|
|
2294
|
-
ruleId: "has-description",
|
|
2295
|
-
severity: "warn",
|
|
2296
|
-
message: `Export '${ctx.export.name}' is missing a description`,
|
|
2297
|
-
fixable: false
|
|
2298
|
-
};
|
|
2299
|
-
}
|
|
2300
|
-
},
|
|
2301
|
-
{
|
|
2302
|
-
id: "has-params",
|
|
2303
|
-
name: "Has Parameters",
|
|
2304
|
-
description: "All parameters are documented",
|
|
2305
|
-
appliesTo: ["function"],
|
|
2306
|
-
affectsCoverage: true,
|
|
2307
|
-
defaultSeverity: "off",
|
|
2308
|
-
check(ctx) {
|
|
2309
|
-
const parameters = (ctx.export.signatures ?? []).flatMap((sig) => sig.parameters ?? []);
|
|
2310
|
-
if (parameters.length === 0)
|
|
2311
|
-
return true;
|
|
2312
|
-
return parameters.every((p) => Boolean(p.description?.trim()));
|
|
2313
|
-
},
|
|
2314
|
-
getViolation(ctx) {
|
|
2315
|
-
return {
|
|
2316
|
-
ruleId: "has-params",
|
|
2317
|
-
severity: "warn",
|
|
2318
|
-
message: `Function '${ctx.export.name}' has undocumented parameters`,
|
|
2319
|
-
fixable: true
|
|
2320
|
-
};
|
|
2321
|
-
}
|
|
2322
|
-
},
|
|
2323
|
-
{
|
|
2324
|
-
id: "has-returns",
|
|
2325
|
-
name: "Has Returns",
|
|
2326
|
-
description: "Return value is documented",
|
|
2327
|
-
appliesTo: ["function"],
|
|
2328
|
-
affectsCoverage: true,
|
|
2329
|
-
defaultSeverity: "off",
|
|
2330
|
-
check(ctx) {
|
|
2331
|
-
const signatures = ctx.export.signatures ?? [];
|
|
2332
|
-
if (signatures.length === 0)
|
|
2333
|
-
return true;
|
|
2334
|
-
return signatures.every((sig) => {
|
|
2335
|
-
const text = sig.returns?.description;
|
|
2336
|
-
return Boolean(text?.trim());
|
|
2337
|
-
});
|
|
2338
|
-
},
|
|
2339
|
-
getViolation(ctx) {
|
|
2340
|
-
return {
|
|
2341
|
-
ruleId: "has-returns",
|
|
2342
|
-
severity: "warn",
|
|
2343
|
-
message: `Function '${ctx.export.name}' has undocumented return value`,
|
|
2344
|
-
fixable: true
|
|
2345
|
-
};
|
|
2346
|
-
}
|
|
2347
|
-
},
|
|
2348
|
-
{
|
|
2349
|
-
id: "has-examples",
|
|
2350
|
-
name: "Has Examples",
|
|
2351
|
-
description: "Export has at least one @example",
|
|
2352
|
-
appliesTo: ["function", "class"],
|
|
2353
|
-
affectsCoverage: true,
|
|
2354
|
-
defaultSeverity: "off",
|
|
2355
|
-
check(ctx) {
|
|
2356
|
-
return Boolean(ctx.export.examples?.length);
|
|
2357
|
-
},
|
|
2358
|
-
getViolation(ctx) {
|
|
2359
|
-
return {
|
|
2360
|
-
ruleId: "has-examples",
|
|
2361
|
-
severity: "warn",
|
|
2362
|
-
message: `Export '${ctx.export.name}' is missing an @example`,
|
|
2363
|
-
fixable: false
|
|
2364
|
-
};
|
|
2365
|
-
}
|
|
2366
|
-
}
|
|
2367
|
-
];
|
|
2368
|
-
var TSDOC_RULES = [
|
|
2369
|
-
{
|
|
2370
|
-
id: "require-release-tag",
|
|
2371
|
-
name: "Require Release Tag",
|
|
2372
|
-
description: "All exports must have @public, @beta, @alpha, or @internal",
|
|
2373
|
-
affectsCoverage: false,
|
|
2374
|
-
defaultSeverity: "off",
|
|
2375
|
-
check(ctx) {
|
|
2376
|
-
const tags = ctx.export.tags ?? [];
|
|
2377
|
-
return tags.some((t) => ["public", "beta", "alpha", "internal"].includes(t.name.toLowerCase()));
|
|
2378
|
-
},
|
|
2379
|
-
getViolation(ctx) {
|
|
2380
|
-
return {
|
|
2381
|
-
ruleId: "require-release-tag",
|
|
2382
|
-
severity: "warn",
|
|
2383
|
-
message: `Export '${ctx.export.name}' is missing a release tag (@public, @beta, @alpha, or @internal)`,
|
|
2384
|
-
fixable: true
|
|
2385
|
-
};
|
|
2386
|
-
}
|
|
2387
|
-
},
|
|
2388
|
-
{
|
|
2389
|
-
id: "internal-underscore",
|
|
2390
|
-
name: "Internal Underscore Prefix",
|
|
2391
|
-
description: "@internal exports should have underscore prefix",
|
|
2392
|
-
affectsCoverage: false,
|
|
2393
|
-
defaultSeverity: "off",
|
|
2394
|
-
check(ctx) {
|
|
2395
|
-
const tags = ctx.export.tags ?? [];
|
|
2396
|
-
const isInternal = tags.some((t) => t.name.toLowerCase() === "internal");
|
|
2397
|
-
if (!isInternal)
|
|
2398
|
-
return true;
|
|
2399
|
-
return ctx.export.name.startsWith("_");
|
|
2400
|
-
},
|
|
2401
|
-
getViolation(ctx) {
|
|
2402
|
-
return {
|
|
2403
|
-
ruleId: "internal-underscore",
|
|
2404
|
-
severity: "warn",
|
|
2405
|
-
message: `Internal export '${ctx.export.name}' should have underscore prefix (_${ctx.export.name})`,
|
|
2406
|
-
fixable: false
|
|
2407
|
-
};
|
|
2408
|
-
}
|
|
2409
|
-
},
|
|
2410
|
-
{
|
|
2411
|
-
id: "no-conflicting-tags",
|
|
2412
|
-
name: "No Conflicting Tags",
|
|
2413
|
-
description: "Cannot have both @internal and @public/@beta/@alpha",
|
|
2414
|
-
affectsCoverage: false,
|
|
2415
|
-
defaultSeverity: "warn",
|
|
2416
|
-
check(ctx) {
|
|
2417
|
-
const tags = ctx.export.tags ?? [];
|
|
2418
|
-
const tagNames = tags.map((t) => t.name.toLowerCase());
|
|
2419
|
-
const hasInternal = tagNames.includes("internal");
|
|
2420
|
-
const hasPublicish = tagNames.some((n) => ["public", "beta", "alpha"].includes(n));
|
|
2421
|
-
return !(hasInternal && hasPublicish);
|
|
2422
|
-
},
|
|
2423
|
-
getViolation(ctx) {
|
|
2424
|
-
return {
|
|
2425
|
-
ruleId: "no-conflicting-tags",
|
|
2426
|
-
severity: "error",
|
|
2427
|
-
message: `Export '${ctx.export.name}' has conflicting release tags (@internal with @public/@beta/@alpha)`,
|
|
2428
|
-
fixable: false
|
|
2429
|
-
};
|
|
2430
|
-
}
|
|
2431
|
-
},
|
|
2432
|
-
{
|
|
2433
|
-
id: "no-forgotten-export",
|
|
2434
|
-
name: "No Forgotten Export",
|
|
2435
|
-
description: "All referenced types must be exported",
|
|
2436
|
-
affectsCoverage: false,
|
|
2437
|
-
defaultSeverity: "off",
|
|
2438
|
-
check(ctx) {
|
|
2439
|
-
if (!ctx.exportRegistry)
|
|
2440
|
-
return true;
|
|
2441
|
-
const refs = extractTypeReferences(ctx.export);
|
|
2442
|
-
for (const ref of refs) {
|
|
2443
|
-
if (!ctx.exportRegistry.has(ref)) {
|
|
2444
|
-
return false;
|
|
2445
|
-
}
|
|
2446
|
-
}
|
|
2447
|
-
return true;
|
|
2448
|
-
},
|
|
2449
|
-
getViolation(ctx) {
|
|
2450
|
-
const refs = extractTypeReferences(ctx.export);
|
|
2451
|
-
const missing = ctx.exportRegistry ? [...refs].filter((r) => !ctx.exportRegistry.has(r)) : [];
|
|
2452
|
-
return {
|
|
2453
|
-
ruleId: "no-forgotten-export",
|
|
2454
|
-
severity: "warn",
|
|
2455
|
-
message: missing.length > 0 ? `Export '${ctx.export.name}' references unexported types: ${missing.join(", ")}` : `Export '${ctx.export.name}' references types that are not exported`,
|
|
2456
|
-
fixable: false
|
|
2457
|
-
};
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
];
|
|
2461
|
-
var STYLE_RULES = [
|
|
2462
|
-
{
|
|
2463
|
-
id: "no-empty-returns",
|
|
2464
|
-
name: "No Empty Returns",
|
|
2465
|
-
description: "@returns tag must have a description",
|
|
2466
|
-
appliesTo: ["function"],
|
|
2467
|
-
affectsCoverage: false,
|
|
2468
|
-
defaultSeverity: "warn",
|
|
2469
|
-
check(ctx) {
|
|
2470
|
-
if (!ctx.rawJSDoc)
|
|
2471
|
-
return true;
|
|
2472
|
-
const returnsMatch = ctx.rawJSDoc.match(/@returns?\s*(?:\{[^}]*\})?\s*$/m);
|
|
2473
|
-
if (returnsMatch)
|
|
2474
|
-
return false;
|
|
2475
|
-
const returnsTypeOnly = ctx.rawJSDoc.match(/@returns?\s+\{[^}]+\}\s*$/m);
|
|
2476
|
-
if (returnsTypeOnly)
|
|
2477
|
-
return false;
|
|
2478
|
-
return true;
|
|
2479
|
-
},
|
|
2480
|
-
getViolation(ctx) {
|
|
2481
|
-
return {
|
|
2482
|
-
ruleId: "no-empty-returns",
|
|
2483
|
-
severity: "warn",
|
|
2484
|
-
message: `Export '${ctx.export.name}' has @returns without a description`,
|
|
2485
|
-
fixable: false
|
|
2486
|
-
};
|
|
2487
|
-
}
|
|
2488
|
-
},
|
|
2489
|
-
{
|
|
2490
|
-
id: "consistent-param-style",
|
|
2491
|
-
name: "Consistent Param Style",
|
|
2492
|
-
description: "@param tags use dash separator",
|
|
2493
|
-
appliesTo: ["function"],
|
|
2494
|
-
affectsCoverage: false,
|
|
2495
|
-
defaultSeverity: "off",
|
|
2496
|
-
check(ctx) {
|
|
2497
|
-
if (!ctx.rawJSDoc)
|
|
2498
|
-
return true;
|
|
2499
|
-
const paramRegex = /@param\s+(?:\{[^}]+\}\s+)?(\S+)\s+([^@\n]+)/g;
|
|
2500
|
-
const matches = ctx.rawJSDoc.matchAll(paramRegex);
|
|
2501
|
-
for (const match of matches) {
|
|
2502
|
-
const rest = match[2]?.trim();
|
|
2503
|
-
if (rest && !rest.startsWith("-") && !rest.startsWith("–")) {
|
|
2504
|
-
return false;
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
return true;
|
|
2508
|
-
},
|
|
2509
|
-
getViolation(ctx) {
|
|
2510
|
-
return {
|
|
2511
|
-
ruleId: "consistent-param-style",
|
|
2512
|
-
severity: "warn",
|
|
2513
|
-
message: `Export '${ctx.export.name}' has @param without dash separator`,
|
|
2514
|
-
fixable: true
|
|
2515
|
-
};
|
|
2516
|
-
},
|
|
2517
|
-
fix(ctx) {
|
|
2518
|
-
if (!ctx.rawJSDoc)
|
|
2519
|
-
return null;
|
|
2520
|
-
const patch = parseJSDocToPatch(ctx.rawJSDoc);
|
|
2521
|
-
if (!patch.params || patch.params.length === 0)
|
|
2522
|
-
return null;
|
|
2523
|
-
return patch;
|
|
2524
|
-
}
|
|
2525
|
-
}
|
|
2526
|
-
];
|
|
2527
|
-
var BUILTIN_RULES = [...CORE_RULES, ...TSDOC_RULES, ...STYLE_RULES];
|
|
2528
|
-
function getCoverageRules() {
|
|
2529
|
-
return BUILTIN_RULES.filter((r) => r.affectsCoverage);
|
|
2530
|
-
}
|
|
2531
|
-
function getRulesForKind(kind) {
|
|
2532
|
-
return BUILTIN_RULES.filter((r) => {
|
|
2533
|
-
if (!r.appliesTo)
|
|
2534
|
-
return true;
|
|
2535
|
-
return r.appliesTo.includes(kind);
|
|
2536
|
-
});
|
|
2537
|
-
}
|
|
2538
|
-
function getRule(id) {
|
|
2539
|
-
return BUILTIN_RULES.find((r) => r.id === id);
|
|
2540
|
-
}
|
|
2541
|
-
function getDefaultConfig() {
|
|
2542
|
-
const config = {};
|
|
2543
|
-
for (const rule of BUILTIN_RULES) {
|
|
2544
|
-
config[rule.id] = rule.defaultSeverity;
|
|
2545
|
-
}
|
|
2546
|
-
return config;
|
|
2547
|
-
}
|
|
2548
|
-
|
|
2549
|
-
// src/quality/engine.ts
|
|
2550
|
-
function evaluateExportQuality(exp, rawJSDoc, config = { rules: {} }, exportRegistry) {
|
|
2551
|
-
const kind = exp.kind ?? "variable";
|
|
2552
|
-
const applicableRules = getRulesForKind(kind);
|
|
2553
|
-
const defaults = getDefaultConfig();
|
|
2554
|
-
const getSeverity = (ruleId, defaultSev) => config.rules[ruleId] ?? defaults[ruleId] ?? defaultSev;
|
|
2555
|
-
const activeCoverageRules = applicableRules.filter((r) => {
|
|
2556
|
-
if (!r.affectsCoverage)
|
|
2557
|
-
return false;
|
|
2558
|
-
const severity = getSeverity(r.id, r.defaultSeverity);
|
|
2559
|
-
return severity !== "off";
|
|
2560
|
-
});
|
|
2561
|
-
const result = {
|
|
2562
|
-
coverageScore: 0,
|
|
2563
|
-
coverage: {
|
|
2564
|
-
satisfied: [],
|
|
2565
|
-
missing: [],
|
|
2566
|
-
applicable: activeCoverageRules.map((r) => r.id)
|
|
2567
|
-
},
|
|
2568
|
-
violations: [],
|
|
2569
|
-
summary: {
|
|
2570
|
-
errorCount: 0,
|
|
2571
|
-
warningCount: 0,
|
|
2572
|
-
fixableCount: 0
|
|
2573
|
-
}
|
|
2574
|
-
};
|
|
2575
|
-
const context = { export: exp, rawJSDoc, exportRegistry };
|
|
2576
|
-
for (const rule of applicableRules) {
|
|
2577
|
-
const passed = rule.check(context);
|
|
2578
|
-
const severity = getSeverity(rule.id, rule.defaultSeverity);
|
|
2579
|
-
if (rule.affectsCoverage && severity !== "off") {
|
|
2580
|
-
if (passed) {
|
|
2581
|
-
result.coverage.satisfied.push(rule.id);
|
|
2582
|
-
} else {
|
|
2583
|
-
result.coverage.missing.push(rule.id);
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
if (!passed && severity !== "off" && rule.getViolation) {
|
|
2587
|
-
const violation = {
|
|
2588
|
-
...rule.getViolation(context),
|
|
2589
|
-
severity: severity === "error" ? "error" : "warn"
|
|
2590
|
-
};
|
|
2591
|
-
result.violations.push(violation);
|
|
2592
|
-
if (violation.severity === "error") {
|
|
2593
|
-
result.summary.errorCount++;
|
|
2594
|
-
} else {
|
|
2595
|
-
result.summary.warningCount++;
|
|
2596
|
-
}
|
|
2597
|
-
if (violation.fixable) {
|
|
2598
|
-
result.summary.fixableCount++;
|
|
2599
|
-
}
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
const { satisfied, applicable } = result.coverage;
|
|
2603
|
-
result.coverageScore = applicable.length === 0 ? 100 : Math.round(satisfied.length / applicable.length * 100);
|
|
2604
|
-
return result;
|
|
2605
|
-
}
|
|
2606
|
-
function evaluateQuality(exports, config = { rules: {} }) {
|
|
2607
|
-
const byExport = new Map;
|
|
2608
|
-
let totalCoverage = 0;
|
|
2609
|
-
let totalErrors = 0;
|
|
2610
|
-
let totalWarnings = 0;
|
|
2611
|
-
for (const { export: exp, rawJSDoc } of exports) {
|
|
2612
|
-
const result = evaluateExportQuality(exp, rawJSDoc, config);
|
|
2613
|
-
byExport.set(exp.id ?? exp.name, result);
|
|
2614
|
-
totalCoverage += result.coverageScore;
|
|
2615
|
-
totalErrors += result.summary.errorCount;
|
|
2616
|
-
totalWarnings += result.summary.warningCount;
|
|
2617
|
-
}
|
|
2618
|
-
const count = exports.length;
|
|
2619
|
-
return {
|
|
2620
|
-
byExport,
|
|
2621
|
-
overall: {
|
|
2622
|
-
coverageScore: count === 0 ? 100 : Math.round(totalCoverage / count),
|
|
2623
|
-
totalViolations: totalErrors + totalWarnings,
|
|
2624
|
-
errorCount: totalErrors,
|
|
2625
|
-
warningCount: totalWarnings
|
|
2626
|
-
}
|
|
2627
|
-
};
|
|
2628
|
-
}
|
|
2629
|
-
function mergeConfig(userConfig) {
|
|
2630
|
-
const defaults = getDefaultConfig();
|
|
2631
|
-
return {
|
|
2632
|
-
rules: {
|
|
2633
|
-
...defaults,
|
|
2634
|
-
...userConfig.rules
|
|
2635
|
-
}
|
|
2636
|
-
};
|
|
2637
|
-
}
|
|
2638
|
-
|
|
2639
2375
|
// src/analysis/enrich.ts
|
|
2640
2376
|
function collectAllMissing(exports) {
|
|
2641
2377
|
const allMissing = new Set;
|
|
@@ -2657,38 +2393,33 @@ function collectAllDrift(exports) {
|
|
|
2657
2393
|
}
|
|
2658
2394
|
return allDrift;
|
|
2659
2395
|
}
|
|
2660
|
-
function
|
|
2661
|
-
const
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
}
|
|
2396
|
+
function computeExportCoverage(exp) {
|
|
2397
|
+
const missing = [];
|
|
2398
|
+
if (!exp.description) {
|
|
2399
|
+
missing.push("has-description");
|
|
2400
|
+
return { score: 0, missing };
|
|
2666
2401
|
}
|
|
2667
|
-
return
|
|
2402
|
+
return { score: 100, missing: [] };
|
|
2668
2403
|
}
|
|
2669
2404
|
function enrichSpec(spec, options = {}) {
|
|
2670
|
-
const { driftByExport
|
|
2405
|
+
const { driftByExport } = options;
|
|
2671
2406
|
const exportRegistry = buildExportRegistry(spec);
|
|
2672
2407
|
let totalCoverage = 0;
|
|
2673
2408
|
const enrichedExports = spec.exports.map((exp) => {
|
|
2674
|
-
const
|
|
2675
|
-
const quality = evaluateExportQuality(exp, rawJSDoc, qualityConfig, exportRegistry);
|
|
2409
|
+
const coverage = computeExportCoverage(exp);
|
|
2676
2410
|
const drift = computeExportDrift(exp, exportRegistry);
|
|
2677
2411
|
const additionalDrift = driftByExport?.get(exp.id);
|
|
2678
2412
|
const allDrift2 = additionalDrift ? [...drift, ...additionalDrift] : drift;
|
|
2679
|
-
totalCoverage +=
|
|
2413
|
+
totalCoverage += coverage.score;
|
|
2680
2414
|
const docs2 = {
|
|
2681
|
-
coverageScore:
|
|
2415
|
+
coverageScore: coverage.score
|
|
2682
2416
|
};
|
|
2683
|
-
if (
|
|
2684
|
-
docs2.missing =
|
|
2417
|
+
if (coverage.missing.length > 0) {
|
|
2418
|
+
docs2.missing = coverage.missing;
|
|
2685
2419
|
}
|
|
2686
2420
|
if (allDrift2.length > 0) {
|
|
2687
2421
|
docs2.drift = allDrift2;
|
|
2688
2422
|
}
|
|
2689
|
-
if (quality.violations.length > 0) {
|
|
2690
|
-
docs2.violations = quality.violations;
|
|
2691
|
-
}
|
|
2692
2423
|
return {
|
|
2693
2424
|
...exp,
|
|
2694
2425
|
docs: docs2
|
|
@@ -2697,7 +2428,6 @@ function enrichSpec(spec, options = {}) {
|
|
|
2697
2428
|
const count = enrichedExports.length;
|
|
2698
2429
|
const allMissing = collectAllMissing(enrichedExports);
|
|
2699
2430
|
const allDrift = collectAllDrift(enrichedExports);
|
|
2700
|
-
const allViolations = collectAllViolations(enrichedExports);
|
|
2701
2431
|
const docs = {
|
|
2702
2432
|
coverageScore: count === 0 ? 100 : Math.round(totalCoverage / count)
|
|
2703
2433
|
};
|
|
@@ -2707,9 +2437,6 @@ function enrichSpec(spec, options = {}) {
|
|
|
2707
2437
|
if (allDrift.length > 0) {
|
|
2708
2438
|
docs.drift = allDrift;
|
|
2709
2439
|
}
|
|
2710
|
-
if (allViolations.length > 0) {
|
|
2711
|
-
docs.violations = allViolations;
|
|
2712
|
-
}
|
|
2713
2440
|
const driftSummary = allDrift.length > 0 ? getDriftSummary(allDrift) : undefined;
|
|
2714
2441
|
return {
|
|
2715
2442
|
...spec,
|
|
@@ -2720,7 +2447,7 @@ function enrichSpec(spec, options = {}) {
|
|
|
2720
2447
|
}
|
|
2721
2448
|
// src/analysis/report.ts
|
|
2722
2449
|
import * as fs3 from "node:fs";
|
|
2723
|
-
import * as
|
|
2450
|
+
import * as path3 from "node:path";
|
|
2724
2451
|
|
|
2725
2452
|
// src/types/report.ts
|
|
2726
2453
|
var REPORT_VERSION = "1.0.0";
|
|
@@ -2796,7 +2523,7 @@ function generateReportFromEnriched(enriched) {
|
|
|
2796
2523
|
}
|
|
2797
2524
|
function loadCachedReport(reportPath = DEFAULT_REPORT_PATH) {
|
|
2798
2525
|
try {
|
|
2799
|
-
const fullPath =
|
|
2526
|
+
const fullPath = path3.resolve(reportPath);
|
|
2800
2527
|
if (!fs3.existsSync(fullPath)) {
|
|
2801
2528
|
return null;
|
|
2802
2529
|
}
|
|
@@ -2807,8 +2534,8 @@ function loadCachedReport(reportPath = DEFAULT_REPORT_PATH) {
|
|
|
2807
2534
|
}
|
|
2808
2535
|
}
|
|
2809
2536
|
function saveReport(report, reportPath = DEFAULT_REPORT_PATH) {
|
|
2810
|
-
const fullPath =
|
|
2811
|
-
const dir =
|
|
2537
|
+
const fullPath = path3.resolve(reportPath);
|
|
2538
|
+
const dir = path3.dirname(fullPath);
|
|
2812
2539
|
if (!fs3.existsSync(dir)) {
|
|
2813
2540
|
fs3.mkdirSync(dir, { recursive: true });
|
|
2814
2541
|
}
|
|
@@ -3010,7 +2737,7 @@ function renderApiSurface(spec) {
|
|
|
3010
2737
|
}
|
|
3011
2738
|
// src/analysis/history.ts
|
|
3012
2739
|
import * as fs4 from "node:fs";
|
|
3013
|
-
import * as
|
|
2740
|
+
import * as path4 from "node:path";
|
|
3014
2741
|
var HISTORY_DIR = ".doccov/history";
|
|
3015
2742
|
var RETENTION_DAYS = {
|
|
3016
2743
|
free: 7,
|
|
@@ -3048,16 +2775,16 @@ function computeSnapshot(spec, options) {
|
|
|
3048
2775
|
};
|
|
3049
2776
|
}
|
|
3050
2777
|
function saveSnapshot(snapshot, cwd) {
|
|
3051
|
-
const historyDir =
|
|
2778
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3052
2779
|
if (!fs4.existsSync(historyDir)) {
|
|
3053
2780
|
fs4.mkdirSync(historyDir, { recursive: true });
|
|
3054
2781
|
}
|
|
3055
2782
|
const filename = getSnapshotFilename(new Date(snapshot.timestamp));
|
|
3056
|
-
const filepath =
|
|
2783
|
+
const filepath = path4.join(historyDir, filename);
|
|
3057
2784
|
fs4.writeFileSync(filepath, JSON.stringify(snapshot, null, 2));
|
|
3058
2785
|
}
|
|
3059
2786
|
function loadSnapshots(cwd) {
|
|
3060
|
-
const historyDir =
|
|
2787
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3061
2788
|
if (!fs4.existsSync(historyDir)) {
|
|
3062
2789
|
return [];
|
|
3063
2790
|
}
|
|
@@ -3065,7 +2792,7 @@ function loadSnapshots(cwd) {
|
|
|
3065
2792
|
const snapshots = [];
|
|
3066
2793
|
for (const file of files) {
|
|
3067
2794
|
try {
|
|
3068
|
-
const content = fs4.readFileSync(
|
|
2795
|
+
const content = fs4.readFileSync(path4.join(historyDir, file), "utf-8");
|
|
3069
2796
|
snapshots.push(JSON.parse(content));
|
|
3070
2797
|
} catch {}
|
|
3071
2798
|
}
|
|
@@ -3105,7 +2832,7 @@ function formatDelta(delta) {
|
|
|
3105
2832
|
return "→0%";
|
|
3106
2833
|
}
|
|
3107
2834
|
function pruneHistory(cwd, keepCount = 100) {
|
|
3108
|
-
const historyDir =
|
|
2835
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3109
2836
|
if (!fs4.existsSync(historyDir)) {
|
|
3110
2837
|
return 0;
|
|
3111
2838
|
}
|
|
@@ -3113,7 +2840,7 @@ function pruneHistory(cwd, keepCount = 100) {
|
|
|
3113
2840
|
const toDelete = files.slice(keepCount);
|
|
3114
2841
|
for (const file of toDelete) {
|
|
3115
2842
|
try {
|
|
3116
|
-
fs4.unlinkSync(
|
|
2843
|
+
fs4.unlinkSync(path4.join(historyDir, file));
|
|
3117
2844
|
} catch {}
|
|
3118
2845
|
}
|
|
3119
2846
|
return toDelete.length;
|
|
@@ -3122,7 +2849,7 @@ function pruneByTier(cwd, tier) {
|
|
|
3122
2849
|
const retentionDays = RETENTION_DAYS[tier];
|
|
3123
2850
|
const cutoffDate = new Date;
|
|
3124
2851
|
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
|
|
3125
|
-
const historyDir =
|
|
2852
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3126
2853
|
if (!fs4.existsSync(historyDir)) {
|
|
3127
2854
|
return 0;
|
|
3128
2855
|
}
|
|
@@ -3130,7 +2857,7 @@ function pruneByTier(cwd, tier) {
|
|
|
3130
2857
|
let deleted = 0;
|
|
3131
2858
|
for (const file of files) {
|
|
3132
2859
|
try {
|
|
3133
|
-
const filepath =
|
|
2860
|
+
const filepath = path4.join(historyDir, file);
|
|
3134
2861
|
const content = fs4.readFileSync(filepath, "utf-8");
|
|
3135
2862
|
const snapshot = JSON.parse(content);
|
|
3136
2863
|
const snapshotDate = new Date(snapshot.timestamp);
|
|
@@ -3237,7 +2964,7 @@ function getExtendedTrend(spec, cwd, options) {
|
|
|
3237
2964
|
// src/cache/hash.ts
|
|
3238
2965
|
import * as crypto from "node:crypto";
|
|
3239
2966
|
import * as fs5 from "node:fs";
|
|
3240
|
-
import * as
|
|
2967
|
+
import * as path5 from "node:path";
|
|
3241
2968
|
function hashFile(filePath) {
|
|
3242
2969
|
try {
|
|
3243
2970
|
const content = fs5.readFileSync(filePath);
|
|
@@ -3254,21 +2981,21 @@ function hashFiles(filePaths, cwd) {
|
|
|
3254
2981
|
for (const filePath of filePaths) {
|
|
3255
2982
|
const hash = hashFile(filePath);
|
|
3256
2983
|
if (hash) {
|
|
3257
|
-
const relativePath =
|
|
2984
|
+
const relativePath = path5.relative(cwd, filePath);
|
|
3258
2985
|
hashes[relativePath] = hash;
|
|
3259
2986
|
}
|
|
3260
2987
|
}
|
|
3261
2988
|
return hashes;
|
|
3262
2989
|
}
|
|
3263
|
-
function diffHashes(
|
|
2990
|
+
function diffHashes(cached, current) {
|
|
3264
2991
|
const changed = [];
|
|
3265
|
-
for (const [file, hash] of Object.entries(
|
|
2992
|
+
for (const [file, hash] of Object.entries(cached)) {
|
|
3266
2993
|
if (current[file] !== hash) {
|
|
3267
2994
|
changed.push(file);
|
|
3268
2995
|
}
|
|
3269
2996
|
}
|
|
3270
2997
|
for (const file of Object.keys(current)) {
|
|
3271
|
-
if (!(file in
|
|
2998
|
+
if (!(file in cached)) {
|
|
3272
2999
|
changed.push(file);
|
|
3273
3000
|
}
|
|
3274
3001
|
}
|
|
@@ -3276,12 +3003,12 @@ function diffHashes(cached2, current) {
|
|
|
3276
3003
|
}
|
|
3277
3004
|
// src/cache/spec-cache.ts
|
|
3278
3005
|
import * as fs6 from "node:fs";
|
|
3279
|
-
import * as
|
|
3006
|
+
import * as path6 from "node:path";
|
|
3280
3007
|
var CACHE_VERSION = "1.0.0";
|
|
3281
3008
|
var SPEC_CACHE_FILE = ".doccov/spec.cache.json";
|
|
3282
3009
|
function loadSpecCache(cwd) {
|
|
3283
3010
|
try {
|
|
3284
|
-
const cachePath =
|
|
3011
|
+
const cachePath = path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3285
3012
|
if (!fs6.existsSync(cachePath)) {
|
|
3286
3013
|
return null;
|
|
3287
3014
|
}
|
|
@@ -3297,7 +3024,7 @@ function saveSpecCache(spec, context) {
|
|
|
3297
3024
|
cacheVersion: CACHE_VERSION,
|
|
3298
3025
|
generatedAt: new Date().toISOString(),
|
|
3299
3026
|
specVersion: spec.openpkg,
|
|
3300
|
-
entryFile:
|
|
3027
|
+
entryFile: path6.relative(cwd, entryFile),
|
|
3301
3028
|
hashes: {
|
|
3302
3029
|
tsconfig: tsconfigPath ? hashFile(tsconfigPath) : null,
|
|
3303
3030
|
packageJson: hashFile(packageJsonPath) ?? "",
|
|
@@ -3306,8 +3033,8 @@ function saveSpecCache(spec, context) {
|
|
|
3306
3033
|
config,
|
|
3307
3034
|
spec
|
|
3308
3035
|
};
|
|
3309
|
-
const cachePath =
|
|
3310
|
-
const dir =
|
|
3036
|
+
const cachePath = path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3037
|
+
const dir = path6.dirname(cachePath);
|
|
3311
3038
|
if (!fs6.existsSync(dir)) {
|
|
3312
3039
|
fs6.mkdirSync(dir, { recursive: true });
|
|
3313
3040
|
}
|
|
@@ -3318,7 +3045,7 @@ function validateSpecCache(cache, context) {
|
|
|
3318
3045
|
if (cache.cacheVersion !== CACHE_VERSION) {
|
|
3319
3046
|
return { valid: false, reason: "cache-version-mismatch" };
|
|
3320
3047
|
}
|
|
3321
|
-
const relativeEntry =
|
|
3048
|
+
const relativeEntry = path6.relative(cwd, entryFile);
|
|
3322
3049
|
if (cache.entryFile !== relativeEntry) {
|
|
3323
3050
|
return { valid: false, reason: "entry-file-changed" };
|
|
3324
3051
|
}
|
|
@@ -3341,7 +3068,7 @@ function validateSpecCache(cache, context) {
|
|
|
3341
3068
|
return { valid: true };
|
|
3342
3069
|
}
|
|
3343
3070
|
function clearSpecCache(cwd) {
|
|
3344
|
-
const cachePath =
|
|
3071
|
+
const cachePath = path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3345
3072
|
if (fs6.existsSync(cachePath)) {
|
|
3346
3073
|
fs6.unlinkSync(cachePath);
|
|
3347
3074
|
return true;
|
|
@@ -3349,325 +3076,26 @@ function clearSpecCache(cwd) {
|
|
|
3349
3076
|
return false;
|
|
3350
3077
|
}
|
|
3351
3078
|
function getSpecCachePath(cwd) {
|
|
3352
|
-
return
|
|
3353
|
-
}
|
|
3354
|
-
// src/codeowners/index.ts
|
|
3355
|
-
import * as fs7 from "node:fs";
|
|
3356
|
-
import * as path6 from "node:path";
|
|
3357
|
-
import { minimatch } from "minimatch";
|
|
3358
|
-
var CODEOWNERS_LOCATIONS = [
|
|
3359
|
-
"CODEOWNERS",
|
|
3360
|
-
".github/CODEOWNERS",
|
|
3361
|
-
"docs/CODEOWNERS"
|
|
3362
|
-
];
|
|
3363
|
-
function parseCodeOwners(content) {
|
|
3364
|
-
const rules = [];
|
|
3365
|
-
const lines = content.split(`
|
|
3366
|
-
`);
|
|
3367
|
-
for (let i = 0;i < lines.length; i++) {
|
|
3368
|
-
const line = lines[i].trim();
|
|
3369
|
-
if (!line || line.startsWith("#"))
|
|
3370
|
-
continue;
|
|
3371
|
-
const parts = line.split(/\s+/);
|
|
3372
|
-
if (parts.length < 2)
|
|
3373
|
-
continue;
|
|
3374
|
-
const pattern = parts[0];
|
|
3375
|
-
const owners = parts.slice(1).filter((o) => o.startsWith("@") || o.includes("@"));
|
|
3376
|
-
if (owners.length > 0) {
|
|
3377
|
-
rules.push({
|
|
3378
|
-
line: i + 1,
|
|
3379
|
-
pattern,
|
|
3380
|
-
owners
|
|
3381
|
-
});
|
|
3382
|
-
}
|
|
3383
|
-
}
|
|
3384
|
-
return rules;
|
|
3385
|
-
}
|
|
3386
|
-
function loadCodeOwners(baseDir) {
|
|
3387
|
-
for (const location of CODEOWNERS_LOCATIONS) {
|
|
3388
|
-
const filePath = path6.join(baseDir, location);
|
|
3389
|
-
if (fs7.existsSync(filePath)) {
|
|
3390
|
-
const content = fs7.readFileSync(filePath, "utf-8");
|
|
3391
|
-
return {
|
|
3392
|
-
filePath,
|
|
3393
|
-
rules: parseCodeOwners(content)
|
|
3394
|
-
};
|
|
3395
|
-
}
|
|
3396
|
-
}
|
|
3397
|
-
return null;
|
|
3398
|
-
}
|
|
3399
|
-
function findOwners(filePath, rules) {
|
|
3400
|
-
const normalizedPath = filePath.replace(/^\.\//, "");
|
|
3401
|
-
let matchingOwners = [];
|
|
3402
|
-
for (const rule of rules) {
|
|
3403
|
-
let pattern = rule.pattern;
|
|
3404
|
-
if (pattern.endsWith("/")) {
|
|
3405
|
-
pattern = pattern + "**";
|
|
3406
|
-
}
|
|
3407
|
-
if (pattern.startsWith("/")) {
|
|
3408
|
-
pattern = pattern.slice(1);
|
|
3409
|
-
} else if (!pattern.startsWith("*")) {
|
|
3410
|
-
pattern = "**/" + pattern;
|
|
3411
|
-
}
|
|
3412
|
-
if (minimatch(normalizedPath, pattern, { matchBase: true, dot: true })) {
|
|
3413
|
-
matchingOwners = rule.owners;
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
return matchingOwners;
|
|
3417
|
-
}
|
|
3418
|
-
function attributeOwners(exports, rules, baseDir) {
|
|
3419
|
-
const ownershipMap = new Map;
|
|
3420
|
-
for (const exp of exports) {
|
|
3421
|
-
const filePath = exp.source?.file;
|
|
3422
|
-
if (!filePath) {
|
|
3423
|
-
ownershipMap.set(exp, []);
|
|
3424
|
-
continue;
|
|
3425
|
-
}
|
|
3426
|
-
let relativePath = filePath;
|
|
3427
|
-
if (path6.isAbsolute(filePath)) {
|
|
3428
|
-
relativePath = path6.relative(baseDir, filePath);
|
|
3429
|
-
}
|
|
3430
|
-
const owners = findOwners(relativePath, rules);
|
|
3431
|
-
ownershipMap.set(exp, owners);
|
|
3432
|
-
}
|
|
3433
|
-
return ownershipMap;
|
|
3434
|
-
}
|
|
3435
|
-
function analyzeOwnership(spec, codeowners, baseDir) {
|
|
3436
|
-
const exports = spec.exports ?? [];
|
|
3437
|
-
const ownershipMap = attributeOwners(exports, codeowners.rules, baseDir);
|
|
3438
|
-
const byOwnerExports = new Map;
|
|
3439
|
-
const unowned = [];
|
|
3440
|
-
for (const [exp, owners] of ownershipMap) {
|
|
3441
|
-
if (owners.length === 0) {
|
|
3442
|
-
unowned.push(exp);
|
|
3443
|
-
} else {
|
|
3444
|
-
for (const owner of owners) {
|
|
3445
|
-
const existing = byOwnerExports.get(owner) ?? [];
|
|
3446
|
-
existing.push(exp);
|
|
3447
|
-
byOwnerExports.set(owner, existing);
|
|
3448
|
-
}
|
|
3449
|
-
}
|
|
3450
|
-
}
|
|
3451
|
-
const byOwner = new Map;
|
|
3452
|
-
for (const [owner, ownerExports] of byOwnerExports) {
|
|
3453
|
-
const documented = ownerExports.filter((e) => e.description);
|
|
3454
|
-
const withDrift = ownerExports.filter((e) => e.docs?.drift && e.docs.drift.length > 0);
|
|
3455
|
-
const withExamples = ownerExports.filter((e) => e.examples && e.examples.length > 0);
|
|
3456
|
-
const undocumented = ownerExports.filter((e) => !e.description);
|
|
3457
|
-
const coverageScore = ownerExports.length === 0 ? 100 : Math.round(documented.length / ownerExports.length * 100);
|
|
3458
|
-
const driftScore = ownerExports.length === 0 ? 0 : Math.round(withDrift.length / ownerExports.length * 100);
|
|
3459
|
-
byOwner.set(owner, {
|
|
3460
|
-
owner,
|
|
3461
|
-
totalExports: ownerExports.length,
|
|
3462
|
-
documentedExports: documented.length,
|
|
3463
|
-
coverageScore,
|
|
3464
|
-
exportsWithDrift: withDrift.length,
|
|
3465
|
-
driftScore,
|
|
3466
|
-
missingExamples: ownerExports.length - withExamples.length,
|
|
3467
|
-
undocumentedExports: undocumented.map((e) => e.name)
|
|
3468
|
-
});
|
|
3469
|
-
}
|
|
3470
|
-
return {
|
|
3471
|
-
codeownersPath: codeowners.filePath,
|
|
3472
|
-
byOwner,
|
|
3473
|
-
unowned,
|
|
3474
|
-
totalExports: exports.length
|
|
3475
|
-
};
|
|
3476
|
-
}
|
|
3477
|
-
function analyzeSpecOwnership(spec, options) {
|
|
3478
|
-
const codeowners = loadCodeOwners(options.baseDir);
|
|
3479
|
-
if (!codeowners)
|
|
3480
|
-
return null;
|
|
3481
|
-
return analyzeOwnership(spec, codeowners, options.baseDir);
|
|
3482
|
-
}
|
|
3483
|
-
// src/contributors/index.ts
|
|
3484
|
-
import { execSync } from "node:child_process";
|
|
3485
|
-
import * as path7 from "node:path";
|
|
3486
|
-
function parseBlameOutput(output) {
|
|
3487
|
-
const results = [];
|
|
3488
|
-
const lines = output.split(`
|
|
3489
|
-
`);
|
|
3490
|
-
let currentCommit = "";
|
|
3491
|
-
let currentAuthor = "";
|
|
3492
|
-
let currentEmail = "";
|
|
3493
|
-
let currentTime = 0;
|
|
3494
|
-
let lineNumber = 0;
|
|
3495
|
-
for (const line of lines) {
|
|
3496
|
-
if (line.match(/^[0-9a-f]{40}/)) {
|
|
3497
|
-
const parts = line.split(" ");
|
|
3498
|
-
currentCommit = parts[0];
|
|
3499
|
-
lineNumber = parseInt(parts[2], 10);
|
|
3500
|
-
} else if (line.startsWith("author ")) {
|
|
3501
|
-
currentAuthor = line.slice(7);
|
|
3502
|
-
} else if (line.startsWith("author-mail ")) {
|
|
3503
|
-
currentEmail = line.slice(12).replace(/[<>]/g, "");
|
|
3504
|
-
} else if (line.startsWith("author-time ")) {
|
|
3505
|
-
currentTime = parseInt(line.slice(12), 10);
|
|
3506
|
-
} else if (line.startsWith("\t")) {
|
|
3507
|
-
results.push({
|
|
3508
|
-
commit: currentCommit,
|
|
3509
|
-
author: currentAuthor,
|
|
3510
|
-
email: currentEmail,
|
|
3511
|
-
line: lineNumber,
|
|
3512
|
-
timestamp: currentTime
|
|
3513
|
-
});
|
|
3514
|
-
}
|
|
3515
|
-
}
|
|
3516
|
-
return results;
|
|
3517
|
-
}
|
|
3518
|
-
function getFileBlame(filePath, cwd) {
|
|
3519
|
-
try {
|
|
3520
|
-
const relativePath = path7.isAbsolute(filePath) ? path7.relative(cwd, filePath) : filePath;
|
|
3521
|
-
const output = execSync(`git blame --porcelain "${relativePath}"`, {
|
|
3522
|
-
cwd,
|
|
3523
|
-
encoding: "utf-8",
|
|
3524
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
3525
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
3526
|
-
});
|
|
3527
|
-
return parseBlameOutput(output);
|
|
3528
|
-
} catch {
|
|
3529
|
-
return null;
|
|
3530
|
-
}
|
|
3531
|
-
}
|
|
3532
|
-
function getBlameForLines(filePath, startLine, endLine, cwd) {
|
|
3533
|
-
try {
|
|
3534
|
-
const relativePath = path7.isAbsolute(filePath) ? path7.relative(cwd, filePath) : filePath;
|
|
3535
|
-
const output = execSync(`git blame --porcelain -L ${startLine},${endLine} "${relativePath}"`, {
|
|
3536
|
-
cwd,
|
|
3537
|
-
encoding: "utf-8",
|
|
3538
|
-
maxBuffer: 10 * 1024 * 1024,
|
|
3539
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
3540
|
-
});
|
|
3541
|
-
return parseBlameOutput(output);
|
|
3542
|
-
} catch {
|
|
3543
|
-
return null;
|
|
3544
|
-
}
|
|
3545
|
-
}
|
|
3546
|
-
function findPrimaryAuthor(blameLines) {
|
|
3547
|
-
if (blameLines.length === 0)
|
|
3548
|
-
return null;
|
|
3549
|
-
const authorCounts = new Map;
|
|
3550
|
-
for (const line of blameLines) {
|
|
3551
|
-
const key = line.email;
|
|
3552
|
-
const existing = authorCounts.get(key);
|
|
3553
|
-
if (existing) {
|
|
3554
|
-
existing.count++;
|
|
3555
|
-
if (line.timestamp > existing.info.timestamp) {
|
|
3556
|
-
existing.info = line;
|
|
3557
|
-
}
|
|
3558
|
-
} else {
|
|
3559
|
-
authorCounts.set(key, { count: 1, info: line });
|
|
3560
|
-
}
|
|
3561
|
-
}
|
|
3562
|
-
let maxCount = 0;
|
|
3563
|
-
let primaryAuthor = null;
|
|
3564
|
-
for (const { count, info } of authorCounts.values()) {
|
|
3565
|
-
if (count > maxCount) {
|
|
3566
|
-
maxCount = count;
|
|
3567
|
-
primaryAuthor = info;
|
|
3568
|
-
}
|
|
3569
|
-
}
|
|
3570
|
-
return primaryAuthor;
|
|
3571
|
-
}
|
|
3572
|
-
function analyzeContributors(spec, baseDir) {
|
|
3573
|
-
const exports = spec.exports ?? [];
|
|
3574
|
-
const byContributor = new Map;
|
|
3575
|
-
const unattributed = [];
|
|
3576
|
-
let totalDocumented = 0;
|
|
3577
|
-
const fileBlameCache = new Map;
|
|
3578
|
-
for (const exp of exports) {
|
|
3579
|
-
if (!exp.description)
|
|
3580
|
-
continue;
|
|
3581
|
-
const filePath = exp.source?.file;
|
|
3582
|
-
const line = exp.source?.line;
|
|
3583
|
-
if (!filePath || !line) {
|
|
3584
|
-
unattributed.push(exp.name);
|
|
3585
|
-
continue;
|
|
3586
|
-
}
|
|
3587
|
-
let fileBlame = fileBlameCache.get(filePath);
|
|
3588
|
-
if (fileBlame === undefined) {
|
|
3589
|
-
fileBlame = getFileBlame(filePath, baseDir);
|
|
3590
|
-
fileBlameCache.set(filePath, fileBlame);
|
|
3591
|
-
}
|
|
3592
|
-
if (!fileBlame) {
|
|
3593
|
-
unattributed.push(exp.name);
|
|
3594
|
-
continue;
|
|
3595
|
-
}
|
|
3596
|
-
const jsDocStartLine = Math.max(1, line - 20);
|
|
3597
|
-
const declarationLine = line;
|
|
3598
|
-
const jsDocBlame = fileBlame.filter((b) => b.line >= jsDocStartLine && b.line < declarationLine);
|
|
3599
|
-
const primaryAuthor = findPrimaryAuthor(jsDocBlame);
|
|
3600
|
-
if (!primaryAuthor) {
|
|
3601
|
-
const declBlame = fileBlame.find((b) => b.line === declarationLine);
|
|
3602
|
-
if (declBlame) {
|
|
3603
|
-
attributeToContributor(byContributor, declBlame, exp.name, 1);
|
|
3604
|
-
totalDocumented++;
|
|
3605
|
-
} else {
|
|
3606
|
-
unattributed.push(exp.name);
|
|
3607
|
-
}
|
|
3608
|
-
continue;
|
|
3609
|
-
}
|
|
3610
|
-
const linesAuthored = jsDocBlame.filter((b) => b.email === primaryAuthor.email).length;
|
|
3611
|
-
attributeToContributor(byContributor, primaryAuthor, exp.name, linesAuthored);
|
|
3612
|
-
totalDocumented++;
|
|
3613
|
-
}
|
|
3614
|
-
return {
|
|
3615
|
-
byContributor,
|
|
3616
|
-
totalDocumented,
|
|
3617
|
-
unattributed
|
|
3618
|
-
};
|
|
3619
|
-
}
|
|
3620
|
-
function attributeToContributor(byContributor, blame, exportName, lines) {
|
|
3621
|
-
const key = blame.email;
|
|
3622
|
-
const existing = byContributor.get(key);
|
|
3623
|
-
if (existing) {
|
|
3624
|
-
existing.documentedExports++;
|
|
3625
|
-
existing.exports.push(exportName);
|
|
3626
|
-
existing.linesAuthored += lines;
|
|
3627
|
-
const blameDate = new Date(blame.timestamp * 1000);
|
|
3628
|
-
if (!existing.lastContribution || blameDate > existing.lastContribution) {
|
|
3629
|
-
existing.lastContribution = blameDate;
|
|
3630
|
-
}
|
|
3631
|
-
} else {
|
|
3632
|
-
byContributor.set(key, {
|
|
3633
|
-
name: blame.author,
|
|
3634
|
-
email: blame.email,
|
|
3635
|
-
documentedExports: 1,
|
|
3636
|
-
exports: [exportName],
|
|
3637
|
-
linesAuthored: lines,
|
|
3638
|
-
lastContribution: new Date(blame.timestamp * 1000)
|
|
3639
|
-
});
|
|
3640
|
-
}
|
|
3641
|
-
}
|
|
3642
|
-
function analyzeSpecContributors(spec, options) {
|
|
3643
|
-
try {
|
|
3644
|
-
execSync("git rev-parse --git-dir", {
|
|
3645
|
-
cwd: options.baseDir,
|
|
3646
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
3647
|
-
});
|
|
3648
|
-
} catch {
|
|
3649
|
-
return null;
|
|
3650
|
-
}
|
|
3651
|
-
return analyzeContributors(spec, options.baseDir);
|
|
3079
|
+
return path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3652
3080
|
}
|
|
3653
3081
|
// src/config/types.ts
|
|
3654
3082
|
function defineConfig(config) {
|
|
3655
3083
|
return config;
|
|
3656
3084
|
}
|
|
3657
3085
|
// src/detect/utils.ts
|
|
3658
|
-
async function safeParseJson(
|
|
3086
|
+
async function safeParseJson(fs7, path7) {
|
|
3659
3087
|
try {
|
|
3660
|
-
if (!await
|
|
3088
|
+
if (!await fs7.exists(path7))
|
|
3661
3089
|
return null;
|
|
3662
|
-
const content = await
|
|
3090
|
+
const content = await fs7.readFile(path7);
|
|
3663
3091
|
return JSON.parse(content);
|
|
3664
3092
|
} catch {
|
|
3665
3093
|
return null;
|
|
3666
3094
|
}
|
|
3667
3095
|
}
|
|
3668
|
-
async function readPackageJson(
|
|
3669
|
-
const
|
|
3670
|
-
return safeParseJson(
|
|
3096
|
+
async function readPackageJson(fs7, dir) {
|
|
3097
|
+
const path7 = dir === "." ? "package.json" : `${dir}/package.json`;
|
|
3098
|
+
return safeParseJson(fs7, path7);
|
|
3671
3099
|
}
|
|
3672
3100
|
|
|
3673
3101
|
// src/detect/build.ts
|
|
@@ -3697,8 +3125,8 @@ var BUILD_TOOL_PATTERNS = [
|
|
|
3697
3125
|
"babel",
|
|
3698
3126
|
"ncc build"
|
|
3699
3127
|
];
|
|
3700
|
-
async function detectBuildInfo(
|
|
3701
|
-
const pkgJson = await readPackageJson(
|
|
3128
|
+
async function detectBuildInfo(fs7, packagePath = ".") {
|
|
3129
|
+
const pkgJson = await readPackageJson(fs7, packagePath);
|
|
3702
3130
|
const scripts = pkgJson?.scripts ?? {};
|
|
3703
3131
|
const scriptNames = Object.keys(scripts);
|
|
3704
3132
|
const buildScriptsByName = scriptNames.filter((name) => BUILD_SCRIPT_NAMES.has(name) || BUILD_SCRIPT_PREFIXES.some((prefix) => name.startsWith(prefix)));
|
|
@@ -3710,10 +3138,10 @@ async function detectBuildInfo(fs8, packagePath = ".") {
|
|
|
3710
3138
|
});
|
|
3711
3139
|
const buildScripts = [...new Set([...buildScriptsByName, ...buildScriptsByContent])];
|
|
3712
3140
|
const tsconfigPath = packagePath === "." ? "tsconfig.json" : `${packagePath}/tsconfig.json`;
|
|
3713
|
-
const hasTsConfig = await
|
|
3141
|
+
const hasTsConfig = await fs7.exists(tsconfigPath);
|
|
3714
3142
|
const hasTsDep = pkgJson?.devDependencies?.typescript !== undefined || pkgJson?.dependencies?.typescript !== undefined;
|
|
3715
3143
|
const hasTypeScript = hasTsConfig || hasTsDep;
|
|
3716
|
-
const wasm = await detectWasmProject(
|
|
3144
|
+
const wasm = await detectWasmProject(fs7, packagePath, pkgJson);
|
|
3717
3145
|
const napi = detectNapiProject(pkgJson);
|
|
3718
3146
|
return {
|
|
3719
3147
|
scripts: buildScripts,
|
|
@@ -3730,11 +3158,11 @@ var WASM_PACKAGES = new Set([
|
|
|
3730
3158
|
"wasm-bindgen",
|
|
3731
3159
|
"@aspect/aspect-cli"
|
|
3732
3160
|
]);
|
|
3733
|
-
async function detectWasmProject(
|
|
3161
|
+
async function detectWasmProject(fs7, packagePath, pkgJson) {
|
|
3734
3162
|
const pkgCargoPath = packagePath === "." ? "Cargo.toml" : `${packagePath}/Cargo.toml`;
|
|
3735
|
-
if (await
|
|
3163
|
+
if (await fs7.exists(pkgCargoPath))
|
|
3736
3164
|
return true;
|
|
3737
|
-
if (packagePath !== "." && await
|
|
3165
|
+
if (packagePath !== "." && await fs7.exists("Cargo.toml"))
|
|
3738
3166
|
return true;
|
|
3739
3167
|
if (pkgJson) {
|
|
3740
3168
|
const deps = Object.keys({
|
|
@@ -3775,15 +3203,15 @@ function getPrimaryBuildScript(buildInfo) {
|
|
|
3775
3203
|
return buildInfo.scripts[0] ?? null;
|
|
3776
3204
|
}
|
|
3777
3205
|
// src/detect/entry-point.ts
|
|
3778
|
-
async function detectEntryPoint(
|
|
3779
|
-
const pkgJson = await readPackageJson(
|
|
3206
|
+
async function detectEntryPoint(fs7, packagePath = ".") {
|
|
3207
|
+
const pkgJson = await readPackageJson(fs7, packagePath);
|
|
3780
3208
|
if (!pkgJson) {
|
|
3781
3209
|
throw new Error("No package.json found - not a valid npm package");
|
|
3782
3210
|
}
|
|
3783
|
-
const tsConfig = await parseTsConfig(
|
|
3211
|
+
const tsConfig = await parseTsConfig(fs7, packagePath);
|
|
3784
3212
|
const typesField = pkgJson.types || pkgJson.typings;
|
|
3785
3213
|
if (typesField && typeof typesField === "string") {
|
|
3786
|
-
const resolved = await resolveToSource(
|
|
3214
|
+
const resolved = await resolveToSource(fs7, packagePath, typesField, tsConfig);
|
|
3787
3215
|
if (resolved) {
|
|
3788
3216
|
return { ...resolved, source: "types" };
|
|
3789
3217
|
}
|
|
@@ -3793,7 +3221,7 @@ async function detectEntryPoint(fs8, packagePath = ".") {
|
|
|
3793
3221
|
if (dotExport && typeof dotExport === "object" && "types" in dotExport) {
|
|
3794
3222
|
const typesPath = dotExport.types;
|
|
3795
3223
|
if (typesPath && typeof typesPath === "string") {
|
|
3796
|
-
const resolved = await resolveToSource(
|
|
3224
|
+
const resolved = await resolveToSource(fs7, packagePath, typesPath, tsConfig);
|
|
3797
3225
|
if (resolved) {
|
|
3798
3226
|
return { ...resolved, source: "exports" };
|
|
3799
3227
|
}
|
|
@@ -3801,13 +3229,13 @@ async function detectEntryPoint(fs8, packagePath = ".") {
|
|
|
3801
3229
|
}
|
|
3802
3230
|
}
|
|
3803
3231
|
if (pkgJson.main && typeof pkgJson.main === "string") {
|
|
3804
|
-
const resolved = await resolveToSource(
|
|
3232
|
+
const resolved = await resolveToSource(fs7, packagePath, pkgJson.main, tsConfig);
|
|
3805
3233
|
if (resolved) {
|
|
3806
3234
|
return { ...resolved, source: "main" };
|
|
3807
3235
|
}
|
|
3808
3236
|
}
|
|
3809
3237
|
if (pkgJson.module && typeof pkgJson.module === "string") {
|
|
3810
|
-
const resolved = await resolveToSource(
|
|
3238
|
+
const resolved = await resolveToSource(fs7, packagePath, pkgJson.module, tsConfig);
|
|
3811
3239
|
if (resolved) {
|
|
3812
3240
|
return { ...resolved, source: "module" };
|
|
3813
3241
|
}
|
|
@@ -3825,27 +3253,27 @@ async function detectEntryPoint(fs8, packagePath = ".") {
|
|
|
3825
3253
|
];
|
|
3826
3254
|
for (const fallback of fallbacks) {
|
|
3827
3255
|
const checkPath = packagePath === "." ? fallback : `${packagePath}/${fallback}`;
|
|
3828
|
-
if (await
|
|
3256
|
+
if (await fs7.exists(checkPath)) {
|
|
3829
3257
|
return { path: fallback, source: "fallback", isDeclarationOnly: false };
|
|
3830
3258
|
}
|
|
3831
3259
|
}
|
|
3832
3260
|
throw new Error("Could not detect TypeScript entry point. No types field in package.json and no common entry paths found.");
|
|
3833
3261
|
}
|
|
3834
|
-
async function parseTsConfig(
|
|
3262
|
+
async function parseTsConfig(fs7, packagePath) {
|
|
3835
3263
|
const tsconfigPath = packagePath === "." ? "tsconfig.json" : `${packagePath}/tsconfig.json`;
|
|
3836
|
-
const tsconfig = await safeParseJson(
|
|
3264
|
+
const tsconfig = await safeParseJson(fs7, tsconfigPath);
|
|
3837
3265
|
if (!tsconfig?.compilerOptions) {
|
|
3838
3266
|
return null;
|
|
3839
3267
|
}
|
|
3840
3268
|
const { outDir, rootDir, baseUrl, paths } = tsconfig.compilerOptions;
|
|
3841
3269
|
return { outDir, rootDir, baseUrl, paths };
|
|
3842
3270
|
}
|
|
3843
|
-
async function resolveToSource(
|
|
3271
|
+
async function resolveToSource(fs7, basePath, filePath, tsConfig) {
|
|
3844
3272
|
const normalized = filePath.replace(/^\.\//, "");
|
|
3845
3273
|
const checkPath = (p) => basePath === "." ? p : `${basePath}/${p}`;
|
|
3846
3274
|
const isSourceTs = normalized.endsWith(".ts") && !normalized.endsWith(".d.ts") || normalized.endsWith(".tsx");
|
|
3847
3275
|
if (isSourceTs) {
|
|
3848
|
-
if (await
|
|
3276
|
+
if (await fs7.exists(checkPath(normalized))) {
|
|
3849
3277
|
return { path: normalized, isDeclarationOnly: false };
|
|
3850
3278
|
}
|
|
3851
3279
|
}
|
|
@@ -3887,19 +3315,19 @@ async function resolveToSource(fs8, basePath, filePath, tsConfig) {
|
|
|
3887
3315
|
for (const candidate of candidates) {
|
|
3888
3316
|
if (candidate.endsWith(".d.ts"))
|
|
3889
3317
|
continue;
|
|
3890
|
-
if (await
|
|
3318
|
+
if (await fs7.exists(checkPath(candidate))) {
|
|
3891
3319
|
return { path: candidate, isDeclarationOnly: false };
|
|
3892
3320
|
}
|
|
3893
3321
|
}
|
|
3894
3322
|
if (normalized.endsWith(".d.ts")) {
|
|
3895
|
-
if (await
|
|
3323
|
+
if (await fs7.exists(checkPath(normalized))) {
|
|
3896
3324
|
return { path: normalized, isDeclarationOnly: true };
|
|
3897
3325
|
}
|
|
3898
3326
|
}
|
|
3899
3327
|
return null;
|
|
3900
3328
|
}
|
|
3901
3329
|
// src/detect/filesystem.ts
|
|
3902
|
-
import * as
|
|
3330
|
+
import * as fs7 from "node:fs";
|
|
3903
3331
|
import * as nodePath from "node:path";
|
|
3904
3332
|
import { Writable } from "node:stream";
|
|
3905
3333
|
|
|
@@ -3912,19 +3340,19 @@ class NodeFileSystem {
|
|
|
3912
3340
|
return nodePath.join(this.basePath, relativePath);
|
|
3913
3341
|
}
|
|
3914
3342
|
async exists(relativePath) {
|
|
3915
|
-
return
|
|
3343
|
+
return fs7.existsSync(this.resolve(relativePath));
|
|
3916
3344
|
}
|
|
3917
3345
|
async readFile(relativePath) {
|
|
3918
|
-
return
|
|
3346
|
+
return fs7.readFileSync(this.resolve(relativePath), "utf-8");
|
|
3919
3347
|
}
|
|
3920
3348
|
async readDir(relativePath) {
|
|
3921
|
-
return
|
|
3349
|
+
return fs7.readdirSync(this.resolve(relativePath));
|
|
3922
3350
|
}
|
|
3923
3351
|
async isDirectory(relativePath) {
|
|
3924
3352
|
const fullPath = this.resolve(relativePath);
|
|
3925
|
-
if (!
|
|
3353
|
+
if (!fs7.existsSync(fullPath))
|
|
3926
3354
|
return false;
|
|
3927
|
-
return
|
|
3355
|
+
return fs7.statSync(fullPath).isDirectory();
|
|
3928
3356
|
}
|
|
3929
3357
|
}
|
|
3930
3358
|
function createCaptureStream() {
|
|
@@ -3940,9 +3368,9 @@ function createCaptureStream() {
|
|
|
3940
3368
|
|
|
3941
3369
|
class FileNotFoundError extends Error {
|
|
3942
3370
|
path;
|
|
3943
|
-
constructor(
|
|
3944
|
-
super(message ?? `File not found: ${
|
|
3945
|
-
this.path =
|
|
3371
|
+
constructor(path7, message) {
|
|
3372
|
+
super(message ?? `File not found: ${path7}`);
|
|
3373
|
+
this.path = path7;
|
|
3946
3374
|
this.name = "FileNotFoundError";
|
|
3947
3375
|
}
|
|
3948
3376
|
}
|
|
@@ -3952,34 +3380,34 @@ class SandboxFileSystem {
|
|
|
3952
3380
|
constructor(sandbox) {
|
|
3953
3381
|
this.sandbox = sandbox;
|
|
3954
3382
|
}
|
|
3955
|
-
async exists(
|
|
3383
|
+
async exists(path7) {
|
|
3956
3384
|
const result = await this.sandbox.runCommand({
|
|
3957
3385
|
cmd: "test",
|
|
3958
|
-
args: ["-e",
|
|
3386
|
+
args: ["-e", path7]
|
|
3959
3387
|
});
|
|
3960
3388
|
return result.exitCode === 0;
|
|
3961
3389
|
}
|
|
3962
|
-
async readFile(
|
|
3963
|
-
const exists = await this.exists(
|
|
3390
|
+
async readFile(path7) {
|
|
3391
|
+
const exists = await this.exists(path7);
|
|
3964
3392
|
if (!exists) {
|
|
3965
|
-
throw new FileNotFoundError(
|
|
3393
|
+
throw new FileNotFoundError(path7);
|
|
3966
3394
|
}
|
|
3967
3395
|
const capture = createCaptureStream();
|
|
3968
3396
|
const result = await this.sandbox.runCommand({
|
|
3969
3397
|
cmd: "cat",
|
|
3970
|
-
args: [
|
|
3398
|
+
args: [path7],
|
|
3971
3399
|
stdout: capture.stream
|
|
3972
3400
|
});
|
|
3973
3401
|
if (result.exitCode !== 0) {
|
|
3974
|
-
throw new FileNotFoundError(
|
|
3402
|
+
throw new FileNotFoundError(path7, `Failed to read file: ${path7}`);
|
|
3975
3403
|
}
|
|
3976
3404
|
return capture.getOutput();
|
|
3977
3405
|
}
|
|
3978
|
-
async readDir(
|
|
3406
|
+
async readDir(path7) {
|
|
3979
3407
|
const capture = createCaptureStream();
|
|
3980
3408
|
const result = await this.sandbox.runCommand({
|
|
3981
3409
|
cmd: "ls",
|
|
3982
|
-
args: ["-1",
|
|
3410
|
+
args: ["-1", path7],
|
|
3983
3411
|
stdout: capture.stream
|
|
3984
3412
|
});
|
|
3985
3413
|
if (result.exitCode !== 0) {
|
|
@@ -3988,20 +3416,20 @@ class SandboxFileSystem {
|
|
|
3988
3416
|
return capture.getOutput().split(`
|
|
3989
3417
|
`).filter(Boolean);
|
|
3990
3418
|
}
|
|
3991
|
-
async isDirectory(
|
|
3419
|
+
async isDirectory(path7) {
|
|
3992
3420
|
const result = await this.sandbox.runCommand({
|
|
3993
3421
|
cmd: "test",
|
|
3994
|
-
args: ["-d",
|
|
3422
|
+
args: ["-d", path7]
|
|
3995
3423
|
});
|
|
3996
3424
|
return result.exitCode === 0;
|
|
3997
3425
|
}
|
|
3998
3426
|
}
|
|
3999
3427
|
// src/detect/monorepo.ts
|
|
4000
|
-
async function detectMonorepo(
|
|
4001
|
-
const pkgJson = await readPackageJson(
|
|
3428
|
+
async function detectMonorepo(fs8) {
|
|
3429
|
+
const pkgJson = await readPackageJson(fs8, ".");
|
|
4002
3430
|
if (pkgJson?.workspaces) {
|
|
4003
3431
|
const patterns = extractWorkspacePatterns(pkgJson.workspaces);
|
|
4004
|
-
const packages = await resolveWorkspacePackages(
|
|
3432
|
+
const packages = await resolveWorkspacePackages(fs8, patterns, pkgJson.name, pkgJson.private);
|
|
4005
3433
|
return {
|
|
4006
3434
|
isMonorepo: packages.length > 0,
|
|
4007
3435
|
type: "npm-workspaces",
|
|
@@ -4009,10 +3437,10 @@ async function detectMonorepo(fs9) {
|
|
|
4009
3437
|
packages
|
|
4010
3438
|
};
|
|
4011
3439
|
}
|
|
4012
|
-
if (await
|
|
4013
|
-
const content = await
|
|
3440
|
+
if (await fs8.exists("pnpm-workspace.yaml")) {
|
|
3441
|
+
const content = await fs8.readFile("pnpm-workspace.yaml");
|
|
4014
3442
|
const patterns = parsePnpmWorkspace(content);
|
|
4015
|
-
const packages = await resolveWorkspacePackages(
|
|
3443
|
+
const packages = await resolveWorkspacePackages(fs8, patterns, pkgJson?.name, pkgJson?.private);
|
|
4016
3444
|
return {
|
|
4017
3445
|
isMonorepo: packages.length > 0,
|
|
4018
3446
|
type: "pnpm-workspaces",
|
|
@@ -4020,10 +3448,10 @@ async function detectMonorepo(fs9) {
|
|
|
4020
3448
|
packages
|
|
4021
3449
|
};
|
|
4022
3450
|
}
|
|
4023
|
-
if (await
|
|
4024
|
-
const lerna = await safeParseJson(
|
|
3451
|
+
if (await fs8.exists("lerna.json")) {
|
|
3452
|
+
const lerna = await safeParseJson(fs8, "lerna.json");
|
|
4025
3453
|
const patterns = lerna?.packages ?? ["packages/*"];
|
|
4026
|
-
const packages = await resolveWorkspacePackages(
|
|
3454
|
+
const packages = await resolveWorkspacePackages(fs8, patterns, pkgJson?.name, pkgJson?.private);
|
|
4027
3455
|
return {
|
|
4028
3456
|
isMonorepo: packages.length > 0,
|
|
4029
3457
|
type: "lerna",
|
|
@@ -4097,7 +3525,7 @@ function parsePnpmWorkspace(content) {
|
|
|
4097
3525
|
}
|
|
4098
3526
|
return patterns.length > 0 ? patterns : ["packages/*"];
|
|
4099
3527
|
}
|
|
4100
|
-
async function resolveWorkspacePackages(
|
|
3528
|
+
async function resolveWorkspacePackages(fs8, patterns, rootPackageName, rootIsPrivate) {
|
|
4101
3529
|
const packages = [];
|
|
4102
3530
|
const seen = new Set;
|
|
4103
3531
|
if (rootPackageName && !rootIsPrivate && rootPackageName !== "root") {
|
|
@@ -4119,18 +3547,18 @@ async function resolveWorkspacePackages(fs9, patterns, rootPackageName, rootIsPr
|
|
|
4119
3547
|
}
|
|
4120
3548
|
dirsToScan.add("packages");
|
|
4121
3549
|
for (const dir of dirsToScan) {
|
|
4122
|
-
if (!await
|
|
3550
|
+
if (!await fs8.exists(dir))
|
|
4123
3551
|
continue;
|
|
4124
|
-
if (!await
|
|
3552
|
+
if (!await fs8.isDirectory(dir))
|
|
4125
3553
|
continue;
|
|
4126
|
-
const subdirs = await
|
|
3554
|
+
const subdirs = await fs8.readDir(dir);
|
|
4127
3555
|
for (const subdir of subdirs) {
|
|
4128
3556
|
const pkgPath = `${dir}/${subdir}`;
|
|
4129
3557
|
const pkgJsonPath = `${pkgPath}/package.json`;
|
|
4130
|
-
if (!await
|
|
3558
|
+
if (!await fs8.exists(pkgJsonPath))
|
|
4131
3559
|
continue;
|
|
4132
3560
|
try {
|
|
4133
|
-
const content = await
|
|
3561
|
+
const content = await fs8.readFile(pkgJsonPath);
|
|
4134
3562
|
const pkg = JSON.parse(content);
|
|
4135
3563
|
if (pkg.name && !seen.has(pkg.name)) {
|
|
4136
3564
|
seen.add(pkg.name);
|
|
@@ -4202,14 +3630,14 @@ var DEFAULT_PM = {
|
|
|
4202
3630
|
installArgs: ["install", "--legacy-peer-deps"],
|
|
4203
3631
|
runPrefix: ["npm", "run"]
|
|
4204
3632
|
};
|
|
4205
|
-
async function detectPackageManager(
|
|
4206
|
-
const pkgJson = await safeParseJson(
|
|
3633
|
+
async function detectPackageManager(fs8) {
|
|
3634
|
+
const pkgJson = await safeParseJson(fs8, "package.json");
|
|
4207
3635
|
if (pkgJson?.packageManager) {
|
|
4208
3636
|
const pmName = parsePackageManagerField(pkgJson.packageManager);
|
|
4209
3637
|
if (pmName && PM_CONFIGS[pmName]) {
|
|
4210
3638
|
const config = PM_CONFIGS[pmName];
|
|
4211
3639
|
for (const lockfile of config.lockfiles) {
|
|
4212
|
-
if (await
|
|
3640
|
+
if (await fs8.exists(lockfile)) {
|
|
4213
3641
|
return { ...config.info, lockfile };
|
|
4214
3642
|
}
|
|
4215
3643
|
}
|
|
@@ -4219,7 +3647,7 @@ async function detectPackageManager(fs9) {
|
|
|
4219
3647
|
const foundLockfiles = [];
|
|
4220
3648
|
for (const [pmName, config] of Object.entries(PM_CONFIGS)) {
|
|
4221
3649
|
for (const lockfile of config.lockfiles) {
|
|
4222
|
-
if (await
|
|
3650
|
+
if (await fs8.exists(lockfile)) {
|
|
4223
3651
|
foundLockfiles.push({ lockfile, pm: pmName });
|
|
4224
3652
|
}
|
|
4225
3653
|
}
|
|
@@ -4251,10 +3679,10 @@ function getRunCommand(pm, script) {
|
|
|
4251
3679
|
return [...pm.runPrefix, script];
|
|
4252
3680
|
}
|
|
4253
3681
|
// src/detect/index.ts
|
|
4254
|
-
async function analyzeProject(
|
|
3682
|
+
async function analyzeProject(fs8, options = {}) {
|
|
4255
3683
|
const [packageManager, monorepo] = await Promise.all([
|
|
4256
|
-
detectPackageManager(
|
|
4257
|
-
detectMonorepo(
|
|
3684
|
+
detectPackageManager(fs8),
|
|
3685
|
+
detectMonorepo(fs8)
|
|
4258
3686
|
]);
|
|
4259
3687
|
let targetPath = ".";
|
|
4260
3688
|
if (monorepo.isMonorepo) {
|
|
@@ -4271,8 +3699,8 @@ async function analyzeProject(fs9, options = {}) {
|
|
|
4271
3699
|
targetPath = pkg.path;
|
|
4272
3700
|
}
|
|
4273
3701
|
const [entryPoint, build] = await Promise.all([
|
|
4274
|
-
detectEntryPoint(
|
|
4275
|
-
detectBuildInfo(
|
|
3702
|
+
detectEntryPoint(fs8, targetPath),
|
|
3703
|
+
detectBuildInfo(fs8, targetPath)
|
|
4276
3704
|
]);
|
|
4277
3705
|
return { packageManager, monorepo, entryPoint, build };
|
|
4278
3706
|
}
|
|
@@ -4316,17 +3744,17 @@ function shouldValidate(validations, check) {
|
|
|
4316
3744
|
return validations.includes(check);
|
|
4317
3745
|
}
|
|
4318
3746
|
// src/typecheck/example-typechecker.ts
|
|
4319
|
-
import * as
|
|
4320
|
-
import * as
|
|
3747
|
+
import * as fs8 from "node:fs";
|
|
3748
|
+
import * as path7 from "node:path";
|
|
4321
3749
|
import ts3 from "typescript";
|
|
4322
3750
|
function stripCodeBlockMarkers(code) {
|
|
4323
3751
|
return code.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
|
|
4324
3752
|
}
|
|
4325
3753
|
function getPackageName(packagePath) {
|
|
4326
|
-
const pkgJsonPath =
|
|
4327
|
-
if (
|
|
3754
|
+
const pkgJsonPath = path7.join(packagePath, "package.json");
|
|
3755
|
+
if (fs8.existsSync(pkgJsonPath)) {
|
|
4328
3756
|
try {
|
|
4329
|
-
const pkgJson = JSON.parse(
|
|
3757
|
+
const pkgJson = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
|
|
4330
3758
|
return pkgJson.name;
|
|
4331
3759
|
} catch {
|
|
4332
3760
|
return;
|
|
@@ -4336,12 +3764,12 @@ function getPackageName(packagePath) {
|
|
|
4336
3764
|
}
|
|
4337
3765
|
function findTsConfig(packagePath) {
|
|
4338
3766
|
let dir = packagePath;
|
|
4339
|
-
while (dir !==
|
|
4340
|
-
const tsConfigPath =
|
|
4341
|
-
if (
|
|
3767
|
+
while (dir !== path7.dirname(dir)) {
|
|
3768
|
+
const tsConfigPath = path7.join(dir, "tsconfig.json");
|
|
3769
|
+
if (fs8.existsSync(tsConfigPath)) {
|
|
4342
3770
|
return tsConfigPath;
|
|
4343
3771
|
}
|
|
4344
|
-
dir =
|
|
3772
|
+
dir = path7.dirname(dir);
|
|
4345
3773
|
}
|
|
4346
3774
|
return;
|
|
4347
3775
|
}
|
|
@@ -4357,10 +3785,10 @@ function createVirtualSource(example, packageName, exportNames) {
|
|
|
4357
3785
|
`);
|
|
4358
3786
|
}
|
|
4359
3787
|
function getCompilerOptions(tsconfigPath) {
|
|
4360
|
-
if (tsconfigPath &&
|
|
3788
|
+
if (tsconfigPath && fs8.existsSync(tsconfigPath)) {
|
|
4361
3789
|
const configFile = ts3.readConfigFile(tsconfigPath, ts3.sys.readFile);
|
|
4362
3790
|
if (!configFile.error) {
|
|
4363
|
-
const parsed = ts3.parseJsonConfigFileContent(configFile.config, ts3.sys,
|
|
3791
|
+
const parsed = ts3.parseJsonConfigFileContent(configFile.config, ts3.sys, path7.dirname(tsconfigPath));
|
|
4364
3792
|
return {
|
|
4365
3793
|
...parsed.options,
|
|
4366
3794
|
noEmit: true,
|
|
@@ -4386,7 +3814,7 @@ function typecheckExample(example, packagePath, options = {}) {
|
|
|
4386
3814
|
const tsconfigPath = options.tsconfig ?? findTsConfig(packagePath);
|
|
4387
3815
|
const compilerOptions = getCompilerOptions(tsconfigPath);
|
|
4388
3816
|
const virtualSource = createVirtualSource(cleanCode, packageName, exportNames);
|
|
4389
|
-
const virtualFileName =
|
|
3817
|
+
const virtualFileName = path7.join(packagePath, "__doccov_example__.ts");
|
|
4390
3818
|
const hasImport = packageName !== undefined && exportNames && exportNames.length > 0;
|
|
4391
3819
|
const lineOffset = hasImport ? 2 : 0;
|
|
4392
3820
|
const sourceFile = ts3.createSourceFile(virtualFileName, virtualSource, ts3.ScriptTarget.ES2022, true);
|
|
@@ -4457,25 +3885,25 @@ function typecheckExamples(examples, packagePath, options = {}) {
|
|
|
4457
3885
|
}
|
|
4458
3886
|
|
|
4459
3887
|
// src/utils/example-runner.ts
|
|
4460
|
-
import { spawn } from "node:child_process";
|
|
4461
|
-
import * as
|
|
3888
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3889
|
+
import * as fs9 from "node:fs";
|
|
4462
3890
|
import * as os from "node:os";
|
|
4463
|
-
import * as
|
|
3891
|
+
import * as path8 from "node:path";
|
|
4464
3892
|
function stripCodeBlockMarkers2(code) {
|
|
4465
3893
|
return code.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
|
|
4466
3894
|
}
|
|
4467
3895
|
async function runExample(code, options = {}) {
|
|
4468
3896
|
const { timeout = 5000, cwd = process.cwd() } = options;
|
|
4469
3897
|
const cleanCode = stripCodeBlockMarkers2(code);
|
|
4470
|
-
const tmpFile =
|
|
3898
|
+
const tmpFile = path8.join(cwd, `doccov-example-${Date.now()}-${Math.random().toString(36).slice(2)}.ts`);
|
|
4471
3899
|
try {
|
|
4472
|
-
|
|
3900
|
+
fs9.writeFileSync(tmpFile, cleanCode, "utf-8");
|
|
4473
3901
|
const startTime = Date.now();
|
|
4474
3902
|
return await new Promise((resolve5) => {
|
|
4475
3903
|
let stdout = "";
|
|
4476
3904
|
let stderr = "";
|
|
4477
3905
|
let killed = false;
|
|
4478
|
-
const proc =
|
|
3906
|
+
const proc = spawn2("node", ["--experimental-strip-types", tmpFile], {
|
|
4479
3907
|
cwd,
|
|
4480
3908
|
timeout,
|
|
4481
3909
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4524,7 +3952,7 @@ async function runExample(code, options = {}) {
|
|
|
4524
3952
|
});
|
|
4525
3953
|
} finally {
|
|
4526
3954
|
try {
|
|
4527
|
-
|
|
3955
|
+
fs9.unlinkSync(tmpFile);
|
|
4528
3956
|
} catch {}
|
|
4529
3957
|
}
|
|
4530
3958
|
}
|
|
@@ -4539,15 +3967,15 @@ async function runExamples(examples, options = {}) {
|
|
|
4539
3967
|
return results;
|
|
4540
3968
|
}
|
|
4541
3969
|
function detectPackageManager2(cwd) {
|
|
4542
|
-
if (
|
|
3970
|
+
if (fs9.existsSync(path8.join(cwd, "bun.lockb")))
|
|
4543
3971
|
return "bun";
|
|
4544
|
-
if (
|
|
3972
|
+
if (fs9.existsSync(path8.join(cwd, "bun.lock")))
|
|
4545
3973
|
return "bun";
|
|
4546
|
-
if (
|
|
3974
|
+
if (fs9.existsSync(path8.join(cwd, "pnpm-lock.yaml")))
|
|
4547
3975
|
return "pnpm";
|
|
4548
|
-
if (
|
|
3976
|
+
if (fs9.existsSync(path8.join(cwd, "yarn.lock")))
|
|
4549
3977
|
return "yarn";
|
|
4550
|
-
if (
|
|
3978
|
+
if (fs9.existsSync(path8.join(cwd, "package-lock.json")))
|
|
4551
3979
|
return "npm";
|
|
4552
3980
|
return "npm";
|
|
4553
3981
|
}
|
|
@@ -4568,7 +3996,7 @@ async function runCommand(cmd, args, options) {
|
|
|
4568
3996
|
let stdout = "";
|
|
4569
3997
|
let stderr = "";
|
|
4570
3998
|
let killed = false;
|
|
4571
|
-
const proc =
|
|
3999
|
+
const proc = spawn2(cmd, args, {
|
|
4572
4000
|
cwd: options.cwd,
|
|
4573
4001
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4574
4002
|
});
|
|
@@ -4615,12 +4043,12 @@ async function runExamplesWithPackage(examples, options) {
|
|
|
4615
4043
|
const { packagePath, packageManager, installTimeout = 60000, timeout = 5000 } = options;
|
|
4616
4044
|
const startTime = Date.now();
|
|
4617
4045
|
const results = new Map;
|
|
4618
|
-
const absolutePackagePath =
|
|
4619
|
-
const workDir =
|
|
4046
|
+
const absolutePackagePath = path8.resolve(packagePath);
|
|
4047
|
+
const workDir = path8.join(os.tmpdir(), `doccov-examples-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
4620
4048
|
try {
|
|
4621
|
-
|
|
4049
|
+
fs9.mkdirSync(workDir, { recursive: true });
|
|
4622
4050
|
const pkgJson = { name: "doccov-example-runner", type: "module" };
|
|
4623
|
-
|
|
4051
|
+
fs9.writeFileSync(path8.join(workDir, "package.json"), JSON.stringify(pkgJson, null, 2));
|
|
4624
4052
|
const pm = packageManager ?? detectPackageManager2(options.cwd ?? process.cwd());
|
|
4625
4053
|
const { cmd, args } = getInstallCommand2(pm, absolutePackagePath);
|
|
4626
4054
|
const installResult = await runCommand(cmd, args, {
|
|
@@ -4648,7 +4076,7 @@ async function runExamplesWithPackage(examples, options) {
|
|
|
4648
4076
|
};
|
|
4649
4077
|
} finally {
|
|
4650
4078
|
try {
|
|
4651
|
-
|
|
4079
|
+
fs9.rmSync(workDir, { recursive: true, force: true });
|
|
4652
4080
|
} catch {}
|
|
4653
4081
|
}
|
|
4654
4082
|
}
|
|
@@ -4827,9 +4255,12 @@ async function validateExamples(exports, options) {
|
|
|
4827
4255
|
}
|
|
4828
4256
|
return result;
|
|
4829
4257
|
}
|
|
4830
|
-
// src/
|
|
4831
|
-
import * as fs12 from "node:fs";
|
|
4258
|
+
// src/extractor.ts
|
|
4832
4259
|
import * as path13 from "node:path";
|
|
4260
|
+
|
|
4261
|
+
// src/analysis/run-analysis.ts
|
|
4262
|
+
import * as fs11 from "node:fs";
|
|
4263
|
+
import * as path12 from "node:path";
|
|
4833
4264
|
// src/utils/type-utils.ts
|
|
4834
4265
|
function getTypeId(type, typeChecker) {
|
|
4835
4266
|
const internalId = type.id;
|
|
@@ -4952,7 +4383,7 @@ function collectReferencedTypesFromNode(node, typeChecker, referencedTypes) {
|
|
|
4952
4383
|
}
|
|
4953
4384
|
|
|
4954
4385
|
// src/analysis/context.ts
|
|
4955
|
-
import * as
|
|
4386
|
+
import * as path10 from "node:path";
|
|
4956
4387
|
|
|
4957
4388
|
// src/options.ts
|
|
4958
4389
|
var DEFAULT_MAX_TYPE_DEPTH = 20;
|
|
@@ -4973,7 +4404,7 @@ function normalizeDocCovOptions(options = {}) {
|
|
|
4973
4404
|
}
|
|
4974
4405
|
|
|
4975
4406
|
// src/analysis/program.ts
|
|
4976
|
-
import * as
|
|
4407
|
+
import * as path9 from "node:path";
|
|
4977
4408
|
var DEFAULT_COMPILER_OPTIONS = {
|
|
4978
4409
|
target: ts.ScriptTarget.Latest,
|
|
4979
4410
|
module: ts.ModuleKind.CommonJS,
|
|
@@ -4983,14 +4414,14 @@ var DEFAULT_COMPILER_OPTIONS = {
|
|
|
4983
4414
|
};
|
|
4984
4415
|
function createProgram({
|
|
4985
4416
|
entryFile,
|
|
4986
|
-
baseDir =
|
|
4417
|
+
baseDir = path9.dirname(entryFile),
|
|
4987
4418
|
content
|
|
4988
4419
|
}) {
|
|
4989
4420
|
const configPath = ts.findConfigFile(baseDir, ts.sys.fileExists, "tsconfig.json");
|
|
4990
4421
|
let compilerOptions = { ...DEFAULT_COMPILER_OPTIONS };
|
|
4991
4422
|
if (configPath) {
|
|
4992
4423
|
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
4993
|
-
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys,
|
|
4424
|
+
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path9.dirname(configPath));
|
|
4994
4425
|
compilerOptions = { ...compilerOptions, ...parsedConfig.options };
|
|
4995
4426
|
}
|
|
4996
4427
|
const allowJsVal = compilerOptions.allowJs;
|
|
@@ -5028,7 +4459,7 @@ function createAnalysisContext({
|
|
|
5028
4459
|
options,
|
|
5029
4460
|
detectedSchemas
|
|
5030
4461
|
}) {
|
|
5031
|
-
const baseDir = packageDir ??
|
|
4462
|
+
const baseDir = packageDir ?? path10.dirname(entryFile);
|
|
5032
4463
|
const normalizedOptions = normalizeDocCovOptions(options);
|
|
5033
4464
|
const programResult = createProgram({ entryFile, baseDir, content });
|
|
5034
4465
|
if (!programResult.sourceFile) {
|
|
@@ -5049,8 +4480,8 @@ function createAnalysisContext({
|
|
|
5049
4480
|
}
|
|
5050
4481
|
|
|
5051
4482
|
// src/analysis/spec-builder.ts
|
|
5052
|
-
import * as
|
|
5053
|
-
import * as
|
|
4483
|
+
import * as fs10 from "node:fs";
|
|
4484
|
+
import * as path11 from "node:path";
|
|
5054
4485
|
import { SCHEMA_URL, SCHEMA_VERSION } from "@openpkg-ts/spec";
|
|
5055
4486
|
|
|
5056
4487
|
// src/analysis/decorator-utils.ts
|
|
@@ -6740,10 +6171,10 @@ function serializeClassMembers(declaration, checker, typeRefs, referencedTypes)
|
|
|
6740
6171
|
if (member.questionToken || isOptionalSymbol) {
|
|
6741
6172
|
flags.optional = true;
|
|
6742
6173
|
}
|
|
6743
|
-
if (member.modifiers?.some((
|
|
6174
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword)) {
|
|
6744
6175
|
flags.readonly = true;
|
|
6745
6176
|
}
|
|
6746
|
-
if (member.modifiers?.some((
|
|
6177
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
6747
6178
|
flags.static = true;
|
|
6748
6179
|
}
|
|
6749
6180
|
members.push({
|
|
@@ -6865,20 +6296,20 @@ function serializeSignature(signature, checker, typeRefs, referencedTypes, doc,
|
|
|
6865
6296
|
function getMemberVisibility(modifiers) {
|
|
6866
6297
|
if (!modifiers)
|
|
6867
6298
|
return;
|
|
6868
|
-
if (modifiers.some((
|
|
6299
|
+
if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.PrivateKeyword)) {
|
|
6869
6300
|
return "private";
|
|
6870
6301
|
}
|
|
6871
|
-
if (modifiers.some((
|
|
6302
|
+
if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.ProtectedKeyword)) {
|
|
6872
6303
|
return "protected";
|
|
6873
6304
|
}
|
|
6874
|
-
if (modifiers.some((
|
|
6305
|
+
if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.PublicKeyword)) {
|
|
6875
6306
|
return "public";
|
|
6876
6307
|
}
|
|
6877
6308
|
return;
|
|
6878
6309
|
}
|
|
6879
6310
|
function getMethodFlags(member) {
|
|
6880
6311
|
const flags = {};
|
|
6881
|
-
if (member.modifiers?.some((
|
|
6312
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
6882
6313
|
flags.static = true;
|
|
6883
6314
|
}
|
|
6884
6315
|
if (member.asteriskToken) {
|
|
@@ -7063,7 +6494,7 @@ function serializeInterfaceMembers(declaration, checker, typeRefs, referencedTyp
|
|
|
7063
6494
|
if (member.questionToken) {
|
|
7064
6495
|
flags.optional = true;
|
|
7065
6496
|
}
|
|
7066
|
-
if (member.modifiers?.some((
|
|
6497
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword)) {
|
|
7067
6498
|
flags.readonly = true;
|
|
7068
6499
|
}
|
|
7069
6500
|
members.push({
|
|
@@ -7361,7 +6792,7 @@ function extractNamespaceMembers(declaration, checker) {
|
|
|
7361
6792
|
return members;
|
|
7362
6793
|
}
|
|
7363
6794
|
for (const statement of body.statements) {
|
|
7364
|
-
const hasExportModifier = ts.canHaveModifiers(statement) && ts.getModifiers(statement)?.some((
|
|
6795
|
+
const hasExportModifier = ts.canHaveModifiers(statement) && ts.getModifiers(statement)?.some((mod) => mod.kind === ts.SyntaxKind.ExportKeyword);
|
|
7365
6796
|
if (!hasExportModifier) {
|
|
7366
6797
|
continue;
|
|
7367
6798
|
}
|
|
@@ -7571,27 +7002,51 @@ function serializeVariable(declaration, symbol, context) {
|
|
|
7571
7002
|
const typeRefs = typeRegistry.getTypeRefs();
|
|
7572
7003
|
const referencedTypes = typeRegistry.getReferencedTypes();
|
|
7573
7004
|
const symbolName = symbol.getName();
|
|
7574
|
-
const
|
|
7575
|
-
if (
|
|
7005
|
+
const standardSchema = context.detectedSchemas?.get(symbolName);
|
|
7006
|
+
if (standardSchema) {
|
|
7576
7007
|
return {
|
|
7577
7008
|
id: symbolName,
|
|
7578
7009
|
name: symbolName,
|
|
7579
7010
|
...metadata,
|
|
7580
7011
|
kind: "variable",
|
|
7581
7012
|
deprecated: isSymbolDeprecated(symbol),
|
|
7582
|
-
schema:
|
|
7013
|
+
schema: standardSchema.schema,
|
|
7583
7014
|
description,
|
|
7584
7015
|
source: getSourceLocation(declaration),
|
|
7585
7016
|
tags: [
|
|
7586
7017
|
...parsedDoc?.tags ?? [],
|
|
7587
|
-
{ name: "
|
|
7018
|
+
{ name: "schemaLibrary", text: standardSchema.vendor },
|
|
7019
|
+
{ name: "schemaSource", text: "standard-schema" }
|
|
7588
7020
|
],
|
|
7589
7021
|
examples: parsedDoc?.examples
|
|
7590
7022
|
};
|
|
7591
7023
|
}
|
|
7024
|
+
if (isSchemaType(variableType, checker)) {
|
|
7025
|
+
const schemaResult = extractSchemaType(variableType, checker);
|
|
7026
|
+
if (schemaResult?.outputType) {
|
|
7027
|
+
collectReferencedTypes(schemaResult.outputType, checker, referencedTypes);
|
|
7028
|
+
const outputTypeRef = formatTypeReference(schemaResult.outputType, checker, typeRefs, referencedTypes);
|
|
7029
|
+
return {
|
|
7030
|
+
id: symbolName,
|
|
7031
|
+
name: symbolName,
|
|
7032
|
+
...metadata,
|
|
7033
|
+
kind: "variable",
|
|
7034
|
+
deprecated: isSymbolDeprecated(symbol),
|
|
7035
|
+
type: outputTypeRef,
|
|
7036
|
+
description,
|
|
7037
|
+
source: getSourceLocation(declaration),
|
|
7038
|
+
tags: [
|
|
7039
|
+
...parsedDoc?.tags ?? [],
|
|
7040
|
+
{ name: "schemaLibrary", text: schemaResult.adapter.id },
|
|
7041
|
+
{ name: "schemaSource", text: "static-ast" }
|
|
7042
|
+
],
|
|
7043
|
+
examples: parsedDoc?.examples
|
|
7044
|
+
};
|
|
7045
|
+
}
|
|
7046
|
+
}
|
|
7592
7047
|
return {
|
|
7593
|
-
id:
|
|
7594
|
-
name:
|
|
7048
|
+
id: symbolName,
|
|
7049
|
+
name: symbolName,
|
|
7595
7050
|
...metadata,
|
|
7596
7051
|
kind: "variable",
|
|
7597
7052
|
deprecated: isSymbolDeprecated(symbol),
|
|
@@ -7677,9 +7132,9 @@ function createDefaultGenerationInfo(entryFile) {
|
|
|
7677
7132
|
}
|
|
7678
7133
|
function buildOpenPkgSpec(context, resolveExternalTypes, generation) {
|
|
7679
7134
|
const { baseDir, checker: typeChecker, sourceFile, program, entryFile } = context;
|
|
7680
|
-
const packageJsonPath =
|
|
7681
|
-
const packageJson =
|
|
7682
|
-
const generationInfo = generation ?? createDefaultGenerationInfo(
|
|
7135
|
+
const packageJsonPath = path11.join(baseDir, "package.json");
|
|
7136
|
+
const packageJson = fs10.existsSync(packageJsonPath) ? JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8")) : {};
|
|
7137
|
+
const generationInfo = generation ?? createDefaultGenerationInfo(path11.relative(baseDir, entryFile));
|
|
7683
7138
|
const spec = {
|
|
7684
7139
|
$schema: SCHEMA_URL,
|
|
7685
7140
|
openpkg: SCHEMA_VERSION,
|
|
@@ -7878,11 +7333,11 @@ function deriveImportPath(sourceFile, baseDir) {
|
|
|
7878
7333
|
if (!sourceFile) {
|
|
7879
7334
|
return;
|
|
7880
7335
|
}
|
|
7881
|
-
const
|
|
7882
|
-
if (!
|
|
7336
|
+
const relative5 = path11.relative(baseDir, sourceFile);
|
|
7337
|
+
if (!relative5 || relative5.startsWith("..")) {
|
|
7883
7338
|
return;
|
|
7884
7339
|
}
|
|
7885
|
-
const normalized =
|
|
7340
|
+
const normalized = relative5.replace(/\\/g, "/");
|
|
7886
7341
|
const withoutExt = stripExtensions(normalized);
|
|
7887
7342
|
if (!withoutExt) {
|
|
7888
7343
|
return;
|
|
@@ -7921,11 +7376,11 @@ function withExportName(entry, exportName) {
|
|
|
7921
7376
|
function findNearestPackageJson(startDir) {
|
|
7922
7377
|
let current = startDir;
|
|
7923
7378
|
while (true) {
|
|
7924
|
-
const candidate =
|
|
7925
|
-
if (
|
|
7379
|
+
const candidate = path12.join(current, "package.json");
|
|
7380
|
+
if (fs11.existsSync(candidate)) {
|
|
7926
7381
|
return candidate;
|
|
7927
7382
|
}
|
|
7928
|
-
const parent =
|
|
7383
|
+
const parent = path12.dirname(current);
|
|
7929
7384
|
if (parent === current) {
|
|
7930
7385
|
return;
|
|
7931
7386
|
}
|
|
@@ -7953,11 +7408,11 @@ function canResolveExternalModules(program, baseDir) {
|
|
|
7953
7408
|
function hasNodeModulesDirectoryFallback(startDir) {
|
|
7954
7409
|
let current = startDir;
|
|
7955
7410
|
while (true) {
|
|
7956
|
-
const candidate =
|
|
7957
|
-
if (
|
|
7411
|
+
const candidate = path12.join(current, "node_modules");
|
|
7412
|
+
if (fs11.existsSync(candidate)) {
|
|
7958
7413
|
return true;
|
|
7959
7414
|
}
|
|
7960
|
-
const parent =
|
|
7415
|
+
const parent = path12.dirname(current);
|
|
7961
7416
|
if (parent === current) {
|
|
7962
7417
|
break;
|
|
7963
7418
|
}
|
|
@@ -8002,8 +7457,8 @@ function hasExternalImports(sourceFile) {
|
|
|
8002
7457
|
if (ts.isImportDeclaration(node) && node.moduleSpecifier) {
|
|
8003
7458
|
const specifier = node.moduleSpecifier;
|
|
8004
7459
|
if (ts.isStringLiteral(specifier)) {
|
|
8005
|
-
const
|
|
8006
|
-
if (!
|
|
7460
|
+
const modulePath = specifier.text;
|
|
7461
|
+
if (!modulePath.startsWith(".") && !modulePath.startsWith("/")) {
|
|
8007
7462
|
found = true;
|
|
8008
7463
|
}
|
|
8009
7464
|
}
|
|
@@ -8113,11 +7568,27 @@ function runAnalysis(input, generationInput) {
|
|
|
8113
7568
|
|
|
8114
7569
|
// src/extractor.ts
|
|
8115
7570
|
async function extractPackageSpec(entryFile, packageDir, content, options) {
|
|
7571
|
+
const baseDir = packageDir ?? path13.dirname(entryFile);
|
|
7572
|
+
const schemaMode = options?.schemaExtraction ?? "static";
|
|
7573
|
+
let detectedSchemas;
|
|
7574
|
+
if (schemaMode === "runtime" || schemaMode === "hybrid") {
|
|
7575
|
+
const extraction = await extractStandardSchemasFromProject(entryFile, baseDir);
|
|
7576
|
+
if (extraction.schemas.size > 0) {
|
|
7577
|
+
detectedSchemas = new Map;
|
|
7578
|
+
for (const [name, result2] of extraction.schemas) {
|
|
7579
|
+
detectedSchemas.set(name, {
|
|
7580
|
+
schema: result2.outputSchema,
|
|
7581
|
+
vendor: result2.vendor
|
|
7582
|
+
});
|
|
7583
|
+
}
|
|
7584
|
+
}
|
|
7585
|
+
}
|
|
8116
7586
|
const result = runAnalysis({
|
|
8117
7587
|
entryFile,
|
|
8118
7588
|
packageDir,
|
|
8119
7589
|
content,
|
|
8120
|
-
options
|
|
7590
|
+
options,
|
|
7591
|
+
detectedSchemas
|
|
8121
7592
|
});
|
|
8122
7593
|
return result.spec;
|
|
8123
7594
|
}
|
|
@@ -8232,11 +7703,11 @@ async function fetchSpecFromGitHubWithPath(parsed, specPath = "openpkg.json") {
|
|
|
8232
7703
|
}
|
|
8233
7704
|
// src/install/index.ts
|
|
8234
7705
|
var DEFAULT_FALLBACK_ORDER = ["bun", "npm"];
|
|
8235
|
-
async function installDependencies(
|
|
7706
|
+
async function installDependencies(fs12, cwd, runCommand2, options = {}) {
|
|
8236
7707
|
const { timeout = 180000, fallbackOrder = DEFAULT_FALLBACK_ORDER, onProgress } = options;
|
|
8237
7708
|
const errors = [];
|
|
8238
7709
|
onProgress?.({ stage: "installing", message: "Detecting package manager..." });
|
|
8239
|
-
const pmInfo = await detectPackageManager(
|
|
7710
|
+
const pmInfo = await detectPackageManager(fs12);
|
|
8240
7711
|
if (pmInfo.lockfile) {
|
|
8241
7712
|
onProgress?.({
|
|
8242
7713
|
stage: "installing",
|
|
@@ -8311,10 +7782,10 @@ function getFallbackInstallCommand(pm) {
|
|
|
8311
7782
|
}
|
|
8312
7783
|
function createNodeCommandRunner() {
|
|
8313
7784
|
return async (cmd, args, options) => {
|
|
8314
|
-
const { execSync
|
|
7785
|
+
const { execSync } = await import("node:child_process");
|
|
8315
7786
|
const fullCmd = [cmd, ...args].join(" ");
|
|
8316
7787
|
try {
|
|
8317
|
-
const stdout =
|
|
7788
|
+
const stdout = execSync(fullCmd, {
|
|
8318
7789
|
cwd: options.cwd,
|
|
8319
7790
|
stdio: "pipe",
|
|
8320
7791
|
timeout: options.timeout ?? 180000
|
|
@@ -9141,7 +8612,7 @@ function getDocsImpactSummary(diff) {
|
|
|
9141
8612
|
}
|
|
9142
8613
|
// src/openpkg.ts
|
|
9143
8614
|
import * as fsSync from "node:fs";
|
|
9144
|
-
import * as
|
|
8615
|
+
import * as fs12 from "node:fs/promises";
|
|
9145
8616
|
import * as path14 from "node:path";
|
|
9146
8617
|
|
|
9147
8618
|
// src/filtering/apply-filters.ts
|
|
@@ -9357,7 +8828,7 @@ class DocCov {
|
|
|
9357
8828
|
}
|
|
9358
8829
|
async analyzeFile(filePath, analyzeOptions = {}) {
|
|
9359
8830
|
const resolvedPath = path14.resolve(filePath);
|
|
9360
|
-
const content = await
|
|
8831
|
+
const content = await fs12.readFile(resolvedPath, "utf-8");
|
|
9361
8832
|
const packageDir = resolvePackageDir(resolvedPath);
|
|
9362
8833
|
const spec = await extractPackageSpec(resolvedPath, packageDir, content, this.options);
|
|
9363
8834
|
return this.applySpecFilters(spec, analyzeOptions.filters).spec;
|
|
@@ -9405,7 +8876,7 @@ class DocCov {
|
|
|
9405
8876
|
};
|
|
9406
8877
|
}
|
|
9407
8878
|
}
|
|
9408
|
-
const content = await
|
|
8879
|
+
const content = await fs12.readFile(resolvedPath, "utf-8");
|
|
9409
8880
|
const detectedSchemas = await this.detectSchemas(resolvedPath, packageDir);
|
|
9410
8881
|
const analysis = runAnalysis({
|
|
9411
8882
|
entryFile: resolvedPath,
|
|
@@ -9526,6 +8997,10 @@ class DocCov {
|
|
|
9526
8997
|
}
|
|
9527
8998
|
}
|
|
9528
8999
|
async detectSchemas(entryFile, packageDir) {
|
|
9000
|
+
const mode = this.options.schemaExtraction ?? "static";
|
|
9001
|
+
if (mode === "static") {
|
|
9002
|
+
return;
|
|
9003
|
+
}
|
|
9529
9004
|
try {
|
|
9530
9005
|
const result = await detectRuntimeSchemas({
|
|
9531
9006
|
baseDir: packageDir,
|
|
@@ -9621,113 +9096,13 @@ function resolvePackageDir(entryFile) {
|
|
|
9621
9096
|
currentDir = parentDir;
|
|
9622
9097
|
}
|
|
9623
9098
|
}
|
|
9624
|
-
// src/policies/index.ts
|
|
9625
|
-
import * as path15 from "node:path";
|
|
9626
|
-
import { minimatch as minimatch2 } from "minimatch";
|
|
9627
|
-
function matchesPolicy(exp, policy, baseDir) {
|
|
9628
|
-
const filePath = exp.source?.file;
|
|
9629
|
-
if (!filePath)
|
|
9630
|
-
return false;
|
|
9631
|
-
let normalizedPath = filePath;
|
|
9632
|
-
if (baseDir && path15.isAbsolute(filePath)) {
|
|
9633
|
-
normalizedPath = path15.relative(baseDir, filePath);
|
|
9634
|
-
}
|
|
9635
|
-
normalizedPath = normalizedPath.replace(/^\.\//, "");
|
|
9636
|
-
return minimatch2(normalizedPath, policy.path, { matchBase: true });
|
|
9637
|
-
}
|
|
9638
|
-
function calculateCoverage(exports) {
|
|
9639
|
-
if (exports.length === 0)
|
|
9640
|
-
return 100;
|
|
9641
|
-
let documented = 0;
|
|
9642
|
-
for (const exp of exports) {
|
|
9643
|
-
if (exp.description) {
|
|
9644
|
-
documented++;
|
|
9645
|
-
}
|
|
9646
|
-
}
|
|
9647
|
-
return Math.round(documented / exports.length * 100);
|
|
9648
|
-
}
|
|
9649
|
-
function calculateDrift(exports) {
|
|
9650
|
-
if (exports.length === 0)
|
|
9651
|
-
return 0;
|
|
9652
|
-
let withDrift = 0;
|
|
9653
|
-
for (const exp of exports) {
|
|
9654
|
-
if (exp.docs?.drift && exp.docs.drift.length > 0) {
|
|
9655
|
-
withDrift++;
|
|
9656
|
-
}
|
|
9657
|
-
}
|
|
9658
|
-
return Math.round(withDrift / exports.length * 100);
|
|
9659
|
-
}
|
|
9660
|
-
function countMissingExamples(exports) {
|
|
9661
|
-
let missing = 0;
|
|
9662
|
-
for (const exp of exports) {
|
|
9663
|
-
const hasExample = exp.examples && exp.examples.length > 0;
|
|
9664
|
-
if (!hasExample) {
|
|
9665
|
-
missing++;
|
|
9666
|
-
}
|
|
9667
|
-
}
|
|
9668
|
-
return missing;
|
|
9669
|
-
}
|
|
9670
|
-
function evaluatePolicy(policy, allExports, baseDir) {
|
|
9671
|
-
const matchedExports = allExports.filter((exp) => matchesPolicy(exp, policy, baseDir));
|
|
9672
|
-
const coverageScore = calculateCoverage(matchedExports);
|
|
9673
|
-
const driftScore = calculateDrift(matchedExports);
|
|
9674
|
-
const missingExamples = countMissingExamples(matchedExports);
|
|
9675
|
-
const failures = [];
|
|
9676
|
-
if (policy.minCoverage !== undefined && coverageScore < policy.minCoverage) {
|
|
9677
|
-
failures.push({
|
|
9678
|
-
type: "coverage",
|
|
9679
|
-
message: `Coverage ${coverageScore}% below minimum ${policy.minCoverage}%`,
|
|
9680
|
-
actual: coverageScore,
|
|
9681
|
-
threshold: policy.minCoverage
|
|
9682
|
-
});
|
|
9683
|
-
}
|
|
9684
|
-
if (policy.maxDrift !== undefined && driftScore > policy.maxDrift) {
|
|
9685
|
-
failures.push({
|
|
9686
|
-
type: "drift",
|
|
9687
|
-
message: `Drift ${driftScore}% exceeds maximum ${policy.maxDrift}%`,
|
|
9688
|
-
actual: driftScore,
|
|
9689
|
-
threshold: policy.maxDrift
|
|
9690
|
-
});
|
|
9691
|
-
}
|
|
9692
|
-
if (policy.requireExamples && missingExamples > 0) {
|
|
9693
|
-
failures.push({
|
|
9694
|
-
type: "examples",
|
|
9695
|
-
message: `${missingExamples} exports missing @example`,
|
|
9696
|
-
actual: missingExamples,
|
|
9697
|
-
threshold: 0
|
|
9698
|
-
});
|
|
9699
|
-
}
|
|
9700
|
-
return {
|
|
9701
|
-
policy,
|
|
9702
|
-
matchedExports,
|
|
9703
|
-
coverageScore,
|
|
9704
|
-
driftScore,
|
|
9705
|
-
missingExamples,
|
|
9706
|
-
passed: failures.length === 0,
|
|
9707
|
-
failures
|
|
9708
|
-
};
|
|
9709
|
-
}
|
|
9710
|
-
function evaluatePolicies(policies, spec, options = {}) {
|
|
9711
|
-
const { baseDir } = options;
|
|
9712
|
-
const exports = spec.exports ?? [];
|
|
9713
|
-
const results = policies.map((policy) => evaluatePolicy(policy, exports, baseDir));
|
|
9714
|
-
const passedCount = results.filter((r) => r.passed).length;
|
|
9715
|
-
const failedCount = results.length - passedCount;
|
|
9716
|
-
return {
|
|
9717
|
-
results,
|
|
9718
|
-
allPassed: failedCount === 0,
|
|
9719
|
-
totalPolicies: policies.length,
|
|
9720
|
-
passedCount,
|
|
9721
|
-
failedCount
|
|
9722
|
-
};
|
|
9723
|
-
}
|
|
9724
9099
|
// src/resolve/index.ts
|
|
9725
|
-
import * as
|
|
9726
|
-
async function resolveTarget(
|
|
9100
|
+
import * as path15 from "node:path";
|
|
9101
|
+
async function resolveTarget(fs13, options) {
|
|
9727
9102
|
let targetDir = options.cwd;
|
|
9728
9103
|
let packageInfo;
|
|
9729
9104
|
if (options.package) {
|
|
9730
|
-
const mono = await detectMonorepo(
|
|
9105
|
+
const mono = await detectMonorepo(fs13);
|
|
9731
9106
|
if (!mono.isMonorepo) {
|
|
9732
9107
|
throw new Error("Not a monorepo. Remove --package flag for single-package repos.");
|
|
9733
9108
|
}
|
|
@@ -9736,21 +9111,21 @@ async function resolveTarget(fs14, options) {
|
|
|
9736
9111
|
const available = mono.packages.map((p) => p.name).join(", ");
|
|
9737
9112
|
throw new Error(`Package "${options.package}" not found. Available: ${available}`);
|
|
9738
9113
|
}
|
|
9739
|
-
targetDir =
|
|
9114
|
+
targetDir = path15.join(options.cwd, pkg.path);
|
|
9740
9115
|
packageInfo = pkg;
|
|
9741
9116
|
}
|
|
9742
9117
|
let entryFile;
|
|
9743
9118
|
let entryPointInfo;
|
|
9744
9119
|
if (!options.entry) {
|
|
9745
|
-
entryPointInfo = await detectEntryPoint(
|
|
9746
|
-
entryFile =
|
|
9120
|
+
entryPointInfo = await detectEntryPoint(fs13, getRelativePath(options.cwd, targetDir));
|
|
9121
|
+
entryFile = path15.join(targetDir, entryPointInfo.path);
|
|
9747
9122
|
} else {
|
|
9748
|
-
const explicitPath =
|
|
9749
|
-
const isDirectory = await isDir(
|
|
9123
|
+
const explicitPath = path15.resolve(targetDir, options.entry);
|
|
9124
|
+
const isDirectory = await isDir(fs13, getRelativePath(options.cwd, explicitPath));
|
|
9750
9125
|
if (isDirectory) {
|
|
9751
9126
|
targetDir = explicitPath;
|
|
9752
|
-
entryPointInfo = await detectEntryPoint(
|
|
9753
|
-
entryFile =
|
|
9127
|
+
entryPointInfo = await detectEntryPoint(fs13, getRelativePath(options.cwd, explicitPath));
|
|
9128
|
+
entryFile = path15.join(explicitPath, entryPointInfo.path);
|
|
9754
9129
|
} else {
|
|
9755
9130
|
entryFile = explicitPath;
|
|
9756
9131
|
entryPointInfo = {
|
|
@@ -9770,20 +9145,20 @@ async function resolveTarget(fs14, options) {
|
|
|
9770
9145
|
function getRelativePath(base, target) {
|
|
9771
9146
|
if (base === target)
|
|
9772
9147
|
return ".";
|
|
9773
|
-
const rel =
|
|
9148
|
+
const rel = path15.relative(base, target);
|
|
9774
9149
|
return rel || ".";
|
|
9775
9150
|
}
|
|
9776
|
-
async function isDir(
|
|
9777
|
-
const hasPackageJson = await
|
|
9151
|
+
async function isDir(fs13, relativePath) {
|
|
9152
|
+
const hasPackageJson = await fs13.exists(path15.join(relativePath, "package.json"));
|
|
9778
9153
|
if (hasPackageJson)
|
|
9779
9154
|
return true;
|
|
9780
9155
|
const commonEntryFiles = ["index.ts", "index.tsx", "src/index.ts", "main.ts"];
|
|
9781
9156
|
for (const entry of commonEntryFiles) {
|
|
9782
|
-
if (await
|
|
9157
|
+
if (await fs13.exists(path15.join(relativePath, entry))) {
|
|
9783
9158
|
return true;
|
|
9784
9159
|
}
|
|
9785
9160
|
}
|
|
9786
|
-
return !
|
|
9161
|
+
return !path15.extname(relativePath);
|
|
9787
9162
|
}
|
|
9788
9163
|
// src/scan/github-context.ts
|
|
9789
9164
|
function parseGitHubUrl2(url) {
|
|
@@ -9794,8 +9169,8 @@ function parseGitHubUrl2(url) {
|
|
|
9794
9169
|
return null;
|
|
9795
9170
|
}
|
|
9796
9171
|
}
|
|
9797
|
-
async function fetchRawFile(owner, repo, ref,
|
|
9798
|
-
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${
|
|
9172
|
+
async function fetchRawFile(owner, repo, ref, path16, authToken) {
|
|
9173
|
+
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path16}`;
|
|
9799
9174
|
try {
|
|
9800
9175
|
const headers = {};
|
|
9801
9176
|
if (authToken) {
|
|
@@ -10026,7 +9401,6 @@ export {
|
|
|
10026
9401
|
validateExamples,
|
|
10027
9402
|
typecheckExamples,
|
|
10028
9403
|
typecheckExample,
|
|
10029
|
-
tryExtractStandardSchema,
|
|
10030
9404
|
shouldValidate,
|
|
10031
9405
|
serializeJSDoc,
|
|
10032
9406
|
saveSpecCache,
|
|
@@ -10037,6 +9411,7 @@ export {
|
|
|
10037
9411
|
runExamples,
|
|
10038
9412
|
runExample,
|
|
10039
9413
|
resolveTarget,
|
|
9414
|
+
resolveCompiledPath,
|
|
10040
9415
|
renderSparkline,
|
|
10041
9416
|
renderApiSurface,
|
|
10042
9417
|
readPackageJson,
|
|
@@ -10049,18 +9424,16 @@ export {
|
|
|
10049
9424
|
parseJSDocToPatch,
|
|
10050
9425
|
parseGitHubUrl,
|
|
10051
9426
|
parseExamplesFlag,
|
|
10052
|
-
parseCodeOwners,
|
|
10053
9427
|
parseAssertions,
|
|
10054
9428
|
mergeFixes,
|
|
10055
9429
|
mergeFilters,
|
|
10056
|
-
mergeConfig,
|
|
10057
9430
|
loadSpecCache,
|
|
10058
9431
|
loadSnapshotsForDays,
|
|
10059
9432
|
loadSnapshots,
|
|
10060
|
-
loadCodeOwners,
|
|
10061
9433
|
loadCachedReport,
|
|
10062
9434
|
listWorkspacePackages,
|
|
10063
9435
|
isStandardJSONSchema,
|
|
9436
|
+
isSchemaType,
|
|
10064
9437
|
isFixableDrift,
|
|
10065
9438
|
isExecutableLang,
|
|
10066
9439
|
isCachedReportValid,
|
|
@@ -10074,22 +9447,18 @@ export {
|
|
|
10074
9447
|
groupDriftsByCategory,
|
|
10075
9448
|
getUndocumentedExports,
|
|
10076
9449
|
getTrend,
|
|
9450
|
+
getSupportedLibraries,
|
|
10077
9451
|
getSpecCachePath,
|
|
10078
9452
|
getRunCommand,
|
|
10079
|
-
getRulesForKind,
|
|
10080
|
-
getRule,
|
|
10081
9453
|
getReportPath,
|
|
9454
|
+
getRegisteredAdapters,
|
|
10082
9455
|
getPrimaryBuildScript,
|
|
10083
9456
|
getInstallCommand,
|
|
10084
|
-
getFileBlame,
|
|
10085
9457
|
getExtendedTrend,
|
|
10086
9458
|
getDriftSummary,
|
|
10087
9459
|
getDocumentedExports,
|
|
10088
9460
|
getDocsImpactSummary,
|
|
10089
9461
|
getDiffReportPath,
|
|
10090
|
-
getDefaultConfig,
|
|
10091
|
-
getCoverageRules,
|
|
10092
|
-
getBlameForLines,
|
|
10093
9462
|
generateWeeklySummaries,
|
|
10094
9463
|
generateReportFromEnriched,
|
|
10095
9464
|
generateReport,
|
|
@@ -10100,22 +9469,21 @@ export {
|
|
|
10100
9469
|
formatDelta,
|
|
10101
9470
|
findRemovedReferences,
|
|
10102
9471
|
findPackageByName,
|
|
10103
|
-
findOwners,
|
|
10104
9472
|
findJSDocLocation,
|
|
10105
9473
|
findExportReferences,
|
|
10106
9474
|
findDeprecatedReferences,
|
|
9475
|
+
findAdapter,
|
|
10107
9476
|
fetchSpecFromGitHub,
|
|
10108
9477
|
fetchSpec,
|
|
10109
9478
|
fetchGitHubContext,
|
|
10110
|
-
|
|
9479
|
+
extractStandardSchemasFromProject,
|
|
9480
|
+
extractStandardSchemas,
|
|
10111
9481
|
extractSpecSummary,
|
|
9482
|
+
extractSchemaType,
|
|
9483
|
+
extractSchemaOutputType,
|
|
10112
9484
|
extractPackageSpec,
|
|
10113
9485
|
extractImports,
|
|
10114
9486
|
extractFunctionCalls,
|
|
10115
|
-
evaluateQuality,
|
|
10116
|
-
evaluatePolicy,
|
|
10117
|
-
evaluatePolicies,
|
|
10118
|
-
evaluateExportQuality,
|
|
10119
9487
|
ensureSpecCoverage,
|
|
10120
9488
|
enrichSpec,
|
|
10121
9489
|
diffSpecWithDocs,
|
|
@@ -10143,33 +9511,23 @@ export {
|
|
|
10143
9511
|
buildDisplayUrl,
|
|
10144
9512
|
buildCloneUrl,
|
|
10145
9513
|
blockReferencesExport,
|
|
10146
|
-
attributeOwners,
|
|
10147
9514
|
applyPatchToJSDoc,
|
|
10148
9515
|
applyEdits,
|
|
10149
|
-
analyzeSpecOwnership,
|
|
10150
|
-
analyzeSpecContributors,
|
|
10151
9516
|
analyzeProject,
|
|
10152
|
-
analyzeOwnership,
|
|
10153
9517
|
analyzeFile,
|
|
10154
9518
|
analyzeDocsImpact,
|
|
10155
|
-
analyzeContributors,
|
|
10156
9519
|
analyze,
|
|
10157
9520
|
VALIDATION_INFO,
|
|
10158
|
-
TSDOC_RULES,
|
|
10159
9521
|
SandboxFileSystem,
|
|
10160
|
-
STYLE_RULES,
|
|
10161
9522
|
SPEC_CACHE_FILE,
|
|
10162
9523
|
RETENTION_DAYS,
|
|
10163
9524
|
REPORT_VERSION,
|
|
10164
9525
|
REPORT_EXTENSIONS,
|
|
10165
9526
|
NodeFileSystem,
|
|
10166
|
-
KNOWN_VENDORS,
|
|
10167
9527
|
HISTORY_DIR,
|
|
10168
9528
|
DocCov,
|
|
10169
9529
|
DEFAULT_REPORT_PATH,
|
|
10170
9530
|
DEFAULT_REPORT_DIR,
|
|
10171
|
-
CORE_RULES,
|
|
10172
9531
|
CACHE_VERSION,
|
|
10173
|
-
BUILTIN_RULES,
|
|
10174
9532
|
ALL_VALIDATIONS
|
|
10175
9533
|
};
|