From e19776bd5ad4f7089266e277785955ff5c728864 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Thu, 22 Dec 2022 17:19:38 +0000 Subject: [PATCH] [Gradle, JS] Make webpack task cacheable [Gradle, JS] Remove redundant dependsOn [Gradle, JS] Private metrics [Gradle, JS] Fix test with run webpack on windows [Gradle, JS] Fix detecting of input files instead of const folder kotlin [Gradle, JS] Change up-to-date annotations in webpack task [Gradle, JS] Add test on webpack considering changes in dependencies in up-to-date checks [Gradle, JS] Add test on buid cache of Webpack task [Gradle, JS] webpack config appliers are nested inputs [Gradle, JS] Webpack task is cacheable with relative pathes ^KT-55476 fixed --- .../kotlin/gradle/BuildCacheRelocationIT.kt | 9 +- .../kotlin/gradle/Kotlin2JsGradlePluginIT.kt | 24 ++++ .../app/build.gradle.kts | 13 ++ .../kotlin-js-dce/mainProject/build.gradle | 19 +++ .../build.gradle.kts | 26 ++-- .../targets/js/dsl/KotlinJsTargetDsl.kt | 48 +------ .../gradle/targets/js/ir/KotlinBrowserJsIr.kt | 50 ++++--- .../targets/js/ir/KotlinJsIrSubTarget.kt | 7 +- .../gradle/targets/js/ir/KotlinNodeJsIr.kt | 3 +- .../targets/js/subtargets/KotlinBrowserJs.kt | 45 +++--- .../js/subtargets/KotlinJsSubTarget.kt | 3 +- .../targets/js/subtargets/KotlinNodeJs.kt | 7 +- .../targets/js/testing/karma/KotlinKarma.kt | 2 +- .../targets/js/webpack/KotlinWebpack.kt | 136 +++++++----------- .../targets/js/webpack/KotlinWebpackConfig.kt | 112 +++------------ 15 files changed, 227 insertions(+), 277 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt index c91473e02a3..2a391877600 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt @@ -84,7 +84,12 @@ class BuildCacheRelocationIT : KGPBaseTest() { firstProject, secondProject, listOf("assemble"), - listOf(":libraryProject:compileKotlinJs", ":mainProject:compileKotlinJs", ":mainProject:compileProductionExecutableKotlinJs") + listOf( + ":libraryProject:compileKotlinJs", + ":mainProject:compileKotlinJs", + ":mainProject:compileProductionExecutableKotlinJs", + ":mainProject:browserProductionWebpack" + ) ) } @@ -300,6 +305,8 @@ class BuildCacheRelocationIT : KGPBaseTest() { assertTasksPackedToCache(*cacheableTasks.toTypedArray()) } + firstProject.build("clean") + secondProject.build(*tasksToExecute.toTypedArray()) { assertTasksFromCache(*cacheableTasks.toTypedArray()) } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 1ada541edc2..5379106bea9 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -399,6 +399,30 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { } } } + + @DisplayName("webpack must consider changes in dependencies in up-to-date") + @GradleTest + fun testWebpackConsiderChangesInDependencies(gradleVersion: GradleVersion) { + project("kotlin-js-browser-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + + projectPath.resolve("app/src/main/kotlin/App.kt").modify { + it.replace("require(\"css/main.css\")", "") + } + + build("runWebpackResult") { + assertOutputContains("Sheldon: 73") + } + + projectPath.resolve("base/src/main/kotlin/Base.kt").modify { + it.replace("73", "37") + } + + build("runWebpackResult") { + assertOutputContains("Sheldon: 37") + } + } + } } @JsGradlePluginTests diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/app/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/app/build.gradle.kts index ed3c9edf7b3..7fc6d00b853 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/app/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/app/build.gradle.kts @@ -52,3 +52,16 @@ kotlin { } } } + +rootProject.plugins.withType { + val kotlinNodeJs = rootProject.extensions.getByType() + + tasks.register("runWebpackResult") { + dependsOn(tasks.named("browserProductionWebpack")) + + executable(kotlinNodeJs.requireConfigured().nodeExecutable) + + workingDir = File("${buildDir}").resolve("distributions") + args("./${project.name}.js") + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dce/mainProject/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dce/mainProject/build.gradle index ab9386d659e..6faa604a32b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dce/mainProject/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dce/mainProject/build.gradle @@ -33,6 +33,25 @@ kotlin { // destinationDirectory = project.layout.buildDirectory.dir("kotlin-js-min") //} +rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin) { + def kotlinNodeJs = rootProject.extensions.getByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension) + + tasks.register("runWebpack", Exec) { + dependsOn(tasks.named('browserProductionWebpack')) + + executable(kotlinNodeJs.requireConfigured().nodeExecutable) + + workingDir = "${rootProject.buildDir}/js/packages/kotlin-js-dce-mainProject" + args = ["../../node_modules/webpack/bin/webpack.js"] + } + + tasks.named("assemble") { + dependsOn("runWebpack") + } +} + + + tasks.register('runRhino', JavaExec) { dependsOn(tasks.named('processDceKotlinJs')) classpath = configurations.runtimeClasspath diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts index 628a4a71c99..fb69510384c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts @@ -19,30 +19,40 @@ kotlin { org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec.create(compilation, "checkConfigDevelopmentWebpack") { inputFileProperty.set(provider { compilation.npmProject.require("webpack/bin/webpack.js") }.map { RegularFile { File(it) } }) dependsOn("browserDevelopmentWebpack") - val configFile = tasks.named("browserDevelopmentWebpack").map { it.configFile }.get() args("configtest") - args(configFile.absolutePath) + val configFile = tasks.named("browserDevelopmentWebpack").flatMap { it.configFile } + + doFirst { + args(configFile.get().absolutePath) + } } org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec.create(compilation, "checkConfigProductionWebpack") { inputFileProperty.set(provider { compilation.npmProject.require("webpack/bin/webpack.js") }.map { RegularFile { File(it) } }) dependsOn("browserProductionWebpack") - val configFile = tasks.named("browserProductionWebpack").map { it.configFile }.get() + val configFile = tasks.named("browserProductionWebpack").flatMap { it.configFile } + args("configtest") - args(configFile.absolutePath) + doFirst { + args(configFile.get().absolutePath) + } } org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec.create(compilation, "checkConfigDevelopmentRun") { inputFileProperty.set(provider { compilation.npmProject.require("webpack/bin/webpack.js") }.map { RegularFile { File(it) } }) dependsOn("browserDevelopmentRun") - val configFile = tasks.named("browserDevelopmentRun").map { it.configFile }.get() + val configFile = tasks.named("browserDevelopmentRun").flatMap { it.configFile } args("configtest") - args(configFile.absolutePath) + doFirst { + args(configFile.get().absolutePath) + } } org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec.create(compilation, "checkConfigProductionRun") { inputFileProperty.set(provider { compilation.npmProject.require("webpack/bin/webpack.js") }.map { RegularFile { File(it) } }) dependsOn("browserProductionRun") - val configFile = tasks.named("browserProductionRun").map { it.configFile }.get() + val configFile = tasks.named("browserProductionRun").flatMap { it.configFile } args("configtest") - args(configFile.absolutePath) + doFirst { + args(configFile.get().absolutePath) + } } binaries.executable() browser { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt index b4b4b1d3eff..16de75ad2c2 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/dsl/KotlinJsTargetDsl.kt @@ -76,58 +76,24 @@ interface KotlinJsTargetDsl : KotlinTarget { interface KotlinJsSubTargetDsl { @ExperimentalDistributionDsl - fun distribution(body: Distribution.() -> Unit) + fun distribution(body: Action) - @ExperimentalDistributionDsl - fun distribution(fn: Action) { - distribution { - fn.execute(this) - } - } - - fun testTask(body: KotlinJsTest.() -> Unit) - fun testTask(fn: Action) { - testTask { - fn.execute(this) - } - } + fun testTask(body: Action) val testRuns: NamedDomainObjectContainer } interface KotlinJsBrowserDsl : KotlinJsSubTargetDsl { - fun commonWebpackConfig(body: KotlinWebpackConfig.() -> Unit) - fun commonWebpackConfig(fn: Action) { - commonWebpackConfig { - fn.execute(this) - } - } + fun commonWebpackConfig(body: Action) - fun runTask(body: KotlinWebpack.() -> Unit) - fun runTask(fn: Action) { - runTask { - fn.execute(this) - } - } + fun runTask(body: Action) - fun webpackTask(body: KotlinWebpack.() -> Unit) - fun webpackTask(fn: Action) { - webpackTask { - fn.execute(this) - } - } + fun webpackTask(body: Action) @ExperimentalDceDsl - fun dceTask(body: KotlinJsDce.() -> Unit) - - @ExperimentalDceDsl - fun dceTask(fn: Action) { - dceTask { - fn.execute(this) - } - } + fun dceTask(body: Action) } interface KotlinJsNodeDsl : KotlinJsSubTargetDsl { - fun runTask(body: NodeJsExec.() -> Unit) + fun runTask(body: Action) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt index eefafc1974a..591b2f2a93f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinBrowserJsIr.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir +import org.gradle.api.Action import org.gradle.api.Task import org.gradle.api.provider.Provider import org.gradle.api.tasks.Copy @@ -38,8 +39,8 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) - private val webpackTaskConfigurations: MutableList Unit> = mutableListOf() - private val runTaskConfigurations: MutableList Unit> = mutableListOf() + private val webpackTaskConfigurations: MutableList> = mutableListOf() + private val runTaskConfigurations: MutableList> = mutableListOf() private val propertiesProvider = PropertiesProvider(project) private val webpackMajorVersion @@ -68,32 +69,32 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : } } - override fun commonWebpackConfig(body: KotlinWebpackConfig.() -> Unit) { + override fun commonWebpackConfig(body: Action) { webpackTaskConfigurations.add { - webpackConfigApplier(body) + it.webpackConfigApplier(body) } runTaskConfigurations.add { - webpackConfigApplier(body) + it.webpackConfigApplier(body) } testTask { - onTestFrameworkSet { + it.onTestFrameworkSet { if (it is KotlinKarma) { - it.webpackConfig.body() + body.execute(it.webpackConfig) } } } } - override fun runTask(body: KotlinWebpack.() -> Unit) { + override fun runTask(body: Action) { runTaskConfigurations.add(body) } - override fun webpackTask(body: KotlinWebpack.() -> Unit) { + override fun webpackTask(body: Action) { webpackTaskConfigurations.add(body) } @ExperimentalDceDsl - override fun dceTask(body: KotlinJsDce.() -> Unit) { + override fun dceTask(body: Action) { project.logger.warn("dceTask configuration is useless with IR compiler. Use @JsExport on declarations instead.") } @@ -120,11 +121,6 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : listOf(compilation) ) { task -> task.dependsOn(binary.linkSyncTask) - val entryFileProvider = binary.linkSyncTask.flatMap { syncTask -> - binary.linkTask.map { - syncTask.destinationDir.resolve(it.outputFileProperty.get().name) - } - } webpackMajorVersion.choose( { task.args.add(0, "serve") }, @@ -159,7 +155,8 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : task.commonConfigure( compilation = compilation, mode = mode, - entryFileProvider = entryFileProvider, + inputFilesDirectory = binary.linkSyncTask.map { it.destinationDir }, + entryModuleName = binary.linkTask.flatMap { it.compilerOptions.moduleName }, configurationActions = runTaskConfigurations, nodeJs = nodeJs ) @@ -206,11 +203,6 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : ), listOf(compilation) ) { task -> - val entryFileProvider = binary.linkSyncTask.zip(binary.linkTask) { sync, link -> - sync.destinationDir - .resolve(link.compilerOptions.moduleName.get() + ".js") - } - task.description = "build webpack ${mode.name.toLowerCaseAsciiOnly()} bundle" task._destinationDirectory = binary.distribution.directory @@ -222,10 +214,13 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : distributeResourcesTask ) + task.dependsOn(binary.linkSyncTask) + task.commonConfigure( compilation = compilation, mode = mode, - entryFileProvider = entryFileProvider, + inputFilesDirectory = binary.linkSyncTask.map { it.destinationDir }, + entryModuleName = binary.linkTask.flatMap { it.compilerOptions.moduleName }, configurationActions = webpackTaskConfigurations, nodeJs = nodeJs ) @@ -257,8 +252,9 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : private fun KotlinWebpack.commonConfigure( compilation: KotlinJsCompilation, mode: KotlinJsBinaryMode, - entryFileProvider: Provider, - configurationActions: List Unit>, + inputFilesDirectory: Provider, + entryModuleName: Provider, + configurationActions: List>, nodeJs: NodeJsRootExtension ) { dependsOn( @@ -269,10 +265,12 @@ abstract class KotlinBrowserJsIr @Inject constructor(target: KotlinJsIrTarget) : configureOptimization(mode) - entryProperty.fileProvider(entryFileProvider) + this.inputFilesDirectory.fileProvider(inputFilesDirectory) + + this.entryModuleName.set(entryModuleName) configurationActions.forEach { configure -> - configure() + configure.execute(this) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt index a8f61a23f64..d411fa452e5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinJsIrSubTarget.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir +import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Task import org.gradle.api.plugins.ExtensionAware @@ -43,10 +44,10 @@ abstract class KotlinJsIrSubTarget( protected val taskGroupName = "Kotlin $disambiguationClassifier" @ExperimentalDistributionDsl - override fun distribution(body: Distribution.() -> Unit) { + override fun distribution(body: Action) { target.binaries .all { - it.distribution.body() + body.execute(it.distribution) } } @@ -79,7 +80,7 @@ abstract class KotlinJsIrSubTarget( produceLibrary } - override fun testTask(body: KotlinJsTest.() -> Unit) { + override fun testTask(body: Action) { testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).executionTask.configure(body) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt index 3f4a5f955fb..51c63d1e86b 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/ir/KotlinNodeJsIr.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.targets.js.ir +import org.gradle.api.Action import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsNodeDsl import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsExec @@ -25,7 +26,7 @@ abstract class KotlinNodeJsIr @Inject constructor(target: KotlinJsIrTarget) : override val testTaskDescription: String get() = "Run all ${target.name} tests inside nodejs using the builtin test framework" - override fun runTask(body: NodeJsExec.() -> Unit) { + override fun runTask(body: Action) { project.tasks.withType().named(runTaskName).configure(body) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt index 8408e8d1a33..4fa9f1416ab 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinBrowserJs.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.targets.js.subtargets +import org.gradle.api.Action import org.gradle.api.Task import org.gradle.api.tasks.Copy import org.gradle.api.tasks.TaskProvider @@ -43,9 +44,9 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : KotlinJsSubTarget(target, "browser"), KotlinJsBrowserDsl { - private val webpackTaskConfigurations: MutableList Unit> = mutableListOf() - private val runTaskConfigurations: MutableList Unit> = mutableListOf() - private val dceConfigurations: MutableList Unit> = mutableListOf() + private val webpackTaskConfigurations: MutableList> = mutableListOf() + private val runTaskConfigurations: MutableList> = mutableListOf() + private val dceConfigurations: MutableList> = mutableListOf() private val distribution: Distribution = DefaultDistribution(project) private val propertiesProvider = PropertiesProvider(project) private val webpackMajorVersion @@ -60,37 +61,37 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : } } - override fun commonWebpackConfig(body: KotlinWebpackConfig.() -> Unit) { + override fun commonWebpackConfig(body: Action) { webpackTaskConfigurations.add { - webpackConfigApplier(body) + it.webpackConfigApplier(body) } runTaskConfigurations.add { - webpackConfigApplier(body) + it.webpackConfigApplier(body) } testTask { - onTestFrameworkSet { + it.onTestFrameworkSet { if (it is KotlinKarma) { - it.webpackConfig.body() + body.execute(it.webpackConfig) } } } } - override fun runTask(body: KotlinWebpack.() -> Unit) { + override fun runTask(body: Action) { runTaskConfigurations.add(body) } @ExperimentalDistributionDsl - override fun distribution(body: Distribution.() -> Unit) { - distribution.body() + override fun distribution(body: Action) { + body.execute(distribution) } - override fun webpackTask(body: KotlinWebpack.() -> Unit) { + override fun webpackTask(body: Action) { webpackTaskConfigurations.add(body) } @ExperimentalDceDsl - override fun dceTask(body: KotlinJsDce.() -> Unit) { + override fun dceTask(body: Action) { dceConfigurations.add(body) } @@ -275,7 +276,7 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : dceTaskProvider: TaskProvider, devDceTaskProvider: TaskProvider, mode: KotlinJsBinaryMode, - configurationActions: List Unit>, + configurationActions: List>, nodeJs: NodeJsRootExtension ) { dependsOn( @@ -293,20 +294,24 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : dependsOn(actualDceTaskProvider) - entryProperty.set( + inputFilesDirectory.set( actualDceTaskProvider.flatMap { dceTask -> compilation.compileTaskProvider.flatMap { compileTask -> - dceTask.destinationDirectory.file( - compileTask.outputFileProperty.map { it.name } - ) + dceTask.destinationDirectory } } ) + entryModuleName.set( + compilation.compileTaskProvider.flatMap { compileTask -> + compileTask.outputFileProperty.map { it.nameWithoutExtension } + } + ) + resolveFromModulesFirst = true configurationActions.forEach { configure -> - configure() + configure.execute(this) } } @@ -331,7 +336,7 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) : it.dceOptions.devMode = true } else { dceConfigurations.forEach { configure -> - it.configure() + configure.execute(it) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt index c54563498ac..a51afe0c763 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinJsSubTarget.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.targets.js.subtargets +import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Task import org.gradle.api.plugins.ExtensionAware @@ -64,7 +65,7 @@ abstract class KotlinJsSubTarget( } } - override fun testTask(body: KotlinJsTest.() -> Unit) { + override fun testTask(body: Action) { testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).executionTask.configure(body) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt index af8740244af..b59d8621d2a 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/subtargets/KotlinNodeJs.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.gradle.targets.js.subtargets +import org.gradle.api.Action import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTarget import org.jetbrains.kotlin.gradle.targets.js.dsl.Distribution @@ -24,16 +25,16 @@ abstract class KotlinNodeJs @Inject constructor(target: KotlinJsTarget) : private val runTaskName = disambiguateCamelCased("run") - override fun runTask(body: NodeJsExec.() -> Unit) { + override fun runTask(body: Action) { project.tasks.withType().named(runTaskName).configure(body) } @ExperimentalDistributionDsl - override fun distribution(body: Distribution.() -> Unit) { + override fun distribution(body: Action) { TODO("Not yet implemented") } - override fun testTask(body: KotlinJsTest.() -> Unit) { + override fun testTask(body: Action) { super.testTask(body) } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt index 9b00cd4ff84..abb20c1e907 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/karma/KotlinKarma.kt @@ -84,7 +84,7 @@ class KotlinKarma( devtool = null, export = false, progressReporter = true, - progressReporterPathFilter = nodeRootPackageDir.absolutePath, + progressReporterPathFilter = nodeRootPackageDir, webpackMajorVersion = webpackMajorVersion, rules = project.objects.webpackRulesContainer(), ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt index 54dceb104f8..ece65c7ee5c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt @@ -5,11 +5,13 @@ package org.jetbrains.kotlin.gradle.targets.js.webpack +import org.gradle.api.Action import org.gradle.api.DefaultTask import org.gradle.api.Incubating -import org.gradle.api.file.FileCollection -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.internal.file.FileResolver +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileTree +import org.gradle.api.file.FileTreeElement +import org.gradle.api.file.RegularFile import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.provider.Provider @@ -37,20 +39,20 @@ import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin import org.jetbrains.kotlin.gradle.targets.js.npm.RequiresNpmDependencies import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig.Mode -import org.jetbrains.kotlin.gradle.testing.internal.reportsDir import org.jetbrains.kotlin.gradle.utils.getValue import org.jetbrains.kotlin.gradle.utils.injected import org.jetbrains.kotlin.gradle.utils.property import java.io.File import javax.inject.Inject +@CacheableTask abstract class KotlinWebpack @Inject constructor( @Internal @Transient override val compilation: KotlinJsCompilation, - objects: ObjectFactory + private val objects: ObjectFactory ) : DefaultTask(), RequiresNpmDependencies, WebpackRulesDsl { @Transient private val nodeJs = NodeJsRootPlugin.apply(project.rootProject) @@ -60,12 +62,6 @@ constructor( private val npmProject = compilation.npmProject - private val projectPath = project.path - - @get:Inject - open val fileResolver: FileResolver - get() = injected - override val rules: KotlinWebpackRulesContainer = project.objects.webpackRulesContainer() @@ -76,8 +72,7 @@ constructor( @get:Internal internal abstract val buildMetricsService: Property - @get:Internal - val metrics: Property = project.objects + private val metrics: Property = project.objects .property(BuildMetricsReporterImpl()) @Suppress("unused") @@ -93,46 +88,52 @@ constructor( var mode: Mode = Mode.DEVELOPMENT @get:Internal - var entry: File - get() = entryProperty.asFile.get() - set(value) { - entryProperty.set(value) - } + abstract val inputFilesDirectory: DirectoryProperty - @get:PathSensitive(PathSensitivity.ABSOLUTE) - @get:InputFile + @get:Input + abstract val entryModuleName: Property + + @get:Internal + val npmProjectDir: Provider + get() = inputFilesDirectory.map { it.asFile.parentFile } + + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:InputFiles @get:NormalizeLineEndings - val entryProperty: RegularFileProperty = objects - .fileProperty() - .fileProvider( - compilation.compileTaskProvider.flatMap { it.outputFileProperty } - ) + val inputFiles: FileTree + get() = objects.fileTree() + // in webpack.config.js there is path relative to npmProjectDir (kotlin/.js). + // And we need have relative path in build cache + // That's why we use npmProjectDir with filter instead of just inputFilesDirectory, + // if we would use inputFilesDirectory, we will get in cache just file names, + // and if directory is changed to kotlin2, webpack config will be invalid. + .from(npmProjectDir) + .matching { + it.include { element: FileTreeElement -> + val inputFilesDirectory = inputFilesDirectory.get().asFile + element.file == inputFilesDirectory || + element.file.parentFile == inputFilesDirectory + } + } + + @get:Internal + val entry: Provider + get() = inputFilesDirectory.map { + it.file(entryModuleName.get() + ".js") + } init { onlyIf { - entry.exists() + entry.get().asFile.exists() } } @get:Internal internal var resolveFromModulesFirst: Boolean = false - @Suppress("unused") - @get:PathSensitive(PathSensitivity.ABSOLUTE) - @get:IgnoreEmptyDirectories - @get:InputFiles - @get:NormalizeLineEndings - val runtimeClasspath: FileCollection by lazy { - compilation.compileDependencyFiles - } - @get:OutputFile - open val configFile: File by lazy { - npmProject.dir.resolve("webpack.config.js") - } - - @Input - var saveEvaluatedConfigFile: Boolean = true + open val configFile: Provider = + npmProjectDir.map { it.resolve("webpack.config.js") } @Nested val output: KotlinWebpackOutput = KotlinWebpackOutput( @@ -153,7 +154,7 @@ constructor( project.distsDirectory.asFile.get() } - @get:Internal + @get:OutputDirectory var destinationDirectory: File get() = _destinationDirectory ?: defaultDestinationDirectory set(value) { @@ -169,13 +170,13 @@ constructor( defaultOutputFileName } - @get:OutputFile + @get:Internal open val outputFile: File get() = destinationDirectory.resolve(outputFileName) private val projectDir = project.projectDir - @get:PathSensitive(PathSensitivity.ABSOLUTE) + @get:PathSensitive(PathSensitivity.NAME_ONLY) @get:Optional @get:IgnoreEmptyDirectories @get:NormalizeLineEndings @@ -183,29 +184,6 @@ constructor( open val configDirectory: File? get() = projectDir.resolve("webpack.config.d").takeIf { it.isDirectory } - @Input - var report: Boolean = false - - private val projectReportsDir = project.reportsDir - - open val reportDir: File - @Internal get() = reportDirProvider.get() - - @get:OutputDirectory - open val reportDirProvider: Provider by lazy { - entryProperty - .map { it.asFile.nameWithoutExtension } - .map { - projectReportsDir.resolve("webpack").resolve(it) - } - } - - open val evaluatedConfigFile: File - @Internal get() = evaluatedConfigFileProvider.get() - - open val evaluatedConfigFileProvider: Provider - @OutputFile get() = reportDirProvider.map { it.resolve("webpack.config.evaluated.js") } - @Input var bin: String = "webpack/bin/webpack.js" @@ -229,20 +207,15 @@ constructor( @Internal var generateConfigOnly: Boolean = false - @Nested - val synthConfig = KotlinWebpackConfig( - rules = project.objects.webpackRulesContainer(), - ) - @Input val webpackMajorVersion = PropertiesProvider(project).webpackMajorVersion - fun webpackConfigApplier(body: KotlinWebpackConfig.() -> Unit) { - synthConfig.body() + fun webpackConfigApplier(body: Action) { webpackConfigAppliers.add(body) } - private val webpackConfigAppliers: MutableList<(KotlinWebpackConfig) -> Unit> = + @get:Nested + internal val webpackConfigAppliers: MutableList> = mutableListOf() private val platformType by project.provider { @@ -254,14 +227,13 @@ constructor( * Otherwise, Gradle will fail the build. */ private fun createWebpackConfig(forNpmDependencies: Boolean = false) = KotlinWebpackConfig( + npmProjectDir = npmProjectDir, mode = mode, - entry = if (forNpmDependencies) null else entry, - reportEvaluatedConfigFile = if (!forNpmDependencies && saveEvaluatedConfigFile) evaluatedConfigFile else null, + entry = if (forNpmDependencies) null else entry.get().asFile, output = output, outputPath = if (forNpmDependencies) null else destinationDirectory, outputFileName = outputFileName, configDirectory = configDirectory, - bundleAnalyzerReportDir = if (!forNpmDependencies && report) reportDir else null, rules = rules, devServer = devServer, devtool = devtool, @@ -281,12 +253,12 @@ constructor( } webpackConfigAppliers - .forEach { it(config) } + .forEach { it.execute(config) } return KotlinWebpackRunner( npmProject, logger, - configFile, + configFile.get(), execHandleFactory, bin, args, @@ -310,7 +282,7 @@ constructor( val runner = createRunner() if (generateConfigOnly) { - runner.config.save(configFile) + runner.config.save(configFile.get()) return } @@ -324,7 +296,7 @@ constructor( runner.copy( config = runner.config.copy( progressReporter = true, - progressReporterPathFilter = rootPackageDir.absolutePath + progressReporterPathFilter = rootPackageDir ) ).execute(services) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt index d1437e0093b..9957324f335 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpackConfig.kt @@ -10,10 +10,9 @@ package org.jetbrains.kotlin.gradle.targets.js.webpack import com.google.gson.GsonBuilder import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer import org.gradle.api.model.ObjectFactory -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* +import org.gradle.work.NormalizeLineEndings import org.jetbrains.kotlin.gradle.targets.js.NpmVersions import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency import org.jetbrains.kotlin.gradle.targets.js.appendConfigsFromDir @@ -22,12 +21,15 @@ import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl import org.jetbrains.kotlin.gradle.targets.js.jsQuoted import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion.Companion.choose import org.jetbrains.kotlin.gradle.utils.appendLine +import org.jetbrains.kotlin.gradle.utils.relativeOrAbsolute import java.io.File import java.io.Serializable import java.io.StringWriter @Suppress("MemberVisibilityCanBePrivate") data class KotlinWebpackConfig( + @Internal + val npmProjectDir: Provider? = null, @Input var mode: Mode = Mode.DEVELOPMENT, @Internal @@ -40,12 +42,12 @@ data class KotlinWebpackConfig( @Input @Optional var outputFileName: String? = entry?.name, - @Internal + @get:PathSensitive(PathSensitivity.NAME_ONLY) + @get:Optional + @get:IgnoreEmptyDirectories + @get:NormalizeLineEndings + @get:InputDirectory var configDirectory: File? = null, - @Internal - var bundleAnalyzerReportDir: File? = null, - @Internal - var reportEvaluatedConfigFile: File? = null, @Input @Optional var devServer: DevServer? = null, @@ -64,38 +66,29 @@ data class KotlinWebpackConfig( var export: Boolean = true, @Input var progressReporter: Boolean = false, - @Input + @Internal @Optional - var progressReporterPathFilter: String? = null, + var progressReporterPathFilter: File? = null, @Input var resolveFromModulesFirst: Boolean = false, @Input val webpackMajorVersion: WebpackMajorVersion = WebpackMajorVersion.V5 ) : WebpackRulesDsl { + @get:Input @get:Optional val entryInput: String? - get() = entry?.absoluteFile?.normalize()?.absolutePath + get() = npmProjectDir?.get()?.let { npmProjectDir -> entry?.relativeOrAbsolute(npmProjectDir) } @get:Input @get:Optional val outputPathInput: String? - get() = outputPath?.absoluteFile?.normalize()?.absolutePath + get() = npmProjectDir?.get()?.let { npmProjectDir -> outputPath?.relativeOrAbsolute(npmProjectDir) } @get:Input @get:Optional - val configDirectoryInput: String? - get() = configDirectory?.absoluteFile?.normalize()?.absolutePath - - @get:Input - @get:Optional - val bundleAnalyzerReportDirInput: String? - get() = bundleAnalyzerReportDir?.absoluteFile?.normalize()?.absolutePath - - @get:Input - @get:Optional - val reportEvaluatedConfigFileInput: String? - get() = reportEvaluatedConfigFile?.absoluteFile?.normalize()?.absolutePath + val progressReporterPathFilterInput: String? + get() = npmProjectDir?.get()?.let { npmProjectDir -> progressReporterPathFilter?.relativeOrAbsolute(npmProjectDir) } fun getRequiredDependencies(versions: NpmVersions) = mutableSetOf().also { @@ -114,10 +107,6 @@ data class KotlinWebpackConfig( ) it.add(versions.formatUtil) - if (bundleAnalyzerReportDir != null) { - it.add(versions.webpackBundleAnalyzer) - } - if (sourceMaps) { it.add( webpackMajorVersion.choose( @@ -148,15 +137,6 @@ data class KotlinWebpackConfig( PRODUCTION("production") } - @Suppress("unused") - data class BundleAnalyzerPlugin( - val analyzerMode: String, - val reportFilename: String, - val openAnalyzer: Boolean, - val generateStatsFile: Boolean, - val statsFilename: String - ) : Serializable - @Suppress("unused") data class DevServer( var open: Any = true, @@ -207,7 +187,6 @@ data class KotlinWebpackConfig( appendResolveModules() appendSourceMaps() appendDevServer() - appendReport() appendProgressReporter() rules.forEach { rule -> if (rule.active) { @@ -216,7 +195,6 @@ data class KotlinWebpackConfig( } appendErrorPlugin() appendFromConfigDir() - appendEvaluatedFileReport() appendExperiments() if (export) { @@ -226,26 +204,6 @@ data class KotlinWebpackConfig( } } - private fun Appendable.appendEvaluatedFileReport() { - if (reportEvaluatedConfigFile == null) return - - val filePath = reportEvaluatedConfigFile!!.canonicalPath.jsQuoted() - - //language=JavaScript 1.8 - appendLine( - """ - // save evaluated config file - ;(function(config) { - const util = require('util'); - const fs = require('fs'); - const evaluatedConfig = util.inspect(config, {showHidden: false, depth: null, compact: false}); - fs.writeFile($filePath, evaluatedConfig, function (err) {}); - })(config); - - """.trimIndent() - ) - } - private fun Appendable.appendFromConfigDir() { if (configDirectory == null || !configDirectory!!.isDirectory) return @@ -254,32 +212,6 @@ data class KotlinWebpackConfig( appendLine() } - private fun Appendable.appendReport() { - if (bundleAnalyzerReportDir == null) return - - entry ?: error("Entry should be defined for report") - - val reportBasePath = "${bundleAnalyzerReportDir!!.canonicalPath}/${entry!!.name}" - val config = BundleAnalyzerPlugin( - "static", - "$reportBasePath.report.html", - false, - true, - "$reportBasePath.stats.json" - ) - - //language=JavaScript 1.8 - appendLine( - """ - // save webpack-bundle-analyzer report - var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; - config.plugins.push(new BundleAnalyzerPlugin(${json(config)})); - - """.trimIndent() - ) - appendLine() - } - private fun Appendable.appendDevServer() { if (devServer == null) return @@ -342,11 +274,11 @@ data class KotlinWebpackConfig( """ // entry config.entry = { - main: [${entry!!.canonicalPath.jsQuoted()}] + main: [require('path').resolve(__dirname, ${entryInput!!.jsQuoted()})] }; config.output = { - path: ${outputPath!!.canonicalPath.jsQuoted()}, + path: require('path').resolve(__dirname, ${outputPathInput!!.jsQuoted()}), filename: (chunkData) => { return chunkData.chunk.name === 'main' ? ${outputFileName!!.jsQuoted()} @@ -406,8 +338,8 @@ data class KotlinWebpackConfig( const p = percentage * 100; let msg = `${"$"}{Math.trunc(p / 10)}${"$"}{Math.trunc(p % 10)}% ${"$"}{message} ${"$"}{args.join(' ')}`; ${ - if (progressReporterPathFilter == null) "" else """ - msg = msg.replace(${progressReporterPathFilter!!.jsQuoted()}, ''); + if (progressReporterPathFilterInput == null) "" else """ + msg = msg.replace(require('path').resolve(__dirname, ${progressReporterPathFilterInput!!.jsQuoted()}), ''); """.trimIndent() }; console.log(msg);