Search code examples
ruby-on-railsasset-pipelineruby-on-rails-7sprocketsrails-sprockets

Sprockets 4 not loading javascript assets from Rails 7 engine


TL;DR.

  • Sprockets 4 isn't loading javascript assets properly from a Rails 7 engine.

Project Overview

  • I am working on a Rails project, which is comprised of a parent-app and engine-app.

  • Both have been upgraded to rails (~> 7.0.8) from rails (~> 5.x).

  • Both are now using sprockets (~> 4.2.1) and sprockets-rails (~> 3.4.2).

    NOTE: We chose to maintain using sprockets since it's a large production application already in use and would be too costly for us to port over to the new way of managing assets at this time.

  • The parent-app is almost bare bones. Most business logic, UI, and assets are within the engine-app.

manifest.js

Sprockets 4 now requires a manifest.js to determine which top-level targets to compile (documentation).

parent-app

In parent-app, I created just such a file. Its contents are straightforward.

// file: parent-app/app/assets/config/manifest.js

//= link_tree ../images
//= link application.js # has one line of code: `//= require_tree .`
//= link application.sass # has one line of code: `@import engine_app/application`
//= link engine_app_manifest.js

engine-app

In engine-app, I created a similar file with a different name. The name of the manifest.js in the engine can be anything from what I've gathered. This is based on information I gathered from the following SO posts:

// file: engine-app/app/assets/config/engine_app_manifest.js

//= link_tree ../fonts/
//= link_tree ../images/
//= link_directory ../javascripts .js
//= link_directory ../stylesheets .css
link_directory ../javascripts

There are several .js files in this directory that require other javascript. The most important of which is application.js. Here's a snippet.

// file: engine-app/app/assets/javascripts/application.js

//= require babel/polyfill
//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require jquery.remotipart
//= require smart_listing
//= require underscore-dev

// ... and more

alert("HELLO WORLD!"); // added this to test if its being loaded

Results

Based on the SO posts referenced above, this is all that should be needed to load assets from a vendor gem or engine, but it seems not only to work partially. The stylesheets from the engine-app seem to be loaded as the UI looks as it should. However, the javascripts are not.

There are Uncaught ReferrenceErrors being thrown and reported in the console. The alert message isn't being shown either. And when I look at the source files loaded using Chrome's DevTools, there are only a few files.

parent-app-rails-7.0.8

Contrast this to what's being loaded in the legacy app.

parent-app-rails-5.x


Solution

  • You can use sprockets directives in main app application.js:

    //= require engine_name/application.js 
    

    or load it directly, in this case this file has to be precompiled as well:

    <%= javascript_include_tag "engine_name/application.js" %>
    
    // link for precompilation in any of the manifest files
    
    //= link engine_name/application.js
    

    link and require directives take asset name as an argument relative to Rails.application.assets.paths.

    Your main issue was the use of module scripts, which changes the load order of the script:

    <script type="module">
      // module scripts are deferred and loaded after the page is loaded
      console.log("second")
    </script>
    
    <script>
      console.log("first")
    </script>


    Installation instructions for i18n-js don't require a module:
    https://github.com/fnando/i18n-js/tree/v3.9.2?tab=readme-ov-file#rails-app-without-asset-pipeline

    Also, note that you can add type="module" to any script tag if needed:

    <script type="module">I18n.globals = {};</script>