website icon indicating copy to clipboard operation
website copied to clipboard

Build JS with Hugo instead of Webpack

Open baltpeter opened this issue 2 months ago • 16 comments

We are currently building website with Webpack 4, which is very outdated and requires Node 14, which is ancient. Migration to Webpack 5 is somewhere between non-trivial and hard. This problem has thwarted efforts to upgrade to newer versions of Node for quite a few years now (cf. #986, #1076, #1147, #1205).

Meanwhile, we have had plans to switch away from Webpack for even longer. In #701, @zner0L attempted to switch to Hugo's js.Build based on esbuild. That would mainly have two advantages: The configuration is much easier and more ergonomic than Webpack. And builds are much faster.

The #701 PR is now very outdated and has even been created before our major refactoring and TS conversion in 2022. As such, I don't think it makes sense to build on that PR. It will be much easier to start anew and copy still relevant bits from the PR.

So, I guess that's what I'm going to attempt now. :D

baltpeter avatar Oct 24 '25 13:10 baltpeter

I'll start by documenting what (non-obvious things) Webpack currently does for us and thus what we'll need to replace/decide to live without:

  • [x] Chunk splitting (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.common.js#L36-L59)
  • [x] Adding a banner to emitted bundle files (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.common.js#L104-L120)
  • [x] Setting window.CODE_VERSION to the version from package.json for the error handler (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.common.js#L122-L125)
    • Though this has been 1.0.0 since day one, so it currently isn't exactly useful. A commit hash would be, though.
  • [x] Doing random import resolving (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.common.js#L131-L132)
    • This is a horrible anti-pattern and was a big mistake because we didn't really understand what it does back in the day. This has already been fixed in all files that have been migrated to TS. We definitely don't want to port this over.
  • [x] Tooling for measuring the bundling speed (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.prod.js#L4-L8)
    • We have occasionally used this in the past to optimize bundling speed. Might be less relevant in the future given how much faster esbuild is.
  • [x] Source maps (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.prod.js#L12-L13)
  • [x] Bundling a web worker (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.common.js#L69-L82)
  • [x] Our custom translations loader that:
    • [x] Backfills missing translations from English (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/scripts/webpack-i18n-loader.js#L8-L12)
    • [x] Replaces macros in the translations (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/scripts/webpack-i18n-loader.js#L13-L21)
    • [x] Extracts and prepares the translations for Hugo and saves them in the correct location (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/scripts/webpack-i18n-loader.js#L23-L54)
    • [x] Removes translations from the bundles that aren't needed in the browser (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/scripts/webpack-i18n-loader.js#L55-L57)
    • [x] Prepares the special requests translations files that contain all languages (https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/scripts/webpack-i18n-loader.js#L64-L82)

baltpeter avatar Oct 24 '25 13:10 baltpeter

And, just to document it for future comparisons, here's how long yarn build currently takes and what it outputs:

❯ yarn build
yarn run v1.22.18
$ cross-env BABEL_ENV=production; webpack --mode=production --config webpack.prod.js
Browserslist: caniuse-lite is outdated. Please run:
  npx browserslist@latest --update-db
  Why you should do it regularly: https://github.com/browserslist/browserslist#browsers-data-updating
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db

Why you should do it regularly:
https://github.com/browserslist/browserslist#browsers-data-updating
The exported identifier "undefined" is not declared in Babel's scope tracker
as a JavaScript value binding, and "@babel/plugin-transform-typescript"
never encountered it as a TypeScript type declaration.
It will be treated as a JavaScript value.

This problem is likely caused by another plugin injecting
"undefined" without registering it in the scope tracker. If you are the author
 of that plugin, please use "scope.registerDeclaration(declarationPath)".
Hash: 4a694ece95fd57248927
Version: webpack 4.44.1
Time: 25821ms
Built at: 10/24/2025 3:52:29 PM
                                   Asset      Size  Chunks                          Chunk Names
                         ../i18n/cs.json  19.8 KiB          [emitted]               
                         ../i18n/de.json  17.1 KiB          [emitted]               
                         ../i18n/en.json  15.5 KiB          [emitted]               
                         ../i18n/es.json  20.7 KiB          [emitted]               
                         ../i18n/fr.json  21.6 KiB          [emitted]               
                         ../i18n/hr.json  19.2 KiB          [emitted]               
                         ../i18n/nl.json  19.8 KiB          [emitted]               
                         ../i18n/pt.json  20.6 KiB          [emitted]               
                     js/20.bundle.gen.js  54.9 KiB      20  [emitted]               
                 js/20.bundle.gen.js.map  56.2 KiB      20  [emitted] [dev]         
             js/act-widget.bundle.gen.js  52.1 KiB       3  [emitted]               act-widget
         js/act-widget.bundle.gen.js.map  53.8 KiB       3  [emitted] [dev]         act-widget
                    js/app.bundle.gen.js   288 KiB   4, 17  [emitted]        [big]  app
                js/app.bundle.gen.js.map   294 KiB   4, 17  [emitted] [dev]         app
    js/bank-transfer-codes.bundle.gen.js  30.6 KiB       5  [emitted]               bank-transfer-codes
js/bank-transfer-codes.bundle.gen.js.map  31.3 KiB       5  [emitted] [dev]         bank-transfer-codes
                js/commons.bundle.gen.js  1.35 MiB       0  [emitted]        [big]  commons
            js/commons.bundle.gen.js.map  1.38 MiB       0  [emitted] [dev]         commons
           js/company-list.bundle.gen.js  15.4 KiB       6  [emitted]               company-list
       js/company-list.bundle.gen.js.map    16 KiB       6  [emitted] [dev]         company-list
        js/donation-widget.bundle.gen.js  9.93 KiB       7  [emitted]               donation-widget
    js/donation-widget.bundle.gen.js.map  10.7 KiB       7  [emitted] [dev]         donation-widget
          js/error-handler.bundle.gen.js  7.17 KiB       8  [emitted]               error-handler
      js/error-handler.bundle.gen.js.map  7.66 KiB       8  [emitted] [dev]         error-handler
                js/general.bundle.gen.js  17.9 KiB       9  [emitted]               general
            js/general.bundle.gen.js.map  18.7 KiB       9  [emitted] [dev]         general
              js/generator.bundle.gen.js  73.2 KiB      10  [emitted]               generator
          js/generator.bundle.gen.js.map  75.8 KiB      10  [emitted] [dev]         generator
                   js/home.bundle.gen.js  3.68 KiB      11  [emitted]               home
               js/home.bundle.gen.js.map  4.03 KiB      11  [emitted] [dev]         home
       js/id-data-controls.bundle.gen.js  45.9 KiB      12  [emitted]               id-data-controls
   js/id-data-controls.bundle.gen.js.map  47.4 KiB      12  [emitted] [dev]         id-data-controls
    js/misconduct-reporter.bundle.gen.js   521 KiB      13  [emitted]        [big]  misconduct-reporter
js/misconduct-reporter.bundle.gen.js.map   526 KiB      13  [emitted] [dev]         misconduct-reporter
            js/my-requests.bundle.gen.js  16.4 KiB      14  [emitted]               my-requests
        js/my-requests.bundle.gen.js.map  17.3 KiB      14  [emitted] [dev]         my-requests
             js/pdf.worker.worker.gen.js  1.86 MiB          [emitted]        [big]  
         js/pdf.worker.worker.gen.js.map  1.89 MiB          [emitted] [dev]         
       js/privacy-controls.bundle.gen.js  8.52 KiB      15  [emitted]               privacy-controls
   js/privacy-controls.bundle.gen.js.map  8.97 KiB      15  [emitted] [dev]         privacy-controls
                       js/runtime.gen.js  3.18 KiB       1  [emitted]               runtime
                   js/runtime.gen.js.map  3.42 KiB       1  [emitted] [dev]         runtime
           js/suggest-edit.bundle.gen.js   166 KiB      16  [emitted]               suggest-edit
       js/suggest-edit.bundle.gen.js.map   182 KiB      16  [emitted] [dev]         suggest-edit
             js/sva-finder.bundle.gen.js  13.5 KiB      17  [emitted]               sva-finder
         js/sva-finder.bundle.gen.js.map  14.4 KiB      17  [emitted] [dev]         sva-finder
         js/test-interface.bundle.gen.js  1.57 KiB      18  [emitted]               test-interface
     js/test-interface.bundle.gen.js.map  1.79 KiB      18  [emitted] [dev]         test-interface
               js/translations-cs.gen.js    57 KiB          [emitted]               
               js/translations-de.gen.js  61.2 KiB          [emitted]               
     js/translations-dummy.bundle.gen.js   475 KiB      19  [emitted]        [big]  translations-dummy
 js/translations-dummy.bundle.gen.js.map   505 KiB      19  [emitted] [dev]         translations-dummy
               js/translations-en.gen.js  55.8 KiB          [emitted]               
               js/translations-es.gen.js  60.8 KiB          [emitted]               
               js/translations-fr.gen.js  65.3 KiB          [emitted]               
               js/translations-hr.gen.js    56 KiB          [emitted]               
               js/translations-nl.gen.js  57.3 KiB          [emitted]               
               js/translations-pt.gen.js  59.6 KiB          [emitted]               
         js/translations-requests.gen.js    13 KiB          [emitted]               
                js/vendors.bundle.gen.js  87.8 KiB       2  [emitted]               vendors
            js/vendors.bundle.gen.js.map  89.9 KiB       2  [emitted] [dev]         vendors
Entrypoint error-handler [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/error-handler.bundle.gen.js js/error-handler.bundle.gen.js.map
Entrypoint general [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/general.bundle.gen.js js/general.bundle.gen.js.map
Entrypoint home [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/home.bundle.gen.js js/home.bundle.gen.js.map
Entrypoint generator [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/vendors.bundle.gen.js js/vendors.bundle.gen.js.map js/generator.bundle.gen.js js/generator.bundle.gen.js.map
Entrypoint app [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/vendors.bundle.gen.js js/vendors.bundle.gen.js.map js/app.bundle.gen.js js/app.bundle.gen.js.map
Entrypoint company-list [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/vendors.bundle.gen.js js/vendors.bundle.gen.js.map js/company-list.bundle.gen.js js/company-list.bundle.gen.js.map
Entrypoint my-requests [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/my-requests.bundle.gen.js js/my-requests.bundle.gen.js.map
Entrypoint privacy-controls [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/privacy-controls.bundle.gen.js js/privacy-controls.bundle.gen.js.map
Entrypoint suggest-edit [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/vendors.bundle.gen.js js/vendors.bundle.gen.js.map js/suggest-edit.bundle.gen.js js/suggest-edit.bundle.gen.js.map
Entrypoint id-data-controls [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/vendors.bundle.gen.js js/vendors.bundle.gen.js.map js/id-data-controls.bundle.gen.js js/id-data-controls.bundle.gen.js.map
Entrypoint sva-finder [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/sva-finder.bundle.gen.js js/sva-finder.bundle.gen.js.map
Entrypoint act-widget [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/vendors.bundle.gen.js js/vendors.bundle.gen.js.map js/act-widget.bundle.gen.js js/act-widget.bundle.gen.js.map
Entrypoint donation-widget [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/donation-widget.bundle.gen.js js/donation-widget.bundle.gen.js.map
Entrypoint misconduct-reporter [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/misconduct-reporter.bundle.gen.js js/misconduct-reporter.bundle.gen.js.map
Entrypoint test-interface [big] = js/runtime.gen.js js/runtime.gen.js.map js/commons.bundle.gen.js js/commons.bundle.gen.js.map js/test-interface.bundle.gen.js js/test-interface.bundle.gen.js.map
Entrypoint translations-dummy [big] = js/runtime.gen.js js/runtime.gen.js.map js/translations-dummy.bundle.gen.js js/translations-dummy.bundle.gen.js.map
 [91] ./src/Components/SvaFinder.tsx 10.9 KiB {4} {17} [built]
[171] ./src/error-handler.js 12.7 KiB {8} [built]
[173] ./src/general.tsx 15.7 KiB {9} [built]
[191] ./src/home.tsx 1.36 KiB {11} [built]
[192] ./src/generator.tsx 1.86 KiB {10} [built]
[243] ./src/app.tsx 692 bytes {4} [built]
[257] ./src/company-list.tsx 3.34 KiB {6} [built]
[262] ./src/my-requests.tsx 1.51 KiB {14} [built]
[263] ./src/privacy-controls.tsx 5.31 KiB {15} [built]
[264] ./src/suggest-edit.js 14.4 KiB {16} [built]
[266] ./src/id-data-controls.tsx 10.5 KiB {12} [built]
[267] ./src/Components/ActWidget.tsx 2.6 KiB {3} [built]
[268] ./src/Components/DonationWidget.tsx 14.7 KiB {7} [built]
[269] ./src/Components/MisconductReporter.tsx 4.94 KiB {13} [built]
[270] ./src/test-interface.tsx 1.02 KiB {18} [built]
    + 423 hidden modules

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: 
  js/pdf.worker.worker.gen.js (1.86 MiB)
  js/commons.bundle.gen.js (1.35 MiB)
  js/app.bundle.gen.js (288 KiB)
  js/misconduct-reporter.bundle.gen.js (521 KiB)
  js/translations-dummy.bundle.gen.js (475 KiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  error-handler (1.36 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/error-handler.bundle.gen.js
  general (1.37 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/general.bundle.gen.js
  home (1.36 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/home.bundle.gen.js
  generator (1.51 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/vendors.bundle.gen.js
      js/generator.bundle.gen.js
  app (1.72 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/vendors.bundle.gen.js
      js/app.bundle.gen.js
  company-list (1.46 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/vendors.bundle.gen.js
      js/company-list.bundle.gen.js
  my-requests (1.37 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/my-requests.bundle.gen.js
  privacy-controls (1.36 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/privacy-controls.bundle.gen.js
  suggest-edit (1.6 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/vendors.bundle.gen.js
      js/suggest-edit.bundle.gen.js
  id-data-controls (1.48 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/vendors.bundle.gen.js
      js/id-data-controls.bundle.gen.js
  sva-finder (1.37 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/sva-finder.bundle.gen.js
  act-widget (1.49 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/vendors.bundle.gen.js
      js/act-widget.bundle.gen.js
  donation-widget (1.36 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/donation-widget.bundle.gen.js
  misconduct-reporter (1.86 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/misconduct-reporter.bundle.gen.js
  test-interface (1.36 MiB)
      js/runtime.gen.js
      js/commons.bundle.gen.js
      js/test-interface.bundle.gen.js
  translations-dummy (478 KiB)
      js/runtime.gen.js
      js/translations-dummy.bundle.gen.js

Child worker-loader node_modules/babel-loader/lib/index.js!node_modules/babel-loader/lib/index.js!src/Utility/pdf.worker.ts:
                              Asset      Size  Chunks                          Chunk Names
        js/pdf.worker.worker.gen.js  1.86 MiB       0  [emitted]        [big]  pdf.worker
    js/pdf.worker.worker.gen.js.map  1.89 MiB       0  [emitted] [dev]         pdf.worker
    Entrypoint pdf.worker = js/pdf.worker.worker.gen.js js/pdf.worker.worker.gen.js.map
    [0] ../letter-generator/dist/utility.js 2.24 KiB {0} [built]
    [1] ../letter-generator/dist/index.js 1.96 KiB {0} [built]
    [2] ./src/Utility/fonts.json 641 KiB {0} [built]
    [3] ./node_modules/babel-loader/lib!./node_modules/babel-loader/lib!./src/Utility/pdf.worker.ts 772 bytes {0} [built]
    [4] ../letter-generator/dist/Template.js 2.21 KiB {0} [built]
    [5] ../letter-generator/dist/Letter.js 3.27 KiB {0} [built]
    [6] ../letter-generator/dist/layouts/din-5008-a.js 1.83 KiB {0} [built]
    [7] ../letter-generator/dist/PdfRenderer.js 1.04 KiB {0} [built]
        + 1 hidden module
Done in 26.66s.

baltpeter avatar Oct 24 '25 13:10 baltpeter

Thank you for your investigations!

Just my 2 cents, feel free to ignore them:

  • The banner doesn't seem to be too useful.

  • I think we can survive without speed measurements and code version. We do not deploy different code versions on prod anyways :D

  • the translation loader feels like something that could be ported to deploy.sh pipeline. :D Maybe except the backfil.

mal-tee avatar Oct 24 '25 15:10 mal-tee

  • the translation loader feels like something that could be ported to deploy.sh pipeline. :D Maybe except the backfil.

Unfortunately, for development we do also need a watch mode for the translations. Having to run deploy.sh every time you change a translation would be very annoying.

baltpeter avatar Oct 25 '25 16:10 baltpeter

I have opened a PR with my current state: https://github.com/datenanfragen/website/pull/1212

So far, I have already migrated over all the JS bundles. I was able to borrow a lot from #701. But a bunch of stuff has gotten easier since then:

  • We don't need to move all code to assets anymore. We can now just mount the src directory there.
  • Thanks to https://github.com/gohugoio/hugo/pull/12119, we don't need to import the JSX stuff in every file anymore.

baltpeter avatar Oct 25 '25 16:10 baltpeter

For some reason, changes made using cy.proceedingsStore() aren't immediately reflected on the website anymore, only after a reload.

I don't really have an idea on why that might be or how to fix it right now, so I'll add reloads to the tests for now, but we might want to investigate this in the future.

baltpeter avatar Oct 26 '25 10:10 baltpeter

Turns out that there is also a timing component. If you reload immediately after adding a proceeding, sometimes it won't be there after the reload. sigh

baltpeter avatar Oct 27 '25 13:10 baltpeter

The reason appears to be our previous chunking/code splitting:

https://github.com/datenanfragen/website/blob/d67464b029ad1dd0234876fcd13349515a7b2e06/webpack.common.js#L34-L60

Due to that, zustand itself was loaded once through vendors.bundle.gen.js and the store was created once through commons.bundle.gen.js. Both the page-specific bundle (e.g. my-requests.bundle.gen.js) and test-interface.bundle.gen.js then used those.

Now, there is no code sharing between the bundles anymore and both the page-specific bundle (e.g. my-requests.bundle.gen.js) and test-interface.bundle.gen.js include their own copy of zustand and create their own instance of the store.

baltpeter avatar Oct 27 '25 13:10 baltpeter

Figuring out how to do code splitting using Hugo's new-ish js.Batch wasn't exactly intuitive (the docs could use some love), but I have got it working now and even better and more granular than it was in Webpack. One major change: esbuild only supports code splitting in ESM, so that's what we're using now. Personally, I like that and ESM support in browsers is older than our project.

Implementing this did indeed fix the problem with the test interface as expected. At least locally, I also only get one test failure now and that looks like Cypress flake rather than an actual problem.

baltpeter avatar Oct 27 '25 18:10 baltpeter

Great. Now, apparently hugo server isn't working in CI anymore.

baltpeter avatar Oct 27 '25 18:10 baltpeter

The problem seems to be that Hugo opens the port immediately after hugo server is started but before the site is built and actually ready.

In this PR, I've upgraded to Hugo 0.140.0, so I assumed that the change may have occurred in that release. From my testing, this behaviour didn't happen in Hugo 0.88.1, which we were using until recently. However, curiously, it does also happen in Hugo 0.139.0, which we adopted in #1202.

https://github.com/user-attachments/assets/d4c50267-4a3e-4a4b-9d75-f3cbb7ae548c

That would imply that we should have run into this problem earlier, but we haven't. I don't know why. My guess is that before, Cypress' timeout happened to just be long enough for Hugo to finish building. But since Hugo now also builds the JS, the build takes longer and might exceed the timeout?

baltpeter avatar Oct 29 '25 10:10 baltpeter

yarn wait-on "http://localhost:1314/generator/" instead of yarn wait-on tcp:1314 should solve that.

baltpeter avatar Oct 29 '25 10:10 baltpeter

Next problem: We intermittently get this error when building:

ERROR render of "/root/project/content/de/blog/gdpr-territorial-scope/index.md" failed: "/root/project/layouts/_default/baseof.html:106:7": execute of template failed: template: _default/single.html:106:7: executing "_default/single.html" at <partial "scripts" .>: error calling partial: "/root/project/layouts/partials/scripts.html:163:14": execute of template failed: template: partials/scripts.html:163:14: executing "partials/scripts.html" at <$batch.Build>: error calling Build: interface conversion: interface is nil, not resources.targetPathProvider
ERROR render of "/root/project/content/de/act/honey/index.md" failed: "/root/project/layouts/_default/baseof.html:106:7": execute of template failed: template: _default/single.html:106:7: executing "_default/single.html" at <partial "scripts" .>: error calling partial: "/root/project/layouts/partials/scripts.html:163:14": execute of template failed: template: partials/scripts.html:163:14: executing "partials/scripts.html" at <$batch.Build>: error calling Build: interface conversion: interface is nil, not resources.targetPathProvider
ERROR render of "/root/project/content/de/act/deutsche-wohnen/index.md" failed: "/root/project/layouts/_default/baseof.html:106:7": execute of template failed: template: _default/single.html:106:7: executing "_default/single.html" at <partial "scripts" .>: error calling partial: "/root/project/layouts/partials/scripts.html:163:14": execute of template failed: template: partials/scripts.html:163:14: executing "partials/scripts.html" at <$batch.Build>: error calling Build: interface conversion: interface is nil, not resources.targetPathProvider
ERROR render of "/root/project/content/de/blog/datenloeschung-bei-werbetreibenden/index.md" failed: "/root/project/layouts/_default/baseof.html:106:7": execute of template failed: template: _default/single.html:106:7: executing "_default/single.html" at <partial "scripts" .>: error calling partial: "/root/project/layouts/partials/scripts.html:163:14": execute of template failed: template: partials/scripts.html:163:14: executing "partials/scripts.html" at <$batch.Build>: error calling Build: interface conversion: interface is nil, not resources.targetPathProvider
Built in 19593 ms
Error: error building site: render: failed to render pages: render of "/root/project/content/de/blog/v2-release/index.md" failed: "/root/project/layouts/_default/baseof.html:106:7": execute of template failed: template: _default/single.html:106:7: executing "_default/single.html" at <partial "scripts" .>: error calling partial: "/root/project/layouts/partials/scripts.html:163:14": execute of template failed: template: partials/scripts.html:163:14: executing "partials/scripts.html" at <$batch.Build>: error calling Build: interface conversion: interface is nil, not resources.targetPathProvider

That seems like a Hugo bug to me (especially since it is only intermittent and the code usually works fine). The obvious test would be to upgrade to a Hugo release that is less than a year old but as @mal-tee tested in #1202, we have a bit of a circular dependency here. :|

baltpeter avatar Oct 29 '25 11:10 baltpeter

Because of that, I guess for now I'll continue working on the missing elements and only test locally and ignore the CI failures.

Once we don't need Webpack anymore, upgrading Node, CI images, and Hugo should be a lot easier and we can tackle this problem then.

baltpeter avatar Oct 29 '25 11:10 baltpeter

Given how much has happened in Hugo since we last tried (especially Hugo mounts), I was sort of hopeful that we might be able to have Hugo handle our i18n-loader after all. However, alas, as far as I can tell, it still isn't possible, so I wrote a small building and watching script.

One improvement, though: We don't need to do the "wrapping in { "other": "<translation>" } dance anymore for Hugo.

baltpeter avatar Nov 03 '25 13:11 baltpeter

With that, we are pretty much done. Here's what's left:

Adding a banner to emitted bundle files

Esbuild does support this feature but as far as no way in Hugo to pass the option to esbuild. If we wanted this, we'd need to raise a PR in Hugo. But I agree with @mal-tee that there really is no reason to have this. Iirc, way back when, I was just going through all the Webpack options and thought it was funny to have this. :D

Setting window.CODE_VERSION to the version from package.json for the error handler

As I said, in its current form, this is entirely useless, so I have just removed it altogether for the time being.

If we wanted to make this useful, Hugo can extract current commit hash (or other Git info). However, I'm wary of doing something like this as I would assume that it would increase build times quite drastically.

Tooling for measuring the bundling speed

I agree with @mal-tee that we don't really need this. I haven't seen an option to implement this, either.

baltpeter avatar Nov 03 '25 14:11 baltpeter