Why is plone-compile-resources creating .compiled versions of individual javascript IResources

I thought I got some basic understanding of the 'grand scheme' of the resource registry in Plone 5 in the last 12-18 months. I even dare to write some introduction tutorials for one of our customers, but went down a small rabbit hole last week when I tried to add Highcharts 8 to a Plone 5.2 site.

(Skip the 'digging myself in' part below for some nice workaround in the second part)


[1] Many javascript projects nowadays distribute their work in AMD/loading aware setup: if you load the js files in a simple page or playground it works like Javascript from 2006, but the files are wrapped in smart code: when it detects a require.js setup the only way is the require way. Highcharts has 4-15 different files you can load to compose your required functionality. (highcharts.js, highcharts-more.js, exporting.js, export-data.js and accessibilty.js in my case).

My first hunch was to create IResourceRegistry entries for each of these files from the Highcharts package. I had put the files from highcharts in a subdirectory in my add'on and registered/published them in the url namespace using zcml to ++resource++my.highcharts.addon/

After that I created an IBundleRegistry with those I resources and hope/expect that everything gets compiled into a ++plone++my.highcharts.addon/highcharts.compiled.min.js file (where I registered "my.highcharts.addon as a ++plone++ resource in my add'on package for highcharts.

bin/plone-compile-resources -s myPlone -b highcharts should then create/compile my bundle to filesystem and/or should write it in a subdirectory of portal_resources in the ZODB when I develop the bundle from the resource registry control panel.

First try seemed to work, but upon closer inspection and when I started changing parameters in the resource registry everything broke down real quick: plone-compile-resources was creating new javascript files in my add'on's mappped ++reource++ directory for the individual IResource. So highcharts.js got a highcharts.compiled.js next to it, a highcharts-more.compiled.js. was created, etc.

Why is this happening? I expect the compilation phase to modify something at the paths configured in the bundle for the jscompilation and csscompilation variables, but never touch individual IReources: they should be served 1:1 when I enable development mode in the resource registry and develop individual bundles's js and otherwise they should be included in a compiled/minified mybundle.js .

I have many more smaller questions and maybe the first answers I'll get are *"it depends on your settings for compile=True etc in the IBundleRegistry configuration". IMHO IResources should never be manipulated/compiled or whatever into new files, independent of Bundle's settings where they get attache to as a resource. So I'm not understanding something here.


[2] When I was out of the rabbit hole: the tricks I used.

In this case my naive thought of bundling the files Highcharts wants in a single js was flawed anyway, because the 'correct' way of getting highcharts in a html-based page is using require.js its packages functionality.

(I had similar issues last year with loading a plugin for jQuery. One has to use require(['jquery','path-to-myplugin'] and then some setup code to get a handle to the 'master' jquery to register your plugin onto, if you just load the plugin.js code directly it's async roulettte if it can find $. )

Trick 1: you can stack multiple require.config calls and it will update the browser site require.js registry, you don't HAVE to have your resources in the config.js which Plone serves right after require.js gets loaded on a page.

So I now have one .js file with this top part:

require.config({
    packages: [{
        name: 'highcharts',
        main: 'highcharts'
    }],
    paths: {
       // Change this to your server if you do not wish to use our CDN.
        'highcharts': '++resource++highcharts/',
        'domReady': '++resource++my.highcharts.addon/domReady'
    }
  });

It is loaded from one IBundleRegistry entry in the Resource registry and adds highcharts and a useful require.js plugin domReady. (I first tried to register the single domReady plugin in Plone's config.js for require.js and even that was a fail.).

Trick 2: you can inspect the require.js configuration after a page has loaded in the the developer tools of your browser by calling requirejs.s.contexts._.config from the console.

With this in place I can add my custom javascript after the extra require.config part to activate higcharts on links to 'graph' content items in the main Text on a Document like this:

require([
  "jquery"
  "domReady",
  "highcharts",
  "highcharts/modules/exporting",
  "highcharts/modules/export-data",
  "highcharts/modules/accessibility",
], function ($, domReady, Highcharts) {
  domReady(function () { 
    $("div[class*='graphlink'] a").each(function (index) {
    // lots of custom code to fetch the chart configuration

And everything is finally loading without errors, undefined's or whatevers, independento of loading errors. Mission accomplished with work arounds, using require.js but skipping the resource registry.


[3] Rant:

To get out of descent into the rabbit hole I started modifying variables in the resource registry bundle and very quickly broke all the javascript in my Plone site. There were in hindsight misleading javascript errors in the console saying 'Highcharts cannot be loaded in javascript-file.compiled.js' where javascript-compiled.js' was nowhere to be found on the Network tab in the browser of loaded js files. Clicking on details of the error showed a 'page cannot be loaded. Huh?

Backtracking I think my custom javascript mess got into the bundle and then 'combined' in the plone.default.js production bundle. The browser downloaded a generated .map for my bundle and looked up and showed the never loaded source javascript file from there to help me push me furhter down.

I got a bit lost in the explanation here, but can you just register the directory and then load everything with requirejs ?

Something like:

  <browser:resourceDirectory
  name="my.addon"
  directory="static"
  />

Then put your js in there

And in your template:

<script>require([
'jquery',
'++resource++my.addon/some.js',
 '++resource++my.addon/someother.js',
], function(Base) {
  $(function(){
   some functions here
  }); 
});

PS: It might be that '.js' should be skipped, so it should be

'++resource++my.addon/some',

Sorry about that, it was a bit of a braindump yesterday after wrestling with finding a correct setup spanning multiple days. If I had documented more detail it would only have become longer ;-(

require([
'jquery',
'++resource++my.addon/some.js',
 '++resource++my.addon/someother.js',
], function(Base) {

I think the problem with adding individual resources like this, is that they are only available inside the function of this require. If you need the resource more than once, require will have to fetch and (or in case of a working browser cache still) initiialize that module again and again for each use in a require.

If you add the resource to requires, config, it's downloaded, parsed and available to all subsequent require() and define()'s.

Meanwhile I've found one cause why the whole resource registry output and your Plone site breaks if you add a missing bundle resource in its bundle jscompilation field. The try/excepts only seem to have been created for resources coming from ++plone++, missing resources from ++resource++ throw different Exceptions when they cannot be found.

Plone Foundation Code of Conduct