/
githubmirror
/
hugo
Обзор
Документация
Войти
/
githubmirror
/
hugo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tpl/css/css.go
303 строки
8 KB
Bjørn Erik Pedersen
Add css.ChromaStyles
29 июл 2026, 19:41
29 июл 2026, 19:41
615e45d
Код
Авторство
О чём код?
package css import ( "context" "errors" "fmt" "sync" "github.com/gohugoio/hugo/common/hashing" "github.com/gohugoio/hugo/common/hmaps" "github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/types/css" "github.com/gohugoio/hugo/deps" "github.com/gohugoio/hugo/identity" "github.com/gohugoio/hugo/markup/highlight" "github.com/gohugoio/hugo/markup/markup_config" "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/resources/resource_transformers/babel" "github.com/gohugoio/hugo/resources/resource_transformers/cssjs" jstransform "github.com/gohugoio/hugo/resources/resource_transformers/js" "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" "github.com/mitchellh/mapstructure" "github.com/spf13/cast" ) const name = "css" // Namespace provides template functions for the "css" namespace. type Namespace struct { d *deps.Deps scssClientLibSass *scss.Client postcssClient *cssjs.PostCSSClient tailwindcssClient *cssjs.TailwindCSSClient babelClient *babel.Client jsTransformClient *jstransform.Client chromaStylesCache *hmaps.Cache[uint64, resource.Resource] highlightConfig highlight.Config // The Dart Client requires a os/exec process, so only // create it if we really need it. // This is mostly to avoid creating one per site build test. scssClientDartSassInit sync.Once scssClientDartSass *dartsass.Client } // ChromaStyles generates a CSS stylesheet for the Chroma code highlighter // and returns it as a Resource. This stylesheet is needed if // markup.highlight.noClasses is disabled in config. // The style defaults to the markup.highlight.style config setting. func (ns *Namespace) ChromaStyles(opts any) (resource.Resource, error) { key := hashing.HashUint64(opts) return ns.chromaStylesCache.GetOrCreate(key, func() (resource.Resource, error) { m, err := hmaps.ToStringMapE(opts) if err != nil { return nil, err } var o struct { TargetPath string highlight.ChromaStylesOptions `mapstructure:",squash"` } if err := mapstructure.WeakDecode(m, &o); err != nil { return nil, err } if o.TargetPath == "" { return nil, errors.New("targetPath cannot be empty") } if o.Style == "" { o.Style = ns.highlightConfig.Style } css, err := highlight.ChromaStylesCSS(o.ChromaStylesOptions) if err != nil { return nil, err } return ns.d.ResourceSpec.NewResource( resources.ResourceSourceDescriptor{ LazyPublish: true, GroupIdentity: identity.Anonymous, OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { return hugio.NewReadSeekerNoOpCloserFromString(css), nil }, TargetPath: o.TargetPath, }) }) } // 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) } // PostCSS processes the given Resource with PostCSS. func (ns *Namespace) PostCSS(args ...any) (resource.Resource, error) { if len(args) > 2 { return nil, errors.New("must not provide more arguments than resource object and options") } r, m, err := resourcehelpers.ResolveArgs(args) if err != nil { return nil, err } return ns.postcssClient.Process(r, m) } // TailwindCSS processes the given Resource with tailwindcss. func (ns *Namespace) TailwindCSS(args ...any) (resource.Resource, error) { if len(args) > 2 { return nil, errors.New("must not provide more arguments than resource object and options") } r, m, err := resourcehelpers.ResolveArgs(args) if err != nil { return nil, err } return ns.tailwindcssClient.Process(r, m) } // Sass processes the given Resource with SASS. func (ns *Namespace) Sass(args ...any) (resource.Resource, error) { if len(args) > 2 { return nil, errors.New("must not provide more arguments than resource object and options") } var ( r resources.ResourceTransformer m map[string]any targetPath string err error ok bool transpiler = sass.TranspilerLibSass // Deprecated default. Will be dartsass in future versions. See below. ) r, targetPath, ok = resourcehelpers.ResolveIfFirstArgIsString(args) if !ok { r, m, err = resourcehelpers.ResolveArgs(args) if err != nil { return nil, err } } if m != nil { if t, _, found := hmaps.LookupEqualFold(m, "transpiler"); found { switch t { case sass.TranspilerDart: transpiler = cast.ToString(t) case sass.TranspilerLibSass: hugo.Deprecate("css.Sass: libsass", "Use dartsass instead. See https://gohugo.io/functions/css/sass/#dart-sass", "v0.153.0") transpiler = cast.ToString(t) default: return nil, fmt.Errorf("unsupported transpiler %q; valid values are %q or %q", t, sass.TranspilerLibSass, sass.TranspilerDart) } } } if transpiler == sass.TranspilerLibSass { var options scss.Options if targetPath != "" { options.TargetPath = paths.ToSlashTrimLeading(targetPath) } else if m != nil { options, err = scss.DecodeOptions(m) if err != nil { return nil, err } } return ns.scssClientLibSass.ToCSS(r, options) } if m == nil { m = make(map[string]any) } if targetPath != "" { m["targetPath"] = targetPath } client, err := ns.getscssClientDartSass() if err != nil { return nil, err } return client.ToCSS(r, m) } // Build processes the given CSS Resource with ESBuild. // Note that this method is identical to the one in the js Namespace. func (ns *Namespace) Build(args ...any) (resource.Resource, error) { var ( r resources.ResourceTransformer m map[string]any targetPath string err error ok bool ) r, targetPath, ok = resourcehelpers.ResolveIfFirstArgIsString(args) if !ok { r, m, err = resourcehelpers.ResolveArgs(args) if err != nil { return nil, err } } if targetPath != "" { m = map[string]any{"targetPath": targetPath} } return ns.jsTransformClient.Process(r, m) } func init() { f := func(d *deps.Deps) *internal.TemplateFuncsNamespace { scssClient, err := scss.New(d.BaseFs.Assets, d.ResourceSpec) if err != nil { panic(err) } ctx := &Namespace{ d: d, scssClientLibSass: scssClient, postcssClient: cssjs.NewPostCSSClient(d.ResourceSpec), tailwindcssClient: cssjs.NewTailwindCSSClient(d.ResourceSpec), babelClient: babel.New(d.ResourceSpec), jsTransformClient: jstransform.New(d.BaseFs.Assets, d.ResourceSpec, true), chromaStylesCache: hmaps.NewCacheWithOptions[uint64, resource.Resource](hmaps.CacheOptions{Size: 10}), highlightConfig: d.Conf.GetConfigSection("markup").(markup_config.Config).Highlight, } ns := &internal.TemplateFuncsNamespace{ Name: name, Context: func(cctx context.Context, args ...any) (any, error) { return ctx, nil }, } ns.AddMethodMapping(ctx.ChromaStyles, nil, [][2]string{}, ) ns.AddMethodMapping(ctx.PostCSS, []string{"postCSS"}, [][2]string{}, ) ns.AddMethodMapping(ctx.Quoted, nil, [][2]string{}, ) ns.AddMethodMapping(ctx.Sass, []string{"toCSS"}, [][2]string{}, ) ns.AddMethodMapping(ctx.Build, nil, [][2]string{}, ) ns.AddMethodMapping(ctx.TailwindCSS, nil, [][2]string{}, ) ns.AddMethodMapping(ctx.Unquoted, nil, [][2]string{}, ) return ns } internal.AddTemplateFuncsNamespace(f) } func (ns *Namespace) getscssClientDartSass() (*dartsass.Client, error) { var err error ns.scssClientDartSassInit.Do(func() { ns.scssClientDartSass, err = dartsass.New(ns.d.BaseFs.Assets, ns.d.ResourceSpec) if err != nil { return } ns.d.BuildClosers.Add(ns.scssClientDartSass) }) return ns.scssClientDartSass, err }