I bet you would get tons more of supporters, if bundler logics was documented for developers. Please, don't just tell how cool it is but rather provide a technical description of its work. I've looked at the code and there are too few comments - it doesn't help either. Open source is only good if there is documentation.
Also, I see a lot of additional requests per page and it should be clear if they are needed when evaluating this module. Without deep understanding of the module I can't do that.

Comments

mikeytown2’s picture

This will require feedback from you in terms of what code needs more explanation. The only function that doesn't have any inline comments in the bundler module is advagg_bundler_analysis(); so I'll work on that, but I'm going to need your input on the rest of the code base. The clearer advagg's code/documentation is the better; and thank you for your interest in my work.

Peter Bowey’s picture

@mikeytown2

+1 on this concept!

I would be most interested in advagg's bundler logic!

Perhaps we could run advagg through Doxygen -> http://www.doxygen.org
and see how 'documented' it looks :)

mikeytown2’s picture

Status: Active » Needs review
StatusFileSize
new4.98 KB

First step for better documentation. Its getting late here, so I didn't end up better documenting how advagg_bundler_merge() works.

crea’s picture

I'm mostly interested in logics to decide which file to put in which bundle

Peter Bowey’s picture

Refer #3

Many thanks, that is very helpful!

mikeytown2’s picture

@crea
80% of that logic happens in the query. The other 20% deals with keeping the execution order of the CSS/JS the same and merging small bundles together in order to hit the desired # of bundles. You need to be more specific in what you want to see in terms of a patch or document. The bundler logic has gone through a couple iterations; the file to bundle logic has been simplified, and the execution order and merging logic has gotten more complex over time. One thing you have to keep in mind is I designed the 2 database tables with bundling in mind, that is why SQL does the selection logic for me.

crea’s picture

Ok, I'll try to grok it with the added comments. Thanks!

mikeytown2’s picture

Status: Needs review » Fixed
StatusFileSize
new7.45 KB

I've committed this patch; re-open if you need more explanation on how this works.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

donquixote’s picture

Status: Closed (fixed) » Needs work

Hi,
could you just make a blog post or sth, to describe the general idea behind the bundler logic?
I am sure I could figure this stuff out by looking at the code and comments, but.. a dedicated article would be so much nicer :)
You don't need to dive into the exact code details, just the general ideas.

Some things I ask myself:
- Does advagg decide itself which files go into which bundle?
- Can the same bundle contain different files, depending what page you are on, which user role you have, etc?
- If I configure the bundler in the wrong way, will this cause a broken site? (due to missing css etc)
- Can it happen that a piece of css is wrongly included on a page, because it happens to be in the bundle that was created for another page?
- Does the bundler preserve order of css files? (sounds difficult to me)

I do have some ideas how I would program such a thing, and I wonder if you are doing it the same way or differently.

mikeytown2’s picture

Issue summary: View changes
Status: Needs work » Fixed

AdvAgg uses a db data structure that allows for it to get what css & js files are used together. The magic is in how the records are stored in the database and joined together. Old records are pruned based on the atime (timestamp).

Every bundle and thus filename is unique. If the css included varies based off of the user role then advagg will do the same; advagg only cares about what css was included as that is how core works as well.

CSS can not be wrongly included on a page, only the css that was asked to be included will go in the bundles. I thought about this and decided against it even for JS as this could cause unforeseen issues.

The bundler does preserve the order of the css files as well as js files. I did have this not be the case while testing this out and even for js I would end up with a broken site. It's why if you use async for all the js bad things happen. I will say that if the order is not preserved advagg generates a lot less files.

donquixote’s picture

So there are two aspects:

  1. The logic that decides which stuff should be combined in a bundle. These bundles are then remembered somewhere.
  2. For a page request with a sequence of e.g. 10 CSS files to be included, advagg checks if any part of this sequence is identical with one of the stored bundles. If so, it replaces these files with the bundle.

So, it could work like this:
Original CSS files needed for page request: a.css, b.css, c.css, d.css, e.css, f.css, g.css, h.css, i.css, j.css
Replaced by: a.css, bc.bundle.css, d.css, e.css, fghi.bundle.css, j.css
Or alternatively: abcdef.bundle.css, ghij.bundle.css

