mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
ba35e69856
Fixes #9048
1177 lines
37 KiB
Go
1177 lines
37 KiB
Go
// Copyright 2019 The Hugo Authors. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package hugolib
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"os"
|
|
|
|
"github.com/gohugoio/hugo/config"
|
|
|
|
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
|
|
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gohugoio/hugo/common/hexec"
|
|
|
|
jww "github.com/spf13/jwalterweatherman"
|
|
|
|
"github.com/gohugoio/hugo/common/herrors"
|
|
|
|
"github.com/gohugoio/hugo/htesting"
|
|
|
|
qt "github.com/frankban/quicktest"
|
|
|
|
"github.com/gohugoio/hugo/hugofs"
|
|
|
|
"github.com/gohugoio/hugo/common/loggers"
|
|
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
|
|
)
|
|
|
|
func TestSCSSWithIncludePaths(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
supports func() bool
|
|
}{
|
|
{"libsass", func() bool { return scss.Supports() }},
|
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
|
} {
|
|
|
|
c.Run(test.name, func(c *qt.C) {
|
|
if !test.supports() {
|
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
|
}
|
|
|
|
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, fmt.Sprintf("hugo-scss-include-%s", test.name))
|
|
c.Assert(err, qt.IsNil)
|
|
defer clean()
|
|
|
|
v := config.New()
|
|
v.Set("workingDir", workDir)
|
|
b := newTestSitesBuilder(c).WithLogger(loggers.NewErrorLogger())
|
|
// Need to use OS fs for this.
|
|
b.Fs = hugofs.NewDefault(v)
|
|
b.WithWorkingDir(workDir)
|
|
b.WithViper(v)
|
|
|
|
fooDir := filepath.Join(workDir, "node_modules", "foo")
|
|
scssDir := filepath.Join(workDir, "assets", "scss")
|
|
c.Assert(os.MkdirAll(fooDir, 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "content", "sect"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "data"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "i18n"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "layouts", "shortcodes"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(scssDir), 0777), qt.IsNil)
|
|
|
|
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", fmt.Sprintf(`
|
|
{{ $cssOpts := (dict "includePaths" (slice "node_modules/foo") "transpiler" %q ) }}
|
|
{{ $r := resources.Get "scss/main.scss" | toCSS $cssOpts | minify }}
|
|
T1: {{ $r.Content }}
|
|
`, test.name))
|
|
b.Build(BuildCfg{})
|
|
|
|
b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: moo{color:#fff}`)
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func TestSCSSWithRegularCSSImport(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
supports func() bool
|
|
}{
|
|
{"libsass", func() bool { return scss.Supports() }},
|
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
|
} {
|
|
|
|
c.Run(test.name, func(c *qt.C) {
|
|
if !test.supports() {
|
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
|
}
|
|
|
|
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, fmt.Sprintf("hugo-scss-include-regular-%s", test.name))
|
|
c.Assert(err, qt.IsNil)
|
|
defer clean()
|
|
|
|
v := config.New()
|
|
v.Set("workingDir", workDir)
|
|
b := newTestSitesBuilder(c).WithLogger(loggers.NewErrorLogger())
|
|
// Need to use OS fs for this.
|
|
b.Fs = hugofs.NewDefault(v)
|
|
b.WithWorkingDir(workDir)
|
|
b.WithViper(v)
|
|
|
|
scssDir := filepath.Join(workDir, "assets", "scss")
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "content", "sect"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "data"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "i18n"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "layouts", "shortcodes"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(scssDir), 0777), qt.IsNil)
|
|
b.WithSourceFile(filepath.Join(scssDir, "regular.css"), ``)
|
|
b.WithSourceFile(filepath.Join(scssDir, "another.css"), ``)
|
|
b.WithSourceFile(filepath.Join(scssDir, "_moo.scss"), `
|
|
$moolor: #fff;
|
|
|
|
moo {
|
|
color: $moolor;
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join(scssDir, "main.scss"), `
|
|
@import "moo";
|
|
@import "regular.css";
|
|
@import "moo";
|
|
@import "another.css";
|
|
|
|
/* foo */
|
|
`)
|
|
|
|
b.WithTemplatesAdded("index.html", fmt.Sprintf(`
|
|
{{ $r := resources.Get "scss/main.scss" | toCSS (dict "transpiler" %q) }}
|
|
T1: {{ $r.Content | safeHTML }}
|
|
`, test.name))
|
|
b.Build(BuildCfg{})
|
|
|
|
if test.name == "libsass" {
|
|
// LibSass does not support regular CSS imports. There
|
|
// is an open bug about it that probably will never be resolved.
|
|
// Hugo works around this by preserving them in place:
|
|
b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `
|
|
T1: moo {
|
|
color: #fff; }
|
|
|
|
@import "regular.css";
|
|
moo {
|
|
color: #fff; }
|
|
|
|
@import "another.css";
|
|
/* foo */
|
|
|
|
`)
|
|
} else {
|
|
// Dart Sass does not follow regular CSS import, but they
|
|
// get pulled to the top.
|
|
b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: @import "regular.css";
|
|
@import "another.css";
|
|
moo {
|
|
color: #fff;
|
|
}
|
|
|
|
moo {
|
|
color: #fff;
|
|
}
|
|
|
|
/* foo */`)
|
|
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestSCSSWithThemeOverrides(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
supports func() bool
|
|
}{
|
|
{"libsass", func() bool { return scss.Supports() }},
|
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
|
} {
|
|
|
|
c.Run(test.name, func(c *qt.C) {
|
|
if !test.supports() {
|
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
|
}
|
|
|
|
workDir, clean1, err := htesting.CreateTempDir(hugofs.Os, fmt.Sprintf("hugo-scss-include-theme-overrides-%s", test.name))
|
|
c.Assert(err, qt.IsNil)
|
|
defer clean1()
|
|
|
|
theme := "mytheme"
|
|
themesDir := filepath.Join(workDir, "themes")
|
|
themeDirs := filepath.Join(themesDir, theme)
|
|
v := config.New()
|
|
v.Set("workingDir", workDir)
|
|
v.Set("theme", theme)
|
|
b := newTestSitesBuilder(c).WithLogger(loggers.NewErrorLogger())
|
|
// Need to use OS fs for this.
|
|
b.Fs = hugofs.NewDefault(v)
|
|
b.WithWorkingDir(workDir)
|
|
b.WithViper(v)
|
|
|
|
fooDir := filepath.Join(workDir, "node_modules", "foo")
|
|
scssDir := filepath.Join(workDir, "assets", "scss")
|
|
scssThemeDir := filepath.Join(themeDirs, "assets", "scss")
|
|
c.Assert(os.MkdirAll(fooDir, 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "content", "sect"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "data"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "i18n"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "layouts", "shortcodes"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(workDir, "layouts", "_default"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(scssDir, "components"), 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(filepath.Join(scssThemeDir, "components"), 0777), qt.IsNil)
|
|
|
|
b.WithSourceFile(filepath.Join(scssThemeDir, "components", "_imports.scss"), `
|
|
@import "moo";
|
|
@import "_boo";
|
|
@import "_zoo";
|
|
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join(scssThemeDir, "components", "_moo.scss"), `
|
|
$moolor: #fff;
|
|
|
|
moo {
|
|
color: $moolor;
|
|
}
|
|
`)
|
|
|
|
// Only in theme.
|
|
b.WithSourceFile(filepath.Join(scssThemeDir, "components", "_zoo.scss"), `
|
|
$zoolor: pink;
|
|
|
|
zoo {
|
|
color: $zoolor;
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join(scssThemeDir, "components", "_boo.scss"), `
|
|
$boolor: orange;
|
|
|
|
boo {
|
|
color: $boolor;
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join(scssThemeDir, "main.scss"), `
|
|
@import "components/imports";
|
|
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join(scssDir, "components", "_moo.scss"), `
|
|
$moolor: #ccc;
|
|
|
|
moo {
|
|
color: $moolor;
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join(scssDir, "components", "_boo.scss"), `
|
|
$boolor: green;
|
|
|
|
boo {
|
|
color: $boolor;
|
|
}
|
|
`)
|
|
|
|
b.WithTemplatesAdded("index.html", fmt.Sprintf(`
|
|
{{ $cssOpts := (dict "includePaths" (slice "node_modules/foo" ) "transpiler" %q ) }}
|
|
{{ $r := resources.Get "scss/main.scss" | toCSS $cssOpts | minify }}
|
|
T1: {{ $r.Content }}
|
|
`, test.name))
|
|
b.Build(BuildCfg{})
|
|
|
|
b.AssertFileContent(
|
|
filepath.Join(workDir, "public/index.html"),
|
|
`T1: moo{color:#ccc}boo{color:green}zoo{color:pink}`,
|
|
)
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
// https://github.com/gohugoio/hugo/issues/6274
|
|
func TestSCSSWithIncludePathsSass(t *testing.T) {
|
|
c := qt.New(t)
|
|
|
|
for _, test := range []struct {
|
|
name string
|
|
supports func() bool
|
|
}{
|
|
{"libsass", func() bool { return scss.Supports() }},
|
|
{"dartsass", func() bool { return dartsass.Supports() }},
|
|
} {
|
|
|
|
c.Run(test.name, func(c *qt.C) {
|
|
if !test.supports() {
|
|
c.Skip(fmt.Sprintf("Skip %s", test.name))
|
|
}
|
|
})
|
|
}
|
|
if !scss.Supports() {
|
|
t.Skip("Skip SCSS")
|
|
}
|
|
workDir, clean1, err := htesting.CreateTempDir(hugofs.Os, "hugo-scss-includepaths")
|
|
c.Assert(err, qt.IsNil)
|
|
defer clean1()
|
|
|
|
v := config.New()
|
|
v.Set("workingDir", workDir)
|
|
v.Set("theme", "mytheme")
|
|
b := newTestSitesBuilder(t).WithLogger(loggers.NewErrorLogger())
|
|
// Need to use OS fs for this.
|
|
b.Fs = hugofs.NewDefault(v)
|
|
b.WithWorkingDir(workDir)
|
|
b.WithViper(v)
|
|
|
|
hulmaDir := filepath.Join(workDir, "node_modules", "hulma")
|
|
scssDir := filepath.Join(workDir, "themes/mytheme/assets", "scss")
|
|
c.Assert(os.MkdirAll(hulmaDir, 0777), qt.IsNil)
|
|
c.Assert(os.MkdirAll(scssDir, 0777), qt.IsNil)
|
|
|
|
b.WithSourceFile(filepath.Join(scssDir, "main.scss"), `
|
|
@import "hulma/hulma";
|
|
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join(hulmaDir, "hulma.sass"), `
|
|
$hulma: #ccc;
|
|
|
|
foo
|
|
color: $hulma;
|
|
|
|
`)
|
|
|
|
b.WithTemplatesAdded("index.html", `
|
|
{{ $scssOptions := (dict "targetPath" "css/styles.css" "enableSourceMap" false "includePaths" (slice "node_modules")) }}
|
|
{{ $r := resources.Get "scss/main.scss" | toCSS $scssOptions | minify }}
|
|
T1: {{ $r.Content }}
|
|
`)
|
|
b.Build(BuildCfg{})
|
|
|
|
b.AssertFileContent(filepath.Join(workDir, "public/index.html"), `T1: foo{color:#ccc}`)
|
|
}
|
|
|
|
func TestResourceChainBasic(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
b := newTestSitesBuilder(t)
|
|
b.WithTemplatesAdded("index.html", `
|
|
{{ $hello := "<h1> Hello World! </h1>" | resources.FromString "hello.html" | fingerprint "sha512" | minify | fingerprint }}
|
|
{{ $cssFingerprinted1 := "body { background-color: lightblue; }" | resources.FromString "styles.css" | minify | fingerprint }}
|
|
{{ $cssFingerprinted2 := "body { background-color: orange; }" | resources.FromString "styles2.css" | minify | fingerprint }}
|
|
|
|
|
|
HELLO: {{ $hello.Name }}|{{ $hello.RelPermalink }}|{{ $hello.Content | safeHTML }}
|
|
|
|
{{ $img := resources.Get "images/sunset.jpg" }}
|
|
{{ $fit := $img.Fit "200x200" }}
|
|
{{ $fit2 := $fit.Fit "100x200" }}
|
|
{{ $img = $img | fingerprint }}
|
|
SUNSET: {{ $img.Name }}|{{ $img.RelPermalink }}|{{ $img.Width }}|{{ len $img.Content }}
|
|
FIT: {{ $fit.Name }}|{{ $fit.RelPermalink }}|{{ $fit.Width }}
|
|
CSS integrity Data first: {{ $cssFingerprinted1.Data.Integrity }} {{ $cssFingerprinted1.RelPermalink }}
|
|
CSS integrity Data last: {{ $cssFingerprinted2.RelPermalink }} {{ $cssFingerprinted2.Data.Integrity }}
|
|
|
|
`)
|
|
|
|
fs := b.Fs.Source
|
|
|
|
imageDir := filepath.Join("assets", "images")
|
|
b.Assert(os.MkdirAll(imageDir, 0777), qt.IsNil)
|
|
src, err := os.Open("testdata/sunset.jpg")
|
|
b.Assert(err, qt.IsNil)
|
|
out, err := fs.Create(filepath.Join(imageDir, "sunset.jpg"))
|
|
b.Assert(err, qt.IsNil)
|
|
_, err = io.Copy(out, src)
|
|
b.Assert(err, qt.IsNil)
|
|
out.Close()
|
|
|
|
b.Running()
|
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
b.Build(BuildCfg{})
|
|
|
|
b.AssertFileContent("public/index.html",
|
|
`
|
|
SUNSET: images/sunset.jpg|/images/sunset.a9bf1d944e19c0f382e0d8f51de690f7d0bc8fa97390c4242a86c3e5c0737e71.jpg|900|90587
|
|
FIT: images/sunset.jpg|/images/sunset_hu59e56ffff1bc1d8d122b1403d34e039f_90587_200x200_fit_q75_box.jpg|200
|
|
CSS integrity Data first: sha256-od9YaHw8nMOL8mUy97Sy8sKwMV3N4hI3aVmZXATxH+8= /styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css
|
|
CSS integrity Data last: /styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css sha256-HPxSmGg2QF03+ZmKY/1t2GCOjEEOXj2x2qow94vCc7o=
|
|
`)
|
|
|
|
b.AssertFileContent("public/styles.min.a1df58687c3c9cc38bf26532f7b4b2f2c2b0315dcde212376959995c04f11fef.css", "body{background-color:#add8e6}")
|
|
b.AssertFileContent("public//styles2.min.1cfc52986836405d37f9998a63fd6dd8608e8c410e5e3db1daaa30f78bc273ba.css", "body{background-color:orange}")
|
|
|
|
b.EditFiles("page1.md", `
|
|
---
|
|
title: "Page 1 edit"
|
|
summary: "Edited summary"
|
|
---
|
|
|
|
Edited content.
|
|
|
|
`)
|
|
|
|
b.Assert(b.Fs.Destination.Remove("public"), qt.IsNil)
|
|
b.H.ResourceSpec.ClearCaches()
|
|
|
|
}
|
|
}
|
|
|
|
func TestResourceChainPostProcess(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
rnd := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
|
|
b := newTestSitesBuilder(t)
|
|
b.WithConfigFile("toml", `[minify]
|
|
minifyOutput = true
|
|
[minify.tdewolff]
|
|
[minify.tdewolff.html]
|
|
keepQuotes = false
|
|
keepWhitespace = false`)
|
|
b.WithContent("page1.md", "---\ntitle: Page1\n---")
|
|
b.WithContent("page2.md", "---\ntitle: Page2\n---")
|
|
|
|
b.WithTemplates(
|
|
"_default/single.html", `{{ $hello := "<h1> Hello World! </h1>" | resources.FromString "hello.html" | minify | fingerprint "md5" | resources.PostProcess }}
|
|
HELLO: {{ $hello.RelPermalink }}
|
|
`,
|
|
"index.html", `Start.
|
|
{{ $hello := "<h1> Hello World! </h1>" | resources.FromString "hello.html" | minify | fingerprint "md5" | resources.PostProcess }}
|
|
|
|
HELLO: {{ $hello.RelPermalink }}|Integrity: {{ $hello.Data.Integrity }}|MediaType: {{ $hello.MediaType.Type }}
|
|
HELLO2: Name: {{ $hello.Name }}|Content: {{ $hello.Content }}|Title: {{ $hello.Title }}|ResourceType: {{ $hello.ResourceType }}
|
|
|
|
// Issue #8884
|
|
<a href="hugo.rocks">foo</a>
|
|
<a href="{{ $hello.RelPermalink }}" integrity="{{ $hello.Data.Integrity}}">Hello</a>
|
|
`+strings.Repeat("a b", rnd.Intn(10)+1)+`
|
|
|
|
|
|
End.`)
|
|
|
|
b.Running()
|
|
b.Build(BuildCfg{})
|
|
b.AssertFileContent("public/index.html",
|
|
`Start.
|
|
HELLO: /hello.min.a2d1cb24f24b322a7dad520414c523e9.html|Integrity: md5-otHLJPJLMip9rVIEFMUj6Q==|MediaType: text/html
|
|
HELLO2: Name: hello.html|Content: <h1>Hello World!</h1>|Title: hello.html|ResourceType: text
|
|
<a href=hugo.rocks>foo</a>
|
|
<a href="/hello.min.a2d1cb24f24b322a7dad520414c523e9.html" integrity="md5-otHLJPJLMip9rVIEFMUj6Q==">Hello</a>
|
|
End.`)
|
|
|
|
b.AssertFileContent("public/page1/index.html", `HELLO: /hello.min.a2d1cb24f24b322a7dad520414c523e9.html`)
|
|
b.AssertFileContent("public/page2/index.html", `HELLO: /hello.min.a2d1cb24f24b322a7dad520414c523e9.html`)
|
|
}
|
|
|
|
func BenchmarkResourceChainPostProcess(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
b.StopTimer()
|
|
s := newTestSitesBuilder(b)
|
|
for i := 0; i < 300; i++ {
|
|
s.WithContent(fmt.Sprintf("page%d.md", i+1), "---\ntitle: Page\n---")
|
|
}
|
|
s.WithTemplates("_default/single.html", `Start.
|
|
Some text.
|
|
|
|
|
|
{{ $hello1 := "<h1> Hello World 2! </h1>" | resources.FromString "hello.html" | minify | fingerprint "md5" | resources.PostProcess }}
|
|
{{ $hello2 := "<h1> Hello World 2! </h1>" | resources.FromString (printf "%s.html" .Path) | minify | fingerprint "md5" | resources.PostProcess }}
|
|
|
|
Some more text.
|
|
|
|
HELLO: {{ $hello1.RelPermalink }}|Integrity: {{ $hello1.Data.Integrity }}|MediaType: {{ $hello1.MediaType.Type }}
|
|
|
|
Some more text.
|
|
|
|
HELLO2: Name: {{ $hello2.Name }}|Content: {{ $hello2.Content }}|Title: {{ $hello2.Title }}|ResourceType: {{ $hello2.ResourceType }}
|
|
|
|
Some more text.
|
|
|
|
HELLO2_2: Name: {{ $hello2.Name }}|Content: {{ $hello2.Content }}|Title: {{ $hello2.Title }}|ResourceType: {{ $hello2.ResourceType }}
|
|
|
|
End.
|
|
`)
|
|
|
|
b.StartTimer()
|
|
s.Build(BuildCfg{})
|
|
|
|
}
|
|
}
|
|
|
|
func TestResourceChains(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
c := qt.New(t)
|
|
|
|
tests := []struct {
|
|
name string
|
|
shouldRun func() bool
|
|
prepare func(b *sitesBuilder)
|
|
verify func(b *sitesBuilder)
|
|
}{
|
|
{"tocss", func() bool { return scss.Supports() }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $scss := resources.Get "scss/styles2.scss" | toCSS }}
|
|
{{ $sass := resources.Get "sass/styles3.sass" | toCSS }}
|
|
{{ $scssCustomTarget := resources.Get "scss/styles2.scss" | toCSS (dict "targetPath" "styles/main.css") }}
|
|
{{ $scssCustomTargetString := resources.Get "scss/styles2.scss" | toCSS "styles/main.css" }}
|
|
{{ $scssMin := resources.Get "scss/styles2.scss" | toCSS | minify }}
|
|
{{ $scssFromTempl := ".{{ .Kind }} { color: blue; }" | resources.FromString "kindofblue.templ" | resources.ExecuteAsTemplate "kindofblue.scss" . | toCSS (dict "targetPath" "styles/templ.css") | minify }}
|
|
{{ $bundle1 := slice $scssFromTempl $scssMin | resources.Concat "styles/bundle1.css" }}
|
|
T1: Len Content: {{ len $scss.Content }}|RelPermalink: {{ $scss.RelPermalink }}|Permalink: {{ $scss.Permalink }}|MediaType: {{ $scss.MediaType.Type }}
|
|
T2: Content: {{ $scssMin.Content }}|RelPermalink: {{ $scssMin.RelPermalink }}
|
|
T3: Content: {{ len $scssCustomTarget.Content }}|RelPermalink: {{ $scssCustomTarget.RelPermalink }}|MediaType: {{ $scssCustomTarget.MediaType.Type }}
|
|
T4: Content: {{ len $scssCustomTargetString.Content }}|RelPermalink: {{ $scssCustomTargetString.RelPermalink }}|MediaType: {{ $scssCustomTargetString.MediaType.Type }}
|
|
T5: Content: {{ $sass.Content }}|T5 RelPermalink: {{ $sass.RelPermalink }}|
|
|
T6: {{ $bundle1.Permalink }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", `T1: Len Content: 24|RelPermalink: /scss/styles2.css|Permalink: http://example.com/scss/styles2.css|MediaType: text/css`)
|
|
b.AssertFileContent("public/index.html", `T2: Content: body{color:#333}|RelPermalink: /scss/styles2.min.css`)
|
|
b.AssertFileContent("public/index.html", `T3: Content: 24|RelPermalink: /styles/main.css|MediaType: text/css`)
|
|
b.AssertFileContent("public/index.html", `T4: Content: 24|RelPermalink: /styles/main.css|MediaType: text/css`)
|
|
b.AssertFileContent("public/index.html", `T5: Content: .content-navigation {`)
|
|
b.AssertFileContent("public/index.html", `T5 RelPermalink: /sass/styles3.css|`)
|
|
b.AssertFileContent("public/index.html", `T6: http://example.com/styles/bundle1.css`)
|
|
|
|
c.Assert(b.CheckExists("public/styles/templ.min.css"), qt.Equals, false)
|
|
b.AssertFileContent("public/styles/bundle1.css", `.home{color:blue}body{color:#333}`)
|
|
}},
|
|
|
|
{"minify", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithConfigFile("toml", `[minify]
|
|
[minify.tdewolff]
|
|
[minify.tdewolff.html]
|
|
keepWhitespace = false
|
|
`)
|
|
b.WithTemplates("home.html", `
|
|
Min CSS: {{ ( resources.Get "css/styles1.css" | minify ).Content }}
|
|
Min JS: {{ ( resources.Get "js/script1.js" | resources.Minify ).Content | safeJS }}
|
|
Min JSON: {{ ( resources.Get "mydata/json1.json" | resources.Minify ).Content | safeHTML }}
|
|
Min XML: {{ ( resources.Get "mydata/xml1.xml" | resources.Minify ).Content | safeHTML }}
|
|
Min SVG: {{ ( resources.Get "mydata/svg1.svg" | resources.Minify ).Content | safeHTML }}
|
|
Min SVG again: {{ ( resources.Get "mydata/svg1.svg" | resources.Minify ).Content | safeHTML }}
|
|
Min HTML: {{ ( resources.Get "mydata/html1.html" | resources.Minify ).Content | safeHTML }}
|
|
|
|
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", `Min CSS: h1{font-style:bold}`)
|
|
b.AssertFileContent("public/index.html", `Min JS: var x;x=5,document.getElementById("demo").innerHTML=x*10`)
|
|
b.AssertFileContent("public/index.html", `Min JSON: {"employees":[{"firstName":"John","lastName":"Doe"},{"firstName":"Anna","lastName":"Smith"},{"firstName":"Peter","lastName":"Jones"}]}`)
|
|
b.AssertFileContent("public/index.html", `Min XML: <hello><world>Hugo Rocks!</<world></hello>`)
|
|
b.AssertFileContent("public/index.html", `Min SVG: <svg height="100" width="100"><path d="M1e2 1e2H3e2 2e2z"/></svg>`)
|
|
b.AssertFileContent("public/index.html", `Min SVG again: <svg height="100" width="100"><path d="M1e2 1e2H3e2 2e2z"/></svg>`)
|
|
b.AssertFileContent("public/index.html", `Min HTML: <html><a href=#>Cool</a></html>`)
|
|
}},
|
|
|
|
{"concat", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $a := "A" | resources.FromString "a.txt"}}
|
|
{{ $b := "B" | resources.FromString "b.txt"}}
|
|
{{ $c := "C" | resources.FromString "c.txt"}}
|
|
{{ $textResources := .Resources.Match "*.txt" }}
|
|
{{ $combined := slice $a $b $c | resources.Concat "bundle/concat.txt" }}
|
|
T1: Content: {{ $combined.Content }}|RelPermalink: {{ $combined.RelPermalink }}|Permalink: {{ $combined.Permalink }}|MediaType: {{ $combined.MediaType.Type }}
|
|
{{ with $textResources }}
|
|
{{ $combinedText := . | resources.Concat "bundle/concattxt.txt" }}
|
|
T2: Content: {{ $combinedText.Content }}|{{ $combinedText.RelPermalink }}
|
|
{{ end }}
|
|
{{/* https://github.com/gohugoio/hugo/issues/5269 */}}
|
|
{{ $css := "body { color: blue; }" | resources.FromString "styles.css" }}
|
|
{{ $minified := resources.Get "css/styles1.css" | minify }}
|
|
{{ slice $css $minified | resources.Concat "bundle/mixed.css" }}
|
|
{{/* https://github.com/gohugoio/hugo/issues/5403 */}}
|
|
{{ $d := "function D {} // A comment" | resources.FromString "d.js"}}
|
|
{{ $e := "(function E {})" | resources.FromString "e.js"}}
|
|
{{ $f := "(function F {})()" | resources.FromString "f.js"}}
|
|
{{ $jsResources := .Resources.Match "*.js" }}
|
|
{{ $combinedJs := slice $d $e $f | resources.Concat "bundle/concatjs.js" }}
|
|
T3: Content: {{ $combinedJs.Content }}|{{ $combinedJs.RelPermalink }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", `T1: Content: ABC|RelPermalink: /bundle/concat.txt|Permalink: http://example.com/bundle/concat.txt|MediaType: text/plain`)
|
|
b.AssertFileContent("public/bundle/concat.txt", "ABC")
|
|
|
|
b.AssertFileContent("public/index.html", `T2: Content: t1t|t2t|`)
|
|
b.AssertFileContent("public/bundle/concattxt.txt", "t1t|t2t|")
|
|
|
|
b.AssertFileContent("public/index.html", `T3: Content: function D {} // A comment
|
|
;
|
|
(function E {})
|
|
;
|
|
(function F {})()|`)
|
|
b.AssertFileContent("public/bundle/concatjs.js", `function D {} // A comment
|
|
;
|
|
(function E {})
|
|
;
|
|
(function F {})()`)
|
|
}},
|
|
|
|
{"concat and fingerprint", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $a := "A" | resources.FromString "a.txt"}}
|
|
{{ $b := "B" | resources.FromString "b.txt"}}
|
|
{{ $c := "C" | resources.FromString "c.txt"}}
|
|
{{ $combined := slice $a $b $c | resources.Concat "bundle/concat.txt" }}
|
|
{{ $fingerprinted := $combined | fingerprint }}
|
|
Fingerprinted: {{ $fingerprinted.RelPermalink }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", "Fingerprinted: /bundle/concat.b5d4045c3f466fa91fe2cc6abe79232a1a57cdf104f7a26e716e0a1e2789df78.txt")
|
|
b.AssertFileContent("public/bundle/concat.b5d4045c3f466fa91fe2cc6abe79232a1a57cdf104f7a26e716e0a1e2789df78.txt", "ABC")
|
|
}},
|
|
|
|
{"fromstring", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $r := "Hugo Rocks!" | resources.FromString "rocks/hugo.txt" }}
|
|
{{ $r.Content }}|{{ $r.RelPermalink }}|{{ $r.Permalink }}|{{ $r.MediaType.Type }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", `Hugo Rocks!|/rocks/hugo.txt|http://example.com/rocks/hugo.txt|text/plain`)
|
|
b.AssertFileContent("public/rocks/hugo.txt", "Hugo Rocks!")
|
|
}},
|
|
{"execute-as-template", func() bool {
|
|
return true
|
|
}, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $var := "Hugo Page" }}
|
|
{{ if .IsHome }}
|
|
{{ $var = "Hugo Home" }}
|
|
{{ end }}
|
|
T1: {{ $var }}
|
|
{{ $result := "{{ .Kind | upper }}" | resources.FromString "mytpl.txt" | resources.ExecuteAsTemplate "result.txt" . }}
|
|
T2: {{ $result.Content }}|{{ $result.RelPermalink}}|{{$result.MediaType.Type }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", `T2: HOME|/result.txt|text/plain`, `T1: Hugo Home`)
|
|
}},
|
|
{"fingerprint", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $r := "ab" | resources.FromString "rocks/hugo.txt" }}
|
|
{{ $result := $r | fingerprint }}
|
|
{{ $result512 := $r | fingerprint "sha512" }}
|
|
{{ $resultMD5 := $r | fingerprint "md5" }}
|
|
T1: {{ $result.Content }}|{{ $result.RelPermalink}}|{{$result.MediaType.Type }}|{{ $result.Data.Integrity }}|
|
|
T2: {{ $result512.Content }}|{{ $result512.RelPermalink}}|{{$result512.MediaType.Type }}|{{ $result512.Data.Integrity }}|
|
|
T3: {{ $resultMD5.Content }}|{{ $resultMD5.RelPermalink}}|{{$resultMD5.MediaType.Type }}|{{ $resultMD5.Data.Integrity }}|
|
|
{{ $r2 := "bc" | resources.FromString "rocks/hugo2.txt" | fingerprint }}
|
|
{{/* https://github.com/gohugoio/hugo/issues/5296 */}}
|
|
T4: {{ $r2.Data.Integrity }}|
|
|
|
|
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", `T1: ab|/rocks/hugo.fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603.txt|text/plain|sha256-+44g/C5MPySMYMOb1lLzwTRymLuXe4tNWQO4UFViBgM=|`)
|
|
b.AssertFileContent("public/index.html", `T2: ab|/rocks/hugo.2d408a0717ec188158278a796c689044361dc6fdde28d6f04973b80896e1823975cdbf12eb63f9e0591328ee235d80e9b5bf1aa6a44f4617ff3caf6400eb172d.txt|text/plain|sha512-LUCKBxfsGIFYJ4p5bGiQRDYdxv3eKNbwSXO4CJbhgjl1zb8S62P54FkTKO4jXYDptb8apqRPRhf/PK9kAOsXLQ==|`)
|
|
b.AssertFileContent("public/index.html", `T3: ab|/rocks/hugo.187ef4436122d1cc2f40dc2b92f0eba0.txt|text/plain|md5-GH70Q2Ei0cwvQNwrkvDroA==|`)
|
|
b.AssertFileContent("public/index.html", `T4: sha256-Hgu9bGhroFC46wP/7txk/cnYCUf86CGrvl1tyNJSxaw=|`)
|
|
}},
|
|
// https://github.com/gohugoio/hugo/issues/5226
|
|
{"baseurl-path", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithSimpleConfigFileAndBaseURL("https://example.com/hugo/")
|
|
b.WithTemplates("home.html", `
|
|
{{ $r1 := "ab" | resources.FromString "rocks/hugo.txt" }}
|
|
T1: {{ $r1.Permalink }}|{{ $r1.RelPermalink }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", `T1: https://example.com/hugo/rocks/hugo.txt|/hugo/rocks/hugo.txt`)
|
|
}},
|
|
|
|
// https://github.com/gohugoio/hugo/issues/4944
|
|
{"Prevent resource publish on .Content only", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $cssInline := "body { color: green; }" | resources.FromString "inline.css" | minify }}
|
|
{{ $cssPublish1 := "body { color: blue; }" | resources.FromString "external1.css" | minify }}
|
|
{{ $cssPublish2 := "body { color: orange; }" | resources.FromString "external2.css" | minify }}
|
|
|
|
Inline: {{ $cssInline.Content }}
|
|
Publish 1: {{ $cssPublish1.Content }} {{ $cssPublish1.RelPermalink }}
|
|
Publish 2: {{ $cssPublish2.Permalink }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html",
|
|
`Inline: body{color:green}`,
|
|
"Publish 1: body{color:blue} /external1.min.css",
|
|
"Publish 2: http://example.com/external2.min.css",
|
|
)
|
|
b.Assert(b.CheckExists("public/external2.css"), qt.Equals, false)
|
|
b.Assert(b.CheckExists("public/external1.css"), qt.Equals, false)
|
|
b.Assert(b.CheckExists("public/external2.min.css"), qt.Equals, true)
|
|
b.Assert(b.CheckExists("public/external1.min.css"), qt.Equals, true)
|
|
b.Assert(b.CheckExists("public/inline.min.css"), qt.Equals, false)
|
|
}},
|
|
|
|
{"unmarshal", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `
|
|
{{ $toml := "slogan = \"Hugo Rocks!\"" | resources.FromString "slogan.toml" | transform.Unmarshal }}
|
|
{{ $csv1 := "\"Hugo Rocks\",\"Hugo is Fast!\"" | resources.FromString "slogans.csv" | transform.Unmarshal }}
|
|
{{ $csv2 := "a;b;c" | transform.Unmarshal (dict "delimiter" ";") }}
|
|
|
|
Slogan: {{ $toml.slogan }}
|
|
CSV1: {{ $csv1 }} {{ len (index $csv1 0) }}
|
|
CSV2: {{ $csv2 }}
|
|
`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html",
|
|
`Slogan: Hugo Rocks!`,
|
|
`[[Hugo Rocks Hugo is Fast!]] 2`,
|
|
`CSV2: [[a b c]]`,
|
|
)
|
|
}},
|
|
{"resources.Get", func() bool { return true }, func(b *sitesBuilder) {
|
|
b.WithTemplates("home.html", `NOT FOUND: {{ if (resources.Get "this-does-not-exist") }}FAILED{{ else }}OK{{ end }}`)
|
|
}, func(b *sitesBuilder) {
|
|
b.AssertFileContent("public/index.html", "NOT FOUND: OK")
|
|
}},
|
|
|
|
{"template", func() bool { return true }, func(b *sitesBuilder) {}, func(b *sitesBuilder) {
|
|
}},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
test := test
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if !test.shouldRun() {
|
|
t.Skip()
|
|
}
|
|
t.Parallel()
|
|
|
|
b := newTestSitesBuilder(t).WithLogger(loggers.NewErrorLogger())
|
|
b.WithContent("_index.md", `
|
|
---
|
|
title: Home
|
|
---
|
|
|
|
Home.
|
|
|
|
`,
|
|
"page1.md", `
|
|
---
|
|
title: Hello1
|
|
---
|
|
|
|
Hello1
|
|
`,
|
|
"page2.md", `
|
|
---
|
|
title: Hello2
|
|
---
|
|
|
|
Hello2
|
|
`,
|
|
"t1.txt", "t1t|",
|
|
"t2.txt", "t2t|",
|
|
)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "css", "styles1.css"), `
|
|
h1 {
|
|
font-style: bold;
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "js", "script1.js"), `
|
|
var x;
|
|
x = 5;
|
|
document.getElementById("demo").innerHTML = x * 10;
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "mydata", "json1.json"), `
|
|
{
|
|
"employees":[
|
|
{"firstName":"John", "lastName":"Doe"},
|
|
{"firstName":"Anna", "lastName":"Smith"},
|
|
{"firstName":"Peter", "lastName":"Jones"}
|
|
]
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "mydata", "svg1.svg"), `
|
|
<svg height="100" width="100">
|
|
<path d="M 100 100 L 300 100 L 200 100 z"/>
|
|
</svg>
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "mydata", "xml1.xml"), `
|
|
<hello>
|
|
<world>Hugo Rocks!</<world>
|
|
</hello>
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "mydata", "html1.html"), `
|
|
<html>
|
|
<a href="#">
|
|
Cool
|
|
</a >
|
|
</html>
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "scss", "styles2.scss"), `
|
|
$color: #333;
|
|
|
|
body {
|
|
color: $color;
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile(filepath.Join("assets", "sass", "styles3.sass"), `
|
|
$color: #333;
|
|
|
|
.content-navigation
|
|
border-color: $color
|
|
|
|
`)
|
|
|
|
test.prepare(b)
|
|
b.Build(BuildCfg{})
|
|
test.verify(b)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMultiSiteResource(t *testing.T) {
|
|
t.Parallel()
|
|
c := qt.New(t)
|
|
|
|
b := newMultiSiteTestDefaultBuilder(t)
|
|
|
|
b.CreateSites().Build(BuildCfg{})
|
|
|
|
// This build is multilingual, but not multihost. There should be only one pipes.txt
|
|
b.AssertFileContent("public/fr/index.html", "French Home Page", "String Resource: /blog/text/pipes.txt")
|
|
c.Assert(b.CheckExists("public/fr/text/pipes.txt"), qt.Equals, false)
|
|
c.Assert(b.CheckExists("public/en/text/pipes.txt"), qt.Equals, false)
|
|
b.AssertFileContent("public/en/index.html", "Default Home Page", "String Resource: /blog/text/pipes.txt")
|
|
b.AssertFileContent("public/text/pipes.txt", "Hugo Pipes")
|
|
}
|
|
|
|
func TestResourcesMatch(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
b := newTestSitesBuilder(t)
|
|
|
|
b.WithContent("page.md", "")
|
|
|
|
b.WithSourceFile(
|
|
"assets/jsons/data1.json", "json1 content",
|
|
"assets/jsons/data2.json", "json2 content",
|
|
"assets/jsons/data3.xml", "xml content",
|
|
)
|
|
|
|
b.WithTemplates("index.html", `
|
|
{{ $jsons := (resources.Match "jsons/*.json") }}
|
|
{{ $json := (resources.GetMatch "jsons/*.json") }}
|
|
{{ printf "JSONS: %d" (len $jsons) }}
|
|
JSON: {{ $json.RelPermalink }}: {{ $json.Content }}
|
|
{{ range $jsons }}
|
|
{{- .RelPermalink }}: {{ .Content }}
|
|
{{ end }}
|
|
`)
|
|
|
|
b.Build(BuildCfg{})
|
|
|
|
b.AssertFileContent("public/index.html",
|
|
"JSON: /jsons/data1.json: json1 content",
|
|
"JSONS: 2", "/jsons/data1.json: json1 content")
|
|
}
|
|
|
|
func TestExecuteAsTemplateWithLanguage(t *testing.T) {
|
|
b := newMultiSiteTestDefaultBuilder(t)
|
|
indexContent := `
|
|
Lang: {{ site.Language.Lang }}
|
|
{{ $templ := "{{T \"hello\"}}" | resources.FromString "f1.html" }}
|
|
{{ $helloResource := $templ | resources.ExecuteAsTemplate (print "f%s.html" .Lang) . }}
|
|
Hello1: {{T "hello"}}
|
|
Hello2: {{ $helloResource.Content }}
|
|
LangURL: {{ relLangURL "foo" }}
|
|
`
|
|
b.WithTemplatesAdded("index.html", indexContent)
|
|
b.WithTemplatesAdded("index.fr.html", indexContent)
|
|
|
|
b.Build(BuildCfg{})
|
|
|
|
b.AssertFileContent("public/en/index.html", `
|
|
Hello1: Hello
|
|
Hello2: Hello
|
|
`)
|
|
|
|
b.AssertFileContent("public/fr/index.html", `
|
|
Hello1: Bonjour
|
|
Hello2: Bonjour
|
|
`)
|
|
}
|
|
|
|
func TestResourceChainPostCSS(t *testing.T) {
|
|
if !htesting.IsCI() {
|
|
t.Skip("skip (relative) long running modules test when running locally")
|
|
}
|
|
|
|
wd, _ := os.Getwd()
|
|
defer func() {
|
|
os.Chdir(wd)
|
|
}()
|
|
|
|
c := qt.New(t)
|
|
|
|
packageJSON := `{
|
|
"scripts": {},
|
|
|
|
"devDependencies": {
|
|
"postcss-cli": "7.1.0",
|
|
"tailwindcss": "1.2.0"
|
|
}
|
|
}
|
|
`
|
|
|
|
postcssConfig := `
|
|
console.error("Hugo Environment:", process.env.HUGO_ENVIRONMENT );
|
|
// https://github.com/gohugoio/hugo/issues/7656
|
|
console.error("package.json:", process.env.HUGO_FILE_PACKAGE_JSON );
|
|
console.error("PostCSS Config File:", process.env.HUGO_FILE_POSTCSS_CONFIG_JS );
|
|
|
|
|
|
module.exports = {
|
|
plugins: [
|
|
require('tailwindcss')
|
|
]
|
|
}
|
|
`
|
|
|
|
tailwindCss := `
|
|
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
@import "components/all.css";
|
|
|
|
h1 {
|
|
@apply text-2xl font-bold;
|
|
}
|
|
|
|
`
|
|
|
|
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-test-postcss")
|
|
c.Assert(err, qt.IsNil)
|
|
defer clean()
|
|
|
|
var logBuf bytes.Buffer
|
|
|
|
newTestBuilder := func(v config.Provider) *sitesBuilder {
|
|
v.Set("workingDir", workDir)
|
|
v.Set("disableKinds", []string{"taxonomy", "term", "page"})
|
|
logger := loggers.NewBasicLoggerForWriter(jww.LevelInfo, &logBuf)
|
|
b := newTestSitesBuilder(t).WithLogger(logger)
|
|
// Need to use OS fs for this.
|
|
b.Fs = hugofs.NewDefault(v)
|
|
b.WithWorkingDir(workDir)
|
|
b.WithViper(v)
|
|
|
|
b.WithContent("p1.md", "")
|
|
b.WithTemplates("index.html", `
|
|
{{ $options := dict "inlineImports" true }}
|
|
{{ $styles := resources.Get "css/styles.css" | resources.PostCSS $options }}
|
|
Styles RelPermalink: {{ $styles.RelPermalink }}
|
|
{{ $cssContent := $styles.Content }}
|
|
Styles Content: Len: {{ len $styles.Content }}|
|
|
|
|
`)
|
|
|
|
return b
|
|
}
|
|
|
|
b := newTestBuilder(config.New())
|
|
|
|
cssDir := filepath.Join(workDir, "assets", "css", "components")
|
|
b.Assert(os.MkdirAll(cssDir, 0777), qt.IsNil)
|
|
|
|
b.WithSourceFile("assets/css/styles.css", tailwindCss)
|
|
b.WithSourceFile("assets/css/components/all.css", `
|
|
@import "a.css";
|
|
@import "b.css";
|
|
`, "assets/css/components/a.css", `
|
|
class-in-a {
|
|
color: blue;
|
|
}
|
|
`, "assets/css/components/b.css", `
|
|
@import "a.css";
|
|
|
|
class-in-b {
|
|
color: blue;
|
|
}
|
|
`)
|
|
|
|
b.WithSourceFile("package.json", packageJSON)
|
|
b.WithSourceFile("postcss.config.js", postcssConfig)
|
|
|
|
b.Assert(os.Chdir(workDir), qt.IsNil)
|
|
cmd, err := hexec.SafeCommand("npm", "install")
|
|
_, err = cmd.CombinedOutput()
|
|
b.Assert(err, qt.IsNil)
|
|
b.Build(BuildCfg{})
|
|
|
|
// Make sure Node sees this.
|
|
b.Assert(logBuf.String(), qt.Contains, "Hugo Environment: production")
|
|
b.Assert(logBuf.String(), qt.Contains, filepath.FromSlash(fmt.Sprintf("PostCSS Config File: %s/postcss.config.js", workDir)))
|
|
b.Assert(logBuf.String(), qt.Contains, filepath.FromSlash(fmt.Sprintf("package.json: %s/package.json", workDir)))
|
|
|
|
b.AssertFileContent("public/index.html", `
|
|
Styles RelPermalink: /css/styles.css
|
|
Styles Content: Len: 770878|
|
|
`)
|
|
|
|
assertCss := func(b *sitesBuilder) {
|
|
content := b.FileContent("public/css/styles.css")
|
|
|
|
b.Assert(strings.Contains(content, "class-in-a"), qt.Equals, true)
|
|
b.Assert(strings.Contains(content, "class-in-b"), qt.Equals, true)
|
|
}
|
|
|
|
assertCss(b)
|
|
|
|
build := func(s string, shouldFail bool) error {
|
|
b.Assert(os.RemoveAll(filepath.Join(workDir, "public")), qt.IsNil)
|
|
|
|
v := config.New()
|
|
v.Set("build", map[string]interface{}{
|
|
"useResourceCacheWhen": s,
|
|
})
|
|
|
|
b = newTestBuilder(v)
|
|
|
|
b.Assert(os.RemoveAll(filepath.Join(workDir, "public")), qt.IsNil)
|
|
|
|
err := b.BuildE(BuildCfg{})
|
|
if shouldFail {
|
|
b.Assert(err, qt.Not(qt.IsNil))
|
|
} else {
|
|
b.Assert(err, qt.IsNil)
|
|
assertCss(b)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
build("always", false)
|
|
build("fallback", false)
|
|
|
|
// Introduce a syntax error in an import
|
|
b.WithSourceFile("assets/css/components/b.css", `@import "a.css";
|
|
|
|
class-in-b {
|
|
@apply asdf;
|
|
}
|
|
`)
|
|
|
|
err = build("never", true)
|
|
|
|
err = herrors.UnwrapErrorWithFileContext(err)
|
|
_, ok := err.(*herrors.ErrorWithFileContext)
|
|
b.Assert(ok, qt.Equals, true)
|
|
|
|
// TODO(bep) for some reason, we have starting to get
|
|
// execute of template failed: template: index.html:5:25
|
|
// on CI (GitHub action).
|
|
//b.Assert(fe.Position().LineNumber, qt.Equals, 5)
|
|
//b.Assert(fe.Error(), qt.Contains, filepath.Join(workDir, "assets/css/components/b.css:4:1"))
|
|
|
|
// Remove PostCSS
|
|
b.Assert(os.RemoveAll(filepath.Join(workDir, "node_modules")), qt.IsNil)
|
|
|
|
build("always", false)
|
|
build("fallback", false)
|
|
build("never", true)
|
|
|
|
// Remove cache
|
|
b.Assert(os.RemoveAll(filepath.Join(workDir, "resources")), qt.IsNil)
|
|
|
|
build("always", true)
|
|
build("fallback", true)
|
|
build("never", true)
|
|
}
|
|
|
|
func TestResourceMinifyDisabled(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
b := newTestSitesBuilder(t).WithConfigFile("toml", `
|
|
baseURL = "https://example.org"
|
|
|
|
[minify]
|
|
disableXML=true
|
|
|
|
|
|
`)
|
|
|
|
b.WithContent("page.md", "")
|
|
|
|
b.WithSourceFile(
|
|
"assets/xml/data.xml", "<root> <foo> asdfasdf </foo> </root>",
|
|
)
|
|
|
|
b.WithTemplates("index.html", `
|
|
{{ $xml := resources.Get "xml/data.xml" | minify | fingerprint }}
|
|
XML: {{ $xml.Content | safeHTML }}|{{ $xml.RelPermalink }}
|
|
`)
|
|
|
|
b.Build(BuildCfg{})
|
|
|
|
b.AssertFileContent("public/index.html", `
|
|
XML: <root> <foo> asdfasdf </foo> </root>|/xml/data.min.3be4fddd19aaebb18c48dd6645215b822df74701957d6d36e59f203f9c30fd9f.xml
|
|
`)
|
|
}
|
|
|
|
// Issue 8954
|
|
func TestMinifyWithError(t *testing.T) {
|
|
b := newTestSitesBuilder(t).WithSimpleConfigFile()
|
|
b.WithSourceFile(
|
|
"assets/js/test.js", `
|
|
new Date(2002, 04, 11)
|
|
`,
|
|
)
|
|
b.WithTemplates("index.html", `
|
|
{{ $js := resources.Get "js/test.js" | minify | fingerprint }}
|
|
<script>
|
|
{{ $js.Content }}
|
|
</script>
|
|
`)
|
|
b.WithContent("page.md", "")
|
|
|
|
err := b.BuildE(BuildCfg{})
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "04") {
|
|
t.Fatalf("expected a message about a legacy octal number, but got: %v", err)
|
|
}
|
|
}
|