Do the mounts in an imported module's config.yaml need to be copied into your module's config.yaml?

It doesn’t seem to work without doing that, but I don’t see this documented anywhere.

I think it’s no, but you’ll need to make sure the mounted files present on the target module.

Take gohugoio/hugo-mod-bootstrap-scss as an example.

The scss and js folder should be included (committed) on the imported module github.com/twbs/bootstrap.

IMHO, it’s not recommend mounting node_modules in a module.

In this case, the mounted files are from node_modules, which isn’t checked in. There’s a package.hugo.json in the imported module and the importer module, both in sync. npm install is run before hugo build to ensure that node_modules is present. But the files aren’t being mounted by the imported module, despite the mounts being in its config.yaml.

IMHO, it’s not recommend mounting node_modules in a module.

Why? And which module do you mean, the importer or the imported?

The imported module should include files mounted by the importer. You can test it with the following sample.

$ git clone -b mods-mounts https://github.com/razonyang/testing
$ cd testing/site
$ hugo

...
WARN 2023/03/20 08:12:21 [mod] bar's bar/js/index.js mounted
WARN 2023/03/20 08:12:21 [mod] bar's bar/scss/index.scss mounted
...
$ cat layouts/index.html 
{{ range resources.Match "bar/**/*" }}
  {{ warnf "[mod] bar's %v mounted" . }}
{{ end }}
{{ range resources.Match "bootstrap/**/*" }}
  {{ warnf "[mod] bootstrap's %v mounted" . }}
{{ end }}
$ cat config.toml
...
[[module.imports]]
path = "github.com/razonyang/testing/fizz"

$ cat ../fizz/hugo.toml
...
[[module.imports]]
path = "github.com/razonyang/testing/bar"
[[module.imports.mounts]]
source = "node_modules/bootstrap"
target = "assets/bootstrap"
[[module.imports.mounts]]
source = "src"
target = "assets/bar"

$ tree ../bar
├── go.mod
├── go.sum
├── hugo.toml
└── src
    ├── js
    │   └── index.js
    └── scss
        └── index.scss

$ tree .
...
├── node_modules
│   └── bootstrap
│       └── dist
│           └── index.js
...

As the example shown.

  1. site imported fizz module.
  2. fizz imported the bar module, and mounted bar’s src and node_modules/bootstrap.
  3. but the node_modules/bootstrap doesn’t exist on the bar module, so there are no files to mount into assets/bootstrap, even if there is node_modules/bootstrap on the site.

You can verify this by creating files under the bar/node_modules/bootstrap folder.

$ mkdir -p ../bar/node_modules/bootstrap/js/
$ touch ../bar/node_modules/bootstrap/js/index.js
$ hugo
...
WARN 2023/03/20 08:27:44 [mod] bar's bar/js/index.js mounted
WARN 2023/03/20 08:27:44 [mod] bar's bar/scss/index.scss mounted
WARN 2023/03/20 08:27:44 [mod] bootstrap's bootstrap/js/index.js mounted
...

It’s no need to use module to mount node_modules, Hugo can resolve the dependencies from node_modules.

I’m not sure I understand what your example shows. There aren’t any files in GitHub - razonyang/testing: Just for testing now. It seems like you described this first:

site/ # module
  layouts/index.html:
    print files mounted in bar/ and bootstrap/
  config.yaml:
    import fizz
  node_modules/bootstrap/dist/index.js
fizz/ # module
  config.yaml:
    import bar
    mount node_modules/bootstrap to assets/bootstrap
    mount src to assets/bar
bar/ # module
  src/
    js/index.js
    scss/index.scss

This mounts bar/js/index.js and bar/scss/index.scss.

Then you said that creating bar/node_modules/bootstrap/js/index.js would mount bootstrap/js/index.js.

Do I understand correctly?

If so, then I’m wondering: What is the point of the hugo mod npm pack command? This command ensures that the NPM dependencies of imported modules are present in the site’s node_modules. The importer (not imported) module doesn’t necessarily know that some node_modules files need to be mounted so the imported module can use them. So what is the intended effect of hugo mod npm pack? That the presence of an NPM module in the site’s node_modules will have some sort of side effect, and is never actually used concretely in the generated code?

Edit: Removed a question. I realized the answer.

You’ll need to switch to the mods-mounting branch (git clone -b mods-mounts https://github.com/razonyang/testing).

Yes.

This command is not related to module, it pulls the all dependencies of modules to package.json.

As the testing sample shown, the mounted files should be included inside the module, in this case, you’ve to include some node_modules packages inside the module, which is not recommended.

Actually, you could mount some packages as module instead mounting it from node_modules.
Take bootstrap as an example (not tested).

[[module.imports]]
ignoreConfig = true
path = "github.com/twbs/bootstrap"
[[module.imports.mounts]]
source = "scss"
target = "assets/bootstrap/scss"
[[module.imports.mounts]]
source = "dist"
target = "assets/bootstrap/dist"
[[module.imports.mounts]]
source = "js"
target = "assets/bootstrap/js"

You can access the pre-built CSS by resources.Get "bootstrap/dist/css/bootstrap.min.css".

If I remember correctly, your theme will use the pre-built Bootstrap CSS (dist/css/bootstrap.min.css) in some case. You cloud either commit that CSS to module and mount it, or mounting the Bootstrap as a module.

But this make things worse in my point of view, since there are two dependencies managers for same package. The simplest way is to use prebuilt CSS via CDN or always build the CSS via Hugo Pipe.

This command is not related to module, it pulls the all dependencies of modules to package.json .

But what is the point, then? If I import module M, and module M declares in its package.hugo.json that it requires bootstrap, then hugo mod npm pack will generate a package.hugo.json in my module that also requires bootstrap, right? But for what purpose? No code in M can rely on bootstrap being mounted, so what’s the point?

If I remember correctly, your theme will use the pre-built Bootstrap CSS (dist/css/bootstrap.min.css) in some case. You cloud either commit that CSS to module and mount it, or mounting the Bootstrap as a module.

This doesn’t work in general, Bootstrap is unusual in that it commits its build artifacts in a dist directory, but many—perhaps most—projects do not. Katex, for example, does not.

But this make things worse in my point of view, since there are two dependencies managers for same package. The simplest way is to use prebuilt CSS via CDN or always build the CSS via Hugo Pipe.

Live downloading of resources isn’t always desirable or possible.

Hmm, I think I’ve explained the hugo mod npm pack.

You could build it from source code (src) via Hugo Pipes instead of using pre-built assets …


The problem is that you’re trying to mount files from a module, which not present in the module, so you may need to change the way how to build and use assets, to follow the current rule how modules mount files.

Your answer there seemed to imply that the node_modules in the site would be mount-able by the modules themselves. Otherwise, what’s the point?

You could build it from source code (src ) via Hugo Pipes instead of using pre-built assets …

This doesn’t work if it’s an imported module that needs to process the imported and mounted assets.

Hugo will resolve dependencies from node modules, see also js.Build | Hugo and the includePaths of Sass/SCSS, it doesn’t mean there are mountable from a module.

I’m not following how hugo mod npm pack enables imported modules to access things in node_modules that haven’t been mounted by the site.

Can you show an example of using hugo mod npm pack to enable a theme to use Katex?

Example with node_modules

This approach uses Hugo Pipes to build JS and CSS from sources.

// assets/katex/js/index.ts
import renderMathInElement from "katex/dist/contrib/auto-render.js";
const options = {}; // ...
document.addEventListener("DOMContentLoaded", () => {
  renderMathInElement(document.body, options);
});
// assets/katex/scss/index.scss
@import 'katex/dist/katex';
{{- $options := dict "targetPath" "assets/katex/bundle.min.js" "minify" hugo.IsProduction -}}
{{- $script := resources.Get "katex/js/index.ts" | js.Build $options | fingerprint -}}
<script data-precache defer src="{{ $script.RelPermalink }}" integrity="{{ $script.Data.Integrity }}" crossorigin="anonymous"></script>

{{- $outputStyle := "expanded" }}
{{- if hugo.IsProduction }}{{ $outputStyle = "compressed" }}{{ end }}
{{- $options := dict "targetPath" "assets/katex/bundle.min.css" "outputStyle" $outputStyle "includePaths" (slice "node_modules") }}
{{- $style := resources.Get "katex/scss/index.scss" | toCSS $options | fingerprint }}
<link data-precache rel="stylesheet" href="{{ $style.RelPermalink }}" integrity="{{ $style.Data.Integrity }}" crossorigin="anonymous">

But there is a limitation that mount the fonts required by KaTex CSS, since the fonts located in the node_nodules, a workaround is that copy the fonts to theme’s static folder and commit those fonts o your theme repo.

I didn’t test the code above, it’s copied from my theme, may contains typo.

Example with module only

This approach doesn’t require node_modules (users side).

$ git clone https://github.com/hugomods/katex
$ cd katex/exampleSite
$ hugo server

The module using webpack to copy the necessary JS, CSS and fonts into assets, and commit those files into the module.

The ideal way is that mount KaTeX as a module, and compile JS and CSS from source, but unfortunately, KaTex uses Flow, which doesn’t support by ESBuild.

In your example, which code is in the site? Which code is in the imported module? Does the site have a package.json? Does the imported module have a package.json? When did you run hugo mod npm pack?

Note that the solution can’t copy files manually out of node_modules; that’s what mounts are for.

The ideal way is that mount KaTeX as a module, and compile JS and CSS from source, but unfortunately, KaTex uses Flow, which doesn’t support by ESBuild.

I wouldn’t want to use ESBuild anyway. I just want to install Katex with NPM, which presumably involves hugo mod npm pack in some way.

a workaround is that copy the fonts to theme’s static folder and commit those fonts o your theme repo.

I want to use NPM to avoid checking Katex into my repo. I thought hugo mod npm pack was supposed to help me do that. If not, then what is pack used for?

The code could be found at GitHub - razonyang/hugo-theme-bootstrap: A fast, responsive, multipurpose and feature-rich Hugo theme. (using node_modules) and GitHub - hugomods/katex: Hugo KaTex Module (using module).

There are also example sites inside the repo, test it yourself, and thinking about what node_modules and hugo mod npn pack used for, and then find a solution/workaround for the way you handle CSS and JS.

Unfortunately, I need to understand at a conceptual level; I don’t think poring through that code would be productive. It seems clear that you use pack there, based on the presence of package.hugo.json, but the example site doesn’t have a config file, and the theme’s config doesn’t mount anything, so I don’t understand how that’s relevant to my original question, or the question of what pack is good for.

Summaries

  1. hugo mod npm pack pulls dependencies from themes and modules into package.json, that’s all.
  2. node_modules can be used by Hugo Pipes to resolve dependencies, such as js.Build and toCSS. If you are not using those functions, then the hugo mod npm pack and node_modules may not mean much to you.
  3. Mounting files from an imported module, those files MUST be present in that imported module, regardless of whether it is node_modules.

Solutions

  1. Copy the necessary files into module’s assets, static folders, those files can be accessed on importer side without mounts setting. In this approach, you’re maintaining the package version yourself.
  2. Import the upstream package repo as a module, like Hugo Bootstrap SCSS Module does.

I believe you could find a better solution to fit your needs.

I understand everything else except what the point of this. For example, in your site, what do you use package.hugo.json for? In other words, if package.hugo.json isn’t there, what wouldn’t work?