diff --git a/hugolib/filesystems/basefs.go b/hugolib/filesystems/basefs.go index 731b5a195..7398957a3 100644 --- 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 index 96d113bf7..ace96b633 100644 --- 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 index 811dbf56f..236672b65 100644 --- 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 index d504b7d75..61ae7e611 100644 --- 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 index 9a72bcbb8..cee1f8c4c 100644 --- 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 index 610ea3845..126727e06 100644 --- 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 index ec4685d87..715d5fd9f 100644 --- 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 }