github.com/fighterlyt/hugo@v0.47.1/deps/deps.go (about) 1 package deps 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/gohugoio/hugo/common/loggers" 8 9 "github.com/gohugoio/hugo/config" 10 "github.com/gohugoio/hugo/helpers" 11 "github.com/gohugoio/hugo/hugofs" 12 "github.com/gohugoio/hugo/langs" 13 "github.com/gohugoio/hugo/media" 14 "github.com/gohugoio/hugo/metrics" 15 "github.com/gohugoio/hugo/output" 16 "github.com/gohugoio/hugo/resource" 17 "github.com/gohugoio/hugo/source" 18 "github.com/gohugoio/hugo/tpl" 19 jww "github.com/spf13/jwalterweatherman" 20 ) 21 22 // Deps holds dependencies used by many. 23 // There will be normally only one instance of deps in play 24 // at a given time, i.e. one per Site built. 25 type Deps struct { 26 27 // The logger to use. 28 Log *jww.Notepad `json:"-"` 29 30 // Used to log errors that may repeat itself many times. 31 DistinctErrorLog *helpers.DistinctLogger 32 33 // The templates to use. This will usually implement the full tpl.TemplateHandler. 34 Tmpl tpl.TemplateFinder `json:"-"` 35 36 // We use this to parse and execute ad-hoc text templates. 37 TextTmpl tpl.TemplateParseFinder `json:"-"` 38 39 // The file systems to use. 40 Fs *hugofs.Fs `json:"-"` 41 42 // The PathSpec to use 43 *helpers.PathSpec `json:"-"` 44 45 // The ContentSpec to use 46 *helpers.ContentSpec `json:"-"` 47 48 // The SourceSpec to use 49 SourceSpec *source.SourceSpec `json:"-"` 50 51 // The Resource Spec to use 52 ResourceSpec *resource.Spec 53 54 // The configuration to use 55 Cfg config.Provider `json:"-"` 56 57 // The translation func to use 58 Translate func(translationID string, args ...interface{}) string `json:"-"` 59 60 Language *langs.Language 61 62 // All the output formats available for the current site. 63 OutputFormatsConfig output.Formats 64 65 templateProvider ResourceProvider 66 WithTemplate func(templ tpl.TemplateHandler) error `json:"-"` 67 68 translationProvider ResourceProvider 69 70 Metrics metrics.Provider 71 72 // Timeout is configurable in site config. 73 Timeout time.Duration 74 75 // BuildStartListeners will be notified before a build starts. 76 BuildStartListeners *Listeners 77 } 78 79 type Listeners struct { 80 sync.Mutex 81 82 // A list of funcs to be notified about an event. 83 listeners []func() 84 } 85 86 func (b *Listeners) Add(f func()) { 87 b.Lock() 88 defer b.Unlock() 89 b.listeners = append(b.listeners, f) 90 } 91 92 func (b *Listeners) Notify() { 93 b.Lock() 94 defer b.Unlock() 95 for _, notify := range b.listeners { 96 notify() 97 } 98 } 99 100 // ResourceProvider is used to create and refresh, and clone resources needed. 101 type ResourceProvider interface { 102 Update(deps *Deps) error 103 Clone(deps *Deps) error 104 } 105 106 // TemplateHandler returns the used tpl.TemplateFinder as tpl.TemplateHandler. 107 func (d *Deps) TemplateHandler() tpl.TemplateHandler { 108 return d.Tmpl.(tpl.TemplateHandler) 109 } 110 111 // LoadResources loads translations and templates. 112 func (d *Deps) LoadResources() error { 113 // Note that the translations need to be loaded before the templates. 114 if err := d.translationProvider.Update(d); err != nil { 115 return err 116 } 117 118 if err := d.templateProvider.Update(d); err != nil { 119 return err 120 } 121 122 if th, ok := d.Tmpl.(tpl.TemplateHandler); ok { 123 th.PrintErrors() 124 } 125 126 return nil 127 } 128 129 // New initializes a Dep struct. 130 // Defaults are set for nil values, 131 // but TemplateProvider, TranslationProvider and Language are always required. 132 func New(cfg DepsCfg) (*Deps, error) { 133 var ( 134 logger = cfg.Logger 135 fs = cfg.Fs 136 ) 137 138 if cfg.TemplateProvider == nil { 139 panic("Must have a TemplateProvider") 140 } 141 142 if cfg.TranslationProvider == nil { 143 panic("Must have a TranslationProvider") 144 } 145 146 if cfg.Language == nil { 147 panic("Must have a Language") 148 } 149 150 if logger == nil { 151 logger = loggers.NewErrorLogger() 152 } 153 154 if fs == nil { 155 // Default to the production file system. 156 fs = hugofs.NewDefault(cfg.Language) 157 } 158 159 ps, err := helpers.NewPathSpec(fs, cfg.Language) 160 161 if err != nil { 162 return nil, err 163 } 164 165 resourceSpec, err := resource.NewSpec(ps, logger, cfg.OutputFormats, cfg.MediaTypes) 166 if err != nil { 167 return nil, err 168 } 169 170 contentSpec, err := helpers.NewContentSpec(cfg.Language) 171 if err != nil { 172 return nil, err 173 } 174 175 sp := source.NewSourceSpec(ps, fs.Source) 176 177 timeoutms := cfg.Language.GetInt("timeout") 178 if timeoutms <= 0 { 179 timeoutms = 3000 180 } 181 182 distinctErrorLogger := helpers.NewDistinctLogger(logger.ERROR) 183 184 d := &Deps{ 185 Fs: fs, 186 Log: logger, 187 DistinctErrorLog: distinctErrorLogger, 188 templateProvider: cfg.TemplateProvider, 189 translationProvider: cfg.TranslationProvider, 190 WithTemplate: cfg.WithTemplate, 191 PathSpec: ps, 192 ContentSpec: contentSpec, 193 SourceSpec: sp, 194 ResourceSpec: resourceSpec, 195 Cfg: cfg.Language, 196 Language: cfg.Language, 197 BuildStartListeners: &Listeners{}, 198 Timeout: time.Duration(timeoutms) * time.Millisecond, 199 } 200 201 if cfg.Cfg.GetBool("templateMetrics") { 202 d.Metrics = metrics.NewProvider(cfg.Cfg.GetBool("templateMetricsHints")) 203 } 204 205 return d, nil 206 } 207 208 // ForLanguage creates a copy of the Deps with the language dependent 209 // parts switched out. 210 func (d Deps) ForLanguage(cfg DepsCfg) (*Deps, error) { 211 l := cfg.Language 212 var err error 213 214 d.PathSpec, err = helpers.NewPathSpecWithBaseBaseFsProvided(d.Fs, l, d.BaseFs) 215 if err != nil { 216 return nil, err 217 } 218 219 d.ContentSpec, err = helpers.NewContentSpec(l) 220 if err != nil { 221 return nil, err 222 } 223 224 // The resource cache is global so reuse. 225 // TODO(bep) clean up these inits. 226 resourceCache := d.ResourceSpec.ResourceCache 227 d.ResourceSpec, err = resource.NewSpec(d.PathSpec, d.Log, cfg.OutputFormats, cfg.MediaTypes) 228 if err != nil { 229 return nil, err 230 } 231 d.ResourceSpec.ResourceCache = resourceCache 232 233 d.Cfg = l 234 d.Language = l 235 236 if err := d.translationProvider.Clone(&d); err != nil { 237 return nil, err 238 } 239 240 if err := d.templateProvider.Clone(&d); err != nil { 241 return nil, err 242 } 243 244 d.BuildStartListeners = &Listeners{} 245 246 return &d, nil 247 248 } 249 250 // DepsCfg contains configuration options that can be used to configure Hugo 251 // on a global level, i.e. logging etc. 252 // Nil values will be given default values. 253 type DepsCfg struct { 254 255 // The Logger to use. 256 Logger *jww.Notepad 257 258 // The file systems to use 259 Fs *hugofs.Fs 260 261 // The language to use. 262 Language *langs.Language 263 264 // The configuration to use. 265 Cfg config.Provider 266 267 // The media types configured. 268 MediaTypes media.Types 269 270 // The output formats configured. 271 OutputFormats output.Formats 272 273 // Template handling. 274 TemplateProvider ResourceProvider 275 WithTemplate func(templ tpl.TemplateHandler) error 276 277 // i18n handling. 278 TranslationProvider ResourceProvider 279 280 // Whether we are in running (server) mode 281 Running bool 282 }