github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/resources/resource_transformers/tocss/dartsass/transform.go (about)

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