KVision applications are built with Gradle . Although it may be possible to manage all the dependencies with other tools (like Maven ), the supported way is to use Kotlin Frontend Plugin with Gradle.
KVision applications have dependencies on some Kotlin libraries as well as a few npm libraries (which of course depend on other libraries). The Kotlin Frontend Plugin provides an easy way to gather dependencies, pack bundles (via webpack ) and test the application using Karma . By using Gradle continuous build, you also can get hot module replacement feature (apply code changes in the browser on the fly).
Requirements
To build a typical KVision application you should have some tools installed on your machine and available on the system PATH:
NodeJS with NPM package manager
Git (with additional UNIX tools if using Windows)
Note: Make sure you are building KVision applications on the local file system.
Creating a new application
The recommended way to create a new application is to download and copy the KVision template project, available on GitHub.
build.gradle.kts
The build.gradle.kts
file is responsible for the definition of the build process. It declares necessary repositories (e.g. external bintray repositories) and all required dependencies. It declares zip
task, typically used to build distribution pack.
Copy import org.jetbrains.kotlin.gradle.frontend.KotlinFrontendExtension
import org.jetbrains.kotlin.gradle.frontend.npm.NpmExtension
import org.jetbrains.kotlin.gradle.frontend.webpack.WebPackExtension
import org.jetbrains.kotlin.gradle.targets.js.nodejs.nodeJs
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinJsDce
buildscript {
extra. set ( "production" , ( findProperty ( "prod" ) ?: findProperty ( "production" ) ?: "false" ) == "true" )
}
plugins {
val kotlinVersion: String by System . getProperties ()
id ( "kotlinx-serialization" ) version kotlinVersion
id ( "kotlin2js" ) version kotlinVersion
id ( "kotlin-dce-js" ) version kotlinVersion
kotlin ( "frontend" ) version System. getProperty ( "frontendPluginVersion" )
}
version = "1.0.0-SNAPSHOT"
group = "com.example"
repositories {
jcenter ()
maven { url = uri ( "https://dl.bintray.com/kotlin/kotlin-eap" ) }
maven { url = uri ( "https://kotlin.bintray.com/kotlinx" ) }
maven { url = uri ( "https://dl.bintray.com/kotlin/kotlin-js-wrappers" ) }
maven { url = uri ( "https://dl.bintray.com/gbaldeck/kotlin" ) }
maven { url = uri ( "https://dl.bintray.com/rjaros/kotlin" ) }
mavenLocal ()
}
// Versions
val kotlinVersion: String by System . getProperties ()
val kvisionVersion: String by project
// Custom Properties
val webDir = file ( "src/main/web" )
val isProductionBuild = project.extra. get ( "production" ) as Boolean
dependencies {
implementation ( kotlin ( "stdlib-js" ))
implementation ( "pl.treksoft:kvision:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-bootstrap:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-select:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-datetime:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-spinner:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-richtext:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-upload:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-handlebars:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-i18n:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-datacontainer:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-dialog:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-redux:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-chart:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-tabulator:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-pace:$kvisionVersion" )
implementation ( "pl.treksoft:kvision-moment:$kvisionVersion" )
testImplementation ( kotlin ( "test-js" ))
}
kotlinFrontend {
sourceMaps = ! isProductionBuild
npm {
devDependency ( "po2json" )
devDependency ( "grunt" )
devDependency ( "grunt-pot" )
}
webpackBundle {
bundleName = "main"
sourceMapEnabled = false
port = 3000
proxyUrl = "http://localhost:8080"
contentPath = webDir
mode = if (isProductionBuild) "production" else "development"
}
define ( "PRODUCTION" , isProductionBuild)
}
sourceSets[ "main" ].resources. srcDir (webDir)
tasks {
withType < Kotlin2JsCompile > {
kotlinOptions {
moduleKind = "umd"
sourceMap = ! isProductionBuild
metaInfo = true
if ( ! isProductionBuild) {
sourceMapEmbedSources = "always"
}
}
}
withType < KotlinJsDce > {
dceOptions {
devMode = ! isProductionBuild
}
inputs. property ( "production" , isProductionBuild)
doFirst {
destinationDir. deleteRecursively ()
}
doLast {
copy {
file ( "$buildDir/node_modules_imported/" ). listFiles ()?. forEach {
if (it.isDirectory && it.name. startsWith ( "kvision" )) {
from (it) {
include ( "css/**" )
include ( "img/**" )
include ( "js/**" )
}
}
}
into ( file (buildDir.path + "/kotlin-js-min/main" ))
}
}
}
create ( "generateGruntfile" ) {
outputs. file ( "$buildDir/Gruntfile.js" )
doLast {
file ( "$buildDir/Gruntfile.js" ). run {
writeText (
"""
module.exports = function (grunt) {
grunt.initConfig({
pot: {
options: {
text_domain: "messages",
dest: "../src/main/resources/i18n/",
keywords: ["tr", "ntr:1,2", "gettext", "ngettext:1,2"],
encoding: "UTF-8"
},
files: {
src: ["../src/main/kotlin/**/*.kt"],
expand: true,
},
}
});
grunt.loadNpmTasks("grunt-pot");
};
""" . trimIndent ()
)
}
}
}
create ( "generatePotFile" , Exec:: class ) {
dependsOn ( "npm-install" , "generateGruntfile" )
workingDir = file ( "$buildDir" )
executable = project.nodeJs.root.nodeCommand
args ( "$buildDir/node_modules/grunt/bin/grunt" , "pot" )
inputs. files (sourceSets[ "main" ].allSource)
outputs. file ( "$projectDir/src/main/resources/i18n/messages.pot" )
}
}
afterEvaluate {
tasks {
getByName ( "processResources" , Copy:: class ) {
dependsOn ( "npm-install" )
exclude ( "**/*.pot" )
doLast ( "Convert PO to JSON" ) {
destinationDir. walkTopDown (). filter {
it.isFile && it.extension == "po"
}. forEach {
exec {
executable = project.nodeJs.root.nodeCommand
args (
"$buildDir/node_modules/po2json/bin/po2json" ,
it.absolutePath,
" ${ it.parent } / ${ it.nameWithoutExtension } .json" ,
"-f" ,
"jed1.x"
)
println ( "Converted ${ it.name } to ${ it.nameWithoutExtension } .json" )
}
it. delete ()
}
}
}
getByName ( "webpack-run" ). dependsOn ( "classes" )
getByName ( "webpack-bundle" ). dependsOn ( "classes" , "runDceKotlinJs" )
create ( "webJar" , Jar:: class ) {
dependsOn ( "webpack-bundle" )
group = "package"
val from = project.tasks[ "webpack-bundle" ].outputs.files + webDir
from (from)
inputs. files (from)
outputs. file (archiveFile)
manifest {
attributes (
mapOf (
"Implementation-Title" to rootProject.name,
"Implementation-Group" to rootProject.group,
"Implementation-Version" to rootProject.version,
"Timestamp" to System. currentTimeMillis ()
)
)
}
}
create ( "zip" , Zip:: class ) {
dependsOn ( "webpack-bundle" )
group = "package"
destinationDirectory. set ( file ( "$buildDir/libs" ))
val from = project.tasks[ "webpack-bundle" ].outputs.files + webDir
from (from)
inputs. files (from)
outputs. file (archiveFile)
}
}
}
fun KotlinFrontendExtension . webpackBundle (block: WebPackExtension .() -> Unit) =
bundle ( "webpack" , delegateClosureOf (block))
fun KotlinFrontendExtension . npm (block: NpmExtension .() -> Unit) = configure (block)
Source code
The source code for the application is contained in src/main
directory. It consists of Kotlin sources in kotlin
directory, optional resources
(e.g. images, CSS files, Handlebars templates, translation files), and main index.html
file in a web
directory.
Test sources are contained in src/test
directory.
Development
To run the application with Gradle continuous build, enter:
Copy ./gradlew -t run (on Linux)
gradlew.bat -t run (on Windows)
After Gradle finishes downloading dependencies and building the application, open http://localhost:3000/ in your favorite browser.
You can import the project in IntelliJ IDEA and open src/main/kotlin/com/example/App.kt
file. You can of course use your favorite text editor.
Add some code inside the start
function:
Copy override fun start (state: Map < String , Any >) {
// ...
root = Root ( "kvapp" ) {
span ( "Hello world!" )
}
}
You should see your changes immediately in the browser.
Production
To build a complete application optimized for production, run:
Copy ./gradlew -Pprod=true clean zip (on Linux)
gradlew.bat -Pprod=true clean zip (on Windows)
The package containing all of application files will be saved as build/libs/template-1.0.0-SNAPSHOT.zip
.