diff --git a/its/plugin/plugins/consumer-plugin/pom.xml b/its/plugin/plugins/consumer-plugin/pom.xml
new file mode 100644
index 00000000000..a1decf43f3c
--- /dev/null
+++ b/its/plugin/plugins/consumer-plugin/pom.xml
@@ -0,0 +1,49 @@
+
+
+ 4.0.0
+
+
+ org.sonarsource.javascript
+ javascript-it-plugin-plugins
+ 10.15.0-SNAPSHOT
+
+
+ consumer-plugin
+ sonar-plugin
+
+ JavaScript :: IT :: Plugin :: Plugins :: Consumer Plugin
+ Consumer Plugin
+
+
+
+ org.sonarsource.api.plugin
+ sonar-plugin-api
+
+
+ org.sonarsource.javascript
+ api
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
+
+
+ org.sonarsource.sonar-packaging-maven-plugin
+ sonar-packaging-maven-plugin
+ true
+
+ ${pluginApiMinVersion}
+ org.sonar.samples.javascript.consumer.ConsumerPlugin
+ js
+
+
+
+
+
+
diff --git a/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/Consumer.java b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/Consumer.java
new file mode 100644
index 00000000000..e1e75c6d2b5
--- /dev/null
+++ b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/Consumer.java
@@ -0,0 +1,55 @@
+/*
+ * SonarQube JavaScript Plugin
+ * Copyright (C) 2012-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.samples.javascript.consumer;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.plugins.javascript.api.JsAnalysisConsumer;
+import org.sonar.plugins.javascript.api.JsFile;
+
+public class Consumer implements JsAnalysisConsumer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(Consumer.class);
+
+ private final List jsFiles = new ArrayList<>();
+ private boolean done;
+
+ @Override
+ public void accept(JsFile jsFile) {
+ LOG.info("Accepted file: {}", jsFile.inputFile());
+ jsFiles.add(jsFile);
+ }
+
+ @Override
+ public void doneAnalysis() {
+ LOG.info("Done analysis");
+ done = true;
+ }
+
+ public List getJsFiles() {
+ return jsFiles;
+ }
+
+ public boolean isDone() {
+ return done;
+ }
+}
diff --git a/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/ConsumerPlugin.java b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/ConsumerPlugin.java
new file mode 100644
index 00000000000..02068fdbeb7
--- /dev/null
+++ b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/ConsumerPlugin.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube JavaScript Plugin
+ * Copyright (C) 2012-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.samples.javascript.consumer;
+
+import org.sonar.api.Plugin;
+
+public class ConsumerPlugin implements Plugin {
+
+ @Override
+ public void define(Context context) {
+ context.addExtensions(
+ Consumer.class,
+ ConsumerSensor.class
+ );
+ }
+}
diff --git a/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/ConsumerSensor.java b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/ConsumerSensor.java
new file mode 100644
index 00000000000..0b95706821e
--- /dev/null
+++ b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/ConsumerSensor.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube JavaScript Plugin
+ * Copyright (C) 2012-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.samples.javascript.consumer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.api.batch.sensor.SensorDescriptor;
+import org.sonar.api.scanner.ScannerSide;
+import org.sonar.api.scanner.sensor.ProjectSensor;
+import org.sonar.plugins.javascript.api.JsFile;
+
+@ScannerSide
+// We depend on the "js-analysis" extension to make sure that the analysis is done before we consume it
+@DependsUpon("js-analysis")
+public class ConsumerSensor implements ProjectSensor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ConsumerSensor.class);
+
+ private final Consumer consumer;
+
+ /**
+ * We use Dependency Injection to get Consumer instance
+ *
+ * @param consumer Consumer instance
+ */
+ public ConsumerSensor(Consumer consumer) {
+ this.consumer = consumer;
+ }
+
+ @Override
+ public void describe(SensorDescriptor descriptor) {
+ descriptor.name("Consumer Sensor");
+ }
+
+ @Override
+ public void execute(SensorContext context) {
+ if (!consumer.isDone()) {
+ throw new IllegalStateException("Consumer is not done");
+ }
+ for (JsFile jsFile : consumer.getJsFiles()) {
+ LOG.info("Processing file {}", jsFile.inputFile());
+ }
+ }
+}
diff --git a/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/package-info.java b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/package-info.java
new file mode 100644
index 00000000000..e36dc61df1d
--- /dev/null
+++ b/its/plugin/plugins/consumer-plugin/src/main/java/org/sonar/samples/javascript/consumer/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.samples.javascript.consumer;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/its/plugin/plugins/consumer-plugin/src/test/java/org/sonar/samples/javascript/consumer/ConsumerTest.java b/its/plugin/plugins/consumer-plugin/src/test/java/org/sonar/samples/javascript/consumer/ConsumerTest.java
new file mode 100644
index 00000000000..185ab56fff0
--- /dev/null
+++ b/its/plugin/plugins/consumer-plugin/src/test/java/org/sonar/samples/javascript/consumer/ConsumerTest.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube JavaScript Plugin
+ * Copyright (C) 2012-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.samples.javascript.consumer;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+class ConsumerTest {
+
+ @Test
+ void test() {
+ var consumer = new Consumer();
+ consumer.doneAnalysis();
+ assertTrue(consumer.isDone());
+ }
+
+}
diff --git a/its/plugin/plugins/eslint-custom-rules-plugin/src/main/java/org/sonar/samples/javascript/package-info.java b/its/plugin/plugins/eslint-custom-rules-plugin/src/main/java/org/sonar/samples/javascript/package-info.java
new file mode 100644
index 00000000000..0522c738ac2
--- /dev/null
+++ b/its/plugin/plugins/eslint-custom-rules-plugin/src/main/java/org/sonar/samples/javascript/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.samples.javascript;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/its/plugin/plugins/pom.xml b/its/plugin/plugins/pom.xml
index 8591fc8715a..4731cff3f29 100644
--- a/its/plugin/plugins/pom.xml
+++ b/its/plugin/plugins/pom.xml
@@ -14,6 +14,7 @@
eslint-custom-rules-plugin
+ consumer-plugin
diff --git a/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/ConsumerPluginTest.java b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/ConsumerPluginTest.java
new file mode 100644
index 00000000000..8a967fdf1a1
--- /dev/null
+++ b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/ConsumerPluginTest.java
@@ -0,0 +1,102 @@
+/*
+ * SonarQube JavaScript Plugin
+ * Copyright (C) 2012-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 com.sonar.javascript.it.plugin;
+
+import static com.sonar.javascript.it.plugin.OrchestratorStarter.JAVASCRIPT_PLUGIN_LOCATION;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.BuildResult;
+import com.sonar.orchestrator.build.SonarScanner;
+import com.sonar.orchestrator.junit5.OrchestratorExtension;
+import com.sonar.orchestrator.locator.FileLocation;
+import java.io.File;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+class ConsumerPluginTest {
+
+ private static final String PLUGIN_ARTIFACT_ID = "consumer-plugin";
+
+ private static OrchestratorExtension orchestrator;
+
+ @BeforeAll
+ public static void before() {
+ orchestrator = initOrchestrator(PLUGIN_ARTIFACT_ID);
+ }
+
+ @AfterAll
+ public static void after() {
+ orchestrator.stop();
+ }
+
+ static OrchestratorExtension initOrchestrator(String customRulesArtifactId) {
+ var orchestrator = OrchestratorExtension
+ .builderEnv()
+ .useDefaultAdminCredentialsForBuilds(true)
+ .setSonarVersion(System.getProperty("sonar.runtimeVersion", "LATEST_RELEASE"))
+ .addPlugin(JAVASCRIPT_PLUGIN_LOCATION)
+ .restoreProfileAtStartup(FileLocation.ofClasspath("/empty-js-profile.xml"))
+ .addPlugin(
+ FileLocation.byWildcardMavenFilename(
+ new File("../plugins/" + customRulesArtifactId + "/target"),
+ customRulesArtifactId + "-*.jar"
+ )
+ )
+ .restoreProfileAtStartup(FileLocation.ofClasspath("/profile-javascript-custom-rules.xml"))
+ .restoreProfileAtStartup(FileLocation.ofClasspath("/profile-typescript-custom-rules.xml"))
+ .restoreProfileAtStartup(FileLocation.ofClasspath("/nosonar.xml"))
+ .build();
+ // Installation of SQ server in orchestrator is not thread-safe, so we need to synchronize
+ synchronized (OrchestratorStarter.class) {
+ orchestrator.start();
+ }
+ return orchestrator;
+ }
+
+ static BuildResult runBuild(Orchestrator orchestrator) {
+ SonarScanner build = OrchestratorStarter
+ .createScanner()
+ .setProjectDir(TestUtils.projectDirNoCopy("custom_rules"))
+ .setProjectKey("custom-rules")
+ .setProjectName("Custom Rules")
+ .setProjectVersion("1.0")
+ .setDebugLogs(true)
+ .setSourceDirs("src");
+ orchestrator.getServer().provisionProject("custom-rules", "Custom Rules");
+ orchestrator
+ .getServer()
+ .associateProjectToQualityProfile("custom-rules", "js", "javascript-custom-rules-profile");
+ orchestrator
+ .getServer()
+ .associateProjectToQualityProfile("custom-rules", "ts", "ts-custom-rules-profile");
+ return orchestrator.executeBuild(build);
+ }
+
+ @Test
+ void test() {
+ var buildResult = runBuild(orchestrator);
+ var logMatch = ".*DEBUG: Registered JsAnalysisConsumers \\[org.sonar.samples.javascript.consumer.Consumer.*]";
+ assertThat(buildResult.getLogsLines(l -> l.matches(logMatch))).hasSize(1);
+
+ assertThat(buildResult.getLogsLines(l -> l.matches(".*Processing file src/dir.*"))).hasSize(2);
+ }
+}
diff --git a/sonar-plugin/api/src/main/java/org/sonar/plugins/javascript/api/JsAnalysisConsumer.java b/sonar-plugin/api/src/main/java/org/sonar/plugins/javascript/api/JsAnalysisConsumer.java
new file mode 100644
index 00000000000..e811533a29b
--- /dev/null
+++ b/sonar-plugin/api/src/main/java/org/sonar/plugins/javascript/api/JsAnalysisConsumer.java
@@ -0,0 +1,44 @@
+/*
+ * 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.api;
+
+import org.sonar.api.scanner.ScannerSide;
+import org.sonarsource.api.sonarlint.SonarLintSide;
+
+/**
+ * Implementations of this interface will be invoked during the analysis of JavaScript/TypeScript files.
+ *
+ */
+@ScannerSide
+@SonarLintSide
+public interface JsAnalysisConsumer {
+
+ /**
+ * Called for each file during the analysis.
+ * @param jsFile the file which was analyzed
+ */
+ void accept(JsFile jsFile);
+
+ /**
+ *
+ * Called at the end of the analysis.
+ */
+ void doneAnalysis();
+}
diff --git a/sonar-plugin/api/src/main/java/org/sonar/plugins/javascript/api/JsFile.java b/sonar-plugin/api/src/main/java/org/sonar/plugins/javascript/api/JsFile.java
new file mode 100644
index 00000000000..2e753468de6
--- /dev/null
+++ b/sonar-plugin/api/src/main/java/org/sonar/plugins/javascript/api/JsFile.java
@@ -0,0 +1,25 @@
+/*
+ * 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.api;
+
+import org.sonar.api.batch.fs.InputFile;
+
+public record JsFile(InputFile inputFile /*, ESTreeNode node */) {
+}
diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/JavaScriptPlugin.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/JavaScriptPlugin.java
index bf0b02a68e8..16964e0e8f3 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/JavaScriptPlugin.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/JavaScriptPlugin.java
@@ -31,6 +31,7 @@
import org.sonar.css.CssRulesDefinition;
import org.sonar.css.StylelintReportSensor;
import org.sonar.css.metrics.CssMetricSensor;
+import org.sonar.plugins.javascript.analysis.AnalysisConsumers;
import org.sonar.plugins.javascript.analysis.AnalysisProcessor;
import org.sonar.plugins.javascript.analysis.AnalysisWithProgram;
import org.sonar.plugins.javascript.analysis.AnalysisWithWatchProgram;
@@ -138,6 +139,7 @@ public class JavaScriptPlugin implements Plugin {
@Override
public void define(Context context) {
context.addExtensions(
+ AnalysisConsumers.class,
JavaScriptLanguage.class,
JavaScriptExclusionsFileFilter.class,
JavaScriptRulesDefinition.class,
diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AbstractAnalysis.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AbstractAnalysis.java
index a05ced58af5..2b763e27f61 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AbstractAnalysis.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AbstractAnalysis.java
@@ -22,16 +22,22 @@
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.sensor.SensorContext;
+import org.sonar.plugins.javascript.CancellationException;
import org.sonar.plugins.javascript.JavaScriptLanguage;
import org.sonar.plugins.javascript.TypeScriptLanguage;
+import org.sonar.plugins.javascript.analysis.cache.CacheAnalysis;
+import org.sonar.plugins.javascript.analysis.cache.CacheStrategies;
+import org.sonar.plugins.javascript.api.JsFile;
import org.sonar.plugins.javascript.bridge.AnalysisMode;
import org.sonar.plugins.javascript.bridge.AnalysisWarningsWrapper;
import org.sonar.plugins.javascript.bridge.BridgeServer;
import org.sonar.plugins.javascript.JavaScriptFilePredicate;
+import org.sonar.plugins.javascript.bridge.BridgeServer.TsProgram;
import org.sonar.plugins.javascript.utils.ProgressReport;
abstract class AbstractAnalysis {
@@ -48,6 +54,7 @@ abstract class AbstractAnalysis {
ProgressReport progressReport;
AnalysisMode analysisMode;
protected final AnalysisWarningsWrapper analysisWarnings;
+ private AnalysisConsumers consumers;
AbstractAnalysis(
BridgeServer bridgeServer,
@@ -65,12 +72,13 @@ protected static String inputFileLanguage(InputFile file) {
: JavaScriptLanguage.KEY;
}
- void initialize(SensorContext context, JsTsChecks checks, AnalysisMode analysisMode) {
+ void initialize(SensorContext context, JsTsChecks checks, AnalysisMode analysisMode, AnalysisConsumers consumers) {
LOG.debug("Initializing {}", getClass().getName());
this.context = context;
contextUtils = new ContextUtils(context);
this.checks = checks;
this.analysisMode = analysisMode;
+ this.consumers = consumers;
}
protected boolean isJavaScript(InputFile file) {
@@ -78,4 +86,57 @@ protected boolean isJavaScript(InputFile file) {
}
abstract void analyzeFiles(List inputFiles, List tsConfigs) throws IOException;
+
+ protected void analyzeFile(InputFile file, @Nullable List tsConfigs, @Nullable TsProgram tsProgram) throws IOException {
+ if (context.isCancelled()) {
+ throw new CancellationException(
+ "Analysis interrupted because the SensorContext is in cancelled state"
+ );
+ }
+ var cacheStrategy = CacheStrategies.getStrategyFor(context, file);
+ if (cacheStrategy.isAnalysisRequired()) {
+ try {
+ LOG.debug("Analyzing file: {}", file.uri());
+ progressReport.nextFile(file.toString());
+ var fileContent = contextUtils.shouldSendFileContent(file) ? file.contents() : null;
+ var request = getJsAnalysisRequest(file, fileContent, tsProgram, tsConfigs);
+
+ var response = isJavaScript(file)
+ ? bridgeServer.analyzeJavaScript(request)
+ : bridgeServer.analyzeTypeScript(request);
+
+ analysisProcessor.processResponse(context, checks, file, response);
+ cacheStrategy.writeAnalysisToCache(
+ CacheAnalysis.fromResponse(response.ucfgPaths(), response.cpdTokens()),
+ file
+ );
+ consumers.accept(new JsFile(file));
+ } catch (IOException e) {
+ LOG.error("Failed to get response while analyzing " + file.uri(), e);
+ throw e;
+ }
+ } else {
+ LOG.debug("Processing cache analysis of file: {}", file.uri());
+ var cacheAnalysis = cacheStrategy.readAnalysisFromCache();
+ analysisProcessor.processCacheAnalysis(context, file, cacheAnalysis);
+ }
+ }
+
+ private BridgeServer.JsAnalysisRequest getJsAnalysisRequest(
+ InputFile file,
+ @Nullable String fileContent,
+ @Nullable TsProgram tsProgram,
+ @Nullable List tsConfigs
+ ) {
+ return new BridgeServer.JsAnalysisRequest(
+ file.absolutePath(),
+ file.type().toString(),
+ inputFileLanguage(file),
+ fileContent,
+ contextUtils.ignoreHeaderComments(),
+ tsConfigs,
+ tsProgram != null ? tsProgram.programId() : null,
+ analysisMode.getLinterIdFor(file)
+ );
+ }
}
diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisConsumers.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisConsumers.java
new file mode 100644
index 00000000000..12213eaeacb
--- /dev/null
+++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisConsumers.java
@@ -0,0 +1,57 @@
+/*
+ * 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.analysis;
+
+import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.scanner.ScannerSide;
+import org.sonar.plugins.javascript.api.JsAnalysisConsumer;
+import org.sonar.plugins.javascript.api.JsFile;
+import org.sonarsource.api.sonarlint.SonarLintSide;
+
+@ScannerSide
+@SonarLintSide
+public class AnalysisConsumers implements JsAnalysisConsumer {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AnalysisConsumers.class);
+
+ private final List consumers;
+
+ public AnalysisConsumers() {
+ consumers = List.of();
+ LOG.debug("No registered JsAnalysisConsumer.");
+ }
+
+ public AnalysisConsumers(List consumers) {
+ this.consumers = List.copyOf(consumers);
+ LOG.debug("Registered JsAnalysisConsumers {}", this.consumers);
+ }
+
+ @Override
+ public void accept(JsFile jsFile) {
+ consumers.forEach(c -> c.accept(jsFile));
+ }
+
+ @Override
+ public void doneAnalysis() {
+ consumers.forEach(JsAnalysisConsumer::doneAnalysis);
+ }
+}
diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithProgram.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithProgram.java
index d7d7fa2d8e6..fd2bc86cd79 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithProgram.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithProgram.java
@@ -26,18 +26,13 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.scanner.ScannerSide;
-import org.sonar.plugins.javascript.CancellationException;
import org.sonar.plugins.javascript.JavaScriptPlugin;
-import org.sonar.plugins.javascript.analysis.cache.CacheAnalysis;
-import org.sonar.plugins.javascript.analysis.cache.CacheStrategies;
import org.sonar.plugins.javascript.bridge.AnalysisWarningsWrapper;
import org.sonar.plugins.javascript.bridge.BridgeServer;
-import org.sonar.plugins.javascript.bridge.BridgeServer.JsAnalysisRequest;
import org.sonar.plugins.javascript.bridge.BridgeServer.TsProgram;
import org.sonar.plugins.javascript.bridge.BridgeServer.TsProgramRequest;
import org.sonar.plugins.javascript.utils.ProgressReport;
@@ -107,7 +102,7 @@ void analyzeFiles(List inputFiles, List tsConfigs) throws IOE
);
for (var f : skippedFiles) {
LOG.debug("File not part of any tsconfig.json: {}", f);
- analyze(f, null);
+ analyzeFile(f, null, null);
}
}
success = true;
@@ -139,7 +134,7 @@ private void analyzeProgram(TsProgram program, Set analyzedFiles) thr
continue;
}
if (analyzedFiles.add(inputFile)) {
- analyze(inputFile, program);
+ analyzeFile(inputFile, null, program);
counter++;
} else {
LOG.debug(
@@ -152,54 +147,4 @@ private void analyzeProgram(TsProgram program, Set analyzedFiles) thr
LOG.info("Analyzed {} file(s) with current program", counter);
}
- private void analyze(InputFile file, @Nullable TsProgram tsProgram) throws IOException {
- if (context.isCancelled()) {
- throw new CancellationException(
- "Analysis interrupted because the SensorContext is in cancelled state"
- );
- }
- var cacheStrategy = CacheStrategies.getStrategyFor(context, file);
- if (cacheStrategy.isAnalysisRequired()) {
- try {
- LOG.debug("Analyzing file: {}", file.uri());
- progressReport.nextFile(file.toString());
- var fileContent = contextUtils.shouldSendFileContent(file) ? file.contents() : null;
- var request = getJsAnalysisRequest(file, tsProgram, fileContent);
-
- var response = isJavaScript(file)
- ? bridgeServer.analyzeJavaScript(request)
- : bridgeServer.analyzeTypeScript(request);
-
- analysisProcessor.processResponse(context, checks, file, response);
- cacheStrategy.writeAnalysisToCache(
- CacheAnalysis.fromResponse(response.ucfgPaths(), response.cpdTokens()),
- file
- );
- } catch (IOException e) {
- LOG.error("Failed to get response while analyzing " + file.uri(), e);
- throw e;
- }
- } else {
- LOG.debug("Processing cache analysis of file: {}", file.uri());
- var cacheAnalysis = cacheStrategy.readAnalysisFromCache();
- analysisProcessor.processCacheAnalysis(context, file, cacheAnalysis);
- }
- }
-
- private JsAnalysisRequest getJsAnalysisRequest(
- InputFile file,
- @Nullable TsProgram tsProgram,
- @Nullable String fileContent
- ) {
- return new JsAnalysisRequest(
- file.absolutePath(),
- file.type().toString(),
- inputFileLanguage(file),
- fileContent,
- contextUtils.ignoreHeaderComments(),
- null,
- tsProgram != null ? tsProgram.programId() : null,
- analysisMode.getLinterIdFor(file)
- );
- }
}
diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithWatchProgram.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithWatchProgram.java
index 8d2eee508d8..1168d97f886 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithWatchProgram.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/AnalysisWithWatchProgram.java
@@ -22,7 +22,6 @@
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
@@ -33,12 +32,8 @@
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.scanner.ScannerSide;
-import org.sonar.plugins.javascript.CancellationException;
-import org.sonar.plugins.javascript.analysis.cache.CacheAnalysis;
-import org.sonar.plugins.javascript.analysis.cache.CacheStrategies;
import org.sonar.plugins.javascript.bridge.AnalysisWarningsWrapper;
import org.sonar.plugins.javascript.bridge.BridgeServer;
-import org.sonar.plugins.javascript.bridge.BridgeServer.JsAnalysisRequest;
import org.sonar.plugins.javascript.bridge.TsConfigFile;
import org.sonar.plugins.javascript.utils.ProgressReport;
import org.sonarsource.api.sonarlint.SonarLintSide;
@@ -121,52 +116,9 @@ private List loadTsConfigs(List tsConfigPaths) {
private void analyzeTsConfig(@Nullable TsConfigFile tsConfigFile, List files)
throws IOException {
+ List tsConfigs = tsConfigFile == null ? List.of() : List.of(tsConfigFile.getFilename());
for (InputFile inputFile : files) {
- if (context.isCancelled()) {
- throw new CancellationException(
- "Analysis interrupted because the SensorContext is in cancelled state"
- );
- }
- analyze(inputFile, tsConfigFile);
- progressReport.nextFile(inputFile.toString());
- }
- }
-
- private void analyze(InputFile file, @Nullable TsConfigFile tsConfigFile) throws IOException {
- var cacheStrategy = CacheStrategies.getStrategyFor(context, file);
- if (cacheStrategy.isAnalysisRequired()) {
- try {
- LOG.debug("Analyzing file: {}", file);
- var fileContent = contextUtils.shouldSendFileContent(file) ? file.contents() : null;
- var tsConfigs = tsConfigFile == null
- ? Collections.emptyList()
- : List.of(tsConfigFile.getFilename());
- var request = new JsAnalysisRequest(
- file.absolutePath(),
- file.type().toString(),
- inputFileLanguage(file),
- fileContent,
- contextUtils.ignoreHeaderComments(),
- tsConfigs,
- null,
- analysisMode.getLinterIdFor(file)
- );
- var response = isJavaScript(file)
- ? bridgeServer.analyzeJavaScript(request)
- : bridgeServer.analyzeTypeScript(request);
- analysisProcessor.processResponse(context, checks, file, response);
- cacheStrategy.writeAnalysisToCache(
- CacheAnalysis.fromResponse(response.ucfgPaths(), response.cpdTokens()),
- file
- );
- } catch (IOException e) {
- LOG.error("Failed to get response while analyzing " + file.uri(), e);
- throw e;
- }
- } else {
- LOG.debug("Processing cache analysis of file: {}", file.uri());
- var cacheAnalysis = cacheStrategy.readAnalysisFromCache();
- analysisProcessor.processCacheAnalysis(context, file, cacheAnalysis);
+ analyzeFile(inputFile, tsConfigs, null);
}
}
}
diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/JsTsSensor.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/JsTsSensor.java
index 1568738e73b..506724d74f1 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/JsTsSensor.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/JsTsSensor.java
@@ -25,6 +25,7 @@
import javax.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.sonar.api.batch.DependedUpon;
import org.sonar.api.batch.fs.FilePredicate;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
@@ -36,6 +37,7 @@
import org.sonar.plugins.javascript.JavaScriptFilePredicate;
import org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingChecker;
+@DependedUpon("js-analysis")
public class JsTsSensor extends AbstractBridgeSensor {
private static final Logger LOG = LoggerFactory.getLogger(JsTsSensor.class);
@@ -43,20 +45,23 @@ public class JsTsSensor extends AbstractBridgeSensor {
private final AnalysisWithWatchProgram analysisWithWatchProgram;
private final JsTsChecks checks;
private final SonarLintTypeCheckingChecker javaScriptProjectChecker;
+ private final AnalysisConsumers consumers;
// Constructor for SonarCloud without the optional dependency (Pico doesn't support optional dependencies)
public JsTsSensor(
JsTsChecks checks,
BridgeServer bridgeServer,
AnalysisWithProgram analysisWithProgram,
- AnalysisWithWatchProgram analysisWithWatchProgram
+ AnalysisWithWatchProgram analysisWithWatchProgram,
+ AnalysisConsumers consumers
) {
this(
checks,
bridgeServer,
null,
analysisWithProgram,
- analysisWithWatchProgram
+ analysisWithWatchProgram,
+ consumers
);
}
@@ -65,13 +70,15 @@ public JsTsSensor(
BridgeServer bridgeServer,
@Nullable SonarLintTypeCheckingChecker javaScriptProjectChecker,
AnalysisWithProgram analysisWithProgram,
- AnalysisWithWatchProgram analysisWithWatchProgram
+ AnalysisWithWatchProgram analysisWithWatchProgram,
+ AnalysisConsumers consumers
) {
super(bridgeServer, "JS/TS");
this.analysisWithProgram = analysisWithProgram;
this.analysisWithWatchProgram = analysisWithWatchProgram;
this.checks = checks;
this.javaScriptProjectChecker = javaScriptProjectChecker;
+ this.consumers = consumers;
}
@Override
@@ -117,8 +124,9 @@ protected void analyzeFiles(List inputFiles) throws IOException {
if (tsConfigs.isEmpty()) {
LOG.info("No tsconfig.json file found");
}
- analysis.initialize(context, checks, analysisMode);
+ analysis.initialize(context, checks, analysisMode, consumers);
analysis.analyzeFiles(inputFiles, tsConfigs);
+ consumers.doneAnalysis();
}
private String createTsConfigFile(String content) throws IOException {
diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/package-info.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/package-info.java
new file mode 100644
index 00000000000..d3c7bdd729e
--- /dev/null
+++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+@javax.annotation.ParametersAreNonnullByDefault
+package org.sonar.plugins.javascript.analysis;
diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptPluginTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptPluginTest.java
index 5346dcc24b2..09dc5b6de43 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptPluginTest.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/JavaScriptPluginTest.java
@@ -39,7 +39,7 @@
class JavaScriptPluginTest {
- private static final int BASE_EXTENSIONS = 35;
+ private static final int BASE_EXTENSIONS = 36;
private static final int JS_ADDITIONAL_EXTENSIONS = 4;
private static final int TS_ADDITIONAL_EXTENSIONS = 3;
private static final int CSS_ADDITIONAL_EXTENSIONS = 3;
diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JavaScriptEslintBasedSensorTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JavaScriptEslintBasedSensorTest.java
index 2393fb7739d..46042e11c61 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JavaScriptEslintBasedSensorTest.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JavaScriptEslintBasedSensorTest.java
@@ -534,7 +534,8 @@ void should_skip_analysis_when_no_files() {
checks(ESLINT_BASED_RULE),
bridgeServerMock,
analysisWithProgram,
- analysisWithWatchProgram
+ analysisWithWatchProgram,
+ new AnalysisConsumers()
);
javaScriptEslintBasedSensor.execute(context);
assertThat(logTester.logs(Level.INFO)).contains("No input files found for analysis");
@@ -550,7 +551,8 @@ void handle_missing_node() throws Exception {
checks(ESLINT_BASED_RULE),
bridgeServerMock,
analysisWithProgram,
- analysisWithWatchProgram
+ analysisWithWatchProgram,
+ new AnalysisConsumers()
);
createInputFile(context);
@@ -612,7 +614,8 @@ void should_not_create_parsing_issue_when_no_rule() throws IOException {
checks(ESLINT_BASED_RULE),
bridgeServerMock,
analysisWithProgram,
- analysisWithWatchProgram
+ analysisWithWatchProgram,
+ new AnalysisConsumers()
)
.execute(context);
Collection issues = context.allIssues();
@@ -793,7 +796,8 @@ private JsTsSensor createSensor(
bridgeServerMock,
sonarlintTypeCheckingChecker,
analysisWithProgram,
- analysisWithWatchProgram
+ analysisWithWatchProgram,
+ new AnalysisConsumers()
);
}
diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JsTsSensorTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JsTsSensorTest.java
index 978855d7437..fbea47081a3 100644
--- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JsTsSensorTest.java
+++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/JsTsSensorTest.java
@@ -39,6 +39,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@@ -87,6 +88,8 @@
import org.sonar.plugins.javascript.JavaScriptPlugin;
import org.sonar.plugins.javascript.TestUtils;
import org.sonar.plugins.javascript.analysis.cache.CacheTestUtils;
+import org.sonar.plugins.javascript.api.JsAnalysisConsumer;
+import org.sonar.plugins.javascript.api.JsFile;
import org.sonar.plugins.javascript.bridge.BridgeServer.AnalysisResponse;
import org.sonar.plugins.javascript.bridge.BridgeServer.JsAnalysisRequest;
import org.sonar.plugins.javascript.bridge.BridgeServer.ParsingError;
@@ -776,12 +779,48 @@ void should_save_cached_cpd_with_program() throws IOException {
.contains("Processing cache analysis of file: " + file.uri());
}
+ @Test
+ void should_invoke_analysis_consumers() throws Exception {
+ var consumer = new JsAnalysisConsumer() {
+ final List files = new ArrayList<>();
+ boolean done;
+
+ @Override
+ public void accept(JsFile jsFile) {
+ files.add(jsFile);
+ }
+
+ @Override
+ public void doneAnalysis() {
+ done = true;
+ }
+ };
+
+ var sensor = new JsTsSensor(
+ checks(ESLINT_BASED_RULE, "S2260"),
+ bridgeServerMock,
+ analysisWithProgram(),
+ analysisWithWatchProgram(),
+ new AnalysisConsumers(List.of(consumer))
+ );
+
+ var inputFile = createInputFile(context);
+ var tsProgram = new TsProgram("1", List.of(inputFile.absolutePath()), List.of(), false, null);
+ when(bridgeServerMock.createProgram(any())).thenReturn(tsProgram);
+
+ sensor.execute(context);
+ assertThat(consumer.files).hasSize(1);
+ assertThat(consumer.files.get(0).inputFile()).isEqualTo(inputFile);
+ assertThat(consumer.done).isTrue();
+ }
+
private JsTsSensor createSensor() {
return new JsTsSensor(
checks(ESLINT_BASED_RULE, "S2260"),
bridgeServerMock,
analysisWithProgram(),
- analysisWithWatchProgram()
+ analysisWithWatchProgram(),
+ new AnalysisConsumers()
);
}