github.com/lyeb/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  }