Gradle, JS: new nodejs project layout
#KT-30530
This commit is contained in:
+17
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. 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.internal
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun File.ensureParentDirsCreated() {
|
||||||
|
val parentFile = parentFile
|
||||||
|
if (!parentFile.exists()) {
|
||||||
|
check(parentFile.mkdirs()) {
|
||||||
|
"Cannot create parent directories for $this"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -11,10 +11,10 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
|
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
|
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
|
||||||
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
|
|
||||||
|
|
||||||
open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
|
open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
|
||||||
KotlinTargetConfigurator<KotlinJsCompilation>(true, true, kotlinPluginVersion) {
|
KotlinTargetConfigurator<KotlinJsCompilation>(true, true, kotlinPluginVersion) {
|
||||||
@@ -28,6 +28,8 @@ open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
|
|||||||
super.configureCompilations(platformTarget)
|
super.configureCompilations(platformTarget)
|
||||||
|
|
||||||
platformTarget.compilations.all {
|
platformTarget.compilations.all {
|
||||||
|
platformTarget.project.npmProject.configureCompilation(it)
|
||||||
|
|
||||||
it.compileKotlinTask.kotlinOptions {
|
it.compileKotlinTask.kotlinOptions {
|
||||||
moduleKind = "commonjs"
|
moduleKind = "commonjs"
|
||||||
sourceMap = true
|
sourceMap = true
|
||||||
|
|||||||
+12
@@ -5,6 +5,7 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.api.plugins.BasePlugin
|
import org.gradle.api.plugins.BasePlugin
|
||||||
import org.gradle.api.tasks.Delete
|
import org.gradle.api.tasks.Delete
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.Companion.NODE_JS
|
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.Companion.NODE_JS
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
|
||||||
|
|
||||||
open class NodeJsPlugin : Plugin<Project> {
|
open class NodeJsPlugin : Plugin<Project> {
|
||||||
override fun apply(project: Project): Unit = project.run {
|
override fun apply(project: Project): Unit = project.run {
|
||||||
@@ -35,6 +36,17 @@ open class NodeJsPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
project.tasks.maybeCreate(BasePlugin.CLEAN_TASK_NAME).dependsOn(it)
|
project.tasks.maybeCreate(BasePlugin.CLEAN_TASK_NAME).dependsOn(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.tasks.create("cleanGradleNodeModules", Delete::class.java) {
|
||||||
|
it.description = "Deletes node_modules_gradle"
|
||||||
|
it.group = BasePlugin.BUILD_GROUP
|
||||||
|
|
||||||
|
it.doLast {
|
||||||
|
project.npmProject.gradleNodeModulesDir.deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
project.tasks.maybeCreate(BasePlugin.CLEAN_TASK_NAME).dependsOn(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
+2
-5
@@ -2,6 +2,7 @@ package org.jetbrains.kotlin.gradle.targets.js.nodejs
|
|||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
import org.jetbrains.kotlin.gradle.logging.kotlinInfo
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
|
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmApi
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn
|
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -19,10 +20,7 @@ open class NodeJsRootExtension(project: Project) : NodeJsExtension(project) {
|
|||||||
|
|
||||||
var nodeCommand = "node"
|
var nodeCommand = "node"
|
||||||
|
|
||||||
var npmCustomVersion: String? = null
|
var layout: NpmProjectLayout = NpmProjectLayout.ROOT_PROJECT_BUILD_DIR
|
||||||
var npmCommand = "npm"
|
|
||||||
|
|
||||||
var manageNodeModules: Boolean = false
|
|
||||||
var packageManager: NpmApi = Yarn
|
var packageManager: NpmApi = Yarn
|
||||||
|
|
||||||
val nodeJsSetupTask: NodeJsSetupTask
|
val nodeJsSetupTask: NodeJsSetupTask
|
||||||
@@ -51,7 +49,6 @@ open class NodeJsRootExtension(project: Project) : NodeJsExtension(project) {
|
|||||||
nodeDir = nodeDir,
|
nodeDir = nodeDir,
|
||||||
nodeBinDir = nodeBinDir,
|
nodeBinDir = nodeBinDir,
|
||||||
nodeExecutable = getExecutable("node", nodeCommand, "exe"),
|
nodeExecutable = getExecutable("node", nodeCommand, "exe"),
|
||||||
npmExecutable = getExecutable("npm", npmCommand, "cmd"),
|
|
||||||
platformName = platform,
|
platformName = platform,
|
||||||
architectureName = architecture,
|
architectureName = architecture,
|
||||||
ivyDependency = getIvyDependency()
|
ivyDependency = getIvyDependency()
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ internal class GradleNodeModuleBuilder(
|
|||||||
val project: Project,
|
val project: Project,
|
||||||
val dependency: ResolvedDependency,
|
val dependency: ResolvedDependency,
|
||||||
val artifacts: Set<ResolvedArtifact>,
|
val artifacts: Set<ResolvedArtifact>,
|
||||||
val cache: GradleNodeModules
|
val cache: GradleNodeModulesBuilder
|
||||||
) {
|
) {
|
||||||
var srcPackageJsonFile: File? = null
|
var srcPackageJsonFile: File? = null
|
||||||
val files = mutableListOf<File>()
|
val files = mutableListOf<File>()
|
||||||
|
|||||||
+9
-48
@@ -8,23 +8,19 @@ package org.jetbrains.kotlin.gradle.targets.js.npm
|
|||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.artifacts.ResolvedArtifact
|
import org.gradle.api.artifacts.ResolvedArtifact
|
||||||
import org.gradle.api.artifacts.ResolvedDependency
|
import org.gradle.api.artifacts.ResolvedDependency
|
||||||
import org.gradle.api.file.CopySpec
|
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
|
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
|
||||||
import org.jetbrains.kotlin.gradle.internal.ProcessedFilesCache
|
import org.jetbrains.kotlin.gradle.internal.ProcessedFilesCache
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.internal.RewriteSourceMapFilterReader
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
|
||||||
|
|
||||||
internal class GradleNodeModules(val project: Project) : AutoCloseable {
|
internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
|
||||||
companion object {
|
companion object {
|
||||||
const val STATE_FILE_NAME = ".visited"
|
const val STATE_FILE_NAME = ".visited"
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val dir = project.buildDir.resolve("node_modules_gradle")
|
internal val dir = project.buildDir.resolve("node_modules_gradle")
|
||||||
private val nodeModulesDir = project.npmProject.nodeModulesDir
|
|
||||||
private val cache = ProcessedFilesCache(project, dir, STATE_FILE_NAME, "5")
|
private val cache = ProcessedFilesCache(project, dir, STATE_FILE_NAME, "5")
|
||||||
private val visited = mutableSetOf<ResolvedDependency>()
|
private val visited = mutableSetOf<ResolvedDependency>()
|
||||||
private val doLast = mutableListOf<() -> Unit>()
|
|
||||||
|
|
||||||
val modules
|
val modules
|
||||||
get() = cache.targets.map {
|
get() = cache.targets.map {
|
||||||
@@ -33,29 +29,16 @@ internal class GradleNodeModules(val project: Project) : AutoCloseable {
|
|||||||
|
|
||||||
fun visitCompilation(compilation: KotlinCompilation<KotlinCommonOptions>) {
|
fun visitCompilation(compilation: KotlinCompilation<KotlinCommonOptions>) {
|
||||||
val project = compilation.target.project
|
val project = compilation.target.project
|
||||||
val kotlin2JsCompile = compilation.compileKotlinTask as Kotlin2JsCompile
|
|
||||||
|
|
||||||
// classpath
|
// classpath
|
||||||
compilation.relatedConfigurationNames.forEach {
|
compilation.relatedConfigurationNames.forEach { configurationName ->
|
||||||
val configuration = project.configurations.getByName(it)
|
val configuration = project.configurations.getByName(configurationName)
|
||||||
if (configuration.isCanBeResolved) {
|
if (configuration.isCanBeResolved) {
|
||||||
configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach {
|
configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach {
|
||||||
visitDependency(compilation, it)
|
visitDependency(compilation, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// output
|
|
||||||
doLast.add {
|
|
||||||
// we should do it only after node package manger work (as it can clean files)
|
|
||||||
if (kotlin2JsCompile.state.executed) {
|
|
||||||
visitCompile(project, kotlin2JsCompile)
|
|
||||||
} else {
|
|
||||||
kotlin2JsCompile.doLast {
|
|
||||||
visitCompile(project, kotlin2JsCompile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitDependency(
|
private fun visitDependency(
|
||||||
@@ -82,37 +65,15 @@ internal class GradleNodeModules(val project: Project) : AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
artifacts.forEach { artifact ->
|
artifacts.forEach { artifact ->
|
||||||
cache.getOrCompute(artifact.file) {
|
if (artifact.id.componentIdentifier !is ProjectComponentIdentifier) {
|
||||||
lazyDirName
|
cache.getOrCompute(artifact.file) {
|
||||||
}
|
lazyDirName
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun visitCompile(project: Project, kotlin2JsCompile: Kotlin2JsCompile) {
|
|
||||||
project.copy { copy ->
|
|
||||||
copy.from(kotlin2JsCompile.outputFile)
|
|
||||||
copy.from(kotlin2JsCompile.outputFile.path + ".map")
|
|
||||||
copy.into(nodeModulesDir)
|
|
||||||
copy.withSourceMapRewriter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CopySpec.withSourceMapRewriter() {
|
|
||||||
eachFile {
|
|
||||||
if (it.name.endsWith(".js.map")) {
|
|
||||||
it.filter(
|
|
||||||
mapOf(
|
|
||||||
"srcSourceRoot" to it.file.parentFile,
|
|
||||||
"targetSourceRoot" to nodeModulesDir
|
|
||||||
),
|
|
||||||
RewriteSourceMapFilterReader::class.java
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
doLast.forEach { it() }
|
|
||||||
cache.close()
|
cache.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -30,7 +30,7 @@ interface NpmApi {
|
|||||||
allWorkspaces: Collection<NpmResolver.NpmPackage>
|
allWorkspaces: Collection<NpmResolver.NpmPackage>
|
||||||
): Boolean = false
|
): Boolean = false
|
||||||
|
|
||||||
open fun getHoistGradleNodeModules(project: Project) = false
|
open fun shouldHoistGradleNodeModules(project: Project) = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun resolveOperationDescription(packageManagerTitle: String): String =
|
fun resolveOperationDescription(packageManagerTitle: String): String =
|
||||||
|
|||||||
+2
-11
@@ -40,7 +40,7 @@ data class NpmDependency(
|
|||||||
fun visit(item: NpmDependency) {
|
fun visit(item: NpmDependency) {
|
||||||
if (item in visited) return
|
if (item in visited) return
|
||||||
visited.add(item)
|
visited.add(item)
|
||||||
item.tryFindNodeModule()?.let {
|
item.project.npmProject.findModuleEntry(item.key)?.let {
|
||||||
all.add(it)
|
all.add(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ data class NpmDependency(
|
|||||||
return all
|
return all
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun resolveProject(): NpmResolver.ResolvedProject? {
|
private fun resolveProject(): NpmResolver.ResolvedProject? {
|
||||||
val result = NpmResolver.resolve(project)
|
val result = NpmResolver.resolve(project)
|
||||||
|
|
||||||
return when (result) {
|
return when (result) {
|
||||||
@@ -72,15 +72,6 @@ data class NpmDependency(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryFindNodeModule(): File? {
|
|
||||||
var p = project
|
|
||||||
do {
|
|
||||||
val result = NpmProjectLayout[p].nodeModulesDir.resolve(key)
|
|
||||||
if (result.exists()) return result
|
|
||||||
p = project.parent ?: return null
|
|
||||||
} while (true)
|
|
||||||
}
|
|
||||||
|
|
||||||
val key: String = if (org == null) name else "@$org/$name"
|
val key: String = if (org == null) name else "@$org/$name"
|
||||||
|
|
||||||
override fun toString() = "$key: $version"
|
override fun toString() = "$key: $version"
|
||||||
|
|||||||
+26
-22
@@ -10,48 +10,54 @@ import com.google.gson.JsonObject
|
|||||||
import groovy.transform.TailRecursive
|
import groovy.transform.TailRecursive
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.process.ExecSpec
|
import org.gradle.process.ExecSpec
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
|
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.nodejs.nodeJs
|
||||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class NpmProject(
|
open class NpmProject(
|
||||||
val project: Project,
|
val project: Project,
|
||||||
val nodeWorkDir: File,
|
val nodeWorkDir: File,
|
||||||
val managed: Boolean
|
val searchInParents: Boolean
|
||||||
) {
|
) {
|
||||||
val nodeModulesDir
|
val nodeModulesDir
|
||||||
get() = nodeWorkDir.resolve(NODE_MODULES)
|
get() = nodeWorkDir.resolve(NODE_MODULES)
|
||||||
|
|
||||||
val packageJsonFile: File
|
val packageJsonFile: File
|
||||||
get() {
|
get() = nodeWorkDir.resolve(PACKAGE_JSON)
|
||||||
return nodeWorkDir.resolve(PACKAGE_JSON)
|
|
||||||
}
|
open val compileOutputCopyDest: File?
|
||||||
|
get() = nodeModulesDir
|
||||||
|
|
||||||
fun useTool(exec: ExecSpec, tool: String, vararg args: String) {
|
fun useTool(exec: ExecSpec, tool: String, vararg args: String) {
|
||||||
NpmResolver.resolve(project)
|
NpmResolver.resolve(project)
|
||||||
|
|
||||||
exec.workingDir = nodeWorkDir
|
exec.workingDir = nodeWorkDir
|
||||||
exec.executable = project.nodeJs.root.environment.nodeExecutable
|
exec.executable = project.nodeJs.root.environment.nodeExecutable
|
||||||
exec.args = listOf(findModule(tool)) + args
|
exec.args = listOf(getModuleEntryPath(tool)) + args
|
||||||
}
|
}
|
||||||
|
|
||||||
val hoistGradleNodeModules: Boolean
|
val hoistGradleNodeModules: Boolean
|
||||||
get() = project.nodeJs.root.packageManager.getHoistGradleNodeModules(project)
|
get() = project.nodeJs.root.packageManager.shouldHoistGradleNodeModules(project)
|
||||||
|
|
||||||
val gradleNodeModulesDir: File
|
val gradleNodeModulesDir: File
|
||||||
get() =
|
get() =
|
||||||
if (hoistGradleNodeModules) project.rootProject.npmProject.nodeModulesDir
|
if (hoistGradleNodeModules) project.rootProject.npmProject.nodeModulesDir
|
||||||
else nodeModulesDir
|
else nodeModulesDir
|
||||||
|
|
||||||
fun moduleOutput(compilationTask: Kotlin2JsCompile): File {
|
open fun moduleOutput(compilationTask: Kotlin2JsCompile): File {
|
||||||
return gradleNodeModulesDir.resolve(compilationTask.outputFile.name)
|
return compileOutputCopyDest?.resolve(compilationTask.outputFile.name) ?: compilationTask.outputFile
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getModuleEntryPath(name: String): String {
|
||||||
|
return findModuleEntry(name)?.canonicalPath ?: error("Cannot find node module $name in $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
@TailRecursive
|
@TailRecursive
|
||||||
fun findModule(name: String, src: NpmProject = this): String {
|
fun findModuleEntry(name: String): File? {
|
||||||
val file = nodeModulesDir.resolve(name)
|
val file = nodeModulesDir.resolve(name)
|
||||||
if (file.isFile) return file.canonicalPath
|
if (file.isFile) return file
|
||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
val packageJsonFile = file.resolve("package.json")
|
val packageJsonFile = file.resolve("package.json")
|
||||||
val main: String = (if (packageJsonFile.isFile) {
|
val main: String = (if (packageJsonFile.isFile) {
|
||||||
@@ -59,18 +65,22 @@ class NpmProject(
|
|||||||
Gson().fromJson(it, JsonObject::class.java)
|
Gson().fromJson(it, JsonObject::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
packageJson["main"] as? String? ?: packageJson["module"] as? String? ?: packageJson["browser"] as? String?
|
packageJson["main"] as? String?
|
||||||
|
?: packageJson["module"] as? String?
|
||||||
|
?: packageJson["browser"] as? String?
|
||||||
} else null) ?: "index.js"
|
} else null) ?: "index.js"
|
||||||
|
|
||||||
val mainFile = file.resolve(main)
|
val mainFile = file.resolve(main)
|
||||||
if (mainFile.isFile) return mainFile.canonicalPath
|
if (mainFile.isFile) return mainFile
|
||||||
}
|
}
|
||||||
|
|
||||||
val parent = project.parent
|
val parent = project.parent
|
||||||
return if (!managed || parent == null) error("Cannot find node module $name in $src")
|
return if (!searchInParents || parent == null) null
|
||||||
else NpmProject[parent].findModule(name, src)
|
else NpmProject[parent].findModuleEntry(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun configureCompilation(compilation: KotlinJsCompilation) = Unit
|
||||||
|
|
||||||
override fun toString() = "NpmProject($nodeWorkDir)"
|
override fun toString() = "NpmProject($nodeWorkDir)"
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -80,13 +90,7 @@ class NpmProject(
|
|||||||
operator fun get(project: Project): NpmProject {
|
operator fun get(project: Project): NpmProject {
|
||||||
val nodeJsRootExtension = NodeJsPlugin.apply(project)
|
val nodeJsRootExtension = NodeJsPlugin.apply(project)
|
||||||
|
|
||||||
val manageNodeModules = nodeJsRootExtension.root.manageNodeModules
|
return nodeJsRootExtension.root.layout.newNpmProject(project)
|
||||||
|
|
||||||
val nodeWorkDir =
|
|
||||||
if (manageNodeModules) project.projectDir
|
|
||||||
else project.buildDir
|
|
||||||
|
|
||||||
return NpmProject(project, nodeWorkDir, manageNodeModules)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-61
@@ -5,76 +5,59 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.gradle.targets.js.npm
|
package org.jetbrains.kotlin.gradle.targets.js.npm
|
||||||
|
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.JsonObject
|
|
||||||
import groovy.transform.TailRecursive
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.process.ExecSpec
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
|
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class NpmProjectLayout(
|
enum class NpmProjectLayout {
|
||||||
val project: Project,
|
PROJECT_DIR {
|
||||||
val nodeWorkDir: File,
|
override val allowYarnWorkspaces: Boolean
|
||||||
val managed: Boolean
|
get() = true
|
||||||
) {
|
|
||||||
val nodeModulesDir
|
|
||||||
get() = nodeWorkDir.resolve(NODE_MODULES)
|
|
||||||
|
|
||||||
val packageJsonFile: File
|
override fun newNpmProject(project: Project) =
|
||||||
get() {
|
NpmProject(project, project.projectDir, searchInParents = true)
|
||||||
return nodeWorkDir.resolve(PACKAGE_JSON)
|
},
|
||||||
}
|
ROOT_PROJECT_BUILD_DIR {
|
||||||
|
override val allowYarnWorkspaces: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
fun useTool(exec: ExecSpec, tool: String, vararg args: String) {
|
override fun newNpmProject(project: Project): NpmProject {
|
||||||
NpmResolver.resolve(project)
|
val nodeJsWorldDir = project.rootProject.buildDir.resolve("nodejs-project")
|
||||||
|
|
||||||
exec.workingDir = nodeWorkDir
|
val workDir =
|
||||||
exec.executable = project.nodeJs.root.environment.nodeExecutable
|
if (project == project.rootProject) nodeJsWorldDir
|
||||||
exec.args = listOf(findModule(tool)) + args
|
else {
|
||||||
}
|
val projectPackage = project.path.removePrefix(":").replace(":", File.separator)
|
||||||
|
nodeJsWorldDir.resolve("packages").resolve(projectPackage)
|
||||||
@TailRecursive
|
|
||||||
fun findModule(name: String, src: NpmProjectLayout = this): String {
|
|
||||||
val file = nodeModulesDir.resolve(name)
|
|
||||||
if (file.isFile) return file.canonicalPath
|
|
||||||
if (file.isDirectory) {
|
|
||||||
val packageJsonFile = file.resolve("package.json")
|
|
||||||
val main: String = (if (packageJsonFile.isFile) {
|
|
||||||
val packageJson = packageJsonFile.reader().use {
|
|
||||||
Gson().fromJson(it, JsonObject::class.java)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
packageJson["main"] as? String? ?: packageJson["module"] as? String? ?: packageJson["browser"] as? String?
|
return object : NpmProject(
|
||||||
} else null) ?: "index.js"
|
project,
|
||||||
|
workDir,
|
||||||
|
searchInParents = true
|
||||||
|
) {
|
||||||
|
override val compileOutputCopyDest: File?
|
||||||
|
get() = nodeWorkDir
|
||||||
|
|
||||||
val mainFile = file.resolve(main)
|
override fun configureCompilation(compilation: KotlinJsCompilation) {
|
||||||
if (mainFile.isFile) return mainFile.canonicalPath
|
// val moduleName = compilation.compileKotlinTask.moduleName
|
||||||
|
// workDir.mkdirs()
|
||||||
|
// compilation.kotlinOptions.outputFile = workDir.resolve("$moduleName.js").absolutePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// override fun moduleOutput(compilationTask: Kotlin2JsCompile) =
|
||||||
|
// compilationTask.outputFile
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
BUILD_DIR {
|
||||||
|
override fun newNpmProject(project: Project) =
|
||||||
|
NpmProject(project, project.buildDir, searchInParents = false)
|
||||||
|
};
|
||||||
|
|
||||||
val parent = project.parent
|
open val allowYarnWorkspaces: Boolean
|
||||||
return if (!managed || parent == null) error("Cannot find node module $name in $src")
|
get() = false
|
||||||
else NpmProjectLayout[parent].findModule(name, src)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
abstract fun newNpmProject(project: Project): NpmProject
|
||||||
const val PACKAGE_JSON = "package.json"
|
}
|
||||||
const val NODE_MODULES = "node_modules"
|
|
||||||
|
|
||||||
operator fun get(project: Project): NpmProjectLayout {
|
|
||||||
val nodeJsRootExtension = NodeJsPlugin.apply(project)
|
|
||||||
|
|
||||||
val manageNodeModules = nodeJsRootExtension.root.manageNodeModules
|
|
||||||
|
|
||||||
val nodeWorkDir =
|
|
||||||
if (manageNodeModules) project.projectDir
|
|
||||||
else project.buildDir
|
|
||||||
|
|
||||||
return NpmProjectLayout(project, nodeWorkDir, manageNodeModules)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val Project.npmProject
|
|
||||||
get() = NpmProjectLayout[this]
|
|
||||||
+91
-47
@@ -8,25 +8,28 @@ package org.jetbrains.kotlin.gradle.targets.js.npm
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.file.CopySpec
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||||
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
|
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
|
||||||
import org.jetbrains.kotlin.gradle.dsl.kotlinExtensionOrNull
|
import org.jetbrains.kotlin.gradle.dsl.kotlinExtensionOrNull
|
||||||
|
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
|
||||||
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||||
|
import org.jetbrains.kotlin.gradle.targets.js.internal.RewriteSourceMapFilterReader
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
|
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
|
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs
|
import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver.ResolutionCallResult.*
|
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver.ResolutionCallResult.*
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [NpmResolver] runs selected [NodeJsRootExtension.packageManager] on root project
|
* Generates `package.json` file for projects with npm or js dependencies and
|
||||||
* with configured `package.json` files in all projects.
|
* runs selected [NodeJsRootExtension.packageManager] to download and install it.
|
||||||
*
|
*
|
||||||
* `package.json`
|
* All [NpmDependency] for configurations related to kotlin/js will be added to `package.json`.
|
||||||
*
|
* For external gradle modules, fake npm packages will be created and added to `package.json`
|
||||||
* - compile(npm(...)) dependencies
|
* as path to directory.
|
||||||
* - compile(npm(...)) dependencies from external modules
|
|
||||||
* - compile(non npm(...)) dependencies transitively
|
|
||||||
*/
|
*/
|
||||||
internal class NpmResolver private constructor(val rootProject: Project) {
|
internal class NpmResolver private constructor(val rootProject: Project) {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -55,8 +58,9 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val nodeJs = NodeJsPlugin.apply(rootProject).root
|
private val nodeJs = NodeJsPlugin.apply(rootProject).root
|
||||||
|
private val npmProject = rootProject.npmProject
|
||||||
private val packageManager = nodeJs.packageManager
|
private val packageManager = nodeJs.packageManager
|
||||||
private val hoistGradleNodeModules = packageManager.getHoistGradleNodeModules(rootProject)
|
private val hoistGradleNodeModules = npmProject.hoistGradleNodeModules
|
||||||
private val npmPackages = mutableListOf<NpmPackage>()
|
private val npmPackages = mutableListOf<NpmPackage>()
|
||||||
private val gson = GsonBuilder()
|
private val gson = GsonBuilder()
|
||||||
.setPrettyPrinting()
|
.setPrettyPrinting()
|
||||||
@@ -82,7 +86,9 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
val npmDependencies: Set<NpmDependency>
|
val npmDependencies: Set<NpmDependency>
|
||||||
) {
|
) {
|
||||||
fun savePackageJson(gson: Gson) {
|
fun savePackageJson(gson: Gson) {
|
||||||
NpmProjectLayout[project].packageJsonFile.writer().use {
|
val packageJsonFile = NpmProject[project].packageJsonFile
|
||||||
|
packageJsonFile.ensureParentDirsCreated()
|
||||||
|
packageJsonFile.writer().use {
|
||||||
gson.toJson(packageJson, it)
|
gson.toJson(packageJson, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,7 +104,7 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
|
|
||||||
private fun resolve(
|
private fun resolve(
|
||||||
project: Project,
|
project: Project,
|
||||||
parentGradleNodeModules: GradleNodeModulesSync?
|
parentGradleModules: GradleNodeModulesBuilder?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val existedProcess = ProjectData[project]
|
val existedProcess = ProjectData[project]
|
||||||
if (existedProcess != null) {
|
if (existedProcess != null) {
|
||||||
@@ -112,15 +118,15 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
ProjectData[project] = it
|
ProjectData[project] = it
|
||||||
}
|
}
|
||||||
|
|
||||||
val gradleNodeModules =
|
val gradleModules =
|
||||||
if (hoistGradleNodeModules && parentGradleNodeModules != null) parentGradleNodeModules
|
if (hoistGradleNodeModules && parentGradleModules != null) parentGradleModules
|
||||||
else newGradleNodeModulesSync(project)
|
else GradleNodeModulesBuilder(project)
|
||||||
|
|
||||||
project.subprojects.forEach {
|
project.subprojects.forEach {
|
||||||
resolve(it, gradleNodeModules)
|
resolve(it, gradleModules)
|
||||||
}
|
}
|
||||||
|
|
||||||
val npmPackage = extractNpmPackage(project, gradleNodeModules)
|
val npmPackage = extractNpmPackage(project, gradleModules)
|
||||||
if (npmPackage != null) {
|
if (npmPackage != null) {
|
||||||
npmPackage.savePackageJson(gson)
|
npmPackage.savePackageJson(gson)
|
||||||
|
|
||||||
@@ -137,70 +143,60 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gradleNodeModules != parentGradleNodeModules) {
|
if (gradleModules != parentGradleModules) {
|
||||||
gradleNodeModules.sync()
|
gradleModules.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun newGradleNodeModulesSync(project: Project): GradleNodeModulesSync {
|
|
||||||
return GradleNodeModulesSync(project).also {
|
|
||||||
it.loadOldState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extractNpmPackage(
|
private fun extractNpmPackage(
|
||||||
project: Project,
|
project: Project,
|
||||||
gradleComponentsSync: GradleNodeModulesSync
|
gradleComponents: GradleNodeModulesBuilder
|
||||||
): NpmPackage? {
|
): NpmPackage? {
|
||||||
val packageJson = PackageJson(project.name, project.version.toString())
|
val packageJson = PackageJson(project.name, project.version.toString())
|
||||||
val npmDependencies = mutableSetOf<NpmDependency>()
|
val npmDependencies = mutableSetOf<NpmDependency>()
|
||||||
|
|
||||||
visitNpmDependencies(project, npmDependencies, gradleComponentsSync, packageJson)
|
visitNpmDependencies(project, npmDependencies, gradleComponents, packageJson)
|
||||||
|
|
||||||
|
if (!hoistGradleNodeModules || project == project.rootProject) {
|
||||||
|
gradleComponents.modules.forEach {
|
||||||
|
val relativePath = it.path.relativeTo(NpmProject[project].nodeWorkDir)
|
||||||
|
packageJson.dependencies[it.name] = "file:$relativePath"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
project.nodeJs.packageJsonHandlers.forEach {
|
project.nodeJs.packageJsonHandlers.forEach {
|
||||||
it(packageJson)
|
it(packageJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
val packageJsonRequired = if (project == rootProject) {
|
val packageJsonRequired =
|
||||||
packageManager.hookRootPackage(rootProject, packageJson, npmPackages)
|
if (project == rootProject) packageManager.hookRootPackage(rootProject, packageJson, npmPackages)
|
||||||
} else false
|
else false
|
||||||
|
|
||||||
return if (packageJsonRequired || !packageJson.empty) NpmPackage(project, packageJson, npmDependencies) else null
|
return if (!packageJsonRequired && packageJson.empty) null
|
||||||
|
else NpmPackage(project, packageJson, npmDependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitNpmDependencies(
|
private fun visitNpmDependencies(
|
||||||
project: Project,
|
project: Project,
|
||||||
npmDependencies: MutableSet<NpmDependency>,
|
npmDependencies: MutableSet<NpmDependency>,
|
||||||
gradleComponentsSync: GradleNodeModulesSync,
|
gradleComponents: GradleNodeModulesBuilder,
|
||||||
packageJson: PackageJson
|
packageJson: PackageJson
|
||||||
) {
|
) {
|
||||||
val kotlin = project.kotlinExtensionOrNull
|
val kotlin = project.kotlinExtensionOrNull
|
||||||
|
|
||||||
if (kotlin != null) {
|
if (kotlin != null) {
|
||||||
val transitiveDependencies = mutableListOf<GradleNodeModulesSync.TransitiveNpmDependency>()
|
|
||||||
|
|
||||||
when (kotlin) {
|
when (kotlin) {
|
||||||
is KotlinSingleTargetExtension -> visitTarget(
|
is KotlinSingleTargetExtension -> visitTarget(kotlin.target, project, npmDependencies, gradleComponents, packageJson)
|
||||||
kotlin.target,
|
|
||||||
project,
|
|
||||||
npmDependencies,
|
|
||||||
gradleComponentsSync,
|
|
||||||
transitiveDependencies
|
|
||||||
)
|
|
||||||
is KotlinMultiplatformExtension -> kotlin.targets.forEach {
|
is KotlinMultiplatformExtension -> kotlin.targets.forEach {
|
||||||
visitTarget(it, project, npmDependencies, gradleComponentsSync, transitiveDependencies)
|
visitTarget(it, project, npmDependencies, gradleComponents, packageJson)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
npmDependencies.forEach {
|
npmDependencies.forEach {
|
||||||
packageJson.dependencies[it.key] = chooseVersion(packageJson.dependencies[it.key], it.version)
|
packageJson.dependencies[it.key] = chooseVersion(packageJson.dependencies[it.key], it.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
transitiveDependencies.forEach {
|
|
||||||
packageJson.dependencies[it.key] = chooseVersion(packageJson.dependencies[it.key], it.version)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +207,8 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
target: KotlinTarget,
|
target: KotlinTarget,
|
||||||
project: Project,
|
project: Project,
|
||||||
npmDependencies: MutableSet<NpmDependency>,
|
npmDependencies: MutableSet<NpmDependency>,
|
||||||
gradleComponentsSync: GradleNodeModulesSync,
|
gradleComponents: GradleNodeModulesBuilder,
|
||||||
transitiveDependencies: MutableList<GradleNodeModulesSync.TransitiveNpmDependency>
|
packageJson: PackageJson
|
||||||
) {
|
) {
|
||||||
if (target.platformType == KotlinPlatformType.js) {
|
if (target.platformType == KotlinPlatformType.js) {
|
||||||
target.compilations.toList().forEach { compilation ->
|
target.compilations.toList().forEach { compilation ->
|
||||||
@@ -224,7 +220,55 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleComponentsSync.visitCompilation(compilation, project, transitiveDependencies)
|
gradleComponents.visitCompilation(compilation)
|
||||||
|
|
||||||
|
if (compilation is KotlinJsCompilation) {
|
||||||
|
visitKotlinJsCompilation(project, compilation)
|
||||||
|
|
||||||
|
if (compilation.name == "main") {
|
||||||
|
packageJson.main = compilation.compileKotlinTask.outputFile.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun visitKotlinJsCompilation(
|
||||||
|
project: Project,
|
||||||
|
compilation: KotlinJsCompilation
|
||||||
|
) {
|
||||||
|
val npmProject = project.npmProject
|
||||||
|
val kotlin2JsCompile = compilation.compileKotlinTask
|
||||||
|
if (npmProject.compileOutputCopyDest != null) {
|
||||||
|
if (kotlin2JsCompile.outputFile.exists()) {
|
||||||
|
visitCompile(project.npmProject, kotlin2JsCompile)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin2JsCompile.doLast {
|
||||||
|
visitCompile(project.npmProject, kotlin2JsCompile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun visitCompile(npmProject: NpmProject, kotlin2JsCompile: Kotlin2JsCompile) {
|
||||||
|
npmProject.project.copy { copy ->
|
||||||
|
copy.from(kotlin2JsCompile.outputFile)
|
||||||
|
copy.from(kotlin2JsCompile.outputFile.path + ".map")
|
||||||
|
copy.into(npmProject.compileOutputCopyDest!!)
|
||||||
|
copy.withSourceMapRewriter(npmProject)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CopySpec.withSourceMapRewriter(npmProject: NpmProject) {
|
||||||
|
eachFile {
|
||||||
|
if (it.name.endsWith(".js.map")) {
|
||||||
|
it.filter(
|
||||||
|
mapOf(
|
||||||
|
"srcSourceRoot" to it.file.parentFile,
|
||||||
|
"targetSourceRoot" to npmProject.compileOutputCopyDest!!
|
||||||
|
),
|
||||||
|
RewriteSourceMapFilterReader::class.java
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -10,7 +10,8 @@ class PackageJson(
|
|||||||
var version: String
|
var version: String
|
||||||
) {
|
) {
|
||||||
val empty: Boolean
|
val empty: Boolean
|
||||||
get() = private == null &&
|
get() = main == null &&
|
||||||
|
private == null &&
|
||||||
workspaces == null &&
|
workspaces == null &&
|
||||||
dependencies.isEmpty() &&
|
dependencies.isEmpty() &&
|
||||||
devDependencies.isEmpty()
|
devDependencies.isEmpty()
|
||||||
|
|||||||
+1
-1
@@ -54,7 +54,7 @@ class KotlinNodeJsTestRunner : KotlinJsTestFramework {
|
|||||||
|
|
||||||
val args = nodeJsArgs +
|
val args = nodeJsArgs +
|
||||||
testRuntimeNodeModules.map {
|
testRuntimeNodeModules.map {
|
||||||
npmProjectLayout.findModule(it)
|
npmProjectLayout.getModuleEntryPath(it)
|
||||||
} +
|
} +
|
||||||
cliArgs.toList()
|
cliArgs.toList()
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -37,14 +37,11 @@ open class KotlinWebpack : DefaultTask() {
|
|||||||
open lateinit var entry: File
|
open lateinit var entry: File
|
||||||
|
|
||||||
open val configFile: File
|
open val configFile: File
|
||||||
@OutputFile get() = project.buildDir.resolve("webpack.config.js")
|
@OutputFile get() = project.npmProject.nodeWorkDir.resolve("webpack.config.js")
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
var saveEvaluatedConfigFile: Boolean = true
|
var saveEvaluatedConfigFile: Boolean = true
|
||||||
|
|
||||||
open val evaluatedConfigFile: File
|
|
||||||
@OutputFile get() = project.buildDir.resolve("webpack.config.evaluated.js")
|
|
||||||
|
|
||||||
open val outputPath: File
|
open val outputPath: File
|
||||||
@OutputDirectory get() = project.buildDir.resolve("lib")
|
@OutputDirectory get() = project.buildDir.resolve("lib")
|
||||||
|
|
||||||
@@ -57,10 +54,14 @@ open class KotlinWebpack : DefaultTask() {
|
|||||||
open val reportDir: File
|
open val reportDir: File
|
||||||
@OutputDirectory get() = project.reportsDir.resolve("webpack").resolve(entry.nameWithoutExtension)
|
@OutputDirectory get() = project.reportsDir.resolve("webpack").resolve(entry.nameWithoutExtension)
|
||||||
|
|
||||||
|
open val evaluatedConfigFile: File
|
||||||
|
@OutputFile get() = reportDir.resolve("webpack.config.evaluated.js")
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
var bin: String = "webpack"
|
var bin: String = "webpack"
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
|
@Optional
|
||||||
var devServer: KotlinWebpackConfig.DevServer? = null
|
var devServer: KotlinWebpackConfig.DevServer? = null
|
||||||
|
|
||||||
internal fun createRunner() = KotlinWebpackRunner(
|
internal fun createRunner() = KotlinWebpackRunner(
|
||||||
@@ -125,6 +126,8 @@ open class KotlinWebpack : DefaultTask() {
|
|||||||
it.dependsOn(compileKotlinTask)
|
it.dependsOn(compileKotlinTask)
|
||||||
|
|
||||||
it.entry = npmProject.moduleOutput(compileKotlinTask)
|
it.entry = npmProject.moduleOutput(compileKotlinTask)
|
||||||
|
|
||||||
|
it.outputs.upToDateWhen { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
compilation.dependencies {
|
compilation.dependencies {
|
||||||
|
|||||||
+9
-3
@@ -13,6 +13,7 @@
|
|||||||
package org.jetbrains.kotlin.gradle.targets.js.webpack
|
package org.jetbrains.kotlin.gradle.targets.js.webpack
|
||||||
|
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.process.ExecResult
|
||||||
import org.gradle.process.internal.ExecHandle
|
import org.gradle.process.internal.ExecHandle
|
||||||
import org.gradle.process.internal.ExecHandleFactory
|
import org.gradle.process.internal.ExecHandleFactory
|
||||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
|
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
|
||||||
@@ -26,9 +27,13 @@ internal class KotlinWebpackRunner(
|
|||||||
val bin: String,
|
val bin: String,
|
||||||
val config: KotlinWebpackConfig
|
val config: KotlinWebpackConfig
|
||||||
) {
|
) {
|
||||||
fun execute() {
|
fun execute(): ExecResult {
|
||||||
val exec = start()
|
val exec = start()
|
||||||
exec.waitForFinish()
|
val result = exec.waitForFinish()
|
||||||
|
check(result.exitValue == 0) {
|
||||||
|
"Webpack exited with non zero exit code (${result.exitValue})"
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun start(): ExecHandle {
|
fun start(): ExecHandle {
|
||||||
@@ -43,12 +48,13 @@ internal class KotlinWebpackRunner(
|
|||||||
config.save(configFile)
|
config.save(configFile)
|
||||||
|
|
||||||
val execFactory = execHandleFactory.newExec()
|
val execFactory = execHandleFactory.newExec()
|
||||||
|
|
||||||
val args = mutableListOf<String>("--config", configFile.absolutePath)
|
val args = mutableListOf<String>("--config", configFile.absolutePath)
|
||||||
if (config.showProgress) {
|
if (config.showProgress) {
|
||||||
args.add("--progress")
|
args.add("--progress")
|
||||||
}
|
}
|
||||||
|
|
||||||
npmProjectLayout.useTool(execFactory, ".bin/$bin")
|
npmProjectLayout.useTool(execFactory, ".bin/$bin", *args.toTypedArray())
|
||||||
val exec = execFactory.build()
|
val exec = execFactory.build()
|
||||||
exec.start()
|
exec.start()
|
||||||
return exec
|
return exec
|
||||||
|
|||||||
+2
-2
@@ -103,7 +103,7 @@ object Yarn : NpmApi {
|
|||||||
rootPackageJson.private = true
|
rootPackageJson.private = true
|
||||||
rootPackageJson.workspaces = allWorkspaces
|
rootPackageJson.workspaces = allWorkspaces
|
||||||
.filter { it.project != rootProject }
|
.filter { it.project != rootProject }
|
||||||
.map { it.project.projectDir.relativeTo(rootProject.rootDir).path }
|
.map { it.project.npmProject.nodeWorkDir.relativeTo(rootProject.npmProject.nodeWorkDir).path }
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ object Yarn : NpmApi {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getHoistGradleNodeModules(project: Project): Boolean =
|
override fun shouldHoistGradleNodeModules(project: Project): Boolean =
|
||||||
project.rootProject.yarn.useWorkspaces
|
project.rootProject.yarn.useWorkspaces
|
||||||
|
|
||||||
@Suppress("EXPOSED_PARAMETER_TYPE")
|
@Suppress("EXPOSED_PARAMETER_TYPE")
|
||||||
|
|||||||
+11
-3
@@ -9,6 +9,7 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.api.internal.project.ProjectInternal
|
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.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.targets.js.yarn.Yarn.packageJsonHashFile
|
||||||
|
|
||||||
@@ -18,17 +19,24 @@ class YarnAvoidance(val project: Project) {
|
|||||||
if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null
|
if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val npmProject = project.npmProject
|
||||||
|
private val packageJsonFile = npmProject.packageJsonFile
|
||||||
|
|
||||||
private val hash by lazy {
|
private val hash by lazy {
|
||||||
val hasher = (project as ProjectInternal).services.get(FileHasher::class.java)
|
val hasher = (project as ProjectInternal).services.get(FileHasher::class.java)
|
||||||
hasher.hash(project.npmProject.packageJsonFile).toByteArray().toHexString()
|
hasher.hash(packageJsonFile).toByteArray().toHexString()
|
||||||
}
|
}
|
||||||
|
|
||||||
val upToDate: Boolean
|
val upToDate: Boolean
|
||||||
get() = prevHash == hash
|
get() = packageJsonFile.exists() &&
|
||||||
|
npmProject.nodeModulesDir.isDirectory &&
|
||||||
|
prevHash == hash
|
||||||
|
|
||||||
fun commit() {
|
fun commit() {
|
||||||
if (!upToDate) {
|
if (!upToDate) {
|
||||||
project.packageJsonHashFile.writeText(hash)
|
val packageJsonHashFile = project.packageJsonHashFile
|
||||||
|
packageJsonHashFile.ensureParentDirsCreated()
|
||||||
|
packageJsonHashFile.writeText(hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ open class YarnRootExtension(val project: Project) {
|
|||||||
var disableWorkspaces: Boolean = false
|
var disableWorkspaces: Boolean = false
|
||||||
|
|
||||||
val useWorkspaces: Boolean
|
val useWorkspaces: Boolean
|
||||||
get() = project.nodeJs.root.manageNodeModules && !disableWorkspaces
|
get() = !disableWorkspaces && project.nodeJs.root.layout.allowYarnWorkspaces
|
||||||
|
|
||||||
internal fun executeSetup() {
|
internal fun executeSetup() {
|
||||||
NodeJsPlugin.apply(project).root.executeSetup()
|
NodeJsPlugin.apply(project).root.executeSetup()
|
||||||
|
|||||||
Reference in New Issue
Block a user