replace fiber with echo

This commit is contained in:
maddalax 2024-09-17 12:13:22 -05:00
parent 16cdb66e0d
commit fe409c363c
37 changed files with 281 additions and 190 deletions

View file

@ -5,6 +5,7 @@ import (
"flag" "flag"
"fmt" "fmt"
"github.com/maddalax/htmgo/cli/tasks/astgen" "github.com/maddalax/htmgo/cli/tasks/astgen"
"github.com/maddalax/htmgo/cli/tasks/copyassets"
"github.com/maddalax/htmgo/cli/tasks/css" "github.com/maddalax/htmgo/cli/tasks/css"
"github.com/maddalax/htmgo/cli/tasks/downloadtemplate" "github.com/maddalax/htmgo/cli/tasks/downloadtemplate"
"github.com/maddalax/htmgo/cli/tasks/process" "github.com/maddalax/htmgo/cli/tasks/process"
@ -56,6 +57,7 @@ func main() {
if taskName == "watch" { if taskName == "watch" {
os.Setenv("ENV", "development") os.Setenv("ENV", "development")
os.Setenv("WATCH_MODE", "true") os.Setenv("WATCH_MODE", "true")
copyassets.CopyAssets()
astgen.GenAst(true) astgen.GenAst(true)
css.GenerateCss(true) css.GenerateCss(true)
run.EntGenerate() run.EntGenerate()

View file

@ -210,7 +210,7 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) {
f := Function{ f := Function{
Name: fName, Name: fName,
Parameters: []NameType{ Parameters: []NameType{
{Name: "ctx", Type: "*fiber.Ctx"}, {Name: "ctx", Type: "echo.Context"},
}, },
Return: []ReturnType{ Return: []ReturnType{
{Type: "*h.Partial"}, {Type: "*h.Partial"},
@ -221,11 +221,11 @@ func buildGetPartialFromContext(builder *CodeBuilder, partials []Partial) {
builder.Append(builder.BuildFunction(f)) builder.Append(builder.BuildFunction(f))
registerFunction := fmt.Sprintf(` registerFunction := fmt.Sprintf(`
func RegisterPartials(f *fiber.App) { func RegisterPartials(f *echo.Echo) {
f.All("%s/partials*", func(ctx *fiber.Ctx) error { f.Any("%s/partials*", func(ctx echo.Context) error {
partial := GetPartialFromContext(ctx) partial := GetPartialFromContext(ctx)
if partial == nil { if partial == nil {
return ctx.SendStatus(404) return ctx.NoContent(404)
} }
return h.PartialView(ctx, partial) return h.PartialView(ctx, partial)
}) })
@ -252,7 +252,7 @@ func writePartialsFile() {
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")
builder.AddImport("github.com/maddalax/htmgo/framework/h") builder.AddImport("github.com/maddalax/htmgo/framework/h")
builder.AddImport("github.com/gofiber/fiber/v2") builder.AddImport("github.com/labstack/echo/v4")
moduleName := GetModuleName() moduleName := GetModuleName()
for _, partial := range partials { for _, partial := range partials {
@ -293,7 +293,7 @@ func writePagesFile() {
builder := NewCodeBuilder(nil) builder := NewCodeBuilder(nil)
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/gofiber/fiber/v2") builder.AddImport("github.com/labstack/echo/v4")
builder.AddImport("github.com/maddalax/htmgo/framework/h") builder.AddImport("github.com/maddalax/htmgo/framework/h")
pages, _ := findPublicFuncsReturningHPage("pages") pages, _ := findPublicFuncsReturningHPage("pages")
@ -319,7 +319,7 @@ func writePagesFile() {
} }
body += fmt.Sprintf(` body += fmt.Sprintf(`
f.Get("%s", func(ctx *fiber.Ctx) error { f.GET("%s", func(ctx echo.Context) error {
return h.HtmlView(ctx, %s(ctx)) return h.HtmlView(ctx, %s(ctx))
}) })
`, formatRoute(page.Path), call) `, formatRoute(page.Path), call)
@ -328,7 +328,7 @@ func writePagesFile() {
f := Function{ f := Function{
Name: fName, Name: fName,
Parameters: []NameType{ Parameters: []NameType{
{Name: "f", Type: "*fiber.App"}, {Name: "f", Type: "*echo.Echo"},
}, },
Body: body, Body: body,
} }

View file

@ -2,12 +2,14 @@ package copyassets
import ( import (
"fmt" "fmt"
"github.com/maddalax/htmgo/cli/tasks/module"
"github.com/maddalax/htmgo/cli/tasks/process" "github.com/maddalax/htmgo/cli/tasks/process"
"golang.org/x/mod/modfile" "golang.org/x/mod/modfile"
"io" "io"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
func getModuleVersion(modulePath string) (string, error) { func getModuleVersion(modulePath string) (string, error) {
@ -85,17 +87,25 @@ func copyDir(srcDir, dstDir string) error {
} }
func CopyAssets() { func CopyAssets() {
modulePath := "github.com/maddalax/htmgo/framework" moduleName := "github.com/maddalax/htmgo/framework"
version, err := getModuleVersion(modulePath) modulePath := module.GetDependencyPath(moduleName)
if err != nil {
log.Fatalf("Error: %v", err) assetDir := ""
} // Is hosted version and not local version from .work file
dirname, err := os.UserHomeDir() if strings.HasPrefix(modulePath, "github.com/") {
if err != nil { version, err := getModuleVersion(modulePath)
log.Fatal(err) if err != nil {
log.Fatalf("Error: %v", err)
}
dirname, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}
assetDir = fmt.Sprintf("%s/go/pkg/mod/%s@%s/assets", dirname, modulePath, version)
} else {
assetDir = fmt.Sprintf("%s/assets", modulePath)
} }
assetDir := fmt.Sprintf("%s/go/pkg/mod/%s@%s/assets", dirname, modulePath, version)
assetDistDir := fmt.Sprintf("%s/dist", assetDir) assetDistDir := fmt.Sprintf("%s/dist", assetDir)
assetCssDir := fmt.Sprintf("%s/css", assetDir) assetCssDir := fmt.Sprintf("%s/css", assetDir)
@ -105,7 +115,7 @@ 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)
err = copyDir(assetCssDir, destDirCss) err = copyDir(assetCssDir, destDirCss)
if err != nil { if err != nil {

View file

@ -0,0 +1,23 @@
package module
import (
"fmt"
"os/exec"
"strings"
)
func GetDependencyPath(dep string) string {
cmd := exec.Command("go", "list", "-m", "-f", "{{.Dir}}", dep)
// Run the command and capture the output
output, err := cmd.CombinedOutput() // Use CombinedOutput to capture both stdout and stderr
if err != nil {
fmt.Printf("Command execution failed: %v\n", err)
}
// Convert output to string
dir := strings.TrimSuffix(string(output), "\n")
if strings.Contains(dir, "not a known dependency") {
return dep
}
return dir
}

View file

@ -161,13 +161,14 @@ func Run(command string, exitOnError bool) error {
return nil return nil
} }
if strings.Contains(err.Error(), "signal: killed") {
return nil
}
if exitOnError { if exitOnError {
log.Println(fmt.Sprintf("error: %v", err)) log.Println(fmt.Sprintf("error: %v", err))
os.Exit(1) os.Exit(1)
} }
if strings.Contains(err.Error(), "signal: killed") {
return nil
}
return err return err
} }

View file

@ -4,7 +4,6 @@ go 1.23.0
require ( require (
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
github.com/gofiber/fiber/v2 v2.52.5 // indirect
github.com/google/uuid v1.5.0 // indirect github.com/google/uuid v1.5.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect github.com/klauspost/compress v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect

File diff suppressed because one or more lines are too long

View file

@ -4,6 +4,7 @@ import "./htmxextensions/trigger-children";
import "./htmxextensions/debug"; import "./htmxextensions/debug";
import "./htmxextensions/response-targets"; import "./htmxextensions/response-targets";
import "./htmxextensions/mutation-error"; import "./htmxextensions/mutation-error";
import "./htmxextensions/livereload"
function watchUrl(callback: (oldUrl: string, newUrl: string) => void) { function watchUrl(callback: (oldUrl: string, newUrl: string) => void) {
let lastUrl = window.location.href; let lastUrl = window.location.href;

View file

@ -0,0 +1,33 @@
import htmx from "htmx.org";
import {createWebSocketClient} from "../util/ws";
let lastVersion = "";
htmx.defineExtension("livereload", {
init: function () {
const host = window.location.host;
console.log('livereload extension initialized.');
createWebSocketClient({
url: `ws://${host}/dev/livereload`,
onOpen: () => {
console.log('LiveReload connected.');
},
onMessage: (message) => {
if(lastVersion === "") {
lastVersion = message;
}
if(lastVersion !== message) {
window.location.reload();
}
},
onError: (error) => {
},
onClose: () => {
}
})
},
// @ts-ignore
onEvent: function (name, evt) {
},
});

View file

@ -0,0 +1,40 @@
type WsOpts = {
url: string;
reconnectInterval?: number;
onOpen?: () => void;
onMessage: (message: string) => void;
onError?: (error: Event) => void;
onClose?: () => void;
}
export function createWebSocketClient(opts: WsOpts) {
let socket: WebSocket | null = null;
const connect = (tries: number) => {
socket = new WebSocket(opts.url);
// Handle connection open
socket.onopen = () => {
};
// Handle incoming messages
socket.onmessage = (event) => {
opts.onMessage(event.data)
};
// Handle connection errors
socket.onerror = (error) => {
};
// Handle connection close and attempt reconnection
socket.onclose = () => {
console.log('WebSocket connection closed. Attempting to reconnect...');
let interval = tries * (opts.reconnectInterval || 50);
setTimeout(() => connect(tries + 1), interval);
};
};
connect(1);
const sendMessage = (message: string) => {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(message);
} else {
setTimeout(() => sendMessage(message), 100);
}
};
return { sendMessage };
}

View file

@ -5,20 +5,27 @@ go 1.23.0
require ( require (
github.com/dave/jennifer v1.7.1 github.com/dave/jennifer v1.7.1
github.com/fsnotify/fsnotify v1.7.0 github.com/fsnotify/fsnotify v1.7.0
github.com/gofiber/fiber/v2 v2.52.5
golang.org/x/net v0.29.0 golang.org/x/net v0.29.0
) )
require ( require (
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.1.0 // indirect
github.com/google/uuid v1.5.0 // indirect github.com/fasthttp/websocket v1.5.10 // indirect
github.com/klauspost/compress v1.17.0 // indirect github.com/gofiber/contrib/websocket v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/labstack/echo/v4 v4.12.0 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect github.com/valyala/fasthttp v1.55.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/sys v0.25.0 // indirect golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.18.0 // indirect
) )

View file

@ -2,7 +2,7 @@ package h
import ( import (
"fmt" "fmt"
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/util/process" "github.com/maddalax/htmgo/framework/util/process"
"log/slog" "log/slog"
"time" "time"
@ -10,7 +10,7 @@ import (
type App struct { type App struct {
LiveReload bool LiveReload bool
Fiber *fiber.App Echo *echo.Echo
} }
var instance *App var instance *App
@ -22,21 +22,21 @@ func GetApp() *App {
return instance return instance
} }
func Start(app *fiber.App, opts App) { func Start(app *echo.Echo, opts App) {
instance = &opts instance = &opts
instance.start(app) instance.start(app)
} }
func (a App) start(app *fiber.App) { func (a App) start(app *echo.Echo) {
a.Fiber = app a.Echo = app
if a.LiveReload { if a.LiveReload {
AddLiveReloadHandler("/livereload", a.Fiber) AddLiveReloadHandler("/dev/livereload", a.Echo)
} }
port := ":3000" port := ":3000"
err := a.Fiber.Listen(port) err := a.Echo.Start(port)
if err != nil { if err != nil {
// If we are in watch mode, just try to kill any processes holding that port // If we are in watch mode, just try to kill any processes holding that port
@ -45,7 +45,7 @@ func (a App) start(app *fiber.App) {
slog.Info("Port already in use, trying to kill the process and start again") slog.Info("Port already in use, trying to kill the process and start again")
process.RunOrExit(fmt.Sprintf("kill -9 $(lsof -t -i%s)", port)) process.RunOrExit(fmt.Sprintf("kill -9 $(lsof -t -i%s)", port))
time.Sleep(time.Millisecond * 50) time.Sleep(time.Millisecond * 50)
err = a.Fiber.Listen(port) err = a.Echo.Start(port)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -55,25 +55,16 @@ func (a App) start(app *fiber.App) {
} }
} }
func HtmlView(c *fiber.Ctx, page *Page) error { func HtmlView(c echo.Context, page *Page) error {
root := page.Root.Render() root := page.Root.Render()
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML) return c.HTML(200,
if GetApp().LiveReload && root.tag == "html" {
root.AppendChild(
LiveReload(),
)
}
return c.SendString(
Render( Render(
root, root,
), ),
) )
} }
func PartialViewWithHeaders(c *fiber.Ctx, headers *Headers, partial *Partial) error { func PartialViewWithHeaders(c echo.Context, headers *Headers, partial *Partial) error {
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
if partial.Headers != nil { if partial.Headers != nil {
for s, a := range *partial.Headers { for s, a := range *partial.Headers {
c.Set(s, a) c.Set(s, a)
@ -86,22 +77,22 @@ func PartialViewWithHeaders(c *fiber.Ctx, headers *Headers, partial *Partial) er
} }
} }
return c.SendString( return c.HTML(200,
Render( Render(
partial.Root, partial.Root,
), ),
) )
} }
func PartialView(c *fiber.Ctx, partial *Partial) error { func PartialView(c echo.Context, partial *Partial) error {
c.Set(fiber.HeaderContentType, fiber.MIMETextHTML) c.Set(echo.HeaderContentType, echo.MIMETextHTML)
if partial.Headers != nil { if partial.Headers != nil {
for s, a := range *partial.Headers { for s, a := range *partial.Headers {
c.Set(s, a) c.Set(s, a)
} }
} }
return c.SendString( return c.HTML(200,
Render( Render(
partial.Root, partial.Root,
), ),

View file

@ -1,7 +1,7 @@
package h package h
import ( import (
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"html" "html"
"net/http" "net/http"
"reflect" "reflect"
@ -51,10 +51,10 @@ func NewPartial(root Renderable) *Partial {
} }
} }
func GetPartialPath(partial func(ctx *fiber.Ctx) *Partial) string { func GetPartialPath(partial func(ctx echo.Context) *Partial) string {
return runtime.FuncForPC(reflect.ValueOf(partial).Pointer()).Name() return runtime.FuncForPC(reflect.ValueOf(partial).Pointer()).Name()
} }
func GetPartialPathWithQs(partial func(ctx *fiber.Ctx) *Partial, qs string) string { func GetPartialPathWithQs(partial func(ctx echo.Context) *Partial, qs string) string {
return html.EscapeString(GetPartialPath(partial) + "?" + qs) return html.EscapeString(GetPartialPath(partial) + "?" + qs)
} }

View file

@ -1,33 +1,30 @@
package h package h
import ( import (
"github.com/gofiber/fiber/v2" "github.com/google/uuid"
"strconv" "github.com/labstack/echo/v4"
"golang.org/x/net/websocket"
"time" "time"
) )
var Version = time.Now().Nanosecond() var Version = uuid.NewString()
func LiveReloadHandler(c *fiber.Ctx) error { func handler(c echo.Context) error {
v := strconv.FormatInt(int64(Version), 10) websocket.Handler(func(ws *websocket.Conn) {
current := c.Cookies("version", v) defer ws.Close()
_ = websocket.Message.Send(ws, Version)
if current != v { // keep ws alive
c.Set("HX-Refresh", "true") for {
} err := websocket.Message.Send(ws, Version)
if err != nil {
c.Cookie(&fiber.Cookie{ return
Name: "version", }
Value: v, time.Sleep(500 * time.Millisecond)
}) }
}).ServeHTTP(c.Response(), c.Request())
return c.SendString("") return nil
} }
func LiveReload() Renderable { func AddLiveReloadHandler(path string, app *echo.Echo) {
return Div(Get("/livereload"), Trigger("every 200ms")) app.GET(path, handler)
}
func AddLiveReloadHandler(path string, app *fiber.App) {
app.Get(path, LiveReloadHandler)
} }

View file

@ -3,7 +3,7 @@ package h
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"html" "html"
"net/url" "net/url"
"strings" "strings"
@ -101,11 +101,11 @@ func Get(path string) Renderable {
return Attribute("hx-get", path) return Attribute("hx-get", path)
} }
func GetPartial(partial func(ctx *fiber.Ctx) *Partial) Renderable { func GetPartial(partial func(ctx echo.Context) *Partial) Renderable {
return Get(GetPartialPath(partial)) return Get(GetPartialPath(partial))
} }
func GetPartialWithQs(partial func(ctx *fiber.Ctx) *Partial, qs string) Renderable { func GetPartialWithQs(partial func(ctx echo.Context) *Partial, qs string) Renderable {
return Get(GetPartialPathWithQs(partial, qs)) return Get(GetPartialPathWithQs(partial, qs))
} }
@ -119,13 +119,13 @@ type ReloadParams struct {
Children Renderable Children Renderable
} }
func ViewOnLoad(partial func(ctx *fiber.Ctx) *Partial) Renderable { func ViewOnLoad(partial func(ctx echo.Context) *Partial) Renderable {
return View(partial, ReloadParams{ return View(partial, ReloadParams{
Triggers: CreateTriggers("load"), Triggers: CreateTriggers("load"),
}) })
} }
func View(partial func(ctx *fiber.Ctx) *Partial, params ReloadParams) Renderable { func View(partial func(ctx echo.Context) *Partial, params ReloadParams) Renderable {
return Div(Attributes(map[string]string{ return Div(Attributes(map[string]string{
"hx-get": GetPartialPath(partial), "hx-get": GetPartialPath(partial),
"hx-trigger": strings.Join(params.Triggers, ", "), "hx-trigger": strings.Join(params.Triggers, ", "),
@ -133,7 +133,7 @@ func View(partial func(ctx *fiber.Ctx) *Partial, params ReloadParams) Renderable
}), params.Children) }), params.Children)
} }
func PartialWithTriggers(partial func(ctx *fiber.Ctx) *Partial, triggers ...string) Renderable { func PartialWithTriggers(partial func(ctx echo.Context) *Partial, triggers ...string) Renderable {
return Div(Attributes(map[string]string{ return Div(Attributes(map[string]string{
"hx-get": GetPartialPath(partial), "hx-get": GetPartialPath(partial),
"hx-trigger": strings.Join(triggers, ", "), "hx-trigger": strings.Join(triggers, ", "),
@ -283,8 +283,8 @@ func CombineHeaders(headers ...*Headers) *Headers {
return &m return &m
} }
func CurrentPath(ctx *fiber.Ctx) string { func CurrentPath(ctx echo.Context) string {
current := ctx.Get("Hx-Current-Url") current := ctx.Request().Header.Get("Hx-Current-Url")
parsed, err := url.Parse(current) parsed, err := url.Parse(current)
if err != nil { if err != nil {
return "" return ""
@ -292,8 +292,8 @@ func CurrentPath(ctx *fiber.Ctx) string {
return parsed.Path return parsed.Path
} }
func PushQsHeader(ctx *fiber.Ctx, key string, value string) *Headers { func PushQsHeader(ctx echo.Context, key string, value string) *Headers {
current := ctx.Get("Hx-Current-Url") current := ctx.Request().Header.Get("Hx-Current-Url")
parsed, err := url.Parse(current) parsed, err := url.Parse(current)
if err != nil { if err != nil {
return NewHeaders() return NewHeaders()
@ -512,11 +512,11 @@ func IfElseLazy(condition bool, cb1 func() Renderable, cb2 func() Renderable) Re
} }
} }
func GetTriggerName(ctx *fiber.Ctx) string { func GetTriggerName(ctx echo.Context) string {
return ctx.Get("HX-Trigger-Name") return ctx.Request().Header.Get("HX-Trigger-Name")
} }
func IfHtmxRequest(ctx *fiber.Ctx, node Renderable) Renderable { func IfHtmxRequest(ctx echo.Context, node Renderable) Renderable {
if ctx.Get("HX-Request") != "" { if ctx.Get("HX-Request") != "" {
return node return node
} }
@ -535,11 +535,11 @@ func NewSwap(selector string, content *Node) SwapArg {
} }
} }
func Swap(ctx *fiber.Ctx, content Renderable) Renderable { func Swap(ctx echo.Context, content Renderable) Renderable {
return SwapWithSelector(ctx, "", content) return SwapWithSelector(ctx, "", content)
} }
func SwapWithSelector(ctx *fiber.Ctx, selector string, content Renderable) Renderable { func SwapWithSelector(ctx echo.Context, selector string, content Renderable) Renderable {
if ctx == nil || ctx.Get("HX-Request") == "" { if ctx == nil || ctx.Get("HX-Request") == "" {
return Empty() return Empty()
} }
@ -547,7 +547,7 @@ func SwapWithSelector(ctx *fiber.Ctx, selector string, content Renderable) Rende
return c.AppendChild(OutOfBandSwap(selector)) return c.AppendChild(OutOfBandSwap(selector))
} }
func SwapMany(ctx *fiber.Ctx, args ...SwapArg) Renderable { func SwapMany(ctx echo.Context, args ...SwapArg) Renderable {
if ctx.Get("HX-Request") == "" { if ctx.Get("HX-Request") == "" {
return Empty() return Empty()
} }

View file

@ -2,7 +2,7 @@ package h
import ( import (
"encoding/json" "encoding/json"
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"net/url" "net/url"
) )
@ -33,10 +33,10 @@ func JsonSerialize(data any) string {
return string(serialized) return string(serialized)
} }
func GetQueryParam(ctx *fiber.Ctx, key string) string { func GetQueryParam(ctx echo.Context, key string) string {
value := ctx.Query(key) value := ctx.QueryParam(key)
if value == "" { if value == "" {
current := ctx.Get("Hx-Current-Url") current := ctx.Request().Header.Get("Hx-Current-Url")
if current != "" { if current != "" {
u, err := url.Parse(current) u, err := url.Parse(current)
if err == nil { if err == nil {

View file

@ -2,8 +2,8 @@ package patient
import ( import (
"errors" "errors"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4"
"starter-template/database" "starter-template/database"
"time" "time"
) )
@ -16,10 +16,10 @@ type Patient struct {
} }
type Service struct { type Service struct {
ctx *fiber.Ctx ctx echo.Context
} }
func NewService(ctx *fiber.Ctx) *Service { func NewService(ctx echo.Context) *Service {
return &Service{} return &Service{}
} }

View file

@ -3,7 +3,6 @@ module sandbox
go 1.23.0 go 1.23.0
require ( require (
github.com/gofiber/fiber/v2 v2.52.5
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/maddalax/htmgo/framework v0.0.0-20240914010415-2397bf9fb057 github.com/maddalax/htmgo/framework v0.0.0-20240914010415-2397bf9fb057
github.com/maddalax/htmgo/framework-ui v0.0.0-20240914003619-c256552b2143 github.com/maddalax/htmgo/framework-ui v0.0.0-20240914003619-c256552b2143

View file

@ -2,8 +2,8 @@ github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= github.com/labstack/echo/v4 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= github.com/labstack/echo/v4 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=

View file

@ -1,8 +1,8 @@
package main package main
import ( import (
"github.com/gofiber/fiber/v2"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"log" "log"
"starter-template/pages" "starter-template/pages"
@ -11,16 +11,16 @@ import (
) )
func main() { func main() {
f := fiber.New() f := echo.New()
f.Static("/public", "./assets/dist") f.Static("/public", "./assets/dist")
f.Use(func(ctx *fiber.Ctx) error { f.Use(func(ctx echo.Context) error {
if ctx.Cookies("htmgo-session") != "" { if ctx.Cookies("htmgo-session") != "" {
return ctx.Next() return ctx.Next()
} }
id := ctx.IP() + uuid.NewString() id := ctx.IP() + uuid.NewString()
ctx.Cookie(&fiber.Cookie{ ctx.Cookie(&echo.Cookie{
Name: "htmgo-session", Name: "htmgo-session",
Value: id, Value: id,
SessionOnly: true, SessionOnly: true,
@ -28,7 +28,7 @@ func main() {
return ctx.Next() return ctx.Next()
}) })
f.Use(func(ctx *fiber.Ctx) error { f.Use(func(ctx echo.Context) error {
if ctx.Path() == "/livereload" { if ctx.Path() == "/livereload" {
return ctx.Next() return ctx.Next()
} }

View file

@ -1,20 +1,20 @@
// Package pages THIS FILE IS GENERATED. DO NOT EDIT. // Package pages THIS FILE IS GENERATED. DO NOT EDIT.
package pages package pages
import "github.com/gofiber/fiber/v2" import "github.com/labstack/echo/v4"
import "github.com/maddalax/htmgo/framework/h" import "github.com/maddalax/htmgo/framework/h"
func RegisterPages(f *fiber.App) { func RegisterPages(f *echo.Echo) {
f.Get("/", func(ctx *fiber.Ctx) error { f.Get("/", func(ctx echo.Context) error {
return h.HtmlView(ctx, IndexPage(ctx)) return h.HtmlView(ctx, IndexPage(ctx))
}) })
f.Get("/news/:id", func(ctx *fiber.Ctx) error { f.Get("/news/:id", func(ctx echo.Context) error {
return h.HtmlView(ctx, Test(ctx)) return h.HtmlView(ctx, Test(ctx))
}) })
f.Get("/news", func(ctx *fiber.Ctx) error { f.Get("/news", func(ctx echo.Context) error {
return h.HtmlView(ctx, ListPage(ctx)) return h.HtmlView(ctx, ListPage(ctx))
}) })
f.Get("/patients", func(ctx *fiber.Ctx) error { f.Get("/patients", func(ctx echo.Context) error {
return h.HtmlView(ctx, PatientsIndex(ctx)) return h.HtmlView(ctx, PatientsIndex(ctx))
}) })
} }

View file

@ -2,13 +2,13 @@ package pages
import ( import (
"fmt" "fmt"
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"os" "os"
"time" "time"
) )
func IndexPage(c *fiber.Ctx) *h.Page { func IndexPage(c echo.Context) *h.Page {
return h.NewPage(h.Html( return h.NewPage(h.Html(
h.Class("bg-background flex flex-col items-center"), h.Class("bg-background flex flex-col items-center"),
h.Head( h.Head(

View file

@ -2,11 +2,11 @@ package pages
import ( import (
"fmt" "fmt"
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
) )
func Test(ctx *fiber.Ctx) *h.Page { func Test(ctx echo.Context) *h.Page {
text := fmt.Sprintf("News ID: %s", ctx.Params("id")) text := fmt.Sprintf("News ID: %s", ctx.Params("id"))
return h.NewPage( return h.NewPage(
h.Div(h.Text(text)), h.Div(h.Text(text)),

View file

@ -1,19 +1,19 @@
package pages package pages
import ( import (
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"starter-template/pages/base" "starter-template/pages/base"
"starter-template/partials" "starter-template/partials"
) )
func ListPage(ctx *fiber.Ctx) *h.Page { func ListPage(ctx echo.Context) *h.Page {
return h.NewPage(base.RootPage( return h.NewPage(base.RootPage(
list(ctx), list(ctx),
)) ))
} }
func list(ctx *fiber.Ctx) h.Renderable { func list(ctx echo.Context) h.Renderable {
return h.Fragment( return h.Fragment(
h.ViewOnLoad(partials.NewsSheet), h.ViewOnLoad(partials.NewsSheet),
h.Div( h.Div(

View file

@ -1,13 +1,13 @@
package pages package pages
import ( import (
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"starter-template/pages/base" "starter-template/pages/base"
"starter-template/partials/patient" "starter-template/partials/patient"
) )
func PatientsIndex(ctx *fiber.Ctx) *h.Page { func PatientsIndex(ctx echo.Context) *h.Page {
return h.NewPage(base.RootPage( return h.NewPage(base.RootPage(
h.Div( h.Div(
h.Class("flex flex-col p-4 w-full"), h.Class("flex flex-col p-4 w-full"),

View file

@ -2,12 +2,12 @@
package load package load
import "github.com/maddalax/htmgo/framework/h" import "github.com/maddalax/htmgo/framework/h"
import "github.com/gofiber/fiber/v2" import "github.com/labstack/echo/v4"
import "starter-template/partials" import "starter-template/partials"
import "starter-template/partials/patient" import "starter-template/partials/patient"
import "starter-template/partials/sheet" import "starter-template/partials/sheet"
func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial { func GetPartialFromContext(ctx echo.Context) *h.Partial {
path := ctx.Path() path := ctx.Path()
if path == "NewsSheet" || path == "/starter-template/partials.NewsSheet" { if path == "NewsSheet" || path == "/starter-template/partials.NewsSheet" {
return partials.NewsSheet(ctx) return partials.NewsSheet(ctx)
@ -33,8 +33,8 @@ func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial {
return nil return nil
} }
func RegisterPartials(f *fiber.App) { func RegisterPartials(f *echo.Echo) {
f.All("starter-template/partials*", func(ctx *fiber.Ctx) error { f.All("starter-template/partials*", func(ctx echo.Context) error {
partial := GetPartialFromContext(ctx) partial := GetPartialFromContext(ctx)
if partial == nil { if partial == nil {
return ctx.SendStatus(404) return ctx.SendStatus(404)

View file

@ -2,13 +2,13 @@ package partials
import ( import (
"fmt" "fmt"
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework-ui/ui" "github.com/maddalax/htmgo/framework-ui/ui"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"starter-template/news" "starter-template/news"
) )
func NewsSheet(ctx *fiber.Ctx) *h.Partial { func NewsSheet(ctx echo.Context) *h.Partial {
open := h.GetQueryParam(ctx, "open") == "true" open := h.GetQueryParam(ctx, "open") == "true"
return h.NewPartialWithHeaders( return h.NewPartialWithHeaders(
&map[string]string{ &map[string]string{
@ -23,7 +23,7 @@ func NewsSheet(ctx *fiber.Ctx) *h.Partial {
) )
} }
func NewsSheetOpenCount(ctx *fiber.Ctx) *h.Partial { func NewsSheetOpenCount(ctx echo.Context) *h.Partial {
open := h.GetQueryParam(ctx, "open") == "true" open := h.GetQueryParam(ctx, "open") == "true"

View file

@ -1,13 +1,13 @@
package patient package patient
import ( import (
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"starter-template/features/patient" "starter-template/features/patient"
"starter-template/partials/sheet" "starter-template/partials/sheet"
) )
func Create(ctx *fiber.Ctx) *h.Partial { func Create(ctx echo.Context) *h.Partial {
name := ctx.FormValue("name") name := ctx.FormValue("name")
reason := ctx.FormValue("reason-for-visit") reason := ctx.FormValue("reason-for-visit")
location := ctx.FormValue("location-name") location := ctx.FormValue("location-name")

View file

@ -1,7 +1,7 @@
package patient package patient
import ( import (
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework-ui/ui" "github.com/maddalax/htmgo/framework-ui/ui"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"starter-template/features/patient" "starter-template/features/patient"
@ -9,7 +9,7 @@ import (
"strings" "strings"
) )
func List(ctx *fiber.Ctx) *h.Partial { func List(ctx echo.Context) *h.Partial {
patients, err := patient.NewService(ctx).List() patients, err := patient.NewService(ctx).List()
if err != nil { if err != nil {
@ -33,7 +33,7 @@ func List(ctx *fiber.Ctx) *h.Partial {
)) ))
} }
func AddPatientSheetPartial(ctx *fiber.Ctx) *h.Partial { func AddPatientSheetPartial(ctx echo.Context) *h.Partial {
closePathQs := h.GetQueryParam(ctx, "onClosePath") closePathQs := h.GetQueryParam(ctx, "onClosePath")
return h.NewPartialWithHeaders( return h.NewPartialWithHeaders(
h.PushQsHeader(ctx, "adding", "true"), h.PushQsHeader(ctx, "adding", "true"),
@ -56,7 +56,7 @@ func AddPatientSheet(onClosePath string) h.Renderable {
}) })
} }
func ValidateForm(ctx *fiber.Ctx) *h.Partial { func ValidateForm(ctx echo.Context) *h.Partial {
trigger := h.GetTriggerName(ctx) trigger := h.GetTriggerName(ctx)
value := ctx.FormValue(trigger) value := ctx.FormValue(trigger)

View file

@ -2,7 +2,7 @@ package sheet
import ( import (
"fmt" "fmt"
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
) )
@ -28,7 +28,7 @@ func Closed() h.Renderable {
return h.Div(h.Id(Id)) return h.Div(h.Id(Id))
} }
func Close(ctx *fiber.Ctx) *h.Partial { func Close(ctx echo.Context) *h.Partial {
return h.NewPartialWithHeaders( return h.NewPartialWithHeaders(
h.Ternary(ctx.Query("path") != "", h.ReplaceUrlHeader(ctx.Query("path")), h.NewHeaders()), h.Ternary(ctx.Query("path") != "", h.ReplaceUrlHeader(ctx.Query("path")), h.NewHeaders()),
h.Swap(ctx, Closed()), h.Swap(ctx, Closed()),

View file

@ -4,7 +4,6 @@ go 1.23.0
require ( require (
entgo.io/ent v0.14.1 entgo.io/ent v0.14.1
github.com/gofiber/fiber/v2 v2.52.5
github.com/maddalax/htmgo/framework v0.0.0-20240916224719-9e5d8edada65 github.com/maddalax/htmgo/framework v0.0.0-20240916224719-9e5d8edada65
) )

View file

@ -1,47 +1,30 @@
package main package main
import ( import (
"context" "github.com/labstack/echo/v4"
"github.com/gofiber/fiber/v2"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"log"
"starter-template/ent"
"starter-template/pages" "starter-template/pages"
"starter-template/partials/load" "starter-template/partials/load"
"time"
) )
func main() { func main() {
f := fiber.New() f := echo.New()
f.Static("/public", "./assets/dist") f.Static("/public", "./assets/dist")
f.Use(func(ctx *fiber.Ctx) error {
if ctx.Path() == "/livereload" {
return ctx.Next()
}
now := time.Now()
err := ctx.Next()
duration := time.Since(now)
ctx.Set("X-Response-Times", duration.String())
// Log or print the request method, URL, and duration
log.Printf("Requests: %s %s took %dms", ctx.Method(), ctx.OriginalURL(), duration.Milliseconds())
return err
})
load.RegisterPartials(f) load.RegisterPartials(f)
pages.RegisterPages(f) pages.RegisterPages(f)
client, err := ent.Open("sqlite3", "file:ent.db?cache=shared&_fk=1") //client, err := ent.Open("sqlite3", "file:ent.db?cache=shared&_fk=1")
if err != nil { //if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err) // log.Fatalf("failed opening connection to sqlite: %v", err)
} //}
defer client.Close() //defer client.Close()
// Run the auto migration tool. //// Run the auto migration tool.
if err := client.Schema.Create(context.Background()); err != nil { //if err := client.Schema.Create(context.Background()); err != nil {
log.Fatalf("failed schema resources: %v", err) // log.Fatalf("failed schema resources: %v", err)
} //}
h.Start(f, h.App{ h.Start(f, h.App{
LiveReload: true, LiveReload: true,

View file

@ -6,7 +6,11 @@ import (
) )
func Extensions() string { func Extensions() string {
return strings.Join([]string{"path-deps", "response-targets", "mutation-error"}, ", ") extensions := []string{"path-deps", "response-targets", "mutation-error"}
if h.IsDevelopment() {
extensions = append(extensions, "livereload")
}
return strings.Join(extensions, ", ")
} }
func RootPage(children ...h.Renderable) h.Renderable { func RootPage(children ...h.Renderable) h.Renderable {

View file

@ -1,11 +1,11 @@
// Package pages THIS FILE IS GENERATED. DO NOT EDIT. // Package pages THIS FILE IS GENERATED. DO NOT EDIT.
package pages package pages
import "github.com/gofiber/fiber/v2" import "github.com/labstack/echo/v4"
import "github.com/maddalax/htmgo/framework/h" import "github.com/maddalax/htmgo/framework/h"
func RegisterPages(f *fiber.App) { func RegisterPages(f *echo.Echo) {
f.Get("/", func(ctx *fiber.Ctx) error { f.GET("/", func(ctx echo.Context) error {
return h.HtmlView(ctx, IndexPage(ctx)) return h.HtmlView(ctx, IndexPage(ctx))
}) })
} }

View file

@ -1,14 +1,16 @@
package pages package pages
import ( import (
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
"starter-template/pages/base"
"starter-template/partials" "starter-template/partials"
) )
func IndexPage(c *fiber.Ctx) *h.Page { func IndexPage(c echo.Context) *h.Page {
return h.NewPage(h.Html( return h.NewPage(h.Html(
h.Class("bg-blue-400 flex flex-col items-center h-full w-full"), h.HxExtension(base.Extensions()),
h.Class("bg-slate-100 flex flex-col items-center h-full w-full"),
h.Head( h.Head(
h.Link("/public/main.css", "stylesheet"), h.Link("/public/main.css", "stylesheet"),
h.Script("/public/htmgo.js"), h.Script("/public/htmgo.js"),
@ -19,7 +21,7 @@ func IndexPage(c *fiber.Ctx) *h.Page {
h.Class("flex flex-col items-center justify-center gap-6 p-12 text-center"), h.Class("flex flex-col items-center justify-center gap-6 p-12 text-center"),
h.H1( h.H1(
h.Class("text-4xl sm:text-5xl font-bold max-w-3xl"), h.Class("text-4xl sm:text-5xl font-bold max-w-3xl"),
h.Text("Welcome to my fast!!"), h.Text("test"),
), ),
h.P( h.P(
h.Class("text-lg sm:text-xl max-w-1xl"), h.Class("text-lg sm:text-xl max-w-1xl"),
@ -33,8 +35,8 @@ func IndexPage(c *fiber.Ctx) *h.Page {
} }
func Button() h.Renderable { func Button() h.Renderable {
return h.Button(h.Class("btn bg-slate-500 p-4 rounded text-white"), return h.Button(h.Class("btn bg-red-500 p-4 rounded text-white"),
h.Text("Ctest"), h.Text("this is my nice this works"),
h.AfterRequest( h.AfterRequest(
h.SetDisabled(true), h.SetDisabled(true),
h.RemoveClass("bg-red-600"), h.RemoveClass("bg-red-600"),

View file

@ -1,18 +1,18 @@
package partials package partials
import ( import (
"github.com/gofiber/fiber/v2" "github.com/labstack/echo/v4"
"github.com/maddalax/htmgo/framework/h" "github.com/maddalax/htmgo/framework/h"
) )
func SamplePartial(ctx *fiber.Ctx) *h.Partial { func SamplePartial(ctx echo.Context) *h.Partial {
return h.NewPartial(h.Div(h.P(h.Text(" asdas")))) return h.NewPartial(h.Div(h.P(h.Text(" asdasasds"))))
} }
func NewPartial(ctx *fiber.Ctx) *h.Partial { func NewPartial(ctx echo.Context) *h.Partial {
return h.NewPartial(h.Div(h.P(h.Text("This sadsl.")))) return h.NewPartial(h.Div(h.P(h.Text("This sadsl."))))
} }
func NewPartial2(ctx *fiber.Ctx) *h.Partial { func NewPartial2(ctx echo.Context) *h.Partial {
return h.NewPartial(h.Div(h.P(h.Text("This sasdsadasdwl.")))) return h.NewPartial(h.Div(h.P(h.Text("This sasdsadasdwl."))))
} }

View file

@ -2,10 +2,10 @@
package load package load
import "github.com/maddalax/htmgo/framework/h" import "github.com/maddalax/htmgo/framework/h"
import "github.com/gofiber/fiber/v2" import "github.com/labstack/echo/v4"
import "starter-template/partials" import "starter-template/partials"
func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial { func GetPartialFromContext(ctx echo.Context) *h.Partial {
path := ctx.Path() path := ctx.Path()
if path == "SamplePartial" || path == "/starter-template/partials.SamplePartial" { if path == "SamplePartial" || path == "/starter-template/partials.SamplePartial" {
return partials.SamplePartial(ctx) return partials.SamplePartial(ctx)
@ -19,11 +19,11 @@ func GetPartialFromContext(ctx *fiber.Ctx) *h.Partial {
return nil return nil
} }
func RegisterPartials(f *fiber.App) { func RegisterPartials(f *echo.Echo) {
f.All("starter-template/partials*", func(ctx *fiber.Ctx) error { f.Any("starter-template/partials*", func(ctx echo.Context) error {
partial := GetPartialFromContext(ctx) partial := GetPartialFromContext(ctx)
if partial == nil { if partial == nil {
return ctx.SendStatus(404) return ctx.NoContent(404)
} }
return h.PartialView(ctx, partial) return h.PartialView(ctx, partial)
}) })