//> using lib "org.jsoup:jsoup:1.14.3" //> using lib "dev.zio::zio:2.0.0-RC2" //> using lib "dev.zio::zio-streams:2.0.0-RC2" // package works.iterative.tailwind.util import zio.Task import org.jsoup.nodes.Document import org.jsoup.Jsoup import org.jsoup.select.NodeVisitor import org.jsoup.nodes.Element import org.jsoup.nodes.Node import scala.jdk.CollectionConverters._ import org.jsoup.nodes.TextNode import zio.ZIOAppDefault import zio.ZIO import zio.stream.ZStream import java.io.EOFException object Laminarize extends ZIOAppDefault: class LaminarNodeVisitor() extends NodeVisitor: private val code: java.lang.StringBuilder = new java.lang.StringBuilder private val attrMap: Map[String, String] = Map("class" -> "cls", "type" -> "tpe").withDefault(identity) override def head(node: Node, depth: Int): Unit = node match { case el: Element => if (depth > 0) code.append(",\n") code.append("\t" * depth) writeElement(el, depth) case t: TextNode if !t.text.isBlank => if (depth > 0) code.append(",\n") code.append("\t" * depth) writeTripleQuoted(t.text) case _ => () } override def tail(node: Node, depth: Int): Unit = node match { case el: Element => code.append("\t" * depth).append("\n)") case _ => () } def getCode(): String = code.toString() private def writeQuoted(t: String): Unit = code.append('"').append(t.replaceAll("\"", "\\\"")).append('"') private def writeTripleQuoted(t: String): Unit = code.append(""""""""").append(t).append(""""""""") private def writeElement(el: Element, depth: Int): Unit = code.append(el.normalName).append("(\n") val attrs = el.attributes.asList.asScala attrs.zipWithIndex.foreach { (attribute, idx) => if (idx != 0) code.append(",\n") code.append("\t" * (depth + 1)) code .append(attrMap(attribute.getKey)) .append(" := ") writeQuoted(attribute.getValue) } def elementToLaminar(el: Element): String = val visitor = LaminarNodeVisitor() el.children.traverse(visitor) visitor.getCode() def htmlToLaminar(html: String): Task[String] = for { doc <- Task.attempt(Jsoup.parseBodyFragment(html)) } yield elementToLaminar(doc.body) override def run = for { console <- ZIO.environment[zio.Console] input <- ZStream .repeatZIO(console.get.readLine.asSome.catchSome { case _: EOFException => ZIO.succeed(None) }) .takeWhile(_.isDefined) .map(_.get) .fold("")((a, s) => a + "\n" + s) code <- htmlToLaminar(input) _ <- console.get.printLine(code) } yield ()