2025-08-16 04:49:13 +02:00
package build
import com.raquo.domtypes.codegen.*
import com.raquo.domtypes.codegen.DefType.LazyVal
import com.raquo.domtypes.defs.styles.StyleTraitDefs
import com.raquo.domtypes.codegen.generators.*
import com.raquo.domtypes.common.*
import com.raquo.domtypes.defs.reflectedAttrs.ReflectedHtmlAttrDefs
class DomDefsGenerator(baseOutputDirectoryPath: String):
private object generator
extends CanonicalGenerator(
baseOutputDirectoryPath = baseOutputDirectoryPath,
basePackagePath = "scalive",
standardTraitCommentLines = List(
"#NOTE: GENERATED CODE",
s" - This file is generated at compile time from the data in Scala DOM Types",
" - See `DomDefsGenerator.mill` for code generation params",
" - Contribute to https://github.com/raquo/scala-dom-types to add missing tags / attrs / props / etc."
),
format = CodeFormatting()
):
override def settersPackagePath: String = basePackagePath + ".modifiers.KeySetter"
override def scalaJsElementTypeParam: String = "Ref"
override def scalaJsDomImport: String = ""
override def tagKeysPackagePath: String = "scalive"
override def keysPackagePath: String = "scalive"
override def generateTagsTrait(
tagType: TagType,
defGroups: List[(String, List[TagDef])],
printDefGroupComments: Boolean,
traitCommentLines: List[String],
traitModifiers: List[String],
traitName: String,
keyKind: String,
baseImplDefComments: List[String],
keyImplName: String,
defType: DefType
): String =
val (defs, defGroupComments) = defsAndGroupComments(defGroups, printDefGroupComments)
val baseImplDef =
if tagType == HtmlTagType then
List(
s"def $keyImplName($keyImplNameArgName: String, void: Boolean = false): $keyKind = ${keyKindConstructor(keyKind)}($keyImplNameArgName, void)"
)
else
List(
s"def $keyImplName($keyImplNameArgName: String): $keyKind = ${keyKindConstructor(keyKind)}($keyImplNameArgName)"
)
val headerLines = List(
s"package $tagDefsPackagePath",
"",
tagKeyTypeImport(keyKind),
scalaJsDomImport,
""
) ++ standardTraitCommentLines.map("// " + _)
new TagsTraitGenerator(
defs = defs,
defGroupComments = defGroupComments,
headerLines = headerLines,
traitCommentLines = traitCommentLines,
traitModifiers = traitModifiers,
traitName = traitName,
traitExtends = Nil,
traitThisType = None,
defType = _ => defType,
keyType = tag => keyKind,
keyImplName = _ => keyImplName,
keyImplNameArgName = keyImplNameArgName,
baseImplDefComments = baseImplDefComments,
baseImplDef = baseImplDef,
outputImplDefs = true,
format = format
).printTrait().getOutput()
end generateTagsTrait
2025-09-18 21:32:02 +02:00
override def generateAttrsTrait(
defGroups: List[(String, List[AttrDef])],
printDefGroupComments: Boolean,
traitCommentLines: List[String],
traitModifiers: List[String],
traitName: String,
keyKind: String,
implNameSuffix: String,
baseImplDefComments: List[String],
baseImplName: String,
namespaceImports: List[String],
namespaceImpl: String => String,
transformAttrDomName: String => String,
defType: DefType
): String =
val (defs, defGroupComments) = defsAndGroupComments(defGroups, printDefGroupComments)
val tagTypes = defs.foldLeft(List[TagType]())((acc, k) => (acc :+ k.tagType).distinct)
if tagTypes.size > 1 then
throw new Exception(
"Sorry, generateAttrsTrait does not support mixing attrs of different types in one call. You can contribute a PR (please contact us first), or bypass this limitation by calling AttrsTraitGenerator manually."
)
val tagType = tagTypes.head
val baseImplDef =
if tagType == SvgTagType then
List(
s"def $baseImplName[V]($keyImplNameArgName: String, encoder: Encoder[V, String], namespace: Option[String]): $keyKind[V] = ${keyKindConstructor(keyKind)}($keyImplNameArgName, encoder, namespace)"
)
else
List(
s"def $baseImplName[V]($keyImplNameArgName: String, encoder: Encoder[V, String]): $keyKind[V] = ${keyKindConstructor(keyKind)}($keyImplNameArgName, encoder)"
)
val headerLines = List(
s"package $attrDefsPackagePath",
"",
keyTypeImport(keyKind),
codecsImport
) ++ namespaceImports ++ List("") ++ standardTraitCommentLines.map("// " + _)
new AttrsTraitGenerator(
defs = defs.map(d => d.copy(domName = transformAttrDomName(d.domName))),
defGroupComments = defGroupComments,
headerLines = headerLines,
traitCommentLines = traitCommentLines,
traitModifiers = traitModifiers,
traitName = traitName,
traitExtends = Nil,
traitThisType = None,
defType = _ => defType,
keyKind = keyKind,
keyImplName = attr => attrImplName(attr.codec, implNameSuffix),
keyImplNameArgName = keyImplNameArgName,
baseImplDefComments = baseImplDefComments,
baseImplName = baseImplName,
baseImplDef = baseImplDef,
transformCodecName = _ + "Encoder",
namespaceImpl = namespaceImpl,
outputImplDefs = true,
format = format
).printTrait().getOutput()
end generateAttrsTrait
2025-08-16 04:49:13 +02:00
end generator
def generate(): Unit =
val defGroups = new CanonicalDefGroups()
// -- HTML tags --
{
val traitName = "HtmlTags"
val fileContent = generator.generateTagsTrait(
tagType = HtmlTagType,
defGroups = defGroups.htmlTagsDefGroups,
printDefGroupComments = true,
traitCommentLines = Nil,
traitModifiers = Nil,
traitName = traitName,
keyKind = "HtmlTag",
baseImplDefComments = List(
"Create HTML tag",
"",
"Note: this simply creates an instance of HtmlTag.",
" - This does not create the element (to do that, call .apply() on the returned tag instance)",
"",
"@param name - e.g. \"div\" or \"mwc-input\""
),
keyImplName = "htmlTag",
defType = LazyVal
)
generator.writeToFile(
packagePath = generator.tagDefsPackagePath,
fileName = traitName,
fileContent = fileContent
)
}
// -- HTML attributes --
{
val traitName = "HtmlAttrs"
val fileContent = generator.generateAttrsTrait(
defGroups = defGroups.htmlAttrDefGroups.appended(
"Reflected Attributes" -> ReflectedHtmlAttrDefs.defs.map(_.toAttrDef)
),
printDefGroupComments = false,
traitCommentLines = Nil,
traitModifiers = Nil,
traitName = traitName,
keyKind = "HtmlAttr",
implNameSuffix = "HtmlAttr",
baseImplDefComments = List(
"Create HTML attribute (Note: for SVG attrs, use L.svg.svgAttr)",
"",
"@param name - name of the attribute, e.g. \"value\"",
2025-09-18 21:32:02 +02:00
"@param codec - used to encode V into String, e.g. StringAsIsEncoder",
2025-08-16 04:49:13 +02:00
"",
"@tparam V - value type for this attr in Scala"
),
baseImplName = "htmlAttr",
namespaceImports = Nil,
namespaceImpl = _ => ???,
transformAttrDomName = identity,
defType = LazyVal
)
generator.writeToFile(
packagePath = generator.attrDefsPackagePath,
fileName = traitName,
fileContent = fileContent
)
}
end generate
end DomDefsGenerator