package config import ( "errors" "fmt" "maps" "os" "path/filepath" "gopkg.in/yaml.v3" ) // The properties that are depended upon in the source code are declared explicitly in the config struct. // The constructors will set default values for most. // Depending on the command, different defaults will be used (serve is assumed to be a "dev" environment // while build is assumed to be prod) // Some defaults could be overridden by cli flags (eg disable live reload on serve). // The user can override some of those via config yaml. // The non declared values found in config yaml will just be passed as site.config values type Config struct { RootDir string SrcDir string TargetDir string LayoutsDir string IncludesDir string DataDir string SiteUrl string PostFormat string Lang string HighlightTheme string Minify bool LiveReload bool LinkStatic bool IncludeDrafts bool ServerHost string ServerPort int pageDefaults map[string]interface{} // the user provided overrides, as found in config.yml // these will passed as found as template context overrides map[string]interface{} } func Load(rootDir string) (*Config, error) { // TODO allow to disable minify config := &Config{ RootDir: rootDir, SrcDir: filepath.Join(rootDir, "src"), TargetDir: filepath.Join(rootDir, "target"), LayoutsDir: filepath.Join(rootDir, "layouts"), IncludesDir: filepath.Join(rootDir, "includes"), DataDir: filepath.Join(rootDir, "data"), PostFormat: "blog/:title.org", Lang: "en", HighlightTheme: "github", Minify: true, LiveReload: false, LinkStatic: false, IncludeDrafts: false, pageDefaults: map[string]interface{}{}, } // load overrides from config.yml configPath := filepath.Join(rootDir, "config.yml") yamlContent, err := os.ReadFile(configPath) if errors.Is(err, os.ErrNotExist) { // config file is not mandatory return config, nil } else if err != nil { return nil, err } err = yaml.Unmarshal(yamlContent, &config.overrides) if err != nil { return nil, err } // set user-provided overrides of declared config keys // TODO less copypasty way of declaring config overrides if url, found := config.overrides["url"]; found { config.SiteUrl = url.(string) } if format, found := config.overrides["post_format"]; found { config.PostFormat = format.(string) } if format, found := config.overrides["lang"]; found { config.Lang = format.(string) } if format, found := config.overrides["highlight_theme"]; found { config.HighlightTheme = format.(string) } return config, nil } func LoadDev(rootDir string, host string, port int, reload bool) (*Config, error) { // TODO revisit is this Load vs LoadDevServer is the best way to handle both modes // TODO some of the options need to be overridable: host, port, live reload at least config, err := Load(rootDir) if err != nil { return nil, err } // setup serve command specific overrides (these could be eventually tweaked with flags) config.ServerHost = host config.ServerPort = port config.LiveReload = reload config.Minify = false config.LinkStatic = true config.IncludeDrafts = true config.SiteUrl = fmt.Sprintf("http://%s:%d", config.ServerHost, config.ServerPort) return config, nil } func (config Config) AsContext() map[string]interface{} { context := map[string]interface{}{ "url": config.SiteUrl, } maps.Copy(context, config.overrides) return context }