From 28fd5048001df9fd6cc2084b5da14ebb1a940975 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Thu, 5 Dec 2024 16:37:13 +0100 Subject: [PATCH 1/5] refactor(node): Rename non-NPM-specific files Make more clear that these files host functions that are for any Node package manager, not just NPM specifically. Also rename the `NodePackageManager` enum to `NodePackageManagerType` to make room for another class that is more appropriate to use that name. Signed-off-by: Sebastian Schuberth --- ...tion.kt => NodePackageManagerDetection.kt} | 20 +++++----- ...upport.kt => NodePackageManagerSupport.kt} | 0 .../node/src/main/kotlin/npm/Npm.kt | 10 ++--- .../main/kotlin/npm/NpmDependencyHandler.kt | 6 +-- .../node/src/main/kotlin/pnpm/Pnpm.kt | 10 ++--- .../main/kotlin/pnpm/PnpmDependencyHandler.kt | 4 +- .../node/src/main/kotlin/yarn/Yarn.kt | 14 +++---- .../node/src/main/kotlin/yarn2/Yarn2.kt | 10 ++--- .../node/src/test/kotlin/NpmDetectionTest.kt | 37 ++++++++++--------- 9 files changed, 56 insertions(+), 55 deletions(-) rename plugins/package-managers/node/src/main/kotlin/{NpmDetection.kt => NodePackageManagerDetection.kt} (93%) rename plugins/package-managers/node/src/main/kotlin/{NpmSupport.kt => NodePackageManagerSupport.kt} (100%) diff --git a/plugins/package-managers/node/src/main/kotlin/NpmDetection.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt similarity index 93% rename from plugins/package-managers/node/src/main/kotlin/NpmDetection.kt rename to plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt index e07a1d6e5f763..4c6bd2047438e 100644 --- a/plugins/package-managers/node/src/main/kotlin/NpmDetection.kt +++ b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt @@ -31,15 +31,15 @@ import org.ossreviewtoolkit.utils.common.collectMessages /** * A class to detect the package managers used for the give [definitionFiles]. */ -internal class NpmDetection(private val definitionFiles: Collection) { +internal class NodePackageManagerDetection(private val definitionFiles: Collection) { /** * A map of project directories to the set of package managers that are most likely responsible for the project. If * the set is empty, none of the package managers is responsible. */ - private val projectDirManagers: Map> by lazy { + private val projectDirManagers: Map> by lazy { definitionFiles.associate { file -> val projectDir = file.parentFile - projectDir to NodePackageManager.forDirectory(projectDir) + projectDir to NodePackageManagerType.forDirectory(projectDir) } } @@ -50,7 +50,7 @@ internal class NpmDetection(private val definitionFiles: Collection) { private val workspacePatterns: Map> by lazy { definitionFiles.associate { file -> val projectDir = file.parentFile - val patterns = NodePackageManager.entries.mapNotNull { it.getWorkspaces(projectDir) }.flatten() + val patterns = NodePackageManagerType.entries.mapNotNull { it.getWorkspaces(projectDir) }.flatten() projectDir to patterns.map { FileSystems.getDefault().getPathMatcher("glob:${it.removeSuffix("/")}") } @@ -70,7 +70,7 @@ internal class NpmDetection(private val definitionFiles: Collection) { /** * Return those [definitionFiles] that define root projects for the given [manager]. */ - fun filterApplicable(manager: NodePackageManager): List = + fun filterApplicable(manager: NodePackageManagerType): List = definitionFiles.filter { file -> val projectDir = file.parentFile @@ -103,17 +103,17 @@ internal class NpmDetection(private val definitionFiles: Collection) { "Any of $managersFromFiles could be the package manager for '$file'. Assuming it is an NPM project." } - manager == NodePackageManager.NPM + manager == NodePackageManagerType.NPM } } /** * An enum of all supported Node package managers. */ -internal enum class NodePackageManager( +internal enum class NodePackageManagerType( val lockfileName: String, val markerFileName: String? = null, - val workspaceFileName: String = NodePackageManager.DEFINITION_FILE + val workspaceFileName: String = NodePackageManagerType.DEFINITION_FILE ) { NPM( lockfileName = "package-lock.json", // See https://docs.npmjs.com/cli/v7/configuring-npm/package-lock-json. @@ -186,8 +186,8 @@ internal enum class NodePackageManager( /** * Return the set of package managers that are most likely responsible for the given [projectDir]. */ - fun forDirectory(projectDir: File): Set { - val scores = NodePackageManager.entries.associateWith { + fun forDirectory(projectDir: File): Set { + val scores = NodePackageManagerType.entries.associateWith { it.getFileScore(projectDir) } diff --git a/plugins/package-managers/node/src/main/kotlin/NpmSupport.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerSupport.kt similarity index 100% rename from plugins/package-managers/node/src/main/kotlin/NpmSupport.kt rename to plugins/package-managers/node/src/main/kotlin/NodePackageManagerSupport.kt diff --git a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt index f6d5298f85344..6a418a23aa9e5 100644 --- a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt +++ b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt @@ -35,8 +35,8 @@ import org.ossreviewtoolkit.model.config.AnalyzerConfiguration import org.ossreviewtoolkit.model.config.PackageManagerConfiguration import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager -import org.ossreviewtoolkit.plugins.packagemanagers.node.NpmDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parseProject @@ -77,7 +77,7 @@ class Npm( } class Factory : AbstractPackageManagerFactory("NPM") { - override val globsForDefinitionFiles = listOf(NodePackageManager.DEFINITION_FILE) + override val globsForDefinitionFiles = listOf(NodePackageManagerType.DEFINITION_FILE) override fun create( analysisRoot: File, @@ -131,10 +131,10 @@ class Npm( ).let { listOf(it) } } - private fun hasLockfile(projectDir: File) = NodePackageManager.NPM.hasLockfile(projectDir) + private fun hasLockfile(projectDir: File) = NodePackageManagerType.NPM.hasLockfile(projectDir) override fun mapDefinitionFiles(definitionFiles: List) = - NpmDetection(definitionFiles).filterApplicable(NodePackageManager.NPM) + NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.NPM) override fun beforeResolution(definitionFiles: List) { // We do not actually depend on any features specific to an NPM version, but we still want to stick to a diff --git a/plugins/package-managers/node/src/main/kotlin/npm/NpmDependencyHandler.kt b/plugins/package-managers/node/src/main/kotlin/npm/NpmDependencyHandler.kt index 7c8acbcf451ea..12337565abaaf 100644 --- a/plugins/package-managers/node/src/main/kotlin/npm/NpmDependencyHandler.kt +++ b/plugins/package-managers/node/src/main/kotlin/npm/NpmDependencyHandler.kt @@ -27,7 +27,7 @@ import org.ossreviewtoolkit.model.Package import org.ossreviewtoolkit.model.PackageLinkage import org.ossreviewtoolkit.model.utils.DependencyHandler import org.ossreviewtoolkit.plugins.packagemanagers.node.GetPackageDetailsFun -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackage import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson @@ -75,7 +75,7 @@ private val ModuleInfo.isProject: Boolean get() = resolved == null private val ModuleInfo.packageJsonFile: File get() = File( checkNotNull(path) { - "The path to '${NodePackageManager.DEFINITION_FILE}' is null in $this." + "The path to '${NodePackageManagerType.DEFINITION_FILE}' is null in $this." }, - NodePackageManager.DEFINITION_FILE + NodePackageManagerType.DEFINITION_FILE ) diff --git a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt index 50cdbb755c844..ba899da8969c0 100644 --- a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt +++ b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt @@ -30,8 +30,8 @@ import org.ossreviewtoolkit.model.ProjectAnalyzerResult import org.ossreviewtoolkit.model.config.AnalyzerConfiguration import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager -import org.ossreviewtoolkit.plugins.packagemanagers.node.NpmDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parseProject @@ -58,7 +58,7 @@ class Pnpm( repoConfig: RepositoryConfiguration ) : PackageManager(name, "PNPM", analysisRoot, analyzerConfig, repoConfig) { class Factory : AbstractPackageManagerFactory("PNPM") { - override val globsForDefinitionFiles = listOf(NodePackageManager.DEFINITION_FILE, "pnpm-lock.yaml") + override val globsForDefinitionFiles = listOf(NodePackageManagerType.DEFINITION_FILE, "pnpm-lock.yaml") override fun create( analysisRoot: File, @@ -87,7 +87,7 @@ class Pnpm( val moduleInfosForScope = scopes.associateWith { scope -> listModules(workingDir, scope) } return workspaceModuleDirs.map { projectDir -> - val packageJsonFile = projectDir.resolve(NodePackageManager.DEFINITION_FILE) + val packageJsonFile = projectDir.resolve(NodePackageManagerType.DEFINITION_FILE) val project = parseProject(packageJsonFile, analysisRoot, managerName) val scopeNames = scopes.mapTo(mutableSetOf()) { scope -> @@ -130,7 +130,7 @@ class Pnpm( PackageManagerResult(projectResults, graphBuilder.build(), graphBuilder.packages()) override fun mapDefinitionFiles(definitionFiles: List) = - NpmDetection(definitionFiles).filterApplicable(NodePackageManager.PNPM) + NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.PNPM) private fun installDependencies(workingDir: File) = PnpmCommand.run( diff --git a/plugins/package-managers/node/src/main/kotlin/pnpm/PnpmDependencyHandler.kt b/plugins/package-managers/node/src/main/kotlin/pnpm/PnpmDependencyHandler.kt index 3e28e5828dce7..782d3db4f1e3b 100644 --- a/plugins/package-managers/node/src/main/kotlin/pnpm/PnpmDependencyHandler.kt +++ b/plugins/package-managers/node/src/main/kotlin/pnpm/PnpmDependencyHandler.kt @@ -27,7 +27,7 @@ import org.ossreviewtoolkit.model.Package import org.ossreviewtoolkit.model.PackageLinkage import org.ossreviewtoolkit.model.utils.DependencyHandler import org.ossreviewtoolkit.plugins.packagemanagers.node.GetPackageDetailsFun -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackage import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson @@ -80,7 +80,7 @@ internal class PnpmDependencyHandler( private val Dependency.workingDir: File get() = File(path) -private val Dependency.packageJsonFile: File get() = workingDir.resolve(NodePackageManager.DEFINITION_FILE) +private val Dependency.packageJsonFile: File get() = workingDir.resolve(NodePackageManagerType.DEFINITION_FILE) /** * pnpm install skips optional dependencies which are not compatible with the environment. In this case the path diff --git a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt index aa43ef28bc213..a37794bdefc11 100644 --- a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt +++ b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt @@ -47,8 +47,8 @@ import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.createAndLogIssue import org.ossreviewtoolkit.model.readTree import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager -import org.ossreviewtoolkit.plugins.packagemanagers.node.NpmDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parseProject @@ -100,7 +100,7 @@ open class Yarn( repoConfig: RepositoryConfiguration ) : PackageManager(name, "Yarn", analysisRoot, analyzerConfig, repoConfig) { class Factory : AbstractPackageManagerFactory("Yarn") { - override val globsForDefinitionFiles = listOf(NodePackageManager.DEFINITION_FILE) + override val globsForDefinitionFiles = listOf(NodePackageManagerType.DEFINITION_FILE) override fun create( analysisRoot: File, @@ -116,7 +116,7 @@ open class Yarn( private val graphBuilder by lazy { DependencyGraphBuilder(YarnDependencyHandler(this)) } - protected fun hasLockfile(projectDir: File) = NodePackageManager.YARN.hasLockfile(projectDir) + protected fun hasLockfile(projectDir: File) = NodePackageManagerType.YARN.hasLockfile(projectDir) /** * Load the submodule directories of the project defined in [moduleDir]. @@ -137,7 +137,7 @@ open class Yarn( } override fun mapDefinitionFiles(definitionFiles: List) = - NpmDetection(definitionFiles).filterApplicable(NodePackageManager.YARN) + NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.YARN) override fun beforeResolution(definitionFiles: List) = // We do not actually depend on any features specific to a Yarn version, but we still want to stick to a @@ -179,7 +179,7 @@ open class Yarn( val issues = mutableListOf() val project = runCatching { - val packageJsonFile = projectDir.resolve(NodePackageManager.DEFINITION_FILE) + val packageJsonFile = projectDir.resolve(NodePackageManagerType.DEFINITION_FILE) parseProject(packageJsonFile, analysisRoot, managerName) }.getOrElse { issues += createAndLogIssue( @@ -302,7 +302,7 @@ open class Yarn( private fun parsePackageJson(moduleDir: File, scopes: Set): RawModuleInfo = rawModuleInfoCache.getOrPut(moduleDir to scopes) { - val packageJsonFile = moduleDir.resolve(NodePackageManager.DEFINITION_FILE) + val packageJsonFile = moduleDir.resolve(NodePackageManagerType.DEFINITION_FILE) logger.debug { "Parsing module info from '${packageJsonFile.absolutePath}'." } val json = packageJsonFile.readTree() diff --git a/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt b/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt index 5dff61ec27267..498fb2e622acd 100644 --- a/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt +++ b/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt @@ -53,8 +53,8 @@ import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.createAndLogIssue import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder import org.ossreviewtoolkit.model.utils.DependencyHandler -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager -import org.ossreviewtoolkit.plugins.packagemanagers.node.NpmDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.fixDownloadUrl import org.ossreviewtoolkit.plugins.packagemanagers.node.mapLicenses @@ -122,7 +122,7 @@ class Yarn2( } class Factory : AbstractPackageManagerFactory("Yarn2") { - override val globsForDefinitionFiles = listOf(NodePackageManager.DEFINITION_FILE) + override val globsForDefinitionFiles = listOf(NodePackageManagerType.DEFINITION_FILE) override fun create( analysisRoot: File, @@ -180,7 +180,7 @@ class Yarn2( } override fun mapDefinitionFiles(definitionFiles: List) = - NpmDetection(definitionFiles).filterApplicable(NodePackageManager.YARN2) + NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.YARN2) override fun beforeResolution(definitionFiles: List) = // We depend on a version >= 2, so we check the version for safety. @@ -709,7 +709,7 @@ private fun getYarnExecutable(workingDir: File): File { */ private fun isCorepackEnabledInManifest(workingDir: File): Boolean = runCatching { - val packageJson = parsePackageJson(workingDir.resolve(NodePackageManager.DEFINITION_FILE)) + val packageJson = parsePackageJson(workingDir.resolve(NodePackageManagerType.DEFINITION_FILE)) !packageJson.packageManager.isNullOrEmpty() }.getOrDefault(false) diff --git a/plugins/package-managers/node/src/test/kotlin/NpmDetectionTest.kt b/plugins/package-managers/node/src/test/kotlin/NpmDetectionTest.kt index eca138f3cb903..475213f0cbee5 100644 --- a/plugins/package-managers/node/src/test/kotlin/NpmDetectionTest.kt +++ b/plugins/package-managers/node/src/test/kotlin/NpmDetectionTest.kt @@ -30,17 +30,17 @@ import io.kotest.matchers.should import io.kotest.matchers.shouldBe import org.ossreviewtoolkit.analyzer.PackageManager -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager.NPM -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager.PNPM -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager.YARN -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager.YARN2 +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType.NPM +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType.PNPM +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType.YARN +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType.YARN2 import org.ossreviewtoolkit.utils.common.withoutPrefix import org.ossreviewtoolkit.utils.test.getAssetFile class NpmDetectionTest : WordSpec({ "All Node package manager detections" should { "ignore empty lockfiles" { - NodePackageManager.entries.forAll { + NodePackageManagerType.entries.forAll { val lockfile = tempdir().resolve(it.lockfileName).apply { writeText("") } @@ -50,7 +50,7 @@ class NpmDetectionTest : WordSpec({ } "ignore empty workspace files" { - NodePackageManager.entries.forAll { + NodePackageManagerType.entries.forAll { val workspaceFile = tempdir().resolve(it.workspaceFileName).apply { writeText("") } @@ -66,7 +66,8 @@ class NpmDetectionTest : WordSpec({ resolve("package.json").writeText("{}") } - NodePackageManager.forDirectory(projectDir) shouldContainExactlyInAnyOrder NodePackageManager.entries + NodePackageManagerType.forDirectory(projectDir) shouldContainExactlyInAnyOrder + NodePackageManagerType.entries } "return only those managers whose lockfiles are present" { @@ -76,7 +77,7 @@ class NpmDetectionTest : WordSpec({ resolve(PNPM.lockfileName).writeText("#") } - NodePackageManager.forDirectory(projectDir).shouldContainExactlyInAnyOrder(NPM, PNPM) + NodePackageManagerType.forDirectory(projectDir).shouldContainExactlyInAnyOrder(NPM, PNPM) } "return only NPM if distinguished by lockfile" { @@ -85,7 +86,7 @@ class NpmDetectionTest : WordSpec({ resolve(NPM.lockfileName).writeText("{}") } - NodePackageManager.forDirectory(projectDir).shouldContainExactlyInAnyOrder(NPM) + NodePackageManagerType.forDirectory(projectDir).shouldContainExactlyInAnyOrder(NPM) } "return only NPM if distinguished by other file" { @@ -94,7 +95,7 @@ class NpmDetectionTest : WordSpec({ NPM.markerFileName?.also { resolve(it).writeText("{}") } } - NodePackageManager.forDirectory(projectDir).shouldContainExactlyInAnyOrder(NPM) + NodePackageManagerType.forDirectory(projectDir).shouldContainExactlyInAnyOrder(NPM) } "return only PNPM if distinguished by lockfile" { @@ -103,7 +104,7 @@ class NpmDetectionTest : WordSpec({ resolve(PNPM.lockfileName).writeText("#") } - NodePackageManager.forDirectory(projectDir).shouldContainExactlyInAnyOrder(PNPM) + NodePackageManagerType.forDirectory(projectDir).shouldContainExactlyInAnyOrder(PNPM) } "return only PNPM if distinguished by workspace file" { @@ -112,7 +113,7 @@ class NpmDetectionTest : WordSpec({ resolve(PNPM.workspaceFileName).writeText("#") } - NodePackageManager.forDirectory(projectDir).shouldContainExactlyInAnyOrder(PNPM) + NodePackageManagerType.forDirectory(projectDir).shouldContainExactlyInAnyOrder(PNPM) } "return only YARN if distinguished by lockfile" { @@ -121,7 +122,7 @@ class NpmDetectionTest : WordSpec({ resolve(YARN.lockfileName).writeText(YARN_LOCK_FILE_HEADER) } - NodePackageManager.forDirectory(projectDir).shouldContainExactlyInAnyOrder(YARN) + NodePackageManagerType.forDirectory(projectDir).shouldContainExactlyInAnyOrder(YARN) } "return only YARN2 if distinguished by lockfile" { @@ -130,7 +131,7 @@ class NpmDetectionTest : WordSpec({ resolve(YARN2.lockfileName).writeText(YARN2_LOCK_FILE_HEADER) } - NodePackageManager.forDirectory(projectDir).shouldContainExactlyInAnyOrder(YARN2) + NodePackageManagerType.forDirectory(projectDir).shouldContainExactlyInAnyOrder(YARN2) } } @@ -156,7 +157,7 @@ class NpmDetectionTest : WordSpec({ val projectDir = getAssetFile("projects/synthetic") val definitionFiles = PackageManager.findManagedFiles(projectDir).values.flatten().toSet() - val filteredFiles = NpmDetection(definitionFiles).filterApplicable(NPM) + val filteredFiles = NodePackageManagerDetection(definitionFiles).filterApplicable(NPM) filteredFiles.map { it.relativeTo(projectDir).invariantSeparatorsPath }.shouldContainExactlyInAnyOrder( "npm/no-lockfile/package.json", @@ -193,7 +194,7 @@ class NpmDetectionTest : WordSpec({ val projectDir = getAssetFile("projects/synthetic") val definitionFiles = PackageManager.findManagedFiles(projectDir).values.flatten().toSet() - val filteredFiles = NpmDetection(definitionFiles).filterApplicable(PNPM) + val filteredFiles = NodePackageManagerDetection(definitionFiles).filterApplicable(PNPM) filteredFiles.map { it.relativeTo(projectDir).invariantSeparatorsPath }.shouldContainExactlyInAnyOrder( "pnpm/babel/package.json", @@ -225,7 +226,7 @@ class NpmDetectionTest : WordSpec({ val projectDir = getAssetFile("projects/synthetic") val definitionFiles = PackageManager.findManagedFiles(projectDir).values.flatten().toSet() - val filteredFiles = NpmDetection(definitionFiles).filterApplicable(YARN) + val filteredFiles = NodePackageManagerDetection(definitionFiles).filterApplicable(YARN) filteredFiles.map { it.relativeTo(projectDir).invariantSeparatorsPath }.shouldContainExactlyInAnyOrder( "yarn/babel/package.json", @@ -256,7 +257,7 @@ class NpmDetectionTest : WordSpec({ val projectDir = getAssetFile("projects/synthetic") val definitionFiles = PackageManager.findManagedFiles(projectDir).values.flatten().toSet() - val filteredFiles = NpmDetection(definitionFiles).filterApplicable(YARN2) + val filteredFiles = NodePackageManagerDetection(definitionFiles).filterApplicable(YARN2) filteredFiles.map { it.relativeTo(projectDir).invariantSeparatorsPath }.shouldContainExactlyInAnyOrder( "yarn2/project-with-lockfile/package.json", From a6b94c60187ec5f16fceeec98a6ffa6b8fd68204 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Wed, 15 Jan 2025 22:09:13 +0100 Subject: [PATCH 2/5] feat(node): Add the project type to the Node package manager type Prepare for generalizing some code. Signed-off-by: Sebastian Schuberth --- .../node/src/main/kotlin/NodePackageManagerDetection.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt index 4c6bd2047438e..d71305d4ddf59 100644 --- a/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt +++ b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt @@ -111,11 +111,13 @@ internal class NodePackageManagerDetection(private val definitionFiles: Collecti * An enum of all supported Node package managers. */ internal enum class NodePackageManagerType( + val projectType: String, val lockfileName: String, val markerFileName: String? = null, val workspaceFileName: String = NodePackageManagerType.DEFINITION_FILE ) { NPM( + projectType = "NPM", lockfileName = "package-lock.json", // See https://docs.npmjs.com/cli/v7/configuring-npm/package-lock-json. markerFileName = "npm-shrinkwrap.json" // See https://docs.npmjs.com/cli/v6/configuring-npm/shrinkwrap-json. ) { @@ -124,6 +126,7 @@ internal enum class NodePackageManagerType( }, PNPM( + projectType = "PNPM", lockfileName = "pnpm-lock.yaml", // See https://pnpm.io/git#lockfiles. workspaceFileName = "pnpm-workspace.yaml" ) { @@ -142,6 +145,7 @@ internal enum class NodePackageManagerType( }, YARN( + projectType = "Yarn", lockfileName = "yarn.lock" // See https://classic.yarnpkg.com/en/docs/yarn-lock. ) { private val lockfileMarker = "# yarn lockfile v1" @@ -157,6 +161,7 @@ internal enum class NodePackageManagerType( }, YARN2( + projectType = "Yarn2", lockfileName = "yarn.lock", // See https://classic.yarnpkg.com/en/docs/yarn-lock. markerFileName = ".yarnrc.yml" ) { From bcf10611e668c0ce6045dd40acdfecc27b17c8b5 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Thu, 5 Dec 2024 17:22:27 +0100 Subject: [PATCH 3/5] refactor(node): Introduce an abstract base class for common code Put code that is common to all Node package managers into an abstract base class to reduce code duplication and to ease upcoming changes. As some types are now exposed through the class hierarchy / constructor parameters, it is required to make them public. While at it, also perform some minor code alignments. Note that this reintroduction of a class hierarchy does not go against the idea of recently made changes to decouple the individual Node package manager implementations from each other: It is still the case that e.g. the PNPM implementation should not inherit from the NPM implementation. Signed-off-by: Sebastian Schuberth --- .../src/main/kotlin/NodePackageManager.kt | 45 +++++++++++++++++++ .../kotlin/NodePackageManagerDetection.kt | 2 +- .../node/src/main/kotlin/npm/ModuleInfo.kt | 2 +- .../node/src/main/kotlin/npm/Npm.kt | 21 +++------ .../node/src/main/kotlin/pnpm/ModuleInfo.kt | 2 +- .../node/src/main/kotlin/pnpm/Pnpm.kt | 17 +++---- .../src/main/kotlin/yarn/NpmModuleInfo.kt | 2 +- .../node/src/main/kotlin/yarn/Yarn.kt | 18 ++------ .../node/src/main/kotlin/yarn2/Yarn2.kt | 20 +++------ 9 files changed, 70 insertions(+), 59 deletions(-) create mode 100644 plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt diff --git a/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt new file mode 100644 index 0000000000000..413ccf7a374fc --- /dev/null +++ b/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 The ORT Project Authors (see ) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package org.ossreviewtoolkit.plugins.packagemanagers.node + +import java.io.File + +import org.ossreviewtoolkit.analyzer.PackageManager +import org.ossreviewtoolkit.analyzer.PackageManagerResult +import org.ossreviewtoolkit.model.ProjectAnalyzerResult +import org.ossreviewtoolkit.model.config.AnalyzerConfiguration +import org.ossreviewtoolkit.model.config.RepositoryConfiguration +import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder + +abstract class NodePackageManager( + managerName: String, + val managerType: NodePackageManagerType, + analysisRoot: File, + analyzerConfig: AnalyzerConfiguration, + repoConfig: RepositoryConfiguration +) : PackageManager(managerName, managerType.projectType, analysisRoot, analyzerConfig, repoConfig) { + protected abstract val graphBuilder: DependencyGraphBuilder<*> + + override fun mapDefinitionFiles(definitionFiles: List) = + NodePackageManagerDetection(definitionFiles).filterApplicable(managerType) + + override fun createPackageManagerResult(projectResults: Map>) = + PackageManagerResult(projectResults, graphBuilder.build(), graphBuilder.packages()) +} diff --git a/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt index d71305d4ddf59..3bba34b236b76 100644 --- a/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt +++ b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerDetection.kt @@ -110,7 +110,7 @@ internal class NodePackageManagerDetection(private val definitionFiles: Collecti /** * An enum of all supported Node package managers. */ -internal enum class NodePackageManagerType( +enum class NodePackageManagerType( val projectType: String, val lockfileName: String, val markerFileName: String? = null, diff --git a/plugins/package-managers/node/src/main/kotlin/npm/ModuleInfo.kt b/plugins/package-managers/node/src/main/kotlin/npm/ModuleInfo.kt index 64d588836bfa5..59f949b618db9 100644 --- a/plugins/package-managers/node/src/main/kotlin/npm/ModuleInfo.kt +++ b/plugins/package-managers/node/src/main/kotlin/npm/ModuleInfo.kt @@ -31,7 +31,7 @@ internal fun parseNpmList(json: String): ModuleInfo = JSON.decodeFromString(json * Module information for installed NPM packages. */ @Serializable -internal data class ModuleInfo( +data class ModuleInfo( /** The name of the package. */ val name: String? = null, diff --git a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt index 6a418a23aa9e5..5ad3d57ebce8f 100644 --- a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt +++ b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt @@ -25,8 +25,6 @@ import java.util.LinkedList import org.apache.logging.log4j.kotlin.logger import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory -import org.ossreviewtoolkit.analyzer.PackageManager -import org.ossreviewtoolkit.analyzer.PackageManagerResult import org.ossreviewtoolkit.model.Issue import org.ossreviewtoolkit.model.Project import org.ossreviewtoolkit.model.ProjectAnalyzerResult @@ -35,7 +33,7 @@ import org.ossreviewtoolkit.model.config.AnalyzerConfiguration import org.ossreviewtoolkit.model.config.PackageManagerConfiguration import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson @@ -70,7 +68,7 @@ class Npm( analysisRoot: File, analyzerConfig: AnalyzerConfiguration, repoConfig: RepositoryConfiguration -) : PackageManager(name, "NPM", analysisRoot, analyzerConfig, repoConfig) { +) : NodePackageManager(name, NodePackageManagerType.NPM, analysisRoot, analyzerConfig, repoConfig) { companion object { /** Name of the configuration option to toggle legacy peer dependency support. */ const val OPTION_LEGACY_PEER_DEPS = "legacyPeerDeps" @@ -89,7 +87,8 @@ class Npm( private val legacyPeerDeps = options[OPTION_LEGACY_PEER_DEPS].toBoolean() private val npmViewCache = mutableMapOf() private val handler = NpmDependencyHandler(projectType, this::getRemotePackageDetails) - private val graphBuilder by lazy { DependencyGraphBuilder(handler) } + + override val graphBuilder by lazy { DependencyGraphBuilder(handler) } override fun resolveDependencies(definitionFile: File, labels: Map): List = stashDirectories(definitionFile.resolveSibling("node_modules")).use { @@ -131,20 +130,12 @@ class Npm( ).let { listOf(it) } } - private fun hasLockfile(projectDir: File) = NodePackageManagerType.NPM.hasLockfile(projectDir) - - override fun mapDefinitionFiles(definitionFiles: List) = - NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.NPM) - override fun beforeResolution(definitionFiles: List) { // We do not actually depend on any features specific to an NPM version, but we still want to stick to a // fixed minor version to be sure to get consistent results. NpmCommand.checkVersion() } - override fun createPackageManagerResult(projectResults: Map>) = - PackageManagerResult(projectResults, graphBuilder.build(), graphBuilder.packages()) - private fun listModules(workingDir: File, issues: MutableList): ModuleInfo { val listProcess = NpmCommand.run(workingDir, "list", "--depth", "Infinity", "--json", "--long") issues += listProcess.extractNpmIssues() @@ -167,7 +158,7 @@ class Npm( } private fun installDependencies(workingDir: File): List { - requireLockfile(workingDir) { hasLockfile(workingDir) } + requireLockfile(workingDir) { managerType.hasLockfile(workingDir) } val options = listOfNotNull( "--ignore-scripts", @@ -175,7 +166,7 @@ class Npm( "--legacy-peer-deps".takeIf { legacyPeerDeps } ) - val subcommand = if (hasLockfile(workingDir)) "ci" else "install" + val subcommand = if (managerType.hasLockfile(workingDir)) "ci" else "install" val process = NpmCommand.run(workingDir, subcommand, *options.toTypedArray()) diff --git a/plugins/package-managers/node/src/main/kotlin/pnpm/ModuleInfo.kt b/plugins/package-managers/node/src/main/kotlin/pnpm/ModuleInfo.kt index 7b9df7cab8ebe..72c906c1ead08 100644 --- a/plugins/package-managers/node/src/main/kotlin/pnpm/ModuleInfo.kt +++ b/plugins/package-managers/node/src/main/kotlin/pnpm/ModuleInfo.kt @@ -27,7 +27,7 @@ private val JSON = Json { ignoreUnknownKeys = true } internal fun parsePnpmList(json: String): List = JSON.decodeFromString(json) @Serializable -internal data class ModuleInfo( +data class ModuleInfo( val name: String? = null, val version: String? = null, val path: String, diff --git a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt index ba899da8969c0..765e16338c5d8 100644 --- a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt +++ b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt @@ -24,13 +24,11 @@ import java.io.File import org.apache.logging.log4j.kotlin.logger import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory -import org.ossreviewtoolkit.analyzer.PackageManager -import org.ossreviewtoolkit.analyzer.PackageManagerResult import org.ossreviewtoolkit.model.ProjectAnalyzerResult import org.ossreviewtoolkit.model.config.AnalyzerConfiguration import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson @@ -56,7 +54,7 @@ class Pnpm( analysisRoot: File, analyzerConfig: AnalyzerConfiguration, repoConfig: RepositoryConfiguration -) : PackageManager(name, "PNPM", analysisRoot, analyzerConfig, repoConfig) { +) : NodePackageManager(name, NodePackageManagerType.PNPM, analysisRoot, analyzerConfig, repoConfig) { class Factory : AbstractPackageManagerFactory("PNPM") { override val globsForDefinitionFiles = listOf(NodePackageManagerType.DEFINITION_FILE, "pnpm-lock.yaml") @@ -67,9 +65,10 @@ class Pnpm( ) = Pnpm(type, analysisRoot, analyzerConfig, repoConfig) } - private val handler = PnpmDependencyHandler(projectType, this::getRemotePackageDetails) - private val graphBuilder by lazy { DependencyGraphBuilder(handler) } private val packageDetailsCache = mutableMapOf() + private val handler = PnpmDependencyHandler(projectType, this::getRemotePackageDetails) + + override val graphBuilder by lazy { DependencyGraphBuilder(handler) } override fun resolveDependencies(definitionFile: File, labels: Map): List = stashDirectories(definitionFile.resolveSibling("node_modules")).use { @@ -126,12 +125,6 @@ class Pnpm( return parsePnpmList(json) } - override fun createPackageManagerResult(projectResults: Map>) = - PackageManagerResult(projectResults, graphBuilder.build(), graphBuilder.packages()) - - override fun mapDefinitionFiles(definitionFiles: List) = - NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.PNPM) - private fun installDependencies(workingDir: File) = PnpmCommand.run( "install", diff --git a/plugins/package-managers/node/src/main/kotlin/yarn/NpmModuleInfo.kt b/plugins/package-managers/node/src/main/kotlin/yarn/NpmModuleInfo.kt index aa00bc36ca1fa..75fac495f3426 100644 --- a/plugins/package-managers/node/src/main/kotlin/yarn/NpmModuleInfo.kt +++ b/plugins/package-managers/node/src/main/kotlin/yarn/NpmModuleInfo.kt @@ -32,7 +32,7 @@ import org.ossreviewtoolkit.model.Project * contain all the information required to identify a module, construct a [Package] from it, and traverse its * dependency tree. */ -internal data class NpmModuleInfo( +data class NpmModuleInfo( /** The identifier for the represented module. */ val id: Identifier, diff --git a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt index a37794bdefc11..80835272d0ce0 100644 --- a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt +++ b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt @@ -36,8 +36,6 @@ import org.apache.logging.log4j.kotlin.logger import org.apache.logging.log4j.kotlin.loggerOf import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory -import org.ossreviewtoolkit.analyzer.PackageManager -import org.ossreviewtoolkit.analyzer.PackageManagerResult import org.ossreviewtoolkit.model.Identifier import org.ossreviewtoolkit.model.Issue import org.ossreviewtoolkit.model.Project @@ -47,7 +45,7 @@ import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.createAndLogIssue import org.ossreviewtoolkit.model.readTree import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson @@ -98,7 +96,7 @@ open class Yarn( analysisRoot: File, analyzerConfig: AnalyzerConfiguration, repoConfig: RepositoryConfiguration -) : PackageManager(name, "Yarn", analysisRoot, analyzerConfig, repoConfig) { +) : NodePackageManager(name, NodePackageManagerType.YARN, analysisRoot, analyzerConfig, repoConfig) { class Factory : AbstractPackageManagerFactory("Yarn") { override val globsForDefinitionFiles = listOf(NodePackageManagerType.DEFINITION_FILE) @@ -114,9 +112,7 @@ open class Yarn( private val rawModuleInfoCache = mutableMapOf>, RawModuleInfo>() - private val graphBuilder by lazy { DependencyGraphBuilder(YarnDependencyHandler(this)) } - - protected fun hasLockfile(projectDir: File) = NodePackageManagerType.YARN.hasLockfile(projectDir) + override val graphBuilder by lazy { DependencyGraphBuilder(YarnDependencyHandler(this)) } /** * Load the submodule directories of the project defined in [moduleDir]. @@ -136,9 +132,6 @@ open class Yarn( } } - override fun mapDefinitionFiles(definitionFiles: List) = - NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.YARN) - override fun beforeResolution(definitionFiles: List) = // We do not actually depend on any features specific to a Yarn version, but we still want to stick to a // fixed minor version to be sure to get consistent results. @@ -341,11 +334,8 @@ open class Yarn( loadWorkspaceSubmodules(moduleDir) } - override fun createPackageManagerResult(projectResults: Map>) = - PackageManagerResult(projectResults, graphBuilder.build(), graphBuilder.packages()) - private fun installDependencies(workingDir: File) { - requireLockfile(workingDir) { hasLockfile(workingDir) } + requireLockfile(workingDir) { managerType.hasLockfile(workingDir) } YarnCommand.run(workingDir, "install", "--ignore-scripts", "--ignore-engines", "--immutable").requireSuccess() } diff --git a/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt b/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt index 498fb2e622acd..2bd2f216ccf09 100644 --- a/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt +++ b/plugins/package-managers/node/src/main/kotlin/yarn2/Yarn2.kt @@ -32,8 +32,6 @@ import kotlinx.coroutines.awaitAll import org.apache.logging.log4j.kotlin.logger import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory -import org.ossreviewtoolkit.analyzer.PackageManager -import org.ossreviewtoolkit.analyzer.PackageManagerResult import org.ossreviewtoolkit.analyzer.parseAuthorString import org.ossreviewtoolkit.downloader.VcsHost import org.ossreviewtoolkit.downloader.VersionControlSystem @@ -53,7 +51,7 @@ import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.createAndLogIssue import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder import org.ossreviewtoolkit.model.utils.DependencyHandler -import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerDetection +import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.fixDownloadUrl @@ -108,7 +106,7 @@ class Yarn2( analysisRoot: File, analyzerConfig: AnalyzerConfiguration, repoConfig: RepositoryConfiguration -) : PackageManager(name, "Yarn2", analysisRoot, analyzerConfig, repoConfig), CommandLineTool { +) : NodePackageManager(name, NodePackageManagerType.YARN2, analysisRoot, analyzerConfig, repoConfig), CommandLineTool { companion object { /** * The name of the option to disable HTTPS server certificate verification. @@ -143,9 +141,6 @@ class Yarn2( private val disableRegistryCertificateVerification = options[OPTION_DISABLE_REGISTRY_CERTIFICATE_VERIFICATION].toBoolean() - // A builder to build the dependency graph of the project. - private val graphBuilder = DependencyGraphBuilder(Yarn2DependencyHandler()) - // All the packages parsed by this package manager, mapped by their ids. private val allPackages = mutableMapOf() @@ -155,6 +150,9 @@ class Yarn2( // The issues that have been found when resolving the dependencies. private val issues = mutableListOf() + // A builder to build the dependency graph of the project. + override val graphBuilder = DependencyGraphBuilder(Yarn2DependencyHandler()) + override fun command(workingDir: File?): String { if (workingDir == null) return "" if (isCorepackEnabled(workingDir)) return "yarn" @@ -179,9 +177,6 @@ class Yarn2( isCorepackEnabledInManifest(workingDir) } - override fun mapDefinitionFiles(definitionFiles: List) = - NodePackageManagerDetection(definitionFiles).filterApplicable(NodePackageManagerType.YARN2) - override fun beforeResolution(definitionFiles: List) = // We depend on a version >= 2, so we check the version for safety. definitionFiles.forEach { checkVersion(it.parentFile) } @@ -591,15 +586,12 @@ class Yarn2( } } }.toList() - - override fun createPackageManagerResult(projectResults: Map>) = - PackageManagerResult(projectResults, graphBuilder.build(), graphBuilder.packages()) } /** * A data class storing information about a specific Yarn 2+ module and its dependencies. */ -private data class YarnModuleInfo( +data class YarnModuleInfo( /** The identifier for the represented module. */ val id: Identifier, From 03169f2f33b25e202f634e846318eaa5b0a20822 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Thu, 5 Dec 2024 18:50:08 +0100 Subject: [PATCH 4/5] refactor(node): Move `parseProject()` to `NodePackageManager` This avoids the need to pass the `managerName` as a parameter, and to have a top-level logger. Signed-off-by: Sebastian Schuberth --- .../src/main/kotlin/NodePackageManager.kt | 51 ++++++++++++++++++ .../main/kotlin/NodePackageManagerSupport.kt | 53 ------------------- .../node/src/main/kotlin/npm/Npm.kt | 5 +- .../node/src/main/kotlin/pnpm/Pnpm.kt | 3 +- .../node/src/main/kotlin/yarn/Yarn.kt | 3 +- 5 files changed, 55 insertions(+), 60 deletions(-) diff --git a/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt index 413ccf7a374fc..4664f1ba5f615 100644 --- a/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt +++ b/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt @@ -21,12 +21,19 @@ package org.ossreviewtoolkit.plugins.packagemanagers.node import java.io.File +import org.apache.logging.log4j.kotlin.logger + import org.ossreviewtoolkit.analyzer.PackageManager import org.ossreviewtoolkit.analyzer.PackageManagerResult +import org.ossreviewtoolkit.analyzer.parseAuthorString +import org.ossreviewtoolkit.downloader.VersionControlSystem +import org.ossreviewtoolkit.model.Identifier +import org.ossreviewtoolkit.model.Project import org.ossreviewtoolkit.model.ProjectAnalyzerResult import org.ossreviewtoolkit.model.config.AnalyzerConfiguration import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.utils.DependencyGraphBuilder +import org.ossreviewtoolkit.utils.common.realFile abstract class NodePackageManager( managerName: String, @@ -37,6 +44,50 @@ abstract class NodePackageManager( ) : PackageManager(managerName, managerType.projectType, analysisRoot, analyzerConfig, repoConfig) { protected abstract val graphBuilder: DependencyGraphBuilder<*> + protected fun parseProject(packageJsonFile: File, analysisRoot: File): Project { + logger.debug { "Parsing project info from '$packageJsonFile'." } + + val packageJson = parsePackageJson(packageJsonFile) + + val rawName = packageJson.name.orEmpty() + val (namespace, name) = splitNamespaceAndName(rawName) + + val projectName = name.ifBlank { + getFallbackProjectName(analysisRoot, packageJsonFile).also { + logger.warn { "'$packageJsonFile' does not define a name, falling back to '$it'." } + } + } + + val version = packageJson.version.orEmpty() + if (version.isBlank()) { + logger.warn { "'$packageJsonFile' does not define a version." } + } + + val declaredLicenses = packageJson.licenses.mapLicenses() + val authors = packageJson.authors.flatMap { parseAuthorString(it.name) } + .mapNotNullTo(mutableSetOf()) { it.name } + val description = packageJson.description.orEmpty() + val homepageUrl = packageJson.homepage.orEmpty() + val projectDir = packageJsonFile.parentFile.realFile() + val vcsFromPackage = parseVcsInfo(packageJson) + + return Project( + id = Identifier( + type = managerName, + namespace = namespace, + name = projectName, + version = version + ), + definitionFilePath = VersionControlSystem.getPathInfo(packageJsonFile.realFile()).path, + authors = authors, + declaredLicenses = declaredLicenses, + vcs = vcsFromPackage, + vcsProcessed = processProjectVcs(projectDir, vcsFromPackage, homepageUrl), + description = description, + homepageUrl = homepageUrl + ) + } + override fun mapDefinitionFiles(definitionFiles: List) = NodePackageManagerDetection(definitionFiles).filterApplicable(managerType) diff --git a/plugins/package-managers/node/src/main/kotlin/NodePackageManagerSupport.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerSupport.kt index 43b0d23ee4b95..da341f9a6320d 100644 --- a/plugins/package-managers/node/src/main/kotlin/NodePackageManagerSupport.kt +++ b/plugins/package-managers/node/src/main/kotlin/NodePackageManagerSupport.kt @@ -20,31 +20,21 @@ package org.ossreviewtoolkit.plugins.packagemanagers.node import java.io.File -import java.lang.invoke.MethodHandles -import org.apache.logging.log4j.kotlin.loggerOf - -import org.ossreviewtoolkit.analyzer.PackageManager.Companion.getFallbackProjectName import org.ossreviewtoolkit.analyzer.PackageManager.Companion.processPackageVcs -import org.ossreviewtoolkit.analyzer.PackageManager.Companion.processProjectVcs import org.ossreviewtoolkit.analyzer.parseAuthorString import org.ossreviewtoolkit.downloader.VcsHost -import org.ossreviewtoolkit.downloader.VersionControlSystem import org.ossreviewtoolkit.model.Hash import org.ossreviewtoolkit.model.Identifier import org.ossreviewtoolkit.model.Package -import org.ossreviewtoolkit.model.Project import org.ossreviewtoolkit.model.RemoteArtifact import org.ossreviewtoolkit.model.VcsInfo import org.ossreviewtoolkit.model.VcsType -import org.ossreviewtoolkit.utils.common.realFile import org.ossreviewtoolkit.utils.common.toUri import org.ossreviewtoolkit.utils.spdx.SpdxConstants internal const val NON_EXISTING_SEMVER = "0.0.0" -private val logger = loggerOf(MethodHandles.lookup().lookupClass()) - /** * Expand an NPM shortcut [url] to a regular URL as used for dependencies, see * https://docs.npmjs.com/cli/v7/configuring-npm/package-json#urls-as-dependencies. @@ -228,49 +218,6 @@ internal fun parsePackage(packageJsonFile: File, getPackageDetails: GetPackageDe return module } -internal fun parseProject(packageJsonFile: File, analysisRoot: File, managerName: String): Project { - logger.debug { "Parsing project info from '$packageJsonFile'." } - - val packageJson = parsePackageJson(packageJsonFile) - - val rawName = packageJson.name.orEmpty() - val (namespace, name) = splitNamespaceAndName(rawName) - - val projectName = name.ifBlank { - getFallbackProjectName(analysisRoot, packageJsonFile).also { - logger.warn { "'$packageJsonFile' does not define a name, falling back to '$it'." } - } - } - - val version = packageJson.version.orEmpty() - if (version.isBlank()) { - logger.warn { "'$packageJsonFile' does not define a version." } - } - - val declaredLicenses = packageJson.licenses.mapLicenses() - val authors = packageJson.authors.flatMap { parseAuthorString(it.name) }.mapNotNullTo(mutableSetOf()) { it.name } - val description = packageJson.description.orEmpty() - val homepageUrl = packageJson.homepage.orEmpty() - val projectDir = packageJsonFile.parentFile.realFile() - val vcsFromPackage = parseVcsInfo(packageJson) - - return Project( - id = Identifier( - type = managerName, - namespace = namespace, - name = projectName, - version = version - ), - definitionFilePath = VersionControlSystem.getPathInfo(packageJsonFile.realFile()).path, - authors = authors, - declaredLicenses = declaredLicenses, - vcs = vcsFromPackage, - vcsProcessed = processProjectVcs(projectDir, vcsFromPackage, homepageUrl), - description = description, - homepageUrl = homepageUrl - ) -} - /** * Split the given [rawName] of a module to a pair with namespace and name. */ diff --git a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt index 5ad3d57ebce8f..3dd79de32223c 100644 --- a/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt +++ b/plugins/package-managers/node/src/main/kotlin/npm/Npm.kt @@ -37,7 +37,6 @@ import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson -import org.ossreviewtoolkit.plugins.packagemanagers.node.parseProject import org.ossreviewtoolkit.utils.common.CommandLineTool import org.ossreviewtoolkit.utils.common.Os import org.ossreviewtoolkit.utils.common.ProcessCapture @@ -101,7 +100,7 @@ class Npm( if (issues.any { it.severity == Severity.ERROR }) { val project = runCatching { - parseProject(definitionFile, analysisRoot, managerName) + parseProject(definitionFile, analysisRoot) }.getOrElse { logger.error { "Failed to parse project information: ${it.collectMessages()}" } Project.EMPTY @@ -110,7 +109,7 @@ class Npm( return listOf(ProjectAnalyzerResult(project, emptySet(), issues)) } - val project = parseProject(definitionFile, analysisRoot, managerName) + val project = parseProject(definitionFile, analysisRoot) val projectModuleInfo = listModules(workingDir, issues).undoDeduplication() val scopeNames = Scope.entries diff --git a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt index 765e16338c5d8..7b2a1b8b028c5 100644 --- a/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt +++ b/plugins/package-managers/node/src/main/kotlin/pnpm/Pnpm.kt @@ -32,7 +32,6 @@ import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson -import org.ossreviewtoolkit.plugins.packagemanagers.node.parseProject import org.ossreviewtoolkit.utils.common.CommandLineTool import org.ossreviewtoolkit.utils.common.Os import org.ossreviewtoolkit.utils.common.stashDirectories @@ -87,7 +86,7 @@ class Pnpm( return workspaceModuleDirs.map { projectDir -> val packageJsonFile = projectDir.resolve(NodePackageManagerType.DEFINITION_FILE) - val project = parseProject(packageJsonFile, analysisRoot, managerName) + val project = parseProject(packageJsonFile, analysisRoot) val scopeNames = scopes.mapTo(mutableSetOf()) { scope -> val scopeName = scope.descriptor diff --git a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt index 80835272d0ce0..7df5e0781dc56 100644 --- a/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt +++ b/plugins/package-managers/node/src/main/kotlin/yarn/Yarn.kt @@ -49,7 +49,6 @@ import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManager import org.ossreviewtoolkit.plugins.packagemanagers.node.NodePackageManagerType import org.ossreviewtoolkit.plugins.packagemanagers.node.PackageJson import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackageJson -import org.ossreviewtoolkit.plugins.packagemanagers.node.parseProject import org.ossreviewtoolkit.plugins.packagemanagers.node.splitNamespaceAndName import org.ossreviewtoolkit.utils.common.CommandLineTool import org.ossreviewtoolkit.utils.common.DiskCache @@ -173,7 +172,7 @@ open class Yarn( val project = runCatching { val packageJsonFile = projectDir.resolve(NodePackageManagerType.DEFINITION_FILE) - parseProject(packageJsonFile, analysisRoot, managerName) + parseProject(packageJsonFile, analysisRoot) }.getOrElse { issues += createAndLogIssue( source = managerName, From ab452ad8acc1ea410feab3e2471b5a4fbe2b2735 Mon Sep 17 00:00:00 2001 From: Sebastian Schuberth Date: Wed, 15 Jan 2025 22:34:04 +0100 Subject: [PATCH 5/5] fix(node): Properly use the `projectType` to create projects Signed-off-by: Sebastian Schuberth --- .../package-managers/node/src/main/kotlin/NodePackageManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt b/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt index 4664f1ba5f615..85339c83e0664 100644 --- a/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt +++ b/plugins/package-managers/node/src/main/kotlin/NodePackageManager.kt @@ -73,7 +73,7 @@ abstract class NodePackageManager( return Project( id = Identifier( - type = managerName, + type = projectType, namespace = namespace, name = projectName, version = version