summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-02-21 18:32:09 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2023-02-22 13:26:10 +0100
commitecf3cd514fe0e4c848513053721ee9573108f666 (patch)
tree4b2815053abe072e0218aa82d92025b5cab6a6fc
parenta1a9c08b5ffee8432d9ed23143233d5f00b50608 (diff)
tocss: Simplify the hugo:vars type handling
Instead of maintaing a list of all CSS units and functions this commit: * Uses 3 regexps to detect typed CSS values (e.g. `24px`) + properly handle numeric Go types. * These regexps may have some false positives -- e.g. strings that needs to be quoted. * For that rare case, you can mark the string with e.g. `"32xxx" | css.Quoted` * For the opposite case: `"32" | css.Unquoted` Updates #10632
-rw-r--r--common/types/css/csstypes.go20
-rw-r--r--resources/resource_transformers/tocss/dartsass/client.go2
-rw-r--r--resources/resource_transformers/tocss/dartsass/integration_test.go17
-rw-r--r--resources/resource_transformers/tocss/internal/sass/cssValues.go72
-rw-r--r--resources/resource_transformers/tocss/internal/sass/helpers.go64
-rw-r--r--resources/resource_transformers/tocss/internal/sass/helpers_test.go44
-rw-r--r--resources/resource_transformers/tocss/scss/client.go2
-rw-r--r--tpl/css/css.go41
-rw-r--r--tpl/tplimpl/template_funcs.go1
9 files changed, 206 insertions, 57 deletions
diff --git a/common/types/css/csstypes.go b/common/types/css/csstypes.go
new file mode 100644
index 000000000..a31df00e7
--- /dev/null
+++ b/common/types/css/csstypes.go
@@ -0,0 +1,20 @@
+// Copyright 2023 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 css
+
+// QuotedString is a string that needs to be quoted in CSS.
+type QuotedString string
+
+// UnquotedString is a string that does not need to be quoted in CSS.
+type UnquotedString string
diff --git a/resources/resource_transformers/tocss/dartsass/client.go b/resources/resource_transformers/tocss/dartsass/client.go
index 49cafb52d..f45e6537a 100644
--- a/resources/resource_transformers/tocss/dartsass/client.go
+++ b/resources/resource_transformers/tocss/dartsass/client.go
@@ -135,7 +135,7 @@ type Options struct {
// Vars will be available in 'hugo:vars', e.g:
// @use "hugo:vars";
// $color: vars.$color;
- Vars map[string]string
+ Vars map[string]any
}
func decodeOptions(m map[string]any) (opts Options, err error) {
diff --git a/resources/resource_transformers/tocss/dartsass/integration_test.go b/resources/resource_transformers/tocss/dartsass/integration_test.go
index e7ddd6e6f..e7432f12b 100644
--- a/resources/resource_transformers/tocss/dartsass/integration_test.go
+++ b/resources/resource_transformers/tocss/dartsass/integration_test.go
@@ -387,6 +387,10 @@ color_hsl = "hsl(0, 0%, 100%)"
dimension = "24px"
percentage = "10%"
flex = "5fr"
+name = "Hugo"
+url = "https://gohugo.io"
+integer = 32
+float = 3.14
-- assets/scss/main.scss --
@use "hugo:vars";
@use "sass:meta";
@@ -397,8 +401,15 @@ flex = "5fr"
@debug meta.type-of(vars.$dimension);
@debug meta.type-of(vars.$percentage);
@debug meta.type-of(vars.$flex);
+@debug meta.type-of(vars.$name);
+@debug meta.type-of(vars.$url);
+@debug meta.type-of(vars.$not_a_number);
+@debug meta.type-of(vars.$integer);
+@debug meta.type-of(vars.$float);
+@debug meta.type-of(vars.$a_number);
-- layouts/index.html --
{{ $vars := site.Params.sassvars}}
+{{ $vars = merge $vars (dict "not_a_number" ("32xxx" | css.Quoted) "a_number" ("234" | css.Unquoted) )}}
{{ $cssOpts := (dict "transpiler" "dartsass" "vars" $vars ) }}
{{ $r := resources.Get "scss/main.scss" | toCSS $cssOpts }}
T1: {{ $r.Content }}
@@ -418,5 +429,11 @@ T1: {{ $r.Content }}
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:6:0: number`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:7:0: number`)
b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:8:0: number`)
+ b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:9:0: string`)
+ b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:10:0: string`)
+ b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:11:0: string`)
+ b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:12:0: number`)
+ b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:13:0: number`)
+ b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:14:0: number`)
}
diff --git a/resources/resource_transformers/tocss/internal/sass/cssValues.go b/resources/resource_transformers/tocss/internal/sass/cssValues.go
index b23c4fc02..410e89e04 100644
--- a/resources/resource_transformers/tocss/internal/sass/cssValues.go
+++ b/resources/resource_transformers/tocss/internal/sass/cssValues.go
@@ -15,65 +15,67 @@ package sass
type cssValue struct {
prefix []string
- sufix []string
+ suffix []string
}
var (
cssValues = cssValue{
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/color_value
prefix: []string{
"#",
- "rgb(",
+ "attr(",
+ "calc(",
+ "clamp(",
"hsl(",
"hwb(",
- "lch(",
"lab(",
- "calc(",
- "min(",
+ "lch(",
"max(",
+ "min(",
"minmax(",
- "clamp(",
- "attr(",
+ "rgb(",
},
- sufix: []string{
- "em",
- "ex",
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units#dimensions
+ suffix: []string{
+ "%",
"cap",
"ch",
- "ic",
- "rem",
- "lh",
- "rlh",
- "vw",
- "vh",
- "vi",
- "vb",
- "vmin",
- "vmax",
- "cqw",
+ "cm",
+ "cqb",
"cqh",
"cqi",
- "cqb",
- "cqmin",
"cqmax",
- "cm",
- "mm",
- "Q",
+ "cqmin",
+ "cqw",
+ "deg",
+ "dpcm",
+ "dpi",
+ "dppx",
+ "em",
+ "ex",
+ "fr",
+ "grad",
+ "ic",
"in",
+ "lh",
+ "mm",
+ "ms",
"pc",
"pt",
"px",
- "deg",
- "grad",
+ "Q",
"rad",
- "turn",
+ "rem",
+ "rlh",
"s",
- "ms",
- "fr",
- "dpi",
- "dpcm",
- "dppx",
+ "turn",
+ "vb",
+ "vh",
+ "vi",
+ "vmax",
+ "vmin",
+ "vw",
"x",
- "%",
},
}
)
diff --git a/resources/resource_transformers/tocss/internal/sass/helpers.go b/resources/resource_transformers/tocss/internal/sass/helpers.go
index e6f246b7c..f04250157 100644
--- a/resources/resource_transformers/tocss/internal/sass/helpers.go
+++ b/resources/resource_transformers/tocss/internal/sass/helpers.go
@@ -1,4 +1,4 @@
-// Copyright 2022 The Hugo Authors. All rights reserved.
+// Copyright 2023 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.
@@ -15,15 +15,18 @@ package sass
import (
"fmt"
+ "regexp"
"sort"
"strings"
+
+ "github.com/gohugoio/hugo/common/types/css"
)
const (
HugoVarsNamespace = "hugo:vars"
)
-func CreateVarsStyleSheet(vars map[string]string) string {
+func CreateVarsStyleSheet(vars map[string]any) string {
if vars == nil {
return ""
}
@@ -35,35 +38,56 @@ func CreateVarsStyleSheet(vars map[string]string) string {
if !strings.HasPrefix(k, "$") {
prefix = "$"
}
- // These variables can be a combination of Sass identifiers (e.g. sans-serif), which
- // should not be quoted, and URLs et, which should be quoted.
- // unquote() is knowing what to do with each.
- // Use quoteVar() to check if the variables should be quoted or not.
- if quoteVar(v) {
- varsSlice = append(varsSlice, fmt.Sprintf("%s%s: unquote(%q);", prefix, k, v))
- } else {
- varsSlice = append(varsSlice, fmt.Sprintf("%s%s: %s;", prefix, k, v))
+
+ switch v.(type) {
+ case css.QuotedString:
+ // Marked by the user as a string that needs to be quoted.
+ varsSlice = append(varsSlice, fmt.Sprintf("%s%s: %q;", prefix, k, v))
+ default:
+ if isTypedCSSValue(v) {
+ // E.g. 24px, 1.5rem, 10%, hsl(0, 0%, 100%), calc(24px + 36px), #fff, #ffffff.
+ varsSlice = append(varsSlice, fmt.Sprintf("%s%s: %v;", prefix, k, v))
+ } else {
+ // unquote will preserve quotes around URLs etc. if needed.
+ varsSlice = append(varsSlice, fmt.Sprintf("%s%s: unquote(%q);", prefix, k, v))
+ }
}
}
sort.Strings(varsSlice)
varsStylesheet = strings.Join(varsSlice, "\n")
+ fmt.Println(varsStylesheet)
+
+ fmt.Println(varsStylesheet)
+
return varsStylesheet
}
-func quoteVar(v string) bool {
- v = strings.Trim(v, "\"")
- for _, p := range cssValues.prefix {
- if strings.HasPrefix(v, p) {
- return false
+var (
+ isCSSColor = regexp.MustCompile(`^#[0-9a-fA-F]{3,6}$`)
+ isCSSFunc = regexp.MustCompile(`^([a-zA-Z-]+)\(`)
+ isCSSUnit = regexp.MustCompile(`^([0-9]+)(\.[0-9]+)?([a-zA-Z-%]+)$`)
+)
+
+// isTypedCSSValue returns true if the given string is a CSS value that
+// we should preserve the type of, as in: Not wrap it in quotes.
+func isTypedCSSValue(v any) bool {
+ switch s := v.(type) {
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, css.UnquotedString:
+ return true
+ case string:
+ if isCSSColor.MatchString(s) {
+ return true
}
- }
- for _, s := range cssValues.sufix {
- if strings.HasSuffix(v, s) {
- return false
+ if isCSSFunc.MatchString(s) {
+ return true
}
+ if isCSSUnit.MatchString(s) {
+ return true
+ }
+
}
- return true
+ return false
}
diff --git a/resources/resource_transformers/tocss/internal/sass/helpers_test.go b/resources/resource_transformers/tocss/internal/sass/helpers_test.go
new file mode 100644
index 000000000..56e73736e
--- /dev/null
+++ b/resources/resource_transformers/tocss/internal/sass/helpers_test.go
@@ -0,0 +1,44 @@
+// Copyright 2023 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 sass
+
+import (
+ "testing"
+
+ qt "github.com/frankban/quicktest"
+)
+
+func TestIsUnquotedCSSValue(t *testing.T) {
+ c := qt.New(t)
+
+ for _, test := range []struct {
+ in any
+ out bool
+ }{
+ {"24px", true},
+ {"1.5rem", true},
+ {"10%", true},
+ {"hsl(0, 0%, 100%)", true},
+ {"calc(24px + 36px)", true},
+ {"24xxx", true}, // a false positive.
+ {123, true},
+ {123.12, true},
+ {"#fff", true},
+ {"#ffffff", true},
+ {"#ffffffff", false},
+ } {
+ c.Assert(isTypedCSSValue(test.in), qt.Equals, test.out)
+ }
+
+}
diff --git a/resources/resource_transformers/tocss/scss/client.go b/resources/resource_transformers/tocss/scss/client.go
index 0d027d888..2028163ff 100644
--- a/resources/resource_transformers/tocss/scss/client.go
+++ b/resources/resource_transformers/tocss/scss/client.go
@@ -63,7 +63,7 @@ type Options struct {
// Vars will be available in 'hugo:vars', e.g:
// @import "hugo:vars";
- Vars map[string]string
+ Vars map[string]any
}
func DecodeOptions(m map[string]any) (opts Options, err error) {
diff --git a/tpl/css/css.go b/tpl/css/css.go
new file mode 100644
index 000000000..e1783334e
--- /dev/null
+++ b/tpl/css/css.go
@@ -0,0 +1,41 @@
+package css
+
+import (
+ "github.com/gohugoio/hugo/common/types/css"
+ "github.com/gohugoio/hugo/deps"
+ "github.com/gohugoio/hugo/tpl/internal"
+ "github.com/spf13/cast"
+)
+
+const name = "css"
+
+// Namespace provides template functions for the "css" namespace.
+type Namespace struct {
+}
+
+// Quoted returns a string that needs to be quoted in CSS.
+func (ns *Namespace) Quoted(v any) css.QuotedString {
+ s := cast.ToString(v)
+ return css.QuotedString(s)
+}
+
+// Unquoted returns a string that does not need to be quoted in CSS.
+func (ns *Namespace) Unquoted(v any) css.UnquotedString {
+ s := cast.ToString(v)
+ return css.UnquotedString(s)
+}
+
+func init() {
+ f := func(d *deps.Deps) *internal.TemplateFuncsNamespace {
+ ctx := &Namespace{}
+
+ ns := &internal.TemplateFuncsNamespace{
+ Name: name,
+ Context: func(args ...any) (any, error) { return ctx, nil },
+ }
+
+ return ns
+ }
+
+ internal.AddTemplateFuncsNamespace(f)
+}
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go
index e664bd6c5..b8102c75d 100644
--- a/tpl/tplimpl/template_funcs.go
+++ b/tpl/tplimpl/template_funcs.go
@@ -36,6 +36,7 @@ import (
_ "github.com/gohugoio/hugo/tpl/collections"
_ "github.com/gohugoio/hugo/tpl/compare"
_ "github.com/gohugoio/hugo/tpl/crypto"
+ _ "github.com/gohugoio/hugo/tpl/css"
_ "github.com/gohugoio/hugo/tpl/data"
_ "github.com/gohugoio/hugo/tpl/debug"
_ "github.com/gohugoio/hugo/tpl/diagrams"