@nielspeter/sonarlint-mcp-server 0.1.3 → 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.
Files changed (86) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +6 -12
  3. package/dist/errors.d.ts +22 -0
  4. package/dist/errors.d.ts.map +1 -0
  5. package/dist/errors.js +44 -0
  6. package/dist/errors.js.map +1 -0
  7. package/dist/index.js +115 -1330
  8. package/dist/index.js.map +1 -1
  9. package/dist/resources/session.d.ts +18 -0
  10. package/dist/resources/session.d.ts.map +1 -0
  11. package/dist/resources/session.js +84 -0
  12. package/dist/resources/session.js.map +1 -0
  13. package/dist/sloop-bridge.d.ts +0 -3
  14. package/dist/sloop-bridge.d.ts.map +1 -1
  15. package/dist/sloop-bridge.js +0 -19
  16. package/dist/sloop-bridge.js.map +1 -1
  17. package/dist/state.d.ts +19 -0
  18. package/dist/state.d.ts.map +1 -0
  19. package/dist/state.js +25 -0
  20. package/dist/state.js.map +1 -0
  21. package/dist/tools/analyze-content.d.ts +7 -0
  22. package/dist/tools/analyze-content.d.ts.map +1 -0
  23. package/dist/tools/analyze-content.js +78 -0
  24. package/dist/tools/analyze-content.js.map +1 -0
  25. package/dist/tools/analyze-file.d.ts +7 -0
  26. package/dist/tools/analyze-file.d.ts.map +1 -0
  27. package/dist/tools/analyze-file.js +66 -0
  28. package/dist/tools/analyze-file.js.map +1 -0
  29. package/dist/tools/analyze-files.d.ts +7 -0
  30. package/dist/tools/analyze-files.d.ts.map +1 -0
  31. package/dist/tools/analyze-files.js +106 -0
  32. package/dist/tools/analyze-files.js.map +1 -0
  33. package/dist/tools/analyze-project.d.ts +7 -0
  34. package/dist/tools/analyze-project.d.ts.map +1 -0
  35. package/dist/tools/analyze-project.js +109 -0
  36. package/dist/tools/analyze-project.js.map +1 -0
  37. package/dist/tools/apply-all-quick-fixes.d.ts +7 -0
  38. package/dist/tools/apply-all-quick-fixes.d.ts.map +1 -0
  39. package/dist/tools/apply-all-quick-fixes.js +166 -0
  40. package/dist/tools/apply-all-quick-fixes.js.map +1 -0
  41. package/dist/tools/apply-quick-fix.d.ts +7 -0
  42. package/dist/tools/apply-quick-fix.d.ts.map +1 -0
  43. package/dist/tools/apply-quick-fix.js +113 -0
  44. package/dist/tools/apply-quick-fix.js.map +1 -0
  45. package/dist/tools/health-check.d.ts +7 -0
  46. package/dist/tools/health-check.d.ts.map +1 -0
  47. package/dist/tools/health-check.js +113 -0
  48. package/dist/tools/health-check.js.map +1 -0
  49. package/dist/tools/list-active-rules.d.ts +7 -0
  50. package/dist/tools/list-active-rules.d.ts.map +1 -0
  51. package/dist/tools/list-active-rules.js +48 -0
  52. package/dist/tools/list-active-rules.js.map +1 -0
  53. package/dist/types.d.ts +62 -0
  54. package/dist/types.d.ts.map +1 -0
  55. package/dist/types.js +5 -0
  56. package/dist/types.js.map +1 -0
  57. package/dist/utils/filesystem.d.ts +8 -0
  58. package/dist/utils/filesystem.d.ts.map +1 -0
  59. package/dist/utils/filesystem.js +74 -0
  60. package/dist/utils/filesystem.js.map +1 -0
  61. package/dist/utils/formatting.d.ts +13 -0
  62. package/dist/utils/formatting.d.ts.map +1 -0
  63. package/dist/utils/formatting.js +94 -0
  64. package/dist/utils/formatting.js.map +1 -0
  65. package/dist/utils/language.d.ts +12 -0
  66. package/dist/utils/language.d.ts.map +1 -0
  67. package/dist/utils/language.js +44 -0
  68. package/dist/utils/language.js.map +1 -0
  69. package/dist/utils/scope.d.ts +8 -0
  70. package/dist/utils/scope.d.ts.map +1 -0
  71. package/dist/utils/scope.js +30 -0
  72. package/dist/utils/scope.js.map +1 -0
  73. package/dist/utils/sloop.d.ts +10 -0
  74. package/dist/utils/sloop.d.ts.map +1 -0
  75. package/dist/utils/sloop.js +39 -0
  76. package/dist/utils/sloop.js.map +1 -0
  77. package/dist/utils/transforms.d.ts +23 -0
  78. package/dist/utils/transforms.d.ts.map +1 -0
  79. package/dist/utils/transforms.js +64 -0
  80. package/dist/utils/transforms.js.map +1 -0
  81. package/package.json +10 -7
  82. package/scripts/setup-sonarlint.sh +115 -39
  83. package/dist/sonarlint-bridge.d.ts +0 -33
  84. package/dist/sonarlint-bridge.d.ts.map +0 -1
  85. package/dist/sonarlint-bridge.js +0 -91
  86. package/dist/sonarlint-bridge.js.map +0 -1
@@ -0,0 +1,39 @@
1
+ /**
2
+ * SLOOP bridge initialization and management
3
+ */
4
+ import { join, dirname } from "path";
5
+ import { existsSync } from "fs";
6
+ import { fileURLToPath } from "url";
7
+ import { SloopBridge } from "../sloop-bridge.js";
8
+ import { setSloopBridge, getSloopBridge } from "../state.js";
9
+ import { SloopError } from "../errors.js";
10
+ // Get package root directory (where sonarlint-backend is installed)
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ export const PACKAGE_ROOT = join(__dirname, '../..'); // Go up from dist/utils/ to package root
14
+ /**
15
+ * Ensure SLOOP bridge is initialized
16
+ */
17
+ export async function ensureSloopBridge() {
18
+ const existing = getSloopBridge();
19
+ if (existing) {
20
+ return existing;
21
+ }
22
+ console.error("[MCP] Initializing SLOOP bridge...");
23
+ // Check if plugins are downloaded
24
+ const pluginsDir = join(PACKAGE_ROOT, "sonarlint-backend", "plugins");
25
+ if (!existsSync(pluginsDir)) {
26
+ throw new SloopError("Backend not found", "SonarLint backend not installed. The postinstall script may have failed. Try reinstalling: npm install -g @nielspeter/sonarlint-mcp-server", false);
27
+ }
28
+ try {
29
+ const bridge = new SloopBridge(PACKAGE_ROOT);
30
+ await bridge.connect();
31
+ setSloopBridge(bridge);
32
+ console.error("[MCP] SLOOP bridge initialized successfully");
33
+ return bridge;
34
+ }
35
+ catch (error) {
36
+ throw new SloopError(`Failed to initialize SLOOP: ${error}`, "Failed to start SonarLint backend. Please check that Java is installed and try again.", true);
37
+ }
38
+ }
39
+ //# sourceMappingURL=sloop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sloop.js","sourceRoot":"","sources":["../../src/utils/sloop.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,oEAAoE;AACpE,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAE,yCAAyC;AAEhG;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAClC,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAEpD,kCAAkC;IAClC,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,mBAAmB,EAAE,SAAS,CAAC,CAAC;IACtE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,UAAU,CAClB,mBAAmB,EACnB,4IAA4I,EAC5I,KAAK,CACN,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,cAAc,CAAC,MAAM,CAAC,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC7D,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,UAAU,CAClB,+BAA+B,KAAK,EAAE,EACtC,uFAAuF,EACvF,IAAI,CACL,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Data transformation utilities
3
+ */
4
+ import type { AnalysisIssue } from "../types.js";
5
+ /**
6
+ * Transform raw SLOOP issues to simplified format
7
+ */
8
+ export declare function transformSloopIssues(rawIssues: any[]): AnalysisIssue[];
9
+ /**
10
+ * Create analysis summary from issues
11
+ */
12
+ export declare function createSummary(issues: AnalysisIssue[], rulesChecked: number): {
13
+ total: number;
14
+ bySeverity: {
15
+ blocker: number;
16
+ critical: number;
17
+ major: number;
18
+ minor: number;
19
+ info: number;
20
+ };
21
+ rulesChecked: number;
22
+ };
23
+ //# sourceMappingURL=transforms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transforms.d.ts","sourceRoot":"","sources":["../../src/utils/transforms.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,aAAa,EAAE,CAsCtE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,YAAY,EAAE,MAAM;;;;;;;;;;EAqB1E"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Data transformation utilities
3
+ */
4
+ /**
5
+ * Transform raw SLOOP issues to simplified format
6
+ */
7
+ export function transformSloopIssues(rawIssues) {
8
+ return rawIssues.map((issue) => {
9
+ // Debug: Log raw issue to understand structure
10
+ if (!issue.startLine && !issue.textRange?.startLine) {
11
+ console.error('[DEBUG] Issue missing line info:', JSON.stringify(issue, null, 2).substring(0, 500));
12
+ }
13
+ const transformed = {
14
+ line: issue.textRange?.startLine || issue.startLine || 1,
15
+ column: issue.textRange?.startLineOffset || issue.startColumn || 0,
16
+ endLine: issue.textRange?.endLine || issue.endLine || issue.textRange?.startLine || issue.startLine || 1,
17
+ endColumn: issue.textRange?.endLineOffset || issue.endColumn || issue.textRange?.startLineOffset || issue.startColumn || 0,
18
+ severity: issue.severity || 'MAJOR',
19
+ rule: issue.ruleKey || 'unknown',
20
+ ruleDescription: issue.ruleDescriptionContextKey || '',
21
+ message: issue.primaryMessage || issue.message || 'No description',
22
+ };
23
+ // Add quick fix if available
24
+ if (issue.quickFixes && issue.quickFixes.length > 0) {
25
+ const firstFix = issue.quickFixes[0];
26
+ const fileEdits = firstFix.inputFileEdits || firstFix.fileEdits || [];
27
+ transformed.quickFix = {
28
+ description: firstFix.message || 'Apply fix',
29
+ edits: fileEdits.flatMap((fileEdit) => (fileEdit.textEdits || []).map((edit) => ({
30
+ startLine: edit.range?.startLine || 1,
31
+ startColumn: edit.range?.startLineOffset || 0,
32
+ endLine: edit.range?.endLine || 1,
33
+ endColumn: edit.range?.endLineOffset || 0,
34
+ newText: edit.newText || '',
35
+ }))),
36
+ };
37
+ }
38
+ return transformed;
39
+ });
40
+ }
41
+ /**
42
+ * Create analysis summary from issues
43
+ */
44
+ export function createSummary(issues, rulesChecked) {
45
+ const summary = {
46
+ total: issues.length,
47
+ bySeverity: {
48
+ blocker: 0,
49
+ critical: 0,
50
+ major: 0,
51
+ minor: 0,
52
+ info: 0,
53
+ },
54
+ rulesChecked,
55
+ };
56
+ for (const issue of issues) {
57
+ const severity = issue.severity.toLowerCase();
58
+ if (severity in summary.bySeverity) {
59
+ summary.bySeverity[severity]++;
60
+ }
61
+ }
62
+ return summary;
63
+ }
64
+ //# sourceMappingURL=transforms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transforms.js","sourceRoot":"","sources":["../../src/utils/transforms.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@nielspeter/sonarlint-mcp-server",
3
- "version": "0.1.3",
3
+ "version": "0.2.2",
4
4
  "description": "MCP server providing SonarLint code analysis for Claude Desktop and other MCP clients",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "engines": {
8
- "node": ">=20.0.0"
8
+ "node": ">=22.7.5"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
@@ -22,6 +22,9 @@
22
22
  "test:ui": "vitest --ui",
23
23
  "test:run": "vitest run",
24
24
  "test:coverage": "vitest run --coverage",
25
+ "test:e2e": "vitest run tests/e2e",
26
+ "test:unit": "vitest run tests/unit",
27
+ "test:integration": "vitest run tests/integration",
25
28
  "inspect": "mcp-inspector dist/index.js",
26
29
  "release": "standard-version",
27
30
  "release:minor": "standard-version --release-as minor",
@@ -29,7 +32,8 @@
29
32
  "release:patch": "standard-version --release-as patch"
30
33
  },
