From 537283f9908e50ebf8252bcb19c0ee1b6ba78cef Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Thu, 31 Oct 2024 12:04:32 +0100 Subject: [PATCH 1/8] Fixup tests --- .../plugins/javascript/JavaScriptPlugin.java | 9 +- .../analysis/AnalysisWithWatchProgram.java | 1 + .../javascript/analysis/JsTsSensor.java | 23 +-- .../javascript/analysis/TsConfigProvider.java | 138 ++++++++++++++++-- .../SonarLintTypeCheckingChecker.java | 26 ---- .../SonarLintTypeCheckingCheckerImpl.java | 114 --------------- .../SonarLintTypeCheckingFilter.java | 116 --------------- .../TsConfigCache.java | 6 +- .../TsConfigCacheImpl.java | 14 +- .../javascript/JavaScriptPluginTest.java | 2 +- .../JavaScriptEslintBasedSensorTest.java | 14 +- .../javascript/analysis/JsTsSensorTest.java | 4 +- .../LookupConfigProviderFilterTest.java} | 8 +- .../analysis/TsConfigCacheTest.java | 11 +- .../analysis/TsConfigProviderTest.java | 130 ++++++++++------- .../SonarLintTypeCheckingCheckerTest.java | 118 --------------- 16 files changed, 248 insertions(+), 486 deletions(-) delete mode 100644 sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingChecker.java delete mode 100644 sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerImpl.java delete mode 100644 sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingFilter.java rename sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/{analysis => sonarlint}/TsConfigCache.java (69%) rename sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/{analysis => sonarlint}/TsConfigCacheImpl.java (95%) rename sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/{sonarlint/SonarLintTypeCheckingFilterTest.java => analysis/LookupConfigProviderFilterTest.java} (93%) delete mode 100644 sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerTest.java 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 1bccbe58f00..0cbe83fef0f 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 @@ -39,7 +39,7 @@ import org.sonar.plugins.javascript.analysis.HtmlSensor; import org.sonar.plugins.javascript.analysis.JsTsChecks; import org.sonar.plugins.javascript.analysis.JsTsSensor; -import org.sonar.plugins.javascript.analysis.TsConfigCacheImpl; +import org.sonar.plugins.javascript.sonarlint.TsConfigCacheImpl; import org.sonar.plugins.javascript.analysis.TsConfigProvider; import org.sonar.plugins.javascript.analysis.YamlSensor; import org.sonar.plugins.javascript.bridge.AnalysisWarningsWrapper; @@ -59,7 +59,6 @@ import org.sonar.plugins.javascript.rules.JavaScriptRulesDefinition; import org.sonar.plugins.javascript.rules.TslintRulesDefinition; import org.sonar.plugins.javascript.rules.TypeScriptRulesDefinition; -import org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingCheckerImpl; public class JavaScriptPlugin implements Plugin { @@ -121,10 +120,15 @@ public class JavaScriptPlugin implements Plugin { public static final String TS_EXCLUSIONS_KEY = "sonar.typescript.exclusions"; public static final String[] EXCLUSIONS_DEFAULT_VALUE = new String[] { "**/node_modules/**", + "**/node_modules", "**/bower_components/**", + "**/bower_components", "**/dist/**", + "**/dist", "**/vendor/**", + "**/vendor", "**/external/**", + "**/external", "**/*.d.ts", }; @@ -338,7 +342,6 @@ public void addSonarLintExtensions( SonarLintPluginAPIVersion sonarLintPluginAPIVersion ) { if (sonarLintPluginAPIVersion.isDependencyAvailable()) { - context.addExtension(SonarLintTypeCheckingCheckerImpl.class); context.addExtension(TsConfigCacheImpl.class); } else { LOG.debug("Error while trying to inject SonarLint extensions"); 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 76a53528411..a82dd2f0d4c 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 @@ -26,6 +26,7 @@ import org.sonar.api.scanner.ScannerSide; import org.sonar.plugins.javascript.bridge.AnalysisWarningsWrapper; import org.sonar.plugins.javascript.bridge.BridgeServer; +import org.sonar.plugins.javascript.sonarlint.TsConfigCache; import org.sonar.plugins.javascript.utils.ProgressReport; import org.sonarsource.api.sonarlint.SonarLintSide; 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 afd7bd0ec30..711011d123a 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 @@ -37,7 +37,7 @@ import org.sonar.plugins.javascript.TypeScriptLanguage; import org.sonar.plugins.javascript.bridge.AnalysisMode; import org.sonar.plugins.javascript.bridge.BridgeServer; -import org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingChecker; +import org.sonar.plugins.javascript.sonarlint.TsConfigCache; @DependedUpon("js-analysis") public class JsTsSensor extends AbstractBridgeSensor { @@ -46,7 +46,6 @@ public class JsTsSensor extends AbstractBridgeSensor { private final AnalysisWithProgram analysisWithProgram; private final AnalysisWithWatchProgram analysisWithWatchProgram; private final JsTsChecks checks; - private final SonarLintTypeCheckingChecker javaScriptProjectChecker; private final AnalysisConsumers consumers; private final TsConfigCache tsConfigCache; @@ -57,30 +56,11 @@ public JsTsSensor( AnalysisWithProgram analysisWithProgram, AnalysisWithWatchProgram analysisWithWatchProgram, AnalysisConsumers consumers - ) { - this( - checks, - bridgeServer, - null, - analysisWithProgram, - analysisWithWatchProgram, - consumers - ); - } - - public JsTsSensor( - JsTsChecks checks, - BridgeServer bridgeServer, - @Nullable SonarLintTypeCheckingChecker javaScriptProjectChecker, - AnalysisWithProgram analysisWithProgram, - AnalysisWithWatchProgram analysisWithWatchProgram, - AnalysisConsumers consumers ) { super(bridgeServer, "JS/TS"); this.analysisWithProgram = analysisWithProgram; this.analysisWithWatchProgram = analysisWithWatchProgram; this.checks = checks; - this.javaScriptProjectChecker = javaScriptProjectChecker; this.consumers = consumers; this.tsConfigCache = analysisWithWatchProgram.tsConfigCache; } @@ -115,7 +95,6 @@ protected void analyzeFiles(List inputFiles) throws IOException { var tsConfigs = getTsConfigs( contextUtils, - javaScriptProjectChecker, this::createTsConfigFile, tsConfigCache ); diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java index e2709c0e4d7..631c64da1fd 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java @@ -19,8 +19,10 @@ */ package org.sonar.plugins.javascript.analysis; +import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import static java.util.stream.Stream.concat; import com.google.gson.Gson; import java.io.File; @@ -36,6 +38,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import java.util.function.Predicate; import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,8 +47,13 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.WildcardPattern; import org.sonar.plugins.javascript.JavaScriptFilePredicate; -import org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingChecker; +import org.sonar.plugins.javascript.JavaScriptLanguage; +import org.sonar.plugins.javascript.JavaScriptPlugin; +import org.sonar.plugins.javascript.TypeScriptLanguage; +import org.sonar.plugins.javascript.sonarlint.TsConfigCache; import org.sonarsource.analyzer.commons.FileProvider; public class TsConfigProvider { @@ -80,12 +88,11 @@ interface TsConfigFileCreator { */ static List getTsConfigs( ContextUtils contextUtils, - @Nullable SonarLintTypeCheckingChecker javaScriptProjectChecker, TsConfigProvider.TsConfigFileCreator tsConfigFileCreator, @Nullable TsConfigCache tsConfigCache ) throws IOException { var defaultProvider = contextUtils.isSonarLint() - ? new TsConfigProvider.WildcardTsConfigProvider(javaScriptProjectChecker, tsConfigFileCreator) + ? new TsConfigProvider.WildcardTsConfigProvider(tsConfigCache, tsConfigFileCreator) : new TsConfigProvider.DefaultTsConfigProvider(tsConfigFileCreator, JavaScriptFilePredicate::getJsTsPredicate); @@ -192,9 +199,11 @@ public List tsconfigs(SensorContext context) { if (tsconfigs != null) { return tsconfigs; } - } var fs = context.fileSystem(); + var fileCount = 0; + var fileFilter = new FileFilter(context.config()); + var pathFilter = new PathFilter(context.config()); var tsconfigs = new ArrayList(); var dirs = new ArrayDeque(); dirs.add(fs.baseDir()); @@ -205,20 +214,89 @@ public List tsconfigs(SensorContext context) { continue; } for (var file : files) { - if (file.isDirectory() && !"node_modules".equals(file.getName())) { + if (file.isDirectory() && !pathFilter.test(file.toPath())) { dirs.add(file); - } else if ("tsconfig.json".equals(file.getName())) { - tsconfigs.add(file.getAbsolutePath()); + } else { + if (fileFilter.test(file.toPath())) { + fileCount++; + } else if ("tsconfig.json".equals(file.getName())) { + tsconfigs.add(file.getAbsolutePath()); + } } } } LOG.info("Found {} tsconfig.json file(s): {}", tsconfigs.size(), tsconfigs); + if (cache != null) { + cache.setProjectSize(fileCount); + } return tsconfigs; } public TsConfigOrigin type() { return TsConfigOrigin.LOOKUP; } + + static class FileFilter implements Predicate { + + private final Set extensions = new HashSet<>(); + + public FileFilter(Configuration config) { + extensions.addAll( + Arrays.asList( + config + .get(JavaScriptLanguage.FILE_SUFFIXES_KEY) + .orElse(JavaScriptLanguage.FILE_SUFFIXES_DEFVALUE) + .split(",") + ) + ); + extensions.addAll( + Arrays.asList( + config + .get(TypeScriptLanguage.FILE_SUFFIXES_KEY) + .orElse(TypeScriptLanguage.FILE_SUFFIXES_DEFVALUE) + .split(",") + ) + ); + } + + @Override + public boolean test(Path path) { + return extensions.stream().anyMatch(ext -> path.toString().endsWith(ext)); + } + } + + static class PathFilter implements Predicate { + + private final WildcardPattern[] exclusions; + + public PathFilter(Configuration config) { + if (!isExclusionOverridden(config)) { + exclusions = WildcardPattern.create(JavaScriptPlugin.EXCLUSIONS_DEFAULT_VALUE); + } else { + WildcardPattern[] jsExcludedPatterns = WildcardPattern.create( + config.getStringArray(JavaScriptPlugin.JS_EXCLUSIONS_KEY) + ); + WildcardPattern[] tsExcludedPatterns = WildcardPattern.create( + config.getStringArray(JavaScriptPlugin.TS_EXCLUSIONS_KEY) + ); + exclusions = + concat(stream(jsExcludedPatterns), stream(tsExcludedPatterns)) + .toArray(WildcardPattern[]::new); + } + } + + private static boolean isExclusionOverridden(Configuration config) { + return ( + config.get(JavaScriptPlugin.JS_EXCLUSIONS_KEY).isPresent() || + config.get(JavaScriptPlugin.TS_EXCLUSIONS_KEY).isPresent() + ); + } + + @Override + public boolean test(Path path) { + return WildcardPattern.match(exclusions, path.toString().replaceAll("[\\\\/]", "/")); + } + } } abstract static class GeneratedTsConfigFileProvider implements Provider { @@ -306,6 +384,9 @@ private File writeToJsonFile(TsConfig tsConfig) throws IOException { } static class WildcardTsConfigProvider extends GeneratedTsConfigFileProvider { + static final String MAX_FILES_PROPERTY = "sonar.javascript.sonarlint.typechecking.maxfiles"; + static final int DEFAULT_MAX_FILES_FOR_TYPE_CHECKING = 20_000; + private static String getProjectRoot(SensorContext context) { var projectBaseDir = context.fileSystem().baseDir().getAbsolutePath(); return "/".equals(File.separator) @@ -317,20 +398,20 @@ private static String getProjectRoot(SensorContext context) { new ConcurrentHashMap<>(); final TsConfigFileCreator tsConfigFileCreator; - SonarLintTypeCheckingChecker checker; + final TsConfigCache tsConfigCache; WildcardTsConfigProvider( - @Nullable SonarLintTypeCheckingChecker checker, + @Nullable TsConfigCache tsConfigCache, TsConfigFileCreator tsConfigFileCreator ) { super(SonarProduct.SONARLINT); this.tsConfigFileCreator = tsConfigFileCreator; - this.checker = checker; + this.tsConfigCache = tsConfigCache; } @Override List getDefaultTsConfigs(SensorContext context) { - boolean deactivated = checker == null || checker.isBeyondLimit(context); + boolean deactivated = tsConfigCache == null || isBeyondLimit(context, tsConfigCache.getProjectSize()); if (deactivated) { return emptyList(); } else { @@ -347,5 +428,40 @@ List writeTsConfigFileFor(String root) { LOG.debug("Using generated tsconfig.json file using wildcards {}", file); return file; } + + static boolean isBeyondLimit(SensorContext context, int projectSize) { + var typeCheckingLimit = getTypeCheckingLimit(context); + + var beyondLimit = projectSize >= typeCheckingLimit; + if (!beyondLimit) { + LOG.info("Turning on type-checking of JavaScript files"); + } else { + // TypeScript type checking mechanism creates performance issues for large projects. Analyzing a file can take more than a minute in + // SonarLint, and it can even lead to runtime errors due to Node.js being out of memory during the process. + LOG.warn( + "Turning off type-checking of JavaScript files due to the project size exceeding the limit ({} files)", + typeCheckingLimit + ); + LOG.warn("This may cause rules dependent on type information to not behave as expected"); + LOG.warn( + "Check the list of impacted rules at https://rules.sonarsource.com/javascript/tag/type-dependent" + ); + LOG.warn( + "To turn type-checking back on, increase the \"{}\" property value", + MAX_FILES_PROPERTY + ); + LOG.warn( + "Please be aware that this could potentially impact the performance of the analysis" + ); + } + return beyondLimit; + } + + static int getTypeCheckingLimit(SensorContext context) { + return Math.max( + context.config().getInt(MAX_FILES_PROPERTY).orElse(DEFAULT_MAX_FILES_FOR_TYPE_CHECKING), + 0 + ); + } } } diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingChecker.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingChecker.java deleted file mode 100644 index e74046284c9..00000000000 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingChecker.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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.sonarlint; - -import org.sonar.api.batch.sensor.SensorContext; - -public interface SonarLintTypeCheckingChecker { - boolean isBeyondLimit(SensorContext context); -} diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerImpl.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerImpl.java deleted file mode 100644 index 4b54a1b9eb9..00000000000 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerImpl.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.sonarlint; - -import static org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingFilter.FileFilter; -import static org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingFilter.PathFilter; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.stream.Stream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.plugins.javascript.utils.PathWalker; -import org.sonarsource.api.sonarlint.SonarLintSide; - -@SonarLintSide(lifespan = "MODULE") -public class SonarLintTypeCheckingCheckerImpl implements SonarLintTypeCheckingChecker { - - private static final Logger LOG = LoggerFactory.getLogger(SonarLintTypeCheckingCheckerImpl.class); - static final String MAX_FILES_PROPERTY = "sonar.javascript.sonarlint.typechecking.maxfiles"; - static final int DEFAULT_MAX_FILES_FOR_TYPE_CHECKING = 20_000; - private static final int FILE_WALK_MAX_DEPTH = 20; - - private boolean beyondLimit = true; - - private boolean shouldCheck = true; - - public boolean isBeyondLimit(SensorContext context) { - if (shouldCheck) { - checkLimit(context); - shouldCheck = false; - } - return beyondLimit; - } - - private void checkLimit(SensorContext context) { - try { - var typeCheckingLimit = getTypeCheckingLimit(context); - var projectSize = countProjectSize(context, typeCheckingLimit); - - beyondLimit = projectSize >= typeCheckingLimit; - if (!beyondLimit) { - LOG.info("Turning on type-checking of JavaScript files"); - } else { - // TypeScript type checking mechanism creates performance issues for large projects. Analyzing a file can take more than a minute in - // SonarLint, and it can even lead to runtime errors due to Node.js being out of memory during the process. - LOG.warn( - "Turning off type-checking of JavaScript files due to the project size exceeding the limit ({} files)", - typeCheckingLimit - ); - LOG.warn("This may cause rules dependent on type information to not behave as expected"); - LOG.warn( - "Check the list of impacted rules at https://rules.sonarsource.com/javascript/tag/type-dependent" - ); - LOG.warn( - "To turn type-checking back on, increase the \"{}\" property value", - MAX_FILES_PROPERTY - ); - LOG.warn( - "Please be aware that this could potentially impact the performance of the analysis" - ); - } - } catch (RuntimeException e) { - // Any runtime error raised by the SonarLint API would be caught here to let the analyzer proceed with the rules that don't require - // type checking. - LOG.warn("Turning off type-checking of JavaScript files due to unexpected error", e); - } - } - - private static long countProjectSize(SensorContext context, long maxSize) { - var fileFilter = new FileFilter(context.config()); - - try (var files = walkProjectFiles(context)) { - return files - .filter(path -> Files.isRegularFile(path) && fileFilter.test(path)) - .limit(maxSize) - .count(); - } - } - - private static Stream walkProjectFiles(SensorContext context) { - // The Files.walk() is failing on Windows with WSL (see https://bugs.openjdk.org/browse/JDK-8259617) - return PathWalker.stream( - context.fileSystem().baseDir().toPath(), - FILE_WALK_MAX_DEPTH, - new PathFilter(context.config()) - ); - } - - private static int getTypeCheckingLimit(SensorContext context) { - return Math.max( - context.config().getInt(MAX_FILES_PROPERTY).orElse(DEFAULT_MAX_FILES_FOR_TYPE_CHECKING), - 0 - ); - } -} diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingFilter.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingFilter.java deleted file mode 100644 index fa98c66289c..00000000000 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingFilter.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.sonarlint; - -import static java.util.Arrays.stream; -import static java.util.stream.Stream.concat; - -import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Predicate; -import org.sonar.api.config.Configuration; -import org.sonar.api.utils.WildcardPattern; -import org.sonar.plugins.javascript.JavaScriptLanguage; -import org.sonar.plugins.javascript.JavaScriptPlugin; -import org.sonar.plugins.javascript.TypeScriptLanguage; -import org.sonar.plugins.javascript.filter.JavaScriptExclusionsFileFilter; - -/** - * This class partially reproduces the behavior of JavaScriptExclusionsFileFilter's implementation. - * - * The support of JavaScript type-checking in SonarLint context depends on the project size, which - * is manually computed by walking the file system. During the traversal, we need to replicate the - * plugin's file filtering logic to consider the same set of files during counting. - * - * The replication is partial; it limits itself to consider files with the expected extensions and - * located in directories that should not be excluded. However, minified files and files that are - * too big are not excluded, as they would require reading their content. Furthermore, TypeScript - * compiler would consider them regardless on program creation. - * - * @see JavaScriptExclusionsFileFilter - */ -public class SonarLintTypeCheckingFilter { - - private SonarLintTypeCheckingFilter() {} - - static class FileFilter implements Predicate { - - private final Set extensions = new HashSet<>(); - - public FileFilter(Configuration config) { - extensions.addAll( - Arrays.asList( - config - .get(JavaScriptLanguage.FILE_SUFFIXES_KEY) - .orElse(JavaScriptLanguage.FILE_SUFFIXES_DEFVALUE) - .split(",") - ) - ); - extensions.addAll( - Arrays.asList( - config - .get(TypeScriptLanguage.FILE_SUFFIXES_KEY) - .orElse(TypeScriptLanguage.FILE_SUFFIXES_DEFVALUE) - .split(",") - ) - ); - } - - @Override - public boolean test(Path path) { - return extensions.stream().anyMatch(ext -> path.toString().endsWith(ext)); - } - } - - static class PathFilter implements Predicate { - - private final WildcardPattern[] exclusions; - - public PathFilter(Configuration config) { - if (!isExclusionOverridden(config)) { - exclusions = WildcardPattern.create(JavaScriptPlugin.EXCLUSIONS_DEFAULT_VALUE); - } else { - WildcardPattern[] jsExcludedPatterns = WildcardPattern.create( - config.getStringArray(JavaScriptPlugin.JS_EXCLUSIONS_KEY) - ); - WildcardPattern[] tsExcludedPatterns = WildcardPattern.create( - config.getStringArray(JavaScriptPlugin.TS_EXCLUSIONS_KEY) - ); - exclusions = - concat(stream(jsExcludedPatterns), stream(tsExcludedPatterns)) - .toArray(WildcardPattern[]::new); - } - } - - private static boolean isExclusionOverridden(Configuration config) { - return ( - config.get(JavaScriptPlugin.JS_EXCLUSIONS_KEY).isPresent() || - config.get(JavaScriptPlugin.TS_EXCLUSIONS_KEY).isPresent() - ); - } - - @Override - public boolean test(Path path) { - return WildcardPattern.match(exclusions, path.toString().replaceAll("[\\\\/]", "/")); - } - } -} diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigCache.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/TsConfigCache.java similarity index 69% rename from sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigCache.java rename to sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/TsConfigCache.java index 6efa9bc14cd..924d0a54956 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigCache.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/TsConfigCache.java @@ -1,7 +1,8 @@ -package org.sonar.plugins.javascript.analysis; +package org.sonar.plugins.javascript.sonarlint; import java.util.List; import org.sonar.api.batch.fs.InputFile; +import org.sonar.plugins.javascript.analysis.TsConfigOrigin; import org.sonar.plugins.javascript.bridge.TsConfigFile; public interface TsConfigCache { @@ -9,4 +10,7 @@ public interface TsConfigCache { void initializeWith(List tsConfigs, TsConfigOrigin origin); List listCachedTsConfigs(TsConfigOrigin origin); void setOrigin(TsConfigOrigin origin); + + void setProjectSize(int projectSize); + int getProjectSize(); } diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigCacheImpl.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/TsConfigCacheImpl.java similarity index 95% rename from sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigCacheImpl.java rename to sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/TsConfigCacheImpl.java index 87268730c9f..7c7320fa5c8 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigCacheImpl.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/sonarlint/TsConfigCacheImpl.java @@ -1,4 +1,4 @@ -package org.sonar.plugins.javascript.analysis; +package org.sonar.plugins.javascript.sonarlint; import java.nio.file.Path; @@ -17,6 +17,7 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.scanner.ScannerSide; import org.sonar.plugins.javascript.JavaScriptFilePredicate; +import org.sonar.plugins.javascript.analysis.TsConfigOrigin; import org.sonar.plugins.javascript.bridge.BridgeServer; import org.sonar.plugins.javascript.bridge.TsConfigFile; import org.sonarsource.api.sonarlint.SonarLintSide; @@ -30,10 +31,11 @@ public class TsConfigCacheImpl implements TsConfigCache, ModuleFileListener { BridgeServer bridgeServer; TsConfigOrigin origin; + int projectSize; Map cacheMap = new EnumMap<>(TsConfigOrigin.class); - TsConfigCacheImpl(BridgeServer bridgeServer) { + public TsConfigCacheImpl(BridgeServer bridgeServer) { this.bridgeServer = bridgeServer; cacheMap.put(TsConfigOrigin.PROPERTY, new Cache()); cacheMap.put(TsConfigOrigin.LOOKUP, new Cache()); @@ -186,4 +188,12 @@ public void process(ModuleFileEvent moduleFileEvent) { cacheMap.values().forEach(Cache::clearFileToTsConfigCache); } } + + public void setProjectSize(int projectSize) { + this.projectSize = projectSize; + } + + public int getProjectSize() { + return projectSize; + } } 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 1b2522534bd..694373ece5a 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 @@ -43,7 +43,7 @@ class JavaScriptPluginTest { private static final int JS_ADDITIONAL_EXTENSIONS = 4; private static final int TS_ADDITIONAL_EXTENSIONS = 3; private static final int CSS_ADDITIONAL_EXTENSIONS = 3; - private static final int SONARLINT_ADDITIONAL_EXTENSIONS = 2; + private static final int SONARLINT_ADDITIONAL_EXTENSIONS = 1; public static final Version LTS_VERSION = Version.create(7, 9); 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 d4622a5fb87..6956089221b 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 @@ -28,7 +28,6 @@ import java.util.Collection; import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -78,7 +77,7 @@ import org.sonar.plugins.javascript.bridge.ServerAlreadyFailedException; import org.sonar.plugins.javascript.bridge.TsConfigFile; import org.sonar.plugins.javascript.nodejs.NodeCommandException; -import org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingChecker; +import org.sonar.plugins.javascript.sonarlint.TsConfigCacheImpl; import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; @@ -389,7 +388,7 @@ void should_save_only_nosonar_metric_in_sonarlint() throws Exception { when(bridgeServerMock.loadTsConfig(any())).thenReturn(tsConfigFile); context.setRuntime(SonarRuntimeImpl.forSonarLint(Version.create(4, 4))); - var sensor = createSensor(mock(SonarLintTypeCheckingChecker.class)); + var sensor = createSensor(); sensor.execute(context); assertThat(inputFile.hasNoSonarAt(7)).isTrue(); @@ -643,7 +642,7 @@ void should_send_content_on_sonarlint() throws Exception { when(bridgeServerMock.analyzeJavaScript(any())).thenReturn(new AnalysisResponse()); var captor = ArgumentCaptor.forClass(JsAnalysisRequest.class); - createSensor(mock(SonarLintTypeCheckingChecker.class)).execute(ctx); + createSensor().execute(ctx); verify(bridgeServerMock).analyzeJavaScript(captor.capture()); assertThat(captor.getValue().fileContent()) .isEqualTo("if (cond)\n" + "doFoo(); \n" + "else \n" + "doFoo();"); @@ -786,16 +785,9 @@ private DefaultInputFile createTestInputFile(SensorContextTester context) { } private JsTsSensor createSensor() { - return createSensor(null); - } - - private JsTsSensor createSensor( - @Nullable SonarLintTypeCheckingChecker sonarlintTypeCheckingChecker - ) { return new JsTsSensor( checks(ESLINT_BASED_RULE, "S2260", "S1451"), bridgeServerMock, - sonarlintTypeCheckingChecker, analysisWithProgram, 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 77aaa7221b2..beab1df632a 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 @@ -41,9 +41,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -97,6 +95,8 @@ import org.sonar.plugins.javascript.bridge.protobuf.Position; import org.sonar.plugins.javascript.bridge.protobuf.Program; import org.sonar.plugins.javascript.bridge.protobuf.SourceLocation; +import org.sonar.plugins.javascript.sonarlint.TsConfigCache; +import org.sonar.plugins.javascript.sonarlint.TsConfigCacheImpl; import static java.util.Collections.emptyList; import static java.util.Collections.singleton; diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingFilterTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java similarity index 93% rename from sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingFilterTest.java rename to sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java index 22e7b42dd7c..fe89b249763 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingFilterTest.java +++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java @@ -17,11 +17,11 @@ * 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.sonarlint; +package org.sonar.plugins.javascript.analysis; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingFilter.FileFilter; -import static org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingFilter.PathFilter; +import static org.sonar.plugins.javascript.analysis.TsConfigProvider.LookupTsConfigProvider.FileFilter; +import static org.sonar.plugins.javascript.analysis.TsConfigProvider.LookupTsConfigProvider.PathFilter; import java.io.IOException; import java.nio.file.Files; @@ -34,7 +34,7 @@ import org.sonar.plugins.javascript.JavaScriptPlugin; import org.sonar.plugins.javascript.TypeScriptLanguage; -class SonarLintTypeCheckingFilterTest { +class LookupConfigProviderFilterTest { @TempDir Path baseDir; diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigCacheTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigCacheTest.java index a494a5c34bd..bfe2ae5e0c2 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigCacheTest.java +++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigCacheTest.java @@ -53,6 +53,7 @@ import org.sonar.plugins.javascript.TypeScriptLanguage; import org.sonar.plugins.javascript.bridge.BridgeServerImpl; import org.sonar.plugins.javascript.bridge.TsConfigFile; +import org.sonar.plugins.javascript.sonarlint.TsConfigCacheImpl; import org.sonarsource.sonarlint.core.analysis.container.module.DefaultModuleFileEvent; import org.sonarsource.sonarlint.plugin.api.module.file.ModuleFileEvent; @@ -102,7 +103,7 @@ void test() throws Exception { Files.createFile(tsConfigPath); } SensorContextTester ctx = SensorContextTester.create(baseDir); - TsConfigProvider.getTsConfigs(new ContextUtils(ctx), null, this::tsConfigFileCreator, tsConfigCache); + TsConfigProvider.getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); when(bridgeServerMock.loadTsConfig(any())) .thenAnswer(invocationOnMock -> { @@ -158,7 +159,7 @@ void testResolvesReferences() throws IOException { Files.createFile(tsconfig2); SensorContextTester ctx = SensorContextTester.create(baseDir); - TsConfigProvider.getTsConfigs(new ContextUtils(ctx), null, this::tsConfigFileCreator, tsConfigCache); + TsConfigProvider.getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); when(bridgeServerMock.loadTsConfig(any())).thenAnswer(invocationOnMock -> { String tsConfigPath = (String) invocationOnMock.getArguments()[0]; if (tsConfigPath.equals(tsConfigFile1.getFilename())) { @@ -205,7 +206,7 @@ void testPropertyTsConfigChanged() throws IOException { "tsconfig.*.json,tsconfig.json" ) ); - TsConfigProvider.getTsConfigs(new ContextUtils(ctx), null, this::tsConfigFileCreator, tsConfigCache); + TsConfigProvider.getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); when(bridgeServerMock.loadTsConfig(any())).thenReturn(tsConfigFile); var foundTsConfig = tsConfigCache.getTsConfigForInputFile(file1); @@ -219,7 +220,7 @@ void testPropertyTsConfigChanged() throws IOException { var propertyCachedTsConfig = tsConfigCache.listCachedTsConfigs(TsConfigOrigin.PROPERTY); assertThat(propertyCachedTsConfig).containsExactly(tsconfig1.toAbsolutePath().toString()); - TsConfigProvider.getTsConfigs(new ContextUtils(ctx), null, this::tsConfigFileCreator, tsConfigCache); + TsConfigProvider.getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); propertyCachedTsConfig = tsConfigCache.listCachedTsConfigs(TsConfigOrigin.PROPERTY); assertThat(propertyCachedTsConfig).containsExactlyInAnyOrder(tsconfig1.toAbsolutePath().toString(), tsconfig2.toAbsolutePath().toString()); } @@ -231,7 +232,7 @@ private Pair prepareFileAndTsConfig() throws IOExceptio Files.createFile(tsconfig1); SensorContextTester ctx = SensorContextTester.create(baseDir); - TsConfigProvider.getTsConfigs(new ContextUtils(ctx), null, this::tsConfigFileCreator, tsConfigCache); + TsConfigProvider.getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); when(bridgeServerMock.loadTsConfig(any())).thenReturn(tsConfigFile); return Pair.of(file1, tsConfigFile); } diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java index 428aeb8f018..ff484df3158 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java +++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java @@ -36,13 +36,18 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; import org.sonar.api.batch.fs.internal.DefaultInputFile; import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.config.Configuration; import org.sonar.api.config.internal.MapSettings; import org.sonar.api.impl.utils.DefaultTempFolder; import org.sonar.api.internal.SonarRuntimeImpl; @@ -50,7 +55,9 @@ import org.sonar.api.utils.TempFolder; import org.sonar.api.utils.Version; import org.sonar.api.utils.log.LoggerLevel; -import org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingChecker; +import org.sonar.plugins.javascript.bridge.BridgeServer; +import org.sonar.plugins.javascript.sonarlint.TsConfigCache; +import org.sonar.plugins.javascript.sonarlint.TsConfigCacheImpl; class TsConfigProviderTest { @@ -88,7 +95,6 @@ void should_lookup_tsconfig_files() throws Exception { List tsconfigs = getTsConfigs( new ContextUtils(ctx), - null, this::tsConfigFileCreator, null ); @@ -117,7 +123,6 @@ void should_use_tsconfig_from_property() throws Exception { List tsconfigs = getTsConfigs( new ContextUtils(ctx), - null, this::tsConfigFileCreator, null ); @@ -141,7 +146,6 @@ void should_use_absolute_path_from_property() throws Exception { List tsconfigs = getTsConfigs( new ContextUtils(ctx), - null, this::tsConfigFileCreator, null ); @@ -167,7 +171,6 @@ void should_use_multiple_tsconfigs_from_property() throws Exception { List tsconfigs = getTsConfigs( new ContextUtils(ctx), - null, this::tsConfigFileCreator, null ); @@ -205,7 +208,6 @@ void should_use_matching_tsconfigs_from_property() throws Exception { List tsconfigs = getTsConfigs( new ContextUtils(ctx), - null, this::tsConfigFileCreator, null ); @@ -228,7 +230,6 @@ void should_use_tsconfigs_from_property_alias() throws Exception { List tsconfigs = getTsConfigs( new ContextUtils(ctx), - null, this::tsConfigFileCreator, null ); @@ -248,18 +249,14 @@ void should_create_tsconfig() throws Exception { List tsconfigs = getTsConfigs( new ContextUtils(ctx), - null, this::tsConfigFileCreator, null ); assertThat(tsconfigs).hasSize(1); - String tsconfig = new String( - Files.readAllBytes(Paths.get(tsconfigs.get(0))), - StandardCharsets.UTF_8 - ); + String tsconfig = Files.readString(Paths.get(tsconfigs.get(0))); assertThat(tsconfig) .isEqualTo( - "{\"files\":[\"moduleKey/file1.ts\",\"moduleKey/file2.ts\"],\"compilerOptions\":{\"allowJs\":true,\"noImplicitAny\":true}}" + String.format("{\"files\":[\"%s/file1.ts\",\"%s/file2.ts\"],\"compilerOptions\":{\"allowJs\":true,\"noImplicitAny\":true}}", baseDir.toString(), baseDir.toString()) ); } @@ -269,15 +266,9 @@ void should_create_wildcard_tsconfig() throws Exception { ctx.setRuntime(SonarRuntimeImpl.forSonarLint(Version.create(4, 4))); createInputFile(ctx, "file1.js"); createInputFile(ctx, "file2.js"); + var tsConfigCache = tsConfigCache(); + var tsconfigs = getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); - var checker = mock(SonarLintTypeCheckingChecker.class); - when(checker.isBeyondLimit(ctx)).thenReturn(false); - - var provider = new WildcardTsConfigProvider( - checker, - TsConfigProviderTest::createTsConfigFile - ); - var tsconfigs = provider.tsconfigs(ctx); assertThat(tsconfigs) .hasSize(1) .extracting(path -> Files.readString(Paths.get(path))) @@ -287,42 +278,36 @@ void should_create_wildcard_tsconfig() throws Exception { baseDir.toFile().getAbsolutePath().replace(File.separator, "/") ) ); - - when(checker.isBeyondLimit(ctx)).thenReturn(true); - provider = - new WildcardTsConfigProvider( - checker, - TsConfigProviderTest::createTsConfigFile - ); - assertThat(provider.tsconfigs(ctx)).isEmpty(); - - provider = - new WildcardTsConfigProvider(checker, TsConfigProviderTest::createTsConfigFile); - assertThat(provider.tsconfigs(ctx)).isEmpty(); } @Test - void should_not_recreate_wildcart_tsconfig_in_sonarlint() throws Exception { - List tsconfigs; - Path file; - + void should_not_recreate_wildcard_tsconfig_in_sonarlint() throws Exception { var ctx = SensorContextTester.create(baseDir); ctx.setRuntime(SonarRuntimeImpl.forSonarLint(Version.create(4, 4))); - var checker = mock(SonarLintTypeCheckingChecker.class); - when(checker.isBeyondLimit(ctx)).thenReturn(false); + var tsConfigCache = tsConfigCache(); + var originalTsConfigs = getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); - tsconfigs = + var tsconfigs = new WildcardTsConfigProvider( - checker, + tsConfigCache, TsConfigProviderTest::createTsConfigFile ) .tsconfigs(ctx); - assertThat(tsconfigs).hasSize(1); + assertThat(tsconfigs).isEqualTo(originalTsConfigs); + } + + @Test + void should_not_create_wildcard_tsconfig_in_sonarlint() throws Exception { + var ctx = SensorContextTester.create(baseDir); + ctx.setRuntime(SonarRuntimeImpl.forSonarLint(Version.create(4, 4))); + ctx.setSettings(new MapSettings().setProperty(WildcardTsConfigProvider.MAX_FILES_PROPERTY, 1)); + createInputFile(ctx, "file.js"); + createInputFile(ctx, "file2.js"); - file = Path.of(tsconfigs.get(0)); - assertThat(file).exists(); - Files.delete(file); + var tsConfigCache = tsConfigCache(); + var tsconfigs = getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); + assertThat(tsconfigs).isEmpty(); } @Test @@ -330,25 +315,66 @@ void should_not_fail_on_exception() throws Exception { var ctx = SensorContextTester.create(baseDir); createInputFile(ctx, "file.js"); - var checker = mock(SonarLintTypeCheckingChecker.class); - when(checker.isBeyondLimit(ctx)).thenReturn(false); + var tsConfigCache = tsConfigCache(); + tsConfigCache.setProjectSize(1); var fileWriter = mock(TsConfigFileCreator.class); when(fileWriter.createTsConfigFile(anyString())).thenThrow(IOException.class); var wildcardTsConfigProvider = new WildcardTsConfigProvider( - checker, + tsConfigCache, fileWriter ); assertThat(wildcardTsConfigProvider.tsconfigs(ctx)).isEmpty(); } - private static void createInputFile(SensorContextTester context, String relativePath) { - DefaultInputFile inputFile = new TestInputFileBuilder("moduleKey", relativePath) + @Test + void should_check_javascript_files() throws IOException { + logger.setLevel(LoggerLevel.INFO); + var ctx = SensorContextTester.create(baseDir); + ctx.setRuntime(SonarRuntimeImpl.forSonarLint(Version.create(4, 4))); + createInputFile(ctx, "file.js"); + createInputFile(ctx, "file.css"); + createInputFile(ctx, "file.d.ts"); + Files.createDirectory(Path.of(baseDir.toString(), "node_modules")); + createInputFile(ctx, "node_modules/dep.js"); + + var tsConfigCache = tsConfigCache(); + getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); + assertThat(logger.logs()).contains("Turning on type-checking of JavaScript files"); + } + + @Test + void should_detect_projects_with_too_many_files() throws IOException { + logger.setLevel(LoggerLevel.WARN); + var ctx = SensorContextTester.create(baseDir); + ctx.setSettings( + new MapSettings().setProperty(WildcardTsConfigProvider.MAX_FILES_PROPERTY, 3) + ); + createInputFile(ctx, "file1.js"); + createInputFile(ctx, "file2.ts"); + createInputFile(ctx, "file3.cjs"); + createInputFile(ctx, "file4.cts"); + var tsConfigCache = tsConfigCache(); + getTsConfigs(new ContextUtils(ctx), this::tsConfigFileCreator, tsConfigCache); + assertThat(WildcardTsConfigProvider.isBeyondLimit(ctx, tsConfigCache.getProjectSize())).isTrue(); + assertThat(logger.logs()) + .contains( + "Turning off type-checking of JavaScript files due to the project size exceeding the limit (3 files)", + "This may cause rules dependent on type information to not behave as expected", + "Check the list of impacted rules at https://rules.sonarsource.com/javascript/tag/type-dependent", + "To turn type-checking back on, increase the \"" + WildcardTsConfigProvider.MAX_FILES_PROPERTY + "\" property value", + "Please be aware that this could potentially impact the performance of the analysis" + ); + } + + private void createInputFile(SensorContextTester context, String relativePath) throws IOException { + DefaultInputFile inputFile = new TestInputFileBuilder(baseDir.toString(), relativePath) .setLanguage("ts") .setContents("if (cond)\ndoFoo(); \nelse \ndoFoo();") .build(); context.fileSystem().add(inputFile); + Files.createFile(Paths.get(baseDir.toString(), relativePath)); } private static String createTsConfigFile(String content) throws IOException { @@ -356,4 +382,8 @@ private static String createTsConfigFile(String content) throws IOException { Files.writeString(tempFile, content, StandardCharsets.UTF_8); return tempFile.toAbsolutePath().toString(); } + + private TsConfigCache tsConfigCache() { + return new TsConfigCacheImpl(mock(BridgeServer.class)); + } } diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerTest.java deleted file mode 100644 index 4dc55f04169..00000000000 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/sonarlint/SonarLintTypeCheckingCheckerTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.sonarlint; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingCheckerImpl.DEFAULT_MAX_FILES_FOR_TYPE_CHECKING; -import static org.sonar.plugins.javascript.sonarlint.SonarLintTypeCheckingCheckerImpl.MAX_FILES_PROPERTY; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.api.io.TempDir; -import org.sonar.api.batch.fs.internal.DefaultFileSystem; -import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.config.Configuration; -import org.sonar.api.testfixtures.log.LogTesterJUnit5; -import org.sonar.api.utils.log.LoggerLevel; - -class SonarLintTypeCheckingCheckerTest { - - @RegisterExtension - LogTesterJUnit5 logTester = new LogTesterJUnit5(); - - @TempDir - Path baseDir; - - @Test - void should_check_javascript_files() throws IOException { - logTester.setLevel(LoggerLevel.INFO); - inputFile("file.js"); - inputFile("file.css"); - inputFile("file.d.ts"); - inputFile("node_modules", "dep.js"); - - var checker = new SonarLintTypeCheckingCheckerImpl(); - assertThat(checker.isBeyondLimit(sensorContext(3))).isFalse(); - assertThat(logTester.logs()).contains("Turning on type-checking of JavaScript files"); - } - - @Test - void should_detect_projects_with_too_many_files() throws IOException { - logTester.setLevel(LoggerLevel.WARN); - inputFile("file1.js"); - inputFile("file2.ts"); - inputFile("file3.cjs"); - inputFile("file4.cts"); - var checker = new SonarLintTypeCheckingCheckerImpl(); - - assertThat(checker.isBeyondLimit(sensorContext(3))).isTrue(); - assertThat(logTester.logs()) - .contains( - "Turning off type-checking of JavaScript files due to the project size exceeding the limit (3 files)", - "This may cause rules dependent on type information to not behave as expected", - "Check the list of impacted rules at https://rules.sonarsource.com/javascript/tag/type-dependent", - "To turn type-checking back on, increase the \"" + MAX_FILES_PROPERTY + "\" property value", - "Please be aware that this could potentially impact the performance of the analysis" - ); - } - - @Test - void should_detect_errors() { - logTester.setLevel(LoggerLevel.WARN); - var checker = new SonarLintTypeCheckingCheckerImpl(); - var context = sensorContext(); - when(context.fileSystem().baseDir()).thenThrow(new IllegalArgumentException()); - - assertThat(checker.isBeyondLimit(context)).isTrue(); - assertThat(logTester.logs()) - .containsExactly("Turning off type-checking of JavaScript files due to unexpected error"); - } - - private SensorContext sensorContext() { - return sensorContext(DEFAULT_MAX_FILES_FOR_TYPE_CHECKING); - } - - private SensorContext sensorContext(int maxFiles) { - var config = mock(Configuration.class); - when(config.getInt(MAX_FILES_PROPERTY)).thenReturn(Optional.of(maxFiles)); - - var context = mock(SensorContext.class); - when(context.config()).thenReturn(config); - when(context.fileSystem()).thenReturn(new DefaultFileSystem(baseDir)); - return context; - } - - private void inputFile(String filename) throws IOException { - var path = baseDir.resolve(filename); - Files.writeString(path, "inputFile"); - } - - private Path inputFile(String dir, String filename) throws IOException { - var path = Files.createDirectories(baseDir.resolve(dir)).resolve(filename); - Files.writeString(path, "inputFile"); - return path; - } -} From 0fae84af1cd0315ed6273eb161c0800beede1612 Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Thu, 31 Oct 2024 12:14:30 +0100 Subject: [PATCH 2/8] extract filters --- .../analysis/LookupConfigProviderFilter.java | 82 +++++++++++++++++++ .../javascript/analysis/TsConfigProvider.java | 72 +--------------- .../LookupConfigProviderFilterTest.java | 4 +- 3 files changed, 86 insertions(+), 72 deletions(-) create mode 100644 sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java new file mode 100644 index 00000000000..0072789ef98 --- /dev/null +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java @@ -0,0 +1,82 @@ +package org.sonar.plugins.javascript.analysis; + +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.WildcardPattern; +import org.sonar.plugins.javascript.JavaScriptLanguage; +import org.sonar.plugins.javascript.JavaScriptPlugin; +import org.sonar.plugins.javascript.TypeScriptLanguage; + +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; + +import static java.util.Arrays.stream; +import static java.util.stream.Stream.concat; + +public class LookupConfigProviderFilter { + private LookupConfigProviderFilter() {} + + static class FileFilter implements Predicate { + + private final Set extensions = new HashSet<>(); + + public FileFilter(Configuration config) { + extensions.addAll( + Arrays.asList( + config + .get(JavaScriptLanguage.FILE_SUFFIXES_KEY) + .orElse(JavaScriptLanguage.FILE_SUFFIXES_DEFVALUE) + .split(",") + ) + ); + extensions.addAll( + Arrays.asList( + config + .get(TypeScriptLanguage.FILE_SUFFIXES_KEY) + .orElse(TypeScriptLanguage.FILE_SUFFIXES_DEFVALUE) + .split(",") + ) + ); + } + + @Override + public boolean test(Path path) { + return extensions.stream().anyMatch(ext -> path.toString().endsWith(ext)); + } + } + + static class PathFilter implements Predicate { + + private final WildcardPattern[] exclusions; + + public PathFilter(Configuration config) { + if (!isExclusionOverridden(config)) { + exclusions = WildcardPattern.create(JavaScriptPlugin.EXCLUSIONS_DEFAULT_VALUE); + } else { + WildcardPattern[] jsExcludedPatterns = WildcardPattern.create( + config.getStringArray(JavaScriptPlugin.JS_EXCLUSIONS_KEY) + ); + WildcardPattern[] tsExcludedPatterns = WildcardPattern.create( + config.getStringArray(JavaScriptPlugin.TS_EXCLUSIONS_KEY) + ); + exclusions = + concat(stream(jsExcludedPatterns), stream(tsExcludedPatterns)) + .toArray(WildcardPattern[]::new); + } + } + + private static boolean isExclusionOverridden(Configuration config) { + return ( + config.get(JavaScriptPlugin.JS_EXCLUSIONS_KEY).isPresent() || + config.get(JavaScriptPlugin.TS_EXCLUSIONS_KEY).isPresent() + ); + } + + @Override + public boolean test(Path path) { + return WildcardPattern.match(exclusions, path.toString().replaceAll("[\\\\/]", "/")); + } + } +} diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java index 631c64da1fd..669b25ddb39 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java @@ -19,10 +19,8 @@ */ package org.sonar.plugins.javascript.analysis; -import static java.util.Arrays.stream; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static java.util.stream.Stream.concat; import com.google.gson.Gson; import java.io.File; @@ -38,7 +36,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import java.util.function.Predicate; import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,12 +44,9 @@ import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.config.Configuration; -import org.sonar.api.utils.WildcardPattern; +import static org.sonar.plugins.javascript.analysis.LookupConfigProviderFilter.FileFilter; +import static org.sonar.plugins.javascript.analysis.LookupConfigProviderFilter.PathFilter; import org.sonar.plugins.javascript.JavaScriptFilePredicate; -import org.sonar.plugins.javascript.JavaScriptLanguage; -import org.sonar.plugins.javascript.JavaScriptPlugin; -import org.sonar.plugins.javascript.TypeScriptLanguage; import org.sonar.plugins.javascript.sonarlint.TsConfigCache; import org.sonarsource.analyzer.commons.FileProvider; @@ -235,68 +229,6 @@ public List tsconfigs(SensorContext context) { public TsConfigOrigin type() { return TsConfigOrigin.LOOKUP; } - - static class FileFilter implements Predicate { - - private final Set extensions = new HashSet<>(); - - public FileFilter(Configuration config) { - extensions.addAll( - Arrays.asList( - config - .get(JavaScriptLanguage.FILE_SUFFIXES_KEY) - .orElse(JavaScriptLanguage.FILE_SUFFIXES_DEFVALUE) - .split(",") - ) - ); - extensions.addAll( - Arrays.asList( - config - .get(TypeScriptLanguage.FILE_SUFFIXES_KEY) - .orElse(TypeScriptLanguage.FILE_SUFFIXES_DEFVALUE) - .split(",") - ) - ); - } - - @Override - public boolean test(Path path) { - return extensions.stream().anyMatch(ext -> path.toString().endsWith(ext)); - } - } - - static class PathFilter implements Predicate { - - private final WildcardPattern[] exclusions; - - public PathFilter(Configuration config) { - if (!isExclusionOverridden(config)) { - exclusions = WildcardPattern.create(JavaScriptPlugin.EXCLUSIONS_DEFAULT_VALUE); - } else { - WildcardPattern[] jsExcludedPatterns = WildcardPattern.create( - config.getStringArray(JavaScriptPlugin.JS_EXCLUSIONS_KEY) - ); - WildcardPattern[] tsExcludedPatterns = WildcardPattern.create( - config.getStringArray(JavaScriptPlugin.TS_EXCLUSIONS_KEY) - ); - exclusions = - concat(stream(jsExcludedPatterns), stream(tsExcludedPatterns)) - .toArray(WildcardPattern[]::new); - } - } - - private static boolean isExclusionOverridden(Configuration config) { - return ( - config.get(JavaScriptPlugin.JS_EXCLUSIONS_KEY).isPresent() || - config.get(JavaScriptPlugin.TS_EXCLUSIONS_KEY).isPresent() - ); - } - - @Override - public boolean test(Path path) { - return WildcardPattern.match(exclusions, path.toString().replaceAll("[\\\\/]", "/")); - } - } } abstract static class GeneratedTsConfigFileProvider implements Provider { diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java index fe89b249763..83f11b36721 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java +++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java @@ -20,8 +20,8 @@ package org.sonar.plugins.javascript.analysis; import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.plugins.javascript.analysis.TsConfigProvider.LookupTsConfigProvider.FileFilter; -import static org.sonar.plugins.javascript.analysis.TsConfigProvider.LookupTsConfigProvider.PathFilter; +import static org.sonar.plugins.javascript.analysis.LookupConfigProviderFilter.FileFilter; +import static org.sonar.plugins.javascript.analysis.LookupConfigProviderFilter.PathFilter; import java.io.IOException; import java.nio.file.Files; From 37f1b0cbb7a17c524db42e0805cbcf0cbca28766 Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Thu, 31 Oct 2024 12:18:11 +0100 Subject: [PATCH 3/8] add back the comment --- .../analysis/LookupConfigProviderFilter.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java index 0072789ef98..07ad011d7a7 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java @@ -15,6 +15,20 @@ import static java.util.Arrays.stream; import static java.util.stream.Stream.concat; +/** + * This class partially reproduces the behavior of JavaScriptExclusionsFileFilter's implementation. + * + * The support of JavaScript type-checking in SonarLint context depends on the project size, which + * is manually computed by walking the file system. During the traversal, we need to replicate the + * plugin's file filtering logic to consider the same set of files during counting. + * + * The replication is partial; it limits itself to consider files with the expected extensions and + * located in directories that should not be excluded. However, minified files and files that are + * too big are not excluded, as they would require reading their content. Furthermore, TypeScript + * compiler would consider them regardless on program creation. + * + * @see JavaScriptExclusionsFileFilter + */ public class LookupConfigProviderFilter { private LookupConfigProviderFilter() {} From a2f71ca1ba329fb858a3bedd7d976b96a5ae5dd3 Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Thu, 31 Oct 2024 14:23:51 +0100 Subject: [PATCH 4/8] add copyright and update test --- .../it/plugin/EslintBasedRulesTest.java | 2 +- .../analysis/LookupConfigProviderFilter.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/EslintBasedRulesTest.java b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/EslintBasedRulesTest.java index 5177e05fc64..30a09ea1c67 100644 --- a/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/EslintBasedRulesTest.java +++ b/its/plugin/tests/src/test/java/com/sonar/javascript/it/plugin/EslintBasedRulesTest.java @@ -141,7 +141,7 @@ void test_exclusion_filter() throws Exception { .setSourceEncoding("UTF-8") .setSourceDirs(".") .setProjectDir(TestUtils.projectDirNoCopy("file-filter/excluded_dir/project")) - .setProperty("sonar.javascript.exclusions", "excluded_dir/**"); + .setProperty("sonar.javascript.exclusions", "excluded_dir/**,**/node_modules"); OrchestratorStarter.setProfile(projectKey, jsProfile, "js"); diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java index 07ad011d7a7..c4f51b2c048 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java @@ -1,3 +1,22 @@ +/* + * 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 org.sonar.api.config.Configuration; From 7a1bd24275a1ced7a973484789b38ed7e20ecc26 Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Thu, 31 Oct 2024 14:48:09 +0100 Subject: [PATCH 5/8] fix more windows --- .../javascript/analysis/TsConfigProvider.java | 18 +++++++++--------- .../analysis/TsConfigProviderTest.java | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java index 669b25ddb39..9e76fe06981 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java @@ -319,26 +319,26 @@ static class WildcardTsConfigProvider extends GeneratedTsConfigFileProvider { static final String MAX_FILES_PROPERTY = "sonar.javascript.sonarlint.typechecking.maxfiles"; static final int DEFAULT_MAX_FILES_FOR_TYPE_CHECKING = 20_000; - private static String getProjectRoot(SensorContext context) { - var projectBaseDir = context.fileSystem().baseDir().getAbsolutePath(); - return "/".equals(File.separator) - ? projectBaseDir - : projectBaseDir.replace(File.separator, "/"); - } - private static final Map> defaultWildcardTsConfig = new ConcurrentHashMap<>(); - final TsConfigFileCreator tsConfigFileCreator; final TsConfigCache tsConfigCache; + final TsConfigFileCreator tsConfigFileCreator; WildcardTsConfigProvider( @Nullable TsConfigCache tsConfigCache, TsConfigFileCreator tsConfigFileCreator ) { super(SonarProduct.SONARLINT); - this.tsConfigFileCreator = tsConfigFileCreator; this.tsConfigCache = tsConfigCache; + this.tsConfigFileCreator = tsConfigFileCreator; + } + + private static String getProjectRoot(SensorContext context) { + var projectBaseDir = context.fileSystem().baseDir().getAbsolutePath(); + return "/".equals(File.separator) + ? projectBaseDir + : projectBaseDir.replace(File.separator, "/"); } @Override diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java index ff484df3158..4a5baaac9af 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java +++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java @@ -256,7 +256,7 @@ void should_create_tsconfig() throws Exception { String tsconfig = Files.readString(Paths.get(tsconfigs.get(0))); assertThat(tsconfig) .isEqualTo( - String.format("{\"files\":[\"%s/file1.ts\",\"%s/file2.ts\"],\"compilerOptions\":{\"allowJs\":true,\"noImplicitAny\":true}}", baseDir.toString(), baseDir.toString()) + String.format("{\"files\":[\"%s/file1.ts\",\"%s/file2.ts\"],\"compilerOptions\":{\"allowJs\":true,\"noImplicitAny\":true}}", baseDir.toString().replaceAll("[\\\\/]", "/"), baseDir.toString().replaceAll("[\\\\/]", "/")) ); } From abcab49a5f4693ebde0ab335e4ae34e156088ced Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Thu, 31 Oct 2024 15:28:26 +0100 Subject: [PATCH 6/8] windows fix --- .../sonar/plugins/javascript/analysis/TsConfigProviderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java index 4a5baaac9af..0cd45a99455 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java +++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/TsConfigProviderTest.java @@ -255,7 +255,7 @@ void should_create_tsconfig() throws Exception { assertThat(tsconfigs).hasSize(1); String tsconfig = Files.readString(Paths.get(tsconfigs.get(0))); assertThat(tsconfig) - .isEqualTo( + .isEqualToIgnoringCase( String.format("{\"files\":[\"%s/file1.ts\",\"%s/file2.ts\"],\"compilerOptions\":{\"allowJs\":true,\"noImplicitAny\":true}}", baseDir.toString().replaceAll("[\\\\/]", "/"), baseDir.toString().replaceAll("[\\\\/]", "/")) ); } From 34c19ff6d3d28803346bf3554512141e0eb7b143 Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Mon, 4 Nov 2024 09:52:58 +0100 Subject: [PATCH 7/8] Simplify settings, update path filter to test -> passing path --- .../plugins/javascript/JavaScriptPlugin.java | 5 ----- .../analysis/LookupConfigProviderFilter.java | 2 +- .../javascript/analysis/TsConfigProvider.java | 5 ++++- .../analysis/LookupConfigProviderFilterTest.java | 16 ++++++++-------- 4 files changed, 13 insertions(+), 15 deletions(-) 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 0cbe83fef0f..e798f3386c8 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 @@ -120,15 +120,10 @@ public class JavaScriptPlugin implements Plugin { public static final String TS_EXCLUSIONS_KEY = "sonar.typescript.exclusions"; public static final String[] EXCLUSIONS_DEFAULT_VALUE = new String[] { "**/node_modules/**", - "**/node_modules", "**/bower_components/**", - "**/bower_components", "**/dist/**", - "**/dist", "**/vendor/**", - "**/vendor", "**/external/**", - "**/external", "**/*.d.ts", }; diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java index c4f51b2c048..43c0f0ae1a6 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilter.java @@ -109,7 +109,7 @@ private static boolean isExclusionOverridden(Configuration config) { @Override public boolean test(Path path) { - return WildcardPattern.match(exclusions, path.toString().replaceAll("[\\\\/]", "/")); + return !WildcardPattern.match(exclusions, path.toString().replaceAll("[\\\\/]", "/")); } } } diff --git a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java index 9e76fe06981..c68ff5e1f37 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java +++ b/sonar-plugin/sonar-javascript-plugin/src/main/java/org/sonar/plugins/javascript/analysis/TsConfigProvider.java @@ -208,7 +208,10 @@ public List tsconfigs(SensorContext context) { continue; } for (var file : files) { - if (file.isDirectory() && !pathFilter.test(file.toPath())) { + if (!pathFilter.test(file.toPath())) { + continue; + } + if (file.isDirectory()) { dirs.add(file); } else { if (fileFilter.test(file.toPath())) { diff --git a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java index 83f11b36721..40c417e8ca0 100644 --- a/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java +++ b/sonar-plugin/sonar-javascript-plugin/src/test/java/org/sonar/plugins/javascript/analysis/LookupConfigProviderFilterTest.java @@ -73,10 +73,10 @@ void should_filter_default_paths() throws IOException { Configuration config = settings.asConfig(); var filter = new PathFilter(config); - assertThat(filter.test(inputFile("node_modules", "file.js"))).isTrue(); - assertThat(filter.test(inputFile("bower_components", "file.jsx"))).isTrue(); - assertThat(filter.test(inputFile("file.d.ts"))).isTrue(); - assertThat(filter.test(inputFile("file.js"))).isFalse(); + assertThat(filter.test(inputFile("node_modules", "file.js"))).isFalse(); + assertThat(filter.test(inputFile("bower_components", "file.jsx"))).isFalse(); + assertThat(filter.test(inputFile("file.d.ts"))).isFalse(); + assertThat(filter.test(inputFile("file.js"))).isTrue(); } @Test @@ -88,10 +88,10 @@ void should_filter_specific_paths() throws IOException { Configuration config = settings.asConfig(); var filter = new PathFilter(config); - assertThat(filter.test(inputFile("foo", "file.js"))).isTrue(); - assertThat(filter.test(inputFile("bar", "file.ts"))).isTrue(); - assertThat(filter.test(inputFile("qux", "file.cjs"))).isFalse(); - assertThat(filter.test(inputFile("file.vue"))).isFalse(); + assertThat(filter.test(inputFile("foo", "file.js"))).isFalse(); + assertThat(filter.test(inputFile("bar", "file.ts"))).isFalse(); + assertThat(filter.test(inputFile("qux", "file.cjs"))).isTrue(); + assertThat(filter.test(inputFile("file.vue"))).isTrue(); } private Path inputFile(String filename) throws IOException { From f05d67c7d03c50d75847c1171b2b5b4487bae805 Mon Sep 17 00:00:00 2001 From: Michal Zgliczynski Date: Mon, 4 Nov 2024 15:37:52 +0100 Subject: [PATCH 8/8] remove unnecessary code --- .../java/org/sonar/plugins/javascript/analysis/JsTsSensor.java | 2 -- 1 file changed, 2 deletions(-) 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 711011d123a..9f0348c9f2b 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 @@ -24,7 +24,6 @@ import java.io.IOException; import java.util.List; import java.util.stream.StreamSupport; -import javax.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.batch.DependedUpon; @@ -49,7 +48,6 @@ public class JsTsSensor extends AbstractBridgeSensor { private final AnalysisConsumers consumers; private final TsConfigCache tsConfigCache; - // Constructor for SonarCloud without the optional dependency (Pico doesn't support optional dependencies) public JsTsSensor( JsTsChecks checks, BridgeServer bridgeServer,