Repository: gohugoio/hugo (language: go)

Issue:
Dart Sass v1.80.1 deprecation warning
Testing Dart Sass v1.80.1 

> WARN  Dart Sass: hugo:vars:0:17: Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
Use string.unquote instead.

Why we're getting this is unclear at the moment.



Discussion / hints:
https://sass-lang.com/documentation/breaking-changes/import/
This test fails with Dart Sass v1.80.1.

```go
// Issue #12961
func TestDartSassVars(t *testing.T) {
	t.Parallel()

	files := `
-- hugo.toml --
disableKinds = ['page','section','rss','sitemap','taxonomy','term']
-- layouts/index.html --
{{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" "vars" (dict "color" "red") }}
{{ with resources.Get "main.scss" | css.Sass $opts }}
  {{ .Content }}
{{ end }}
-- assets/main.scss --
@use "hugo:vars" as v;
body {color: v.$color;}
`

	b := hugolib.Test(t, files, hugolib.TestOptWarn())

	b.AssertFileContent("public/index.html", "body{color:red}")
	b.AssertLogContains("! WARN  Dart Sass: hugo:vars") // failing assertion
}
```

The warning message generated by the above is:

> WARN  Dart Sass: hugo:vars:0:8: Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.\nUse string.unquote instead.

<https://sass-lang.com/blog/import-is-deprecated/#built-in-functions>
https://github.com/gohugoio/hugo/blob/e971b7d866d8834022f0038b094dce4352338f74/resources/resource_transformers/tocss/internal/sass/helpers.go#L52-L58

This solves the problem when using the Dart Sass transpiler:

```go
				varsSlice = append(varsSlice, fmt.Sprintf("%s%s: string.unquote(%q);", prefix, k, v))
			}
		}
	}
	sort.Strings(varsSlice)
	varsStylesheet = `@use "sass:string";` + "\n" + strings.Join(varsSlice, "\n")
	return varsStylesheet
}
```

But the containing function (`CreateVarsStyleSheet`) is called when using either transpiler, so the changes above need to be specific to dart sass, but I haven't found a way to get the transpiler value ("libsass" or "dartsass") within that function.
>But the containing function (CreateVarsStyleSheet)

I guess adding some kind of identifier as the first arg to CreateVarsStyleSheet would solve it.

Fix patch:
diff --git a/resources/resource_transformers/tocss/dartsass/transform.go b/resources/resource_transformers/tocss/dartsass/transform.go
index f0bd6634af5..d8b6ae5b42c 100644
--- a/resources/resource_transformers/tocss/dartsass/transform.go
+++ b/resources/resource_transformers/tocss/dartsass/transform.go
@@ -29,7 +29,7 @@ import (
 	"github.com/gohugoio/hugo/resources"
 
 	"github.com/gohugoio/hugo/resources/internal"
-	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/internal/sass"
+	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/sass"
 
 	"github.com/spf13/afero"
 
@@ -85,7 +85,7 @@ func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error {
 			c:                 t.c,
 			dependencyManager: ctx.DependencyManager,
 
-			varsStylesheet: godartsass.Import{Content: sass.CreateVarsStyleSheet(opts.Vars)},
+			varsStylesheet: godartsass.Import{Content: sass.CreateVarsStyleSheet(sass.TranspilerDart, opts.Vars)},
 		},
 		OutputStyle:             godartsass.ParseOutputStyle(opts.OutputStyle),
 		EnableSourceMap:         opts.EnableSourceMap,
diff --git a/resources/resource_transformers/tocss/internal/sass/helpers.go b/resources/resource_transformers/tocss/sass/helpers.go
similarity index 75%
rename from resources/resource_transformers/tocss/internal/sass/helpers.go
rename to resources/resource_transformers/tocss/sass/helpers.go
index c1cef141ec0..d4091a39c4b 100644
--- a/resources/resource_transformers/tocss/internal/sass/helpers.go
+++ b/resources/resource_transformers/tocss/sass/helpers.go
@@ -24,9 +24,14 @@ import (
 
 const (
 	HugoVarsNamespace = "hugo:vars"
+	// Transpiler implementation can be controlled from the client by
+	// setting the 'transpiler' option.
+	// Default is currently 'libsass', but that may change.
+	TranspilerDart    = "dartsass"
+	TranspilerLibSass = "libsass"
 )
 
-func CreateVarsStyleSheet(vars map[string]any) string {
+func CreateVarsStyleSheet(transpiler string, vars map[string]any) string {
 	if vars == nil {
 		return ""
 	}
@@ -49,12 +54,22 @@ func CreateVarsStyleSheet(vars map[string]any) string {
 				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))
+				if transpiler == TranspilerDart {
+					varsSlice = append(varsSlice, fmt.Sprintf("%s%s: string.unquote(%q);", prefix, k, v))
+				} else {
+					varsSlice = append(varsSlice, fmt.Sprintf("%s%s: unquote(%q);", prefix, k, v))
+				}
 			}
 		}
 	}
 	sort.Strings(varsSlice)
-	varsStylesheet = strings.Join(varsSlice, "\n")
+
+	if transpiler == TranspilerDart {
+		varsStylesheet = `@use "sass:string";` + "\n" + strings.Join(varsSlice, "\n")
+	} else {
+		varsStylesheet = strings.Join(varsSlice, "\n")
+	}
+
 	return varsStylesheet
 }
 
diff --git a/resources/resource_transformers/tocss/scss/tocss.go b/resources/resource_transformers/tocss/scss/tocss.go
index 36ef2a77db4..4b9c51ce05f 100644
--- a/resources/resource_transformers/tocss/scss/tocss.go
+++ b/resources/resource_transformers/tocss/scss/tocss.go
@@ -31,7 +31,7 @@ import (
 	"github.com/gohugoio/hugo/identity"
 	"github.com/gohugoio/hugo/media"
 	"github.com/gohugoio/hugo/resources"
-	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/internal/sass"
+	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/sass"
 )
 
 // Used in tests. This feature requires Hugo to be built with the extended tag.
@@ -64,7 +64,7 @@ func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx
 		}
 	}
 
