I was thinking about new module. It would become a dependency for other modules which alter CSS and JS files, optimize CSS and JS code, compress it with gzip, update some JS modules up to actual versions, alter CSS and JS files' host, etc. Almost all such modules have to do some non-trivial things now:
- Alter own module's weight to have MODULE_theme_registry_alter called after all other modules
- Alter theme registry to be sure MODULE_preprocess_page is the latest preprocess_page hook
- Do the job in MODULE_preprocess_page
- Many of them also have to re-generate HTML code which will be included to the page head
As you can see, there are a lot of problems:
- First and biggest: potential incompatibility with other modules which do other job on the same JS/CSS code
- Non-trivial code which really needs explanation with comments
- Performance loss, because of HTML code regeneration again and again, aggregated CSS/JS loading and saving many times
Few words about incompatibility. For example, there are two modules: module A optimizes aggregated CSS file, module B compresses aggregated CSS file with gzip. If module A set it's weight to 9999 and module B - to 999, then module A will break site with errors, because it assumes aggregated CSS is text file, not gzip archive. Two modules is the most simple case, by the way. Usually, modules have to write compatibility code now. So, these modules will work together only if they have workarounds to support each other.
I'd like to develop module which helps solving all these problems. Module will hack into theme registry, replace standard template_preprocess_page with its own (emulating original but without CSS and JS processing) and attach another function to the end of chain. Inside this function module will finally generate HTLM code to attach CSS and JS to the page: it will invoke some hooks to alter CSS/JS:
- hook_alter_css($op, $data), $op can be
- 'process' - called with nested array of all included files got from drupal_add_css(). jquery_update can use it, for example, to replace old Jquery with the new one
- 'aggregate' - called with aggregated CSS code before it is saved to file. It will be called only if aggregation was enabled. This part is for all kind of code compression/optimization modules
- 'post-process' - will be called with nested array of the same format as in 'process' part but all aggregated files will be replaced with aggregated file. Also, will be called only with aggregation enabled.
- hook_alter_js($op, $data) - pretty much the same as for CSS
Maybe there will be one more $op - 'pre-process'. It would be useful for replacing files with custom versions, so inside 'process' step we can be sure all files will be processed correctly.
Module should understand some extended options for $data in 'process' and 'post-process' compared to standard format. At least "host" should be added (so module can be used with CDN-related modules) and "preprocess" should be extended to support aggregation groups (so module can select which files to aggregate together). Also, it would be great to have "browser" field only on 'process' step for conditional comments and this field can give us ability to aggregate multiple files for different browsers depending on what was attached.
I think the latter needs explanation. For example, we have two CSS files: "all.css" for all browsers and "ie7.css" for IE <= 7. So we fill "browser" field like this in hook_alter_css with $op = 'process':
- For "all.css" : array("'IE' => 'gte IE 8', 'W3C' => TRUE)
- For "ie7.css": array("'IE' => 'lt IE 8', 'W3C' => FALSE)
The result should be: two aggregated files both inside conditional comments - one for IE <= 7 containing aggregated CSS code from both "all.css" and "ie7.css", and second for IE >= 8 and W3C-compliant browsers with CSS only from "all.css". Finally, every browser will download only one CSS file (even old IE)
The only problem left is order of hooks invocation. We can fix it with providing some default instructions to set module weight in different ranges (1-10 for CSS code cleaners, 11-20 for gzip compressors), but it is not enough. So maybe module will have UI to enable/disable and reorder all implemented hook_alter_js and hook_alter_css for each $op parameter separately. If you have any other ideas about this problem, please write comments.
Does anyone need such module? Are there any modules implementing all described features? Before starting, we should fully discuss future API of this module.
P.S. Sorry for bad English.