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 (
|
||||
"sync"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
module github.com/maddalax/htmgo/cli
|
||||
module github.com/maddalax/htmgo/cli/htmgo
|
||||
|
||||
go 1.23.0
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package htmgo
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
|
@ -20,7 +20,7 @@ func main() {
|
|||
done := RegisterSignals()
|
||||
|
||||
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 {
|
||||
commandMap[command] = flag.NewFlagSet(command, flag.ExitOnError)
|
||||
|
|
@ -100,6 +100,8 @@ func main() {
|
|||
downloadtemplate.DownloadTemplate(fmt.Sprintf("./%s", text))
|
||||
} else if taskName == "build" {
|
||||
run.Build()
|
||||
} else if taskName == "generate" {
|
||||
astgen.GenAst(process.ExitOnError)
|
||||
} else {
|
||||
fmt.Println(fmt.Sprintf("Usage: htmgo [%s]", strings.Join(commands, " | ")))
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package htmgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -56,7 +56,7 @@ func sliceCommonPrefix(dir1, dir2 string) string {
|
|||
return slicedDir2
|
||||
}
|
||||
|
||||
func findPublicFuncsReturningHPartial(dir string) ([]Partial, error) {
|
||||
func findPublicFuncsReturningHPartial(dir string, predicate func(partial Partial) bool) ([]Partial, error) {
|
||||
var partials []Partial
|
||||
cwd := process.GetWorkingDir()
|
||||
|
||||
|
|
@ -93,11 +93,14 @@ func findPublicFuncsReturningHPartial(dir string) ([]Partial, error) {
|
|||
// Check if the package name is 'h' and type is 'Partial'.
|
||||
if ident, ok := selectorExpr.X.(*ast.Ident); ok && ident.Name == "h" {
|
||||
if selectorExpr.Sel.Name == "Partial" {
|
||||
partials = append(partials, Partial{
|
||||
p := Partial{
|
||||
Package: node.Name.Name,
|
||||
Import: sliceCommonPrefix(cwd, filepath.Dir(path)),
|
||||
FuncName: funcDecl.Name.Name,
|
||||
})
|
||||
}
|
||||
if predicate(p) {
|
||||
partials = append(partials, p)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -191,6 +194,10 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) {
|
|||
path := ctx.Request().URL.Path
|
||||
`
|
||||
|
||||
if len(partials) == 0 {
|
||||
body = ""
|
||||
}
|
||||
|
||||
moduleName := GetModuleName()
|
||||
for _, f := range partials {
|
||||
if f.FuncName == fName {
|
||||
|
|
@ -240,16 +247,15 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) {
|
|||
func writePartialsFile() {
|
||||
cwd := process.GetWorkingDir()
|
||||
partialPath := filepath.Join(cwd, "partials")
|
||||
partials, err := findPublicFuncsReturningHPartial(partialPath)
|
||||
partials, err := findPublicFuncsReturningHPartial(partialPath, func(partial Partial) bool {
|
||||
return partial.FuncName != "GetPartialFromContext"
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(partials) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
builder := NewCodeBuilder(nil)
|
||||
builder.AppendLine(`// Package partials THIS FILE IS GENERATED. DO NOT EDIT.`)
|
||||
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")
|
||||
builder.AddImport("github.com/labstack/echo/v4")
|
||||
builder.AddImport("github.com/maddalax/htmgo/framework/h")
|
||||
|
||||
pages, _ := findPublicFuncsReturningHPage("pages")
|
||||
|
||||
if len(pages) == 0 {
|
||||
return
|
||||
if len(pages) > 0 {
|
||||
builder.AddImport("github.com/maddalax/htmgo/framework/h")
|
||||
}
|
||||
|
||||
for _, page := range pages {
|
||||
|
|
@ -58,7 +58,7 @@ func copyFile(src, dst string) error {
|
|||
}
|
||||
|
||||
// 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.
|
||||
return filepath.Walk(srcDir, func(srcPath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
|
|
@ -77,6 +77,9 @@ func copyDir(srcDir, dstDir string) error {
|
|||
return fmt.Errorf("failed to create directory: %v", err)
|
||||
}
|
||||
} else {
|
||||
if skip != nil && skip(srcPath) {
|
||||
return nil
|
||||
}
|
||||
// If it's a file, copy the file.
|
||||
err := copyFile(srcPath, dstPath)
|
||||
if err != nil {
|
||||
|
|
@ -116,8 +119,15 @@ func CopyAssets() {
|
|||
destDirDist := fmt.Sprintf("%s/dist", destDir)
|
||||
destDirCss := fmt.Sprintf("%s/css", destDir)
|
||||
|
||||
err := copyDir(assetDistDir, destDirDist)
|
||||
err = copyDir(assetCssDir, destDirCss)
|
||||
err := copyDir(assetDistDir, destDirDist, func(path string) bool {
|
||||
return false
|
||||
})
|
||||
err = copyDir(assetCssDir, destDirCss, func(path string) bool {
|
||||
if strings.HasSuffix(path, "tailwind.config.js") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Error: %v", err)
|
||||
|
|
@ -84,6 +84,10 @@ func OnFileChange(events []*fsnotify.Event) {
|
|||
tasks.Run = true
|
||||
}
|
||||
|
||||
if c.HasAnySuffix(".md") {
|
||||
tasks.Run = true
|
||||
}
|
||||
|
||||
if c.HasAnySuffix("tailwind.config.js", ".css") {
|
||||
tasks.Run = true
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package htmgo
|
||||
|
||||
import (
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
|
@ -244,6 +244,17 @@ func Body(children ...Ren) *Element {
|
|||
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 {
|
||||
attributeMap := AttributeMap{
|
||||
"href": href,
|
||||
|
|
@ -291,6 +302,10 @@ func Div(children ...Ren) *Element {
|
|||
return Tag("div", children...)
|
||||
}
|
||||
|
||||
func Article(children ...Ren) *Element {
|
||||
return Tag("article", children...)
|
||||
}
|
||||
|
||||
func ReplaceUrlHeader(url string) *Headers {
|
||||
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
|
||||
}
|
||||
|
||||
func Fragment(children ...Ren) *Element {
|
||||
return &Element{
|
||||
tag: "",
|
||||
children: children,
|
||||
}
|
||||
func Fragment(children ...Ren) *ChildList {
|
||||
return Children(children...)
|
||||
}
|
||||
|
||||
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": {
|
||||
"configFile": null,
|
||||
"classRegex": [[
|
||||
"tailwind|Class|h.Class\\(([^)]*)\\)",
|
||||
"Class|h.Class\\(([^)]*)\\)",
|
||||
"[\"'`]([^\"'`]*).*?[\"'`]"
|
||||
]]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue