javascriptruby-on-railssprocketsjs-routesbrowserify-rails

Requiring a sprockets-preprocessed file with Browserify and browserify-rails


I'm using browserify-rails and I'm trying to get sprockets to preprocess a file that contains a sprockets directive, so that when I require() it using browserify, it will contain the generated JavaScript.

The sprockets directive tries to include the output of the gem js-routes, in order to allow me to access the Rails routes from the clientside.


This is my setup (within app/assets/javascripts):

system/
  rails_routes.js
application.js

application.js is the main file, and it runs the rest of the application. I would like to be able to do something like

var rr = require("./system/rails_routes.js");

in it, and get access to the routes object.


Within system/react_routes.js, I have the following:

//= require js-routes

console.log("Does this work?");

(as an aside, I configured js-routes to place the output in an object called module.exports, so to comply with the CommonJS model, as described in railsware/js-routes#121)

The only issue is that when I look at the generated bundle, the sprockets directive is still there and has not been expanded.

The console.log call is also there and gets executed when I require() the module.

Is there a way to get this to work? What is the correct way to have sprockets preprocess a file before bundling it with browserify-rails?


Solution

  • I’ve spent endless hours on integrating browserify-rails in my project and making JS Routes work within this setup…

    The solution I came to and described below is the result of me not being able to have Sprockets pre-process my routes file before Browserify would come in. I have spent quite some time in both the source code of browserify-rails and sprockets but couldn't find a way to turn things around and have each component act in the correct order for this to work.

    So my solution was to use a Rails hook to generate the complete JS file « by hand » in the development environment, so that routes are always up to date with the latest Rails routes files. I then assume the routes JS file will be up to date when pushing to production.

    Doing so in the environment loading makes sure the JS file is ready before Sprockets/browserify chime in: for them it’s just another plain JS file.

    Here's the code to include in development.rb:

    ActionDispatch::Reloader.to_prepare do
      Rails.application.reload_routes!
    
      if JsRoutes.assert_usable_configuration!
        JsRoutes.generate!(Rails.root.join('app/assets/javascripts/routes.js'))
      end
    end
    

    You'll want to always reload routes, otherwise the generated file will always represent the second to last state of the Rails routes file. I never figured out why...

    In my application.js, I then just removed all //= directives but the jQuery ones (to keep a global jQuery available), and used the require method for all other modules so that browserify would pick the files I want to include.

    So this is a bit hacky, but it does work.

    I'd be interested to see whether someone with better knowledge of the Sprockets pipeline could come with a better solution?