Gradle, JS: webpack support
#KT-31013
This commit is contained in:
+2
-2
@@ -46,8 +46,8 @@ abstract class AbstractKotlinTargetConfigurator<KotlinTargetType : KotlinTarget>
|
||||
}
|
||||
|
||||
|
||||
abstract protected fun configureArchivesAndComponent(target: KotlinTargetType)
|
||||
abstract protected fun configureTest(target: KotlinTargetType)
|
||||
protected abstract fun configureArchivesAndComponent(target: KotlinTargetType)
|
||||
protected abstract fun configureTest(target: KotlinTargetType)
|
||||
|
||||
private fun Project.registerOutputsForStaleOutputCleanup(kotlinCompilation: KotlinCompilation<*>) {
|
||||
val cleanTask = tasks.getByName(LifecycleBasePlugin.CLEAN_TASK_NAME) as Delete
|
||||
|
||||
+4
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.defaultSourceSetLanguageSettingsChecker
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.getSourceSetHierarchy
|
||||
import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateTask
|
||||
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import java.util.*
|
||||
@@ -48,6 +49,9 @@ abstract class AbstractKotlinCompilation<T : KotlinCommonOptions>(
|
||||
override val compileKotlinTask: KotlinCompile<T>
|
||||
get() = (target.project.tasks.getByName(compileKotlinTaskName) as KotlinCompile<T>)
|
||||
|
||||
val compileKotlinTaskHolder: TaskHolder<KotlinCompile<T>>
|
||||
get() = target.project.locateTask(compileKotlinTaskName)!!
|
||||
|
||||
// Don't declare this property in the constructor to avoid NPE
|
||||
// when an overriding property of a subclass is accessed instead.
|
||||
override val target: KotlinTarget = target
|
||||
|
||||
+18
-19
@@ -1,22 +1,19 @@
|
||||
package org.jetbrains.kotlin.gradle.targets.js
|
||||
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsPlugin
|
||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.configureConventions
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.registerTestTask
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
internal class KotlinJsCompilationTestsConfigurator(
|
||||
val compilation: KotlinCompilationToRunnableFiles<*>
|
||||
) {
|
||||
internal open class KotlinJsCompilationTestsConfigurator(val compilation: KotlinJsCompilation) {
|
||||
private val target get() = compilation.target
|
||||
private val disambiguationClassifier get() = target.disambiguationClassifier
|
||||
private val project get() = target.project
|
||||
private val compileTestKotlin2Js get() = compilation.compileKotlinTask as Kotlin2JsCompile
|
||||
private val compileTask get() = compilation.compileKotlinTask
|
||||
|
||||
private fun disambiguate(name: String, includeCompilation: Boolean = false): MutableList<String> {
|
||||
val components = mutableListOf<String>()
|
||||
@@ -37,35 +34,37 @@ internal class KotlinJsCompilationTestsConfigurator(
|
||||
get() = disambiguateCamelCased("test", false)
|
||||
|
||||
fun configure() {
|
||||
// apply plugin (cannot do it lazy)
|
||||
// apply plugin (cannot be done at task instantiation time)
|
||||
val nodeJs = NodeJsPlugin.apply(target.project).root
|
||||
|
||||
val testJs = project.createOrRegisterTask<KotlinJsTest>(testTaskName) { testJs ->
|
||||
testJs.group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
|
||||
testJs.dependsOn(compileTestKotlin2Js, nodeJs.nodeJsSetupTask)
|
||||
testJs.dependsOn(compileTask, nodeJs.nodeJsSetupTask)
|
||||
|
||||
testJs.onlyIf {
|
||||
compileTestKotlin2Js.outputFile.exists()
|
||||
compileTask.outputFile.exists()
|
||||
}
|
||||
|
||||
testJs.runtimeDependencyHandler = compilation
|
||||
testJs.targetName = disambiguationClassifier
|
||||
testJs.nodeModulesToLoad.add(compileTestKotlin2Js.outputFile.name)
|
||||
testJs.nodeModulesToLoad.add(compileTask.outputFile.name)
|
||||
|
||||
testJs.configureConventions()
|
||||
}
|
||||
|
||||
testJs.doGetTask().useNodeJs { }
|
||||
|
||||
registerTestTask(testJs)
|
||||
|
||||
// project.afterEvaluate {
|
||||
// testJs.configure {
|
||||
// if (it.testFramework == null) {
|
||||
// it.useNodeJs { }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
project.afterEvaluate {
|
||||
testJs.configure {
|
||||
if (it.testFramework == null) {
|
||||
configureDefaultTestFramework(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun configureDefaultTestFramework(it: KotlinJsTest) {
|
||||
it.useNodeJs { }
|
||||
}
|
||||
}
|
||||
+35
-11
@@ -5,17 +5,19 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.js
|
||||
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.Kotlin2JsSourceSetProcessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetProcessor
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetConfigurator
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinOnlyTarget
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProjectLayout
|
||||
import org.jetbrains.kotlin.gradle.targets.js.testing.KotlinJsTest
|
||||
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<KotlinJsCompilation>(true, true, kotlinPluginVersion) {
|
||||
open class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
|
||||
KotlinTargetConfigurator<KotlinJsCompilation>(true, true, kotlinPluginVersion) {
|
||||
|
||||
override fun buildCompilationProcessor(compilation: KotlinJsCompilation): KotlinSourceSetProcessor<*> {
|
||||
val tasksProvider = KotlinTasksProvider(compilation.target.targetName)
|
||||
@@ -35,20 +37,42 @@ class KotlinJsTargetConfigurator(kotlinPluginVersion: String) :
|
||||
}
|
||||
|
||||
override fun configureTest(target: KotlinOnlyTarget<KotlinJsCompilation>) {
|
||||
val project = target.project
|
||||
val npmProject = NpmProjectLayout[project]
|
||||
|
||||
target.compilations.all { compilation ->
|
||||
if (isTestCompilation(compilation)) {
|
||||
KotlinJsCompilationTestsConfigurator(compilation).configure()
|
||||
} else {
|
||||
KotlinWebpack.configure(compilation)
|
||||
newTestsConfigurator(compilation).configure()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal open fun newTestsConfigurator(compilation: KotlinJsCompilation) =
|
||||
KotlinJsCompilationTestsConfigurator(compilation)
|
||||
|
||||
companion object {
|
||||
internal fun isTestCompilation(it: KotlinJsCompilation) =
|
||||
it.name == KotlinCompilation.TEST_COMPILATION_NAME
|
||||
}
|
||||
}
|
||||
|
||||
class KotlinJsSingleTargetConfigurator(kotlinPluginVersion: String) :
|
||||
KotlinJsTargetConfigurator(kotlinPluginVersion) {
|
||||
|
||||
override fun configureTarget(target: KotlinOnlyTarget<KotlinJsCompilation>) {
|
||||
super.configureTarget(target)
|
||||
configureApplication(target)
|
||||
}
|
||||
|
||||
private fun configureApplication(target: KotlinOnlyTarget<KotlinJsCompilation>) {
|
||||
target.compilations.all {
|
||||
if (it.name == KotlinCompilation.MAIN_COMPILATION_NAME) {
|
||||
KotlinWebpack.configure(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun newTestsConfigurator(compilation: KotlinJsCompilation) =
|
||||
object : KotlinJsCompilationTestsConfigurator(compilation) {
|
||||
override fun configureDefaultTestFramework(it: KotlinJsTest) {
|
||||
it.useMocha { }
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsSingleTargetConfigurator
|
||||
import org.jetbrains.kotlin.gradle.targets.js.KotlinJsTargetConfigurator
|
||||
|
||||
open class KotlinJsTargetPreset(
|
||||
@@ -42,4 +43,6 @@ class KotlinJsSingleTargetPreset(
|
||||
|
||||
// In a Kotlin/JS single-platform project, we don't need any disambiguation suffixes or prefixes in the names:
|
||||
override fun provideTargetDisambiguationClassifier(target: KotlinOnlyTarget<KotlinJsCompilation>): String? = null
|
||||
|
||||
override fun createKotlinTargetConfigurator() = KotlinJsSingleTargetConfigurator(kotlinPluginVersion)
|
||||
}
|
||||
+2
@@ -71,6 +71,8 @@ class NpmProject(
|
||||
else NpmProject[parent].findModule(name, src)
|
||||
}
|
||||
|
||||
override fun toString() = "NpmProject($nodeWorkDir)"
|
||||
|
||||
companion object {
|
||||
const val PACKAGE_JSON = "package.json"
|
||||
const val NODE_MODULES = "node_modules"
|
||||
|
||||
+68
-95
@@ -5,21 +5,22 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.js.webpack
|
||||
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.internal.file.FileResolver
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.deployment.internal.Deployment
|
||||
import org.gradle.deployment.internal.DeploymentHandle
|
||||
import org.gradle.deployment.internal.DeploymentRegistry
|
||||
import org.gradle.process.internal.ExecHandle
|
||||
import org.gradle.process.internal.ExecHandleFactory
|
||||
import org.gradle.process.internal.ExecHandleState
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilationToRunnableFiles
|
||||
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
|
||||
import org.jetbrains.kotlin.gradle.tasks.createOrRegisterTask
|
||||
import org.jetbrains.kotlin.gradle.testing.internal.reportsDir
|
||||
import org.jetbrains.kotlin.gradle.utils.injected
|
||||
import java.io.File
|
||||
import java.io.StringWriter
|
||||
import javax.inject.Inject
|
||||
|
||||
open class KotlinWebpack : DefaultTask() {
|
||||
@@ -39,9 +40,9 @@ open class KotlinWebpack : DefaultTask() {
|
||||
@OutputFile get() = project.buildDir.resolve("webpack.config.js")
|
||||
|
||||
@Input
|
||||
var saveConfigFileEffective: Boolean = true
|
||||
var saveEvaluatedConfigFile: Boolean = true
|
||||
|
||||
open val configFileEffective: File
|
||||
open val evaluatedConfigFile: File
|
||||
@OutputFile get() = project.buildDir.resolve("webpack.config.evaluated.js")
|
||||
|
||||
open val outputPath: File
|
||||
@@ -56,103 +57,54 @@ open class KotlinWebpack : DefaultTask() {
|
||||
open val reportDir: File
|
||||
@OutputDirectory get() = project.reportsDir.resolve("webpack").resolve(entry.nameWithoutExtension)
|
||||
|
||||
@Input
|
||||
var bin: String = "webpack"
|
||||
|
||||
@Input
|
||||
var devServer: KotlinWebpackConfig.DevServer? = null
|
||||
|
||||
internal fun createRunner() = KotlinWebpackRunner(
|
||||
project,
|
||||
configFile,
|
||||
execHandleFactory,
|
||||
bin,
|
||||
KotlinWebpackConfig(
|
||||
entry,
|
||||
if (saveEvaluatedConfigFile) evaluatedConfigFile else null,
|
||||
outputPath,
|
||||
configDirectory,
|
||||
if (report) reportDir else null,
|
||||
devServer
|
||||
)
|
||||
)
|
||||
|
||||
@TaskAction
|
||||
fun execute() {
|
||||
check(entry.isFile) {
|
||||
"${this}: Entry file not existed \"$entry\""
|
||||
}
|
||||
val runner = createRunner()
|
||||
|
||||
NpmResolver.resolve(project)
|
||||
|
||||
val npmProjectLayout = NpmProjectLayout[project]
|
||||
|
||||
configFile.writeText(buildConfig())
|
||||
|
||||
val execFactory = execHandleFactory.newExec()
|
||||
npmProjectLayout.useTool(
|
||||
execFactory,
|
||||
".bin/webpack",
|
||||
"--config", configFile.absolutePath
|
||||
)
|
||||
val exec = execFactory.build()
|
||||
exec.start()
|
||||
exec.waitForFinish()
|
||||
}
|
||||
|
||||
private fun buildConfig(): String {
|
||||
return buildString {
|
||||
//language=JavaScript 1.8
|
||||
append(
|
||||
"""
|
||||
const config = {
|
||||
mode: 'development',
|
||||
entry: '${entry.canonicalPath}',
|
||||
output: {
|
||||
path: '${outputPath.canonicalPath}',
|
||||
filename: '${entry.name}'
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
"node_modules"
|
||||
]
|
||||
},
|
||||
plugins: [],
|
||||
module: {
|
||||
rules: []
|
||||
}
|
||||
};
|
||||
"""
|
||||
)
|
||||
|
||||
if (saveConfigFileEffective) {
|
||||
//language=JavaScript 1.8
|
||||
append(
|
||||
"""
|
||||
const util = require('util');
|
||||
const fs = require("fs");
|
||||
const evaluatedConfig = util.inspect(config, {showHidden: false, depth: null, compact: false});
|
||||
fs.writeFile(${jsQuotedString(configFileEffective.canonicalPath)}, evaluatedConfig, (err) => {});
|
||||
"""
|
||||
)
|
||||
if (project.gradle.startParameter.isContinuous) {
|
||||
val deploymentRegistry = services.get(DeploymentRegistry::class.java)
|
||||
val deploymentHandle = deploymentRegistry.get("webpack", Handle::class.java)
|
||||
if (deploymentHandle == null) {
|
||||
deploymentRegistry.start("webpack", DeploymentRegistry.ChangeBehavior.BLOCK, Handle::class.java, runner)
|
||||
}
|
||||
|
||||
if (report) {
|
||||
//language=JavaScript 1.8
|
||||
append(
|
||||
"""
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
config.plugins.push(new BundleAnalyzerPlugin({
|
||||
analyzerMode: "static",
|
||||
reportFilename: "${entry.name}.report.html",
|
||||
openAnalyzer: false,
|
||||
generateStatsFile: true,
|
||||
statsFilename: "${entry.name}.stats.json"
|
||||
})
|
||||
)
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
loadConfigs(configDirectory)
|
||||
|
||||
append("module.exports = config")
|
||||
} else {
|
||||
runner.execute()
|
||||
}
|
||||
}
|
||||
|
||||
private fun jsQuotedString(str: String) = StringWriter().also {
|
||||
JsonWriter(it).value(str)
|
||||
}.toString()
|
||||
internal open class Handle @Inject constructor(val runner: KotlinWebpackRunner) : DeploymentHandle {
|
||||
var process: ExecHandle? = null
|
||||
|
||||
private fun StringBuilder.loadConfigs(confDir: File) {
|
||||
if (confDir.isDirectory) confDir
|
||||
.listFiles()
|
||||
?.toList()
|
||||
?.filter { it.name.endsWith(".js") }
|
||||
?.forEach {
|
||||
append(it.readText())
|
||||
appendln()
|
||||
appendln()
|
||||
}
|
||||
override fun isRunning() = process != null
|
||||
|
||||
override fun start(deployment: Deployment) {
|
||||
process = runner.start()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
process?.abort()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -174,6 +126,27 @@ open class KotlinWebpack : DefaultTask() {
|
||||
|
||||
it.entry = npmProject.moduleOutput(compileKotlinTask)
|
||||
}
|
||||
|
||||
compilation.dependencies {
|
||||
runtimeOnly(npm("webpack-dev-server", "3.3.1"))
|
||||
}
|
||||
|
||||
project.createOrRegisterTask<KotlinWebpack>("run") {
|
||||
val compileKotlinTask = compilation.compileKotlinTask
|
||||
compileKotlinTask as Kotlin2JsCompile
|
||||
|
||||
it.dependsOn(compileKotlinTask)
|
||||
|
||||
it.bin = "webpack-dev-server"
|
||||
it.entry = npmProject.moduleOutput(compileKotlinTask)
|
||||
|
||||
val projectDir = compilation.target.project.projectDir.canonicalPath
|
||||
it.devServer = KotlinWebpackConfig.DevServer(
|
||||
contentBase = listOf("$projectDir/src/main/resources")
|
||||
)
|
||||
|
||||
it.outputs.upToDateWhen { false }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress("NodeJsCodingAssistanceForCoreModules", "JSUnresolvedFunction")
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.js.webpack
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.io.StringWriter
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class KotlinWebpackConfig(
|
||||
val entry: File,
|
||||
val reportEvaluatedConfigFile: File?,
|
||||
val outputPath: File,
|
||||
val configDirectory: File,
|
||||
val reportDir: File?,
|
||||
var devServer: DevServer? = null,
|
||||
val showProgress: Boolean = false
|
||||
) {
|
||||
@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(
|
||||
val inline: Boolean = true,
|
||||
val lazy: Boolean = false,
|
||||
val noInfo: Boolean = true,
|
||||
val open: Any = true,
|
||||
val overlay: Any = false,
|
||||
val port: Int = 8080,
|
||||
val proxy: Map<String, Any>? = null,
|
||||
val contentBase: List<String>
|
||||
) : Serializable
|
||||
|
||||
fun save(configFile: File) {
|
||||
configFile.writeText(build())
|
||||
}
|
||||
|
||||
fun build() = buildString {
|
||||
//language=JavaScript 1.8
|
||||
appendln(
|
||||
"""
|
||||
var config = {
|
||||
mode: 'development',
|
||||
entry: '${entry.canonicalPath}',
|
||||
output: {
|
||||
path: '${outputPath.canonicalPath}',
|
||||
filename: '${entry.name}'
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
"node_modules"
|
||||
]
|
||||
},
|
||||
plugins: [],
|
||||
module: {
|
||||
rules: []
|
||||
}
|
||||
};"""
|
||||
)
|
||||
appendln()
|
||||
|
||||
if (devServer != null) {
|
||||
appendln("// dev server")
|
||||
appendln("config.devServer = ${json(devServer!!)};")
|
||||
appendln()
|
||||
}
|
||||
|
||||
if (reportDir != null) {
|
||||
val reportBasePath = "${reportDir.canonicalPath}/${entry.name}"
|
||||
val config = KotlinWebpackConfig.BundleAnalyzerPlugin(
|
||||
"static",
|
||||
"$reportBasePath.report.html",
|
||||
false,
|
||||
true,
|
||||
"$reportBasePath.stats.json"
|
||||
)
|
||||
|
||||
//language=JavaScript 1.8
|
||||
appendln(
|
||||
"""
|
||||
// save webpack-bundle-analyzer report
|
||||
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
config.plugins.push(new BundleAnalyzerPlugin(${json(config)}));
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
appendln()
|
||||
loadConfigs(configDirectory)
|
||||
appendln()
|
||||
|
||||
if (reportEvaluatedConfigFile != null) {
|
||||
val filePath = jsQuotedString(reportEvaluatedConfigFile.canonicalPath)
|
||||
|
||||
//language=JavaScript 1.8
|
||||
appendln(
|
||||
"""
|
||||
// save evaluated config file
|
||||
var util = require('util');
|
||||
var fs = require("fs");
|
||||
var evaluatedConfig = util.inspect(config, {showHidden: false, depth: null, compact: false});
|
||||
fs.writeFile($filePath, evaluatedConfig, function (err) {});
|
||||
"""
|
||||
)
|
||||
appendln()
|
||||
}
|
||||
|
||||
appendln("module.exports = config")
|
||||
}
|
||||
|
||||
private fun json(obj: Any) = StringWriter().also {
|
||||
GsonBuilder().setPrettyPrinting().create().toJson(obj, it)
|
||||
}.toString()
|
||||
|
||||
private fun jsQuotedString(str: String) = StringWriter().also {
|
||||
JsonWriter(it).value(str)
|
||||
}.toString()
|
||||
|
||||
private fun StringBuilder.loadConfigs(confDir: File) {
|
||||
if (confDir.isDirectory) confDir
|
||||
.listFiles()
|
||||
?.toList()
|
||||
?.filter { it.name.endsWith(".js") }
|
||||
?.forEach {
|
||||
appendln("// ${it.name}")
|
||||
append(it.readText())
|
||||
appendln()
|
||||
appendln()
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@file:Suppress(
|
||||
"JSAnnotator",
|
||||
"NodeJsCodingAssistanceForCoreModules",
|
||||
"BadExpressionStatementJS",
|
||||
"JSUnresolvedFunction"
|
||||
)
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.js.webpack
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.process.internal.ExecHandle
|
||||
import org.gradle.process.internal.ExecHandleFactory
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmProject
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.NpmResolver
|
||||
import java.io.File
|
||||
|
||||
internal class KotlinWebpackRunner(
|
||||
val project: Project,
|
||||
val configFile: File,
|
||||
val execHandleFactory: ExecHandleFactory,
|
||||
val bin: String,
|
||||
val config: KotlinWebpackConfig
|
||||
) {
|
||||
fun execute() {
|
||||
val exec = start()
|
||||
exec.waitForFinish()
|
||||
}
|
||||
|
||||
fun start(): ExecHandle {
|
||||
check(config.entry.isFile) {
|
||||
"${this}: Entry file not existed \"${config.entry}\""
|
||||
}
|
||||
|
||||
NpmResolver.resolve(project)
|
||||
|
||||
val npmProjectLayout = NpmProject[project]
|
||||
|
||||
config.save(configFile)
|
||||
|
||||
val execFactory = execHandleFactory.newExec()
|
||||
val args = mutableListOf<String>("--config", configFile.absolutePath)
|
||||
if (config.showProgress) {
|
||||
args.add("--progress")
|
||||
}
|
||||
|
||||
npmProjectLayout.useTool(execFactory, ".bin/$bin")
|
||||
val exec = execFactory.build()
|
||||
exec.start()
|
||||
return exec
|
||||
}
|
||||
}
|
||||
+30
-18
@@ -26,28 +26,19 @@ object Yarn : NpmApi {
|
||||
project: Project,
|
||||
description: String,
|
||||
vararg args: String,
|
||||
npmProjectLayout: NpmProjectLayout = NpmProjectLayout[project]
|
||||
npmProject: NpmProject = NpmProject[project]
|
||||
) {
|
||||
val nodeJsEnv = NodeJsPlugin.apply(project).root.environment
|
||||
val yarnEnv = YarnPlugin.apply(project).environment
|
||||
|
||||
val packageJsonHashFile = project.packageJsonHashFile
|
||||
val packageJsonHash = if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null
|
||||
|
||||
val hasher = (project as ProjectInternal).services.get(FileHasher::class.java)
|
||||
val hash = hasher.hash(npmProjectLayout.packageJsonFile).toByteArray().toHexString()
|
||||
|
||||
if (packageJsonHash == hash) return
|
||||
|
||||
packageJsonHashFile.writeText(hash)
|
||||
|
||||
val nodeWorkDir = npmProjectLayout.nodeWorkDir
|
||||
val nodeWorkDir = npmProject.nodeWorkDir
|
||||
|
||||
project.execWithProgress(description) { exec ->
|
||||
exec.executable = nodeJsEnv.nodeExecutable
|
||||
exec.args = listOf(yarnEnv.home.resolve("bin/yarn.js").absolutePath) + args
|
||||
exec.workingDir = nodeWorkDir
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun yarnLockReadTransitiveDependencies(
|
||||
@@ -67,11 +58,13 @@ object Yarn : NpmApi {
|
||||
val deps = byKey[key]
|
||||
if (deps != null) {
|
||||
src.dependencies.addAll(deps.dependencies.map { dep ->
|
||||
val scopedName = dep.scopedName
|
||||
|
||||
resolveRecursively(
|
||||
NpmDependency(
|
||||
src.project,
|
||||
dep.group,
|
||||
dep.packageName,
|
||||
scopedName.scope,
|
||||
scopedName.name,
|
||||
dep.version ?: "*"
|
||||
)
|
||||
)
|
||||
@@ -93,8 +86,10 @@ object Yarn : NpmApi {
|
||||
override fun resolveProject(npmPackage: NpmResolver.NpmPackage) {
|
||||
val project = npmPackage.project
|
||||
if (!project.yarn.useWorkspaces) {
|
||||
yarnExec(project, NpmApi.resolveOperationDescription("yarn for ${project.path}"))
|
||||
yarnLockReadTransitiveDependencies(NpmProjectLayout[project].nodeWorkDir, npmPackage.npmDependencies)
|
||||
YarnAvoidance(project).updateIfNeeded {
|
||||
yarnExec(project, NpmApi.resolveOperationDescription("yarn for ${project.path}"))
|
||||
yarnLockReadTransitiveDependencies(NpmProject[project].nodeWorkDir, npmPackage.npmDependencies)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +122,7 @@ object Yarn : NpmApi {
|
||||
check(rootProject == rootProject.rootProject)
|
||||
|
||||
if (rootProject.yarn.useWorkspaces) {
|
||||
yarnExec(rootProject, NpmApi.resolveOperationDescription("yarn"))
|
||||
yarnLockReadTransitiveDependencies(NpmProjectLayout[rootProject].nodeWorkDir, subprojects.flatMap { it.npmDependencies })
|
||||
resolveWorkspaces(rootProject, subprojects)
|
||||
} else {
|
||||
if (subprojects.any { it.project != rootProject }) {
|
||||
// todo: proofread message
|
||||
@@ -144,6 +138,24 @@ object Yarn : NpmApi {
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveWorkspaces(
|
||||
rootProject: Project,
|
||||
subprojects: MutableList<NpmResolver.NpmPackage>
|
||||
) {
|
||||
val upToDateChecks = subprojects.map {
|
||||
YarnAvoidance(it.project)
|
||||
}
|
||||
|
||||
if (upToDateChecks.all { it.upToDate }) return
|
||||
|
||||
yarnExec(rootProject, NpmApi.resolveOperationDescription("yarn"))
|
||||
yarnLockReadTransitiveDependencies(NpmProject[rootProject].nodeWorkDir, subprojects.flatMap { it.npmDependencies })
|
||||
|
||||
upToDateChecks.forEach {
|
||||
it.commit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanProject(project: Project) {
|
||||
super.cleanProject(project)
|
||||
project.packageJsonHashFile.delete()
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.targets.js.yarn
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.internal.hash.FileHasher
|
||||
import org.jetbrains.kotlin.daemon.common.toHexString
|
||||
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
|
||||
import org.jetbrains.kotlin.gradle.targets.js.yarn.Yarn.packageJsonHashFile
|
||||
|
||||
class YarnAvoidance(val project: Project) {
|
||||
private val prevHash by lazy {
|
||||
val packageJsonHashFile = project.packageJsonHashFile
|
||||
if (packageJsonHashFile.exists()) packageJsonHashFile.readText() else null
|
||||
}
|
||||
|
||||
private val hash by lazy {
|
||||
val hasher = (project as ProjectInternal).services.get(FileHasher::class.java)
|
||||
hasher.hash(project.npmProject.packageJsonFile).toByteArray().toHexString()
|
||||
}
|
||||
|
||||
val upToDate: Boolean
|
||||
get() = prevHash == hash
|
||||
|
||||
fun commit() {
|
||||
if (!upToDate) {
|
||||
project.packageJsonHashFile.writeText(hash)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun updateIfNeeded(body: () -> Unit) {
|
||||
if (!upToDate) {
|
||||
body()
|
||||
commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -50,10 +50,11 @@ open class YarnSetupTask : DefaultTask() {
|
||||
}
|
||||
|
||||
private fun extract(archive: File, destination: File) {
|
||||
val dirInTar = archive.name.removeSuffix(".tar.gz") + File.pathSeparator
|
||||
val dirInTar = archive.name.removeSuffix(".tar.gz") + File.separator
|
||||
project.copy {
|
||||
it.from(project.tarTree(archive))
|
||||
it.into(destination)
|
||||
it.includeEmptyDirs = false
|
||||
it.eachFile { fileCopy ->
|
||||
fileCopy.path = fileCopy.path.removePrefix(dirInTar)
|
||||
}
|
||||
|
||||
+5
-5
@@ -53,16 +53,16 @@ internal fun <T : Task> Project.createOrRegisterTask(name: String, type: Class<T
|
||||
/**
|
||||
* Locates a task by [name] and [type], without triggering its creation or configuration.
|
||||
*/
|
||||
internal fun <T : Task> locateTask(project: Project, name: String, type: Class<T>): TaskHolder<T>? =
|
||||
internal inline fun <reified T : Task> Project.locateTask(name: String): TaskHolder<T>? =
|
||||
if (canLocateTask) {
|
||||
try {
|
||||
TaskProviderHolder(name, project, project.tasks.named(name, type))
|
||||
TaskProviderHolder(name, this, tasks.named(name, T::class.java))
|
||||
} catch (e: UnknownTaskException) {
|
||||
null
|
||||
}
|
||||
} else {
|
||||
project.tasks.findByName(name)?.let {
|
||||
check(type.isInstance(it))
|
||||
tasks.findByName(name)?.let {
|
||||
check(T::class.java.isInstance(it))
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
LegacyTaskHolder(it as T)
|
||||
@@ -74,7 +74,7 @@ internal fun <T : Task> locateTask(project: Project, name: String, type: Class<T
|
||||
* with [name], type [T] and initialization script [body]
|
||||
*/
|
||||
internal inline fun <reified T : Task> Project.locateOrRegisterTask(name: String, noinline body: (T) -> (Unit)): TaskHolder<T> {
|
||||
return locateTask(project, name, T::class.java) ?: registerTask(project, name, T::class.java, body)
|
||||
return project.locateTask(name) ?: registerTask(project, name, T::class.java, body)
|
||||
}
|
||||
|
||||
internal open class KotlinTasksProvider(val targetName: String) {
|
||||
|
||||
Reference in New Issue
Block a user