Skip to content

Commit 1394274

Browse files
committed
refactor(npm): Move parsePackage() outside of the Npm class
Prepare for re-using this function from current child classes of `Npm`, once they get refactored to not inherit from `Npm` anymore. Signed-off-by: Frank Viernau <[email protected]>
1 parent 6999a12 commit 1394274

File tree

2 files changed

+99
-91
lines changed

2 files changed

+99
-91
lines changed

plugins/package-managers/node/src/main/kotlin/Npm.kt

+95-90
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.apache.logging.log4j.kotlin.logger
2828

2929
import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory
3030
import org.ossreviewtoolkit.analyzer.PackageManager
31+
import org.ossreviewtoolkit.analyzer.PackageManager.Companion.processPackageVcs
3132
import org.ossreviewtoolkit.analyzer.PackageManagerResult
3233
import org.ossreviewtoolkit.downloader.VcsHost
3334
import org.ossreviewtoolkit.downloader.VersionControlSystem
@@ -240,96 +241,7 @@ open class Npm(
240241
}
241242
}
242243

243-
/**
244-
* Construct a [Package] by parsing its _package.json_ file and - if applicable - querying additional
245-
* content via the `npm view` command. The result is a [Pair] with the raw identifier and the new package.
246-
*/
247-
internal fun parsePackage(workingDir: File, packageJsonFile: File): Package {
248-
val packageJson = parsePackageJson(packageJsonFile)
249-
250-
// The "name" and "version" fields are only required if the package is going to be published, otherwise they are
251-
// optional, see
252-
// - https://docs.npmjs.com/cli/v10/configuring-npm/package-json#name
253-
// - https://docs.npmjs.com/cli/v10/configuring-npm/package-json#version
254-
// So, projects analyzed by ORT might not have these fields set.
255-
val rawName = packageJson.name.orEmpty() // TODO: Fall back to a generated name if the name is unset.
256-
val (namespace, name) = splitNpmNamespaceAndName(rawName)
257-
val version = packageJson.version ?: NON_EXISTING_SEMVER
258-
259-
val declaredLicenses = packageJson.licenses.mapNpmLicenses()
260-
val authors = parseNpmAuthor(packageJson.authors.firstOrNull()) // TODO: parse all authors.
261-
262-
var description = packageJson.description.orEmpty()
263-
var homepageUrl = packageJson.homepage.orEmpty()
264-
265-
// Note that all fields prefixed with "_" are considered private to NPM and should not be relied on.
266-
var downloadUrl = expandNpmShortcutUrl(packageJson.resolved.orEmpty()).ifEmpty {
267-
// If the normalized form of the specified dependency contains a URL as the version, expand and use it.
268-
val fromVersion = packageJson.from.orEmpty().substringAfterLast('@')
269-
expandNpmShortcutUrl(fromVersion).takeIf { it != fromVersion }.orEmpty()
270-
}
271-
272-
var hash = Hash.create(packageJson.integrity.orEmpty())
273-
274-
var vcsFromPackage = parseNpmVcsInfo(packageJson)
275-
276-
val id = Identifier("NPM", namespace, name, version)
277-
278-
val hasIncompleteData = description.isEmpty() || homepageUrl.isEmpty() || downloadUrl.isEmpty()
279-
|| hash == Hash.NONE || vcsFromPackage == VcsInfo.EMPTY
280-
281-
if (hasIncompleteData) {
282-
getRemotePackageDetails(workingDir, "$rawName@$version")?.let { details ->
283-
if (description.isEmpty()) description = details.description.orEmpty()
284-
if (homepageUrl.isEmpty()) homepageUrl = details.homepage.orEmpty()
285-
286-
details.dist?.let { dist ->
287-
if (downloadUrl.isEmpty() || hash == Hash.NONE) {
288-
downloadUrl = dist.tarball.orEmpty()
289-
hash = Hash.create(dist.shasum.orEmpty())
290-
}
291-
}
292-
293-
// Do not replace but merge, because it happens that `package.json` has VCS info while
294-
// `npm view` doesn't, for example for dependencies hosted on GitLab package registry.
295-
vcsFromPackage = vcsFromPackage.merge(parseNpmVcsInfo(details))
296-
}
297-
}
298-
299-
downloadUrl = downloadUrl.fixNpmDownloadUrl()
300-
301-
val vcsFromDownloadUrl = VcsHost.parseUrl(downloadUrl)
302-
if (vcsFromDownloadUrl.url != downloadUrl) {
303-
vcsFromPackage = vcsFromPackage.merge(vcsFromDownloadUrl)
304-
}
305-
306-
val module = Package(
307-
id = id,
308-
authors = authors,
309-
declaredLicenses = declaredLicenses,
310-
description = description,
311-
homepageUrl = homepageUrl,
312-
binaryArtifact = RemoteArtifact.EMPTY,
313-
sourceArtifact = RemoteArtifact(
314-
url = VcsHost.toArchiveDownloadUrl(vcsFromDownloadUrl) ?: downloadUrl,
315-
hash = hash
316-
),
317-
vcs = vcsFromPackage,
318-
vcsProcessed = processPackageVcs(vcsFromPackage, homepageUrl)
319-
)
320-
321-
require(module.id.name.isNotEmpty()) {
322-
"Generated package info for '${id.toCoordinates()}' has no name."
323-
}
324-
325-
require(module.id.version.isNotEmpty()) {
326-
"Generated package info for '${id.toCoordinates()}' has no version."
327-
}
328-
329-
return module
330-
}
331-
332-
protected open fun getRemotePackageDetails(workingDir: File, packageName: String): PackageJson? {
244+
internal open fun getRemotePackageDetails(workingDir: File, packageName: String): PackageJson? {
333245
npmViewCache[packageName]?.let { return it }
334246

335247
return runCatching {
@@ -636,3 +548,96 @@ internal fun List<String>.groupLines(vararg markers: String): List<String> {
636548
nonFooterLines.map { it.trim() }
637549
}
638550
}
551+
552+
/**
553+
* Construct a [Package] by parsing its _package.json_ file and - if applicable - querying additional
554+
* content via the `npm view` command. The result is a [Pair] with the raw identifier and the new package.
555+
*/
556+
internal fun parsePackage(
557+
workingDir: File,
558+
packageJsonFile: File,
559+
getRemotePackageDetails: (workingDir: File, packageName: String) -> PackageJson?
560+
): Package {
561+
val packageJson = parsePackageJson(packageJsonFile)
562+
563+
// The "name" and "version" fields are only required if the package is going to be published, otherwise they are
564+
// optional, see
565+
// - https://docs.npmjs.com/cli/v10/configuring-npm/package-json#name
566+
// - https://docs.npmjs.com/cli/v10/configuring-npm/package-json#version
567+
// So, projects analyzed by ORT might not have these fields set.
568+
val rawName = packageJson.name.orEmpty() // TODO: Fall back to a generated name if the name is unset.
569+
val (namespace, name) = splitNpmNamespaceAndName(rawName)
570+
val version = packageJson.version ?: NON_EXISTING_SEMVER
571+
572+
val declaredLicenses = packageJson.licenses.mapNpmLicenses()
573+
val authors = parseNpmAuthor(packageJson.authors.firstOrNull()) // TODO: parse all authors.
574+
575+
var description = packageJson.description.orEmpty()
576+
var homepageUrl = packageJson.homepage.orEmpty()
577+
578+
// Note that all fields prefixed with "_" are considered private to NPM and should not be relied on.
579+
var downloadUrl = expandNpmShortcutUrl(packageJson.resolved.orEmpty()).ifEmpty {
580+
// If the normalized form of the specified dependency contains a URL as the version, expand and use it.
581+
val fromVersion = packageJson.from.orEmpty().substringAfterLast('@')
582+
expandNpmShortcutUrl(fromVersion).takeIf { it != fromVersion }.orEmpty()
583+
}
584+
585+
var hash = Hash.create(packageJson.integrity.orEmpty())
586+
587+
var vcsFromPackage = parseNpmVcsInfo(packageJson)
588+
589+
val id = Identifier("NPM", namespace, name, version)
590+
591+
val hasIncompleteData = description.isEmpty() || homepageUrl.isEmpty() || downloadUrl.isEmpty()
592+
|| hash == Hash.NONE || vcsFromPackage == VcsInfo.EMPTY
593+
594+
if (hasIncompleteData) {
595+
getRemotePackageDetails(workingDir, "$rawName@$version")?.let { details ->
596+
if (description.isEmpty()) description = details.description.orEmpty()
597+
if (homepageUrl.isEmpty()) homepageUrl = details.homepage.orEmpty()
598+
599+
details.dist?.let { dist ->
600+
if (downloadUrl.isEmpty() || hash == Hash.NONE) {
601+
downloadUrl = dist.tarball.orEmpty()
602+
hash = Hash.create(dist.shasum.orEmpty())
603+
}
604+
}
605+
606+
// Do not replace but merge, because it happens that `package.json` has VCS info while
607+
// `npm view` doesn't, for example for dependencies hosted on GitLab package registry.
608+
vcsFromPackage = vcsFromPackage.merge(parseNpmVcsInfo(details))
609+
}
610+
}
611+
612+
downloadUrl = downloadUrl.fixNpmDownloadUrl()
613+
614+
val vcsFromDownloadUrl = VcsHost.parseUrl(downloadUrl)
615+
if (vcsFromDownloadUrl.url != downloadUrl) {
616+
vcsFromPackage = vcsFromPackage.merge(vcsFromDownloadUrl)
617+
}
618+
619+
val module = Package(
620+
id = id,
621+
authors = authors,
622+
declaredLicenses = declaredLicenses,
623+
description = description,
624+
homepageUrl = homepageUrl,
625+
binaryArtifact = RemoteArtifact.EMPTY,
626+
sourceArtifact = RemoteArtifact(
627+
url = VcsHost.toArchiveDownloadUrl(vcsFromDownloadUrl) ?: downloadUrl,
628+
hash = hash
629+
),
630+
vcs = vcsFromPackage,
631+
vcsProcessed = processPackageVcs(vcsFromPackage, homepageUrl)
632+
)
633+
634+
require(module.id.name.isNotEmpty()) {
635+
"Generated package info for '${id.toCoordinates()}' has no name."
636+
}
637+
638+
require(module.id.version.isNotEmpty()) {
639+
"Generated package info for '${id.toCoordinates()}' has no version."
640+
}
641+
642+
return module
643+
}

plugins/package-managers/node/src/main/kotlin/utils/NpmDependencyHandler.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.ossreviewtoolkit.model.PackageLinkage
2828
import org.ossreviewtoolkit.model.Project
2929
import org.ossreviewtoolkit.model.utils.DependencyHandler
3030
import org.ossreviewtoolkit.plugins.packagemanagers.node.Npm
31+
import org.ossreviewtoolkit.plugins.packagemanagers.node.parsePackage
3132

3233
/**
3334
* A data class storing information about a specific NPM module and its dependencies.
@@ -75,5 +76,7 @@ internal class NpmDependencyHandler(private val npm: Npm) : DependencyHandler<Np
7576
PackageLinkage.DYNAMIC.takeUnless { dependency.isProject } ?: PackageLinkage.PROJECT_DYNAMIC
7677

7778
override fun createPackage(dependency: NpmModuleInfo, issues: MutableCollection<Issue>): Package? =
78-
npm.takeUnless { dependency.isProject }?.parsePackage(dependency.workingDir, dependency.packageFile)
79+
npm.takeUnless { dependency.isProject }?.let {
80+
parsePackage(dependency.workingDir, dependency.packageFile, it::getRemotePackageDetails)
81+
}
7982
}

0 commit comments

Comments
 (0)