]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
tpl/css: Allow the user to override single loader entries
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 14 Mar 2026 10:35:33 +0000 (11:35 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 14 Mar 2026 14:17:21 +0000 (15:17 +0100)
Fixes #14623

Co-authored-by: Joe Mooring <joe.mooring@veriphor.com>
internal/js/esbuild/options.go
internal/js/esbuild/resolve.go
tpl/css/build_integration_test.go

index e5309fbbe0faefcf1e0b1a9ae474c40e43cff3d9..1e522f0ea099b222834018d0fd3ad84fbeef4c33 100644 (file)
@@ -289,8 +289,19 @@ OUTER:
        }
 
        var loaders map[string]api.Loader
-       if opts.Loaders != nil {
+       if opts.IsCSS {
                loaders = make(map[string]api.Loader)
+               // Add default CSS file loaders.
+               // May be overridden by opts.Loaders.
+               for ext, loader := range extensionToLoaderMapCSS {
+                       loaders[ext] = loader
+               }
+       }
+       if opts.Loaders != nil {
+               if loaders == nil {
+                       loaders = make(map[string]api.Loader)
+               }
+
                for k, v := range opts.Loaders {
                        loader, found := nameLoader[v]
                        if !found {
@@ -299,15 +310,6 @@ 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
@@ -471,17 +473,10 @@ func (o Options) loaderFromFilename(filename string) api.Loader {
                        return l
                }
        }
-       if o.IsCSS {
-               l, found := extensionToLoaderMapCSS[ext]
-               if found {
+       if !o.IsCSS {
+               if l, found := extensionToLoaderMapJS[ext]; found {
                        return l
                }
-       } else {
-               l, found := extensionToLoaderMapJS[ext]
-               if found {
-                       return l
-               }
-
        }
        return api.LoaderDefault
 }
index 3bdfda2daa2192f5597f3850dd6f0c26d71168c4..4349bdcc26742563eee4b3f5c29bfd0f56cab574 100644 (file)
@@ -60,12 +60,21 @@ var extensionToLoaderMapJS = map[string]api.Loader{
 
 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",
+       // Common static file extensions that should use the file loader in CSS builds.
+       ".png":  api.LoaderFile,
+       ".jpg":  api.LoaderFile,
+       ".jpeg": api.LoaderFile,
+       ".gif":  api.LoaderFile,
+       ".svg":  api.LoaderFile,
+       ".webp": api.LoaderFile,
+       ".avif": api.LoaderFile,
+
+       ".woff":  api.LoaderFile,
+       ".woff2": api.LoaderFile,
+       ".ttf":   api.LoaderFile,
+       ".eot":   api.LoaderFile,
+       ".otf":   api.LoaderFile,
 }
 
 // This is a common sub-set of ESBuild's default extensions.
index a36ed0774f6dd9e4eadec84cd6d0df4e1b1ae99e..dd674d63d5a4af375ac4e337f94474ad39ccb3dd 100644 (file)
@@ -313,6 +313,32 @@ div {
        b.AssertFileExists("public/css/issue14619-NJRUOINY.png", true)
 }
 
+// Issue #14623
+func TestCSSBuildLoadersPartial(t *testing.T) {
+       t.Parallel()
+
+       files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+-- layouts/home.html --
+{{ $opts := dict "loaders" (dict ".svg" "dataurl") }}
+{{ with resources.Get "css/main.css" | css.Build $opts }}
+  <link rel="stylesheet" href="{{ .RelPermalink }}">
+{{ end }}
+-- assets/css/main.css --
+body { background-image: url('images/pixel.png'); }
+div { background-image: url('images/foo.svg'); }
+-- assets/images/pixel.png --
+iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
+-- assets/images/foo.svg --
+SVG file.
+`
+       b := hugolib.Test(t, files, hugolib.TestOptOsFs())
+       b.AssertFileExists("public/css/pixel-NJRUOINY.png", true)
+       b.AssertFileContent("public/css/main.css", `url(data:image/svg`) // svg should be inlined as dataurl.
+       b.AssertFileContent("public/css/main.css", `pixel-NJRUOINY.png`) // png should be referenced as a hashed file, not inlined.
+}
+
 func TestCSSBuildBootstrapFromNPM(t *testing.T) {
        htesting.SkipSlowTestUnlessCI(t)
        t.Parallel()