31
34
  "bin": {
32
- "sonarlint-mcp": "./dist/index.js"
35
+ "sonarlint-mcp": "./dist/index.js",
36
+ "sonarlint-mcp-server": "./dist/index.js"
33
37
  },
34
38
  "files": [
35
39
  "dist",
@@ -53,13 +57,12 @@
53
57
  },
54
58
  "homepage": "https://github.com/nielspeter/sonarlint-mcp-server#readme",
55
59
  "dependencies": {
56
- "@modelcontextprotocol/sdk": "^0.5.0",
57
- "ws": "^8.16.0"
60
+ "@modelcontextprotocol/sdk": "^1.21.1",
61
+ "zod": "^3.25.76"
58
62
  },
59
63
  "devDependencies": {
60
- "@modelcontextprotocol/inspector": "^0.15.0",
64
+ "@modelcontextprotocol/inspector": "^0.16.6",
61
65
  "@types/node": "^20.10.0",
62
- "@types/ws": "^8.5.10",
63
66
  "@vitest/ui": "^4.0.8",
64
67
  "standard-version": "^9.5.0",
65
68
  "tsx": "^4.6.0",
@@ -8,19 +8,21 @@ VERSION="10.32.0.82302"
8
8
  BACKEND_DIR="./sonarlint-backend"
9
9
  MAVEN_BASE="https://repo1.maven.org/maven2/org/sonarsource/sonarlint/core/sonarlint-backend-cli/${VERSION}"
10
10
 
11
- echo "🚀 Setting up SonarLint Backend v${VERSION}..."
11
+ JS_PLUGIN_VERSION="11.3.0.34350"
12
+ PYTHON_PLUGIN_VERSION="5.16.0.29940"
13
+
14
+ echo "Setting up SonarLint Backend v${VERSION}..."
12
15
  echo ""
13
16
 
14
- # Check if already installed
15
17
  if [ -d "$BACKEND_DIR/lib" ] && [ -d "$BACKEND_DIR/jre" ] && [ -d "$BACKEND_DIR/plugins" ]; then
16
- echo "SonarLint backend already installed"
18
+ echo "SonarLint backend already installed"
17
19
  echo " To reinstall, run: rm -rf $BACKEND_DIR && npm run setup"
18
20
  exit 0
19
21
  fi
20
22
 
21
- # Detect OS and architecture
22
23
  OS=$(uname -s)
23
24
  ARCH=$(uname -m)
25
+ IS_WINDOWS=false
24
26
 
25
27
  case "$OS" in
26
28
  Darwin)
@@ -44,9 +46,10 @@ case "$OS" in
44
46
  MINGW*|MSYS*|CYGWIN*)
45
47
  DIST_FILE="sonarlint-backend-cli-${VERSION}-windows_x64.zip"
46
48
  PLATFORM="Windows x64"
49
+ IS_WINDOWS=true
47
50
  ;;
48
51
  *)
49
- echo " Unsupported OS: $OS"
52
+ echo "ERROR: Unsupported OS: $OS"
50
53
  echo " Supported platforms: macOS (ARM64/x64), Linux (ARM64/x64), Windows (x64)"
51
54
  exit 1
52
55
  ;;
@@ -55,60 +58,131 @@ esac
55
58
  echo "Platform detected: $PLATFORM"
56
59
  echo ""
57
60
 
58
- # Clean existing installation
59
61
  if [ -d "$BACKEND_DIR" ]; then
60
- echo "🧹 Cleaning existing installation..."
62
+ echo "Cleaning existing installation..."
61
63
  rm -rf "$BACKEND_DIR"
62
64
  fi
63
65
 
64
66
  mkdir -p "$BACKEND_DIR"
65
67
 
66
68
  echo ""
67
- echo "📦 Step 1/2: Downloading SonarLint Backend..."
69
+ echo "Step 1/3: Downloading SonarLint Backend..."
68
70
  echo " URL: $MAVEN_BASE/$DIST_FILE"
69
71
  echo ""
70
72
 
71
- # Download the platform-specific distribution
72
- curl -L --progress-bar -o "/tmp/${DIST_FILE}" "$MAVEN_BASE/$DIST_FILE"
73
+ TEMP_DIR=$(mktemp -d)
74
+ EXTRACT_DIR="$TEMP_DIR/extract"
75
+ mkdir -p "$EXTRACT_DIR"
76
+
77
+ cleanup() {
78
+ rm -rf "$TEMP_DIR"
79
+ }
80
+ trap cleanup EXIT
81
+
82
+ curl -L --progress-bar -o "$TEMP_DIR/${DIST_FILE}" "$MAVEN_BASE/$DIST_FILE"
73
83
 
74
84
  echo ""
75
- echo "📂 Extracting backend..."
85
+ echo "Extracting backend..."
76
86
 
77
- # Extract (auto-detects tar.gz or zip)
78
87
  if [[ "$DIST_FILE" == *.zip ]]; then
79
- unzip -q "/tmp/${DIST_FILE}" -d "/tmp/sonarlint-extract"
88
+ if ! command -v unzip &> /dev/null; then
89
+ echo "ERROR: 'unzip' is required but not installed"
90
+ exit 1
91
+ fi
92
+ unzip -q "$TEMP_DIR/${DIST_FILE}" -d "$EXTRACT_DIR"
93
+ else
94
+ tar -xzf "$TEMP_DIR/${DIST_FILE}" -C "$EXTRACT_DIR"
95
+ fi
96
+
97
+ SOURCE_DIR=""
98
+ if [ -d "$EXTRACT_DIR/jre" ] && [ -d "$EXTRACT_DIR/lib" ]; then
99
+ SOURCE_DIR="$EXTRACT_DIR"
80
100
  else
