Bundling resources not working

I was reading more about the new features added a few versions ago to see if some could improve the way I was managing CSS/JS and found this topic

I implemented it slightly different from the code posted just because my array of files is in a FrontMatter Parameter:

some-page.en.md

include:
  footer:
    js:
      - /src/js/components/footable.core.min.js
      - /src/js/components/footable.paging.min.js
      - /src/js/components/footable.sorting.min.js
      - /src/js/components/jquery.whensync.min.js

footer.html

{{ $.Scratch.Set "js" slice }}

{{ $.Scratch.Add "js" (resources.Get "/src/js/components/jquery.min.js" ) }}

{{ with $params.include.footer.js }}
  {{ range . }}
    {{ $.Scratch.Add "js" (resources.Get . ) }}
  {{ end }}
{{ end }}

{{ $.Scratch.Add "js" (resources.Get "/src/js/some-code-that-must-go-after-everything.js" ) }}

{{ $js := $.Scratch.Get "js" | resources.Concat "/assets/js/chaotic.js" | minify | fingerprint }}

After the {{ with }} block, if I debug the Scratch I’ve built, as expected, I have an array of Resources:

[Resource(javascript: src/js/components/jquery.min.js) Resource(javascript: src/js/components/footable.core.min.js) Resource(javascript: src/js/components/footable.paging.min.js) Resource(javascript: src/js/components/footable.sorting.min.js) Resource(javascript: src/js/components/jquery.whensync.min.js) Resource(javascript: /src/js/some-code-that-must-go-after-everything.js)]

But when debugging $js or even reading the generated file, all generated resources coming from FrontMatter Parameter added during the iteration are not there. Plus, I have this error:

{0xc04215ddd0 <nil> 0xc04c710290 {{0 0} 0} <nil> {{0 0} 0} { map[]} 0xc04e1c3550}

And, obviously, it doesn’t work

What if you use the new ability to redefine variables and then just redefine $js to an actual slice to Concat those resources? Just spitballing here without source to try locally…

I didn’t get to that part of my readings so I don’t known what you’re talking about (rs) but why this could solve the issue if I’m not re-using the variable? I’m creating it after all Resources needed have been read just like bep did on his “tip”

You need to show the full source of your problematic project. It is impossible for us to judge this by your code listings.

Every time I post something here you say the same old thing and I insist, full source is not possible (and, in fact, it could be even more overwhelming than this).

It’s just a few lines of code, based in your own code you published here, involving only these two files.

A code that runs/fails is always more clear than reading partial code listings. I understand that posting links to source code isn’t always possible, but then understand that I cannot help you. Others may chime in.

1 Like

I really, really try to not be rude but… why did you join the discussion then if you’re unable to read a few lines you wrote yourself, except the little change to use a FrontMatter Parameter.

Every… single… time, almost looks personal.

I read most threads here on the forum, and I often respond to threads even if I don’t understand them fully. I willl leave this thread to others now.

Another information I just tested.

I’ve added the files listed in the FrontMatter Parameter manually, just like I did above with jQuery (and following even more the code posted here) and then it worked, which means that it’s not my fault, my code is perfect as it should be, but the Resources “dynamically” loaded are simply vanishing

Well, if someone reasonable enough is really interested in helping me, even though I believe it’s a Hugo issue since I’ve got the same results in a brand new repository, here’s the link of a temporary repository for it.

Demo page: /some-file
Code: /themes/ananke/layouts/partials/site-footer.html

@magnusthorek Are you trying to create a page-specific, minified, concatenated javascript file or are you looking to write the contents of these files to the page?

If you’re trying to add scripts, you could do something as simple as the following (although I appreciate this isn’t going to give you the concat/minify I think you want and would include you putting specific js files inside /static instead):

{{ with .Params.include.footer.js }}
    {{ range . }}
      <script src="/js/{{.}}"></script>
    {{ end }}
{{ end }}

Or use (better) .RelPermalink.

Actual Solution (Hopefully)

Right now I think your issue is that .Scratch is an interface, so that’s why you’re not able to concatthe way you want to. but I’m deferring to the real Golang gurus on the forum.

I used the sample repo you provided, so hopefully the following gives you an idea of how to fix your issue in your private repo.

The following solution uses resources.FromString, as well as the index function and .Content to get the value inside of Scratch and the text content inside of each js file, respectively. The comment at the beginning of the code block is directly from your example repo. You can find more in the Hugo docs as well:

[EDITED. See solution below]

As an aside…

As I’ve hopefully demonstrated, this is not a Hugo “issue,” but please let me know if you can’t get the above to work and we can try something else.

Famous last words :grin:

Since I’m giving you free advice on your templating, please allow me to give you some free advice on these forums: When the maintainer of a project or another person of the community asks, “Can you please provide a source repo so that I can help you?”, here’s the translation:

For no real reason, I’m going to take my free time to download your project, which you may or may not be profiting from, download it locally, and debug a project that provides me no material benefit. While I want to help, I can’t read your mind, but in the interest of giving you the most comprehensive answer, I’m willing to dig into the code you’ve written, which could take anywhere from a few minutes to a few hours.

If that’s your definition of a “personal” attack or a volunteer not being “reasonable,” I think we will just have to agree to disagree.

Good luck and please let me know if the above solution doesn’t work for you. :smile:

4 Likes

A file. I would be adding files common to all pages before (jQuery) and after polyfills, maybe) the individual ones, iterated.

