Skip to content

Commit

Permalink
HTMX attributes prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
JPonte authored and davesmith00000 committed Jan 23, 2024
1 parent cf9cf04 commit c193c7d
Show file tree
Hide file tree
Showing 13 changed files with 453 additions and 32 deletions.
102 changes: 76 additions & 26 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ lazy val commonSettings: Seq[sbt.Def.Setting[_]] = Seq(
version := tyrianVersion,
organization := "io.indigoengine",
libraryDependencies ++= Seq(
"org.typelevel" %%% "munit-cats-effect" % Dependancies.munitCatsEffect3 % Test,
"org.typelevel" %%% "discipline-munit" % Dependancies.disciplineMUnit % Test,
"org.typelevel" %%% "cats-laws" % Dependancies.catsLaws % Test
"org.typelevel" %%% "munit-cats-effect" % Dependencies.munitCatsEffect3 % Test,
"org.typelevel" %%% "discipline-munit" % Dependencies.disciplineMUnit % Test,
"org.typelevel" %%% "cats-laws" % Dependencies.catsLaws % Test
),
testFrameworks += new TestFramework("munit.Framework"),
scalacOptions ++= Seq("-language:strictEquality"),
Expand Down Expand Up @@ -57,10 +57,10 @@ lazy val commonJsSettings: Seq[sbt.Def.Setting[_]] = Seq(
lazy val commonBrowserTestJsSettings: Seq[sbt.Def.Setting[_]] = Seq(
scalacOptions ++= commonScalacOptions.value,
libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % Dependancies.scalajsDomVersion,
"org.typelevel" %%% "cats-effect" % Dependancies.catsEffect,
"io.circe" %%% "circe-core" % Dependancies.circe,
"io.circe" %%% "circe-parser" % Dependancies.circe
"org.scala-js" %%% "scalajs-dom" % Dependencies.scalajsDomVersion,
"org.typelevel" %%% "cats-effect" % Dependencies.catsEffect,
"io.circe" %%% "circe-core" % Dependencies.circe,
"io.circe" %%% "circe-parser" % Dependencies.circe
)
)

Expand Down Expand Up @@ -129,7 +129,10 @@ lazy val tyrianProject =
sandboxZIO.js,
firefoxTests.js,
chromeTests.js,
docs
docs,
tyrianHtmx.js,
tyrianHtmx.jvm,
sandboxSSR.jvm
)

lazy val tyrian =
Expand All @@ -145,9 +148,9 @@ lazy val tyrian =
.jsSettings(
commonJsSettings,
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-effect-kernel" % Dependancies.catsEffect,
"co.fs2" %%% "fs2-core" % Dependancies.fs2,
"io.github.buntec" %%% "scala-js-snabbdom" % Dependancies.scalajsSnabbdom
"org.typelevel" %%% "cats-effect-kernel" % Dependencies.catsEffect,
"co.fs2" %%% "fs2-core" % Dependencies.fs2,
"io.github.buntec" %%% "scala-js-snabbdom" % Dependencies.scalajsSnabbdom
)
)

Expand All @@ -163,8 +166,8 @@ lazy val tyrianIO =
.jsSettings(
commonJsSettings,
libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % Dependancies.scalajsDomVersion,
"org.typelevel" %%% "cats-effect" % Dependancies.catsEffect
"org.scala-js" %%% "scalajs-dom" % Dependencies.scalajsDomVersion,
"org.typelevel" %%% "cats-effect" % Dependencies.catsEffect
)
)
.dependsOn(tyrian)
Expand All @@ -181,9 +184,9 @@ lazy val tyrianZIO =
.jsSettings(
commonJsSettings,
libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % Dependancies.scalajsDomVersion,
"io.github.cquiroz" %%% "scala-java-time" % Dependancies.scalaJavaTime,
"dev.zio" %%% "zio" % Dependancies.zio
"org.scala-js" %%% "scalajs-dom" % Dependencies.scalajsDomVersion,
"io.github.cquiroz" %%% "scala-java-time" % Dependencies.scalaJavaTime,
"dev.zio" %%% "zio" % Dependencies.zio
)
)
.dependsOn(tyrian)
Expand Down Expand Up @@ -213,11 +216,28 @@ lazy val sandboxZIO =
name := "Sandbox ZIO",
scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) },
libraryDependencies ++= Seq(
"dev.zio" %%% "zio-interop-cats" % Dependancies.zioInteropCats
"dev.zio" %%% "zio-interop-cats" % Dependencies.zioInteropCats
),
scalacOptions -= "-language:strictEquality"
)

