All Downloads are FREE. Search and download functionalities are using the official Maven repository.

toolkit.plugins.packagemanagers.bower-package-manager.41.0.0.source-code.Bower.kt Maven / Gradle / Ivy

Go to download

Part of the OSS Review Toolkit (ORT), a suite to automate software compliance checks.

There is a newer version: 44.0.0
Show newest version
/*
 * Copyright (C) 2017 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.bower

import java.io.File
import java.util.LinkedList

import org.ossreviewtoolkit.analyzer.AbstractPackageManagerFactory
import org.ossreviewtoolkit.analyzer.PackageManager
import org.ossreviewtoolkit.analyzer.PackageManager.Companion.processProjectVcs
import org.ossreviewtoolkit.analyzer.parseAuthorString
import org.ossreviewtoolkit.downloader.VersionControlSystem
import org.ossreviewtoolkit.model.Identifier
import org.ossreviewtoolkit.model.Package
import org.ossreviewtoolkit.model.PackageReference
import org.ossreviewtoolkit.model.Project
import org.ossreviewtoolkit.model.ProjectAnalyzerResult
import org.ossreviewtoolkit.model.RemoteArtifact
import org.ossreviewtoolkit.model.Scope
import org.ossreviewtoolkit.model.VcsInfo
import org.ossreviewtoolkit.model.VcsType
import org.ossreviewtoolkit.model.collectDependencies
import org.ossreviewtoolkit.model.config.AnalyzerConfiguration
import org.ossreviewtoolkit.model.config.RepositoryConfiguration
import org.ossreviewtoolkit.utils.common.CommandLineTool
import org.ossreviewtoolkit.utils.common.Os
import org.ossreviewtoolkit.utils.common.stashDirectories

import org.semver4j.RangesList
import org.semver4j.RangesListFactory

/**
 * The [Bower](https://bower.io/) package manager for JavaScript.
 */
class Bower(
    name: String,
    analysisRoot: File,
    analyzerConfig: AnalyzerConfiguration,
    repoConfig: RepositoryConfiguration
) : PackageManager(name, "Bower", analysisRoot, analyzerConfig, repoConfig), CommandLineTool {
    class Factory : AbstractPackageManagerFactory("Bower") {
        override val globsForDefinitionFiles = listOf("bower.json")

        override fun create(
            analysisRoot: File,
            analyzerConfig: AnalyzerConfiguration,
            repoConfig: RepositoryConfiguration
        ) = Bower(type, analysisRoot, analyzerConfig, repoConfig)
    }

    override fun command(workingDir: File?) = if (Os.isWindows) "bower.cmd" else "bower"

    override fun getVersionRequirement(): RangesList = RangesListFactory.create(">=1.8.8")

    override fun beforeResolution(definitionFiles: List) = checkVersion()

    override fun resolveDependencies(definitionFile: File, labels: Map): List {
        val workingDir = definitionFile.parentFile

        stashDirectories(workingDir.resolve("bower_components")).use { _ ->
            val projectPackageInfo = getProjectPackageInfo(workingDir)
            val packageInfoForName = projectPackageInfo
                .getTransitiveDependencies()
                .associateBy { checkNotNull(it.pkgMeta.name) }

            val scopes = SCOPE_NAMES.mapTo(mutableSetOf()) { scopeName ->
                Scope(
                    name = scopeName,
                    dependencies = projectPackageInfo.toPackageReferences(scopeName, packageInfoForName)
                )
            }

            val project = projectPackageInfo.toProject(definitionFile, scopes)

            val referencedPackages = scopes.collectDependencies()
            val packages = packageInfoForName.values
                .map { it.toPackage() }
                .filterTo(mutableSetOf()) { it.id in referencedPackages }

            return listOf(ProjectAnalyzerResult(project, packages))
        }
    }

    private fun getProjectPackageInfo(workingDir: File): PackageInfo {
        run(workingDir, "--allow-root", "install").requireSuccess()
        val json = run(workingDir, "--allow-root", "list", "--json").stdout
        return parsePackageInfoJson(json)
    }
}

private const val SCOPE_NAME_DEPENDENCIES = "dependencies"
private const val SCOPE_NAME_DEV_DEPENDENCIES = "devDependencies"
private val SCOPE_NAMES = setOf(SCOPE_NAME_DEPENDENCIES, SCOPE_NAME_DEV_DEPENDENCIES)

private fun PackageInfo.getScopeDependencies(scopeName: String): Set =
    when (scopeName) {
        SCOPE_NAME_DEPENDENCIES -> pkgMeta.dependencies.keys
        SCOPE_NAME_DEV_DEPENDENCIES -> pkgMeta.devDependencies.keys
        else -> error("Invalid scope name: '$scopeName'.")
    }

private fun PackageInfo.getTransitiveDependencies(): List {
    val result = LinkedList()
    val queue = LinkedList(dependencies.values)

    while (queue.isNotEmpty()) {
        val info = queue.removeFirst()
        result += info
        queue += info.dependencies.values
    }

    return result
}

private fun PackageInfo.toId() =
    Identifier(
        type = "Bower",
        namespace = "",
        name = pkgMeta.name.orEmpty(),
        version = pkgMeta.version.orEmpty()
    )

private fun PackageInfo.toVcsInfo() =
    VcsInfo(
        type = VcsType.forName(pkgMeta.repository?.type.orEmpty()),
        url = pkgMeta.repository?.url ?: pkgMeta.source.orEmpty(),
        revision = pkgMeta.resolution?.commit ?: pkgMeta.resolution?.tag.orEmpty()
    )

private fun PackageInfo.toPackage() =
    Package(
        id = toId(),
        // See https://github.com/bower/spec/blob/master/json.md#authors.
        authors = pkgMeta.authors.flatMap { parseAuthorString(it.name) }.mapNotNullTo(mutableSetOf()) { it.name },
        declaredLicenses = setOfNotNull(pkgMeta.license?.takeUnless { it.isEmpty() }),
        description = pkgMeta.description.orEmpty(),
        homepageUrl = pkgMeta.homepage.orEmpty(),
        binaryArtifact = RemoteArtifact.EMPTY,
        sourceArtifact = RemoteArtifact.EMPTY, // TODO: implement me!
        vcs = toVcsInfo()
    )

private fun PackageInfo.toProject(definitionFile: File, scopes: Set) =
    with(toPackage()) {
        Project(
            id = id,
            definitionFilePath = VersionControlSystem.getPathInfo(definitionFile).path,
            authors = authors,
            declaredLicenses = declaredLicenses,
            vcs = vcs,
            vcsProcessed = processProjectVcs(definitionFile.parentFile, vcs, homepageUrl),
            homepageUrl = homepageUrl,
            scopeDependencies = scopes
        )
    }

private fun PackageInfo.toPackageReferences(
    scopeName: String,
    packageInfoForName: Map
): Set =
    getScopeDependencies(scopeName).mapTo(mutableSetOf()) { name ->
        // Bower leaves out a dependency entry for a child if there exists a similar entry to its parent entry
        // with the exact same name and resolved target. This makes it necessary to retrieve the information
        // about the subtree rooted at the parent from that other entry containing the full dependency
        // information.
        // See https://github.com/bower/bower/blob/6bc778d/lib/core/Manager.js#L557 and below.
        val childInfo = dependencies[name] ?: packageInfoForName.getValue(name)

        PackageReference(
            id = childInfo.toId(),
            dependencies = childInfo.toPackageReferences(
                scopeName = SCOPE_NAME_DEPENDENCIES,
                packageInfoForName = packageInfoForName
            )
        )
    }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy