refactor(tools): modularize CLI with urfave/cli/v2, externalise template metadata
- Split monolithic build-preview.go into modular packages: tools.go (entry), cli/ (list/create/delete/preview commands), config/ (JSON loading), preview/ (rendering engine, funcs, locale) - Extract all template metadata into data/templates_config.json as single source of truth — no hardcoded template data in Go - Replace hand-rolled CLI parsing with github.com/urfave/cli/v2 - Add sensible defaults for --folder (../themes) and --config (./data/templates_config.json) — most commands now run bare - Add type-coercing gt/lt/ge/le template funcs for JSON float64 - Fix AppUrl to gitea.com matching official template convention - rendered.js now exports __REGISTRY__ and __PARAMS__ alongside __RENDERED__ — preview/index.html reads all from single source - Cross-validate all 11 template data contexts against official Gitea source (services/mailer/*.go + templates/mail/*.tmpl) - Update all docs (README, AGENTS, CONTRIBUTING, 5 languages) to reflect new CLI workflow with create scaffolding command Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
77854ee0ed
commit
0bf982ffb9
30 files changed
+1274
-540
No files matched your search
@@ -0,0 +1,85 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// TemplateConfig holds metadata for a single mail template type.
|
||||
type TemplateConfig struct {
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
Path []string `json:"path"`
|
||||
Params map[string]any `json:"params"`
|
||||
}
|
||||
|
||||
// TemplatesConfig is the root structure of templates_config.json.
|
||||
type TemplatesConfig struct {
|
||||
Templates map[string]TemplateConfig `json:"templates"`
|
||||
}
|
||||
|
||||
// Load reads and parses a templates_config.json file.
|
||||
func Load(path string) (*TemplatesConfig, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot read config file %s: %w", path, err)
|
||||
}
|
||||
|
||||
var cfg TemplatesConfig
|
||||
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("cannot parse config file %s: %w", path, err)
|
||||
}
|
||||
|
||||
if len(cfg.Templates) == 0 {
|
||||
return nil, fmt.Errorf("config file %s contains no template definitions", path)
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// PathStr returns the file path of a template definition joined with OS separator.
|
||||
func (t *TemplateConfig) PathStr() string {
|
||||
return filepath.Join(t.Path...)
|
||||
}
|
||||
|
||||
// Registry builds a lookup map suitable for serializing into rendered.js
|
||||
// as window.__REGISTRY__: maps template ID → {name, desc, path}.
|
||||
func Registry(cfg *TemplatesConfig) map[string]map[string]string {
|
||||
reg := make(map[string]map[string]string, len(cfg.Templates))
|
||||
for id, t := range cfg.Templates {
|
||||
reg[id] = map[string]string{
|
||||
"name": t.Name,
|
||||
"desc": t.Desc,
|
||||
"path": strings.Join(t.Path, "/"),
|
||||
}
|
||||
}
|
||||
return reg
|
||||
}
|
||||
|
||||
// FlattenParams converts nested template params into a flat dot-notation map
|
||||
// suitable for the preview panel display in rendered.js.
|
||||
func FlattenParams(params map[string]any) map[string]string {
|
||||
flat := make(map[string]string)
|
||||
flatten("", params, flat)
|
||||
return flat
|
||||
}
|
||||
|
||||
func flatten(prefix string, val any, out map[string]string) {
|
||||
switch v := val.(type) {
|
||||
case map[string]any:
|
||||
for k, inner := range v {
|
||||
key := k
|
||||
if prefix != "" {
|
||||
key = prefix + "." + k
|
||||
}
|
||||
flatten(key, inner, out)
|
||||
}
|
||||
case []any:
|
||||
out[prefix] = fmt.Sprintf("[%d items]", len(v))
|
||||
default:
|
||||
out[prefix] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user