-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Declare test dependencies using AMD #713
Comments
I'm not sure it's relevant to go with AMD if it's done like that. Pinged @jrburke to get his opinion. (My concerns are around hardcoded name and implicit globals, I'll let him have at it though). |
Its relevant because we can run a build through r.js and it'll do the right thing |
More specifically, now that I'm not mobile: On a normal build, even Almond will nearly double the size of the code. We'd also need namespacing on our require system, etc, so it didn't conflict with other people's. Then if we wanted you to be able to require tests via modules, we'd need to optionally turn off that namespacing code. My ideal is that Modernizr registers (optionally, perhaps) as an AMD module and the whole object is returned. However, I think we can use the great dependency managed build system internally in order to generate a small build without having to do builds the way we have been in the past (writing everything as a file and removing the tests with regex). Now, we can run r.js and then remove the wrappers around each module, know that they are in the correct order, and add the |
We can avoid both of these things, really. If we have matching filenames to our property names: define(['modernizr', 'window', 'document', 'addTest'], function (Modernizr, window, document /*, dont bring in the rest */) {
Modernizr.addTest('filename', function (){
return !!document.filename || !!window.filename;
});
}); But it wouldn't actually cause any issues if we assumed those three things to be implicit globals. |
Having not used r.js I am wondering how it handles local variables inside the closure. Are they hoisted to the top of the combined file? How does it deal with name conflicts? |
At that point, I'd go in and strip the wrappers (probably with an IIFE re: name conflicts). Then I'd run uglify on them. Does that seem reasonable? (I've been considering the same thing for the modular jQuery pseudo AMD thing, since you had requested no module wrappers left over) |
Ah, so For jQuery core we just came up with a naming convention for globals so that variables could be shared across "modules" after the IIFEs are removed. It's not elegant but it works. |
Right. I'm essentially using AMD to use the awesome toolchain for building. But we don't want to expose those modules or require their cruft to the average user. (Though it would be easy to make a build that did just that). |
I was invoked by @theodoreb, but I believe @SlexAxton has the right approach if using r.js for the builds. I'm sure @SlexAxton has enough context, but using the onBuildWrite hook in the build config for r.js would allow you to use a regexp to do the define() stripping/transform. Feel free to ping me if you want me to look anything over, need pointers on how to get it done. |
Thanks for stopping by! If it ends up possible to have AMD definitions we can use in a bigger AMD project I'm fine with whatever really :) What is so good about Modernizr are the tests, not so much the global What I had in mind was something like :
Which looks very much like And if the modules can be defined as Then the init of the lib can end up as:
I don't know where I mean not trying to get in the way, just want to provide a possible alternative. I know you guys have awesome build tools with grunt and all so that's probably not much of a concern for you the way it looks like in the source. I prefer writing source code than build code, so that'd be how I'd use AMD for it :p |
I totally feel you on most of this, but unfortunately, we are the minority.
Modernizr didn't invent feature testing. It just put a bunch of them into one big object and added some css classes to
This is not a bad idea, but unfortunately, for many uses of modernizr, this won't work. We need to actually run all of the modernizr modules synchronously at load time, because they get added to the Not to mention, people would need to require modernizr tests from our In other words, we kind of have to build and pre-run each test. I'm not against eventually creating a no-css lazy evaluation mode, but we've got to maintain some semblance of backwards compatibility.
That's what almond.js is for, if I'm not mistaken. We can namespace It seems like it'd be easier to do it the cheating way, and convert it to the lazy eval all AMD module way. We'd just need to add a |
almond.js does kinda much more than needed here. it takes care of the config object, resolve paths and all. AMD doesn't have to be Async, it's the definition that is, not necessarily the loader. Code 100% not tested:
And the require part is just a way to make r.js pick up the right modules to include. Obviously it needs a check for the number of arguments and some safeguards in some spots, still should work pretty well considering the tests are a TRUE/FALSE type of results. The execution would be sync since we don't have to load anything and r.js made sure it's in the proper order. |
You are correct that we could reimplement our own AMD runtime. What say you about the filesize argument? |
That depends on how much dependencies you have between the tests. When there are no dependencies it'd go from
to
and dependencies would give
I tried on the current tests (copy/pasted all the tests[] without the rest), as you said, gzip takes care of most of it. It's very crude testing so take the number as-is: I have 30b of diff over a 1792b of minified+gziped JS so around 1.7% of filesize increase with the define+deps. If there are a lot of dependencies it's different. Can you give me an idea of how much inter-dependencies there are? But I assume that a build script can take care of stripping most if not all of the overhead off, It'd make it simpler to use what is needed from the outside though. It all comes down to the priorities I guess. Since I'm from Drupal and that's kinda how we'd like to use Modernizr, I'm obviously biaised :D |
You guys are looking into using Modernizr as AMD for each of the individual tests? That's great. I'd love to figure out something that does this. I think you're on to something with the auto-running define calls. There is definitely some environment stuff to work out, though (colliding or registering in other environements). If we have a |
Well I don't know about the other Drupal guys but I am :) Why do you think @rupl and @seutje worked on the branch to split everything up? Because we'd need it for Drupal. And actually, now it's using Modernizr.addTest() instead of tests[] so It's even closer to the define thing :) rupl tells me that there is hardly any inter-dependencies so the filesize would actually increase by less than 1.7% after their branch is merged. As for the define issue, to me there are two very different use cases:
So no it wouldn't dual register. A third party would be using the AMD modules, nothing special. It'd be his resposibility to minify/concatenate (even name?) the modules in his application. if he can't do that he should just stick with the standalone build anyway. |
I apologize because I'm not up to speed on all things Modernizr/Drupal, but this is my understanding how the use cases @theodoreb outlined would work. I think this is just restating what @theodoreb is expressing above: The Modernizr tests use define() with their dependencies in source form, then the Modernizr build process can use r.js to combine all the modules together, and then convert the If someone else wanted to use the Modernizr tests with a r.js build config that does not translate the define() calls to IIFEs, then that should work fine too, as long as the define() calls inside the tests use relative module IDs. That other build config could then either decide to bundle almond, use the r.js namespacing, whatever it likes, even its own define() implementation that immediately executes the define() factory functions. I would probably expect most folks to just use the Modernizr build config though, given the needs of some of the tests (delayed execution would not be desired), and that there normally should not be a need for two different Modernizrs on a page. |
Let me know if you need more data or examples or anything. I'd be happy to help. FYI, on the Drupal side, having Modernizr in Drupal 8 core happens before 1st of december or it doesn't happen. I appreciate that you have your own schedule and I'm not trying to rush things. Modernizr would be very helpful to us and I'd like to see it happens. It'd be too bad if we miss the opportunity because of a few weeks :) |
I don't think it's a matter of how long it takes to program it. It'll probably be one or two nights of getting core stuff in place, and maybe a few more nights to get the builder working again. I think this discussion shows, though, that we don't know exactly how we want to do everything. I'll dive into some code with this feedback and see if I can't come up with something that everyone's happy with. I think I agree with what everybody wants. I don't know if we're all on the same page implementation-wise, but perhaps that just means the first implementation should shed enough light to either keep or fix. I'll keep you updated. |
https://github.com/slexaxton/Modernizr/tree/mod-mod This is a full AMD-ification of everything in Modernizr. This still needs require.js to function, so now I'll need to write the code that strips out the wrappers. If everyone could just serve the base directory ( |
If you want to build, just run |
Wow nice work. I've grabbed your branch and converted the build.sh file into a grunt task so we can just do |
this is huge. super nice work, alex. On Wed, Nov 7, 2012 at 8:47 PM, Ryan Seddon notifications@github.comwrote:
|
Thanks a bunch, Alex, you even split up the core helper functions, awesome! |
I added a few things and cleaned up a few of the tests in the process of porting them over. I made the Modernizr object a I added The I added an We need to standardize the naming conventions of the tests. They're all over the place. But in the name of backwards compat, we should probably keep around the old names for a version. You can put them in the I'm getting close to successfully removing the AMD wrappers and @jrburke is a badass and said he could get in a browser capable version of r.js in soon, so we're in good shape to have this stuff nailed down. I'm sure I have more, but this is good for now. |
Oooh. I think I liked about the |
Quick update: The So this is now minimally viable. I just need to work on the input format for choosing tests and stuff, since this just always builds everything. I'm not great with grunt or shell scripting so if someone wants to clean up what I did, that'd be great. Note: The file size output only works on os x, I couldn't find a good cross platform way of outputting file size in bash. maybe just switch that part to node :/ |
Another good task for someone would be to verify that I didn't break any of the tests. Can someone compare the output of the new modernizr and the old modernizr and make sure it all matches up? |
Another idea if someone wants to take it: There is some danger that we blow away local variables when we strip AMD wrappers. I made sure that every module only has one or zero top level local variables, but in the future people might not realize that. So it'd be good to run a minimal jshint over a stripped version during a build and check for 'this variable has already been declared' warnings. Or something similar. Then we can catch these situations before they cause problems. |
@SlexAxton you should move this branch into Modernizr and call it AMD or something so we can all work on it. I've got grunt doing all the work on my machine that your build.sh script was doing so I'll add that in when you move it over. |
I put my mod-mod branch as the https://github.com/Modernizr/Modernizr/tree/3pre So I'll work out of that from now. |
I've added the build step to the grunt file ea721fb I also added a package.json which will be the source of truth for the version, license and project name. Also has reference to some dependencies, grunt and a grunt-contrib plugin so you'll need to run Once you've got all that the default task for grunt will do the full build. |
Sorry for the Australian delay. I think I have all the necessary pieces in place, just needs some clean up at this point. If you check out the 3pre branch (on the main fork) at the root directory, you can: A) run This could probably be ported to something in grunt, but I figured that was someone else's area of expertise. Right now it's a 4 line shell script that calls into some modules. If you mess with these, make sure you don't break the browser compat. The config structure is pretty simple now. We will probably want to add a spot to do class prefixes and stuff, but this is a good start. B) serve the root directory. Open up This will show 3 textareas. The first is a big configuration object that you can edit. I default it to everything just for stress testing, but feel free to change it. Then when you press the button, it will run the config through the same code as on the server, but via the clientside r.js stuff that @jrburke graciously released for us. The only unforgivable hack is that I'm generating the root Once it requests and builds, etc, it injects the 'dev' version into the top box, then uses the instance of Uglify2 (yay free upgrade) that's on the It's quite late, so I'm sure I'm forgetting bunches, but this should get us over the hump. It's all a bit messy though, so all you guys/gals who have an eye for clean code should jump in. Still needed: We need to decide on official names and categorization asap. |
@SlexAxton I committed a change in r.js master (will be part of an eventual 2.1.3 release, but no ETA for it yet) where you can seed text content for given module IDs via a rawText build config option: https://github.com/jrburke/r.js/blob/master/build/example.build.js#L486 You can try it out by using the latest master build of r.js: https://raw.github.com/jrburke/r.js/master/dist/r.js The change is tracked in requirejs/r.js#324 if you need to give feedback on the rawText feature. |
Alright, I think I have things working pretty well. https://travis-ci.org/Modernizr/Modernizr/builds/3508819 All tests pass in my browsers and in phantom/travis.
There are no tests with hyphens in them right now, so we may not have the best organized tests, but at least they all are easy to access, and are backwards compat. If people want to organize them better, I think it's a good idea, but I'm not sure it should hold up a release. My next steps are to more or less hook up the 3.0 code to the current |
Given |
This seems complete to me, @SlexAxton do you agree? |
I think so! |
beauty, thanks dude |
With #486 taken care of we have individual files for all core detects. Now we have to declare dependencies. @SlexAxton suggested AMD and there seems to be consensus on that. He suggested cheating a lil' bit by assigning results directly to Modernizr global.
@paulirish asked Alex "for your AMD proposal, why wouldnt we return inside the modules and pull in the value as the dep?" And he said that's so we can ship the built version without any wrappers at all (almond or otherwise).
Couple examples:
or CJS-style
The text was updated successfully, but these errors were encountered: