How to inspect Hugo's virtual union filesystem

Only recently I started getting familiar with Hugo modules and its module.mounts configuration.

While fiddling around with my mounts config, I repeatedly wished for an easy way to inspect the resulting virtual union filesystem, i.e. to be able “to see” that a certain mount happened as intended. Currently, I have to write some dummy template code that “materializes” my assumptions in HTML.

Is there a way to easily inspect/print/display the whole resulting union filesystem directly, e.g. a command like hugo config vfs that would print a tree of it?

I also noticed that how mounts work differs between Hugo’s component folders (I didn’t systematically test them all):

  • Overlay order: For most component folders, mounts configured later on have precedence over ones configured earlier, i.e. overwrite/overlay them. But the opposite seems true for the assets component folder.

    Why? Are there more folders affected?

  • Deep merging: At least the content component folder is merged deeply, but static is not.

    What about the other folders? Why does the merging strategy differ between Hugo’s component folders?

  • As to overlay order, the order is well defined for components: From left to right, starting with the project. For the order of mounts within a module I only remember adding test case for content (where the languages makes this interesting/relevant); I have never ever had a use for overlapping /assets mount in the same project, so I haven’t cared too much, but I don’t see how/why the order should be different.

  • As to different merge strategies; looking at my own comment you link to I see I wasn’t very precise. We certainly do merge static dirs between different modules, but when the end merge strategy is different between them, that’s because … they’re different, some examples:

  • For content, we have merge logic based on a mix of content files in different languages.

  • For data; because the file type (e.g. /data/foo.json and /data/foo.toml), we cannot just read in the overlay fs.

  • For i18n we merge on “language string/key level”

The original motivation behind these mounts was to get a way to mount (and merge …) remote folders via modules. There may be some limitations (and certainly also bugs) with these mounts (possibly related to overlapping mounts), but I suspect most of these issues is a symptom of a too complex setup.

3 Likes

Thanks for the quick response and your explanations!

I realized that my initial assumption about assets/ just being overlaid in reverse was incorrect. I don’t know what qualifies as a “too complex setup”… but the Hyas framework might be a candidate. :stuck_out_tongue:

What I’m trying to do is overwrite some Bootstrap SCSS files from the Doks theme (based on Hyas; initalized using a recent version of the create-hyas CLI).

There are three relevant assets mounts involved:

[[mounts]]
  source = "node_modules/@hyas/core/assets"
  target = "assets"

[[mounts]]
  source = "node_modules/@hyas/doks-core/assets"
  target = "assets"

[[mounts]]
  source = "assets"
  target = "assets"

In this order I would expect that I can overwrite any of doks-core’s asset files (living under node_modules/@hyas/doks-core/assets/) by placing a customized version in my project’s assets/ folder.

What I experience instead, is that my custom assets/scss/app.scss is respected, but not the SCSS files that are @imported by this file (compiled via this Hugo template) like assets/scss/layouts/_pages.scss (the corresponding file from doks-core is used instead).

My custom assets/scss/layouts/_pages.scss is properly respected if I change the mounts rule from above to:

[[mounts]]
  source = "node_modules/@hyas/doks-core/assets"
  target = "assets"
  excludeFiles = "scss/layouts/_pages.scss"

(The same applies to other @imported SCSS files.)

When I change the mount order to the following OTOH, my custom assets/scss/app.scss is not respected anymore (i.e. the corresponding file from doks-core is used instead), but the assets/scss/layouts/_pages.scss file and other @imported ones are:

[[mounts]]
  source = "assets"
  target = "assets"

[[mounts]]
  source = "node_modules/@hyas/core/assets"
  target = "assets"

[[mounts]]
  source = "node_modules/@hyas/doks-core/assets"
  target = "assets"

I get a little bit dizzy looking at that “doks-core” project.

  • One of the motivations of Hugo Modules was to try to avoid using NPM as much as possible.
  • As I said, the mount merge logic is very well defined across modules (the project file will always win).

I haven’t tested this, but, you should be able to

My main take from this is to try to avoid mixing mounts from other modules into your project mount setup.

1 Like

I get a little bit dizzy looking at that “doks-core” project.

Yeah, I felt the same when I started using it. Discussion about pros and cons of using Node.js packages (npm) vs. Hugo modules happens here. :slightly_smiling_face:

I haven’t tested this, but, you should be able to (…)

You also need to convert the other mounts needed by the whole Hyas/Doks setup to imports, since they are supposed to be managed via npm.

Anyways, you find a working setup in this GitLab repository. You can clone it, run npm install and then play around with its module config to reproduce what I described above (which actually strikes me as a bug in how multiple assets mounts within the same module get overlaid).

As I said, Hugo’s overlay file system overlays the different modules’ file systems on to of each other (starting with the project), so you need to rework your setup (which should be possible) in line with that mind set.

Does that mean multiple mounts with target = "assets" in the same module (e.g. the top-level project) like the following is not supported / supposed to work in a predictable way?

[[mounts]]
  source = "assets"
  target = "assets"

[[mounts]]
  source = "node_modules/@hyas/core/assets"
  target = "assets"

[[mounts]]
  source = "node_modules/@hyas/doks-core/assets"
  target = "assets"

(After all I could successfully work around the problem at hand via explicit excludeFiles in the lower mount…:thinking:)

It means that:

  • The overlay file system is built from MODULES starting from the project with a well defined merge behaviour.
  • Each module can have its set of mounts The term mount is borrowed from *nix. There’s no consept of merging or overlapping mounts in *nix mounts. Hugo’s mounts are more powerful compared to the Linux version but there are limitations.
  • The documented merging overlay file system is the composite of modules/themes NOT the mounts within one module.

Sure, we can certainly and will propably improve how Hugo’s file system mounts works, but my recommendation would be to try to use Hugo Modules as it was designed.

Note that the mounts configuration was a way to replace configDir etc. We do allow multiple settings for that mount, we even allow overlapping folder structures and it mostly works gread – but you should test it.

Thank you again for taking the time to answer my (presumedly naive) questions.

(Where) is that merge behaviour documented?

Are these powers and limitations (also) documented somewhere?

Looking forward to it! :heart_eyes: