]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
tpl/css: Make default loader resolution for CSS @import and url() always behave the...
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 13 Mar 2026 12:14:40 +0000 (13:14 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 13 Mar 2026 19:24:32 +0000 (20:24 +0100)
Before this commit, we did dynamic loader resolution for CSS bundling for resources resolved by Hugo while any fallback to ESBuild would fall back to a (potentially) empty loaders config.

This revises the logic to always use a static list (see below) if `loaders` is not set. This should be easier do document and less confusing for the end user.

````
".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".avif",
".woff", ".woff2", ".ttf", ".eot", ".otf"
````

Fixes #14619

internal/js/esbuild/options.go
internal/js/esbuild/resolve.go
tpl/css/build_integration_test.go

index 32d2a6251a06253496e6ea778d79ca7d1bc7cd0b..e5309fbbe0faefcf1e0b1a9ae474c40e43cff3d9 100644 (file)
@@ -299,6 +299,15 @@ OUTER:
                        }
                        loaders[k] = loader
                }
+       } else if opts.IsCSS {
+               loaders = make(map[string]api.Loader)
+               // For CSS builds, default to the file loader for common static
+               // file extensions so that url() references are handled correctly
+               // even when resolved by ESBuild's native resolver.
+               // See #14619.
+               for _, ext := range defaultCSSFileLoaderExts {
+                       loaders[ext] = api.LoaderFile
+               }
        }
 
        mediaType := opts.MediaType
@@ -467,16 +476,14 @@ func (o Options) loaderFromFilename(filename string) api.Loader {
                if found {
                        return l
                }
-               // For CSS builds, handling, default to the file loader for unknown extensions.
-               return api.LoaderFile
        } else {
                l, found := extensionToLoaderMapJS[ext]
                if found {
                        return l
                }
-       }
 
-       return api.LoaderJS
+       }
+       return api.LoaderDefault
 }
 
 func (opts *Options) validate() error {
index e9cfa7f9b085b317f730204ea4db0835d6e22218..3bdfda2daa2192f5597f3850dd6f0c26d71168c4 100644 (file)
@@ -62,6 +62,12 @@ var extensionToLoaderMapCSS = map[string]api.Loader{
        ".css": api.LoaderCSS,
 }
 
+// Common static file extensions that should use the file loader in CSS builds.
+var defaultCSSFileLoaderExts = []string{
+       ".png", ".jpg", ".jpeg", ".gif", ".svg", ".webp", ".avif",
+       ".woff", ".woff2", ".ttf", ".eot", ".otf",
+}
+
 // This is a common sub-set of ESBuild's default extensions.
 // We assume that imports of JSON, CSS etc. will be using their full
 // name with extension.
index c294452b7a4e21e27d5b8d8e9590d24d21edb6bd..718357de4b9101987ae2379f936f0cbf91e5be54 100644 (file)
@@ -270,10 +270,15 @@ func TestCSSBuildLoadersDefault(t *testing.T) {
 -- hugo.toml --
 -- assets/a/pixel.png --
 iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
+-- static/b/issue14619.png --
+iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
 -- assets/css/main.css --
 body {
   background-image: url("a/pixel.png");
 }
+div {
+  background-image: url("static/b/issue14619.png");
+}
 -- layouts/home.html --
 {{ with resources.Get "css/main.css"  }}
 {{ with . | css.Build (dict "minify" true)  }}
@@ -285,6 +290,8 @@ body {
        b := hugolib.Test(t, files, hugolib.TestOptOsFs())
        b.AssertFileContent("public/css/main.css", `./pixel-NJRUOINY.png`)
        b.AssertFileExists("public/css/pixel-NJRUOINY.png", true)
+       b.AssertFileContent("public/css/main.css", `url("./issue14619-NJRUOINY.png")`)
+       b.AssertFileExists("public/css/issue14619-NJRUOINY.png", true)
 }
 
 func TestCSSBuildBootstrapFromNPM(t *testing.T) {