[Gradle, JS] Add new state for npm resolution

#KT-34832 fixed
This commit is contained in:
Ilya Goncharov
2020-04-16 17:55:10 +03:00
parent 9174e184d3
commit 0af6454e44
5 changed files with 35 additions and 23 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. * Copyright 2010-2020 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. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/ */
@@ -17,7 +17,7 @@ open class PackageJsonDukatTask : AbstractDukatTask() {
@get:Internal @get:Internal
val dts by lazy { val dts by lazy {
val resolvedCompilation = nodeJs.npmResolutionManager.requireInstalled()[project][compilation] val resolvedCompilation = nodeJs.npmResolutionManager.requireResolved()[project][compilation]
val dtsResolver = DtsResolver(resolvedCompilation.npmProject) val dtsResolver = DtsResolver(resolvedCompilation.npmProject)
dtsResolver.getAllDts(resolvedCompilation.externalNpmDependencies) dtsResolver.getAllDts(resolvedCompilation.externalNpmDependencies)
} }
@@ -53,14 +53,14 @@ import java.io.File
* All created package.json files will be gathered and package manager will be executed. * All created package.json files will be gathered and package manager will be executed.
* Package manager will create lock file, that will be parsed for transitive npm dependencies * Package manager will create lock file, that will be parsed for transitive npm dependencies
* that will be added to the root [NpmDependency] objects. `kotlinNpmInstall` task may be up-to-date. * that will be added to the root [NpmDependency] objects. `kotlinNpmInstall` task may be up-to-date.
* In this case, installed state will be reached by first call of [installIfNeeded] without executing * In this case, installed state will be reached by first call of [prepareIfNeeded] without executing
* package manager. * package manager.
* *
* Resolution will be used from [NpmDependency] by calling [getNpmDependencyResolvedCompilation]. * Resolution will be used from [NpmDependency] by calling [getNpmDependencyResolvedCompilation].
* Also resolution will be checked before calling [NpmProject.require] and executing any task * Also resolution will be checked before calling [NpmProject.require] and executing any task
* that requires npm dependencies or node_modules. * that requires npm dependencies or node_modules.
* *
* User can call [requireInstalled] to get resolution info. * User can call [requirePrepared] to get resolution info.
* *
* ## Resolving process during Idea import * ## Resolving process during Idea import
* *
@@ -94,23 +94,31 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
} }
class Prepared(val resolved: KotlinRootNpmResolution) : ResolutionState() { open class Prepared(val resolved: KotlinRootNpmResolution) : ResolutionState() {
override val npmProjects: List<NpmProject> override val npmProjects: List<NpmProject>
get() = resolved.projects.values.flatMap { it.npmProjects.map { it.npmProject } } get() = resolved.projects.values.flatMap { it.npmProjects.map { it.npmProject } }
} }
class Resolved(resolved: KotlinRootNpmResolution) : Prepared(resolved)
} }
@Incubating @Incubating
internal fun requireInstalled() = installIfNeeded(requireUpToDateReason = "") internal fun requirePrepared() = prepareIfNeeded(requireUpToDateReason = "")
internal fun requireConfiguringState(): KotlinRootNpmResolver = internal fun requireConfiguringState(): KotlinRootNpmResolver =
(this.state as? ResolutionState.Configuring ?: error("NPM Dependencies already resolved and installed")).resolver (this.state as? ResolutionState.Configuring ?: error("NPM Dependencies already resolved and installed")).resolver
internal fun install() = installIfNeeded(requireNotInstalled = true) internal fun prepare() = prepareIfNeeded(requireNotInstalled = true)
internal fun resolve(args: List<String> = emptyList()) { internal fun requireResolved(
val npmResolution = requireInstalled() reason: String = "",
args: List<String> = emptyList()
): KotlinRootNpmResolution {
if (state is ResolutionState.Resolved) {
return (state as ResolutionState.Resolved).resolved
}
val npmResolution = prepareIfNeeded(requireUpToDateReason = reason)
val npmResolutions = npmResolution val npmResolutions = npmResolution
.projects .projects
.values .values
@@ -122,12 +130,16 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
args args
) )
npmResolution.plugins return ResolutionState.Resolved((state as ResolutionState.Prepared).resolved)
.forEach { it.resolve(npmResolution) } .apply {
state = this
npmResolution.plugins
.forEach { it.resolve(npmResolution) }
}.resolved
} }
internal fun requireAlreadyInstalled(project: Project, reason: String = ""): KotlinProjectNpmResolution = internal fun requireAlreadyResolved(project: Project, reason: String = ""): KotlinProjectNpmResolution =
installIfNeeded(requireUpToDateReason = reason)[project] requireResolved(reason = reason)[project]
internal val packageJsonFiles: Collection<File> internal val packageJsonFiles: Collection<File>
get() = state.npmProjects.map { it.packageJsonFile } get() = state.npmProjects.map { it.packageJsonFile }
@@ -137,7 +149,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
* or it is up-to-date but just not closed. Show given message if it is not. * or it is up-to-date but just not closed. Show given message if it is not.
* @param requireNotInstalled Check that project is not resolved * @param requireNotInstalled Check that project is not resolved
*/ */
private fun installIfNeeded( private fun prepareIfNeeded(
requireUpToDateReason: String? = null, requireUpToDateReason: String? = null,
requireNotInstalled: Boolean = false requireNotInstalled: Boolean = false
): KotlinRootNpmResolution { ): KotlinRootNpmResolution {
@@ -177,7 +189,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
val resolvedProject = val resolvedProject =
if (forceFullResolve) { if (forceFullResolve) {
installIfNeeded()[project] prepareIfNeeded()[project]
} else { } else {
// may return null only during npm resolution // may return null only during npm resolution
// (it can be called since NpmDependency added to configuration that // (it can be called since NpmDependency added to configuration that
@@ -192,7 +204,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
} }
} }
resolve() requireResolved()
return resolvedProject.npmProjectsByNpmDependency[npmDependency] ?: error("NPM project resolved without $this") return resolvedProject.npmProjectsByNpmDependency[npmDependency] ?: error("NPM project resolved without $this")
} }
@@ -201,7 +213,7 @@ class KotlinNpmResolutionManager(private val nodeJsSettings: NodeJsRootExtension
where T : RequiresNpmDependencies, where T : RequiresNpmDependencies,
T : Task { T : Task {
val project = task.project val project = task.project
val requestedTaskDependencies = requireAlreadyInstalled(project, "before $task execution").taskRequirements val requestedTaskDependencies = requireAlreadyResolved(project, "before $task execution").taskRequirements
val targetRequired = requestedTaskDependencies[task]?.toSet() ?: setOf() val targetRequired = requestedTaskDependencies[task]?.toSet() ?: setOf()
task.requiredNpmDependencies.forEach { task.requiredNpmDependencies.forEach {
@@ -1,6 +1,6 @@
/* /*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* that can be found in the license/LICENSE.txt file. * 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 package org.jetbrains.kotlin.gradle.targets.js.npm
@@ -79,7 +79,7 @@ open class NpmProject(val compilation: KotlinJsCompilation) {
* Require [request] nodejs module and return canonical path to it's main js file. * Require [request] nodejs module and return canonical path to it's main js file.
*/ */
fun require(request: String): String { fun require(request: String): String {
nodeJs.npmResolutionManager.requireAlreadyInstalled(project) nodeJs.npmResolutionManager.requireAlreadyResolved(project)
return modules.require(request) return modules.require(request)
} }
@@ -45,7 +45,7 @@ open class KotlinNpmInstallTask : DefaultTask() {
@TaskAction @TaskAction
fun resolve() { fun resolve() {
resolutionManager.resolve(args) resolutionManager.requireResolved(args = args)
} }
companion object { companion object {
@@ -29,7 +29,7 @@ open class RootPackageJsonTask : DefaultTask() {
@TaskAction @TaskAction
fun resolve() { fun resolve() {
resolutionManager.install() resolutionManager.prepare()
} }
companion object { companion object {