working on landing page
This commit is contained in:
parent
279a3c7163
commit
f036fae5e7
48 changed files with 574 additions and 26 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package htmgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
module github.com/maddalax/htmgo/cli
|
module github.com/maddalax/htmgo/cli/htmgo
|
||||||
|
|
||||||
go 1.23.0
|
go 1.23.0
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package htmgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
|
@ -20,7 +20,7 @@ func main() {
|
||||||
done := RegisterSignals()
|
done := RegisterSignals()
|
||||||
|
|
||||||
commandMap := make(map[string]*flag.FlagSet)
|
commandMap := make(map[string]*flag.FlagSet)
|
||||||
commands := []string{"template", "run", "watch", "build", "setup", "css", "schema"}
|
commands := []string{"template", "run", "watch", "build", "setup", "css", "schema", "generate"}
|
||||||
|
|
||||||
for _, command := range commands {
|
for _, command := range commands {
|
||||||
commandMap[command] = flag.NewFlagSet(command, flag.ExitOnError)
|
commandMap[command] = flag.NewFlagSet(command, flag.ExitOnError)
|
||||||
|
|
@ -100,6 +100,8 @@ func main() {
|
||||||
downloadtemplate.DownloadTemplate(fmt.Sprintf("./%s", text))
|
downloadtemplate.DownloadTemplate(fmt.Sprintf("./%s", text))
|
||||||
} else if taskName == "build" {
|
} else if taskName == "build" {
|
||||||
run.Build()
|
run.Build()
|
||||||
|
} else if taskName == "generate" {
|
||||||
|
astgen.GenAst(process.ExitOnError)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println(fmt.Sprintf("Usage: htmgo [%s]", strings.Join(commands, " | ")))
|
fmt.Println(fmt.Sprintf("Usage: htmgo [%s]", strings.Join(commands, " | ")))
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package htmgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -56,7 +56,7 @@ func sliceCommonPrefix(dir1, dir2 string) string {
|
||||||
return slicedDir2
|
return slicedDir2
|
||||||
}
|
}
|
||||||
|
|
||||||
func findPublicFuncsReturningHPartial(dir string) ([]Partial, error) {
|
func findPublicFuncsReturningHPartial(dir string, predicate func(partial Partial) bool) ([]Partial, error) {
|
||||||
var partials []Partial
|
var partials []Partial
|
||||||
cwd := process.GetWorkingDir()
|
cwd := process.GetWorkingDir()
|
||||||
|
|
||||||
|
|
@ -93,11 +93,14 @@ func findPublicFuncsReturningHPartial(dir string) ([]Partial, error) {
|
||||||
// Check if the package name is 'h' and type is 'Partial'.
|
// Check if the package name is 'h' and type is 'Partial'.
|
||||||
if ident, ok := selectorExpr.X.(*ast.Ident); ok && ident.Name == "h" {
|
if ident, ok := selectorExpr.X.(*ast.Ident); ok && ident.Name == "h" {
|
||||||
if selectorExpr.Sel.Name == "Partial" {
|
if selectorExpr.Sel.Name == "Partial" {
|
||||||
partials = append(partials, Partial{
|
p := Partial{
|
||||||
Package: node.Name.Name,
|
Package: node.Name.Name,
|
||||||
Import: sliceCommonPrefix(cwd, filepath.Dir(path)),
|
Import: sliceCommonPrefix(cwd, filepath.Dir(path)),
|
||||||
FuncName: funcDecl.Name.Name,
|
FuncName: funcDecl.Name.Name,
|
||||||
})
|
}
|
||||||
|
if predicate(p) {
|
||||||
|
partials = append(partials, p)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +194,10 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) {
|
||||||
path := ctx.Request().URL.Path
|
path := ctx.Request().URL.Path
|
||||||
`
|
`
|
||||||
|
|
||||||
|
if len(partials) == 0 {
|
||||||
|
body = ""
|
||||||
|
}
|
||||||
|
|
||||||
moduleName := GetModuleName()
|
moduleName := GetModuleName()
|
||||||
for _, f := range partials {
|
for _, f := range partials {
|
||||||
if f.FuncName == fName {
|
if f.FuncName == fName {
|
||||||
|
|
@ -240,16 +247,15 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) {
|
||||||
func writePartialsFile() {
|
func writePartialsFile() {
|
||||||
cwd := process.GetWorkingDir()
|
cwd := process.GetWorkingDir()
|
||||||
partialPath := filepath.Join(cwd, "partials")
|
partialPath := filepath.Join(cwd, "partials")
|
||||||
partials, err := findPublicFuncsReturningHPartial(partialPath)
|
partials, err := findPublicFuncsReturningHPartial(partialPath, func(partial Partial) bool {
|
||||||
|
return partial.FuncName != "GetPartialFromContext"
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(partials) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
builder := NewCodeBuilder(nil)
|
builder := NewCodeBuilder(nil)
|
||||||
builder.AppendLine(`// Package partials THIS FILE IS GENERATED. DO NOT EDIT.`)
|
builder.AppendLine(`// Package partials THIS FILE IS GENERATED. DO NOT EDIT.`)
|
||||||
builder.AppendLine("package load")
|
builder.AppendLine("package load")
|
||||||
|
|
@ -296,12 +302,11 @@ func writePagesFile() {
|
||||||
builder.AppendLine(`// Package pages THIS FILE IS GENERATED. DO NOT EDIT.`)
|
builder.AppendLine(`// Package pages THIS FILE IS GENERATED. DO NOT EDIT.`)
|
||||||
builder.AppendLine("package pages")
|
builder.AppendLine("package pages")
|
||||||
builder.AddImport("github.com/labstack/echo/v4")
|
builder.AddImport("github.com/labstack/echo/v4")
|
||||||
builder.AddImport("github.com/maddalax/htmgo/framework/h")
|
|
||||||
|
|
||||||
pages, _ := findPublicFuncsReturningHPage("pages")
|
pages, _ := findPublicFuncsReturningHPage("pages")
|
||||||
|
|
||||||
if len(pages) == 0 {
|
if len(pages) > 0 {
|
||||||
return
|
builder.AddImport("github.com/maddalax/htmgo/framework/h")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, page := range pages {
|
for _, page := range pages {
|
||||||
|
|
@ -58,7 +58,7 @@ func copyFile(src, dst string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyDir copies a directory recursively from src to dst.
|
// copyDir copies a directory recursively from src to dst.
|
||||||
func copyDir(srcDir, dstDir string) error {
|
func copyDir(srcDir, dstDir string, skip func(path string) bool) error {
|
||||||
// Walk the source directory tree.
|
// Walk the source directory tree.
|
||||||
return filepath.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
return filepath.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -77,6 +77,9 @@ func copyDir(srcDir, dstDir string) error {
|
||||||
return fmt.Errorf("failed to create directory: %v", err)
|
return fmt.Errorf("failed to create directory: %v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if skip != nil && skip(srcPath) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
// If it's a file, copy the file.
|
// If it's a file, copy the file.
|
||||||
err := copyFile(srcPath, dstPath)
|
err := copyFile(srcPath, dstPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -116,8 +119,15 @@ func CopyAssets() {
|
||||||
destDirDist := fmt.Sprintf("%s/dist", destDir)
|
destDirDist := fmt.Sprintf("%s/dist", destDir)
|
||||||
destDirCss := fmt.Sprintf("%s/css", destDir)
|
destDirCss := fmt.Sprintf("%s/css", destDir)
|
||||||
|
|
||||||
err := copyDir(assetDistDir, destDirDist)
|
err := copyDir(assetDistDir, destDirDist, func(path string) bool {
|
||||||
err = copyDir(assetCssDir, destDirCss)
|
return false
|
||||||
|
})
|
||||||
|
err = copyDir(assetCssDir, destDirCss, func(path string) bool {
|
||||||
|
if strings.HasSuffix(path, "tailwind.config.js") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error: %v", err)
|
log.Fatalf("Error: %v", err)
|
||||||
|
|
@ -84,6 +84,10 @@ func OnFileChange(events []*fsnotify.Event) {
|
||||||
tasks.Run = true
|
tasks.Run = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.HasAnySuffix(".md") {
|
||||||
|
tasks.Run = true
|
||||||
|
}
|
||||||
|
|
||||||
if c.HasAnySuffix("tailwind.config.js", ".css") {
|
if c.HasAnySuffix("tailwind.config.js", ".css") {
|
||||||
tasks.Run = true
|
tasks.Run = true
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package htmgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
|
|
@ -244,6 +244,17 @@ func Body(children ...Ren) *Element {
|
||||||
return Tag("body", children...)
|
return Tag("body", children...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Meta(name string, content string) Ren {
|
||||||
|
return &Element{
|
||||||
|
tag: "meta",
|
||||||
|
attributes: map[string]string{
|
||||||
|
"name": name,
|
||||||
|
"content": content,
|
||||||
|
},
|
||||||
|
children: make([]Ren, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Link(href string, rel string) Ren {
|
func Link(href string, rel string) Ren {
|
||||||
attributeMap := AttributeMap{
|
attributeMap := AttributeMap{
|
||||||
"href": href,
|
"href": href,
|
||||||
|
|
@ -291,6 +302,10 @@ func Div(children ...Ren) *Element {
|
||||||
return Tag("div", children...)
|
return Tag("div", children...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Article(children ...Ren) *Element {
|
||||||
|
return Tag("article", children...)
|
||||||
|
}
|
||||||
|
|
||||||
func ReplaceUrlHeader(url string) *Headers {
|
func ReplaceUrlHeader(url string) *Headers {
|
||||||
return NewHeaders("HX-Replace-Url", url)
|
return NewHeaders("HX-Replace-Url", url)
|
||||||
}
|
}
|
||||||
|
|
@ -363,11 +378,8 @@ func List[T any](items []T, mapper func(item T, index int) *Element) *Element {
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
func Fragment(children ...Ren) *Element {
|
func Fragment(children ...Ren) *ChildList {
|
||||||
return &Element{
|
return Children(children...)
|
||||||
tag: "",
|
|
||||||
children: children,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AttributeList(children ...*AttributeMap) *AttributeMap {
|
func AttributeList(children ...*AttributeMap) *AttributeMap {
|
||||||
|
|
|
||||||
16
htmgo-site/Taskfile.yml
Normal file
16
htmgo-site/Taskfile.yml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
run:
|
||||||
|
cmds:
|
||||||
|
- go run github.com/maddalax/htmgo/cli@latest run
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- go run github.com/maddalax/htmgo/cli@latest build
|
||||||
|
|
||||||
|
watch:
|
||||||
|
cmds:
|
||||||
|
- go run github.com/maddalax/htmgo/cli@latest watch
|
||||||
|
silent: true
|
||||||
3
htmgo-site/assets/css/input.css
Normal file
3
htmgo-site/assets/css/input.css
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
11
htmgo-site/assets/css/tailwind.config.js
Normal file
11
htmgo-site/assets/css/tailwind.config.js
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
const {join} = require("node:path");
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
const root = join(__dirname, "../../");
|
||||||
|
const contentGo = join(root, "**/*.go");
|
||||||
|
const contentJs = join(root, "**/pages/**/*.js");
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
content: [contentGo, contentJs],
|
||||||
|
plugins: [ require('@tailwindcss/typography')],
|
||||||
|
};
|
||||||
BIN
htmgo-site/assets/css/tailwindcss
Executable file
BIN
htmgo-site/assets/css/tailwindcss
Executable file
Binary file not shown.
26
htmgo-site/go.mod
Normal file
26
htmgo-site/go.mod
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
module htmgo-site
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/labstack/echo/v4 v4.12.0
|
||||||
|
github.com/maddalax/htmgo/framework v0.0.0-20240920021308-279a3c716342
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/chroma/v2 v2.2.0 // indirect
|
||||||
|
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||||
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
github.com/yuin/goldmark v1.7.4 // indirect
|
||||||
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc // indirect
|
||||||
|
golang.org/x/crypto v0.27.0 // indirect
|
||||||
|
golang.org/x/net v0.29.0 // indirect
|
||||||
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
|
golang.org/x/text v0.18.0 // indirect
|
||||||
|
)
|
||||||
53
htmgo-site/go.sum
Normal file
53
htmgo-site/go.sum
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
|
||||||
|
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||||
|
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
|
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
|
||||||
|
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||||
|
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||||
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
|
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||||
|
github.com/maddalax/htmgo/framework v0.0.0-20240920021308-279a3c716342 h1:r7Gr/9jj+vAqhgMAnWt96mNA9rXYsiyCwrqn9FcWryE=
|
||||||
|
github.com/maddalax/htmgo/framework v0.0.0-20240920021308-279a3c716342/go.mod h1:WRIlLlHJG/xB+RR84LgNFq3hwYFKXvLfEEG8RzTUH50=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
|
||||||
|
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||||
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
|
||||||
|
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
|
||||||
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
|
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/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
37
htmgo-site/internal/dirwalk/walk.go
Normal file
37
htmgo-site/internal/dirwalk/walk.go
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package dirwalk
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Page struct {
|
||||||
|
RoutePath string
|
||||||
|
FilePath string
|
||||||
|
Parts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func WalkPages(dir string) []Page {
|
||||||
|
pages := make([]Page, 0)
|
||||||
|
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name := info.Name()
|
||||||
|
if !info.IsDir() && (strings.HasSuffix(name, ".md") || strings.HasSuffix(name, ".go")) {
|
||||||
|
fullPath := strings.Replace(path, dir, "", 1)
|
||||||
|
fullPath = strings.TrimSuffix(fullPath, ".md")
|
||||||
|
pages = append(pages, Page{
|
||||||
|
RoutePath: fullPath,
|
||||||
|
FilePath: path,
|
||||||
|
Parts: h.Filter(strings.Split(fullPath, string(os.PathSeparator)), func(item string) bool {
|
||||||
|
return item != ""
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return pages
|
||||||
|
}
|
||||||
66
htmgo-site/internal/markdown/render.go
Normal file
66
htmgo-site/internal/markdown/render.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
package markdown
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/yuin/goldmark"
|
||||||
|
highlighting "github.com/yuin/goldmark-highlighting/v2"
|
||||||
|
"github.com/yuin/goldmark/extension"
|
||||||
|
"github.com/yuin/goldmark/parser"
|
||||||
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Renderer struct {
|
||||||
|
cache map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRenderer() *Renderer {
|
||||||
|
return &Renderer{cache: make(map[string]string)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Renderer) RenderFile(source string) string {
|
||||||
|
if val, ok := r.cache[source]; ok {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
o, err := os.Open(source)
|
||||||
|
defer func(o *os.File) {
|
||||||
|
_ = o.Close()
|
||||||
|
}(o)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := RenderMarkdown(o)
|
||||||
|
r.cache[source] = buf.String()
|
||||||
|
return r.cache[source]
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderMarkdown(reader io.Reader) bytes.Buffer {
|
||||||
|
md := goldmark.New(
|
||||||
|
goldmark.WithExtensions(extension.GFM),
|
||||||
|
goldmark.WithParserOptions(
|
||||||
|
parser.WithAutoHeadingID(),
|
||||||
|
),
|
||||||
|
goldmark.WithRendererOptions(
|
||||||
|
html.WithHardWraps(),
|
||||||
|
html.WithXHTML(),
|
||||||
|
html.WithUnsafe(),
|
||||||
|
),
|
||||||
|
goldmark.WithExtensions(
|
||||||
|
highlighting.NewHighlighting(
|
||||||
|
highlighting.WithStyle("github"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
source, _ := io.ReadAll(reader)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := md.Convert(source, &buf); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
30
htmgo-site/main.go
Normal file
30
htmgo-site/main.go
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"github.com/maddalax/htmgo/framework/htmgo/service"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
"htmgo-site/internal/markdown"
|
||||||
|
"htmgo-site/pages"
|
||||||
|
"htmgo-site/partials/load"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
locator := service.NewLocator()
|
||||||
|
|
||||||
|
service.Set(locator, service.Singleton, markdown.NewRenderer)
|
||||||
|
|
||||||
|
h.Start(h.AppOpts{
|
||||||
|
ServiceLocator: locator,
|
||||||
|
LiveReload: true,
|
||||||
|
Register: func(e *echo.Echo) {
|
||||||
|
e.Static("/public", "./assets/dist")
|
||||||
|
load.RegisterPartials(e)
|
||||||
|
pages.RegisterPages(e)
|
||||||
|
pages.RegisterMarkdown(e, "md", func(ctx echo.Context, path string) error {
|
||||||
|
return pages.MarkdownHandler(ctx.(*h.RequestContext), path)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
0
htmgo-site/md/docs/example.md
Normal file
0
htmgo-site/md/docs/example.md
Normal file
6
htmgo-site/md/docs/quick-start/installation.md
Normal file
6
htmgo-site/md/docs/quick-start/installation.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
### **Installation Instructions**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go install github.com/maddalax/htmgo@latest
|
||||||
|
go run htmgo template
|
||||||
|
```
|
||||||
36
htmgo-site/md/index.md
Normal file
36
htmgo-site/md/index.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
## **htmgo**
|
||||||
|
|
||||||
|
### build simple and scalable systems with go + htmx
|
||||||
|
|
||||||
|
-------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**introduction:**
|
||||||
|
|
||||||
|
htmgo is a lightweight pure go way to build interactive websites / web applications using go & htmx.
|
||||||
|
|
||||||
|
By combining the speed & simplicity of go + hypermedia attributes ([htmx](https://htmx.org)) to add interactivity to websites, all conveniently wrapped in pure go, you can build simple, fast, interactive websites without touching javascript. All compiled to a **single deployable binary**.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func IndexPage(ctx *h.RequestContext) *h.Page {
|
||||||
|
now := time.Now()
|
||||||
|
return h.NewPage(
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex gap-2"),
|
||||||
|
h.TextF("the current time is %s", now.String())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**core features:**
|
||||||
|
|
||||||
|
1. deployable single binary
|
||||||
|
2. live reload (rebuilds css, go, ent schema, and routes upon change)
|
||||||
|
3. automatic page and partial registration based on file path
|
||||||
|
4. built in tailwindcss support, no need to configure anything by default
|
||||||
|
5. plugin architecture to include optional plugins to streamline development, such as http://entgo.io
|
||||||
|
6. custom [htmx extensions](https://github.com/maddalax/htmgo/tree/b610aefa36e648b98a13823a6f8d87566120cfcc/framework/assets/js/htmxextensions) to reduce boilerplate with common tasks
|
||||||
32
htmgo-site/pages/base/root.go
Normal file
32
htmgo-site/pages/base/root.go
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"htmgo-site/partials"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Extensions() string {
|
||||||
|
extensions := []string{"path-deps", "response-targets", "mutation-error"}
|
||||||
|
if h.IsDevelopment() {
|
||||||
|
extensions = append(extensions, "livereload")
|
||||||
|
}
|
||||||
|
return strings.Join(extensions, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RootPage(children ...h.Ren) *h.Element {
|
||||||
|
return h.Html(
|
||||||
|
h.HxExtension(Extensions()),
|
||||||
|
h.Head(
|
||||||
|
h.Meta("viewport", "width=device-width, initial-scale=1"),
|
||||||
|
h.Link("/public/main.css", "stylesheet"),
|
||||||
|
h.Script("/public/htmgo.js"),
|
||||||
|
h.Raw(`
|
||||||
|
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||||
|
`),
|
||||||
|
),
|
||||||
|
h.Class("bg-neutral-50 min-h-screen"),
|
||||||
|
partials.NavBar(),
|
||||||
|
h.Fragment(children...),
|
||||||
|
)
|
||||||
|
}
|
||||||
24
htmgo-site/pages/docs.go
Normal file
24
htmgo-site/pages/docs.go
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"htmgo-site/internal/dirwalk"
|
||||||
|
"htmgo-site/pages/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DocsPage(ctx *h.RequestContext) *h.Page {
|
||||||
|
pages := dirwalk.WalkPages("md/docs")
|
||||||
|
|
||||||
|
return h.NewPage(base.RootPage(
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex flex-col p-4 justify-center items-center"),
|
||||||
|
h.List(pages, func(page dirwalk.Page, index int) *h.Element {
|
||||||
|
return MarkdownContent(ctx, page.FilePath)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
h.Div(
|
||||||
|
h.Class("min-h-12"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
16
htmgo-site/pages/generated.go
Normal file
16
htmgo-site/pages/generated.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Package pages THIS FILE IS GENERATED. DO NOT EDIT.
|
||||||
|
package pages
|
||||||
|
|
||||||
|
import "github.com/labstack/echo/v4"
|
||||||
|
import "github.com/maddalax/htmgo/framework/h"
|
||||||
|
|
||||||
|
func RegisterPages(f *echo.Echo) {
|
||||||
|
f.GET("/docs", func(ctx echo.Context) error {
|
||||||
|
cc := ctx.(*h.RequestContext)
|
||||||
|
return h.HtmlView(ctx, DocsPage(cc))
|
||||||
|
})
|
||||||
|
f.GET("/", func(ctx echo.Context) error {
|
||||||
|
cc := ctx.(*h.RequestContext)
|
||||||
|
return h.HtmlView(ctx, IndexPage(cc))
|
||||||
|
})
|
||||||
|
}
|
||||||
9
htmgo-site/pages/index.go
Normal file
9
htmgo-site/pages/index.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IndexPage(ctx *h.RequestContext) *h.Page {
|
||||||
|
return h.NewPage(MarkdownPage(ctx, "md/index.md"))
|
||||||
|
}
|
||||||
34
htmgo-site/pages/markdown.go
Normal file
34
htmgo-site/pages/markdown.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"github.com/maddalax/htmgo/framework/htmgo/service"
|
||||||
|
"htmgo-site/internal/markdown"
|
||||||
|
"htmgo-site/pages/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarkdownHandler(ctx *h.RequestContext, path string) error {
|
||||||
|
return h.HtmlView(ctx, h.NewPage(MarkdownPage(ctx, path)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarkdownPage(ctx *h.RequestContext, path string) *h.Element {
|
||||||
|
return base.RootPage(
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex flex-col p-4 justify-center items-center"),
|
||||||
|
MarkdownContent(ctx, path),
|
||||||
|
h.Div(
|
||||||
|
h.Class("min-h-12"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarkdownContent(ctx *h.RequestContext, path string) *h.Element {
|
||||||
|
renderer := service.Get[markdown.Renderer](ctx.ServiceLocator())
|
||||||
|
return h.Div(
|
||||||
|
h.Article(
|
||||||
|
h.Class("prose max-w-[95%] pt-3 md:p-4 md:max-w-2xl prose-code:text-black"),
|
||||||
|
h.Raw(renderer.RenderFile(path)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
14
htmgo-site/pages/setup.go
Normal file
14
htmgo-site/pages/setup.go
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package pages
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/labstack/echo/v4"
|
||||||
|
"htmgo-site/internal/dirwalk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterMarkdown(app *echo.Echo, dir string, handler func(ctx echo.Context, path string) error) {
|
||||||
|
for _, page := range dirwalk.WalkPages(dir) {
|
||||||
|
app.GET(page.RoutePath, func(ctx echo.Context) error {
|
||||||
|
return handler(ctx, page.FilePath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
19
htmgo-site/partials/load/generated.go
Normal file
19
htmgo-site/partials/load/generated.go
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Package partials THIS FILE IS GENERATED. DO NOT EDIT.
|
||||||
|
package load
|
||||||
|
|
||||||
|
import "github.com/maddalax/htmgo/framework/h"
|
||||||
|
import "github.com/labstack/echo/v4"
|
||||||
|
|
||||||
|
func GetPartialFromContext(ctx echo.Context) *h.Partial {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterPartials(f *echo.Echo) {
|
||||||
|
f.Any("htmgo-site/partials*", func(ctx echo.Context) error {
|
||||||
|
partial := GetPartialFromContext(ctx)
|
||||||
|
if partial == nil {
|
||||||
|
return ctx.NoContent(404)
|
||||||
|
}
|
||||||
|
return h.PartialView(ctx, partial)
|
||||||
|
})
|
||||||
|
}
|
||||||
60
htmgo-site/partials/navbar.go
Normal file
60
htmgo-site/partials/navbar.go
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
package partials
|
||||||
|
|
||||||
|
import "github.com/maddalax/htmgo/framework/h"
|
||||||
|
|
||||||
|
type NavItem struct {
|
||||||
|
Name string
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NavBar() *h.Element {
|
||||||
|
|
||||||
|
star := h.Raw(`
|
||||||
|
<a
|
||||||
|
class="github-button"
|
||||||
|
href="https://github.com/maddalax/htmgo"
|
||||||
|
data-color-scheme="no-preference: light; light: light; dark: dark;"
|
||||||
|
data-icon="octicon-star"
|
||||||
|
data-size="large"
|
||||||
|
data-show-count="true"
|
||||||
|
aria-label="Star maddalax/htmgo on GitHub">Star</a>
|
||||||
|
`)
|
||||||
|
|
||||||
|
navItems := []NavItem{
|
||||||
|
{Name: "Docs", Url: "/docs"},
|
||||||
|
{Name: "Examples", Url: "/examples"},
|
||||||
|
}
|
||||||
|
|
||||||
|
return h.Nav(
|
||||||
|
h.Class("bg-neutral-100 border border-b-slate-300 p-4 md:p-3"),
|
||||||
|
h.Div(
|
||||||
|
h.Class("max-w-[95%] md:max-w-prose mx-auto"),
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex justify-between items-center"),
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex items-center"),
|
||||||
|
h.A(
|
||||||
|
h.Boost(),
|
||||||
|
h.Class("text-2xl"),
|
||||||
|
h.Href("/"),
|
||||||
|
h.Text("htmgo"),
|
||||||
|
)),
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex gap-4 items-center"),
|
||||||
|
h.List(navItems, func(item NavItem, index int) *h.Element {
|
||||||
|
return h.Div(
|
||||||
|
h.Class("flex items-center"),
|
||||||
|
h.A(
|
||||||
|
h.Boost(),
|
||||||
|
h.Class(""),
|
||||||
|
h.Href(item.Url),
|
||||||
|
h.Text(item.Name),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
h.Div(h.Class("ml-2"), star),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
23
htmgo-site/partials/sidebar.go
Normal file
23
htmgo-site/partials/sidebar.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package partials
|
||||||
|
|
||||||
|
import "github.com/maddalax/htmgo/framework/h"
|
||||||
|
|
||||||
|
func SideBar() *h.Element {
|
||||||
|
return h.Div(
|
||||||
|
h.Class("w-40 top-[57px] absolute min-h-screen bg-neutral-50 border border-r-slate-300 p-3"),
|
||||||
|
h.Div(
|
||||||
|
h.Class("max-w-prose mx-auto"),
|
||||||
|
h.Div(
|
||||||
|
h.Class("flex flex-col gap-4"),
|
||||||
|
h.A(
|
||||||
|
h.Href("/docs"),
|
||||||
|
h.Text("Docs"),
|
||||||
|
),
|
||||||
|
h.A(
|
||||||
|
h.Href("/examples"),
|
||||||
|
h.Text("Examples"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
"experimental": {
|
"experimental": {
|
||||||
"configFile": null,
|
"configFile": null,
|
||||||
"classRegex": [[
|
"classRegex": [[
|
||||||
"tailwind|Class|h.Class\\(([^)]*)\\)",
|
"Class|h.Class\\(([^)]*)\\)",
|
||||||
"[\"'`]([^\"'`]*).*?[\"'`]"
|
"[\"'`]([^\"'`]*).*?[\"'`]"
|
||||||
]]
|
]]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue