Add a jorge meta command to get site metadata in scripts (#49)
Some checks failed
Test project / build (push) Has been cancelled

* add a jorge meta command

* add a basic metadata eval test

* fix staticcheck
This commit is contained in:
Facundo Olano 2024-09-16 20:16:30 -03:00 committed by GitHub
parent c252507af9
commit 2e5403d07f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 88 additions and 10 deletions

View file

@ -49,3 +49,25 @@ func Prompt(label string) string {
} }
return strings.TrimSpace(s) return strings.TrimSpace(s)
} }
type Meta struct {
Expression string `arg:"" name:"expression" default:"site" help:"liquid expression to be evaluated (what goes inside of {{ ... }} in templates)"`
}
// Load the site metadata and use it as context to evaluate a liquid expression
func (cmd *Meta) Run(ctx *kong.Context) error {
config, err := config.Load(".")
if err != nil {
return err
}
// remove optional {{}} wrapper
expression := strings.Trim(cmd.Expression, " {}")
result, err := site.EvalMetadata(*config, expression)
if err == nil {
fmt.Println(result)
}
return err
}

View file

@ -10,6 +10,7 @@ var cli struct {
Build commands.Build `cmd:"" help:"Build a website project." aliases:"b"` Build commands.Build `cmd:"" help:"Build a website project." aliases:"b"`
Post commands.Post `cmd:"" help:"Initialize a new post template file." aliases:"p"` Post commands.Post `cmd:"" help:"Initialize a new post template file." aliases:"p"`
Serve commands.Serve `cmd:"" help:"Run a local server for the website." aliases:"s"` Serve commands.Serve `cmd:"" help:"Run a local server for the website." aliases:"s"`
Meta commands.Meta `cmd:"" help:"Get the JSON results from evaluating a liquid template expression within the site context." aliases:"m"`
Version kong.VersionFlag `short:"v"` Version kong.VersionFlag `short:"v"`
} }

View file

@ -42,6 +42,11 @@ func NewEngine(siteUrl string, includesDir string) *Engine {
return e return e
} }
func EvalExpression(engine *Engine, expression string, context map[string]interface{}) (string, error) {
template := fmt.Sprintf("{{ %s | json }}", expression)
return engine.ParseAndRenderString(template, context)
}
// Try to parse a liquid template at the given location. // Try to parse a liquid template at the given location.
// Files starting with front matter (--- sorrrounded yaml) // Files starting with front matter (--- sorrrounded yaml)
// are considered templates. If the given file is not headed by front matter // are considered templates. If the given file is not headed by front matter

View file

@ -49,6 +49,16 @@ func Build(config config.Config) error {
return site.build() return site.build()
} }
// Parse and render the given liquid expression, eg. " site.posts | map:title "
// and return the results as a json string.
func EvalMetadata(config config.Config, expression string) (string, error) {
site, err := load(config)
if err != nil {
return "", err
}
return markup.EvalExpression(site.templateEngine, expression, site.AsContext())
}
// Create a new site instance by scanning the project directories // Create a new site instance by scanning the project directories
// pointed by `config`, loading layouts, templates and data files. // pointed by `config`, loading layouts, templates and data files.
func load(config config.Config) (*site, error) { func load(config config.Config) (*site, error) {
@ -374,16 +384,7 @@ func (site *site) buildFile(path string) error {
} }
func (site *site) render(templ *markup.Template) ([]byte, error) { func (site *site) render(templ *markup.Template) ([]byte, error) {
ctx := map[string]interface{}{ ctx := site.AsContext()
"site": map[string]interface{}{
"config": site.config.AsContext(),
"posts": site.posts,
"tags": site.tags,
"pages": site.pages,
"static_files": site.static_files,
"data": site.data,
},
}
ctx["page"] = templ.Metadata ctx["page"] = templ.Metadata
content, err := templ.RenderWith(ctx, site.config.HighlightTheme) content, err := templ.RenderWith(ctx, site.config.HighlightTheme)
@ -410,6 +411,19 @@ func (site *site) render(templ *markup.Template) ([]byte, error) {
return content, nil return content, nil
} }
func (site *site) AsContext() map[string]interface{} {
return map[string]interface{}{
"site": map[string]interface{}{
"config": site.config.AsContext(),
"posts": site.posts,
"tags": site.tags,
"pages": site.pages,
"static_files": site.static_files,
"data": site.data,
},
}
}
func checkFileError(err error) error { func checkFileError(err error) error {
// When walking the source dir it can happen that a file is present when walking starts // When walking the source dir it can happen that a file is present when walking starts
// but missing or inaccessible when trying to open it (this is particularly frequent with // but missing or inaccessible when trying to open it (this is particularly frequent with

View file

@ -271,6 +271,42 @@ date: 2023-01-01
</ul>`) </ul>`)
} }
func TestEvalMetadata(t *testing.T) {
config := newProject()
defer os.RemoveAll(config.RootDir)
content := `---
title: hello world!
date: 2024-01-01
---
<p>Hello world!</p>`
file := newFile(config.SrcDir, "hello.html", content)
defer os.Remove(file.Name())
content = `---
title: goodbye!
date: 2024-02-01
---
<p>goodbye world!</p>`
file = newFile(config.SrcDir, "goodbye.html", content)
defer os.Remove(file.Name())
content = `---
title: an oldie!
date: 2023-01-01
---
<p>oldie</p>`
file = newFile(config.SrcDir, "an-oldie.html", content)
defer os.Remove(file.Name())
output, err := EvalMetadata(*config, "site.posts | map:'title'")
assertEqual(t, err, nil)
assertEqual(t, output, `["goodbye!","hello world!","an oldie!"]`)
_, err = EvalMetadata(*config, "site.posts | map:'title")
assert(t, strings.Contains(err.Error(), "Liquid error"))
}
func TestRenderTags(t *testing.T) { func TestRenderTags(t *testing.T) {
config := newProject() config := newProject()
defer os.RemoveAll(config.RootDir) defer os.RemoveAll(config.RootDir)