KT-52637 SCSS support

^KT-52637 fixed
This commit is contained in:
Martynas Petuška
2022-06-09 19:18:29 +01:00
committed by teamcity
parent b42cbf6650
commit 6cf085aafb
18 changed files with 397 additions and 207 deletions
@@ -92,7 +92,7 @@ class BuildReportsIT : KGPBaseTest() {
}
}
@DisplayName("")
@DisplayName("smoke")
@GradleTest
fun testSingleBuildMetricsFileSmoke(gradleVersion: GradleVersion) {
project("simpleProject", gradleVersion) {
@@ -109,4 +109,4 @@ class BuildReportsIT : KGPBaseTest() {
}
}
}
}
}
@@ -8,8 +8,10 @@ package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.configuration.WarningMode
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
import javax.inject.Inject
abstract class AbstractJsConfigurationCacheIT(protected val irBackend: Boolean) : KGPBaseTest() {
private val defaultJsOptions = BuildOptions.JsOptions(
@@ -93,4 +95,4 @@ abstract class AbstractJsConfigurationCacheIT(protected val irBackend: Boolean)
class JsConfigurationCacheIT : AbstractJsConfigurationCacheIT(irBackend = false)
@JsGradlePluginTests
class JsIrConfigurationCacheIT : AbstractJsConfigurationCacheIT(irBackend = true)
class JsIrConfigurationCacheIT : AbstractJsConfigurationCacheIT(irBackend = true)
@@ -74,7 +74,12 @@ fun KGPBaseTest.project(
localRepoDir?.let { testProject.configureLocalRepository(localRepoDir) }
if (buildJdk != null) testProject.setupNonDefaultJdk(buildJdk)
testProject.test()
runCatching {
testProject.test()
}.onFailure {
// A convenient place to place a breakpoint to be able to inspect project output files
throw it
}
return testProject
}
@@ -411,6 +416,7 @@ internal fun Path.addPluginManagementToSettings() {
it
}
}
Files.exists(settingsGradleKts) -> settingsGradleKts.modify {
if (!it.contains("pluginManagement {")) {
"""
@@ -422,6 +428,7 @@ internal fun Path.addPluginManagementToSettings() {
it
}
}
else -> settingsGradle.toFile().writeText(DEFAULT_GROOVY_SETTINGS_FILE)
}
@@ -519,6 +526,7 @@ internal fun Path.enableCacheRedirector() {
""".trimIndent()
)
}
"build.gradle.kts" -> {
it.appendText(
"""
@@ -12,7 +12,12 @@ kotlin {
js(IR) {
browser {
commonWebpackConfig {
cssSupport.enabled = true
cssSupport {
enabled.set(true)
}
scssSupport {
enabled.set(true)
}
}
}
}
@@ -20,4 +25,4 @@ kotlin {
dependencies {
"jsMainImplementation"(npm("decamelize", "4.0.0", generateExternals = true))
}
}
@@ -1,3 +1,6 @@
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
import javax.inject.Inject
plugins {
kotlin("js")
}
@@ -9,18 +12,35 @@ dependencies {
testImplementation(kotlin("test-js"))
}
abstract class CustomWebpackRule
@javax.inject.Inject
constructor(name: String) : org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackRule(name) {
init {
test.set("none")
}
override fun loaders() = listOf<org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackRule.Loader>()
}
kotlin {
target {
browser {
webpackTask {
cssSupport.enabled = true
cssSupport {
enabled.set(true)
}
scssSupport {
enabled.set(true)
}
rules {
rule<CustomWebpackRule>("custom")
}
}
testTask {
useKarma {
useChromeHeadless()
}
enabled = false // Task is disabled because it requires browser to be installed. That may be a problem on CI.
// Disabled but configured task allows us to check at least a part of configuration cache correctness.
// Disabled but configured task allows us to check at least a part of configuration cache correctness.
}
}
binaries.executable()
@@ -31,4 +51,4 @@ kotlin {
}
}
}
}
}
@@ -19,4 +19,4 @@ listOf(generateNpmVersions).forEach {
"org.jetbrains.kotlin.generators.gradle.targets.js.outputSourceRoot",
project(":kotlin-gradle-plugin").projectDir.resolve("src/common/kotlin").absolutePath
)
}
}
@@ -19,6 +19,8 @@ val npmPackages = listOf(
NpmPackage("source-map-support"),
NpmPackage("css-loader"),
NpmPackage("style-loader"),
NpmPackage("sass-loader"),
NpmPackage("sass"),
NpmPackage("to-string-loader"),
NpmPackage("mini-css-extract-plugin"),
NpmPackage("mocha"),
@@ -40,4 +42,4 @@ data class NpmPackage(
val name: String,
val version: String? = null,
val displayName: String = name
)
)
@@ -25,6 +25,8 @@ class NpmVersions : Serializable {
val sourceMapSupport = NpmPackageVersion("source-map-support", "0.5.21")
val cssLoader = NpmPackageVersion("css-loader", "6.7.1")
val styleLoader = NpmPackageVersion("style-loader", "3.3.1")
val sassLoader = NpmPackageVersion("sass-loader", "13.0.0")
val sass = NpmPackageVersion("sass", "1.52.3")
val toStringLoader = NpmPackageVersion("to-string-loader", "1.2.0")
val miniCssExtractPlugin = NpmPackageVersion("mini-css-extract-plugin", "2.6.1")
val mocha = NpmPackageVersion("mocha", "10.0.0")
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.dsl
import org.gradle.api.Action
import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer
import org.gradle.api.model.ObjectFactory
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackRule
class KotlinWebpackRulesContainer(
container: ExtensiblePolymorphicDomainObjectContainer<KotlinWebpackRule>,
private val objectFactory: ObjectFactory,
) : ExtensiblePolymorphicDomainObjectContainer<KotlinWebpackRule> by container {
inline fun <reified T : KotlinWebpackRule> rule(name: String, config: Action<T> = Action {}) {
rule(T::class.java, name, config)
}
fun <T : KotlinWebpackRule> rule(type: Class<T>, name: String, config: Action<T> = Action {}) {
add(objectFactory.newInstance(type, name).also(config::execute))
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.dsl
import org.gradle.api.Action
import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer
import org.gradle.api.model.ObjectFactory
import org.gradle.api.tasks.Nested
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssRule
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackRule
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackScssRule
import org.jetbrains.kotlin.gradle.utils.newInstance
interface WebpackRulesDsl {
@get:Nested
val rules: KotlinWebpackRulesContainer
fun rules(action: Action<KotlinWebpackRulesContainer>) {
action.execute(rules)
}
fun cssSupport(action: Action<KotlinWebpackCssRule>) {
val rule = rules.maybeCreate("css", KotlinWebpackCssRule::class.java)
action.execute(rule)
}
fun scssSupport(action: Action<KotlinWebpackScssRule>) {
val rule = rules.maybeCreate("scss", KotlinWebpackScssRule::class.java)
action.execute(rule)
}
companion object {
private inline fun <reified T : KotlinWebpackRule> ObjectFactory.bindTo(
container: ExtensiblePolymorphicDomainObjectContainer<KotlinWebpackRule>
) = container.registerFactory(T::class.java) { newInstance<T>(it) }
fun ObjectFactory.webpackRulesContainer(): KotlinWebpackRulesContainer {
val delegate = polymorphicDomainObjectContainer(KotlinWebpackRule::class.java).also {
bindTo<KotlinWebpackCssRule>(it)
bindTo<KotlinWebpackScssRule>(it)
}
return KotlinWebpackRulesContainer(delegate, this)
}
}
}
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.gradle.internal.testing.TCServiceMessagesTestExecuti
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJsCompilation
import org.jetbrains.kotlin.gradle.targets.js.*
import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl.Companion.webpackRulesContainer
import org.jetbrains.kotlin.gradle.targets.js.internal.parseNodeJsStackTraceAsJvm
import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin
import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject
@@ -40,11 +41,11 @@ class KotlinKarma(
@Transient override val compilation: KotlinJsCompilation,
private val services: () -> ServiceRegistry,
private val basePath: String
) :
KotlinJsTestFramework {
) : KotlinJsTestFramework {
@Transient
private val project: Project = compilation.target.project
private val npmProject = compilation.npmProject
@Transient
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
private val nodeRootPackageDir by lazy { nodeJs.rootPackageDir }
@@ -80,7 +81,8 @@ class KotlinKarma(
export = false,
progressReporter = true,
progressReporterPathFilter = nodeRootPackageDir.absolutePath,
webpackMajorVersion = webpackMajorVersion
webpackMajorVersion = webpackMajorVersion,
rules = project.objects.webpackRulesContainer(),
)
init {
@@ -29,6 +29,9 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.distsDirectory
import org.jetbrains.kotlin.gradle.report.BuildMetricsReporterService
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWebpackRulesContainer
import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl
import org.jetbrains.kotlin.gradle.targets.js.dsl.WebpackRulesDsl.Companion.webpackRulesContainer
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
@@ -47,7 +50,7 @@ constructor(
@Transient
override val compilation: KotlinJsCompilation,
objects: ObjectFactory
) : DefaultTask(), RequiresNpmDependencies {
) : DefaultTask(), RequiresNpmDependencies, WebpackRulesDsl {
@Transient
private val nodeJs = NodeJsRootPlugin.apply(project.rootProject)
private val versions = nodeJs.versions
@@ -62,6 +65,9 @@ constructor(
open val fileResolver: FileResolver
get() = injected
override val rules: KotlinWebpackRulesContainer =
project.objects.webpackRulesContainer()
@get:Inject
open val execHandleFactory: ExecHandleFactory
get() = injected
@@ -204,9 +210,6 @@ constructor(
@Input
var sourceMaps: Boolean = true
@Nested
val cssSupport: KotlinWebpackCssSupport = KotlinWebpackCssSupport()
@Input
@Optional
var devServer: KotlinWebpackConfig.DevServer? = null
@@ -219,7 +222,9 @@ constructor(
var generateConfigOnly: Boolean = false
@Nested
val synthConfig = KotlinWebpackConfig()
val synthConfig = KotlinWebpackConfig(
rules = project.objects.webpackRulesContainer(),
)
@Input
val webpackMajorVersion = PropertiesProvider(project).webpackMajorVersion
@@ -249,7 +254,7 @@ constructor(
outputFileName = outputFileName,
configDirectory = configDirectory,
bundleAnalyzerReportDir = if (!forNpmDependencies && report) reportDir else null,
cssSupport = cssSupport,
rules = rules,
devServer = devServer,
devtool = devtool,
sourceMaps = sourceMaps,
@@ -8,6 +8,8 @@
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
@@ -15,10 +17,9 @@ import org.gradle.api.tasks.Optional
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.appendConfigsFromDir
import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinWebpackRulesContainer
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.KotlinWebpackCssMode.EXTRACT
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssMode.IMPORT
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssMode.INLINE
import org.jetbrains.kotlin.gradle.targets.js.webpack.WebpackMajorVersion.Companion.choose
import org.jetbrains.kotlin.gradle.utils.appendLine
import java.io.File
@@ -51,7 +52,7 @@ data class KotlinWebpackConfig(
@Input
var experiments: MutableSet<String> = mutableSetOf(),
@Nested
var cssSupport: KotlinWebpackCssSupport = KotlinWebpackCssSupport(),
override val rules: KotlinWebpackRulesContainer,
@Input
@Optional
var devtool: String? = WebpackDevtool.EVAL_SOURCE_MAP,
@@ -70,7 +71,7 @@ data class KotlinWebpackConfig(
var resolveFromModulesFirst: Boolean = false,
@Input
val webpackMajorVersion: WebpackMajorVersion = WebpackMajorVersion.V5
) {
) : WebpackRulesDsl {
@get:Input
@get:Optional
val entryInput: String?
@@ -135,18 +136,11 @@ data class KotlinWebpackConfig(
)
}
if (!cssSupport.enabled || cssSupport.rules.isEmpty()) return@also
it.add(versions.cssLoader)
cssSupport.rules.forEach { rule ->
when (rule.mode) {
EXTRACT -> it.add(versions.miniCssExtractPlugin)
INLINE -> it.add(versions.styleLoader)
IMPORT -> it.add(versions.toStringLoader)
else -> cssError()
rules.forEach { rule ->
if (rule.active) {
it.addAll(rule.dependencies(versions))
}
}
}
enum class Mode(val code: String) {
@@ -215,7 +209,11 @@ data class KotlinWebpackConfig(
appendDevServer()
appendReport()
appendProgressReporter()
appendCssSupport()
rules.forEach { rule ->
if (rule.active) {
with(rule) { appendToWebpackConfig() }
}
}
appendErrorPlugin()
appendFromConfigDir()
appendEvaluatedFileReport()
@@ -363,109 +361,6 @@ data class KotlinWebpackConfig(
)
}
private fun Appendable.appendCssSupport() {
if (!cssSupport.enabled || cssSupport.rules.isEmpty())
return
appendLine(
"""
// css settings
;(function(config) {
""".trimIndent()
)
val extractedCss =
"""
| const MiniCssExtractPlugin = require('mini-css-extract-plugin');
| use.unshift({
| loader: MiniCssExtractPlugin.loader,
| options: {}
| })
| config.plugins.push(new MiniCssExtractPlugin())
""".trimMargin()
val inlinedCss =
"""
| use.unshift({
| loader: 'style-loader',
| options: {}
| })
|
""".trimMargin()
val importedCss =
"""
| use.unshift({
| loader: 'to-string-loader',
| options: {}
| })
|
""".trimMargin()
cssSupport.rules.forEach { rule ->
appendLine(
"""
| ;(function(config) {
""".trimMargin()
)
appendLine(
"""
| const use = [
| {
| loader: 'css-loader',
| options: {},
| }
| ]
""".trimMargin()
)
when (rule.mode) {
EXTRACT -> appendLine(extractedCss)
INLINE -> appendLine(inlinedCss)
IMPORT -> appendLine(importedCss)
else -> cssError()
}
val excluded = rule.exclude.let {
if (it.isNotEmpty()) {
"[${it.joinToString()}]"
} else null
}
val included = rule.include.let {
if (it.isNotEmpty()) {
"[${it.joinToString()}]"
} else null
}
appendLine(
"""
| config.module.rules.push({
| test: /\.css${'$'}/,
| use: use,
| ${excluded?.let { "exclude: $it," } ?: ""}
| ${included?.let { "include: $it" } ?: ""}
| })
""".trimMargin()
)
appendLine(
"""
| })(config);
""".trimMargin()
)
}
appendLine(
"""
})(config);
""".trimIndent()
)
}
private fun Appendable.appendErrorPlugin() {
//language=ES6
appendLine(
@@ -525,18 +420,6 @@ data class KotlinWebpackConfig(
)
}
private fun cssError() {
throw IllegalStateException(
"""
Possible values for cssSupport.mode:
- EXTRACT
- INLINE
- IMPORT
""".trimIndent()
)
}
private fun json(obj: Any) = StringWriter().also {
GsonBuilder().setPrettyPrinting().create().toJson(obj, it)
}.toString()
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.webpack
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssMode.EXTRACT
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssMode.IMPORT
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssMode.INLINE
import javax.inject.Inject
@Deprecated(
message = "Renamed to KotlinWebpackCssRule", replaceWith = ReplaceWith(
expression = "KotlinWebpackCssRule",
imports = arrayOf("org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssRule")
)
)
typealias KotlinWebpackCssSupport = KotlinWebpackCssRule
@Suppress("LeakingThis")
abstract class KotlinWebpackCssRule @Inject constructor(name: String) : KotlinWebpackRule(name) {
@get:Input
abstract val mode: Property<String>
init {
mode.convention(INLINE)
test.convention("/\\.css\$/")
}
override fun validate(): Boolean {
if (mode.get() !in arrayOf(EXTRACT, INLINE, IMPORT)) {
error(
"""
Possible values for cssSupport.mode:
- EXTRACT
- INLINE
- IMPORT
""".trimIndent()
)
}
return true
}
override fun dependencies(versions: NpmVersions): Collection<RequiredKotlinJsDependency> {
return mutableListOf<RequiredKotlinJsDependency>().apply {
add(versions.cssLoader)
when (mode.get()) {
EXTRACT -> add(versions.miniCssExtractPlugin)
INLINE -> add(versions.styleLoader)
IMPORT -> add(versions.toStringLoader)
}
}
}
override fun loaders(): List<Loader> = when (mode.get()) {
EXTRACT -> listOf(
Loader(
loader = "MiniCssExtractPlugin.loader",
prerequisites = listOf(
"const MiniCssExtractPlugin = require('mini-css-extract-plugin');",
"config.plugins.push(new MiniCssExtractPlugin())"
)
),
)
INLINE -> listOf(Loader(loader = "'style-loader'"))
IMPORT -> listOf(Loader(loader = "'to-string-loader'"))
else -> listOf()
} + Loader(loader = "'css-loader'")
}
object KotlinWebpackCssMode {
const val EXTRACT = "extract"
const val INLINE = "inline"
const val IMPORT = "import"
}
@@ -1,55 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.webpack
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Nested
import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackCssMode.INLINE
data class KotlinWebpackCssSupport(
@Input
var enabled: Boolean = false,
@Nested
var rules: MutableList<KotlinWebpackCssRule> = mutableListOf(
KotlinWebpackCssRule()
)
) {
@get:Internal
var mode: String
get() =
rules
.singleOrNull()
?.mode ?: singleRuleError()
set(value) {
rules.singleOrNull()
?.let {
it.mode = value
} ?: singleRuleError()
}
private fun singleRuleError(): Nothing {
error("CSS mode shortcut can be applied only with one css rule")
}
}
data class KotlinWebpackCssRule(
@Input
var mode: String = INLINE,
@Input
var include: MutableList<String> = mutableListOf(),
@Input
var exclude: MutableList<String> = mutableListOf()
)
object KotlinWebpackCssMode {
const val EXTRACT = "extract"
const val INLINE = "inline"
const val IMPORT = "import"
}
@@ -0,0 +1,135 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.webpack
import com.google.gson.GsonBuilder
import org.gradle.api.Named
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import org.jetbrains.kotlin.gradle.utils.appendLine
import java.io.StringWriter
import javax.inject.Inject
@Suppress("LeakingThis")
abstract class KotlinWebpackRule @Inject constructor(private val name: String) : Named {
@get:Input
abstract val enabled: Property<Boolean>
/**
* Raw rule `test` field value. Needs to be wrapped in quotes when using string notation.
*/
@get:Input
abstract val test: Property<String>
@get:Input
abstract val include: ListProperty<String>
@get:Input
abstract val exclude: ListProperty<String>
@get:Input
protected open val description: String
get() = (this::class.simpleName?.removeSuffix("_Decorated") ?: "KotlinWebpackRule") + "[${getName()}]"
init {
enabled.convention(false)
}
/**
* Validates the rule state just before it getting applied.
* Returning false will skip the rule silently. To terminate the build instead, throw an error.
*/
open fun validate(): Boolean = true
/**
* Provides a list of required npm dependencies for the rule to function.
*/
open fun dependencies(versions: NpmVersions): Collection<RequiredKotlinJsDependency> = listOf()
/**
* Provides a loaders sequence to apply to the rule.
*/
protected abstract fun loaders(): List<Loader>
@get:Internal
internal val active: Boolean get() = enabled.get() && validate()
internal fun Appendable.appendToWebpackConfig() {
appendLine(
"""
// $description
;(function(config) {
""".trimIndent()
)
val loaders = loaders()
loaders.flatMap(Loader::prerequisites).forEach(::appendLine)
val use = loaders.joinToString(
separator = ",",
prefix = "[",
postfix = "]",
) {
"""
{
loader: ${it.loader},
options: ${json(it.options)}
}
""".trimIndent()
}
appendLine(
"""
const use = $use
""".trimIndent()
)
val excluded = exclude.get().takeIf(List<*>::isNotEmpty)
?.joinToString(separator = ",", prefix = "[", postfix = "]") ?: "undefined"
val included = include.get().takeIf(List<*>::isNotEmpty)
?.joinToString(separator = ",", prefix = "[", postfix = "]") ?: "undefined"
appendLine(
"""
config.module.rules.push({
test: ${test.get()},
use: use,
exclude: $excluded,
include: $included,
})
""".trimIndent()
)
appendLine(
"""
})(config);
""".trimIndent()
)
}
protected fun json(obj: Any) = StringWriter().also {
GsonBuilder().setPrettyPrinting().create().toJson(obj, it)
}.toString()
@Internal
override fun getName(): String = name
override fun equals(other: Any?): Boolean = other is KotlinWebpackRule && getName() == other.getName()
override fun hashCode(): Int = getName().hashCode()
data class Loader(
/**
* Raw `loader` field value. Needs to be wrapped in quotes if using string notation.
*/
val loader: String,
/**
* Loader options map if any. Will be converted to json object via Gson.
*/
val options: Map<String, Any?> = mapOf(),
/**
* Any prerequisite code to be added before building the loader object.
*/
val prerequisites: List<String> = listOf(),
)
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.webpack
import org.jetbrains.kotlin.gradle.targets.js.NpmVersions
import org.jetbrains.kotlin.gradle.targets.js.RequiredKotlinJsDependency
import javax.inject.Inject
@Suppress("LeakingThis")
abstract class KotlinWebpackScssRule @Inject constructor(name: String) : KotlinWebpackCssRule(name) {
init {
test.convention("/\\.(scss|sass)\$/")
}
override fun dependencies(versions: NpmVersions): Collection<RequiredKotlinJsDependency> {
return super.dependencies(versions) + versions.sass + versions.sassLoader
}
override fun loaders(): List<Loader> {
return super.loaders() + Loader(loader = "'sass-loader'")
}
}
@@ -9,4 +9,7 @@ import org.gradle.api.Named
import org.gradle.api.model.ObjectFactory
internal inline fun <reified T : Named> ObjectFactory.named(name: String): T =
named(T::class.java, name)
named(T::class.java, name)
internal inline fun <reified T : Named> ObjectFactory.newInstance(vararg args: Any?): T =
newInstance(T::class.java, *args)