On the other hand, we cannot use a bundle abcx.css, because it contains a file that is not supposed to be on this page
EDIT: Also, abj.bundle.css, or ba.bundle.css, or hji.bundle.css cannot be used, because here the files are in the wrong order, or some required files are omitted.

So, it can happen that more than one combination of bundles is possible to replace a given sequence of CSS files.
To choose which bundles to use, the following criteria are relevant:

  • Attempt to minimize the number of files.
  • Prefer files that are already cached on client-side by this particular user. This would require to remember this in the session.
  • Prefer "popular" bundles, that can be reused on other pages.

Are these assumptions correct?

mikeytown2’s picture

The logic that decides which stuff should be combined in a bundle. These bundles are then remembered somewhere.

This is correct. In the database I have a "root" column. If it is 1 then that is an original bundle, just as core created it; usually all files in one aggregate. If it is a 0 then that means it is a derivative that was created by the bundler. When the bundler does it's analysis query it doesn't want the non root bundles to influence the results. D6 Query below where you can see root = 1.

        SELECT
          count,
          bundle_md5,
          filename
        FROM (
          SELECT
            LPAD(COUNT(*), 8, '00000000') AS count,
            bundle_md5,
            filename_md5
          FROM
            {advagg_bundles}
          WHERE
            root = 1
            AND
            timestamp > %d
          GROUP BY
            filename_md5) AS ab
        INNER JOIN {advagg_files} AS af USING ( filename_md5 )
For a page request with a sequence of e.g. 10 CSS files to be included, advagg checks if any part of this sequence is identical with one of the stored bundles. If so, it replaces these files with the bundle.

incorrect. The above query will output count and bundle_md5; these 2 then give a hierarchy of what css and js files get included the most often, count being the most important thus it gets used first in the analysis array; bundle_md5 is there to make sure that 2 things with the same count are not included together.

Like I stated before the magic is in the data structure. Just know that advagg can get every combination of how the css and js has been used based on the timestamp (access time) so older structures are not included in the count & bundle_md5. Also noted is that the highest value of count is how many unique combinations of css or js aggregates have been generated by core.

Or alternatively: abcdef.bundle.css, ghij.bundle.css

This is correct.

So, it can happen that more than one combination of bundles is possible to replace a given sequence of CSS files.

This is also correct. If all the files in that bundle all have the same "count bundle_md5" value then that is the optimal size and it means that those files are never seen apart from each other and thus there is only one bundle possible. AdvAgg knows that modules/system/system.menus.css & modules/system/system.messages.css are always next to eachother and are included together and thus they will always be in the same bundle. See https://api.drupal.org/api/drupal/modules!system!system.module/function/...

What ends up happening is advagg first generates the optimal bundles; grouping the files that always are next to each other. This usually results in about 15 groups on a semi complex site. From there it then starts to combine the bundles together in order to hit the target goal of 4 (which is the default and configurable). AdvAgg starts at the heaviest weight (files at the end of the aggregate) and then from there will start to merge groups together that do not have a lot of files in them. If there is a tie (same number of files in the group) then it will merge the files with the smaller count value from "count bundle_md5".

This algorithm could be improved by taking into account what files change the most often, filesize and bundle reuse. Currently it tries to make each bundle contain about the same amount of files. So there is some room for improvement here (patches welcome). Also noted that one can alter the analysis array via hook_advagg_bundler_analysis_alter.

To choose which bundles to use, the following criteria are relevant:

Attempt to minimize the number of files.
Prefer files that are already cached on client-side by this particular user. This would require to remember this in the session.
Prefer "popular" bundles, that can be reused on other pages.

Are these assumptions correct?

"Attempt to minimize the number of files." - This is correct. The bundlers optimal number of files is usually pretty big, thus it merges them down into a smaller number of files (default of 4).

"Prefer files that are already cached on client-side by this particular user. This would require to remember this in the session." - AdvAgg does not do this. If the bundler took into account bundles that it already created then the merge loop could be better. Using session data seems like a bad idea for caching purposes.

"Prefer "popular" bundles, that can be reused on other pages." - This is 100% correct up until the merging happens. Once the merging of the optimal bundles together happens, then reuse of the bundles goes down (bad) while files that need to be downloaded goes down as well (good).

mikeytown2’s picture

