Requiring a sprockets-preprocessed file with Brows

2019-04-07 19:10发布

问题:

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?

回答1:

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?