hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

publisher.go (5239B)

    1 // Copyright 2020 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 publisher
   15 
   16 import (
   17 	"errors"
   18 	"fmt"
   19 	"io"
   20 	"net/url"
   21 	"sync/atomic"
   22 
   23 	"github.com/gohugoio/hugo/resources"
   24 
   25 	"github.com/gohugoio/hugo/media"
   26 
   27 	"github.com/gohugoio/hugo/minifiers"
   28 
   29 	bp "github.com/gohugoio/hugo/bufferpool"
   30 	"github.com/gohugoio/hugo/helpers"
   31 
   32 	"github.com/spf13/afero"
   33 
   34 	"github.com/gohugoio/hugo/output"
   35 	"github.com/gohugoio/hugo/transform"
   36 	"github.com/gohugoio/hugo/transform/livereloadinject"
   37 	"github.com/gohugoio/hugo/transform/metainject"
   38 	"github.com/gohugoio/hugo/transform/urlreplacers"
   39 )
   40 
   41 // Descriptor describes the needed publishing chain for an item.
   42 type Descriptor struct {
   43 	// The content to publish.
   44 	Src io.Reader
   45 
   46 	// The OutputFormat of the this content.
   47 	OutputFormat output.Format
   48 
   49 	// Where to publish this content. This is a filesystem-relative path.
   50 	TargetPath string
   51 
   52 	// Counter for the end build summary.
   53 	StatCounter *uint64
   54 
   55 	// Configuration that trigger pre-processing.
   56 	// LiveReload script will be injected if this is != nil
   57 	LiveReloadBaseURL *url.URL
   58 
   59 	// Enable to inject the Hugo generated tag in the header. Is currently only
   60 	// injected on the home page for HTML type of output formats.
   61 	AddHugoGeneratorTag bool
   62 
   63 	// If set, will replace all relative URLs with this one.
   64 	AbsURLPath string
   65 
   66 	// Enable to minify the output using the OutputFormat defined above to
   67 	// pick the correct minifier configuration.
   68 	Minify bool
   69 }
   70 
   71 // DestinationPublisher is the default and currently only publisher in Hugo. This
   72 // publisher prepares and publishes an item to the defined destination, e.g. /public.
   73 type DestinationPublisher struct {
   74 	fs                    afero.Fs
   75 	min                   minifiers.Client
   76 	htmlElementsCollector *htmlElementsCollector
   77 }
   78 
   79 // NewDestinationPublisher creates a new DestinationPublisher.
   80 func NewDestinationPublisher(rs *resources.Spec, outputFormats output.Formats, mediaTypes media.Types) (pub DestinationPublisher, err error) {
   81 	fs := rs.BaseFs.PublishFs
   82 	cfg := rs.Cfg
   83 	var classCollector *htmlElementsCollector
   84 	if rs.BuildConfig.WriteStats {
   85 		classCollector = newHTMLElementsCollector()
   86 	}
   87 	pub = DestinationPublisher{fs: fs, htmlElementsCollector: classCollector}
   88 	pub.min, err = minifiers.New(mediaTypes, outputFormats, cfg)
   89 	return
   90 }
   91 
   92 // Publish applies any relevant transformations and writes the file
   93 // to its destination, e.g. /public.
   94 func (p DestinationPublisher) Publish(d Descriptor) error {
   95 	if d.TargetPath == "" {
   96 		return errors.New("Publish: must provide a TargetPath")
   97 	}
   98 
   99 	src := d.Src
  100 
  101 	transformers := p.createTransformerChain(d)
  102 
  103 	if len(transformers) != 0 {
  104 		b := bp.GetBuffer()
  105 		defer bp.PutBuffer(b)
  106 
  107 		if err := transformers.Apply(b, d.Src); err != nil {
  108 			return fmt.Errorf("failed to process %q: %w", d.TargetPath, err)
  109 		}
  110 
  111 		// This is now what we write to disk.
  112 		src = b
  113 	}
  114 
  115 	f, err := helpers.OpenFileForWriting(p.fs, d.TargetPath)
  116 	if err != nil {
  117 		return err
  118 	}
  119 	defer f.Close()
  120 
  121 	var w io.Writer = f
  122 
  123 	if p.htmlElementsCollector != nil && d.OutputFormat.IsHTML {
  124 		w = io.MultiWriter(w, newHTMLElementsCollectorWriter(p.htmlElementsCollector))
  125 	}
  126 
  127 	_, err = io.Copy(w, src)
  128 	if err == nil && d.StatCounter != nil {
  129 		atomic.AddUint64(d.StatCounter, uint64(1))
  130 	}
  131 
  132 	return err
  133 }
  134 
  135 func (p DestinationPublisher) PublishStats() PublishStats {
  136 	if p.htmlElementsCollector == nil {
  137 		return PublishStats{}
  138 	}
  139 
  140 	return PublishStats{
  141 		HTMLElements: p.htmlElementsCollector.getHTMLElements(),
  142 	}
  143 }
  144 
  145 type PublishStats struct {
  146 	HTMLElements HTMLElements `json:"htmlElements"`
  147 }
  148 
  149 // Publisher publishes a result file.
  150 type Publisher interface {
  151 	Publish(d Descriptor) error
  152 	PublishStats() PublishStats
  153 }
  154 
  155 // XML transformer := transform.New(urlreplacers.NewAbsURLInXMLTransformer(path))
  156 func (p DestinationPublisher) createTransformerChain(f Descriptor) transform.Chain {
  157 	transformers := transform.NewEmpty()
  158 
  159 	isHTML := f.OutputFormat.IsHTML
  160 
  161 	if f.AbsURLPath != "" {
  162 		if isHTML {
  163 			transformers = append(transformers, urlreplacers.NewAbsURLTransformer(f.AbsURLPath))
  164 		} else {
  165 			// Assume XML.
  166 			transformers = append(transformers, urlreplacers.NewAbsURLInXMLTransformer(f.AbsURLPath))
  167 		}
  168 	}
  169 
  170 	if isHTML {
  171 		if f.LiveReloadBaseURL != nil {
  172 			transformers = append(transformers, livereloadinject.New(*f.LiveReloadBaseURL))
  173 		}
  174 
  175 		// This is only injected on the home page.
  176 		if f.AddHugoGeneratorTag {
  177 			transformers = append(transformers, metainject.HugoGenerator)
  178 		}
  179 
  180 	}
  181 
  182 	if p.min.MinifyOutput {
  183 		minifyTransformer := p.min.Transformer(f.OutputFormat.MediaType)
  184 		if minifyTransformer != nil {
  185 			transformers = append(transformers, minifyTransformer)
  186 		}
  187 	}
  188 
  189 	return transformers
  190 }