81
- mkdir -p "/tmp/sonarlint-extract"
82
- tar -xzf "/tmp/${DIST_FILE}" -C "/tmp/sonarlint-extract"
101
+ SUBDIR_COUNT=$(find "$EXTRACT_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l | tr -d ' ')
102
+ if [ "$SUBDIR_COUNT" -eq 1 ]; then
103
+ SINGLE_DIR=$(find "$EXTRACT_DIR" -mindepth 1 -maxdepth 1 -type d)
104
+ if [ -d "$SINGLE_DIR/jre" ] && [ -d "$SINGLE_DIR/lib" ]; then
105
+ SOURCE_DIR="$SINGLE_DIR"
106
+ fi
107
+ fi
108
+ fi
109
+
110
+ if [ -z "$SOURCE_DIR" ]; then
111
+ echo "ERROR: Unexpected archive layout. Expected jre/ and lib/ directories."
112
+ echo "Archive contents:"
113
+ ls -la "$EXTRACT_DIR"
114
+ exit 1
83
115
  fi
84
116
 
85
- # Move extracted contents to backend directory
86
- mv /tmp/sonarlint-extract/*/* "$BACKEND_DIR/"
87
- rm -rf "/tmp/sonarlint-extract"
88
- rm "/tmp/${DIST_FILE}"
117
+ mv "$SOURCE_DIR"/* "$BACKEND_DIR/"
89
118
 
90
119
  echo ""
91
- echo "🔌 Step 2/2: Downloading Language Plugins..."
120
+ echo "Step 2/3: Validating backend installation..."
121
+
122
+ JAR_COUNT=$(find "$BACKEND_DIR/lib" -name "*.jar" 2>/dev/null | wc -l | tr -d ' ')
123
+ if [ "$JAR_COUNT" -lt 10 ]; then
124
+ echo "ERROR: Backend lib/ directory missing or incomplete (found $JAR_COUNT JARs, expected 50+)"
125
+ exit 1
126
+ fi
127
+ echo " lib/: $JAR_COUNT JARs"
128
+
129
+ JAVA_PATH=""
130
+ if [ "$IS_WINDOWS" = true ]; then
131
+ if [ -f "$BACKEND_DIR/jre/bin/java.exe" ]; then
132
+ JAVA_PATH="$BACKEND_DIR/jre/bin/java.exe"
133
+ elif [ -f "$BACKEND_DIR/jre/bin/java" ]; then
134
+ JAVA_PATH="$BACKEND_DIR/jre/bin/java"
135
+ fi
136
+ else
137
+ if [ -f "$BACKEND_DIR/jre/bin/java" ]; then
138
+ JAVA_PATH="$BACKEND_DIR/jre/bin/java"
139
+ fi
140
+ fi
141
+
142
+ if [ -z "$JAVA_PATH" ]; then
143
+ echo "ERROR: JRE not found at $BACKEND_DIR/jre/bin/java"
144
+ exit 1
145
+ fi
146
+ echo " jre/: $(basename "$JAVA_PATH") found"
147
+
148
+ echo ""
149
+ echo "Step 3/3: Downloading Language Plugins..."
92
150
  echo ""
93
151
 
94
152
  PLUGINS_DIR="$BACKEND_DIR/plugins"
95
153
  mkdir -p "$PLUGINS_DIR"
96
154
 
97
- # JavaScript/TypeScript Plugin (WebStorm 2025.2 compatible)
98
- echo "- JavaScript/TypeScript 11.3.0..."
99
- curl -L --progress-bar -o "$PLUGINS_DIR/sonar-javascript-plugin-11.3.0.34350.jar" \
100
- "https://repo1.maven.org/maven2/org/sonarsource/javascript/sonar-javascript-plugin/11.3.0.34350/sonar-javascript-plugin-11.3.0.34350.jar"
155
+ JS_PLUGIN="sonar-javascript-plugin-${JS_PLUGIN_VERSION}.jar"
156
+ echo "- JavaScript/TypeScript ${JS_PLUGIN_VERSION%.*}..."
157
+ curl -L --progress-bar -o "$PLUGINS_DIR/$JS_PLUGIN" \
158
+ "https://repo1.maven.org/maven2/org/sonarsource/javascript/sonar-javascript-plugin/${JS_PLUGIN_VERSION}/$JS_PLUGIN"
101
159
 
102
- # Python Plugin
103
- echo "- Python 5.9.0..."
104
- curl -L --progress-bar -o "$PLUGINS_DIR/sonar-python-plugin-5.9.0.23806.jar" \
105
- "https://repo1.maven.org/maven2/org/sonarsource/python/sonar-python-plugin/5.9.0.23806/sonar-python-plugin-5.9.0.23806.jar"
160
+ PYTHON_PLUGIN="sonar-python-plugin-${PYTHON_PLUGIN_VERSION}.jar"
161
+ echo "- Python ${PYTHON_PLUGIN_VERSION%.*}..."
162
+ curl -L --progress-bar -o "$PLUGINS_DIR/$PYTHON_PLUGIN" \
163
+ "https://repo1.maven.org/maven2/org/sonarsource/python/sonar-python-plugin/${PYTHON_PLUGIN_VERSION}/$PYTHON_PLUGIN"
106
164
 
107
- # Extract eslint-bridge from JavaScript plugin
108
165
  echo ""
109
- echo "📤 Extracting eslint-bridge..."
166
+ echo "Validating plugins..."
167
+
168
+ JS_SIZE=$(stat -f%z "$PLUGINS_DIR/$JS_PLUGIN" 2>/dev/null || stat -c%s "$PLUGINS_DIR/$JS_PLUGIN" 2>/dev/null || echo "0")
169
+ if [ "$JS_SIZE" -lt 1000000 ]; then
170
+ echo "ERROR: JavaScript plugin download failed or incomplete (size: $JS_SIZE bytes)"
171
+ exit 1
172
+ fi
173
+ echo " $JS_PLUGIN: $(( JS_SIZE / 1024 / 1024 ))MB"
174
+
175
+ PYTHON_SIZE=$(stat -f%z "$PLUGINS_DIR/$PYTHON_PLUGIN" 2>/dev/null || stat -c%s "$PLUGINS_DIR/$PYTHON_PLUGIN" 2>/dev/null || echo "0")
176
+ if [ "$PYTHON_SIZE" -lt 1000000 ]; then
177
+ echo "ERROR: Python plugin download failed or incomplete (size: $PYTHON_SIZE bytes)"
178
+ exit 1
179
+ fi
180
+ echo " $PYTHON_PLUGIN: $(( PYTHON_SIZE / 1024 / 1024 ))MB"
181
+
182
+ echo ""
183
+ echo "Extracting eslint-bridge..."
110
184
  cd "$PLUGINS_DIR"
111
- unzip -q sonar-javascript-plugin-11.3.0.34350.jar sonarjs-1.0.0.tgz 2>/dev/null || true
185
+ unzip -q "$JS_PLUGIN" sonarjs-1.0.0.tgz 2>/dev/null || true
112
186
  if [ -f sonarjs-1.0.0.tgz ]; then
113
187
  mkdir -p eslint-bridge
114
188
  tar -xzf sonarjs-1.0.0.tgz -C eslint-bridge
@@ -116,23 +190,25 @@ if [ -f sonarjs-1.0.0.tgz ]; then
116
190
  fi
117
191
  cd - > /dev/null
118
192
 
193
+ if [ ! -f "$PLUGINS_DIR/eslint-bridge/package/bin/server.cjs" ]; then
194
+ echo "WARNING: eslint-bridge extraction may have failed"
195
+ echo " Expected: $PLUGINS_DIR/eslint-bridge/package/bin/server.cjs"
196
+ fi
197
+
119
198
  echo ""
120
- echo "Setup Complete!"
199
+ echo "Setup Complete!"
121
200
  echo ""
122
- echo "📊 Installation Summary:"
201
+ echo "Installation Summary:"
123
202
  echo " Platform: $PLATFORM"
124
- echo " Backend: $BACKEND_DIR/lib/"
125
- echo " JRE: $BACKEND_DIR/jre/"
203
+ echo " Backend: $BACKEND_DIR/lib/ ($JAR_COUNT JARs)"
204
+ echo " JRE: $JAVA_PATH"
126
205
  echo " Plugins: $PLUGINS_DIR/"
127
206
  echo ""
128
- echo " Installed components:"
129
- ls -1 "$BACKEND_DIR" | sed 's/^/ - /'
130
- echo ""
131
207
  echo " Downloaded plugins:"
132
208
  ls -1 "$PLUGINS_DIR" | grep ".jar$" | sed 's/^/ - /'
133
209
  echo ""
134
210
  echo " Total size: $(du -sh "$BACKEND_DIR" | cut -f1)"
135
211
  echo ""
136
- echo "Next steps:"
212
+ echo "Next steps:"
137
213
  echo " npm run build"
138
214
  echo " npm start"
@@ -1,33 +0,0 @@
1
- import { EventEmitter } from "events";
2
- interface SonarLintStatus {
3
- ideName: string;
4
- description: string;
5
- needsToken: boolean;
6
- capabilities: {
7
- canOpenFixSuggestion: boolean;
8
- };
9
- }
10
- export declare class SonarLintBridge extends EventEmitter {
11
- private readonly host;
12
- private readonly port;
13
- private readonly timeout;
14
- private connected;
15
- private readonly baseUrl;
16
- private readonly sonarQubeServerUrl;
17
- constructor(host: string, port: number, timeout?: number, sonarQubeServerUrl?: string);
18
- connect(): Promise<void>;
19
- private fetchAPI;
20
- getStatus(): Promise<SonarLintStatus>;
21
- isConnected(): boolean;
22
- disconnect(): Promise<void>;
23
- getCurrentIssues(filters?: any): Promise<any[]>;
24
- requestAnalysis(filePaths: string[]): Promise<string>;
25
- waitForAnalysisResults(analysisId: string): Promise<any>;
26
- getFileIssues(filePath: string, includeFixed?: boolean): Promise<any[]>;
27
- getProjectMetrics(options?: any): Promise<any>;
28
- toggleAutoAnalysis(enabled: boolean): Promise<void>;
29
- getRuleDetails(ruleKey: string): Promise<any>;
30
- getSecurityHotspots(filters?: any): Promise<any[]>;
31
- }
32
- export {};
33
- //# sourceMappingURL=sonarlint-bridge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sonarlint-bridge.d.ts","sourceRoot":"","sources":["../src/sonarlint-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE;QACZ,oBAAoB,EAAE,OAAO,CAAC;KAC/B,CAAC;CACH;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAM7C,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;gBAGzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,SAAQ,EAChC,kBAAkB,CAAC,EAAE,MAAM;IAQvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAchB,QAAQ;IA0BhB,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC;IAI3C,WAAW,IAAI,OAAO;IAIhB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,gBAAgB,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAI/C,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAIrD,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIxD,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,UAAQ,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAIrE,iBAAiB,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAI9C,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAI7C,mBAAmB,CAAC,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;CAGzD"}
@@ -1,91 +0,0 @@
1
- import { EventEmitter } from "events";
2
- export class SonarLintBridge extends EventEmitter {
3
- host;
4
- port;
5
- timeout;
6
- connected = false;
7
- baseUrl;
8
- sonarQubeServerUrl;
9
- constructor(host, port, timeout = 30000, sonarQubeServerUrl) {
10
- super();
11
- this.host = host;
12
- this.port = port;
13
- this.timeout = timeout;
14
- this.baseUrl = `http://${host}:${port}`;
15
- // Default to common SonarQube server URL
16
- this.sonarQubeServerUrl = sonarQubeServerUrl || "http://localhost:9000";
17
- }
18
- async connect() {
19
- if (this.connected)
20
- return;
21
- try {
22
- // Test connection by calling the status endpoint
23
- const status = await this.getStatus();
24
- this.connected = true;
25
- console.log(`Connected to ${status.ideName}`);
26
- }
27
- catch (error) {
28
- this.connected = false;
29
- throw new Error(`Failed to connect to SonarLint IDE at ${this.baseUrl}: ${error}`);
30
- }
31
- }
32
- async fetchAPI(endpoint, options = {}) {
33
- const url = `${this.baseUrl}${endpoint}`;
34
- // Always include the Origin header - required by SonarLint IDE
35
- const headers = {
36
- 'Origin': this.sonarQubeServerUrl,
37
- ...options.headers,
38
- };
39
- const response = await fetch(url, {
40
- ...options,
41
- headers,
42
- });
43
- if (!response.ok && response.status !== 400) {
44
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
45
- }
46
- const contentType = response.headers.get('content-type');
47
- if (contentType?.includes('application/json')) {
48
- return response.json();
49
- }
50
- return response.text();
51
- }
52
- async getStatus() {
53
- return this.fetchAPI('/sonarlint/api/status');
54
- }
55
- isConnected() {
56
- return this.connected;
57
- }
58
- async disconnect() {
59
- this.connected = false;
60
- }
61
- // NOTE: The SonarLint IDE API is primarily designed for "Open in IDE" functionality
62
- // from SonarQube Server/Cloud. The following methods are placeholders that would need
63
- // proper endpoint discovery and authentication tokens to work.
64
- // The actual endpoints and their full capabilities are not publicly documented.
65
- // This implementation shows what we've discovered so far.
66
- async getCurrentIssues(filters) {
67
- throw new Error("Direct issue querying not supported by SonarLint IDE API. Use SonarQube Server API instead.");
68
- }
69
- async requestAnalysis(filePaths) {
70
- throw new Error("Analysis triggering not supported by SonarLint IDE API.");
71
- }
72
- async waitForAnalysisResults(analysisId) {
73
- throw new Error("Analysis results not available from IDE API.");
74
- }
75
- async getFileIssues(filePath, includeFixed = false) {
76
- throw new Error("Direct file issue querying not supported. Use SonarQube Server API.");
77
- }
78
- async getProjectMetrics(options) {
79
- throw new Error("Project metrics not available from IDE API. Use SonarQube Server API.");
80
- }
81
- async toggleAutoAnalysis(enabled) {
82
- throw new Error("Auto-analysis toggle not available via API.");
83
- }
84
- async getRuleDetails(ruleKey) {
85
- throw new Error("Rule details not available from IDE API. Use SonarQube Server API.");
86
- }
87
- async getSecurityHotspots(filters) {
88
- throw new Error("Security hotspots not available from IDE API. Use SonarQube Server API.");
89
- }
90
- }
91
- //# sourceMappingURL=sonarlint-bridge.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sonarlint-bridge.js","sourceRoot":"","sources":["../src/sonarlint-bridge.ts"],"names":[],"mappings":"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"}