Below is output from a D7 site that has a minimal amount of modules and has seen very little traffic (dev box).
Looking at the JS we have 7 unique combinations of js aggregates; and we have a hash value after that. Looking at this we can see that the following files have always shown up together on this site, but not necessarily together in the same order.

00000007 OyN8uXQvDPA4_fqHaxRal4zVJshOqruJN4WIFOZ0s8U

sites/all/modules/admin_menu/admin_menu.js
sites/all/modules/devel/devel_krumo_path.js
sites/all/modules/devel/devel.js
sites/all/modules/admin_menu/admin_devel/admin_devel.js
misc/drupal.js
misc/jquery.once.js

After this grouping we have
00000005 aPOzYiQ2IWPK9cw0kK4oKyEgxKgxXC8s5WTrXeVtAC4
misc/form.js

Followed by
00000004 TfLetGhWKnR0AsrPaDCwU1b8VXLuWMOm8gW4wGOhxvM
misc/collapse.js

So right here we can see that we have some form pages that do not have a collapse-able field group; if all form pages did use a collapse-able field group then these 2 files would be grouped together.

Followed by
00000002 3L2eJphLZOq9yIvaDLTIu_R127GKOYABpbo57wBLuAE
misc/states.js

Followed by
00000001 u6-sXGuLb155-n-1ReFY3p0QmQ79j2O7gtFnCPHe09c
misc/textarea.js

Followed by
00000001 Wyjun7zr6HiXwvON-mNyh7fL41d94bANURoVScxfjwk
misc/tableheader.js

Here's another interesting thing. On this site textarea.js and tableheader.js are never together. This can only be inferred when the count is 1.

Followed by
00000001 CGqsSfpaWl2yp_WrskEoYJGfkxo9IugtFSi8NnULrwo
modules/contextual/contextual.js

Followed by
00000001 7Ec_knF-La7SMcLpIchUcRAZLuJNIP_b1yD0EqFPvts
sites/all/modules/advagg/advagg.admin.js

So we have a total of 8 distinct groups. From here we take what js files have been included and put them where they belong. This is where things get tricky. The groupings listed here do not respect ordering. It just says that whenever you see drupal.js you can always expect to see jquery.once.js. They might be in a different order and not next to each other but they are always on the same page.

So on this site on the admin/config/development/performance/advagg/bundler page these are the files grouped together when the bundler has no bounds.

[jquery.once.js, drupal.js]
[states.js]
[form.js]
[admin_devel.js, admin_menu.js, devel_krumo_path.js, devel.js]
[textarea.js]
[collapse.js]

So if I were to reorder this I would make states.js and form.js load after devel.js so the big groups would combine together. But because the bundler respects the order this doesn't happen.

If I change the bundler so that only 5 js aggregates will be built then textarea.js and collapse.js will be combined like so

[jquery.once.js, drupal.js]
[states.js]
[form.js]
[admin_devel.js, admin_menu.js, devel_krumo_path.js, devel.js]
[textarea.js, collapse.js]

And when I go to 4

[jquery.once.js, drupal.js]
[states.js, form.js]
[admin_devel.js, admin_menu.js, devel_krumo_path.js, devel.js]
[textarea.js, collapse.js]

And 3

[jquery.once.js, drupal.js, states.js, form.js]
[admin_devel.js, admin_menu.js, devel_krumo_path.js, devel.js]
[textarea.js, collapse.js]

If I were doing this by hand I might choose to put textarea.js & collapse.js in the next group up, but the alogrithem goes for the smallest number of files in a group so states.js & form.js moved up. Ideally states.js & form.js would go after devel.js, but this is not the order in which they were added to drupal and thus the weights enforce this ordering.

And 2

[jquery.once.js, drupal.js, states.js, form.js]
[admin_devel.js, admin_menu.js, devel_krumo_path.js, devel.js, textarea.js, collapse.js]

Below is the raw array built by the bundle query.

