Skip to content

Commit

Permalink
JS-155 SonarJS exposes entry point to the parser for testing
Browse files Browse the repository at this point in the history
  • Loading branch information
quentin-jaquier-sonarsource committed Jun 13, 2024
1 parent 4413715 commit 0cf360c
Show file tree
Hide file tree
Showing 7 changed files with 302 additions and 1 deletion.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"_:plugin:prepare-bridge": "npm pack && node tools/check-distribution-filepath-length.js && npm run _:plugin:copy-bridge",
"_:plugin-fetch-node": "node tools/fetch-node/scripts/wrapper.mjs",
"_:plugin:pre-build": "npm run bridge:build && npm run _:plugin:prepare-bridge && npm run _:plugin-fetch-node",
"_:plugin:copy-bridge": "cpy sonarjs-1.0.0.tgz sonar-plugin/sonar-javascript-plugin/target/classes"
"_:plugin:copy-bridge": "cpy sonarjs-1.0.0.tgz sonar-plugin/sonar-javascript-plugin/target/classes && cpy sonarjs-1.0.0.tgz sonar-plugin/standalone/target/classes"
},
"repository": {
"type": "git",
Expand Down
1 change: 1 addition & 0 deletions sonar-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<module>css</module>
<module>javascript-checks</module>
<module>sonar-javascript-plugin</module>
<module>standalone</module>
</modules>

</project>
45 changes: 45 additions & 0 deletions sonar-plugin/standalone/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>

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

<name>SonarQube JavaScript :: Standalone</name>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>bridge</artifactId>
</dependency>
<dependency>
<groupId>org.sonarsource.api.plugin</groupId>
<artifactId>sonar-plugin-api</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
</dependency>
<dependency>
<groupId>org.sonarsource.api.plugin</groupId>
<artifactId>sonar-plugin-api-test-fixtures</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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.standalone;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.Optional;
import org.sonar.api.SonarProduct;
import org.sonar.plugins.javascript.api.estree.ESTree;
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.bridge.BridgeServerConfig;
import org.sonar.plugins.javascript.bridge.BridgeServerImpl;
import org.sonar.plugins.javascript.bridge.BundleImpl;
import org.sonar.plugins.javascript.bridge.ESTreeFactory;
import org.sonar.plugins.javascript.bridge.EmbeddedNode;
import org.sonar.plugins.javascript.bridge.Environment;
import org.sonar.plugins.javascript.bridge.NodeDeprecationWarning;
import org.sonar.plugins.javascript.bridge.RulesBundles;
import org.sonar.plugins.javascript.bridge.protobuf.Node;
import org.sonar.plugins.javascript.nodejs.NodeCommandBuilderImpl;
import org.sonar.plugins.javascript.nodejs.ProcessWrapperImpl;

public class StandaloneParser implements AutoCloseable {

private final BridgeServerImpl bridge;

public StandaloneParser() {
ProcessWrapperImpl processWrapper = new ProcessWrapperImpl();
EmptyConfiguration emptyConfiguration = new EmptyConfiguration();
bridge = new BridgeServerImpl(new NodeCommandBuilderImpl(processWrapper), new BundleImpl(), new RulesBundles(),
new NodeDeprecationWarning(new AnalysisWarningsWrapper()), new StandaloneTemporaryFolder(), new EmbeddedNode(processWrapper, new Environment(emptyConfiguration)));
try {
bridge.startServerLazily(new BridgeServerConfig(emptyConfiguration, new File(".").getAbsolutePath(), SonarProduct.SONARLINT));
bridge.initLinter(List.of(), List.of(), List.of(), AnalysisMode.DEFAULT, null, List.of());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

public ESTree.Program parse(String code) {
BridgeServer.JsAnalysisRequest request = new BridgeServer.JsAnalysisRequest(
"file.js",
"MAIN",
"js",
code,
true,
null,
null,
AnalysisMode.DEFAULT_LINTER_ID);
try {
BridgeServer.AnalysisResponse result = bridge.analyzeJavaScript(request);
Node ast = result.ast();
if (ast == null) {
throw new IllegalStateException("Failed to parse the code");
}
return ESTreeFactory.from(ast, ESTree.Program.class);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public void close() {
bridge.stop();
}

private static class EmptyConfiguration implements org.sonar.api.config.Configuration {

@Override
public Optional<String> get(String key) {
return Optional.empty();
}

@Override
public boolean hasKey(String key) {
return false;
}

@Override
public String[] getStringArray(String key) {
return new String[0];
}
}
}

Original file line number Diff line number Diff line change
@@ -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.standalone;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import javax.annotation.Nullable;

public class StandaloneTemporaryFolder implements org.sonar.api.utils.TempFolder {

@Override
public File newDir() {
return newDir("sonarjs");
}

@Override
public File newDir(String name) {
try {
return Files.createTempDirectory(name).toFile();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public File newFile() {
return newFile(null, null);
}

@Override
public File newFile(@Nullable String prefix, @Nullable String suffix) {
try {
return Files.createTempFile(prefix, suffix).toFile();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -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.plugins.javascript.standalone;

import javax.annotation.ParametersAreNonnullByDefault;
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.standalone;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.sonar.plugins.javascript.api.estree.ESTree;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.sonar.plugins.javascript.api.estree.ESTree.Program;

class StandaloneParserTest {

private static StandaloneParser parser;

@BeforeAll
static void setUp() {
parser = new StandaloneParser();
}

@AfterAll
static void tearDown() {
parser.close();
}

@Test
void should_parse_multiple_time() {
Program firstSample = parser.parse("var a = 42;");
assertThat(firstSample.body()).hasSize(1);
var firstElement = firstSample.body().get(0);
assertThat(firstElement).isInstanceOfSatisfying(ESTree.VariableDeclaration.class, v -> {
assertThat(v.declarations()).hasSize(1);
var declaration = v.declarations().get(0);
assertThat(declaration).isInstanceOfSatisfying(ESTree.VariableDeclarator.class, d -> {
assertThat(d.id()).isInstanceOfSatisfying(ESTree.Identifier.class, i -> assertThat(i.name()).isEqualTo("a"));
assertThat(d.init().get()).isInstanceOfSatisfying(ESTree.SimpleLiteral.class, l -> assertThat(l.value()).isEqualTo(42));
});
});
Program secondSample = parser.parse("let x;");
assertThat(secondSample.body()).hasSize(1);
}

@Test
void should_throw_exception_when_fail_to_parse_code() {
assertThatThrownBy(() -> parser.parse("..."))
.isInstanceOf(IllegalStateException.class)
.hasMessage("Failed to parse the code");
}

}

0 comments on commit 0cf360c

Please sign in to comment.