hugo

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

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

client.go (4495B)

    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 godartsass integrates with the Dass Sass Embedded protocol to transpile
   15 // SCSS/SASS.
   16 package dartsass
   17 
   18 import (
   19 	"fmt"
   20 	"io"
   21 	"strings"
   22 
   23 	"github.com/gohugoio/hugo/common/herrors"
   24 	"github.com/gohugoio/hugo/helpers"
   25 	"github.com/gohugoio/hugo/hugofs"
   26 	"github.com/gohugoio/hugo/hugolib/filesystems"
   27 	"github.com/gohugoio/hugo/resources"
   28 	"github.com/gohugoio/hugo/resources/resource"
   29 	"github.com/spf13/afero"
   30 
   31 	"github.com/bep/godartsass"
   32 	"github.com/mitchellh/mapstructure"
   33 )
   34 
   35 // used as part of the cache key.
   36 const transformationName = "tocss-dart"
   37 
   38 // See https://github.com/sass/dart-sass-embedded/issues/24
   39 // Note: This prefix must be all lower case.
   40 const dartSassStdinPrefix = "hugostdin:"
   41 
   42 func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error) {
   43 	if !Supports() {
   44 		return &Client{dartSassNotAvailable: true}, nil
   45 	}
   46 
   47 	if err := rs.ExecHelper.Sec().CheckAllowedExec(dartSassEmbeddedBinaryName); err != nil {
   48 		return nil, err
   49 	}
   50 
   51 	transpiler, err := godartsass.Start(godartsass.Options{
   52 		LogEventHandler: func(event godartsass.LogEvent) {
   53 			message := strings.ReplaceAll(event.Message, dartSassStdinPrefix, "")
   54 			switch event.Type {
   55 			case godartsass.LogEventTypeDebug:
   56 				// Log as Info for now, we may adjust this if it gets too chatty.
   57 				rs.Logger.Infof("Dart Sass: %s", message)
   58 			default:
   59 				// The rest are either deprecations or @warn statements.
   60 				rs.Logger.Warnf("Dart Sass: %s", message)
   61 			}
   62 		},
   63 	})
   64 	if err != nil {
   65 		return nil, err
   66 	}
   67 	return &Client{sfs: fs, workFs: rs.BaseFs.Work, rs: rs, transpiler: transpiler}, nil
   68 }
   69 
   70 type Client struct {
   71 	dartSassNotAvailable bool
   72 	rs                   *resources.Spec
   73 	sfs                  *filesystems.SourceFilesystem
   74 	workFs               afero.Fs
   75 	transpiler           *godartsass.Transpiler
   76 }
   77 
   78 func (c *Client) ToCSS(res resources.ResourceTransformer, args map[string]any) (resource.Resource, error) {
   79 	if c.dartSassNotAvailable {
   80 		return res.Transform(resources.NewFeatureNotAvailableTransformer(transformationName, args))
   81 	}
   82 	return res.Transform(&transform{c: c, optsm: args})
   83 }
   84 
   85 func (c *Client) Close() error {
   86 	if c.transpiler == nil {
   87 		return nil
   88 	}
   89 	return c.transpiler.Close()
   90 }
   91 
   92 func (c *Client) toCSS(args godartsass.Args, src io.Reader) (godartsass.Result, error) {
   93 	var res godartsass.Result
   94 
   95 	in := helpers.ReaderToString(src)
   96 	args.Source = in
   97 
   98 	res, err := c.transpiler.Execute(args)
   99 	if err != nil {
  100 		if err.Error() == "unexpected EOF" {
  101 			return res, fmt.Errorf("got unexpected EOF when executing %q. The user running hugo must have read and execute permissions on this program. With execute permissions only, this error is thrown.", dartSassEmbeddedBinaryName)
  102 		}
  103 		return res, herrors.NewFileErrorFromFileInErr(err, hugofs.Os, herrors.OffsetMatcher)
  104 	}
  105 
  106 	return res, err
  107 }
  108 
  109 type Options struct {
  110 
  111 	// Hugo, will by default, just replace the extension of the source
  112 	// to .css, e.g. "scss/main.scss" becomes "scss/main.css". You can
  113 	// control this by setting this, e.g. "styles/main.css" will create
  114 	// a Resource with that as a base for RelPermalink etc.
  115 	TargetPath string
  116 
  117 	// Hugo automatically adds the entry directories (where the main.scss lives)
  118 	// for project and themes to the list of include paths sent to LibSASS.
  119 	// Any paths set in this setting will be appended. Note that these will be
  120 	// treated as relative to the working dir, i.e. no include paths outside the
  121 	// project/themes.
  122 	IncludePaths []string
  123 
  124 	// Default is nested.
  125 	// One of nested, expanded, compact, compressed.
  126 	OutputStyle string
  127 
  128 	// When enabled, Hugo will generate a source map.
  129 	EnableSourceMap bool
  130 }
  131 
  132 func decodeOptions(m map[string]any) (opts Options, err error) {
  133 	if m == nil {
  134 		return
  135 	}
  136 	err = mapstructure.WeakDecode(m, &opts)
  137 
  138 	if opts.TargetPath != "" {
  139 		opts.TargetPath = helpers.ToSlashTrimLeading(opts.TargetPath)
  140 	}
  141 
  142 	return
  143 }