Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve node_modules relative to package.json #380

Merged
merged 6 commits into from
Oct 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ private Set<Dependency> dependencyParser(File baseDir, InputFile packageJsonFile
JsonObject packageJsonDependencies = packageJson.getJsonObject("dependencies");
if (packageJsonDependencies != null)
{
scanDependencies(baseDir, packageJsonDependencies.keySet(), dependencies);
scanDependencies(new File(packageJsonFile.uri().resolve("node_modules")), packageJsonDependencies.keySet(),
dependencies);
dependencies.forEach(dependency ->
{
dependency.setInputComponent(packageJsonFile);
Expand All @@ -87,7 +88,7 @@ private Set<Dependency> dependencyParser(File baseDir, InputFile packageJsonFile
return dependencies;
}

private void scanDependencies(File baseDir, Set<String> packageNames, Set<Dependency> dependencies)
private void scanDependencies(File nodeModulesDir, Set<String> packageNames, Set<Dependency> dependencies)
{
LOGGER.info("Scanning NPM packages " + packageNames);

Expand All @@ -99,11 +100,11 @@ private void scanDependencies(File baseDir, Set<String> packageNames, Set<Depend
continue;
}

File packageJsonFile = new File(baseDir, "node_modules/" + packageName + "/package.json");
File packageJsonFile = new File(nodeModulesDir, packageName + "/package.json");
if (!packageJsonFile.exists())
{
LOGGER.warn("No package.json file found for package {} in node_modules - skipping dependency",
packageName);
LOGGER.warn("No package.json file found for package {} at {} - skipping dependency.",
packageName, packageJsonFile);
continue;
}

Expand All @@ -123,7 +124,7 @@ private void scanDependencies(File baseDir, Set<String> packageNames, Set<Depend
JsonObject packageJsonDependencies = packageJson.getJsonObject("dependencies");
if (packageJsonDependencies != null)
{
scanDependencies(baseDir, packageJsonDependencies.keySet(), dependencies);
scanDependencies(nodeModulesDir, packageJsonDependencies.keySet(), dependencies);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,49 +8,69 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.Test;
import org.sonar.api.batch.fs.FileSystem;
import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.internal.DefaultFileSystem;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.sensor.SensorContext;

import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import at.porscheinformatik.sonarqube.licensecheck.licensemapping.LicenseMappingService;
import at.porscheinformatik.sonarqube.licensecheck.npm.PackageJsonDependencyScanner;

public class PackageJsonDependencyScannerTest
{
private static final File RESOURCE_FOLDER = new File("src/test/resources");
private static final Path RESOURCE_FOLDER = Path.of("src/test/resources");

private static final Logger LOGGER = Loggers.get(PackageJsonDependencyScannerTest.class);

private SensorContext createContext(File folder)
private SensorContext createContext(Path moduleBaseDir)
{
SensorContext context = mock(SensorContext.class);
InputFile packageJson = mock(InputFile.class);
when(packageJson.language()).thenReturn("json");
when(packageJson.filename()).thenReturn("package.json");
when(packageJson.relativePath()).thenReturn("/package.json");
when(packageJson.type()).thenReturn(InputFile.Type.MAIN);
try
{
when(packageJson.inputStream()).thenAnswer(i -> new FileInputStream(new File(folder, "package.json")));
}
catch (IOException e)
{
DefaultFileSystem fileSystem = new DefaultFileSystem(moduleBaseDir);
try {
// Provide all **source** files in the moduleBaseDir
Files.walk(moduleBaseDir).forEach(path -> {
try {
if (Files.isDirectory(path)) {
return;
}
if (path.toString().contains("/node_modules/")) {
LOGGER.info("Ignoring file {}, because it is in a node_modules folder", path);
return;
}
var lines = Files.readAllLines(path);
InputFile inputFile = TestInputFileBuilder.create("test", moduleBaseDir.toFile(), path.toFile())
// Required to get correct metadata
.setContents(lines.stream().collect(Collectors.joining("\n")))
// Otherwise .inputStream() will throw a NPE
.setCharset(Charset.forName("UTF-8"))
.build();
LOGGER.info("Added {} to the fs", path);
fileSystem.add(inputFile);

} catch (Exception e) {
throw new RuntimeException(e);
}

});
} catch (Exception e) {
throw new RuntimeException(e);
}
FileSystem fileSystem = new DefaultFileSystem(folder.toPath()).add(packageJson);
when(context.fileSystem()).thenReturn(fileSystem);
return context;
}

@Test
public void testHappyPath()
{
Set<Dependency> dependencies = createScanner().scan(createContext(RESOURCE_FOLDER));
Set<Dependency> dependencies = createScanner().scan(createContext(RESOURCE_FOLDER.resolve("example")));

assertThat(dependencies, hasSize(2));
assertThat(dependencies, containsInAnyOrder(
Expand All @@ -61,7 +81,7 @@ public void testHappyPath()
@Test
public void testTransitive()
{
Set<Dependency> dependencies = createScanner(true).scan(createContext(RESOURCE_FOLDER));
Set<Dependency> dependencies = createScanner(true).scan(createContext(RESOURCE_FOLDER.resolve("example")));

assertThat(dependencies, hasSize(4));
assertThat(dependencies, containsInAnyOrder(
Expand All @@ -71,26 +91,35 @@ public void testTransitive()
new Dependency("retry", "0.10.1", "MIT")));
}

@Test
public void testPackageJsonNotInBaseDir() throws Exception {
Set<Dependency> dependencies = createScanner().scan(createContext(RESOURCE_FOLDER.resolve("example_nested")));
assertThat(dependencies, containsInAnyOrder(
new Dependency("angular", "1.5.0", "MIT"),
new Dependency("arangojs", "5.6.0", "Apache-2.0")));

}

@Test
public void testNoPackageJson()
{
Set<Dependency> dependencies = createScanner().scan(createContext(new File("src")));
Set<Dependency> dependencies = createScanner().scan(createContext(RESOURCE_FOLDER.resolve("no_package_json")));

assertThat(dependencies, hasSize(0));
}

@Test
public void testNoNodeModules()
{
Set<Dependency> dependencies = createScanner().scan(createContext(new File(RESOURCE_FOLDER, "node_modules/arangojs")));
Set<Dependency> dependencies = createScanner().scan(createContext(RESOURCE_FOLDER.resolve("example/node_modules/arangojs")));

assertThat(dependencies, hasSize(0));
}

@Test
public void testLicenseInDeprecatedLicenseFormat()
{
final Set<Dependency> dependencies = createScanner().scan(createContext(new File(RESOURCE_FOLDER, "deprecated_project")));
final Set<Dependency> dependencies = createScanner().scan(createContext(RESOURCE_FOLDER.resolve("deprecated_project")));

assertEquals(1, dependencies.size());

Expand All @@ -101,7 +130,7 @@ public void testLicenseInDeprecatedLicenseFormat()
@Test
public void testLicenseInDeprecatedLicensesFormat()
{
final Set<Dependency> dependencies = createScanner().scan(createContext(new File(RESOURCE_FOLDER, "deprecated_multilicense_project")));
final Set<Dependency> dependencies = createScanner().scan(createContext(RESOURCE_FOLDER.resolve("deprecated_multilicense_project")));

assertEquals(1, dependencies.size());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class IOUtilsTest
@Test
public void testLoadFile() throws IOException
{
String packageJson = IOUtils.readToString(IOUtilsTest.class.getResourceAsStream("/package.json"));
String packageJson = IOUtils.readToString(IOUtilsTest.class.getResourceAsStream("/example/package.json"));
assertThat(packageJson, containsString("\"name\": \"test\""));
}
}
16 changes: 16 additions & 0 deletions src/test/resources/example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "test",
"version": "1.0.0",
"description": "An example module to illustrate the usage of a package.json",
"author": "Your Name <[email protected]>",
"dependencies": {
"angular": "^1.4.3",
"angular-ui-router" : "~0.2.18",
"angular-ui-bootstrap" : "1.1.2",
"arangojs" : "5.6.0"
},
"devDependencies": {
"gulp": "^3.9.1"
},
"license": "MIT"
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading