@eduardbar/drift 0.2.1 → 0.2.2

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.
@@ -3,9 +3,6 @@ name: Publish to npm
3
3
  on:
4
4
  release:
5
5
  types: [published]
6
- push:
7
- tags:
8
- - 'v*'
9
6
  workflow_dispatch:
10
7
  inputs:
11
8
  tag:
@@ -23,7 +20,7 @@ jobs:
23
20
  - name: Checkout
24
21
  uses: actions/checkout@v4
25
22
  with:
26
- ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/v{0}', inputs.tag) || '' }}
23
+ ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/v{0}', inputs.tag) || github.ref }}
27
24
 
28
25
  - name: Setup Node.js
29
26
  uses: actions/setup-node@v4
@@ -46,13 +43,26 @@ jobs:
46
43
  fi
47
44
  echo "Version check passed: $PKG_VERSION"
48
45
 
46
+ - name: Check if version already published
47
+ run: |
48
+ PKG_VERSION=$(node -p "require('./package.json').version")
49
+ if npm view @eduardbar/drift@$PKG_VERSION version 2>/dev/null; then
50
+ echo "Version $PKG_VERSION already published on npm. Skipping."
51
+ echo "skip_publish=true" >> $GITHUB_ENV
52
+ fi
53
+ env:
54
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
55
+
49
56
  - name: Install dependencies
57
+ if: env.skip_publish != 'true'
50
58
  run: npm ci
51
59
 
52
60
  - name: Build
61
+ if: env.skip_publish != 'true'
53
62
  run: npm run build
54
63
 
55
64
  - name: Publish to npm
65
+ if: env.skip_publish != 'true'
56
66
  run: npm publish --access public
57
67
  env:
58
68
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/dist/reporter.js CHANGED
@@ -23,52 +23,62 @@ export function buildReport(targetPath, files) {
23
23
  },
24
24
  };
25
25
  }
