Split javascript files in media into modules

Description

​Yesterday in dev chat, ideas for splitting up distinct javascript modules in were discussed. As we move towards more and more javascript-heavy administrative interfaces, this will prove quite helpful in readability, and by extension maintainability. A 6000 line file made up of 50-some views is not easy to step into. A folder of well organized modules for one feature would be.

This will only affect the src/ directory in the develop repository, which will use our build process to concatenate into the build/ directory, so nothing will change for developers using the build/ directory or the core repo.

We'll start this effort with media, and end up with a standardized way of doing this going forward.

browserify.diff​ breaks up the Backbone classes in media-models.js, media-views.js, media-audiovideo.js, and media-grid.js into CommonJS modules and injects them via Browserify on build/watch into a built file. Let's start at the beginning.

Brain overload

Files that are 1000s of lines long are hard to consume. We try to alleviate this by adding copious amounts of docs. Still, it's a lot to look at. Ideally, we would break our files into smaller modules and then somehow join them together in a build process.

Require vs Browserify

Require is a great tool for converting AMD modules into built files. Require leans on Dependency Injection in its syntax:

This syntax works great, unless you have way more dependencies. Refactoring code could unwind a module that has a lot of dependencies, but if you are just trying to convert legacy classes into a module, Require starts to get a little weird.

Require becomes a Grunt task to makes one big file by recursing the dependency tree in an initial manifest. Require, by default, loads JS asynchronously, which can cause race conditions with plugins or themes that expect code to be registered on $(document).ready() or window.onload.

Require works even if you don't build via Grunt.

Browserify is a tool that allows you to use Node-style CommonJS modules and run them in a browser without changing from the Node syntax. Browserify requires a build for this to work.

After spending a lot of time messing around with both: I think we should use Browserify.

Converting "Legacy" Code

The media JS code is some of the most "modern" code in WordPress, but it still clunkily lives in huge files. To convert the code into Common JS modules, we need to make a lot of individual files (one for each Backbone class). We also need to make sure we maintain the existing wp.media namespaces for backwards compatibility. We don't want any existing functionality to change, we just want to build the files differently.

Since we are in experimentation phase, I have added a folder to wp-includes/js, media, that contains the modules and the built manifests. My patch adjusts script-loader.php to use these new paths.

*.manifest.js files get built into *.js files when you change a file in media/*, provided you are running the grunt watch task. The watcher will automatically call browserify:media and uglify:media when those files change. This allows you to run with your site from src or build , and you will still get Browserify'd files. SCRIPT_DEBUG will either run *.js or *.min.js, just like any other minified JS in core.

This is an experiment

I would like us to do something like this. I got the idea yesterday to do this proof of concept and just ran with it. Consider this a request for comments.

I removed the "debug" option for now, because that doesn't do anything. If we want that, we need to add it under browserifyOptions. But patches should never be uploaded, or committed, with that option. It adds the absolute path to all the files, which is different for everyone of course. Not sure how we should solve this. Normally you don't version control the built files. When "watching", we could add that option, but before uploading the patch the contributor should run browserify without it.

About the watch task. Imo we should really use Browserify's build in "Watchify". It's a lot faster because it caches previous work. Just running grunt watch:browserify on my computer takes about 80s each time a file changes. This would need to be a separate watch task though. You can still add the grunt watch task after it and do more stuff, and leave out the keepAlive option for Browserify.

I'll wait to make a patch, don't want to make a huge patch combining the previous one.

Here's what the Gruntfile could look like with Watchify. You can run grunt watchify to watch all browserify bundles (I had to split them, I couldn't get it to work otherwise). The last task is kept alive, but you can just as well use grunt watch for that. You can watch the Browserify bundles (instead of the modules themselves) to run any tasks after Browserify did its thing (e.g. uglify).

I also added browserify to the precommit task so you can use it to remove debug information.

I am the author of ​Media Library Assistant, a plugin that adds some features to the Media Manager Modal Window. I have struggled with earlier versions of the media JS code and I am happy to see these improvements. However, the 4.2 beta completely breaks my plugin and I am at a loss on how to modify my code to support the new version. Where can I go to find the "right" way to, for example, add a control to the toolbar or to replace the existing search box with an enhanced version? If there is a better place to ask this question, let me know. Thanks for any help you can provide.

Thank you for your instant response! Let me be clear - I am all in favor of the improvements you've made and I know it's my responsibility to update my code to conform. I admit my JavaScript, etc. skills lag my PHP skills and my current Media Manager support is a hack; I just want to know how to do the right thing.

By the way, my code still runs fine (mostly) with the Media/Library grid view, but it fails completely in the popup "Add Media" modal window.

I am relieved to report that the latest changes have restored my plugin (and probably others) to working order. I look forward to the day when I can lay down my machete and go back to the scalpel. Thank you for your great work!