Because #2158771: CSS hardening was addressed and you're looking for feedback, I figured I'd spend 30 minutes on my Saturday morning to test it and provide that feedback. Unfortunately, I could not get Navbar to work because of the Modernizr/Underscore/Backbone library detection that was seemingly completely broken.
That happens for various reasons:
-
Libraries API can only detect versions on the non-minified file. So if you want to use the minified Backbone/Underscore/Modernizr, you must either download both the unminified (backbone.js) and the minified (backbone-min.js), or only download the minified and rename it to backbone.js. Otherwise Libraries API will fail.
- The instructions don't explain *at all* how to use the minified libraries. They assume you want poor front-end performance.
- At the same time, the instructions implicitly assumed you *did* download the minified libraries and then renamed them as if they weren't minified. If you did download the non-minified libraries as per the instructions, then the version detection wouldn't work!!!
- Incorrectly, though understandably,
navbar_libraries_info()
pretended it supported a "minified" variant, but it could never have worked, because it did not implement the required "variant callback". If no variant callback is enabled, then Libraries API … just assumes that the variant is installed (!!! WTF !!!). This makes sense when downloading packages (zip/tar.gz/…) that contain both the minified and unminified files, but this does not reflect reality. So, in fact, you should always implement the variant callback, but Navbar did not yet do this. - To deal with this variant craziness, Navbar had lots of logic to deal with checking if the minified version actually existed or not (instead of letting Libraries API do this), leading to more code complexity and
$variants = variable_get('navbar_libraries_variants', array());
which was introduced recently. If we use Libraries API correctly (even though that's ridiculously complex), then we can just ask Libraries API whether the minified variant exists, and if so, we just use that. In other words: gone is thatnavbar_libaries_variants
variable, instead you just remove (or rename) the minified JS file if you want to use the non-minified JS. Much simpler.
So, I wanted to fix this once and for all (and then Edit module could use the same techniques). Unfortunately, the tests are so artificial that they don't really test real-world use cases, the docs are so theoretical that they're useless and all of the example modules listed at https://drupal.org/node/1570568 fall in either of two categories: A) they stopped using Libraries API (gee, I wonder why), B) they use Libraries API in the most simple way possible (that is often also incorrect): they don't support a minified "variant".
That's why I ended up doing deep debugging of the Libraries API module and reading its "docs" and "tests" in-depth.
I'm probably forgetting about a significant percentage of frustration and misery, but I'm hopeful that the collective pain endured so far will have led us to this patch that will hopefully allow us to erased this part of our memories, to be happily forgotten, forever.
Comment | File | Size | Author |
---|---|---|---|
#27 | libraries_minified_only_broken-2167021-27.patch | 766 bytes | Wim Leers |
#21 | opposite-case-2.png | 329.87 KB | jessebeach |
#21 | snuffleluffigus-8.png | 345.84 KB | jessebeach |
#18 | navbar_libraries_api_revamp-2167021-18.patch | 18.41 KB | jessebeach |
#18 | interdiff.txt | 5.01 KB | jessebeach |
Comments
Comment #1
Wim LeersComment #2
jbeckers CreditAttribution: jbeckers commentedHave not yet tried the patch, but two things come to mind (probably follow-ups though):
Comment #3
jbeckers CreditAttribution: jbeckers commentedPatch seems to work perfectly.
Comment #4
Wim Leers#2:
Comment #5
Wim LeersThe part I didn't mention yet about this patch: it ensures that things *also* work when you download the minified version and rename it as if it wasn't minified. Even though that's not entirely correct, the patch tries to make the site admin's life as easy as possible.
Comment #6
jbeckers CreditAttribution: jbeckers commentedOne thing I noticed: with this patch the minified file should be named modernizr-min.js, while the README says modernizr.min.js
Comment #7
Wim Leers#6: good catch. The README should be updated. The file already had to be named "modernizr-min.js" before this patch.
Comment #8
jessebeach CreditAttribution: jessebeach commentedTwo different file names. I'll change the variant check to modernizr.min.js
Comment #9
jessebeach CreditAttribution: jessebeach commentedOk, so by declaring 'versions', we get a minimum version number for Libraries API to check against.
Comment #10
jessebeach CreditAttribution: jessebeach commentedAlright, this is finally the implementation that I'm happy with. Wim, I hope you're cool with it as well.
All files are now declared in a named variant under a version. We can do this because of the variant existence checking that Wim added in the previous patch.
A library is determined to be installed if at least one of its version's variants is installed. Previously, a library was assumed to be installed if it had a version that matched the physical file's version even if eventually nothing got loaded.
The variant loading order is determined by the
variant order
property on the library (custom to navbar). This property can be overridden by another module usinghook_libraries_info_alter
. So no where now does the code impose the minified version; the variants and their preferred selection order are determined by configuration of the library info. Another module could conceivably add a variant to the library info of Navbar if needed for the installed library version.Also, the status report now lists what variant will be loaded.
Comment #11
Wim LeersHehe, nice cleverness in there :) It's also impressive how many hoops we have to jump through to get reasonable behavior that most people expect.
I have only one nitpick:
I'd return FALSE or NULL here.
… and one significant concern. AFAICT, the current patch pretends as if it is possible to install only the minified version of a library (e.g.
backbone-min.js
). But that's not really the case, because the version check will still always check the unminified file. In other words: you must still have the unminified file available, otherwise Libraries API will say the library is missing, because no version could be detected, because the unminified file does not exist.Comment #12
tstoecklerAs @Wim Leers already mentioned the version detection is currently quite fragile. This fixes that which also allows to remove the post-detect callback. (In other words the logic in the post-detect callback is moved into the version callback.)
I also added a @todo to _navbar_convert_libraries_to_library(). That function is really unfortunate and Libraries API should do that for you.
The major conceptual problem with this is that you are trying to enforce that if the 'minified' variant is available that it be used. There is no reason to do that, however. If people want to load the 'source' variant for some reason, they should totally be able to do that. One thing that Libraries API *should* do however, is to let libraries provide some default. To put this into code:
This might mean moving the 'variant order' that you introduced into Libraries API proper and then checking that in libraries_load(). Not sure yet, but I will open an issue to discuss that.
Comment #13
tstoecklerHere we go: #2167447: Allow libraries to declare their preferred variants
Comment #14
tstoecklerOh, I forgot to say: I did not actually test that #12 works, so statistically it's probably broken.
Also, the one thing I don't understand about the patch is the version specification in hook_libraries_info(). Why is that done?
Comment #15
jessebeach CreditAttribution: jessebeach commentedre #11, I opened an issue with a patch to make the error messages for version detection more explicit: #2167249: Make version/variant callbacks provide error messages themselves for better DX/UX.
tstoeckler, I'm psyched we're collaborating on this. I really want the Spark modules to have THE definitive implementation of Libraries.
Comment #16
tstoecklerYes, I saw that. Haven't commented there yet, but it's a great idea. Thanks for that! That would also help the version callback I introduced here.
Comment #17
jessebeach CreditAttribution: jessebeach commentedtstoekler, I don't quite understand this comment from your patch:
Does this mean that a variant label such as
minified
is declared under two numbered version arrays? e.g.Would this result in no file being loaded?
Comment #18
jessebeach CreditAttribution: jessebeach commentedGiven the current code in
libraries_detect
, I don't think we can move the variant callback logic to theversion callback
.For one, the callback in invoked around line 527 of libraries.module, but the variants are not moved to a top-level property in the
$libraries
variable until around line 558, when the versions property is processed, thus the version checking fails here:The
version callback
is meant to return a version for the library, a string.So, I thought about this some more and made the attached changes. Now,
version callback
compares the variant versions. If the versions are the same or NULL (meaning the version couldn't be determined), but not all NULL (meaning no version could be determined), then we return the unique single version string.We unfortunately lose some error information here because we get one error message (maybe return an array instead?). So I've tried to return at least some useful info with additional error messages. I tested all the scenarios.
Comment #19
Wim LeersSo, what is left to be done here? I think just some thorough testing to find any remaining problems?
Well, I've unfortunately still found some problems.
modernizr.js
(no minified variant), then the Status Report won't complain, but Navbar will try to load the minified one, even though it does not exist.modernizr.min.js
, then the Status Report will fail (and importantly, Navbar will also not loadmodernizr.min.js
, even though it exists!). This is why I said that Libraries API assumes you always have the non-minified variant. I don't think it's possible to work around that assumption. The same applies to having onlybackbone-min.js
orunderscore-min.js
.Comment #20
Wim LeersHere's the sister issue for Edit module: whatever we do here, I must apply to Edit module as well — issue created: [#2169507.
Comment #21
jessebeach CreditAttribution: jessebeach commentedre: #19, to your first point, I'm not seeing this behavior with the latest Navbar code. If I change the name of the minified Modernizr file from
modernizr-min.js
tozmodernizr-min.js
(note the prepended 'z' character), then the source file is loaded. Note that the name of the minified file should bemodernizr-min.js
, notmodernizr.min.js
so that the minified file name matches the Backbone and Underscore minified file name pattern.And the opposite case where the
modernizr.js
file is renamed and made unfindable.I think it's working about as well as we can get it to for the moment. Can you retest and see if you replicate the behavior I'm seeing? If you can, I vote we go with the code in Navbar as the pattern for the Spark module Libraries implementation.
Comment #22
jessebeach CreditAttribution: jessebeach commentedComment #23
Wim Leers#21: ah, you changed the naming scheme! After testing this again, I can confirm that this works flawlessly!
AWESOME WORK!
Comment #24
tstoecklerSorry for not following up earlier! #18 is certainly an improvement over the status quo so I won't un-RTBC, but I still think we can do better.
I at first didn't understand what you meant in #18 because I skipped over the code snippet. I just re-read that and now I get where you're coming from. Thanks for providing that snippet, that was helpful.
The problem is that the actual library (variant) file information is currently "hidden away" in the 'versions' property. Per definition this property can only be accessed once the version is known. However, since #12 it contains the version detection information for the variants. This is paradox, in short: You need to know the version to figure out the version. Instead the variant version detection information should be moved into the top-level 'variants' property at which point #12 starts working.
As I already mentioned above, but maybe did not explain well enough: I don't understand the reason why the 'versions' property is used at all. It is used when e.g. libraries move around files in newer versions and thus the library definition needs to differ depending on the version. In the code comment, it says something about the 'min' version.
What I think is happening, now that I think about it, is that the 'navbar' code depends on a certain minimum library version in order to work. Is that correct? In that case, what should really happen is that the navbar declares a *versioned* dependency on the navbar library. But since modules can't currently declare dependencies on libraries at all, I think it would be fine to leave in the 'versions' property for now.
The paragraph from above is still correct, though: The version-detection information should move up. I will provide an updated patch soon, but it might be tomorrow or Wednesday. (Obviously) Feel free to commit this, for now, though.
Comment #25
jessebeach CreditAttribution: jessebeach commentedCommited to 7.x-1.x e2e2a44d
This change is incorporated in 7.x-1.3
Comment #26
Wim LeersPorted to D7 Edit: #2169507: Improve Libraries API integration & http://drupalcode.org/project/edit.git/commit/892cfdb :)
Comment #27
Wim LeersWhen using *only* the minified Underscore or Backbone, version detection fails, because the patterns are wrong.
Comment #28
Wim LeersComment #33
jessebeach CreditAttribution: jessebeach commentedThanks Wim! Pushed to 7.x-1.x (503dae83). I'm about to cut 7.x-1.4.