On December 4, at WordCamp US, an idea was formed. Over beers and snacks, a small group of friends lamented the lack of appreciation for Tweetstorming.

“Get a WordPress” people say, as if the thought never crossed their minds that 37 tweets in a row about the same thing starts to defeat the purpose of Twitter.

Myself, being the most stubborn, independent, and likely drunk of our group, volunteered to solve this problem the only way I knew how – a WordPress plugin.

Having helped build Jetpack’s Publicize feature all those years ago, it wouldn’t be my first rodeo, but now that it’s working, it just might be my last.

Introducing Publishiza, the shittiest way to Tweet your thoughts while also blogging about them too. Check out https://publishiza.com to learn more.

WordPress, to me, is an independent publishing platform. It grants me the right to complete & total autonomy when it’s desireable, but also allows me to group up with others when that makes sense too.

An article from The New York Times puts into a nice perspective why I think WordPress is really important to the future of the web.

Every pirated music video or song posted on YouTube or Facebook robs the creators of income, and YouTube in particular is dominated by unlicensed content. Google’s YouTube has an over 55 percent market share in the streaming audio business and yet provides less than 11 percent of the streaming audio revenues to the content owners and creators. But Facebook, which refuses to enter into any licensing agreement on music or video, is challenging YouTube in the free online video and music world.

And this…

“They are taking all the money,” he noted. “They have algorithms we don’t understand, which are a filter between what we do and how people receive it.”

I don’t think this is anything new – tides will shift, and new technologies will emerge to try and help with distribution of content – but it’s scary to me now that so much of what’s being published funnels out into our enormous world through only a few hoses.

There’s more money in the world changing hands than every in recorded history, and I suppose it’s always been this way – content creators are starving artists and content distributors are benefactors – but the trickle-down distribution of wealth continues to run perpetually dryer vs. wetter.

I think in the WordPress space, companies like Envato are undervalued. Their operating costs are surely not as low as one may think, yet they continue to pay out millions of dollars to digital artists & creators. I think there may be room for more Envato’s to carve out their own niches, and WordPress plugins like Easy Digital Downloads and WooCommerce are the long-term solutions for people hoping to have a sustainable independent lifestyle.

In 2014, Matt Mullenweg challenged the WordPress community to volunteer 5% of their time towards open-source and WordPress.org.

A few months later, I ran a successful fundraising compaign that allowed me to donate 6 months worth of time towards BuddyPress & bbPress.

For 2017, and hopefully with your help, I’m going to try something a little different that I’ve nicknamed: 💯∞.

My goal is be a fully funded independent ambassador for WordPress & the surrounding initiatives, backed by many of the best companies who continue to push WordPress beyond its limits on a daily basis.

Practically speaking, I imagine this to work like a monthly retainer to work on WordPress core and Dotorg. Someone pays, say, $7500, and I get to say January 2017’s progress was brought to you by Pagely, February by GoDaddy, March by CrowdFavorite, April by Jetpack, May by WebDevStudios, June by GiveWP, and so on, forever.

(The structure is a bit TBD. Maybe it’s weekly rather than monthly, or quarterly, or something else entirely. Hopefully you get the idea.)

It’s like a podcast, but instead of airtime ad placement, it’s coretime leadership, contributor relations, and issue management.

Some of y’all who think this is somewhat unconventional may say – Why not just be a full-time employee?

That may end up happening – it may actually be the smarter move in the long run here – but I think there’s high value in being an independent, nonpartisan thought leader that *wants* to help everyone navigate the ins & outs of contributing to open-source, more specifically WordPress, et al. Since that’s where I’ve seen the most success, I’d like to put maximum effort into continuing that trend; if it’s not sustainable, then we learned something together and try something different.

This weekend at WordCamp US, I’ve had the pleasure of meeting with several folks who love the idea and are… deeply… interested in making this work however they can, up to and including growth beyond myself for other individuals who are able to provide more value as an independent voice.

If you’d like to help, please email me. If you have questions, concerns, criticisms, or feedback, please get in touch somehow.

Thanks for having an open mind, and for considering investing in the future of something that really, truly means a lot. To the folks who are already onboard, I’m so excited you are willing to give this a try with me.

P.S. Have I mentioned how much better WordPress is on mobile? I wrote all of this from my iPhone without any issues, which is still so incredible to me. Great job, mobile team!

P.P.S. If you’re unfamiliar with why I think I can do this, I’ve contributed to every major WordPress release since 2.5, am a member of the security team, meta team, and have helped lead BuddyPress & bbPress since 2009. I love WordPress and want to help it continue to grow in whatever direction it needs to take. I’m going to do it anyways, and with your help we can do it together.

Normally I’d start with a history lesson, but for this I’ll cut to the chase since most of you reading this already know what I’m talking about…

Projects like Django function & thrive without their REST API in every core installation. Not being in core means the component maintainers are able to develop independently of any core release cycle – both faster & slower if need be.

Externally facing API’s are a mouse on a wheel. Just as XML-RPC is ready, REST is here to take its place. Once we are all comfortable with REST 10 years from now, something else will replace it to, and so on.

WordPress is of the size and popularity now where building wp-admin into a hybrid of REST & AJAX calls will be confusing to future contributors.

Going back and rebuilding wp-admin to be fully REST is a thankless job, and would take ten years to complete at the current pace. It’s more likely wp-admin would be replaced, which would break backwards compatibility with existing plugins. It’s akin to hiding Windows 3.1 under the 95 skin, and abandoning all previous applications. No way.

Many contributors see a core-merge as a natural graduation path to their efforts, for lots of things – even small things like ideas and recommendations. We get attached to them, and define success & ultimate acceptance as the end result being gifted to the world via WordPress core.

That path is unrealistic for most, and unhealthy for WordPress in the long run. This is why plugins exist, and why plugins are awesome. The REST plugin is one of the most awesome ones, and it should stay that way so that the team of passionate people who love it, can continue to love it by totally avoiding core conflict.

Maybe it gets bundled with WordPress like Akismet does, but it needs to be activated to work. Maybe it comes active by default, and can be disabled or deleted for those who will never need it.

A merge into core, right now, I think is still not the right call.

For years I wanted, pleaded, planned, and architects the concept of user profiles and activity streams on WordPress.com. I still think it would have been enormously popular (and still can be) but looking back, I understand now how my ideas didn’t match Matt’s overall vision and needs for a product that he designed originally.

I think the REST API is a similar thing.

This project is so huge and the media attention so great, it is now bigger than itself. It’s almost less about the code/team/effort, and more about the spiritual support and endorsement of those efforts. Maybe that’s what I’m turned off by right now – that it’s been so long, it’s impolite to suggest a core-merge of this code isn’t the right thing to do.

FWIW, even if it had 100% API coverage, I still don’t think I’d want it on in core WordPress by default, because spiritually I’m really vibing on features like this being optional plugins right now, and I’m afraid that a core-merge will complicate the momentum and progress of the REST API core code once it’s in.

My $0.02. Don’t mean to offend. And I’m down to help anyone no matter what the end results ever are. 💜
P.S. I wrote this totally from the iOS app, which was a pretty pleasant experience. Great work, from a team of developers outside of WordPress core.

This weekend I’ll be driving down to WordCamp Chicago in my BRZ. It’s about 2 hours total from home to hotel, and I could use a playlist for the drive there and back.

I’ll be in Chicago chatting about the differences between BuddyPress and bbPress, and I’ll probably talk a bit about Prince, too. I’m on stage at 3pm, and I’d love it if you stopped by to say hi. 💜

If you’re using WordPress Multisite in a highly scalable environment using HyperDB or LudicrousDB, you may have seen global__r errors in your logs.

Can't select global__r... yada yada yada

The “global” part of “global__r” comes from these database drop-ins defaulting to a “global” dataset if nothing is found or explicitly passed in. The “__r” part comes from looking at databases intended for reading — databases designated as slaves (vs. master databases intended for writing “__w”).

