tpl/css: Make default loader resolution for CSS @import and url() always behave the same

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
This commit is contained in:
Bjørn Erik Pedersen
2026-03-13 13:14:40 +01:00
parent 59e0446fe5
commit a7cbcf15f0
3 changed files with 24 additions and 4 deletions
+11 -4
View 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 {
+6
View 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.
+7
View 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) {