@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.
@@ -1,145 +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 {
10
- scanFiles,
11
- calculateDocDrift,
12
- getFileCommitTimestamps,
13
- getLineRangeLastModifiedCached,
14
- Severity,
15
- IssueType
16
- } from "@aiready/core";
17
- import { readFileSync } from "fs";
18
- import { parse } from "@typescript-eslint/typescript-estree";
19
- async function analyzeDocDrift(options) {
20
- const files = await scanFiles(options);
21
- const issues = [];
22
- const staleMonths = options.staleMonths ?? 6;
23
- const staleSeconds = staleMonths * 30 * 24 * 60 * 60;
24
- let uncommentedExports = 0;
25
- let totalExports = 0;
26
- let outdatedComments = 0;
27
- let undocumentedComplexity = 0;
28
- const now = Math.floor(Date.now() / 1e3);
29
- let processed = 0;
30
- for (const file of files) {
31
- processed++;
32
- options.onProgress?.(processed, files.length, `doc-drift: analyzing files`);
33
- let code;
34
- try {
35
- code = readFileSync(file, "utf-8");
36
- } catch {
37
- continue;
38
- }
39
- let ast;
40
- try {
41
- ast = parse(code, {
42
- jsx: file.endsWith(".tsx") || file.endsWith(".jsx"),
43
- loc: true,
44
- comment: true
45
- });
46
- } catch {
47
- continue;
48
- }
49
- const comments = ast.comments || [];
50
- let fileLineStamps;
51
- for (const node of ast.body) {
52
- if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
53
- const decl = node.declaration;
54
- if (!decl) continue;
55
- if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration" || decl.type === "VariableDeclaration") {
56
- totalExports++;
57
- const nodeLine = node.loc.start.line;
58
- const jsdocs = comments.filter(
59
- (c) => c.type === "Block" && c.value.startsWith("*") && c.loc.end.line === nodeLine - 1
60
- );
61
- if (jsdocs.length === 0) {
62
- uncommentedExports++;
63
- if (decl.type === "FunctionDeclaration" && decl.body?.loc) {
64
- const lines = decl.body.loc.end.line - decl.body.loc.start.line;
65
- if (lines > 20) undocumentedComplexity++;
66
- }
67
- } else {
68
- const jsdoc = jsdocs[0];
69
- const jsdocText = jsdoc.value;
70
- if (decl.type === "FunctionDeclaration") {
71
- const params = decl.params.map((p) => p.name || p.left && p.left.name).filter(Boolean);
72
- const paramTags = Array.from(
73
- jsdocText.matchAll(/@param\s+(?:\{[^}]+\}\s+)?([a-zA-Z0-9_]+)/g)
74
- ).map((m) => m[1]);
75
- const missingParams = params.filter(
76
- (p) => !paramTags.includes(p)
77
- );
78
- if (missingParams.length > 0) {
79
- outdatedComments++;
80
- issues.push({
81
- type: IssueType.DocDrift,
82
- severity: Severity.Major,
83
- message: `JSDoc @param mismatch: function has parameters (${missingParams.join(", ")}) not documented in JSDoc.`,
84
- location: { file, line: nodeLine }
85
- });
86
- continue;
87
- }
88
- }
89
- if (!fileLineStamps) {
90
- fileLineStamps = getFileCommitTimestamps(file);
91
- }
92
- const commentModified = getLineRangeLastModifiedCached(
93
- fileLineStamps,
94
- jsdoc.loc.start.line,
95
- jsdoc.loc.end.line
96
- );
97
- const bodyModified = getLineRangeLastModifiedCached(
98
- fileLineStamps,
99
- decl.loc.start.line,
100
- decl.loc.end.line
101
- );
102
- if (commentModified > 0 && bodyModified > 0) {
103
- if (now - commentModified > staleSeconds && bodyModified - commentModified > staleSeconds / 2) {
104
- outdatedComments++;
105
- issues.push({
106
- type: IssueType.DocDrift,
107
- severity: Severity.Minor,
108
- message: `JSDoc is significantly older than the function body implementation. Code may have drifted.`,
109
- location: { file, line: jsdoc.loc.start.line }
110
- });
111
- }
112
- }
113
- }
114
- }
115
- }
116
- }
117
- }
118
- const riskResult = calculateDocDrift({
119
- uncommentedExports,
120
- totalExports,
121
- outdatedComments,
122
- undocumentedComplexity
123
- });
124
- return {
125
- summary: {
126
- filesAnalyzed: files.length,
127
- functionsAnalyzed: totalExports,
128
- score: riskResult.score,
129
- rating: riskResult.rating
130
- },
131
- issues,
132
- rawData: {
133
- uncommentedExports,
134
- totalExports,
135
- outdatedComments,
136
- undocumentedComplexity
137
- },
138
- recommendations: riskResult.recommendations
139
- };
140
- }
141
-
142
- export {
143
- __require,
144
- analyzeDocDrift
145
- };
@@ -1,152 +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 {
10
- scanFiles,
11
- calculateDocDrift,
12
- getFileCommitTimestamps,
13
- getLineRangeLastModifiedCached,
14
- Severity,
15
- IssueType,
16
- emitProgress
17
- } from "@aiready/core";
18
- import { readFileSync } from "fs";
19
- import { parse } from "@typescript-eslint/typescript-estree";
20
- async function analyzeDocDrift(options) {
21
- const files = await scanFiles(options);
22
- const issues = [];
23
- const staleMonths = options.staleMonths ?? 6;
24
- const staleSeconds = staleMonths * 30 * 24 * 60 * 60;
25
- let uncommentedExports = 0;
26
- let totalExports = 0;
27
- let outdatedComments = 0;
28
- let undocumentedComplexity = 0;
29
- const now = Math.floor(Date.now() / 1e3);
30
- let processed = 0;
31
- for (const file of files) {
32
- processed++;
33
- emitProgress(
34
- processed,
35
- files.length,
36
- "doc-drift",
37
- "analyzing files",
38
- options.onProgress
39
- );
40
- let code;
41
- try {
42
- code = readFileSync(file, "utf-8");
43
- } catch {
44
- continue;
45
- }
46
- let ast;
47
- try {
48
- ast = parse(code, {
49
- jsx: file.endsWith(".tsx") || file.endsWith(".jsx"),
50
- loc: true,
51
- comment: true
52
- });
53
- } catch {
54
- continue;
55
- }
56
- const comments = ast.comments || [];
57
- let fileLineStamps;
58
- for (const node of ast.body) {
59
- if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
60
- const decl = node.declaration;
61
- if (!decl) continue;
62
- if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration" || decl.type === "VariableDeclaration") {
63
- totalExports++;
64
- const nodeLine = node.loc.start.line;
65
- const jsdocs = comments.filter(
66
- (c) => c.type === "Block" && c.value.startsWith("*") && c.loc.end.line === nodeLine - 1
67
- );
68
- if (jsdocs.length === 0) {
69
- uncommentedExports++;
70
- if (decl.type === "FunctionDeclaration" && decl.body?.loc) {
71
- const lines = decl.body.loc.end.line - decl.body.loc.start.line;
72
- if (lines > 20) undocumentedComplexity++;
73
- }
74
- } else {
75
- const jsdoc = jsdocs[0];
76
- const jsdocText = jsdoc.value;
77
- if (decl.type === "FunctionDeclaration") {
78
- const params = decl.params.map((p) => p.name || p.left && p.left.name).filter(Boolean);
79
- const paramTags = Array.from(
80
- jsdocText.matchAll(/@param\s+(?:\{[^}]+\}\s+)?([a-zA-Z0-9_]+)/g)
81
- ).map((m) => m[1]);
82
- const missingParams = params.filter(
83
- (p) => !paramTags.includes(p)
84
- );
85
- if (missingParams.length > 0) {
86
- outdatedComments++;
87
- issues.push({
88
- type: IssueType.DocDrift,
89
- severity: Severity.Major,
90
- message: `JSDoc @param mismatch: function has parameters (${missingParams.join(", ")}) not documented in JSDoc.`,
91
- location: { file, line: nodeLine }
92
- });
93
- continue;
94
- }
95
- }
96
- if (!fileLineStamps) {
97
- fileLineStamps = getFileCommitTimestamps(file);
98
- }
99
- const commentModified = getLineRangeLastModifiedCached(
100
- fileLineStamps,
101
- jsdoc.loc.start.line,
102
- jsdoc.loc.end.line
103
- );
104
- const bodyModified = getLineRangeLastModifiedCached(
105
- fileLineStamps,
106
- decl.loc.start.line,
107
- decl.loc.end.line
108
- );
109
- if (commentModified > 0 && bodyModified > 0) {
110
- if (now - commentModified > staleSeconds && bodyModified - commentModified > staleSeconds / 2) {
111
- outdatedComments++;
112
- issues.push({
113
- type: IssueType.DocDrift,
114
- severity: Severity.Minor,
115
- message: `JSDoc is significantly older than the function body implementation. Code may have drifted.`,
116
- location: { file, line: jsdoc.loc.start.line }
117
- });
118
- }
119
- }
120
- }
121
- }
122
- }
123
- }
124
- }
125
- const riskResult = calculateDocDrift({
126
- uncommentedExports,
127
- totalExports,
128
- outdatedComments,
129
- undocumentedComplexity
130
- });
131
- return {
132
- summary: {
133
- filesAnalyzed: files.length,
134
- functionsAnalyzed: totalExports,
135
- score: riskResult.score,
136
- rating: riskResult.rating
137
- },
138
- issues,
139
- rawData: {
140
- uncommentedExports,
141
- totalExports,
142
- outdatedComments,
143
- undocumentedComplexity
144
- },
145
- recommendations: riskResult.recommendations
146
- };
147
- }
148
-
149
- export {
150
- __require,
151
- analyzeDocDrift
152
- };
@@ -1,143 +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 {
10
- scanFiles,
11
- calculateDocDrift,
12
- getFileCommitTimestamps,
13
- getLineRangeLastModifiedCached
14
- } from "@aiready/core";
15
- import { readFileSync } from "fs";
16
- import { parse } from "@typescript-eslint/typescript-estree";
17
- async function analyzeDocDrift(options) {
18
- const files = await scanFiles(options);
19
- const issues = [];
20
- const staleMonths = options.staleMonths ?? 6;
21
- const staleSeconds = staleMonths * 30 * 24 * 60 * 60;
22
- let uncommentedExports = 0;
23
- let totalExports = 0;
24
- let outdatedComments = 0;
25
- let undocumentedComplexity = 0;
26
- const now = Math.floor(Date.now() / 1e3);
27
- let processed = 0;
28
- for (const file of files) {
29
- processed++;
30
- options.onProgress?.(processed, files.length, `doc-drift: analyzing files`);
31
- let code;
32
- try {
33
- code = readFileSync(file, "utf-8");
34
- } catch {
35
- continue;
36
- }
37
- let ast;
38
- try {
39
- ast = parse(code, {
40
- jsx: file.endsWith(".tsx") || file.endsWith(".jsx"),
41
- loc: true,
42
- comment: true
43
- });
44
- } catch {
45
- continue;
46
- }
47
- const comments = ast.comments || [];
48
- let fileLineStamps;
49
- for (const node of ast.body) {
50
- if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
51
- const decl = node.declaration;
52
- if (!decl) continue;
53
- if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration" || decl.type === "VariableDeclaration") {
54
- totalExports++;
55
- const nodeLine = node.loc.start.line;
56
- const jsdocs = comments.filter(
57
- (c) => c.type === "Block" && c.value.startsWith("*") && c.loc.end.line === nodeLine - 1
58
- );
59
- if (jsdocs.length === 0) {
60
- uncommentedExports++;
61
- if (decl.type === "FunctionDeclaration" && decl.body?.loc) {
62
- const lines = decl.body.loc.end.line - decl.body.loc.start.line;
63
- if (lines > 20) undocumentedComplexity++;
64
- }
65
- } else {
66
- const jsdoc = jsdocs[0];
67
- const jsdocText = jsdoc.value;
68
- if (decl.type === "FunctionDeclaration") {
69
- const params = decl.params.map((p) => p.name || p.left && p.left.name).filter(Boolean);
70
- const paramTags = Array.from(
71
- jsdocText.matchAll(/@param\s+(?:\{[^}]+\}\s+)?([a-zA-Z0-9_]+)/g)
72
- ).map((m) => m[1]);
73
- const missingParams = params.filter(
74
- (p) => !paramTags.includes(p)
75
- );
76
- if (missingParams.length > 0) {
77
- outdatedComments++;
78
- issues.push({
79
- type: "doc-drift",
80
- severity: "major",
81
- message: `JSDoc @param mismatch: function has parameters (${missingParams.join(", ")}) not documented in JSDoc.`,
82
- location: { file, line: nodeLine }
83
- });
84
- continue;
85
- }
86
- }
87
- if (!fileLineStamps) {
88
- fileLineStamps = getFileCommitTimestamps(file);
89
- }
90
- const commentModified = getLineRangeLastModifiedCached(
91
- fileLineStamps,
92
- jsdoc.loc.start.line,
93
- jsdoc.loc.end.line
94
- );
95
- const bodyModified = getLineRangeLastModifiedCached(
96
- fileLineStamps,
97
- decl.loc.start.line,
98
- decl.loc.end.line
99
- );
100
- if (commentModified > 0 && bodyModified > 0) {
101
- if (now - commentModified > staleSeconds && bodyModified - commentModified > staleSeconds / 2) {
102
- outdatedComments++;
103
- issues.push({
104
- type: "doc-drift",
105
- severity: "minor",
106
- message: `JSDoc is significantly older than the function body implementation. Code may have drifted.`,
107
- location: { file, line: jsdoc.loc.start.line }
108
- });
109
- }
110
- }
111
- }
112
- }
113
- }
114
- }
115
- }
116
- const riskResult = calculateDocDrift({
117
- uncommentedExports,
118
- totalExports,
119
- outdatedComments,
120
- undocumentedComplexity
121
- });
122
- return {
123
- summary: {
124
- filesAnalyzed: files.length,
125
- functionsAnalyzed: totalExports,
126
- score: riskResult.score,
127
- rating: riskResult.rating
128
- },
129
- issues,
130
- rawData: {
131
- uncommentedExports,
132
- totalExports,
133
- outdatedComments,
134
- undocumentedComplexity
135
- },
136
- recommendations: riskResult.recommendations
137
- };
138
- }
139
-
140
- export {
141
- __require,
142
- analyzeDocDrift
143
- };
@@ -1,165 +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 = ["node_modules", "dist", ".git", "coverage", ".turbo", "build"];
16
- function collectFiles(dir, options, depth = 0) {
17
- if (depth > 20) return [];
18
- const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
19
- let entries;
20
- try {
21
- entries = readdirSync(dir);
22
- } catch {
23
- return [];
24
- }
25
- const files = [];
26
- for (const entry of entries) {
27
- if (excludes.some((ex) => entry === ex || entry.includes(ex))) continue;
28
- const full = join(dir, entry);
29
- let stat;
30
- try {
31
- stat = statSync(full);
32
- } catch {
33
- continue;
34
- }
35
- if (stat.isDirectory()) {
36
- files.push(...collectFiles(full, options, depth + 1));
37
- } else if (stat.isFile() && SRC_EXTENSIONS.has(extname(full))) {
38
- if (!options.include || options.include.some((p) => full.includes(p))) {
39
- files.push(full);
40
- }
41
- }
42
- }
43
- return files;
44
- }
45
- function getLineRangeLastModified(file, startLine, endLine) {
46
- try {
47
- const output = execSync(`git log -1 --format=%ct -L ${startLine},${endLine}:"${file}"`, {
48
- encoding: "utf-8",
49
- stdio: ["ignore", "pipe", "ignore"]
50
- });
51
- const match = output.trim().split("\n")[0];
52
- if (match && !isNaN(parseInt(match, 10))) {
53
- return parseInt(match, 10);
54
- }
55
- } catch {
56
- }
57
- return 0;
58
- }
59
- async function analyzeDocDrift(options) {
60
- const rootDir = options.rootDir;
61
- const files = collectFiles(rootDir, options);
62
- const staleMonths = options.staleMonths ?? 6;
63
- const staleSeconds = staleMonths * 30 * 24 * 60 * 60;
64
- let uncommentedExports = 0;
65
- let totalExports = 0;
66
- let outdatedComments = 0;
67
- let undocumentedComplexity = 0;
68
- const issues = [];
69
- const now = Math.floor(Date.now() / 1e3);
70
- for (const file of files) {
71
- let code;
72
- try {
73
- code = readFileSync(file, "utf-8");
74
- } catch {
75
- continue;
76
- }
77
- let ast;
78
- try {
79
- ast = parse(code, {
80
- jsx: file.endsWith(".tsx") || file.endsWith(".jsx"),
81
- loc: true,
82
- comment: true
83
- });
84
- } catch {
85
- continue;
86
- }
87
- const comments = ast.comments || [];
88
- for (const node of ast.body) {
89
- if (node.type === "ExportNamedDeclaration" || node.type === "ExportDefaultDeclaration") {
90
- const decl = node.declaration;
91
- if (!decl) continue;
92
- if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration" || decl.type === "VariableDeclaration") {
93
- totalExports++;
94
- const nodeLine = node.loc.start.line;
95
- const jsdocs = comments.filter((c) => c.type === "Block" && c.value.startsWith("*") && c.loc.end.line === nodeLine - 1);
96
- if (jsdocs.length === 0) {
97
- uncommentedExports++;
98
- if (decl.type === "FunctionDeclaration" && decl.body?.loc) {
99
- const lines = decl.body.loc.end.line - decl.body.loc.start.line;
100
- if (lines > 20) undocumentedComplexity++;
101
- }
102
- } else {
103
- const jsdoc = jsdocs[0];
104
- const jsdocText = jsdoc.value;
105
- if (decl.type === "FunctionDeclaration") {
106
- const params = decl.params.map((p) => p.name || p.left && p.left.name).filter(Boolean);
107
- const paramTags = Array.from(jsdocText.matchAll(/@param\s+(?:\{[^}]+\}\s+)?([a-zA-Z0-9_]+)/g)).map((m) => m[1]);
108
- const missingParams = params.filter((p) => !paramTags.includes(p));
109
- if (missingParams.length > 0) {
110
- outdatedComments++;
111
- issues.push({
112
- type: "doc-drift",
113
- severity: "major",
114
- message: `JSDoc @param mismatch: function has parameters (${missingParams.join(", ")}) not documented in JSDoc.`,
115
- location: { file, line: nodeLine }
116
- });
117
- continue;
118
- }
119
- }
120
- const commentModified = getLineRangeLastModified(file, jsdoc.loc.start.line, jsdoc.loc.end.line);
121
- const bodyModified = getLineRangeLastModified(file, decl.loc.start.line, decl.loc.end.line);
122
- if (commentModified > 0 && bodyModified > 0) {
123
- if (now - commentModified > staleSeconds && bodyModified - commentModified > staleSeconds / 2) {
124
- outdatedComments++;
125
- issues.push({
126
- type: "doc-drift",
127
- severity: "minor",
128
- message: `JSDoc is significantly older than the function body implementation. Code may have drifted.`,
129
- location: { file, line: jsdoc.loc.start.line }
130
- });
131
- }
132
- }
133
- }
134
- }
135
- }
136
- }
137
- }
138
- const riskResult = calculateDocDrift({
139
- uncommentedExports,
140
- totalExports,
141
- outdatedComments,
142
- undocumentedComplexity
143
- });
144
- return {
145
- summary: {
146
- filesAnalyzed: files.length,
147
- functionsAnalyzed: totalExports,
148
- score: riskResult.score,
149
- rating: riskResult.rating
150
- },
151
- issues,
152
- rawData: {
153
- uncommentedExports,
154
- totalExports,
155
- outdatedComments,
156
- undocumentedComplexity
157
- },
158
- recommendations: riskResult.recommendations
159
- };
160
- }
161
-
162
- export {
163
- __require,
164
- analyzeDocDrift
165
- };