Array
(
    [sites/all/modules/admin_menu/admin_menu.js] => Array
        (
            [group_hash] => 00000007 OyN8uXQvDPA4_fqHaxRal4zVJshOqruJN4WIFOZ0s8U
            [mtime] => 1428438537
            [filesize] => 12267
            [linecount] => 385
            [changes] => 2
        )

    [sites/all/modules/devel/devel_krumo_path.js] => Array
        (
            [group_hash] => 00000007 OyN8uXQvDPA4_fqHaxRal4zVJshOqruJN4WIFOZ0s8U
            [mtime] => 1428438537
            [filesize] => 2192
            [linecount] => 66
            [changes] => 2
        )

    [sites/all/modules/devel/devel.js] => Array
        (
            [group_hash] => 00000007 OyN8uXQvDPA4_fqHaxRal4zVJshOqruJN4WIFOZ0s8U
            [mtime] => 1428438537
            [filesize] => 1349
            [linecount] => 45
            [changes] => 2
        )

    [sites/all/modules/admin_menu/admin_devel/admin_devel.js] => Array
        (
            [group_hash] => 00000007 OyN8uXQvDPA4_fqHaxRal4zVJshOqruJN4WIFOZ0s8U
            [mtime] => 1428438537
            [filesize] => 982
            [linecount] => 40
            [changes] => 2
        )

    [misc/drupal.js] => Array
        (
            [group_hash] => 00000007 OyN8uXQvDPA4_fqHaxRal4zVJshOqruJN4WIFOZ0s8U
            [mtime] => 1427942954
            [filesize] => 14544
            [linecount] => 433
            [changes] => 3
        )

    [misc/jquery.once.js] => Array
        (
            [group_hash] => 00000007 OyN8uXQvDPA4_fqHaxRal4zVJshOqruJN4WIFOZ0s8U
            [mtime] => 1427942954
            [filesize] => 2974
            [linecount] => 79
            [changes] => 3
        )

    [misc/form.js] => Array
        (
            [group_hash] => 00000005 aPOzYiQ2IWPK9cw0kK4oKyEgxKgxXC8s5WTrXeVtAC4
            [mtime] => 1427942954
            [filesize] => 2460
            [linecount] => 78
            [changes] => 3
        )

    [misc/collapse.js] => Array
        (
            [group_hash] => 00000004 TfLetGhWKnR0AsrPaDCwU1b8VXLuWMOm8gW4wGOhxvM
            [mtime] => 1427942954
            [filesize] => 3323
            [linecount] => 103
            [changes] => 3
        )

    [sites/all/modules/admin_menu/admin_menu.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1428438537
            [filesize] => 5276
            [linecount] => 58
            [changes] => 2
        )

    [sites/all/modules/devel/devel.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1428438537
            [filesize] => 2317
            [linecount] => 29
            [changes] => 2
        )

    [sites/all/modules/admin_menu/admin_menu.uid1.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1428438537
            [filesize] => 174
            [linecount] => 1
            [changes] => 2
        )

    [modules/system/system.base.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 5428
            [linecount] => 45
            [changes] => 3
        )

    [modules/system/system.theme.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 3711
            [linecount] => 54
            [changes] => 3
        )

    [modules/system/system.menus.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 2035
            [linecount] => 23
            [changes] => 3
        )

    [modules/user/user.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 1827
            [linecount] => 25
            [changes] => 3
        )

    [modules/system/system.messages.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 961
            [linecount] => 19
            [changes] => 3
        )

    [modules/search/search.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 564
            [linecount] => 10
            [changes] => 3
        )

    [modules/field/theme/field.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 550
            [linecount] => 8
            [changes] => 3
        )

    [modules/comment/comment.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 184
            [linecount] => 4
            [changes] => 3
        )

    [modules/node/node.css] => Array
        (
            [group_hash] => 00000003 D0DDKASHqBWBj6pG6J5aPc--eYt6iXkG9Q_y1PkofEY
            [mtime] => 1427942954
            [filesize] => 144
            [linecount] => 3
            [changes] => 3
        )

    [modules/system/system.admin.css] => Array
        (
            [group_hash] => 00000002 c9RwFt9t-xiSTzUbljwgqPTabeUzr1VGnpVqkbscgug
            [mtime] => 1427942954
            [filesize] => 5117
            [linecount] => 67
            [changes] => 3
        )

    [misc/states.js] => Array
        (
            [group_hash] => 00000002 3L2eJphLZOq9yIvaDLTIu_R127GKOYABpbo57wBLuAE
            [mtime] => 1427942954
            [filesize] => 17354
            [linecount] => 544
            [changes] => 3
        )

    [misc/textarea.js] => Array
        (
            [group_hash] => 00000001 u6-sXGuLb155-n-1ReFY3p0QmQ79j2O7gtFnCPHe09c
            [mtime] => 1427942954
            [filesize] => 920
            [linecount] => 32
            [changes] => 2
        )

    [misc/tableheader.js] => Array
        (
            [group_hash] => 00000001 Wyjun7zr6HiXwvON-mNyh7fL41d94bANURoVScxfjwk
            [mtime] => 1427942954
            [filesize] => 5330
            [linecount] => 133
            [changes] => 3
        )

    [themes/bartik/css/print.css] => Array
        (
            [group_hash] => 00000001 SrXkWjxQ5S-rTVvEIs6QV2R4WX-YeaV_MxafZBcvxfw
            [mtime] => 1427942954
            [filesize] => 656
            [linecount] => 24
            [changes] => 2
        )

    [themes/seven/style.css] => Array
        (
            [group_hash] => 00000001 NVHw479_ZmW0tZSAw7MhIODM2KFUOEst6KAfA72M2so
            [mtime] => 1427942954
            [filesize] => 18454
            [linecount] => 269
            [changes] => 3
        )

    [themes/seven/reset.css] => Array
        (
            [group_hash] => 00000001 NVHw479_ZmW0tZSAw7MhIODM2KFUOEst6KAfA72M2so
            [mtime] => 1427942954
            [filesize] => 2947
            [linecount] => 144
            [changes] => 3
        )

    [themes/bartik/css/style.css] => Array
        (
            [group_hash] => 00000001 LFHwVqo5MXRX4e1XavqN49W520QYiAcztRKBxuBuvYg
            [mtime] => 1427942954
            [filesize] => 32702
            [linecount] => 429
            [changes] => 2
        )

    [modules/contextual/contextual.css] => Array
        (
            [group_hash] => 00000001 LFHwVqo5MXRX4e1XavqN49W520QYiAcztRKBxuBuvYg
            [mtime] => 1427942954
            [filesize] => 2340
            [linecount] => 16
            [changes] => 2
        )

    [themes/bartik/css/layout.css] => Array
        (
            [group_hash] => 00000001 LFHwVqo5MXRX4e1XavqN49W520QYiAcztRKBxuBuvYg
            [mtime] => 1427942954
            [filesize] => 1634
            [linecount] => 44
            [changes] => 2
        )

    [themes/bartik/css/colors.css] => Array
        (
            [group_hash] => 00000001 LFHwVqo5MXRX4e1XavqN49W520QYiAcztRKBxuBuvYg
            [mtime] => 1427942954
            [filesize] => 1312
            [linecount] => 23
            [changes] => 2
        )

    [modules/contextual/contextual.js] => Array
        (
            [group_hash] => 00000001 CGqsSfpaWl2yp_WrskEoYJGfkxo9IugtFSi8NnULrwo
            [mtime] => 1427942954
            [filesize] => 1804
            [linecount] => 52
            [changes] => 2
        )

    [modules/dblog/dblog.css] => Array
        (
            [group_hash] => 00000001 8nURjgcDNd4zBV811zR96ayqnnBNfFFIyvxoHsf9alU
            [mtime] => 1427942954
            [filesize] => 1398
            [linecount] => 20
            [changes] => 3
        )

    [sites/all/modules/advagg/advagg.admin.js] => Array
        (
            [group_hash] => 00000001 7Ec_knF-La7SMcLpIchUcRAZLuJNIP_b1yD0EqFPvts
            [mtime] => 1432949093
            [filesize] => 4151
            [linecount] => 126
            [changes] => 2
        )

)
donquixote’s picture

