thuban 0.3.1 → 0.3.3

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.
@@ -17,6 +17,31 @@ const DependencyGraph = require('./dependency-graph');
17
17
  const DriftDetector = require('./drift-detector');
18
18
  const WidgetGenerator = require('./widget-generator');
19
19
 
20
+ /**
21
+ * Convert a JSDoc-style /** ... * / widget string to the appropriate comment
22
+ * style for the target file's language.
23
+ */
24
+ function formatWidgetComment(jsDocWidget, filePath) {
25
+ const ext = path.extname(filePath).toLowerCase();
26
+ if (ext === '.py') {
27
+ return jsDocWidget
28
+ .replace(/^\/\*\*\n/, '')
29
+ .replace(/\s*\*\/\s*$/, '')
30
+ .split('\n')
31
+ .map(l => l.replace(/^\s*\*\s?/, '# '))
32
+ .join('\n');
33
+ } else if (ext === '.go') {
34
+ return jsDocWidget
35
+ .replace(/^\/\*\*\n/, '')
36
+ .replace(/\s*\*\/\s*$/, '')
37
+ .split('\n')
38
+ .map(l => l.replace(/^\s*\*\s?/, '// '))
39
+ .join('\n');
40
+ }
41
+ // JS, TS, Java, C# — keep /** */ format
42
+ return jsDocWidget;
43
+ }
44
+
20
45
  class TechDebtAnalyzer {
21
46
  constructor(config = {}) {
22
47
  this.config = {
@@ -81,18 +106,14 @@ class TechDebtAnalyzer {
81
106
 
82
107
  // ─── 1. Security Debt ───────────────────────────────────
83
108
  const securityIssues = [];
84
- for (const [file, result] of Object.entries(scanResults)) {
85
- if (result.issues) {
86
- for (const issue of result.issues) {
87
- if (issue.id?.startsWith('SEC') || issue.category === 'security') {
88
- securityIssues.push({
89
- file: path.relative(this.config.rootPath, file),
90
- ...issue,
91
- fixable: issue.id === 'SEC001' || issue.id === 'SEC002', // Can extract to env vars
92
- fixAction: issue.id === 'SEC001' ? 'extract_to_env' : issue.id === 'SEC002' ? 'extract_to_env' : null,
93
- });
94
- }
95
- }
109
+ for (const issue of (scanResults.issues || [])) {
110
+ if (issue.id?.startsWith('SEC') || issue.category === 'security') {
111
+ securityIssues.push({
112
+ file: path.relative(this.config.rootPath, issue.file),
113
+ ...issue,
114
+ fixable: issue.id === 'SEC001' || issue.id === 'SEC002', // Can extract to env vars
115
+ fixAction: issue.id === 'SEC001' ? 'extract_to_env' : issue.id === 'SEC002' ? 'extract_to_env' : null,
116
+ });
96
117
  }
97
118
  }
98
119
  debt.scores.security = Math.max(0, 100 - (securityIssues.filter(i => i.severity === 'critical').length * 25) - (securityIssues.filter(i => i.severity === 'high').length * 10));
@@ -182,18 +203,14 @@ class TechDebtAnalyzer {
182
203
  }
183
204
  }
184
205
 
185
- for (const [file, result] of Object.entries(scanResults)) {
186
- if (result.issues) {
187
- for (const issue of result.issues) {
188
- if (issue.id?.startsWith('QUAL') || issue.category === 'quality' || issue.category === 'maintainability') {
189
- qualityIssues.push({
190
- file: path.relative(this.config.rootPath, file),
191
- ...issue,
192
- fixable: ['QUAL001', 'QUAL003', 'QUAL005'].includes(issue.id), // console.log removal, etc.
193
- fixAction: issue.id === 'QUAL001' ? 'remove_console_log' : null,
194
- });
195
- }
196
- }
206
+ for (const issue of (scanResults.issues || [])) {
207
+ if (issue.id?.startsWith('QUAL') || issue.category === 'quality' || issue.category === 'maintainability') {
208
+ qualityIssues.push({
209
+ file: path.relative(this.config.rootPath, issue.file),
210
+ ...issue,
211
+ fixable: ['QUAL001', 'QUAL003', 'QUAL005'].includes(issue.id), // console.log removal, etc.
212
+ fixAction: issue.id === 'QUAL001' ? 'remove_console_log' : null,
213
+ });
197
214
  }
198
215
  }
199
216
  debt.scores.codeQuality = Math.max(0, 100 - (qualityIssues.filter(i => i.severity === 'high').length * 5) - (qualityIssues.filter(i => i.severity === 'medium').length * 2));
@@ -319,7 +336,8 @@ class TechDebtAnalyzer {
319
336
  const content = fs.readFileSync(fullPath, 'utf-8');
320
337
  const widget = await this.widgetGen.generateWidget(fullPath, content);
321
338
  if (widget && widget.widgetString) {
322
- fs.writeFileSync(fullPath, widget.widgetString + '\n\n' + content, 'utf-8');
339
+ const commentBlock = formatWidgetComment(widget.widgetString, fullPath);
340
+ fs.writeFileSync(fullPath, commentBlock + '\n\n' + content, 'utf-8');
323
341
  fixes.push({ ...item, status: 'fixed' });
324
342
  }
325
343
  break;
@@ -333,7 +351,8 @@ class TechDebtAnalyzer {
333
351
  // Generate new widget with current state
334
352
  const widget = await this.widgetGen.generateWidget(fullPath, stripped);
335
353
  if (widget && widget.widgetString) {
336
- fs.writeFileSync(fullPath, widget.widgetString + '\n\n' + stripped, 'utf-8');
354
+ const commentBlock = formatWidgetComment(widget.widgetString, fullPath);
355
+ fs.writeFileSync(fullPath, commentBlock + '\n\n' + stripped, 'utf-8');
337
356
  fixes.push({ ...item, status: 'fixed' });
338
357
  }
339
358
  break;
@@ -364,8 +383,7 @@ class TechDebtAnalyzer {
364
383
  content = content.replace(/require\(['"]sys['"]\)/g, "require('util')");
365
384
  // util.isArray() → Array.isArray()
366
385
  content = content.replace(/util\.isArray\(/g, 'Array.isArray(');
367
- // util.isFunction() typeof x === 'function'
368
- content = content.replace(/util\.isFunction\(([^)]+)\)/g, "typeof $1 === 'function'");
386
+ // util.isFunction() is skipped regex cannot safely handle arrow function arguments
369
387
  // fs.exists() callback → fs.existsSync or fs.access
370
388
  content = content.replace(/fs\.exists\(([^,]+),\s*/g, 'fs.access($1, ');
371
389
  if (content !== before) {