hugo

Unnamed repository; edit this file 'description' to name the repository.

git clone git://git.shimmy1996.com/hugo.git
commit 166483fe1227b0c59c6b4d88cfdfaf7d7b0d79c5
parent f01505c910a325acc18742ac6b3637aa01975e37
Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date:   Fri, 20 Jul 2018 15:02:35 +0200

resource/scss: Add IncludePaths config option

Takes paths relative to the current working dir.

Fixes #4921

Diffstat:
Mhugolib/filesystems/basefs.go | 3+--
Mhugolib/page_bundler_capture_test.go | 3++-
Mhugolib/page_bundler_test.go | 20+++++++-------------
Mhugolib/resource_chain_test.go | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mhugolib/testhelpers_test.go | 16++++++++++++++++
Mresource/tocss/scss/client.go | 14+++++++++++---
Mresource/tocss/scss/tocss.go | 7+++++--
7 files changed, 100 insertions(+), 21 deletions(-)
diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go
@@ -244,8 +244,7 @@ func (d *SourceFilesystem) RealDirs(from string) []string {
 	var dirnames []string
 	for _, dir := range d.Dirnames {
 		dirname := filepath.Join(dir, from)
-
-		if _, err := hugofs.Os.Stat(dirname); err == nil {
+		if _, err := d.SourceFs.Stat(dirname); err == nil {
 			dirnames = append(dirnames, dirname)
 		}
 	}
diff --git a/hugolib/page_bundler_capture_test.go b/hugolib/page_bundler_capture_test.go
@@ -90,8 +90,9 @@ func TestPageBundlerCaptureSymlinks(t *testing.T) {
 	}
 
 	assert := require.New(t)
-	ps, workDir := newTestBundleSymbolicSources(t)
+	ps, clean, workDir := newTestBundleSymbolicSources(t)
 	sourceSpec := source.NewSourceSpec(ps, ps.BaseFs.Content.Fs)
+	defer clean()
 
 	fileStore := &storeFilenames{}
 	logger := loggers.NewErrorLogger()
diff --git a/hugolib/page_bundler_test.go b/hugolib/page_bundler_test.go
@@ -14,13 +14,10 @@
 package hugolib
 
 import (
-	"io/ioutil"
-
 	"github.com/gohugoio/hugo/common/loggers"
 
 	"os"
 	"runtime"
-	"strings"
 	"testing"
 
 	"github.com/gohugoio/hugo/helpers"
@@ -325,7 +322,9 @@ func TestPageBundlerSiteWitSymbolicLinksInContent(t *testing.T) {
 	}
 
 	assert := require.New(t)
-	ps, workDir := newTestBundleSymbolicSources(t)
+	ps, clean, workDir := newTestBundleSymbolicSources(t)
+	defer clean()
+
 	cfg := ps.Cfg
 	fs := ps.Fs
 
@@ -667,7 +666,7 @@ TheContent.
 	return fs, cfg
 }
 
-func newTestBundleSymbolicSources(t *testing.T) (*helpers.PathSpec, string) {
+func newTestBundleSymbolicSources(t *testing.T) (*helpers.PathSpec, func(), string) {
 	assert := require.New(t)
 	// We need to use the OS fs for this.
 	cfg := viper.New()
@@ -675,13 +674,8 @@ func newTestBundleSymbolicSources(t *testing.T) (*helpers.PathSpec, string) {
 	fs.Destination = &afero.MemMapFs{}
 	loadDefaultSettingsFor(cfg)
 
-	workDir, err := ioutil.TempDir("", "hugosym")
-
-	if runtime.GOOS == "darwin" && !strings.HasPrefix(workDir, "/private") {
-		// To get the entry folder in line with the rest. This its a little bit
-		// mysterious, but so be it.
-		workDir = "/private" + workDir
-	}
+	workDir, clean, err := createTempDir("hugosym")
+	assert.NoError(err)
 
 	contentDir := "base"
 	cfg.Set("workingDir", workDir)
@@ -753,5 +747,5 @@ TheContent.
 
 	ps, _ := helpers.NewPathSpec(fs, cfg)
 
-	return ps, workDir
+	return ps, clean, workDir
 }
diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go
@@ -14,13 +14,71 @@
 package hugolib
 
 import (
+	"os"
 	"path/filepath"
 	"testing"
 
+	"github.com/spf13/viper"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/gohugoio/hugo/hugofs"
+
 	"github.com/gohugoio/hugo/common/loggers"
 	"github.com/gohugoio/hugo/resource/tocss/scss"
 )
 
+func TestSCSSWithIncludePaths(t *testing.T) {
+	if !scss.Supports() {
+		t.Skip("Skip SCSS")
+	}
+	assert := require.New(t)
+	workDir, clean, err := createTempDir("hugo-scss-include")
+	assert.NoError(err)
+	defer clean()
+
+	v := viper.New()
+	v.Set("workingDir", workDir)
+	b := newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger())
+	b.WithViper(v)
+	b.WithWorkingDir(workDir)
+	// Need to use OS fs for this.
+	b.Fs = hugofs.NewDefault(v)
+
+	fooDir := filepath.Join(workDir, "node_modules", "foo")
+	scssDir := filepath.Join(workDir, "assets", "scss")
+	assert.NoError(os.MkdirAll(fooDir, 0777))
+	assert.NoError(os.MkdirAll(filepath.Join(workDir, "content", "sect"), 0777))
+	assert.NoError(os.MkdirAll(filepath.Join(workDir, "data"), 0777))
+	assert.NoError(os.MkdirAll(filepath.Join(workDir, "i18n"), 0777))
+	assert.NoError(os.MkdirAll(filepath.Join(workDir, "layouts", "shortcodes"), 0777))
+	assert.NoError(os.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777))
+	assert.NoError(os.MkdirAll(filepath.Join(scssDir), 0777))
+
+	b.WithSourceFile(filepath.Join(fooDir, "_moo.scss"), `
+$moolor: #fff;
+
+moo {
+  color: $moolor;
+}
+`)
+
+	b.WithSourceFile(filepath.Join(scssDir, "main.scss"), `
+@import "moo";
+
+`)
+
+	b.WithTemplatesAdded("index.html", `
+{{ $cssOpts := (dict "includePaths" (slice "node_modules/foo" ) ) }}
+{{ $r := resources.Get "scss/main.scss" |  toCSS $cssOpts  | minify  }}
+T1: {{ $r.Content }}
+`)
+	b.Build(BuildCfg{})
+
+	b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: moo{color:#fff}`)
+
+}
+
 func TestResourceChain(t *testing.T) {
 	t.Parallel()
 
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
@@ -1,7 +1,9 @@
 package hugolib
 
 import (
+	"io/ioutil"
 	"path/filepath"
+	"runtime"
 	"testing"
 
 	"bytes"
@@ -82,6 +84,20 @@ func newTestSitesBuilder(t testing.TB) *sitesBuilder {
 	return &sitesBuilder{T: t, Fs: fs, configFormat: "toml", dumper: litterOptions}
 }
 
+func createTempDir(prefix string) (string, func(), error) {
+	workDir, err := ioutil.TempDir("", prefix)
+	if err != nil {
+		return "", nil, err
+	}
+
+	if runtime.GOOS == "darwin" && !strings.HasPrefix(workDir, "/private") {
+		// To get the entry folder in line with the rest. This its a little bit
+		// mysterious, but so be it.
+		workDir = "/private" + workDir
+	}
+	return workDir, func() { os.RemoveAll(workDir) }, nil
+}
+
 func (s *sitesBuilder) Running() *sitesBuilder {
 	s.running = true
 	return s
diff --git a/resource/tocss/scss/client.go b/resource/tocss/scss/client.go
@@ -22,12 +22,13 @@ import (
 )
 
 type Client struct {
-	rs  *resource.Spec
-	sfs *filesystems.SourceFilesystem
+	rs     *resource.Spec
+	sfs    *filesystems.SourceFilesystem
+	workFs *filesystems.SourceFilesystem
 }
 
 func New(fs *filesystems.SourceFilesystem, rs *resource.Spec) (*Client, error) {
-	return &Client{sfs: fs, rs: rs}, nil
+	return &Client{sfs: fs, workFs: rs.BaseFs.Work, rs: rs}, nil
 }
 
 type Options struct {
@@ -38,6 +39,13 @@ type Options struct {
 	// a Resource with that as a base for RelPermalink etc.
 	TargetPath string
 
+	// Hugo automatically adds the entry directories (where the main.scss lives)
+	// for project and themes to the list of include paths sent to LibSASS.
+	// Any paths set in this setting will be appended. Note that these will be
+	// treated as relative to the working dir, i.e. no include paths outside the
+	// project/themes.
+	IncludePaths []string
+
 	// Default is nested.
 	// One of nested, expanded, compact, compressed.
 	OutputStyle string
diff --git a/resource/tocss/scss/tocss.go b/resource/tocss/scss/tocss.go
@@ -49,10 +49,13 @@ func (t *toCSSTransformation) Transform(ctx *resource.ResourceTransformationCtx)
 
 	options := t.options
 
-	// We may allow the end user to add IncludePaths later, if we find a use
-	// case for that.
 	options.to.IncludePaths = t.c.sfs.RealDirs(path.Dir(ctx.SourcePath))
 
+	// Append any workDir relative include paths
+	for _, ip := range options.from.IncludePaths {
+		options.to.IncludePaths = append(options.to.IncludePaths, t.c.workFs.RealDirs(filepath.Clean(ip))...)
+	}
+
 	if ctx.InMediaType.SubType == media.SASSType.SubType {
 		options.to.SassSyntax = true
 	}