One thing that crossed my mind would be that the file created was getting overwritten by some other Page processed after the one with the Parameter, resulting in a file with the same name passed to resources.Concat.

I thought about this because while struggling on my PC I made some changes to the working part (those manually added to the Scratch) and the file wasn’t updated (I had to shutdown the server, delete the file and start it again)

But IMHO I think that different contents should generate different hashes and thus different files. I’ll test further tomorrow but it wouldn’t explain the scenario of this repository that has only one Page - even though the file didn’t even render at all in this demo to me.

@rdwatters I’ll check that tomorrow

Okay, got it. It’s because you were originally assigning js to a slice. This should now work. Note that I’ve removed the solution above as well. This now also settles your issue with getting different filenames:

<!-- MODIFIED FOR RESOURCES ISSUE DEMONSTRATION -->

{{ $.Scratch.Set "js" "" }}

{{ $.Scratch.Add "js" (( resources.Get "/src/js/components/jquery.min.js" ).Content) }}

{{ with .Params.include.footer.js }}
  {{ range . }}
    {{ $.Scratch.Add "js" ((resources.Get . ).Content) }}
  {{ end }}
{{ end }}

{{ $.Scratch.Add "js" ( (resources.Get "/src/js/some-code-that-must-go-after-everything.js" ).Content )}}

{{ $js := $.Scratch.Get "js" }}
<!--Give your js files names based on the page they are used in-->
{{ $scriptPath := printf "js/%s.js" ( .Name | urlize ) }}
{{ $scriptFromString := $js | resources.FromString $scriptPath | minify | fingerprint }}
<script src="{{$scriptFromString.RelPermalink}}"></script>
1 Like

Please explain how the above does not meet all of these needs you’re still bringing up as far as “bugs” or “issues.”

BTW, here is the source of what I’ve been running locally, which doesn’t seem to have any issues, AFAICT:

1 Like

Right, that worked as expected, all files added before and after any that could’ve been listed under the FrontMatter Parameter structure are all there, compiled, minified and, more important, working. However, that yielded me with one JS file (and CSS, as I’m applying for them as well) for every single page in the site, 643 additional files, ~80 MB, severely slowing down the compiling time.

I tried to define a single name instead of using .Name as you used but I fell in the same “bug” — which ended up not being actually a bug, just something I thought thoroughly only when I calmed myself after the… “situation” in here.

Would it be possible to not have that many duplications? Maybe comparing the hash of the newly generated file before it is effectively written on the disk? The solution for this I have right away, already working, would be to have (another) dimension in the array with an additional entry for the filename, defaulting in the template to some fixed string if missing (example below) but I would like to know if there’s a smarter way.

file.md

include:
  footer:
    js:
      output: "desired-output-file.js"
      files:
        - /src/js/components/footable.core.min.js
        - /src/js/components/footable.paging.min.js

footer.html

{{ $filename := ( replaceRE "(\\.?(min))?(\\.?(js))?" "" ($params.include.footer.js.output | default "site") ) }}
{{ $path := printf "assets/js/%s.js" ( $filename ) }}
{{ $js := ($.Scratch.Get "js") | resources.FromString $path | minify | fingerprint }}

With this, after compiling, I have, as of today, only two files, desired-output-file.min.HASH.js and site.min.js

wow…! what a great answer… I truly envy your levelheadedness (what a word I sneaked-in, not bad for a non-native english speaker :stuck_out_tongue: )

You get my respect :+1:

2 Likes

Absolutely. I was just providing a solution to what I thought was your particular need, but generally, I think creating different scripts for every single pages is more than just a bit insane. I get the feeling you’re trying to do a sorta code-splitting-a-la-webpack deal here maybe?

Methinks the use of replaceRE is overkill here. When you pipe things through minify, it adds .min to the output filename.

If you could tell me a bit more about your use case, that might help as well. For example, there are ways to include scripts based on shortcode as well (i.e. with .HasShortcode) and lots of additional ways to be flexible.

Since my Hugo projects are already static sites and therefore super speedy, I typically just include one app.min.js that handles all the global interactions and then, as needed, add additional scripts in frontmatter (as you’re doing) or based on presence of a shortcode inside a page’s content. One easy way to not create so many files is the original solution I showed above, where you just keep the additional scripts themselves inside static and then just range through them on a page-by-page basis. I get why you would want to concat and minify everything, but truth be told, adding an additional, say, 2k <script> source for one page is going to do very little harm (I would say even less than negligible) to your performance.

If you find that these additional scripts are often repeated across multiple pages/templates, why not create a partial that checks for a conditional inside the frontmatter?

footerjs: true

Then inside your layouts/partials/scripts.html (or whatever partial you’re using to add script tags just above </body> inside your layouts):

<!--Your additional script-related templating here...-->

{{ with .Params.footerjs }}
<script src="whatever-js-file-you-want-here.js"></script>
{{ end }}

Hmmm… Parameter by Parameter I guess I’ll stick to the approach of a manually set filename.

That overkill is because I don’t know if someone else will, eventually, maintain the project and would prefer to name as if the file was created manually, even though I’m documenting it exhaustively.

Anyway, thank you very much for your time and… @bep, I’m truly sorry for unloading that shitload on you. It wasn’t fair nor mature of me.

I won’t quote each paragraph indidually to not obfuscate the brilliant answer here.

1 Like