lazy val sandboxSSR =
crossProject(JVMPlatform)
.crossType(CrossType.Pure)
.dependsOn(tyrian, tyrianHtmx)
.in(file("sandbox-ssr"))
.settings(
neverPublish,
commonSettings,
name := "sandbox-ssr",
scalacOptions -= "-language:strictEquality",
libraryDependencies ++= Seq(
"org.http4s" %% "http4s-ember-server" % Dependencies.http4sServer,
"org.http4s" %% "http4s-dsl" % Dependencies.http4sServer
),
run / fork := true
)

lazy val unidocs =
project
.enablePlugins(ScalaJSPlugin, ScalaUnidocPlugin)
Expand All @@ -240,14 +260,14 @@ lazy val jsdocs =
neverPublish,
organization := "io.indigoengine",
libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % Dependancies.scalajsDomVersion,
"io.circe" %%% "circe-core" % Dependancies.circe,
"io.circe" %%% "circe-parser" % Dependancies.circe,
"io.indigoengine" %%% "indigo" % indigoDocsVersion,
"io.indigoengine" %%% "tyrian-io" % tyrianDocsVersion,
"org.http4s" %%% "http4s-dom" % Dependancies.http4sDom,
"org.http4s" %%% "http4s-circe" % Dependancies.http4sCirce,
"org.typelevel" %%% "cats-effect" % Dependancies.catsEffect
"org.scala-js" %%% "scalajs-dom" % Dependencies.scalajsDomVersion,
"io.circe" %%% "circe-core" % Dependencies.circe,
"io.circe" %%% "circe-parser" % Dependencies.circe,
"io.indigoengine" %%% "indigo" % indigoDocsVersion,
"io.indigoengine" %%% "tyrian-io" % tyrianDocsVersion,
"org.http4s" %%% "http4s-dom" % Dependencies.http4sDom,
"org.http4s" %%% "http4s-circe" % Dependencies.http4sCirce,
"org.typelevel" %%% "cats-effect" % Dependencies.catsEffect
),
Compile / tpolecatExcludeOptions ++= Set(
ScalacOptions.warnValueDiscard,
Expand Down Expand Up @@ -311,6 +331,24 @@ lazy val chromeTests =
.jsSettings(commonBrowserTestJsSettings)
.dependsOn(tyrian)

lazy val tyrianHtmx =
crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Full)
.in(file("tyrian-htmx"))
.settings(
name := "tyrian-htmx",
commonSettings ++ publishSettings,
Compile / sourceGenerators += codeGen(
"tyrian.htmx",
(fullyQualifiedPath, sourceManagedDir) =>
List(HtmxAttributes.gen(fullyQualifiedPath, sourceManagedDir)(HtmxAttributes.htmxAttrsList))
).taskValue
)
.jsSettings(
commonJsSettings
)
.dependsOn(tyrian)

addCommandAlias(
"sandboxBuild",
List(
Expand All @@ -323,6 +361,18 @@ addCommandAlias(
"sandboxZIO/fastLinkJS"
).mkString("", ";", ";")
)
addCommandAlias(
"sandboxSSRBuild",
List(
"sandboxSSRJVM/compile"
).mkString("", ";", ";")
)
addCommandAlias(
"sandboxSSRServer",
List(
"sandboxSSRJVM/run"
).mkString("", ";", ";")
)

addCommandAlias(
"gendocs",
Expand Down Expand Up @@ -394,7 +444,7 @@ addCommandAlias(
"tyrianJS/test",
"tyrianJVM/test",
"tyrianIO/test",
"tyrianZIO/test",
"tyrianZIO/test"
).mkString(";", ";", "")
)

Expand Down
6 changes: 3 additions & 3 deletions examples/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ lazy val indigo =
name := "indigo-bridge",
libraryDependencies ++= Seq(
"io.indigoengine" %%% "tyrian-indigo-bridge" % tyrianVersion,
"io.indigoengine" %%% "indigo" % Dependancies.indigoVersion,
"io.indigoengine" %%% "indigo-extras" % Dependancies.indigoVersion,
"io.indigoengine" %%% "indigo-json-circe" % Dependancies.indigoVersion
"io.indigoengine" %%% "indigo" % Dependencies.indigoVersion,
"io.indigoengine" %%% "indigo-extras" % Dependencies.indigoVersion,
"io.indigoengine" %%% "indigo-json-circe" % Dependencies.indigoVersion
)
)

