Compare commits

..

1 Commits

Author SHA1 Message Date
Bjørn Erik Pedersen 10ddc2ef92 Improve error messages for PostCSS etc.
Fixes #9730
2023-07-17 19:42:13 +02:00
18 changed files with 54 additions and 326 deletions
+7 -6
View File
@@ -86,12 +86,13 @@ func flagsToCfgWithAdditionalConfigBase(cd *simplecobra.Commandeer, cfg config.P
// Flags that we for some reason don't want to expose in the site config.
internalKeySet := map[string]bool{
"quiet": true,
"verbose": true,
"watch": true,
"liveReloadPort": true,
"renderToMemory": true,
"clock": true,
"quiet": true,
"verbose": true,
"watch": true,
"disableLiveReload": true,
"liveReloadPort": true,
"renderToMemory": true,
"clock": true,
}
cmd := cd.CobraCommand
+8 -9
View File
@@ -470,6 +470,14 @@ func (c *serverCommand) Name() string {
}
func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, args []string) error {
err := func() error {
defer c.r.timeTrack(time.Now(), "Built")
err := c.build()
return err
}()
if err != nil {
return err
}
// Watch runs its own server as part of the routine
if c.serverWatch {
@@ -493,15 +501,6 @@ func (c *serverCommand) Run(ctx context.Context, cd *simplecobra.Commandeer, arg
}
err := func() error {
defer c.r.timeTrack(time.Now(), "Built")
err := c.build()
return err
}()
if err != nil {
return err
}
return c.serve()
}
-83
View File
@@ -1,83 +0,0 @@
// Copyright 2022 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 htime_test
import (
"testing"
"github.com/gohugoio/hugo/hugolib"
)
// Issue #11267
func TestApplyWithContext(t *testing.T) {
t.Parallel()
files := `
-- config.toml --
defaultContentLanguage = 'it'
-- layouts/index.html --
{{ $dates := slice
"2022-01-03"
"2022-02-01"
"2022-03-02"
"2022-04-07"
"2022-05-06"
"2022-06-04"
"2022-07-03"
"2022-08-01"
"2022-09-06"
"2022-10-05"
"2022-11-03"
"2022-12-02"
}}
{{ range $dates }}
{{ . | time.Format "month: _January_ weekday: _Monday_" }}
{{ . | time.Format "month: _Jan_ weekday: _Mon_" }}
{{ end }}
`
b := hugolib.NewIntegrationTestBuilder(
hugolib.IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
b.AssertFileContent("public/index.html", `
month: _gennaio_ weekday: _lunedì_
month: _gen_ weekday: _lun_
month: _febbraio_ weekday: _martedì_
month: _feb_ weekday: _mar_
month: _marzo_ weekday: _mercoledì_
month: _mar_ weekday: _mer_
month: _aprile_ weekday: _giovedì_
month: _apr_ weekday: _gio_
month: _maggio_ weekday: _venerdì_
month: _mag_ weekday: _ven_
month: _giugno_ weekday: _sabato_
month: _giu_ weekday: _sab_
month: _luglio_ weekday: _domenica_
month: _lug_ weekday: _dom_
month: _agosto_ weekday: _lunedì_
month: _ago_ weekday: _lun_
month: _settembre_ weekday: _martedì_
month: _set_ weekday: _mar_
month: _ottobre_ weekday: _mercoledì_
month: _ott_ weekday: _mer_
month: _novembre_ weekday: _giovedì_
month: _nov_ weekday: _gio_
month: _dicembre_ weekday: _venerdì_
month: _dic_ weekday: _ven_
`)
}
+4 -7
View File
@@ -124,15 +124,12 @@ func (f TimeFormatter) Format(t time.Time, layout string) string {
monthIdx := t.Month() - 1 // Month() starts at 1.
dayIdx := t.Weekday()
if strings.Contains(layout, "January") {
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
} else if strings.Contains(layout, "Jan") {
s = strings.ReplaceAll(s, longMonthNames[monthIdx], f.ltr.MonthWide(t.Month()))
if !strings.Contains(s, f.ltr.MonthWide(t.Month())) {
s = strings.ReplaceAll(s, shortMonthNames[monthIdx], f.ltr.MonthAbbreviated(t.Month()))
}
if strings.Contains(layout, "Monday") {
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
} else if strings.Contains(layout, "Mon") {
s = strings.ReplaceAll(s, longDayNames[dayIdx], f.ltr.WeekdayWide(t.Weekday()))
if !strings.Contains(s, f.ltr.WeekdayWide(t.Weekday())) {
s = strings.ReplaceAll(s, shortDayNames[dayIdx], f.ltr.WeekdayAbbreviated(t.Weekday()))
}
+7 -9
View File
@@ -57,11 +57,12 @@ type InternalConfig struct {
// Server mode?
Running bool
Quiet bool
Verbose bool
Clock string
Watch bool
LiveReloadPort int
Quiet bool
Verbose bool
Clock string
Watch bool
DisableLiveReload bool
LiveReloadPort int
}
// All non-params config keys for language.
@@ -241,7 +242,7 @@ func (c *Config) CompileConfig(logger loggers.Logger) error {
kind = strings.ToLower(kind)
if kind == "taxonomyterm" {
// Legacy config.
kind = "taxonomy"
kind = "term"
}
disabledKinds[kind] = true
}
@@ -453,9 +454,6 @@ type RootConfig struct {
// Disable the injection of the Hugo generator tag on the home page.
DisableHugoGeneratorInject bool
// Disable live reloading in server mode.
DisableLiveReload bool
// Enable replacement in Pages' Content of Emoji shortcodes with their equivalent Unicode characters.
// <docsmeta>{"identifiers": ["Content", "Unicode"] }</docsmeta>
EnableEmoji bool
+8 -8
View File
@@ -333,11 +333,11 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
}
source := c.Source
target := c.Target
sourceRe, err := regexp.Compile(source)
if err != nil {
return fmt.Errorf("failed to compile cache buster source %q: %w", c.Source, err)
}
target := c.Target
var compileErr error
debugl := logger.Logger().WithLevel(logg.LevelDebug).WithField(loggers.FieldNameCmd, "cachebuster")
@@ -353,23 +353,23 @@ func (c *CacheBuster) CompileConfig(logger loggers.Logger) error {
return nil
}
groups := m[1:]
currentTarget := target
// Replace $1, $2 etc. in target.
for i, g := range groups {
currentTarget = strings.ReplaceAll(target, fmt.Sprintf("$%d", i+1), g)
target = strings.ReplaceAll(target, fmt.Sprintf("$%d", i+1), g)
}
targetRe, err := regexp.Compile(currentTarget)
targetRe, err := regexp.Compile(target)
if err != nil {
compileErr = fmt.Errorf("failed to compile cache buster target %q: %w", currentTarget, err)
compileErr = fmt.Errorf("failed to compile cache buster target %q: %w", target, err)
return nil
}
return func(ss string) bool {
match = targetRe.MatchString(ss)
return func(s string) bool {
match = targetRe.MatchString(s)
matchString := "no match"
if match {
matchString = "match!"
}
logger.Debugf("Matching %q with target %q: %s", ss, currentTarget, matchString)
logger.Debugf("Matching %q with target %q: %s", s, target, matchString)
return match
}
-33
View File
@@ -164,36 +164,3 @@ func TestBuildConfigCacheBusters(t *testing.T) {
c.Assert(m("json"), qt.IsTrue)
}
func TestBuildConfigCacheBusterstTailwindSetup(t *testing.T) {
c := qt.New(t)
cfg := New()
cfg.Set("build", map[string]interface{}{
"cacheBusters": []map[string]string{
{
"source": "assets/watching/hugo_stats\\.json",
"target": "css",
},
{
"source": "(postcss|tailwind)\\.config\\.js",
"target": "css",
},
{
"source": "assets/.*\\.(js|ts|jsx|tsx)",
"target": "js",
},
{
"source": "assets/.*\\.(.*)$",
"target": "$1",
},
},
})
conf := DecodeBuildConfig(cfg)
l := loggers.NewDefault()
c.Assert(conf.CompileConfig(l), qt.IsNil)
m, err := conf.MatchCacheBuster(l, "assets/watching/hugo_stats.json")
c.Assert(err, qt.IsNil)
c.Assert(m("css"), qt.IsTrue)
}
-2
View File
@@ -31,8 +31,6 @@ const (
FilenamePackageHugoJSON = "package.hugo.json"
// The NPM package file.
FilenamePackageJSON = "package.json"
FilenameHugoStatsJSON = "hugo_stats.json"
)
var (
+2 -38
View File
@@ -1060,7 +1060,7 @@ func TestConfigLegacyValues(t *testing.T) {
files := `
-- hugo.toml --
# taxonomyTerm was renamed to taxonomy in Hugo 0.60.0.
# taxonomyTerm was renamed to term in Hugo 0.60.0.
disableKinds = ["taxonomyTerm"]
-- layouts/index.html --
@@ -1081,7 +1081,7 @@ Home
`)
conf := b.H.Configs.Base
b.Assert(conf.IsKindEnabled("taxonomy"), qt.Equals, false)
b.Assert(conf.IsKindEnabled("term"), qt.Equals, false)
}
// Issue #11000
@@ -1534,39 +1534,3 @@ disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "page", "sect
})
}
// Issue #11257
func TestDisableKindsTaxonomyTerm(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
baseURL = "https://example.com"
disableKinds = ['taxonomyTerm']
[taxonomies]
category = 'categories'
-- content/p1.md --
---
title: "P1"
categories: ["c1"]
---
-- layouts/index.html --
Home.
-- layouts/_default/list.html --
List.
`
b := NewIntegrationTestBuilder(
IntegrationTestConfig{
T: t,
TxtarString: files,
},
).Build()
b.AssertDestinationExists("index.html", true)
b.AssertDestinationExists("categories/c1/index.html", true)
b.AssertDestinationExists("categories/index.html", false)
}
+3 -2
View File
@@ -23,7 +23,6 @@ import (
"time"
"github.com/bep/logg"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/gohugoio/hugo/langs"
"github.com/gohugoio/hugo/publisher"
"github.com/gohugoio/hugo/tpl"
@@ -492,12 +491,14 @@ func (h *HugoSites) writeBuildStats() error {
HTMLElements: *htmlElements,
}
const hugoStatsName = "hugo_stats.json"
js, err := json.MarshalIndent(stats, "", " ")
if err != nil {
return err
}
filename := filepath.Join(h.Configs.LoadingInfo.BaseConfig.WorkingDir, files.FilenameHugoStatsJSON)
filename := filepath.Join(h.Configs.LoadingInfo.BaseConfig.WorkingDir, hugoStatsName)
if existingContent, err := afero.ReadFile(hugofs.Os, filename); err == nil {
// Check if the content has changed.
+1 -1
View File
@@ -1058,7 +1058,7 @@ func (s *Site) renderAndWritePage(statCounter *uint64, name string, targetPath s
pd.AbsURLPath = s.absURLPath(targetPath)
}
if s.watching() && s.conf.Internal.Running && !s.conf.DisableLiveReload {
if s.watching() && s.conf.Internal.Running && !s.conf.Internal.DisableLiveReload {
pd.LiveReloadBaseURL = s.Conf.BaseURLLiveReload().URL()
}
+2 -3
View File
@@ -1,8 +1,7 @@
# Release env.
# These will be replaced by script before release.
HUGORELEASER_TAG=v0.115.4
HUGORELEASER_COMMITISH=dc9524521270f81d1c038ebbb200f0cfa3427cc5
HUGORELEASER_TAG=v0.115.3
HUGORELEASER_COMMITISH=5c2e014a5150553a9fa4f9c1eb7dc4db89c0f1ab
+1 -15
View File
@@ -664,21 +664,7 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou
// Verify that Source exists
_, err := c.fs.Stat(sourceDir)
if err != nil {
if strings.HasSuffix(sourceDir, files.FilenameHugoStatsJSON) {
// A common pattern for Tailwind 3 is to mount that file to get it on the server watch list.
// A common pattern is also to add hugo_stats.json to .gitignore.
// Create an empty file.
f, err := c.fs.Create(sourceDir)
if err != nil {
return nil, fmt.Errorf("%s: %q", errMsg, err)
}
f.Close()
} else {
continue
}
continue
}
// Verify that target points to one of the predefined component dirs
+10 -46
View File
@@ -32,7 +32,7 @@ const eof = -1
var (
htmlJsonFixer = strings.NewReplacer(", ", "\n")
jsonAttrRe = regexp.MustCompile(`'?(.*?)'?:\s.*`)
jsonAttrRe = regexp.MustCompile(`'?(.*?)'?:.*`)
classAttrRe = regexp.MustCompile(`(?i)^class$|transition`)
skipInnerElementRe = regexp.MustCompile(`(?i)^(pre|textarea|script|style)`)
@@ -404,31 +404,21 @@ func (w *htmlElementsCollectorWriter) parseHTMLElement(elStr string) (el htmlEle
if conf.DisableClasses {
continue
}
if classAttrRe.MatchString(a.Key) {
el.Classes = append(el.Classes, strings.Fields(a.Val)...)
} else {
key := strings.ToLower(a.Key)
val := strings.TrimSpace(a.Val)
if strings.Contains(key, ":class") {
if strings.HasPrefix(val, "{") {
// This looks like a Vue or AlpineJS class binding.
val = htmlJsonFixer.Replace(strings.Trim(val, "{}"))
lines := strings.Split(val, "\n")
for i, l := range lines {
lines[i] = strings.TrimSpace(l)
}
val = strings.Join(lines, "\n")
val = jsonAttrRe.ReplaceAllString(val, "$1")
el.Classes = append(el.Classes, strings.Fields(val)...)
if strings.Contains(key, "class") && strings.HasPrefix(val, "{") {
// This looks like a Vue or AlpineJS class binding.
val = htmlJsonFixer.Replace(strings.Trim(val, "{}"))
lines := strings.Split(val, "\n")
for i, l := range lines {
lines[i] = strings.TrimSpace(l)
}
// Also add single quoted strings.
// This may introduce some false positives, but it covers some missing cases in the above.
// E.g. AlpinesJS' :class="isTrue 'class1' : 'class2'"
el.Classes = append(el.Classes, extractSingleQuotedStrings(val)...)
val = strings.Join(lines, "\n")
val = jsonAttrRe.ReplaceAllString(val, "$1")
el.Classes = append(el.Classes, strings.Fields(val)...)
}
}
}
@@ -529,29 +519,3 @@ LOOP:
func isSpace(b byte) bool {
return b == ' ' || b == '\t' || b == '\n'
}
func extractSingleQuotedStrings(s string) []string {
var (
inQuote bool
lo int
hi int
)
var words []string
for i, r := range s {
switch {
case r == '\'':
if !inQuote {
inQuote = true
lo = i + 1
} else {
inQuote = false
hi = i
words = append(words, strings.Fields(s[lo:hi])...)
}
}
}
return words
}
+1 -5
View File
@@ -99,8 +99,6 @@ func TestClassCollector(t *testing.T) {
pl-2: b == 3,
'text-gray-600': (a > 1)
}" class="block w-36 cursor-pointer pr-3 no-underline capitalize"></a>`, f("a", "block capitalize cursor-pointer no-underline pl-2 pl-3 pr-3 text-a text-b text-gray-600 w-36", "")},
{"AlpineJS bind 6", `<button :class="isActive(32) ? 'border-gray-500 bg-white pt border-t-2' : 'border-transparent hover:bg-gray-100'"></button>`, f("button", "bg-white border-gray-500 border-t-2 border-transparent hover:bg-gray-100 pt", "")},
{"AlpineJS bind 7", `<button :class="{ 'border-gray-500 bg-white pt border-t-2': isActive(32), 'border-transparent hover:bg-gray-100': !isActive(32) }"></button>`, f("button", "bg-white border-gray-500 border-t-2 border-transparent hover:bg-gray-100 pt", "")},
{"AlpineJS transition 1", `<div x-transition:enter-start="opacity-0 transform mobile:-translate-x-8 sm:-translate-y-8">`, f("div", "mobile:-translate-x-8 opacity-0 sm:-translate-y-8 transform", "")},
{"Vue bind", `<div v-bind:class="{ active: isActive }"></div>`, f("div", "active", "")},
// Issue #7746
@@ -138,9 +136,7 @@ func TestClassCollector(t *testing.T) {
{minify: true},
} {
name := fmt.Sprintf("%s--minify-%t", test.name, variant.minify)
c.Run(name, func(c *qt.C) {
c.Run(fmt.Sprintf("%s--minify-%t", test.name, variant.minify), func(c *qt.C) {
w := newHTMLElementsCollectorWriter(newHTMLElementsCollector(
config.BuildStats{Enable: true},
))
@@ -1,18 +0,0 @@
hugo server &
waitServer
stopServer
! stderr .
exists hugo_stats.json
-- hugo.toml --
title = "Hugo Server Test"
baseURL = "https://example.org/"
disableKinds = ["taxonomy", "term", "sitemap"]
[module]
[[module.mounts]]
source = "hugo_stats.json"
target = "assets/watching/hugo_stats.json"
-- layouts/index.html --
<body>Home</body>
@@ -1,20 +0,0 @@
hugo server --renderToDisk --disableLiveReload &
waitServer
! grep 'livereload' public/index.html
stopServer
! stderr .
-- hugo.toml --
baseURL = "http://example.org/"
disableKinds = ["RSS", "sitemap", "robotsTXT", "404", "taxonomy", "term"]
-- layouts/index.html --
<html>
<head>
</head>
<body>
Home.
</body>
</html>
@@ -1,21 +0,0 @@
hugo server --renderToDisk &
waitServer
! grep 'livereload' public/index.html
stopServer
! stderr .
-- hugo.toml --
baseURL = "http://example.org/"
disableKinds = ["RSS", "sitemap", "robotsTXT", "404", "taxonomy", "term"]
disableLiveReload = true
-- layouts/index.html --
<html>
<head>
</head>
<body>
Home.
</body>
</html>