hugo

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

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

transform.go (4451B)

    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 dartsass
   15 
   16 import (
   17 	"fmt"
   18 	"io"
   19 	"path"
   20 	"path/filepath"
   21 	"strings"
   22 
   23 	"github.com/gohugoio/hugo/common/hexec"
   24 	"github.com/gohugoio/hugo/common/paths"
   25 	"github.com/gohugoio/hugo/htesting"
   26 	"github.com/gohugoio/hugo/media"
   27 
   28 	"github.com/gohugoio/hugo/resources"
   29 
   30 	"github.com/gohugoio/hugo/resources/internal"
   31 
   32 	"github.com/spf13/afero"
   33 
   34 	"github.com/gohugoio/hugo/hugofs"
   35 
   36 	"github.com/bep/godartsass"
   37 )
   38 
   39 const (
   40 	dartSassEmbeddedBinaryName = "dart-sass-embedded"
   41 )
   42 
   43 // Supports returns whether dart-sass-embedded is found in $PATH.
   44 func Supports() bool {
   45 	if htesting.SupportsAll() {
   46 		return true
   47 	}
   48 	return hexec.InPath(dartSassEmbeddedBinaryName)
   49 }
   50 
   51 type transform struct {
   52 	optsm map[string]any
   53 	c     *Client
   54 }
   55 
   56 func (t *transform) Key() internal.ResourceTransformationKey {
   57 	return internal.NewResourceTransformationKey(transformationName, t.optsm)
   58 }
   59 
   60 func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error {
   61 	ctx.OutMediaType = media.CSSType
   62 
   63 	opts, err := decodeOptions(t.optsm)
   64 	if err != nil {
   65 		return err
   66 	}
   67 
   68 	if opts.TargetPath != "" {
   69 		ctx.OutPath = opts.TargetPath
   70 	} else {
   71 		ctx.ReplaceOutPathExtension(".css")
   72 	}
   73 
   74 	baseDir := path.Dir(ctx.SourcePath)
   75 	filename := dartSassStdinPrefix
   76 
   77 	if ctx.SourcePath != "" {
   78 		filename += t.c.sfs.RealFilename(ctx.SourcePath)
   79 	}
   80 
   81 	args := godartsass.Args{
   82 		URL:          filename,
   83 		IncludePaths: t.c.sfs.RealDirs(baseDir),
   84 		ImportResolver: importResolver{
   85 			baseDir: baseDir,
   86 			c:       t.c,
   87 		},
   88 		OutputStyle:     godartsass.ParseOutputStyle(opts.OutputStyle),
   89 		EnableSourceMap: opts.EnableSourceMap,
   90 	}
   91 
   92 	// Append any workDir relative include paths
   93 	for _, ip := range opts.IncludePaths {
   94 		info, err := t.c.workFs.Stat(filepath.Clean(ip))
   95 		if err == nil {
   96 			filename := info.(hugofs.FileMetaInfo).Meta().Filename
   97 			args.IncludePaths = append(args.IncludePaths, filename)
   98 		}
   99 	}
  100 
  101 	if ctx.InMediaType.SubType == media.SASSType.SubType {
  102 		args.SourceSyntax = godartsass.SourceSyntaxSASS
  103 	}
  104 
  105 	res, err := t.c.toCSS(args, ctx.From)
  106 	if err != nil {
  107 		return err
  108 	}
  109 
  110 	out := res.CSS
  111 
  112 	_, err = io.WriteString(ctx.To, out)
  113 	if err != nil {
  114 		return err
  115 	}
  116 
  117 	if opts.EnableSourceMap && res.SourceMap != "" {
  118 		if err := ctx.PublishSourceMap(res.SourceMap); err != nil {
  119 			return err
  120 		}
  121 		_, err = fmt.Fprintf(ctx.To, "\n\n/*# sourceMappingURL=%s */", path.Base(ctx.OutPath)+".map")
  122 	}
  123 
  124 	return err
  125 }
  126 
  127 type importResolver struct {
  128 	baseDir string
  129 	c       *Client
  130 }
  131 
  132 func (t importResolver) CanonicalizeURL(url string) (string, error) {
  133 	filePath, isURL := paths.UrlToFilename(url)
  134 	var prevDir string
  135 	var pathDir string
  136 	if isURL {
  137 		var found bool
  138 		prevDir, found = t.c.sfs.MakePathRelative(filepath.Dir(filePath))
  139 
  140 		if !found {
  141 			// Not a member of this filesystem, let Dart Sass handle it.
  142 			return "", nil
  143 		}
  144 	} else {
  145 		prevDir = t.baseDir
  146 		pathDir = path.Dir(url)
  147 	}
  148 
  149 	basePath := filepath.Join(prevDir, pathDir)
  150 	name := filepath.Base(filePath)
  151 
  152 	// Pick the first match.
  153 	var namePatterns []string
  154 	if strings.Contains(name, ".") {
  155 		namePatterns = []string{"_%s", "%s"}
  156 	} else if strings.HasPrefix(name, "_") {
  157 		namePatterns = []string{"_%s.scss", "_%s.sass"}
  158 	} else {
  159 		namePatterns = []string{"_%s.scss", "%s.scss", "_%s.sass", "%s.sass"}
  160 	}
  161 
  162 	name = strings.TrimPrefix(name, "_")
  163 
  164 	for _, namePattern := range namePatterns {
  165 		filenameToCheck := filepath.Join(basePath, fmt.Sprintf(namePattern, name))
  166 		fi, err := t.c.sfs.Fs.Stat(filenameToCheck)
  167 		if err == nil {
  168 			if fim, ok := fi.(hugofs.FileMetaInfo); ok {
  169 				return "file://" + filepath.ToSlash(fim.Meta().Filename), nil
  170 			}
  171 		}
  172 	}
  173 
  174 	// Not found, let Dart Dass handle it
  175 	return "", nil
  176 }
  177 
  178 func (t importResolver) Load(url string) (string, error) {
  179 	filename, _ := paths.UrlToFilename(url)
  180 	b, err := afero.ReadFile(hugofs.Os, filename)
  181 	return string(b), err
  182 }