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

org.jetbrains.kotlin.gradle.targets.js.npm.resolver.KotlinRootNpmResolver.kt Maven / Gradle / Ivy

There is a newer version: 2.0.20-RC
Show newest version
/*
 * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.gradle.targets.js.npm.resolver

import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.dukat.DukatRootResolverPlugin
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
import org.jetbrains.kotlin.gradle.targets.js.npm.GradleNodeModulesCache
import org.jetbrains.kotlin.gradle.targets.js.npm.KotlinNpmResolutionManager
import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJsonUpToDateCheck
import org.jetbrains.kotlin.gradle.targets.js.npm.plugins.RootResolverPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinCompilationNpmResolution
import org.jetbrains.kotlin.gradle.targets.js.npm.resolved.KotlinRootNpmResolution

/**
 * See [KotlinNpmResolutionManager] for details about resolution process.
 */
internal class KotlinRootNpmResolver internal constructor(
    val nodeJs: NodeJsRootExtension,
    val forceFullResolve: Boolean
) {
    val rootProject: Project
        get() = nodeJs.rootProject

    val plugins = mutableListOf().also {
        if (nodeJs.experimental.generateKotlinExternals) {
            it.add(DukatRootResolverPlugin(this))
        }
    }

    private var closed: Boolean = false

    val gradleNodeModules = GradleNodeModulesCache(nodeJs)
    private val projectResolvers = mutableMapOf()

    fun alreadyResolvedMessage(action: String) = "Cannot $action. NodeJS projects already resolved."

    @Synchronized
    fun addProject(target: Project) {
        check(!closed) { alreadyResolvedMessage("add new project: $target") }
        projectResolvers[target] = KotlinProjectNpmResolver(target, this)
    }

    operator fun get(project: Project) = projectResolvers[project] ?: error("$project is not configured for JS usage")

    val compilations: Collection
        get() = projectResolvers.values.flatMap { it.compilationResolvers.map { it.compilation } }

    fun findDependentResolver(src: Project, target: Project): KotlinCompilationNpmResolver? {
        // todo: proper finding using KotlinTargetComponent.findUsageContext
        val targetResolver = this[target]
        val mainCompilations = targetResolver.compilationResolvers.filter { it.compilation.name == KotlinCompilation.MAIN_COMPILATION_NAME }

        return if (mainCompilations.isNotEmpty()) {
            if (mainCompilations.size > 1) {
                error(
                    "Cannot resolve project dependency $src -> $target." +
                            "Dependency to project with multiple js compilation not supported yet."
                )
            }

            mainCompilations.single()
        } else null
    }

    /**
     * Don't use directly, use [NodeJsRootExtension.resolveIfNeeded] instead.
     */
    internal fun close(forceUpToDate: Boolean): KotlinRootNpmResolution {
        check(!closed)
        closed = true

        val projectResolutions = projectResolvers.values
            .map { it.close() }
            .associateBy { it.project }
        val allNpmPackages = projectResolutions.values.flatMap { it.npmProjects }

        gradleNodeModules.close()

        // we need manual up-to-date checking to avoid call package manager during
        // idea import if nothing was changed
        // we should call it even kotlinNpmInstall task is up-to-date (skipPackageManager is true)
        // because our upToDateChecks saves state for next execution
        val upToDateChecks = allNpmPackages.map {
            PackageJsonUpToDateCheck(it.npmProject)
        }
        val upToDate = forceUpToDate || upToDateChecks.all { it.upToDate }

        val hasNodeModulesDependentTasks = projectResolvers.values.any { it.taskRequirements.hasNodeModulesDependentTasks }
        if (hasNodeModulesDependentTasks) {
            nodeJs.packageManager.resolveRootProject(rootProject, allNpmPackages, upToDate)
        }

        nodeJs.rootNodeModulesStateFile.writeText(System.currentTimeMillis().toString())

        upToDateChecks.forEach { it.commit() }

        return KotlinRootNpmResolution(rootProject, projectResolutions)
    }

    internal fun closePlugins(resolution: KotlinRootNpmResolution) {
        plugins.forEach {
            it.close(resolution)
        }
    }

    private fun removeOutdatedPackages(nodeJs: NodeJsRootExtension, allNpmPackages: List) {
        val packages = allNpmPackages.mapTo(mutableSetOf()) { it.npmProject.name }
        nodeJs.projectPackagesDir.listFiles()?.forEach {
            if (it.name !in packages) {
                it.deleteRecursively()
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy