Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c77ccc0796 | ||
|
|
d4b831492e | ||
|
|
ea7b9ad4a1 |
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
// actionTrigger represents a trigger to an event handler fn
|
||||
|
||||
@@ -37,4 +37,12 @@ type Options struct {
|
||||
// with scs LoadAndSave middleware. Configure the session manager before
|
||||
// passing it (lifetime, cookie settings, store, etc).
|
||||
SessionManager *scs.SessionManager
|
||||
|
||||
// DatastarContent is the Datastar.js script content.
|
||||
// If nil, the embedded default is used.
|
||||
DatastarContent []byte
|
||||
|
||||
// DatastarPath is the URL path where the script is served.
|
||||
// Defaults to "/_datastar.js" if empty.
|
||||
DatastarPath string
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
// Context is the living bridge between Go and the browser.
|
||||
|
||||
6
go.mod
6
go.mod
@@ -1,4 +1,4 @@
|
||||
module github.com/go-via/via
|
||||
module github.com/ryanhamamura/via
|
||||
|
||||
go 1.25.4
|
||||
|
||||
@@ -6,6 +6,7 @@ require maragu.dev/gomponents v1.2.0
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
||||
github.com/alexedwards/scs/v2 v2.9.0
|
||||
github.com/mattn/go-sqlite3 v1.14.32
|
||||
github.com/starfederation/datastar-go v1.0.3
|
||||
github.com/stretchr/testify v1.10.0
|
||||
@@ -13,13 +14,10 @@ require (
|
||||
|
||||
require (
|
||||
github.com/CAFxX/httpcompression v0.0.9 // indirect
|
||||
github.com/alexedwards/scs/v2 v2.9.0 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
8
go.sum
8
go.sum
@@ -17,11 +17,6 @@ github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
|
||||
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ=
|
||||
@@ -45,9 +40,8 @@ github.com/valyala/gozstd v1.20.1 h1:xPnnnvjmaDDitMFfDxmQ4vpx0+3CdTg2o3lALvXTU/g
|
||||
github.com/valyala/gozstd v1.20.1/go.mod h1:y5Ew47GLlP37EkTB+B4s7r6A5rdaeB7ftbl9zoYiIPQ=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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=
|
||||
|
||||
1
h/h.go
1
h/h.go
@@ -79,7 +79,6 @@ func HTML5(p HTML5Props) H {
|
||||
Body: retype(p.Body),
|
||||
HTMLAttrs: retype(p.HTMLAttrs),
|
||||
}
|
||||
gp.Head = append(gp.Head, Script(Type("module"), Src("/_datastar.js")))
|
||||
return gc.HTML5(gp)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package main
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/go-via/via"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-via/via"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
type Counter struct{ Count int }
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-via/via"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-via/via"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-via/via"
|
||||
"github.com/ryanhamamura/via"
|
||||
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
type Counter struct{ Count int }
|
||||
|
||||
@@ -3,9 +3,9 @@ package main
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/go-via/via"
|
||||
"github.com/ryanhamamura/via"
|
||||
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||
. "github.com/go-via/via/h"
|
||||
. "github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-via/via"
|
||||
"github.com/ryanhamamura/via"
|
||||
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
type Counter struct{ Count int }
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
_ "embed"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-via/via"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
// Example of a Via application with a plugin that adds PicoCSS. The plugin
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/go-via/via"
|
||||
"github.com/ryanhamamura/via"
|
||||
// "github.com/go-via/via-plugin-picocss/picocss"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-via/via"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/go-via/via"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
)
|
||||
|
||||
// Signal represents a value that is reactive in the browser. Signals
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
// "net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
||||
46
via.go
46
via.go
@@ -22,7 +22,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/alexedwards/scs/v2"
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
"github.com/starfederation/datastar-go/datastar"
|
||||
)
|
||||
|
||||
@@ -32,14 +32,17 @@ var datastarJS []byte
|
||||
// V is the root application.
|
||||
// It manages page routing, user sessions, and SSE connections for live updates.
|
||||
type V struct {
|
||||
cfg Options
|
||||
mux *http.ServeMux
|
||||
contextRegistry map[string]*Context
|
||||
contextRegistryMutex sync.RWMutex
|
||||
documentHeadIncludes []h.H
|
||||
documentFootIncludes []h.H
|
||||
devModePageInitFnMap map[string]func(*Context)
|
||||
sessionManager *scs.SessionManager
|
||||
cfg Options
|
||||
mux *http.ServeMux
|
||||
contextRegistry map[string]*Context
|
||||
contextRegistryMutex sync.RWMutex
|
||||
documentHeadIncludes []h.H
|
||||
documentFootIncludes []h.H
|
||||
devModePageInitFnMap map[string]func(*Context)
|
||||
sessionManager *scs.SessionManager
|
||||
datastarPath string
|
||||
datastarContent []byte
|
||||
datastarOnce sync.Once
|
||||
}
|
||||
|
||||
func (v *V) logFatal(format string, a ...any) {
|
||||
@@ -108,6 +111,12 @@ func (v *V) Config(cfg Options) {
|
||||
if cfg.SessionManager != nil {
|
||||
v.sessionManager = cfg.SessionManager
|
||||
}
|
||||
if cfg.DatastarContent != nil {
|
||||
v.datastarContent = cfg.DatastarContent
|
||||
}
|
||||
if cfg.DatastarPath != "" {
|
||||
v.datastarPath = cfg.DatastarPath
|
||||
}
|
||||
}
|
||||
|
||||
// AppendToHead appends the given h.H nodes to the head of the base HTML document.
|
||||
@@ -141,6 +150,7 @@ func (v *V) AppendToFoot(elements ...h.H) {
|
||||
// })
|
||||
// })
|
||||
func (v *V) Page(route string, initContextFn func(c *Context)) {
|
||||
v.ensureDatastarHandler()
|
||||
// check for panics
|
||||
func() {
|
||||
defer func() {
|
||||
@@ -176,7 +186,7 @@ func (v *V) Page(route string, initContextFn func(c *Context)) {
|
||||
if v.cfg.DevMode {
|
||||
v.devModePersist(c)
|
||||
}
|
||||
headElements := []h.H{}
|
||||
headElements := []h.H{h.Script(h.Type("module"), h.Src(v.datastarPath))}
|
||||
headElements = append(headElements, v.documentHeadIncludes...)
|
||||
headElements = append(headElements,
|
||||
h.Meta(h.Data("signals", fmt.Sprintf("{'via-ctx':'%s'}", id))),
|
||||
@@ -258,6 +268,15 @@ func (v *V) HTTPServeMux() *http.ServeMux {
|
||||
return v.mux
|
||||
}
|
||||
|
||||
func (v *V) ensureDatastarHandler() {
|
||||
v.datastarOnce.Do(func() {
|
||||
v.mux.HandleFunc("GET "+v.datastarPath, func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/javascript")
|
||||
_, _ = w.Write(v.datastarContent)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (v *V) devModePersist(c *Context) {
|
||||
p := filepath.Join(".via", "devmode", "ctx.json")
|
||||
if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
|
||||
@@ -378,6 +397,8 @@ func New() *V {
|
||||
contextRegistry: make(map[string]*Context),
|
||||
devModePageInitFnMap: make(map[string]func(*Context)),
|
||||
sessionManager: scs.New(),
|
||||
datastarPath: "/_datastar.js",
|
||||
datastarContent: datastarJS,
|
||||
cfg: Options{
|
||||
DevMode: false,
|
||||
ServerAddress: ":3000",
|
||||
@@ -386,11 +407,6 @@ func New() *V {
|
||||
},
|
||||
}
|
||||
|
||||
v.mux.HandleFunc("GET /_datastar.js", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/javascript")
|
||||
_, _ = w.Write(datastarJS)
|
||||
})
|
||||
|
||||
v.mux.HandleFunc("GET /_sse", func(w http.ResponseWriter, r *http.Request) {
|
||||
isReconnect := false
|
||||
if r.Header.Get("last-event-id") == "via" {
|
||||
|
||||
50
via_test.go
50
via_test.go
@@ -5,7 +5,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-via/via/h"
|
||||
"github.com/ryanhamamura/via/h"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -28,6 +28,10 @@ func TestPageRoute(t *testing.T) {
|
||||
|
||||
func TestDatastarJS(t *testing.T) {
|
||||
v := New()
|
||||
v.Page("/", func(c *Context) {
|
||||
c.View(func() h.H { return h.Div() })
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/_datastar.js", nil)
|
||||
w := httptest.NewRecorder()
|
||||
v.mux.ServeHTTP(w, req)
|
||||
@@ -37,6 +41,50 @@ func TestDatastarJS(t *testing.T) {
|
||||
assert.Contains(t, w.Body.String(), "🖕JS_DS🚀")
|
||||
}
|
||||
|
||||
func TestCustomDatastarContent(t *testing.T) {
|
||||
customScript := []byte("// Custom Datastar Script")
|
||||
v := New()
|
||||
v.Config(Options{
|
||||
DatastarContent: customScript,
|
||||
})
|
||||
v.Page("/", func(c *Context) {
|
||||
c.View(func() h.H { return h.Div() })
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/_datastar.js", nil)
|
||||
w := httptest.NewRecorder()
|
||||
v.mux.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "application/javascript", w.Header().Get("Content-Type"))
|
||||
assert.Contains(t, w.Body.String(), "Custom Datastar Script")
|
||||
}
|
||||
|
||||
func TestCustomDatastarPath(t *testing.T) {
|
||||
v := New()
|
||||
v.Config(Options{
|
||||
DatastarPath: "/assets/datastar.js",
|
||||
})
|
||||
v.Page("/test", func(c *Context) {
|
||||
c.View(func() h.H { return h.Div() })
|
||||
})
|
||||
|
||||
// Custom path should serve the script
|
||||
req := httptest.NewRequest("GET", "/assets/datastar.js", nil)
|
||||
w := httptest.NewRecorder()
|
||||
v.mux.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, "application/javascript", w.Header().Get("Content-Type"))
|
||||
assert.Contains(t, w.Body.String(), "🖕JS_DS🚀")
|
||||
|
||||
// Page should reference the custom path in script tag
|
||||
req2 := httptest.NewRequest("GET", "/test", nil)
|
||||
w2 := httptest.NewRecorder()
|
||||
v.mux.ServeHTTP(w2, req2)
|
||||
assert.Contains(t, w2.Body.String(), `src="/assets/datastar.js"`)
|
||||
}
|
||||
|
||||
func TestSignal(t *testing.T) {
|
||||
var sig *signal
|
||||
v := New()
|
||||
|
||||
Reference in New Issue
Block a user