@doccov/sdk 0.18.0 → 0.19.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 +393 -817
- package/dist/index.js +572 -1237
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -4,13 +4,13 @@ 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;
|
|
@@ -18,154 +18,345 @@ var __toESM = (mod2, isNodeMode, target) => {
|
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
20
|
// src/analysis/schema-detection.ts
|
|
21
|
-
|
|
21
|
+
async function detectRuntimeSchemas(_context) {
|
|
22
|
+
return {
|
|
23
|
+
schemas: new Map,
|
|
24
|
+
errors: []
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function clearSchemaCache() {}
|
|
28
|
+
// src/extract/schema/types.ts
|
|
29
|
+
function isTypeReference(type) {
|
|
30
|
+
return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
|
|
31
|
+
}
|
|
32
|
+
function getNonNullableType(type) {
|
|
33
|
+
if (type.isUnion()) {
|
|
34
|
+
const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
|
|
35
|
+
if (nonNullable.length === 1) {
|
|
36
|
+
return nonNullable[0];
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return type;
|
|
40
|
+
}
|
|
41
|
+
// src/extract/schema/adapters/zod.ts
|
|
42
|
+
var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
|
|
43
|
+
var zodAdapter = {
|
|
44
|
+
id: "zod",
|
|
45
|
+
packages: ["zod"],
|
|
46
|
+
matches(type, checker) {
|
|
47
|
+
const typeName = checker.typeToString(type);
|
|
48
|
+
return ZOD_TYPE_PATTERN.test(typeName);
|
|
49
|
+
},
|
|
50
|
+
extractOutputType(type, checker) {
|
|
51
|
+
const outputSymbol = type.getProperty("_output");
|
|
52
|
+
if (outputSymbol) {
|
|
53
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
54
|
+
}
|
|
55
|
+
const typeSymbol = type.getProperty("_type");
|
|
56
|
+
if (typeSymbol) {
|
|
57
|
+
return checker.getTypeOfSymbol(typeSymbol);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
},
|
|
61
|
+
extractInputType(type, checker) {
|
|
62
|
+
const inputSymbol = type.getProperty("_input");
|
|
63
|
+
if (inputSymbol) {
|
|
64
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
22
69
|
|
|
23
|
-
// src/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
70
|
+
// src/extract/schema/adapters/valibot.ts
|
|
71
|
+
var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
|
|
72
|
+
var valibotAdapter = {
|
|
73
|
+
id: "valibot",
|
|
74
|
+
packages: ["valibot"],
|
|
75
|
+
matches(type, checker) {
|
|
76
|
+
const typeName = checker.typeToString(type);
|
|
77
|
+
return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
|
|
78
|
+
},
|
|
79
|
+
extractOutputType(type, checker) {
|
|
80
|
+
const typesSymbol = type.getProperty("~types");
|
|
81
|
+
if (!typesSymbol) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
85
|
+
typesType = getNonNullableType(typesType);
|
|
86
|
+
const outputSymbol = typesType.getProperty("output");
|
|
87
|
+
if (!outputSymbol) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return checker.getTypeOfSymbol(outputSymbol);
|
|
91
|
+
},
|
|
92
|
+
extractInputType(type, checker) {
|
|
93
|
+
const typesSymbol = type.getProperty("~types");
|
|
94
|
+
if (!typesSymbol) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
98
|
+
typesType = getNonNullableType(typesType);
|
|
99
|
+
const inputSymbol = typesType.getProperty("input");
|
|
100
|
+
if (!inputSymbol) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return checker.getTypeOfSymbol(inputSymbol);
|
|
27
104
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// src/extract/schema/adapters/typebox.ts
|
|
108
|
+
var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
|
|
109
|
+
var typeboxAdapter = {
|
|
110
|
+
id: "typebox",
|
|
111
|
+
packages: ["@sinclair/typebox"],
|
|
112
|
+
matches(type, checker) {
|
|
113
|
+
const typeName = checker.typeToString(type);
|
|
114
|
+
if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const typeProperty = type.getProperty("type");
|
|
118
|
+
return typeProperty !== undefined;
|
|
119
|
+
},
|
|
120
|
+
extractOutputType(type, checker) {
|
|
121
|
+
const staticSymbol = type.getProperty("static");
|
|
122
|
+
if (staticSymbol) {
|
|
123
|
+
return checker.getTypeOfSymbol(staticSymbol);
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
32
126
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// src/extract/schema/adapters/arktype.ts
|
|
130
|
+
var ARKTYPE_TYPE_PATTERN = /^Type</;
|
|
131
|
+
var arktypeAdapter = {
|
|
132
|
+
id: "arktype",
|
|
133
|
+
packages: ["arktype"],
|
|
134
|
+
matches(type, checker) {
|
|
135
|
+
const typeName = checker.typeToString(type);
|
|
136
|
+
return ARKTYPE_TYPE_PATTERN.test(typeName);
|
|
137
|
+
},
|
|
138
|
+
extractOutputType(type, checker) {
|
|
139
|
+
if (!isTypeReference(type)) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
const args = checker.getTypeArguments(type);
|
|
143
|
+
if (args.length < 1) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
return args[0];
|
|
147
|
+
},
|
|
148
|
+
extractInputType(type, checker) {
|
|
149
|
+
if (!isTypeReference(type)) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
const args = checker.getTypeArguments(type);
|
|
153
|
+
if (args.length < 2) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return args[1];
|
|
36
157
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/extract/schema/registry.ts
|
|
161
|
+
var adapters = [
|
|
162
|
+
zodAdapter,
|
|
163
|
+
arktypeAdapter,
|
|
164
|
+
typeboxAdapter,
|
|
165
|
+
valibotAdapter
|
|
166
|
+
];
|
|
167
|
+
function findAdapter(type, checker) {
|
|
168
|
+
for (const adapter of adapters) {
|
|
169
|
+
if (adapter.matches(type, checker)) {
|
|
170
|
+
return adapter;
|
|
171
|
+
}
|
|
40
172
|
}
|
|
41
|
-
|
|
42
|
-
return typeof jsonSchemaObj.output === "function";
|
|
173
|
+
return null;
|
|
43
174
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
const target = options.target ?? "draft-2020-12";
|
|
47
|
-
const extractedSchema = standard.jsonSchema.output({ target });
|
|
48
|
-
return {
|
|
49
|
-
schema: extractedSchema,
|
|
50
|
-
vendor: standard.vendor,
|
|
51
|
-
version: standard.version
|
|
52
|
-
};
|
|
175
|
+
function isSchemaType(type, checker) {
|
|
176
|
+
return findAdapter(type, checker) !== null;
|
|
53
177
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
178
|
+
function extractSchemaOutputType(type, checker) {
|
|
179
|
+
const adapter = findAdapter(type, checker);
|
|
180
|
+
if (!adapter) {
|
|
56
181
|
return null;
|
|
57
182
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
183
|
+
return adapter.extractOutputType(type, checker);
|
|
184
|
+
}
|
|
185
|
+
function extractSchemaType(type, checker) {
|
|
186
|
+
const adapter = findAdapter(type, checker);
|
|
187
|
+
if (!adapter) {
|
|
61
188
|
return null;
|
|
62
189
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
arktype: { minVersion: "2.0.0", homepage: "https://arktype.io" },
|
|
67
|
-
valibot: { minVersion: "1.0.0", homepage: "https://valibot.dev" }
|
|
68
|
-
};
|
|
69
|
-
// src/analysis/schema-detection.ts
|
|
70
|
-
var moduleCache = new Map;
|
|
71
|
-
function getFileMtime(filePath) {
|
|
72
|
-
try {
|
|
73
|
-
return fs.statSync(filePath).mtimeMs;
|
|
74
|
-
} catch {
|
|
75
|
-
return 0;
|
|
190
|
+
const outputType = adapter.extractOutputType(type, checker);
|
|
191
|
+
if (!outputType) {
|
|
192
|
+
return null;
|
|
76
193
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
194
|
+
const result = {
|
|
195
|
+
adapter,
|
|
196
|
+
outputType
|
|
197
|
+
};
|
|
198
|
+
if (adapter.extractInputType) {
|
|
199
|
+
const inputType = adapter.extractInputType(type, checker);
|
|
200
|
+
if (inputType) {
|
|
201
|
+
result.inputType = inputType;
|
|
202
|
+
}
|
|
83
203
|
}
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
function getRegisteredAdapters() {
|
|
207
|
+
return adapters;
|
|
208
|
+
}
|
|
209
|
+
function getSupportedLibraries() {
|
|
210
|
+
return adapters.flatMap((a) => a.packages);
|
|
211
|
+
}
|
|
212
|
+
// src/extract/schema/standard-schema.ts
|
|
213
|
+
import { spawn } from "node:child_process";
|
|
214
|
+
import * as fs from "node:fs";
|
|
215
|
+
import * as path from "node:path";
|
|
216
|
+
function isStandardJSONSchema(obj) {
|
|
217
|
+
if (typeof obj !== "object" || obj === null)
|
|
218
|
+
return false;
|
|
219
|
+
const std = obj["~standard"];
|
|
220
|
+
if (typeof std !== "object" || std === null)
|
|
221
|
+
return false;
|
|
222
|
+
const stdObj = std;
|
|
223
|
+
if (typeof stdObj.version !== "number")
|
|
224
|
+
return false;
|
|
225
|
+
if (typeof stdObj.vendor !== "string")
|
|
226
|
+
return false;
|
|
227
|
+
const jsonSchema = stdObj.jsonSchema;
|
|
228
|
+
if (typeof jsonSchema !== "object" || jsonSchema === null)
|
|
229
|
+
return false;
|
|
230
|
+
const jsObj = jsonSchema;
|
|
231
|
+
return typeof jsObj.output === "function";
|
|
232
|
+
}
|
|
233
|
+
var WORKER_SCRIPT = `
|
|
234
|
+
const path = require('path');
|
|
235
|
+
|
|
236
|
+
async function extract() {
|
|
237
|
+
// With node -e, argv is: [node, arg1, arg2, ...]
|
|
238
|
+
// (the -e script is NOT in argv)
|
|
239
|
+
const [modulePath, target] = process.argv.slice(1);
|
|
240
|
+
|
|
84
241
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
242
|
+
// Import the module
|
|
243
|
+
const mod = require(path.resolve(modulePath));
|
|
244
|
+
const results = [];
|
|
245
|
+
|
|
246
|
+
// Check each export
|
|
247
|
+
for (const [name, value] of Object.entries(mod)) {
|
|
248
|
+
if (name.startsWith('_')) continue;
|
|
249
|
+
if (typeof value !== 'object' || value === null) continue;
|
|
250
|
+
|
|
251
|
+
const std = value['~standard'];
|
|
252
|
+
if (!std || typeof std !== 'object') continue;
|
|
253
|
+
if (typeof std.version !== 'number') continue;
|
|
254
|
+
if (typeof std.vendor !== 'string') continue;
|
|
255
|
+
if (!std.jsonSchema || typeof std.jsonSchema.output !== 'function') continue;
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const outputSchema = std.jsonSchema.output(target);
|
|
259
|
+
const inputSchema = std.jsonSchema.input ? std.jsonSchema.input(target) : undefined;
|
|
260
|
+
|
|
261
|
+
results.push({
|
|
262
|
+
exportName: name,
|
|
263
|
+
vendor: std.vendor,
|
|
264
|
+
outputSchema,
|
|
265
|
+
inputSchema
|
|
266
|
+
});
|
|
267
|
+
} catch (e) {
|
|
268
|
+
// Skip schemas that fail to extract
|
|
269
|
+
}
|
|
88
270
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
271
|
+
|
|
272
|
+
console.log(JSON.stringify({ success: true, results }));
|
|
273
|
+
} catch (e) {
|
|
274
|
+
console.log(JSON.stringify({ success: false, error: e.message }));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
extract();
|
|
279
|
+
`;
|
|
280
|
+
function resolveCompiledPath(tsPath, baseDir) {
|
|
281
|
+
const relativePath = path.relative(baseDir, tsPath);
|
|
282
|
+
const withoutExt = relativePath.replace(/\.tsx?$/, "");
|
|
283
|
+
const candidates = [
|
|
284
|
+
path.join(baseDir, `${withoutExt}.js`),
|
|
285
|
+
path.join(baseDir, "dist", `${withoutExt.replace(/^src\//, "")}.js`),
|
|
286
|
+
path.join(baseDir, "build", `${withoutExt.replace(/^src\//, "")}.js`),
|
|
287
|
+
path.join(baseDir, "lib", `${withoutExt.replace(/^src\//, "")}.js`)
|
|
288
|
+
];
|
|
289
|
+
for (const candidate of candidates) {
|
|
290
|
+
if (fs.existsSync(candidate)) {
|
|
291
|
+
return candidate;
|
|
100
292
|
}
|
|
101
293
|
}
|
|
102
|
-
|
|
103
|
-
return mod;
|
|
294
|
+
return null;
|
|
104
295
|
}
|
|
105
|
-
async function
|
|
296
|
+
async function extractStandardSchemas(compiledJsPath, options = {}) {
|
|
297
|
+
const { timeout = 1e4, target = "draft-2020-12" } = options;
|
|
106
298
|
const result = {
|
|
107
299
|
schemas: new Map,
|
|
108
300
|
errors: []
|
|
109
301
|
};
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
302
|
+
if (!fs.existsSync(compiledJsPath)) {
|
|
303
|
+
result.errors.push(`Compiled JS not found: ${compiledJsPath}`);
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
return new Promise((resolve) => {
|
|
307
|
+
const child = spawn("node", ["-e", WORKER_SCRIPT, compiledJsPath, target], {
|
|
308
|
+
timeout,
|
|
309
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
310
|
+
});
|
|
311
|
+
let stdout = "";
|
|
312
|
+
let stderr = "";
|
|
313
|
+
child.stdout.on("data", (data) => {
|
|
314
|
+
stdout += data.toString();
|
|
315
|
+
});
|
|
316
|
+
child.stderr.on("data", (data) => {
|
|
317
|
+
stderr += data.toString();
|
|
318
|
+
});
|
|
319
|
+
child.on("close", (code) => {
|
|
320
|
+
if (code !== 0) {
|
|
321
|
+
result.errors.push(`Extraction process failed: ${stderr || `exit code ${code}`}`);
|
|
322
|
+
resolve(result);
|
|
323
|
+
return;
|
|
129
324
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
result.schemas.set(
|
|
325
|
+
try {
|
|
326
|
+
const parsed = JSON.parse(stdout);
|
|
327
|
+
if (!parsed.success) {
|
|
328
|
+
result.errors.push(`Extraction failed: ${parsed.error}`);
|
|
329
|
+
resolve(result);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
for (const item of parsed.results) {
|
|
333
|
+
result.schemas.set(item.exportName, {
|
|
334
|
+
exportName: item.exportName,
|
|
335
|
+
vendor: item.vendor,
|
|
336
|
+
outputSchema: item.outputSchema,
|
|
337
|
+
inputSchema: item.inputSchema
|
|
338
|
+
});
|
|
139
339
|
}
|
|
340
|
+
} catch (e) {
|
|
341
|
+
result.errors.push(`Failed to parse extraction output: ${e}`);
|
|
140
342
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
343
|
+
resolve(result);
|
|
344
|
+
});
|
|
345
|
+
child.on("error", (err) => {
|
|
346
|
+
result.errors.push(`Subprocess error: ${err.message}`);
|
|
347
|
+
resolve(result);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
146
350
|
}
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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 {}
|
|
351
|
+
async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
|
|
352
|
+
const compiledPath = resolveCompiledPath(entryFile, baseDir);
|
|
353
|
+
if (!compiledPath) {
|
|
354
|
+
return {
|
|
355
|
+
schemas: new Map,
|
|
356
|
+
errors: [`Could not find compiled JS for ${entryFile}. Build the project first.`]
|
|
357
|
+
};
|
|
164
358
|
}
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
function clearSchemaCache() {
|
|
168
|
-
moduleCache.clear();
|
|
359
|
+
return extractStandardSchemas(compiledPath, options);
|
|
169
360
|
}
|
|
170
361
|
// src/analysis/docs-coverage.ts
|
|
171
362
|
import {
|
|
@@ -597,7 +788,7 @@ function categorizeDrifts(drifts) {
|
|
|
597
788
|
}
|
|
598
789
|
// src/fix/jsdoc-writer.ts
|
|
599
790
|
import * as fs2 from "node:fs";
|
|
600
|
-
import * as
|
|
791
|
+
import * as path2 from "node:path";
|
|
601
792
|
|
|
602
793
|
// src/ts-module.ts
|
|
603
794
|
import * as tsNamespace from "typescript";
|
|
@@ -964,7 +1155,7 @@ async function applyEdits(edits) {
|
|
|
964
1155
|
}
|
|
965
1156
|
function createSourceFile(filePath) {
|
|
966
1157
|
const content = fs2.readFileSync(filePath, "utf-8");
|
|
967
|
-
return ts.createSourceFile(
|
|
1158
|
+
return ts.createSourceFile(path2.basename(filePath), content, ts.ScriptTarget.Latest, true, filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
968
1159
|
}
|
|
969
1160
|
// src/utils/builtin-detection.ts
|
|
970
1161
|
function isBuiltInTypeName(name) {
|
|
@@ -2162,480 +2353,6 @@ function ensureSpecCoverage(spec) {
|
|
|
2162
2353
|
}
|
|
2163
2354
|
};
|
|
2164
2355
|
}
|
|
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
2356
|
// src/analysis/enrich.ts
|
|
2640
2357
|
function collectAllMissing(exports) {
|
|
2641
2358
|
const allMissing = new Set;
|
|
@@ -2657,38 +2374,33 @@ function collectAllDrift(exports) {
|
|
|
2657
2374
|
}
|
|
2658
2375
|
return allDrift;
|
|
2659
2376
|
}
|
|
2660
|
-
function
|
|
2661
|
-
const
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
}
|
|
2377
|
+
function computeExportCoverage(exp) {
|
|
2378
|
+
const missing = [];
|
|
2379
|
+
if (!exp.description) {
|
|
2380
|
+
missing.push("has-description");
|
|
2381
|
+
return { score: 0, missing };
|
|
2666
2382
|
}
|
|
2667
|
-
return
|
|
2383
|
+
return { score: 100, missing: [] };
|
|
2668
2384
|
}
|
|
2669
2385
|
function enrichSpec(spec, options = {}) {
|
|
2670
|
-
const { driftByExport
|
|
2386
|
+
const { driftByExport } = options;
|
|
2671
2387
|
const exportRegistry = buildExportRegistry(spec);
|
|
2672
2388
|
let totalCoverage = 0;
|
|
2673
2389
|
const enrichedExports = spec.exports.map((exp) => {
|
|
2674
|
-
const
|
|
2675
|
-
const quality = evaluateExportQuality(exp, rawJSDoc, qualityConfig, exportRegistry);
|
|
2390
|
+
const coverage = computeExportCoverage(exp);
|
|
2676
2391
|
const drift = computeExportDrift(exp, exportRegistry);
|
|
2677
2392
|
const additionalDrift = driftByExport?.get(exp.id);
|
|
2678
2393
|
const allDrift2 = additionalDrift ? [...drift, ...additionalDrift] : drift;
|
|
2679
|
-
totalCoverage +=
|
|
2394
|
+
totalCoverage += coverage.score;
|
|
2680
2395
|
const docs2 = {
|
|
2681
|
-
coverageScore:
|
|
2396
|
+
coverageScore: coverage.score
|
|
2682
2397
|
};
|
|
2683
|
-
if (
|
|
2684
|
-
docs2.missing =
|
|
2398
|
+
if (coverage.missing.length > 0) {
|
|
2399
|
+
docs2.missing = coverage.missing;
|
|
2685
2400
|
}
|
|
2686
2401
|
if (allDrift2.length > 0) {
|
|
2687
2402
|
docs2.drift = allDrift2;
|
|
2688
2403
|
}
|
|
2689
|
-
if (quality.violations.length > 0) {
|
|
2690
|
-
docs2.violations = quality.violations;
|
|
2691
|
-
}
|
|
2692
2404
|
return {
|
|
2693
2405
|
...exp,
|
|
2694
2406
|
docs: docs2
|
|
@@ -2697,7 +2409,6 @@ function enrichSpec(spec, options = {}) {
|
|
|
2697
2409
|
const count = enrichedExports.length;
|
|
2698
2410
|
const allMissing = collectAllMissing(enrichedExports);
|
|
2699
2411
|
const allDrift = collectAllDrift(enrichedExports);
|
|
2700
|
-
const allViolations = collectAllViolations(enrichedExports);
|
|
2701
2412
|
const docs = {
|
|
2702
2413
|
coverageScore: count === 0 ? 100 : Math.round(totalCoverage / count)
|
|
2703
2414
|
};
|
|
@@ -2707,9 +2418,6 @@ function enrichSpec(spec, options = {}) {
|
|
|
2707
2418
|
if (allDrift.length > 0) {
|
|
2708
2419
|
docs.drift = allDrift;
|
|
2709
2420
|
}
|
|
2710
|
-
if (allViolations.length > 0) {
|
|
2711
|
-
docs.violations = allViolations;
|
|
2712
|
-
}
|
|
2713
2421
|
const driftSummary = allDrift.length > 0 ? getDriftSummary(allDrift) : undefined;
|
|
2714
2422
|
return {
|
|
2715
2423
|
...spec,
|
|
@@ -2720,7 +2428,7 @@ function enrichSpec(spec, options = {}) {
|
|
|
2720
2428
|
}
|
|
2721
2429
|
// src/analysis/report.ts
|
|
2722
2430
|
import * as fs3 from "node:fs";
|
|
2723
|
-
import * as
|
|
2431
|
+
import * as path3 from "node:path";
|
|
2724
2432
|
|
|
2725
2433
|
// src/types/report.ts
|
|
2726
2434
|
var REPORT_VERSION = "1.0.0";
|
|
@@ -2796,7 +2504,7 @@ function generateReportFromEnriched(enriched) {
|
|
|
2796
2504
|
}
|
|
2797
2505
|
function loadCachedReport(reportPath = DEFAULT_REPORT_PATH) {
|
|
2798
2506
|
try {
|
|
2799
|
-
const fullPath =
|
|
2507
|
+
const fullPath = path3.resolve(reportPath);
|
|
2800
2508
|
if (!fs3.existsSync(fullPath)) {
|
|
2801
2509
|
return null;
|
|
2802
2510
|
}
|
|
@@ -2807,8 +2515,8 @@ function loadCachedReport(reportPath = DEFAULT_REPORT_PATH) {
|
|
|
2807
2515
|
}
|
|
2808
2516
|
}
|
|
2809
2517
|
function saveReport(report, reportPath = DEFAULT_REPORT_PATH) {
|
|
2810
|
-
const fullPath =
|
|
2811
|
-
const dir =
|
|
2518
|
+
const fullPath = path3.resolve(reportPath);
|
|
2519
|
+
const dir = path3.dirname(fullPath);
|
|
2812
2520
|
if (!fs3.existsSync(dir)) {
|
|
2813
2521
|
fs3.mkdirSync(dir, { recursive: true });
|
|
2814
2522
|
}
|
|
@@ -3010,7 +2718,7 @@ function renderApiSurface(spec) {
|
|
|
3010
2718
|
}
|
|
3011
2719
|
// src/analysis/history.ts
|
|
3012
2720
|
import * as fs4 from "node:fs";
|
|
3013
|
-
import * as
|
|
2721
|
+
import * as path4 from "node:path";
|
|
3014
2722
|
var HISTORY_DIR = ".doccov/history";
|
|
3015
2723
|
var RETENTION_DAYS = {
|
|
3016
2724
|
free: 7,
|
|
@@ -3048,16 +2756,16 @@ function computeSnapshot(spec, options) {
|
|
|
3048
2756
|
};
|
|
3049
2757
|
}
|
|
3050
2758
|
function saveSnapshot(snapshot, cwd) {
|
|
3051
|
-
const historyDir =
|
|
2759
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3052
2760
|
if (!fs4.existsSync(historyDir)) {
|
|
3053
2761
|
fs4.mkdirSync(historyDir, { recursive: true });
|
|
3054
2762
|
}
|
|
3055
2763
|
const filename = getSnapshotFilename(new Date(snapshot.timestamp));
|
|
3056
|
-
const filepath =
|
|
2764
|
+
const filepath = path4.join(historyDir, filename);
|
|
3057
2765
|
fs4.writeFileSync(filepath, JSON.stringify(snapshot, null, 2));
|
|
3058
2766
|
}
|
|
3059
2767
|
function loadSnapshots(cwd) {
|
|
3060
|
-
const historyDir =
|
|
2768
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3061
2769
|
if (!fs4.existsSync(historyDir)) {
|
|
3062
2770
|
return [];
|
|
3063
2771
|
}
|
|
@@ -3065,7 +2773,7 @@ function loadSnapshots(cwd) {
|
|
|
3065
2773
|
const snapshots = [];
|
|
3066
2774
|
for (const file of files) {
|
|
3067
2775
|
try {
|
|
3068
|
-
const content = fs4.readFileSync(
|
|
2776
|
+
const content = fs4.readFileSync(path4.join(historyDir, file), "utf-8");
|
|
3069
2777
|
snapshots.push(JSON.parse(content));
|
|
3070
2778
|
} catch {}
|
|
3071
2779
|
}
|
|
@@ -3105,7 +2813,7 @@ function formatDelta(delta) {
|
|
|
3105
2813
|
return "→0%";
|
|
3106
2814
|
}
|
|
3107
2815
|
function pruneHistory(cwd, keepCount = 100) {
|
|
3108
|
-
const historyDir =
|
|
2816
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3109
2817
|
if (!fs4.existsSync(historyDir)) {
|
|
3110
2818
|
return 0;
|
|
3111
2819
|
}
|
|
@@ -3113,7 +2821,7 @@ function pruneHistory(cwd, keepCount = 100) {
|
|
|
3113
2821
|
const toDelete = files.slice(keepCount);
|
|
3114
2822
|
for (const file of toDelete) {
|
|
3115
2823
|
try {
|
|
3116
|
-
fs4.unlinkSync(
|
|
2824
|
+
fs4.unlinkSync(path4.join(historyDir, file));
|
|
3117
2825
|
} catch {}
|
|
3118
2826
|
}
|
|
3119
2827
|
return toDelete.length;
|
|
@@ -3122,7 +2830,7 @@ function pruneByTier(cwd, tier) {
|
|
|
3122
2830
|
const retentionDays = RETENTION_DAYS[tier];
|
|
3123
2831
|
const cutoffDate = new Date;
|
|
3124
2832
|
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
|
|
3125
|
-
const historyDir =
|
|
2833
|
+
const historyDir = path4.resolve(cwd, HISTORY_DIR);
|
|
3126
2834
|
if (!fs4.existsSync(historyDir)) {
|
|
3127
2835
|
return 0;
|
|
3128
2836
|
}
|
|
@@ -3130,7 +2838,7 @@ function pruneByTier(cwd, tier) {
|
|
|
3130
2838
|
let deleted = 0;
|
|
3131
2839
|
for (const file of files) {
|
|
3132
2840
|
try {
|
|
3133
|
-
const filepath =
|
|
2841
|
+
const filepath = path4.join(historyDir, file);
|
|
3134
2842
|
const content = fs4.readFileSync(filepath, "utf-8");
|
|
3135
2843
|
const snapshot = JSON.parse(content);
|
|
3136
2844
|
const snapshotDate = new Date(snapshot.timestamp);
|
|
@@ -3237,7 +2945,7 @@ function getExtendedTrend(spec, cwd, options) {
|
|
|
3237
2945
|
// src/cache/hash.ts
|
|
3238
2946
|
import * as crypto from "node:crypto";
|
|
3239
2947
|
import * as fs5 from "node:fs";
|
|
3240
|
-
import * as
|
|
2948
|
+
import * as path5 from "node:path";
|
|
3241
2949
|
function hashFile(filePath) {
|
|
3242
2950
|
try {
|
|
3243
2951
|
const content = fs5.readFileSync(filePath);
|
|
@@ -3254,21 +2962,21 @@ function hashFiles(filePaths, cwd) {
|
|
|
3254
2962
|
for (const filePath of filePaths) {
|
|
3255
2963
|
const hash = hashFile(filePath);
|
|
3256
2964
|
if (hash) {
|
|
3257
|
-
const relativePath =
|
|
2965
|
+
const relativePath = path5.relative(cwd, filePath);
|
|
3258
2966
|
hashes[relativePath] = hash;
|
|
3259
2967
|
}
|
|
3260
2968
|
}
|
|
3261
2969
|
return hashes;
|
|
3262
2970
|
}
|
|
3263
|
-
function diffHashes(
|
|
2971
|
+
function diffHashes(cached, current) {
|
|
3264
2972
|
const changed = [];
|
|
3265
|
-
for (const [file, hash] of Object.entries(
|
|
2973
|
+
for (const [file, hash] of Object.entries(cached)) {
|
|
3266
2974
|
if (current[file] !== hash) {
|
|
3267
2975
|
changed.push(file);
|
|
3268
2976
|
}
|
|
3269
2977
|
}
|
|
3270
2978
|
for (const file of Object.keys(current)) {
|
|
3271
|
-
if (!(file in
|
|
2979
|
+
if (!(file in cached)) {
|
|
3272
2980
|
changed.push(file);
|
|
3273
2981
|
}
|
|
3274
2982
|
}
|
|
@@ -3276,12 +2984,12 @@ function diffHashes(cached2, current) {
|
|
|
3276
2984
|
}
|
|
3277
2985
|
// src/cache/spec-cache.ts
|
|
3278
2986
|
import * as fs6 from "node:fs";
|
|
3279
|
-
import * as
|
|
2987
|
+
import * as path6 from "node:path";
|
|
3280
2988
|
var CACHE_VERSION = "1.0.0";
|
|
3281
2989
|
var SPEC_CACHE_FILE = ".doccov/spec.cache.json";
|
|
3282
2990
|
function loadSpecCache(cwd) {
|
|
3283
2991
|
try {
|
|
3284
|
-
const cachePath =
|
|
2992
|
+
const cachePath = path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3285
2993
|
if (!fs6.existsSync(cachePath)) {
|
|
3286
2994
|
return null;
|
|
3287
2995
|
}
|
|
@@ -3297,7 +3005,7 @@ function saveSpecCache(spec, context) {
|
|
|
3297
3005
|
cacheVersion: CACHE_VERSION,
|
|
3298
3006
|
generatedAt: new Date().toISOString(),
|
|
3299
3007
|
specVersion: spec.openpkg,
|
|
3300
|
-
entryFile:
|
|
3008
|
+
entryFile: path6.relative(cwd, entryFile),
|
|
3301
3009
|
hashes: {
|
|
3302
3010
|
tsconfig: tsconfigPath ? hashFile(tsconfigPath) : null,
|
|
3303
3011
|
packageJson: hashFile(packageJsonPath) ?? "",
|
|
@@ -3306,8 +3014,8 @@ function saveSpecCache(spec, context) {
|
|
|
3306
3014
|
config,
|
|
3307
3015
|
spec
|
|
3308
3016
|
};
|
|
3309
|
-
const cachePath =
|
|
3310
|
-
const dir =
|
|
3017
|
+
const cachePath = path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3018
|
+
const dir = path6.dirname(cachePath);
|
|
3311
3019
|
if (!fs6.existsSync(dir)) {
|
|
3312
3020
|
fs6.mkdirSync(dir, { recursive: true });
|
|
3313
3021
|
}
|
|
@@ -3318,7 +3026,7 @@ function validateSpecCache(cache, context) {
|
|
|
3318
3026
|
if (cache.cacheVersion !== CACHE_VERSION) {
|
|
3319
3027
|
return { valid: false, reason: "cache-version-mismatch" };
|
|
3320
3028
|
}
|
|
3321
|
-
const relativeEntry =
|
|
3029
|
+
const relativeEntry = path6.relative(cwd, entryFile);
|
|
3322
3030
|
if (cache.entryFile !== relativeEntry) {
|
|
3323
3031
|
return { valid: false, reason: "entry-file-changed" };
|
|
3324
3032
|
}
|
|
@@ -3341,7 +3049,7 @@ function validateSpecCache(cache, context) {
|
|
|
3341
3049
|
return { valid: true };
|
|
3342
3050
|
}
|
|
3343
3051
|
function clearSpecCache(cwd) {
|
|
3344
|
-
const cachePath =
|
|
3052
|
+
const cachePath = path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3345
3053
|
if (fs6.existsSync(cachePath)) {
|
|
3346
3054
|
fs6.unlinkSync(cachePath);
|
|
3347
3055
|
return true;
|
|
@@ -3349,325 +3057,26 @@ function clearSpecCache(cwd) {
|
|
|
3349
3057
|
return false;
|
|
3350
3058
|
}
|
|
3351
3059
|
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);
|
|
3060
|
+
return path6.resolve(cwd, SPEC_CACHE_FILE);
|
|
3652
3061
|
}
|
|
3653
3062
|
// src/config/types.ts
|
|
3654
3063
|
function defineConfig(config) {
|
|
3655
3064
|
return config;
|
|
3656
3065
|
}
|
|
3657
3066
|
// src/detect/utils.ts
|
|
3658
|
-
async function safeParseJson(
|
|
3067
|
+
async function safeParseJson(fs7, path7) {
|
|
3659
3068
|
try {
|
|
3660
|
-
if (!await
|
|
3069
|
+
if (!await fs7.exists(path7))
|
|
3661
3070
|
return null;
|
|
3662
|
-
const content = await
|
|
3071
|
+
const content = await fs7.readFile(path7);
|
|
3663
3072
|
return JSON.parse(content);
|
|
3664
3073
|
} catch {
|
|
3665
3074
|
return null;
|
|
3666
3075
|
}
|
|
3667
3076
|
}
|
|
3668
|
-
async function readPackageJson(
|
|
3669
|
-
const
|
|
3670
|
-
return safeParseJson(
|
|
3077
|
+
async function readPackageJson(fs7, dir) {
|
|
3078
|
+
const path7 = dir === "." ? "package.json" : `${dir}/package.json`;
|
|
3079
|
+
return safeParseJson(fs7, path7);
|
|
3671
3080
|
}
|
|
3672
3081
|
|
|
3673
3082
|
// src/detect/build.ts
|
|
@@ -3697,8 +3106,8 @@ var BUILD_TOOL_PATTERNS = [
|
|
|
3697
3106
|
"babel",
|
|
3698
3107
|
"ncc build"
|
|
3699
3108
|
];
|
|
3700
|
-
async function detectBuildInfo(
|
|
3701
|
-
const pkgJson = await readPackageJson(
|
|
3109
|
+
async function detectBuildInfo(fs7, packagePath = ".") {
|
|
3110
|
+
const pkgJson = await readPackageJson(fs7, packagePath);
|
|
3702
3111
|
const scripts = pkgJson?.scripts ?? {};
|
|
3703
3112
|
const scriptNames = Object.keys(scripts);
|
|
3704
3113
|
const buildScriptsByName = scriptNames.filter((name) => BUILD_SCRIPT_NAMES.has(name) || BUILD_SCRIPT_PREFIXES.some((prefix) => name.startsWith(prefix)));
|
|
@@ -3710,10 +3119,10 @@ async function detectBuildInfo(fs8, packagePath = ".") {
|
|
|
3710
3119
|
});
|
|
3711
3120
|
const buildScripts = [...new Set([...buildScriptsByName, ...buildScriptsByContent])];
|
|
3712
3121
|
const tsconfigPath = packagePath === "." ? "tsconfig.json" : `${packagePath}/tsconfig.json`;
|
|
3713
|
-
const hasTsConfig = await
|
|
3122
|
+
const hasTsConfig = await fs7.exists(tsconfigPath);
|
|
3714
3123
|
const hasTsDep = pkgJson?.devDependencies?.typescript !== undefined || pkgJson?.dependencies?.typescript !== undefined;
|
|
3715
3124
|
const hasTypeScript = hasTsConfig || hasTsDep;
|
|
3716
|
-
const wasm = await detectWasmProject(
|
|
3125
|
+
const wasm = await detectWasmProject(fs7, packagePath, pkgJson);
|
|
3717
3126
|
const napi = detectNapiProject(pkgJson);
|
|
3718
3127
|
return {
|
|
3719
3128
|
scripts: buildScripts,
|
|
@@ -3730,11 +3139,11 @@ var WASM_PACKAGES = new Set([
|
|
|
3730
3139
|
"wasm-bindgen",
|
|
3731
3140
|
"@aspect/aspect-cli"
|
|
3732
3141
|
]);
|
|
3733
|
-
async function detectWasmProject(
|
|
3142
|
+
async function detectWasmProject(fs7, packagePath, pkgJson) {
|
|
3734
3143
|
const pkgCargoPath = packagePath === "." ? "Cargo.toml" : `${packagePath}/Cargo.toml`;
|
|
3735
|
-
if (await
|
|
3144
|
+
if (await fs7.exists(pkgCargoPath))
|
|
3736
3145
|
return true;
|
|
3737
|
-
if (packagePath !== "." && await
|
|
3146
|
+
if (packagePath !== "." && await fs7.exists("Cargo.toml"))
|
|
3738
3147
|
return true;
|
|
3739
3148
|
if (pkgJson) {
|
|
3740
3149
|
const deps = Object.keys({
|
|
@@ -3775,15 +3184,15 @@ function getPrimaryBuildScript(buildInfo) {
|
|
|
3775
3184
|
return buildInfo.scripts[0] ?? null;
|
|
3776
3185
|
}
|
|
3777
3186
|
// src/detect/entry-point.ts
|
|
3778
|
-
async function detectEntryPoint(
|
|
3779
|
-
const pkgJson = await readPackageJson(
|
|
3187
|
+
async function detectEntryPoint(fs7, packagePath = ".") {
|
|
3188
|
+
const pkgJson = await readPackageJson(fs7, packagePath);
|
|
3780
3189
|
if (!pkgJson) {
|
|
3781
3190
|
throw new Error("No package.json found - not a valid npm package");
|
|
3782
3191
|
}
|
|
3783
|
-
const tsConfig = await parseTsConfig(
|
|
3192
|
+
const tsConfig = await parseTsConfig(fs7, packagePath);
|
|
3784
3193
|
const typesField = pkgJson.types || pkgJson.typings;
|
|
3785
3194
|
if (typesField && typeof typesField === "string") {
|
|
3786
|
-
const resolved = await resolveToSource(
|
|
3195
|
+
const resolved = await resolveToSource(fs7, packagePath, typesField, tsConfig);
|
|
3787
3196
|
if (resolved) {
|
|
3788
3197
|
return { ...resolved, source: "types" };
|
|
3789
3198
|
}
|
|
@@ -3793,7 +3202,7 @@ async function detectEntryPoint(fs8, packagePath = ".") {
|
|
|
3793
3202
|
if (dotExport && typeof dotExport === "object" && "types" in dotExport) {
|
|
3794
3203
|
const typesPath = dotExport.types;
|
|
3795
3204
|
if (typesPath && typeof typesPath === "string") {
|
|
3796
|
-
const resolved = await resolveToSource(
|
|
3205
|
+
const resolved = await resolveToSource(fs7, packagePath, typesPath, tsConfig);
|
|
3797
3206
|
if (resolved) {
|
|
3798
3207
|
return { ...resolved, source: "exports" };
|
|
3799
3208
|
}
|
|
@@ -3801,13 +3210,13 @@ async function detectEntryPoint(fs8, packagePath = ".") {
|
|
|
3801
3210
|
}
|
|
3802
3211
|
}
|
|
3803
3212
|
if (pkgJson.main && typeof pkgJson.main === "string") {
|
|
3804
|
-
const resolved = await resolveToSource(
|
|
3213
|
+
const resolved = await resolveToSource(fs7, packagePath, pkgJson.main, tsConfig);
|
|
3805
3214
|
if (resolved) {
|
|
3806
3215
|
return { ...resolved, source: "main" };
|
|
3807
3216
|
}
|
|
3808
3217
|
}
|
|
3809
3218
|
if (pkgJson.module && typeof pkgJson.module === "string") {
|
|
3810
|
-
const resolved = await resolveToSource(
|
|
3219
|
+
const resolved = await resolveToSource(fs7, packagePath, pkgJson.module, tsConfig);
|
|
3811
3220
|
if (resolved) {
|
|
3812
3221
|
return { ...resolved, source: "module" };
|
|
3813
3222
|
}
|
|
@@ -3825,27 +3234,27 @@ async function detectEntryPoint(fs8, packagePath = ".") {
|
|
|
3825
3234
|
];
|
|
3826
3235
|
for (const fallback of fallbacks) {
|
|
3827
3236
|
const checkPath = packagePath === "." ? fallback : `${packagePath}/${fallback}`;
|
|
3828
|
-
if (await
|
|
3237
|
+
if (await fs7.exists(checkPath)) {
|
|
3829
3238
|
return { path: fallback, source: "fallback", isDeclarationOnly: false };
|
|
3830
3239
|
}
|
|
3831
3240
|
}
|
|
3832
3241
|
throw new Error("Could not detect TypeScript entry point. No types field in package.json and no common entry paths found.");
|
|
3833
3242
|
}
|
|
3834
|
-
async function parseTsConfig(
|
|
3243
|
+
async function parseTsConfig(fs7, packagePath) {
|
|
3835
3244
|
const tsconfigPath = packagePath === "." ? "tsconfig.json" : `${packagePath}/tsconfig.json`;
|
|
3836
|
-
const tsconfig = await safeParseJson(
|
|
3245
|
+
const tsconfig = await safeParseJson(fs7, tsconfigPath);
|
|
3837
3246
|
if (!tsconfig?.compilerOptions) {
|
|
3838
3247
|
return null;
|
|
3839
3248
|
}
|
|
3840
3249
|
const { outDir, rootDir, baseUrl, paths } = tsconfig.compilerOptions;
|
|
3841
3250
|
return { outDir, rootDir, baseUrl, paths };
|
|
3842
3251
|
}
|
|
3843
|
-
async function resolveToSource(
|
|
3252
|
+
async function resolveToSource(fs7, basePath, filePath, tsConfig) {
|
|
3844
3253
|
const normalized = filePath.replace(/^\.\//, "");
|
|
3845
3254
|
const checkPath = (p) => basePath === "." ? p : `${basePath}/${p}`;
|
|
3846
3255
|
const isSourceTs = normalized.endsWith(".ts") && !normalized.endsWith(".d.ts") || normalized.endsWith(".tsx");
|
|
3847
3256
|
if (isSourceTs) {
|
|
3848
|
-
if (await
|
|
3257
|
+
if (await fs7.exists(checkPath(normalized))) {
|
|
3849
3258
|
return { path: normalized, isDeclarationOnly: false };
|
|
3850
3259
|
}
|
|
3851
3260
|
}
|
|
@@ -3887,19 +3296,19 @@ async function resolveToSource(fs8, basePath, filePath, tsConfig) {
|
|
|
3887
3296
|
for (const candidate of candidates) {
|
|
3888
3297
|
if (candidate.endsWith(".d.ts"))
|
|
3889
3298
|
continue;
|
|
3890
|
-
if (await
|
|
3299
|
+
if (await fs7.exists(checkPath(candidate))) {
|
|
3891
3300
|
return { path: candidate, isDeclarationOnly: false };
|
|
3892
3301
|
}
|
|
3893
3302
|
}
|
|
3894
3303
|
if (normalized.endsWith(".d.ts")) {
|
|
3895
|
-
if (await
|
|
3304
|
+
if (await fs7.exists(checkPath(normalized))) {
|
|
3896
3305
|
return { path: normalized, isDeclarationOnly: true };
|
|
3897
3306
|
}
|
|
3898
3307
|
}
|
|
3899
3308
|
return null;
|
|
3900
3309
|
}
|
|
3901
3310
|
// src/detect/filesystem.ts
|
|
3902
|
-
import * as
|
|
3311
|
+
import * as fs7 from "node:fs";
|
|
3903
3312
|
import * as nodePath from "node:path";
|
|
3904
3313
|
import { Writable } from "node:stream";
|
|
3905
3314
|
|
|
@@ -3912,19 +3321,19 @@ class NodeFileSystem {
|
|
|
3912
3321
|
return nodePath.join(this.basePath, relativePath);
|
|
3913
3322
|
}
|
|
3914
3323
|
async exists(relativePath) {
|
|
3915
|
-
return
|
|
3324
|
+
return fs7.existsSync(this.resolve(relativePath));
|
|
3916
3325
|
}
|
|
3917
3326
|
async readFile(relativePath) {
|
|
3918
|
-
return
|
|
3327
|
+
return fs7.readFileSync(this.resolve(relativePath), "utf-8");
|
|
3919
3328
|
}
|
|
3920
3329
|
async readDir(relativePath) {
|
|
3921
|
-
return
|
|
3330
|
+
return fs7.readdirSync(this.resolve(relativePath));
|
|
3922
3331
|
}
|
|
3923
3332
|
async isDirectory(relativePath) {
|
|
3924
3333
|
const fullPath = this.resolve(relativePath);
|
|
3925
|
-
if (!
|
|
3334
|
+
if (!fs7.existsSync(fullPath))
|
|
3926
3335
|
return false;
|
|
3927
|
-
return
|
|
3336
|
+
return fs7.statSync(fullPath).isDirectory();
|
|
3928
3337
|
}
|
|
3929
3338
|
}
|
|
3930
3339
|
function createCaptureStream() {
|
|
@@ -3940,9 +3349,9 @@ function createCaptureStream() {
|
|
|
3940
3349
|
|
|
3941
3350
|
class FileNotFoundError extends Error {
|
|
3942
3351
|
path;
|
|
3943
|
-
constructor(
|
|
3944
|
-
super(message ?? `File not found: ${
|
|
3945
|
-
this.path =
|
|
3352
|
+
constructor(path7, message) {
|
|
3353
|
+
super(message ?? `File not found: ${path7}`);
|
|
3354
|
+
this.path = path7;
|
|
3946
3355
|
this.name = "FileNotFoundError";
|
|
3947
3356
|
}
|
|
3948
3357
|
}
|
|
@@ -3952,34 +3361,34 @@ class SandboxFileSystem {
|
|
|
3952
3361
|
constructor(sandbox) {
|
|
3953
3362
|
this.sandbox = sandbox;
|
|
3954
3363
|
}
|
|
3955
|
-
async exists(
|
|
3364
|
+
async exists(path7) {
|
|
3956
3365
|
const result = await this.sandbox.runCommand({
|
|
3957
3366
|
cmd: "test",
|
|
3958
|
-
args: ["-e",
|
|
3367
|
+
args: ["-e", path7]
|
|
3959
3368
|
});
|
|
3960
3369
|
return result.exitCode === 0;
|
|
3961
3370
|
}
|
|
3962
|
-
async readFile(
|
|
3963
|
-
const exists = await this.exists(
|
|
3371
|
+
async readFile(path7) {
|
|
3372
|
+
const exists = await this.exists(path7);
|
|
3964
3373
|
if (!exists) {
|
|
3965
|
-
throw new FileNotFoundError(
|
|
3374
|
+
throw new FileNotFoundError(path7);
|
|
3966
3375
|
}
|
|
3967
3376
|
const capture = createCaptureStream();
|
|
3968
3377
|
const result = await this.sandbox.runCommand({
|
|
3969
3378
|
cmd: "cat",
|
|
3970
|
-
args: [
|
|
3379
|
+
args: [path7],
|
|
3971
3380
|
stdout: capture.stream
|
|
3972
3381
|
});
|
|
3973
3382
|
if (result.exitCode !== 0) {
|
|
3974
|
-
throw new FileNotFoundError(
|
|
3383
|
+
throw new FileNotFoundError(path7, `Failed to read file: ${path7}`);
|
|
3975
3384
|
}
|
|
3976
3385
|
return capture.getOutput();
|
|
3977
3386
|
}
|
|
3978
|
-
async readDir(
|
|
3387
|
+
async readDir(path7) {
|
|
3979
3388
|
const capture = createCaptureStream();
|
|
3980
3389
|
const result = await this.sandbox.runCommand({
|
|
3981
3390
|
cmd: "ls",
|
|
3982
|
-
args: ["-1",
|
|
3391
|
+
args: ["-1", path7],
|
|
3983
3392
|
stdout: capture.stream
|
|
3984
3393
|
});
|
|
3985
3394
|
if (result.exitCode !== 0) {
|
|
@@ -3988,20 +3397,20 @@ class SandboxFileSystem {
|
|
|
3988
3397
|
return capture.getOutput().split(`
|
|
3989
3398
|
`).filter(Boolean);
|
|
3990
3399
|
}
|
|
3991
|
-
async isDirectory(
|
|
3400
|
+
async isDirectory(path7) {
|
|
3992
3401
|
const result = await this.sandbox.runCommand({
|
|
3993
3402
|
cmd: "test",
|
|
3994
|
-
args: ["-d",
|
|
3403
|
+
args: ["-d", path7]
|
|
3995
3404
|
});
|
|
3996
3405
|
return result.exitCode === 0;
|
|
3997
3406
|
}
|
|
3998
3407
|
}
|
|
3999
3408
|
// src/detect/monorepo.ts
|
|
4000
|
-
async function detectMonorepo(
|
|
4001
|
-
const pkgJson = await readPackageJson(
|
|
3409
|
+
async function detectMonorepo(fs8) {
|
|
3410
|
+
const pkgJson = await readPackageJson(fs8, ".");
|
|
4002
3411
|
if (pkgJson?.workspaces) {
|
|
4003
3412
|
const patterns = extractWorkspacePatterns(pkgJson.workspaces);
|
|
4004
|
-
const packages = await resolveWorkspacePackages(
|
|
3413
|
+
const packages = await resolveWorkspacePackages(fs8, patterns, pkgJson.name, pkgJson.private);
|
|
4005
3414
|
return {
|
|
4006
3415
|
isMonorepo: packages.length > 0,
|
|
4007
3416
|
type: "npm-workspaces",
|
|
@@ -4009,10 +3418,10 @@ async function detectMonorepo(fs9) {
|
|
|
4009
3418
|
packages
|
|
4010
3419
|
};
|
|
4011
3420
|
}
|
|
4012
|
-
if (await
|
|
4013
|
-
const content = await
|
|
3421
|
+
if (await fs8.exists("pnpm-workspace.yaml")) {
|
|
3422
|
+
const content = await fs8.readFile("pnpm-workspace.yaml");
|
|
4014
3423
|
const patterns = parsePnpmWorkspace(content);
|
|
4015
|
-
const packages = await resolveWorkspacePackages(
|
|
3424
|
+
const packages = await resolveWorkspacePackages(fs8, patterns, pkgJson?.name, pkgJson?.private);
|
|
4016
3425
|
return {
|
|
4017
3426
|
isMonorepo: packages.length > 0,
|
|
4018
3427
|
type: "pnpm-workspaces",
|
|
@@ -4020,10 +3429,10 @@ async function detectMonorepo(fs9) {
|
|
|
4020
3429
|
packages
|
|
4021
3430
|
};
|
|
4022
3431
|
}
|
|
4023
|
-
if (await
|
|
4024
|
-
const lerna = await safeParseJson(
|
|
3432
|
+
if (await fs8.exists("lerna.json")) {
|
|
3433
|
+
const lerna = await safeParseJson(fs8, "lerna.json");
|
|
4025
3434
|
const patterns = lerna?.packages ?? ["packages/*"];
|
|
4026
|
-
const packages = await resolveWorkspacePackages(
|
|
3435
|
+
const packages = await resolveWorkspacePackages(fs8, patterns, pkgJson?.name, pkgJson?.private);
|
|
4027
3436
|
return {
|
|
4028
3437
|
isMonorepo: packages.length > 0,
|
|
4029
3438
|
type: "lerna",
|
|
@@ -4097,7 +3506,7 @@ function parsePnpmWorkspace(content) {
|
|
|
4097
3506
|
}
|
|
4098
3507
|
return patterns.length > 0 ? patterns : ["packages/*"];
|
|
4099
3508
|
}
|
|
4100
|
-
async function resolveWorkspacePackages(
|
|
3509
|
+
async function resolveWorkspacePackages(fs8, patterns, rootPackageName, rootIsPrivate) {
|
|
4101
3510
|
const packages = [];
|
|
4102
3511
|
const seen = new Set;
|
|
4103
3512
|
if (rootPackageName && !rootIsPrivate && rootPackageName !== "root") {
|
|
@@ -4119,18 +3528,18 @@ async function resolveWorkspacePackages(fs9, patterns, rootPackageName, rootIsPr
|
|
|
4119
3528
|
}
|
|
4120
3529
|
dirsToScan.add("packages");
|
|
4121
3530
|
for (const dir of dirsToScan) {
|
|
4122
|
-
if (!await
|
|
3531
|
+
if (!await fs8.exists(dir))
|
|
4123
3532
|
continue;
|
|
4124
|
-
if (!await
|
|
3533
|
+
if (!await fs8.isDirectory(dir))
|
|
4125
3534
|
continue;
|
|
4126
|
-
const subdirs = await
|
|
3535
|
+
const subdirs = await fs8.readDir(dir);
|
|
4127
3536
|
for (const subdir of subdirs) {
|
|
4128
3537
|
const pkgPath = `${dir}/${subdir}`;
|
|
4129
3538
|
const pkgJsonPath = `${pkgPath}/package.json`;
|
|
4130
|
-
if (!await
|
|
3539
|
+
if (!await fs8.exists(pkgJsonPath))
|
|
4131
3540
|
continue;
|
|
4132
3541
|
try {
|
|
4133
|
-
const content = await
|
|
3542
|
+
const content = await fs8.readFile(pkgJsonPath);
|
|
4134
3543
|
const pkg = JSON.parse(content);
|
|
4135
3544
|
if (pkg.name && !seen.has(pkg.name)) {
|
|
4136
3545
|
seen.add(pkg.name);
|
|
@@ -4202,14 +3611,14 @@ var DEFAULT_PM = {
|
|
|
4202
3611
|
installArgs: ["install", "--legacy-peer-deps"],
|
|
4203
3612
|
runPrefix: ["npm", "run"]
|
|
4204
3613
|
};
|
|
4205
|
-
async function detectPackageManager(
|
|
4206
|
-
const pkgJson = await safeParseJson(
|
|
3614
|
+
async function detectPackageManager(fs8) {
|
|
3615
|
+
const pkgJson = await safeParseJson(fs8, "package.json");
|
|
4207
3616
|
if (pkgJson?.packageManager) {
|
|
4208
3617
|
const pmName = parsePackageManagerField(pkgJson.packageManager);
|
|
4209
3618
|
if (pmName && PM_CONFIGS[pmName]) {
|
|
4210
3619
|
const config = PM_CONFIGS[pmName];
|
|
4211
3620
|
for (const lockfile of config.lockfiles) {
|
|
4212
|
-
if (await
|
|
3621
|
+
if (await fs8.exists(lockfile)) {
|
|
4213
3622
|
return { ...config.info, lockfile };
|
|
4214
3623
|
}
|
|
4215
3624
|
}
|
|
@@ -4219,7 +3628,7 @@ async function detectPackageManager(fs9) {
|
|
|
4219
3628
|
const foundLockfiles = [];
|
|
4220
3629
|
for (const [pmName, config] of Object.entries(PM_CONFIGS)) {
|
|
4221
3630
|
for (const lockfile of config.lockfiles) {
|
|
4222
|
-
if (await
|
|
3631
|
+
if (await fs8.exists(lockfile)) {
|
|
4223
3632
|
foundLockfiles.push({ lockfile, pm: pmName });
|
|
4224
3633
|
}
|
|
4225
3634
|
}
|
|
@@ -4251,10 +3660,10 @@ function getRunCommand(pm, script) {
|
|
|
4251
3660
|
return [...pm.runPrefix, script];
|
|
4252
3661
|
}
|
|
4253
3662
|
// src/detect/index.ts
|
|
4254
|
-
async function analyzeProject(
|
|
3663
|
+
async function analyzeProject(fs8, options = {}) {
|
|
4255
3664
|
const [packageManager, monorepo] = await Promise.all([
|
|
4256
|
-
detectPackageManager(
|
|
4257
|
-
detectMonorepo(
|
|
3665
|
+
detectPackageManager(fs8),
|
|
3666
|
+
detectMonorepo(fs8)
|
|
4258
3667
|
]);
|
|
4259
3668
|
let targetPath = ".";
|
|
4260
3669
|
if (monorepo.isMonorepo) {
|
|
@@ -4271,8 +3680,8 @@ async function analyzeProject(fs9, options = {}) {
|
|
|
4271
3680
|
targetPath = pkg.path;
|
|
4272
3681
|
}
|
|
4273
3682
|
const [entryPoint, build] = await Promise.all([
|
|
4274
|
-
detectEntryPoint(
|
|
4275
|
-
detectBuildInfo(
|
|
3683
|
+
detectEntryPoint(fs8, targetPath),
|
|
3684
|
+
detectBuildInfo(fs8, targetPath)
|
|
4276
3685
|
]);
|
|
4277
3686
|
return { packageManager, monorepo, entryPoint, build };
|
|
4278
3687
|
}
|
|
@@ -4316,17 +3725,17 @@ function shouldValidate(validations, check) {
|
|
|
4316
3725
|
return validations.includes(check);
|
|
4317
3726
|
}
|
|
4318
3727
|
// src/typecheck/example-typechecker.ts
|
|
4319
|
-
import * as
|
|
4320
|
-
import * as
|
|
3728
|
+
import * as fs8 from "node:fs";
|
|
3729
|
+
import * as path7 from "node:path";
|
|
4321
3730
|
import ts3 from "typescript";
|
|
4322
3731
|
function stripCodeBlockMarkers(code) {
|
|
4323
3732
|
return code.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
|
|
4324
3733
|
}
|
|
4325
3734
|
function getPackageName(packagePath) {
|
|
4326
|
-
const pkgJsonPath =
|
|
4327
|
-
if (
|
|
3735
|
+
const pkgJsonPath = path7.join(packagePath, "package.json");
|
|
3736
|
+
if (fs8.existsSync(pkgJsonPath)) {
|
|
4328
3737
|
try {
|
|
4329
|
-
const pkgJson = JSON.parse(
|
|
3738
|
+
const pkgJson = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
|
|
4330
3739
|
return pkgJson.name;
|
|
4331
3740
|
} catch {
|
|
4332
3741
|
return;
|
|
@@ -4336,12 +3745,12 @@ function getPackageName(packagePath) {
|
|
|
4336
3745
|
}
|
|
4337
3746
|
function findTsConfig(packagePath) {
|
|
4338
3747
|
let dir = packagePath;
|
|
4339
|
-
while (dir !==
|
|
4340
|
-
const tsConfigPath =
|
|
4341
|
-
if (
|
|
3748
|
+
while (dir !== path7.dirname(dir)) {
|
|
3749
|
+
const tsConfigPath = path7.join(dir, "tsconfig.json");
|
|
3750
|
+
if (fs8.existsSync(tsConfigPath)) {
|
|
4342
3751
|
return tsConfigPath;
|
|
4343
3752
|
}
|
|
4344
|
-
dir =
|
|
3753
|
+
dir = path7.dirname(dir);
|
|
4345
3754
|
}
|
|
4346
3755
|
return;
|
|
4347
3756
|
}
|
|
@@ -4357,10 +3766,10 @@ function createVirtualSource(example, packageName, exportNames) {
|
|
|
4357
3766
|
`);
|
|
4358
3767
|
}
|
|
4359
3768
|
function getCompilerOptions(tsconfigPath) {
|
|
4360
|
-
if (tsconfigPath &&
|
|
3769
|
+
if (tsconfigPath && fs8.existsSync(tsconfigPath)) {
|
|
4361
3770
|
const configFile = ts3.readConfigFile(tsconfigPath, ts3.sys.readFile);
|
|
4362
3771
|
if (!configFile.error) {
|
|
4363
|
-
const parsed = ts3.parseJsonConfigFileContent(configFile.config, ts3.sys,
|
|
3772
|
+
const parsed = ts3.parseJsonConfigFileContent(configFile.config, ts3.sys, path7.dirname(tsconfigPath));
|
|
4364
3773
|
return {
|
|
4365
3774
|
...parsed.options,
|
|
4366
3775
|
noEmit: true,
|
|
@@ -4386,7 +3795,7 @@ function typecheckExample(example, packagePath, options = {}) {
|
|
|
4386
3795
|
const tsconfigPath = options.tsconfig ?? findTsConfig(packagePath);
|
|
4387
3796
|
const compilerOptions = getCompilerOptions(tsconfigPath);
|
|
4388
3797
|
const virtualSource = createVirtualSource(cleanCode, packageName, exportNames);
|
|
4389
|
-
const virtualFileName =
|
|
3798
|
+
const virtualFileName = path7.join(packagePath, "__doccov_example__.ts");
|
|
4390
3799
|
const hasImport = packageName !== undefined && exportNames && exportNames.length > 0;
|
|
4391
3800
|
const lineOffset = hasImport ? 2 : 0;
|
|
4392
3801
|
const sourceFile = ts3.createSourceFile(virtualFileName, virtualSource, ts3.ScriptTarget.ES2022, true);
|
|
@@ -4457,25 +3866,25 @@ function typecheckExamples(examples, packagePath, options = {}) {
|
|
|
4457
3866
|
}
|
|
4458
3867
|
|
|
4459
3868
|
// src/utils/example-runner.ts
|
|
4460
|
-
import { spawn } from "node:child_process";
|
|
4461
|
-
import * as
|
|
3869
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3870
|
+
import * as fs9 from "node:fs";
|
|
4462
3871
|
import * as os from "node:os";
|
|
4463
|
-
import * as
|
|
3872
|
+
import * as path8 from "node:path";
|
|
4464
3873
|
function stripCodeBlockMarkers2(code) {
|
|
4465
3874
|
return code.replace(/^```(?:ts|typescript|js|javascript)?\n?/i, "").replace(/\n?```$/i, "").trim();
|
|
4466
3875
|
}
|
|
4467
3876
|
async function runExample(code, options = {}) {
|
|
4468
3877
|
const { timeout = 5000, cwd = process.cwd() } = options;
|
|
4469
3878
|
const cleanCode = stripCodeBlockMarkers2(code);
|
|
4470
|
-
const tmpFile =
|
|
3879
|
+
const tmpFile = path8.join(cwd, `doccov-example-${Date.now()}-${Math.random().toString(36).slice(2)}.ts`);
|
|
4471
3880
|
try {
|
|
4472
|
-
|
|
3881
|
+
fs9.writeFileSync(tmpFile, cleanCode, "utf-8");
|
|
4473
3882
|
const startTime = Date.now();
|
|
4474
3883
|
return await new Promise((resolve5) => {
|
|
4475
3884
|
let stdout = "";
|
|
4476
3885
|
let stderr = "";
|
|
4477
3886
|
let killed = false;
|
|
4478
|
-
const proc =
|
|
3887
|
+
const proc = spawn2("node", ["--experimental-strip-types", tmpFile], {
|
|
4479
3888
|
cwd,
|
|
4480
3889
|
timeout,
|
|
4481
3890
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -4524,7 +3933,7 @@ async function runExample(code, options = {}) {
|
|
|
4524
3933
|
});
|
|
4525
3934
|
} finally {
|
|
4526
3935
|
try {
|
|
4527
|
-
|
|
3936
|
+
fs9.unlinkSync(tmpFile);
|
|
4528
3937
|
} catch {}
|
|
4529
3938
|
}
|
|
4530
3939
|
}
|
|
@@ -4539,15 +3948,15 @@ async function runExamples(examples, options = {}) {
|
|
|
4539
3948
|
return results;
|
|
4540
3949
|
}
|
|
4541
3950
|
function detectPackageManager2(cwd) {
|
|
4542
|
-
if (
|
|
3951
|
+
if (fs9.existsSync(path8.join(cwd, "bun.lockb")))
|
|
4543
3952
|
return "bun";
|
|
4544
|
-
if (
|
|
3953
|
+
if (fs9.existsSync(path8.join(cwd, "bun.lock")))
|
|
4545
3954
|
return "bun";
|
|
4546
|
-
if (
|
|
3955
|
+
if (fs9.existsSync(path8.join(cwd, "pnpm-lock.yaml")))
|
|
4547
3956
|
return "pnpm";
|
|
4548
|
-
if (
|
|
3957
|
+
if (fs9.existsSync(path8.join(cwd, "yarn.lock")))
|
|
4549
3958
|
return "yarn";
|
|
4550
|
-
if (
|
|
3959
|
+
if (fs9.existsSync(path8.join(cwd, "package-lock.json")))
|
|
4551
3960
|
return "npm";
|
|
4552
3961
|
return "npm";
|
|
4553
3962
|
}
|
|
@@ -4568,7 +3977,7 @@ async function runCommand(cmd, args, options) {
|
|
|
4568
3977
|
let stdout = "";
|
|
4569
3978
|
let stderr = "";
|
|
4570
3979
|
let killed = false;
|
|
4571
|
-
const proc =
|
|
3980
|
+
const proc = spawn2(cmd, args, {
|
|
4572
3981
|
cwd: options.cwd,
|
|
4573
3982
|
stdio: ["ignore", "pipe", "pipe"]
|
|
4574
3983
|
});
|
|
@@ -4615,12 +4024,12 @@ async function runExamplesWithPackage(examples, options) {
|
|
|
4615
4024
|
const { packagePath, packageManager, installTimeout = 60000, timeout = 5000 } = options;
|
|
4616
4025
|
const startTime = Date.now();
|
|
4617
4026
|
const results = new Map;
|
|
4618
|
-
const absolutePackagePath =
|
|
4619
|
-
const workDir =
|
|
4027
|
+
const absolutePackagePath = path8.resolve(packagePath);
|
|
4028
|
+
const workDir = path8.join(os.tmpdir(), `doccov-examples-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
4620
4029
|
try {
|
|
4621
|
-
|
|
4030
|
+
fs9.mkdirSync(workDir, { recursive: true });
|
|
4622
4031
|
const pkgJson = { name: "doccov-example-runner", type: "module" };
|
|
4623
|
-
|
|
4032
|
+
fs9.writeFileSync(path8.join(workDir, "package.json"), JSON.stringify(pkgJson, null, 2));
|
|
4624
4033
|
const pm = packageManager ?? detectPackageManager2(options.cwd ?? process.cwd());
|
|
4625
4034
|
const { cmd, args } = getInstallCommand2(pm, absolutePackagePath);
|
|
4626
4035
|
const installResult = await runCommand(cmd, args, {
|
|
@@ -4648,7 +4057,7 @@ async function runExamplesWithPackage(examples, options) {
|
|
|
4648
4057
|
};
|
|
4649
4058
|
} finally {
|
|
4650
4059
|
try {
|
|
4651
|
-
|
|
4060
|
+
fs9.rmSync(workDir, { recursive: true, force: true });
|
|
4652
4061
|
} catch {}
|
|
4653
4062
|
}
|
|
4654
4063
|
}
|
|
@@ -4827,9 +4236,12 @@ async function validateExamples(exports, options) {
|
|
|
4827
4236
|
}
|
|
4828
4237
|
return result;
|
|
4829
4238
|
}
|
|
4830
|
-
// src/
|
|
4831
|
-
import * as fs12 from "node:fs";
|
|
4239
|
+
// src/extractor.ts
|
|
4832
4240
|
import * as path13 from "node:path";
|
|
4241
|
+
|
|
4242
|
+
// src/analysis/run-analysis.ts
|
|
4243
|
+
import * as fs11 from "node:fs";
|
|
4244
|
+
import * as path12 from "node:path";
|
|
4833
4245
|
// src/utils/type-utils.ts
|
|
4834
4246
|
function getTypeId(type, typeChecker) {
|
|
4835
4247
|
const internalId = type.id;
|
|
@@ -4952,7 +4364,7 @@ function collectReferencedTypesFromNode(node, typeChecker, referencedTypes) {
|
|
|
4952
4364
|
}
|
|
4953
4365
|
|
|
4954
4366
|
// src/analysis/context.ts
|
|
4955
|
-
import * as
|
|
4367
|
+
import * as path10 from "node:path";
|
|
4956
4368
|
|
|
4957
4369
|
// src/options.ts
|
|
4958
4370
|
var DEFAULT_MAX_TYPE_DEPTH = 20;
|
|
@@ -4973,7 +4385,7 @@ function normalizeDocCovOptions(options = {}) {
|
|
|
4973
4385
|
}
|
|
4974
4386
|
|
|
4975
4387
|
// src/analysis/program.ts
|
|
4976
|
-
import * as
|
|
4388
|
+
import * as path9 from "node:path";
|
|
4977
4389
|
var DEFAULT_COMPILER_OPTIONS = {
|
|
4978
4390
|
target: ts.ScriptTarget.Latest,
|
|
4979
4391
|
module: ts.ModuleKind.CommonJS,
|
|
@@ -4983,14 +4395,14 @@ var DEFAULT_COMPILER_OPTIONS = {
|
|
|
4983
4395
|
};
|
|
4984
4396
|
function createProgram({
|
|
4985
4397
|
entryFile,
|
|
4986
|
-
baseDir =
|
|
4398
|
+
baseDir = path9.dirname(entryFile),
|
|
4987
4399
|
content
|
|
4988
4400
|
}) {
|
|
4989
4401
|
const configPath = ts.findConfigFile(baseDir, ts.sys.fileExists, "tsconfig.json");
|
|
4990
4402
|
let compilerOptions = { ...DEFAULT_COMPILER_OPTIONS };
|
|
4991
4403
|
if (configPath) {
|
|
4992
4404
|
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
4993
|
-
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys,
|
|
4405
|
+
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path9.dirname(configPath));
|
|
4994
4406
|
compilerOptions = { ...compilerOptions, ...parsedConfig.options };
|
|
4995
4407
|
}
|
|
4996
4408
|
const allowJsVal = compilerOptions.allowJs;
|
|
@@ -5028,7 +4440,7 @@ function createAnalysisContext({
|
|
|
5028
4440
|
options,
|
|
5029
4441
|
detectedSchemas
|
|
5030
4442
|
}) {
|
|
5031
|
-
const baseDir = packageDir ??
|
|
4443
|
+
const baseDir = packageDir ?? path10.dirname(entryFile);
|
|
5032
4444
|
const normalizedOptions = normalizeDocCovOptions(options);
|
|
5033
4445
|
const programResult = createProgram({ entryFile, baseDir, content });
|
|
5034
4446
|
if (!programResult.sourceFile) {
|
|
@@ -5049,8 +4461,8 @@ function createAnalysisContext({
|
|
|
5049
4461
|
}
|
|
5050
4462
|
|
|
5051
4463
|
// src/analysis/spec-builder.ts
|
|
5052
|
-
import * as
|
|
5053
|
-
import * as
|
|
4464
|
+
import * as fs10 from "node:fs";
|
|
4465
|
+
import * as path11 from "node:path";
|
|
5054
4466
|
import { SCHEMA_URL, SCHEMA_VERSION } from "@openpkg-ts/spec";
|
|
5055
4467
|
|
|
5056
4468
|
// src/analysis/decorator-utils.ts
|
|
@@ -6740,10 +6152,10 @@ function serializeClassMembers(declaration, checker, typeRefs, referencedTypes)
|
|
|
6740
6152
|
if (member.questionToken || isOptionalSymbol) {
|
|
6741
6153
|
flags.optional = true;
|
|
6742
6154
|
}
|
|
6743
|
-
if (member.modifiers?.some((
|
|
6155
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword)) {
|
|
6744
6156
|
flags.readonly = true;
|
|
6745
6157
|
}
|
|
6746
|
-
if (member.modifiers?.some((
|
|
6158
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
6747
6159
|
flags.static = true;
|
|
6748
6160
|
}
|
|
6749
6161
|
members.push({
|
|
@@ -6865,20 +6277,20 @@ function serializeSignature(signature, checker, typeRefs, referencedTypes, doc,
|
|
|
6865
6277
|
function getMemberVisibility(modifiers) {
|
|
6866
6278
|
if (!modifiers)
|
|
6867
6279
|
return;
|
|
6868
|
-
if (modifiers.some((
|
|
6280
|
+
if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.PrivateKeyword)) {
|
|
6869
6281
|
return "private";
|
|
6870
6282
|
}
|
|
6871
|
-
if (modifiers.some((
|
|
6283
|
+
if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.ProtectedKeyword)) {
|
|
6872
6284
|
return "protected";
|
|
6873
6285
|
}
|
|
6874
|
-
if (modifiers.some((
|
|
6286
|
+
if (modifiers.some((mod) => mod.kind === ts.SyntaxKind.PublicKeyword)) {
|
|
6875
6287
|
return "public";
|
|
6876
6288
|
}
|
|
6877
6289
|
return;
|
|
6878
6290
|
}
|
|
6879
6291
|
function getMethodFlags(member) {
|
|
6880
6292
|
const flags = {};
|
|
6881
|
-
if (member.modifiers?.some((
|
|
6293
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.StaticKeyword)) {
|
|
6882
6294
|
flags.static = true;
|
|
6883
6295
|
}
|
|
6884
6296
|
if (member.asteriskToken) {
|
|
@@ -7063,7 +6475,7 @@ function serializeInterfaceMembers(declaration, checker, typeRefs, referencedTyp
|
|
|
7063
6475
|
if (member.questionToken) {
|
|
7064
6476
|
flags.optional = true;
|
|
7065
6477
|
}
|
|
7066
|
-
if (member.modifiers?.some((
|
|
6478
|
+
if (member.modifiers?.some((mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword)) {
|
|
7067
6479
|
flags.readonly = true;
|
|
7068
6480
|
}
|
|
7069
6481
|
members.push({
|
|
@@ -7361,7 +6773,7 @@ function extractNamespaceMembers(declaration, checker) {
|
|
|
7361
6773
|
return members;
|
|
7362
6774
|
}
|
|
7363
6775
|
for (const statement of body.statements) {
|
|
7364
|
-
const hasExportModifier = ts.canHaveModifiers(statement) && ts.getModifiers(statement)?.some((
|
|
6776
|
+
const hasExportModifier = ts.canHaveModifiers(statement) && ts.getModifiers(statement)?.some((mod) => mod.kind === ts.SyntaxKind.ExportKeyword);
|
|
7365
6777
|
if (!hasExportModifier) {
|
|
7366
6778
|
continue;
|
|
7367
6779
|
}
|
|
@@ -7571,27 +6983,51 @@ function serializeVariable(declaration, symbol, context) {
|
|
|
7571
6983
|
const typeRefs = typeRegistry.getTypeRefs();
|
|
7572
6984
|
const referencedTypes = typeRegistry.getReferencedTypes();
|
|
7573
6985
|
const symbolName = symbol.getName();
|
|
7574
|
-
const
|
|
7575
|
-
if (
|
|
6986
|
+
const standardSchema = context.detectedSchemas?.get(symbolName);
|
|
6987
|
+
if (standardSchema) {
|
|
7576
6988
|
return {
|
|
7577
6989
|
id: symbolName,
|
|
7578
6990
|
name: symbolName,
|
|
7579
6991
|
...metadata,
|
|
7580
6992
|
kind: "variable",
|
|
7581
6993
|
deprecated: isSymbolDeprecated(symbol),
|
|
7582
|
-
schema:
|
|
6994
|
+
schema: standardSchema.schema,
|
|
7583
6995
|
description,
|
|
7584
6996
|
source: getSourceLocation(declaration),
|
|
7585
6997
|
tags: [
|
|
7586
6998
|
...parsedDoc?.tags ?? [],
|
|
7587
|
-
{ name: "
|
|
6999
|
+
{ name: "schemaLibrary", text: standardSchema.vendor },
|
|
7000
|
+
{ name: "schemaSource", text: "standard-schema" }
|
|
7588
7001
|
],
|
|
7589
7002
|
examples: parsedDoc?.examples
|
|
7590
7003
|
};
|
|
7591
7004
|
}
|
|
7005
|
+
if (isSchemaType(variableType, checker)) {
|
|
7006
|
+
const schemaResult = extractSchemaType(variableType, checker);
|
|
7007
|
+
if (schemaResult?.outputType) {
|
|
7008
|
+
collectReferencedTypes(schemaResult.outputType, checker, referencedTypes);
|
|
7009
|
+
const outputTypeRef = formatTypeReference(schemaResult.outputType, checker, typeRefs, referencedTypes);
|
|
7010
|
+
return {
|
|
7011
|
+
id: symbolName,
|
|
7012
|
+
name: symbolName,
|
|
7013
|
+
...metadata,
|
|
7014
|
+
kind: "variable",
|
|
7015
|
+
deprecated: isSymbolDeprecated(symbol),
|
|
7016
|
+
type: outputTypeRef,
|
|
7017
|
+
description,
|
|
7018
|
+
source: getSourceLocation(declaration),
|
|
7019
|
+
tags: [
|
|
7020
|
+
...parsedDoc?.tags ?? [],
|
|
7021
|
+
{ name: "schemaLibrary", text: schemaResult.adapter.id },
|
|
7022
|
+
{ name: "schemaSource", text: "static-ast" }
|
|
7023
|
+
],
|
|
7024
|
+
examples: parsedDoc?.examples
|
|
7025
|
+
};
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7592
7028
|
return {
|
|
7593
|
-
id:
|
|
7594
|
-
name:
|
|
7029
|
+
id: symbolName,
|
|
7030
|
+
name: symbolName,
|
|
7595
7031
|
...metadata,
|
|
7596
7032
|
kind: "variable",
|
|
7597
7033
|
deprecated: isSymbolDeprecated(symbol),
|
|
@@ -7677,9 +7113,9 @@ function createDefaultGenerationInfo(entryFile) {
|
|
|
7677
7113
|
}
|
|
7678
7114
|
function buildOpenPkgSpec(context, resolveExternalTypes, generation) {
|
|
7679
7115
|
const { baseDir, checker: typeChecker, sourceFile, program, entryFile } = context;
|
|
7680
|
-
const packageJsonPath =
|
|
7681
|
-
const packageJson =
|
|
7682
|
-
const generationInfo = generation ?? createDefaultGenerationInfo(
|
|
7116
|
+
const packageJsonPath = path11.join(baseDir, "package.json");
|
|
7117
|
+
const packageJson = fs10.existsSync(packageJsonPath) ? JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8")) : {};
|
|
7118
|
+
const generationInfo = generation ?? createDefaultGenerationInfo(path11.relative(baseDir, entryFile));
|
|
7683
7119
|
const spec = {
|
|
7684
7120
|
$schema: SCHEMA_URL,
|
|
7685
7121
|
openpkg: SCHEMA_VERSION,
|
|
@@ -7878,11 +7314,11 @@ function deriveImportPath(sourceFile, baseDir) {
|
|
|
7878
7314
|
if (!sourceFile) {
|
|
7879
7315
|
return;
|
|
7880
7316
|
}
|
|
7881
|
-
const
|
|
7882
|
-
if (!
|
|
7317
|
+
const relative5 = path11.relative(baseDir, sourceFile);
|
|
7318
|
+
if (!relative5 || relative5.startsWith("..")) {
|
|
7883
7319
|
return;
|
|
7884
7320
|
}
|
|
7885
|
-
const normalized =
|
|
7321
|
+
const normalized = relative5.replace(/\\/g, "/");
|
|
7886
7322
|
const withoutExt = stripExtensions(normalized);
|
|
7887
7323
|
if (!withoutExt) {
|
|
7888
7324
|
return;
|
|
@@ -7921,11 +7357,11 @@ function withExportName(entry, exportName) {
|
|
|
7921
7357
|
function findNearestPackageJson(startDir) {
|
|
7922
7358
|
let current = startDir;
|
|
7923
7359
|
while (true) {
|
|
7924
|
-
const candidate =
|
|
7925
|
-
if (
|
|
7360
|
+
const candidate = path12.join(current, "package.json");
|
|
7361
|
+
if (fs11.existsSync(candidate)) {
|
|
7926
7362
|
return candidate;
|
|
7927
7363
|
}
|
|
7928
|
-
const parent =
|
|
7364
|
+
const parent = path12.dirname(current);
|
|
7929
7365
|
if (parent === current) {
|
|
7930
7366
|
return;
|
|
7931
7367
|
}
|
|
@@ -7953,11 +7389,11 @@ function canResolveExternalModules(program, baseDir) {
|
|
|
7953
7389
|
function hasNodeModulesDirectoryFallback(startDir) {
|
|
7954
7390
|
let current = startDir;
|
|
7955
7391
|
while (true) {
|
|
7956
|
-
const candidate =
|
|
7957
|
-
if (
|
|
7392
|
+
const candidate = path12.join(current, "node_modules");
|
|
7393
|
+
if (fs11.existsSync(candidate)) {
|
|
7958
7394
|
return true;
|
|
7959
7395
|
}
|
|
7960
|
-
const parent =
|
|
7396
|
+
const parent = path12.dirname(current);
|
|
7961
7397
|
if (parent === current) {
|
|
7962
7398
|
break;
|
|
7963
7399
|
}
|
|
@@ -8002,8 +7438,8 @@ function hasExternalImports(sourceFile) {
|
|
|
8002
7438
|
if (ts.isImportDeclaration(node) && node.moduleSpecifier) {
|
|
8003
7439
|
const specifier = node.moduleSpecifier;
|
|
8004
7440
|
if (ts.isStringLiteral(specifier)) {
|
|
8005
|
-
const
|
|
8006
|
-
if (!
|
|
7441
|
+
const modulePath = specifier.text;
|
|
7442
|
+
if (!modulePath.startsWith(".") && !modulePath.startsWith("/")) {
|
|
8007
7443
|
found = true;
|
|
8008
7444
|
}
|
|
8009
7445
|
}
|
|
@@ -8113,11 +7549,27 @@ function runAnalysis(input, generationInput) {
|
|
|
8113
7549
|
|
|
8114
7550
|
// src/extractor.ts
|
|
8115
7551
|
async function extractPackageSpec(entryFile, packageDir, content, options) {
|
|
7552
|
+
const baseDir = packageDir ?? path13.dirname(entryFile);
|
|
7553
|
+
const schemaMode = options?.schemaExtraction ?? "static";
|
|
7554
|
+
let detectedSchemas;
|
|
7555
|
+
if (schemaMode === "runtime" || schemaMode === "hybrid") {
|
|
7556
|
+
const extraction = await extractStandardSchemasFromProject(entryFile, baseDir);
|
|
7557
|
+
if (extraction.schemas.size > 0) {
|
|
7558
|
+
detectedSchemas = new Map;
|
|
7559
|
+
for (const [name, result2] of extraction.schemas) {
|
|
7560
|
+
detectedSchemas.set(name, {
|
|
7561
|
+
schema: result2.outputSchema,
|
|
7562
|
+
vendor: result2.vendor
|
|
7563
|
+
});
|
|
7564
|
+
}
|
|
7565
|
+
}
|
|
7566
|
+
}
|
|
8116
7567
|
const result = runAnalysis({
|
|
8117
7568
|
entryFile,
|
|
8118
7569
|
packageDir,
|
|
8119
7570
|
content,
|
|
8120
|
-
options
|
|
7571
|
+
options,
|
|
7572
|
+
detectedSchemas
|
|
8121
7573
|
});
|
|
8122
7574
|
return result.spec;
|
|
8123
7575
|
}
|
|
@@ -8232,11 +7684,11 @@ async function fetchSpecFromGitHubWithPath(parsed, specPath = "openpkg.json") {
|
|
|
8232
7684
|
}
|
|
8233
7685
|
// src/install/index.ts
|
|
8234
7686
|
var DEFAULT_FALLBACK_ORDER = ["bun", "npm"];
|
|
8235
|
-
async function installDependencies(
|
|
7687
|
+
async function installDependencies(fs12, cwd, runCommand2, options = {}) {
|
|
8236
7688
|
const { timeout = 180000, fallbackOrder = DEFAULT_FALLBACK_ORDER, onProgress } = options;
|
|
8237
7689
|
const errors = [];
|
|
8238
7690
|
onProgress?.({ stage: "installing", message: "Detecting package manager..." });
|
|
8239
|
-
const pmInfo = await detectPackageManager(
|
|
7691
|
+
const pmInfo = await detectPackageManager(fs12);
|
|
8240
7692
|
if (pmInfo.lockfile) {
|
|
8241
7693
|
onProgress?.({
|
|
8242
7694
|
stage: "installing",
|
|
@@ -8311,10 +7763,10 @@ function getFallbackInstallCommand(pm) {
|
|
|
8311
7763
|
}
|
|
8312
7764
|
function createNodeCommandRunner() {
|
|
8313
7765
|
return async (cmd, args, options) => {
|
|
8314
|
-
const { execSync
|
|
7766
|
+
const { execSync } = await import("node:child_process");
|
|
8315
7767
|
const fullCmd = [cmd, ...args].join(" ");
|
|
8316
7768
|
try {
|
|
8317
|
-
const stdout =
|
|
7769
|
+
const stdout = execSync(fullCmd, {
|
|
8318
7770
|
cwd: options.cwd,
|
|
8319
7771
|
stdio: "pipe",
|
|
8320
7772
|
timeout: options.timeout ?? 180000
|
|
@@ -9141,7 +8593,7 @@ function getDocsImpactSummary(diff) {
|
|
|
9141
8593
|
}
|
|
9142
8594
|
// src/openpkg.ts
|
|
9143
8595
|
import * as fsSync from "node:fs";
|
|
9144
|
-
import * as
|
|
8596
|
+
import * as fs12 from "node:fs/promises";
|
|
9145
8597
|
import * as path14 from "node:path";
|
|
9146
8598
|
|
|
9147
8599
|
// src/filtering/apply-filters.ts
|
|
@@ -9357,7 +8809,7 @@ class DocCov {
|
|
|
9357
8809
|
}
|
|
9358
8810
|
async analyzeFile(filePath, analyzeOptions = {}) {
|
|
9359
8811
|
const resolvedPath = path14.resolve(filePath);
|
|
9360
|
-
const content = await
|
|
8812
|
+
const content = await fs12.readFile(resolvedPath, "utf-8");
|
|
9361
8813
|
const packageDir = resolvePackageDir(resolvedPath);
|
|
9362
8814
|
const spec = await extractPackageSpec(resolvedPath, packageDir, content, this.options);
|
|
9363
8815
|
return this.applySpecFilters(spec, analyzeOptions.filters).spec;
|
|
@@ -9405,7 +8857,7 @@ class DocCov {
|
|
|
9405
8857
|
};
|
|
9406
8858
|
}
|
|
9407
8859
|
}
|
|
9408
|
-
const content = await
|
|
8860
|
+
const content = await fs12.readFile(resolvedPath, "utf-8");
|
|
9409
8861
|
const detectedSchemas = await this.detectSchemas(resolvedPath, packageDir);
|
|
9410
8862
|
const analysis = runAnalysis({
|
|
9411
8863
|
entryFile: resolvedPath,
|
|
@@ -9621,113 +9073,13 @@ function resolvePackageDir(entryFile) {
|
|
|
9621
9073
|
currentDir = parentDir;
|
|
9622
9074
|
}
|
|
9623
9075
|
}
|
|
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
9076
|
// src/resolve/index.ts
|
|
9725
|
-
import * as
|
|
9726
|
-
async function resolveTarget(
|
|
9077
|
+
import * as path15 from "node:path";
|
|
9078
|
+
async function resolveTarget(fs13, options) {
|
|
9727
9079
|
let targetDir = options.cwd;
|
|
9728
9080
|
let packageInfo;
|
|
9729
9081
|
if (options.package) {
|
|
9730
|
-
const mono = await detectMonorepo(
|
|
9082
|
+
const mono = await detectMonorepo(fs13);
|
|
9731
9083
|
if (!mono.isMonorepo) {
|
|
9732
9084
|
throw new Error("Not a monorepo. Remove --package flag for single-package repos.");
|
|
9733
9085
|
}
|
|
@@ -9736,21 +9088,21 @@ async function resolveTarget(fs14, options) {
|
|
|
9736
9088
|
const available = mono.packages.map((p) => p.name).join(", ");
|
|
9737
9089
|
throw new Error(`Package "${options.package}" not found. Available: ${available}`);
|
|
9738
9090
|
}
|
|
9739
|
-
targetDir =
|
|
9091
|
+
targetDir = path15.join(options.cwd, pkg.path);
|
|
9740
9092
|
packageInfo = pkg;
|
|
9741
9093
|
}
|
|
9742
9094
|
let entryFile;
|
|
9743
9095
|
let entryPointInfo;
|
|
9744
9096
|
if (!options.entry) {
|
|
9745
|
-
entryPointInfo = await detectEntryPoint(
|
|
9746
|
-
entryFile =
|
|
9097
|
+
entryPointInfo = await detectEntryPoint(fs13, getRelativePath(options.cwd, targetDir));
|
|
9098
|
+
entryFile = path15.join(targetDir, entryPointInfo.path);
|
|
9747
9099
|
} else {
|
|
9748
|
-
const explicitPath =
|
|
9749
|
-
const isDirectory = await isDir(
|
|
9100
|
+
const explicitPath = path15.resolve(targetDir, options.entry);
|
|
9101
|
+
const isDirectory = await isDir(fs13, getRelativePath(options.cwd, explicitPath));
|
|
9750
9102
|
if (isDirectory) {
|
|
9751
9103
|
targetDir = explicitPath;
|
|
9752
|
-
entryPointInfo = await detectEntryPoint(
|
|
9753
|
-
entryFile =
|
|
9104
|
+
entryPointInfo = await detectEntryPoint(fs13, getRelativePath(options.cwd, explicitPath));
|
|
9105
|
+
entryFile = path15.join(explicitPath, entryPointInfo.path);
|
|
9754
9106
|
} else {
|
|
9755
9107
|
entryFile = explicitPath;
|
|
9756
9108
|
entryPointInfo = {
|
|
@@ -9770,20 +9122,20 @@ async function resolveTarget(fs14, options) {
|
|
|
9770
9122
|
function getRelativePath(base, target) {
|
|
9771
9123
|
if (base === target)
|
|
9772
9124
|
return ".";
|
|
9773
|
-
const rel =
|
|
9125
|
+
const rel = path15.relative(base, target);
|
|
9774
9126
|
return rel || ".";
|
|
9775
9127
|
}
|
|
9776
|
-
async function isDir(
|
|
9777
|
-
const hasPackageJson = await
|
|
9128
|
+
async function isDir(fs13, relativePath) {
|
|
9129
|
+
const hasPackageJson = await fs13.exists(path15.join(relativePath, "package.json"));
|
|
9778
9130
|
if (hasPackageJson)
|
|
9779
9131
|
return true;
|
|
9780
9132
|
const commonEntryFiles = ["index.ts", "index.tsx", "src/index.ts", "main.ts"];
|
|
9781
9133
|
for (const entry of commonEntryFiles) {
|
|
9782
|
-
if (await
|
|
9134
|
+
if (await fs13.exists(path15.join(relativePath, entry))) {
|
|
9783
9135
|
return true;
|
|
9784
9136
|
}
|
|
9785
9137
|
}
|
|
9786
|
-
return !
|
|
9138
|
+
return !path15.extname(relativePath);
|
|
9787
9139
|
}
|
|
9788
9140
|
// src/scan/github-context.ts
|
|
9789
9141
|
function parseGitHubUrl2(url) {
|
|
@@ -9794,8 +9146,8 @@ function parseGitHubUrl2(url) {
|
|
|
9794
9146
|
return null;
|
|
9795
9147
|
}
|
|
9796
9148
|
}
|
|
9797
|
-
async function fetchRawFile(owner, repo, ref,
|
|
9798
|
-
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${
|
|
9149
|
+
async function fetchRawFile(owner, repo, ref, path16, authToken) {
|
|
9150
|
+
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path16}`;
|
|
9799
9151
|
try {
|
|
9800
9152
|
const headers = {};
|
|
9801
9153
|
if (authToken) {
|
|
@@ -10026,7 +9378,6 @@ export {
|
|
|
10026
9378
|
validateExamples,
|
|
10027
9379
|
typecheckExamples,
|
|
10028
9380
|
typecheckExample,
|
|
10029
|
-
tryExtractStandardSchema,
|
|
10030
9381
|
shouldValidate,
|
|
10031
9382
|
serializeJSDoc,
|
|
10032
9383
|
saveSpecCache,
|
|
@@ -10037,6 +9388,7 @@ export {
|
|
|
10037
9388
|
runExamples,
|
|
10038
9389
|
runExample,
|
|
10039
9390
|
resolveTarget,
|
|
9391
|
+
resolveCompiledPath,
|
|
10040
9392
|
renderSparkline,
|
|
10041
9393
|
renderApiSurface,
|
|
10042
9394
|
readPackageJson,
|
|
@@ -10049,18 +9401,16 @@ export {
|
|
|
10049
9401
|
parseJSDocToPatch,
|
|
10050
9402
|
parseGitHubUrl,
|
|
10051
9403
|
parseExamplesFlag,
|
|
10052
|
-
parseCodeOwners,
|
|
10053
9404
|
parseAssertions,
|
|
10054
9405
|
mergeFixes,
|
|
10055
9406
|
mergeFilters,
|
|
10056
|
-
mergeConfig,
|
|
10057
9407
|
loadSpecCache,
|
|
10058
9408
|
loadSnapshotsForDays,
|
|
10059
9409
|
loadSnapshots,
|
|
10060
|
-
loadCodeOwners,
|
|
10061
9410
|
loadCachedReport,
|
|
10062
9411
|
listWorkspacePackages,
|
|
10063
9412
|
isStandardJSONSchema,
|
|
9413
|
+
isSchemaType,
|
|
10064
9414
|
isFixableDrift,
|
|
10065
9415
|
isExecutableLang,
|
|
10066
9416
|
isCachedReportValid,
|
|
@@ -10074,22 +9424,18 @@ export {
|
|
|
10074
9424
|
groupDriftsByCategory,
|
|
10075
9425
|
getUndocumentedExports,
|
|
10076
9426
|
getTrend,
|
|
9427
|
+
getSupportedLibraries,
|
|
10077
9428
|
getSpecCachePath,
|
|
10078
9429
|
getRunCommand,
|
|
10079
|
-
getRulesForKind,
|
|
10080
|
-
getRule,
|
|
10081
9430
|
getReportPath,
|
|
9431
|
+
getRegisteredAdapters,
|
|
10082
9432
|
getPrimaryBuildScript,
|
|
10083
9433
|
getInstallCommand,
|
|
10084
|
-
getFileBlame,
|
|
10085
9434
|
getExtendedTrend,
|
|
10086
9435
|
getDriftSummary,
|
|
10087
9436
|
getDocumentedExports,
|
|
10088
9437
|
getDocsImpactSummary,
|
|
10089
9438
|
getDiffReportPath,
|
|
10090
|
-
getDefaultConfig,
|
|
10091
|
-
getCoverageRules,
|
|
10092
|
-
getBlameForLines,
|
|
10093
9439
|
generateWeeklySummaries,
|
|
10094
9440
|
generateReportFromEnriched,
|
|
10095
9441
|
generateReport,
|
|
@@ -10100,22 +9446,21 @@ export {
|
|
|
10100
9446
|
formatDelta,
|
|
10101
9447
|
findRemovedReferences,
|
|
10102
9448
|
findPackageByName,
|
|
10103
|
-
findOwners,
|
|
10104
9449
|
findJSDocLocation,
|
|
10105
9450
|
findExportReferences,
|
|
10106
9451
|
findDeprecatedReferences,
|
|
9452
|
+
findAdapter,
|
|
10107
9453
|
fetchSpecFromGitHub,
|
|
10108
9454
|
fetchSpec,
|
|
10109
9455
|
fetchGitHubContext,
|
|
10110
|
-
|
|
9456
|
+
extractStandardSchemasFromProject,
|
|
9457
|
+
extractStandardSchemas,
|
|
10111
9458
|
extractSpecSummary,
|
|
9459
|
+
extractSchemaType,
|
|
9460
|
+
extractSchemaOutputType,
|
|
10112
9461
|
extractPackageSpec,
|
|
10113
9462
|
extractImports,
|
|
10114
9463
|
extractFunctionCalls,
|
|
10115
|
-
evaluateQuality,
|
|
10116
|
-
evaluatePolicy,
|
|
10117
|
-
evaluatePolicies,
|
|
10118
|
-
evaluateExportQuality,
|
|
10119
9464
|
ensureSpecCoverage,
|
|
10120
9465
|
enrichSpec,
|
|
10121
9466
|
diffSpecWithDocs,
|
|
@@ -10143,33 +9488,23 @@ export {
|
|
|
10143
9488
|
buildDisplayUrl,
|
|
10144
9489
|
buildCloneUrl,
|
|
10145
9490
|
blockReferencesExport,
|
|
10146
|
-
attributeOwners,
|
|
10147
9491
|
applyPatchToJSDoc,
|
|
10148
9492
|
applyEdits,
|
|
10149
|
-
analyzeSpecOwnership,
|
|
10150
|
-
analyzeSpecContributors,
|
|
10151
9493
|
analyzeProject,
|
|
10152
|
-
analyzeOwnership,
|
|
10153
9494
|
analyzeFile,
|
|
10154
9495
|
analyzeDocsImpact,
|
|
10155
|
-
analyzeContributors,
|
|
10156
9496
|
analyze,
|
|
10157
9497
|
VALIDATION_INFO,
|
|
10158
|
-
TSDOC_RULES,
|
|
10159
9498
|
SandboxFileSystem,
|
|
10160
|
-
STYLE_RULES,
|
|
10161
9499
|
SPEC_CACHE_FILE,
|
|
10162
9500
|
RETENTION_DAYS,
|
|
10163
9501
|
REPORT_VERSION,
|
|
10164
9502
|
REPORT_EXTENSIONS,
|
|
10165
9503
|
NodeFileSystem,
|
|
10166
|
-
KNOWN_VENDORS,
|
|
10167
9504
|
HISTORY_DIR,
|
|
10168
9505
|
DocCov,
|
|
10169
9506
|
DEFAULT_REPORT_PATH,
|
|
10170
9507
|
DEFAULT_REPORT_DIR,
|
|
10171
|
-
CORE_RULES,
|
|
10172
9508
|
CACHE_VERSION,
|
|
10173
|
-
BUILTIN_RULES,
|
|
10174
9509
|
ALL_VALIDATIONS
|
|
10175
9510
|
};
|