ruby-on-railsrubyrubygemsrails-sprockets

Sprocket Rails V4 link to manifest files of external gems


Using Ruby 3, Rails 7+ and Sprockets V4 I'm having trouble compiling the css assets from gems.

I've created a gem (as a Rails Engine) and it contains some .css assets in app/assets/stylesheets/my_gem (note that this directory is located inside the my_gem project not in the main Rails application) and also it has a app/assets/config/my_gem_manifest.js file that includes the actual .css files I need.

The only way I can use my_gem assets into the main Rails application is to explicitly set the gem's path and manually add the css file on the main Rail application's load path like:

(on main app application.rb )

config.assets.paths << '/usr/local/bundle/gems/my_gem-0.1.7/app/assets/stylesheets/my_gem'

By doing this I can actually require my css without errors but my goal is to add only the "my_gem"'s manifest file and have Sprockets to actually compile the gem's css assets and make it available to the main Rails application.

I've tried also to manually add the directory in which the gem's manifest file lives and even tried to add the manifest file to the precompile list of the main app but without success, like this:

config.assets.paths << '/usr/local/bundle/gems/my_gem-0.1.7/app/assets/config'
config.assets.precompile << "my_gem_manifest.js"

Struggling with this for days! Any help appreciated. Thank you.


Solution

  • Rails::Railtie

    Setup:

    bin/rails plugin new my_gem
    
    # my_gem/lib/my_gem/railtie.rb
    
    module MyGem
      class Railtie < ::Rails::Railtie
        def root
          @root ||= Pathname.new(__dir__).parent.parent
        end
    
        initializer :my_gem_assets do |app|
          # This is to be able to require your styles from main app application.css
          # like this:
          #
          #   /* =require my_gem/application.css */
          #
          app.config.assets.paths << root.join("app/assets/stylesheets")
    
          # This is to be able to directly link your styles from your templates:
          #
          #   <%= stylesheet_link_tag "my_gem/application.css" %>
          #
          # this path is relative to any `app.config.assets.paths`
          app.config.assets.precompile << "my_gem/application.css"
        end
      end
    end
    

    If you want to use manifest:

    # my_gem/lib/my_gem/railtie.rb
    
    module MyGem
      class Railtie < ::Rails::Railtie
        def root
          @root ||= Pathname.new(__dir__).parent.parent
        end
    
        initializer :my_gem_assets do |app|
          app.config.assets.paths << root.join("app/assets/stylesheets")
          app.config.assets.paths << root.join("app/assets/config") # <-----------------------.
          app.config.assets.precompile << "my_gem_manifest.js"      # relative to asset path -'
        end
      end
    end
    
    // my_gem/app/assets/config/my_gem_manifest.js
    
    //= link my_gem/application.css
    

    https://github.com/rails/sprockets#directives


    Rails::Engine

    Setup:

    bin/rails plugin new my_engine --mountable
    

    Unlike Railtie, Engine takes care of a lot of stuff for you:

    # my_engine/lib/my_engine/engine.rb
    
    module MyEngine
      class Engine < ::Rails::Engine
        isolate_namespace MyEngine
    
        initializer :my_engine_assets do |app|
          # This is automatically done by Rails::Engine
          # app.config.assets.paths << root.join("app/assets/stylesheets")
          # so you can just require files without extra config
          #
          #   /* =require my_engine/application.css */
    
          # If you want to link directly:
          #
          #   <%= stylesheet_link_tag "my_engine/application.css" %>
          #
          # add that file to be precompiled
          # app.config.assets.precompile << "my_engine/application.css"
          #
          # or use manifest
          # ('app/assets/config' is automatically added to assets paths)
          app.config.assets.precompile << "my_engine_manifest.js"
        end
      end
    end
    
    // my_engine/app/assets/config/my_engine_manifest.js
    
    //= link_directory ../stylesheets/my_engine .css
    

    Main app

    Now you can use your gem/engine assets from the main app:

    # app/views/layouts/application.html.erb
    
    # we can do this because these files are in
    # Rails.application.config.assets.paths and
    # Rails.application.config.assets.precompile
    <%= stylesheet_link_tag "my_gem/application.css" %>
    <%= stylesheet_link_tag "my_engine/application.css" %>
    

    or you can require files from main app application.css:

    /* app/assets/stylesheets/application.css */
    
    /* in this case you don't have to precompile them
     * =require my_gem/application.css
     * =require my_engine/application.css
     */