hugofs: Make node_modules a "special case" mount

For this and similar mounts in a theme:

```toml
[[module.mounts]]
source = 'node_modules/bootstrap'
target = 'assets/vendor/bootstrap'
```

We first check the theme itself, then the project root.

For backwards compatibility, we also make any `../../node_modules/...` `source` paths into `node_modules/...` paths when defined in themes/modules.

Fixes #14089
This commit is contained in:
Bjørn Erik Pedersen
2025-10-24 10:36:32 +02:00
parent 08a0679a89
commit 809ebe01fa
2 changed files with 76 additions and 3 deletions
+36 -3
View File
@@ -695,6 +695,19 @@ func (c *collector) mountCommonJSConfig(owner *moduleAdapter, mounts []Mount) ([
return mounts, nil
}
func (c *collector) nodeModulesRoot(s string) string {
s = filepath.ToSlash(s)
if strings.HasPrefix(s, "node_modules/") {
return s
}
if strings.HasPrefix(s, "../../node_modules/") {
// See #14083. This was a common construct to mount node_modules from the project root.
// This started failing in v0.152.0 when we tightened the validation.
return strings.TrimPrefix(s, "../../")
}
return ""
}
func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mount, error) {
var out []Mount
dir := owner.Dir()
@@ -706,6 +719,17 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou
return nil, errors.New(errMsg + ": both source and target must be set")
}
// Special case for node_modules imports in themes/modules.
// See #14089.
var isModuleNodeModulesImport bool
if !owner.projectMod {
nodeModulesImportSource := c.nodeModulesRoot(mnt.Source)
if nodeModulesImportSource != "" {
isModuleNodeModulesImport = true
mnt.Source = nodeModulesImportSource
}
}
mnt.Source = filepath.Clean(mnt.Source)
mnt.Target = filepath.Clean(mnt.Target)
var sourceDir string
@@ -741,9 +765,18 @@ func (c *collector) normalizeMounts(owner *moduleAdapter, mounts []Mount) ([]Mou
}
f.Close()
} else {
// TODO(bep) commenting out for now, as this will create to much noise.
// c.logger.Warnf("module %q: mount source %q does not exist", owner.Path(), sourceDir)
continue
if isModuleNodeModulesImport {
// A module imported a path inside node_modules, but it didn't exist.
// Make this a special case and also try relative to the project root.
sourceDir = filepath.Join(c.ccfg.WorkingDir, mnt.Source)
_, err := c.fs.Stat(sourceDir)
if err != nil {
continue
}
mnt.Source = sourceDir
} else {
continue
}
}
}