diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt index c8df6c96c0a..bea6cef3b7e 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/Yarn.kt @@ -6,141 +6,20 @@ package org.jetbrains.kotlin.gradle.targets.js.yarn import org.gradle.api.Project -import org.jetbrains.kotlin.gradle.internal.execWithProgress -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin -import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs -import org.jetbrains.kotlin.gradle.targets.js.npm.* -import java.io.File +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectPackage object Yarn : NpmApi { - override fun setup(project: Project) { - YarnPlugin.apply(project).executeSetup() - } + private fun getDelegate(project: Project): NpmApi = + if (project.yarn.useWorkspaces) YarnWorkspaces + else YarnSimple - val NpmProject.packageJsonHashFile: File - get() = dir.resolve("package.json.hash") + override fun setup(project: Project) = + getDelegate(project.rootProject).setup(project) - fun yarnExec( - project: Project, - dir: File, - description: String, - vararg args: String - ) { - val nodeJsEnv = NodeJsPlugin.apply(project).root.environment - val yarnEnv = YarnPlugin.apply(project).environment + override fun resolveProject(resolvedNpmProject: NpmProjectPackage) = + getDelegate(resolvedNpmProject.project).resolveProject(resolvedNpmProject) - project.execWithProgress(description) { exec -> - exec.executable = nodeJsEnv.nodeExecutable - exec.args = listOf(yarnEnv.home.resolve("bin/yarn.js").absolutePath) + args - exec.workingDir = dir - } - - } - - private fun yarnLockReadTransitiveDependencies( - nodeWorkDir: File, - srcDependenciesList: Collection - ) { - val yarnLock = nodeWorkDir.resolve("yarn.lock") - if (yarnLock.isFile) { - val byKey = YarnLock.parse(yarnLock).entries.associateBy { it.key } - val visited = mutableSetOf() - - fun resolveRecursively(src: NpmDependency): NpmDependency { - if (src in visited) return src - visited.add(src) - - val key = YarnLock.key(src.key, src.version) - val deps = byKey[key] - if (deps != null) { - src.dependencies.addAll(deps.dependencies.map { dep -> - val scopedName = dep.scopedName - - resolveRecursively( - NpmDependency( - src.project, - scopedName.scope, - scopedName.name, - dep.version ?: "*" - ) - ) - }) - } else { - // todo: [WARN] cannot find $key in yarn.lock - } - - return src - } - - srcDependenciesList.forEach { src -> - resolveRecursively(src) - } - } - } - - override fun resolveProject(resolvedNpmProject: NpmProjectPackage) { - val project = resolvedNpmProject.project - if (!project.yarn.useWorkspaces) { - YarnAvoidance(resolvedNpmProject.npmProject).updateIfNeeded { - yarnExec(project, resolvedNpmProject.npmProject.dir, NpmApi.resolveOperationDescription("yarn for ${project.path}")) - yarnLockReadTransitiveDependencies(resolvedNpmProject.npmProject.dir, resolvedNpmProject.npmDependencies) - } - } - } - - override fun resolveRootProject(rootProject: Project, subProjects: MutableList) { - check(rootProject == rootProject.rootProject) - - if (rootProject.yarn.useWorkspaces) { - resolveWorkspaces(rootProject, subProjects) - } - } - - private fun resolveWorkspaces( - rootProject: Project, - npmProjects: MutableList - ) { - val upToDateChecks = npmProjects.map { - YarnAvoidance(it.npmProject) - } - - if (upToDateChecks.all { it.upToDate }) return - - val nodeJsWorldDir = rootProject.nodeJs.root.rootPackageDir - - saveRootProjectWorkspacesPackageJson(rootProject, npmProjects, nodeJsWorldDir) - - yarnExec(rootProject, nodeJsWorldDir, NpmApi.resolveOperationDescription("yarn")) - yarnLockReadTransitiveDependencies(nodeJsWorldDir, npmProjects.flatMap { it.npmDependencies }) - - upToDateChecks.forEach { - it.commit() - } - } - - private fun saveRootProjectWorkspacesPackageJson( - rootProject: Project, - npmProjects: MutableList, - nodeJsWorldDir: File - ) { - val rootPackageJson = PackageJson(rootProject.name, rootProject.version.toString()) - rootPackageJson.private = true - - val npmProjectWorkspaces = npmProjects.map { it.npmProject.dir.relativeTo(nodeJsWorldDir).path } - val importedProjectWorkspaces = npmProjects.flatMapTo(mutableSetOf()) { - it.gradleDependencies.externalModules.map { importedProject -> - importedProject.path.relativeTo(nodeJsWorldDir).path - } - } - - rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces - - rootProject.nodeJs.packageJsonHandlers.forEach { - it(rootPackageJson) - } - - rootPackageJson.saveTo( - nodeJsWorldDir.resolve(NpmProject.PACKAGE_JSON) - ) - } + override fun resolveRootProject(rootProject: Project, subProjects: MutableList) = + getDelegate(rootProject.project).resolveRootProject(rootProject, subProjects) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt new file mode 100644 index 00000000000..df128bec3bf --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnBasics.kt @@ -0,0 +1,78 @@ +/* + * 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.yarn + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.internal.execWithProgress +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmDependency +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import java.io.File + +abstract class YarnBasics : NpmApi { + override fun setup(project: Project) { + YarnPlugin.apply(project).executeSetup() + } + + fun yarnExec( + project: Project, + dir: File, + description: String, + vararg args: String + ) { + val nodeJsEnv = NodeJsPlugin.apply(project).root.environment + val yarnEnv = YarnPlugin.apply(project).environment + + project.execWithProgress(description) { exec -> + exec.executable = nodeJsEnv.nodeExecutable + exec.args = listOf(yarnEnv.home.resolve("bin/yarn.js").absolutePath) + args + exec.workingDir = dir + } + + } + + protected fun yarnLockReadTransitiveDependencies( + nodeWorkDir: File, + srcDependenciesList: Collection + ) { + val yarnLock = nodeWorkDir.resolve("yarn.lock") + if (yarnLock.isFile) { + val byKey = YarnLock.parse(yarnLock).entries.associateBy { it.key } + val visited = mutableSetOf() + + fun resolveRecursively(src: NpmDependency): NpmDependency { + if (src in visited) return src + visited.add(src) + + val key = YarnLock.key(src.key, src.version) + val deps = byKey[key] + if (deps != null) { + src.dependencies.addAll(deps.dependencies.map { dep -> + val scopedName = dep.scopedName + + resolveRecursively( + NpmDependency( + src.project, + scopedName.scope, + scopedName.name, + dep.version ?: "*" + ) + ) + }) + } else { + // todo: [WARN] cannot find $key in yarn.lock + } + + return src + } + + srcDependenciesList.forEach { src -> + resolveRecursively(src) + } + } + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSimple.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSimple.kt new file mode 100644 index 00000000000..522104e1140 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnSimple.kt @@ -0,0 +1,23 @@ +/* + * 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.yarn + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectPackage + +object YarnSimple : YarnBasics() { + override fun resolveProject(resolvedNpmProject: NpmProjectPackage) { + val project = resolvedNpmProject.project + + YarnUpToDateCheck(resolvedNpmProject.npmProject).updateIfNeeded { + yarnExec(project, resolvedNpmProject.npmProject.dir, NpmApi.resolveOperationDescription("yarn for ${project.path}")) + yarnLockReadTransitiveDependencies(resolvedNpmProject.npmProject.dir, resolvedNpmProject.npmDependencies) + } + } + + override fun resolveRootProject(rootProject: Project, subProjects: MutableList) = Unit +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnAvoidance.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnUpToDateCheck.kt similarity index 83% rename from libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnAvoidance.kt rename to libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnUpToDateCheck.kt index 42e41a20bc3..b237d4a7ed1 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnAvoidance.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnUpToDateCheck.kt @@ -10,16 +10,16 @@ import org.gradle.api.internal.project.ProjectInternal import org.gradle.internal.hash.FileHasher import org.jetbrains.kotlin.daemon.common.toHexString import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject -import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject -import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn.packageJsonHashFile -import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile +import java.io.File -class YarnAvoidance(val npmProject: NpmProject) { +class YarnUpToDateCheck(val npmProject: NpmProject) { val project: Project get() = npmProject.project + private val NpmProject.packageJsonHashFile: File + get() = dir.resolve("package.json.hash") + private val prevHash by lazy { val packageJsonHashFile = npmProject.packageJsonHashFile if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt new file mode 100644 index 00000000000..7ae41c02db3 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/yarn/YarnWorkspaces.kt @@ -0,0 +1,71 @@ +/* + * 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.yarn + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectPackage +import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson +import java.io.File + +object YarnWorkspaces : YarnBasics() { + override fun resolveProject(resolvedNpmProject: NpmProjectPackage) = Unit + + override fun resolveRootProject(rootProject: Project, subProjects: MutableList) { + check(rootProject == rootProject.rootProject) + resolveWorkspaces(rootProject, subProjects) + } + + private fun resolveWorkspaces( + rootProject: Project, + npmProjects: MutableList + ) { + val upToDateChecks = npmProjects.map { + YarnUpToDateCheck(it.npmProject) + } + + if (upToDateChecks.all { it.upToDate }) return + + val nodeJsWorldDir = rootProject.nodeJs.root.rootPackageDir + + saveRootProjectWorkspacesPackageJson(rootProject, npmProjects, nodeJsWorldDir) + + yarnExec(rootProject, nodeJsWorldDir, NpmApi.resolveOperationDescription("yarn")) + yarnLockReadTransitiveDependencies(nodeJsWorldDir, npmProjects.flatMap { it.npmDependencies }) + + upToDateChecks.forEach { + it.commit() + } + } + + private fun saveRootProjectWorkspacesPackageJson( + rootProject: Project, + npmProjects: MutableList, + nodeJsWorldDir: File + ) { + val rootPackageJson = PackageJson(rootProject.name, rootProject.version.toString()) + rootPackageJson.private = true + + val npmProjectWorkspaces = npmProjects.map { it.npmProject.dir.relativeTo(nodeJsWorldDir).path } + val importedProjectWorkspaces = npmProjects.flatMapTo(mutableSetOf()) { + it.gradleDependencies.externalModules.map { importedProject -> + importedProject.path.relativeTo(nodeJsWorldDir).path + } + } + + rootPackageJson.workspaces = npmProjectWorkspaces + importedProjectWorkspaces + + rootProject.nodeJs.packageJsonHandlers.forEach { + it(rootPackageJson) + } + + rootPackageJson.saveTo( + nodeJsWorldDir.resolve(NpmProject.PACKAGE_JSON) + ) + } +} \ No newline at end of file