26
+ function formatHeader(report, grade) {
27
+ return [
28
+ `# drift report`,
29
+ ``,
30
+ `> Generated: ${new Date(report.scannedAt).toLocaleString()}`,
31
+ `> Path: \`${report.targetPath}\``,
32
+ ``,
33
+ `## Overall drift score: ${report.totalScore}/100 ${grade.badge}`,
34
+ ``,
35
+ `| | Count |`,
36
+ `|---|---|`,
37
+ `| Errors | ${report.summary.errors} |`,
38
+ `| Warnings | ${report.summary.warnings} |`,
39
+ `| Info | ${report.summary.infos} |`,
40
+ `| Files with issues | ${report.files.length} |`,
41
+ `| Total issues | ${report.totalIssues} |`,
42
+ ``,
43
+ ];
44
+ }
45
+ function formatByRule(byRule) {
46
+ if (Object.keys(byRule).length === 0)
47
+ return [];
48
+ const sorted = Object.entries(byRule).sort((a, b) => b[1] - a[1]);
49
+ return [
50
+ `## Issues by rule`,
51
+ ``,
52
+ ...sorted.map(([rule, count]) => `- \`${rule}\`: ${count}`),
53
+ ``,
54
+ ];
55
+ }
56
+ function formatFileSection(file) {
57
+ const lines = [
58
+ `### \`${file.path}\` — score ${file.score}/100`,
59
+ ``,
60
+ ];
61
+ for (const issue of file.issues) {
62
+ lines.push(`**${severityIcon(issue.severity)} [${issue.rule}]** Line ${issue.line}: ${issue.message}`);
63
+ lines.push(`\`\`\`typescript`);
64
+ lines.push(issue.snippet);
65
+ lines.push(`\`\`\``);
66
+ lines.push(``);
67
+ }
68
+ return lines;
69
+ }
26
70
  export function formatMarkdown(report) {
27
71
  const grade = scoreToGradeText(report.totalScore);
28
72
  const lines = [];
29
- lines.push(`# drift report`);
30
- lines.push(``);
31
- lines.push(`> Generated: ${new Date(report.scannedAt).toLocaleString()}`);
32
- lines.push(`> Path: \`${report.targetPath}\``);
33
- lines.push(``);
34
- lines.push(`## Overall drift score: ${report.totalScore}/100 ${grade.badge}`);
35
- lines.push(``);
36
- lines.push(`| | Count |`);
37
- lines.push(`|---|---|`);
38
- lines.push(`| Errors | ${report.summary.errors} |`);
39
- lines.push(`| Warnings | ${report.summary.warnings} |`);
40
- lines.push(`| Info | ${report.summary.infos} |`);
41
- lines.push(`| Files with issues | ${report.files.length} |`);
42
- lines.push(`| Total issues | ${report.totalIssues} |`);
43
- lines.push(``);
44
- if (Object.keys(report.summary.byRule).length > 0) {
45
- lines.push(`## Issues by rule`);
46
- lines.push(``);
47
- const sorted = Object.entries(report.summary.byRule).sort((a, b) => b[1] - a[1]);
48
- for (const [rule, count] of sorted) {
49
- lines.push(`- \`${rule}\`: ${count}`);
50
- }
51
- lines.push(``);
52
- }
73
+ lines.push(...formatHeader(report, grade));
74
+ lines.push(...formatByRule(report.summary.byRule));
53
75
  if (report.files.length === 0) {
54
- lines.push(`## No drift detected`);
55
- lines.push(``);
56
- lines.push(`No issues found. Clean codebase.`);
76
+ lines.push(`## No drift detected`, ``, `No issues found. Clean codebase.`);
57
77
  }
58
78
  else {
59
- lines.push(`## Files (sorted by drift score)`);
60
- lines.push(``);
79
+ lines.push(`## Files (sorted by drift score)`, ``);
61
80
  for (const file of report.files) {
62
- lines.push(`### \`${file.path}\` — score ${file.score}/100`);
63
- lines.push(``);
64
- for (const issue of file.issues) {
65
- const icon = severityIcon(issue.severity);
66
- lines.push(`**${icon} [${issue.rule}]** Line ${issue.line}: ${issue.message}`);
67
- lines.push(`\`\`\`typescript`);
68
- lines.push(issue.snippet);
69
- lines.push(`\`\`\``);
70
- lines.push(``);
71
- }
81
+ lines.push(...formatFileSection(file));
72
82
  }
73
83
  }
74
84
  return lines.join('\n');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eduardbar/drift",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Detect silent technical debt left by AI-generated code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/reporter.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { FileReport, DriftReport } from './types.js'
1
+ import type { FileReport, DriftReport, DriftIssue } from './types.js'
2
2
  import { scoreToGradeText, severityIcon } from './utils.js'
3
3
 
4
4
  export function buildReport(targetPath: string, files: FileReport[]): DriftReport {
@@ -30,54 +30,65 @@ export function buildReport(targetPath: string, files: FileReport[]): DriftRepor
30
30
  }
31
31
  }
32
32
 
33
- export function formatMarkdown(report: DriftReport): string {
34
- const grade = scoreToGradeText(report.totalScore)
35
- const lines: string[] = []
33
+ function formatHeader(report: DriftReport, grade: { badge: string }): string[] {
34
+ return [
35
+ `# drift report`,
36
+ ``,
37
+ `> Generated: ${new Date(report.scannedAt).toLocaleString()}`,
38
+ `> Path: \`${report.targetPath}\``,
39
+ ``,
40
+ `## Overall drift score: ${report.totalScore}/100 ${grade.badge}`,
41
+ ``,
42
+ `| | Count |`,
43
+ `|---|---|`,
44
+ `| Errors | ${report.summary.errors} |`,
45
+ `| Warnings | ${report.summary.warnings} |`,
46
+ `| Info | ${report.summary.infos} |`,
47
+ `| Files with issues | ${report.files.length} |`,
48
+ `| Total issues | ${report.totalIssues} |`,
49
+ ``,
50
+ ]
51
+ }
36
52
 
37
- lines.push(`# drift report`)
38
- lines.push(``)
39
- lines.push(`> Generated: ${new Date(report.scannedAt).toLocaleString()}`)
40
- lines.push(`> Path: \`${report.targetPath}\``)
41
- lines.push(``)
42
- lines.push(`## Overall drift score: ${report.totalScore}/100 ${grade.badge}`)
43
- lines.push(``)
44
- lines.push(`| | Count |`)
45
- lines.push(`|---|---|`)
46
- lines.push(`| Errors | ${report.summary.errors} |`)
47
- lines.push(`| Warnings | ${report.summary.warnings} |`)
48
- lines.push(`| Info | ${report.summary.infos} |`)
49
- lines.push(`| Files with issues | ${report.files.length} |`)
50
- lines.push(`| Total issues | ${report.totalIssues} |`)
51
- lines.push(``)
53
+ function formatByRule(byRule: Record<string, number>): string[] {
54
+ if (Object.keys(byRule).length === 0) return []
55
+ const sorted = Object.entries(byRule).sort((a, b) => b[1] - a[1])
56
+ return [
57
+ `## Issues by rule`,
58
+ ``,
59
+ ...sorted.map(([rule, count]) => `- \`${rule}\`: ${count}`),
60
+ ``,
61
+ ]
62
+ }
52
63
 
53
- if (Object.keys(report.summary.byRule).length > 0) {
54
- lines.push(`## Issues by rule`)
55
- lines.push(``)
56
- const sorted = Object.entries(report.summary.byRule).sort((a, b) => b[1] - a[1])
57
- for (const [rule, count] of sorted) {
58
- lines.push(`- \`${rule}\`: ${count}`)
59
- }
64
+ function formatFileSection(file: { path: string; score: number; issues: DriftIssue[] }): string[] {
65
+ const lines: string[] = [
66
+ `### \`${file.path}\` — score ${file.score}/100`,
67
+ ``,
68
+ ]
69
+ for (const issue of file.issues) {
70
+ lines.push(`**${severityIcon(issue.severity)} [${issue.rule}]** Line ${issue.line}: ${issue.message}`)
71
+ lines.push(`\`\`\`typescript`)
72
+ lines.push(issue.snippet)
73
+ lines.push(`\`\`\``)
60
74
  lines.push(``)
61
75
  }
76
+ return lines
77
+ }
78
+
79
+ export function formatMarkdown(report: DriftReport): string {
80
+ const grade = scoreToGradeText(report.totalScore)
81
+ const lines: string[] = []
82
+
83
+ lines.push(...formatHeader(report, grade))
84
+ lines.push(...formatByRule(report.summary.byRule))
62
85
 
63
86
  if (report.files.length === 0) {
64
- lines.push(`## No drift detected`)
65
- lines.push(``)
66
- lines.push(`No issues found. Clean codebase.`)
87
+ lines.push(`## No drift detected`, ``, `No issues found. Clean codebase.`)
67
88
  } else {
68
- lines.push(`## Files (sorted by drift score)`)
69
- lines.push(``)
89
+ lines.push(`## Files (sorted by drift score)`, ``)
70
90
  for (const file of report.files) {
71
- lines.push(`### \`${file.path}\` — score ${file.score}/100`)
72
- lines.push(``)
73
- for (const issue of file.issues) {
74
- const icon = severityIcon(issue.severity)
75
- lines.push(`**${icon} [${issue.rule}]** Line ${issue.line}: ${issue.message}`)
76
- lines.push(`\`\`\`typescript`)
77
- lines.push(issue.snippet)
78
- lines.push(`\`\`\``)
79
- lines.push(``)
80
- }
91
+ lines.push(...formatFileSection(file))
81
92
  }
82
93
  }
83
94