package deps import ( "io/ioutil" "log" "os" "time" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/metrics" "github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/source" "github.com/gohugoio/hugo/tpl" jww "github.com/spf13/jwalterweatherman" ) // Deps holds dependencies used by many. // There will be normally only one instance of deps in play // at a given time, i.e. one per Site built. type Deps struct { // The logger to use. Log *jww.Notepad `json:"-"` // Used to log errors that may repeat itself many times. DistinctErrorLog *helpers.DistinctLogger // The templates to use. This will usually implement the full tpl.TemplateHandler. Tmpl tpl.TemplateFinder `json:"-"` // The file systems to use. Fs *hugofs.Fs `json:"-"` // The PathSpec to use *helpers.PathSpec `json:"-"` // The ContentSpec to use *helpers.ContentSpec `json:"-"` // The SourceSpec to use SourceSpec *source.SourceSpec `json:"-"` // The configuration to use Cfg config.Provider `json:"-"` // The translation func to use Translate func(translationID string, args ...interface{}) string `json:"-"` Language *helpers.Language // All the output formats available for the current site. OutputFormatsConfig output.Formats templateProvider ResourceProvider WithTemplate func(templ tpl.TemplateHandler) error `json:"-"` translationProvider ResourceProvider Metrics metrics.Provider // Timeout is configurable in site config. Timeout time.Duration } // ResourceProvider is used to create and refresh, and clone resources needed. type ResourceProvider interface { Update(deps *Deps) error Clone(deps *Deps) error } // TemplateHandler returns the used tpl.TemplateFinder as tpl.TemplateHandler. func (d *Deps) TemplateHandler() tpl.TemplateHandler { return d.Tmpl.(tpl.TemplateHandler) } // LoadResources loads translations and templates. func (d *Deps) LoadResources() error { // Note that the translations need to be loaded before the templates. if err := d.translationProvider.Update(d); err != nil { return err } if err := d.templateProvider.Update(d); err != nil { return err } if th, ok := d.Tmpl.(tpl.TemplateHandler); ok { th.PrintErrors() } return nil } // New initializes a Dep struct. // Defaults are set for nil values, // but TemplateProvider, TranslationProvider and Language are always required. func New(cfg DepsCfg) (*Deps, error) { var ( logger = cfg.Logger fs = cfg.Fs ) if cfg.TemplateProvider == nil { panic("Must have a TemplateProvider") } if cfg.TranslationProvider == nil { panic("Must have a TranslationProvider") } if cfg.Language == nil { panic("Must have a Language") } if logger == nil { logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime) } if fs == nil { // Default to the production file system. fs = hugofs.NewDefault(cfg.Language) } ps, err := helpers.NewPathSpec(fs, cfg.Language) if err != nil { return nil, err } contentSpec, err := helpers.NewContentSpec(cfg.Language) if err != nil { return nil, err } sp := source.NewSourceSpec(ps, fs.Source) timeoutms := cfg.Language.GetInt("timeout") if timeoutms <= 0 { timeoutms = 3000 } distinctErrorLogger := helpers.NewDistinctLogger(logger.ERROR) d := &Deps{ Fs: fs, Log: logger, DistinctErrorLog: distinctErrorLogger, templateProvider: cfg.TemplateProvider, translationProvider: cfg.TranslationProvider, WithTemplate: cfg.WithTemplate, PathSpec: ps, ContentSpec: contentSpec, SourceSpec: sp, Cfg: cfg.Language, Language: cfg.Language, Timeout: time.Duration(timeoutms) * time.Millisecond, } if cfg.Cfg.GetBool("templateMetrics") { d.Metrics = metrics.NewProvider(cfg.Cfg.GetBool("templateMetricsHints")) } return d, nil } // ForLanguage creates a copy of the Deps with the language dependent // parts switched out. func (d Deps) ForLanguage(l *helpers.Language) (*Deps, error) { var err error d.PathSpec, err = helpers.NewPathSpec(d.Fs, l) if err != nil { return nil, err } d.ContentSpec, err = helpers.NewContentSpec(l) if err != nil { return nil, err } d.Cfg = l d.Language = l if err := d.translationProvider.Clone(&d); err != nil { return nil, err } if err := d.templateProvider.Clone(&d); err != nil { return nil, err } return &d, nil } // DepsCfg contains configuration options that can be used to configure Hugo // on a global level, i.e. logging etc. // Nil values will be given default values. type DepsCfg struct { // The Logger to use. Logger *jww.Notepad // The file systems to use Fs *hugofs.Fs // The language to use. Language *helpers.Language // The configuration to use. Cfg config.Provider // Template handling. TemplateProvider ResourceProvider WithTemplate func(templ tpl.TemplateHandler) error // i18n handling. TranslationProvider ResourceProvider // Whether we are in running (server) mode Running bool }