Bundle "many" resources

{{ $.Scratch.Set "jslibs" slice }}
{{ $libs := slice "js/underscore.js" "js/webmidi.js" "js/base.js" }}
{{ range $libs }}
{{ $.Scratch.Add "jslibs" (resources.Get . ) }}
{{ end }}
{{ $js := .Scratch.Get "jslibs" | resources.Concat "js/main.js" }}
{{ if .Site.IsServer }}
<script src="{{ $js.Permalink }}"></script>
{{ else }}
{{ $js := $js | minify | fingerprint }}
<script src="{{ $js.Permalink }}" integrity="{{ $js.Data.Integrity }}"></script>
{{ end }}

Looking at the above, we might eventually bite the bullet and add some better NPM support, but the above looks clean enough for me for the above simple Web MIDI hobby project.

4 Likes

Are you still thinking about adding resources.Match?

I would love to reduce the above to:
{{ $js := resources.Match "js/*" | resources.Concat "js/main.js" }} :star_struck:

1 Like

I dreamt about it last night. So, technically I think about it.

5 Likes

Would it not be even better to (also) do:

$bundle := resources.Concat "js/*.js" 

Etc.

2 Likes

Yes of course! :smile:

Keep in mind that you than have no control about the concat order. Unless you name your files accordingly (like 01-jquery.js, 02-myjs.js etc.). But in some cases the proposed way might make sense.

True!

But I suppose you could use where, sort etc… on the collection of resources returned by resources.Match.

1 Like

Great tip!

In most cases you only need a coarse grained control, and the most important part here is stable order. Then you could put libs in js/libs and other in js/main or something and fetch them with .resources.Get "js/**.js".

2 Likes

Extending on @bep’s suggestion. I pass a JSON file which contains the URLs (relative to the assets directory) and then iterate over this.

The above method adds the benefit of a cleaner file and allows us to specify the concat order for our JS. For those that use a frontend framework like Foundation, this also allows us to selectively pick our JS modules to reduce file size on build.

    {{ $scripts := getJSON "./assets/js/scripts.json" }}
    {{ $.Scratch.Set "jslibs" slice }}
    {{ range $scripts.scripts }}
       {{ $.Scratch.Add "jslibs" (resources.Get . ) }}
    {{ end }}
    {{ $js := .Scratch.Get "jslibs" | resources.Concat "js/combined-scripts.js" | resources.Minify | fingerprint }}
    <script src="{{ $js.Permalink }}"></script>

I package this up into a partial “site-scripts.html” and call it at the foot of baseof.html

{{ partial "common/site-scripts" . }}

… and feed in a JSON file with my required JS files

    {
       "scripts" : [
          "dependencies/jquery/dist/jquery.min.js",
          "dependencies/what-input/dist/what-input.min.js",
          "dependencies/flickity/dist/flickity.pkgd.min.js",
          "dependencies/algoliasearch/dist/algoliasearch.jquery.min.js",
          "dependencies/autocomplete.js/dist/autocomplete.jquery.min.js",
          "dependencies/mixitup/dist/mixitup.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.core.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.util.mediaQuery.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.util.box.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.util.keyboard.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.util.motion.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.util.nest.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.util.triggers.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.abide.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.accordionMenu.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.dropdownMenu.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.responsiveMenu.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.responsiveToggle.min.js",
          "dependencies/foundation-sites/dist/js/plugins/foundation.toggler.min.js",
          "js/init-foundation.js"
       ]
    }

Extra tip for those that are using a package manage like Yarn, NPM etc.

I run a bash script to selectively copy my required dependencies to my assets directory, which is gitignored as the site is built on each push using a CI tool. This mitigates the limitations of referencing resources that live outside of the assets directory without having to resort to symlinks (which can break when using a CI tool).

    #!/usr/bin/env bash

    # Make dependencies directory inside assets and copy dependencies if directory does not exist
    if [ ! -d "assets/dependencies" ]
    then
       mkdir assets/dependencies
       
       # Copy dependencies
       cp -rf node_modules/algoliasearch assets/dependencies/algoliasearch
       cp -rf node_modules/autocomplete.js assets/dependencies/autocomplete.js
       cp -rf node_modules/flickity assets/dependencies/flickity
       cp -rf node_modules/foundation-sites assets/dependencies/foundation-sites
       cp -rf node_modules/jquery assets/dependencies/jquery
       cp -rf node_modules/mixitup assets/dependencies/mixitup
       cp -rf node_modules/what-input assets/dependencies/what-input

       # Rename any CSS files to SCSS
       mv assets/dependencies/flickity/dist/flickity.css assets/dependencies/flickity/dist/flickity.scss

       echo Dependancies copied!
    fi
5 Likes

There is a way around local and remote symlinks. See: Quick Tip for Symbolic Links

Otherwise thank you for your excellent post. I really like the JSON approach and I will check it out.

I am trying to implement this and receive an error like:

at <$.Scratch.Add>: error calling Add: append element type mismatch: expected resource.Resource, got *resource.genericResource

Using Hugo 0.49.1. With Hugo 0.49 I just get error calling Concat: slice []interface {} not supported in concat.. Relates to resources.Concat fails with processed scss file on hugo 0.49 · Issue #5269 · gohugoio/hugo · GitHub I think.

Playing around with the new append syntax shown in tpl/collections: Add collections.Append · gohugoio/hugo@e27fd4c · GitHub

{{- $stylesheets := slice -}}
{{- $libs := slice "bootstrap/css/bootstrap.min.css" "fontawesome/all.min.css" -}}
{{- range $libs -}}
    {{- $stylesheets = $stylesheets | append (resources.Get . ) -}}
{{- end -}}

still causes

executing “partials/include/assets-css.html” at <append (resources.Ge…>: error calling append: append element type mismatch: expected resource.Resource, got *resource.genericResource

Is there something obvious I am missing?

It’s a (stupid) bug. I’m sorry, I fix this asap.

2 Likes

This is awesome - tnx for sharing.

Tipp: You can rename files already with the cp:

cp -f node_modules/modern-normalize/modern-normalize.css assets/dependencies/_modern-normalize.scss

Fixed in

6 Likes