[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
This commit is contained in:
Ilya Goncharov
2022-12-22 17:19:38 +00:00
committed by Space Team
parent 72701ada29
commit e19776bd5a
15 changed files with 227 additions and 277 deletions
@@ -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())
}
@@ -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
@@ -52,3 +52,16 @@ kotlin {
}
}
}
rootProject.plugins.withType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin> {
val kotlinNodeJs = rootProject.extensions.getByType<org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension>()
tasks.register<Exec>("runWebpackResult") {
dependsOn(tasks.named("browserProductionWebpack"))
executable(kotlinNodeJs.requireConfigured().nodeExecutable)
workingDir = File("${buildDir}").resolve("distributions")
args("./${project.name}.js")
}
}
@@ -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
@@ -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<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("browserDevelopmentWebpack").map { it.configFile }.get()
args("configtest")
args(configFile.absolutePath)
val configFile = tasks.named<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("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<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("browserProductionWebpack").map { it.configFile }.get()
val configFile = tasks.named<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("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<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("browserDevelopmentRun").map { it.configFile }.get()
val configFile = tasks.named<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("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<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("browserProductionRun").map { it.configFile }.get()
val configFile = tasks.named<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack>("browserProductionRun").flatMap { it.configFile }
args("configtest")
args(configFile.absolutePath)
doFirst {
args(configFile.get().absolutePath)
}
}
binaries.executable()
browser {
@@ -76,58 +76,24 @@ interface KotlinJsTargetDsl : KotlinTarget {
interface KotlinJsSubTargetDsl {
@ExperimentalDistributionDsl
fun distribution(body: Distribution.() -> Unit)
fun distribution(body: Action<Distribution>)
@ExperimentalDistributionDsl
fun distribution(fn: Action<Distribution>) {
distribution {
fn.execute(this)
}
}
fun testTask(body: KotlinJsTest.() -> Unit)
fun testTask(fn: Action<KotlinJsTest>) {
testTask {
fn.execute(this)
}
}
fun testTask(body: Action<KotlinJsTest>)
val testRuns: NamedDomainObjectContainer<KotlinJsPlatformTestRun>
}
interface KotlinJsBrowserDsl : KotlinJsSubTargetDsl {
fun commonWebpackConfig(body: KotlinWebpackConfig.() -> Unit)
fun commonWebpackConfig(fn: Action<KotlinWebpackConfig>) {
commonWebpackConfig {
fn.execute(this)
}
}
fun commonWebpackConfig(body: Action<KotlinWebpackConfig>)
fun runTask(body: KotlinWebpack.() -> Unit)
fun runTask(fn: Action<KotlinWebpack>) {
runTask {
fn.execute(this)
}
}
fun runTask(body: Action<KotlinWebpack>)
fun webpackTask(body: KotlinWebpack.() -> Unit)
fun webpackTask(fn: Action<KotlinWebpack>) {
webpackTask {
fn.execute(this)
}
}
fun webpackTask(body: Action<KotlinWebpack>)
@ExperimentalDceDsl
fun dceTask(body: KotlinJsDce.() -> Unit)
@ExperimentalDceDsl
fun dceTask(fn: Action<KotlinJsDce>) {
dceTask {
fn.execute(this)
}
}
fun dceTask(body: Action<KotlinJsDce>)
}
interface KotlinJsNodeDsl : KotlinJsSubTargetDsl {
fun runTask(body: NodeJsExec.() -> Unit)
fun runTask(body: Action<NodeJsExec>)
}
@@ -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<KotlinWebpack.() -> Unit> = mutableListOf()
private val runTaskConfigurations: MutableList<KotlinWebpack.() -> Unit> = mutableListOf()
private val webpackTaskConfigurations: MutableList<Action<KotlinWebpack>> = mutableListOf()
private val runTaskConfigurations: MutableList<Action<KotlinWebpack>> = 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<KotlinWebpackConfig>) {
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<KotlinWebpack>) {
runTaskConfigurations.add(body)
}
override fun webpackTask(body: KotlinWebpack.() -> Unit) {
override fun webpackTask(body: Action<KotlinWebpack>) {
webpackTaskConfigurations.add(body)
}
@ExperimentalDceDsl
override fun dceTask(body: KotlinJsDce.() -> Unit) {
override fun dceTask(body: Action<KotlinJsDce>) {
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<File>,
configurationActions: List<KotlinWebpack.() -> Unit>,
inputFilesDirectory: Provider<File>,
entryModuleName: Provider<String>,
configurationActions: List<Action<KotlinWebpack>>,
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)
}
}
@@ -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<Distribution>) {
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<KotlinJsTest>) {
testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).executionTask.configure(body)
}
@@ -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<NodeJsExec>) {
project.tasks.withType<NodeJsExec>().named(runTaskName).configure(body)
}
@@ -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<KotlinWebpack.() -> Unit> = mutableListOf()
private val runTaskConfigurations: MutableList<KotlinWebpack.() -> Unit> = mutableListOf()
private val dceConfigurations: MutableList<KotlinJsDce.() -> Unit> = mutableListOf()
private val webpackTaskConfigurations: MutableList<Action<KotlinWebpack>> = mutableListOf()
private val runTaskConfigurations: MutableList<Action<KotlinWebpack>> = mutableListOf()
private val dceConfigurations: MutableList<Action<KotlinJsDce>> = 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<KotlinWebpackConfig>) {
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<KotlinWebpack>) {
runTaskConfigurations.add(body)
}
@ExperimentalDistributionDsl
override fun distribution(body: Distribution.() -> Unit) {
distribution.body()
override fun distribution(body: Action<Distribution>) {
body.execute(distribution)
}
override fun webpackTask(body: KotlinWebpack.() -> Unit) {
override fun webpackTask(body: Action<KotlinWebpack>) {
webpackTaskConfigurations.add(body)
}
@ExperimentalDceDsl
override fun dceTask(body: KotlinJsDce.() -> Unit) {
override fun dceTask(body: Action<KotlinJsDce>) {
dceConfigurations.add(body)
}
@@ -275,7 +276,7 @@ abstract class KotlinBrowserJs @Inject constructor(target: KotlinJsTarget) :
dceTaskProvider: TaskProvider<KotlinJsDceTask>,
devDceTaskProvider: TaskProvider<KotlinJsDceTask>,
mode: KotlinJsBinaryMode,
configurationActions: List<KotlinWebpack.() -> Unit>,
configurationActions: List<Action<KotlinWebpack>>,
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)
}
}
@@ -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<KotlinJsTest>) {
testRuns.getByName(KotlinTargetWithTests.DEFAULT_TEST_RUN_NAME).executionTask.configure(body)
}
@@ -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<NodeJsExec>) {
project.tasks.withType<NodeJsExec>().named(runTaskName).configure(body)
}
@ExperimentalDistributionDsl
override fun distribution(body: Distribution.() -> Unit) {
override fun distribution(body: Action<Distribution>) {
TODO("Not yet implemented")
}
override fun testTask(body: KotlinJsTest.() -> Unit) {
override fun testTask(body: Action<KotlinJsTest>) {
super<KotlinJsSubTarget>.testTask(body)
}
@@ -84,7 +84,7 @@ class KotlinKarma(
devtool = null,
export = false,
progressReporter = true,
progressReporterPathFilter = nodeRootPackageDir.absolutePath,
progressReporterPathFilter = nodeRootPackageDir,
webpackMajorVersion = webpackMajorVersion,
rules = project.objects.webpackRulesContainer(),
)
@@ -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<BuildMetricsService?>
@get:Internal
val metrics: Property<BuildMetricsReporter> = project.objects
private val metrics: Property<BuildMetricsReporter> = 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<String>
@get:Internal
val npmProjectDir: Provider<File>
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/<module>.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<RegularFile>
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<File> =
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<File> 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<File>
@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<KotlinWebpackConfig>) {
webpackConfigAppliers.add(body)
}
private val webpackConfigAppliers: MutableList<(KotlinWebpackConfig) -> Unit> =
@get:Nested
internal val webpackConfigAppliers: MutableList<Action<KotlinWebpackConfig>> =
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)
@@ -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<File>? = 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<RequiredKotlinJsDependency>().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);