Generating a javascript manifest with Sprockets

1.6k views Asked by At

We frequently need to reference rails app assets from our Javascript. I've seen methods where you create an assets.js.erb file and you include your references to assets in there. Similar to the example here: Avoid *.js.erb files by building all the asset_path values

I think this is quite messy and I'd like to hook into manifest generation and generate a manifest.js at the same time. I came up with the following to do this https://gist.github.com/49d3f12bed298f0685a1

This works fine when you run assets:precompile, however, for development I need this manifest.js to be dynamically generated. I can't find an appropriate place to do this though. Is there some middleware that routes requests to /assets/* to /app/assets// in development mode, would it be appropriate to hook in at that stage?

Any suggestions would be welcome.

1

There are 1 answers

2
johnnypez On

I've been working away at this after reading the sprockets source and some of the actionpack sprockets stuff too I came up with this approach. Just put it in app/assets/javascripts/assets.js.erb or something like that. It uses the each_logical_path method from Sprockets::Environment to iterate over all asset files, then instantiates an Asset object with that path. The correct asset path is returned depending on whether asset digests are enabled for your rails env or not.

<%
  manifest = {}
  app = Rails.application
  env = app.assets
  env.each_logical_path do |logical_path|
    if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
      logical_path.sub!(/\/index\./, '.')
    end

    # grabbed from Sprockets::Environment#find_asset
    pathname = Pathname.new(logical_path)
    if pathname.absolute?
      return unless stat(pathname)
      logical_path = attributes_for(pathname).logical_path
    else
      begin
        pathname = resolve(logical_path)
      rescue Sprockets::FileNotFound
        return nil
      end
    end

    asset = Sprockets::Asset.new(env, logical_path, pathname)
    manifest[logical_path] = app.config.assets.digest ? asset.digest_path : asset.logical_path
  end
%>

!function(window, document, undefined){

  var assets = <%= ActiveSupport::JSON.encode(manifest) %>;

  var asset_path = function(path){
    if(assets.hasOwnProperty(path)){
      return '/assets/' + assets[path];
    }
    else{
      throw Error('missing asset: ' + path);
    }
  };
  window.asset_path = asset_path;
  window.asset_url = function(path){ return window.location.protocol + '//' + window.location.host + asset_path(path); };
}(window, document);

This still needs some work but I think I'm on the right track.