gen code for assets
This commit is contained in:
parent
4c6187e18d
commit
50bc55e9ca
8 changed files with 128 additions and 26 deletions
|
|
@ -9,10 +9,12 @@ import (
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"golang.org/x/mod/modfile"
|
"golang.org/x/mod/modfile"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
|
|
@ -37,6 +39,32 @@ const ModuleName = "github.com/maddalax/htmgo/framework/h"
|
||||||
var PackageName = fmt.Sprintf("package %s", GeneratedDirName)
|
var PackageName = fmt.Sprintf("package %s", GeneratedDirName)
|
||||||
var GeneratedFileLine = fmt.Sprintf("// Package %s THIS FILE IS GENERATED. DO NOT EDIT.", GeneratedDirName)
|
var GeneratedFileLine = fmt.Sprintf("// Package %s THIS FILE IS GENERATED. DO NOT EDIT.", GeneratedDirName)
|
||||||
|
|
||||||
|
func toPascaleCase(input string) string {
|
||||||
|
words := strings.Split(input, "_")
|
||||||
|
for i := range words {
|
||||||
|
words[i] = strings.Title(strings.ToLower(words[i]))
|
||||||
|
}
|
||||||
|
return strings.Join(words, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidGoVariableName(name string) bool {
|
||||||
|
// Variable name must not be empty
|
||||||
|
if name == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// First character must be a letter or underscore
|
||||||
|
if !unicode.IsLetter(rune(name[0])) && name[0] != '_' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Remaining characters must be letters, digits, or underscores
|
||||||
|
for _, char := range name[1:] {
|
||||||
|
if !unicode.IsLetter(char) && !unicode.IsDigit(char) && char != '_' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func normalizePath(path string) string {
|
func normalizePath(path string) string {
|
||||||
return strings.ReplaceAll(path, `\`, "/")
|
return strings.ReplaceAll(path, `\`, "/")
|
||||||
}
|
}
|
||||||
|
|
@ -390,6 +418,63 @@ func writePagesFile() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeAssetsFile() {
|
||||||
|
cwd := process.GetWorkingDir()
|
||||||
|
config := dirutil.GetConfig()
|
||||||
|
distAssets := filepath.Join(cwd, "assets", "dist")
|
||||||
|
hasAssets := false
|
||||||
|
|
||||||
|
builder := strings.Builder{}
|
||||||
|
|
||||||
|
builder.WriteString(`package assets`)
|
||||||
|
builder.WriteString("\n")
|
||||||
|
|
||||||
|
filepath.WalkDir(distAssets, func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(d.Name(), ".") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
path = strings.ReplaceAll(path, distAssets, "")
|
||||||
|
httpUrl := fmt.Sprintf("%s%s", config.PublicAssetPath, path)
|
||||||
|
|
||||||
|
path = normalizePath(path)
|
||||||
|
path = strings.ReplaceAll(path, "/", "_")
|
||||||
|
path = strings.ReplaceAll(path, "//", "_")
|
||||||
|
|
||||||
|
name := strings.ReplaceAll(path, ".", "_")
|
||||||
|
name = strings.ReplaceAll(name, "-", "_")
|
||||||
|
|
||||||
|
name = toPascaleCase(name)
|
||||||
|
|
||||||
|
if isValidGoVariableName(name) {
|
||||||
|
builder.WriteString(fmt.Sprintf(`const %s = "%s"`, name, httpUrl))
|
||||||
|
builder.WriteString("\n")
|
||||||
|
hasAssets = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
builder.WriteString("\n")
|
||||||
|
|
||||||
|
str := builder.String()
|
||||||
|
|
||||||
|
if hasAssets {
|
||||||
|
WriteFile(filepath.Join(GeneratedDirName, "assets", "assets-generated.go"), func(content *ast.File) string {
|
||||||
|
return str
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func GetModuleName() string {
|
func GetModuleName() string {
|
||||||
wd := process.GetWorkingDir()
|
wd := process.GetWorkingDir()
|
||||||
modPath := filepath.Join(wd, "go.mod")
|
modPath := filepath.Join(wd, "go.mod")
|
||||||
|
|
@ -411,6 +496,7 @@ func GenAst(flags ...process.RunFlag) error {
|
||||||
}
|
}
|
||||||
writePartialsFile()
|
writePartialsFile()
|
||||||
writePagesFile()
|
writePagesFile()
|
||||||
|
writeAssetsFile()
|
||||||
|
|
||||||
WriteFile("__htmgo/setup-generated.go", func(content *ast.File) string {
|
WriteFile("__htmgo/setup-generated.go", func(content *ast.File) string {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/maddalax/htmgo/cli/htmgo/tasks/process"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -14,6 +15,7 @@ type ProjectConfig struct {
|
||||||
WatchFiles []string `yaml:"watch_files"`
|
WatchFiles []string `yaml:"watch_files"`
|
||||||
AutomaticPageRoutingIgnore []string `yaml:"automatic_page_routing_ignore"`
|
AutomaticPageRoutingIgnore []string `yaml:"automatic_page_routing_ignore"`
|
||||||
AutomaticPartialRoutingIgnore []string `yaml:"automatic_partial_routing_ignore"`
|
AutomaticPartialRoutingIgnore []string `yaml:"automatic_partial_routing_ignore"`
|
||||||
|
PublicAssetPath string `yaml:"public_asset_path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultProjectConfig() *ProjectConfig {
|
func DefaultProjectConfig() *ProjectConfig {
|
||||||
|
|
@ -25,6 +27,7 @@ func DefaultProjectConfig() *ProjectConfig {
|
||||||
WatchFiles: []string{
|
WatchFiles: []string{
|
||||||
"**/*.go", "**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.yaml", "**/*.yml", "**/*.md",
|
"**/*.go", "**/*.html", "**/*.css", "**/*.js", "**/*.json", "**/*.yaml", "**/*.yml", "**/*.md",
|
||||||
},
|
},
|
||||||
|
PublicAssetPath: "/public",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,9 +60,19 @@ func (cfg *ProjectConfig) Enhance() *ProjectConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.PublicAssetPath == "" {
|
||||||
|
cfg.PublicAssetPath = "/public"
|
||||||
|
}
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Get() *ProjectConfig {
|
||||||
|
cwd := process.GetWorkingDir()
|
||||||
|
config := FromConfigFile(cwd)
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
func FromConfigFile(workingDir string) *ProjectConfig {
|
func FromConfigFile(workingDir string) *ProjectConfig {
|
||||||
defaultCfg := DefaultProjectConfig()
|
defaultCfg := DefaultProjectConfig()
|
||||||
names := []string{"htmgo.yaml", "htmgo.yml", "_htmgo.yaml", "_htmgo.yml"}
|
names := []string{"htmgo.yaml", "htmgo.yml", "_htmgo.yaml", "_htmgo.yml"}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,15 @@ func TestShouldPrefixAutomaticPartialRoutingIgnore_1(t *testing.T) {
|
||||||
assert.Equal(t, []string{"partials/somefile/*"}, cfg.AutomaticPartialRoutingIgnore)
|
assert.Equal(t, []string{"partials/somefile/*"}, cfg.AutomaticPartialRoutingIgnore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPublicAssetPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
cfg := DefaultProjectConfig()
|
||||||
|
assert.Equal(t, "/public", cfg.PublicAssetPath)
|
||||||
|
|
||||||
|
cfg.PublicAssetPath = "/assets"
|
||||||
|
assert.Equal(t, "/assets", cfg.PublicAssetPath)
|
||||||
|
}
|
||||||
|
|
||||||
func writeConfigFile(t *testing.T, content string) string {
|
func writeConfigFile(t *testing.T, content string) string {
|
||||||
temp := os.TempDir()
|
temp := os.TempDir()
|
||||||
os.Mkdir(temp, 0755)
|
os.Mkdir(temp, 0755)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package base
|
||||||
import (
|
import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/maddalax/htmgo/framework/h"
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"htmgo-site/__htmgo/assets"
|
||||||
"htmgo-site/partials"
|
"htmgo-site/partials"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -43,8 +44,8 @@ func ConfigurableRootPage(ctx *h.RequestContext, props RootPageProps) *h.Page {
|
||||||
h.Title(
|
h.Title(
|
||||||
h.Text(title),
|
h.Text(title),
|
||||||
),
|
),
|
||||||
h.Link("/public/favicon.ico", "icon"),
|
h.Link(assets.FaviconIco, "icon"),
|
||||||
h.Link("/public/apple-touch-icon.png", "apple-touch-icon"),
|
h.Link(assets.AppleTouchIconPng, "apple-touch-icon"),
|
||||||
h.Meta("charset", "utf-8"),
|
h.Meta("charset", "utf-8"),
|
||||||
h.Meta("author", "htmgo"),
|
h.Meta("author", "htmgo"),
|
||||||
h.Meta("description", description),
|
h.Meta("description", description),
|
||||||
|
|
@ -53,8 +54,8 @@ func ConfigurableRootPage(ctx *h.RequestContext, props RootPageProps) *h.Page {
|
||||||
h.Link("canonical", canonical),
|
h.Link("canonical", canonical),
|
||||||
h.Link("https://cdn.jsdelivr.net/npm/@docsearch/css@3", "stylesheet"),
|
h.Link("https://cdn.jsdelivr.net/npm/@docsearch/css@3", "stylesheet"),
|
||||||
h.Meta("og:description", description),
|
h.Meta("og:description", description),
|
||||||
h.LinkWithVersion("/public/main.css", "stylesheet", Version),
|
h.LinkWithVersion(assets.MainCss, "stylesheet", Version),
|
||||||
h.ScriptWithVersion("/public/htmgo.js", Version),
|
h.ScriptWithVersion(assets.HtmgoJs, Version),
|
||||||
h.Style(`
|
h.Style(`
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
|
|
||||||
|
|
@ -61,20 +61,3 @@ h.JoinExtensions(
|
||||||
h.HxExtension("my-extension"),
|
h.HxExtension("my-extension"),
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
|
|
||||||
const htmxExtensions = `
|
|
||||||
h.HxOnLoad
|
|
||||||
h.HxOnAfterSwap
|
|
||||||
h.OnClick
|
|
||||||
h.OnSubmit
|
|
||||||
h.HxBeforeSseMessage
|
|
||||||
h.HxAfterSseMessage
|
|
||||||
h.OnClick
|
|
||||||
h.OnSubmit
|
|
||||||
h.HxOnSseError
|
|
||||||
h.HxOnSseClose
|
|
||||||
h.HxOnSseConnecting
|
|
||||||
h.HxOnSseOpen
|
|
||||||
h.HxAfterRequest
|
|
||||||
h.HxOnMutationError
|
|
||||||
`
|
|
||||||
|
|
|
||||||
|
|
@ -16,3 +16,6 @@ automatic_page_routing_ignore: ["root.go"]
|
||||||
# files or directories to ignore when automatically registering routes for partials
|
# files or directories to ignore when automatically registering routes for partials
|
||||||
# supports glob patterns through https://github.com/bmatcuk/doublestar
|
# supports glob patterns through https://github.com/bmatcuk/doublestar
|
||||||
automatic_partial_routing_ignore: []
|
automatic_partial_routing_ignore: []
|
||||||
|
|
||||||
|
# url path of where the public assets are located
|
||||||
|
public_asset_path: "/public"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/maddalax/htmgo/framework/config"
|
||||||
"github.com/maddalax/htmgo/framework/h"
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
"github.com/maddalax/htmgo/framework/service"
|
"github.com/maddalax/htmgo/framework/service"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
|
@ -10,6 +12,7 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
locator := service.NewLocator()
|
locator := service.NewLocator()
|
||||||
|
cfg := config.Get()
|
||||||
|
|
||||||
h.Start(h.AppOpts{
|
h.Start(h.AppOpts{
|
||||||
ServiceLocator: locator,
|
ServiceLocator: locator,
|
||||||
|
|
@ -23,7 +26,10 @@ func main() {
|
||||||
|
|
||||||
http.FileServerFS(sub)
|
http.FileServerFS(sub)
|
||||||
|
|
||||||
app.Router.Handle("/public/*", http.StripPrefix("/public", http.FileServerFS(sub)))
|
// change this in htmgo.yml (public_asset_path)
|
||||||
|
app.Router.Handle(fmt.Sprintf("%s/*", cfg.PublicAssetPath),
|
||||||
|
http.StripPrefix(cfg.PublicAssetPath, http.FileServerFS(sub)))
|
||||||
|
|
||||||
__htmgo.Register(app.Router)
|
__htmgo.Register(app.Router)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package pages
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/maddalax/htmgo/framework/h"
|
"github.com/maddalax/htmgo/framework/h"
|
||||||
|
"starter-template/__htmgo/assets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RootPage(children ...h.Ren) *h.Page {
|
func RootPage(children ...h.Ren) *h.Page {
|
||||||
|
|
@ -20,8 +21,8 @@ func RootPage(children ...h.Ren) *h.Page {
|
||||||
h.Text(title),
|
h.Text(title),
|
||||||
),
|
),
|
||||||
h.Meta("viewport", "width=device-width, initial-scale=1"),
|
h.Meta("viewport", "width=device-width, initial-scale=1"),
|
||||||
h.Link("/public/favicon.ico", "icon"),
|
h.Link(assets.FaviconIco, "icon"),
|
||||||
h.Link("/public/apple-touch-icon.png", "apple-touch-icon"),
|
h.Link(assets.AppleTouchIconPng, "apple-touch-icon"),
|
||||||
h.Meta("title", title),
|
h.Meta("title", title),
|
||||||
h.Meta("charset", "utf-8"),
|
h.Meta("charset", "utf-8"),
|
||||||
h.Meta("author", author),
|
h.Meta("author", author),
|
||||||
|
|
@ -30,8 +31,8 @@ func RootPage(children ...h.Ren) *h.Page {
|
||||||
h.Meta("og:url", url),
|
h.Meta("og:url", url),
|
||||||
h.Link("canonical", url),
|
h.Link("canonical", url),
|
||||||
h.Meta("og:description", description),
|
h.Meta("og:description", description),
|
||||||
h.Link("/public/main.css", "stylesheet"),
|
h.Link(assets.MainCss, "stylesheet"),
|
||||||
h.Script("/public/htmgo.js"),
|
h.Script(assets.HtmgoJs),
|
||||||
),
|
),
|
||||||
h.Body(
|
h.Body(
|
||||||
h.Div(
|
h.Div(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue