
org.sonar.plugins.javascript.external.ExternalIssueRepository Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sonar-javascript-plugin Show documentation
Show all versions of sonar-javascript-plugin Show documentation
Code Analyzer for JavaScript/TypeScript/CSS
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2025 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* 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 Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
package org.sonar.plugins.javascript.external;
import static org.sonar.plugins.javascript.utils.UnicodeEscape.unicodeEscape;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Pattern;
import org.sonar.api.batch.sensor.SensorContext;
import org.sonar.plugins.javascript.bridge.BridgeServer;
/**
* This is the application of the Repository Pattern.
*
* In a proper Domain-Driven project, the repository implementation is hosted by the Infrastructure layer,
* and its purpose is to serve as a delegate between the Domain and the persistence layer.
* We can fit this class into the pattern, using the following rationalizations:
*
* * The `Issue` instance is the Domain entity - i.e. the Conceptual entity of the Conceptual Data Model
* * The `SensorContext` instance is the persistence layer - i.e. SonarQube is the infrastructure Host, which provides the persistence layer through the `NewExternalIssue` class
*/
public class ExternalIssueRepository {
private ExternalIssueRepository() {}
/**
* Persist the passed issue into the passed context, using the passed rule repository key to resolve the belonging rule.
*/
public static void save(ExternalIssue issue, SensorContext context) {
var file = issue.file();
var newIssue = context.newExternalIssue();
var newLocation = newIssue.newLocation();
newLocation.on(file);
if (issue.message() != null) {
var escapedMsg = unicodeEscape(issue.message());
newLocation.message(escapedMsg);
}
newLocation.at(
file.newRange(
issue.location().start().line(),
issue.location().start().lineOffset(),
issue.location().end().line(),
issue.location().end().lineOffset()
)
);
newIssue
.severity(issue.severity())
.remediationEffortMinutes(issue.effort())
.at(newLocation)
.engineId(issue.engineId())
.ruleId(issue.name())
.type(issue.type())
.save();
}
public static void saveESLintIssues(
SensorContext context,
List externalIssues,
List issues
) {
if (!externalIssues.isEmpty()) {
var deduplicatedExternalIssues = ExternalIssueRepository.deduplicateIssues(
externalIssues,
issues
);
for (var issue : deduplicatedExternalIssues) {
ExternalIssueRepository.save(issue, context);
}
}
}
public static List deduplicateIssues(
List externalIssues,
List issues
) {
var deduplicatedIssues = new ArrayList();
// normalize issues of JS/TS analyzer into set of strigs
var normalizedIssues = new HashSet<>();
for (BridgeServer.Issue issue : issues) {
for (String ruleKey : issue.ruleESLintKeys()) {
String issueKey = String.format(
"%s-%s-%d-%d-%d-%d",
ruleKey,
issue.filePath().replaceAll(Pattern.quote(File.separator), "/"),
issue.line(),
issue.column(),
issue.endLine(),
issue.endColumn()
);
normalizedIssues.add(issueKey);
}
}
// at that point, we have the list of issues that were persisted
// we can now persist the ESLint issues that match none of the persisted issues
for (var externalIssue : externalIssues) {
var issueKey = String.format(
"%s-%s-%d-%d-%d-%d",
externalIssue.name(),
externalIssue.file().absolutePath().replaceAll(Pattern.quote(File.separator), "/"),
externalIssue.location().start().line(),
externalIssue.location().start().lineOffset(),
externalIssue.location().end().line(),
externalIssue.location().end().lineOffset()
);
if (!normalizedIssues.contains(issueKey)) {
deduplicatedIssues.add(externalIssue);
}
}
return deduplicatedIssues;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy