Compare commits
4 Commits
ce92e1fae9
...
cf09799bc6
| Author | SHA1 | Date | |
|---|---|---|---|
| cf09799bc6 | |||
| a856d5e425 | |||
| f56ec93498 | |||
| 66878900f5 |
18
.editorconfig
Normal file
18
.editorconfig
Normal file
@ -0,0 +1,18 @@
|
||||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{kt, kts}]
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
continuation_indent_size=4
|
||||
max_line_length = 120
|
||||
disabled_rules = no-wildcard-imports
|
||||
kotlin_imports_layout = idea
|
||||
37
config.toml
37
config.toml
@ -29,48 +29,47 @@ url = "https://dl.bintray.com/arrow-kt/arrow-kt/"
|
||||
[dependencies.http4k]
|
||||
groupId = "org.http4k"
|
||||
artifactId = "http4k-core"
|
||||
version = "3.261.0"
|
||||
category = "server"
|
||||
version = "3.264.0"
|
||||
category = "http4k"
|
||||
default = true
|
||||
|
||||
[dependencies.http4k-server-jetty]
|
||||
groupId = "org.http4k"
|
||||
artifactId = "http4k-server-jetty"
|
||||
version = "3.261.0"
|
||||
category = "server"
|
||||
version = "3.264.0"
|
||||
category = "http4k"
|
||||
default = true
|
||||
logger = "org.eclipse.jetty"
|
||||
|
||||
[dependencies.http4k-server-apache]
|
||||
groupId = "org.http4k"
|
||||
artifactId = "http4k-server-apache"
|
||||
version = "3.261.0"
|
||||
category = "server"
|
||||
version = "3.264.0"
|
||||
category = "http4k"
|
||||
|
||||
[dependencies.http4k-client-apache]
|
||||
groupId = "org.http4k"
|
||||
artifactId = "http4k-client-apache"
|
||||
version = "3.261.0"
|
||||
category = "server"
|
||||
version = "3.264.0"
|
||||
category = "http4k"
|
||||
|
||||
[dependencies.http4k-format-jackson]
|
||||
groupId = "org.http4k"
|
||||
artifactId = "http4k-format-jackson"
|
||||
version = "3.261.0"
|
||||
category = "server"
|
||||
version = "3.264.0"
|
||||
category = "http4k"
|
||||
|
||||
[dependencies.http4k-format-kotlinx-serialization]
|
||||
groupId = "org.http4k"
|
||||
artifactId = "http4k-format-kotlinx-serialization"
|
||||
version = "3.264.0"
|
||||
category = "http4k"
|
||||
|
||||
[dependencies.http4k-contract]
|
||||
groupId = "org.http4k"
|
||||
artifactId = "http4k-contract"
|
||||
version = "3.261.0"
|
||||
category = "server"
|
||||
|
||||
[dependencies.javalin]
|
||||
groupId = "io.javalin"
|
||||
artifactId = "javalin"
|
||||
version = "3.10.1"
|
||||
category = "server"
|
||||
logger = "org.eclipse.jetty"
|
||||
version = "3.264.0"
|
||||
category = "http4k"
|
||||
|
||||
[dependencies.pebble]
|
||||
groupId = "io.pebbletemplates"
|
||||
|
||||
6
pom.xml
6
pom.xml
@ -10,6 +10,7 @@
|
||||
<maven.compiler.source>11</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<kotlin.version>1.4.10</kotlin.version>
|
||||
<kotlin.code.style>official</kotlin.code.style>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
@ -51,6 +52,11 @@
|
||||
<artifactId>koin-core</artifactId>
|
||||
<version>2.1.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.30</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
module.exports = {
|
||||
purge: {
|
||||
content: [
|
||||
'../main/resources/views/**/*.twig'
|
||||
'../main/resources/views/**/*.twig',
|
||||
'../main/kotlin/starter/PebbleModule.kt',
|
||||
]
|
||||
},
|
||||
theme: {},
|
||||
|
||||
@ -4,9 +4,9 @@ import com.electronwill.nightconfig.core.file.FileConfig
|
||||
import com.electronwill.nightconfig.core.Config as NightConfig
|
||||
|
||||
data class StarterConfig(
|
||||
val dependencies: List<Dependency>,
|
||||
val inputs: List<Input>,
|
||||
val repositories: List<Repository>,
|
||||
val dependencies: List<Dependency>,
|
||||
val inputs: List<Input>,
|
||||
val repositories: List<Repository>,
|
||||
)
|
||||
|
||||
class Config {
|
||||
@ -19,31 +19,30 @@ class Config {
|
||||
cfg.load()
|
||||
|
||||
val dependencies = cfg.configMap("dependencies")
|
||||
.map { (name, values) ->
|
||||
Dependency(
|
||||
name,
|
||||
values["groupId"],
|
||||
values["artifactId"],
|
||||
values["version"],
|
||||
values.getOrElse("default", false),
|
||||
values.getEnumOrElse("category", Category.Other),
|
||||
values.getEnumOrElse("scope", Scope.Compile),
|
||||
values["logger"],
|
||||
values["repository"],
|
||||
)
|
||||
}
|
||||
.map { (name, values) ->
|
||||
Dependency(
|
||||
name,
|
||||
values["groupId"],
|
||||
values["artifactId"],
|
||||
values["version"],
|
||||
values.getOrElse("default", false),
|
||||
values.getEnumOrElse("category", Category.Other),
|
||||
values.getEnumOrElse("scope", Scope.Compile),
|
||||
values["logger"],
|
||||
values["repository"],
|
||||
)
|
||||
}
|
||||
|
||||
val inputs = cfg.configMap("inputs")
|
||||
.map { (name, values) ->
|
||||
Input(name, values["display"], values["default"])
|
||||
}
|
||||
.map { (name, values) ->
|
||||
Input(name, values["display"], values["default"])
|
||||
}
|
||||
|
||||
val repositories = cfg.configMap("repositories")
|
||||
.map { (name, values) ->
|
||||
Repository(name, values["url"])
|
||||
}
|
||||
.map { (name, values) ->
|
||||
Repository(name, values["url"])
|
||||
}
|
||||
|
||||
return StarterConfig(dependencies, inputs, repositories)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,15 +1,34 @@
|
||||
package starter
|
||||
|
||||
import org.http4k.core.then
|
||||
import org.http4k.filter.ServerFilters
|
||||
import org.http4k.routing.ResourceLoader
|
||||
import org.http4k.routing.RoutingHttpHandler
|
||||
import org.http4k.routing.routes
|
||||
import org.http4k.routing.static
|
||||
import org.http4k.server.SunHttp
|
||||
import org.http4k.server.asServer
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import org.koin.dsl.onClose
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import starter.routes.IndexRouteSupplier
|
||||
import starter.routes.RouteSupplier
|
||||
import starter.routes.ZipRouteSupplier
|
||||
import starter.routes.toRouter
|
||||
import starter.templates.*
|
||||
|
||||
val mainModule = module {
|
||||
single(createdAtStart = true) {
|
||||
get<Logger>().info("Starting on http://localhost:7000")
|
||||
get<RoutingHttpHandler>().asServer(SunHttp(7000)).start()
|
||||
} onClose { it?.stop() }
|
||||
|
||||
single { Config().load() }
|
||||
single { PebbleModule().engine() }
|
||||
single { Server(get(), get(), get()) }
|
||||
single { Views(get()) }
|
||||
single { LoggerFactory.getLogger("Starter") }
|
||||
single { Views(get(), get()) }
|
||||
single { ProjectZip(getAll()) }
|
||||
}
|
||||
|
||||
@ -20,10 +39,20 @@ val templateModule = module {
|
||||
single { GitignoreTemplate(get()) } bind Template::class
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val koin = startKoin {
|
||||
modules(mainModule, templateModule)
|
||||
}.koin
|
||||
val server = koin.get<Server>()
|
||||
server.run()
|
||||
val routesModule = module {
|
||||
single { IndexRouteSupplier(get()) } bind RouteSupplier::class
|
||||
single { ZipRouteSupplier(get(), get()) } bind RouteSupplier::class
|
||||
single {
|
||||
ServerFilters.CatchAll().then(
|
||||
routes(
|
||||
static(ResourceLoader.Classpath("/assets")),
|
||||
getAll<RouteSupplier>().toRouter()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
startKoin {
|
||||
modules(mainModule, pebbleModule, templateModule, routesModule)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package starter
|
||||
|
||||
enum class Category {
|
||||
Server, Injection, Database, Serialization, Test, Other
|
||||
Http4k, Injection, Database, Serialization, Test, Other
|
||||
}
|
||||
|
||||
enum class Scope {
|
||||
@ -9,15 +9,15 @@ enum class Scope {
|
||||
}
|
||||
|
||||
data class Dependency(
|
||||
val name: String,
|
||||
val groupId: String,
|
||||
val artifactId: String,
|
||||
val version: String,
|
||||
val default: Boolean,
|
||||
val category: Category,
|
||||
val scope: Scope,
|
||||
val logger: String?,
|
||||
val repository: String?,
|
||||
val name: String,
|
||||
val groupId: String,
|
||||
val artifactId: String,
|
||||
val version: String,
|
||||
val default: Boolean,
|
||||
val category: Category,
|
||||
val scope: Scope,
|
||||
val logger: String?,
|
||||
val repository: String?,
|
||||
)
|
||||
|
||||
data class Repository(val name: String, val url: String)
|
||||
@ -25,9 +25,9 @@ data class Repository(val name: String, val url: String)
|
||||
data class Input(val name: String, val display: String, val value: String? = null)
|
||||
|
||||
data class Project(
|
||||
val name: String,
|
||||
val basePackage: String,
|
||||
val inputs: List<Input>,
|
||||
val dependencies: List<Dependency>,
|
||||
val repositories: List<Repository>,
|
||||
val name: String,
|
||||
val basePackage: String,
|
||||
val inputs: List<Input>,
|
||||
val dependencies: List<Dependency>,
|
||||
val repositories: List<Repository>,
|
||||
)
|
||||
|
||||
@ -1,15 +1,57 @@
|
||||
package starter
|
||||
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.loader.ClasspathLoader
|
||||
import com.mitchellbosecke.pebble.extension.escaper.SafeString
|
||||
import com.mitchellbosecke.pebble.template.EvaluationContext
|
||||
import com.mitchellbosecke.pebble.template.PebbleTemplate
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import starter.utils.PebbleEngineBuilder
|
||||
import starter.utils.PebbleEngineBuilder.CacheType.ConcurrentMap
|
||||
import starter.utils.PebbleFunction
|
||||
|
||||
class PebbleModule {
|
||||
fun engine(): PebbleEngine {
|
||||
val loader = ClasspathLoader()
|
||||
loader.suffix = ".twig"
|
||||
return PebbleEngine.Builder()
|
||||
.loader(loader)
|
||||
.cacheActive(true)
|
||||
.build()
|
||||
class DepAsXmlPebbleFunction : PebbleFunction {
|
||||
override val name = "depAsXml"
|
||||
override fun getArgumentNames() = listOf("dependency")
|
||||
override fun execute(args: Map<String, Any>, self: PebbleTemplate, context: EvaluationContext, lineNumber: Int): Any {
|
||||
val dep = args["dependency"] as Dependency
|
||||
|
||||
fun startTag(name: String): String {
|
||||
@Language("html") @Suppress("UnnecessaryVariable")
|
||||
val result = """<span class="text-gray-700"><</span><span class="text-red-700">$name</span><span class="text-gray-700">></span>"""
|
||||
return result
|
||||
}
|
||||
|
||||
fun endTag(name: String): String {
|
||||
@Language("html") @Suppress("UnnecessaryVariable")
|
||||
val result = """<span class="text-gray-700"></</span><span class="text-red-700">$name</span><span class="text-gray-700">></span>"""
|
||||
return result
|
||||
}
|
||||
|
||||
fun tag(name: String, content: String): String {
|
||||
return """${startTag(name)}$content${endTag(name)}"""
|
||||
}
|
||||
|
||||
val result = """
|
||||
|${startTag("dependency")}
|
||||
| ${tag("groupId", dep.groupId)}
|
||||
| ${tag("artifactId", dep.artifactId)}
|
||||
| ${tag("version", dep.version)}
|
||||
|${endTag("dependency")}
|
||||
""".trimMargin()
|
||||
|
||||
return SafeString(result)
|
||||
}
|
||||
}
|
||||
|
||||
val pebbleModule = module {
|
||||
single { DepAsXmlPebbleFunction() } bind PebbleFunction::class
|
||||
|
||||
single {
|
||||
PebbleEngineBuilder {
|
||||
cache = ConcurrentMap
|
||||
functions(getAll())
|
||||
classPath { suffix = ".twig" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,5 +17,4 @@ class ProjectZip(private val templates: List<Template>) {
|
||||
}
|
||||
return zipOutput.outputStream
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,68 +0,0 @@
|
||||
package starter
|
||||
|
||||
import org.http4k.core.HttpHandler
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.core.body.form
|
||||
import org.http4k.core.body.formAsMap
|
||||
import org.http4k.routing.ResourceLoader
|
||||
import org.http4k.routing.bind
|
||||
import org.http4k.routing.routes
|
||||
import org.http4k.routing.static
|
||||
import org.http4k.server.SunHttp
|
||||
import org.http4k.server.asServer
|
||||
import starter.utils.sanitizeFilename
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
class Server(
|
||||
private val views: Views,
|
||||
private val conf: StarterConfig,
|
||||
private val projectZip: ProjectZip,
|
||||
) {
|
||||
|
||||
fun run() {
|
||||
val indexHandler: HttpHandler = {
|
||||
Response(Status.OK).body(views.index(conf.dependencies, conf.inputs)).header("Content-Type", "text/html")
|
||||
}
|
||||
val zipHandler: HttpHandler = { req ->
|
||||
val deps = conf.dependencies.filter {
|
||||
req.form(it.name) != null
|
||||
}
|
||||
|
||||
val inputKeys = conf.inputs.map { it.name }
|
||||
val inputs = req.formAsMap()
|
||||
.filter { it.key in inputKeys }
|
||||
.map { (name, value) ->
|
||||
conf.inputs.find { it.name == name }!!.copy(value = value.first())
|
||||
}
|
||||
|
||||
val projectName = inputs.find { it.name == "name" }!!.value!!
|
||||
val basePackage = inputs.find { it.name == "basePackage" }!!.value!!
|
||||
|
||||
if (basePackage.contains("/") || basePackage.contains("..")) {
|
||||
Response(Status.BAD_REQUEST).body("Invalid Base Package")
|
||||
} else {
|
||||
val repositories = conf.repositories
|
||||
.filter { repo -> repo.name in deps.mapNotNull { it.repository } }
|
||||
|
||||
val project = Project(projectName, basePackage, inputs, deps, repositories)
|
||||
val outputStream = projectZip.createZip(project)
|
||||
|
||||
Response(Status.OK).header("Content-Type", "application/zip")
|
||||
.header("Content-Disposition", "attachment; filename=\"${sanitizeFilename(projectName)}.zip\"")
|
||||
.body(ByteArrayInputStream(outputStream.toByteArray()))
|
||||
}
|
||||
}
|
||||
|
||||
val app = routes(
|
||||
"/" bind Method.GET to indexHandler,
|
||||
"/" bind Method.POST to zipHandler,
|
||||
static(ResourceLoader.Classpath("/assets"))
|
||||
)
|
||||
|
||||
app.asServer(SunHttp(7000)).start()
|
||||
println("Started on http://localhost:7000")
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,17 +1,13 @@
|
||||
package starter
|
||||
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import org.slf4j.LoggerFactory
|
||||
import starter.utils.render
|
||||
|
||||
class Views(private val engine: PebbleEngine) {
|
||||
private val logger = LoggerFactory.getLogger(javaClass)
|
||||
class Views(private val engine: PebbleEngine, config: StarterConfig) {
|
||||
private val args = mapOf(
|
||||
"dependencies" to config.dependencies.groupBy { it.category }.toSortedMap(),
|
||||
"inputs" to config.inputs
|
||||
)
|
||||
|
||||
fun index(dependencies: List<Dependency>, inputs: List<Input>): String {
|
||||
val dependenciesByCategory = dependencies.groupBy { it.category }.toSortedMap()
|
||||
return engine.render("views/index",
|
||||
mapOf("dependencies" to dependenciesByCategory, "inputs" to inputs)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
fun index() = engine.render("views/index", args)
|
||||
}
|
||||
|
||||
17
src/main/kotlin/starter/extensions/Http4kExtensions.kt
Normal file
17
src/main/kotlin/starter/extensions/Http4kExtensions.kt
Normal file
@ -0,0 +1,17 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package starter.extensions
|
||||
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import starter.utils.sanitizeFilename
|
||||
import java.io.InputStream
|
||||
|
||||
inline fun Response.Companion.ok() = Response(Status.OK)
|
||||
inline fun Response.Companion.badRequest() = Response(Status.BAD_REQUEST)
|
||||
|
||||
fun attachment(value: InputStream, name: String, contentType: String) = { res: Response ->
|
||||
res.header("Content-Type", contentType)
|
||||
.header("Content-Disposition", "attachment; filename=\"${sanitizeFilename(name)}\"")
|
||||
.body(value)
|
||||
}
|
||||
16
src/main/kotlin/starter/routes/IndexRouteSupplier.kt
Normal file
16
src/main/kotlin/starter/routes/IndexRouteSupplier.kt
Normal file
@ -0,0 +1,16 @@
|
||||
package starter.routes
|
||||
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.Status
|
||||
import org.http4k.routing.bind
|
||||
import starter.StarterConfig
|
||||
import starter.Views
|
||||
|
||||
class IndexRouteSupplier(private val views: Views) : RouteSupplier {
|
||||
override fun get() = "/" bind Method.GET to {
|
||||
Response(Status.OK)
|
||||
.body(views.index())
|
||||
.header("Content-Type", "text/html")
|
||||
}
|
||||
}
|
||||
10
src/main/kotlin/starter/routes/RouteSupplier.kt
Normal file
10
src/main/kotlin/starter/routes/RouteSupplier.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package starter.routes
|
||||
|
||||
import org.http4k.routing.RoutingHttpHandler
|
||||
import org.http4k.routing.routes
|
||||
|
||||
interface RouteSupplier {
|
||||
fun get(): RoutingHttpHandler
|
||||
}
|
||||
|
||||
fun List<RouteSupplier>.toRouter() = routes(*map { it.get() }.toTypedArray())
|
||||
56
src/main/kotlin/starter/routes/ZipRouteSupplier.kt
Normal file
56
src/main/kotlin/starter/routes/ZipRouteSupplier.kt
Normal file
@ -0,0 +1,56 @@
|
||||
package starter.routes
|
||||
|
||||
import org.http4k.core.Method
|
||||
import org.http4k.core.Response
|
||||
import org.http4k.core.body.form
|
||||
import org.http4k.core.body.formAsMap
|
||||
import org.http4k.core.with
|
||||
import org.http4k.routing.bind
|
||||
import starter.Project
|
||||
import starter.ProjectZip
|
||||
import starter.StarterConfig
|
||||
import starter.extensions.attachment
|
||||
import starter.extensions.badRequest
|
||||
import starter.extensions.ok
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
class ZipRouteSupplier(
|
||||
private val conf: StarterConfig,
|
||||
private val projectZip: ProjectZip,
|
||||
) : RouteSupplier {
|
||||
|
||||
override fun get() = "/" bind Method.POST to { req ->
|
||||
|
||||
val deps = conf.dependencies.filter {
|
||||
req.form(it.name) != null
|
||||
}
|
||||
|
||||
val inputKeys = conf.inputs.map { it.name }
|
||||
val inputs = req.formAsMap()
|
||||
.filter { it.key in inputKeys }
|
||||
.map { (name, value) ->
|
||||
conf.inputs.find { it.name == name }!!.copy(value = value.first())
|
||||
}
|
||||
|
||||
val projectName = inputs.find { it.name == "name" }!!.value!!
|
||||
val basePackage = inputs.find { it.name == "basePackage" }!!.value!!
|
||||
|
||||
if (basePackage.contains("/") || basePackage.contains("..")) {
|
||||
Response.badRequest()
|
||||
} else {
|
||||
val repositories = conf.repositories
|
||||
.filter { repo -> repo.name in deps.mapNotNull { it.repository } }
|
||||
|
||||
val project = Project(projectName, basePackage, inputs, deps, repositories)
|
||||
val outputStream = projectZip.createZip(project)
|
||||
|
||||
Response.ok().with(
|
||||
attachment(
|
||||
value = ByteArrayInputStream(outputStream.toByteArray()),
|
||||
name = "$projectName.zip",
|
||||
contentType = "application/zip"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,8 +6,8 @@ import starter.utils.render
|
||||
|
||||
class GitignoreTemplate(private val engine: PebbleEngine) : Template {
|
||||
override fun path(project: Project) =
|
||||
".gitignore"
|
||||
".gitignore"
|
||||
|
||||
override fun render(project: Project) =
|
||||
engine.render("starter/gitignore/index")
|
||||
engine.render("starter/gitignore/index")
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ class LogbackTemplate(private val engine: PebbleEngine) : Template {
|
||||
|
||||
override fun render(project: Project): String {
|
||||
val args = mapOf(
|
||||
"loggers" to project.dependencies.mapNotNull { it.logger }.toSet()
|
||||
"loggers" to project.dependencies.mapNotNull { it.logger }.toSet()
|
||||
)
|
||||
val rendered = engine.render("starter/logback/index", args)
|
||||
return prettyPrintXml(rendered)
|
||||
|
||||
@ -6,8 +6,8 @@ import starter.utils.render
|
||||
|
||||
class MainTemplate(private val engine: PebbleEngine) : Template {
|
||||
override fun path(project: Project) =
|
||||
"src/main/kotlin/" + project.basePackage.replace('.', '/') + "/" + project.name.toLowerCase().capitalize() + ".kt"
|
||||
"src/main/kotlin/" + project.basePackage.replace('.', '/') + "/" + project.name.toLowerCase().capitalize() + ".kt"
|
||||
|
||||
override fun render(project: Project) =
|
||||
engine.render("starter/main/index", mapOf("basePackage" to project.basePackage))
|
||||
engine.render("starter/main/index", mapOf("basePackage" to project.basePackage))
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@ class PomTemplate(private val engine: PebbleEngine) : Template {
|
||||
|
||||
override fun render(project: Project): String {
|
||||
val args: MutableMap<String, Any?> = mutableMapOf(
|
||||
"dependencies" to project.dependencies.sortedBy { it.scope },
|
||||
"repositories" to project.repositories,
|
||||
"kotlinxSerialization" to project.dependencies.any { it.name == "Kotlinx-serialization" },
|
||||
"dependencies" to project.dependencies.sortedBy { it.scope },
|
||||
"repositories" to project.repositories,
|
||||
"kotlinxSerialization" to project.dependencies.any { it.name == "Kotlinx-serialization" },
|
||||
)
|
||||
|
||||
project.inputs.forEach {
|
||||
|
||||
@ -1,11 +1,87 @@
|
||||
package starter.utils
|
||||
|
||||
import com.mitchellbosecke.pebble.PebbleEngine
|
||||
import com.mitchellbosecke.pebble.cache.tag.CaffeineTagCache
|
||||
import com.mitchellbosecke.pebble.cache.tag.ConcurrentMapTagCache
|
||||
import com.mitchellbosecke.pebble.cache.tag.NoOpTagCache
|
||||
import com.mitchellbosecke.pebble.cache.template.CaffeineTemplateCache
|
||||
import com.mitchellbosecke.pebble.cache.template.ConcurrentMapTemplateCache
|
||||
import com.mitchellbosecke.pebble.cache.template.NoOpTemplateCache
|
||||
import com.mitchellbosecke.pebble.extension.AbstractExtension
|
||||
import com.mitchellbosecke.pebble.extension.Filter
|
||||
import com.mitchellbosecke.pebble.extension.Function
|
||||
import com.mitchellbosecke.pebble.loader.ClasspathLoader
|
||||
import java.io.StringWriter
|
||||
|
||||
fun PebbleEngine.render(name: String, args: Map<String, Any?> = mapOf()): String {
|
||||
val template = getTemplate(name)
|
||||
val writer = StringWriter()
|
||||
template.evaluate(writer, args)
|
||||
return writer.toString()
|
||||
}
|
||||
fun PebbleEngine.render(name: String, args: Map<String, Any?> = mapOf()) =
|
||||
getTemplate(name).let { StringWriter().apply { it.evaluate(this, args) }.toString() }
|
||||
|
||||
fun PebbleEngine.render(name: String, vararg args: Pair<String, Any?>) =
|
||||
render(name, mapOf(*args))
|
||||
|
||||
interface PebbleFunction : Function {
|
||||
val name: String
|
||||
}
|
||||
|
||||
interface PebbleFilter : Filter {
|
||||
val name: String
|
||||
}
|
||||
|
||||
class PebbleEngineBuilder {
|
||||
enum class CacheType {
|
||||
None, Caffeine, ConcurrentMap
|
||||
}
|
||||
|
||||
private var loader = ClasspathLoader()
|
||||
var cache = CacheType.ConcurrentMap
|
||||
|
||||
private val functions = mutableListOf<PebbleFunction>()
|
||||
private val filters = mutableListOf<PebbleFilter>()
|
||||
|
||||
fun function(function: PebbleFunction) {
|
||||
functions.add(function)
|
||||
}
|
||||
|
||||
fun functions(functions: Iterable<PebbleFunction>) {
|
||||
this.functions.addAll(functions)
|
||||
}
|
||||
|
||||
fun filter(filter: PebbleFilter) {
|
||||
filters.add(filter)
|
||||
}
|
||||
|
||||
fun filters(filters: Iterable<PebbleFilter>) {
|
||||
this.filters.addAll(filters)
|
||||
}
|
||||
|
||||
fun classPath(block: ClasspathLoader.() -> Unit) {
|
||||
loader = ClasspathLoader().apply(block)
|
||||
}
|
||||
|
||||
companion object {
|
||||
operator fun invoke(block: PebbleEngineBuilder.() -> Unit): PebbleEngine =
|
||||
PebbleEngineBuilder().apply(block).let { builder ->
|
||||
PebbleEngine.Builder()
|
||||
.loader(builder.loader)
|
||||
.cacheActive(builder.cache != CacheType.None)
|
||||
.templateCache(when (builder.cache) {
|
||||
CacheType.None -> NoOpTemplateCache()
|
||||
CacheType.Caffeine -> CaffeineTemplateCache()
|
||||
CacheType.ConcurrentMap -> ConcurrentMapTemplateCache()
|
||||
})
|
||||
.tagCache(when (builder.cache) {
|
||||
CacheType.None -> NoOpTagCache()
|
||||
CacheType.Caffeine -> CaffeineTagCache()
|
||||
CacheType.ConcurrentMap -> ConcurrentMapTagCache()
|
||||
})
|
||||
.extension(object : AbstractExtension() {
|
||||
override fun getFunctions(): Map<String, Function>? =
|
||||
builder.functions.associateBy { it.name }.ifEmpty { null }
|
||||
|
||||
override fun getFilters(): Map<String, Filter>? =
|
||||
builder.filters.associateBy { it.name }.ifEmpty { null }
|
||||
})
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,22 +13,23 @@ import javax.xml.transform.stream.StreamResult
|
||||
import javax.xml.xpath.XPathConstants
|
||||
import javax.xml.xpath.XPathFactory
|
||||
|
||||
|
||||
/*
|
||||
@see https://stackoverflow.com/a/33541820
|
||||
*/
|
||||
fun prettyPrintXml(xml: String, indent: Int = 4): String {
|
||||
// Turn xml string into a document
|
||||
val document: Document = DocumentBuilderFactory.newInstance()
|
||||
.newDocumentBuilder()
|
||||
.parse(InputSource(ByteArrayInputStream(xml.encodeToByteArray())))
|
||||
.newDocumentBuilder()
|
||||
.parse(InputSource(ByteArrayInputStream(xml.encodeToByteArray())))
|
||||
|
||||
// Remove whitespaces outside tags
|
||||
document.normalize()
|
||||
val xPath = XPathFactory.newInstance().newXPath()
|
||||
val nodeList = xPath.evaluate("//text()[normalize-space()='']",
|
||||
document,
|
||||
XPathConstants.NODESET) as NodeList
|
||||
val nodeList = xPath.evaluate(
|
||||
"//text()[normalize-space()='']",
|
||||
document,
|
||||
XPathConstants.NODESET
|
||||
) as NodeList
|
||||
for (i in 0 until nodeList.length) {
|
||||
val node = nodeList.item(i)
|
||||
node.parentNode.removeChild(node)
|
||||
@ -46,4 +47,4 @@ fun prettyPrintXml(xml: String, indent: Int = 4): String {
|
||||
val stringWriter = StringWriter()
|
||||
transformer.transform(DOMSource(document), StreamResult(stringWriter))
|
||||
return stringWriter.toString()
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,9 +1,14 @@
|
||||
{% macro dependency(dependency) %}
|
||||
<label class="m-2">
|
||||
<input name="{{ dependency.name }}"
|
||||
type="checkbox"{% if dependency.default %} checked{% endif %}>
|
||||
<span>{{ dependency.name }}</span>
|
||||
</label>
|
||||
<details>
|
||||
<summary>
|
||||
<label class="m-2">
|
||||
<input name="{{ dependency.name }}"
|
||||
type="checkbox"{% if dependency.default %} checked{% endif %}>
|
||||
<span>{{ dependency.name }}</span>
|
||||
</label>
|
||||
</summary>
|
||||
<pre><code>{{ depAsXml(dependency) }}</code></pre>
|
||||
</details>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro input(input) %}
|
||||
@ -17,4 +22,4 @@
|
||||
<input name="{{ input.name }}" id="{{ input.name }}" class="input"{% if input.value %} value="{{ input.value }}"{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
{% endmacro %}
|
||||
|
||||
@ -21,6 +21,6 @@
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<button type="submit" class="w-full btn btn-purple">Submit</button>
|
||||
<button type="submit" class="my-4 w-full btn btn-purple">Submit</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user