[JS, Wizard] Add react application template

^KT-41417 fixed
This commit is contained in:
Ilya Goncharov
2020-08-24 17:07:51 +03:00
parent 96e1b1c0e0
commit 01064a4e46
7 changed files with 292 additions and 200 deletions
@@ -158,6 +158,10 @@ module.template.js.simple.run.configuration.dev=BrowserDevelopmentRun in continu
module.template.js.simple.run.configuration.prod=BrowserProductionRun in continuous mode
module.template.simple.use.kotlinx.html=Use kotlinx.html
module.template.js.react.title=React Application
module.template.js.react.description=React Application
module.template.react.use.styled.components=Use styled-components
module.template.simple.nodejs.title=Node.JS Application
module.template.simple.nodejs.description=A blank application targeting Node.js
module.template.simple.nodejs.use.kotlinx.nodejs=Use experimental Node.js API (kotlinx-nodejs)
@@ -0,0 +1,183 @@
/*
* 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.tools.projectWizard.templates
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.tools.projectWizard.WizardGradleRunConfiguration
import org.jetbrains.kotlin.tools.projectWizard.WizardRunConfiguration
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.DefaultTargetConfigurationIR
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.*
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.TemplateInterceptor
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.interceptTemplate
abstract class JsClientTemplate : Template() {
override val moduleTypes: Set<ModuleType> = setOf(ModuleType.js)
override fun isApplicableTo(
reader: Reader,
module: Module
): Boolean = when (module.configurator) {
JsBrowserTargetConfigurator -> true
BrowserJsSinglePlatformModuleConfigurator -> {
with(reader) {
inContextOfModuleConfigurator(module, module.configurator) {
JSConfigurator.kind.reference.notRequiredSettingValue == JsTargetKind.APPLICATION
}
}
}
else -> false
}
override fun Reader.createRunConfigurations(module: ModuleIR): List<WizardRunConfiguration> = buildList {
if (module.originalModule.kind == ModuleKind.singleplatformJsBrowser) {
+WizardGradleRunConfiguration(
org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle.message("module.template.js.simple.run.configuration.dev"),
"browserDevelopmentRun",
listOf("--continuous")
)
+WizardGradleRunConfiguration(
org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle.message("module.template.js.simple.run.configuration.prod"),
"browserProductionRun",
listOf("--continuous")
)
}
}
override fun createInterceptors(module: ModuleIR): List<TemplateInterceptor> = buildList {
+interceptTemplate(KtorServerTemplate()) {
applicableIf { buildFileIR ->
val tasks = buildFileIR.irsOfTypeOrNull<GradleConfigureTaskIR>() ?: return@applicableIf false
tasks.none { it.taskAccess.safeAs<GradleByNameTaskAccessIR>()?.name?.endsWith("Jar") == true }
}
interceptAtPoint(template.routes) { value ->
if (value.isNotEmpty()) return@interceptAtPoint value
buildList {
+value
+"""
static("/static") {
resources()
}
""".trimIndent()
}
}
interceptAtPoint(template.imports) { value ->
if (value.isNotEmpty()) return@interceptAtPoint value
buildList {
+value
+"io.ktor.http.content.resources"
+"io.ktor.http.content.static"
}
}
interceptAtPoint(template.elements) { value ->
if (value.isNotEmpty()) return@interceptAtPoint value
buildList {
+value
+"""
div {
id = "root"
}
""".trimIndent()
+"""script(src = "/static/$JS_OUTPUT_FILE_NAME") {}"""
}
}
transformBuildFile { buildFileIR ->
val jsSourcesetName = module.safeAs<MultiplatformModuleIR>()?.name ?: return@transformBuildFile null
val jvmTarget = buildFileIR.targets.firstOrNull { target ->
target.safeAs<DefaultTargetConfigurationIR>()?.targetAccess?.type == ModuleSubType.jvm
} as? DefaultTargetConfigurationIR ?: return@transformBuildFile null
val jvmTargetName = jvmTarget.targetName
val webPackTaskName = "$jsSourcesetName$WEBPACK_TASK_SUFFIX"
val jvmJarTaskAccess = GradleByNameTaskAccessIR("${jvmTargetName}Jar", "Jar")
val jvmJarTaskConfiguration = run {
val webPackTaskVariable = CreateGradleValueIR(
webPackTaskName,
GradleByNameTaskAccessIR(webPackTaskName, WEBPACK_TASK_CLASS)
)
val from = GradleCallIr(
"from",
listOf(
GradleNewInstanceCall(
"File",
listOf(
GradlePropertyAccessIR("$webPackTaskName.destinationDirectory"),
GradlePropertyAccessIR("$webPackTaskName.outputFileName")
)
)
)
)
GradleConfigureTaskIR(
jvmJarTaskAccess,
dependsOn = listOf(GradleByNameTaskAccessIR(webPackTaskName)),
irs = listOf(
webPackTaskVariable,
from
)
)
}
val runTaskConfiguration = run {
val taskAccess = GradleByNameTaskAccessIR("run", "JavaExec")
val classpath = GradleCallIr("classpath", listOf(jvmJarTaskAccess))
GradleConfigureTaskIR(
taskAccess,
dependsOn = listOf(jvmJarTaskAccess),
irs = listOf(classpath)
)
}
buildFileIR.withIrs(jvmJarTaskConfiguration, runTaskConfiguration)
}
}
}
override fun Writer.getIrsToAddToBuildFile(module: ModuleIR): List<BuildSystemIR> = buildList {
+RepositoryIR(DefaultRepository.JCENTER)
if (module is MultiplatformModuleIR) {
+GradleImportIR("org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack")
val taskAccessIR = GradleByNameTaskAccessIR(
"${module.name}$WEBPACK_TASK_SUFFIX",
WEBPACK_TASK_CLASS
)
+GradleConfigureTaskIR(
taskAccessIR,
irs = listOf(
GradleAssignmentIR(
"outputFileName", GradleStringConstIR(JS_OUTPUT_FILE_NAME)
)
)
)
}
}
protected fun hasKtorServNeighbourTarget(module: ModuleIR) =
module.safeAs<MultiplatformModuleIR>()
?.neighbourTargetModules()
.orEmpty()
.any { it.template is KtorServerTemplate }
companion object {
@NonNls
private const val JS_OUTPUT_FILE_NAME = "output.js"
@NonNls
private const val WEBPACK_TASK_CLASS = "KotlinWebpack"
@NonNls
private const val WEBPACK_TASK_SUFFIX = "BrowserProductionWebpack"
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2010-2019 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.tools.projectWizard.templates
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle
import org.jetbrains.kotlin.tools.projectWizard.Versions
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.TemplateSetting
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Module
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repositories
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
class ReactJsClientTemplate : JsClientTemplate() {
override val title: String = KotlinNewProjectWizardBundle.message("module.template.js.react.title")
override val description: String = KotlinNewProjectWizardBundle.message("module.template.js.react.description")
@NonNls
override val id: String = "reactJsClient"
val useStyledComponents by booleanSetting(
KotlinNewProjectWizardBundle.message("module.template.react.use.styled.components"),
GenerationPhase.PROJECT_GENERATION
) {
defaultValue = value(false)
}
override val settings: List<TemplateSetting<*, *>> = listOf(useStyledComponents)
override fun Writer.getRequiredLibraries(module: ModuleIR): List<DependencyIR> = withSettingsOf(module.originalModule) {
buildList {
val kotlinVersion = KotlinPlugin.version.propertyValue
+Dependencies.KOTLIN_REACT(kotlinVersion.version)
+Dependencies.KOTLIN_REACT_DOM(kotlinVersion.version)
if (useStyledComponents.reference.settingValue) {
+Dependencies.KOTLIN_STYLED(kotlinVersion.version)
}
}
}
override fun Reader.getFileTemplates(module: ModuleIR): List<FileTemplateDescriptorWithPath> =
withSettingsOf(module.originalModule) {
buildList {
val hasKtorServNeighbourTarget = hasKtorServNeighbourTarget(module)
if (!hasKtorServNeighbourTarget) {
+(FileTemplateDescriptor("$id/index.html.vm") asResourceOf SourcesetType.main)
}
+(FileTemplateDescriptor("$id/reactClient.kt.vm", "client.kt".asPath()) asSrcOf SourcesetType.main)
+(FileTemplateDescriptor("$id/reactComponent.kt.vm", "welcome.kt".asPath()) asSrcOf SourcesetType.main)
if (useStyledComponents.reference.settingValue) {
+(FileTemplateDescriptor("$id/WelcomeStyles.kt.vm") asSrcOf SourcesetType.main)
}
}
}
override fun Reader.getAdditionalSettings(module: Module): Map<String, Any> = withSettingsOf(module) {
mapOf("useStyledComponents" to (useStyledComponents.reference.settingValue))
}
private object Dependencies {
val KOTLIN_REACT = { kotlinVersion: Version ->
ArtifactBasedLibraryDependencyIR(
MavenArtifact(Repositories.KOTLIN_JS_WRAPPERS_BINTRAY, "org.jetbrains", "kotlin-react"),
Versions.JS_WRAPPERS.KOTLIN_REACT(kotlinVersion),
DependencyType.MAIN
)
}
val KOTLIN_REACT_DOM = { kotlinVersion: Version ->
ArtifactBasedLibraryDependencyIR(
MavenArtifact(Repositories.KOTLIN_JS_WRAPPERS_BINTRAY, "org.jetbrains", "kotlin-react-dom"),
Versions.JS_WRAPPERS.KOTLIN_REACT_DOM(kotlinVersion),
DependencyType.MAIN
)
}
val KOTLIN_STYLED = { kotlinVersion: Version ->
ArtifactBasedLibraryDependencyIR(
MavenArtifact(Repositories.KOTLIN_JS_WRAPPERS_BINTRAY, "org.jetbrains", "kotlin-styled"),
Versions.JS_WRAPPERS.KOTLIN_STYLED(kotlinVersion),
DependencyType.MAIN
)
}
}
}
@@ -9,48 +9,28 @@ package org.jetbrains.kotlin.tools.projectWizard.templates
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.tools.projectWizard.KotlinNewProjectWizardBundle
import org.jetbrains.kotlin.tools.projectWizard.Versions
import org.jetbrains.kotlin.tools.projectWizard.WizardGradleRunConfiguration
import org.jetbrains.kotlin.tools.projectWizard.WizardRunConfiguration
import org.jetbrains.kotlin.tools.projectWizard.core.*
import org.jetbrains.kotlin.tools.projectWizard.core.Reader
import org.jetbrains.kotlin.tools.projectWizard.core.Writer
import org.jetbrains.kotlin.tools.projectWizard.core.asPath
import org.jetbrains.kotlin.tools.projectWizard.core.buildList
import org.jetbrains.kotlin.tools.projectWizard.core.entity.settings.TemplateSetting
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.*
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.gradle.multiplatform.DefaultTargetConfigurationIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ArtifactBasedLibraryDependencyIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.DependencyIR
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.DependencyType
import org.jetbrains.kotlin.tools.projectWizard.ir.buildsystem.ModuleIR
import org.jetbrains.kotlin.tools.projectWizard.library.MavenArtifact
import org.jetbrains.kotlin.tools.projectWizard.moduleConfigurators.*
import org.jetbrains.kotlin.tools.projectWizard.phases.GenerationPhase
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.KotlinPlugin
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleSubType
import org.jetbrains.kotlin.tools.projectWizard.plugins.kotlin.ModuleType
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.*
import org.jetbrains.kotlin.tools.projectWizard.settings.version.Version
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.TemplateInterceptor
import org.jetbrains.kotlin.tools.projectWizard.transformers.interceptors.interceptTemplate
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.Repositories
import org.jetbrains.kotlin.tools.projectWizard.settings.buildsystem.SourcesetType
class SimpleJsClientTemplate : Template() {
class SimpleJsClientTemplate : JsClientTemplate() {
override val title: String = KotlinNewProjectWizardBundle.message("module.template.js.simple.title")
override val description: String = KotlinNewProjectWizardBundle.message("module.template.js.simple.description")
override val moduleTypes: Set<ModuleType> = setOf(ModuleType.js)
@NonNls
override val id: String = "simpleJsClient"
override fun isApplicableTo(
reader: Reader,
module: Module
): Boolean = when (module.configurator) {
JsBrowserTargetConfigurator -> true
BrowserJsSinglePlatformModuleConfigurator -> {
with(reader) {
inContextOfModuleConfigurator(module, module.configurator) {
JSConfigurator.kind.reference.notRequiredSettingValue == JsTargetKind.APPLICATION
}
}
}
else -> false
}
val useKotlinxHtml by booleanSetting(
KotlinNewProjectWizardBundle.message("module.template.simple.use.kotlinx.html"),
GenerationPhase.PROJECT_GENERATION
@@ -60,21 +40,6 @@ class SimpleJsClientTemplate : Template() {
override val settings: List<TemplateSetting<*, *>> = listOf(useKotlinxHtml)
override fun Reader.createRunConfigurations(module: ModuleIR): List<WizardRunConfiguration> = buildList {
if (module.originalModule.kind == ModuleKind.singleplatformJsBrowser) {
+WizardGradleRunConfiguration(
KotlinNewProjectWizardBundle.message("module.template.js.simple.run.configuration.dev"),
"browserDevelopmentRun",
listOf("--continuous")
)
+WizardGradleRunConfiguration(
KotlinNewProjectWizardBundle.message("module.template.js.simple.run.configuration.prod"),
"browserProductionRun",
listOf("--continuous")
)
}
}
override fun Writer.getRequiredLibraries(module: ModuleIR): List<DependencyIR> = withSettingsOf(module.originalModule) {
buildList {
if (useKotlinxHtml.reference.settingValue()) {
@@ -87,16 +52,10 @@ class SimpleJsClientTemplate : Template() {
}
}
override fun Reader.getFileTemplates(module: ModuleIR): List<FileTemplateDescriptorWithPath> =
withSettingsOf(module.originalModule) {
buildList {
val hasKtorServNeighbourTarget = module.safeAs<MultiplatformModuleIR>()
?.neighbourTargetModules()
.orEmpty()
.any { module ->
module.template is KtorServerTemplate
}
val hasKtorServNeighbourTarget = hasKtorServNeighbourTarget(module)
if (!hasKtorServNeighbourTarget) {
+(FileTemplateDescriptor("$id/index.html.vm") asResourceOf SourcesetType.main)
}
@@ -106,151 +65,4 @@ class SimpleJsClientTemplate : Template() {
}
}
}
override fun createInterceptors(module: ModuleIR): List<TemplateInterceptor> = buildList {
+interceptTemplate(KtorServerTemplate()) {
applicableIf { buildFileIR ->
val tasks = buildFileIR.irsOfTypeOrNull<GradleConfigureTaskIR>() ?: return@applicableIf false
tasks.none { it.taskAccess.safeAs<GradleByNameTaskAccessIR>()?.name?.endsWith("Jar") == true }
}
interceptAtPoint(template.routes) { value ->
if (value.isNotEmpty()) return@interceptAtPoint value
buildList {
+value
+"""
static("/static") {
resources()
}
""".trimIndent()
}
}
interceptAtPoint(template.imports) { value ->
if (value.isNotEmpty()) return@interceptAtPoint value
buildList {
+value
+"io.ktor.http.content.resources"
+"io.ktor.http.content.static"
}
}
interceptAtPoint(template.elements) { value ->
if (value.isNotEmpty()) return@interceptAtPoint value
buildList {
+value
+"""
div {
id = "root"
}
""".trimIndent()
+"""script(src = "/static/$JS_OUTPUT_FILE_NAME") {}"""
}
}
transformBuildFile { buildFileIR ->
val jsSourcesetName = module.safeAs<MultiplatformModuleIR>()?.name ?: return@transformBuildFile null
val jvmTarget = buildFileIR.targets.firstOrNull { target ->
target.safeAs<DefaultTargetConfigurationIR>()?.targetAccess?.type == ModuleSubType.jvm
} as? DefaultTargetConfigurationIR ?: return@transformBuildFile null
val jvmTargetName = jvmTarget.targetName
val webPackTaskName = "$jsSourcesetName$WEBPACK_TASK_SUFFIX"
val jvmJarTaskAccess = GradleByNameTaskAccessIR("${jvmTargetName}Jar", "Jar")
val jvmJarTaskConfiguration = run {
val webPackTaskVariable = CreateGradleValueIR(
webPackTaskName,
GradleByNameTaskAccessIR(webPackTaskName, WEBPACK_TASK_CLASS)
)
val from = GradleCallIr(
"from",
listOf(
GradleNewInstanceCall(
"File",
listOf(
GradlePropertyAccessIR("$webPackTaskName.destinationDirectory"),
GradlePropertyAccessIR("$webPackTaskName.outputFileName")
)
)
)
)
GradleConfigureTaskIR(
jvmJarTaskAccess,
dependsOn = listOf(GradleByNameTaskAccessIR(webPackTaskName)),
irs = listOf(
webPackTaskVariable,
from
)
)
}
val runTaskConfiguration = run {
val taskAccess = GradleByNameTaskAccessIR("run", "JavaExec")
val classpath = GradleCallIr("classpath", listOf(jvmJarTaskAccess))
GradleConfigureTaskIR(
taskAccess,
dependsOn = listOf(jvmJarTaskAccess),
irs = listOf(classpath)
)
}
buildFileIR.withIrs(jvmJarTaskConfiguration, runTaskConfiguration)
}
}
}
override fun Writer.getIrsToAddToBuildFile(module: ModuleIR): List<BuildSystemIR> = buildList {
+RepositoryIR(DefaultRepository.JCENTER)
if (module is MultiplatformModuleIR) {
+GradleImportIR("org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpack")
val taskAccessIR = GradleByNameTaskAccessIR(
"${module.name}$WEBPACK_TASK_SUFFIX",
WEBPACK_TASK_CLASS
)
+GradleConfigureTaskIR(
taskAccessIR,
irs = listOf(
GradleAssignmentIR(
"outputFileName", GradleStringConstIR(JS_OUTPUT_FILE_NAME)
)
)
)
}
}
companion object {
@NonNls
private const val JS_OUTPUT_FILE_NAME = "output.js"
@NonNls
private const val WEBPACK_TASK_CLASS = "KotlinWebpack"
@NonNls
private const val WEBPACK_TASK_SUFFIX = "BrowserProductionWebpack"
}
private object Dependencies {
val KOTLIN_REACT = { kotlinVersion: Version ->
ArtifactBasedLibraryDependencyIR(
MavenArtifact(Repositories.KOTLIN_JS_WRAPPERS_BINTRAY, "org.jetbrains", "kotlin-react"),
Versions.JS_WRAPPERS.KOTLIN_REACT(kotlinVersion),
DependencyType.MAIN
)
}
val KOTLIN_REACT_DOM = { kotlinVersion: Version ->
ArtifactBasedLibraryDependencyIR(
MavenArtifact(Repositories.KOTLIN_JS_WRAPPERS_BINTRAY, "org.jetbrains", "kotlin-react-dom"),
Versions.JS_WRAPPERS.KOTLIN_REACT_DOM(kotlinVersion),
DependencyType.MAIN
)
}
val KOTLIN_STYLED = { kotlinVersion: Version ->
ArtifactBasedLibraryDependencyIR(
MavenArtifact(Repositories.KOTLIN_JS_WRAPPERS_BINTRAY, "org.jetbrains", "kotlin-styled"),
Versions.JS_WRAPPERS.KOTLIN_STYLED(kotlinVersion),
DependencyType.MAIN
)
}
}
}