From 39ce257e6455620b1d4594c9be57d8da2c06eaf6 Mon Sep 17 00:00:00 2001 From: Sergey Rostov Date: Thu, 11 Apr 2019 15:46:52 +0300 Subject: [PATCH] Gradle: webpack #KT-31013 Fixed #KT-31014 Fixed --- .../targets/js/KotlinJsTargetConfigurator.kt | 8 +- .../gradle/targets/js/npm/NpmProjectLayout.kt | 68 ++++++++++++- .../gradle/targets/js/npm/NpmResolver.kt | 97 +++++++++++-------- .../targets/js/webpack/KotlinWebpack.kt | 4 +- 4 files changed, 128 insertions(+), 49 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt index 32ac54c2894..7be0f789be3 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/KotlinJsTargetConfigurator.kt @@ -8,9 +8,11 @@ package org.jetbrains.kotlin.gradle.targets.js import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider +import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask +import org.jetbrains.kotlin.gradle.tasks.registerTask class KotlinJsTargetConfigurator(kotlinPluginVersion: String) : KotlinTargetConfigurator(true, true, kotlinPluginVersion) { @@ -25,7 +27,7 @@ class KotlinJsTargetConfigurator(kotlinPluginVersion: String) : platformTarget.compilations.all { it.compileKotlinTask.kotlinOptions { - moduleKind = "commonjs" + moduleKind = "umd" sourceMap = true sourceMapEmbedSources = null } @@ -34,7 +36,7 @@ class KotlinJsTargetConfigurator(kotlinPluginVersion: String) : override fun configureTest(target: KotlinOnlyTarget) { val project = target.project - val npmProject = NpmProject[project] + val npmProject = NpmProjectLayout[project] target.compilations.all { compilation -> if (isTestCompilation(compilation)) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProjectLayout.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProjectLayout.kt index 1a8a605e87e..59c69d6d370 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProjectLayout.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmProjectLayout.kt @@ -5,11 +5,21 @@ 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.process.ExecSpec import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin +import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs +import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile import java.io.File -class NpmProjectLayout(val nodeWorkDir: File) { +class NpmProjectLayout( + val project: Project, + val nodeWorkDir: File, + val managed: Boolean +) { val nodeModulesDir get() = nodeWorkDir.resolve(NODE_MODULES) @@ -18,18 +28,66 @@ class NpmProjectLayout(val nodeWorkDir: File) { return nodeWorkDir.resolve(PACKAGE_JSON) } + fun useTool(exec: ExecSpec, tool: String, vararg args: String) { + NpmResolver.resolve(project) + + exec.workingDir = nodeWorkDir + exec.executable = project.nodeJs.root.environment.nodeExecutable + exec.args = listOf(findModule(tool)) + args + } + + val hoistGradleNodeModules: Boolean + get() = project.nodeJs.root.packageManager.getHoistGradleNodeModules(project) + + val gradleNodeModulesDir: File + get() = + if (hoistGradleNodeModules) project.rootProject.npmProject.nodeModulesDir + else nodeModulesDir + + fun moduleOutput(compilationTask: Kotlin2JsCompile): File { + return gradleNodeModulesDir.resolve(compilationTask.outputFile.name) + } + + @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? + } else null) ?: "index.js" + + val mainFile = file.resolve(main) + if (mainFile.isFile) return mainFile.canonicalPath + } + + val parent = project.parent + return if (!managed || parent == null) error("Cannot find node module $name in $src") + else NpmProjectLayout[parent].findModule(name, src) + } + companion object { const val PACKAGE_JSON = "package.json" const val NODE_MODULES = "node_modules" operator fun get(project: Project): NpmProjectLayout { - val manageNodeModules = NodeJsPlugin[project].manageNodeModules + val nodeJsRootExtension = NodeJsPlugin.apply(project) + + val manageNodeModules = nodeJsRootExtension.root.manageNodeModules val nodeWorkDir = - if (manageNodeModules) project.rootDir + if (manageNodeModules) project.projectDir else project.buildDir - return NpmProjectLayout(nodeWorkDir) + return NpmProjectLayout(project, nodeWorkDir, manageNodeModules) } } -} \ No newline at end of file +} + +val Project.npmProject + get() = NpmProjectLayout[this] \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolver.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolver.kt index 304ff85991f..daa658137c8 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolver.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/npm/NpmResolver.kt @@ -10,12 +10,12 @@ import com.google.gson.GsonBuilder import org.gradle.api.Project import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension -import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.gradle.dsl.kotlinExtensionOrNull import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinTarget -import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExtension 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.nodeJs import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver.ResolutionCallResult.* /** @@ -40,7 +40,11 @@ internal class NpmResolver private constructor(val rootProject: Project) { val resolved = process?.resolved return if (resolved != null) AlreadyResolved(resolved) - else ResolvedNow(NpmResolver(rootProject).resolve(rootProject, null)!!) + else { + val resolver = NpmResolver(rootProject) + check(resolver.resolve(rootProject, null)) + ResolvedNow(ResolvedProject(resolver.npmPackages)) + } } } @@ -50,9 +54,10 @@ internal class NpmResolver private constructor(val rootProject: Project) { class ResolvedNow(val resolution: ResolvedProject) : ResolutionCallResult() } - private val nodeJs = NodeJsPlugin[rootProject] + private val nodeJs = NodeJsPlugin.apply(rootProject).root + private val npmProject = rootProject.npmProject private val packageManager = nodeJs.packageManager - private val hoistGradleNodeModules = packageManager.hoistGradleNodeModules + private val hoistGradleNodeModules = npmProject.hoistGradleNodeModules private val npmPackages = mutableListOf() private val gson = GsonBuilder() .setPrettyPrinting() @@ -66,7 +71,11 @@ internal class NpmResolver private constructor(val rootProject: Project) { } } - class ResolvedProject(val dependencies: Set) + class ResolvedProject(val npmPackages: Collection) { + val dependencies by lazy { + npmPackages.flatMapTo(mutableSetOf()) { it.npmDependencies } + } + } class NpmPackage( val project: Project, @@ -91,14 +100,14 @@ internal class NpmResolver private constructor(val rootProject: Project) { private fun resolve( project: Project, parentGradleNodeModules: GradleNodeModulesSync? - ): ResolvedProject? { + ): Boolean { val existedProcess = ProjectData[project] - val process = if (existedProcess != null) { + if (existedProcess != null) { if (existedProcess.resolved != null) error("yarn dependencies for $project already resolved") else { // called inside resolution of classpath (from visitTarget) // we should return as we are already in progress - return null + return false } } else ProjectData().also { ProjectData[project] = it @@ -133,10 +142,7 @@ internal class NpmResolver private constructor(val rootProject: Project) { gradleNodeModules.sync() } - val npmDependencies = npmPackage?.npmDependencies ?: setOf() - return ResolvedProject(npmDependencies).also { - process.resolved = it - } + return true } private fun newGradleNodeModulesSync(project: Project): GradleNodeModulesSync { @@ -150,40 +156,53 @@ internal class NpmResolver private constructor(val rootProject: Project) { gradleComponentsSync: GradleNodeModulesSync ): NpmPackage? { val packageJson = PackageJson(project.name, project.version.toString()) - val transitiveDependencies = mutableListOf() - - val kotlin = project.kotlinExtension val npmDependencies = mutableSetOf() - when (kotlin) { - is KotlinSingleTargetExtension -> visitTarget( - kotlin.target, - project, - npmDependencies, - gradleComponentsSync, - transitiveDependencies - ) - is KotlinMultiplatformExtension -> kotlin.targets.forEach { - visitTarget(it, project, npmDependencies, gradleComponentsSync, transitiveDependencies) - } - } - npmDependencies.forEach { - packageJson.dependencies[it.key] = chooseVersion(packageJson.dependencies[it.key], it.version) - } + visitNpmDependencies(project, npmDependencies, gradleComponentsSync, packageJson) - transitiveDependencies.forEach { - packageJson.dependencies[it.key] = chooseVersion(packageJson.dependencies[it.key], it.version) - } - - NodeJsExtension[project].packageJsonHandlers.forEach { + project.nodeJs.packageJsonHandlers.forEach { it(packageJson) } - if (project == rootProject) { + val packageJsonRequired = if (project == rootProject) { packageManager.hookRootPackage(rootProject, packageJson, npmPackages) - } + } else false - return if (!packageJson.empty) NpmPackage(project, packageJson, npmDependencies) else null + return if (packageJsonRequired || !packageJson.empty) NpmPackage(project, packageJson, npmDependencies) else null + } + + private fun visitNpmDependencies( + project: Project, + npmDependencies: MutableSet, + gradleComponentsSync: GradleNodeModulesSync, + packageJson: PackageJson + ) { + val kotlin = project.kotlinExtensionOrNull + + if (kotlin != null) { + val transitiveDependencies = mutableListOf() + + when (kotlin) { + is KotlinSingleTargetExtension -> visitTarget( + kotlin.target, + project, + npmDependencies, + gradleComponentsSync, + transitiveDependencies + ) + is KotlinMultiplatformExtension -> kotlin.targets.forEach { + visitTarget(it, project, npmDependencies, gradleComponentsSync, transitiveDependencies) + } + } + + npmDependencies.forEach { + packageJson.dependencies[it.key] = chooseVersion(packageJson.dependencies[it.key], it.version) + } + + transitiveDependencies.forEach { + packageJson.dependencies[it.key] = chooseVersion(packageJson.dependencies[it.key], it.version) + } + } } private fun chooseVersion(oldVersion: String?, newVersion: String): String = diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt index 1eaeb9fa0db..159753327ee 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt @@ -11,7 +11,7 @@ import org.gradle.api.internal.file.FileResolver import org.gradle.api.tasks.* import org.gradle.process.internal.ExecHandleFactory import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles -import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject +import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile @@ -64,7 +64,7 @@ open class KotlinWebpack : DefaultTask() { NpmResolver.resolve(project) - val npmProjectLayout = NpmProject[project] + val npmProjectLayout = NpmProjectLayout[project] configFile.writeText(buildConfig())