Gradle, js, yarn: split into workspaces and simple implementations
#KT-31018 #KT-31017
This commit is contained in:
+11
-132
@@ -6,141 +6,20 @@
|
|||||||
package org.jetbrains.kotlin.gradle.targets.js.yarn
|
package org.jetbrains.kotlin.gradle.targets.js.yarn
|
||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.jetbrains.kotlin.gradle.internal.execWithProgress
|
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
|
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectPackage
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs
|
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.*
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
object Yarn : NpmApi {
|
object Yarn : NpmApi {
|
||||||
override fun setup(project: Project) {
|
private fun getDelegate(project: Project): NpmApi =
|
||||||
YarnPlugin.apply(project).executeSetup()
|
if (project.yarn.useWorkspaces) YarnWorkspaces
|
||||||
}
|
else YarnSimple
|
||||||
|
|
||||||
val NpmProject.packageJsonHashFile: File
|
override fun setup(project: Project) =
|
||||||
get() = dir.resolve("package.json.hash")
|
getDelegate(project.rootProject).setup(project)
|
||||||
|
|
||||||
fun yarnExec(
|
override fun resolveProject(resolvedNpmProject: NpmProjectPackage) =
|
||||||
project: Project,
|
getDelegate(resolvedNpmProject.project).resolveProject(resolvedNpmProject)
|
||||||
dir: File,
|
|
||||||
description: String,
|
|
||||||
vararg args: String
|
|
||||||
) {
|
|
||||||
val nodeJsEnv = NodeJsPlugin.apply(project).root.environment
|
|
||||||
val yarnEnv = YarnPlugin.apply(project).environment
|
|
||||||
|
|
||||||
project.execWithProgress(description) { exec ->
|
override fun resolveRootProject(rootProject: Project, subProjects: MutableList<NpmProjectPackage>) =
|
||||||
exec.executable = nodeJsEnv.nodeExecutable
|
getDelegate(rootProject.project).resolveRootProject(rootProject, subProjects)
|
||||||
exec.args = listOf(yarnEnv.home.resolve("bin/yarn.js").absolutePath) + args
|
|
||||||
exec.workingDir = dir
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun yarnLockReadTransitiveDependencies(
|
|
||||||
nodeWorkDir: File,
|
|
||||||
srcDependenciesList: Collection<NpmDependency>
|
|
||||||
) {
|
|
||||||
val yarnLock = nodeWorkDir.resolve("yarn.lock")
|
|
||||||
if (yarnLock.isFile) {
|
|
||||||
val byKey = YarnLock.parse(yarnLock).entries.associateBy { it.key }
|
|
||||||
val visited = mutableSetOf<NpmDependency>()
|
|
||||||
|
|
||||||
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<NpmProjectPackage>) {
|
|
||||||
check(rootProject == rootProject.rootProject)
|
|
||||||
|
|
||||||
if (rootProject.yarn.useWorkspaces) {
|
|
||||||
resolveWorkspaces(rootProject, subProjects)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resolveWorkspaces(
|
|
||||||
rootProject: Project,
|
|
||||||
npmProjects: MutableList<NpmProjectPackage>
|
|
||||||
) {
|
|
||||||
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<NpmProjectPackage>,
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+78
@@ -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<NpmDependency>
|
||||||
|
) {
|
||||||
|
val yarnLock = nodeWorkDir.resolve("yarn.lock")
|
||||||
|
if (yarnLock.isFile) {
|
||||||
|
val byKey = YarnLock.parse(yarnLock).entries.associateBy { it.key }
|
||||||
|
val visited = mutableSetOf<NpmDependency>()
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
@@ -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<NpmProjectPackage>) = Unit
|
||||||
|
}
|
||||||
+5
-5
@@ -10,16 +10,16 @@ import org.gradle.api.internal.project.ProjectInternal
|
|||||||
import org.gradle.internal.hash.FileHasher
|
import org.gradle.internal.hash.FileHasher
|
||||||
import org.jetbrains.kotlin.daemon.common.toHexString
|
import org.jetbrains.kotlin.daemon.common.toHexString
|
||||||
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
|
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.npm.npmProject
|
import java.io.File
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn.packageJsonHashFile
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
|
||||||
|
|
||||||
class YarnAvoidance(val npmProject: NpmProject) {
|
class YarnUpToDateCheck(val npmProject: NpmProject) {
|
||||||
val project: Project
|
val project: Project
|
||||||
get() = npmProject.project
|
get() = npmProject.project
|
||||||
|
|
||||||
|
private val NpmProject.packageJsonHashFile: File
|
||||||
|
get() = dir.resolve("package.json.hash")
|
||||||
|
|
||||||
private val prevHash by lazy {
|
private val prevHash by lazy {
|
||||||
val packageJsonHashFile = npmProject.packageJsonHashFile
|
val packageJsonHashFile = npmProject.packageJsonHashFile
|
||||||
if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null
|
if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null
|
||||||
+71
@@ -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<NpmProjectPackage>) {
|
||||||
|
check(rootProject == rootProject.rootProject)
|
||||||
|
resolveWorkspaces(rootProject, subProjects)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveWorkspaces(
|
||||||
|
rootProject: Project,
|
||||||
|
npmProjects: MutableList<NpmProjectPackage>
|
||||||
|
) {
|
||||||
|
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<NpmProjectPackage>,
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user