Skip to content

Commit 130f227

Browse files
committed
Merge commit '5a95b0566e4733b46a30d908335a2a535c4e7d8e' into update/template
2 parents a335404 + 5a95b05 commit 130f227

File tree

20 files changed

+2041
-2
lines changed

20 files changed

+2041
-2
lines changed

.git-blame-ignore-revs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
# Scala Steward: Reformat with scalafmt 3.1.2
22
fb6cfb8aea15a1b339e3ed69e1e96acd7df4cae6
3+
e57856703dc0573ff9c27a60c0a18fb25caa0848
4+
5+
# Scala Steward: Reformat with scalafmt 3.6.1
6+
1b4641000bbc841d5ddbff621617cfee74379ccb
7+
8+
# Scala Steward: Reformat with scalafmt 3.7.2
9+
8983a05df195349dc360ec83772778866e721653

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/html/src/main/scala/org/lrng/binding/AttributeFactories.scala linguist-generated
2+
/html/src/main/scala/org/lrng/binding/ElementFactories.scala linguist-generated
3+
/html/src/main/scala/org/lrng/binding/EntityBuilders.scala linguist-generated

.github/workflows/scala.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
scala:
19-
- 2.12.15
19+
- 3.2.2
2020

2121
steps:
2222
- uses: actions/checkout@v3

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
target/
22
local.sbt
33
secret/
4+
.idea/
45
.metals/
56
.bloop/
67
metals.sbt

.sbtopts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-J-Xss5m
2+
-J-XX:MaxMetaspaceSize=9999g

.scalafmt.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
runner.dialect = scala212source3
1+
runner.dialect = scala3
22
version = "3.7.5"
33
maxColumn = 80

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2019 GlasslabGames
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# html.scala
2+
3+
[![Scala CI](https://github.com/Atry/html.scala/actions/workflows/scala.yml/badge.svg)](https://github.com/Atry/html.scala/actions/workflows/scala.yml)
4+
[![html Scala version support](https://index.scala-lang.org/atry/html.scala/html/latest.svg)](https://index.scala-lang.org/atry/html.scala/html)
5+
[![Scaladoc](https://javadoc.io/badge/com.yang-bo/html_sjs1_3.svg?label=Scaladoc)](https://javadoc.io/page/com.yang-bo/html_sjs1_3/latest/com/yang_bo/html.html)
6+
7+
8+
This **html.scala** 3.x provides the `html"""..."""` interpolation for creating reactive HTML templates. It is a reimplementation of `@dom` and `@html` annotation in [Binding.scala](https://github.com/ThoughtWorksInc/Binding.scala), supporting Scala 3. The typing rules are also improved, preventing red marks in IDEs.
9+
10+
For Scala 2 users, use `html.scala` 2.x instead.
11+
12+
## Links
13+
14+
* [API Documentation](https://javadoc.io/page/com.yang-bo/html_sjs1_3/latest/com/yang_bo/html.html)
15+
* [Binding.scala](https://github.com/ThoughtWorksInc/Binding.scala/)
16+
* [All-in-One Scala.js Static Web Project Template (including html.scala)](https://github.com/Atry/scalajs-all-in-one-template)
17+
* [Binding.scala • TodoMVC](http://todomvc.com/examples/binding-scala/)

build.sbt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
lazy val `html-Definitions` = project
2+
3+
lazy val `html-InterpolationParser` = project
4+
5+
lazy val `html` =
6+
project.dependsOn(`html-InterpolationParser`, `html-Definitions`)
7+
8+
ThisBuild / organization := "com.yang-bo"
9+
10+
publish / skip := true
11+
12+
enablePlugins(ScalaUnidocPlugin)

html-Definitions/build.sbt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
enablePlugins(ScalaJSPlugin)
2+
3+
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.4.0"

html-Definitions/code-generator.sbt

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import scala.meta.dialects.Scala3
2+
3+
val generateHtmlDefinitions =
4+
taskKey[File]("Generate Definitions.scala file from HTML Living Standard")
5+
generateHtmlDefinitions := {
6+
import com.gargoylesoftware.htmlunit.WebClient
7+
import com.gargoylesoftware.htmlunit.html.{HtmlPage, HtmlTable}
8+
import org.portablescala.sbtplatformdeps.PlatformDepsPlugin
9+
import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
10+
11+
import scala.collection.JavaConverters._
12+
import scala.meta._
13+
type InterfaceName = String
14+
type ElementName = String
15+
type PropertyName = String
16+
type AttributeName = String
17+
type TypeName = String
18+
19+
final case class AttributeDefinition(
20+
interfacesByTagName: Map[ElementName, InterfaceName],
21+
interfacesByAttribute: Map[AttributeName, Seq[InterfaceName]]
22+
)
23+
val attributeDefinition: AttributeDefinition = {
24+
val webClient = new WebClient()
25+
try {
26+
webClient.getOptions.setJavaScriptEnabled(false)
27+
webClient.getOptions.setCssEnabled(false)
28+
val document = webClient.getPage[HtmlPage](
29+
"https://html.spec.whatwg.org/multipage/indices.html"
30+
)
31+
try {
32+
val interfacesByTagName = {
33+
for {
34+
tbody <- document
35+
.querySelector("#element-interfaces ~ table")
36+
.asInstanceOf[HtmlTable]
37+
.getBodies
38+
.asScala
39+
.view
40+
row <- tbody.getRows.asScala.view
41+
Seq(tagNameCell, interfaceCell) = row.getCells.asScala
42+
tagNameCode <- tagNameCell
43+
.getElementsByTagName("code")
44+
.asScala
45+
.view
46+
} yield tagNameCode.asNormalizedText -> interfaceCell
47+
.getElementsByTagName("code")
48+
.asScala
49+
.head
50+
.asNormalizedText
51+
}.toMap
52+
val interfacesByAttribute = {
53+
for {
54+
tbody <- document
55+
.getElementById("attributes-1")
56+
.asInstanceOf[HtmlTable]
57+
.getBodies
58+
.asScala
59+
.view ++
60+
document
61+
.querySelector("#attributes-1 ~ table")
62+
.asInstanceOf[HtmlTable]
63+
.getBodies
64+
.asScala
65+
.view
66+
row <- tbody.getRows.asScala.view
67+
interfaceName <-
68+
if (row.getCell(1).asNormalizedText == "HTML elements") {
69+
Seq("HTMLElement")
70+
} else {
71+
row
72+
.getCell(1)
73+
.getElementsByTagName("code")
74+
.asScala
75+
.view
76+
.map { code =>
77+
interfacesByTagName(code.asNormalizedText)
78+
}
79+
}
80+
} yield row.getCell(0).asNormalizedText -> interfaceName
81+
}.groupBy(_._1).mapValues(_.map(_._2).distinct.toList)
82+
AttributeDefinition(interfacesByTagName, interfacesByAttribute)
83+
} finally {
84+
document.cleanUp()
85+
}
86+
} finally {
87+
webClient.close()
88+
}
89+
}
90+
91+
val elementCases =
92+
for (
93+
(tagName, domInterface) <-
94+
attributeDefinition.interfacesByTagName
95+
) yield {
96+
p"""
97+
case $tagName =>
98+
Type.of[${Type.Name(domInterface)}]
99+
"""
100+
}
101+
val attributeCases =
102+
for (
103+
(attributeName, domInterfaces) <-
104+
attributeDefinition.interfacesByAttribute
105+
) yield {
106+
p"""
107+
case $attributeName =>
108+
TypeRepr.of[E] <:< TypeRepr.of[${domInterfaces
109+
.map { domInterface => Type.Name(domInterface): Type }
110+
.reduce { (t1, t2) =>
111+
t"$t1 | $t2"
112+
}}]
113+
"""
114+
}
115+
val generatedAst = q"""
116+
package com.yang_bo.html {
117+
import org.scalajs.dom.*
118+
import scala.quoted.*
119+
import ScalaJsDomMissingTypes.*
120+
private[html] object Definitions {
121+
def findTypeByTagName(tagName: String)(using quotes: Quotes): Type[_ <: HTMLElement] = {
122+
(tagName : @scala.annotation.switch) match {
123+
..case ${elementCases.toList}
124+
case _ =>
125+
Type.of[HTMLUnknownElement]
126+
}
127+
}
128+
def isValidAttribute[E](attributeName: String)(using elementType: Type[E], quotes: Quotes): Boolean = {
129+
import quotes.reflect.TypeRepr
130+
(attributeName : @scala.annotation.switch) match {
131+
..case ${attributeCases.toList}
132+
case _ =>
133+
false
134+
}
135+
}
136+
}
137+
}
138+
"""
139+
val file =
140+
(Compile / scalaSource).value / "com" / "yang_bo" / "html" / "Definitions.scala"
141+
val fileContent = Seq(
142+
"// Don't edit this file, because it is generated by `sbt generateHtmlDefinitions`",
143+
"// format: off",
144+
generatedAst.syntax
145+
)
146+
IO.writeLines(file, fileContent)
147+
file
148+
}

0 commit comments

Comments
 (0)