Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea7b9ad4a1 |
@@ -2,6 +2,17 @@ package via
|
||||
|
||||
import "github.com/alexedwards/scs/v2"
|
||||
|
||||
// DatastarConfig configures a custom Datastar.js script.
|
||||
type DatastarConfig struct {
|
||||
// Content is the Datastar.js script content.
|
||||
// If nil, the embedded default is used.
|
||||
Content []byte
|
||||
|
||||
// Path is the URL path where the script is served.
|
||||
// Defaults to "/_datastar.js" if empty.
|
||||
Path string
|
||||
}
|
||||
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
@@ -37,4 +48,8 @@ type Options struct {
|
||||
// with scs LoadAndSave middleware. Configure the session manager before
|
||||
// passing it (lifetime, cookie settings, store, etc).
|
||||
SessionManager *scs.SessionManager
|
||||
|
||||
// Datastar configures a custom Datastar.js script.
|
||||
// If nil, Via uses its embedded default.
|
||||
Datastar *DatastarConfig
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
32
via.go
32
via.go
@@ -40,6 +40,9 @@ type V struct {
|
||||
documentFootIncludes []h.H
|
||||
devModePageInitFnMap map[string]func(*Context)
|
||||
sessionManager *scs.SessionManager
|
||||
datastarPath string
|
||||
datastarContent []byte
|
||||
datastarHandlerRegistered bool
|
||||
}
|
||||
|
||||
func (v *V) logFatal(format string, a ...any) {
|
||||
@@ -108,6 +111,14 @@ func (v *V) Config(cfg Options) {
|
||||
if cfg.SessionManager != nil {
|
||||
v.sessionManager = cfg.SessionManager
|
||||
}
|
||||
if cfg.Datastar != nil {
|
||||
if cfg.Datastar.Content != nil {
|
||||
v.datastarContent = cfg.Datastar.Content
|
||||
}
|
||||
if cfg.Datastar.Path != "" {
|
||||
v.datastarPath = cfg.Datastar.Path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AppendToHead appends the given h.H nodes to the head of the base HTML document.
|
||||
@@ -141,6 +152,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 +188,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 +270,17 @@ func (v *V) HTTPServeMux() *http.ServeMux {
|
||||
return v.mux
|
||||
}
|
||||
|
||||
func (v *V) ensureDatastarHandler() {
|
||||
if v.datastarHandlerRegistered {
|
||||
return
|
||||
}
|
||||
v.datastarHandlerRegistered = true
|
||||
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 +401,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 +411,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" {
|
||||
|
||||
52
via_test.go
52
via_test.go
@@ -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,54 @@ 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{
|
||||
Datastar: &DatastarConfig{
|
||||
Content: 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{
|
||||
Datastar: &DatastarConfig{
|
||||
Path: "/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