mirror of
https://github.com/gohugoio/hugo.git
synced 2026-08-24 15:28:54 +00:00
Add importContext option to css.Build, js.Build, css.Sass and css.TailwindCSS
This allows @import statements to be resolved in a set of user provided resources (e.g. from resources.FromString or css.ChromaStyles) before the assets filesystem. The option also applies to css.PostCSS via the shared import inlining, and css.Sass requires the dartsass transpiler. The import context is part of the transformation cache key. Fixes #15103 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
package cssjs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
@@ -30,6 +31,8 @@ import (
|
||||
"github.com/gohugoio/hugo/common/text"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
@@ -46,10 +49,12 @@ type fileOffset struct {
|
||||
}
|
||||
|
||||
type importResolver struct {
|
||||
ctx context.Context
|
||||
r io.Reader
|
||||
inPath string
|
||||
opts InlineImports
|
||||
|
||||
importContext resource.ResourceGetter
|
||||
contentSeen map[string]bool
|
||||
dependencyManager identity.Manager
|
||||
linemap map[int]fileOffset
|
||||
@@ -57,8 +62,9 @@ type importResolver struct {
|
||||
logger loggers.Logger
|
||||
}
|
||||
|
||||
func newImportResolver(r io.Reader, inPath string, opts InlineImports, fs afero.Fs, logger loggers.Logger, dependencyManager identity.Manager) *importResolver {
|
||||
return &importResolver{
|
||||
func newImportResolver(ctx context.Context, r io.Reader, inPath string, opts InlineImports, fs afero.Fs, logger loggers.Logger, dependencyManager identity.Manager) *importResolver {
|
||||
imp := &importResolver{
|
||||
ctx: ctx,
|
||||
r: r,
|
||||
dependencyManager: dependencyManager,
|
||||
inPath: inPath,
|
||||
@@ -66,6 +72,10 @@ func newImportResolver(r io.Reader, inPath string, opts InlineImports, fs afero.
|
||||
linemap: make(map[int]fileOffset), contentSeen: make(map[string]bool),
|
||||
opts: opts,
|
||||
}
|
||||
if opts.ImportContext != nil {
|
||||
imp.importContext = resource.NewCachedResourceGetter(opts.ImportContext)
|
||||
}
|
||||
return imp
|
||||
}
|
||||
|
||||
func (imp *importResolver) contentHash(filename string) ([]byte, string) {
|
||||
@@ -73,9 +83,13 @@ func (imp *importResolver) contentHash(filename string) ([]byte, string) {
|
||||
if err != nil {
|
||||
return nil, ""
|
||||
}
|
||||
return b, hashBytes(b)
|
||||
}
|
||||
|
||||
func hashBytes(b []byte) string {
|
||||
h := sha256.New()
|
||||
h.Write(b)
|
||||
return b, hex.EncodeToString(h.Sum(nil))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func (imp *importResolver) importRecursive(
|
||||
@@ -105,8 +119,35 @@ func (imp *importResolver) importRecursive(
|
||||
} else {
|
||||
path := strings.Trim(strings.TrimPrefix(line, importIdentifier), " \"';")
|
||||
filename := filepath.Join(basePath, path)
|
||||
imp.dependencyManager.AddIdentity(identity.CleanStringIdentity(filename))
|
||||
importContent, hash := imp.contentHash(filename)
|
||||
var (
|
||||
importContent []byte
|
||||
hash string
|
||||
nestedInPath string
|
||||
)
|
||||
if imp.importContext != nil {
|
||||
// Try first the path as written in the import statement,
|
||||
// then resolved relative to the importing file.
|
||||
for _, name := range []string{path, filepath.ToSlash(filename)} {
|
||||
r := imp.importContext.Get(name)
|
||||
if r == nil {
|
||||
continue
|
||||
}
|
||||
imp.dependencyManager.AddIdentity(identity.FirstIdentity(r))
|
||||
s, err := resources.InternalResourceSourceContent(imp.ctx, r)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
importContent, hash = []byte(s), hashBytes([]byte(s))
|
||||
nestedInPath = name
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if importContent == nil {
|
||||
imp.dependencyManager.AddIdentity(identity.CleanStringIdentity(filename))
|
||||
importContent, hash = imp.contentHash(filename)
|
||||
nestedInPath = filepath.ToSlash(filename)
|
||||
}
|
||||
|
||||
if importContent == nil {
|
||||
if imp.opts.SkipInlineImportsNotFound {
|
||||
@@ -135,7 +176,7 @@ func (imp *importResolver) importRecursive(
|
||||
imp.contentSeen[hash] = true
|
||||
|
||||
// Handle recursive imports.
|
||||
l, nested, err := imp.importRecursive(i+lineNum, string(importContent), filepath.ToSlash(filename))
|
||||
l, nested, err := imp.importRecursive(i+lineNum, string(importContent), nestedInPath)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package cssjs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -21,10 +22,12 @@ import (
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cast"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
@@ -103,6 +106,7 @@ LOCAL_STYLE
|
||||
@import "e.css";`)
|
||||
|
||||
imp := newImportResolver(
|
||||
t.Context(),
|
||||
mainStyles,
|
||||
"styles.css",
|
||||
InlineImports{},
|
||||
@@ -130,6 +134,70 @@ E_STYLE`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestImportResolverImportContext(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
fs := afero.NewMemMapFs()
|
||||
|
||||
writeFile := func(name, content string) {
|
||||
c.Assert(afero.WriteFile(fs, name, []byte(content), 0o777), qt.IsNil)
|
||||
}
|
||||
|
||||
// Loses to the import context entry with the same name.
|
||||
writeFile("css/a.css", "A_STYLE_FS")
|
||||
writeFile("css/b.css", "B_STYLE_FS")
|
||||
|
||||
importContext := testImportContext{
|
||||
"a.css": "@import \"c.css\";\nA_STYLE",
|
||||
"c.css": "C_STYLE",
|
||||
"css/d.css": "D_STYLE",
|
||||
}
|
||||
|
||||
mainStyles := strings.NewReader(`@import "a.css";
|
||||
@import "b.css";
|
||||
@import "./d.css";
|
||||
LOCAL_STYLE`)
|
||||
|
||||
imp := newImportResolver(
|
||||
t.Context(),
|
||||
mainStyles,
|
||||
"css/styles.css",
|
||||
InlineImports{ImportContext: importContext},
|
||||
fs, loggers.NewDefault(),
|
||||
identity.NopManager,
|
||||
)
|
||||
|
||||
r, err := imp.resolve()
|
||||
c.Assert(err, qt.IsNil)
|
||||
rs := helpers.ReaderToString(r)
|
||||
result := regexp.MustCompile(`\n+`).ReplaceAllString(rs, "\n")
|
||||
|
||||
c.Assert(result, hqt.IsSameString, `C_STYLE
|
||||
A_STYLE
|
||||
B_STYLE_FS
|
||||
D_STYLE
|
||||
LOCAL_STYLE`)
|
||||
}
|
||||
|
||||
type testImportContext map[string]string
|
||||
|
||||
func (g testImportContext) Get(name any) resource.Resource {
|
||||
s := cast.ToString(name)
|
||||
if content, found := g[s]; found {
|
||||
return testImportContextResource{name: s, content: content}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type testImportContextResource struct {
|
||||
resource.Resource
|
||||
name string
|
||||
content string
|
||||
}
|
||||
|
||||
func (r testImportContextResource) Name() string { return r.name }
|
||||
|
||||
func (r testImportContextResource) Content(context.Context) (any, error) { return r.content, nil }
|
||||
|
||||
func BenchmarkImportResolver(b *testing.B) {
|
||||
c := qt.New(b)
|
||||
fs := afero.NewMemMapFs()
|
||||
@@ -161,6 +229,7 @@ LOCAL_STYLE
|
||||
for b.Loop() {
|
||||
b.StopTimer()
|
||||
imp := newImportResolver(
|
||||
b.Context(),
|
||||
strings.NewReader(mainStyles),
|
||||
"styles.css",
|
||||
InlineImports{},
|
||||
|
||||
@@ -87,6 +87,10 @@ type InlineImports struct {
|
||||
// Note that the inline importer does not process url location or imports with media queries,
|
||||
// so those will be left as-is even without enabling this option.
|
||||
SkipInlineImportsNotFound bool
|
||||
|
||||
// User provided import context. If set, imports are looked up here first,
|
||||
// by the path as written in the @import statement, then in the assets filesystem.
|
||||
ImportContext any
|
||||
}
|
||||
|
||||
// Some of the options from https://github.com/postcss/postcss-cli
|
||||
@@ -213,6 +217,7 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
|
||||
src := ctx.From
|
||||
|
||||
imp := newImportResolver(
|
||||
ctx.Ctx,
|
||||
ctx.From,
|
||||
ctx.InPath,
|
||||
options.InlineImports,
|
||||
|
||||
@@ -124,6 +124,7 @@ func (t *tailwindcssTransformation) Transform(ctx *resources.ResourceTransformat
|
||||
src := ctx.From
|
||||
|
||||
imp := newImportResolver(
|
||||
ctx.Ctx,
|
||||
ctx.From,
|
||||
ctx.InPath,
|
||||
options.InlineImports,
|
||||
|
||||
@@ -64,6 +64,45 @@ CSS: {{ $css.Content | safeCSS }}|
|
||||
b.AssertFileContent("public/index.html", "/*! tailwindcss v4.")
|
||||
}
|
||||
|
||||
// See issue 15103.
|
||||
func TestTailwindCSSImportContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
htesting.SkipSlowTestUnlessCI(t)
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
disableKinds = ['page','rss','section','sitemap','taxonomy','term']
|
||||
-- assets/css/main.css --
|
||||
@import "tailwindcss";
|
||||
|
||||
@import "foo.css";
|
||||
@import "bar.css";
|
||||
-- assets/css/foo.css --
|
||||
.foo {color: orange;}
|
||||
-- layouts/home.html --
|
||||
{{ $foo := resources.FromString "foo.css" ".foo {color: blue;}" }}
|
||||
{{ $bar := resources.FromString "bar.css" ".bar {color: green;}" }}
|
||||
{{ $opts := dict "importContext" (slice $foo $bar) }}
|
||||
{{ $css := resources.Get "css/main.css" | css.TailwindCSS $opts }}
|
||||
CSS: {{ $css.Content | safeCSS }}|
|
||||
-- package.json --
|
||||
{
|
||||
"devDependencies": {
|
||||
"@tailwindcss/cli": "^4.1.7",
|
||||
"tailwindcss": "^4.1.7"
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptOsFs(), hugolib.TestOptWithNpmInstall(), hugolib.TestOptInfo())
|
||||
|
||||
// foo.css resolves in the import context before the assets filesystem.
|
||||
b.AssertFileContent("public/index.html",
|
||||
".foo {\n color: blue;\n }",
|
||||
".bar {\n color: green;\n }",
|
||||
)
|
||||
}
|
||||
|
||||
func TestTailwindCSSNoInlineImportsIssue13719(t *testing.T) {
|
||||
t.Parallel()
|
||||
htesting.SkipSlowTestUnlessCI(t)
|
||||
|
||||
@@ -42,6 +42,8 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec, cssMode bool) *Cl
|
||||
}
|
||||
|
||||
// Process processes a resource with the user provided options.
|
||||
// If importContext is not nil, imports are resolved in it first,
|
||||
// then in the assets filesystem (the same precedence as in js.Batch).
|
||||
func (c *Client) Process(res resources.ResourceTransformer, opts map[string]any) (resource.Resource, error) {
|
||||
return res.Transform(
|
||||
&buildTransformation{c: c, optsm: opts},
|
||||
|
||||
@@ -18,10 +18,14 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/evanw/esbuild/pkg/api"
|
||||
"github.com/gohugoio/hugo/common/hmaps"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
"github.com/gohugoio/hugo/internal/js/esbuild"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/gohugoio/hugo/resources/internal"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
)
|
||||
|
||||
type buildTransformation struct {
|
||||
@@ -69,6 +73,29 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||
opts.MediaType = ctx.InMediaType
|
||||
opts.Stdin = true
|
||||
opts.IsCSS = t.c.c.CssMode
|
||||
var ic resource.ResourceGetter
|
||||
if opts.ImportContext != nil {
|
||||
ic = resource.NewCachedResourceGetter(opts.ImportContext)
|
||||
}
|
||||
|
||||
if ic != nil {
|
||||
resolved := hmaps.NewCache[string, resource.Resource]()
|
||||
opts.ImportOnResolveFunc = func(imp string, args api.OnResolveArgs) string {
|
||||
if r := esbuild.ResolveResource(imp, ic); r != nil {
|
||||
p := esbuild.PrefixHugoVirtual + resources.InternalResourceTargetPath(r)
|
||||
resolved.Set(p, r)
|
||||
ctx.DependencyManager.AddIdentity(identity.FirstIdentity(r))
|
||||
return p
|
||||
}
|
||||
return ""
|
||||
}
|
||||
opts.ImportOnLoadFunc = func(args api.OnLoadArgs) (string, error) {
|
||||
if r, found := resolved.Get(args.Path); found {
|
||||
return resources.InternalResourceSourceContent(ctx.Ctx, r)
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
_, err = t.c.transform(opts, ctx)
|
||||
|
||||
|
||||
@@ -158,6 +158,10 @@ type Options struct {
|
||||
// $color: vars.$color;
|
||||
Vars map[string]any
|
||||
|
||||
// User provided import context. If set, imports are looked up here first,
|
||||
// then in the assets filesystem.
|
||||
ImportContext any
|
||||
|
||||
// Deprecations IDs in this slice will be silenced.
|
||||
// The IDs can be found in the Dart Sass log output, e.g. "import" in
|
||||
// WARN Dart Sass: DEPRECATED [import].
|
||||
|
||||
@@ -51,6 +51,35 @@ T1: {{ $r.Content }}
|
||||
b.AssertFileContent("public/index.html", `T1: moo{color:#fff}`)
|
||||
}
|
||||
|
||||
// See issue 15103.
|
||||
func TestTransformImportContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !dartsass.Supports() {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
files := `
|
||||
-- hugo.toml --
|
||||
-- assets/scss/_foo.scss --
|
||||
body { color: orange; }
|
||||
-- assets/scss/main.scss --
|
||||
@import "foo";
|
||||
@import "bar";
|
||||
-- layouts/home.html --
|
||||
{{ $foo := resources.FromString "foo.scss" "body { color: blue; }" }}
|
||||
{{ $bar := resources.FromString "bar.scss" "@import \"baz\";\nbody { color: green; }" }}
|
||||
{{ $baz := resources.FromString "baz.scss" "p { color: red; }" }}
|
||||
{{ $opts := dict "transpiler" "dartsass" "outputStyle" "compressed" "importContext" (slice $foo $bar $baz) }}
|
||||
{{ $r := resources.Get "scss/main.scss" | css.Sass $opts }}
|
||||
T1: {{ $r.Content }}
|
||||
`
|
||||
|
||||
b := hugolib.Test(t, files, hugolib.TestOptOsFs())
|
||||
|
||||
// foo resolves in the import context before the assets filesystem.
|
||||
b.AssertFileContent("public/index.html", `T1: body{color:blue}p{color:red}body{color:green}`)
|
||||
}
|
||||
|
||||
func TestTransformImportRegularCSS(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !dartsass.Supports() {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
package dartsass
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
"github.com/gohugoio/hugo/media"
|
||||
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/internal"
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/sass"
|
||||
@@ -38,6 +40,10 @@ import (
|
||||
"github.com/bep/godartsass/v2"
|
||||
)
|
||||
|
||||
// Prefix for canonical URLs of stylesheets resolved in the user provided import context.
|
||||
// Note: This prefix must be all lower case.
|
||||
const dartSassImportContextPrefix = "hugoimportcontext:"
|
||||
|
||||
// Supports returns whether sass, dart-sass, or dart-sass-embedded is found in $PATH.
|
||||
func Supports() bool {
|
||||
if htesting.SupportsAll() {
|
||||
@@ -76,6 +82,11 @@ func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error {
|
||||
filename += t.c.sfs.RealFilename(ctx.SourcePath)
|
||||
}
|
||||
|
||||
var ic resource.ResourceGetter
|
||||
if opts.ImportContext != nil {
|
||||
ic = resource.NewCachedResourceGetter(opts.ImportContext)
|
||||
}
|
||||
|
||||
args := godartsass.Args{
|
||||
URL: filename,
|
||||
IncludePaths: t.c.sfs.RealDirs(baseDir),
|
||||
@@ -83,6 +94,8 @@ func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error {
|
||||
baseDir: baseDir,
|
||||
c: t.c,
|
||||
dependencyManager: ctx.DependencyManager,
|
||||
importContext: ic,
|
||||
ctx: ctx.Ctx,
|
||||
|
||||
vars: opts.Vars,
|
||||
},
|
||||
@@ -132,6 +145,8 @@ type importResolver struct {
|
||||
baseDir string
|
||||
c *Client
|
||||
dependencyManager identity.Manager
|
||||
importContext resource.ResourceGetter
|
||||
ctx context.Context
|
||||
vars map[string]any
|
||||
}
|
||||
|
||||
@@ -140,6 +155,11 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) {
|
||||
return strings.ToLower(url), nil
|
||||
}
|
||||
|
||||
if r := t.resolveInImportContext(url); r != nil {
|
||||
t.dependencyManager.AddIdentity(identity.FirstIdentity(r))
|
||||
return dartSassImportContextPrefix + paths.ToSlashTrimLeading(resources.InternalResourceTargetPath(r)), nil
|
||||
}
|
||||
|
||||
filePath, isURL := paths.UrlStringToFilename(url)
|
||||
var prevDir string
|
||||
var pathDir string
|
||||
@@ -160,21 +180,7 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) {
|
||||
name := filepath.Base(filePath)
|
||||
|
||||
// Pick the first match.
|
||||
var namePatterns []string
|
||||
if strings.Contains(name, ".") {
|
||||
namePatterns = []string{"_%s", "%s"}
|
||||
} else if strings.HasPrefix(name, "_") {
|
||||
namePatterns = []string{"_%s.scss", "_%s.sass", "_%s.css"}
|
||||
} else {
|
||||
namePatterns = []string{
|
||||
"_%s.scss", "%s.scss",
|
||||
"_%s.sass", "%s.sass",
|
||||
"_%s.css", "%s.css",
|
||||
"%s/_index.scss", "%s/_index.sass",
|
||||
"%s/index.scss", "%s/index.sass",
|
||||
}
|
||||
}
|
||||
|
||||
namePatterns := sassNamePatterns(name)
|
||||
name = strings.TrimPrefix(name, "_")
|
||||
|
||||
for _, namePattern := range namePatterns {
|
||||
@@ -192,21 +198,72 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func sassNamePatterns(name string) []string {
|
||||
if strings.Contains(name, ".") {
|
||||
return []string{"_%s", "%s"}
|
||||
}
|
||||
if strings.HasPrefix(name, "_") {
|
||||
return []string{"_%s.scss", "_%s.sass", "_%s.css"}
|
||||
}
|
||||
return []string{
|
||||
"_%s.scss", "%s.scss",
|
||||
"_%s.sass", "%s.sass",
|
||||
"_%s.css", "%s.css",
|
||||
"%s/_index.scss", "%s/_index.sass",
|
||||
"%s/index.scss", "%s/index.sass",
|
||||
}
|
||||
}
|
||||
|
||||
// resolveInImportContext resolves url in the user provided import context, if any,
|
||||
// using the same name patterns as for the file system.
|
||||
func (t importResolver) resolveInImportContext(url string) resource.Resource {
|
||||
if t.importContext == nil {
|
||||
return nil
|
||||
}
|
||||
url = strings.TrimPrefix(url, dartSassImportContextPrefix)
|
||||
if r := t.importContext.Get(url); r != nil {
|
||||
return r
|
||||
}
|
||||
dir, name := path.Split(url)
|
||||
namePatterns := sassNamePatterns(name)
|
||||
name = strings.TrimPrefix(name, "_")
|
||||
for _, namePattern := range namePatterns {
|
||||
if r := t.importContext.Get(path.Join(dir, fmt.Sprintf(namePattern, name))); r != nil {
|
||||
return r
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t importResolver) Load(url string) (godartsass.Import, error) {
|
||||
if subPath, ok := sass.HugoVarsSubPath(url); ok {
|
||||
return godartsass.Import{
|
||||
Content: sass.CreateVarsStyleSheet(sass.TranspilerDart, sass.ResolveVars(t.vars, subPath)),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(url, dartSassImportContextPrefix) {
|
||||
r := t.resolveInImportContext(url)
|
||||
if r == nil {
|
||||
return godartsass.Import{}, fmt.Errorf("could not find %q in the import context", url)
|
||||
}
|
||||
content, err := resources.InternalResourceSourceContent(t.ctx, r)
|
||||
return godartsass.Import{Content: content, SourceSyntax: sassSourceSyntax(url)}, err
|
||||
}
|
||||
|
||||
filename, _ := paths.UrlStringToFilename(url)
|
||||
b, err := afero.ReadFile(hugofs.Os, filename)
|
||||
|
||||
sourceSyntax := godartsass.SourceSyntaxSCSS
|
||||
if strings.HasSuffix(filename, ".sass") {
|
||||
sourceSyntax = godartsass.SourceSyntaxSASS
|
||||
} else if strings.HasSuffix(filename, ".css") {
|
||||
sourceSyntax = godartsass.SourceSyntaxCSS
|
||||
}
|
||||
|
||||
return godartsass.Import{Content: string(b), SourceSyntax: sourceSyntax}, err
|
||||
return godartsass.Import{Content: string(b), SourceSyntax: sassSourceSyntax(filename)}, err
|
||||
}
|
||||
|
||||
func sassSourceSyntax(name string) godartsass.SourceSyntax {
|
||||
switch {
|
||||
case strings.HasSuffix(name, ".sass"):
|
||||
return godartsass.SourceSyntaxSASS
|
||||
case strings.HasSuffix(name, ".css"):
|
||||
return godartsass.SourceSyntaxCSS
|
||||
default:
|
||||
return godartsass.SourceSyntaxSCSS
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user