So if a SELECT query is failing, why would that be?

The first and most logical reason is that the database is down. Check to make sure it’s not by attempting to communicate with the database directly via whatever you are most comfortable with (command line, SequalPRO, etc…)

The second most logical reason is that your web server (powering the PHP part of your application) is unable to reach your database server. Check to make sure fail2ban or some other firewall utility hasn’t erroneously blocked things, and then try to manually ping & connect the two servers together to ensure you receive a good response.

The final and less obvious reason this will occur, is harder to track down, and I think might be the source of your error log entries if everything else checks out and you’ve made it to this blog post after scouring the web for answers.

There are two queries that run inside of upgrade_network() and populate_options() respectfully, that try to delete all of the transients for a specific site and a specific network. These two queries are unlikely to get caught by the matching regex used to map database table names to what HyperDB or LudicrousDB use to route queries to their respective servers. They look something like this:

DELETE a, b FROM $wpdb->options a, $wpdb->options b

and

DELETE a, b FROM $wpdb->sitemeta a, $wpdb->sitemeta b

If you search all of WordPress, these are the only two places raw queries like this are done, and they’re only ran under specific conditions where WordPress is cleaning up after itself during a database upgrade. This means the conditions are perfect for a surprise entry in your error logs once in a blue moon when you aren’t hand-holding a huge WordPress multisite/multi-network database upgrade.

How do we prevent these, and what’s the repercussion? The solution is probably a regex fix upstream to these plugins and/or WordPress’s WPDB base class to properly match these queries. The repercussion is transients that don’t get deleted, which isn’t usually a huge problem unless it causes the database upgrade to continuously run; if that was the case, you’d have lots of entries in your error logs.

I have a hunch this issue is exacerbated by object caching plugins that store transients in memory and not in the database. In these types of installations, these raw queries are trying to delete data that never would have been there in the first place.

I’ve also been staring at this code back and forth for a few weeks now, and while there are a lot of moving parts, I haven’t identified any data corruption or loss issues, and these queries are properly escaped and prepared, so it’s unlikely HyperDB or LudicrousDB would introduce anything that might be harmful to existing data.

If you have these issues, hopefully this helps you isolate the root cause to identify whether this is a configuration issue, a caching issue, a communication issue, or an issue with the database itself. If you have more info, I’d love to hear about it in the comments below. <3

Running npm install for WordPress is a terrifying experience. It installs so many libraries and dependencies, it would take a lifetime to learn them all. If you’ve never had the pleasure, here’s what it looks like today:

WordPress@4.5.0 /Users/johnjamesjacoby/Work/VVV/www/wordpress-develop
├─┬ autoprefixer@6.1.2
│ ├── browserslist@1.0.1
│ ├── caniuse-db@1.0.30000384
│ ├── num2fraction@1.2.2
│ ├─┬ postcss@5.0.14
│ │ ├── js-base64@2.1.9
│ │ ├── source-map@0.5.3
│ │ └─┬ supports-color@3.1.2
│ │   └── has-flag@1.0.0
│ └── postcss-value-parser@3.2.3
├─┬ grunt@0.4.5
│ ├── async@0.1.22
│ ├── coffee-script@1.3.3
│ ├── colors@0.6.2
│ ├── dateformat@1.0.2-1.2.3
│ ├── eventemitter2@0.4.14
│ ├── exit@0.1.2
│ ├─┬ findup-sync@0.1.3
│ │ ├─┬ glob@3.2.11
│ │ │ └── minimatch@0.3.0
│ │ └── lodash@2.4.2
│ ├── getobject@0.1.0
│ ├─┬ glob@3.1.21
│ │ ├── graceful-fs@1.2.3
│ │ └── inherits@1.0.2
│ ├─┬ grunt-legacy-log@0.1.3
│ │ ├─┬ grunt-legacy-log-utils@0.1.1
│ │ │ ├── lodash@2.4.2
│ │ │ └── underscore.string@2.3.3
│ │ ├── lodash@2.4.2
│ │ └── underscore.string@2.3.3
│ ├── hooker@0.2.3
│ ├── iconv-lite@0.2.11
│ ├─┬ js-yaml@2.0.5
│ │ └─┬ argparse@0.1.16
│ │   ├── underscore@1.7.0
│ │   └── underscore.string@2.4.0
│ ├── lodash@0.9.2
│ ├─┬ minimatch@0.2.14
│ │ ├── lru-cache@2.7.3
│ │ └── sigmund@1.0.1
│ ├─┬ nopt@1.0.10
│ │ └── abbrev@1.0.7
│ ├── rimraf@2.2.8
│ ├── underscore.string@2.2.1
│ └── which@1.0.9
├─┬ grunt-browserify@4.0.1
│ ├── async@0.9.2
│ ├─┬ browserify@11.2.0
│ │ ├── assert@1.3.0
│ │ ├─┬ browser-pack@5.0.1
│ │ │ ├─┬ combine-source-map@0.6.1
│ │ │ │ ├── convert-source-map@1.1.3
│ │ │ │ ├─┬ inline-source-map@0.5.0
│ │ │ │ │ └── source-map@0.4.4
│ │ │ │ ├── lodash.memoize@3.0.4
│ │ │ │ └── source-map@0.4.4
│ │ │ └── umd@3.0.1
│ │ ├── browser-resolve@1.11.0
│ │ ├─┬ browserify-zlib@0.1.4
│ │ │ └── pako@0.2.8
│ │ ├─┬ buffer@3.6.0
│ │ │ ├── base64-js@0.0.8
│ │ │ ├── ieee754@1.1.6
│ │ │ └── isarray@1.0.0
│ │ ├── builtins@0.0.7
│ │ ├── commondir@0.0.1
│ │ ├─┬ concat-stream@1.4.10
│ │ │ ├── readable-stream@1.1.13
│ │ │ └── typedarray@0.0.6
│ │ ├─┬ console-browserify@1.1.0
│ │ │ └── date-now@0.1.4
│ │ ├── constants-browserify@0.0.1
│ │ ├─┬ crypto-browserify@3.11.0
│ │ │ ├─┬ browserify-cipher@1.0.0
│ │ │ │ ├─┬ browserify-aes@1.0.5
│ │ │ │ │ └── buffer-xor@1.0.3
│ │ │ │ ├─┬ browserify-des@1.0.0
│ │ │ │ │ └─┬ des.js@1.0.0
│ │ │ │ │   └── minimalistic-assert@1.0.0
│ │ │ │ └── evp_bytestokey@1.0.0
│ │ │ ├─┬ browserify-sign@4.0.0
│ │ │ │ ├── bn.js@4.6.2
│ │ │ │ ├── browserify-rsa@4.0.0
│ │ │ │ ├─┬ elliptic@6.0.2
│ │ │ │ │ ├── brorand@1.0.5
│ │ │ │ │ └── hash.js@1.0.3
│ │ │ │ └─┬ parse-asn1@5.0.0
│ │ │ │   └── asn1.js@4.3.0
│ │ │ ├── create-ecdh@4.0.0
│ │ │ ├─┬ create-hash@1.1.2
│ │ │ │ ├── cipher-base@1.0.2
│ │ │ │ └── ripemd160@1.0.1
│ │ │ ├── create-hmac@1.1.4
│ │ │ ├─┬ diffie-hellman@5.0.0
│ │ │ │ └── miller-rabin@4.0.0
│ │ │ ├── pbkdf2@3.0.4
│ │ │ ├── public-encrypt@4.0.0
│ │ │ └── randombytes@2.0.1
│ │ ├── defined@1.0.0
│ │ ├── deps-sort@1.3.9
│ │ ├── domain-browser@1.1.7
│ │ ├─┬ duplexer2@0.0.2
│ │ │ └── readable-stream@1.1.13
│ │ ├── events@1.0.2
│ │ ├─┬ glob@4.5.3
│ │ │ └── minimatch@2.0.10
│ │ ├─┬ has@1.0.1
│ │ │ └── function-bind@1.0.2
│ │ ├── htmlescape@1.1.0
│ │ ├── https-browserify@0.0.1
│ │ ├── inherits@2.0.1
│ │ ├─┬ insert-module-globals@6.6.3
│ │ │ ├── is-buffer@1.1.1
│ │ │ └─┬ lexical-scope@1.2.0
│ │ │   └── astw@2.0.0
│ │ ├── isarray@0.0.1
│ │ ├─┬ JSONStream@1.0.7
│ │ │ ├── jsonparse@1.2.0
│ │ │ └── through@2.3.8
│ │ ├─┬ labeled-stream-splicer@1.0.2
│ │ │ └─┬ stream-splicer@1.3.2
│ │ │   └── readable-stream@1.1.13
│ │ ├─┬ module-deps@3.9.1
│ │ │ ├── detective@4.3.1
│ │ │ ├── readable-stream@1.1.13
│ │ │ └─┬ stream-combiner2@1.0.2
│ │ │   └─┬ through2@0.5.1
│ │ │     ├── readable-stream@1.0.33
│ │ │     └── xtend@3.0.0
│ │ ├── os-browserify@0.1.2
│ │ ├─┬ parents@1.0.1
│ │ │ └── path-platform@0.11.15
│ │ ├── path-browserify@0.0.0
│ │ ├── process@0.11.2
│ │ ├── punycode@1.4.0
│ │ ├── querystring-es3@0.2.1
│ │ ├─┬ read-only-stream@1.1.1
│ │ │ ├── readable-stream@1.1.13
│ │ │ └─┬ readable-wrap@1.0.0
│ │ │   └── readable-stream@1.1.13
│ │ ├─┬ readable-stream@2.0.5
│ │ │ ├── core-util-is@1.0.2
│ │ │ ├── process-nextick-args@1.0.6
│ │ │ └── util-deprecate@1.0.2
│ │ ├─┬ shasum@1.0.2
│ │ │ ├─┬ json-stable-stringify@0.0.1
│ │ │ │ └── jsonify@0.0.0
│ │ │ └── sha.js@2.4.4
│ │ ├── shell-quote@0.0.1
│ │ ├── stream-browserify@2.0.1
│ │ ├─┬ stream-http@1.7.1
│ │ │ ├── builtin-status-codes@1.0.0
│ │ │ ├── foreach@2.0.5
│ │ │ ├── indexof@0.0.1
│ │ │ └── object-keys@1.0.9
│ │ ├── string_decoder@0.10.31
│ │ ├─┬ subarg@1.0.0
│ │ │ └── minimist@1.2.0
│ │ ├─┬ syntax-error@1.1.4
│ │ │ └── acorn@1.2.2
│ │ ├─┬ through2@1.1.1
│ │ │ └── readable-stream@1.1.13
│ │ ├── timers-browserify@1.4.2
│ │ ├── tty-browserify@0.0.0
│ │ ├─┬ url@0.10.3
│ │ │ ├── punycode@1.3.2
│ │ │ └── querystring@0.2.0
│ │ ├── util@0.10.3
│ │ ├── vm-browserify@0.0.4
│ │ └── xtend@4.0.1
│ ├─┬ glob@5.0.15
│ │ ├─┬ inflight@1.0.4
│ │ │ └── wrappy@1.0.1
│ │ ├─┬ minimatch@3.0.0
│ │ │ └─┬ brace-expansion@1.1.2
│ │ │   ├── balanced-match@0.3.0
│ │ │   └── concat-map@0.0.1
│ │ ├── once@1.3.3
│ │ └── path-is-absolute@1.0.0
│ ├── lodash@3.10.1
│ ├── resolve@1.1.6
│ └─┬ watchify@3.6.1
│   ├─┬ anymatch@1.3.0
│   │ ├── arrify@1.0.1
│   │ └─┬ micromatch@2.3.7
│   │   ├─┬ arr-diff@2.0.0
│   │   │ └── arr-flatten@1.0.1
│   │   ├── array-unique@0.2.1
│   │   ├─┬ braces@1.8.3
│   │   │ ├─┬ expand-range@1.8.1
│   │   │ │ └─┬ fill-range@2.2.3
│   │   │ │   ├── is-number@2.1.0
│   │   │ │   ├── isobject@2.0.0
│   │   │ │   ├── randomatic@1.1.5
│   │   │ │   └── repeat-string@1.5.2
│   │   │ ├── preserve@0.2.0
│   │   │ └── repeat-element@1.1.2
│   │   ├── expand-brackets@0.1.4
│   │   ├─┬ extglob@0.3.1
│   │   │ ├─┬ ansi-green@0.1.1
│   │   │ │ └── ansi-wrap@0.1.0
│   │   │ └── success-symbol@0.1.0
│   │   ├── filename-regex@2.0.0
│   │   ├── is-extglob@1.0.0
│   │   ├── kind-of@3.0.2
│   │   ├── normalize-path@2.0.1
│   │   ├─┬ object.omit@2.0.0
│   │   │ ├─┬ for-own@0.1.3
│   │   │ │ └── for-in@0.1.4
│   │   │ └── is-extendable@0.1.1
│   │   ├─┬ parse-glob@3.0.4
│   │   │ ├── glob-base@0.3.0
│   │   │ └── is-dotfile@1.0.2
│   │   └─┬ regex-cache@0.4.2
│   │     ├── is-equal-shallow@0.1.3
│   │     └── is-primitive@2.0.0
│   ├─┬ browserify@12.0.1
│   │ ├─┬ browser-pack@6.0.1
│   │ │ └─┬ combine-source-map@0.7.1
│   │ │   ├── inline-source-map@0.6.1
│   │ │   └── source-map@0.4.2
│   │ ├── concat-stream@1.5.1
│   │ ├── constants-browserify@1.0.0
│   │ ├── deps-sort@2.0.0
│   │ ├── duplexer2@0.1.4
│   │ ├── events@1.1.0
│   │ ├─┬ glob@5.0.15
│   │ │ └── minimatch@3.0.0
│   │ ├── insert-module-globals@7.0.1
│   │ ├─┬ labeled-stream-splicer@2.0.0
│   │ │ └── stream-splicer@2.0.0
│   │ ├─┬ module-deps@4.0.5
│   │ │ └── stream-combiner2@1.1.1
│   │ ├── read-only-stream@2.0.0
│   │ ├── shell-quote@1.4.3
│   │ ├── stream-http@2.0.2
│   │ └─┬ url@0.11.0
│   │   └── punycode@1.3.2
│   ├─┬ chokidar@1.4.2
│   │ ├── async-each@0.1.6
│   │ ├─┬ fsevents@1.0.6
│   │ │ └─┬ node-pre-gyp@0.6.17
│   │ │   ├─┬ mkdirp@0.5.1
│   │ │   │ └── minimist@0.0.8
│   │ │   ├─┬ nopt@3.0.6
│   │ │   │ └── abbrev@1.0.7
│   │ │   ├─┬ npmlog@2.0.0
│   │ │   │ ├── ansi@0.3.0
│   │ │   │ ├─┬ are-we-there-yet@1.0.4
│   │ │   │ │ ├── delegates@0.1.0
│   │ │   │ │ └─┬ readable-stream@1.1.13
│   │ │   │ │   ├── core-util-is@1.0.2
│   │ │   │ │   ├── isarray@0.0.1
│   │ │   │ │   └── string_decoder@0.10.31
│   │ │   │ └─┬ gauge@1.2.2
│   │ │   │   ├── has-unicode@1.0.1
│   │ │   │   ├─┬ lodash.pad@3.1.1
│   │ │   │   │ ├── lodash._basetostring@3.0.1
│   │ │   │   │ └─┬ lodash._createpadding@3.6.1
│   │ │   │   │   └── lodash.repeat@3.0.1
│   │ │   │   ├── lodash.padleft@3.1.1
│   │ │   │   └── lodash.padright@3.1.1
│   │ │   ├─┬ rc@1.1.5
│   │ │   │ ├── ini@1.3.4
│   │ │   │ ├── minimist@1.2.0
│   │ │   │ └── strip-json-comments@1.0.4
│   │ │   ├─┬ request@2.67.0
│   │ │   │ ├── aws-sign2@0.6.0
│   │ │   │ ├─┬ bl@1.0.0
│   │ │   │ │ └─┬ readable-stream@2.0.4
│   │ │   │ │   ├── core-util-is@1.0.2
│   │ │   │ │   ├── inherits@2.0.1
│   │ │   │ │   ├── isarray@0.0.1
│   │ │   │ │   ├── string_decoder@0.10.31
│   │ │   │ │   └── util-deprecate@1.0.2
│   │ │   │ ├── caseless@0.11.0
│   │ │   │ ├─┬ combined-stream@1.0.5
│   │ │   │ │ └── delayed-stream@1.0.0
│   │ │   │ ├── extend@3.0.0
│   │ │   │ ├── forever-agent@0.6.1
│   │ │   │ ├── form-data@1.0.0-rc3
│   │ │   │ ├─┬ har-validator@2.0.3
│   │ │   │ │ ├─┬ chalk@1.1.1
│   │ │   │ │ │ ├── ansi-styles@2.1.0
│   │ │   │ │ │ ├─┬ has-ansi@2.0.0
│   │ │   │ │ │ │ └── ansi-regex@2.0.0
│   │ │   │ │ │ ├── strip-ansi@3.0.0
│   │ │   │ │ │ └── supports-color@2.0.0
│   │ │   │ │ ├─┬ commander@2.9.0
│   │ │   │ │ │ └── graceful-readlink@1.0.1
│   │ │   │ │ ├─┬ is-my-json-valid@2.12.3
│   │ │   │ │ │ ├── generate-function@2.0.0
│   │ │   │ │ │ ├─┬ generate-object-property@1.2.0
│   │ │   │ │ │ │ └── is-property@1.0.2
│   │ │   │ │ │ ├── jsonpointer@2.0.0
│   │ │   │ │ │ └── xtend@4.0.1
│   │ │   │ │ └─┬ pinkie-promise@2.0.0
│   │ │   │ │   └── pinkie@2.0.1
│   │ │   │ ├─┬ hawk@3.1.2
│   │ │   │ │ ├── boom@2.10.1
│   │ │   │ │ ├── cryptiles@2.0.5
│   │ │   │ │ ├── hoek@2.16.3
│   │ │   │ │ └── sntp@1.0.9
│   │ │   │ ├─┬ http-signature@1.1.0
│   │ │   │ │ ├── assert-plus@0.1.5
│   │ │   │ │ ├─┬ jsprim@1.2.2
│   │ │   │ │ │ ├── extsprintf@1.0.2
│   │ │   │ │ │ ├── json-schema@0.2.2
│   │ │   │ │ │ └── verror@1.3.6
│   │ │   │ │ └─┬ sshpk@1.7.0
│   │ │   │ │   ├── asn1@0.2.3
│   │ │   │ │   ├── assert-plus@0.2.0
│   │ │   │ │   ├── ecc-jsbn@0.1.1
│   │ │   │ │   ├── jodid25519@1.0.2
│   │ │   │ │   └── jsbn@0.1.0
│   │ │   │ ├── is-typedarray@1.0.0
│   │ │   │ ├── isstream@0.1.2
│   │ │   │ ├── json-stringify-safe@5.0.1
│   │ │   │ ├── node-uuid@1.4.7
│   │ │   │ ├── oauth-sign@0.8.0
│   │ │   │ ├── qs@5.2.0
│   │ │   │ ├── stringstream@0.0.5
│   │ │   │ └── tough-cookie@2.2.1
│   │ │   ├─┬ rimraf@2.4.4
│   │ │   │ └─┬ glob@5.0.15
│   │ │   │   ├─┬ inflight@1.0.4
│   │ │   │   │ └── wrappy@1.0.1
│   │ │   │   ├── inherits@2.0.1
│   │ │   │   ├─┬ minimatch@3.0.0
│   │ │   │   │ └─┬ brace-expansion@1.1.1
│   │ │   │   │   └── concat-map@0.0.1
│   │ │   │   ├─┬ once@1.3.3
│   │ │   │   │ └── wrappy@1.0.1
│   │ │   │   └── path-is-absolute@1.0.0
│   │ │   ├── semver@5.1.0
│   │ │   ├─┬ tar@2.2.1
│   │ │   │ ├── block-stream@0.0.8
│   │ │   │ ├── fstream@1.0.8
│   │ │   │ └── inherits@2.0.1
│   │ │   └─┬ tar-pack@3.1.0
│   │ │     ├── debug@0.7.4
│   │ │     ├─┬ fstream-ignore@1.0.3
│   │ │     │ └─┬ minimatch@3.0.0
│   │ │     │   └─┬ brace-expansion@1.1.1
│   │ │     │     └── concat-map@0.0.1
│   │ │     ├── graceful-fs@4.1.2
│   │ │     ├─┬ readable-stream@1.0.33
│   │ │     │ ├── core-util-is@1.0.2
│   │ │     │ ├── inherits@2.0.1
│   │ │     │ ├── isarray@0.0.1
│   │ │     │ └── string_decoder@0.10.31
│   │ │     └── rimraf@2.2.8
│   │ ├── glob-parent@2.0.0
│   │ ├─┬ is-binary-path@1.0.1
│   │ │ └── binary-extensions@1.4.0
│   │ ├── is-glob@2.0.1
│   │ └─┬ readdirp@2.0.0
│   │   ├── graceful-fs@4.1.2
│   │   └── minimatch@2.0.10
│   ├─┬ outpipe@1.1.1
│   │ └─┬ shell-quote@1.4.3
│   │   ├── array-filter@0.0.1
│   │   ├── array-map@0.0.0
│   │   └── array-reduce@0.0.0
│   └── through2@2.0.0
├── grunt-contrib-clean@0.6.0
├─┬ grunt-contrib-compress@0.14.0
│ ├─┬ archiver@0.16.0
│ │ ├── async@1.4.2
│ │ ├── buffer-crc32@0.2.5
│ │ ├─┬ glob@5.0.15
│ │ │ └── minimatch@3.0.0
│ │ ├─┬ lazystream@0.1.0
│ │ │ └── readable-stream@1.0.33
│ │ ├── lodash@3.10.1
│ │ ├── readable-stream@1.0.33
│ │ ├─┬ tar-stream@1.2.2
│ │ │ ├── bl@1.0.0
│ │ │ └── end-of-stream@1.1.0
│ │ └─┬ zip-stream@0.6.0
│ │   ├─┬ compress-commons@0.3.0
│ │   │ ├─┬ crc32-stream@0.3.4
│ │   │ │ └── readable-stream@1.0.33
│ │   │ ├── node-int64@0.4.0
│ │   │ └── readable-stream@1.0.33
│ │   ├── lodash@3.10.1
│ │   └── readable-stream@1.0.33
│ ├─┬ chalk@1.1.1
│ │ ├── ansi-styles@2.1.0
│ │ ├── escape-string-regexp@1.0.4
│ │ ├─┬ has-ansi@2.0.0
│ │ │ └── ansi-regex@2.0.0
│ │ ├── strip-ansi@3.0.0
│ │ └── supports-color@2.0.0
│ └─┬ pretty-bytes@2.0.1
│   ├── get-stdin@4.0.1
│   ├─┬ meow@3.7.0
│   │ ├─┬ camelcase-keys@2.0.0
│   │ │ └── camelcase@2.0.1
│   │ ├── decamelize@1.1.2
│   │ ├─┬ loud-rejection@1.2.0
│   │ │ └── signal-exit@2.1.2
│   │ ├── map-obj@1.0.1
│   │ ├─┬ normalize-package-data@2.3.5
│   │ │ ├── hosted-git-info@2.1.4
│   │ │ ├─┬ is-builtin-module@1.0.0
│   │ │ │ └── builtin-modules@1.1.1
│   │ │ └─┬ validate-npm-package-license@3.0.1
│   │ │   ├─┬ spdx-correct@1.0.2
│   │ │   │ └── spdx-license-ids@1.1.0
│   │ │   └─┬ spdx-expression-parse@1.0.2
│   │ │     └── spdx-exceptions@1.0.4
│   │ ├─┬ read-pkg-up@1.0.1
│   │ │ ├─┬ find-up@1.1.0
│   │ │ │ └── path-exists@2.1.0
│   │ │ └─┬ read-pkg@1.1.0
│   │ │   ├─┬ load-json-file@1.1.0
│   │ │   │ ├── graceful-fs@4.1.2
│   │ │   │ ├─┬ parse-json@2.2.0
│   │ │   │ │ └─┬ error-ex@1.3.0
│   │ │   │ │   └── is-arrayish@0.2.1
│   │ │   │ └── pify@2.3.0
│   │ │   └─┬ path-type@1.1.0
│   │ │     └── graceful-fs@4.1.2
│   │ ├─┬ redent@1.0.0
│   │ │ ├─┬ indent-string@2.1.0
│   │ │ │ └─┬ repeating@2.0.0
│   │ │ │   └── is-finite@1.0.1
│   │ │ └── strip-indent@1.0.1
│   │ └── trim-newlines@1.0.0
│   └── number-is-nan@1.0.0
├─┬ grunt-contrib-concat@0.5.1
│ ├─┬ chalk@0.5.1
│ │ ├── ansi-styles@1.1.0
│ │ ├─┬ has-ansi@0.1.0
│ │ │ └── ansi-regex@0.2.1
│ │ ├── strip-ansi@0.3.0
│ │ └── supports-color@0.2.0
│ └─┬ source-map@0.3.0
│   └── amdefine@1.0.0
├─┬ grunt-contrib-copy@0.8.2
│ └── file-sync-cmp@0.1.1
├─┬ grunt-contrib-cssmin@0.14.0
│ ├─┬ clean-css@3.4.9
│ │ ├─┬ commander@2.8.1
│ │ │ └── graceful-readlink@1.0.1
│ │ └── source-map@0.4.4
│ └─┬ maxmin@1.1.0
│   ├── figures@1.4.0
│   ├── gzip-size@1.0.0
│   └── pretty-bytes@1.0.4
├─┬ grunt-contrib-imagemin@1.0.0
│ ├── async@0.9.2
│ ├── gulp-rename@1.2.2
│ ├─┬ imagemin@4.0.0
│ │ ├─┬ buffer-to-vinyl@1.1.0
│ │ │ ├── file-type@3.4.0
│ │ │ ├── uuid@2.0.1
│ │ │ └─┬ vinyl@1.1.0
│ │ │   ├── clone@1.0.2
│ │ │   ├── clone-stats@0.0.1
│ │ │   └── replace-ext@0.0.1
│ │ ├─┬ imagemin-gifsicle@4.2.0
│ │ │ ├─┬ gifsicle@3.0.3
│ │ │ │ ├─┬ bin-build@2.2.0
│ │ │ │ │ ├── archive-type@3.2.0
│ │ │ │ │ ├─┬ decompress@3.0.0
│ │ │ │ │ │ ├─┬ decompress-tar@3.1.0
│ │ │ │ │ │ │ ├── is-tar@1.0.0
│ │ │ │ │ │ │ ├── object-assign@2.1.1
│ │ │ │ │ │ │ ├─┬ strip-dirs@1.1.1
│ │ │ │ │ │ │ │ ├── is-natural-number@2.0.0
│ │ │ │ │ │ │ │ └── sum-up@1.0.2
│ │ │ │ │ │ │ ├─┬ through2@0.6.5
│ │ │ │ │ │ │ │ └── readable-stream@1.0.33
│ │ │ │ │ │ │ └─┬ vinyl@0.4.6
│ │ │ │ │ │ │   └── clone@0.2.0
│ │ │ │ │ │ ├─┬ decompress-tarbz2@3.1.0
│ │ │ │ │ │ │ ├── is-bzip2@1.0.0
│ │ │ │ │ │ │ ├── object-assign@2.1.1
│ │ │ │ │ │ │ ├── seek-bzip@1.0.5
│ │ │ │ │ │ │ ├─┬ through2@0.6.5
│ │ │ │ │ │ │ │ └── readable-stream@1.0.33
│ │ │ │ │ │ │ └─┬ vinyl@0.4.6
│ │ │ │ │ │ │   └── clone@0.2.0
│ │ │ │ │ │ ├─┬ decompress-targz@3.1.0
│ │ │ │ │ │ │ ├── is-gzip@1.0.0
│ │ │ │ │ │ │ ├── object-assign@2.1.1
│ │ │ │ │ │ │ ├─┬ through2@0.6.5
│ │ │ │ │ │ │ │ └── readable-stream@1.0.33
│ │ │ │ │ │ │ └─┬ vinyl@0.4.6
│ │ │ │ │ │ │   └── clone@0.2.0
│ │ │ │ │ │ ├─┬ decompress-unzip@3.4.0
│ │ │ │ │ │ │ ├── is-zip@1.0.0
│ │ │ │ │ │ │ ├── stat-mode@0.2.1
│ │ │ │ │ │ │ ├── through2@2.0.0
│ │ │ │ │ │ │ └─┬ yauzl@2.4.1
│ │ │ │ │ │ │   └─┬ fd-slicer@1.0.1
│ │ │ │ │ │ │     └── pend@1.2.0
│ │ │ │ │ │ ├─┬ stream-combiner2@1.1.1
│ │ │ │ │ │ │ └── duplexer2@0.1.4
│ │ │ │ │ │ └── vinyl-assign@1.2.1
│ │ │ │ │ ├─┬ download@4.4.3
│ │ │ │ │ │ ├─┬ caw@1.2.0
│ │ │ │ │ │ │ ├─┬ get-proxy@1.0.1
│ │ │ │ │ │ │ │ └─┬ rc@0.5.5
│ │ │ │ │ │ │ │   ├── deep-extend@0.2.11
│ │ │ │ │ │ │ │   ├── minimist@0.0.10
│ │ │ │ │ │ │ │   └── strip-json-comments@0.1.3
│ │ │ │ │ │ │ ├── is-obj@1.0.0
│ │ │ │ │ │ │ └── object-assign@3.0.0
│ │ │ │ │ │ ├─┬ filenamify@1.2.0
│ │ │ │ │ │ │ ├── filename-reserved-regex@1.0.0
│ │ │ │ │ │ │ ├── strip-outer@1.0.0
│ │ │ │ │ │ │ └── trim-repeated@1.0.0
│ │ │ │ │ │ ├─┬ got@5.3.0
│ │ │ │ │ │ │ ├─┬ create-error-class@2.0.1
│ │ │ │ │ │ │ │ └── capture-stack-trace@1.0.0
│ │ │ │ │ │ │ ├── is-plain-obj@1.1.0
│ │ │ │ │ │ │ ├── is-redirect@1.0.0
│ │ │ │ │ │ │ ├── lowercase-keys@1.0.0
│ │ │ │ │ │ │ ├── node-status-codes@1.0.0
│ │ │ │ │ │ │ ├── timed-out@2.0.0
│ │ │ │ │ │ │ ├── unzip-response@1.0.0
│ │ │ │ │ │ │ └─┬ url-parse-lax@1.0.0
│ │ │ │ │ │ │   └── prepend-http@1.0.3
│ │ │ │ │ │ ├─┬ gulp-decompress@1.2.0
│ │ │ │ │ │ │ └─┬ gulp-util@3.0.7
│ │ │ │ │ │ │   ├── array-differ@1.0.0
│ │ │ │ │ │ │   ├── array-uniq@1.0.2
│ │ │ │ │ │ │   ├── beeper@1.1.0
│ │ │ │ │ │ │   ├── dateformat@1.0.12
│ │ │ │ │ │ │   ├─┬ fancy-log@1.1.0
│ │ │ │ │ │ │   │ └── dateformat@1.0.12
│ │ │ │ │ │ │   ├─┬ gulplog@1.0.0
│ │ │ │ │ │ │   │ └── glogg@1.0.0
│ │ │ │ │ │ │   ├─┬ has-gulplog@0.1.0
│ │ │ │ │ │ │   │ └── sparkles@1.0.0
│ │ │ │ │ │ │   ├── lodash._reescape@3.0.0
│ │ │ │ │ │ │   ├── lodash._reevaluate@3.0.0
│ │ │ │ │ │ │   ├── lodash._reinterpolate@3.0.0
│ │ │ │ │ │ │   ├─┬ lodash.template@3.6.2
│ │ │ │ │ │ │   │ ├── lodash._basecopy@3.0.1
│ │ │ │ │ │ │   │ ├── lodash._basevalues@3.0.0
│ │ │ │ │ │ │   │ ├── lodash._isiterateecall@3.0.9
│ │ │ │ │ │ │   │ ├── lodash.escape@3.0.0
│ │ │ │ │ │ │   │ ├─┬ lodash.keys@3.1.2
│ │ │ │ │ │ │   │ │ ├── lodash._getnative@3.9.1
│ │ │ │ │ │ │   │ │ ├── lodash.isarguments@3.0.4
│ │ │ │ │ │ │   │ │ └── lodash.isarray@3.0.4
│ │ │ │ │ │ │   │ ├── lodash.restparam@3.6.1
│ │ │ │ │ │ │   │ └── lodash.templatesettings@3.1.0
│ │ │ │ │ │ │   ├── multipipe@0.1.2
│ │ │ │ │ │ │   ├── object-assign@3.0.0
│ │ │ │ │ │ │   ├── through2@2.0.0
│ │ │ │ │ │ │   └── vinyl@0.5.3
│ │ │ │ │ │ ├── is-url@1.2.1
│ │ │ │ │ │ ├─┬ read-all-stream@3.0.1
│ │ │ │ │ │ │ └─┬ pinkie-promise@1.0.0
│ │ │ │ │ │ │   └── pinkie@1.0.0
│ │ │ │ │ │ ├─┬ stream-combiner2@1.1.1
│ │ │ │ │ │ │ └── duplexer2@0.1.4
│ │ │ │ │ │ └─┬ ware@1.3.0
│ │ │ │ │ │   └─┬ wrap-fn@0.1.4
│ │ │ │ │ │     └── co@3.1.0
│ │ │ │ │ ├─┬ exec-series@1.0.2
│ │ │ │ │ │ └── async-each-series@1.1.0
│ │ │ │ │ └─┬ url-regex@3.1.0
│ │ │ │ │   └── ip-regex@1.0.3
│ │ │ │ ├─┬ bin-wrapper@3.0.2
│ │ │ │ │ ├─┬ bin-check@2.0.0
│ │ │ │ │ │ └── executable@1.1.0
│ │ │ │ │ ├─┬ bin-version-check@2.1.0
│ │ │ │ │ │ ├─┬ bin-version@1.0.4
│ │ │ │ │ │ │ └─┬ find-versions@1.2.1
│ │ │ │ │ │ │   └── semver-regex@1.0.0
│ │ │ │ │ │ ├── semver@4.3.6
│ │ │ │ │ │ └── semver-truncate@1.1.0
│ │ │ │ │ ├── lazy-req@1.1.0
│ │ │ │ │ └── os-filter-obj@1.0.3
│ │ │ │ └─┬ logalot@2.1.0
│ │ │ │   └─┬ squeak@1.3.0
│ │ │ │     ├── console-stream@0.1.1
│ │ │ │     └─┬ lpad-align@1.1.0
│ │ │ │       ├── longest@1.0.1
│ │ │ │       └── lpad@2.0.1
│ │ │ ├── is-gif@1.0.0
│ │ │ └─┬ through2@0.6.5
│ │ │   └── readable-stream@1.0.33
│ │ ├─┬ imagemin-jpegtran@4.3.2
│ │ │ ├── is-jpg@1.0.0
│ │ │ ├── jpegtran-bin@3.0.6
│ │ │ └── through2@2.0.0
│ │ ├─┬ imagemin-optipng@4.3.0
│ │ │ ├─┬ exec-buffer@2.0.1
│ │ │ │ └── tempfile@1.1.1
│ │ │ ├── is-png@1.0.0
│ │ │ ├── optipng-bin@3.0.4
│ │ │ └─┬ through2@0.6.5
│ │ │   └── readable-stream@1.0.33
│ │ ├─┬ imagemin-svgo@4.2.0
│ │ │ ├── is-svg@1.1.1
│ │ │ ├─┬ svgo@0.6.1
│ │ │ │ ├─┬ coa@1.0.1
│ │ │ │ │ └── q@1.4.1
│ │ │ │ ├── colors@1.1.2
│ │ │ │ ├─┬ csso@1.4.4
│ │ │ │ │ └── clap@1.0.10
│ │ │ │ ├─┬ js-yaml@3.4.6
│ │ │ │ │ ├─┬ argparse@1.0.3
│ │ │ │ │ │ ├── lodash@3.10.1
│ │ │ │ │ │ └── sprintf-js@1.0.3
│ │ │ │ │ ├── esprima@2.7.1
│ │ │ │ │ └── inherit@2.2.2
│ │ │ │ ├── sax@1.1.4
│ │ │ │ └── whet.extend@0.9.9
│ │ │ └── through2@2.0.0
│ │ ├── optional@0.1.3
│ │ ├─┬ stream-combiner2@1.1.1
│ │ │ └── duplexer2@0.1.4
│ │ └─┬ vinyl-fs@2.2.1
│ │   ├─┬ duplexify@3.4.2
│ │   │ └── end-of-stream@1.0.0
│ │   ├─┬ glob-stream@5.3.1
│ │   │ ├─┬ glob@5.0.15
│ │   │ │ └── minimatch@3.0.0
│ │   │ ├─┬ ordered-read-streams@0.3.0
│ │   │ │ └── is-stream@1.0.1
│ │   │ ├─┬ through2@0.6.5
│ │   │ │ └── readable-stream@1.0.33
│ │   │ ├─┬ to-absolute-glob@0.1.1
│ │   │ │ └── extend-shallow@2.0.1
│ │   │ └── unique-stream@2.2.0
│ │   ├── graceful-fs@4.1.2
│ │   ├─┬ gulp-sourcemaps@1.6.0
│ │   │ ├── graceful-fs@4.1.2
│ │   │ └── through2@2.0.0
│ │   ├── is-valid-glob@0.3.0
│ │   ├── merge-stream@1.0.0
│ │   ├─┬ strip-bom@2.0.0
│ │   │ └── is-utf8@0.2.1
│ │   ├─┬ strip-bom-stream@1.0.0
│ │   │ └── first-chunk-stream@1.0.0
│ │   ├── through2@2.0.0
│ │   └─┬ through2-filter@2.0.0
│ │     └── through2@2.0.0
│ └── pretty-bytes@1.0.4
├─┬ grunt-contrib-jshint@0.11.3
│ └─┬ jshint@2.8.0
│   ├─┬ cli@0.6.6
│   │ └─┬ glob@3.2.11
│   │   └── minimatch@0.3.0
│   ├─┬ htmlparser2@3.8.3
│   │ ├── domelementtype@1.3.0
│   │ ├── domhandler@2.3.0
│   │ ├─┬ domutils@1.5.1
│   │ │ └─┬ dom-serializer@0.1.0
│   │ │   ├── domelementtype@1.1.3
│   │ │   └── entities@1.1.1
│   │ ├── entities@1.0.0
│   │ └── readable-stream@1.1.13
│   ├── lodash@3.7.0
│   ├── minimatch@2.0.10
│   ├── shelljs@0.3.0
│   └── strip-json-comments@1.0.4
├─┬ grunt-contrib-qunit@0.7.0
│ └─┬ grunt-lib-phantomjs@0.6.0
│   ├─┬ phantomjs@1.9.19
│   │ ├── adm-zip@0.4.4
│   │ ├─┬ fs-extra@0.23.1
│   │ │ ├── graceful-fs@4.1.2
│   │ │ └── jsonfile@2.2.3
│   │ ├── kew@0.4.0
│   │ ├─┬ md5@2.0.0
│   │ │ ├── charenc@0.0.1
│   │ │ ├── crypt@0.0.1
│   │ │ └── is-buffer@1.0.2
│   │ ├─┬ npmconf@2.1.1
│   │ │ ├── nopt@3.0.6
│   │ │ └── semver@4.3.6
│   │ ├── progress@1.1.8
│   │ ├─┬ request@2.42.0
│   │ │ ├── aws-sign2@0.5.0
│   │ │ ├─┬ bl@0.9.4
│   │ │ │ └── readable-stream@1.0.33
│   │ │ ├── caseless@0.6.0
│   │ │ ├── hawk@1.1.1
│   │ │ ├── mime-types@1.0.2
│   │ │ ├── oauth-sign@0.4.0
│   │ │ └── qs@1.2.2
│   │ └─┬ request-progress@0.3.1
│   │   └── throttleit@0.0.2
│   ├── semver@1.0.14
│   └─┬ temporary@0.0.8
│     └── package@1.0.1
├─┬ grunt-contrib-uglify@0.10.1
│ ├─┬ chalk@1.0.0
│ │ ├─┬ has-ansi@1.0.3
│ │ │ └── ansi-regex@1.1.1
│ │ ├── strip-ansi@2.0.1
│ │ └── supports-color@1.3.1
│ ├── lodash@3.2.0
│ ├─┬ maxmin@1.0.1
│ │ └── pretty-bytes@1.0.4
│ ├─┬ uglify-js@2.5.0
│ │ ├── async@0.2.10
│ │ ├── uglify-to-browserify@1.0.2
│ │ └─┬ yargs@3.5.4
│ │   ├── camelcase@1.2.1
│ │   ├── window-size@0.1.0
│ │   └── wordwrap@0.0.2
│ └── uri-path@1.0.0
├─┬ grunt-contrib-watch@0.6.1
│ ├── async@0.2.10
│ ├─┬ gaze@0.5.2
│ │ └─┬ globule@0.1.0
│ │   └── lodash@1.0.2
│ ├── lodash@2.4.2
│ └─┬ tiny-lr-fork@0.0.5
│   ├── debug@0.7.4
│   ├── faye-websocket@0.4.4
│   ├─┬ noptify@0.0.3
│   │ └── nopt@2.0.0
│   └── qs@0.5.6
├── grunt-includes@0.5.2
├─┬ grunt-jsvalidate@0.2.2
│ └── esprima@1.0.4
├── grunt-legacy-util@0.2.0
├─┬ grunt-patch-wordpress@0.3.0
│ ├─┬ inquirer@0.2.5
│ │ ├── async@0.2.10
│ │ ├─┬ cli-color@0.2.3
│ │ │ ├── es5-ext@0.9.2
│ │ │ └─┬ memoizee@0.2.6
│ │ │   ├── event-emitter@0.2.2
│ │ │   └── next-tick@0.1.0
│ │ ├── lodash@1.2.1
│ │ └── mute-stream@0.0.3
│ ├─┬ request@2.27.0
│ │ ├── aws-sign@0.3.0
│ │ ├── cookie-jar@0.3.0
│ │ ├── forever-agent@0.5.2
│ │ ├─┬ form-data@0.1.4
│ │ │ ├── async@0.9.2
│ │ │ └─┬ combined-stream@0.0.7
│ │ │   └── delayed-stream@0.0.5
│ │ ├─┬ hawk@1.0.0
│ │ │ ├── boom@0.4.2
│ │ │ ├── cryptiles@0.2.2
│ │ │ ├── hoek@0.9.1
│ │ │ └── sntp@0.2.4
│ │ ├─┬ http-signature@0.10.1
│ │ │ ├── asn1@0.1.11
│ │ │ ├── assert-plus@0.1.5
│ │ │ └── ctype@0.5.3
│ │ ├── json-stringify-safe@5.0.1
│ │ ├── mime@1.2.11
│ │ ├── node-uuid@1.4.7
│ │ ├── oauth-sign@0.3.0
│ │ ├── qs@0.6.6
│ │ └── tunnel-agent@0.3.0
│ ├── underscore@1.5.2
│ └── underscore.string@2.3.3
├─┬ grunt-postcss@0.7.1
│ ├── diff@2.2.1
│ └── es6-promise@3.0.2
├─┬ grunt-rtlcss@1.6.0
│ └─┬ rtlcss@1.7.2
│   ├─┬ findup@0.1.5
│   │ └── commander@2.1.0
│   ├─┬ mkdirp@0.5.0
│   │ └── minimist@0.0.8
│   └── strip-json-comments@1.0.4
├─┬ grunt-sass@1.1.0
│ ├─┬ each-async@1.1.1
│ │ ├── onetime@1.1.0
│ │ └── set-immediate-shim@1.0.1
│ ├─┬ node-sass@3.4.2
│ │ ├── async-foreach@0.1.3
│ │ ├─┬ cross-spawn@2.1.4
│ │ │ ├─┬ cross-spawn-async@2.1.6
│ │ │ │ ├─┬ lru-cache@4.0.0
│ │ │ │ │ ├── pseudomap@1.0.2
│ │ │ │ │ └── yallist@2.0.0
│ │ │ │ └─┬ which@1.2.1
│ │ │ │   └─┬ is-absolute@0.1.7
│ │ │ │     └── is-relative@0.1.3
│ │ │ └─┬ spawn-sync@1.0.15
│ │ │   └── os-shim@0.1.3
│ │ ├─┬ glob@5.0.15
│ │ │ └── minimatch@3.0.0
│ │ ├─┬ mkdirp@0.5.1
│ │ │ └── minimist@0.0.8
│ │ ├── nan@2.1.0
│ │ ├─┬ node-gyp@3.2.1
│ │ │ ├─┬ fstream@1.0.8
│ │ │ │ └── graceful-fs@4.1.2
│ │ │ ├── graceful-fs@4.1.2
│ │ │ ├── minimatch@1.0.0
│ │ │ ├── nopt@3.0.6
│ │ │ ├─┬ npmlog@1.2.1
│ │ │ │ ├── ansi@0.3.0
│ │ │ │ ├─┬ are-we-there-yet@1.0.5
│ │ │ │ │ └── delegates@0.1.0
│ │ │ │ └─┬ gauge@1.2.2
│ │ │ │   ├── has-unicode@1.0.1
│ │ │ │   ├─┬ lodash.pad@3.1.1
│ │ │ │   │ ├── lodash._basetostring@3.0.1
│ │ │ │   │ └─┬ lodash._createpadding@3.6.1
│ │ │ │   │   └── lodash.repeat@3.0.1
│ │ │ │   ├── lodash.padleft@3.1.1
│ │ │ │   └── lodash.padright@3.1.1
│ │ │ ├─┬ osenv@0.1.3
│ │ │ │ ├── os-homedir@1.0.1
│ │ │ │ └── os-tmpdir@1.0.1
│ │ │ ├─┬ path-array@1.0.0
│ │ │ │ └── array-index@0.1.1
│ │ │ ├── semver@5.1.0
│ │ │ └─┬ tar@2.2.1
│ │ │   └── block-stream@0.0.8
│ │ ├─┬ npmconf@2.1.2
│ │ │ ├─┬ config-chain@1.1.9
│ │ │ │ └── proto-list@1.2.4
│ │ │ ├── ini@1.3.4
│ │ │ ├── nopt@3.0.6
│ │ │ ├── semver@4.3.6
│ │ │ └── uid-number@0.0.5
│ │ ├─┬ request@2.67.0
│ │ │ ├── aws-sign2@0.6.0
│ │ │ ├── caseless@0.11.0
│ │ │ ├─┬ combined-stream@1.0.5
│ │ │ │ └── delayed-stream@1.0.0
│ │ │ ├── extend@3.0.0
│ │ │ ├── forever-agent@0.6.1
│ │ │ ├─┬ form-data@1.0.0-rc3
│ │ │ │ └── async@1.5.1
│ │ │ ├─┬ har-validator@2.0.3
│ │ │ │ ├── commander@2.9.0
│ │ │ │ ├─┬ is-my-json-valid@2.12.3
│ │ │ │ │ ├── generate-function@2.0.0
│ │ │ │ │ ├─┬ generate-object-property@1.2.0
│ │ │ │ │ │ └── is-property@1.0.2
│ │ │ │ │ └── jsonpointer@2.0.0
│ │ │ │ └─┬ pinkie-promise@2.0.0
│ │ │ │   └── pinkie@2.0.1
│ │ │ ├─┬ hawk@3.1.2
│ │ │ │ ├── boom@2.10.1
│ │ │ │ ├── cryptiles@2.0.5
│ │ │ │ ├── hoek@2.16.3
│ │ │ │ └── sntp@1.0.9
│ │ │ ├─┬ http-signature@1.1.0
│ │ │ │ ├─┬ jsprim@1.2.2
│ │ │ │ │ ├── extsprintf@1.0.2
│ │ │ │ │ ├── json-schema@0.2.2
│ │ │ │ │ └── verror@1.3.6
│ │ │ │ └─┬ sshpk@1.7.2
│ │ │ │   ├── asn1@0.2.3
│ │ │ │   ├── assert-plus@0.2.0
│ │ │ │   ├── dashdash@1.11.0
│ │ │ │   ├── ecc-jsbn@0.1.1
│ │ │ │   ├── jodid25519@1.0.2
│ │ │ │   ├── jsbn@0.1.0
│ │ │ │   └── tweetnacl@0.13.3
│ │ │ ├── is-typedarray@1.0.0
│ │ │ ├── isstream@0.1.2
│ │ │ ├─┬ mime-types@2.1.9
│ │ │ │ └── mime-db@1.21.0
│ │ │ ├── oauth-sign@0.8.0
│ │ │ ├── qs@5.2.0
│ │ │ ├── stringstream@0.0.5
│ │ │ ├── tough-cookie@2.2.1
│ │ │ └── tunnel-agent@0.4.2
│ │ └─┬ sass-graph@2.0.1
│ │   ├─┬ glob@5.0.15
│ │   │ └── minimatch@3.0.0
│ │   ├── lodash@3.10.1
│ │   └─┬ yargs@3.31.0
│ │     ├─┬ cliui@3.1.0
│ │     │ └── wrap-ansi@1.0.0
│ │     ├─┬ os-locale@1.4.0
│ │     │ └─┬ lcid@1.0.0
│ │     │   └── invert-kv@1.0.0
│ │     ├─┬ string-width@1.0.1
│ │     │ ├── code-point-at@1.0.0
│ │     │ └── is-fullwidth-code-point@1.0.0
│ │     ├── window-size@0.1.4
│ │     └── y18n@3.2.0
│ └── object-assign@4.0.1
└─┬ matchdep@1.0.0
  ├─┬ findup-sync@0.3.0
  │ └─┬ glob@5.0.15
  │   └── minimatch@3.0.0
  ├─┬ globule@0.2.0
  │ ├─┬ glob@3.2.11
  │ │ └── minimatch@0.3.0
  │ ├── lodash@2.4.2
  │ └── minimatch@0.2.14
  └── stack-trace@0.0.9

And this is just for WordPress core; it doesn’t include the tools we use for BuddyPress or bbPress.

I don’t like this. I don’t like not knowing what these tools do or why they’re necessary. It feels like having one-thousand hammers instead of drill-bits and screwdrivers and power-tools. It’s not immediately obvious what the return on learning each or any of these unique tools is.

And when a tool becomes obsolete or out-of-date, the rabbit hole is full of eels:

npm WARN deprecated lodash@0.9.2: lodash@<2.0.0 is no longer maintained. Upgrade to lodash@^3.0.0
npm WARN deprecated npmconf@2.1.1: this package has been reintegrated into npm and is now out of date with respect to npm
npm WARN deprecated lodash@1.0.2: lodash@<2.0.0 is no longer maintained. Upgrade to lodash@^3.0.0
npm WARN deprecated lodash@1.2.1: lodash@<2.0.0 is no longer maintained. Upgrade to lodash@^3.0.0
npm WARN deprecated npmconf@2.1.2: this package has been reintegrated into npm and is now out of date with respect to npm
npm WARN deprecated lodash@2.4.2: lodash@<3.0.0 is no longer maintained. Upgrade to lodash@^3.0.0.
n

These relatively helpful messages may be outside of your control. They might be directly your fault. They might be globally installed modules or locally installed ones. Upgrading might be good for WordPress but break everything else you work on without warning.

Here’s usually what happens:

  • Something in the mystery toolbox breaks and complains about it
  • Try to upgrade the broken tool according to the feedback message(s)
  • The tool is still broken
  • Delete the entire `node_modules` directory and `npm install` again
  • Fixed!

Now, I understand what’s going on here is a miracle of modern software engineering. An enormous amount of automation is going on here, and the fact it actually works most of the time I’ll consider another closely coupled miracle. It still feels like there must be a better way, even though I can’t claim the fame of knowing right now what that might actually be.

I get that this is all awesome. I get that this process, and having & using these tools, is better than smashing things with rocks and hoping for the best. I get that a ton of work has gone into making this as seamless and wrinkle-free as possible.

I accept it, and work with it, and try not to think about it, but the trend of installing and trusting hundreds of tiny unknown libraries feels a little too organic and alive for a man-made computing machine. Like millions of nerve-endings and neurons and vessels and muscles working in unison to blink your eyes and sip on some coffee, the line between being a software developer or a software doctor is an increasingly jagged one.

If we aren’t careful, we’ll end up as lost amongst our own creations as we are inside of mother nature’s.

NSA REST

Direct Link and Mirrored Here, from March 25, 2011:

The key point to remember is that in order to ensure that an operation / implementation is RESTful, these methods must be used as they were defined in RFC 2616, the HTTP 1.1 specification. If implementations abuse these methods, they not only depart from RESTful behavior, but also jeopardize the application’s ability to interoperate with other RESTful capabilities.

In the past month I’ve released two WordPress plugins that I think ended up complementing each other rather nicely:

Together they make filtering posts in WordPress a much nicer experience:

Chosen + Pretty Filters

If you’re a plugin author that’s bundling the Chosen library or doing something interesting with post filters, I hope you’ll consider referring your users to install these instead.

Good plugins are small, purposeful, and they naturally blend into WordPress like bananas in a smoothie, but when you bundle libraries inside your large plugin, you will collide with other plugins that use that same library, and WordPress just isn’t designed to handle this very well quite yet.

Chances are, your plugin works great without Chosen. Users can still drop down a select box and type in it to find what they’re looking for, it’s just not a fancy experience. If you think your users would enjoy a fancy experience, maybe consider pointing them to WP Chosen instead, and update your plugin to support it and avoid the additional support burden while you’re at it.

P.S. All sites on Flox.io now have both of these active by default. <3