github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/config/configLoader.go (about) 1 // Copyright 2018 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package config 15 16 import ( 17 "fmt" 18 "os" 19 "path/filepath" 20 "strings" 21 22 "github.com/gohugoio/hugo/common/herrors" 23 24 "github.com/gohugoio/hugo/common/paths" 25 26 "github.com/gohugoio/hugo/common/maps" 27 "github.com/gohugoio/hugo/parser/metadecoders" 28 "github.com/spf13/afero" 29 ) 30 31 var ( 32 // See issue #8979 for context. 33 // Hugo has always used config.toml etc. as the default config file name. 34 // But hugo.toml is a more descriptive name, but we need to check for both. 35 DefaultConfigNames = []string{"hugo", "config"} 36 37 DefaultConfigNamesSet = make(map[string]bool) 38 39 ValidConfigFileExtensions = []string{"toml", "yaml", "yml", "json"} 40 validConfigFileExtensionsMap map[string]bool = make(map[string]bool) 41 ) 42 43 func init() { 44 for _, name := range DefaultConfigNames { 45 DefaultConfigNamesSet[name] = true 46 } 47 48 for _, ext := range ValidConfigFileExtensions { 49 validConfigFileExtensionsMap[ext] = true 50 } 51 } 52 53 // IsValidConfigFilename returns whether filename is one of the supported 54 // config formats in Hugo. 55 func IsValidConfigFilename(filename string) bool { 56 ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(filename), ".")) 57 return validConfigFileExtensionsMap[ext] 58 } 59 60 func FromTOMLConfigString(config string) Provider { 61 cfg, err := FromConfigString(config, "toml") 62 if err != nil { 63 panic(err) 64 } 65 return cfg 66 } 67 68 // FromConfigString creates a config from the given YAML, JSON or TOML config. This is useful in tests. 69 func FromConfigString(config, configType string) (Provider, error) { 70 m, err := readConfig(metadecoders.FormatFromString(configType), []byte(config)) 71 if err != nil { 72 return nil, err 73 } 74 return NewFrom(m), nil 75 } 76 77 // FromFile loads the configuration from the given filename. 78 func FromFile(fs afero.Fs, filename string) (Provider, error) { 79 m, err := loadConfigFromFile(fs, filename) 80 if err != nil { 81 fe := herrors.UnwrapFileError(err) 82 if fe != nil { 83 pos := fe.Position() 84 pos.Filename = filename 85 fe.UpdatePosition(pos) 86 return nil, err 87 } 88 return nil, herrors.NewFileErrorFromFile(err, filename, fs, nil) 89 } 90 return NewFrom(m), nil 91 } 92 93 // FromFileToMap is the same as FromFile, but it returns the config values 94 // as a simple map. 95 func FromFileToMap(fs afero.Fs, filename string) (map[string]any, error) { 96 return loadConfigFromFile(fs, filename) 97 } 98 99 func readConfig(format metadecoders.Format, data []byte) (map[string]any, error) { 100 m, err := metadecoders.Default.UnmarshalToMap(data, format) 101 if err != nil { 102 return nil, err 103 } 104 105 RenameKeys(m) 106 107 return m, nil 108 } 109 110 func loadConfigFromFile(fs afero.Fs, filename string) (map[string]any, error) { 111 m, err := metadecoders.Default.UnmarshalFileToMap(fs, filename) 112 if err != nil { 113 return nil, err 114 } 115 RenameKeys(m) 116 return m, nil 117 } 118 119 func LoadConfigFromDir(sourceFs afero.Fs, configDir, environment string) (Provider, []string, error) { 120 defaultConfigDir := filepath.Join(configDir, "_default") 121 environmentConfigDir := filepath.Join(configDir, environment) 122 cfg := New() 123 124 var configDirs []string 125 // Merge from least to most specific. 126 for _, dir := range []string{defaultConfigDir, environmentConfigDir} { 127 if _, err := sourceFs.Stat(dir); err == nil { 128 configDirs = append(configDirs, dir) 129 } 130 } 131 132 if len(configDirs) == 0 { 133 return nil, nil, nil 134 } 135 136 // Keep track of these so we can watch them for changes. 137 var dirnames []string 138 139 for _, configDir := range configDirs { 140 err := afero.Walk(sourceFs, configDir, func(path string, fi os.FileInfo, err error) error { 141 if fi == nil || err != nil { 142 return nil 143 } 144 145 if fi.IsDir() { 146 dirnames = append(dirnames, path) 147 return nil 148 } 149 150 if !IsValidConfigFilename(path) { 151 return nil 152 } 153 154 name := paths.Filename(filepath.Base(path)) 155 156 item, err := metadecoders.Default.UnmarshalFileToMap(sourceFs, path) 157 if err != nil { 158 // This will be used in error reporting, use the most specific value. 159 dirnames = []string{path} 160 return fmt.Errorf("failed to unmarshal config for path %q: %w", path, err) 161 } 162 163 var keyPath []string 164 if !DefaultConfigNamesSet[name] { 165 // Can be params.jp, menus.en etc. 166 name, lang := paths.FileAndExtNoDelimiter(name) 167 168 keyPath = []string{name} 169 170 if lang != "" { 171 keyPath = []string{"languages", lang} 172 switch name { 173 case "menu", "menus": 174 keyPath = append(keyPath, "menus") 175 case "params": 176 keyPath = append(keyPath, "params") 177 } 178 } 179 } 180 181 root := item 182 if len(keyPath) > 0 { 183 root = make(map[string]any) 184 m := root 185 for i, key := range keyPath { 186 if i >= len(keyPath)-1 { 187 m[key] = item 188 } else { 189 nm := make(map[string]any) 190 m[key] = nm 191 m = nm 192 } 193 } 194 } 195 196 // Migrate menu => menus etc. 197 RenameKeys(root) 198 199 // Set will overwrite keys with the same name, recursively. 200 cfg.Set("", root) 201 202 return nil 203 }) 204 if err != nil { 205 return nil, dirnames, err 206 } 207 208 } 209 210 return cfg, dirnames, nil 211 212 } 213 214 var keyAliases maps.KeyRenamer 215 216 func init() { 217 var err error 218 keyAliases, err = maps.NewKeyRenamer( 219 // Before 0.53 we used singular for "menu". 220 "{menu,languages/*/menu}", "menus", 221 ) 222 223 if err != nil { 224 panic(err) 225 } 226 } 227 228 // RenameKeys renames config keys in m recursively according to a global Hugo 229 // alias definition. 230 func RenameKeys(m map[string]any) { 231 keyAliases.Rename(m) 232 }