-	varsStylesheet := sass.CreateVarsStyleSheet(options.from.Vars)
+	varsStylesheet := sass.CreateVarsStyleSheet(sass.TranspilerLibSass, options.from.Vars)
 
 	// To allow for overrides of SCSS files anywhere in the project/theme hierarchy, we need
 	// to help libsass revolve the filename by looking in the composite filesystem first.
diff --git a/tpl/css/css.go b/tpl/css/css.go
index 48a526c1722..199deda6902 100644
--- a/tpl/css/css.go
+++ b/tpl/css/css.go
@@ -15,6 +15,7 @@ import (
 	"github.com/gohugoio/hugo/resources/resource_transformers/babel"
 	"github.com/gohugoio/hugo/resources/resource_transformers/cssjs"
 	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
+	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/sass"
 	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
 	"github.com/gohugoio/hugo/tpl/internal"
 	"github.com/gohugoio/hugo/tpl/internal/resourcehelpers"
@@ -84,21 +85,13 @@ func (ns *Namespace) Sass(args ...any) (resource.Resource, error) {
 		return nil, errors.New("must not provide more arguments than resource object and options")
 	}
 
-	const (
-		// Transpiler implementation can be controlled from the client by
-		// setting the 'transpiler' option.
-		// Default is currently 'libsass', but that may change.
-		transpilerDart    = "dartsass"
-		transpilerLibSass = "libsass"
-	)
-
 	var (
 		r          resources.ResourceTransformer
 		m          map[string]any
 		targetPath string
 		err        error
 		ok         bool
-		transpiler = transpilerLibSass
+		transpiler = sass.TranspilerLibSass
 	)
 
 	r, targetPath, ok = resourcehelpers.ResolveIfFirstArgIsString(args)
@@ -113,15 +106,15 @@ func (ns *Namespace) Sass(args ...any) (resource.Resource, error) {
 	if m != nil {
 		if t, _, found := maps.LookupEqualFold(m, "transpiler"); found {
 			switch t {
-			case transpilerDart, transpilerLibSass:
+			case sass.TranspilerDart, sass.TranspilerLibSass:
 				transpiler = cast.ToString(t)
 			default:
-				return nil, fmt.Errorf("unsupported transpiler %q; valid values are %q or %q", t, transpilerLibSass, transpilerDart)
+				return nil, fmt.Errorf("unsupported transpiler %q; valid values are %q or %q", t, sass.TranspilerLibSass, sass.TranspilerDart)
 			}
 		}
 	}
 
-	if transpiler == transpilerLibSass {
+	if transpiler == sass.TranspilerLibSass {
 		var options scss.Options
 		if targetPath != "" {
 			options.TargetPath = paths.ToSlashTrimLeading(targetPath)


Test patch:
diff --git a/resources/resource_transformers/tocss/internal/sass/helpers_test.go b/resources/resource_transformers/tocss/sass/helpers_test.go
similarity index 100%
rename from resources/resource_transformers/tocss/internal/sass/helpers_test.go
rename to resources/resource_transformers/tocss/sass/helpers_test.go
diff --git a/tpl/resources/resources_integration_test.go b/tpl/resources/resources_integration_test.go
index cfd03dc735e..563b3c45582 100644
--- a/tpl/resources/resources_integration_test.go
+++ b/tpl/resources/resources_integration_test.go
@@ -18,6 +18,8 @@ import (
 
 	qt "github.com/frankban/quicktest"
 	"github.com/gohugoio/hugo/hugolib"
+	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
+	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
 )
 
 func TestCopy(t *testing.T) {
@@ -238,3 +240,45 @@ match /files/C*: 2|
 	b.AssertFileContent("public/files/b.txt", "I am b.txt")
 	b.AssertFileContent("public/files/C.txt", "I am C.txt")
 }
+
+// Issue #12961
+func TestDartSassVars(t *testing.T) {
+	t.Parallel()
+
+	if !scss.Supports() || !dartsass.Supports() {
+		t.Skip()
+	}
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','section','rss','sitemap','taxonomy','term']
+-- layouts/index.html --
+{{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" "vars" (dict "color" "red") }}
+{{ with resources.Get "dartsass.scss" | css.Sass $opts }}
+  {{ .Content }}
+{{ end }}
+
+{{ $opts := dict "transpiler" "libsass" "outputStyle" "compressed" "vars" (dict "color" "blue") }}
+{{ with resources.Get "libsass.scss" | css.Sass $opts }}
+  {{ .Content }}
+{{ end }}
+-- assets/dartsass.scss --
+@use "hugo:vars" as v;
+.dartsass {
+  color: v.$color;
+}
+-- assets/libsass.scss --
+@import "hugo:vars";
+.libsass {
+  color: $color;
+}
+`
+
+	b := hugolib.Test(t, files, hugolib.TestOptWarn())
+
+	b.AssertFileContent("public/index.html",
+		".dartsass{color:red}",
+		".libsass{color:blue}",
+	)
+	b.AssertLogContains("! WARN  Dart Sass: hugo:vars")
+}