@aiready/doc-drift 0.13.4 → 0.13.6
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/.turbo/turbo-build.log +10 -10
- package/.turbo/turbo-test.log +6 -6
- package/dist/{chunk-NWJ6PSNT.mjs → chunk-6EHZXHM7.mjs} +26 -12
- package/dist/chunk-MAPV5YQR.mjs +142 -0
- package/dist/{chunk-5BGWZWHD.mjs → chunk-P74XAVQ3.mjs} +21 -6
- package/dist/cli.js +21 -9
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +39 -20
- package/dist/index.mjs +19 -12
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +1 -0
- package/src/__tests__/provider.test.ts +1 -0
- package/src/__tests__/scoring.test.ts +1 -0
- package/src/analyzer.ts +34 -20
- package/src/scoring.ts +29 -12
- package/src/types.ts +2 -0
- package/dist/chunk-5EFFNN6L.mjs +0 -145
- package/dist/chunk-BBGJNBVI.mjs +0 -189
- package/dist/chunk-CGSYYULO.mjs +0 -145
- package/dist/chunk-E3YCVHHH.mjs +0 -152
- package/dist/chunk-FMK4O4O7.mjs +0 -143
- package/dist/chunk-TSLAGWBV.mjs +0 -165
- package/dist/chunk-VLBPAYS3.mjs +0 -209
package/dist/chunk-VLBPAYS3.mjs
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
// src/analyzer.ts
|
|
9
|
-
import { calculateDocDrift } from "@aiready/core";
|
|
10
|
-
import { readdirSync, statSync, readFileSync } from "fs";
|
|
11
|
-
import { join, extname } from "path";
|
|
12
|
-
import { parse } from "@typescript-eslint/typescript-estree";
|
|
13
|
-
import { execSync } from "child_process";
|
|
14
|
-
var SRC_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
15
|
-
var DEFAULT_EXCLUDES = [
|
|
16
|
-
"node_modules",
|
|
17
|
-
"dist",
|
|
18
|
-
".git",
|
|
19
|
-
"coverage",
|
|
20
|
-
".turbo",
|
|
21
|
-
"build"
|
|
22
|
-
];
|
|
23
|
-
function collectFiles(dir, options, depth = 0) {
|
|
24
|
-
if (depth > 20) return [];
|
|
25
|
-
const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
|
|
26
|
-
let entries;
|
|
27
|
-
try {
|
|
28
|
-
entries = readdirSync(dir);
|
|
29
|
-
} catch {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
const files = [];
|
|
33
|
-
for (const entry of entries) {
|
|
34
|
-
if (excludes.some((ex) => entry === ex || entry.includes(ex))) continue;
|
|
35
|
-
const full = join(dir, entry);
|
|
36
|
-
let stat;
|
|
37
|
-
try {
|
|
38
|
-
stat = statSync(full);
|
|
39
|
-
} catch {
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (stat.isDirectory()) {
|
|
43
|
-
files.push(...collectFiles(full, options, depth + 1));
|
|
44
|
-
} else if (stat.isFile() && SRC_EXTENSIONS.has(extname(full))) {
|
|
45
|
-
if (!options.include || options.include.some((p) => full.includes(p))) {
|
|
46
|
-
files.push(full);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return files;
|
|
51
|
-
}
|
|
52
|
-
function getFileCommitTimestamps(file) {
|
|
53
|
-
const lineStamps = {};
|
|
54
|
-
try {
|
|
55
|
-
const output = execSync(`git blame -t "${file}"`, {
|
|
56
|
-
encoding: "utf-8",
|
|
57
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
58
|
-
});
|
|
59
|
-
const lines = output.split("\n");
|
|
60
|
-
for (const line of lines) {
|
|
61
|
-
if (!line) continue;
|
|
62
|
-
const match = line.match(/^\S+\s+\(.*?(\d{10,})\s+[-+]\d+\s+(\d+)\)/);
|
|
63
|
-
if (match) {
|
|
64
|
-
const ts = parseInt(match[1], 10);
|
|
65
|
-
const ln = parseInt(match[2], 10);
|
|
66
|
-
lineStamps[ln] = ts;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
} catch {
|
|
70
|
-
}
|
|
71
|
-
return lineStamps;
|
|
72
|
-
}
|
|
73
|
-
function getLineRangeLastModifiedCached(lineStamps, startLine, endLine) {
|
|
74
|
-
let latest = 0;
|
|
75
|
-
for (let i = startLine; i <= endLine; i++) {
|
|
76
|
-
if (lineStamps[i] && lineStamps[i] > latest) {
|
|
77
|
-
latest = lineStamps[i];
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return latest;
|
|
81
|
-
}
|
|
82
|
-
async function analyzeDocDrift(options) {
|
|
83
|
-
const rootDir = options.rootDir;
|
|
84
|
-
const files = collectFiles(rootDir, options);
|
|
85
|
-
const staleMonths = options.staleMonths ?? 6;
|
|
86
|
-
const staleSeconds = staleMonths * 30 * 24 * 60 * 60;
|
|
87
|
-
let uncommentedExports = 0;
|
|
88
|
-
let totalExports = 0;
|
|
89
|
-
let outdatedComments = 0;
|
|
90
|
-
let undocumentedComplexity = 0;
|
|
91
|
-
const issues = [];
|
|
92
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
93
|
-
let processed = 0;
|
|
94
|
-
for (const file of files) {
|
|
95
|
-
processed++;
|
|
96
|
-
options.onProgress?.(processed, files.length, `doc-drift: analyzing ${file.substring(rootDir.length + 1)}`);
|
|
97
|
-
let code;
|
|
98
|
-
try {
|
|
99
|
-
code = readFileSync(file, "utf-8");
|
|
100
|
-
} catch {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
let ast;
|
|
104
|
-
try {
|
|
105
|
-
ast = parse(code, {
|
|
106
|
-
jsx: file.endsWith(".tsx") || file.endsWith(".jsx"),
|
|
107
|
-
loc: true,
|
|
108
|
-
comment: true
|
|
109
|
-
});
|
|
110
|
-
} catch {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
const comments = ast.comments || [];
|
|
114
|
-
let fileLineStamps;
|
|
115
|
-
for (const node of ast.body) {
|
|
116
|
-
if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
|
|
117
|
-
const decl = node.declaration;
|
|
118
|
-
if (!decl) continue;
|
|
119
|
-
if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration" || decl.type === "VariableDeclaration") {
|
|
120
|
-
totalExports++;
|
|
121
|
-
const nodeLine = node.loc.start.line;
|
|
122
|
-
const jsdocs = comments.filter(
|
|
123
|
-
(c) => c.type === "Block" && c.value.startsWith("*") && c.loc.end.line === nodeLine - 1
|
|
124
|
-
);
|
|
125
|
-
if (jsdocs.length === 0) {
|
|
126
|
-
uncommentedExports++;
|
|
127
|
-
if (decl.type === "FunctionDeclaration" && decl.body?.loc) {
|
|
128
|
-
const lines = decl.body.loc.end.line - decl.body.loc.start.line;
|
|
129
|
-
if (lines > 20) undocumentedComplexity++;
|
|
130
|
-
}
|
|
131
|
-
} else {
|
|
132
|
-
const jsdoc = jsdocs[0];
|
|
133
|
-
const jsdocText = jsdoc.value;
|
|
134
|
-
if (decl.type === "FunctionDeclaration") {
|
|
135
|
-
const params = decl.params.map((p) => p.name || p.left && p.left.name).filter(Boolean);
|
|
136
|
-
const paramTags = Array.from(
|
|
137
|
-
jsdocText.matchAll(/@param\s+(?:\{[^}]+\}\s+)?([a-zA-Z0-9_]+)/g)
|
|
138
|
-
).map((m) => m[1]);
|
|
139
|
-
const missingParams = params.filter(
|
|
140
|
-
(p) => !paramTags.includes(p)
|
|
141
|
-
);
|
|
142
|
-
if (missingParams.length > 0) {
|
|
143
|
-
outdatedComments++;
|
|
144
|
-
issues.push({
|
|
145
|
-
type: "doc-drift",
|
|
146
|
-
severity: "major",
|
|
147
|
-
message: `JSDoc @param mismatch: function has parameters (${missingParams.join(", ")}) not documented in JSDoc.`,
|
|
148
|
-
location: { file, line: nodeLine }
|
|
149
|
-
});
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (!fileLineStamps) {
|
|
154
|
-
fileLineStamps = getFileCommitTimestamps(file);
|
|
155
|
-
}
|
|
156
|
-
const commentModified = getLineRangeLastModifiedCached(
|
|
157
|
-
fileLineStamps,
|
|
158
|
-
jsdoc.loc.start.line,
|
|
159
|
-
jsdoc.loc.end.line
|
|
160
|
-
);
|
|
161
|
-
const bodyModified = getLineRangeLastModifiedCached(
|
|
162
|
-
fileLineStamps,
|
|
163
|
-
decl.loc.start.line,
|
|
164
|
-
decl.loc.end.line
|
|
165
|
-
);
|
|
166
|
-
if (commentModified > 0 && bodyModified > 0) {
|
|
167
|
-
if (now - commentModified > staleSeconds && bodyModified - commentModified > staleSeconds / 2) {
|
|
168
|
-
outdatedComments++;
|
|
169
|
-
issues.push({
|
|
170
|
-
type: "doc-drift",
|
|
171
|
-
severity: "minor",
|
|
172
|
-
message: `JSDoc is significantly older than the function body implementation. Code may have drifted.`,
|
|
173
|
-
location: { file, line: jsdoc.loc.start.line }
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const riskResult = calculateDocDrift({
|
|
183
|
-
uncommentedExports,
|
|
184
|
-
totalExports,
|
|
185
|
-
outdatedComments,
|
|
186
|
-
undocumentedComplexity
|
|
187
|
-
});
|
|
188
|
-
return {
|
|
189
|
-
summary: {
|
|
190
|
-
filesAnalyzed: files.length,
|
|
191
|
-
functionsAnalyzed: totalExports,
|
|
192
|
-
score: riskResult.score,
|
|
193
|
-
rating: riskResult.rating
|
|
194
|
-
},
|
|
195
|
-
issues,
|
|
196
|
-
rawData: {
|
|
197
|
-
uncommentedExports,
|
|
198
|
-
totalExports,
|
|
199
|
-
outdatedComments,
|
|
200
|
-
undocumentedComplexity
|
|
201
|
-
},
|
|
202
|
-
recommendations: riskResult.recommendations
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export {
|
|
207
|
-
__require,
|
|
208
|
-
analyzeDocDrift
|
|
209
|
-
};
|