Expand Down
2 changes: 1 addition & 1 deletion examples/project/Dependancies.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
object Dependancies {
object Dependencies {

val indigoVersion = "0.15.2"

Expand Down
4 changes: 2 additions & 2 deletions project/Dependancies.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
object Dependancies {
object Dependencies {

val catsEffect = "3.5.2"
val circe = "0.14.6"
Expand All @@ -13,5 +13,5 @@ object Dependancies {
val catsLaws = "2.10.0"
val http4sCirce = "0.23.24"
val http4sDom = "0.2.11"

val http4sServer = "0.23.24"
}
114 changes: 114 additions & 0 deletions project/HtmxAttributes.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import sbt._
import scala.sys.process._

object HtmxAttributes {
lazy val htmxAttrs = List(
Normal("hxOn", "hx-on"), // TODO: this is the deprecated version, should be hx-on:event

Normal("hxBoost", "hx-boost"),
Normal("hxGet", "hx-get"),
Normal("hxPost", "hx-post"),
Normal("hxPushUrl", "hx-push-url"),
Normal("hxSelect", "hx-select"),
Normal("hxSelectOob", "hx-select-oob"),
Normal("hxSwap", "hx-swap"),
Normal("hxSwapOob", "hx-swap-oob"),
Normal("hxTarget", "hx-target"),
Normal("hxTrigger", "hx-trigger"),
Normal("hxVals", "hx-vals"),
Normal("hxConfirm", "hx-confirm"),
Normal("hxDelete", "hx-delete"),
NoValue("hxDisable", "hx-disable"),
Normal("hxDisabledElt", "hx-disabled-elt"),
Normal("hxDisinherit", "hx-disinherit"),
Normal("hxEncoding", "hx-encoding"),
Normal("hxExt", "hx-ext"),
Normal("hxHeaders", "hx-headers"),
Normal("hxHistory", "hx-history").withTypes("String", "Boolean"),
NoValue("hxHistoryElt", "hx-history-elt"),
Normal("hxInclude", "hx-include"),
Normal("hxIndicator", "hx-indicator"),
Normal("hxParams", "hx-params"),
Normal("hxPatch", "hx-patch"),
NoValue("hxPreserve", "hx-preserve"),
Normal("hxPrompt", "hx-prompt"),
Normal("hxPut", "hx-put"),
Normal("hxReplaceUrl", "hx-replace-url").withTypes("String", "Boolean"),
Normal("hxRequest", "hx-request"),
Normal("hxSync", "hx-sync"),
Normal("hxValidate", "hx-validate"),
Normal("hxVars", "hx-vars")
)

def htmxAttrsList: AttributesList = AttributesList(htmxAttrs, List(), "HtmxAttributes")

def genAttr(tag: AttributeType, isAttribute: Boolean): String =
tag match {
case Normal("hxTrigger", Some("hx-trigger"), _) => genHtmxTrigger
case Normal(name, attrName, types) => AttributeGen.genNormal(name, attrName, types)
case NoValue(name, attrName) => AttributeGen.genNoValue(name, attrName)
}

def triggerAttributeName =
s""" final class AttributeNameTrigger(name: String):
| def :=(value: Trigger): Attribute = Attribute(name.toString, value.render)
| final class AttributeNameTriggers(name: String):
| def :=(values: List[Trigger]): Attribute = Attribute(name.toString, values.map(_.render).mkString(","))
|""".stripMargin

def genHtmxTrigger: String = {
val res = s""" @targetName("hxTrigger-Trigger")
| val hxTrigger: AttributeNameTrigger = AttributeNameTrigger("hx-trigger")
| @targetName("hxTrigger-Triggers")
| val hxTrigger: AttributeNameTriggers = AttributeNameTriggers("hx-trigger")
| @targetName("hxTrigger-String")
| val hxTrigger: AttributeNameString = AttributeNameString("hx-trigger")
|""".stripMargin

"\n" + res + "\n"
}

def template(moduleName: String, fullyQualifiedPath: String, contents: String): String =
s"""package $fullyQualifiedPath
|
|import tyrian.*
|import tyrian.Html.*
|import scala.annotation.targetName
|
|// GENERATED by AttributeGen.scala - DO NOT EDIT
|trait $moduleName {
|
|$contents
|
|}
""".stripMargin

def gen(fullyQualifiedPath: String, sourceManagedDir: File)(attributesList: AttributesList): File =
attributesList match {
case AttributesList(attrs, props, name) =>
val file: File =
sourceManagedDir / s"$name.scala"

if (!file.exists()) {
println("Generating Html Attributes")

val contents: String =
AttributeGen.generateAttributeNameTypes +
AttributeGen.genAttributesAndProperties +
triggerAttributeName +
"\n\n // Attributes\n\n" +
attrs.map(a => genAttr(a, true)).mkString +
"\n\n // Properties\n\n" +
props.map(p => genAttr(p, false)).mkString

val newContents: String =
template(name, fullyQualifiedPath, contents)

IO.write(file, newContents)

println("Written: " + file.getCanonicalPath)
}

file
}
}
43 changes: 43 additions & 0 deletions sandbox-ssr/.jvm/src/main/scala/example/CorvidDatabase.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package example

import cats.Applicative
import cats.implicits.*

trait CorvidDatabase[F[_]]:
def search(query: String): F[List[(String, String)]]
def listAll: F[List[(String, String)]]

object CorvidDatabase:
private val data: List[(String, String)] = List(
"Canada Jay" -> "Perisoreus canadensis",
"Green Jay" -> "Cyanocorax yncas",
"Pinyon Jay" -> "Gymnorhinus cyanocephalus",
"Steller's Jay" -> "Cyanocitta stelleri",
"Blue Jay" -> "Cyanocitta cristata",
"Florida Scrub-Jay" -> "Aphelocoma coerulescens",
"California Scrub-Jay" -> "Aphelocoma californica",
"Woodhouse's Scrub-Jay" -> "Aphelocoma woodhouseii",
"Mexican Jay" -> "Aphelocoma wollweberi",
"Black-billed Magpie" -> "Pica hudsonia",
"Yellow-billed Magpie" -> "Pica nuttalli",
"Clark's Nutcracker" -> "Nucifraga columbiana",
"American Crow" -> "Corvus brachyrhynchos",
"Fish Crow" -> "Corvus ossifragus",
"Chihuahuan Raven" -> "Corvus cryptoleucus",
"Eurasian Magpie" -> "Pica pica",
"Eurasian Jackdaw" -> "Corvus monedula",
"Common Raven" -> "Corvus corax"
)

def fakeImpl[F[_]: Applicative]: CorvidDatabase[F] =
new CorvidDatabase[F]:
override def search(query: String): F[List[(String, String)]] =
data
.filter(row =>
row._1.toLowerCase.contains(query.toLowerCase) ||
row._2.toLowerCase.contains(query.toLowerCase)
)
.pure[F]

override def listAll: F[List[(String, String)]] =
data.pure[F]
Loading

0 comments on commit c193c7d

Please sign in to comment.