I'm trying to add custom JavaScript in a Ruby on Rails 7 project, so:
I created the file app/assets/javascripts/application.js containing my custom code.
In app/assets/config/manifest.js I added (found) the following code.
File app/assets/config/manifest.js:
//= link_tree ../images
//= link_directory ../stylesheets .css
//= link_tree ../../javascript .js
//= link_tree ../../../vendor/javascript .js
Now, when I visit my app pages then Stimulus doesn't work anymore even if in the browser's Console tab I don't get any error. And I have this problem even if the file created at step 1 is an empty file.
The only thing I noted in the browser's Network tab is that when the application.js file is present in the app/assets/javascripts/ folder then the Stimulus controller files don't load. When I remove the application.js file from app/assets/javascripts/ everything works again (after restarting the server).
Without app/assets/javascripts/application.js
With app/assets/javascripts/application.js
What is the problem? Am I doing something wrong or forgetting something?
You're overriding application.js
from app/javascript
. All the asset helpers will look for a matching file in asset paths and return the first match:
>> Rails.application.assets.find_asset("application.js").filename
=> "/home/alex/code/stackoverflow/app/javascript/application.js"
# ^ ^
>> Rails.root.join("app/assets/javascripts/application.js").write("var GLOB;")
>> Rails.application.assets.find_asset("application.js").filename
=> "/home/alex/code/stackoverflow/app/assets/javascripts/application.js"
# ^ ^
# now it returns a different file for the same "application.js"
In your layout, it does the same, just through javascript helpers:
>> helper.javascript_include_tag("application")
=> "<script src=\"/assets/application-04024382391bb910584145d8113cf35ef376b55d125bb4516cebeb14ce788597.js\"></script>"
>> require "open-uri"
>> URI.open(helper.javascript_path("application", host: "http://localhost:3000")).read
=> "var GLOB;\n"
>> Rails.root.join("app/assets/javascripts/application.js").delete
>> reload!
>> URI.open(helper.javascript_path("application", host: "http://localhost:3000")).read
=> "import \"@hotwired/turbo-rails\";\nimport \"controllers\";\n"
Name the file something else:
// app/assets/javascripts/globals.js
const GLOBAL = "setting";
Add it to manifest:
// app/assets/config/manifest.js
//= globals.js
// or add the whole directory
//= link_directory ../javascripts .js
Load it:
<%= javascript_include_tag "globals" %>
To do this in javascript modules, add globals to window
:
// app/javascript/globals.js
window.GLOBAL = "setting";
// app/javascript/application.js
// you have to do this in a separate file, because imports will run first
window.LOADED = "after imports";
import "./globals";
import "./components"; // <= can use GLOBAL now; LOADED is not assigned yet
However, you should just import what you need where you need it, instead of relying on global variables.