Sage, BrowserSync, and WordPress multisite

Update: I’ve got confirmation from the Sage dev’s that this is not common to Sage and is likely a result of my particular Vagrant box.

Update:┬áTurns out my original suspicions were true and this has nothing to do with Sage or BrowserSync. Turns out there is a known issue with VirtualBox and the way Apache cache’s certain files. The fix was a simple matter of adding (or un-commenting)

EnableSendfile Off

to my VM’s httpd.conf file and restarting apache.


As my comfort level with Sage increases I find that I like it more and more. Although I certainly wouldn’t say it’s the only way to do WordPress theme development, for those of us who’ve adopted modern front end development tools, it’s arguably exactly what you’d have if you tailored your development environment yourself. The choices to go with Gulp, SCSS and BrowserSync were all good ones in my opinion but the latter has a bit of an issue in multisite WordPress installs.

I currently don’t use Bedrock or Trellis which might be part of my issue. We have a custom Vagrant environment and I am not a Vagrant nor Gulp expert. Basically what I’m saying is your milage may vary. Upon a fresh WordPress multisite and properly configured Sage install, BrowserSync will run, send messages to the browser, the control panel works etc. The CSS however will simply not update. Nor will it recognize the logged in user. That second one is a known issue in BrowserSync 2.8.2

My fix, while it probably doesn’t get at the core issue, does at least let our designers take of auto-update goodness.

First, edit Sage’s gulpfile.js watch task (around line 246) to look like this.

gulp.task('watch', function() {
 browserSync.init({
 files: ['{lib,templates}/**/*.php', '*.php'],
 // proxy: config.devUrl,
 scriptPath: function (path, port, options) {
 return "http://localhost:" + port + options.getIn(['scriptPaths', 'path']);
 },
 socket: {
 domain: 'localhost:3000'
 },
 snippetOptions: {
 whitelist: ['/wp-admin/admin-ajax.php'],
 blacklist: ['/wp-admin/**']
 }
 });
 gulp.watch([path.source + 'styles/**/*'], ['styles']);
 gulp.watch([path.source + 'scripts/**/*'], ['jshint', 'scripts']);
 gulp.watch([path.source + 'fonts/**/*'], ['fonts']);
 gulp.watch([path.source + 'images/**/*'], ['images']);
 gulp.watch(['bower.json', 'assets/manifest.json'], ['build']);
});

Here, we have commented out the proxy setting which means we will no longer be able view our dev page at localhost:3000. This also turns on BrowserSyncs snippets mode (more on that in a sec) which means in order to use BrowserSync at this point, we need to drop a <script> snippet in our footer.

The default snippet generated by BrowserSync however will attempt to prepend your location.hostname as the resource for the javascript being served by BrowserSync, which of course is not where it’s at. I’ll show you how to fix that in the Snippet section below but just know that this will only fix the first communication with BrowserSync, not the rest. To fix that we add a scriptPath configuration that fixes the domain for all communication. Since I’m running Vagrant, I also needed to set a socket to get that fix to work.

That’s it for the Gulp file, now on to the snippet.

The Snippet

If you now quit and re-run gulp watch you’ll get a <script> snippet in the output that looks like this.

<script type='text/javascript' id="__bs_script__">//<![CDATA[
    document.write("<script async src='http://HOST:3000/browser-sync/browser-sync-client.2.10.0.js'><\/script>".replace("HOST", location.hostname));
//]]></script>

The .replace() needs to be changed so that your snippet now looks like this.

<script type='text/javascript' id="__bs_script__">//<![CDATA[
    document.write("<script async src='http://HOST:3000/browser-sync/browser-sync-client.2.10.0.js'><\/script>".replace("HOST", 'localhost'));
//]]></script>

This forces your site to request the BrowserSync javascript from the correct location even though you’ll viewing your page at its normal development address.

Now, all we need to do is drop in our snippet. This is the real downside to this whole fix as we are now stuck with this in our source. At least we can prevent it from running with a simple environment detection.

Open up base.php and just before the close of </body> enter this.

<?php if ( 'development' == WP_ENV ) : ?>
 <script type='text/javascript' id="__bs_script__">//<![CDATA[
 document.write("<script async src='http://HOST:3000/browser-sync/browser-sync-client.2.10.0.js'><\/script>".replace("HOST", 'localhost'));
 //]]></script>
 <?php endif; ?>

If you have the WP_ENV global that Sage has by default, this will work. Reload the page and you should be good to go.

I look forward to an official fix for this problem from Sage, BrowserSync or both but until then I can at least avoid manually refreshing.

I’d also love to hear if anyone has a better fix. Hope that helps.