Skip to content

Commit

Permalink
JS-161 Separate bridge from sonar-javascript-plugin (#4697)
Browse files Browse the repository at this point in the history
  • Loading branch information
saberduck authored May 24, 2024
1 parent 16a434e commit 6789d31
Show file tree
Hide file tree
Showing 132 changed files with 1,142 additions and 1,170 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private static List<Path> findUcfgFilesIn(Path projectPath) throws IOException {
try (
var stream = Files.find(projectPath.resolve(".scannerwork"), 3, BuildResultAssert::isUcfgFile)
) {
return stream.collect(toList());
return stream.toList();
}
}

Expand Down Expand Up @@ -176,7 +176,7 @@ public FileCacheStrategy forFiles(String... files) {
}

public FileCacheStrategy withCachedFilesCounts(int... cachedFilesCounts) {
this.cachedFilesCounts = IntStream.of(cachedFilesCounts).boxed().collect(toList());
this.cachedFilesCounts = IntStream.of(cachedFilesCounts).boxed().toList();
return this;
}

Expand Down
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<sonar.version>10.4.1.88267</sonar.version>
<sonar.api.version>10.7.0.2191</sonar.api.version>
<sonar-orchestrator.version>4.9.0.1920</sonar-orchestrator.version>
<gson.version>2.8.9</gson.version>
<gson.version>2.11.0</gson.version>
<analyzer-commons.version>2.7.0.1482</analyzer-commons.version>
<sslr.version>1.22</sslr.version>
<sonarlint.plugin.api.version>9.1.1.74346</sonarlint.plugin.api.version>
Expand Down Expand Up @@ -120,6 +120,11 @@
<dependencies>

<!-- Default dependencies (compile) -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>bridge</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>javascript-checks</artifactId>
Expand Down
96 changes: 96 additions & 0 deletions sonar-plugin/bridge/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.sonarsource.javascript</groupId>
<artifactId>sonar-plugin</artifactId>
<version>10.15.0-SNAPSHOT</version>
</parent>

<artifactId>bridge</artifactId>

<name>SonarQube JavaScript :: Bridge</name>

<dependencies>
<dependency>
<!-- TODO this should be replaced by new API module -->
<groupId>${project.groupId}</groupId>
<artifactId>javascript-checks</artifactId>
</dependency>
<dependency>
<groupId>org.sonarsource.api.plugin</groupId>
<artifactId>sonar-plugin-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
</dependency>
<dependency>
<groupId>org.sonarsource.api.plugin</groupId>
<artifactId>sonar-plugin-api-test-fixtures</artifactId>
</dependency>
<dependency>
<groupId>org.sonarsource.sonarqube</groupId>
<artifactId>sonar-plugin-api-impl</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<!-- copy runtimes so they are accessible by unit tests -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>generate-test-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/test-classes</outputDirectory>
<resources>
<resource>
<directory>${basedir}/../sonar-javascript-plugin/target/node</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,19 @@
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.utils.Version;

public enum AnalysisMode {
DEFAULT,
SKIP_UNCHANGED;

static final String DEFAULT_LINTER_ID = "default";
static final String UNCHANGED_LINTER_ID = "unchanged";
public static final String DEFAULT_LINTER_ID = "default";
public static final String UNCHANGED_LINTER_ID = "unchanged";
private static final Logger LOG = LoggerFactory.getLogger(AnalysisMode.class);

public static boolean isRuntimeApiCompatible(SensorContext context) {
return context.runtime().getApiVersion().isGreaterThanOrEqual(Version.create(9, 4));
}

static AnalysisMode getMode(SensorContext context, List<EslintRule> rules) {
public static AnalysisMode getMode(SensorContext context, List<EslintRule> rules) {
var logDefaultMode = "Analysis of unchanged files will not be skipped ({})";

if (!isRuntimeApiCompatible(context)) {
LOG.debug(logDefaultMode, "runtime API is not compatible");
return AnalysisMode.DEFAULT;
}

var canSkipUnchangedFiles = context.canSkipUnchangedFiles();
if (!canSkipUnchangedFiles) {
LOG.debug(logDefaultMode, "current analysis requires all files to be analyzed");
Expand All @@ -69,7 +60,7 @@ static AnalysisMode getMode(SensorContext context, List<EslintRule> rules) {
return AnalysisMode.SKIP_UNCHANGED;
}

static List<EslintRule> getUnchangedFileRules(List<EslintRule> rules) {
public static List<EslintRule> getUnchangedFileRules(List<EslintRule> rules) {
var rule = EslintRule.findFirstRuleWithKey(rules, EslintRule.UCFG_ESLINT_KEY);
return rule == null ? emptyList() : List.of(rule);
}
Expand All @@ -81,14 +72,14 @@ static List<EslintRule> getUnchangedFileRules(List<EslintRule> rules) {
* @param rules
* @return
*/
static List<EslintRule> getHtmlFileRules(List<EslintRule> rules) {
public static List<EslintRule> getHtmlFileRules(List<EslintRule> rules) {
var blackListRuleKeys = new HashSet<String>();
blackListRuleKeys.add("no-var");
blackListRuleKeys.add("ucfg");
return EslintRule.findAllBut(rules, blackListRuleKeys);
}

String getLinterIdFor(InputFile file) {
public String getLinterIdFor(InputFile file) {
if (this == SKIP_UNCHANGED && file.status() == InputFile.Status.SAME) {
return UNCHANGED_LINTER_ID;
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.plugins.javascript.bridge;

import static org.sonarsource.api.sonarlint.SonarLintSide.INSTANCE;

import java.io.IOException;
import java.util.List;
import javax.annotation.Nullable;
import org.sonar.api.Startable;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.TextRange;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.api.scanner.ScannerSide;
import org.sonarsource.api.sonarlint.SonarLintSide;

@ScannerSide
@SonarLintSide(lifespan = INSTANCE)
public interface BridgeServer extends Startable {
void startServerLazily(SensorContext context) throws IOException;

void initLinter(List<EslintRule> rules, List<String> environments, List<String> globals, AnalysisMode analysisMode, String baseDir,
List<String> exclusions) throws IOException;

AnalysisResponse analyzeJavaScript(JsAnalysisRequest request) throws IOException;

AnalysisResponse analyzeTypeScript(JsAnalysisRequest request) throws IOException;

AnalysisResponse analyzeCss(CssAnalysisRequest request) throws IOException;

AnalysisResponse analyzeYaml(JsAnalysisRequest request) throws IOException;

AnalysisResponse analyzeHtml(JsAnalysisRequest request) throws IOException;

void clean();

String getCommandInfo();

boolean isAlive();

boolean newTsConfig();

TsConfigFile loadTsConfig(String tsConfigAbsolutePath);

TsProgram createProgram(TsProgramRequest tsProgramRequest) throws IOException;

boolean deleteProgram(TsProgram tsProgram) throws IOException;

TsConfigFile createTsConfigFile(String content) throws IOException;

record JsAnalysisRequest(String filePath, String fileType, String language, @Nullable String fileContent, boolean ignoreHeaderComments,
@Nullable List<String> tsConfigs, @Nullable String programId, String linterId) {

}

record CssAnalysisRequest(String filePath, @Nullable String fileContent, List<StylelintRule> rules) {
}

record AnalysisResponse(@Nullable ParsingError parsingError, List<Issue> issues, List<Highlight> highlights,
List<HighlightedSymbol> highlightedSymbols, Metrics metrics, List<CpdToken> cpdTokens, List<String> ucfgPaths) {
public AnalysisResponse() {
this(null, List.of(), List.of(), List.of(), new Metrics(), List.of(), List.of());
}

public AnalysisResponse(@Nullable ParsingError parsingError, @Nullable List<Issue> issues, @Nullable List<Highlight> highlights,
@Nullable List<HighlightedSymbol> highlightedSymbols, @Nullable Metrics metrics, @Nullable List<CpdToken> cpdTokens, List<String> ucfgPaths) {
this.parsingError = parsingError;
this.issues = issues != null ? issues : List.of();
this.highlights = highlights != null ? highlights : List.of();
this.highlightedSymbols = highlightedSymbols != null ? highlightedSymbols : List.of();
this.metrics = metrics != null ? metrics : new Metrics();
this.cpdTokens = cpdTokens != null ? cpdTokens : List.of();
this.ucfgPaths = ucfgPaths;
}
}

record ParsingError(String message, Integer line, ParsingErrorCode code) {
}

enum ParsingErrorCode {
PARSING, FAILING_TYPESCRIPT, GENERAL_ERROR,
}

record Issue(Integer line, Integer column, Integer endLine, Integer endColumn, String message, String ruleId,
List<IssueLocation> secondaryLocations, Double cost, List<QuickFix> quickFixes) {
}

record QuickFix(String message, List<QuickFixEdit> edits) {
}

record QuickFixEdit(String text, IssueLocation loc) {
}

record IssueLocation(

Integer line, Integer column, Integer endLine, Integer endColumn, String message) {
}

record Highlight(

Location location, String textType) {
}

record HighlightedSymbol(

Location declaration, List<Location> references) {
}

record Location(

int startLine, int startCol, int endLine, int endCol) {

public TextRange toTextRange(InputFile inputFile) {
return inputFile.newRange(this.startLine, this.startCol, this.endLine, this.endCol);
}

@Override
public String toString() {
return String.format("%d:%d-%d:%d", startLine, startCol, endLine, endCol);
}
}

record Metrics(

List<Integer> ncloc, List<Integer> commentLines, List<Integer> nosonarLines, List<Integer> executableLines, int functions,
int statements, int classes, int complexity, int cognitiveComplexity) {
public Metrics() {
this(List.of(), List.of(), List.of(), List.of(), 0, 0, 0, 0, 0);
}
}

record CpdToken(

Location location, String image) {
}

class TsConfigResponse {

final List<String> files;
final List<String> projectReferences;
final String error;
final ParsingErrorCode errorCode;

TsConfigResponse(List<String> files, List<String> projectReferences, @Nullable String error, @Nullable ParsingErrorCode errorCode) {
this.files = files;
this.projectReferences = projectReferences;
this.error = error;
this.errorCode = errorCode;
}
}

record TsProgram(
@Nullable String programId, @Nullable List<String> files, @Nullable List<String> projectReferences, boolean missingTsConfig,
@Nullable String error) {

public TsProgram(String programId, @Nullable List<String> files, @Nullable List<String> projectReferences) {
this(programId, files, projectReferences, false, null);
}

public TsProgram(String programId, List<String> files, List<String> projectReferences, boolean missingTsConfig) {
this(programId, files, projectReferences, missingTsConfig, null);
}

public TsProgram(String error) {
this(null, null, null, false, error);
}

@Override
public String toString() {
if (error == null) {
return ("TsProgram{" + "programId='" + programId + '\'' + ", files=" + files + ", projectReferences=" + projectReferences + '}');
} else {
return "TsProgram{ error='" + error + "'}";
}
}
}

record TsProgramRequest(String tsConfig) {
}
}
Loading

0 comments on commit 6789d31

Please sign in to comment.