2

I'm trying to configure Rails 7 app to load all js Stimulus controllers automatically.

I use esbuild as JavaScript bundler.

If we're using Stimulus for Rails together with an import map, the integration will automatically load all controller files from app/javascript/controllers.

// app/javascript/controllers/index.js

import { application } from "controllers/application"

import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
eagerLoadControllersFrom("controllers", application)

This @hotwired/stimulus-loading package comes from importmap

# config/importmap.rb

pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true

But it looks like @hotwired/stimulus-loading has not been published to NPM yet and is meant to be used only with importmaps in Rails.

When we're using Webpacker as JavaScript bundler we can use @hotwired/stimulus-webpack-helpers package to get the same form of autoloading behavior.

// app/javascript/application.js

import { Application } from "@hotwired/stimulus"
import { definitionsFromContext } from "@hotwired/stimulus-webpack-helpers"

window.Stimulus = Application.start()
const context = require.context("./controllers", true, /\.js$/)
Stimulus.load(definitionsFromContext(context))

But when we're using esbuild JavaScript bundler on the official Stimulus page recommended to import each controller individually.

Stimulus works with other build systems too, but without support for controller autoloading. Instead, you must explicitly load and register controller files with your application instance:

// app/javascript/application.js

import { Application } from "@hotwired/stimulus"

import HelloController from "./controllers/hello_controller"
import ClipboardController from "./controllers/clipboard_controller"

window.Stimulus = Application.start()
Stimulus.register("hello", HelloController)
Stimulus.register("clipboard", ClipboardController)

So my question is:

Is it possible to configure Rails 7 with esbuild as JavaScript bundler to load all js Stimulus controller files from app/javascript/controllers automatically?

Yurii Stefaniuk
  • 1,594
  • 2
  • 11
  • 16

1 Answers1

3

Manually running a task to include all stimulus controllers

To include a new stimulus controller that you manually created in the app/javascript/controllers/index.js file, you can run:

rails stimulus:manifest:update

Automatically including a newly created stimulus controller

When you create a stimulus controller from the terminal with the generator, rails will automatically run the manifest:update command and so include your controller in js build.

rails generate stimulus controllerName

Watching for live file changes to stimulus controllers

To watch for live changes to your stimulus controllers, an additional watch process is needed. This additional watch process is appended to the Procfile.dev file after installing esbuild.

# Procfilde.dev

web: rails s -p 3000
css: yarn build:css --watch
js: yarn build --watch

The yarn build --watch command triggers the build command specified in the packacke.json file.

{
  "name": "myapplication",
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --outdir=app/assets/builds"
   }
}
Thomas Van Holder
  • 1,137
  • 9
  • 12
  • Unfortunately it doesn't solve the problem. `rails stimulus:manifest:update` just goes through all controllers and does the same include it one by one in `app/javascript/controllers/index.js` so we have bunch of Stimulus js file imports. `rails generate stimulus HelloController` also does the same creates controller and adds these lines `import HelloController from "./hello_controller.js" application.register("hello", HelloController)` to the `app/javascript/controllers/index.js`. So if we add one more we will have two imports and two registers. But I want to load all controllers at ones. – Yurii Stefaniuk Jul 07 '22 at 08:47
  • I'm not sure if it can be done without adding additional esbuild config file. You can have a look at [this package](https://github.com/excid3/esbuild-rails). – Thomas Van Holder Jul 08 '22 at 08:37