diff --git a/cli/htmgo/htmltogo/entry.go b/cli/htmgo/htmltogo/entry.go
deleted file mode 100644
index 9406939..0000000
--- a/cli/htmgo/htmltogo/entry.go
+++ /dev/null
@@ -1,119 +0,0 @@
-package main
-
-import (
- "bytes"
- "fmt"
- "log"
- "strings"
-
- "github.com/dave/jennifer/jen"
- "golang.org/x/net/html"
-)
-
-func main() {
- // Example HTML input
- htmlData := `
-
Manage Patients
Name: Sydne
Reason for visit: arm hurts
- `
-
- // Parse the HTML
- doc, err := html.Parse(bytes.NewReader([]byte(htmlData)))
- if err != nil {
- log.Fatal(err)
- }
-
- // Create a new Jennifer file
- f := jen.NewFile("main")
-
- // Generate Jennifer code for the parsed HTML tree
- generatedCode := processNode(doc.FirstChild)
-
- // Add the generated code to the file
- f.Func().Id("Render").Params().Block(generatedCode...)
-
- // Render the generated code
- var buf bytes.Buffer
- err = f.Render(&buf)
- if err != nil {
- log.Fatal(err)
- }
-
- //// Format the generated code
- //formattedCode, err := format.Source(buf.Bytes())
- //if err != nil {
- // log.Fatal(err)
- //}
-
- // Output the formatted code
- fmt.Println(string(buf.Bytes()))
-}
-
-// Recursively process the HTML nodes and generate Jennifer code
-func processNode(n *html.Node) []jen.Code {
- var code []jen.Code
-
- // Only process element nodes
- if n.Type == html.ElementNode {
- // Create a dynamic method call based on the tag name
- tagMethod := strings.Title(n.Data) // Capitalize the first letter of the tag
-
- // Add dynamic method call for the tag (e.g., h.Div(), h.Button(), etc.)
- code = append(code, jen.Id("h").Dot(tagMethod).Call(mergeArgs(n)...))
- }
-
- return code
-}
-
-// Merge attributes and children into a single slice for Call()
-func mergeArgs(n *html.Node) []jen.Code {
- // Process attributes
- attrs := processAttributes(n.Attr)
-
- // Process children
- children := processChildren(n)
-
- // Combine attributes and children into one slice
- return append(attrs, children...)
-}
-
-// Process child nodes of a given HTML node
-func processChildren(n *html.Node) []jen.Code {
- var children []jen.Code
-
- for c := n.FirstChild; c != nil; c = c.NextSibling {
- children = append(children, processNode(c)...)
- }
-
- return children
-}
-
-func FormatFieldName(name string) string {
- split := strings.Split(name, "_")
- if strings.Contains(name, "-") {
- split = strings.Split(name, "-")
- }
- parts := make([]string, 0)
- for _, s := range split {
- parts = append(parts, PascalCase(s))
- }
- return strings.Join(parts, "")
-}
-
-func PascalCase(s string) string {
- if s == "" {
- return s
- }
- // Convert the first rune (character) to uppercase and concatenate with the rest of the string
- return strings.ToUpper(string(s[0])) + s[1:]
-}
-
-// Process the attributes of an HTML node and return Jennifer code
-func processAttributes(attrs []html.Attribute) []jen.Code {
- var args []jen.Code
- for _, attr := range attrs {
- // Dynamically handle all attributes
- attrMethod := FormatFieldName(attr.Key) // E.g., convert "data-role" to "DataRole"
- args = append(args, jen.Id("h").Dot(attrMethod).Call(jen.Lit(attr.Val)))
- }
- return args
-}
diff --git a/tools/html-to-htmgo/go.mod b/tools/html-to-htmgo/go.mod
new file mode 100644
index 0000000..5025aea
--- /dev/null
+++ b/tools/html-to-htmgo/go.mod
@@ -0,0 +1,8 @@
+module html-to-htmgo
+
+go 1.23.0
+
+require (
+ golang.org/x/net v0.30.0
+ golang.org/x/text v0.19.0
+)
diff --git a/tools/html-to-htmgo/go.sum b/tools/html-to-htmgo/go.sum
new file mode 100644
index 0000000..613920c
--- /dev/null
+++ b/tools/html-to-htmgo/go.sum
@@ -0,0 +1,8 @@
+golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
+golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
+golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
+golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
+golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
+golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
diff --git a/tools/html-to-htmgo/internal/adapters/services/formatter/formatter.go b/tools/html-to-htmgo/internal/adapters/services/formatter/formatter.go
new file mode 100644
index 0000000..f6cc02a
--- /dev/null
+++ b/tools/html-to-htmgo/internal/adapters/services/formatter/formatter.go
@@ -0,0 +1,29 @@
+package formatter
+
+import (
+ "go/format"
+ "html-to-htmgo/internal/domain"
+)
+
+type Formatter struct {
+}
+
+func (f Formatter) Format(node *domain.CustomNode) string {
+ b := []byte(`package main
+import (
+ "github.com/maddalax/htmgo/framework/h"
+)
+func MyComponent() *h.Element {
+ return ` + node.String() + `
+}`)
+ dist, err := format.Source(b)
+ if err != nil {
+ return string(b)
+ }
+
+ return string(dist)
+}
+
+func New() Formatter {
+ return Formatter{}
+}
diff --git a/tools/html-to-htmgo/internal/adapters/services/parser/parser.go b/tools/html-to-htmgo/internal/adapters/services/parser/parser.go
new file mode 100644
index 0000000..3583d46
--- /dev/null
+++ b/tools/html-to-htmgo/internal/adapters/services/parser/parser.go
@@ -0,0 +1,69 @@
+package parser
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "golang.org/x/net/html"
+ "html-to-htmgo/internal/domain"
+ "strings"
+)
+
+type Parser struct {
+}
+
+var ParseErr = errors.New("parse error")
+
+func (p Parser) FromBytes(in []byte) (*domain.CustomNode, error) {
+ hNode, err := html.Parse(bytes.NewReader(in))
+ if err != nil {
+ return nil, fmt.Errorf("%w: %v", ParseErr, err)
+ }
+ var findBody func(n *html.Node) *html.Node
+ findBody = func(n *html.Node) *html.Node {
+ if n.Data == "body" {
+ return n
+ }
+ var e *html.Node
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ e = findBody(c)
+ }
+ return e
+ }
+
+ body := findBody(hNode)
+ if body == nil {
+ return nil, fmt.Errorf("%w", ParseErr)
+ }
+
+ var f func(*html.Node, *domain.CustomNode) *domain.CustomNode
+ f = func(n *html.Node, cNode *domain.CustomNode) *domain.CustomNode {
+ if n.Type == html.ElementNode {
+ cNode.SetType(n.Data)
+
+ for _, attr := range n.Attr {
+ cNode.AddAttr(attr.Key, attr.Val)
+ }
+ }
+
+ if n.Type == html.TextNode && len(strings.TrimSpace(n.Data)) > 0 {
+ cNode.ParentNode.AddAttr("h.Text", strings.TrimSpace(n.Data))
+ }
+
+ var i uint
+ for c := n.FirstChild; c != nil; c = c.NextSibling {
+ cNode.Nodes = append(cNode.Nodes, &domain.CustomNode{ParentNode: cNode, Level: cNode.Level + 1})
+ cNode.Nodes[i] = f(c, cNode.Nodes[i])
+ i++
+ }
+ return cNode
+ }
+ output := &domain.CustomNode{}
+ out := f(body, output)
+
+ return out, nil
+}
+
+func New() Parser {
+ return Parser{}
+}
diff --git a/tools/html-to-htmgo/internal/adapters/services/parser/parser_test.go b/tools/html-to-htmgo/internal/adapters/services/parser/parser_test.go
new file mode 100644
index 0000000..5d2de8d
--- /dev/null
+++ b/tools/html-to-htmgo/internal/adapters/services/parser/parser_test.go
@@ -0,0 +1,33 @@
+package parser
+
+import (
+ "errors"
+ "testing"
+)
+
+func FuzzFromBytes(f *testing.F) {
+ serviceParser := New()
+ f.Add([]byte("Hello World"))
+ f.Add([]byte("TestSample"))
+ f.Add([]byte("Some random text
"))
+ f.Add([]byte("Invalid HTML"))
+ f.Add([]byte(""))
+ f.Add([]byte("