Wow, this is a long read :)
Just some quick ideas:
- It would help if we knew that some css files can be optionally included without harm. E.g. if you write a module with a display suite layout, then the css file for this layout only needs to be included on pages that use this layout. On the other hand, it would not hurt to include it on other pages, if that helps the bundler strategy. Also, the order of these css files does not matter as much, because it is quite random anyway.
- It would also help if the order of css files was more consistent. drupal_add_css(), and the order in which it is called, can cause a lot of randomness. The same for ['#attached']['css']. CSS added with stylesheets[] is more predictable, but then it will be on all pages.

There is a tradeoff of having the same files everywhere, which is better for bundle reuse, vs having some files only on specific pages, to reduce the average page download size.

So what if the stylesheets for a ds layout were declared with something like stylesheets[], but then marked as optional? They would only have to be included on pages that uses this layout. But whenever they are included, they would be in the position (order) based on the stylesheets[] entry. And they could still be included on pages where it benefits the bundler.

And then another, crazy idea :)
What if we would (optionally) serve the css with jsonp, and use js to add it to the page header? This would allow to serve the css in any order, and including css files that don't belong on this page.

That much for now. I am going to have a closer look later.

mikeytown2’s picture

#2433067: HTTP/2 and concatenated files will make bundler merging not necessary; might even make advagg obsolete.

#2149321: Create a submodule allowing one to modify every CSS & JS asset would allow for weights to be adjusted so css & js files are aggregated better. This would allow for the bundler to generate better aggregates via manual intervention.

#2396609: Inline critical css is how advagg will be using JS to load CSS async. It can do async css currently but putting the above the fold css inline is tricky.

So what if the stylesheets for a ds layout were declared with something like stylesheets[], but then marked as optional? They would only have to be included on pages that uses this layout. But whenever they are included, they would be in the position (order) based on the stylesheets[] entry. And they could still be included on pages where it benefits the bundler.

Can you explain this more?

donquixote’s picture

So what if the stylesheets for a ds layout were declared with something like stylesheets[], but then marked as optional? They would only have to be included on pages that uses this layout. But whenever they are included, they would be in the position (order) based on the stylesheets[] entry. And they could still be included on pages where it benefits the bundler.

Can you explain this more?

The idea was to provide an extended API to other modules, so they can declare which of their stylesheets are optional, or predefine the position where it should be included. How exactly this API would look like is secondary. The first idea was to extend on the stylesheets[] setting in the info file.

So let's assume you write a custom module with stylesheets X.css and Y.css that are going to be in ['#attached'] on some pages, independently of each other.

Normally this would lead to a situation like this:
One page: a.css, b.css, c.css, d.css, e.css
Another page: a.css, b.css, c.css, X.css, d.css, e.css, Y.css
Another page: a.css, b.css, c.css, d.css, Y.css, X.css, e.css
Another page: a.css, b.css, c.css, d.css, e.css, X.css
This makes bundle reuse unnecessarily difficult.

With the new API, in addition to the ['#attached'], you would now declare the stylesheets in stylesheets[], but mark them as optional.
This also reserves a place in the stylesheet order.

So on regular pages you would have:
a.css, b.css, c.css, X.css (optional), Y.css (optional), d.css, e.css
It would be up to the bundler whether to include X and/or Y.

But on specific pages with ['#attached']['css'][] = 'X.css', the "optional" marker would be gone from X.css:
a.css, b.css, c.css, X.css, Y.css (optional), d.css, e.css

A bundle that includes all the files would be reusable everywhere.

I don't really know if advagg is the suitable place for this :)

mikeytown2’s picture

I know that in D8 with libraries, dependencies are made explicit and thus the bundler can safely change the order of js files in order to get better bundler reuse. With #2149321: Create a submodule allowing one to modify every CSS & JS asset the same could be done.

donquixote’s picture

Looking at this we can see that the following files have always shown up together on this site, but not necessarily together in the same order.

Since all these core js files are pretty much known to us, would it be safe to always put them in the same order?

We could have an API for "reordering strategies", and let the site builder choose which strategy they want.
Advagg could provide a default strategy, that would always put the core js in the same order, and before any other js files.
This could happen even if aggregation is off.

If you build a site and the strategy somehow has undesired side effects, you simply disable it, or provide your own strategy.

For a start we don't really need the full API, but could have just one strategy that can be switched on and off.
(Because any "API" would need to be BC-supported in the future)

mikeytown2’s picture

That's a good idea! Do core + a couple of the most popular modules (like views, etc) setting what they depend on. Create a reorder algorithm that runs after the analysis query but before the merging function.

dependencies in D7 seems to have carried over in D8 so I'll use it https://api.drupal.org/api/drupal/modules!system!system.api.php/function...
https://api.drupal.org/api/drupal/core!modules!node!node.libraries.yml/8 (view source).

Going to add this to #2500791: Have an admin section of the bundler offer ordering analysis/advice; build out the core dependencies and create a reorder function/algorithm

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.