railsware / js-routes

Brings Rails named routes to javascript
http://railsware.github.io/js-routes/
MIT License
1.6k stars 146 forks source link

Rails engine support: Engine configurations impact the main app #313

Open jamesst20 opened 1 week ago

jamesst20 commented 1 week ago

Hi,

First of all, thank you for this amazing gem. I have been using it for a while and it works very well!

I'm writting a Ruby on Rails gem (Rails Engine) that is meant to be installed in many applications.

My gem provides a built in administration written in Svelte and I use js-routes to handle my routing.

An issue that I am currently facing is that my Engine needs this configuration:

JsRoutes.setup do |c|
  c.application = My::Engine
  c.file = Rcf::Engine.root.join("app/frontend/rails/routes.js")
end

As soon as I install my gem in an app that also uses JsRoutes, I need to explicitly define

  c.application = Rails.application

even if it is the default settings because the gem inself overrides this configuration.

Note: The gem comes with already pre-built javascript/css assets bundled with ruby-vite so the JsRoutes configuration really no longer matter to the gem in an application.

It would be great if instead of having like a "Global" configuration we could have kind of an "Instance" configuration per engine

jamesst20 commented 1 week ago

In case this can be of any source of inspiration, I am able to have a ViteRuby configuration that doesn't impact any application that also uses Vite and my Gem.

ViteRuby uses the "Singleton" pattern to only have one ViteRuby instance and the configuration is under that specific instance. https://github.com/ElMassimo/vite_ruby/blob/main/vite_ruby/lib/vite_ruby.rb#L39

Out of the box indeed it wouldn't work but this provides the flexibility to do that

module MyEngine
  class Engine < ::Rails::Engine
      delegate :vite_ruby, to: :class

      def self.vite_ruby
        ViteRuby.instance
          @vite_ruby ||= ::ViteRuby.new(
            root: root,
            mode: ::Rails.application.class.module_parent.to_s == "Dummy" ? "development" : "production"
          )
      end
  end
end

module MyEngine
  module ApplicationHelper
    # Override vite helper to use our own engine instance configuration
    def vite_manifest
      ::MyEngine::Engine.vite_ruby.manifest
    end
  end
end

Edit: I figured out a workaround. I will leave open in case you have better recommendations


require "js_routes"

module MyEngine
  module Middlewares
    class JsRoutes < ::JsRoutes::Middleware
      protected

      def regenerate
        ::JsRoutes.generate!(**my_engine_configuration(false))
        ::JsRoutes.definitions!(**my_engine_configuration(true))
      end

      def my_engine_configuration(definitions)
        {
          **::JsRoutes::Configuration::DEFAULTS,
          application: MyEngine::Engine,
          module_type: definitions ? "DTS" : "ESM",
          file: MyEngine::Engine.root.join("app/frontend/rails/routes#{definitions ? 'd.ts' : '.js'}")
        }
      end
    end
  end
end

PS: Configuration could have been simplified if module_type and file_name had higher priority than opts https://github.com/railsware/js-routes/blob/master/lib/js_routes.rb#L38

bogdan commented 1 week ago

I am open for a patch that changes priorities. It feels reasonable.